summaryrefslogtreecommitdiffstats
path: root/extensions/source
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /extensions/source
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--extensions/source/abpilot/abp.component26
-rw-r--r--extensions/source/abpilot/abpfinalpage.cxx227
-rw-r--r--extensions/source/abpilot/abpfinalpage.hxx74
-rw-r--r--extensions/source/abpilot/abptypes.hxx40
-rw-r--r--extensions/source/abpilot/abspage.cxx74
-rw-r--r--extensions/source/abpilot/abspage.hxx56
-rw-r--r--extensions/source/abpilot/abspilot.cxx449
-rw-r--r--extensions/source/abpilot/abspilot.hxx119
-rw-r--r--extensions/source/abpilot/addresssettings.hxx56
-rw-r--r--extensions/source/abpilot/admininvokationimpl.cxx116
-rw-r--r--extensions/source/abpilot/admininvokationimpl.hxx50
-rw-r--r--extensions/source/abpilot/admininvokationpage.cxx90
-rw-r--r--extensions/source/abpilot/admininvokationpage.hxx52
-rw-r--r--extensions/source/abpilot/datasourcehandling.cxx620
-rw-r--r--extensions/source/abpilot/datasourcehandling.hxx181
-rw-r--r--extensions/source/abpilot/fieldmappingimpl.cxx317
-rw-r--r--extensions/source/abpilot/fieldmappingimpl.hxx111
-rw-r--r--extensions/source/abpilot/fieldmappingpage.cxx77
-rw-r--r--extensions/source/abpilot/fieldmappingpage.hxx46
-rw-r--r--extensions/source/abpilot/moduleabp.cxx22
-rw-r--r--extensions/source/abpilot/tableselectionpage.cxx101
-rw-r--r--extensions/source/abpilot/tableselectionpage.hxx50
-rw-r--r--extensions/source/abpilot/typeselectionpage.cxx224
-rw-r--r--extensions/source/abpilot/typeselectionpage.hxx81
-rw-r--r--extensions/source/abpilot/unodialogabp.cxx156
-rw-r--r--extensions/source/abpilot/unodialogabp.hxx78
-rw-r--r--extensions/source/activex/README.txt33
-rw-r--r--extensions/source/activex/SOActionsApproval.cxx58
-rw-r--r--extensions/source/activex/SOActionsApproval.h105
-rw-r--r--extensions/source/activex/SOActionsApproval.rgs24
-rw-r--r--extensions/source/activex/SOActiveX.cxx1162
-rw-r--r--extensions/source/activex/SOActiveX.h212
-rw-r--r--extensions/source/activex/SOActiveX.rgs33
-rw-r--r--extensions/source/activex/SOComWindowPeer.cxx57
-rw-r--r--extensions/source/activex/SOComWindowPeer.h159
-rw-r--r--extensions/source/activex/SOComWindowPeer.rgs23
-rw-r--r--extensions/source/activex/SODispatchInterceptor.cxx248
-rw-r--r--extensions/source/activex/SODispatchInterceptor.h175
-rw-r--r--extensions/source/activex/SODispatchInterceptor.rgs23
-rw-r--r--extensions/source/activex/StdAfx2.cxx30
-rw-r--r--extensions/source/activex/StdAfx2.h68
-rw-r--r--extensions/source/activex/com_uno_helper.h44
-rw-r--r--extensions/source/activex/example.html43
-rw-r--r--extensions/source/activex/resource.h46
-rw-r--r--extensions/source/activex/so_activex.cxx774
-rw-r--r--extensions/source/activex/so_activex.def13
-rw-r--r--extensions/source/activex/so_activex.idl230
-rw-r--r--extensions/source/activex/so_activex.rc124
-rw-r--r--extensions/source/bibliography/bib.component27
-rw-r--r--extensions/source/bibliography/bibbeam.cxx267
-rw-r--r--extensions/source/bibliography/bibbeam.hxx71
-rw-r--r--extensions/source/bibliography/bibconfig.cxx297
-rw-r--r--extensions/source/bibliography/bibconfig.hxx153
-rw-r--r--extensions/source/bibliography/bibcont.cxx241
-rw-r--r--extensions/source/bibliography/bibcont.hxx93
-rw-r--r--extensions/source/bibliography/bibload.cxx609
-rw-r--r--extensions/source/bibliography/bibmod.cxx89
-rw-r--r--extensions/source/bibliography/bibmod.hxx50
-rw-r--r--extensions/source/bibliography/bibresid.hxx27
-rw-r--r--extensions/source/bibliography/bibshortcuthandler.hxx66
-rw-r--r--extensions/source/bibliography/bibtools.hxx42
-rw-r--r--extensions/source/bibliography/bibview.cxx189
-rw-r--r--extensions/source/bibliography/bibview.hxx83
-rw-r--r--extensions/source/bibliography/datman.cxx1390
-rw-r--r--extensions/source/bibliography/datman.hxx169
-rw-r--r--extensions/source/bibliography/formcontrolcontainer.cxx136
-rw-r--r--extensions/source/bibliography/formcontrolcontainer.hxx65
-rw-r--r--extensions/source/bibliography/framectr.cxx869
-rw-r--r--extensions/source/bibliography/framectr.hxx117
-rw-r--r--extensions/source/bibliography/general.cxx876
-rw-r--r--extensions/source/bibliography/general.hxx158
-rw-r--r--extensions/source/bibliography/loadlisteneradapter.cxx185
-rw-r--r--extensions/source/bibliography/loadlisteneradapter.hxx151
-rw-r--r--extensions/source/bibliography/toolbar.cxx620
-rw-r--r--extensions/source/bibliography/toolbar.hxx217
-rw-r--r--extensions/source/config/WinUserInfo/WinUserInfoBe.component16
-rw-r--r--extensions/source/config/WinUserInfo/WinUserInfoBe.cxx433
-rw-r--r--extensions/source/config/WinUserInfo/WinUserInfoBe.hxx101
-rw-r--r--extensions/source/config/ldap/ldapaccess.cxx289
-rw-r--r--extensions/source/config/ldap/ldapaccess.hxx133
-rw-r--r--extensions/source/config/ldap/ldapbe2.component26
-rw-r--r--extensions/source/config/ldap/ldapuserprofilebe.cxx214
-rw-r--r--extensions/source/config/ldap/ldapuserprofilebe.hxx113
-rw-r--r--extensions/source/dbpilots/commonpagesdbp.cxx449
-rw-r--r--extensions/source/dbpilots/commonpagesdbp.hxx120
-rw-r--r--extensions/source/dbpilots/controlwizard.cxx663
-rw-r--r--extensions/source/dbpilots/controlwizard.hxx149
-rw-r--r--extensions/source/dbpilots/dbp.component34
-rw-r--r--extensions/source/dbpilots/dbptools.cxx62
-rw-r--r--extensions/source/dbpilots/dbptools.hxx37
-rw-r--r--extensions/source/dbpilots/dbptypes.hxx36
-rw-r--r--extensions/source/dbpilots/gridwizard.cxx446
-rw-r--r--extensions/source/dbpilots/gridwizard.hxx103
-rw-r--r--extensions/source/dbpilots/groupboxwiz.cxx468
-rw-r--r--extensions/source/dbpilots/groupboxwiz.hxx180
-rw-r--r--extensions/source/dbpilots/listcombowizard.cxx485
-rw-r--r--extensions/source/dbpilots/listcombowizard.hxx178
-rw-r--r--extensions/source/dbpilots/moduledbp.cxx22
-rw-r--r--extensions/source/dbpilots/optiongrouplayouter.cxx204
-rw-r--r--extensions/source/dbpilots/optiongrouplayouter.hxx58
-rw-r--r--extensions/source/dbpilots/unoautopilot.hxx116
-rw-r--r--extensions/source/dbpilots/wizardcontext.hxx82
-rw-r--r--extensions/source/dbpilots/wizardservices.cxx65
-rw-r--r--extensions/source/inc/componentmodule.cxx35
-rw-r--r--extensions/source/inc/componentmodule.hxx34
-rw-r--r--extensions/source/logging/consolehandler.cxx264
-rw-r--r--extensions/source/logging/csvformatter.cxx320
-rw-r--r--extensions/source/logging/filehandler.cxx359
-rw-r--r--extensions/source/logging/log.component47
-rw-r--r--extensions/source/logging/logger.cxx265
-rw-r--r--extensions/source/logging/loggerconfig.cxx283
-rw-r--r--extensions/source/logging/loggerconfig.hxx50
-rw-r--r--extensions/source/logging/loghandler.cxx183
-rw-r--r--extensions/source/logging/loghandler.hxx142
-rw-r--r--extensions/source/logging/logrecord.cxx86
-rw-r--r--extensions/source/logging/logrecord.hxx53
-rw-r--r--extensions/source/logging/methodguard.hxx55
-rw-r--r--extensions/source/logging/plaintextformatter.cxx154
-rw-r--r--extensions/source/logging/simpletextformatter.cxx98
-rw-r--r--extensions/source/macosx/spotlight/GetMetadataForFile.h26
-rw-r--r--extensions/source/macosx/spotlight/GetMetadataForFile.m64
-rw-r--r--extensions/source/macosx/spotlight/OOoContentDataParser.h51
-rw-r--r--extensions/source/macosx/spotlight/OOoContentDataParser.m144
-rw-r--r--extensions/source/macosx/spotlight/OOoMetaDataParser.h46
-rw-r--r--extensions/source/macosx/spotlight/OOoMetaDataParser.m205
-rw-r--r--extensions/source/macosx/spotlight/OOoSpotlightImporter.h37
-rw-r--r--extensions/source/macosx/spotlight/OOoSpotlightImporter.m487
-rw-r--r--extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/.gitignore1
-rw-r--r--extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/project.pbxproj308
-rw-r--r--extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/xcshareddata/xcschemes/SpotlightImporterTester.xcscheme88
-rw-r--r--extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester/main.m30
-rw-r--r--extensions/source/macosx/spotlight/main.m211
-rw-r--r--extensions/source/macosx/spotlight/mdimporter/Info.plist87
-rw-r--r--extensions/source/macosx/spotlight/mdimporter/en.lproj/schema.strings1
-rw-r--r--extensions/source/macosx/spotlight/mdimporter/schema.xml413
-rw-r--r--extensions/source/macosx/spotlight/version.plist33
-rw-r--r--extensions/source/ole/comifaces.hxx62
-rw-r--r--extensions/source/ole/jscriptclasses.cxx312
-rw-r--r--extensions/source/ole/jscriptclasses.hxx147
-rw-r--r--extensions/source/ole/ole2uno.cxx47
-rw-r--r--extensions/source/ole/ole2uno.hxx70
-rw-r--r--extensions/source/ole/oleautobridge.component37
-rw-r--r--extensions/source/ole/oledll.cxx70
-rw-r--r--extensions/source/ole/oleobjw.cxx2513
-rw-r--r--extensions/source/ole/oleobjw.hxx244
-rw-r--r--extensions/source/ole/olethread.cxx66
-rw-r--r--extensions/source/ole/servprov.cxx548
-rw-r--r--extensions/source/ole/servprov.hxx184
-rw-r--r--extensions/source/ole/servreg.cxx94
-rw-r--r--extensions/source/ole/unoconversionutilities.hxx2363
-rw-r--r--extensions/source/ole/unoobjw.cxx3437
-rw-r--r--extensions/source/ole/unoobjw.hxx268
-rw-r--r--extensions/source/ole/unotypewrapper.cxx160
-rw-r--r--extensions/source/ole/unotypewrapper.hxx81
-rw-r--r--extensions/source/ole/wincrap.hxx64
-rw-r--r--extensions/source/ole/windata.hxx196
-rw-r--r--extensions/source/propctrlr/MasterDetailLinkDialog.cxx133
-rw-r--r--extensions/source/propctrlr/MasterDetailLinkDialog.hxx67
-rw-r--r--extensions/source/propctrlr/browserline.cxx405
-rw-r--r--extensions/source/propctrlr/browserline.hxx127
-rw-r--r--extensions/source/propctrlr/browserlistbox.cxx817
-rw-r--r--extensions/source/propctrlr/browserlistbox.hxx163
-rw-r--r--extensions/source/propctrlr/browserpage.cxx41
-rw-r--r--extensions/source/propctrlr/browserpage.hxx61
-rw-r--r--extensions/source/propctrlr/browserview.cxx64
-rw-r--r--extensions/source/propctrlr/browserview.hxx56
-rw-r--r--extensions/source/propctrlr/buttonnavigationhandler.cxx268
-rw-r--r--extensions/source/propctrlr/buttonnavigationhandler.hxx71
-rw-r--r--extensions/source/propctrlr/cellbindinghandler.cxx487
-rw-r--r--extensions/source/propctrlr/cellbindinghandler.hxx92
-rw-r--r--extensions/source/propctrlr/cellbindinghelper.cxx540
-rw-r--r--extensions/source/propctrlr/cellbindinghelper.hxx272
-rw-r--r--extensions/source/propctrlr/commoncontrol.cxx134
-rw-r--r--extensions/source/propctrlr/commoncontrol.hxx208
-rw-r--r--extensions/source/propctrlr/composeduiupdate.cxx786
-rw-r--r--extensions/source/propctrlr/composeduiupdate.hxx208
-rw-r--r--extensions/source/propctrlr/controlfontdialog.cxx148
-rw-r--r--extensions/source/propctrlr/controlfontdialog.hxx82
-rw-r--r--extensions/source/propctrlr/controltype.hxx35
-rw-r--r--extensions/source/propctrlr/defaultforminspection.cxx215
-rw-r--r--extensions/source/propctrlr/defaultforminspection.hxx66
-rw-r--r--extensions/source/propctrlr/defaulthelpprovider.cxx184
-rw-r--r--extensions/source/propctrlr/defaulthelpprovider.hxx78
-rw-r--r--extensions/source/propctrlr/editpropertyhandler.cxx308
-rw-r--r--extensions/source/propctrlr/editpropertyhandler.hxx68
-rw-r--r--extensions/source/propctrlr/eformshelper.cxx762
-rw-r--r--extensions/source/propctrlr/eformshelper.hxx255
-rw-r--r--extensions/source/propctrlr/eformspropertyhandler.cxx598
-rw-r--r--extensions/source/propctrlr/eformspropertyhandler.hxx95
-rw-r--r--extensions/source/propctrlr/enumrepresentation.hxx62
-rw-r--r--extensions/source/propctrlr/eventhandler.cxx1112
-rw-r--r--extensions/source/propctrlr/eventhandler.hxx241
-rw-r--r--extensions/source/propctrlr/fontdialog.cxx572
-rw-r--r--extensions/source/propctrlr/fontdialog.hxx68
-rw-r--r--extensions/source/propctrlr/fontitemids.hxx48
-rw-r--r--extensions/source/propctrlr/formbrowsertools.cxx132
-rw-r--r--extensions/source/propctrlr/formbrowsertools.hxx89
-rw-r--r--extensions/source/propctrlr/formcomponenthandler.cxx3305
-rw-r--r--extensions/source/propctrlr/formcomponenthandler.hxx435
-rw-r--r--extensions/source/propctrlr/formcontroller.cxx241
-rw-r--r--extensions/source/propctrlr/formcontroller.hxx103
-rw-r--r--extensions/source/propctrlr/formgeometryhandler.cxx820
-rw-r--r--extensions/source/propctrlr/formlinkdialog.cxx630
-rw-r--r--extensions/source/propctrlr/formlinkdialog.hxx127
-rw-r--r--extensions/source/propctrlr/formmetadata.cxx694
-rw-r--r--extensions/source/propctrlr/formmetadata.hxx345
-rw-r--r--extensions/source/propctrlr/formstrings.hxx302
-rw-r--r--extensions/source/propctrlr/genericpropertyhandler.cxx619
-rw-r--r--extensions/source/propctrlr/genericpropertyhandler.hxx139
-rw-r--r--extensions/source/propctrlr/handlerhelper.cxx314
-rw-r--r--extensions/source/propctrlr/handlerhelper.hxx231
-rw-r--r--extensions/source/propctrlr/inspectorhelpwindow.cxx42
-rw-r--r--extensions/source/propctrlr/inspectorhelpwindow.hxx44
-rw-r--r--extensions/source/propctrlr/inspectormodelbase.cxx246
-rw-r--r--extensions/source/propctrlr/inspectormodelbase.hxx93
-rw-r--r--extensions/source/propctrlr/linedescriptor.hxx51
-rw-r--r--extensions/source/propctrlr/listselectiondlg.cxx138
-rw-r--r--extensions/source/propctrlr/listselectiondlg.hxx60
-rw-r--r--extensions/source/propctrlr/modulepcr.cxx30
-rw-r--r--extensions/source/propctrlr/modulepcr.hxx30
-rw-r--r--extensions/source/propctrlr/newdatatype.cxx79
-rw-r--r--extensions/source/propctrlr/newdatatype.hxx53
-rw-r--r--extensions/source/propctrlr/objectinspectormodel.cxx194
-rw-r--r--extensions/source/propctrlr/pcr.component103
-rw-r--r--extensions/source/propctrlr/pcrcommon.cxx60
-rw-r--r--extensions/source/propctrlr/pcrcommon.hxx121
-rw-r--r--extensions/source/propctrlr/pcrcommontypes.hxx33
-rw-r--r--extensions/source/propctrlr/pcrstrings.hxx35
-rw-r--r--extensions/source/propctrlr/pcrunodialogs.cxx143
-rw-r--r--extensions/source/propctrlr/pcrunodialogs.hxx79
-rw-r--r--extensions/source/propctrlr/propcontroller.cxx1652
-rw-r--r--extensions/source/propctrlr/propcontroller.hxx368
-rw-r--r--extensions/source/propctrlr/propcontrolobserver.hxx46
-rw-r--r--extensions/source/propctrlr/propertycomposer.cxx484
-rw-r--r--extensions/source/propctrlr/propertycomposer.hxx142
-rw-r--r--extensions/source/propctrlr/propertycontrolextender.cxx125
-rw-r--r--extensions/source/propctrlr/propertycontrolextender.hxx63
-rw-r--r--extensions/source/propctrlr/propertyeditor.cxx380
-rw-r--r--extensions/source/propctrlr/propertyeditor.hxx134
-rw-r--r--extensions/source/propctrlr/propertyhandler.cxx421
-rw-r--r--extensions/source/propctrlr/propertyhandler.hxx369
-rw-r--r--extensions/source/propctrlr/propertyinfo.hxx50
-rw-r--r--extensions/source/propctrlr/propeventtranslation.cxx89
-rw-r--r--extensions/source/propctrlr/propeventtranslation.hxx70
-rw-r--r--extensions/source/propctrlr/proplinelistener.hxx43
-rw-r--r--extensions/source/propctrlr/pushbuttonnavigation.cxx298
-rw-r--r--extensions/source/propctrlr/pushbuttonnavigation.hxx98
-rw-r--r--extensions/source/propctrlr/selectlabeldialog.cxx279
-rw-r--r--extensions/source/propctrlr/selectlabeldialog.hxx62
-rw-r--r--extensions/source/propctrlr/sqlcommanddesign.cxx355
-rw-r--r--extensions/source/propctrlr/sqlcommanddesign.hxx193
-rw-r--r--extensions/source/propctrlr/standardcontrol.cxx865
-rw-r--r--extensions/source/propctrlr/standardcontrol.hxx412
-rw-r--r--extensions/source/propctrlr/stringrepresentation.cxx604
-rw-r--r--extensions/source/propctrlr/submissionhandler.cxx431
-rw-r--r--extensions/source/propctrlr/submissionhandler.hxx110
-rw-r--r--extensions/source/propctrlr/taborder.cxx320
-rw-r--r--extensions/source/propctrlr/taborder.hxx74
-rw-r--r--extensions/source/propctrlr/unourl.cxx65
-rw-r--r--extensions/source/propctrlr/unourl.hxx50
-rw-r--r--extensions/source/propctrlr/usercontrol.cxx276
-rw-r--r--extensions/source/propctrlr/usercontrol.hxx148
-rw-r--r--extensions/source/propctrlr/xsddatatypes.cxx185
-rw-r--r--extensions/source/propctrlr/xsddatatypes.hxx89
-rw-r--r--extensions/source/propctrlr/xsdvalidationhelper.cxx406
-rw-r--r--extensions/source/propctrlr/xsdvalidationhelper.hxx137
-rw-r--r--extensions/source/propctrlr/xsdvalidationpropertyhandler.cxx673
-rw-r--r--extensions/source/propctrlr/xsdvalidationpropertyhandler.hxx88
-rw-r--r--extensions/source/scanner/grid.cxx700
-rw-r--r--extensions/source/scanner/grid.hxx51
-rw-r--r--extensions/source/scanner/sane.cxx996
-rw-r--r--extensions/source/scanner/sane.hxx190
-rw-r--r--extensions/source/scanner/sanedlg.cxx1467
-rw-r--r--extensions/source/scanner/sanedlg.hxx111
-rw-r--r--extensions/source/scanner/scanner.cxx89
-rw-r--r--extensions/source/scanner/scanner.hxx85
-rw-r--r--extensions/source/scanner/scanunx.cxx341
-rw-r--r--extensions/source/scanner/scanwin.cxx646
-rw-r--r--extensions/source/scanner/scn.component26
-rw-r--r--extensions/source/scanner/twain32shim.cxx604
-rw-r--r--extensions/source/scanner/twain32shim.hxx68
-rw-r--r--extensions/source/update/check/actionlistener.hxx39
-rw-r--r--extensions/source/update/check/download.cxx427
-rw-r--r--extensions/source/update/check/download.hxx80
-rw-r--r--extensions/source/update/check/onlinecheck.cxx49
-rw-r--r--extensions/source/update/check/onlinecheck.hxx28
-rw-r--r--extensions/source/update/check/org/openoffice/Office/Addons.xcu42
-rw-r--r--extensions/source/update/check/org/openoffice/Office/Jobs.xcu66
-rw-r--r--extensions/source/update/check/updatecheck.cxx1617
-rw-r--r--extensions/source/update/check/updatecheck.hxx189
-rw-r--r--extensions/source/update/check/updatecheckconfig.cxx660
-rw-r--r--extensions/source/update/check/updatecheckconfig.hxx205
-rw-r--r--extensions/source/update/check/updatecheckconfiglistener.hxx37
-rw-r--r--extensions/source/update/check/updatecheckjob.cxx322
-rw-r--r--extensions/source/update/check/updatehdl.cxx1281
-rw-r--r--extensions/source/update/check/updatehdl.hxx207
-rw-r--r--extensions/source/update/check/updateinfo.hxx61
-rw-r--r--extensions/source/update/check/updateprotocol.cxx317
-rw-r--r--extensions/source/update/check/updateprotocol.hxx68
-rw-r--r--extensions/source/update/check/updateprotocoltest.cxx76
-rw-r--r--extensions/source/update/check/updchk.uno.component30
-rw-r--r--extensions/source/update/feed/test/updatefeedtest.cxx86
-rw-r--r--extensions/source/update/feed/updatefeed.component26
-rw-r--r--extensions/source/update/feed/updatefeed.cxx755
-rw-r--r--extensions/source/update/ui/updatecheckui.cxx306
-rw-r--r--extensions/source/update/ui/updchk.component26
306 files changed, 78524 insertions, 0 deletions
diff --git a/extensions/source/abpilot/abp.component b/extensions/source/abpilot/abp.component
new file mode 100644
index 000000000..91b9638c4
--- /dev/null
+++ b/extensions/source/abpilot/abp.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="org.openoffice.comp.abp.OAddressBookSourcePilot"
+ constructor="org_openoffice_comp_abp_OAddressBookSourcePilot">
+ <service name="com.sun.star.ui.dialogs.AddressBookSourcePilot"/>
+ </implementation>
+</component>
diff --git a/extensions/source/abpilot/abpfinalpage.cxx b/extensions/source/abpilot/abpfinalpage.cxx
new file mode 100644
index 000000000..045a66d3f
--- /dev/null
+++ b/extensions/source/abpilot/abpfinalpage.cxx
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "abpfinalpage.hxx"
+#include "addresssettings.hxx"
+#include "abspilot.hxx"
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <svtools/inettbc.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/pathoptions.hxx>
+#include <svl/filenotation.hxx>
+#include <sfx2/docfilt.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace abp
+{
+
+ using namespace ::svt;
+ using namespace ::utl;
+
+ static std::shared_ptr<const SfxFilter> lcl_getBaseFilter()
+ {
+ std::shared_ptr<const SfxFilter> pFilter = SfxFilter::GetFilterByName("StarOffice XML (Base)");
+ OSL_ENSURE(pFilter,"Filter: StarOffice XML (Base) could not be found!");
+ return pFilter;
+ }
+
+ FinalPage::FinalPage(weld::Container* pPage, OAddressBookSourcePilot* pWizard)
+ : AddressBookSourcePage(pPage, pWizard, "modules/sabpilot/ui/datasourcepage.ui",
+ "DataSourcePage")
+ , m_xLocation(new SvtURLBox(m_xBuilder->weld_combo_box("location")))
+ , m_xBrowse(m_xBuilder->weld_button("browse"))
+ , m_xRegisterName(m_xBuilder->weld_check_button("available"))
+ , m_xEmbed(m_xBuilder->weld_check_button("embed"))
+ , m_xNameLabel(m_xBuilder->weld_label("nameft"))
+ , m_xLocationLabel(m_xBuilder->weld_label("locationft"))
+ , m_xName(m_xBuilder->weld_entry("name"))
+ , m_xDuplicateNameError(m_xBuilder->weld_label("warning"))
+ {
+ m_xLocation->SetSmartProtocol(INetProtocol::File);
+ m_xLocation->DisableHistory();
+
+ m_xLocationController.reset( new svx::DatabaseLocationInputController(pWizard->getORB(),
+ *m_xLocation, *m_xBrowse, *pWizard->getDialog()) );
+
+ m_xName->connect_changed( LINK(this, FinalPage, OnEntryNameModified) );
+ m_xLocation->connect_changed( LINK(this, FinalPage, OnComboNameModified) );
+ m_xRegisterName->connect_toggled( LINK( this, FinalPage, OnRegister ) );
+ m_xRegisterName->set_active(true);
+ m_xEmbed->connect_toggled( LINK( this, FinalPage, OnEmbed ) );
+ m_xEmbed->set_active(true);
+ }
+
+ FinalPage::~FinalPage()
+ {
+ m_xLocationController.reset();
+ }
+
+ bool FinalPage::isValidName() const
+ {
+ OUString sCurrentName(m_xName->get_text());
+
+ if (sCurrentName.isEmpty())
+ // the name must not be empty
+ return false;
+
+ if ( m_aInvalidDataSourceNames.find( sCurrentName ) != m_aInvalidDataSourceNames.end() )
+ // there already is a data source with this name
+ return false;
+
+ return true;
+ }
+
+ void FinalPage::setFields()
+ {
+ AddressSettings& rSettings = getSettings();
+
+ INetURLObject aURL( rSettings.sDataSourceName );
+ if( aURL.GetProtocol() == INetProtocol::NotValid )
+ {
+ OUString sPath = SvtPathOptions().GetWorkPath();
+ sPath += "/" + rSettings.sDataSourceName;
+
+ std::shared_ptr<const SfxFilter> pFilter = lcl_getBaseFilter();
+ if ( pFilter )
+ {
+ OUString sExt = pFilter->GetDefaultExtension();
+ sPath += o3tl::getToken(sExt,1,'*');
+ }
+
+ aURL.SetURL(sPath);
+ }
+ OSL_ENSURE( aURL.GetProtocol() != INetProtocol::NotValid ,"No valid file name!");
+ rSettings.sDataSourceName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ m_xLocationController->setURL( rSettings.sDataSourceName );
+ OUString sName = aURL.getName( );
+ sal_Int32 nPos = sName.indexOf(aURL.GetFileExtension());
+ if ( nPos != -1 )
+ {
+ sName = sName.replaceAt(nPos-1, 4, u"");
+ }
+ m_xName->set_text(sName);
+
+ OnRegister(*m_xRegisterName);
+ }
+
+
+ void FinalPage::initializePage()
+ {
+ AddressBookSourcePage::initializePage();
+
+ setFields();
+ }
+
+ bool FinalPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!AddressBookSourcePage::commitPage(_eReason))
+ return false;
+
+ if ( ( ::vcl::WizardTypes::eTravelBackward != _eReason )
+ && ( !m_xLocationController->prepareCommit() )
+ )
+ return false;
+
+ AddressSettings& rSettings = getSettings();
+ rSettings.sDataSourceName = m_xLocationController->getURL();
+ rSettings.bRegisterDataSource = m_xRegisterName->get_active();
+ if ( rSettings.bRegisterDataSource )
+ rSettings.sRegisteredDataSourceName = m_xName->get_text();
+ rSettings.bEmbedDataSource = m_xEmbed->get_active();
+
+ return true;
+ }
+
+ void FinalPage::Activate()
+ {
+ AddressBookSourcePage::Activate();
+
+ // get the names of all data sources
+ ODataSourceContext aContext( getORB() );
+ aContext.getDataSourceNames( m_aInvalidDataSourceNames );
+
+ // give the name edit the focus
+ m_xLocation->grab_focus();
+
+ // default the finish button
+ getDialog()->defaultButton( WizardButtonFlags::FINISH );
+
+ OnEmbed(*m_xEmbed);
+ }
+
+ void FinalPage::Deactivate()
+ {
+ AddressBookSourcePage::Deactivate();
+
+ // default the "next" button, again
+ getDialog()->defaultButton( WizardButtonFlags::NEXT );
+ // disable the finish button
+ getDialog()->enableButtons( WizardButtonFlags::FINISH, false );
+ }
+
+
+ bool FinalPage::canAdvance() const
+ {
+ return false;
+ }
+
+ void FinalPage::implCheckName()
+ {
+ bool bValidName = isValidName();
+ bool bEmptyName = m_xName->get_text().isEmpty();
+ bool bEmptyLocation = m_xLocation->get_active_text().isEmpty();
+
+ // enable or disable the finish button
+ getDialog()->enableButtons( WizardButtonFlags::FINISH, !bEmptyLocation && (!m_xRegisterName->get_active() || bValidName) );
+
+ // show the error message for an invalid name
+ m_xDuplicateNameError->set_visible(!bValidName && !bEmptyName);
+ }
+
+ IMPL_LINK_NOARG( FinalPage, OnEntryNameModified, weld::Entry&, void )
+ {
+ implCheckName();
+ }
+
+ IMPL_LINK_NOARG( FinalPage, OnComboNameModified, weld::ComboBox&, void )
+ {
+ implCheckName();
+ }
+
+ IMPL_LINK_NOARG(FinalPage, OnRegister, weld::Toggleable&, void)
+ {
+ bool bEnable = m_xRegisterName->get_active();
+ m_xNameLabel->set_sensitive(bEnable);
+ m_xName->set_sensitive(bEnable);
+ implCheckName();
+ }
+
+ IMPL_LINK_NOARG(FinalPage, OnEmbed, weld::Toggleable&, void)
+ {
+ bool bEmbed = m_xEmbed->get_active();
+ m_xLocationLabel->set_sensitive(!bEmbed);
+ m_xLocation->set_sensitive(!bEmbed);
+ m_xBrowse->set_sensitive(!bEmbed);
+ }
+
+} // namespace abp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/abpfinalpage.hxx b/extensions/source/abpilot/abpfinalpage.hxx
new file mode 100644
index 000000000..6c6c5d69d
--- /dev/null
+++ b/extensions/source/abpilot/abpfinalpage.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 "abspage.hxx"
+#include "abptypes.hxx"
+
+#include <svx/databaselocationinput.hxx>
+#include <vcl/vclptr.hxx>
+
+namespace abp
+{
+
+ class FinalPage final : public AddressBookSourcePage
+ {
+ std::unique_ptr<SvtURLBox> m_xLocation;
+ std::unique_ptr<weld::Button> m_xBrowse;
+ std::unique_ptr<weld::CheckButton> m_xRegisterName;
+ std::unique_ptr<weld::CheckButton> m_xEmbed;
+ std::unique_ptr<weld::Label> m_xNameLabel;
+ std::unique_ptr<weld::Label> m_xLocationLabel;
+ std::unique_ptr<weld::Entry> m_xName;
+ std::unique_ptr<weld::Label> m_xDuplicateNameError;
+
+ std::unique_ptr<svx::DatabaseLocationInputController>
+ m_xLocationController;
+
+ StringBag m_aInvalidDataSourceNames;
+
+ public:
+ explicit FinalPage(weld::Container* pPage, OAddressBookSourcePilot* pController);
+ virtual ~FinalPage() override;
+
+ private:
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+
+ // BuilderPage overridables
+ virtual void Activate() override;
+ virtual void Deactivate() override;
+
+ // OImportPage overridables
+ virtual bool canAdvance() const override;
+
+ DECL_LINK(OnEntryNameModified, weld::Entry&, void);
+ DECL_LINK(OnComboNameModified, weld::ComboBox&, void);
+ DECL_LINK(OnRegister, weld::Toggleable&, void);
+ DECL_LINK(OnEmbed, weld::Toggleable&, void);
+
+ bool isValidName() const;
+ void implCheckName();
+ void setFields();
+ };
+
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/abptypes.hxx b/extensions/source/abpilot/abptypes.hxx
new file mode 100644
index 000000000..0339253ff
--- /dev/null
+++ b/extensions/source/abpilot/abptypes.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <map>
+#include <set>
+
+#include <rtl/ustring.hxx>
+
+
+namespace abp
+{
+
+
+ typedef std::set<OUString> StringBag;
+
+ typedef std::map<OUString, OUString> MapString2String;
+
+
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/abspage.cxx b/extensions/source/abpilot/abspage.cxx
new file mode 100644
index 000000000..ee36cee27
--- /dev/null
+++ b/extensions/source/abpilot/abspage.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 "abspage.hxx"
+#include "abspilot.hxx"
+
+namespace abp
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+
+ AddressBookSourcePage::AddressBookSourcePage(weld::Container* pPage, OAddressBookSourcePilot* pDialog, const OUString& rUIXMLDescription, const OString& rID)
+ : AddressBookSourcePage_Base(pPage, pDialog, rUIXMLDescription, rID)
+ , m_pDialog(pDialog)
+ {
+ }
+
+ void AddressBookSourcePage::Activate()
+ {
+ AddressBookSourcePage_Base::Activate();
+ m_pDialog->updateTravelUI();
+ }
+
+ void AddressBookSourcePage::Deactivate()
+ {
+ AddressBookSourcePage_Base::Deactivate();
+ m_pDialog->enableButtons(WizardButtonFlags::NEXT, true);
+ }
+
+ OAddressBookSourcePilot* AddressBookSourcePage::getDialog()
+ {
+ return m_pDialog;
+ }
+
+ const OAddressBookSourcePilot* AddressBookSourcePage::getDialog() const
+ {
+ return m_pDialog;
+ }
+
+ AddressSettings& AddressBookSourcePage::getSettings()
+ {
+ return m_pDialog->getSettings();
+ }
+
+ const AddressSettings& AddressBookSourcePage::getSettings() const
+ {
+ return m_pDialog->getSettings();
+ }
+
+ const Reference< XComponentContext > & AddressBookSourcePage::getORB() const
+ {
+ return m_pDialog->getORB();
+ }
+
+} // namespace abp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/abspage.hxx b/extensions/source/abpilot/abspage.hxx
new file mode 100644
index 000000000..4d78533ad
--- /dev/null
+++ b/extensions/source/abpilot/abspage.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/wizardmachine.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <strings.hrc>
+#include <componentmodule.hxx>
+
+namespace abp
+{
+ class OAddressBookSourcePilot;
+ struct AddressSettings;
+
+ typedef ::vcl::OWizardPage AddressBookSourcePage_Base;
+ /// the base class for all tab pages in the address book source wizard
+ class AddressBookSourcePage : public AddressBookSourcePage_Base
+ {
+ OAddressBookSourcePilot* m_pDialog;
+
+ protected:
+ AddressBookSourcePage(weld::Container* pPage, OAddressBookSourcePilot* pController, const OUString& rUIXMLDescription, const OString& rID);
+
+ protected:
+ // helper
+ OAddressBookSourcePilot* getDialog();
+ const OAddressBookSourcePilot* getDialog() const;
+ const css::uno::Reference< css::uno::XComponentContext > &
+ getORB() const;
+ AddressSettings& getSettings();
+ const AddressSettings& getSettings() const;
+
+ // BuilderPage overridables
+ virtual void Activate() override;
+ virtual void Deactivate() override;
+ };
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/abspilot.cxx b/extensions/source/abpilot/abspilot.cxx
new file mode 100644
index 000000000..0a5cba073
--- /dev/null
+++ b/extensions/source/abpilot/abspilot.cxx
@@ -0,0 +1,449 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "abspilot.hxx"
+#include <helpids.h>
+#include <strings.hrc>
+#include <componentmodule.hxx>
+#include <tools/debug.hxx>
+#include "typeselectionpage.hxx"
+#include "admininvokationpage.hxx"
+#include "tableselectionpage.hxx"
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <osl/diagnose.h>
+#include "abpfinalpage.hxx"
+#include "fieldmappingpage.hxx"
+#include "fieldmappingimpl.hxx"
+
+using vcl::RoadmapWizardTypes::PathId;
+
+namespace abp
+{
+
+
+#define STATE_SELECT_ABTYPE 0
+#define STATE_INVOKE_ADMIN_DIALOG 1
+#define STATE_TABLE_SELECTION 2
+#define STATE_MANUAL_FIELD_MAPPING 3
+#define STATE_FINAL_CONFIRM 4
+
+#define PATH_COMPLETE 1
+#define PATH_NO_SETTINGS 2
+#define PATH_NO_FIELDS 3
+#define PATH_NO_SETTINGS_NO_FIELDS 4
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+
+ OAddressBookSourcePilot::OAddressBookSourcePilot(weld::Window* _pParent, const Reference< XComponentContext >& _rxORB)
+ :OAddressBookSourcePilot_Base( _pParent )
+ ,m_xORB(_rxORB)
+ ,m_aNewDataSource(_rxORB)
+ ,m_eNewDataSourceType( AST_INVALID )
+ {
+ declarePath( PATH_COMPLETE,
+ {STATE_SELECT_ABTYPE,
+ STATE_INVOKE_ADMIN_DIALOG,
+ STATE_TABLE_SELECTION,
+ STATE_MANUAL_FIELD_MAPPING,
+ STATE_FINAL_CONFIRM}
+ );
+ declarePath( PATH_NO_SETTINGS,
+ {STATE_SELECT_ABTYPE,
+ STATE_TABLE_SELECTION,
+ STATE_MANUAL_FIELD_MAPPING,
+ STATE_FINAL_CONFIRM}
+ );
+ declarePath( PATH_NO_FIELDS,
+ {STATE_SELECT_ABTYPE,
+ STATE_INVOKE_ADMIN_DIALOG,
+ STATE_TABLE_SELECTION,
+ STATE_FINAL_CONFIRM}
+ );
+ declarePath( PATH_NO_SETTINGS_NO_FIELDS,
+ {STATE_SELECT_ABTYPE,
+ STATE_TABLE_SELECTION,
+ STATE_FINAL_CONFIRM}
+ );
+
+ m_xPrevPage->set_help_id(HID_ABSPILOT_PREVIOUS);
+ m_xNextPage->set_help_id(HID_ABSPILOT_NEXT);
+ m_xCancel->set_help_id(HID_ABSPILOT_CANCEL);
+ m_xFinish->set_help_id(HID_ABSPILOT_FINISH);
+ m_xHelp->set_help_id(UID_ABSPILOT_HELP);
+
+ // some initial settings
+#ifdef UNX
+#ifdef MACOSX
+ m_aSettings.eType = AST_MACAB;
+#else
+// FIXME: if KDE use KAB instead
+ m_aSettings.eType = AST_EVOLUTION;
+#endif
+#else
+ m_aSettings.eType = AST_OTHER;
+#endif
+ m_aSettings.sDataSourceName = compmodule::ModuleRes(RID_STR_DEFAULT_NAME);
+ m_aSettings.bRegisterDataSource = false;
+ m_aSettings.bEmbedDataSource = false;
+ m_aSettings.bIgnoreNoTable = false;
+
+ defaultButton(WizardButtonFlags::NEXT);
+ enableButtons(WizardButtonFlags::FINISH, false);
+ ActivatePage();
+ m_xAssistant->set_current_page(0);
+
+ typeSelectionChanged( m_aSettings.eType );
+
+ OUString sDialogTitle = compmodule::ModuleRes(RID_STR_ABSOURCEDIALOGTITLE);
+ setTitleBase(sDialogTitle);
+ m_xAssistant->set_help_id(HID_ABSPILOT);
+ }
+
+ OUString OAddressBookSourcePilot::getStateDisplayName( WizardState _nState ) const
+ {
+ TranslateId pResId;
+ switch ( _nState )
+ {
+ case STATE_SELECT_ABTYPE: pResId = RID_STR_SELECT_ABTYPE; break;
+ case STATE_INVOKE_ADMIN_DIALOG: pResId = RID_STR_INVOKE_ADMIN_DIALOG; break;
+ case STATE_TABLE_SELECTION: pResId = RID_STR_TABLE_SELECTION; break;
+ case STATE_MANUAL_FIELD_MAPPING: pResId = RID_STR_MANUAL_FIELD_MAPPING; break;
+ case STATE_FINAL_CONFIRM: pResId = RID_STR_FINAL_CONFIRM; break;
+ }
+ DBG_ASSERT( pResId, "OAddressBookSourcePilot::getStateDisplayName: don't know this state!" );
+
+ OUString sDisplayName;
+ if (pResId)
+ {
+ sDisplayName = compmodule::ModuleRes(pResId);
+ }
+
+ return sDisplayName;
+ }
+
+ void OAddressBookSourcePilot::implCommitAll()
+ {
+ // in real, the data source already exists in the data source context
+ // Thus, if the user changed the name, we have to rename the data source
+ if ( m_aSettings.sDataSourceName != m_aNewDataSource.getName() )
+ m_aNewDataSource.rename( m_aSettings.sDataSourceName );
+
+ // 1. the data source
+ m_aNewDataSource.store(m_aSettings);
+
+ // 2. check if we need to register the data source
+ if ( m_aSettings.bRegisterDataSource )
+ m_aNewDataSource.registerDataSource(m_aSettings.sRegisteredDataSourceName);
+
+ // 3. write the data source / table names into the configuration
+ addressconfig::writeTemplateAddressSource( getORB(), m_aSettings.bRegisterDataSource ? m_aSettings.sRegisteredDataSourceName : m_aSettings.sDataSourceName, m_aSettings.sSelectedTable );
+
+ // 4. write the field mapping
+ fieldmapping::writeTemplateAddressFieldMapping( getORB(), std::map(m_aSettings.aFieldMapping) );
+ }
+
+ void OAddressBookSourcePilot::implCleanup()
+ {
+ if ( m_aNewDataSource.isValid() )
+ m_aNewDataSource.remove();
+ }
+
+ short OAddressBookSourcePilot::run()
+ {
+ short nRet = OAddressBookSourcePilot_Base::run();
+
+ implCleanup();
+
+ return nRet;
+ }
+
+ bool OAddressBookSourcePilot::onFinish()
+ {
+ if ( !OAddressBookSourcePilot_Base::onFinish() )
+ return false;
+
+ implCommitAll();
+
+ addressconfig::markPilotSuccess( getORB() );
+
+ return true;
+ }
+
+ void OAddressBookSourcePilot::enterState( WizardState _nState )
+ {
+ switch ( _nState )
+ {
+ case STATE_SELECT_ABTYPE:
+ impl_updateRoadmap( static_cast< TypeSelectionPage* >( GetPage( STATE_SELECT_ABTYPE ) )->getSelectedType() );
+ break;
+
+ case STATE_FINAL_CONFIRM:
+ if ( !needManualFieldMapping( ) )
+ implDoAutoFieldMapping();
+ break;
+
+ case STATE_TABLE_SELECTION:
+ implDefaultTableName();
+ break;
+ }
+
+ OAddressBookSourcePilot_Base::enterState(_nState);
+ }
+
+
+ bool OAddressBookSourcePilot::prepareLeaveCurrentState( CommitPageReason _eReason )
+ {
+ if ( !OAddressBookSourcePilot_Base::prepareLeaveCurrentState( _eReason ) )
+ return false;
+
+ if ( _eReason == vcl::WizardTypes::eTravelBackward )
+ return true;
+
+ bool bAllow = true;
+
+ switch ( getCurrentState() )
+ {
+ case STATE_SELECT_ABTYPE:
+ implCreateDataSource();
+ if ( needAdminInvokationPage() )
+ break;
+ [[fallthrough]];
+
+ case STATE_INVOKE_ADMIN_DIALOG:
+ if ( !connectToDataSource( false ) )
+ {
+ // connecting did not succeed -> do not allow proceeding
+ bAllow = false;
+ break;
+ }
+
+
+ // now that we connected to the data source, check whether we need the "table selection" page
+ const StringBag& aTables = m_aNewDataSource.getTableNames();
+
+ if ( aTables.empty() )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xAssistant.get(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ compmodule::ModuleRes(getSettings().eType == AST_EVOLUTION_GROUPWISE ? RID_STR_QRY_NO_EVO_GW : RID_STR_QRY_NOTABLES)));
+
+ if (RET_YES != xBox->run())
+ {
+ // cannot ask the user, or the user chose to use this data source, though there are no tables
+ bAllow = false;
+ break;
+ }
+
+ m_aSettings.bIgnoreNoTable = true;
+ }
+
+ if ( aTables.size() == 1 )
+ // remember the one and only table we have
+ m_aSettings.sSelectedTable = *aTables.begin();
+
+ break;
+ }
+
+ impl_updateRoadmap( m_aSettings.eType );
+ return bAllow;
+ }
+
+ void OAddressBookSourcePilot::implDefaultTableName()
+ {
+ const StringBag& rTableNames = getDataSource().getTableNames();
+ if ( rTableNames.end() != rTableNames.find( getSettings().sSelectedTable ) )
+ // already a valid table selected
+ return;
+
+ const char* pGuess = nullptr;
+ switch ( getSettings().eType )
+ {
+ case AST_THUNDERBIRD : pGuess = "Personal Address book"; break;
+ case AST_EVOLUTION :
+ case AST_EVOLUTION_GROUPWISE:
+ case AST_EVOLUTION_LDAP : pGuess = "Personal"; break;
+ default:
+ OSL_FAIL( "OAddressBookSourcePilot::implDefaultTableName: unhandled case!" );
+ return;
+ }
+ const OUString sGuess = OUString::createFromAscii( pGuess );
+ if ( rTableNames.end() != rTableNames.find( sGuess ) )
+ getSettings().sSelectedTable = sGuess;
+ }
+
+ void OAddressBookSourcePilot::implDoAutoFieldMapping()
+ {
+ DBG_ASSERT( !needManualFieldMapping( ), "OAddressBookSourcePilot::implDoAutoFieldMapping: invalid call!" );
+
+ fieldmapping::defaultMapping( getORB(), m_aSettings.aFieldMapping );
+ }
+
+ void OAddressBookSourcePilot::implCreateDataSource()
+ {
+ if (m_aNewDataSource.isValid())
+ { // we already have a data source object
+ if ( m_aSettings.eType == m_eNewDataSourceType )
+ // and it already has the correct type
+ return;
+
+ // it has a wrong type -> remove it
+ m_aNewDataSource.remove();
+ }
+
+ ODataSourceContext aContext( getORB() );
+ aContext.disambiguate( m_aSettings.sDataSourceName );
+
+ switch (m_aSettings.eType)
+ {
+ case AST_THUNDERBIRD:
+ m_aNewDataSource = aContext.createNewThunderbird( m_aSettings.sDataSourceName );
+ break;
+
+ case AST_EVOLUTION:
+ m_aNewDataSource = aContext.createNewEvolution( m_aSettings.sDataSourceName );
+ break;
+
+ case AST_EVOLUTION_GROUPWISE:
+ m_aNewDataSource = aContext.createNewEvolutionGroupwise( m_aSettings.sDataSourceName );
+ break;
+
+ case AST_EVOLUTION_LDAP:
+ m_aNewDataSource = aContext.createNewEvolutionLdap( m_aSettings.sDataSourceName );
+ break;
+
+ case AST_KAB:
+ m_aNewDataSource = aContext.createNewKab( m_aSettings.sDataSourceName );
+ break;
+
+ case AST_MACAB:
+ m_aNewDataSource = aContext.createNewMacab( m_aSettings.sDataSourceName );
+ break;
+
+ case AST_OTHER:
+ m_aNewDataSource = aContext.createNewOther( m_aSettings.sDataSourceName );
+ break;
+
+ case AST_INVALID:
+ OSL_FAIL( "OAddressBookSourcePilot::implCreateDataSource: illegal data source type!" );
+ break;
+ }
+ m_eNewDataSourceType = m_aSettings.eType;
+ }
+
+ bool OAddressBookSourcePilot::connectToDataSource( bool _bForceReConnect )
+ {
+ DBG_ASSERT( m_aNewDataSource.isValid(), "OAddressBookSourcePilot::implConnect: invalid current data source!" );
+
+ weld::WaitObject aWaitCursor(m_xAssistant.get());
+ if ( _bForceReConnect && m_aNewDataSource.isConnected( ) )
+ m_aNewDataSource.disconnect( );
+
+ return m_aNewDataSource.connect(m_xAssistant.get());
+ }
+
+ std::unique_ptr<BuilderPage> OAddressBookSourcePilot::createPage(WizardState _nState)
+ {
+ OString sIdent(OString::number(_nState));
+ weld::Container* pPageContainer = m_xAssistant->append_page(sIdent);
+
+ std::unique_ptr<vcl::OWizardPage> xRet;
+
+ switch (_nState)
+ {
+ case STATE_SELECT_ABTYPE:
+ xRet = std::make_unique<TypeSelectionPage>(pPageContainer, this);
+ break;
+ case STATE_INVOKE_ADMIN_DIALOG:
+ xRet = std::make_unique<AdminDialogInvokationPage>(pPageContainer, this);
+ break;
+ case STATE_TABLE_SELECTION:
+ xRet = std::make_unique<TableSelectionPage>(pPageContainer, this);
+ break;
+ case STATE_MANUAL_FIELD_MAPPING:
+ xRet = std::make_unique<FieldMappingPage>(pPageContainer, this);
+ break;
+ case STATE_FINAL_CONFIRM:
+ xRet = std::make_unique<FinalPage>(pPageContainer, this);
+ break;
+ default:
+ assert(false && "OAddressBookSourcePilot::createPage: invalid state!");
+ break;
+ }
+
+ m_xAssistant->set_page_title(sIdent, getStateDisplayName(_nState));
+
+ return xRet;
+ }
+
+ void OAddressBookSourcePilot::impl_updateRoadmap( AddressSourceType _eType )
+ {
+ bool bSettingsPage = needAdminInvokationPage( _eType );
+ bool bTablesPage = needTableSelection( _eType );
+ bool bFieldsPage = needManualFieldMapping( _eType );
+
+ bool bConnected = m_aNewDataSource.isConnected();
+ bool bCanSkipTables =
+ ( m_aNewDataSource.hasTable( m_aSettings.sSelectedTable )
+ || m_aSettings.bIgnoreNoTable
+ );
+
+ enableState( STATE_INVOKE_ADMIN_DIALOG, bSettingsPage );
+
+ enableState( STATE_TABLE_SELECTION,
+ bTablesPage && ( bConnected ? !bCanSkipTables : !bSettingsPage )
+ // if we do not need a settings page, we connect upon "Next" on the first page
+ );
+
+ enableState( STATE_MANUAL_FIELD_MAPPING,
+ bFieldsPage && bConnected && m_aNewDataSource.hasTable( m_aSettings.sSelectedTable )
+ );
+
+ enableState( STATE_FINAL_CONFIRM,
+ bConnected && bCanSkipTables
+ );
+ }
+
+ void OAddressBookSourcePilot::typeSelectionChanged( AddressSourceType _eType )
+ {
+ PathId nCurrentPathID( PATH_COMPLETE );
+ bool bSettingsPage = needAdminInvokationPage( _eType );
+ bool bFieldsPage = needManualFieldMapping( _eType );
+ if ( !bSettingsPage )
+ if ( !bFieldsPage )
+ nCurrentPathID = PATH_NO_SETTINGS_NO_FIELDS;
+ else
+ nCurrentPathID = PATH_NO_SETTINGS;
+ else
+ if ( !bFieldsPage )
+ nCurrentPathID = PATH_NO_FIELDS;
+ else
+ nCurrentPathID = PATH_COMPLETE;
+ activatePath( nCurrentPathID, true );
+
+ m_aNewDataSource.disconnect();
+ m_aSettings.bIgnoreNoTable = false;
+ impl_updateRoadmap( _eType );
+ }
+
+} // namespace abp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/abspilot.hxx b/extensions/source/abpilot/abspilot.hxx
new file mode 100644
index 000000000..45c965d2b
--- /dev/null
+++ b/extensions/source/abpilot/abspilot.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 <vcl/roadmapwizard.hxx>
+#include "addresssettings.hxx"
+#include "datasourcehandling.hxx"
+
+using vcl::WizardTypes::WizardState;
+using vcl::WizardTypes::CommitPageReason;
+
+namespace abp
+{
+ typedef ::vcl::RoadmapWizardMachine OAddressBookSourcePilot_Base;
+ class OAddressBookSourcePilot final : public OAddressBookSourcePilot_Base
+ {
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xORB;
+ AddressSettings m_aSettings;
+
+ ODataSource m_aNewDataSource;
+ AddressSourceType m_eNewDataSourceType;
+
+ public:
+ /// ctor
+ OAddressBookSourcePilot(
+ weld::Window* _pParent,
+ const css::uno::Reference< css::uno::XComponentContext >& _rxORB);
+
+ virtual short run() override;
+
+ /// get the service factory which was used to create the dialog
+ const css::uno::Reference< css::uno::XComponentContext >&
+ getORB() const { return m_xORB; }
+ AddressSettings& getSettings() { return m_aSettings; }
+ const AddressSettings& getSettings() const { return m_aSettings; }
+
+ const ODataSource& getDataSource() const { return m_aNewDataSource; }
+
+ bool connectToDataSource( bool _bForceReConnect );
+
+ void travelNext( ) { OAddressBookSourcePilot_Base::travelNext(); }
+
+ /// to be called when the selected type changed
+ void typeSelectionChanged( AddressSourceType _eType );
+
+ private:
+ // OWizardMachine overridables
+ virtual std::unique_ptr<BuilderPage> createPage( WizardState _nState ) override;
+ virtual void enterState( WizardState _nState ) override;
+ virtual bool prepareLeaveCurrentState( CommitPageReason _eReason ) override;
+ virtual bool onFinish() override;
+
+ // RoadmapWizard
+ virtual OUString getStateDisplayName( WizardState _nState ) const override;
+
+ /** creates a new data source of the type indicated by m_aSettings
+ <p>If another data source has been created before, this one is deleted.</p>
+ */
+ void implCreateDataSource();
+
+ /// does an automatic field mapping (possible for all types except AST_OTHER)
+ void implDoAutoFieldMapping();
+
+ /// guesses a default for the table name, if no valid table is selected
+ void implDefaultTableName();
+
+ static bool needAdminInvokationPage( AddressSourceType _eType )
+ {
+ return ( AST_OTHER == _eType );
+ }
+ /// check if with the current settings, we would need to invoke he administration dialog for more details about the data source
+ bool needAdminInvokationPage() const
+ {
+ return needAdminInvokationPage( m_aSettings.eType );
+ }
+
+ static bool needManualFieldMapping( AddressSourceType _eType )
+ {
+ return ( AST_OTHER == _eType ) || ( AST_KAB == _eType ) ||
+ ( AST_EVOLUTION == _eType ) || ( AST_EVOLUTION_GROUPWISE == _eType ) ||
+ ( AST_EVOLUTION_LDAP == _eType );
+ }
+ /// checks if we need a manual (user-guided) field mapping
+ bool needManualFieldMapping() const
+ {
+ return needManualFieldMapping( m_aSettings.eType );
+ }
+
+ /// determines whether the given address book type does provide one table only
+ static bool needTableSelection( AddressSourceType _eType )
+ {
+ return ( AST_KAB != _eType );
+ }
+
+ void implCleanup();
+ void implCommitAll();
+
+ void impl_updateRoadmap( AddressSourceType _eType );
+ };
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/addresssettings.hxx b/extensions/source/abpilot/addresssettings.hxx
new file mode 100644
index 000000000..542a2fe67
--- /dev/null
+++ b/extensions/source/abpilot/addresssettings.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include "abptypes.hxx"
+
+
+namespace abp
+{
+
+ enum AddressSourceType
+ {
+ AST_THUNDERBIRD,
+ AST_EVOLUTION,
+ AST_EVOLUTION_GROUPWISE,
+ AST_EVOLUTION_LDAP,
+ AST_KAB,
+ AST_MACAB,
+ AST_OTHER,
+
+ AST_INVALID
+ };
+
+ struct AddressSettings
+ {
+ AddressSourceType eType;
+ OUString sDataSourceName;
+ OUString sRegisteredDataSourceName;
+ OUString sSelectedTable;
+ bool bIgnoreNoTable;
+ MapString2String aFieldMapping;
+ bool bRegisterDataSource;
+ bool bEmbedDataSource;
+ };
+
+
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/admininvokationimpl.cxx b/extensions/source/abpilot/admininvokationimpl.cxx
new file mode 100644
index 000000000..c8ec00b68
--- /dev/null
+++ b/extensions/source/abpilot/admininvokationimpl.cxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "admininvokationimpl.hxx"
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/sdbc/DriverManager.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <strings.hrc>
+#include <componentmodule.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/weld.hxx>
+
+namespace abp
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::ui::dialogs;
+ using namespace ::com::sun::star::sdbc;
+
+ OAdminDialogInvokation::OAdminDialogInvokation(const Reference< XComponentContext >& _rxContext,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxDataSource,
+ weld::Window* _pMessageParent)
+ :m_xContext(_rxContext)
+ ,m_xDataSource(_rxDataSource)
+ ,m_pMessageParent(_pMessageParent)
+ {
+ DBG_ASSERT(m_xContext.is(), "OAdminDialogInvokation::OAdminDialogInvokation: invalid service factory!");
+ DBG_ASSERT(m_xDataSource.is(), "OAdminDialogInvokation::OAdminDialogInvokation: invalid preferred name!");
+ assert(m_pMessageParent && "OAdminDialogInvokation::OAdminDialogInvokation: invalid message parent!");
+ }
+
+
+ bool OAdminDialogInvokation::invokeAdministration()
+ {
+ if (!m_xContext.is())
+ return false;
+
+ try
+ {
+ // the service name of the administration dialog
+ static const char16_t s_sAdministrationServiceName[] = u"com.sun.star.sdb.DatasourceAdministrationDialog";
+ static constexpr OUStringLiteral s_sDataSourceTypeChangeDialog = u"com.sun.star.sdb.DataSourceTypeChangeDialog";
+
+ // the parameters for the call
+ Sequence<Any> aArguments(comphelper::InitAnyPropertySequence(
+ {
+ {"ParentWindow", Any(m_pMessageParent->GetXWindow())},
+ {"Title", Any(compmodule::ModuleRes(RID_STR_ADMINDIALOGTITLE))},
+ {"InitialSelection", Any(m_xDataSource)}, // the name of the new data source
+ }));
+
+
+ // create the dialog
+ Reference< XExecutableDialog > xDialog;
+ {
+ // creating the dialog service is potentially expensive (if all the libraries invoked need to be loaded)
+ // so we display a wait cursor
+ weld::WaitObject aWaitCursor(m_pMessageParent);
+ Reference<XInterface> x = m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(s_sDataSourceTypeChangeDialog, aArguments, m_xContext);
+ xDialog.set( x, UNO_QUERY );
+
+ // just for a smoother UI: What the dialog does upon execution, is (amongst other things) creating
+ // the DriverManager service
+ // If this context has never been accessed before, this may be expensive (it includes loading of
+ // at least one library).
+ // As this wizard is intended to run on the first office start, it is very likely that the
+ // context needs to be freshly created
+ // Thus, we access the context here (within the WaitCursor), which means the user sees a waitcursor
+ // while his/her office blocks a few seconds...
+ DriverManager::create( m_xContext );
+ }
+
+ if (xDialog.is())
+ { // execute it
+ if (xDialog->execute())
+ return true;
+ }
+ else
+ ShowServiceNotAvailableError(m_pMessageParent, s_sAdministrationServiceName, true);
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot",
+ "caught an exception while executing the dialog!");
+ }
+ return false;
+ }
+
+
+} // namespace abp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/admininvokationimpl.hxx b/extensions/source/abpilot/admininvokationimpl.hxx
new file mode 100644
index 000000000..2003809c8
--- /dev/null
+++ b/extensions/source/abpilot/admininvokationimpl.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+namespace weld { class Window; }
+
+namespace abp
+{
+ /** outsourced from AdminDialogInvokationPage, 'cause this class here, in opposite to
+ the page, needs exception handling to be enabled.
+ */
+ class OAdminDialogInvokation
+ {
+ private:
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xContext;
+ css::uno::Reference< css::beans::XPropertySet > m_xDataSource;
+ weld::Window* m_pMessageParent;
+
+ public:
+ OAdminDialogInvokation(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ const css::uno::Reference< css::beans::XPropertySet >& _rDataSource,
+ weld::Window* _pMessageParent
+ );
+
+ bool invokeAdministration();
+ };
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/admininvokationpage.cxx b/extensions/source/abpilot/admininvokationpage.cxx
new file mode 100644
index 000000000..2f51d8639
--- /dev/null
+++ b/extensions/source/abpilot/admininvokationpage.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "admininvokationpage.hxx"
+#include "abspilot.hxx"
+#include "admininvokationimpl.hxx"
+
+namespace abp
+{
+ AdminDialogInvokationPage::AdminDialogInvokationPage(weld::Container* pPage, OAddressBookSourcePilot* pController)
+ : AddressBookSourcePage(pPage, pController, "modules/sabpilot/ui/invokeadminpage.ui", "InvokeAdminPage")
+ , m_xInvokeAdminDialog(m_xBuilder->weld_button("settings"))
+ , m_xErrorMessage(m_xBuilder->weld_label("warning"))
+ {
+ m_xInvokeAdminDialog->connect_clicked(LINK(this, AdminDialogInvokationPage, OnInvokeAdminDialog));
+ }
+
+ AdminDialogInvokationPage::~AdminDialogInvokationPage()
+ {
+ }
+
+ void AdminDialogInvokationPage::Activate()
+ {
+ AddressBookSourcePage::Activate();
+ m_xInvokeAdminDialog->grab_focus();
+ }
+
+ void AdminDialogInvokationPage::implUpdateErrorMessage()
+ {
+ const bool bIsConnected = getDialog()->getDataSource().isConnected();
+ m_xErrorMessage->set_visible( !bIsConnected );
+ }
+
+ void AdminDialogInvokationPage::initializePage()
+ {
+ AddressBookSourcePage::initializePage();
+ m_xErrorMessage->hide();
+ // if we're entering this page, we assume we had no connection trial with this data source
+ }
+
+ void AdminDialogInvokationPage::implTryConnect()
+ {
+ getDialog()->connectToDataSource( true );
+
+ // show our error message if and only if we could not connect
+ implUpdateErrorMessage();
+
+ // the status of the next button may have changed
+ updateDialogTravelUI();
+
+ // automatically go to the next page (if successfully connected)
+ if ( canAdvance() )
+ getDialog()->travelNext();
+ }
+
+ bool AdminDialogInvokationPage::canAdvance() const
+ {
+ return AddressBookSourcePage::canAdvance() && getDialog()->getDataSource().isConnected();
+ }
+
+ // davido: Do we need it?
+ IMPL_LINK_NOARG(AdminDialogInvokationPage, OnInvokeAdminDialog, weld::Button&, void)
+ {
+ OAdminDialogInvokation aInvokation(getORB(), getDialog()->getDataSource().getDataSource(), getDialog()->getDialog());
+ if ( aInvokation.invokeAdministration() )
+ {
+ // try to connect to this data source
+ implTryConnect();
+ }
+ }
+
+} // namespace abp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/admininvokationpage.hxx b/extensions/source/abpilot/admininvokationpage.hxx
new file mode 100644
index 000000000..3d4f676e2
--- /dev/null
+++ b/extensions/source/abpilot/admininvokationpage.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 "abspage.hxx"
+
+
+namespace abp
+{
+ class AdminDialogInvokationPage final : public AddressBookSourcePage
+ {
+ std::unique_ptr<weld::Button> m_xInvokeAdminDialog;
+ std::unique_ptr<weld::Label> m_xErrorMessage;
+
+ public:
+ explicit AdminDialogInvokationPage(weld::Container* pPage, OAddressBookSourcePilot* pDialog);
+ virtual ~AdminDialogInvokationPage() override;
+ private:
+ // BuilderPage overridables
+ virtual void Activate() override;
+
+ // OWizard overridables
+ virtual void initializePage() override;
+
+ // OImportPage overridables
+ virtual bool canAdvance() const override;
+
+ DECL_LINK( OnInvokeAdminDialog, weld::Button&, void );
+
+ void implTryConnect();
+ void implUpdateErrorMessage();
+ };
+
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/datasourcehandling.cxx b/extensions/source/abpilot/datasourcehandling.cxx
new file mode 100644
index 000000000..a411c2536
--- /dev/null
+++ b/extensions/source/abpilot/datasourcehandling.cxx
@@ -0,0 +1,620 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include "abptypes.hxx"
+#include <componentmodule.hxx>
+#include "datasourcehandling.hxx"
+#include "addresssettings.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/sdb/XDatabaseRegistrations.hpp>
+#include <com/sun/star/sdb/XDocumentDataSource.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
+
+#include <comphelper/interaction.hxx>
+#include <comphelper/processfactory.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/sharedunocomponent.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/propertysequence.hxx>
+
+namespace
+{
+
+/// Returns the URL of this object shell.
+OUString lcl_getOwnURL(SfxObjectShell const * pObjectShell)
+{
+ OUString aRet;
+
+ if (!pObjectShell)
+ return aRet;
+
+ const INetURLObject& rURLObject = pObjectShell->GetMedium()->GetURLObject();
+ aRet = rURLObject.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ return aRet;
+}
+
+}
+
+namespace abp
+{
+
+
+ using namespace ::utl;
+ using namespace ::comphelper;
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::task;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::frame;
+
+ static Reference< XDatabaseContext > lcl_getDataSourceContext( const Reference< XComponentContext >& _rxContext )
+ {
+ Reference<XDatabaseContext> xContext = DatabaseContext::create(_rxContext);
+ return xContext;
+ }
+
+
+ /// creates a new data source and inserts it into the context
+ static void lcl_implCreateAndInsert(
+ const Reference< XComponentContext >& _rxContext, const OUString& _rName,
+ Reference< XPropertySet >& /* [out] */ _rxNewDataSource )
+ {
+
+ // get the data source context
+ Reference< XDatabaseContext > xContext = lcl_getDataSourceContext( _rxContext );
+
+ DBG_ASSERT( !xContext->hasByName( _rName ), "lcl_implCreateAndInsert: name already used!" );
+
+
+ // create a new data source
+ Reference< XPropertySet > xNewDataSource;
+ if (xContext.is())
+ xNewDataSource.set( xContext->createInstance(), UNO_QUERY );
+ DBG_ASSERT( xNewDataSource.is(), "lcl_implCreateAndInsert: could not create a new data source!" );
+
+
+ // insert the data source into the context
+ DBG_ASSERT( xContext.is(), "lcl_implCreateAndInsert: missing an interface on the context (XNamingService)!" );
+ if (xContext.is())
+ {
+ // xDynamicContext->registerObject( _rName, xNewDataSource );
+ _rxNewDataSource = xNewDataSource;
+ }
+ }
+
+
+ /// creates and inserts a data source, and sets its URL property to the string given
+ static ODataSource lcl_implCreateAndSetURL(
+ const Reference< XComponentContext >& _rxORB, const OUString& _rName,
+ const char* _pInitialAsciiURL )
+ {
+ ODataSource aReturn( _rxORB );
+ try
+ {
+ // create the new data source
+ Reference< XPropertySet > xNewDataSource;
+ lcl_implCreateAndInsert( _rxORB, _rName, xNewDataSource );
+
+
+ // set the URL property
+ if (xNewDataSource.is())
+ {
+ xNewDataSource->setPropertyValue(
+ "URL",
+ Any( OUString::createFromAscii( _pInitialAsciiURL ) )
+ );
+ }
+
+ aReturn.setDataSource( xNewDataSource, _rName );
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot",
+ "caught an exception while creating the data source!");
+ }
+
+ return aReturn;
+ }
+
+ static void lcl_registerDataSource(
+ const Reference< XComponentContext >& _rxORB, const OUString& _sName,
+ const OUString& _sURL )
+ {
+ OSL_ENSURE( !_sName.isEmpty(), "lcl_registerDataSource: invalid name!" );
+ OSL_ENSURE( !_sURL.isEmpty(), "lcl_registerDataSource: invalid URL!" );
+ try
+ {
+ Reference< XDatabaseContext > xRegistrations( DatabaseContext::create(_rxORB) );
+ if ( xRegistrations->hasRegisteredDatabase( _sName ) )
+ xRegistrations->changeDatabaseLocation( _sName, _sURL );
+ else
+ xRegistrations->registerDatabaseLocation( _sName, _sURL );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.abpilot");
+ }
+ }
+
+ struct ODataSourceContextImpl
+ {
+ Reference< XComponentContext > xORB;
+ Reference< XNameAccess > xContext; /// the UNO data source context
+ StringBag aDataSourceNames; /// for quicker name checks (without the UNO overhead)
+
+ explicit ODataSourceContextImpl(const Reference< XComponentContext >& _rxORB)
+ : xORB(_rxORB)
+ {
+ }
+ ODataSourceContextImpl(const ODataSourceContextImpl&) = delete;
+ ODataSourceContextImpl& operator=(const ODataSourceContextImpl&) = delete;
+ };
+
+ ODataSourceContext::ODataSourceContext(const Reference< XComponentContext >& _rxORB)
+ :m_pImpl( new ODataSourceContextImpl( _rxORB ) )
+ {
+ try
+ {
+ // create the UNO context
+ m_pImpl->xContext.set( lcl_getDataSourceContext( _rxORB ), UNO_QUERY_THROW );
+
+ // collect the data source names
+ Sequence< OUString > aDSNames = m_pImpl->xContext->getElementNames();
+ const OUString* pDSNames = aDSNames.getConstArray();
+ const OUString* pDSNamesEnd = pDSNames + aDSNames.getLength();
+
+ for ( ;pDSNames != pDSNamesEnd; ++pDSNames )
+ m_pImpl->aDataSourceNames.insert( *pDSNames );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.abpilot", "ODataSourceContext::ODataSourceContext" );
+ }
+ }
+ ODataSourceContext::~ODataSourceContext()
+ {
+ }
+
+
+ void ODataSourceContext::disambiguate(OUString& _rDataSourceName)
+ {
+ OUString sCheck( _rDataSourceName );
+ StringBag::const_iterator aPos = m_pImpl->aDataSourceNames.find( sCheck );
+
+ sal_Int32 nPostfix = 1;
+ while ( ( m_pImpl->aDataSourceNames.end() != aPos ) && ( nPostfix < 65535 ) )
+ { // there already is a data source with this name
+ sCheck = _rDataSourceName + OUString::number( nPostfix++ );
+
+ aPos = m_pImpl->aDataSourceNames.find( sCheck );
+ }
+
+ _rDataSourceName = sCheck;
+ }
+
+
+ void ODataSourceContext::getDataSourceNames( StringBag& _rNames ) const
+ {
+ _rNames = m_pImpl->aDataSourceNames;
+ }
+
+ ODataSource ODataSourceContext::createNewThunderbird( const OUString& _rName )
+ {
+ return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:thunderbird" );
+ }
+
+
+ ODataSource ODataSourceContext::createNewEvolutionLdap( const OUString& _rName)
+ {
+ return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:ldap" );
+ }
+
+ ODataSource ODataSourceContext::createNewEvolutionGroupwise( const OUString& _rName)
+ {
+ return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:groupwise" );
+ }
+
+ ODataSource ODataSourceContext::createNewEvolution( const OUString& _rName)
+ {
+ return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:local" );
+ }
+
+
+ ODataSource ODataSourceContext::createNewKab( const OUString& _rName)
+ {
+ return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:kab" );
+ }
+
+
+ ODataSource ODataSourceContext::createNewMacab( const OUString& _rName)
+ {
+ return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:macab" );
+ }
+
+
+ // tdf117101: Spreadsheet by default
+ ODataSource ODataSourceContext::createNewOther( const OUString& _rName)
+ {
+ return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:calc:" );
+ }
+
+ struct ODataSourceImpl
+ {
+ public:
+ Reference< XComponentContext > xORB; /// the service factory
+ Reference< XPropertySet > xDataSource; /// the UNO data source
+ ::utl::SharedUNOComponent< XConnection >
+ xConnection;
+ StringBag aTables; // the cached table names
+ OUString sName;
+
+ explicit ODataSourceImpl(const Reference< XComponentContext >& _rxORB)
+ : xORB(_rxORB)
+ {
+ }
+ };
+
+
+ ODataSource::ODataSource( const ODataSource& _rSource )
+ {
+ *this = _rSource;
+ }
+
+ ODataSource& ODataSource::operator=( const ODataSource& _rSource )
+ {
+ if( this != &_rSource )
+ {
+ m_pImpl.reset( new ODataSourceImpl( *_rSource.m_pImpl ) );
+ }
+ return *this;
+ }
+
+ ODataSource& ODataSource::operator=(ODataSource&& _rSource) noexcept
+ {
+ m_pImpl = std::move(_rSource.m_pImpl);
+ return *this;
+ }
+
+ ODataSource::ODataSource( const Reference< XComponentContext >& _rxORB )
+ :m_pImpl(new ODataSourceImpl(_rxORB))
+ {
+ }
+
+ ODataSource::~ODataSource( )
+ {
+ }
+
+ void ODataSource::store(const AddressSettings& rSettings)
+ {
+ if (!isValid())
+ // nothing to do
+ return;
+ try
+ {
+ Reference< XDocumentDataSource > xDocAccess( m_pImpl->xDataSource, UNO_QUERY );
+ Reference< XStorable > xStorable;
+ if ( xDocAccess.is() )
+ xStorable.set(xDocAccess->getDatabaseDocument(), css::uno::UNO_QUERY);
+ OSL_ENSURE( xStorable.is(),"DataSource is no XStorable!" );
+ if ( xStorable.is() )
+ {
+ SfxViewFrame* pFrame = SfxViewFrame::Current();
+ SfxObjectShell* pObjectShell = pFrame ? pFrame->GetObjectShell() : nullptr;
+ OUString aOwnURL = lcl_getOwnURL(pObjectShell); // empty if pObjectShell is nullptr
+ if (aOwnURL.isEmpty() || !rSettings.bEmbedDataSource)
+ {
+ // Cannot or should not embed.
+ xStorable->storeAsURL(m_pImpl->sName,Sequence<PropertyValue>());
+ }
+ else
+ {
+ // Embed.
+ OUString aStreamRelPath = "EmbeddedDatabase";
+ auto xContext(comphelper::getProcessComponentContext());
+ auto xUri = css::uri::UriReferenceFactory::create(xContext)->parse(aOwnURL);
+ assert(xUri.is());
+ xUri = css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext)->createVndSunStarPkgUrlReference(xUri);
+ assert(xUri.is());
+ OUString const sTmpName = xUri->getUriReference() + "/" + aStreamRelPath;
+ assert(pObjectShell);
+ uno::Reference<embed::XStorage> xStorage = pObjectShell->GetStorage();
+ uno::Sequence<beans::PropertyValue> aSequence = comphelper::InitPropertySequence(
+ {
+ {"TargetStorage", uno::Any(xStorage)},
+ {"StreamRelPath", uno::Any(aStreamRelPath)},
+ {"BaseURI", uno::Any(aOwnURL)}
+ });
+ xStorable->storeAsURL(sTmpName, aSequence);
+ m_pImpl->sName = sTmpName;
+
+ // Refer to the sub-storage name in the document settings, so
+ // we can load it again next time the file is imported.
+ uno::Reference<lang::XMultiServiceFactory> xFactory(pObjectShell->GetModel(), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPropertySet(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
+ xPropertySet->setPropertyValue("EmbeddedDatabaseName", uno::Any(aStreamRelPath));
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot",
+ "caught an exception while creating the data source!");
+ }
+ }
+
+ void ODataSource::registerDataSource( const OUString& _sRegisteredDataSourceName)
+ {
+ if (!isValid())
+ // nothing to do
+ return;
+
+ try
+ {
+ // invalidate ourself
+ lcl_registerDataSource(m_pImpl->xORB,_sRegisteredDataSourceName,m_pImpl->sName);
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot",
+ "caught an exception while creating the data source!");
+ }
+ }
+
+
+ void ODataSource::setDataSource( const Reference< XPropertySet >& _rxDS,const OUString& _sName )
+ {
+ if (m_pImpl->xDataSource.get() == _rxDS.get())
+ // nothing to do
+ return;
+
+ if ( isConnected() )
+ disconnect();
+
+ m_pImpl->sName = _sName;
+ m_pImpl->xDataSource = _rxDS;
+ }
+
+
+ void ODataSource::remove()
+ {
+ if (!isValid())
+ // nothing to do
+ return;
+
+ try
+ {
+ // invalidate ourself
+ m_pImpl->xDataSource.clear();
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot",
+ "caught an exception while creating the data source!");
+ }
+ }
+
+
+ bool ODataSource::rename( const OUString& _rName )
+ {
+ if (!isValid())
+ // nothing to do
+ return false;
+
+ m_pImpl->sName = _rName;
+ return true;
+ }
+
+
+ OUString ODataSource::getName() const
+ {
+ if ( !isValid() )
+ return OUString();
+ return m_pImpl->sName;
+ }
+
+
+ bool ODataSource::hasTable( const OUString& _rTableName ) const
+ {
+ if ( !isConnected() )
+ return false;
+
+ const StringBag& aTables( getTableNames() );
+ return aTables.find( _rTableName ) != aTables.end();
+ }
+
+
+ const StringBag& ODataSource::getTableNames() const
+ {
+ m_pImpl->aTables.clear();
+ if ( !isConnected() )
+ {
+ OSL_FAIL( "ODataSource::getTableNames: not connected!" );
+ }
+ else
+ {
+ try
+ {
+ // get the tables container from the connection
+ Reference< XTablesSupplier > xSuppTables( m_pImpl->xConnection.getTyped(), UNO_QUERY );
+ Reference< XNameAccess > xTables;
+ if ( xSuppTables.is( ) )
+ xTables = xSuppTables->getTables();
+ DBG_ASSERT( xTables.is(), "ODataSource::getTableNames: could not retrieve the tables container!" );
+
+ // get the names
+ Sequence< OUString > aTableNames;
+ if ( xTables.is( ) )
+ aTableNames = xTables->getElementNames( );
+
+ // copy the names
+ const OUString* pTableNames = aTableNames.getConstArray();
+ const OUString* pTableNamesEnd = pTableNames + aTableNames.getLength();
+ for (;pTableNames < pTableNamesEnd; ++pTableNames)
+ m_pImpl->aTables.insert( *pTableNames );
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+
+ // now the table cache is up-to-date
+ return m_pImpl->aTables;
+ }
+
+
+ bool ODataSource::connect(weld::Window* _pMessageParent)
+ {
+ if ( isConnected( ) )
+ // nothing to do
+ return true;
+
+
+ // create the interaction handler (needed for authentication and error handling)
+ Reference< XInteractionHandler > xInteractions;
+ try
+ {
+ xInteractions = InteractionHandler::createWithParent(m_pImpl->xORB, nullptr);
+ }
+ catch(const Exception&)
+ {
+ }
+
+
+ // failure to create the interaction handler is a serious issue ...
+ if (!xInteractions.is())
+ {
+ if ( _pMessageParent )
+ ShowServiceNotAvailableError( _pMessageParent, u"com.sun.star.task.InteractionHandler", true );
+ return false;
+ }
+
+
+ // open the connection
+ Any aError;
+ Reference< XConnection > xConnection;
+ try
+ {
+ Reference< XCompletedConnection > xComplConn( m_pImpl->xDataSource, UNO_QUERY );
+ DBG_ASSERT( xComplConn.is(), "ODataSource::connect: missing the XCompletedConnection interface on the data source!" );
+ if ( xComplConn.is() )
+ xConnection = xComplConn->connectWithCompletion( xInteractions );
+ }
+ catch( const SQLContext& e ) { aError <<= e; }
+ catch( const SQLWarning& e ) { aError <<= e; }
+ catch( const SQLException& e ) { aError <<= e; }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot", "");
+ }
+
+
+ // handle errors
+ if ( aError.hasValue() && _pMessageParent )
+ {
+ try
+ {
+ SQLException aException;
+ aError >>= aException;
+ if ( aException.Message.isEmpty() )
+ {
+ // prepend some context info
+ SQLContext aDetailedError;
+ aDetailedError.Message = compmodule::ModuleRes(RID_STR_NOCONNECTION);
+ aDetailedError.Details = compmodule::ModuleRes(RID_STR_PLEASECHECKSETTINGS);
+ aDetailedError.NextException = aError;
+ // handle (aka display) the new context info
+ xInteractions->handle( new OInteractionRequest( Any( aDetailedError ) ) );
+ }
+ else
+ {
+ // handle (aka display) the original error
+ xInteractions->handle( new OInteractionRequest( Any( aException ) ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot",
+ "caught an exception while trying to display the error!");
+ }
+ }
+
+ if ( !xConnection.is() )
+ return false;
+
+
+ // success
+ m_pImpl->xConnection.reset( xConnection );
+ m_pImpl->aTables.clear();
+
+ return true;
+ }
+
+
+ void ODataSource::disconnect( )
+ {
+ m_pImpl->xConnection.clear();
+ m_pImpl->aTables.clear();
+ }
+
+
+ bool ODataSource::isConnected( ) const
+ {
+ return m_pImpl->xConnection.is();
+ }
+
+
+ bool ODataSource::isValid() const
+ {
+ return m_pImpl && m_pImpl->xDataSource.is();
+ }
+
+ Reference< XPropertySet > ODataSource::getDataSource() const
+ {
+ return m_pImpl ? m_pImpl->xDataSource : Reference< XPropertySet >();
+ }
+
+
+} // namespace abp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/datasourcehandling.hxx b/extensions/source/abpilot/datasourcehandling.hxx
new file mode 100644
index 000000000..485c3c15b
--- /dev/null
+++ b/extensions/source/abpilot/datasourcehandling.hxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <memory>
+
+#include "abptypes.hxx"
+
+
+namespace com::sun::star {
+ namespace beans {
+ class XPropertySet;
+ }
+}
+
+namespace weld { class Window; }
+
+
+namespace abp
+{
+
+ struct ODataSourceContextImpl;
+ class ODataSource;
+ /// a non-UNO wrapper for the data source context
+ class ODataSourceContext
+ {
+ private:
+ std::unique_ptr<ODataSourceContextImpl> m_pImpl;
+
+ public:
+ explicit ODataSourceContext(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxORB
+ );
+ ~ODataSourceContext();
+
+ /// retrieves the names of all data sources
+ void getDataSourceNames( StringBag& _rNames ) const;
+
+ /// disambiguates the given name by appending successive numbers
+ void disambiguate(OUString& _rDataSourceName);
+
+ /// creates a new Thunderbird data source
+ ODataSource createNewThunderbird( const OUString& _rName );
+
+ /// creates a new Evolution local data source
+ ODataSource createNewEvolution( const OUString& _rName );
+
+ /// creates a new Evolution LDAP data source
+ ODataSource createNewEvolutionLdap( const OUString& _rName );
+
+ /// creates a new Evolution GROUPWISE data source
+ ODataSource createNewEvolutionGroupwise( const OUString& _rName );
+
+ /// creates a new KDE address book data source
+ ODataSource createNewKab( const OUString& _rName );
+
+ /// creates a new macOS address book data source
+ ODataSource createNewMacab( const OUString& _rName );
+
+ /// creates a new Other data source; tdf117101: Spreadsheet by default
+ ODataSource createNewOther( const OUString& _rName );
+ };
+
+ struct ODataSourceImpl;
+ struct AddressSettings;
+ /** a non-UNO wrapper for a data source
+ <p>This class allows to access data sources without the need to compile the respective file with
+ exception handling enabled (hopefully :).</p>
+ <p>In addition to wrapping a UNO data source, an instance of this class can handle at most
+ one valid connection, as obtained from the data source.</p>
+ */
+ class ODataSource
+ {
+ private:
+ std::unique_ptr<ODataSourceImpl> m_pImpl;
+
+ public:
+
+ // - ctor/dtor/assignment
+
+ /// constructs an object which is initially invalid
+ explicit ODataSource(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxORB
+ );
+
+ /// copy ctor
+ ODataSource( const ODataSource& _rSource );
+
+ /// dtor
+ ~ODataSource( );
+
+ /// copy assignment
+ ODataSource& operator=( const ODataSource& _rSource );
+
+ /// move assignment
+ ODataSource& operator=(ODataSource&& _rSource) noexcept;
+
+ /// checks whether or not the object represents a valid data source
+ bool isValid() const;
+
+
+ /// removes the data source represented by the object from the data source context
+ void remove();
+ // TODO: put this into the context class
+
+ /// returns the name of the data source
+ OUString
+ getName() const;
+
+ /// renames the data source
+ bool rename( const OUString& _rName );
+ // TODO: put this into the context class
+
+
+ // - connection handling
+
+ /** connects to the data source represented by this object
+ @param _pMessageParent
+ the window to use as parent for any error messages. If this is <NULL/>, no messages are displayed
+ at all.
+ @see isConnected
+ */
+ bool connect(weld::Window* _pMessageParent);
+
+ /// returns <TRUE/> if the object has a valid connection, obtained from its data source
+ bool isConnected( ) const;
+
+ /// disconnects from the data source (i.e. disposes the UNO connection hold internally)
+ void disconnect( );
+
+ /// stores the database file
+ void store(const AddressSettings& rSettings);
+
+ /// register the data source under the given name in the configuration
+ void registerDataSource( const OUString& _sRegisteredDataSourceName );
+
+
+ /** retrieves the tables names from the connection
+ <p>to be called when <method>isConnected</method> returns <TRUE/> only</p>
+ */
+ const StringBag& getTableNames() const;
+
+ /** determines whether a given table exists
+ */
+ bool hasTable( const OUString& _rTableName ) const;
+
+ /// return the intern data source object
+ css::uno::Reference< css::beans::XPropertySet > getDataSource() const;
+
+
+ /** set a new data source.
+ <p>Available to selected clients only</p>
+ */
+ void setDataSource(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxDS
+ ,const OUString& _sName
+ );
+ };
+
+
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/fieldmappingimpl.cxx b/extensions/source/abpilot/fieldmappingimpl.cxx
new file mode 100644
index 000000000..145732bdf
--- /dev/null
+++ b/extensions/source/abpilot/fieldmappingimpl.cxx
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "fieldmappingimpl.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/ui/AddressBookSourceDialog.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/weld.hxx>
+#include <com/sun/star/util/AliasProgrammaticPair.hpp>
+#include <strings.hrc>
+#include <componentmodule.hxx>
+#include <unotools/confignode.hxx>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+
+
+namespace abp
+{
+
+
+ using namespace ::utl;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::ui;
+ using namespace ::com::sun::star::ui::dialogs;
+
+
+ const char16_t sDriverSettingsNodeName[] = u"/org.openoffice.Office.DataAccess/DriverSettings/com.sun.star.comp.sdbc.MozabDriver";
+ constexpr OUStringLiteral sAddressBookNodeName = u"/org.openoffice.Office.DataAccess/AddressBook";
+
+ namespace fieldmapping
+ {
+ bool invokeDialog( const Reference< XComponentContext >& _rxORB, class weld::Window* _pParent,
+ const Reference< XPropertySet >& _rxDataSource, AddressSettings& _rSettings )
+ {
+ _rSettings.aFieldMapping.clear();
+
+ DBG_ASSERT( _rxORB.is(), "fieldmapping::invokeDialog: invalid service factory!" );
+ DBG_ASSERT( _rxDataSource.is(), "fieldmapping::invokeDialog: invalid data source!" );
+ if ( !_rxORB.is() || !_rxDataSource.is() )
+ return false;
+
+ try
+ {
+
+ // create an instance of the dialog service
+ Reference< XWindow > xDialogParent = _pParent->GetXWindow();
+ OUString sTitle(compmodule::ModuleRes(RID_STR_FIELDDIALOGTITLE));
+ Reference< XExecutableDialog > xDialog = AddressBookSourceDialog::createWithDataSource(_rxORB,
+ // the parent window
+ xDialogParent,
+ _rxDataSource,
+ _rSettings.bRegisterDataSource ? _rSettings.sRegisteredDataSourceName : _rSettings.sDataSourceName,
+ // the table to use
+ _rSettings.sSelectedTable,
+ sTitle);
+
+ // execute the dialog
+ if ( xDialog->execute() )
+ {
+ // retrieve the field mapping as set by he user
+ Reference< XPropertySet > xDialogProps( xDialog, UNO_QUERY );
+
+ Sequence< AliasProgrammaticPair > aMapping;
+ bool bSuccess =
+ xDialogProps->getPropertyValue("FieldMapping") >>= aMapping;
+ DBG_ASSERT( bSuccess, "fieldmapping::invokeDialog: invalid property type for FieldMapping!" );
+
+ // and copy it into the map
+ const AliasProgrammaticPair* pMapping = aMapping.getConstArray();
+ const AliasProgrammaticPair* pMappingEnd = pMapping + aMapping.getLength();
+ for (;pMapping != pMappingEnd; ++pMapping)
+ _rSettings.aFieldMapping[ pMapping->ProgrammaticName ] = pMapping->Alias;
+
+ return true;
+ }
+
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot",
+ "caught an exception while executing the dialog!");
+ }
+ return false;
+ }
+
+
+ void defaultMapping( const Reference< XComponentContext >& _rxContext, MapString2String& _rFieldAssignment )
+ {
+ _rFieldAssignment.clear();
+
+ try
+ {
+ // what we have:
+ // a) For the address data source, we need a mapping from programmatic names (1) to real column names
+ // b) The SDBC driver has a fixed set of columns, which, when returned, are named according to
+ // some configuration entries. E.g., the driver displays the field which it knows contains
+ // the first name as "First Name" - the latter string is stored in the config.
+ // For this, the driver uses programmatic names, too, but they differ from the programmatic names the
+ // template documents have.
+ // So what we need first is a mapping from programmatic names (1) to programmatic names (2)
+ const char* pMappingProgrammatics[] =
+ {
+ "FirstName", "FirstName",
+ "LastName", "LastName",
+ "Street", "HomeAddress",
+ "Zip", "HomeZipCode",
+ "City", "HomeCity",
+ "State", "HomeState",
+ "Country", "HomeCountry",
+ "PhonePriv", "HomePhone",
+ "PhoneComp", "WorkPhone",
+ "PhoneCell", "CellularNumber",
+ "Pager", "PagerNumber",
+ "Fax", "FaxNumber",
+ "EMail", "PrimaryEmail",
+ "URL", "WebPage1",
+ "Note", "Notes",
+ "Altfield1", "Custom1",
+ "Altfield2", "Custom2",
+ "Altfield3", "Custom3",
+ "Altfield4", "Custom4",
+ "Title", "JobTitle",
+ "Company", "Company",
+ "Department", "Department"
+ };
+ // (this list is not complete: both lists of programmatic names are larger in real,
+ // but this list above is the intersection)
+
+
+ // access the configuration information which the driver uses for determining its column names
+ OUString sDriverAliasesNodeName(
+ OUString::Concat(sDriverSettingsNodeName)
+ + "/ColumnAliases");
+
+ // create a config node for this
+ OConfigurationTreeRoot aDriverFieldAliasing = OConfigurationTreeRoot::createWithComponentContext(
+ _rxContext, sDriverAliasesNodeName, -1, OConfigurationTreeRoot::CM_READONLY);
+
+ // loop through all programmatic pairs
+ DBG_ASSERT( 0 == SAL_N_ELEMENTS( pMappingProgrammatics ) % 2,
+ "fieldmapping::defaultMapping: invalid programmatic map!" );
+ // number of pairs
+ sal_Int32 const nIntersectedProgrammatics = SAL_N_ELEMENTS( pMappingProgrammatics ) / 2;
+
+ const char** pProgrammatic = pMappingProgrammatics;
+ OUString sAddressProgrammatic;
+ OUString sDriverProgrammatic;
+ OUString sDriverUI;
+ for ( sal_Int32 i=0;
+ i < nIntersectedProgrammatics;
+ ++i
+ )
+ {
+ sAddressProgrammatic = OUString::createFromAscii( *pProgrammatic++ );
+ sDriverProgrammatic = OUString::createFromAscii( *pProgrammatic++ );
+
+ if ( aDriverFieldAliasing.hasByName( sDriverProgrammatic ) )
+ {
+ aDriverFieldAliasing.getNodeValue( sDriverProgrammatic ) >>= sDriverUI;
+ if ( 0 == sDriverUI.getLength() )
+ {
+ OSL_FAIL( "fieldmapping::defaultMapping: invalid driver UI column name!");
+ }
+ else
+ _rFieldAssignment[ sAddressProgrammatic ] = sDriverUI;
+ }
+ else
+ {
+ OSL_FAIL( "fieldmapping::defaultMapping: invalid driver programmatic name!" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot",
+ "code is assumed to throw no exceptions!");
+ // the config nodes we're using herein should not do this...
+ }
+ }
+
+
+ void writeTemplateAddressFieldMapping( const Reference< XComponentContext >& _rxContext, MapString2String&& aFieldAssignment )
+ {
+ // access the configuration information which the driver uses for determining its column names
+
+ // create a config node for this
+ OConfigurationTreeRoot aAddressBookSettings = OConfigurationTreeRoot::createWithComponentContext(
+ _rxContext, sAddressBookNodeName);
+
+ OConfigurationNode aFields = aAddressBookSettings.openNode( OUString( "Fields" ) );
+
+ // loop through all existent fields
+ Sequence< OUString > aExistentFields = aFields.getNodeNames();
+ const OUString* pExistentFields = aExistentFields.getConstArray();
+ const OUString* pExistentFieldsEnd = pExistentFields + aExistentFields.getLength();
+
+ static const OUStringLiteral sProgrammaticNodeName( u"ProgrammaticFieldName" );
+ static const OUStringLiteral sAssignedNodeName( u"AssignedFieldName" );
+
+ for ( ; pExistentFields != pExistentFieldsEnd; ++pExistentFields )
+ {
+ SAL_WARN_IF(
+ ((aFields.openNode(*pExistentFields)
+ .getNodeValue(sProgrammaticNodeName).get<OUString>())
+ != *pExistentFields),
+ "extensions.abpilot",
+ "fieldmapping::writeTemplateAddressFieldMapping: inconsistent config data!");
+ // there should be a redundancy in the config data... if this asserts, there isn't anymore!
+
+ // do we have a new alias for the programmatic?
+ MapString2String::iterator aPos = aFieldAssignment.find( *pExistentFields );
+ if ( aFieldAssignment.end() != aPos )
+ { // yes
+ // -> set a new value
+ OConfigurationNode aExistentField = aFields.openNode( *pExistentFields );
+ aExistentField.setNodeValue( sAssignedNodeName, Any( aPos->second ) );
+ // and remove the mapping entry
+ aFieldAssignment.erase( *pExistentFields );
+ }
+ else
+ { // no
+ // -> remove it
+ aFields.removeNode( *pExistentFields );
+ }
+ }
+
+ // now everything remaining in aFieldAssignment marks a mapping entry which was not present
+ // in the config before
+ for (auto const& elem : aFieldAssignment)
+ {
+ DBG_ASSERT( !aFields.hasByName( elem.first ),
+ "fieldmapping::writeTemplateAddressFieldMapping: inconsistence!" );
+ // in case the config node for the fields already has the node named <aNewMapping->first>,
+ // the entry should have been removed from aNewMapping (in the above loop)
+ OConfigurationNode aNewField = aFields.createNode( elem.first );
+ aNewField.setNodeValue( sProgrammaticNodeName, Any( elem.first ) );
+ aNewField.setNodeValue( sAssignedNodeName, Any( elem.second ) );
+ }
+
+ // commit the changes done
+ aAddressBookSettings.commit();
+ }
+
+
+ } // namespace fieldmapping
+
+
+ namespace addressconfig
+ {
+
+
+ void writeTemplateAddressSource( const Reference< XComponentContext >& _rxContext,
+ const OUString& _rDataSourceName, const OUString& _rTableName )
+ {
+ // access the configuration information which the driver uses for determining its column names
+
+ // create a config node for this
+ OConfigurationTreeRoot aAddressBookSettings = OConfigurationTreeRoot::createWithComponentContext(
+ _rxContext, sAddressBookNodeName);
+
+ aAddressBookSettings.setNodeValue( OUString( "DataSourceName" ), Any( _rDataSourceName ) );
+ aAddressBookSettings.setNodeValue( OUString( "Command" ), Any( _rTableName ) );
+ aAddressBookSettings.setNodeValue( OUString( "CommandType" ), Any( sal_Int16(CommandType::TABLE) ) );
+
+ // commit the changes done
+ aAddressBookSettings.commit();
+ }
+
+
+ void markPilotSuccess( const Reference< XComponentContext >& _rxContext )
+ {
+ // access the configuration information which the driver uses for determining its column names
+
+ // create a config node for this
+ OConfigurationTreeRoot aAddressBookSettings = OConfigurationTreeRoot::createWithComponentContext(
+ _rxContext, sAddressBookNodeName);
+
+ // set the flag
+ aAddressBookSettings.setNodeValue( OUString( "AutoPilotCompleted" ), Any( true ) );
+
+ // commit the changes done
+ aAddressBookSettings.commit();
+ }
+
+
+ } // namespace addressconfig
+
+
+} // namespace abp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/fieldmappingimpl.hxx b/extensions/source/abpilot/fieldmappingimpl.hxx
new file mode 100644
index 000000000..e4a2dd1c9
--- /dev/null
+++ b/extensions/source/abpilot/fieldmappingimpl.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <rtl/ustring.hxx>
+#include "abptypes.hxx"
+#include <com/sun/star/uno/Reference.hxx>
+#include "addresssettings.hxx"
+
+namespace com::sun::star {
+ namespace lang {
+ class XMultiServiceFactory;
+ }
+ namespace uno {
+ class XComponentContext;
+ }
+ namespace beans {
+ class XPropertySet;
+ }
+}
+namespace weld { class Window; }
+
+namespace abp
+{
+
+
+ namespace fieldmapping
+ {
+
+
+ /** invokes the field mapping dialog
+ @param _rxORB
+ service factory to use for creating UNO services
+ @param _pParent
+ window to use as parent for the dialog and error messages
+ @param _rSettings
+ current settings. Upon return, the field mapping member of this
+ structure will be filled with the settings the user did in the
+ field mapping dialog.
+ */
+ bool invokeDialog(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxORB,
+ class weld::Window* _pParent,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxDataSource,
+ AddressSettings& _rSettings
+ );
+
+
+ /** creates a default field mapping for usage with the address book SDBC driver
+ <p>The column names as used by the SDBC driver for address books is stored in the configuration,
+ and this function creates a mapping which uses this configuration information.</p>
+ */
+ void defaultMapping(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ MapString2String& /* [out] */ _rFieldAssignment
+ );
+
+
+ /** writes a field mapping for the template document address source
+ */
+ void writeTemplateAddressFieldMapping(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ MapString2String&& _rFieldAssignment
+ );
+
+
+ } // namespace fieldmapping
+
+
+ namespace addressconfig
+ {
+
+
+ /** writes the data source / table name given into the configuration, to where the template documents
+ expect it.
+ */
+ void writeTemplateAddressSource(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ const OUString& _rDataSourceName,
+ const OUString& _rTableName
+ );
+
+ /** writes the configuration entry which states the pilot has been completed successfully
+ */
+ void markPilotSuccess(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+
+ } // namespace addressconfig
+
+
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/fieldmappingpage.cxx b/extensions/source/abpilot/fieldmappingpage.cxx
new file mode 100644
index 000000000..1652723fd
--- /dev/null
+++ b/extensions/source/abpilot/fieldmappingpage.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "fieldmappingpage.hxx"
+#include "fieldmappingimpl.hxx"
+#include "addresssettings.hxx"
+#include "abspilot.hxx"
+
+
+namespace abp
+{
+ FieldMappingPage::FieldMappingPage(weld::Container* pPage, OAddressBookSourcePilot* pController)
+ : AddressBookSourcePage(pPage, pController, "modules/sabpilot/ui/fieldassignpage.ui", "FieldAssignPage")
+ , m_xInvokeDialog(m_xBuilder->weld_button("assign"))
+ , m_xHint(m_xBuilder->weld_label("hint"))
+ {
+ m_xInvokeDialog->connect_clicked(LINK(this, FieldMappingPage, OnInvokeDialog));
+ }
+
+ FieldMappingPage::~FieldMappingPage()
+ {
+ }
+
+ void FieldMappingPage::Activate()
+ {
+ AddressBookSourcePage::Activate();
+ m_xInvokeDialog->grab_focus();
+ }
+
+ void FieldMappingPage::initializePage()
+ {
+ AddressBookSourcePage::initializePage();
+ implUpdateHint();
+ }
+
+ void FieldMappingPage::implUpdateHint()
+ {
+ const AddressSettings& rSettings = getSettings();
+ OUString sHint;
+ if ( rSettings.aFieldMapping.empty() )
+ sHint = compmodule::ModuleRes(RID_STR_NOFIELDSASSIGNED);
+ m_xHint->set_label(sHint);
+ }
+
+ IMPL_LINK_NOARG( FieldMappingPage, OnInvokeDialog, weld::Button&, void )
+ {
+ AddressSettings& rSettings = getSettings();
+
+ // invoke the dialog doing the mapping
+ if ( fieldmapping::invokeDialog( getORB(), getDialog()->getDialog(), getDialog()->getDataSource().getDataSource(), rSettings ) )
+ {
+ if ( !rSettings.aFieldMapping.empty() )
+ getDialog()->travelNext();
+ else
+ implUpdateHint();
+ }
+ }
+
+} // namespace abp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/fieldmappingpage.hxx b/extensions/source/abpilot/fieldmappingpage.hxx
new file mode 100644
index 000000000..e3ef93794
--- /dev/null
+++ b/extensions/source/abpilot/fieldmappingpage.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "abspage.hxx"
+#include <vcl/weld.hxx>
+
+namespace abp
+{
+ class FieldMappingPage final : public AddressBookSourcePage
+ {
+ std::unique_ptr<weld::Button> m_xInvokeDialog;
+ std::unique_ptr<weld::Label> m_xHint;
+ public:
+ explicit FieldMappingPage(weld::Container* pPage, OAddressBookSourcePilot* pController);
+ virtual ~FieldMappingPage() override;
+ private:
+ // OWizardPage overridables
+ virtual void initializePage() override;
+
+ // BuilderPage overridables
+ virtual void Activate() override;
+
+ DECL_LINK(OnInvokeDialog, weld::Button&, void);
+
+ void implUpdateHint();
+ };
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/moduleabp.cxx b/extensions/source/abpilot/moduleabp.cxx
new file mode 100644
index 000000000..0a8f8fd4d
--- /dev/null
+++ b/extensions/source/abpilot/moduleabp.cxx
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <componentmodule.cxx>
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/tableselectionpage.cxx b/extensions/source/abpilot/tableselectionpage.cxx
new file mode 100644
index 000000000..abf8e5829
--- /dev/null
+++ b/extensions/source/abpilot/tableselectionpage.cxx
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "tableselectionpage.hxx"
+#include "abptypes.hxx"
+#include "addresssettings.hxx"
+#include "abspilot.hxx"
+#include <tools/debug.hxx>
+
+
+namespace abp
+{
+
+ TableSelectionPage::TableSelectionPage(weld::Container* pPage, OAddressBookSourcePilot* pController)
+ : AddressBookSourcePage(pPage, pController, "modules/sabpilot/ui/selecttablepage.ui", "SelectTablePage")
+ , m_xTableList(m_xBuilder->weld_tree_view("table"))
+ {
+ m_xTableList->connect_changed( LINK( this, TableSelectionPage, OnTableSelected ) );
+ m_xTableList->connect_row_activated( LINK( this, TableSelectionPage, OnTableDoubleClicked ) );
+ }
+
+ TableSelectionPage::~TableSelectionPage()
+ {
+ }
+
+ void TableSelectionPage::Activate()
+ {
+ AddressBookSourcePage::Activate();
+
+ m_xTableList->grab_focus();
+ }
+
+ void TableSelectionPage::initializePage()
+ {
+ AddressBookSourcePage::initializePage();
+
+ const AddressSettings& rSettings = getSettings();
+
+ m_xTableList->clear();
+
+ // get the table names
+ const StringBag& aTableNames = getDialog()->getDataSource().getTableNames();
+ DBG_ASSERT( aTableNames.size() > 1, "TableSelectionPage::initializePage: to be called for more than one table only!");
+ // this page should never bother the user if there is 1 or less tables.
+
+ // fill the list
+ for (auto const& tableName : aTableNames)
+ m_xTableList->append_text(tableName);
+
+ // initially select the proper table
+ m_xTableList->select_text(rSettings.sSelectedTable);
+ }
+
+ IMPL_LINK_NOARG( TableSelectionPage, OnTableDoubleClicked, weld::TreeView&, bool )
+ {
+ if (m_xTableList->count_selected_rows() == 1)
+ getDialog()->travelNext();
+ return true;
+ }
+
+ IMPL_LINK_NOARG( TableSelectionPage, OnTableSelected, weld::TreeView&, void )
+ {
+ updateDialogTravelUI();
+ }
+
+ bool TableSelectionPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!AddressBookSourcePage::commitPage(_eReason))
+ return false;
+
+ AddressSettings& rSettings = getSettings();
+ rSettings.sSelectedTable = m_xTableList->get_selected_text();
+
+ return true;
+ }
+
+ bool TableSelectionPage::canAdvance() const
+ {
+ return AddressBookSourcePage::canAdvance()
+ && (m_xTableList->count_selected_rows() > 0);
+ }
+
+} // namespace abp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/tableselectionpage.hxx b/extensions/source/abpilot/tableselectionpage.hxx
new file mode 100644
index 000000000..a150bf844
--- /dev/null
+++ b/extensions/source/abpilot/tableselectionpage.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "abspage.hxx"
+#include <vcl/weld.hxx>
+
+namespace abp
+{
+ class TableSelectionPage final : public AddressBookSourcePage
+ {
+ std::unique_ptr<weld::TreeView> m_xTableList;
+
+ public:
+ explicit TableSelectionPage(weld::Container* pPage, OAddressBookSourcePilot* pController);
+ virtual ~TableSelectionPage() override;
+ private:
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+
+ // BuilderPage overridables
+ virtual void Activate() override;
+
+ // OImportPage overridables
+ virtual bool canAdvance() const override;
+
+ DECL_LINK(OnTableSelected, weld::TreeView&, void);
+ DECL_LINK(OnTableDoubleClicked, weld::TreeView&, bool);
+ };
+
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/typeselectionpage.cxx b/extensions/source/abpilot/typeselectionpage.cxx
new file mode 100644
index 000000000..79cfd5d6b
--- /dev/null
+++ b/extensions/source/abpilot/typeselectionpage.cxx
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "typeselectionpage.hxx"
+#include "addresssettings.hxx"
+#include "abspilot.hxx"
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <com/sun/star/sdbc/XDriver.hpp>
+#include <com/sun/star/sdbc/DriverManager.hpp>
+
+namespace abp
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::sdbc;
+
+ // TypeSelectionPage
+ TypeSelectionPage::TypeSelectionPage(weld::Container* pPage, OAddressBookSourcePilot* pDialog)
+ : AddressBookSourcePage(pPage, pDialog, "modules/sabpilot/ui/selecttypepage.ui", "SelectTypePage")
+ , m_xEvolution(m_xBuilder->weld_radio_button("evolution"))
+ , m_xEvolutionGroupwise(m_xBuilder->weld_radio_button("groupwise"))
+ , m_xEvolutionLdap(m_xBuilder->weld_radio_button("evoldap"))
+ , m_xThunderbird(m_xBuilder->weld_radio_button("thunderbird"))
+ , m_xKab(m_xBuilder->weld_radio_button("kde"))
+ , m_xMacab(m_xBuilder->weld_radio_button("macosx"))
+ , m_xOther(m_xBuilder->weld_radio_button("other"))
+ {
+ //TODO: For now, try to keep offering the same choices like before the
+ // Mozilla cleanup, even if the status of what driver actually
+ // provides which functionality is somewhat unclear, see the discussions
+ // of fdo#57285 "Address Book Data Source Wizard lists 'macOS address
+ // book' on Linux" and fdo#57322 "Moz-free LDAP Address Book driver."
+ // In accordance with ancient OOo 3.3, this is as follows:
+ //
+ // On Linux:
+ // - EVOLUTION, EVOLUTION_GROUPWISE, EVOLUTION_LDAP (if applicable)
+ // - KAB (if applicable)
+ // - OTHER
+ //
+ // On macOS:
+ // - MACAB (if applicable)
+ // - OTHER
+ //
+ // On Windows:
+ // - THUNDERBIRD
+ // - OTHER
+
+#if !defined(_WIN32)
+ bool bHaveEvolution = false;
+ bool bHaveKab = false;
+ bool bHaveMacab = false;
+
+ Reference< XDriverManager2 > xManager = DriverManager::create( pDialog->getORB() );
+
+ try
+ {
+ // check whether Evolution is available
+ Reference< XDriver > xDriver( xManager->getDriverByURL("sdbc:address:evolution:local") );
+ if ( xDriver.is() )
+ bHaveEvolution = true;
+ }
+ catch (...)
+ {
+ }
+
+ // check whether KDE address book is available
+ try
+ {
+ Reference< XDriver > xDriver( xManager->getDriverByURL("sdbc:address:kab") );
+ if ( xDriver.is() )
+ bHaveKab = true;
+ }
+ catch (...)
+ {
+ }
+
+ try
+ {
+ // check whether macOS address book is available
+ Reference< XDriver > xDriver( xManager->getDriverByURL("sdbc:address:macab") );
+ if ( xDriver.is() )
+ bHaveMacab = true;
+ }
+ catch(...)
+ {
+ }
+#else
+ bool const bHaveEvolution = false;
+ bool const bHaveKab = false;
+ bool const bHaveMacab = false;
+#endif
+
+ // Items are displayed in list order
+ m_aAllTypes.push_back( ButtonItem( m_xEvolution.get(), AST_EVOLUTION, bHaveEvolution ) );
+ m_aAllTypes.push_back( ButtonItem( m_xEvolutionGroupwise.get(), AST_EVOLUTION_GROUPWISE, bHaveEvolution ) );
+ m_aAllTypes.push_back( ButtonItem( m_xEvolutionLdap.get(), AST_EVOLUTION_LDAP, bHaveEvolution ) );
+ m_aAllTypes.push_back( ButtonItem( m_xThunderbird.get(), AST_THUNDERBIRD, true ) );
+ m_aAllTypes.push_back( ButtonItem( m_xKab.get(), AST_KAB, bHaveKab ) );
+ m_aAllTypes.push_back( ButtonItem( m_xMacab.get(), AST_MACAB, bHaveMacab ) );
+ m_aAllTypes.push_back( ButtonItem( m_xOther.get(), AST_OTHER, true ) );
+
+ Link<weld::Toggleable&,void> aTypeSelectionHandler = LINK(this, TypeSelectionPage, OnTypeSelected );
+ for (auto const& elem : m_aAllTypes)
+ {
+ if (!elem.m_bVisible)
+ elem.m_pItem->hide();
+ else
+ {
+ elem.m_pItem->connect_toggled( aTypeSelectionHandler );
+ elem.m_pItem->show();
+ }
+ }
+ }
+
+ TypeSelectionPage::~TypeSelectionPage()
+ {
+ for (auto & elem : m_aAllTypes)
+ {
+ elem.m_bVisible = false;
+ }
+ }
+
+ void TypeSelectionPage::Activate()
+ {
+ AddressBookSourcePage::Activate();
+
+ for (auto const& elem : m_aAllTypes)
+ {
+ if( elem.m_pItem->get_active() && elem.m_bVisible )
+ {
+ elem.m_pItem->grab_focus();
+ break;
+ }
+ }
+
+ getDialog()->enableButtons(WizardButtonFlags::PREVIOUS, false);
+ }
+
+ void TypeSelectionPage::Deactivate()
+ {
+ AddressBookSourcePage::Deactivate();
+ getDialog()->enableButtons(WizardButtonFlags::PREVIOUS, true);
+ }
+
+ void TypeSelectionPage::selectType( AddressSourceType _eType )
+ {
+ for (auto const& elem : m_aAllTypes)
+ {
+ elem.m_pItem->set_active( _eType == elem.m_eType );
+ }
+ }
+
+ AddressSourceType TypeSelectionPage::getSelectedType() const
+ {
+ for (auto const& elem : m_aAllTypes)
+ {
+ if ( elem.m_pItem->get_active() && elem.m_bVisible )
+ return elem.m_eType;
+ }
+
+ return AST_INVALID;
+ }
+
+ void TypeSelectionPage::initializePage()
+ {
+ AddressBookSourcePage::initializePage();
+
+ const AddressSettings& rSettings = getSettings();
+ selectType(rSettings.eType);
+ }
+
+ bool TypeSelectionPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!AddressBookSourcePage::commitPage(_eReason))
+ return false;
+
+ if (AST_INVALID == getSelectedType( ))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xContainer.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ compmodule::ModuleRes(RID_STR_NEEDTYPESELECTION)));
+ xBox->run();
+ return false;
+ }
+
+ AddressSettings& rSettings = getSettings();
+ rSettings.eType = getSelectedType();
+
+ return true;
+ }
+
+ bool TypeSelectionPage::canAdvance() const
+ {
+ return AddressBookSourcePage::canAdvance()
+ && (AST_INVALID != getSelectedType());
+ }
+
+ IMPL_LINK(TypeSelectionPage, OnTypeSelected, weld::Toggleable&, rButton, void)
+ {
+ if (!rButton.get_active())
+ return;
+ getDialog()->typeSelectionChanged( getSelectedType() );
+ updateDialogTravelUI();
+ }
+
+} // namespace abp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/typeselectionpage.hxx b/extensions/source/abpilot/typeselectionpage.hxx
new file mode 100644
index 000000000..7c908057d
--- /dev/null
+++ b/extensions/source/abpilot/typeselectionpage.hxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "abspage.hxx"
+#include "addresssettings.hxx"
+#include <vcl/weld.hxx>
+
+namespace abp
+{
+
+ class TypeSelectionPage final : public AddressBookSourcePage
+ {
+ std::unique_ptr<weld::RadioButton> m_xEvolution;
+ std::unique_ptr<weld::RadioButton> m_xEvolutionGroupwise;
+ std::unique_ptr<weld::RadioButton> m_xEvolutionLdap;
+ std::unique_ptr<weld::RadioButton> m_xThunderbird;
+ std::unique_ptr<weld::RadioButton> m_xKab;
+ std::unique_ptr<weld::RadioButton> m_xMacab;
+ std::unique_ptr<weld::RadioButton> m_xOther;
+
+ struct ButtonItem {
+ weld::RadioButton* m_pItem;
+ AddressSourceType m_eType;
+ bool m_bVisible;
+
+ ButtonItem( weld::RadioButton *pItem,
+ AddressSourceType eType,
+ bool bVisible ) :
+ m_pItem( pItem ),
+ m_eType( eType ),
+ m_bVisible( bVisible )
+ {}
+ };
+
+ std::vector< ButtonItem > m_aAllTypes;
+
+ public:
+ explicit TypeSelectionPage(weld::Container* pPage, OAddressBookSourcePilot* pController);
+ virtual ~TypeSelectionPage() override;
+
+ // retrieves the currently selected type
+ AddressSourceType getSelectedType() const;
+
+ private:
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+
+ // BuilderPage overridables
+ virtual void Activate() override;
+ virtual void Deactivate() override;
+
+ // OImportPage overridables
+ virtual bool canAdvance() const override;
+
+ DECL_LINK( OnTypeSelected, weld::Toggleable&, void );
+
+ void selectType( AddressSourceType _eType );
+ };
+
+
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/unodialogabp.cxx b/extensions/source/abpilot/unodialogabp.cxx
new file mode 100644
index 000000000..4409a1911
--- /dev/null
+++ b/extensions/source/abpilot/unodialogabp.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 "unodialogabp.hxx"
+#include "abspilot.hxx"
+#include <comphelper/sequence.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+
+#define PROPERTY_ID_DATASOURCENAME 3
+
+namespace abp
+{
+ 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::ui::dialogs;
+
+ OABSPilotUno::OABSPilotUno(const Reference< XComponentContext >& _rxORB)
+ :OGenericUnoDialog(_rxORB)
+ {
+ registerProperty( "DataSourceName", PROPERTY_ID_DATASOURCENAME, PropertyAttribute::READONLY ,
+ &m_sDataSourceName, cppu::UnoType<decltype(m_sDataSourceName)>::get() );
+ }
+
+ Any SAL_CALL OABSPilotUno::queryInterface( const Type& aType )
+ {
+ Any aReturn = svt::OGenericUnoDialog::queryInterface( aType );
+ return aReturn.hasValue() ? aReturn : OABSPilotUno_JBase::queryInterface( aType );
+ }
+
+ void SAL_CALL OABSPilotUno::acquire( ) noexcept
+ {
+ svt::OGenericUnoDialog::acquire();
+ }
+
+ void SAL_CALL OABSPilotUno::release( ) noexcept
+ {
+ svt::OGenericUnoDialog::release();
+ }
+
+ Sequence< Type > SAL_CALL OABSPilotUno::getTypes( )
+ {
+ return ::comphelper::concatSequences(
+ svt::OGenericUnoDialog::getTypes(),
+ OABSPilotUno_JBase::getTypes()
+ );
+ }
+
+ Sequence<sal_Int8> SAL_CALL OABSPilotUno::getImplementationId( )
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+ OUString SAL_CALL OABSPilotUno::getImplementationName()
+ {
+ return "org.openoffice.comp.abp.OAddressBookSourcePilot";
+ }
+
+ css::uno::Sequence<OUString> SAL_CALL OABSPilotUno::getSupportedServiceNames()
+ {
+ return { "com.sun.star.ui.dialogs.AddressBookSourcePilot" };
+ }
+
+ Reference<XPropertySetInfo> SAL_CALL OABSPilotUno::getPropertySetInfo()
+ {
+ Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+ }
+
+
+ ::cppu::IPropertyArrayHelper& OABSPilotUno::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+
+ ::cppu::IPropertyArrayHelper* OABSPilotUno::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties(aProps);
+ return new ::cppu::OPropertyArrayHelper(aProps);
+ }
+
+ void SAL_CALL OABSPilotUno::initialize( const Sequence< Any >& aArguments )
+ {
+ Reference<awt::XWindow> xParentWindow;
+ if (aArguments.getLength() == 1 && (aArguments[0] >>= xParentWindow) ) {
+ Sequence< Any > aNewArgs{ Any(PropertyValue(
+ "ParentWindow", 0, Any(xParentWindow), PropertyState_DIRECT_VALUE )) };
+ OGenericUnoDialog::initialize(aNewArgs);
+ } else {
+ OGenericUnoDialog::initialize(aArguments);
+ }
+ }
+
+ std::unique_ptr<weld::DialogController> OABSPilotUno::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent)
+ {
+ return std::make_unique<OAddressBookSourcePilot>(Application::GetFrameWeld(rParent), m_aContext);
+ }
+
+ Any SAL_CALL OABSPilotUno::execute( const Sequence< NamedValue >& /*lArgs*/ )
+ {
+ // not interested in the context, not interested in the args
+ // -> call the execute method of the XExecutableDialog
+ static_cast< XExecutableDialog* >( this )->execute();
+
+ // result interest not really ...
+ // We show this dialog one times only!
+ // User has one chance to accept it or not.
+ // (or he can start it again by using wizard-menu!)
+ // So we should deregister it on our general job execution service by using right protocol parameters.
+ css::uno::Sequence< css::beans::NamedValue > lProtocol { { "Deactivate", css::uno::Any( true ) } };
+ return Any( lProtocol );
+ }
+
+ void OABSPilotUno::executedDialog(sal_Int16 _nExecutionResult)
+ {
+ if ( _nExecutionResult == RET_OK )
+ {
+ const AddressSettings& aSettings = static_cast<OAddressBookSourcePilot*>(m_xDialog.get())->getSettings();
+ m_sDataSourceName = aSettings.bRegisterDataSource ? aSettings.sRegisteredDataSourceName : aSettings.sDataSourceName;
+ }
+ }
+
+
+} // namespace abp
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+org_openoffice_comp_abp_OAddressBookSourcePilot(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new abp::OABSPilotUno(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/abpilot/unodialogabp.hxx b/extensions/source/abpilot/unodialogabp.hxx
new file mode 100644
index 000000000..b8bd940af
--- /dev/null
+++ b/extensions/source/abpilot/unodialogabp.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 <svtools/genericunodialog.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <componentmodule.hxx>
+#include <com/sun/star/task/XJob.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+namespace abp
+{
+ class OABSPilotUno;
+ typedef ::cppu::ImplHelper1< css::task::XJob > OABSPilotUno_JBase;
+ typedef ::comphelper::OPropertyArrayUsageHelper< OABSPilotUno > OABSPilotUno_PBase;
+ /// the UNO wrapper for the address book source pilot
+ class OABSPilotUno
+ : public svt::OGenericUnoDialog
+ , public OABSPilotUno_JBase
+ , public OABSPilotUno_PBase
+ {
+ OUString m_sDataSourceName;
+
+ public:
+ explicit OABSPilotUno(const css::uno::Reference< css::uno::XComponentContext >& _rxORB);
+
+ private:
+ // XInterface (disambiguation)
+ 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;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ // XJob
+ virtual css::uno::Any SAL_CALL execute( const css::uno::Sequence< css::beans::NamedValue >& lArgs ) override;
+
+ // XInitialisation
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ using svt::OGenericUnoDialog::execute;
+ // OGenericUnoDialog overridables
+ virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override;
+ virtual void executedDialog(sal_Int16 _nExecutionResult) override;
+ };
+
+} // namespace abp
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/README.txt b/extensions/source/activex/README.txt
new file mode 100644
index 000000000..50c209150
--- /dev/null
+++ b/extensions/source/activex/README.txt
@@ -0,0 +1,33 @@
+Description.
+
+The StarOffice ActiveX control shows an example of access to UNO through COM technology.
+It requires a properly installed StarOffice version 6.0/6.1 or OpenOffice 1.0.
+This is a Lite ActiveX control so it can be used only in containers that
+allows to use such controls.
+
+Pressing to any link to staroffice document should activate the control.
+So the document will be opened in ReadOnly mode.
+
+Also it can be activated with an <OBJECT> tag from a html-page.
+Without any parameters for an object tag a new writer document will be
+opened for editing. Possible parameters are
+ src - full URL to the file that should be edited/viewed;
+ it can contain "private:factory/..." URLs to open new documents
+ for edit, for example "private:factory/swriter"
+ readonly - the default value is "true", in case it is set to any other
+ value the document is opened for editing
+
+As any ActiveX control this one should be registered.
+To let MSIE register it itself the "CODEBASE" parameter
+for the "OBJECT" tag should be specified
+with a URL to the library "so_activex.dll".
+The example of registration with "OBJECT" tag is in example.html.
+
+Also it can be done using regsvr32 application.
+To do it please write
+<Path to Windows installation>\System32\regsvr32 so_activex.dll
+
+To unregister the control please use /u option:
+<Path to Windows installation>\system32\regsvr32 so_activex.dll /u
+
+
diff --git a/extensions/source/activex/SOActionsApproval.cxx b/extensions/source/activex/SOActionsApproval.cxx
new file mode 100644
index 000000000..a40921662
--- /dev/null
+++ b/extensions/source/activex/SOActionsApproval.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// SOActionsApproval.cpp : Implementation of CHelpApp and DLL registration.
+
+#include <sal/config.h>
+
+#include <cstddef>
+
+#include "StdAfx2.h"
+
+#include "SOActionsApproval.h"
+#include <sal/macros.h>
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+#include <so_activex.h>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP SOActionsApproval::InterfaceSupportsErrorInfo(REFIID riid)
+{
+ static const IID* arr[] = {
+ &IID_ISOActionsApproval,
+ };
+
+ for (std::size_t i = 0; i < SAL_N_ELEMENTS(arr); i++)
+ {
+#ifdef _MSC_VER
+ if (InlineIsEqualGUID(*arr[i], riid))
+#else
+ if (::ATL::InlineIsEqualGUID(*arr[i], riid))
+#endif
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/SOActionsApproval.h b/extensions/source/activex/SOActionsApproval.h
new file mode 100644
index 000000000..2484e7462
--- /dev/null
+++ b/extensions/source/activex/SOActionsApproval.h
@@ -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 .
+ */
+
+// SOActionsApproval.h: Definition of the SOActionsApproval class
+
+#pragma once
+
+#ifdef _MSC_VER
+#pragma once
+#endif
+
+#include "resource.h"
+#include <ExDispID.h>
+#include <ExDisp.h>
+#include <shlguid.h>
+
+#include <atlctl.h>
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+#include <so_activex.h>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+// SOActionsApproval
+
+class SOActionsApproval :
+ public IDispatchImpl<ISOActionsApproval, &IID_ISOActionsApproval, &LIBID_SO_ACTIVEXLib>,
+ public ISupportErrorInfo,
+ public CComObjectRoot,
+ public CComCoClass<SOActionsApproval,&CLSID_SOActionsApproval>
+{
+public:
+ SOActionsApproval() {}
+ virtual ~SOActionsApproval() {}
+
+BEGIN_COM_MAP(SOActionsApproval)
+ COM_INTERFACE_ENTRY(IDispatch)
+ COM_INTERFACE_ENTRY(ISOActionsApproval)
+ COM_INTERFACE_ENTRY(ISupportErrorInfo)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#endif
+END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+DECLARE_NOT_AGGREGATABLE(SOActionsApproval)
+// Remove the comment from the line above if you don't want your object to
+// support aggregation.
+
+DECLARE_REGISTRY_RESOURCEID(IDR_SODOCUMENTEVENTLISTENER)
+
+// ISupportsErrorInfo
+ STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid) override;
+
+// ISOActionsApproval
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE approveAction(
+ /* [in] */ long nActionID,
+ /* [retval][out] */ boolean *pbApproval) override
+ {
+ // only PreventClose is approved
+ USES_CONVERSION;
+ *pbApproval = ( nActionID == 1 );
+
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Bridge_implementedInterfaces(
+ /* [retval][out] */ SAFEARRAY __RPC_FAR * __RPC_FAR *pVal) override
+ {
+ *pVal = SafeArrayCreateVector( VT_BSTR, 0, 1 );
+
+ if( !*pVal )
+ return E_FAIL;
+
+ LONG ix = 0;
+ CComBSTR aInterface( OLESTR( "com.sun.star.embed.XActionsApproval" ) );
+ SafeArrayPutElement( *pVal, &ix, aInterface );
+
+ return S_OK;
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/SOActionsApproval.rgs b/extensions/source/activex/SOActionsApproval.rgs
new file mode 100644
index 000000000..543320813
--- /dev/null
+++ b/extensions/source/activex/SOActionsApproval.rgs
@@ -0,0 +1,24 @@
+HKCR
+{
+9F3697AC-7A18-4335-AF0A-65FAC2C35CC1
+ so_activex.SOActionsApproval.1 = s 'SOActionsApproval Class'
+ {
+ CLSID = s '{9F3697AC-7A18-4335-AF0A-65FAC2C35CC1}'
+ }
+ so_activex.SOActionsApproval = s 'SOActionsApproval Class'
+ {
+ CLSID = s '{9F3697AC-7A18-4335-AF0A-65FAC2C35CC1}'
+ }
+ NoRemove CLSID
+ {
+ ForceRemove {9F3697AC-7A18-4335-AF0A-65FAC2C35CC1} = s 'SOActionsApproval Class'
+ {
+ ProgID = s 'so_activex.SOActionsApproval.1'
+ VersionIndependentProgID = s 'so_activex.SOActionsApproval'
+ InprocServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'both'
+ }
+ }
+ }
+}
diff --git a/extensions/source/activex/SOActiveX.cxx b/extensions/source/activex/SOActiveX.cxx
new file mode 100644
index 000000000..88b3ad769
--- /dev/null
+++ b/extensions/source/activex/SOActiveX.cxx
@@ -0,0 +1,1162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// SOActiveX.cpp : Implementation of CSOActiveX
+
+#include "StdAfx2.h"
+#include "SOActiveX.h"
+#include "SOComWindowPeer.h"
+#include "SODispatchInterceptor.h"
+#include "SOActionsApproval.h"
+#include "com_uno_helper.h"
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+#include <so_activex.h>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+#define STAROFFICE_WINDOWCLASS L"SOParentWindow"
+
+
+static void OutputError_Impl( HWND hw, HRESULT ErrorCode )
+{
+ LPWSTR sMessage = nullptr;
+ FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ ErrorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ reinterpret_cast<LPWSTR>(&sMessage),
+ 0,
+ nullptr
+ );
+ MessageBoxW( hw, sMessage, nullptr, MB_OK | MB_ICONINFORMATION );
+ HeapFree( GetProcessHeap(), 0, sMessage );
+}
+
+HRESULT ExecuteFunc( IDispatch* idispUnoObject,
+ OLECHAR const * sFuncName,
+ CComVariant* params,
+ unsigned int count,
+ CComVariant* pResult )
+{
+ if( !idispUnoObject )
+ return E_FAIL;
+
+ DISPID id;
+ HRESULT hr = idispUnoObject->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sFuncName), 1, LOCALE_USER_DEFAULT, &id);
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ DISPPARAMS dispparams= { params, nullptr, count, 0};
+
+ // DEBUG
+ EXCEPINFO myInfo;
+ hr = idispUnoObject->Invoke( id, IID_NULL,LOCALE_USER_DEFAULT, DISPATCH_METHOD,
+ &dispparams, pResult, &myInfo, nullptr);
+
+ // for debugging purposes
+ // USES_CONVERSION;
+ // if ( !SUCCEEDED( hr ) )
+ // ::MessageBox( NULL, OLE2A( myInfo.bstrDescription ), OLE2A( myInfo.bstrSource ), MB_OK | MB_ICONINFORMATION );
+
+ return hr;
+}
+
+static HRESULT GetIDispByFunc( IDispatch* idispUnoObject,
+ OLECHAR const * sFuncName,
+ CComVariant* params,
+ unsigned int count,
+ CComPtr<IDispatch>& pdispResult )
+{
+ if( !idispUnoObject )
+ return E_FAIL;
+
+ CComVariant result;
+ HRESULT hr = ExecuteFunc( idispUnoObject, sFuncName, params, count, &result );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ if( result.vt != VT_DISPATCH || result.pdispVal == nullptr )
+ return E_FAIL;
+
+ pdispResult = CComPtr<IDispatch>( result.pdispVal );
+
+ return S_OK;
+}
+
+static HRESULT PutPropertiesToIDisp( IDispatch* pdispObject,
+ OLECHAR const ** sMemberNames,
+ CComVariant* pVariant,
+ unsigned int count )
+{
+ for( unsigned int ind = 0; ind < count; ind++ )
+ {
+ DISPID id;
+ HRESULT hr = pdispObject->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sMemberNames[ind]), 1, LOCALE_USER_DEFAULT, &id );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ hr = CComDispatchDriver::PutProperty( pdispObject, id, &pVariant[ind] );
+ if( !SUCCEEDED( hr ) ) return hr;
+ }
+
+ return S_OK;
+}
+
+HRESULT GetPropertiesFromIDisp( IDispatch* pdispObject,
+ OLECHAR const ** sMemberNames,
+ CComVariant* pVariant,
+ unsigned int count )
+{
+ for( unsigned int ind = 0; ind < count; ind++ )
+ {
+ DISPID id;
+ HRESULT hr = pdispObject->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sMemberNames[ind]), 1, LOCALE_USER_DEFAULT, &id );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ hr = CComDispatchDriver::GetProperty( pdispObject, id, &pVariant[ind] );
+ if( !SUCCEEDED( hr ) ) return hr;
+ }
+
+ return S_OK;
+}
+
+// CSOActiveX
+
+CSOActiveX::CSOActiveX()
+: mCookie(0)
+, mCurFileUrl( L"private:factory/swriter" )
+, mbLoad( FALSE )
+, mbViewOnly( TRUE )
+, mParentWin( nullptr )
+, mOffWin( nullptr )
+, mpDispatchInterceptor( nullptr )
+, mnVersion( SO_NOT_DETECTED )
+, mbReadyForActivation( FALSE )
+, mbDrawLocked( false )
+{
+ CLSID const clsFactory = {0x82154420,0x0FBF,0x11d4,{0x83, 0x13,0x00,0x50,0x04,0x52,0x6A,0xB4}};
+ HRESULT hr = CoCreateInstance( clsFactory, nullptr, CLSCTX_ALL, __uuidof(IDispatch), reinterpret_cast<void**>(&mpDispFactory));
+ if( !SUCCEEDED( hr ) )
+ OutputError_Impl( nullptr, hr );
+
+ mPWinClass.style = CS_HREDRAW|CS_VREDRAW;
+ mPWinClass.lpfnWndProc = DefWindowProcW;
+ mPWinClass.cbClsExtra = 0;
+ mPWinClass.cbWndExtra = 0;
+ mPWinClass.hInstance = GetModuleHandleW(nullptr); //myInstance;
+ mPWinClass.hIcon = nullptr;
+ mPWinClass.hCursor = nullptr;
+ mPWinClass.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND);
+ mPWinClass.lpszMenuName = nullptr;
+ mPWinClass.lpszClassName = STAROFFICE_WINDOWCLASS;
+
+ RegisterClassW(&mPWinClass);
+}
+
+CSOActiveX::~CSOActiveX()
+{
+ Cleanup();
+
+}
+
+HRESULT CSOActiveX::Cleanup()
+{
+ CComVariant dummyResult;
+
+ if( mpDispatchInterceptor )
+ {
+ if( mpDispFrame )
+ {
+ // remove dispatch interceptor
+ CComQIPtr< IDispatch, &IID_IDispatch > pIDispDispInter( mpDispatchInterceptor );
+ CComVariant aVariant( pIDispDispInter );
+ ExecuteFunc( mpDispFrame,
+ L"releaseDispatchProviderInterceptor",
+ &aVariant,
+ 1,
+ &dummyResult );
+ }
+
+ mpDispatchInterceptor->ClearParent();
+ mpDispatchInterceptor->Release();
+ mpDispatchInterceptor = nullptr;
+ }
+
+ mpDispTempFile = CComPtr< IDispatch >();
+ mbReadyForActivation = FALSE;
+
+ if( mpInstanceLocker )
+ {
+ ExecuteFunc( mpInstanceLocker, L"dispose", nullptr, 0, &dummyResult );
+ mpInstanceLocker = CComPtr< IDispatch >();
+ }
+
+ if( mpDispFrame )
+ {
+ bool bCloserActivated = false;
+
+ CComPtr<IDispatch> pDispDocumentCloser;
+ CComVariant aDocCloser( L"com.sun.star.embed.DocumentCloser" );
+ HRESULT hr = GetIDispByFunc( mpDispFactory,
+ L"createInstance",
+ &aDocCloser,
+ 1,
+ pDispDocumentCloser );
+ if ( SUCCEEDED( hr ) && pDispDocumentCloser )
+ {
+ SAFEARRAY* pInitFrame = SafeArrayCreateVector(VT_VARIANT, 0, 1);
+ LONG nInitInd = 0;
+ CComVariant pFrameVariant( mpDispFrame );
+ SafeArrayPutElement( pInitFrame, &nInitInd, &pFrameVariant );
+ CComVariant aVarInitFrame;
+ aVarInitFrame.vt = VT_ARRAY | VT_VARIANT; aVarInitFrame.parray = pInitFrame;
+ hr = ExecuteFunc( pDispDocumentCloser, L"initialize", &aVarInitFrame, 1, &dummyResult );
+ if( SUCCEEDED( hr ) )
+ {
+ // the following call will let the closing happen
+ hr = ExecuteFunc( pDispDocumentCloser, L"dispose", nullptr, 0, &dummyResult );
+ bCloserActivated = SUCCEEDED( hr );
+ }
+ }
+
+ if ( !bCloserActivated )
+ {
+ CComVariant aPropVar;
+ aPropVar.vt = VT_BOOL; aPropVar.boolVal = VARIANT_TRUE;
+ if ( !SUCCEEDED( ExecuteFunc( mpDispFrame, L"close", &aPropVar, 1, &dummyResult ) ) )
+ ExecuteFunc( mpDispFrame, L"dispose", nullptr, 0, &dummyResult );
+ }
+
+ mpDispFrame = CComPtr< IDispatch >();
+ }
+
+ if( ::IsWindow( mOffWin ) )
+ ::DestroyWindow( mOffWin );
+
+ TerminateOffice();
+
+ return S_OK;
+}
+
+HRESULT CSOActiveX::TerminateOffice()
+{
+ // create desktop
+ CComPtr<IDispatch> pdispDesktop;
+ CComVariant aDesktopServiceName( L"com.sun.star.frame.Desktop" );
+
+ HRESULT hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aDesktopServiceName, 1, pdispDesktop );
+ if( !pdispDesktop || !SUCCEEDED( hr ) ) return hr;
+
+ // create tree of frames
+ CComPtr<IDispatch> pdispChildren;
+ hr = GetIDispByFunc( pdispDesktop, L"getFrames", nullptr, 0, pdispChildren );
+ if( !pdispChildren || !SUCCEEDED( hr ) ) return hr;
+
+ CComVariant aFrames;
+ CComVariant nFlag( 4 );
+ hr = ExecuteFunc( pdispChildren, L"queryFrames", &nFlag, 1, &aFrames );
+ if ( SUCCEEDED( hr ) )
+ {
+ if ( ( aFrames.vt == ( VT_ARRAY | VT_DISPATCH ) || aFrames.vt == ( VT_ARRAY | VT_VARIANT ) )
+ && ( !aFrames.parray || (aFrames.parray->cDims == 1 && aFrames.parray->rgsabound[0].cElements == 0) ) )
+ {
+ // there is no frames open
+ // TODO: check whether the frames are hidden if they are open?
+ CComVariant dummyResult;
+ hr = ExecuteFunc( pdispDesktop, L"terminate", nullptr, 0, &dummyResult );
+ }
+ }
+
+ return hr;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP CSOActiveX::InitNew ()
+{
+ mnVersion = GetVersionConnected();
+ mbLoad = TRUE;
+ return S_OK;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP CSOActiveX::Load ( LPSTREAM /*pStm*/ )
+{
+ mnVersion = GetVersionConnected();
+ mbLoad = TRUE;
+
+ // may be later?
+ // for now just ignore
+
+ return S_OK;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP CSOActiveX::Load( LPPROPERTYBAG pPropBag, LPERRORLOG /*pErrorLog*/ )
+{
+ mnVersion = GetVersionConnected();
+
+ IPropertyBag2* pPropBag2;
+ HRESULT hr = pPropBag->QueryInterface( IID_IPropertyBag2, reinterpret_cast<void**>(&pPropBag2) );
+ //ATLASSERT( hr >= 0 );
+
+ if( !SUCCEEDED( hr ) )
+ return hr;
+
+ unsigned long aNum;
+ hr = pPropBag2->CountProperties( &aNum );
+ //ATLASSERT( hr >= 0 );
+ if( !SUCCEEDED( hr ) )
+ return hr;
+
+ PROPBAG2* aPropNames = new PROPBAG2[aNum];
+ unsigned long aReaded;
+
+ hr = pPropBag2->GetPropertyInfo( 0,
+ aNum,
+ aPropNames,
+ &aReaded );
+ //ATLASSERT( hr >= 0 );
+ if( !SUCCEEDED( hr ) )
+ {
+ delete[] aPropNames;
+ return hr;
+ }
+
+ CComVariant* aVal = new CComVariant[aNum];
+ HRESULT* hvs = new HRESULT[aNum];
+ hr = pPropBag2->Read( aNum,
+ aPropNames,
+ nullptr,
+ aVal,
+ hvs );
+ //ATLASSERT( hr >= 0 );
+ if( !SUCCEEDED( hr ) )
+ {
+ delete[] hvs;
+ delete[] aVal;
+ delete[] aPropNames;
+ return hr;
+ }
+
+ for( unsigned long ind = 0; ind < aNum; ind++ )
+ {
+ // all information from the 'object' tag is in strings
+ if (aVal[ind].vt == VT_BSTR && !wcscmp(aPropNames[ind].pstrName, L"src"))
+ {
+ mCurFileUrl.AssignBSTR(aVal[ind].bstrVal);
+ }
+ else if( aVal[ind].vt == VT_BSTR
+ && !wcscmp(aPropNames[ind].pstrName, L"readonly"))
+ {
+ if (!wcscmp(aVal[ind].bstrVal, L"true"))
+ {
+ // the default value
+ mbViewOnly = TRUE;
+ }
+ else
+ {
+ mbViewOnly = FALSE;
+ }
+ }
+ }
+
+ delete[] hvs;
+ delete[] aVal;
+ delete[] aPropNames;
+
+ if( !mpDispFactory )
+ return hr;
+
+ mbReadyForActivation = FALSE;
+ hr = CBindStatusCallback<CSOActiveX>::Download(
+ this, &CSOActiveX::CallbackCreateXInputStream, mCurFileUrl, m_spClientSite, FALSE);
+ if (hr == MK_S_ASYNCHRONOUS)
+ hr = S_OK;
+
+ if ( !SUCCEEDED( hr ) )
+ {
+ // trigger initialization without stream
+ mbLoad = TRUE;
+
+ Invalidate();
+ UpdateWindow();
+ }
+
+ return hr;
+}
+
+HRESULT CSOActiveX::GetUnoStruct( OLECHAR const * sStructName, CComPtr<IDispatch>& pdispResult )
+{
+ CComVariant aComStruct( sStructName );
+ return GetIDispByFunc( mpDispFactory, L"Bridge_GetStruct", &aComStruct, 1, pdispResult );
+}
+
+HRESULT CSOActiveX::GetUrlStruct( OLECHAR const * sUrl, CComPtr<IDispatch>& pdispUrl )
+{
+ HRESULT hr = GetUnoStruct( L"com.sun.star.util.URL", pdispUrl );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ OLECHAR const * sURLMemberName = L"Complete";
+ DISPID nURLID;
+ hr = pdispUrl->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sURLMemberName), 1, LOCALE_USER_DEFAULT, &nURLID );
+ if( !SUCCEEDED( hr ) ) return hr;
+ CComVariant aComUrl( sUrl );
+ hr = CComDispatchDriver::PutProperty( pdispUrl, nURLID, &aComUrl );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ CComPtr<IDispatch> pdispTransformer;
+ CComVariant aServiceName( L"com.sun.star.util.URLTransformer" );
+ hr = GetIDispByFunc( mpDispFactory,
+ L"createInstance",
+ &aServiceName,
+ 1,
+ pdispTransformer );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ CComVariant dummyResult;
+ CComVariant aParam[2];
+ aParam[1].ppdispVal = &pdispUrl;
+ aParam[1].vt = VT_DISPATCH | VT_BYREF;
+ aParam[0] = CComVariant( L"file:///" );
+
+ hr = ExecuteFunc( pdispTransformer, L"parseSmart", aParam, 2, &dummyResult );
+ if( !SUCCEEDED( hr ) || dummyResult.vt != VT_BOOL || !dummyResult.boolVal ) return hr;
+
+ return S_OK;
+}
+
+HRESULT CSOActiveX::SetLayoutManagerProps()
+{
+ if ( !mpDispFrame )
+ return E_FAIL;
+
+ CComVariant pVarLayoutMgr;
+ OLECHAR const * sLMPropName = L"LayoutManager";
+ HRESULT hr = GetPropertiesFromIDisp( mpDispFrame, &sLMPropName, &pVarLayoutMgr, 1 );
+ if( pVarLayoutMgr.vt != VT_DISPATCH || pVarLayoutMgr.pdispVal == nullptr )
+ return E_FAIL;
+
+ CComPtr<IDispatch> pdispLM( pVarLayoutMgr.pdispVal );
+
+
+ if( !SUCCEEDED( hr ) || !pdispLM )
+ return E_FAIL;
+
+ OLECHAR const * sATName = L"AutomaticToolbars";
+ CComVariant pATProp;
+ pATProp.vt = VT_BOOL; pATProp.boolVal = VARIANT_FALSE ;
+ hr = PutPropertiesToIDisp( pdispLM, &sATName, &pATProp, 1 );
+
+ return hr;
+}
+
+HRESULT CSOActiveX::CreateFrameOldWay( HWND hwnd, int width, int height )
+{
+ if( !mpDispFactory )
+ return E_FAIL;
+
+ // create window handle holder
+ CComPtr< CComObject< SOComWindowPeer > > pPeerToSend = new CComObject<SOComWindowPeer>();
+ pPeerToSend->SetHWNDInternally( hwnd );
+ CComQIPtr< IDispatch, &IID_IDispatch > pIDispToSend( pPeerToSend );
+
+ // create rectangle structure
+ CComPtr<IDispatch> pdispRectangle;
+ HRESULT hr = GetUnoStruct( L"com.sun.star.awt.Rectangle", pdispRectangle );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ OLECHAR const * sRectMemberNames[4] = { L"X",
+ L"Y",
+ L"Width",
+ L"Height" };
+ CComVariant pRectVariant[4];
+ pRectVariant[0] = pRectVariant[1] = pRectVariant[2] = pRectVariant[3] = CComVariant( 0 );
+
+ hr = PutPropertiesToIDisp( pdispRectangle, sRectMemberNames, pRectVariant, 4 );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // create WindowDescriptor structure
+ CComPtr<IDispatch> pdispWinDescr;
+ hr = GetUnoStruct( L"com.sun.star.awt.WindowDescriptor", pdispWinDescr );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // fill in descriptor with info
+ OLECHAR const * sDescriptorMemberNames[6] = { L"Type",
+ L"WindowServiceName",
+ L"ParentIndex",
+ L"Parent",
+ L"Bounds",
+ L"WindowAttributes" };
+ CComVariant pDescriptorVar[6];
+ pDescriptorVar[0] = CComVariant( 0 );
+ pDescriptorVar[1] = CComVariant( L"workwindow" );
+ pDescriptorVar[2] = CComVariant( 1 );
+ pDescriptorVar[3] = CComVariant( pIDispToSend );
+ pDescriptorVar[4] = CComVariant( pdispRectangle );
+ pDescriptorVar[5] = CComVariant( 33 );
+ hr = PutPropertiesToIDisp( pdispWinDescr, sDescriptorMemberNames, pDescriptorVar, 6 );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // create XToolkit instance
+ CComPtr<IDispatch> pdispToolkit;
+ CComVariant aServiceName( L"com.sun.star.awt.Toolkit" );
+ hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, pdispToolkit );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // create window with toolkit
+ CComVariant aWinDescr( pdispWinDescr );
+ hr = GetIDispByFunc( pdispToolkit, L"createWindow", &aWinDescr, 1, mpDispWin );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // create frame
+ aServiceName = CComVariant( L"com.sun.star.frame.Task" );
+ hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, mpDispFrame );
+ if( !SUCCEEDED( hr ) || !mpDispFrame )
+ {
+ // the interface com.sun.star.frame.Task is removed in 6.1
+ // but the interface com.sun.star.frame.Frame has some bugs in 6.0
+ aServiceName = CComVariant( L"com.sun.star.frame.Frame" );
+ hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, mpDispFrame );
+ if( !SUCCEEDED( hr ) ) return hr;
+ }
+
+ // initialize frame
+ CComVariant dummyResult;
+ CComVariant aDispWin( mpDispWin );
+ hr = ExecuteFunc( mpDispFrame, L"initialize", &aDispWin, 1, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // set some properties to the layout manager, ignore errors for now
+ SetLayoutManagerProps();
+
+ // create desktop
+ CComPtr<IDispatch> pdispDesktop;
+ aServiceName = CComVariant( L"com.sun.star.frame.Desktop" );
+ hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, pdispDesktop );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // create tree of frames
+ CComPtr<IDispatch> pdispChildren;
+ hr = GetIDispByFunc( pdispDesktop, L"getFrames", nullptr, 0, pdispChildren );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // insert new frame into desktop hierarchy
+ CComVariant aDispFrame( mpDispFrame );
+ hr = ExecuteFunc( pdispChildren, L"append", &aDispFrame, 1, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // initialize window
+ CComVariant aTransparent( long(0xFFFFFFFF) );
+ hr = ExecuteFunc( mpDispWin, L"setBackground", &aTransparent, 1, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ CComVariant aTrue( TRUE );
+ hr = ExecuteFunc( mpDispWin, L"setVisible", &aTrue, 1, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ CComVariant aPosArgs[5];
+ aPosArgs[4] = CComVariant( 0 );
+ aPosArgs[3] = CComVariant( 0 );
+ aPosArgs[2] = CComVariant( width );
+ aPosArgs[1] = CComVariant( height );
+ aPosArgs[0] = CComVariant( 12 );
+ hr = ExecuteFunc( mpDispWin, L"setPosSize", aPosArgs, 5, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // create frame locker if there is such service
+ aServiceName = CComVariant( L"com.sun.star.embed.InstanceLocker" );
+ hr = GetIDispByFunc( mpDispFactory, L"createInstance", &aServiceName, 1, mpInstanceLocker );
+ if( SUCCEEDED( hr ) && mpInstanceLocker )
+ {
+ SAFEARRAY* pInitVals = SafeArrayCreateVector(VT_VARIANT, 0, 3);
+
+ // the first sequence element
+ LONG nInitInd = 0;
+ CComVariant pFrameVariant( mpDispFrame );
+ SafeArrayPutElement( pInitVals, &nInitInd, &pFrameVariant );
+
+ // the second sequence element
+ nInitInd = 1;
+ CComVariant pStrArr( 1 );
+ SafeArrayPutElement( pInitVals, &nInitInd, &pStrArr );
+
+ // the third sequence element
+ nInitInd = 2;
+ CComPtr<IDispatch> pdispValueObj;
+ hr = GetIDispByFunc( mpDispFactory, L"Bridge_GetValueObject", nullptr, 0, pdispValueObj );
+ if( !SUCCEEDED( hr ) || !pdispValueObj ) return hr;
+
+ CComVariant aValueArgs[2];
+ aValueArgs[1] = CComVariant( L"com.sun.star.embed.XActionsApproval" );
+ CComPtr< CComObject< SOActionsApproval > > pApproval( new CComObject<SOActionsApproval>() );
+ aValueArgs[0] = CComVariant ( pApproval );
+
+ hr = ExecuteFunc( pdispValueObj, L"Set", aValueArgs, 2, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ CComVariant aValueObj( pdispValueObj );
+ SafeArrayPutElement( pInitVals, &nInitInd, &aValueObj );
+
+ // execute initialize()
+ CComVariant aVarInitVals;
+ aVarInitVals.vt = VT_ARRAY | VT_VARIANT; aVarInitVals.parray = pInitVals;
+ hr = ExecuteFunc( mpInstanceLocker, L"initialize", &aVarInitVals, 1, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+ }
+
+ return S_OK;
+}
+
+HRESULT CSOActiveX::CallLoadComponentFromURL1PBool( OLECHAR const * sUrl, OLECHAR const * sArgName, BOOL sArgVal )
+{
+ SAFEARRAY* pPropVals = SafeArrayCreateVector(VT_DISPATCH, 0, 1);
+ LONG ix = 0;
+ CComPtr<IDispatch> pdispPropVal;
+ HRESULT hr = GetUnoStruct( L"com.sun.star.beans.PropertyValue", pdispPropVal );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ OLECHAR const * sPropMemberNames[2] = { L"Name", L"Value" };
+ CComVariant pPropVar[2];
+ pPropVar[0] = CComVariant( sArgName );
+ pPropVar[1].vt = VT_BOOL; pPropVar[1].boolVal = sArgVal ? VARIANT_TRUE : VARIANT_FALSE ;
+ hr = PutPropertiesToIDisp( pdispPropVal, sPropMemberNames, pPropVar, 2 );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ SafeArrayPutElement( pPropVals, &ix, pdispPropVal );
+
+ CComVariant aDispArgs[4];
+ aDispArgs[3] = CComVariant( sUrl );
+ aDispArgs[2] = CComVariant( L"_self" );
+ aDispArgs[1] = CComVariant( 0 );
+ // aDispArgs[0] = CComVariant( pPropVals ); such constructor is not defined ??!
+ aDispArgs[0].vt = VT_ARRAY | VT_DISPATCH; aDispArgs[0].parray = pPropVals;
+
+ CComVariant dummyResult;
+ hr = ExecuteFunc( mpDispFrame, L"loadComponentFromURL", aDispArgs, 4, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ return S_OK;
+}
+
+HRESULT CSOActiveX::CallDispatchMethod( OLECHAR const * sUrl,
+ CComVariant* aArgNames,
+ CComVariant* aArgVals,
+ unsigned int count )
+{
+ CComPtr<IDispatch> pdispURL;
+ HRESULT hr = GetUrlStruct( sUrl, pdispURL );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ CComPtr<IDispatch> pdispXDispatch;
+ CComVariant aArgs[3];
+ aArgs[2] = CComVariant( pdispURL );
+ aArgs[1] = CComVariant( L"" );
+ aArgs[0] = CComVariant( int(0) );
+ hr = GetIDispByFunc( mpDispFrame,
+ L"queryDispatch",
+ aArgs,
+ 3,
+ pdispXDispatch );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ SAFEARRAY* pPropVals = SafeArrayCreateVector(VT_DISPATCH, 0, count);
+ for( LONG ix = 0; ix < static_cast<LONG>(count); ix ++ )
+ {
+ CComPtr<IDispatch> pdispPropVal;
+ hr = GetUnoStruct( L"com.sun.star.beans.PropertyValue", pdispPropVal );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ OLECHAR const * sPropMemberNames[2] = { L"Name", L"Value" };
+ CComVariant pPropVar[2];
+ pPropVar[0] = aArgNames[ix];
+ pPropVar[1] = aArgVals[ix];
+ hr = PutPropertiesToIDisp( pdispPropVal, sPropMemberNames, pPropVar, 2 );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ SafeArrayPutElement( pPropVals, &ix, pdispPropVal );
+ }
+
+ CComVariant aDispArgs[2];
+ aDispArgs[1] = CComVariant( pdispURL );
+ // aDispArgs[0] = CComVariant( pPropVals ); such constructor is not defined ??!
+ aDispArgs[0].vt = VT_ARRAY | VT_DISPATCH; aDispArgs[0].parray = pPropVals;
+
+ CComVariant dummyResult;
+ hr = ExecuteFunc( pdispXDispatch, L"dispatch", aDispArgs, 2, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ return S_OK;
+}
+
+void CSOActiveX::CallbackCreateXInputStream( CBindStatusCallback<CSOActiveX>* /*pbsc*/, BYTE* pBytes, DWORD dwSize )
+{
+ if ( mbReadyForActivation )
+ return;
+
+ bool bSuccess = false;
+ bool bFinishDownload = false;
+ if ( !pBytes )
+ {
+ // means the download is finished, dwSize contains hresult
+ bFinishDownload = true;
+ if ( SUCCEEDED( dwSize ) )
+ bSuccess = true;
+ }
+ else
+ {
+ HRESULT hr = S_OK;
+
+ if ( !mpDispTempFile )
+ {
+ CComVariant aServiceName( L"com.sun.star.io.TempFile" );
+ hr = GetIDispByFunc( mpDispFactory,
+ L"createInstance",
+ &aServiceName,
+ 1,
+ mpDispTempFile );
+ }
+
+ if( SUCCEEDED( hr ) && mpDispTempFile )
+ {
+ SAFEARRAY* pDataArray = SafeArrayCreateVector(VT_I1, 0, dwSize);
+
+ if ( pDataArray )
+ {
+ hr = SafeArrayLock( pDataArray );
+ if ( SUCCEEDED( hr ) )
+ {
+ for( DWORD ix = 0; ix < dwSize; ix++ )
+ static_cast<BYTE*>(pDataArray->pvData)[ix] = pBytes[ix];
+ hr = SafeArrayUnlock( pDataArray );
+ if ( SUCCEEDED( hr ) )
+ {
+ CComVariant aArgs[1];
+ aArgs[0].vt = VT_ARRAY | VT_I1; aArgs[0].parray = pDataArray;
+ CComVariant dummyResult;
+ hr = ExecuteFunc( mpDispTempFile, L"writeBytes", aArgs, 1, &dummyResult );
+ if( SUCCEEDED( hr ) )
+ bSuccess = true;
+ }
+ }
+ }
+ }
+ }
+
+ if ( !bSuccess )
+ {
+ // the download failed, let StarOffice download
+ bFinishDownload = true;
+ mpDispTempFile = CComPtr< IDispatch >();
+ }
+
+ if ( bFinishDownload )
+ {
+ // trigger the loading now
+ mbLoad = TRUE;
+ mbReadyForActivation = TRUE;
+
+ Invalidate();
+ UpdateWindow();
+ }
+}
+
+HRESULT CSOActiveX::LoadURLToFrame( )
+{
+ CComVariant aArgNames[4] = { L"ReadOnly", L"ViewOnly", L"AsTemplate", L"InputStream" };
+ CComVariant aArgVals[4];
+ unsigned int nCount = 3; // the 4-th argument is used only if the stream can be retrieved
+
+ aArgVals[0].vt = VT_BOOL; aArgVals[0].boolVal = mbViewOnly ? VARIANT_TRUE : VARIANT_FALSE;
+ aArgVals[1].vt = VT_BOOL; aArgVals[1].boolVal = mbViewOnly ? VARIANT_TRUE : VARIANT_FALSE;
+ aArgVals[2].vt = VT_BOOL; aArgVals[2].boolVal = VARIANT_FALSE;
+
+ if ( mpDispTempFile )
+ {
+ aArgVals[3] = CComVariant( mpDispTempFile );
+ nCount = 4;
+ }
+
+ HRESULT hr = CallDispatchMethod( mCurFileUrl, aArgNames, aArgVals, nCount );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ // try to get the model and set the presentation specific property, the setting will fail for other document formats
+ CComPtr<IDispatch> pdispController;
+ hr = GetIDispByFunc( mpDispFrame, L"getController", nullptr, 0, pdispController );
+ if ( SUCCEEDED( hr ) && pdispController )
+ {
+ CComPtr<IDispatch> pdispModel;
+ hr = GetIDispByFunc( pdispController, L"getModel", nullptr, 0, pdispModel );
+ if ( SUCCEEDED( hr ) && pdispModel )
+ {
+ CComPtr<IDispatch> pdispPres;
+ hr = GetIDispByFunc( pdispModel, L"getPresentation", nullptr, 0, pdispPres );
+ if ( SUCCEEDED( hr ) && pdispPres )
+ {
+ // this is a presentation
+ // let the slide show be shown in the document window
+ OLECHAR const * pPropName = L"IsFullScreen";
+ CComVariant pPresProp;
+ pPresProp.vt = VT_BOOL; pPresProp.boolVal = VARIANT_FALSE ;
+ hr = PutPropertiesToIDisp( pdispPres, &pPropName, &pPresProp, 1 );
+
+ // start the slide show
+ if ( SUCCEEDED( hr ) )
+ {
+ CComVariant dummyResult;
+ ExecuteFunc( pdispPres, L"Start", nullptr, 0, &dummyResult );
+ }
+ }
+ }
+ }
+
+ // create dispatch interceptor
+ mpDispatchInterceptor = new CComObject< SODispatchInterceptor >();
+ mpDispatchInterceptor->AddRef();
+ mpDispatchInterceptor->SetParent( this );
+ CComQIPtr< IDispatch, &IID_IDispatch > pIDispDispInter( mpDispatchInterceptor );
+
+ // register dispatch interceptor in the frame
+ CComVariant aDispVariant( pIDispDispInter );
+ CComVariant dummyResult;
+ hr = ExecuteFunc( mpDispFrame,
+ L"registerDispatchProviderInterceptor",
+ &aDispVariant,
+ 1,
+ &dummyResult );
+
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ return S_OK;
+}
+
+SOVersion CSOActiveX::GetVersionConnected()
+{
+ SOVersion bResult = SO_NOT_DETECTED;
+ if( mpDispFactory )
+ {
+ // create ConfigurationProvider instance
+ CComPtr<IDispatch> pdispConfProv;
+ CComVariant aServiceName( L"com.sun.star.configuration.ConfigurationProvider" );
+ HRESULT hr = GetIDispByFunc( mpDispFactory,
+ L"createInstance",
+ &aServiceName,
+ 1,
+ pdispConfProv );
+
+ if( SUCCEEDED( hr ) && pdispConfProv )
+ {
+ CComPtr<IDispatch> pdispConfAccess;
+
+ SAFEARRAY* pInitParams = SafeArrayCreateVector( VT_VARIANT, 0, 1 );
+
+ if( pInitParams )
+ {
+ LONG ix = 0;
+ CComVariant aConfPath( L"org.openoffice.Setup" );
+ SafeArrayPutElement( pInitParams, &ix, &aConfPath );
+
+ CComVariant aArgs[2];
+ aArgs[1] = CComVariant( L"com.sun.star.configuration.ConfigurationAccess" );
+ aArgs[0].vt = VT_ARRAY | VT_VARIANT; aArgs[0].parray = pInitParams;
+
+ hr = GetIDispByFunc( pdispConfProv,
+ L"createInstanceWithArguments",
+ aArgs,
+ 2,
+ pdispConfAccess );
+
+ if( SUCCEEDED( hr ) && pdispConfAccess )
+ {
+ CComVariant aOfficeName;
+
+ CComVariant aProductName( L"Product/ooName" );
+ hr = ExecuteFunc( pdispConfAccess,
+ L"getByHierarchicalName",
+ &aProductName,
+ 1,
+ &aOfficeName );
+
+ if( SUCCEEDED( hr ) && aOfficeName.vt == VT_BSTR )
+ {
+ CComVariant aOfficeVersion;
+
+ CComVariant aProductVersion( L"Product/ooSetupVersion" );
+ hr = ExecuteFunc( pdispConfAccess,
+ L"getByHierarchicalName",
+ &aProductVersion,
+ 1,
+ &aOfficeVersion );
+
+ if( SUCCEEDED( hr ) && aOfficeVersion.vt == VT_BSTR )
+ {
+ if (!wcscmp(aOfficeName.bstrVal, L"StarOffice"))
+ {
+ if (!wcsncmp(aOfficeVersion.bstrVal, L"6.1", 3))
+ bResult = SO_61;
+ else if (!wcsncmp(aOfficeVersion.bstrVal, L"6.0", 3))
+ bResult = SO_60;
+ else if (!wcsncmp(aOfficeVersion.bstrVal, L"5.2", 3))
+ bResult = SO_52;
+ else
+ bResult = SO_UNKNOWN;
+ }
+ else // OpenOffice
+ {
+ if (!wcsncmp(aOfficeVersion.bstrVal, L"1.1", 3))
+ bResult = OO_11;
+ else if (!wcsncmp(aOfficeVersion.bstrVal, L"1.0", 3))
+ bResult = OO_10;
+ else
+ bResult = OO_UNKNOWN;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bResult;
+}
+
+namespace {
+
+class LockingGuard
+{
+ bool& mbLocked;
+public:
+ explicit LockingGuard( bool& bLocked )
+ : mbLocked( bLocked )
+ {
+ mbLocked = true;
+ }
+
+ ~LockingGuard()
+ {
+ mbLocked = false;
+ }
+};
+
+}
+
+HRESULT CSOActiveX::OnDrawAdvanced( ATL_DRAWINFO& di )
+{
+ // This method is called only in main thread, no need to lock it
+
+ // Get read of reentrance problems
+ if ( mbDrawLocked )
+ return S_OK;
+ LockingGuard aGuard( mbDrawLocked );
+
+ if( m_spInPlaceSite && mCurFileUrl && mbReadyForActivation )
+ {
+ HWND hwnd;
+ HRESULT hr = m_spInPlaceSite->GetWindow( &hwnd );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ if( mParentWin != hwnd || !mOffWin )
+ {
+ if( mpDispFrame )
+ {
+ CComVariant dummyResult;
+ CComVariant aPropVar;
+ aPropVar.vt = VT_BOOL; aPropVar.boolVal = VARIANT_FALSE;
+ (void) ExecuteFunc( mpDispFrame, L"close", &aPropVar, 1, &dummyResult );
+ mpDispFrame = CComPtr<IDispatch>();
+ }
+
+ mParentWin = hwnd;
+ mOffWin = CreateWindowW(
+ STAROFFICE_WINDOWCLASS,
+ L"OfficeContainer",
+ WS_CHILD | WS_CLIPCHILDREN | WS_BORDER,
+ di.prcBounds->left,
+ di.prcBounds->top,
+ di.prcBounds->right - di.prcBounds->left,
+ di.prcBounds->bottom - di.prcBounds->top,
+ mParentWin,
+ nullptr,
+ nullptr,
+ nullptr );
+
+ ::ShowWindow( mOffWin, SW_SHOW );
+ }
+ else
+ {
+ RECT aRect;
+ ::GetWindowRect( mOffWin, &aRect );
+
+ if( aRect.left != di.prcBounds->left || aRect.top != di.prcBounds->top
+ || aRect.right != di.prcBounds->right || aRect.bottom != di.prcBounds->bottom )
+ {
+ // on this state the office window should exist already
+ ::SetWindowPos( mOffWin,
+ HWND_TOP,
+ di.prcBounds->left,
+ di.prcBounds->top,
+ di.prcBounds->right - di.prcBounds->left,
+ di.prcBounds->bottom - di.prcBounds->top,
+ SWP_NOZORDER );
+
+ CComVariant aPosArgs[5];
+ aPosArgs[4] = CComVariant( 0 );
+ aPosArgs[3] = CComVariant( 0 );
+ aPosArgs[2] = CComVariant( int(di.prcBounds->right - di.prcBounds->left) );
+ aPosArgs[1] = CComVariant( int(di.prcBounds->bottom - di.prcBounds->top) );
+ aPosArgs[0] = CComVariant( 12 );
+ CComVariant dummyResult;
+ hr = ExecuteFunc( mpDispWin, L"setPosSize", aPosArgs, 5, &dummyResult );
+ if( !SUCCEEDED( hr ) ) return hr;
+ }
+ }
+
+ if (mnVersion == SO_NOT_DETECTED)
+ {
+ OutputError_Impl( mOffWin, CS_E_INVALID_VERSION );
+ return E_FAIL;
+ }
+
+ if( ! mpDispFrame )
+ {
+ hr = CreateFrameOldWay( mOffWin,
+ di.prcBounds->right - di.prcBounds->left,
+ di.prcBounds->bottom - di.prcBounds->top );
+
+ if( !SUCCEEDED( hr ) )
+ {
+ // if the frame can not be opened do not try any more
+ mbReadyForActivation = FALSE;
+ OutputError_Impl( mOffWin, STG_E_ABNORMALAPIEXIT );
+ return hr;
+ }
+ }
+
+ if( mbLoad )
+ {
+ hr = LoadURLToFrame();
+ mbLoad = FALSE;
+
+ if( !SUCCEEDED( hr ) )
+ {
+ // if the document can not be opened do not try any more
+ mbReadyForActivation = FALSE;
+
+ OutputError_Impl( mOffWin, STG_E_ABNORMALAPIEXIT );
+
+ return hr;
+ }
+ }
+ }
+ else
+ {
+ // activate the fallback
+ CComControl<CSOActiveX>::OnDrawAdvanced( di );
+ }
+
+ return S_OK;
+}
+
+HRESULT CSOActiveX::OnDraw( ATL_DRAWINFO& di )
+{
+ // fallback that is activated by the parent class
+ if ( di.hdcDraw )
+ FillRect( di.hdcDraw, reinterpret_cast<RECT const *>(di.prcBounds), reinterpret_cast<HBRUSH>(COLOR_BACKGROUND) );
+
+ return S_OK;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP CSOActiveX::SetClientSite( IOleClientSite* aClientSite )
+{
+ HRESULT hr = IOleObjectImpl<CSOActiveX>::SetClientSite( aClientSite );
+
+ if( !aClientSite )
+ {
+ //ATLASSERT( mWebBrowser2 );
+ if( mWebBrowser2 )
+ AtlUnadvise( mWebBrowser2, DIID_DWebBrowserEvents2, mCookie );
+ return hr;
+ }
+
+ CComPtr<IOleContainer> aContainer;
+ m_spClientSite->GetContainer( &aContainer );
+// ATLASSERT( aContainer );
+
+ if( SUCCEEDED( hr ) && aContainer )
+ {
+ CComQIPtr<IServiceProvider, &IID_IServiceProvider> aServiceProvider( aContainer );
+ //ATLASSERT( aServiceProvider );
+
+ if( aServiceProvider )
+ {
+ aServiceProvider->QueryService( SID_SInternetExplorer,
+ IID_IWebBrowser,
+ reinterpret_cast<void**>(&mWebBrowser2) );
+// ATLASSERT( mWebBrowser2 );
+ if( mWebBrowser2 )
+ AtlAdvise( mWebBrowser2, GetUnknown(), DIID_DWebBrowserEvents2, &mCookie );
+ }
+ }
+
+ return hr;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP CSOActiveX::Invoke(DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ DISPPARAMS* pDispParams,
+ VARIANT* pvarResult,
+ EXCEPINFO* pExcepInfo,
+ UINT* puArgErr)
+{
+ if (riid != IID_NULL)
+ return DISP_E_UNKNOWNINTERFACE;
+
+ if (!pDispParams)
+ return DISP_E_PARAMNOTOPTIONAL;
+
+ if ( dispidMember == DISPID_ONQUIT )
+ Cleanup();
+
+ IDispatchImpl<ISOActiveX, &IID_ISOActiveX,
+ &LIBID_SO_ACTIVEXLib>::Invoke(
+ dispidMember, riid, lcid, wFlags, pDispParams,
+ pvarResult, pExcepInfo, puArgErr);
+
+ return S_OK;
+}
+
+HRESULT CSOActiveX::GetURL( const OLECHAR* url,
+ const OLECHAR* target )
+{
+ CComVariant aEmpty1, aEmpty2, aEmpty3;
+ CComVariant aUrl( url );
+ CComVariant aTarget;
+ if ( target )
+ aTarget = CComVariant( target );
+
+ return mWebBrowser2->Navigate2( &aUrl,
+ &aEmpty1,
+ &aTarget,
+ &aEmpty2,
+ &aEmpty3 );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/SOActiveX.h b/extensions/source/activex/SOActiveX.h
new file mode 100644
index 000000000..37d983ae9
--- /dev/null
+++ b/extensions/source/activex/SOActiveX.h
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// SOActiveX.h : Declaration of the CSOActiveX
+
+#pragma once
+
+#include "resource.h"
+
+#include <ExDispID.h>
+#include <ExDisp.h>
+#include <shlguid.h>
+
+#include <atlctl.h>
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+#include <so_activex.h>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+class SODispatchInterceptor;
+
+enum SOVersion {
+ SO_NOT_DETECTED = 0,
+ SO_52,
+ SO_60,
+ SO_61,
+ SO_UNKNOWN,
+ OO_10,
+ OO_11,
+ OO_UNKNOWN
+};
+
+
+// CSOActiveX
+class ATL_NO_VTABLE CSOActiveX :
+ public CComObjectRootEx<CComSingleThreadModel>,
+ public IDispatchImpl<ISOActiveX, &IID_ISOActiveX, &LIBID_SO_ACTIVEXLib>,
+ public CComControl<CSOActiveX>,
+ public IPersistStreamInitImpl<CSOActiveX>,
+ public IOleControlImpl<CSOActiveX>,
+ public IOleObjectImpl<CSOActiveX>,
+ public IOleInPlaceActiveObjectImpl<CSOActiveX>,
+ public IViewObjectExImpl<CSOActiveX>,
+ public IOleInPlaceObjectWindowlessImpl<CSOActiveX>,
+// public IConnectionPointContainerImpl<CSOActiveX>,
+ public CComCoClass<CSOActiveX, &CLSID_SOActiveX>,
+// public CProxy_ItryPluginEvents< CSOActiveX >,
+ public IPersistPropertyBagImpl< CSOActiveX >,
+ public IProvideClassInfo2Impl< &CLSID_SOActiveX,
+ &DIID__ISOActiveXEvents,
+ &LIBID_SO_ACTIVEXLib >,
+ public IObjectSafetyImpl< CSOActiveX,
+ INTERFACESAFE_FOR_UNTRUSTED_DATA >
+{
+protected:
+ CComPtr<IWebBrowser2> mWebBrowser2;
+ DWORD mCookie;
+
+ CComPtr<IDispatch> mpDispFactory;
+ CComPtr<IDispatch> mpDispFrame;
+ CComPtr<IDispatch> mpInstanceLocker;
+ CComPtr<IDispatch> mpDispWin;
+ CComBSTR mCurFileUrl;
+ BOOL mbLoad;
+ BOOL mbViewOnly;
+ WNDCLASSW mPWinClass;
+ HWND mParentWin;
+ HWND mOffWin;
+
+ SODispatchInterceptor* mpDispatchInterceptor;
+ SOVersion mnVersion;
+
+ BOOL mbReadyForActivation;
+ CComPtr<IDispatch> mpDispTempFile;
+
+ bool mbDrawLocked;
+
+public:
+ CSOActiveX();
+ ~CSOActiveX() override;
+
+DECLARE_REGISTRY_RESOURCEID(IDR_SOACTIVEX)
+
+DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+BEGIN_COM_MAP(CSOActiveX)
+ COM_INTERFACE_ENTRY(ISOActiveX)
+ COM_INTERFACE_ENTRY(IDispatch)
+ COM_INTERFACE_ENTRY(IViewObjectEx)
+ COM_INTERFACE_ENTRY(IViewObject2)
+ COM_INTERFACE_ENTRY(IViewObject)
+ COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
+ COM_INTERFACE_ENTRY(IOleInPlaceObject)
+ COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless)
+ COM_INTERFACE_ENTRY(IOleInPlaceActiveObject)
+ COM_INTERFACE_ENTRY(IOleControl)
+ COM_INTERFACE_ENTRY(IOleObject)
+ COM_INTERFACE_ENTRY(IPersistStreamInit)
+ COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)
+// COM_INTERFACE_ENTRY(IConnectionPointContainer)
+ COM_INTERFACE_ENTRY(IProvideClassInfo)
+ COM_INTERFACE_ENTRY(IProvideClassInfo2)
+ COM_INTERFACE_ENTRY(IPersistPropertyBag)
+ COM_INTERFACE_ENTRY(IObjectSafety)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#endif
+END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winvalid-offsetof"
+ // offset of on non-standard-layout type '_PropMapClass' (aka 'CSOActiveX'),
+ // expanded from macro 'PROP_DATA_ENTRY'
+#endif
+BEGIN_PROP_MAP(CSOActiveX)
+ PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
+ PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
+ // Example entries
+ // PROP_ENTRY("Property Description", dispid, clsid)
+ // PROP_PAGE(CLSID_StockColorPage)
+END_PROP_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+BEGIN_CONNECTION_POINT_MAP(CSOActiveX)
+END_CONNECTION_POINT_MAP()
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#endif
+BEGIN_MSG_MAP(CSOActiveX)
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+ CHAIN_MSG_MAP(CComControl<CSOActiveX>)
+ DEFAULT_REFLECTION_HANDLER()
+END_MSG_MAP()
+// Handler prototypes:
+// LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+// LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
+// LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);
+
+
+
+// IViewObjectEx
+ static DECLARE_VIEW_STATUS(VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE)
+
+// ISOActiveX
+public:
+
+ STDMETHOD(SetClientSite)( IOleClientSite* aClientSite ) override;
+ STDMETHOD(Invoke)( DISPID dispidMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ DISPPARAMS* pDispParams,
+ VARIANT* pvarResult,
+ EXCEPINFO* pExcepInfo,
+ UINT* puArgErr) override;
+ STDMETHOD(Load) ( LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog ) override;
+ STDMETHOD(Load) ( LPSTREAM pStm ) override;
+ STDMETHOD(InitNew) () override;
+ HRESULT OnDrawAdvanced(ATL_DRAWINFO& di) override;
+ HRESULT OnDraw(ATL_DRAWINFO& di) override;
+
+ HRESULT SetLayoutManagerProps();
+ HRESULT CreateFrameOldWay( HWND hwnd, int width, int height );
+ HRESULT GetUnoStruct( OLECHAR const * sStructName, CComPtr<IDispatch>& pdispResult );
+ HRESULT LoadURLToFrame();
+ HRESULT CallDispatchMethod( OLECHAR const * sUrl, CComVariant* sArgNames, CComVariant* sArgVal, unsigned int count );
+ HRESULT CallLoadComponentFromURL1PBool( OLECHAR const * sUrl, OLECHAR const * sArgName, BOOL sArgVal );
+ HRESULT GetUrlStruct( OLECHAR const * sUrl, CComPtr<IDispatch>& pdispUrl );
+ HRESULT Cleanup();
+ HRESULT TerminateOffice();
+ HRESULT GetURL( const OLECHAR* url,
+ const OLECHAR* target );
+
+ void CallbackCreateXInputStream( CBindStatusCallback<CSOActiveX>* pbsc, BYTE* pBytes, DWORD dwSize );
+
+
+ SOVersion GetVersionConnected();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/SOActiveX.rgs b/extensions/source/activex/SOActiveX.rgs
new file mode 100644
index 000000000..d3814df3b
--- /dev/null
+++ b/extensions/source/activex/SOActiveX.rgs
@@ -0,0 +1,33 @@
+HKCR
+{
+ so_activex.SOActiveX.1 = s 'SOActiveX Class'
+ {
+ CLSID = s '{67F2A879-82D5-4A6D-8CC5-FFB3C114B69D}'
+ }
+ so_activex.SOActiveX = s 'SOActiveX Class'
+ {
+ CLSID = s '{67F2A879-82D5-4A6D-8CC5-FFB3C114B69D}'
+ CurVer = s 'so_activex.SOActiveX.1'
+ }
+ NoRemove CLSID
+ {
+ ForceRemove {67F2A879-82D5-4A6D-8CC5-FFB3C114B69D} = s 'SOActiveX Class'
+ {
+ ProgID = s 'so_activex.SOActiveX.1'
+ VersionIndependentProgID = s 'so_activex.SOActiveX'
+ ForceRemove 'Programmable'
+ InprocServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'Apartment'
+ }
+ ForceRemove 'Control'
+ ForceRemove 'ToolboxBitmap32' = s '%MODULE%, 101'
+ 'MiscStatus' = s '0'
+ {
+ '1' = s '131473'
+ }
+ 'TypeLib' = s '{61FA3F13-8061-4796-B055-3697ED28CB38}'
+ 'Version' = s '1.0'
+ }
+ }
+}
diff --git a/extensions/source/activex/SOComWindowPeer.cxx b/extensions/source/activex/SOComWindowPeer.cxx
new file mode 100644
index 000000000..0a556466d
--- /dev/null
+++ b/extensions/source/activex/SOComWindowPeer.cxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// SOComWindowPeer.cpp : Implementation of CHelpApp and DLL registration.
+
+#include <sal/config.h>
+
+#include <cstddef>
+
+#include "StdAfx2.h"
+#include "SOComWindowPeer.h"
+#include <sal/macros.h>
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+#include <so_activex.h>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP SOComWindowPeer::InterfaceSupportsErrorInfo(REFIID riid)
+{
+ static const IID* arr[] = {
+ &IID_ISOComWindowPeer,
+ };
+
+ for (std::size_t i = 0; i < SAL_N_ELEMENTS(arr); i++)
+ {
+#ifdef _MSC_VER
+ if (InlineIsEqualGUID(*arr[i], riid))
+#else
+ if (::ATL::InlineIsEqualGUID(*arr[i], riid))
+#endif
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/SOComWindowPeer.h b/extensions/source/activex/SOComWindowPeer.h
new file mode 100644
index 000000000..379ca767a
--- /dev/null
+++ b/extensions/source/activex/SOComWindowPeer.h
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// SOComWindowPeer.h: Definition of the SOComWindowPeer class
+
+#pragma once
+
+#ifdef _MSC_VER
+#pragma once
+#endif
+
+#include "resource.h"
+#include <ExDispID.h>
+#include <ExDisp.h>
+#include <shlguid.h>
+
+#include <atlctl.h>
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+#include <so_activex.h>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+// SOComWindowPeer
+
+class SOComWindowPeer :
+ public IDispatchImpl<ISOComWindowPeer, &IID_ISOComWindowPeer, &LIBID_SO_ACTIVEXLib>,
+ public ISupportErrorInfo,
+ public CComObjectRoot,
+ public CComCoClass<SOComWindowPeer,&CLSID_SOComWindowPeer>
+{
+ HWND m_hwnd;
+public:
+ SOComWindowPeer() : m_hwnd( nullptr ) {}
+ virtual ~SOComWindowPeer() { }
+
+BEGIN_COM_MAP(SOComWindowPeer)
+ COM_INTERFACE_ENTRY(IDispatch)
+ COM_INTERFACE_ENTRY(ISOComWindowPeer)
+ COM_INTERFACE_ENTRY(ISupportErrorInfo)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#endif
+END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+DECLARE_NOT_AGGREGATABLE(SOComWindowPeer)
+// Remove the comment from the line above if you don't want your object to
+// support aggregation.
+
+DECLARE_REGISTRY_RESOURCEID(IDR_SOCOMWINDOWPEER)
+
+// ISupportsErrorInfo
+ STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid) override;
+
+// ISOComWindowPeer
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE getWindowHandle(
+ /* [in] */ SAFEARRAY __RPC_FAR * /*procId*/,
+ /* [in] */ short /*s*/,
+ /* [retval][out] */ long __RPC_FAR *ret) override
+ {
+ *ret = HandleToLong( m_hwnd );
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE getToolkit(
+ /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *retVal) override
+ {
+ *retVal = nullptr;
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE setPointer(
+ /* [in] */ IDispatch __RPC_FAR* /*xPointer*/) override
+ {
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE setBackground(
+ /* [in] */ int /*nColor*/) override
+ {
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE invalidate(
+ /* [in] */ short /*__MIDL_0015*/) override
+ {
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE invalidateRect(
+ /* [in] */ IDispatch __RPC_FAR* /*aRect*/,
+ /* [in] */ short /*nFlags*/) override
+ {
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE dispose( void) override
+ {
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE addEventListener(
+ /* [in] */ IDispatch __RPC_FAR* /*xListener*/) override
+ {
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE removeEventListener(
+ /* [in] */ IDispatch __RPC_FAR* /*xListener*/) override
+ {
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Bridge_implementedInterfaces(
+ /* [retval][out] */ SAFEARRAY __RPC_FAR * __RPC_FAR *pVal) override
+ {
+ *pVal = SafeArrayCreateVector( VT_BSTR, 0, 2 );
+
+ if( !*pVal )
+ return E_FAIL;
+
+ LONG ix = 0;
+ CComBSTR aInterface( OLESTR( "com.sun.star.awt.XSystemDependentWindowPeer" ) );
+ SafeArrayPutElement( *pVal, &ix, aInterface );
+
+ ix = 1;
+ aInterface = CComBSTR( OLESTR( "com.sun.star.awt.XWindowPeer" ) );
+ SafeArrayPutElement( *pVal, &ix, aInterface );
+
+ return S_OK;
+ }
+
+ void SetHWNDInternally( HWND hwnd ) { m_hwnd = hwnd; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/SOComWindowPeer.rgs b/extensions/source/activex/SOComWindowPeer.rgs
new file mode 100644
index 000000000..42e985a31
--- /dev/null
+++ b/extensions/source/activex/SOComWindowPeer.rgs
@@ -0,0 +1,23 @@
+HKCR
+{
+ so_activex.SOComWindowPeer.1 = s 'SOComWindowPeer Class'
+ {
+ CLSID = s '{EE51BD3E-8BB6-4FB8-B319-F65B1BE3B21D}'
+ }
+ so_activex.SOComWindowPeer = s 'SOComWindowPeer Class'
+ {
+ CLSID = s '{EE51BD3E-8BB6-4FB8-B319-F65B1BE3B21D}'
+ }
+ NoRemove CLSID
+ {
+ ForceRemove {EE51BD3E-8BB6-4FB8-B319-F65B1BE3B21D} = s 'SOComWindowPeer Class'
+ {
+ ProgID = s 'so_activex.SOComWindowPeer.1'
+ VersionIndependentProgID = s 'so_activex.SOComWindowPeer'
+ InprocServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'both'
+ }
+ }
+ }
+}
diff --git a/extensions/source/activex/SODispatchInterceptor.cxx b/extensions/source/activex/SODispatchInterceptor.cxx
new file mode 100644
index 000000000..f40f62635
--- /dev/null
+++ b/extensions/source/activex/SODispatchInterceptor.cxx
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// SODispatchInterceptor.cpp : Implementation of CHelpApp and DLL registration.
+
+#include <sal/config.h>
+
+#include <cstddef>
+
+#include <stdio.h>
+#include "StdAfx2.h"
+#include "SOActiveX.h"
+#include "SODispatchInterceptor.h"
+#include "com_uno_helper.h"
+#include <sal/macros.h>
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+#include <so_activex.h>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP SODispatchInterceptor::InterfaceSupportsErrorInfo(REFIID riid)
+{
+ static const IID* arr[] =
+ {
+ &IID_ISODispatchInterceptor,
+ };
+
+ for (std::size_t i=0;i<SAL_N_ELEMENTS(arr);i++)
+ {
+#ifdef _MSC_VER
+ if (InlineIsEqualGUID(*arr[i],riid))
+#else
+ if (::ATL::InlineIsEqualGUID(*arr[i],riid))
+#endif
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+STDMETHODIMP SODispatchInterceptor::queryDispatch(IDispatch* aURL, BSTR aTargetFrameName,
+ long nSearchFlags, IDispatch** retVal)
+{
+ if ( !aURL || !retVal ) return E_FAIL;
+
+ CComVariant aTargetUrl;
+ OLECHAR const * sURLMemberName = L"Complete";
+ DISPID nURLID;
+ HRESULT hr = aURL->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sURLMemberName), 1, LOCALE_USER_DEFAULT, &nURLID );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ hr = CComDispatchDriver::GetProperty( aURL, nURLID, &aTargetUrl );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ if( aTargetUrl.vt != VT_BSTR ) return E_FAIL;
+
+ if (!wcsncmp(aTargetUrl.bstrVal, L".uno:OpenHyperlink", 18))
+ {
+ CComQIPtr< IDispatch, &IID_IDispatch > pIDisp( this );
+ if( pIDisp )
+ {
+ this->AddRef();
+ *retVal = pIDisp;
+ }
+ }
+ else
+ {
+ if( !m_xSlave )
+ {
+ *retVal = nullptr;
+ return S_OK;
+ }
+
+ CComVariant aResult;
+ CComVariant aArgs[3];
+ aArgs[0] = CComVariant( nSearchFlags );
+ aArgs[1] = CComVariant( aTargetFrameName );
+ aArgs[2] = CComVariant( aURL );
+
+ hr = ExecuteFunc( m_xSlave, L"queryDispatch", aArgs, 3, &aResult );
+ if( !SUCCEEDED( hr ) || aResult.vt != VT_DISPATCH || aResult.pdispVal == nullptr )
+ {
+ *retVal = nullptr;
+ return S_OK;
+ }
+
+ *retVal = aResult.pdispVal;
+
+ CComQIPtr< IUnknown, &IID_IUnknown > pIUnk( *retVal );
+ if( pIUnk )
+ (*retVal)->AddRef();
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP SODispatchInterceptor::queryDispatches(SAFEARRAY* aDescripts, SAFEARRAY** retVal)
+{
+ if ( !aDescripts || !retVal || SafeArrayGetDim( aDescripts ) != 1 )
+ return E_FAIL;
+
+ LONG nLB, nUB;
+
+ HRESULT hr = SafeArrayGetLBound( aDescripts, 1, &nLB );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ hr = SafeArrayGetUBound( aDescripts, 1, &nUB );
+ if( !SUCCEEDED( hr ) ) return hr;
+ if( nUB < nLB ) return E_FAIL;
+
+ *retVal = SafeArrayCreateVector( VT_DISPATCH, 0, nUB - nLB );
+
+ for ( LONG ind = nLB; ind <= nUB; ind ++ )
+ {
+ CComPtr<IDispatch> pElem;
+ SafeArrayGetElement( aDescripts, &ind, pElem );
+ if( pElem )
+ {
+ OLECHAR const * pMemberNames[3] = { L"FeatureURL", L"FrameName", L"SearchFlags" };
+ CComVariant pValues[3];
+ hr = GetPropertiesFromIDisp( pElem, pMemberNames, pValues, 3 );
+ if( !SUCCEEDED( hr ) ) return hr;
+ if( pValues[0].vt != VT_DISPATCH || pValues[0].pdispVal == nullptr
+ || pValues[1].vt != VT_BSTR || pValues[2].vt != VT_I4 )
+ return E_FAIL;
+
+ CComPtr<IDispatch> aRes;
+ hr = queryDispatch( pValues[0].pdispVal, pValues[1].bstrVal, pValues[2].lVal, &aRes );
+ SafeArrayPutElement( *retVal, &ind, aRes );
+ }
+ }
+
+ return S_OK;
+}
+
+
+STDMETHODIMP SODispatchInterceptor::dispatch(IDispatch* aURL, SAFEARRAY* aArgs)
+{
+ // get url from aURL
+ OLECHAR const * pUrlName = L"Complete";
+ CComVariant pValue;
+ HRESULT hr = GetPropertiesFromIDisp( aURL, &pUrlName, &pValue, 1 );
+ if( !SUCCEEDED( hr ) ) return hr;
+ if( pValue.vt != VT_BSTR || pValue.bstrVal == nullptr )
+ return E_FAIL;
+
+ if (!wcsncmp(pValue.bstrVal, L".uno:OpenHyperlink", 18))
+ {
+ LONG nLB = 0, nUB = 0;
+ // long nDim = SafeArrayGetDim( aArgs );
+
+ hr = SafeArrayGetLBound( aArgs, 1, &nLB );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ hr = SafeArrayGetUBound( aArgs, 1, &nUB );
+ if( !SUCCEEDED( hr ) ) return hr;
+ if( nUB < nLB ) return E_FAIL;
+
+ for ( LONG ind = nLB; ind <= nUB; ind ++ )
+ {
+ CComVariant pVarElem;
+ SafeArrayGetElement( aArgs, &ind, &pVarElem );
+ if( pVarElem.vt == VT_DISPATCH && pVarElem.pdispVal != nullptr )
+ {
+ OLECHAR const * pMemberNames[2] = { L"Name", L"Value" };
+ CComVariant pValues[2];
+ hr = GetPropertiesFromIDisp( pVarElem.pdispVal, pMemberNames, pValues, 2 );
+ if( !SUCCEEDED( hr ) ) return hr;
+
+ if( pValues[0].vt == VT_BSTR && pValues[1].vt == VT_BSTR )
+ {
+ if (!wcsncmp(pValues[0].bstrVal, L"URL", 3))
+ {
+ EnterCriticalSection( &mMutex );
+ if( m_xParentControl )
+ {
+ // call GetUrl to the browser instance
+ m_xParentControl->GetURL( pValues[1].bstrVal, L"_self" );
+ }
+ LeaveCriticalSection( &mMutex );
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP SODispatchInterceptor::addStatusListener(IDispatch* /*xControl*/, IDispatch* /*aURL*/)
+{
+ // not implemented
+ return S_OK;
+}
+
+STDMETHODIMP SODispatchInterceptor::removeStatusListener(IDispatch* /*xControl*/,
+ IDispatch* /*aURL*/)
+{
+ // not implemented
+ return S_OK;
+}
+
+STDMETHODIMP SODispatchInterceptor::getInterceptedURLs(SAFEARRAY** pVal)
+{
+ *pVal = SafeArrayCreateVector( VT_BSTR, 0, 3 );
+
+ if( !*pVal )
+ return E_FAIL;
+
+ LONG ix = 0;
+ CComBSTR aPattern( OLESTR( "ftp://*" ) );
+ SafeArrayPutElement( *pVal, &ix, aPattern );
+
+ ix = 1;
+ aPattern = CComBSTR( OLESTR( "http://*" ) );
+ SafeArrayPutElement( *pVal, &ix, aPattern );
+
+ ix = 2;
+ aPattern = CComBSTR( OLESTR( "file://*" ) );
+ SafeArrayPutElement( *pVal, &ix, aPattern );
+
+ return S_OK;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/SODispatchInterceptor.h b/extensions/source/activex/SODispatchInterceptor.h
new file mode 100644
index 000000000..3c0604348
--- /dev/null
+++ b/extensions/source/activex/SODispatchInterceptor.h
@@ -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 .
+ */
+
+// SODispatchInterceptor.h: Definition of the SODispatchInterceptor class
+
+#pragma once
+
+#ifdef _MSC_VER
+#pragma once
+#endif
+
+#include "resource.h"
+#include <ExDispID.h>
+#include <ExDisp.h>
+#include <shlguid.h>
+
+#include <atlctl.h>
+
+#include <so_activex.h>
+
+class CSOActiveX;
+
+
+// SODispatchInterceptor
+
+class SODispatchInterceptor :
+ public IDispatchImpl<ISODispatchInterceptor, &IID_ISODispatchInterceptor, &LIBID_SO_ACTIVEXLib>,
+ public ISupportErrorInfo,
+ public CComObjectRoot,
+ public CComCoClass<SODispatchInterceptor,&CLSID_SODispatchInterceptor>
+{
+ CComPtr<IDispatch> m_xMaster;
+ CComPtr<IDispatch> m_xSlave;
+ CSOActiveX* m_xParentControl;
+ CRITICAL_SECTION mMutex;
+public:
+ SODispatchInterceptor() : m_xParentControl( nullptr ) { InitializeCriticalSection(&mMutex); }
+ virtual ~SODispatchInterceptor() { ATLASSERT( !m_xParentControl ); DeleteCriticalSection(&mMutex); }
+
+BEGIN_COM_MAP(SODispatchInterceptor)
+ COM_INTERFACE_ENTRY(IDispatch)
+ COM_INTERFACE_ENTRY(ISODispatchInterceptor)
+ COM_INTERFACE_ENTRY(ISupportErrorInfo)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#endif
+END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+DECLARE_NOT_AGGREGATABLE(SODispatchInterceptor)
+// Remove the comment from the line above if you don't want your object to
+// support aggregation.
+
+DECLARE_REGISTRY_RESOURCEID(IDR_SODISPATCHINTERCEPTOR)
+
+ void SetParent( CSOActiveX* pParent )
+ {
+ ATLASSERT( !m_xParentControl );
+ EnterCriticalSection( &mMutex );
+ m_xParentControl = pParent;
+ LeaveCriticalSection( &mMutex );
+ }
+
+ void ClearParent()
+ {
+ EnterCriticalSection( &mMutex );
+ m_xParentControl = nullptr;
+ LeaveCriticalSection( &mMutex );
+ }
+
+// ISupportsErrorInfo
+ STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid) override;
+
+// ISODispatchInterceptor
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE getSlaveDispatchProvider(
+ /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *retVal) override
+ {
+ *retVal = m_xSlave;
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE setSlaveDispatchProvider(
+ /* [in] */ IDispatch __RPC_FAR *xNewDispatchProvider) override
+ {
+ m_xSlave = xNewDispatchProvider;
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE getMasterDispatchProvider(
+ /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *retVal) override
+ {
+ *retVal = m_xMaster;
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE setMasterDispatchProvider(
+ /* [in] */ IDispatch __RPC_FAR *xNewSupplier) override
+ {
+ m_xMaster = xNewSupplier;
+ return S_OK;
+ }
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE queryDispatch(
+ /* [in] */ IDispatch __RPC_FAR *aURL,
+ /* [in] */ BSTR aTargetFrameName,
+ /* [in] */ long nSearchFlags,
+ /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *retVal) override;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE queryDispatches(
+ /* [in] */ SAFEARRAY __RPC_FAR * aDescripts,
+ /* [retval][out] */ SAFEARRAY __RPC_FAR * __RPC_FAR *retVal) override;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE dispatch(
+ /* [in] */ IDispatch __RPC_FAR *aURL,
+ /* [in] */ SAFEARRAY __RPC_FAR * aArgs) override;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE addStatusListener(
+ /* [in] */ IDispatch __RPC_FAR *xControl,
+ /* [in] */ IDispatch __RPC_FAR *aURL) override;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE removeStatusListener(
+ /* [in] */ IDispatch __RPC_FAR *xControl,
+ /* [in] */ IDispatch __RPC_FAR *aURL) override;
+
+ virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE getInterceptedURLs(
+ /* [retval][out] */ SAFEARRAY __RPC_FAR * __RPC_FAR *pVal) override;
+
+ virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Bridge_implementedInterfaces(
+ /* [retval][out] */ SAFEARRAY __RPC_FAR * __RPC_FAR *pVal) override
+ {
+ *pVal = SafeArrayCreateVector( VT_BSTR, 0, 4 );
+
+ if( !*pVal )
+ return E_FAIL;
+
+ LONG ix = 0;
+ CComBSTR aInterface( OLESTR( "com.sun.star.frame.XDispatchProviderInterceptor" ) );
+ SafeArrayPutElement( *pVal, &ix, aInterface );
+
+ ix = 1;
+ aInterface = CComBSTR( OLESTR( "com.sun.star.frame.XDispatchProvider" ) );
+ SafeArrayPutElement( *pVal, &ix, aInterface );
+
+ ix = 2;
+ aInterface = CComBSTR( OLESTR( "com.sun.star.frame.XDispatch" ) );
+ SafeArrayPutElement( *pVal, &ix, aInterface );
+
+ ix = 3;
+ aInterface = CComBSTR( OLESTR( "com.sun.star.frame.XInterceptorInfo" ) );
+ SafeArrayPutElement( *pVal, &ix, aInterface );
+
+ return S_OK;
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/SODispatchInterceptor.rgs b/extensions/source/activex/SODispatchInterceptor.rgs
new file mode 100644
index 000000000..19fe0b5f0
--- /dev/null
+++ b/extensions/source/activex/SODispatchInterceptor.rgs
@@ -0,0 +1,23 @@
+HKCR
+{
+ so_activex.SODispatchInterceptor.1 = s 'SODispatchInterceptor Class'
+ {
+ CLSID = s '{C5D6D568-57DA-4D6C-819A-451CB565E682}'
+ }
+ so_activex.SODispatchInterceptor = s 'SODispatchInterceptor Class'
+ {
+ CLSID = s '{C5D6D568-57DA-4D6C-819A-451CB565E682}'
+ }
+ NoRemove CLSID
+ {
+ ForceRemove {C5D6D568-57DA-4D6C-819A-451CB565E682} = s 'SODispatchInterceptor Class'
+ {
+ ProgID = s 'so_activex.SODispatchInterceptor.1'
+ VersionIndependentProgID = s 'so_activex.SODispatchInterceptor'
+ InprocServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'both'
+ }
+ }
+ }
+}
diff --git a/extensions/source/activex/StdAfx2.cxx b/extensions/source/activex/StdAfx2.cxx
new file mode 100644
index 000000000..c2df5c5c0
--- /dev/null
+++ b/extensions/source/activex/StdAfx2.cxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// stdafx1.cpp : source file that includes just the standard includes
+// stdafx1.pch will be the pre-compiled header
+// stdafx1.obj will contain the pre-compiled type information
+
+#include "StdAfx2.h"
+
+#ifdef _ATL_STATIC_REGISTRY
+#include <statreg.h>
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/StdAfx2.h b/extensions/source/activex/StdAfx2.h
new file mode 100644
index 000000000..56bd75b8a
--- /dev/null
+++ b/extensions/source/activex/StdAfx2.h
@@ -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 .
+ */
+
+// stdafx1.h : include file for standard system include files,
+// or project specific include files that are used frequently,
+// but are changed infrequently
+
+#pragma once
+
+#define STRICT
+#define _ATL_APARTMENT_THREADED
+#define _ATL_STATIC_REGISTRY
+
+#pragma warning(push)
+// local variable is initialized but not referenced - in atlctl.h
+#pragma warning(disable : 4189)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wall"
+#pragma clang diagnostic ignored "-Wattributes"
+#pragma clang diagnostic ignored "-Wdelete-incomplete"
+#pragma clang diagnostic ignored "-Wdynamic-class-memaccess"
+#pragma clang diagnostic ignored "-Wint-to-pointer-cast"
+#pragma clang diagnostic ignored "-Winvalid-noreturn"
+#pragma clang diagnostic ignored "-Wmicrosoft"
+#pragma clang diagnostic ignored "-Wmissing-field-initializers"
+#pragma clang diagnostic ignored "-Wnon-pod-varargs"
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#pragma clang diagnostic ignored "-Wnonportable-include-path"
+#pragma clang diagnostic ignored "-Wsequence-point"
+#pragma clang diagnostic ignored "-Wsign-compare"
+#pragma clang diagnostic ignored "-Wtypename-missing"
+#endif
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#include <atlbase.h>
+
+//You may derive a class from CComModule and use it if you want to override
+//something, but do not change the name of _Module
+extern CComModule _Module;
+#include <atlcom.h>
+#include <atlctl.h>
+
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+#pragma warning(pop)
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/com_uno_helper.h b/extensions/source/activex/com_uno_helper.h
new file mode 100644
index 000000000..89aa0a7d1
--- /dev/null
+++ b/extensions/source/activex/com_uno_helper.h
@@ -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 .
+ */
+
+#include "StdAfx2.h"
+
+HRESULT ExecuteFunc( IDispatch* idispUnoObject,
+ OLECHAR const * sFuncName,
+ CComVariant* params,
+ unsigned int count,
+ CComVariant* pResult );
+
+HRESULT GetIDispByFunc( IDispatch* idispUnoObject,
+ OLECHAR* sFuncName,
+ CComVariant* params,
+ unsigned int count,
+ CComPtr<IDispatch>& pdispResult );
+
+HRESULT PutPropertiesToIDisp( IDispatch* pdispObject,
+ OLECHAR** sMemberNames,
+ CComVariant* pVariant,
+ unsigned int count );
+
+HRESULT GetPropertiesFromIDisp( IDispatch* pdispObject,
+ OLECHAR const ** sMemberNames,
+ CComVariant* pVariant,
+ unsigned int count );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/example.html b/extensions/source/activex/example.html
new file mode 100644
index 000000000..96e764c8f
--- /dev/null
+++ b/extensions/source/activex/example.html
@@ -0,0 +1,43 @@
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<HTML>
+<HEAD>
+<TITLE>Document Title</TITLE>
+</HEAD>
+<BODY>
+
+<center>
+First you should edit the example.html file!!!
+</center>
+<center>
+<!-- Please edit CODEBASE parameter -->
+<!-- In case ActiveX control is already registered the parameter can be removed -->
+<OBJECT CLASSID="clsid:67F2A879-82D5-4A6D-8CC5-FFB3C114B69D" width="500" height="500"
+ CODEBASE="..\..\..\WINexample.out\bin\so_activex.dll">
+<!-- Full URL to a document
+ <PARAM NAME="src" VALUE="file:///d:/example.sxw">
+-->
+<!-- Just view the document, do not edit
+ <PARAM NAME="readonly" VALUE="true">
+-->
+</OBJECT>
+
+</center>
+
+</BODY>
+</HTML>
diff --git a/extensions/source/activex/resource.h b/extensions/source/activex/resource.h
new file mode 100644
index 000000000..b7a77a5c9
--- /dev/null
+++ b/extensions/source/activex/resource.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by so_activex.rc
+
+#define IDS_PROJNAME 100
+#define IDB_SOACTIVEX 101
+#define IDR_SOACTIVEX 102
+#define IDB_SOCOMWINDOWPEER 103
+#define IDR_SOCOMWINDOWPEER 104
+#define IDB_SODISPATCHINTERCEPTOR 105
+#define IDR_SODISPATCHINTERCEPTOR 106
+#define IDB_SODOCUMENTEVENTLISTENER 107
+#define IDR_SODOCUMENTEVENTLISTENER 108
+
+
+// Next default values for new objects
+
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 201
+#define _APS_NEXT_COMMAND_VALUE 32768
+#define _APS_NEXT_CONTROL_VALUE 201
+#define _APS_NEXT_SYMED_VALUE 107
+#endif
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/so_activex.cxx b/extensions/source/activex/so_activex.cxx
new file mode 100644
index 000000000..70682a416
--- /dev/null
+++ b/extensions/source/activex/so_activex.cxx
@@ -0,0 +1,774 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// so_activex.cpp : Implementation of DLL Exports.
+
+// Note: Proxy/Stub Information
+// To build a separate proxy/stub DLL,
+// run nmake -f so_activexps.mk in the project directory.
+
+#include <stdio.h>
+#include "StdAfx2.h"
+#include "resource.h"
+#include <initguid.h>
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+#include <so_activex.h>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra-tokens"
+ // "#endif !_MIDL_USE_GUIDDEF_" in midl-generated code
+#endif
+#include <so_activex_i.c>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+#include "SOActiveX.h"
+
+#include <comphelper\documentconstants.hxx>
+#include <sal/types.h>
+#include <exception>
+
+CComModule _Module;
+
+BEGIN_OBJECT_MAP(ObjectMap)
+OBJECT_ENTRY(CLSID_SOActiveX, CSOActiveX)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-field-initializers"
+#endif
+END_OBJECT_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+#define X64_LIB_NAME L"so_activex_x64.dll"
+#define X32_LIB_NAME L"so_activex.dll"
+
+const REGSAM n64KeyAccess = KEY_ALL_ACCESS | KEY_WOW64_64KEY;
+const REGSAM n32KeyAccess = KEY_ALL_ACCESS;
+
+#ifdef _AMD64_
+const bool bX64 = true;
+#define REG_DELETE_KEY_A( key, aPath, nKeyAccess ) RegDeleteKeyExA( key, aPath, nKeyAccess, 0 )
+#else
+const bool bX64 = false;
+#define REG_DELETE_KEY_A( key, aPath, nKeyAccess ) RegDeleteKeyA( key, aPath )
+#endif
+
+// DLL Entry Point
+
+extern "C"
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
+{
+ if (dwReason == DLL_PROCESS_ATTACH)
+ {
+ _Module.Init(ObjectMap, hInstance, &LIBID_SO_ACTIVEXLib);
+ DisableThreadLibraryCalls(hInstance);
+ }
+ else if (dwReason == DLL_PROCESS_DETACH)
+ _Module.Term();
+ return TRUE; // ok
+}
+
+
+// Used to determine whether the DLL can be unloaded by OLE
+
+STDAPI DllCanUnloadNow()
+{
+ return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
+}
+
+
+// Returns a class factory to create an object of the requested type
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
+{
+ return _Module.GetClassObject(rclsid, riid, ppv);
+}
+
+
+// DllRegisterServer - Adds entries to the system registry
+
+namespace
+{
+// Wraps an updatable Win32 error; keeps first incoming error.
+// Ctor defines if upd will throw std::exception on error or
+// return false.
+class Status
+{
+public:
+ explicit Status(bool bTrow)
+ : m_bThrow(bTrow)
+ {
+ }
+ // used to check success of an operation, and update the status if it's still ERROR_SUCCESS
+ bool upd(LSTATUS nNewStatus)
+ {
+ if (m_nStatus == ERROR_SUCCESS)
+ m_nStatus = nNewStatus;
+ if (m_bThrow && nNewStatus != ERROR_SUCCESS)
+ throw std::exception();
+ return nNewStatus == ERROR_SUCCESS;
+ };
+ LSTATUS get() { return m_nStatus; }
+ operator bool() { return m_nStatus == ERROR_SUCCESS; }
+
+private:
+ LSTATUS m_nStatus = ERROR_SUCCESS;
+ const bool m_bThrow;
+};
+
+class HRegKey
+{
+public:
+ ~HRegKey()
+ {
+ if (m_hkey)
+ RegCloseKey(m_hkey);
+ }
+ PHKEY operator&() { return &m_hkey; }
+ operator HKEY() { return m_hkey; }
+
+private:
+ HKEY m_hkey = nullptr;
+};
+}
+
+// for now database component and chart are always installed
+#define SUPPORTED_EXT_NUM 30
+const char* const aFileExt[] = { ".vor",
+ ".sds", ".sda", ".sdd", ".sdp", ".sdc", ".sdw", ".smf",
+ ".stw", ".stc", ".sti", ".std",
+ ".sxw", ".sxc", ".sxi", ".sxd", ".sxg", ".sxm",
+ ".ott", ".otg", ".otp", ".ots", ".otf",
+ ".odt", ".oth", ".odm", ".odg", ".odp", ".ods", ".odf"};
+const sal_Unicode* const aMimeType[] = {
+ u"application/vnd.stardivision.writer",
+
+ u"application/vnd.stardivision.chart",
+ u"application/vnd.stardivision.draw",
+ u"application/vnd.stardivision.impress",
+ u"application/vnd.stardivision.impress-packed",
+ u"application/vnd.stardivision.calc",
+ u"application/vnd.stardivision.writer",
+ u"application/vnd.stardivision.math",
+
+ MIMETYPE_VND_SUN_XML_WRITER_TEMPLATE_ASCII.getStr(),
+ MIMETYPE_VND_SUN_XML_CALC_TEMPLATE_ASCII.getStr(),
+ MIMETYPE_VND_SUN_XML_IMPRESS_TEMPLATE_ASCII.getStr(),
+ MIMETYPE_VND_SUN_XML_DRAW_TEMPLATE_ASCII.getStr(),
+
+ MIMETYPE_VND_SUN_XML_WRITER_ASCII.getStr(),
+ MIMETYPE_VND_SUN_XML_CALC_ASCII.getStr(),
+ MIMETYPE_VND_SUN_XML_IMPRESS_ASCII.getStr(),
+ MIMETYPE_VND_SUN_XML_DRAW_ASCII.getStr(),
+ MIMETYPE_VND_SUN_XML_WRITER_GLOBAL_ASCII.getStr(),
+ MIMETYPE_VND_SUN_XML_MATH_ASCII.getStr(),
+
+ MIMETYPE_OASIS_OPENDOCUMENT_TEXT_TEMPLATE_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_TEMPLATE_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_TEMPLATE_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_TEMPLATE_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_TEMPLATE_ASCII.getStr(),
+
+ MIMETYPE_OASIS_OPENDOCUMENT_TEXT_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_TEXT_WEB_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_TEXT_GLOBAL_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_DRAWING_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_SPREADSHEET_ASCII.getStr(),
+ MIMETYPE_OASIS_OPENDOCUMENT_FORMULA_ASCII.getStr() };
+
+const int nForModes[] = { 16,
+ 1, 2, 4, 4, 8, 16, 32,
+ 16, 8, 4, 2,
+ 16, 8, 4, 2, 16, 32,
+ 16, 2, 4, 8, 32,
+ 16, 16, 16, 2, 4, 8, 32 };
+
+const char* const aClassID = "{67F2A879-82D5-4A6D-8CC5-FFB3C114B69D}";
+const char* const aTypeLib = "{61FA3F13-8061-4796-B055-3697ED28CB38}";
+
+// ISOComWindowPeer interface information
+const char* const aInterIDWinPeer = "{BF5D10F3-8A10-4A0B-B150-2B6AA2D7E118}";
+const char* const aProxyStubWinPeer = "{00020424-0000-0000-C000-000000000046}";
+
+// ISODispatchInterceptor interface information
+const char* const aInterIDDispInt = "{9337694C-B27D-4384-95A4-9D8E0EABC9E5}";
+const char* const aProxyStubDispInt = "{00020424-0000-0000-C000-000000000046}";
+
+// ISOActionsApproval interface information
+const char* const aInterIDActApprove = "{029E9F1E-2B3F-4297-9160-8197DE7ED54F}";
+const char* const aProxyStubActApprove = "{00020424-0000-0000-C000-000000000046}";
+
+// The following prefix is required for HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER ( not for HKEY_CLASSES_ROOT )
+const char* const aLocalPrefix = "Software\\Classes\\";
+
+static LSTATUS createKey( HKEY hkey,
+ const char* aKeyToCreate,
+ REGSAM nKeyAccess,
+ const char* aValue = nullptr,
+ const char* aChildName = nullptr,
+ const char* aChildValue = nullptr )
+{
+ Status s(false); // no throw
+ HRegKey hkey1;
+ if (s.upd(RegCreateKeyExA(hkey, aKeyToCreate, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey1, nullptr)))
+ {
+ if (aValue)
+ s.upd(RegSetValueExA(hkey1, "", 0, REG_SZ, reinterpret_cast<const BYTE*>(aValue),
+ sal::static_int_cast<DWORD>(strlen(aValue))));
+ if (aChildName)
+ s.upd(RegSetValueExA(hkey1, aChildName, 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(aChildValue),
+ sal::static_int_cast<DWORD>(strlen(aChildValue))));
+ }
+ return s.get();
+}
+
+static LSTATUS createKey(HKEY hkey,
+ const wchar_t* aKeyToCreate,
+ REGSAM nKeyAccess,
+ const wchar_t* aValue = nullptr,
+ const wchar_t* aChildName = nullptr,
+ const wchar_t* aChildValue = nullptr )
+{
+ Status s(false); // no throw
+ HRegKey hkey1;
+ if (s.upd(RegCreateKeyExW(hkey, aKeyToCreate, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey1, nullptr)))
+ {
+ if (aValue)
+ s.upd(RegSetValueExW(hkey1, L"", 0, REG_SZ, reinterpret_cast<const BYTE*>(aValue),
+ sal::static_int_cast<DWORD>(wcslen(aValue) * sizeof(wchar_t))));
+ if (aChildName)
+ s.upd(RegSetValueExW(
+ hkey1, aChildName, 0, REG_SZ, reinterpret_cast<const BYTE*>(aChildValue),
+ sal::static_int_cast<DWORD>(wcslen(aChildValue) * sizeof(wchar_t))));
+ }
+ return s.get();
+}
+
+EXTERN_C __declspec(dllexport) HRESULT STDAPICALLTYPE DllUnregisterServerNative( int nMode, BOOL bForAllUsers, BOOL bFor64Bit );
+static HRESULT DllRegisterServerNative_Impl( int nMode, bool bForAllUsers, REGSAM nKeyAccess, const wchar_t* pProgramPath, const wchar_t* pLibName )
+{
+ char aSubKey[513];
+ int ind;
+ const char* aPrefix = aLocalPrefix; // bForAllUsers ? "" : aLocalPrefix;
+
+ // In case SO7 is installed for this user he can have local registry entries that will prevent him from
+ // using SO8 ActiveX control. The fix is just to clean up the local entries related to ActiveX control.
+ // Unfortunately it can be done only for the user who installs the office.
+ if ( bForAllUsers )
+ DllUnregisterServerNative( nMode, false, false );
+
+ Status s(true); // throw
+ try
+ {
+ if (pProgramPath && wcslen(pProgramPath) < 1024)
+ {
+ wchar_t pActiveXPath[1124];
+ wchar_t pActiveXPath101[1124];
+
+ swprintf(pActiveXPath, L"%s\\%s", pProgramPath, pLibName);
+ swprintf(pActiveXPath101, L"%s\\%s, 101", pProgramPath, pLibName);
+
+ {
+ wsprintfA(aSubKey, "%sCLSID\\%s", aPrefix, aClassID);
+ HRegKey hkey;
+ s.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey, nullptr));
+ s.upd(RegSetValueExA(hkey, "", 0, REG_SZ,
+ reinterpret_cast<const BYTE*>("SOActiveX Class"), 17));
+ s.upd(createKey(hkey, "Control", nKeyAccess));
+ s.upd(createKey(hkey, "EnableFullPage", nKeyAccess));
+ s.upd(createKey(hkey, L"InprocServer32", nKeyAccess, pActiveXPath,
+ L"ThreadingModel", L"Apartment"));
+ s.upd(createKey(hkey, "MiscStatus", nKeyAccess, "0"));
+ s.upd(createKey(hkey, "MiscStatus\\1", nKeyAccess, "131473"));
+ s.upd(createKey(hkey, "ProgID", nKeyAccess, "so_activex.SOActiveX.1"));
+ s.upd(createKey(hkey, "Programmable", nKeyAccess));
+ s.upd(createKey(hkey, L"ToolboxBitmap32", nKeyAccess, pActiveXPath101));
+ s.upd(createKey(hkey, "TypeLib", nKeyAccess, aTypeLib));
+ s.upd(createKey(hkey, "Version", nKeyAccess, "1.0"));
+ s.upd(createKey(hkey, "VersionIndependentProgID", nKeyAccess,
+ "so_activex.SOActiveX"));
+ }
+ {
+ HRegKey hkey;
+ s.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ aPrefix, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey, nullptr));
+ s.upd(createKey(hkey, "so_activex.SOActiveX", nKeyAccess, "SOActiveX Class"));
+ {
+ HRegKey hkey1;
+ s.upd(RegCreateKeyExA(hkey, "so_activex.SOActiveX", 0, nullptr,
+ REG_OPTION_NON_VOLATILE, nKeyAccess, nullptr, &hkey1,
+ nullptr));
+ s.upd(createKey(hkey1, "CLSID", nKeyAccess, aClassID));
+ s.upd(createKey(hkey1, "CurVer", nKeyAccess, "so_activex.SOActiveX.1"));
+ }
+ s.upd(createKey(hkey, "so_activex.SOActiveX.1", nKeyAccess, "SOActiveX Class"));
+ {
+ HRegKey hkey1;
+ s.upd(RegCreateKeyExA(hkey, "so_activex.SOActiveX.1", 0, nullptr,
+ REG_OPTION_NON_VOLATILE, nKeyAccess, nullptr, &hkey1,
+ nullptr));
+ s.upd(createKey(hkey1, "CLSID", nKeyAccess, aClassID));
+ }
+ {
+ HRegKey hkey1;
+ s.upd(RegCreateKeyExA(hkey, "TypeLib", 0, nullptr, REG_OPTION_NON_VOLATILE,
+ nKeyAccess, nullptr, &hkey1, nullptr));
+ {
+ HRegKey hkey2;
+ s.upd(RegCreateKeyExA(hkey1, aTypeLib, 0, nullptr, REG_OPTION_NON_VOLATILE,
+ nKeyAccess, nullptr, &hkey2, nullptr));
+ s.upd(createKey(hkey2, "1.0", nKeyAccess, "wrap_activex 1.0 Type Library"));
+ {
+ HRegKey hkey3;
+ s.upd(RegCreateKeyExA(hkey2, "1.0", 0, nullptr, REG_OPTION_NON_VOLATILE,
+ nKeyAccess, nullptr, &hkey3, nullptr));
+ {
+ HRegKey hkey4;
+ s.upd(RegCreateKeyExA(hkey3, "0", 0, nullptr,
+ REG_OPTION_NON_VOLATILE, nKeyAccess, nullptr,
+ &hkey4, nullptr));
+ s.upd(createKey(hkey4, L"win32", nKeyAccess, pActiveXPath));
+ }
+ s.upd(createKey(hkey3, "FLAGS", nKeyAccess, "0"));
+ s.upd(createKey(hkey3, L"HELPDIR", nKeyAccess, pProgramPath));
+ }
+ }
+ }
+ {
+ HRegKey hkey1;
+ s.upd(RegCreateKeyExA(hkey, "Interface", 0, nullptr, REG_OPTION_NON_VOLATILE,
+ nKeyAccess, nullptr, &hkey1, nullptr));
+ s.upd(createKey(hkey1, aInterIDWinPeer, nKeyAccess, "ISOComWindowPeer"));
+ {
+ HRegKey hkey2;
+ s.upd(RegCreateKeyExA(hkey1, aInterIDWinPeer, 0, nullptr,
+ REG_OPTION_NON_VOLATILE, nKeyAccess, nullptr, &hkey2,
+ nullptr));
+ s.upd(createKey(hkey2, "ProxyStubClsid", nKeyAccess, aProxyStubWinPeer));
+ s.upd(createKey(hkey2, "ProxyStubClsid32", nKeyAccess, aProxyStubWinPeer));
+ s.upd(createKey(hkey2, "TypeLib", nKeyAccess, aTypeLib, "Version", "1.0"));
+ }
+ s.upd(createKey(hkey1, aInterIDActApprove, nKeyAccess, "ISOActionsApproval"));
+ {
+ HRegKey hkey2;
+ s.upd(RegCreateKeyExA(hkey1, aInterIDActApprove, 0, nullptr,
+ REG_OPTION_NON_VOLATILE, nKeyAccess, nullptr, &hkey2,
+ nullptr));
+ s.upd(createKey(hkey2, "ProxyStubClsid", nKeyAccess, aProxyStubActApprove));
+ s.upd(
+ createKey(hkey2, "ProxyStubClsid32", nKeyAccess, aProxyStubActApprove));
+ s.upd(createKey(hkey2, "TypeLib", nKeyAccess, aTypeLib, "Version", "1.0"));
+ }
+ s.upd(createKey(hkey1, aInterIDDispInt, nKeyAccess, "ISODispatchInterceptor"));
+ {
+ HRegKey hkey2;
+ s.upd(RegCreateKeyExA(hkey1, aInterIDDispInt, 0, nullptr,
+ REG_OPTION_NON_VOLATILE, nKeyAccess, nullptr, &hkey2,
+ nullptr));
+ s.upd(createKey(hkey2, "ProxyStubClsid", nKeyAccess, aProxyStubDispInt));
+ s.upd(createKey(hkey2, "ProxyStubClsid32", nKeyAccess, aProxyStubDispInt));
+ s.upd(createKey(hkey2, "TypeLib", nKeyAccess, aTypeLib, "Version", "1.0"));
+ }
+ }
+ }
+ }
+
+ for (ind = 0; ind < SUPPORTED_EXT_NUM; ind++)
+ {
+ if (nForModes[ind] & nMode)
+ {
+ wsprintfA(aSubKey, "%sMIME\\DataBase\\Content Type\\%ls", aPrefix, aMimeType[ind]);
+ HRegKey hkey;
+ s.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey, nullptr));
+ s.upd(RegSetValueExA(hkey, "CLSID", 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(aClassID),
+ sal::static_int_cast<DWORD>(strlen(aClassID))));
+ }
+ }
+
+ {
+ wsprintfA(aSubKey, "%sCLSID\\%s", aPrefix, aClassID);
+ HRegKey hkey;
+ s.upd(RegOpenKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey, 0,
+ nKeyAccess, &hkey));
+ for (ind = 0; ind < SUPPORTED_EXT_NUM; ind++)
+ {
+ wsprintfA(aSubKey, "EnableFullPage\\%s", aFileExt[ind]);
+ HRegKey hkey1;
+ s.upd(RegCreateKeyExA(hkey, aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE,
+ nKeyAccess, nullptr, &hkey1, nullptr));
+ }
+ }
+ }
+ catch (const std::exception&) {}
+
+ return HRESULT_FROM_WIN32(s.get());
+}
+
+EXTERN_C __declspec(dllexport) HRESULT STDAPICALLTYPE DllRegisterServerNative( int nMode, BOOL bForAllUsers, BOOL bFor64Bit, const wchar_t* pProgramPath )
+{
+ HRESULT hr = S_OK;
+ if ( bFor64Bit )
+ hr = DllRegisterServerNative_Impl( nMode, bForAllUsers, n64KeyAccess, pProgramPath, X64_LIB_NAME );
+
+ if ( SUCCEEDED( hr ) )
+ hr = DllRegisterServerNative_Impl( nMode, bForAllUsers, n32KeyAccess, pProgramPath, X32_LIB_NAME );
+
+ return hr;
+}
+
+
+// DllUnregisterServer - Removes entries from the system registry
+static HRESULT DeleteKeyTree( HKEY hkey, const char* pPath, REGSAM nKeyAccess )
+{
+ char pSubKeyName[256];
+ // first delete the subkeys
+ while (true)
+ {
+ HRegKey hkey1;
+ if (ERROR_SUCCESS != RegOpenKeyExA(hkey, pPath, 0, nKeyAccess, &hkey1)
+ || ERROR_SUCCESS != RegEnumKeyA(hkey1, 0, pSubKeyName, 256)
+ || ERROR_SUCCESS != DeleteKeyTree(hkey1, pSubKeyName, nKeyAccess))
+ break;
+ }
+
+ // delete the key itself
+ return REG_DELETE_KEY_A( hkey, pPath, nKeyAccess & ( KEY_WOW64_64KEY | KEY_WOW64_32KEY ) );
+}
+
+static HRESULT DllUnregisterServerNative_Impl( int nMode, bool bForAllUsers, REGSAM nKeyAccess )
+{
+ char aSubKey[513];
+ const char* aPrefix = aLocalPrefix; // bForAllUsers ? "" : aLocalPrefix;
+
+ Status s(false); // no throw
+ for( int ind = 0; ind < SUPPORTED_EXT_NUM; ind++ )
+ {
+ if( nForModes[ind] & nMode )
+ {
+ DWORD nSubKeys = 0, nValues = 0;
+ wsprintfA(aSubKey, "%sMIME\\DataBase\\Content Type\\%ls", aPrefix, aMimeType[ind]);
+ Status s1(false); // no throw
+ {
+ HRegKey hkey;
+ if (s1.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey, nullptr)))
+ {
+ s1.upd(RegDeleteValueA(hkey, "CLSID"));
+ s1.upd(RegQueryInfoKeyA(hkey, nullptr, nullptr, nullptr, &nSubKeys, nullptr,
+ nullptr, &nValues, nullptr, nullptr, nullptr, nullptr));
+ }
+ }
+ if (s1 && !nSubKeys && !nValues)
+ DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess);
+ s.upd(s1.get());
+
+ wsprintfA(aSubKey, "%s%s", aPrefix, aFileExt[ind]);
+ Status s2(false); // no throw
+ {
+ HRegKey hkey;
+ if (s2.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey, nullptr)))
+ {
+ s2.upd(RegQueryInfoKeyA(hkey, nullptr, nullptr, nullptr, &nSubKeys, nullptr,
+ nullptr, &nValues, nullptr, nullptr, nullptr, nullptr));
+ }
+ }
+ if (s2 && !nSubKeys && !nValues)
+ DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess);
+ s.upd(s2.get());
+ }
+ }
+
+ wsprintfA(aSubKey, "%sCLSID\\%s", aPrefix, aClassID);
+ s.upd(DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess));
+
+ wsprintfA(aSubKey, "%sso_activex.SOActiveX", aPrefix);
+ s.upd(DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess));
+
+ wsprintfA(aSubKey, "%sso_activex.SOActiveX.1", aPrefix);
+ s.upd(DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess));
+
+ wsprintfA(aSubKey, "%s\\TypeLib\\%s", aPrefix, aTypeLib);
+ s.upd(DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess));
+
+ wsprintfA(aSubKey, "%s\\Interface\\%s", aPrefix, aInterIDWinPeer);
+ s.upd(DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess));
+
+ wsprintfA(aSubKey, "%s\\Interface\\%s", aPrefix, aInterIDDispInt);
+ s.upd(DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess));
+
+ wsprintfA(aSubKey, "%s\\Interface\\%s", aPrefix, aInterIDActApprove);
+ s.upd(DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess));
+
+ return HRESULT_FROM_WIN32(s.get());
+}
+
+STDAPI DllUnregisterServerNative( int nMode, BOOL bForAllUsers, BOOL bFor64Bit )
+{
+ HRESULT hr = DllUnregisterServerNative_Impl( nMode, bForAllUsers, n32KeyAccess );
+ if ( SUCCEEDED( hr ) && bFor64Bit )
+ hr = DllUnregisterServerNative_Impl( nMode, bForAllUsers, n64KeyAccess );
+
+ return hr;
+}
+
+
+// DllRegisterServerDoc - Adds entries to the system registry
+
+#define SUPPORTED_MSEXT_NUM 7
+const char* const aMSFileExt[] = { ".dot", ".doc", ".xlt", ".xls", ".pot", ".ppt", ".pps" };
+const char* const aMSMimeType[] = { "application/msword",
+ "application/msword",
+ "application/vnd.ms-excel",
+ "application/vnd.ms-excel",
+ "application/vnd.ms-powerpoint",
+ "application/vnd.ms-powerpoint",
+ "application/vnd.ms-powerpoint" };
+const int nForMSModes[] = { 1, 1, 2, 2, 4, 4, 4 };
+
+EXTERN_C __declspec(dllexport) HRESULT STDAPICALLTYPE DllUnregisterServerDoc( int nMode, BOOL bForAllUsers, BOOL bFor64Bit );
+static HRESULT DllRegisterServerDoc_Impl( int nMode, bool bForAllUsers, REGSAM nKeyAccess )
+{
+ char aSubKey[513];
+ int ind;
+ const char* aPrefix = aLocalPrefix; // bForAllUsers ? "" : aLocalPrefix;
+
+ // In case SO7 is installed for this user he can have local registry entries that will prevent him from
+ // using SO8 ActiveX control. The fix is just to clean up the local entries related to ActiveX control.
+ // Unfortunately it can be done only for the user who installs the office.
+ if ( bForAllUsers )
+ DllUnregisterServerDoc( nMode, false, false );
+
+ Status s(true); // throw
+ try
+ {
+ for (ind = 0; ind < SUPPORTED_MSEXT_NUM; ind++)
+ {
+ if (nForMSModes[ind] & nMode)
+ {
+ {
+ wsprintfA(aSubKey, "%sMIME\\DataBase\\Content Type\\%s", aPrefix,
+ aMSMimeType[ind]);
+ HRegKey hkey;
+ s.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey, nullptr));
+ s.upd(RegSetValueExA(hkey, "Extension", 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(aMSFileExt[ind]),
+ sal::static_int_cast<DWORD>(strlen(aMSFileExt[ind]))));
+ s.upd(RegSetValueExA(hkey, "CLSID", 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(aClassID),
+ sal::static_int_cast<DWORD>(strlen(aClassID))));
+ }
+ {
+ wsprintfA(aSubKey, "%s%s", aPrefix, aMSFileExt[ind]);
+ HRegKey hkey;
+ s.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey, nullptr));
+ s.upd(RegSetValueExA(hkey, "Content Type", 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(aMSMimeType[ind]),
+ sal::static_int_cast<DWORD>(strlen(aMSMimeType[ind]))));
+ }
+ }
+ }
+
+ wsprintfA(aSubKey, "%sCLSID\\%s", aPrefix, aClassID);
+ HRegKey hkey;
+ s.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey, 0,
+ nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess, nullptr, &hkey,
+ nullptr));
+ s.upd(createKey(hkey, "EnableFullPage", nKeyAccess));
+ for (ind = 0; ind < SUPPORTED_MSEXT_NUM; ind++)
+ {
+ if (nForMSModes[ind] & nMode)
+ {
+ wsprintfA(aSubKey, "EnableFullPage\\%s", aMSFileExt[ind]);
+ HRegKey hkey1;
+ s.upd(RegCreateKeyExA(hkey, aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE,
+ nKeyAccess, nullptr, &hkey1, nullptr));
+ }
+ }
+ }
+ catch (const std::exception&) {}
+
+ return HRESULT_FROM_WIN32(s.get());
+}
+
+EXTERN_C __declspec(dllexport) HRESULT STDAPICALLTYPE DllRegisterServerDoc( int nMode, BOOL bForAllUsers, BOOL bFor64Bit )
+{
+ HRESULT hr = S_OK;
+ if ( bFor64Bit )
+ hr = DllRegisterServerDoc_Impl( nMode, bForAllUsers, n64KeyAccess );
+
+ if ( SUCCEEDED( hr ) )
+ hr = DllRegisterServerDoc_Impl( nMode, bForAllUsers, n32KeyAccess );
+
+ return hr;
+}
+
+
+// DllUnregisterServerDoc - Removes entries from the system registry
+
+static HRESULT DllUnregisterServerDoc_Impl( int nMode, bool bForAllUsers, REGSAM nKeyAccess )
+{
+ char aSubKey[513];
+ const char* aPrefix = aLocalPrefix; // bForAllUsers ? "" : aLocalPrefix;
+
+ Status s(false); // no throw
+ for (int ind = 0; ind < SUPPORTED_MSEXT_NUM; ind++)
+ {
+ if (nForMSModes[ind] & nMode)
+ {
+ DWORD nSubKeys = 0, nValues = 0;
+ Status s1(false); // no throw
+ {
+ wsprintfA(aSubKey, "%sMIME\\DataBase\\Content Type\\%s", aPrefix, aMSMimeType[ind]);
+ HRegKey hkey;
+ if (s1.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey, nullptr)))
+ {
+ s.upd(RegDeleteValueA(hkey, "Extension"));
+ s.upd(RegDeleteValueA(hkey, "CLSID"));
+ s1.upd(RegQueryInfoKeyA(hkey, nullptr, nullptr, nullptr, &nSubKeys, nullptr,
+ nullptr, &nValues, nullptr, nullptr, nullptr, nullptr));
+ }
+ }
+ if (s1 && !nSubKeys && !nValues)
+ DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess);
+ s.upd(s1.get());
+
+ Status s2(false); // no throw
+ {
+ wsprintfA(aSubKey, "%s%s", aPrefix, aMSFileExt[ind]);
+ HRegKey hkey;
+ if (s2.upd(RegCreateKeyExA(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ aSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE, nKeyAccess,
+ nullptr, &hkey, nullptr)))
+ {
+ s.upd(RegDeleteValueA(hkey, "Content Type"));
+ s2.upd(RegQueryInfoKeyA(hkey, nullptr, nullptr, nullptr, &nSubKeys, nullptr,
+ nullptr, &nValues, nullptr, nullptr, nullptr, nullptr));
+ }
+ }
+ if (s2 && !nSubKeys && !nValues)
+ DeleteKeyTree(bForAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, aSubKey,
+ nKeyAccess);
+ s.upd(s2.get());
+ }
+ }
+
+ return HRESULT_FROM_WIN32(s.get());
+}
+
+STDAPI DllUnregisterServerDoc( int nMode, BOOL bForAllUsers, BOOL bFor64Bit )
+{
+ HRESULT hr = S_OK;
+ if ( bFor64Bit )
+ hr = DllUnregisterServerDoc_Impl( nMode, bForAllUsers, n64KeyAccess );
+
+ if ( SUCCEEDED( hr ) )
+ hr = DllUnregisterServerDoc_Impl( nMode, bForAllUsers, n32KeyAccess );
+
+ return hr;
+}
+
+
+// DllRegisterServer - regsvr32 entry point
+
+STDAPI DllRegisterServer()
+{
+ HRESULT aResult = E_FAIL;
+
+ HMODULE aCurModule{};
+ GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
+ | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<LPCWSTR>(&DllRegisterServer), &aCurModule);
+ if( aCurModule )
+ {
+ wchar_t pProgramPath[1024];
+ wchar_t* pPathEnd = nullptr;
+ DWORD nLen = GetModuleFileNameW( aCurModule, pProgramPath, SAL_N_ELEMENTS(pProgramPath) );
+ if ( nLen && nLen < SAL_N_ELEMENTS(pProgramPath) )
+ pPathEnd = wcsrchr(pProgramPath, '\\');
+ if (pPathEnd)
+ {
+ *pPathEnd = 0;
+ aResult = DllRegisterServerNative( 31, TRUE, bX64, pProgramPath );
+ if( SUCCEEDED( aResult ) )
+ aResult = DllRegisterServerDoc( 31, TRUE, bX64 );
+ else
+ {
+ aResult = DllRegisterServerNative( 31, FALSE, bX64, pProgramPath );
+ if( SUCCEEDED( aResult ) )
+ aResult = DllRegisterServerDoc( 31, FALSE, bX64 );
+ }
+ }
+ }
+
+ return aResult;
+}
+
+
+// DllUnregisterServer - regsvr32 entry point
+
+STDAPI DllUnregisterServer()
+{
+ DllUnregisterServerDoc( 63, FALSE, bX64 );
+ DllUnregisterServerNative( 63, FALSE, bX64 );
+ DllUnregisterServerDoc( 63, TRUE, bX64 );
+ return DllUnregisterServerNative( 63, TRUE, bX64 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/so_activex.def b/extensions/source/activex/so_activex.def
new file mode 100644
index 000000000..9f81e7917
--- /dev/null
+++ b/extensions/source/activex/so_activex.def
@@ -0,0 +1,13 @@
+; iervp.def : Declares the module parameters.
+
+LIBRARY
+
+EXPORTS
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
+ DllRegisterServerNative PRIVATE
+ DllRegisterServerDoc PRIVATE
+ DllUnregisterServerNative PRIVATE
+ DllUnregisterServerDoc PRIVATE
diff --git a/extensions/source/activex/so_activex.idl b/extensions/source/activex/so_activex.idl
new file mode 100644
index 000000000..a84dde219
--- /dev/null
+++ b/extensions/source/activex/so_activex.idl
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// so_activex.idl : IDL source for so_activex.dll
+
+
+// This file will be processed by the MIDL tool to
+// produce the type library (so_activex.tlb) and marshalling code.
+
+import "oaidl.idl";
+import "ocidl.idl";
+#include <olectl.h>
+
+
+ [
+ object,
+ uuid(DACF7E3F-626B-4BF9-964B-F4910C843711),
+ dual,
+ helpstring("ISOActiveX Interface"),
+ pointer_default(unique)
+ ]
+ interface ISOActiveX : IDispatch
+ {
+ };
+
+[
+ object,
+ uuid(BF5D10F3-8A10-4A0B-B150-2B6AA2D7E118),
+ dual,
+ helpstring("ISOComWindowPeer Interface"),
+ pointer_default(unique)
+]
+interface ISOComWindowPeer : IDispatch
+{
+ [id(1), helpstring("method getWindowHandle")]
+ HRESULT getWindowHandle( [in] SAFEARRAY(VARIANT) procId,
+ [in] short s,
+ [out,retval] long* ret);
+
+ [id(2), helpstring("method getToolkit")]
+ HRESULT getToolkit( [out,retval] IDispatch** retVal );
+
+ [id(3), helpstring("method setPointer")]
+ HRESULT setPointer( [in] IDispatch* xPointer );
+
+ [id(4), helpstring("method setBackground")]
+ HRESULT setBackground( [in] int nColor );
+
+ [id(5), helpstring("method invalidate")]
+ HRESULT invalidate( [in] short );
+
+ [id(6), helpstring("method invalidateRect")]
+ HRESULT invalidateRect( [in] IDispatch* aRect, [in] short nFlags );
+
+ [id(7), helpstring("method dispose")]
+ HRESULT dispose();
+
+ [id(8), helpstring("method addEventListener")]
+ HRESULT addEventListener( [in] IDispatch* xListener );
+
+ [id(9), helpstring("method removeEventListener")]
+ HRESULT removeEventListener( [in] IDispatch* xListener );
+
+ [propget, id(10), helpstring("property_implementedInterfaces")]
+ HRESULT Bridge_implementedInterfaces([out, retval] SAFEARRAY(BSTR) *pVal);
+};
+
+[
+ object,
+ uuid(9337694C-B27D-4384-95A4-9D8E0EABC9E5),
+ dual,
+ helpstring("ISODispatchInterceptor Interface"),
+ pointer_default(unique)
+]
+interface ISODispatchInterceptor : IDispatch
+{
+//com.sun.star.frame.XDispatchProviderInterceptor
+
+ [id(1), helpstring("method getSlaveDispatchProvider")]
+ HRESULT getSlaveDispatchProvider( [out,retval] IDispatch** retVal );
+
+ [id(2), helpstring("method setSlaveDispatchProvider")]
+ HRESULT setSlaveDispatchProvider( [in] IDispatch* xNewDispatchProvider );
+
+ [id(3), helpstring("method getMasterDispatchProvider")]
+ HRESULT getMasterDispatchProvider( [out,retval] IDispatch** retVal );
+
+ [id(4), helpstring("method setMasterDispatchProvider")]
+ HRESULT setMasterDispatchProvider( [in] IDispatch* xNewSupplier );
+
+// com.sun.star.frame.XDispatchProvider
+
+ [id(5), helpstring("method queryDispatch")]
+ HRESULT queryDispatch( [in] IDispatch* aURL,
+ [in] BSTR aTargetFrameName,
+ [in] long nSearchFlags,
+ [out,retval] IDispatch** retVal );
+
+ [id(6), helpstring("method queryDispatches")]
+ HRESULT queryDispatches( [in] SAFEARRAY(IDispatch*) aDescripts,
+ [out,retval] SAFEARRAY(VARIANT)* retVal );
+
+
+// com.sun.star.frame.XDispatch
+
+ [id(7), helpstring("method dispatch")]
+ HRESULT dispatch( [in] IDispatch* aURL,
+ [in] SAFEARRAY(VARIANT) aArgs );
+
+ [id(8), helpstring("method addStatusListener")]
+ HRESULT addStatusListener( [in] IDispatch* xControl,
+ [in] IDispatch* aURL );
+
+ [id(9), helpstring("method removeStatusListener")]
+ HRESULT removeStatusListener( [in] IDispatch* xControl,
+ [in] IDispatch* aURL );
+
+// com.sun.star.frame.XInterceptorInfo
+
+
+ [id(10), helpstring("method getInterceptedURLs")]
+ HRESULT getInterceptedURLs( [out,retval] SAFEARRAY(BSTR)* pVal );
+
+// the common UNO-COM staff
+ [propget, id(11), helpstring("property_implementedInterfaces")]
+ HRESULT Bridge_implementedInterfaces([out, retval] SAFEARRAY(BSTR) *pVal);
+
+};
+
+
+
+
+[
+ object,
+ uuid(029E9F1E-2B3F-4297-9160-8197DE7ED54F),
+ dual,
+ helpstring("ISOActionsApproval Interface"),
+ pointer_default(unique)
+]
+interface ISOActionsApproval : IDispatch
+{
+//com.sun.star.embed.XActionsApproval
+
+ [id(1), helpstring("method approveAction")]
+ HRESULT approveAction( [in] long aActionID,
+ [out,retval] boolean* pbApproval );
+
+// the common UNO-COM staff
+ [propget, id(2), helpstring("property_implementedInterfaces")]
+ HRESULT Bridge_implementedInterfaces([out, retval] SAFEARRAY(BSTR) *pVal);
+};
+
+
+
+[
+ uuid(61FA3F13-8061-4796-B055-3697ED28CB38),
+ version(1.0),
+ helpstring("so_activex 1.0 Type Library")
+]
+library SO_ACTIVEXLib
+{
+ importlib("stdole32.tlb");
+ importlib("stdole2.tlb");
+
+ [
+ uuid(7F760565-5719-4F04-BA86-112C474B10EA),
+ helpstring("_ISOActiveXEvents Interface")
+ ]
+ dispinterface _ISOActiveXEvents
+ {
+ properties:
+ methods:
+ };
+
+ [
+ uuid(67F2A879-82D5-4A6D-8CC5-FFB3C114B69D),
+ helpstring("SOActiveX Class")
+ ]
+ coclass SOActiveX
+ {
+ [default] interface ISOActiveX;
+ [default, source] dispinterface _ISOActiveXEvents;
+ };
+
+ [
+ uuid(EE51BD3E-8BB6-4FB8-B319-F65B1BE3B21D),
+ helpstring("SOComWindowPeer Class")
+ ]
+ coclass SOComWindowPeer
+ {
+ [default] interface ISOComWindowPeer;
+ };
+
+ [
+ uuid(C5D6D568-57DA-4D6C-819A-451CB565E682),
+ helpstring("SODispatchInterceptor Class")
+ ]
+ coclass SODispatchInterceptor
+ {
+ [default] interface ISODispatchInterceptor;
+ };
+
+ [
+ uuid(9F3697AC-7A18-4335-AF0A-65FAC2C35CC1),
+ helpstring("SOActionsApproval Class")
+ ]
+ coclass SOActionsApproval
+ {
+ [default] interface ISOActionsApproval;
+ };
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/activex/so_activex.rc b/extensions/source/activex/so_activex.rc
new file mode 100644
index 000000000..3891fa965
--- /dev/null
+++ b/extensions/source/activex/so_activex.rc
@@ -0,0 +1,124 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+//Microsoft Developer Studio generated resource script.
+
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+
+
+// Generated from the TEXTINCLUDE 2 resource.
+
+#include <winresrc.h>
+#define LB_ADDSTRING (WM_USER+1)
+#define CB_ADDSTRING (WM_USER+3)
+#define IDC_STATIC (-1)
+
+
+#undef APSTUDIO_READONLY_SYMBOLS
+
+
+// Russian resources
+
+//#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
+//#ifdef _WIN32
+//LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+//#pragma code_page(1251)
+//#endif //_WIN32
+
+
+
+// Bitmap
+
+
+//IDB_SOACTIVEX BITMAP DISCARDABLE "soacti.bmp"
+
+
+
+
+// REGISTRY
+
+
+IDR_SOACTIVEX REGISTRY DISCARDABLE "SOActiveX.rgs"
+IDR_SOCOMWINDOWPEER REGISTRY DISCARDABLE "SOComWindowPeer.rgs"
+IDR_SODISPATCHINTERCEPTOR REGISTRY DISCARDABLE "SODispatchInterceptor.rgs"
+//#endif // Russian resources
+
+
+
+
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+
+
+// TEXTINCLUDE
+
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "1 TYPELIB ""so_activex.tlb""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+
+// String Table
+
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_PROJNAME "so_activex"
+END
+
+#endif // English (U.S.) resources
+
+
+
+
+#ifndef APSTUDIO_INVOKED
+
+
+// Generated from the TEXTINCLUDE 3 resource.
+
+
+1 TYPELIB SO_ACTIVEX_TLB
+
+
+#endif // not APSTUDIO_INVOKED
+
diff --git a/extensions/source/bibliography/bib.component b/extensions/source/bibliography/bib.component
new file mode 100644
index 000000000..19841cc9e
--- /dev/null
+++ b/extensions/source/bibliography/bib.component
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.extensions.Bibliography"
+ constructor="extensions_BibliographyLoader_get_implementation">
+ <service name="com.sun.star.frame.Bibliography"/>
+ <service name="com.sun.star.frame.FrameLoader"/>
+ </implementation>
+</component>
diff --git a/extensions/source/bibliography/bibbeam.cxx b/extensions/source/bibliography/bibbeam.cxx
new file mode 100644
index 000000000..a38c72385
--- /dev/null
+++ b/extensions/source/bibliography/bibbeam.cxx
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <vcl/taskpanelist.hxx>
+#include <tools/debug.hxx>
+#include "bibbeam.hxx"
+#include "datman.hxx"
+#include "bibtools.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+
+
+#define ID_TOOLBAR 1
+#define ID_GRIDWIN 2
+
+namespace bib
+{
+
+ void HandleTaskPaneList( vcl::Window* pWindow, bool bAddToList )
+ {
+ vcl::Window* pParent = pWindow->GetParent();
+
+ DBG_ASSERT( pParent, "-GetTaskPaneList(): everybody here should have a parent!" );
+
+ SystemWindow* pSysWin = pParent->GetSystemWindow();
+ if( pSysWin )
+ {
+ TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList();
+ if( pTaskPaneList )
+ {
+ if( bAddToList )
+ pTaskPaneList->AddWindow( pWindow );
+ else
+ pTaskPaneList->RemoveWindow( pWindow );
+ }
+ }
+ }
+
+
+ class BibGridwin
+ :public vcl::Window //DockingWindow
+ {
+ private:
+ Reference< awt::XWindow > m_xGridWin;
+ Reference< awt::XControlModel > m_xGridModel;
+ Reference< awt::XControl > m_xControl;
+ Reference< awt::XControlContainer > m_xControlContainer;
+ Reference< frame::XDispatchProviderInterception> m_xDispatchProviderInterception;
+
+ protected:
+
+ virtual void Resize() override;
+
+ public:
+
+ BibGridwin(vcl::Window* pParent, WinBits nStyle );
+ virtual ~BibGridwin() override;
+ virtual void dispose() override;
+
+ void createGridWin(const Reference< awt::XControlModel > & xDbForm);
+ void disposeGridWin();
+
+ const Reference< awt::XControlContainer >& getControlContainer() const { return m_xControlContainer; }
+ const Reference< frame::XDispatchProviderInterception>& getDispatchProviderInterception() const { return m_xDispatchProviderInterception; }
+
+ virtual void GetFocus() override;
+ };
+
+ BibGridwin::BibGridwin( vcl::Window* _pParent, WinBits _nStyle ) : Window( _pParent, _nStyle )
+ {
+ m_xControlContainer = VCLUnoHelper::CreateControlContainer(this);
+
+ AddToTaskPaneList( this );
+ }
+
+ BibGridwin::~BibGridwin()
+ {
+ disposeOnce();
+ }
+
+ void BibGridwin::dispose()
+ {
+ RemoveFromTaskPaneList( this );
+
+ disposeGridWin();
+ vcl::Window::dispose();
+ }
+
+ void BibGridwin::Resize()
+ {
+ if(m_xGridWin.is())
+ {
+ ::Size aSize = GetOutputSizePixel();
+ m_xGridWin->setPosSize(0, 0, aSize.Width(),aSize.Height(), awt::PosSize::SIZE);
+ }
+ }
+
+ void BibGridwin::createGridWin(const uno::Reference< awt::XControlModel > & xGModel)
+ {
+ m_xGridModel = xGModel;
+
+ if( !m_xControlContainer.is())
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+
+ if ( !m_xGridModel.is())
+ return;
+
+ uno::Reference< XPropertySet > xPropSet( m_xGridModel, UNO_QUERY );
+
+ if ( xPropSet.is() && m_xGridModel.is() )
+ {
+ uno::Any aAny = xPropSet->getPropertyValue( "DefaultControl" );
+ OUString aControlName;
+ aAny >>= aControlName;
+
+ m_xControl.set( xContext->getServiceManager()->createInstanceWithContext(aControlName, xContext), UNO_QUERY_THROW );
+ m_xControl->setModel( m_xGridModel );
+ }
+
+ if ( !m_xControl.is() )
+ return;
+
+ // Peer as Child to the FrameWindow
+ m_xControlContainer->addControl("GridControl", m_xControl);
+ m_xGridWin.set(m_xControl, UNO_QUERY );
+ m_xDispatchProviderInterception.set(m_xControl, UNO_QUERY );
+ m_xGridWin->setVisible( true );
+ m_xControl->setDesignMode( true );
+ // initially switch on the design mode - switch it off _after_ loading the form
+
+ ::Size aSize = GetOutputSizePixel();
+ m_xGridWin->setPosSize(0, 0, aSize.Width(),aSize.Height(), awt::PosSize::POSSIZE);
+ }
+
+ void BibGridwin::disposeGridWin()
+ {
+ if ( m_xControl.is() )
+ {
+ Reference< awt::XControl > xDel( m_xControl );
+ m_xControl = nullptr;
+ m_xGridWin = nullptr;
+
+ m_xControlContainer->removeControl( xDel );
+ xDel->dispose();
+ }
+ }
+
+ void BibGridwin::GetFocus()
+ {
+ if(m_xGridWin.is())
+ m_xGridWin->setFocus();
+ }
+
+ BibBeamer::BibBeamer( vcl::Window* _pParent, BibDataManager* _pDM )
+ :BibSplitWindow( _pParent, WB_3DLOOK | WB_NOSPLITDRAW )
+ ,pDatMan( _pDM )
+ ,pToolBar( nullptr )
+ ,pGridWin( nullptr )
+ {
+ createToolBar();
+ createGridWin();
+ pDatMan->SetToolbar(pToolBar);
+ pGridWin->Show();
+ connectForm( pDatMan );
+ }
+
+ BibBeamer::~BibBeamer()
+ {
+ disposeOnce();
+ }
+
+ void BibBeamer::dispose()
+ {
+ if ( isFormConnected() )
+ disconnectForm();
+
+ if ( pToolBar )
+ pDatMan->SetToolbar(nullptr);
+
+ pToolBar.disposeAndClear();
+ pGridWin.disposeAndClear();
+ BibSplitWindow::dispose();
+ }
+
+ void BibBeamer::createToolBar()
+ {
+ pToolBar= VclPtr<BibToolBar>::Create(this, LINK( this, BibBeamer, RecalcLayout_Impl ));
+ ::Size aSize=pToolBar->get_preferred_size();
+ InsertItem(ID_TOOLBAR, pToolBar, aSize.Height(), 0, 0, SplitWindowItemFlags::Fixed );
+ if ( m_xController.is() )
+ pToolBar->SetXController( m_xController );
+ }
+
+ void BibBeamer::createGridWin()
+ {
+ pGridWin = VclPtr<BibGridwin>::Create(this,0);
+
+ InsertItem(ID_GRIDWIN, pGridWin, 40, 1, 0, SplitWindowItemFlags::RelativeSize );
+
+ pGridWin->createGridWin( pDatMan->updateGridModel() );
+ }
+
+ Reference< awt::XControlContainer > BibBeamer::getControlContainer()
+ {
+ Reference< awt::XControlContainer > xReturn;
+ if ( pGridWin )
+ xReturn = pGridWin->getControlContainer();
+ return xReturn;
+ }
+
+ Reference< frame::XDispatchProviderInterception > BibBeamer::getDispatchProviderInterception() const
+ {
+ Reference< frame::XDispatchProviderInterception > xReturn;
+ if ( pGridWin )
+ xReturn = pGridWin->getDispatchProviderInterception();
+ return xReturn;
+ }
+
+ void BibBeamer::SetXController(const uno::Reference< frame::XController > & xCtr)
+ {
+ m_xController = xCtr;
+
+ if ( pToolBar )
+ pToolBar->SetXController( m_xController );
+
+ }
+
+ void BibBeamer::GetFocus()
+ {
+ if( pGridWin )
+ pGridWin->GrabFocus();
+ }
+
+ IMPL_LINK_NOARG( BibBeamer, RecalcLayout_Impl, void*, void )
+ {
+ tools::Long nHeight = pToolBar->get_preferred_size().Height();
+ SetItemSize( ID_TOOLBAR, nHeight );
+ }
+
+} // namespace bib
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibbeam.hxx b/extensions/source/bibliography/bibbeam.hxx
new file mode 100644
index 000000000..4cc32d7da
--- /dev/null
+++ b/extensions/source/bibliography/bibbeam.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+#include "toolbar.hxx"
+#include "formcontrolcontainer.hxx"
+#include "bibshortcuthandler.hxx"
+
+class BibDataManager;
+
+
+namespace bib
+{
+
+
+ class BibGridwin;
+ class BibBeamer final
+ :public BibSplitWindow
+ ,public FormControlContainer
+ {
+ css::uno::Reference< css::frame::XController > m_xController;
+
+ BibDataManager* pDatMan;
+ VclPtr<BibToolBar> pToolBar;
+ VclPtr<BibGridwin> pGridWin;
+
+ DECL_LINK( RecalcLayout_Impl, void*, void );
+
+ void createToolBar();
+ void createGridWin();
+
+ // FormControlContainer ----------
+ virtual css::uno::Reference< css::awt::XControlContainer >
+ getControlContainer() override;
+ public:
+ css::uno::Reference< css::frame::XDispatchProviderInterception >
+ getDispatchProviderInterception() const;
+
+ BibBeamer(vcl::Window* pParent,BibDataManager* pDatMan );
+ virtual ~BibBeamer() override;
+ virtual void dispose() override;
+
+ void SetXController(const css::uno::Reference< css::frame::XController > &);
+
+ virtual void GetFocus() override;
+ };
+
+
+} // namespace bib
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibconfig.cxx b/extensions/source/bibliography/bibconfig.cxx
new file mode 100644
index 000000000..14203ae6c
--- /dev/null
+++ b/extensions/source/bibliography/bibconfig.cxx
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include "bibconfig.hxx"
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <o3tl/any.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdb;
+
+
+constexpr OUStringLiteral cDataSourceHistory = u"DataSourceHistory";
+
+Sequence<OUString> const & BibConfig::GetPropertyNames()
+{
+ static Sequence<OUString> aNames =
+ {
+ "CurrentDataSource/DataSourceName",
+ "CurrentDataSource/Command",
+ "CurrentDataSource/CommandType",
+ "BeamerHeight",
+ "ViewHeight",
+ "QueryText",
+ "QueryField",
+ "ShowColumnAssignmentWarning"
+ };
+ return aNames;
+}
+
+BibConfig::BibConfig()
+ : ConfigItem("Office.DataAccess/Bibliography", ConfigItemMode::NONE)
+ , nTblOrQuery(0)
+ , nBeamerSize(0)
+ , nViewSize(0)
+ , bShowColumnAssignmentWarning(false)
+{
+ //Names of the default columns
+ aColumnDefaults[0] = "Identifier";
+ aColumnDefaults[1] = "BibliographyType";
+ aColumnDefaults[2] = "Author";
+ aColumnDefaults[3] = "Title";
+ aColumnDefaults[4] = "Year";
+ aColumnDefaults[5] = "ISBN";
+ aColumnDefaults[6] = "Booktitle";
+ aColumnDefaults[7] = "Chapter";
+ aColumnDefaults[8] = "Edition";
+ aColumnDefaults[9] = "Editor";
+ aColumnDefaults[10] = "Howpublished";
+ aColumnDefaults[11] = "Institution";
+ aColumnDefaults[12] = "Journal";
+ aColumnDefaults[13] = "Month";
+ aColumnDefaults[14] = "Note";
+ aColumnDefaults[15] = "Annote";
+ aColumnDefaults[16] = "Number";
+ aColumnDefaults[17] = "Organizations";
+ aColumnDefaults[18] = "Pages";
+ aColumnDefaults[19] = "Publisher";
+ aColumnDefaults[20] = "Address";
+ aColumnDefaults[21] = "School";
+ aColumnDefaults[22] = "Series";
+ aColumnDefaults[23] = "ReportType";
+ aColumnDefaults[24] = "Volume";
+ aColumnDefaults[25] = "URL";
+ aColumnDefaults[26] = "Custom1";
+ aColumnDefaults[27] = "Custom2";
+ aColumnDefaults[28] = "Custom3";
+ aColumnDefaults[29] = "Custom4";
+ aColumnDefaults[30] = "Custom5";
+ aColumnDefaults[31] = "LocalURL";
+
+
+ const Sequence< OUString > aPropertyNames = GetPropertyNames();
+ const Sequence<Any> aPropertyValues = GetProperties( aPropertyNames );
+ const Any* pValues = aPropertyValues.getConstArray();
+ if(aPropertyValues.getLength() == aPropertyNames.getLength())
+ {
+ for(int nProp = 0; nProp < aPropertyNames.getLength(); nProp++)
+ {
+ if(pValues[nProp].hasValue())
+ {
+ switch(nProp)
+ {
+ case 0: pValues[nProp] >>= sDataSource; break;
+ case 1: pValues[nProp] >>= sTableOrQuery; break;
+ case 2: pValues[nProp] >>= nTblOrQuery; break;
+ case 3: pValues[nProp] >>= nBeamerSize; break;
+ case 4: pValues[nProp] >>= nViewSize ; break;
+ case 5: pValues[nProp] >>= sQueryText ; break;
+ case 6: pValues[nProp] >>= sQueryField; break;
+ case 7:
+ bShowColumnAssignmentWarning = *o3tl::doAccess<bool>(pValues[nProp]);
+ break;
+ }
+ }
+ }
+ }
+ const Sequence< OUString > aNodeNames = GetNodeNames(cDataSourceHistory);
+ for(OUString const & nodeName : aNodeNames)
+ {
+ Sequence<OUString> aHistoryNames(3);
+ OUString* pHistoryNames = aHistoryNames.getArray();
+
+ OUString sPrefix = OUString::Concat(cDataSourceHistory) + "/" + nodeName + "/";
+ pHistoryNames[0] = sPrefix + "DataSourceName";
+ pHistoryNames[1] = sPrefix + "Command";
+ pHistoryNames[2] = sPrefix + "CommandType";
+
+ Sequence<Any> aHistoryValues = GetProperties( aHistoryNames );
+ const Any* pHistoryValues = aHistoryValues.getConstArray();
+
+ if(aHistoryValues.getLength() == aHistoryNames.getLength())
+ {
+ Mapping* pMapping = new Mapping;
+ pHistoryValues[0] >>= pMapping->sURL;
+ pHistoryValues[1] >>= pMapping->sTableName;
+ pHistoryValues[2] >>= pMapping->nCommandType;
+ //field assignment is contained in another set
+ sPrefix += "Fields";
+ const Sequence< OUString > aAssignmentNodeNames = GetNodeNames(sPrefix);
+ Sequence<OUString> aAssignmentPropertyNames(aAssignmentNodeNames.getLength() * 2);
+ OUString* pAssignmentPropertyNames = aAssignmentPropertyNames.getArray();
+ sal_Int16 nFieldIdx = 0;
+ for(OUString const & assignName : aAssignmentNodeNames)
+ {
+ OUString sSubPrefix = sPrefix + "/" + assignName;
+ pAssignmentPropertyNames[nFieldIdx] = sSubPrefix;
+ pAssignmentPropertyNames[nFieldIdx++] += "/ProgrammaticFieldName";
+ pAssignmentPropertyNames[nFieldIdx] = sSubPrefix;
+ pAssignmentPropertyNames[nFieldIdx++] += "/AssignedFieldName";
+ }
+ Sequence<Any> aAssignmentValues = GetProperties(aAssignmentPropertyNames);
+ const Any* pAssignmentValues = aAssignmentValues.getConstArray();
+ OUString sTempLogical;
+ OUString sTempReal;
+ sal_Int16 nSetMapping = 0;
+ nFieldIdx = 0;
+ for(sal_Int32 nFieldVal = 0; nFieldVal < aAssignmentValues.getLength() / 2; nFieldVal++)
+ {
+ pAssignmentValues[nFieldIdx++] >>= sTempLogical;
+ pAssignmentValues[nFieldIdx++] >>= sTempReal;
+ if(!(sTempLogical.isEmpty() || sTempReal.isEmpty()))
+ {
+ pMapping->aColumnPairs[nSetMapping].sLogicalColumnName = sTempLogical;
+ pMapping->aColumnPairs[nSetMapping++].sRealColumnName = sTempReal;
+ }
+ }
+ mvMappings.push_back(std::unique_ptr<Mapping>(pMapping));
+ }
+ }
+}
+
+BibConfig::~BibConfig()
+{
+ assert(!IsModified()); // should have been committed
+}
+
+BibDBDescriptor BibConfig::GetBibliographyURL()
+{
+ BibDBDescriptor aRet;
+ aRet.sDataSource = sDataSource;
+ aRet.sTableOrQuery = sTableOrQuery;
+ aRet.nCommandType = nTblOrQuery;
+ return aRet;
+};
+
+void BibConfig::SetBibliographyURL(const BibDBDescriptor& rDesc)
+{
+ sDataSource = rDesc.sDataSource;
+ sTableOrQuery = rDesc.sTableOrQuery;
+ nTblOrQuery = rDesc.nCommandType;
+ SetModified();
+};
+
+void BibConfig::Notify( const css::uno::Sequence<OUString>& )
+{
+}
+
+void BibConfig::ImplCommit()
+{
+ PutProperties(
+ GetPropertyNames(),
+ {css::uno::Any(sDataSource), css::uno::Any(sTableOrQuery),
+ css::uno::Any(nTblOrQuery), css::uno::Any(nBeamerSize),
+ css::uno::Any(nViewSize), css::uno::Any(sQueryText),
+ css::uno::Any(sQueryField),
+ css::uno::Any(bShowColumnAssignmentWarning)});
+ ClearNodeSet(cDataSourceHistory);
+ Sequence< PropertyValue > aNodeValues(mvMappings.size() * 3);
+ PropertyValue* pNodeValues = aNodeValues.getArray();
+
+ sal_Int32 nIndex = 0;
+ for(sal_Int32 i = 0; i < static_cast<sal_Int32>(mvMappings.size()); i++)
+ {
+ const Mapping* pMapping = mvMappings[i].get();
+ OUString sPrefix = OUString::Concat(cDataSourceHistory) + "/_" + OUString::number(i) + "/";
+ pNodeValues[nIndex].Name = sPrefix + "DataSourceName";
+ pNodeValues[nIndex++].Value <<= pMapping->sURL;
+ pNodeValues[nIndex].Name = sPrefix + "Command";
+ pNodeValues[nIndex++].Value <<= pMapping->sTableName;
+ pNodeValues[nIndex].Name = sPrefix + "CommandType";
+ pNodeValues[nIndex++].Value <<= pMapping->nCommandType;
+ SetSetProperties(cDataSourceHistory, aNodeValues);
+
+ sPrefix += "Fields";
+ sal_Int32 nFieldAssignment = 0;
+ OUString sFieldName = "/ProgrammaticFieldName";
+ OUString sDatabaseFieldName = "/AssignedFieldName";
+ ClearNodeSet( sPrefix );
+
+ while(nFieldAssignment < COLUMN_COUNT &&
+ !pMapping->aColumnPairs[nFieldAssignment].sLogicalColumnName.isEmpty())
+ {
+ OUString sSubPrefix = sPrefix + "/_" + OUString::number(nFieldAssignment);
+ Sequence< PropertyValue > aAssignmentValues
+ {
+ comphelper::makePropertyValue(sSubPrefix + sFieldName, pMapping->aColumnPairs[nFieldAssignment].sLogicalColumnName),
+ comphelper::makePropertyValue(sSubPrefix + sDatabaseFieldName, pMapping->aColumnPairs[nFieldAssignment].sRealColumnName)
+ };
+ SetSetProperties( sPrefix, aAssignmentValues );
+ nFieldAssignment++;
+ }
+ }
+}
+
+const Mapping* BibConfig::GetMapping(const BibDBDescriptor& rDesc) const
+{
+ for(std::unique_ptr<Mapping> const & i : mvMappings)
+ {
+ Mapping& rMapping = *i;
+ bool bURLEqual = rDesc.sDataSource == rMapping.sURL;
+ if(rDesc.sTableOrQuery == rMapping.sTableName && bURLEqual)
+ return &rMapping;
+ }
+ return nullptr;
+}
+
+void BibConfig::SetMapping(const BibDBDescriptor& rDesc, const Mapping* pSetMapping)
+{
+ for(size_t i = 0; i < mvMappings.size(); i++)
+ {
+ Mapping& rMapping = *mvMappings[i];
+ bool bURLEqual = rDesc.sDataSource == rMapping.sURL;
+ if(rDesc.sTableOrQuery == rMapping.sTableName && bURLEqual)
+ {
+ mvMappings.erase(mvMappings.begin()+i);
+ break;
+ }
+ }
+ mvMappings.push_back(std::make_unique<Mapping>(*pSetMapping));
+ SetModified();
+}
+
+DBChangeDialogConfig_Impl::DBChangeDialogConfig_Impl()
+{
+}
+
+DBChangeDialogConfig_Impl::~DBChangeDialogConfig_Impl()
+{
+}
+
+const Sequence<OUString>& DBChangeDialogConfig_Impl::GetDataSourceNames()
+{
+ if(!aSourceNames.hasElements())
+ {
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ Reference<XDatabaseContext> xDBContext = DatabaseContext::create(xContext);
+ aSourceNames = xDBContext->getElementNames();
+ }
+ return aSourceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibconfig.hxx b/extensions/source/bibliography/bibconfig.hxx
new file mode 100644
index 000000000..0d81491ff
--- /dev/null
+++ b/extensions/source/bibliography/bibconfig.hxx
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <unotools/configitem.hxx>
+#include <tools/long.hxx>
+#include <vector>
+#include <memory>
+
+struct Mapping;
+typedef std::vector<std::unique_ptr<Mapping> > MappingArray;
+
+
+#define COLUMN_COUNT 32
+#define IDENTIFIER_POS 0
+#define AUTHORITYTYPE_POS 1
+#define AUTHOR_POS 2
+#define TITLE_POS 3
+#define YEAR_POS 4
+#define ISBN_POS 5
+#define BOOKTITLE_POS 6
+#define CHAPTER_POS 7
+#define EDITION_POS 8
+#define EDITOR_POS 9
+#define HOWPUBLISHED_POS 10
+#define INSTITUTION_POS 11
+#define JOURNAL_POS 12
+#define MONTH_POS 13
+#define NOTE_POS 14
+#define ANNOTE_POS 15
+#define NUMBER_POS 16
+#define ORGANIZATIONS_POS 17
+#define PAGES_POS 18
+#define PUBLISHER_POS 19
+#define ADDRESS_POS 20
+#define SCHOOL_POS 21
+#define SERIES_POS 22
+#define REPORTTYPE_POS 23
+#define VOLUME_POS 24
+#define URL_POS 25
+#define CUSTOM1_POS 26
+#define CUSTOM2_POS 27
+#define CUSTOM3_POS 28
+#define CUSTOM4_POS 29
+#define CUSTOM5_POS 30
+#define LOCAL_URL_POS 31
+
+struct StringPair
+{
+ OUString sRealColumnName;
+ OUString sLogicalColumnName;
+};
+
+struct Mapping
+{
+ OUString sTableName;
+ OUString sURL;
+ sal_Int16 nCommandType;
+ StringPair aColumnPairs[COLUMN_COUNT];
+
+ Mapping() :
+ nCommandType(0){}
+};
+
+struct BibDBDescriptor
+{
+ OUString sDataSource;
+ OUString sTableOrQuery;
+ sal_Int32 nCommandType;
+};
+
+
+class BibConfig : public utl::ConfigItem
+{
+ OUString sDataSource;
+ OUString sTableOrQuery;
+ sal_Int32 nTblOrQuery;
+
+ OUString sQueryField;
+ OUString sQueryText;
+ MappingArray mvMappings;
+ tools::Long nBeamerSize;
+ tools::Long nViewSize;
+ bool bShowColumnAssignmentWarning;
+
+ OUString aColumnDefaults[COLUMN_COUNT];
+
+ static css::uno::Sequence<OUString> const & GetPropertyNames();
+
+ virtual void ImplCommit() override;
+
+public:
+ BibConfig();
+ virtual ~BibConfig() override;
+
+ virtual void Notify( const css::uno::Sequence<OUString>& aPropertyNames) override;
+
+ BibDBDescriptor GetBibliographyURL();
+ void SetBibliographyURL(const BibDBDescriptor& rDesc);
+
+ const Mapping* GetMapping(const BibDBDescriptor& rDesc) const;
+ void SetMapping(const BibDBDescriptor& rDesc, const Mapping* pMapping);
+
+ const OUString& GetDefColumnName(sal_uInt16 nIndex) const
+ {return aColumnDefaults[nIndex];}
+
+
+ void setBeamerSize(tools::Long nSize) {SetModified(); nBeamerSize = nSize;}
+ tools::Long getBeamerSize()const {return nBeamerSize;}
+ void setViewSize(tools::Long nSize) {SetModified(); nViewSize = nSize;}
+ tools::Long getViewSize() const {return nViewSize;}
+
+ const OUString& getQueryField() const {return sQueryField;}
+ void setQueryField(const OUString& rSet) {SetModified(); sQueryField = rSet;}
+
+ const OUString& getQueryText() const {return sQueryText;}
+ void setQueryText(const OUString& rSet) {SetModified(); sQueryText = rSet;}
+
+ bool IsShowColumnAssignmentWarning() const
+ { return bShowColumnAssignmentWarning;}
+ void SetShowColumnAssignmentWarning(bool bSet)
+ { bShowColumnAssignmentWarning = bSet;}
+};
+
+class DBChangeDialogConfig_Impl
+{
+ css::uno::Sequence<OUString> aSourceNames;
+public:
+ DBChangeDialogConfig_Impl();
+ ~DBChangeDialogConfig_Impl();
+
+ const css::uno::Sequence<OUString>& GetDataSourceNames();
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibcont.cxx b/extensions/source/bibliography/bibcont.cxx
new file mode 100644
index 000000000..7c24b0fb3
--- /dev/null
+++ b/extensions/source/bibliography/bibcont.cxx
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <vcl/event.hxx>
+#include "bibconfig.hxx"
+
+
+#include "bibcont.hxx"
+
+
+BibShortCutHandler::~BibShortCutHandler()
+{
+}
+
+bool BibShortCutHandler::HandleShortCutKey( const KeyEvent& )
+{
+ return false;
+}
+
+
+BibWindow::BibWindow( vcl::Window* pParent, WinBits nStyle ) : Window( pParent, nStyle ), BibShortCutHandler( this )
+{
+}
+
+BibWindow::~BibWindow()
+{
+}
+
+
+BibSplitWindow::BibSplitWindow( vcl::Window* pParent, WinBits nStyle ) : SplitWindow( pParent, nStyle ), BibShortCutHandler( this )
+{
+}
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+//split window size is a percent value
+#define WIN_MIN_HEIGHT 10
+#define WIN_STEP_SIZE 5
+
+BibWindowContainer::BibWindowContainer( vcl::Window* pParent, BibShortCutHandler* pChildWin ) :
+ BibWindow( pParent, WB_3DLOOK ),
+ pChild( pChildWin )
+{
+ if(pChild!=nullptr)
+ {
+ vcl::Window* pChildWindow = GetChild();
+ pChildWindow->SetParent(this);
+ pChildWindow->Show();
+ pChildWindow->SetPosPixel(Point(0,0));
+ }
+}
+
+BibWindowContainer::~BibWindowContainer()
+{
+ disposeOnce();
+}
+
+void BibWindowContainer::dispose()
+{
+ if( pChild )
+ {
+ VclPtr<vcl::Window> pDel = GetChild();
+ pChild = nullptr; // prevents GetFocus for child while deleting!
+ pDel.disposeAndClear();
+ }
+ vcl::Window::dispose();
+}
+
+void BibWindowContainer::Resize()
+{
+ if( pChild )
+ GetChild()->SetSizePixel( GetOutputSizePixel() );
+}
+
+void BibWindowContainer::GetFocus()
+{
+ if( pChild )
+ GetChild()->GrabFocus();
+}
+
+bool BibWindowContainer::HandleShortCutKey( const KeyEvent& rKeyEvent )
+{
+ return pChild && pChild->HandleShortCutKey( rKeyEvent );
+}
+
+
+BibBookContainer::BibBookContainer(vcl::Window* pParent):
+ BibSplitWindow(pParent,WB_3DLOOK),
+ pTopWin(nullptr),
+ pBottomWin(nullptr),
+ aIdle("extensions BibBookContainer Split Idle")
+{
+ pBibMod = OpenBibModul();
+ aIdle.SetInvokeHandler(LINK( this, BibBookContainer, SplitHdl));
+ aIdle.SetPriority(TaskPriority::LOWEST);
+}
+
+BibBookContainer::~BibBookContainer()
+{
+ disposeOnce();
+}
+
+void BibBookContainer::dispose()
+{
+ if( pTopWin )
+ {
+ VclPtr<vcl::Window> pDel = pTopWin;
+ pTopWin = nullptr; // prevents GetFocus for child while deleting!
+ pDel.disposeAndClear();
+ }
+
+ if( pBottomWin )
+ {
+ VclPtr<vcl::Window> pDel = pBottomWin;
+ pBottomWin = nullptr; // prevents GetFocus for child while deleting!
+ pDel.disposeAndClear();
+ }
+
+ CloseBibModul( pBibMod );
+ pTopWin.clear();
+ pBottomWin.clear();
+ BibSplitWindow::dispose();
+}
+
+void BibBookContainer::Split()
+{
+ aIdle.Start();
+}
+IMPL_LINK_NOARG( BibBookContainer, SplitHdl, Timer*, void)
+{
+ tools::Long nSize= GetItemSize( TOP_WINDOW);
+ BibConfig* pConfig = BibModul::GetConfig();
+ pConfig->setBeamerSize(nSize);
+ nSize = GetItemSize( BOTTOM_WINDOW);
+ pConfig->setViewSize(nSize);
+}
+
+void BibBookContainer::createTopFrame( BibShortCutHandler* pWin )
+{
+ if(pTopWin)
+ {
+ RemoveItem(TOP_WINDOW);
+ pTopWin.disposeAndClear();
+ }
+ pTopWin=VclPtr<BibWindowContainer>::Create(this,pWin);
+ pTopWin->Show();
+ BibConfig* pConfig = BibModul::GetConfig();
+ tools::Long nSize = pConfig->getBeamerSize();
+ InsertItem(TOP_WINDOW, pTopWin, nSize, 1, 0, SplitWindowItemFlags::PercentSize );
+
+}
+
+void BibBookContainer::createBottomFrame( BibShortCutHandler* pWin )
+{
+ if(pBottomWin)
+ {
+ RemoveItem(BOTTOM_WINDOW);
+ pBottomWin.disposeAndClear();
+ }
+
+ pBottomWin=VclPtr<BibWindowContainer>::Create(this,pWin);
+
+ BibConfig* pConfig = BibModul::GetConfig();
+ tools::Long nSize = pConfig->getViewSize();
+ InsertItem(BOTTOM_WINDOW, pBottomWin, nSize, 1, 0, SplitWindowItemFlags::PercentSize );
+
+}
+
+void BibBookContainer::GetFocus()
+{
+ if( pBottomWin )
+ pBottomWin->GrabFocus();
+}
+
+bool BibBookContainer::PreNotify( NotifyEvent& rNEvt )
+{
+ bool bHandled = false;
+ if( MouseNotifyEvent::KEYINPUT == rNEvt.GetType() )
+ {
+ const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
+ const vcl::KeyCode aKeyCode = pKEvt->GetKeyCode();
+ sal_uInt16 nKey = aKeyCode.GetCode();
+ const sal_uInt16 nModifier = aKeyCode.GetModifier();
+
+ if( KEY_MOD2 == nModifier )
+ {
+ if( KEY_UP == nKey || KEY_DOWN == nKey )
+ {
+ if(pTopWin && pBottomWin)
+ {
+ sal_uInt16 nFirstWinId = KEY_UP == nKey ? TOP_WINDOW : BOTTOM_WINDOW;
+ sal_uInt16 nSecondWinId = KEY_UP == nKey ? BOTTOM_WINDOW : TOP_WINDOW;
+ tools::Long nHeight = GetItemSize( nFirstWinId );
+ nHeight -= WIN_STEP_SIZE;
+ if(nHeight < WIN_MIN_HEIGHT)
+ nHeight = WIN_MIN_HEIGHT;
+ SetItemSize( nFirstWinId, nHeight );
+ SetItemSize( nSecondWinId, 100 - nHeight );
+ }
+ bHandled = true;
+ }
+ else if( pKEvt->GetCharCode() && HandleShortCutKey( *pKEvt ) )
+ bHandled = true;
+ }
+ }
+
+ return bHandled;
+}
+
+bool BibBookContainer::HandleShortCutKey( const KeyEvent& rKeyEvent )
+{
+ bool bRet = false;
+
+ if( pTopWin )
+ bRet = pTopWin->HandleShortCutKey( rKeyEvent );
+
+ if( !bRet && pBottomWin )
+ bRet = pBottomWin->HandleShortCutKey( rKeyEvent );
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibcont.hxx b/extensions/source/bibliography/bibcont.hxx
new file mode 100644
index 000000000..d90952ac3
--- /dev/null
+++ b/extensions/source/bibliography/bibcont.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+#include "bibshortcuthandler.hxx"
+
+#include "bibmod.hxx"
+
+#define TOP_WINDOW 1
+#define BOTTOM_WINDOW 2
+
+class BibWindowContainer : public BibWindow //Window
+{
+ private:
+ // !BibShortCutHandler is also always a Window!
+ BibShortCutHandler* pChild;
+
+ protected:
+ virtual void Resize() override;
+
+ public:
+ BibWindowContainer( vcl::Window* pParent, BibShortCutHandler* pChild);
+ virtual ~BibWindowContainer() override;
+ virtual void dispose() override;
+
+ inline vcl::Window* GetChild();
+
+ virtual void GetFocus() override;
+
+ virtual bool HandleShortCutKey( const KeyEvent& rKeyEvent ) override; // returns true, if key was handled
+
+ using Window::GetChild;
+};
+
+inline vcl::Window* BibWindowContainer::GetChild()
+{
+ return pChild ? pChild->GetWindow() : nullptr;
+}
+
+
+class BibBookContainer: public BibSplitWindow
+{
+ private:
+
+ VclPtr<BibWindowContainer> pTopWin;
+ VclPtr<BibWindowContainer> pBottomWin;
+ HdlBibModul pBibMod;
+ Idle aIdle;
+
+ DECL_LINK( SplitHdl, Timer*, void );
+
+ protected:
+
+ virtual void Split() override;
+
+ virtual bool PreNotify( NotifyEvent& rNEvt ) override;
+
+ public:
+
+ explicit BibBookContainer(vcl::Window* pParent );
+ virtual ~BibBookContainer() override;
+ virtual void dispose() override;
+
+ // !BibShortCutHandler is also always a Window!
+ void createTopFrame( BibShortCutHandler* pWin );
+
+ void createBottomFrame( BibShortCutHandler* pWin );
+
+ virtual void GetFocus() override;
+
+ virtual bool HandleShortCutKey( const KeyEvent& rKeyEvent ) override; // returns true, if key was handled
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibload.cxx b/extensions/source/bibliography/bibload.cxx
new file mode 100644
index 000000000..82c08e007
--- /dev/null
+++ b/extensions/source/bibliography/bibload.cxx
@@ -0,0 +1,609 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <svl/itemprop.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/frame/XFrameLoader.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/text/BibliographyDataField.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+
+#include "bibresid.hxx"
+#include <strings.hrc>
+#include "bibcont.hxx"
+#include "bibbeam.hxx"
+#include "bibmod.hxx"
+#include "bibview.hxx"
+#include "framectr.hxx"
+#include "datman.hxx"
+#include "bibconfig.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <o3tl/string_view.hxx>
+
+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::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+
+namespace {
+
+class BibliographyLoader : public cppu::WeakImplHelper
+ < XServiceInfo, XNameAccess, XPropertySet, XFrameLoader >
+{
+ HdlBibModul m_pBibMod;
+ rtl::Reference<BibDataManager> m_xDatMan;
+ Reference< XNameAccess > m_xColumns;
+ Reference< XResultSet > m_xCursor;
+
+private:
+
+ void loadView(const Reference< XFrame > & aFrame,
+ const Reference< XLoadEventListener > & aListener);
+
+ BibDataManager* GetDataManager()const;
+ Reference< XNameAccess > const & GetDataColumns() const;
+ Reference< XResultSet > const & GetDataCursor() const;
+ Reference< sdb::XColumn > GetIdentifierColumn() const;
+
+public:
+ BibliographyLoader();
+ virtual ~BibliographyLoader() override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ //XNameAccess
+ virtual Any SAL_CALL getByName(const OUString& aName) override;
+ virtual Sequence< OUString > SAL_CALL getElementNames() override;
+ virtual sal_Bool SAL_CALL hasByName(const OUString& aName) override;
+
+ //XElementAccess
+ virtual Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ //XPropertySet
+ virtual Reference< XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(const OUString& PropertyName, const Any& aValue) override;
+ virtual Any SAL_CALL getPropertyValue(const OUString& PropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(const OUString& PropertyName, const Reference< XPropertyChangeListener > & aListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(const OUString& PropertyName, const Reference< XPropertyChangeListener > & aListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& PropertyName, const Reference< XVetoableChangeListener > & aListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName, const Reference< XVetoableChangeListener > & aListener) override;
+
+ // XLoader
+ virtual void SAL_CALL load(const Reference< XFrame > & aFrame, const OUString& aURL,
+ const Sequence< PropertyValue >& aArgs,
+ const Reference< XLoadEventListener > & aListener) override;
+ virtual void SAL_CALL cancel() override;
+};
+
+}
+
+BibliographyLoader::BibliographyLoader() :
+ m_pBibMod(nullptr)
+{
+}
+
+BibliographyLoader::~BibliographyLoader()
+{
+ Reference< lang::XComponent > xComp(m_xCursor, UNO_QUERY);
+ if (xComp.is())
+ xComp->dispose();
+ if(m_pBibMod)
+ CloseBibModul(m_pBibMod);
+}
+
+
+// XServiceInfo
+OUString BibliographyLoader::getImplementationName()
+{
+ return "com.sun.star.extensions.Bibliography";
+}
+
+// XServiceInfo
+sal_Bool BibliographyLoader::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+// XServiceInfo
+Sequence< OUString > BibliographyLoader::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.FrameLoader", "com.sun.star.frame.Bibliography" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_BibliographyLoader_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new BibliographyLoader());
+}
+
+void BibliographyLoader::cancel()
+{
+ //!
+ //!
+}
+
+void BibliographyLoader::load(const Reference< XFrame > & rFrame, const OUString& rURL,
+ const Sequence< PropertyValue >& /*rArgs*/,
+ const Reference< XLoadEventListener > & rListener)
+{
+
+ SolarMutexGuard aGuard;
+
+ m_pBibMod = OpenBibModul();
+
+ std::u16string_view aPartName = o3tl::getToken(rURL, 1, '/' );
+ Reference<XPropertySet> xPrSet(rFrame, UNO_QUERY);
+ if(xPrSet.is())
+ {
+ Any aTitle;
+ aTitle <<= BibResId(RID_BIB_STR_FRAME_TITLE);
+ xPrSet->setPropertyValue("Title", aTitle);
+ }
+ if(aPartName == u"View" || aPartName == u"View1")
+ {
+ loadView(rFrame, rListener);
+ }
+}
+
+
+void BibliographyLoader::loadView(const Reference< XFrame > & rFrame,
+ const Reference< XLoadEventListener > & rListener)
+{
+ SolarMutexGuard aGuard;
+ //!
+ if(!m_pBibMod)
+ m_pBibMod = OpenBibModul();
+
+ m_xDatMan = BibModul::createDataManager();
+ BibDBDescriptor aBibDesc = BibModul::GetConfig()->GetBibliographyURL();
+
+ if(aBibDesc.sDataSource.isEmpty())
+ {
+ DBChangeDialogConfig_Impl aConfig;
+ const Sequence<OUString> aSources = aConfig.GetDataSourceNames();
+ if(aSources.hasElements())
+ aBibDesc.sDataSource = aSources.getConstArray()[0];
+ }
+
+ m_xDatMan->createDatabaseForm( aBibDesc );
+
+ Reference<awt::XWindow> aWindow = rFrame->getContainerWindow();
+
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow( aWindow );
+
+ VclPtrInstance<BibBookContainer> pMyWindow( pParent );
+ pMyWindow->Show();
+
+ VclPtrInstance< ::bib::BibView> pView( pMyWindow, m_xDatMan.get(), WB_VSCROLL | WB_HSCROLL | WB_3DLOOK );
+ pView->Show();
+ m_xDatMan->SetView( pView );
+
+ VclPtrInstance< ::bib::BibBeamer> pBeamer( pMyWindow, m_xDatMan.get() );
+ pBeamer->Show();
+ pMyWindow->createTopFrame(pBeamer);
+
+ pMyWindow->createBottomFrame(pView);
+
+ Reference< awt::XWindow > xWin ( pMyWindow->GetComponentInterface(), UNO_QUERY );
+
+ Reference< XController > xCtrRef( new BibFrameController_Impl( xWin, m_xDatMan.get() ) );
+
+ xCtrRef->attachFrame(rFrame);
+ rFrame->setComponent( xWin, xCtrRef);
+ pBeamer->SetXController(xCtrRef);
+
+ if (aWindow)
+ {
+ // not earlier because SetFocus() is triggered in setVisible()
+ aWindow->setVisible(true);
+ }
+
+ Reference<XLoadable>(m_xDatMan)->load();
+ m_xDatMan->RegisterInterceptor(pBeamer);
+
+ if ( rListener.is() )
+ rListener->loadFinished( this );
+
+ // attach menu bar
+ Reference< XPropertySet > xPropSet( rFrame, UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ Any a = xPropSet->getPropertyValue("LayoutManager");
+ a >>= xLayoutManager;
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+ }
+
+ if ( xLayoutManager.is() )
+ xLayoutManager->createElement( "private:resource/menubar/menubar" );
+}
+
+BibDataManager* BibliographyLoader::GetDataManager()const
+{
+ if(!m_xDatMan.is())
+ {
+ if(!m_pBibMod)
+ const_cast< BibliographyLoader* >( this )->m_pBibMod = OpenBibModul();
+ const_cast< BibliographyLoader* >( this )->m_xDatMan = BibModul::createDataManager();
+ }
+ return m_xDatMan.get();
+}
+
+Reference< XNameAccess > const & BibliographyLoader::GetDataColumns() const
+{
+ if (!m_xColumns.is())
+ {
+ Reference< XMultiServiceFactory > xMgr = comphelper::getProcessServiceFactory();
+ Reference< XRowSet > xRowSet(xMgr->createInstance("com.sun.star.sdb.RowSet"), UNO_QUERY);
+ Reference< XPropertySet > xResultSetProps(xRowSet, UNO_QUERY);
+ DBG_ASSERT(xResultSetProps.is() , "BibliographyLoader::GetDataCursor : invalid row set (no XResultSet or no XPropertySet) !");
+
+ BibDBDescriptor aBibDesc = BibModul::GetConfig()->GetBibliographyURL();
+
+ Any aBibUrlAny; aBibUrlAny <<= aBibDesc.sDataSource;
+ xResultSetProps->setPropertyValue("DataSourceName", aBibUrlAny);
+ Any aCommandType; aCommandType <<= aBibDesc.nCommandType;
+ xResultSetProps->setPropertyValue("CommandType", aCommandType);
+ Any aTableName; aTableName <<= aBibDesc.sTableOrQuery;
+ xResultSetProps->setPropertyValue("Command", aTableName);
+ Any aResultSetType; aResultSetType <<= sal_Int32(ResultSetType::SCROLL_INSENSITIVE);
+ xResultSetProps->setPropertyValue("ResultSetType", aResultSetType);
+ Any aResultSetCurrency; aResultSetCurrency <<= sal_Int32(ResultSetConcurrency::UPDATABLE);
+ xResultSetProps->setPropertyValue("ResultSetConcurrency", aResultSetCurrency);
+
+ bool bSuccess = false;
+ try
+ {
+ xRowSet->execute();
+ bSuccess = true;
+ }
+ catch(const SQLException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.biblio");
+ }
+ catch(const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.biblio");
+ bSuccess = false;
+ }
+
+ if (!bSuccess)
+ {
+ Reference< XComponent > xSetComp(xRowSet, UNO_QUERY);
+ if (xSetComp.is())
+ xSetComp->dispose();
+ xRowSet = nullptr;
+ }
+ else
+ const_cast<BibliographyLoader*>(this)->m_xCursor = xRowSet.get();
+
+ Reference< sdbcx::XColumnsSupplier > xSupplyCols(m_xCursor, UNO_QUERY);
+ if (xSupplyCols.is())
+ const_cast<BibliographyLoader*>(this)->m_xColumns = xSupplyCols->getColumns();
+ }
+
+ return m_xColumns;
+}
+
+Reference< sdb::XColumn > BibliographyLoader::GetIdentifierColumn() const
+{
+ BibDataManager* pDatMan = GetDataManager();
+ Reference< XNameAccess > xColumns = GetDataColumns();
+ OUString sIdentifierColumnName = pDatMan->GetIdentifierMapping();
+
+ Reference< sdb::XColumn > xReturn;
+ if (xColumns.is() && xColumns->hasByName(sIdentifierColumnName))
+ {
+ xReturn.set(xColumns->getByName(sIdentifierColumnName), UNO_QUERY);
+ }
+ return xReturn;
+}
+
+Reference< XResultSet > const & BibliographyLoader::GetDataCursor() const
+{
+ if (!m_xCursor.is())
+ GetDataColumns();
+ if (m_xCursor.is())
+ m_xCursor->first();
+ return m_xCursor;
+}
+
+static OUString lcl_AddProperty(const Reference< XNameAccess >& xColumns,
+ const Mapping* pMapping, const OUString& rColumnName)
+{
+ OUString sColumnName(rColumnName);
+ if(pMapping)
+ {
+ for(const auto & aColumnPair : pMapping->aColumnPairs)
+ {
+ if(aColumnPair.sLogicalColumnName == rColumnName)
+ {
+ sColumnName = aColumnPair.sRealColumnName;
+ break;
+ }
+ }
+ }
+ OUString uColumnName(sColumnName);
+ OUString uRet;
+ Reference< sdb::XColumn > xCol;
+ if (xColumns->hasByName(uColumnName))
+ xCol.set(xColumns->getByName(uColumnName), UNO_QUERY);
+ if (xCol.is())
+ uRet = xCol->getString();
+ return uRet;
+}
+
+Any BibliographyLoader::getByName(const OUString& rName)
+{
+ Any aRet;
+ try
+ {
+ BibDataManager* pDatMan = GetDataManager();
+ Reference< XResultSet > xCursor = GetDataCursor();
+ Reference< sdbcx::XColumnsSupplier > xSupplyCols(xCursor, UNO_QUERY);
+ Reference< XNameAccess > xColumns;
+ if (!xSupplyCols.is())
+ return aRet;
+ xColumns = xSupplyCols->getColumns();
+ DBG_ASSERT(xSupplyCols.is(), "BibliographyLoader::getByName : invalid columns returned by the data cursor (may be the result set is not alive ?) !");
+ if (!xColumns.is())
+ return aRet;
+
+ const OUString sIdentifierMapping = pDatMan->GetIdentifierMapping();
+ Reference< sdb::XColumn > xColumn;
+ if (xColumns->hasByName(sIdentifierMapping))
+ xColumn.set(xColumns->getByName(sIdentifierMapping), UNO_QUERY);
+ if (xColumn.is())
+ {
+ do
+ {
+ if ((rName == xColumn->getString()) && !xColumn->wasNull())
+ {
+ Sequence<PropertyValue> aPropSequ(COLUMN_COUNT);
+ PropertyValue* pValues = aPropSequ.getArray();
+ BibConfig* pConfig = BibModul::GetConfig();
+ BibDBDescriptor aBibDesc = BibModul::GetConfig()->GetBibliographyURL();
+ const Mapping* pMapping = pConfig->GetMapping(aBibDesc);
+ for(sal_uInt16 nEntry = 0; nEntry < COLUMN_COUNT; nEntry++)
+ {
+ const OUString& sColName = pConfig->GetDefColumnName(
+ nEntry);
+ pValues[nEntry].Name = sColName;
+ pValues[nEntry].Value <<= lcl_AddProperty(xColumns, pMapping, sColName);
+ }
+ aRet <<= aPropSequ;
+
+ break;
+ }
+ }
+ while(xCursor->next());
+ }
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.biblio");
+ }
+ return aRet;
+}
+
+Sequence< OUString > BibliographyLoader::getElementNames()
+{
+ Sequence< OUString > aRet(10);
+ int nRealNameCount = 0;
+ try
+ {
+ Reference< XResultSet > xCursor(GetDataCursor());
+ Reference< sdb::XColumn > xIdColumn(GetIdentifierColumn());
+ if (xIdColumn.is()) // implies xCursor.is()
+ {
+ do
+ {
+ OUString sTemp = xIdColumn->getString();
+ if (!sTemp.isEmpty() && !xIdColumn->wasNull())
+ {
+ int nLen = aRet.getLength();
+ if(nLen == nRealNameCount)
+ aRet.realloc(nLen + 10);
+ OUString* pArray = aRet.getArray();
+ pArray[nRealNameCount] = sTemp;
+ nRealNameCount++;
+ }
+ }
+ while (xCursor->next());
+ }
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.biblio");
+ }
+
+ aRet.realloc(nRealNameCount);
+ return aRet;
+}
+
+sal_Bool BibliographyLoader::hasByName(const OUString& rName)
+{
+ bool bRet = false;
+ try
+ {
+ Reference< XResultSet > xCursor = GetDataCursor();
+ Reference< sdb::XColumn > xIdColumn = GetIdentifierColumn();
+
+ if (xIdColumn.is()) // implies xCursor.is()
+ {
+ do
+ {
+ OUString sCurrentId = xIdColumn->getString();
+ if (!xIdColumn->wasNull() && rName.startsWith(sCurrentId))
+ {
+ bRet = true;
+ break;
+ }
+ }
+ while(xCursor->next());
+ }
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.biblio");
+ }
+ return bRet;
+}
+
+Type BibliographyLoader::getElementType()
+{
+ return cppu::UnoType<Sequence<PropertyValue>>::get();
+}
+
+sal_Bool BibliographyLoader::hasElements()
+{
+ Reference< XNameAccess > xColumns = GetDataColumns();
+ return xColumns.is() && xColumns->getElementNames().hasElements();
+}
+
+Reference< XPropertySetInfo > BibliographyLoader::getPropertySetInfo()
+{
+ static const SfxItemPropertyMapEntry aBibProps_Impl[] =
+ {
+ { u"BibliographyDataFieldNames", 0, cppu::UnoType<Sequence<PropertyValue>>::get(), PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static Reference< XPropertySetInfo > xRet =
+ SfxItemPropertySet(aBibProps_Impl).getPropertySetInfo();
+ return xRet;
+}
+
+void BibliographyLoader::setPropertyValue(const OUString& /*PropertyName*/,
+ const Any& /*aValue*/)
+{
+ throw UnknownPropertyException();
+ //no changeable properties
+}
+
+Any BibliographyLoader::getPropertyValue(const OUString& rPropertyName)
+{
+ Any aRet;
+ static const sal_uInt16 aInternalMapping[] =
+ {
+ IDENTIFIER_POS , // BibliographyDataField_IDENTIFIER
+ AUTHORITYTYPE_POS , // BibliographyDataField_BIBILIOGRAPHIC_TYPE
+ ADDRESS_POS , // BibliographyDataField_ADDRESS
+ ANNOTE_POS , // BibliographyDataField_ANNOTE
+ AUTHOR_POS , // BibliographyDataField_AUTHOR
+ BOOKTITLE_POS , // BibliographyDataField_BOOKTITLE
+ CHAPTER_POS , // BibliographyDataField_CHAPTER
+ EDITION_POS , // BibliographyDataField_EDITION
+ EDITOR_POS , // BibliographyDataField_EDITOR
+ HOWPUBLISHED_POS , // BibliographyDataField_HOWPUBLISHED
+ INSTITUTION_POS , // BibliographyDataField_INSTITUTION
+ JOURNAL_POS , // BibliographyDataField_JOURNAL
+ MONTH_POS , // BibliographyDataField_MONTH
+ NOTE_POS , // BibliographyDataField_NOTE
+ NUMBER_POS , // BibliographyDataField_NUMBER
+ ORGANIZATIONS_POS , // BibliographyDataField_ORGANIZATIONS
+ PAGES_POS , // BibliographyDataField_PAGES
+ PUBLISHER_POS , // BibliographyDataField_PUBLISHER
+ SCHOOL_POS , // BibliographyDataField_SCHOOL
+ SERIES_POS , // BibliographyDataField_SERIES
+ TITLE_POS , // BibliographyDataField_TITLE
+ REPORTTYPE_POS , // BibliographyDataField_REPORT_TYPE
+ VOLUME_POS , // BibliographyDataField_VOLUME
+ YEAR_POS , // BibliographyDataField_YEAR
+ URL_POS , // BibliographyDataField_URL
+ CUSTOM1_POS , // BibliographyDataField_CUSTOM1
+ CUSTOM2_POS , // BibliographyDataField_CUSTOM2
+ CUSTOM3_POS , // BibliographyDataField_CUSTOM3
+ CUSTOM4_POS , // BibliographyDataField_CUSTOM4
+ CUSTOM5_POS , // BibliographyDataField_CUSTOM5
+ ISBN_POS , // BibliographyDataField_ISBN
+ LOCAL_URL_POS // BibliographyDataField_LOCAL_URL
+ };
+ if(rPropertyName != "BibliographyDataFieldNames")
+ throw UnknownPropertyException(rPropertyName);
+ Sequence<PropertyValue> aSeq(COLUMN_COUNT);
+ PropertyValue* pArray = aSeq.getArray();
+ BibConfig* pConfig = BibModul::GetConfig();
+ for(sal_uInt16 i = 0; i <= text::BibliographyDataField::LOCAL_URL ; i++)
+ {
+ pArray[i].Name = pConfig->GetDefColumnName(aInternalMapping[i]);
+ pArray[i].Value <<= static_cast<sal_Int16>(i);
+ }
+ aRet <<= aSeq;
+ return aRet;
+}
+
+void BibliographyLoader::addPropertyChangeListener(
+ const OUString& /*PropertyName*/, const Reference< XPropertyChangeListener > & /*aListener*/)
+{
+ //no bound properties
+}
+
+void BibliographyLoader::removePropertyChangeListener(
+ const OUString& /*PropertyName*/, const Reference< XPropertyChangeListener > & /*aListener*/)
+{
+ //no bound properties
+}
+
+void BibliographyLoader::addVetoableChangeListener(
+ const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener > & /*aListener*/)
+{
+ //no vetoable properties
+}
+
+void BibliographyLoader::removeVetoableChangeListener(
+ const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener > & /*aListener*/)
+{
+ //no vetoable properties
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibmod.cxx b/extensions/source/bibliography/bibmod.cxx
new file mode 100644
index 000000000..3a6d06672
--- /dev/null
+++ b/extensions/source/bibliography/bibmod.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 <unotools/resmgr.hxx>
+
+#include "bibmod.hxx"
+#include "bibresid.hxx"
+#include "datman.hxx"
+#include "bibconfig.hxx"
+#include <ucbhelper/content.hxx>
+
+static BibModul* pBibModul=nullptr;
+static sal_uInt32 nBibModulCount=0;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ucb;
+
+HdlBibModul OpenBibModul()
+{
+ if(pBibModul==nullptr)
+ {
+ pBibModul=new BibModul();
+ }
+ nBibModulCount++;
+ return &pBibModul;
+}
+
+void CloseBibModul(HdlBibModul ppBibModul)
+{
+ nBibModulCount--;
+ if(nBibModulCount==0 && ppBibModul!=nullptr)
+ {
+ delete pBibModul;
+ pBibModul=nullptr;
+ }
+}
+
+OUString BibResId(TranslateId aId)
+{
+ return Translate::get(aId, pBibModul->GetResLocale());
+}
+
+BibConfig* BibModul::pBibConfig = nullptr;
+
+BibModul::BibModul()
+ : m_aResLocale(Translate::Create("pcr"))
+{
+}
+
+BibModul::~BibModul()
+{
+ if (pBibConfig && pBibConfig->IsModified())
+ pBibConfig->Commit();
+ delete pBibConfig;
+ pBibConfig = nullptr;
+}
+
+rtl::Reference<BibDataManager> BibModul::createDataManager()
+{
+ return new BibDataManager();
+}
+
+BibConfig* BibModul::GetConfig()
+{
+ if(! pBibConfig)
+ pBibConfig = new BibConfig;
+ return pBibConfig;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibmod.hxx b/extensions/source/bibliography/bibmod.hxx
new file mode 100644
index 000000000..a695f93d6
--- /dev/null
+++ b/extensions/source/bibliography/bibmod.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <locale>
+#include <rtl/ref.hxx>
+
+class BibDataManager;
+class BibConfig;
+
+class BibModul
+{
+ private:
+ std::locale m_aResLocale;
+ static BibConfig* pBibConfig;
+
+ public:
+ BibModul();
+ ~BibModul();
+
+ const std::locale& GetResLocale() const { return m_aResLocale; }
+ static BibConfig* GetConfig();
+
+ static rtl::Reference<BibDataManager> createDataManager();
+
+};
+
+typedef BibModul** HdlBibModul;
+
+HdlBibModul OpenBibModul();
+void CloseBibModul(HdlBibModul ppBibModul);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibresid.hxx b/extensions/source/bibliography/bibresid.hxx
new file mode 100644
index 000000000..6e17e76f2
--- /dev/null
+++ b/extensions/source/bibliography/bibresid.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 <rtl/ustring.hxx>
+#include <unotools/resmgr.hxx>
+
+OUString BibResId(TranslateId aId);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibshortcuthandler.hxx b/extensions/source/bibliography/bibshortcuthandler.hxx
new file mode 100644
index 000000000..79563543b
--- /dev/null
+++ b/extensions/source/bibliography/bibshortcuthandler.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 <vcl/window.hxx>
+#include <vcl/splitwin.hxx>
+
+// additional classes to handle shortcuts
+// code in bibcont.cxx
+
+
+class BibShortCutHandler
+{
+private:
+ VclPtr<vcl::Window> pBaseClass; // in cases, where BibShortCutHandler also has to be a window
+
+protected:
+ explicit BibShortCutHandler( vcl::Window* pBaseClass );
+
+public:
+ virtual ~BibShortCutHandler();
+ virtual bool HandleShortCutKey( const KeyEvent& rKeyEvent ); // returns true, if key was handled
+
+ inline vcl::Window* GetWindow();
+};
+
+inline BibShortCutHandler::BibShortCutHandler( vcl::Window* _pBaseClass ) : pBaseClass( _pBaseClass )
+{
+}
+
+inline vcl::Window* BibShortCutHandler::GetWindow()
+{
+ return pBaseClass;
+}
+
+class BibWindow : public vcl::Window, public BibShortCutHandler
+{
+public:
+ BibWindow( vcl::Window* pParent, WinBits nStyle);
+ virtual ~BibWindow() override;
+};
+
+class BibSplitWindow : public SplitWindow, public BibShortCutHandler
+{
+public:
+ BibSplitWindow( vcl::Window* pParent, WinBits nStyle);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibtools.hxx b/extensions/source/bibliography/bibtools.hxx
new file mode 100644
index 000000000..9b0d388fa
--- /dev/null
+++ b/extensions/source/bibliography/bibtools.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/window.hxx>
+
+namespace bib
+{
+ // source in bibbeam.cxx
+
+ void HandleTaskPaneList( vcl::Window* pWindow, bool bAddToList );
+ // pWindow: just an system window or something which is child of a system window
+
+ inline void AddToTaskPaneList( vcl::Window* pWindowToBeHandled )
+ {
+ HandleTaskPaneList( pWindowToBeHandled, true );
+ }
+
+ inline void RemoveFromTaskPaneList( vcl::Window* pWindowToBeHandled )
+ {
+ HandleTaskPaneList( pWindowToBeHandled, false );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibview.cxx b/extensions/source/bibliography/bibview.cxx
new file mode 100644
index 000000000..b175fb78a
--- /dev/null
+++ b/extensions/source/bibliography/bibview.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include "general.hxx"
+#include "bibview.hxx"
+#include "datman.hxx"
+#include "bibresid.hxx"
+#include "bibmod.hxx"
+#include "bibconfig.hxx"
+
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+namespace
+{
+ class MessageWithCheck : public weld::MessageDialogController
+ {
+ private:
+ std::unique_ptr<weld::CheckButton> m_xWarningOnBox;
+ public:
+ MessageWithCheck(weld::Window *pParent)
+ : MessageDialogController(pParent, "modules/sbibliography/ui/querydialog.ui", "QueryDialog", "ask")
+ , m_xWarningOnBox(m_xBuilder->weld_check_button("ask"))
+ {
+ }
+ bool get_active() const { return m_xWarningOnBox->get_active(); }
+ };
+}
+
+namespace bib
+{
+
+
+ BibView::BibView( vcl::Window* _pParent, BibDataManager* _pManager, WinBits _nStyle )
+ :BibWindow( _pParent, _nStyle )
+ ,m_pDatMan( _pManager )
+ ,m_xDatMan( _pManager )
+ ,m_pGeneralPage( nullptr )
+ ,m_aFormControlContainer(this)
+ {
+ if ( m_xDatMan.is() )
+ m_aFormControlContainer.connectForm( m_xDatMan );
+ }
+
+
+ BibView::~BibView()
+ {
+ disposeOnce();
+ }
+
+ void BibView::dispose()
+ {
+ VclPtr<BibGeneralPage> pGeneralPage = m_pGeneralPage;
+ m_pGeneralPage.clear();
+ pGeneralPage.disposeAndClear(); // dispose will commit any uncommitted weld::Entry changes
+
+ if ( m_aFormControlContainer.isFormConnected() )
+ m_aFormControlContainer.disconnectForm();
+
+ BibWindow::dispose();
+ }
+
+ void BibView::UpdatePages()
+ {
+ // TODO:
+ // this is _strange_: Why not updating the existent general page?
+ // I consider the current behaviour a HACK.
+ if ( m_pGeneralPage )
+ {
+ m_pGeneralPage->Hide();
+ m_pGeneralPage.disposeAndClear();
+ }
+
+ m_pGeneralPage = VclPtr<BibGeneralPage>::Create( this, m_pDatMan );
+ m_pGeneralPage->Show();
+
+ if( HasFocus() )
+ // "delayed" GetFocus() because GetFocus() is initially called before GeneralPage is created
+ m_pGeneralPage->GrabFocus();
+
+ OUString sErrorString( m_pGeneralPage->GetErrorString() );
+ if ( sErrorString.isEmpty() )
+ return;
+
+ bool bExecute = BibModul::GetConfig()->IsShowColumnAssignmentWarning();
+ if(!m_pDatMan->HasActiveConnection())
+ {
+ //no connection is available -> the data base has to be assigned
+ m_pDatMan->DispatchDBChangeDialog();
+ bExecute = false;
+ }
+ else if(bExecute)
+ {
+ sErrorString += "\n" + BibResId(RID_MAP_QUESTION);
+
+ MessageWithCheck aQueryBox(GetFrameWeld());
+ aQueryBox.set_primary_text(sErrorString);
+
+ short nResult = aQueryBox.run();
+ BibModul::GetConfig()->SetShowColumnAssignmentWarning(!aQueryBox.get_active());
+
+ if( RET_YES != nResult )
+ {
+ bExecute = false;
+ }
+ }
+ if(bExecute)
+ {
+ Application::PostUserEvent( LINK( this, BibView, CallMappingHdl ), nullptr, true );
+ }
+ }
+
+ BibViewFormControlContainer::BibViewFormControlContainer(BibView *pBibView) : mpBibView(pBibView) {}
+
+ void BibViewFormControlContainer::_loaded( const EventObject& _rEvent )
+ {
+ mpBibView->UpdatePages();
+ FormControlContainer::_loaded( _rEvent );
+ mpBibView->Resize();
+ }
+
+ void BibViewFormControlContainer::_reloaded( const EventObject& _rEvent )
+ {
+ mpBibView->UpdatePages();
+ FormControlContainer::_loaded( _rEvent );
+ mpBibView->Resize();
+ }
+
+ IMPL_LINK_NOARG( BibView, CallMappingHdl, void*, void)
+ {
+ m_pDatMan->CreateMappingDialog(GetFrameWeld());
+ }
+
+ void BibView::Resize()
+ {
+ if ( m_pGeneralPage )
+ {
+ ::Size aSz( GetOutputSizePixel() );
+ m_pGeneralPage->SetSizePixel( aSz );
+ }
+ Window::Resize();
+ }
+
+ Reference< awt::XControlContainer > BibViewFormControlContainer::getControlContainer()
+ {
+ return nullptr;
+ }
+
+ void BibView::GetFocus()
+ {
+ if( m_pGeneralPage )
+ m_pGeneralPage->GrabFocus();
+ }
+
+ bool BibView::HandleShortCutKey( const KeyEvent& rKeyEvent )
+ {
+ return m_pGeneralPage && m_pGeneralPage->HandleShortCutKey( rKeyEvent );
+ }
+
+
+} // namespace bib
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/bibview.hxx b/extensions/source/bibliography/bibview.hxx
new file mode 100644
index 000000000..3bed8ed13
--- /dev/null
+++ b/extensions/source/bibliography/bibview.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 <com/sun/star/awt/XControlContainer.hpp>
+#include "formcontrolcontainer.hxx"
+#include "bibshortcuthandler.hxx"
+
+class BibGeneralPage;
+class BibDataManager;
+
+namespace com::sun::star::awt{ class XFocusListener;}
+
+namespace bib
+{
+ class BibView;
+ class BibViewFormControlContainer : public FormControlContainer
+ {
+ private:
+ VclPtr<BibView> mpBibView;
+ protected:
+ // FormControlContainer
+ virtual css::uno::Reference< css::awt::XControlContainer >
+ getControlContainer() override;
+ // XLoadListener equivalents
+ virtual void _loaded( const css::lang::EventObject& _rEvent ) override;
+ virtual void _reloaded( const css::lang::EventObject& _rEvent ) override;
+ public:
+ using FormControlContainer::connectForm;
+ using FormControlContainer::disconnectForm;
+ using FormControlContainer::isFormConnected;
+ explicit BibViewFormControlContainer(BibView *pBibView);
+ };
+
+ class BibView : public BibWindow
+ {
+ private:
+ BibDataManager* m_pDatMan;
+ css::uno::Reference< css::form::XLoadable> m_xDatMan;
+ VclPtr<BibGeneralPage> m_pGeneralPage;
+ BibViewFormControlContainer m_aFormControlContainer;
+
+ private:
+ DECL_LINK(CallMappingHdl, void*, void);
+
+ public:
+ // Window overridables
+ virtual void Resize() override;
+
+ public:
+ BibView( vcl::Window* _pParent, BibDataManager* _pDatMan, WinBits nStyle );
+ virtual ~BibView() override;
+ virtual void dispose() override;
+
+ void UpdatePages();
+
+ virtual void GetFocus() override;
+
+ virtual bool HandleShortCutKey( const KeyEvent& rKeyEvent ) override; // returns true, if key was handled
+ };
+
+
+} // namespace bib
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/datman.cxx b/extensions/source/bibliography/datman.cxx
new file mode 100644
index 000000000..e010f8185
--- /dev/null
+++ b/extensions/source/bibliography/datman.cxx
@@ -0,0 +1,1390 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/any.hxx>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
+#include <com/sun/star/sdbcx/XRowLocate.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
+#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
+#include <com/sun/star/sdbc/XDataSource.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/form/ListSourceType.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/runtime/FormController.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/weld.hxx>
+#include "datman.hxx"
+#include "bibresid.hxx"
+#include "bibmod.hxx"
+#include "bibview.hxx"
+#include "toolbar.hxx"
+#include "bibconfig.hxx"
+#include "bibbeam.hxx"
+#include "general.hxx"
+#include <strings.hrc>
+#include <helpids.h>
+#include <connectivity/dbtools.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+
+// PropertyNames
+constexpr OUStringLiteral FM_PROP_LABEL = u"Label";
+constexpr OUStringLiteral FM_PROP_CONTROLSOURCE = u"DataField";
+constexpr OUStringLiteral FM_PROP_NAME = u"Name";
+
+static Reference< XConnection > getConnection(const OUString& _rURL)
+{
+ // first get the sdb::DataSource corresponding to the url
+ Reference< XDataSource > xDataSource;
+ // is it a favorite title ?
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ Reference< XDatabaseContext > xNamingContext = DatabaseContext::create(xContext);
+ if (xNamingContext->hasByName(_rURL))
+ {
+ DBG_ASSERT(xNamingContext.is(), "::getDataSource : no NamingService interface on the sdb::DatabaseAccessContext !");
+ try
+ {
+ xDataSource.set(xNamingContext->getRegisteredObject(_rURL), UNO_QUERY);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.biblio", "");
+ }
+ }
+ // build the connection from the data source
+ Reference< XConnection > xConn;
+ if (xDataSource.is())
+ {
+ // need user/pwd for this
+ Reference< XCompletedConnection > xComplConn(xDataSource, UNO_QUERY);
+ try
+ {
+ Reference<task::XInteractionHandler> xIHdl( task::InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW);
+ xConn = xComplConn->connectWithCompletion(xIHdl);
+ }
+ catch (const SQLException&)
+ {
+ // TODO : a real error handling
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+ return xConn;
+}
+
+static Reference< XConnection > getConnection(const Reference< XInterface > & xRowSet)
+{
+ Reference< XConnection > xConn;
+ try
+ {
+ Reference< XPropertySet > xFormProps(xRowSet, UNO_QUERY);
+ if (!xFormProps.is())
+ return xConn;
+
+ xConn.set(xFormProps->getPropertyValue("ActiveConnection"), UNO_QUERY);
+ if (!xConn.is())
+ {
+ SAL_INFO("extensions.biblio", "no active connection");
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.biblio", "");
+ }
+
+ return xConn;
+}
+
+static Reference< XNameAccess > getColumns(const Reference< XForm > & _rxForm)
+{
+ Reference< XNameAccess > xReturn;
+ // check if the form is alive
+ Reference< XColumnsSupplier > xSupplyCols( _rxForm, UNO_QUERY );
+ if (xSupplyCols.is())
+ xReturn = xSupplyCols->getColumns();
+
+ if (!xReturn.is() || !xReturn->getElementNames().hasElements())
+ { // no...
+ xReturn = nullptr;
+ // -> get the table the form is bound to and ask it for their columns
+ Reference< XTablesSupplier > xSupplyTables( getConnection( _rxForm ), UNO_QUERY );
+ Reference< XPropertySet > xFormProps( _rxForm, UNO_QUERY );
+ if (xFormProps.is() && xSupplyTables.is())
+ {
+ try
+ {
+ DBG_ASSERT(*o3tl::forceAccess<sal_Int32>(xFormProps->getPropertyValue("CommandType")) == CommandType::TABLE,
+ "::getColumns : invalid form (has no table as data source) !");
+ OUString sTable;
+ xFormProps->getPropertyValue("Command") >>= sTable;
+ Reference< XNameAccess > xTables = xSupplyTables->getTables();
+ if (xTables.is() && xTables->hasByName(sTable))
+ xSupplyCols.set(xTables->getByName(sTable), UNO_QUERY);
+ if (xSupplyCols.is())
+ xReturn = xSupplyCols->getColumns();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.biblio", "::getColumns");
+ }
+
+ }
+ }
+ return xReturn;
+}
+
+namespace {
+
+class MappingDialog_Impl : public weld::GenericDialogController
+{
+ BibDataManager* pDatMan;
+
+ OUString sNone;
+ bool bModified;
+
+ std::unique_ptr<weld::Button> m_xOKBT;
+ std::unique_ptr<weld::ComboBox> m_xIdentifierLB;
+ std::unique_ptr<weld::ComboBox> m_xAuthorityTypeLB;
+ std::unique_ptr<weld::ComboBox> m_xAuthorLB;
+ std::unique_ptr<weld::ComboBox> m_xTitleLB;
+ std::unique_ptr<weld::ComboBox> m_xMonthLB;
+ std::unique_ptr<weld::ComboBox> m_xYearLB;
+ std::unique_ptr<weld::ComboBox> m_xISBNLB;
+ std::unique_ptr<weld::ComboBox> m_xBooktitleLB;
+ std::unique_ptr<weld::ComboBox> m_xChapterLB;
+ std::unique_ptr<weld::ComboBox> m_xEditionLB;
+ std::unique_ptr<weld::ComboBox> m_xEditorLB;
+ std::unique_ptr<weld::ComboBox> m_xHowpublishedLB;
+ std::unique_ptr<weld::ComboBox> m_xInstitutionLB;
+ std::unique_ptr<weld::ComboBox> m_xJournalLB;
+ std::unique_ptr<weld::ComboBox> m_xNoteLB;
+ std::unique_ptr<weld::ComboBox> m_xAnnoteLB;
+ std::unique_ptr<weld::ComboBox> m_xNumberLB;
+ std::unique_ptr<weld::ComboBox> m_xOrganizationsLB;
+ std::unique_ptr<weld::ComboBox> m_xPagesLB;
+ std::unique_ptr<weld::ComboBox> m_xPublisherLB;
+ std::unique_ptr<weld::ComboBox> m_xAddressLB;
+ std::unique_ptr<weld::ComboBox> m_xSchoolLB;
+ std::unique_ptr<weld::ComboBox> m_xSeriesLB;
+ std::unique_ptr<weld::ComboBox> m_xReportTypeLB;
+ std::unique_ptr<weld::ComboBox> m_xVolumeLB;
+ std::unique_ptr<weld::ComboBox> m_xURLLB;
+ std::unique_ptr<weld::ComboBox> m_xCustom1LB;
+ std::unique_ptr<weld::ComboBox> m_xCustom2LB;
+ std::unique_ptr<weld::ComboBox> m_xCustom3LB;
+ std::unique_ptr<weld::ComboBox> m_xCustom4LB;
+ std::unique_ptr<weld::ComboBox> m_xCustom5LB;
+ std::unique_ptr<weld::ComboBox> m_xLocalURLLB;
+ weld::ComboBox* aListBoxes[COLUMN_COUNT];
+
+ DECL_LINK(OkHdl, weld::Button&, void);
+ DECL_LINK(ListBoxSelectHdl, weld::ComboBox&, void);
+
+public:
+ MappingDialog_Impl(weld::Window* pParent, BibDataManager* pDatMan);
+};
+
+}
+
+static sal_uInt16 lcl_FindLogicalName(BibConfig const * pConfig ,
+ std::u16string_view rLogicalColumnName)
+{
+ for(sal_uInt16 i = 0; i < COLUMN_COUNT; i++)
+ {
+ if(rLogicalColumnName == pConfig->GetDefColumnName(i))
+ return i;
+ }
+ return USHRT_MAX;
+}
+
+MappingDialog_Impl::MappingDialog_Impl(weld::Window* pParent, BibDataManager* pMan)
+ : GenericDialogController(pParent, "modules/sbibliography/ui/mappingdialog.ui", "MappingDialog")
+ , pDatMan(pMan)
+ , sNone(BibResId(RID_BIB_STR_NONE))
+ , bModified(false)
+ , m_xOKBT(m_xBuilder->weld_button("ok"))
+ , m_xIdentifierLB(m_xBuilder->weld_combo_box("identifierCombobox"))
+ , m_xAuthorityTypeLB(m_xBuilder->weld_combo_box("authorityTypeCombobox"))
+ , m_xAuthorLB(m_xBuilder->weld_combo_box("authorCombobox"))
+ , m_xTitleLB(m_xBuilder->weld_combo_box("titleCombobox"))
+ , m_xMonthLB(m_xBuilder->weld_combo_box("monthCombobox"))
+ , m_xYearLB(m_xBuilder->weld_combo_box("yearCombobox"))
+ , m_xISBNLB(m_xBuilder->weld_combo_box("ISBNCombobox"))
+ , m_xBooktitleLB(m_xBuilder->weld_combo_box("bookTitleCombobox"))
+ , m_xChapterLB(m_xBuilder->weld_combo_box("chapterCombobox"))
+ , m_xEditionLB(m_xBuilder->weld_combo_box("editionCombobox"))
+ , m_xEditorLB(m_xBuilder->weld_combo_box("editorCombobox"))
+ , m_xHowpublishedLB(m_xBuilder->weld_combo_box("howPublishedCombobox"))
+ , m_xInstitutionLB(m_xBuilder->weld_combo_box("institutionCombobox"))
+ , m_xJournalLB(m_xBuilder->weld_combo_box("journalCombobox"))
+ , m_xNoteLB(m_xBuilder->weld_combo_box("noteCombobox"))
+ , m_xAnnoteLB(m_xBuilder->weld_combo_box("annoteCombobox"))
+ , m_xNumberLB(m_xBuilder->weld_combo_box("numberCombobox"))
+ , m_xOrganizationsLB(m_xBuilder->weld_combo_box("organizationCombobox"))
+ , m_xPagesLB(m_xBuilder->weld_combo_box("pagesCombobox"))
+ , m_xPublisherLB(m_xBuilder->weld_combo_box("publisherCombobox"))
+ , m_xAddressLB(m_xBuilder->weld_combo_box("addressCombobox"))
+ , m_xSchoolLB(m_xBuilder->weld_combo_box("schoolCombobox"))
+ , m_xSeriesLB(m_xBuilder->weld_combo_box("seriesCombobox"))
+ , m_xReportTypeLB(m_xBuilder->weld_combo_box("reportTypeCombobox"))
+ , m_xVolumeLB(m_xBuilder->weld_combo_box("volumeCombobox"))
+ , m_xURLLB(m_xBuilder->weld_combo_box("URLCombobox"))
+ , m_xCustom1LB(m_xBuilder->weld_combo_box("custom1Combobox"))
+ , m_xCustom2LB(m_xBuilder->weld_combo_box("custom2Combobox"))
+ , m_xCustom3LB(m_xBuilder->weld_combo_box("custom3Combobox"))
+ , m_xCustom4LB(m_xBuilder->weld_combo_box("custom4Combobox"))
+ , m_xCustom5LB(m_xBuilder->weld_combo_box("custom5Combobox"))
+ , m_xLocalURLLB(m_xBuilder->weld_combo_box("LocalURLCombobox"))
+{
+ m_xOKBT->connect_clicked(LINK(this, MappingDialog_Impl, OkHdl));
+ OUString sTitle = m_xDialog->get_title();
+ sTitle = sTitle.replaceFirst("%1", pDatMan->getActiveDataTable());
+ m_xDialog->set_title(sTitle);
+
+ aListBoxes[0] = m_xIdentifierLB.get();
+ aListBoxes[1] = m_xAuthorityTypeLB.get();
+ aListBoxes[2] = m_xAuthorLB.get();
+ aListBoxes[3] = m_xTitleLB.get();
+ aListBoxes[4] = m_xYearLB.get();
+ aListBoxes[5] = m_xISBNLB.get();
+ aListBoxes[6] = m_xBooktitleLB.get();
+ aListBoxes[7] = m_xChapterLB.get();
+ aListBoxes[8] = m_xEditionLB.get();
+ aListBoxes[9] = m_xEditorLB.get();
+ aListBoxes[10] = m_xHowpublishedLB.get();
+ aListBoxes[11] = m_xInstitutionLB.get();
+ aListBoxes[12] = m_xJournalLB.get();
+ aListBoxes[13] = m_xMonthLB.get();
+ aListBoxes[14] = m_xNoteLB.get();
+ aListBoxes[15] = m_xAnnoteLB.get();
+ aListBoxes[16] = m_xNumberLB.get();
+ aListBoxes[17] = m_xOrganizationsLB.get();
+ aListBoxes[18] = m_xPagesLB.get();
+ aListBoxes[19] = m_xPublisherLB.get();
+ aListBoxes[20] = m_xAddressLB.get();
+ aListBoxes[21] = m_xSchoolLB.get();
+ aListBoxes[22] = m_xSeriesLB.get();
+ aListBoxes[23] = m_xReportTypeLB.get();
+ aListBoxes[24] = m_xVolumeLB.get();
+ aListBoxes[25] = m_xURLLB.get();
+ aListBoxes[26] = m_xCustom1LB.get();
+ aListBoxes[27] = m_xCustom2LB.get();
+ aListBoxes[28] = m_xCustom3LB.get();
+ aListBoxes[29] = m_xCustom4LB.get();
+ aListBoxes[30] = m_xCustom5LB.get();
+ aListBoxes[31] = m_xLocalURLLB.get();
+
+ aListBoxes[0]->append_text(sNone);
+ Reference< XNameAccess > xFields = getColumns( pDatMan->getForm() );
+ DBG_ASSERT(xFields.is(), "MappingDialog_Impl::MappingDialog_Impl : gave me an invalid form !");
+ if (xFields.is())
+ {
+ const Sequence<OUString> aFieldNames = xFields->getElementNames();
+ for(const OUString& rName : aFieldNames)
+ aListBoxes[0]->append_text(rName);
+ }
+
+ Link<weld::ComboBox&,void> aLnk = LINK(this, MappingDialog_Impl, ListBoxSelectHdl);
+
+ aListBoxes[0]->set_active(0);
+ aListBoxes[0]->connect_changed(aLnk);
+ for(sal_uInt16 i = 1; i < COLUMN_COUNT; i++)
+ {
+ for(sal_Int32 j = 0, nEntryCount = aListBoxes[0]->get_count(); j < nEntryCount; ++j)
+ aListBoxes[i]->append_text(aListBoxes[0]->get_text(j));
+ aListBoxes[i]->set_active(0);
+ aListBoxes[i]->connect_changed(aLnk);
+ }
+ BibConfig* pConfig = BibModul::GetConfig();
+ BibDBDescriptor aDesc;
+ aDesc.sDataSource = pDatMan->getActiveDataSource();
+ aDesc.sTableOrQuery = pDatMan->getActiveDataTable();
+ aDesc.nCommandType = CommandType::TABLE;
+ const Mapping* pMapping = pConfig->GetMapping(aDesc);
+ if(pMapping)
+ {
+ for(const auto & aColumnPair : pMapping->aColumnPairs)
+ {
+ sal_uInt16 nListBoxIndex = lcl_FindLogicalName( pConfig, aColumnPair.sLogicalColumnName);
+ if(nListBoxIndex < COLUMN_COUNT)
+ {
+ aListBoxes[nListBoxIndex]->set_active_text(aColumnPair.sRealColumnName);
+ }
+ }
+ }
+}
+
+IMPL_LINK(MappingDialog_Impl, ListBoxSelectHdl, weld::ComboBox&, rListBox, void)
+{
+ const sal_Int32 nEntryPos = rListBox.get_active();
+ if (0 < nEntryPos)
+ {
+ for(auto & pListBoxe : aListBoxes)
+ {
+ if (&rListBox != pListBoxe && pListBoxe->get_active() == nEntryPos)
+ pListBoxe->set_active(0);
+ }
+ }
+ bModified = true;
+}
+
+IMPL_LINK_NOARG(MappingDialog_Impl, OkHdl, weld::Button&, void)
+{
+ if(bModified)
+ {
+ Mapping aNew;
+ aNew.sTableName = pDatMan->getActiveDataTable();
+ aNew.sURL = pDatMan->getActiveDataSource();
+
+ sal_uInt16 nWriteIndex = 0;
+ BibConfig* pConfig = BibModul::GetConfig();
+ for(sal_uInt16 nEntry = 0; nEntry < COLUMN_COUNT; nEntry++)
+ {
+ OUString sSel = aListBoxes[nEntry]->get_active_text();
+ if(sSel != sNone)
+ {
+ aNew.aColumnPairs[nWriteIndex].sRealColumnName = sSel;
+ aNew.aColumnPairs[nWriteIndex].sLogicalColumnName = pConfig->GetDefColumnName(nEntry);
+ nWriteIndex++;
+ }
+ }
+ BibDBDescriptor aDesc;
+ aDesc.sDataSource = pDatMan->getActiveDataSource();
+ aDesc.sTableOrQuery = pDatMan->getActiveDataTable();
+ aDesc.nCommandType = CommandType::TABLE;
+ pDatMan->ResetIdentifierMapping();
+ pConfig->SetMapping(aDesc, &aNew);
+ }
+ m_xDialog->response(bModified ? RET_OK : RET_CANCEL);
+}
+
+namespace {
+
+class DBChangeDialog_Impl : public weld::GenericDialogController
+{
+ DBChangeDialogConfig_Impl aConfig;
+
+ std::unique_ptr<weld::TreeView> m_xSelectionLB;
+
+ DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
+public:
+ DBChangeDialog_Impl(weld::Window* pParent, const BibDataManager* pMan);
+
+ OUString GetCurrentURL()const;
+};
+
+}
+
+DBChangeDialog_Impl::DBChangeDialog_Impl(weld::Window* pParent, const BibDataManager* pDatMan )
+ : GenericDialogController(pParent, "modules/sbibliography/ui/choosedatasourcedialog.ui", "ChooseDataSourceDialog")
+ , m_xSelectionLB(m_xBuilder->weld_tree_view("treeview"))
+{
+ m_xSelectionLB->set_size_request(-1, m_xSelectionLB->get_height_rows(6));
+ m_xSelectionLB->connect_row_activated(LINK(this, DBChangeDialog_Impl, DoubleClickHdl));
+ m_xSelectionLB->make_sorted();
+
+ try
+ {
+ OUString sActiveSource = pDatMan->getActiveDataSource();
+ for (const OUString& rSourceName : aConfig.GetDataSourceNames())
+ m_xSelectionLB->append_text(rSourceName);
+ m_xSelectionLB->select_text(sActiveSource);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.biblio", "");
+ }
+}
+
+IMPL_LINK_NOARG(DBChangeDialog_Impl, DoubleClickHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+OUString DBChangeDialog_Impl::GetCurrentURL()const
+{
+ return m_xSelectionLB->get_selected_text();
+}
+
+// XDispatchProvider
+BibInterceptorHelper::BibInterceptorHelper( const ::bib::BibBeamer* pBibBeamer, css::uno::Reference< css::frame::XDispatch > const & xDispatch)
+{
+ if( pBibBeamer )
+ {
+ xInterception = pBibBeamer->getDispatchProviderInterception();
+ if( xInterception.is() )
+ xInterception->registerDispatchProviderInterceptor( this );
+ }
+ if( xDispatch.is() )
+ xFormDispatch = xDispatch;
+}
+
+BibInterceptorHelper::~BibInterceptorHelper( )
+{
+}
+
+void BibInterceptorHelper::ReleaseInterceptor()
+{
+ if ( xInterception.is() )
+ xInterception->releaseDispatchProviderInterceptor( this );
+ xInterception.clear();
+}
+
+css::uno::Reference< css::frame::XDispatch > SAL_CALL
+ BibInterceptorHelper::queryDispatch( const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags )
+{
+ Reference< XDispatch > xReturn;
+
+ OUString aCommand( aURL.Path );
+ if ( aCommand == "FormSlots/ConfirmDeletion" )
+ xReturn = xFormDispatch;
+ else
+ if ( xSlaveDispatchProvider.is() )
+ xReturn = xSlaveDispatchProvider->queryDispatch( aURL, aTargetFrameName, nSearchFlags);
+
+ return xReturn;
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL
+ BibInterceptorHelper::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& aDescripts )
+{
+ Sequence< Reference< XDispatch> > aReturn( aDescripts.getLength() );
+ Reference< XDispatch >* pReturn = aReturn.getArray();
+ for ( const DispatchDescriptor& rDescript : aDescripts )
+ {
+ *pReturn++ = queryDispatch( rDescript.FeatureURL, rDescript.FrameName, rDescript.SearchFlags );
+ }
+ return aReturn;
+}
+
+// XDispatchProviderInterceptor
+css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL
+ BibInterceptorHelper::getSlaveDispatchProvider( )
+{
+ return xSlaveDispatchProvider;
+}
+
+void SAL_CALL BibInterceptorHelper::setSlaveDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewSlaveDispatchProvider )
+{
+ xSlaveDispatchProvider = xNewSlaveDispatchProvider;
+}
+
+css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL
+ BibInterceptorHelper::getMasterDispatchProvider( )
+{
+ return xMasterDispatchProvider;
+}
+
+void SAL_CALL BibInterceptorHelper::setMasterDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewMasterDispatchProvider )
+{
+ xMasterDispatchProvider = xNewMasterDispatchProvider;
+}
+
+
+constexpr OUStringLiteral gGridName(u"theGrid");
+
+BibDataManager::BibDataManager()
+ :BibDataManager_Base( GetMutex() )
+ ,m_aLoadListeners(m_aMutex)
+ ,pBibView( nullptr )
+ ,pToolbar(nullptr)
+{
+}
+
+
+BibDataManager::~BibDataManager()
+{
+ Reference< XLoadable > xLoad( m_xForm, UNO_QUERY );
+ Reference< XPropertySet > xPrSet( m_xForm, UNO_QUERY );
+ Reference< XComponent > xComp( m_xForm, UNO_QUERY );
+ if ( m_xForm.is() )
+ {
+ Reference< XComponent > xConnection;
+ xPrSet->getPropertyValue("ActiveConnection") >>= xConnection;
+ if (xLoad.is())
+ xLoad->unload();
+ if (xComp.is())
+ xComp->dispose();
+ if(xConnection.is())
+ xConnection->dispose();
+ m_xForm = nullptr;
+ }
+ if( m_xInterceptorHelper.is() )
+ {
+ m_xInterceptorHelper->ReleaseInterceptor();
+ m_xInterceptorHelper.clear();
+ }
+}
+
+void BibDataManager::InsertFields(const Reference< XFormComponent > & _rxGrid)
+{
+ if ( !_rxGrid.is() )
+ return;
+
+ try
+ {
+ Reference< XNameContainer > xColContainer( _rxGrid, UNO_QUERY );
+ // remove the old fields
+ if ( xColContainer->hasElements() )
+ {
+ const Sequence<OUString> aOldNames = xColContainer->getElementNames();
+ for ( const OUString& rName : aOldNames )
+ xColContainer->removeByName( rName );
+ }
+
+ Reference< XNameAccess > xFields = getColumns( m_xForm );
+ if (!xFields.is())
+ return;
+
+ Reference< XGridColumnFactory > xColFactory( _rxGrid, UNO_QUERY );
+
+ Reference< XPropertySet > xField;
+
+ const Sequence<OUString> aFieldNames = xFields->getElementNames();
+ for ( const OUString& rField : aFieldNames )
+ {
+ xFields->getByName( rField ) >>= xField;
+
+ OUString sCurrentModelType;
+ sal_Int32 nType = 0;
+ bool bIsFormatted = false;
+ bool bFormattedIsNumeric = true;
+ xField->getPropertyValue("Type") >>= nType;
+ switch(nType)
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ sCurrentModelType = "CheckBox";
+ break;
+
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ case DataType::LONGVARBINARY:
+ case DataType::BLOB:
+ sCurrentModelType = "TextField";
+ break;
+
+ case DataType::VARCHAR:
+ case DataType::LONGVARCHAR:
+ case DataType::CHAR:
+ case DataType::CLOB:
+ bFormattedIsNumeric = false;
+ [[fallthrough]];
+ default:
+ sCurrentModelType = "FormattedField";
+ bIsFormatted = true;
+ break;
+ }
+
+ Reference< XPropertySet > xCurrentCol = xColFactory->createColumn(sCurrentModelType);
+ if (bIsFormatted)
+ {
+ OUString sFormatKey("FormatKey");
+ xCurrentCol->setPropertyValue(sFormatKey, xField->getPropertyValue(sFormatKey));
+ Any aFormatted(bFormattedIsNumeric);
+ xCurrentCol->setPropertyValue("TreatAsNumber", aFormatted);
+ }
+ Any aColName( rField );
+ xCurrentCol->setPropertyValue(FM_PROP_CONTROLSOURCE, aColName);
+ xCurrentCol->setPropertyValue(FM_PROP_LABEL, aColName);
+
+ xColContainer->insertByName( rField, Any( xCurrentCol ) );
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.biblio", "");
+ }
+}
+
+Reference< awt::XControlModel > BibDataManager::updateGridModel()
+{
+ return updateGridModel( m_xForm );
+}
+
+Reference< awt::XControlModel > const & BibDataManager::updateGridModel(const Reference< XForm > & xDbForm)
+{
+ try
+ {
+ Reference< XPropertySet > aFormPropSet( xDbForm, UNO_QUERY );
+ OUString sName;
+ aFormPropSet->getPropertyValue("Command") >>= sName;
+
+ if ( !m_xGridModel.is() )
+ {
+ m_xGridModel = createGridModel( gGridName );
+
+ Reference< XNameContainer > xNameCont(xDbForm, UNO_QUERY);
+ xNameCont->insertByName( sName, Any( m_xGridModel ) );
+ }
+
+ // insert the fields
+ Reference< XFormComponent > xFormComp( m_xGridModel, UNO_QUERY );
+ InsertFields( xFormComp );
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("::updateGridModel: something went wrong !");
+ }
+
+ return m_xGridModel;
+}
+
+Reference< XForm > BibDataManager::createDatabaseForm(BibDBDescriptor& rDesc)
+{
+ Reference< XForm > xResult;
+ try
+ {
+ Reference< XMultiServiceFactory > xMgr = comphelper::getProcessServiceFactory();
+ m_xForm.set( xMgr->createInstance( "com.sun.star.form.component.Form" ), UNO_QUERY );
+
+ Reference< XPropertySet > aPropertySet( m_xForm, UNO_QUERY );
+
+ aDataSourceURL = rDesc.sDataSource;
+ if(aPropertySet.is())
+ {
+ Any aVal;
+ aVal <<= sal_Int32(ResultSetType::SCROLL_INSENSITIVE);
+ aPropertySet->setPropertyValue("ResultSetType",aVal );
+ aVal <<= sal_Int32(ResultSetConcurrency::READ_ONLY);
+ aPropertySet->setPropertyValue("ResultSetConcurrency", aVal);
+
+ //Caching for Performance
+ aVal <<= sal_Int32(50);
+ aPropertySet->setPropertyValue("FetchSize", aVal);
+
+ Reference< XConnection > xConnection = getConnection(rDesc.sDataSource);
+ aVal <<= xConnection;
+ aPropertySet->setPropertyValue("ActiveConnection", aVal);
+
+ Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
+ Reference< XNameAccess > xTables = xSupplyTables.is() ?
+ xSupplyTables->getTables() : Reference< XNameAccess > ();
+
+ Sequence< OUString > aTableNameSeq;
+ if (xTables.is())
+ aTableNameSeq = xTables->getElementNames();
+
+ if(aTableNameSeq.hasElements())
+ {
+ if(!rDesc.sTableOrQuery.isEmpty())
+ aActiveDataTable = rDesc.sTableOrQuery;
+ else
+ {
+ rDesc.sTableOrQuery = aActiveDataTable = aTableNameSeq[0];
+ rDesc.nCommandType = CommandType::TABLE;
+ }
+
+ aVal <<= aActiveDataTable;
+ aPropertySet->setPropertyValue("Command", aVal);
+ aVal <<= rDesc.nCommandType;
+ aPropertySet->setPropertyValue("CommandType", aVal);
+
+
+ Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
+ aQuoteChar = xMetaData->getIdentifierQuoteString();
+
+ Reference< XMultiServiceFactory > xFactory(xConnection, UNO_QUERY);
+ if ( xFactory.is() )
+ m_xParser.set( xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"), UNO_QUERY );
+
+ OUString aString("SELECT * FROM ");
+
+ OUString sCatalog, sSchema, sName;
+ ::dbtools::qualifiedNameComponents( xMetaData, aActiveDataTable, sCatalog, sSchema, sName, ::dbtools::EComposeRule::InDataManipulation );
+ aString += ::dbtools::composeTableNameForSelect( xConnection, sCatalog, sSchema, sName );
+
+ m_xParser->setElementaryQuery(aString);
+ BibConfig* pConfig = BibModul::GetConfig();
+ pConfig->setQueryField(getQueryField());
+ startQueryWith(pConfig->getQueryText());
+
+ xResult = m_xForm;
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("::createDatabaseForm: something went wrong !");
+ }
+
+ return xResult;
+}
+
+Sequence< OUString > BibDataManager::getDataSources() const
+{
+ Sequence< OUString > aTableNameSeq;
+
+ try
+ {
+ Reference< XTablesSupplier > xSupplyTables( getConnection( m_xForm ), UNO_QUERY );
+ Reference< XNameAccess > xTables;
+ if (xSupplyTables.is())
+ xTables = xSupplyTables->getTables();
+ if (xTables.is())
+ aTableNameSeq = xTables->getElementNames();
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("::getDataSources: something went wrong !");
+ }
+
+ return aTableNameSeq;
+}
+
+
+void BibDataManager::setFilter(const OUString& rQuery)
+{
+ if(!m_xParser.is())
+ return;
+ try
+ {
+ m_xParser->setFilter( rQuery );
+ OUString aQuery = m_xParser->getFilter();
+ Reference< XPropertySet > xFormProps( m_xForm, UNO_QUERY_THROW );
+ xFormProps->setPropertyValue( "Filter", Any( aQuery ) );
+ xFormProps->setPropertyValue( "ApplyFilter", Any( true ) );
+ reload();
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.biblio");
+ }
+
+
+}
+
+OUString BibDataManager::getFilter() const
+{
+
+ OUString aQueryString;
+ try
+ {
+ Reference< XPropertySet > xFormProps( m_xForm, UNO_QUERY_THROW );
+ OSL_VERIFY( xFormProps->getPropertyValue( "Filter" ) >>= aQueryString );
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.biblio");
+ }
+
+
+ return aQueryString;
+
+}
+
+Sequence< OUString > BibDataManager::getQueryFields() const
+{
+ Sequence< OUString > aFieldSeq;
+ Reference< XNameAccess > xFields = getColumns( m_xForm );
+ if (xFields.is())
+ aFieldSeq = xFields->getElementNames();
+ return aFieldSeq;
+}
+
+OUString BibDataManager::getQueryField() const
+{
+ BibConfig* pConfig = BibModul::GetConfig();
+ OUString aFieldString = pConfig->getQueryField();
+ if(aFieldString.isEmpty())
+ {
+ const Sequence< OUString > aSeq = getQueryFields();
+ if(aSeq.hasElements())
+ {
+ aFieldString=aSeq[0];
+ }
+ }
+ return aFieldString;
+}
+
+void BibDataManager::startQueryWith(const OUString& rQuery)
+{
+ BibConfig* pConfig = BibModul::GetConfig();
+ pConfig->setQueryText( rQuery );
+
+ OUString aQueryString;
+ if(!rQuery.isEmpty())
+ {
+ aQueryString=aQuoteChar + getQueryField() + aQuoteChar + " like '";
+ OUString sQuery = rQuery.replaceAll("?","_").replaceAll("*","%");
+ aQueryString += sQuery + "%'";
+ }
+ setFilter(aQueryString);
+}
+
+void BibDataManager::setActiveDataSource(const OUString& rURL)
+{
+ OUString sTmp(aDataSourceURL);
+ aDataSourceURL = rURL;
+
+ Reference< XPropertySet > aPropertySet( m_xForm, UNO_QUERY );
+ if(!aPropertySet.is())
+ return;
+
+ unload();
+
+ Reference< XComponent > xOldConnection;
+ aPropertySet->getPropertyValue("ActiveConnection") >>= xOldConnection;
+
+ Reference< XConnection > xConnection = getConnection(rURL);
+ if(!xConnection.is())
+ {
+ aDataSourceURL = sTmp;
+ return;
+ }
+ Any aVal; aVal <<= xConnection;
+ aPropertySet->setPropertyValue("ActiveConnection", aVal);
+ Reference< XMultiServiceFactory > xFactory(xConnection, UNO_QUERY);
+ if ( xFactory.is() )
+ m_xParser.set( xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"), UNO_QUERY );
+
+ if(xOldConnection.is())
+ xOldConnection->dispose();
+
+ Sequence< OUString > aTableNameSeq;
+ Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
+ if(xSupplyTables.is())
+ {
+ Reference< XNameAccess > xAccess = xSupplyTables->getTables();
+ aTableNameSeq = xAccess->getElementNames();
+ }
+ if(aTableNameSeq.hasElements())
+ {
+ aActiveDataTable = aTableNameSeq[0];
+ aVal <<= aActiveDataTable;
+ aPropertySet->setPropertyValue("Command", aVal);
+ aPropertySet->setPropertyValue("CommandType", Any(CommandType::TABLE));
+ //Caching for Performance
+ aVal <<= sal_Int32(50);
+ aPropertySet->setPropertyValue("FetchSize", aVal);
+ OUString aString("SELECT * FROM ");
+ // quote the table name which may contain catalog.schema.table
+ Reference<XDatabaseMetaData> xMetaData = xConnection->getMetaData();
+ aQuoteChar = xMetaData->getIdentifierQuoteString();
+
+ OUString sCatalog, sSchema, sName;
+ ::dbtools::qualifiedNameComponents( xMetaData, aActiveDataTable, sCatalog, sSchema, sName, ::dbtools::EComposeRule::InDataManipulation );
+ aString += ::dbtools::composeTableNameForSelect( xConnection, sCatalog, sSchema, sName );
+
+ m_xParser->setElementaryQuery(aString);
+ BibConfig* pConfig = BibModul::GetConfig();
+ pConfig->setQueryField(getQueryField());
+ startQueryWith(pConfig->getQueryText());
+ setActiveDataTable(aActiveDataTable);
+ }
+ FeatureStateEvent aEvent;
+ util::URL aURL;
+ aEvent.IsEnabled = true;
+ aEvent.Requery = false;
+ aEvent.FeatureDescriptor = getActiveDataTable();
+
+ aEvent.State <<= getDataSources();
+
+ if(pToolbar)
+ {
+ aURL.Complete =".uno:Bib/source";
+ aEvent.FeatureURL = aURL;
+ pToolbar->statusChanged( aEvent );
+ }
+
+ updateGridModel();
+ load();
+}
+
+
+void BibDataManager::setActiveDataTable(const OUString& rTable)
+{
+ ResetIdentifierMapping();
+ try
+ {
+ Reference< XPropertySet > aPropertySet( m_xForm, UNO_QUERY );
+
+ if(aPropertySet.is())
+ {
+ Reference< XConnection > xConnection = getConnection( m_xForm );
+ Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
+ Reference< XNameAccess > xAccess = xSupplyTables->getTables();
+ Sequence< OUString > aTableNameSeq = xAccess->getElementNames();
+ sal_uInt32 nCount = aTableNameSeq.getLength();
+
+ const OUString* pTableNames = aTableNameSeq.getConstArray();
+ const OUString* pTableNamesEnd = pTableNames + nCount;
+
+ for ( ; pTableNames != pTableNamesEnd; ++pTableNames )
+ {
+ if ( rTable == *pTableNames )
+ {
+ aActiveDataTable = rTable;
+ Any aVal; aVal <<= rTable;
+ aPropertySet->setPropertyValue( "Command", aVal );
+ break;
+ }
+ }
+ if (pTableNames != pTableNamesEnd)
+ {
+ Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
+ aQuoteChar = xMetaData->getIdentifierQuoteString();
+
+ Reference< XMultiServiceFactory > xFactory(xConnection, UNO_QUERY);
+ if ( xFactory.is() )
+ m_xParser.set( xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"), UNO_QUERY );
+
+ OUString aString("SELECT * FROM ");
+
+ OUString sCatalog, sSchema, sName;
+ ::dbtools::qualifiedNameComponents( xMetaData, aActiveDataTable, sCatalog, sSchema, sName, ::dbtools::EComposeRule::InDataManipulation );
+ aString += ::dbtools::composeTableNameForSelect( xConnection, sCatalog, sSchema, sName );
+
+ m_xParser->setElementaryQuery(aString);
+
+ BibConfig* pConfig = BibModul::GetConfig();
+ pConfig->setQueryField(getQueryField());
+ startQueryWith(pConfig->getQueryText());
+
+ BibDBDescriptor aDesc;
+ aDesc.sDataSource = aDataSourceURL;
+ aDesc.sTableOrQuery = aActiveDataTable;
+ aDesc.nCommandType = CommandType::TABLE;
+ BibModul::GetConfig()->SetBibliographyURL(aDesc);
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("::setActiveDataTable: something went wrong !");
+ }
+}
+
+
+void SAL_CALL BibDataManager::load( )
+{
+ if ( isLoaded() )
+ // nothing to do
+ return;
+
+ Reference< XLoadable > xFormAsLoadable( m_xForm, UNO_QUERY );
+ DBG_ASSERT( xFormAsLoadable.is() || !m_xForm.is(), "BibDataManager::load: invalid form!");
+ if ( xFormAsLoadable.is() )
+ {
+ xFormAsLoadable->load();
+
+ EventObject aEvt( static_cast< XWeak* >( this ) );
+ m_aLoadListeners.notifyEach( &XLoadListener::loaded, aEvt );
+ }
+}
+
+
+void SAL_CALL BibDataManager::unload( )
+{
+ if ( !isLoaded() )
+ // nothing to do
+ return;
+
+ Reference< XLoadable >xFormAsLoadable( m_xForm, UNO_QUERY );
+ DBG_ASSERT( xFormAsLoadable.is() || !m_xForm.is(), "BibDataManager::unload: invalid form!");
+ if ( !xFormAsLoadable.is() )
+ return;
+
+ EventObject aEvt( static_cast< XWeak* >( this ) );
+
+ {
+ m_aLoadListeners.notifyEach( &XLoadListener::unloading, aEvt );
+ }
+
+ xFormAsLoadable->unload();
+
+ {
+ m_aLoadListeners.notifyEach( &XLoadListener::unloaded, aEvt );
+ }
+}
+
+
+void SAL_CALL BibDataManager::reload( )
+{
+ if ( !isLoaded() )
+ // nothing to do
+ return;
+
+ Reference< XLoadable >xFormAsLoadable( m_xForm, UNO_QUERY );
+ DBG_ASSERT( xFormAsLoadable.is() || !m_xForm.is(), "BibDataManager::unload: invalid form!");
+ if ( !xFormAsLoadable.is() )
+ return;
+
+ EventObject aEvt( static_cast< XWeak* >( this ) );
+
+ {
+ m_aLoadListeners.notifyEach( &XLoadListener::reloading, aEvt );
+ }
+
+ xFormAsLoadable->reload();
+
+ {
+ m_aLoadListeners.notifyEach( &XLoadListener::reloaded, aEvt );
+ }
+}
+
+
+sal_Bool SAL_CALL BibDataManager::isLoaded( )
+{
+ Reference< XLoadable >xFormAsLoadable( m_xForm, UNO_QUERY );
+ DBG_ASSERT( xFormAsLoadable.is() || !m_xForm.is(), "BibDataManager::isLoaded: invalid form!");
+
+ bool bLoaded = false;
+ if ( xFormAsLoadable.is() )
+ bLoaded = xFormAsLoadable->isLoaded();
+ return bLoaded;
+}
+
+
+void SAL_CALL BibDataManager::addLoadListener( const Reference< XLoadListener >& aListener )
+{
+ m_aLoadListeners.addInterface( aListener );
+}
+
+
+void SAL_CALL BibDataManager::removeLoadListener( const Reference< XLoadListener >& aListener )
+{
+ m_aLoadListeners.removeInterface( aListener );
+}
+
+
+Reference< awt::XControlModel > BibDataManager::createGridModel(const OUString& rName)
+{
+ Reference< awt::XControlModel > xModel;
+
+ try
+ {
+ // create the control model
+ Reference< XMultiServiceFactory > xMgr = ::comphelper::getProcessServiceFactory();
+ Reference< XInterface > xObject = xMgr->createInstance("com.sun.star.form.component.GridControl");
+ xModel.set( xObject, UNO_QUERY );
+
+ // set the
+ Reference< XPropertySet > xPropSet( xModel, UNO_QUERY );
+ xPropSet->setPropertyValue( "Name", Any( rName ) );
+
+ // set the name of the to-be-created control
+ Any aAny(OUString("com.sun.star.form.control.InteractionGridControl"));
+ xPropSet->setPropertyValue( "DefaultControl",aAny );
+
+ // the helpURL
+ OUString uProp("HelpURL");
+ Reference< XPropertySetInfo > xPropInfo = xPropSet->getPropertySetInfo();
+ if (xPropInfo->hasPropertyByName(uProp))
+ {
+ xPropSet->setPropertyValue(
+ uProp, Any(OUString(INET_HID_SCHEME + HID_BIB_DB_GRIDCTRL)));
+ }
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("::createGridModel: something went wrong !");
+ }
+
+ return xModel;
+}
+
+OUString BibDataManager::getControlName(sal_Int32 nFormatKey )
+{
+ OUString aResStr;
+ switch (nFormatKey)
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ aResStr="CheckBox";
+ break;
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ aResStr="NumericField";
+ break;
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ aResStr="FormattedField";
+ break;
+ case DataType::TIMESTAMP:
+ aResStr="FormattedField";
+ break;
+ case DataType::DATE:
+ aResStr="DateField";
+ break;
+ case DataType::TIME:
+ aResStr="TimeField";
+ break;
+ case DataType::CHAR:
+ case DataType::VARCHAR:
+ case DataType::LONGVARCHAR:
+ default:
+ aResStr="TextField";
+ break;
+ }
+ return aResStr;
+}
+
+Reference< awt::XControlModel > BibDataManager::loadControlModel(
+ const OUString& rName, bool bForceListBox)
+{
+ Reference< awt::XControlModel > xModel;
+ OUString aName = "View_" + rName;
+
+ try
+ {
+ Reference< XNameAccess > xFields = getColumns( m_xForm );
+ if (!xFields.is())
+ return xModel;
+ Reference< XPropertySet > xField;
+
+ Any aElement;
+
+ if(xFields->hasByName(rName))
+ {
+ aElement = xFields->getByName(rName);
+ aElement >>= xField;
+
+ sal_Int32 nFormatKey = 0;
+ xField->getPropertyValue("Type") >>= nFormatKey;
+
+ OUString aInstanceName("com.sun.star.form.component.");
+
+ if (bForceListBox)
+ aInstanceName += "ListBox";
+ else
+ aInstanceName += getControlName(nFormatKey);
+
+ Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+ Reference< XInterface > xObject = xContext->getServiceManager()->createInstanceWithContext(aInstanceName, xContext);
+ xModel.set( xObject, UNO_QUERY );
+ Reference< XPropertySet > xPropSet( xModel, UNO_QUERY );
+ Any aFieldName; aFieldName <<= aName;
+
+ xPropSet->setPropertyValue( FM_PROP_NAME,aFieldName);
+ xPropSet->setPropertyValue( FM_PROP_CONTROLSOURCE, Any( rName ) );
+ xPropSet->setPropertyValue("NativeWidgetLook", Any( true ) );
+
+ if (bForceListBox)
+ {
+ uno::Any aAny;
+
+ //uno::Reference< beans::XPropertySet > xPropSet(xControl, UNO_QUERY);
+ aAny <<= sal_Int16(1);
+ xPropSet->setPropertyValue("BoundColumn", aAny);
+ aAny <<= ListSourceType_VALUELIST;
+ xPropSet->setPropertyValue("ListSourceType", aAny);
+
+ uno::Sequence<OUString> aListSource(TYPE_COUNT);
+ OUString* pListSourceArr = aListSource.getArray();
+ //pListSourceArr[0] = "select TypeName, TypeIndex from TypeNms";
+ for(sal_Int32 i = 0; i < TYPE_COUNT; ++i)
+ pListSourceArr[i] = OUString::number(i);
+ aAny <<= aListSource;
+
+ xPropSet->setPropertyValue("ListSource", aAny);
+
+ uno::Sequence<OUString> aValues(TYPE_COUNT + 1);
+ OUString* pValuesArr = aValues.getArray();
+ pValuesArr[0] = BibResId(ST_TYPE_ARTICLE);
+ pValuesArr[1] = BibResId(ST_TYPE_BOOK);
+ pValuesArr[2] = BibResId(ST_TYPE_BOOKLET);
+ pValuesArr[3] = BibResId(ST_TYPE_CONFERENCE);
+ pValuesArr[4] = BibResId(ST_TYPE_INBOOK );
+ pValuesArr[5] = BibResId(ST_TYPE_INCOLLECTION);
+ pValuesArr[6] = BibResId(ST_TYPE_INPROCEEDINGS);
+ pValuesArr[7] = BibResId(ST_TYPE_JOURNAL );
+ pValuesArr[8] = BibResId(ST_TYPE_MANUAL );
+ pValuesArr[9] = BibResId(ST_TYPE_MASTERSTHESIS);
+ pValuesArr[10] = BibResId(ST_TYPE_MISC );
+ pValuesArr[11] = BibResId(ST_TYPE_PHDTHESIS );
+ pValuesArr[12] = BibResId(ST_TYPE_PROCEEDINGS );
+ pValuesArr[13] = BibResId(ST_TYPE_TECHREPORT );
+ pValuesArr[14] = BibResId(ST_TYPE_UNPUBLISHED );
+ pValuesArr[15] = BibResId(ST_TYPE_EMAIL );
+ pValuesArr[16] = BibResId(ST_TYPE_WWW );
+ pValuesArr[17] = BibResId(ST_TYPE_CUSTOM1 );
+ pValuesArr[18] = BibResId(ST_TYPE_CUSTOM2 );
+ pValuesArr[19] = BibResId(ST_TYPE_CUSTOM3 );
+ pValuesArr[20] = BibResId(ST_TYPE_CUSTOM4 );
+ pValuesArr[21] = BibResId(ST_TYPE_CUSTOM5 );
+ // empty string if an invalid value no values is set
+ pValuesArr[TYPE_COUNT].clear();
+
+ aAny <<= aValues;
+
+ xPropSet->setPropertyValue("StringItemList", aAny);
+
+ xPropSet->setPropertyValue( "Dropdown", Any(true) );
+ }
+
+ Reference< XFormComponent > aFormComp(xModel,UNO_QUERY );
+
+ Reference< XNameContainer > xNameCont( m_xForm, UNO_QUERY );
+ xNameCont->insertByName(aName, Any( aFormComp ) );
+
+ // now if the form where we inserted the new model is already loaded, notify the model of this
+ // Note that this implementation below is a HACK as it relies on the fact that the model adds itself
+ // as load listener to its parent, which is an implementation detail of the model.
+ //
+ // the better solution would be the following:
+ // in the current scenario, we insert a control model into a form. This results in the control model
+ // adding itself as load listener to the form. Now, the form should realize that it's already loaded
+ // and notify the model (which it knows as XLoadListener only) immediately. This seems to make sense.
+ // (as an analogon to the XStatusListener semantics).
+ //
+ // But this would be way too risky for this last-day fix here.
+ Reference< XLoadable > xLoad( m_xForm, UNO_QUERY );
+ if ( xLoad.is() && xLoad->isLoaded() )
+ {
+ Reference< XLoadListener > xListener( aFormComp, UNO_QUERY );
+ if ( xListener.is() )
+ {
+ EventObject aLoadSource;
+ aLoadSource.Source = xLoad;
+ xListener->loaded( aLoadSource );
+ }
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("::loadControlModel: something went wrong !");
+ }
+ return xModel;
+}
+
+void BibDataManager::CreateMappingDialog(weld::Window* pParent)
+{
+ MappingDialog_Impl aDlg(pParent, this);
+ if (RET_OK == aDlg.run() && pBibView)
+ {
+ reload();
+ }
+}
+
+OUString BibDataManager::CreateDBChangeDialog(weld::Window* pParent)
+{
+ OUString uRet;
+ DBChangeDialog_Impl aDlg(pParent, this);
+ if (aDlg.run() == RET_OK)
+ {
+ OUString sNewURL = aDlg.GetCurrentURL();
+ if(sNewURL != getActiveDataSource())
+ {
+ uRet = sNewURL;
+ }
+ }
+ return uRet;
+}
+
+void BibDataManager::DispatchDBChangeDialog()
+{
+ if (pToolbar)
+ pToolbar->SendDispatch(pToolbar->GetChangeSourceId(), Sequence< PropertyValue >());
+}
+
+const OUString& BibDataManager::GetIdentifierMapping()
+{
+ if(sIdentifierMapping.isEmpty())
+ {
+ BibConfig* pConfig = BibModul::GetConfig();
+ BibDBDescriptor aDesc;
+ aDesc.sDataSource = getActiveDataSource();
+ aDesc.sTableOrQuery = getActiveDataTable();
+ aDesc.nCommandType = CommandType::TABLE;
+ const Mapping* pMapping = pConfig->GetMapping(aDesc);
+ sIdentifierMapping = pConfig->GetDefColumnName(IDENTIFIER_POS);
+ if(pMapping)
+ {
+ for(const auto & aColumnPair : pMapping->aColumnPairs)
+ {
+ if(aColumnPair.sLogicalColumnName == sIdentifierMapping)
+ {
+ sIdentifierMapping = aColumnPair.sRealColumnName;
+ break;
+ }
+ }
+ }
+ }
+ return sIdentifierMapping;
+}
+
+void BibDataManager::SetToolbar(BibToolBar* pSet)
+{
+ pToolbar = pSet;
+ if(pToolbar)
+ pToolbar->SetDatMan(*this);
+}
+
+uno::Reference< form::runtime::XFormController > const & BibDataManager::GetFormController()
+{
+ if(!m_xFormCtrl.is())
+ {
+ Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ m_xFormCtrl = form::runtime::FormController::create(xContext);
+ m_xFormCtrl->setModel(uno::Reference< awt::XTabControllerModel > (getForm(), UNO_QUERY));
+ m_xFormDispatch.set( m_xFormCtrl, UNO_QUERY);
+ }
+ return m_xFormCtrl;
+}
+
+void BibDataManager::RegisterInterceptor( const ::bib::BibBeamer* pBibBeamer)
+{
+ DBG_ASSERT( !m_xInterceptorHelper.is(), "BibDataManager::RegisterInterceptor: called twice!" );
+
+ if( pBibBeamer )
+ m_xInterceptorHelper = new BibInterceptorHelper( pBibBeamer, m_xFormDispatch);
+}
+
+
+bool BibDataManager::HasActiveConnection() const
+{
+ return getConnection( m_xForm ).is();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/datman.hxx b/extensions/source/bibliography/datman.hxx
new file mode 100644
index 000000000..36cbdcd59
--- /dev/null
+++ b/extensions/source/bibliography/datman.hxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "bibview.hxx"
+
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <comphelper/broadcasthelper.hxx>
+#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp>
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <vcl/vclptr.hxx>
+
+namespace weld { class Window; }
+
+namespace bib
+{
+ class BibView;
+ class BibBeamer;
+}
+
+class BibToolBar;
+struct BibDBDescriptor;
+
+class BibInterceptorHelper
+ :public cppu::WeakImplHelper< css::frame::XDispatchProviderInterceptor >
+{
+private:
+ css::uno::Reference< css::frame::XDispatchProvider > xMasterDispatchProvider;
+ css::uno::Reference< css::frame::XDispatchProvider > xSlaveDispatchProvider;
+ css::uno::Reference< css::frame::XDispatch > xFormDispatch;
+ css::uno::Reference< css::frame::XDispatchProviderInterception > xInterception;
+
+protected:
+ virtual ~BibInterceptorHelper( ) override;
+
+public:
+ BibInterceptorHelper( const ::bib::BibBeamer* pBibBeamer, css::uno::Reference< css::frame::XDispatch > const & xDispatch);
+
+ void ReleaseInterceptor();
+
+ // XDispatchProvider
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& aDescripts ) override;
+ // XDispatchProviderInterceptor
+ virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL getSlaveDispatchProvider( ) override;
+ virtual void SAL_CALL setSlaveDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewSlaveDispatchProvider ) override;
+ virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL getMasterDispatchProvider( ) override;
+ virtual void SAL_CALL setMasterDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewMasterDispatchProvider ) override;
+};
+
+typedef cppu::WeakComponentImplHelper < css::form::XLoadable
+ > BibDataManager_Base;
+class BibDataManager final
+ :public ::comphelper::OMutexAndBroadcastHelper
+ ,public BibDataManager_Base
+{
+private:
+ css::uno::Reference< css::form::XForm > m_xForm;
+ css::uno::Reference< css::awt::XControlModel > m_xGridModel;
+ css::uno::Reference< css::sdb::XSingleSelectQueryComposer > m_xParser;
+ css::uno::Reference< css::form::runtime::XFormController > m_xFormCtrl;
+ css::uno::Reference< css::frame::XDispatch > m_xFormDispatch;
+ rtl::Reference<BibInterceptorHelper> m_xInterceptorHelper;
+
+ OUString aActiveDataTable;
+ OUString aDataSourceURL;
+ OUString aQuoteChar;
+
+ ::comphelper::OInterfaceContainerHelper2 m_aLoadListeners;
+
+ VclPtr< ::bib::BibView> pBibView;
+ VclPtr<BibToolBar> pToolbar;
+
+ OUString sIdentifierMapping;
+
+ void InsertFields(const css::uno::Reference< css::form::XFormComponent > & xGrid);
+
+ css::uno::Reference< css::awt::XControlModel > const &
+ updateGridModel(const css::uno::Reference< css::form::XForm > & xDbForm);
+ static css::uno::Reference< css::awt::XControlModel >
+ createGridModel( const OUString& rName );
+
+ // XLoadable
+ virtual void SAL_CALL load( ) override;
+ virtual void SAL_CALL unload( ) override;
+ virtual void SAL_CALL reload( ) override;
+ virtual sal_Bool SAL_CALL isLoaded( ) override;
+ virtual void SAL_CALL addLoadListener( const css::uno::Reference< css::form::XLoadListener >& aListener ) override;
+ virtual void SAL_CALL removeLoadListener( const css::uno::Reference< css::form::XLoadListener >& aListener ) override;
+
+ using WeakComponentImplHelperBase::disposing;
+
+public:
+
+ BibDataManager();
+ virtual ~BibDataManager() override;
+
+ css::uno::Reference< css::form::XForm > createDatabaseForm( BibDBDescriptor& aDesc);
+
+ css::uno::Reference< css::awt::XControlModel > updateGridModel();
+
+ css::uno::Sequence< OUString> getDataSources() const;
+
+ const OUString& getActiveDataSource() const {return aDataSourceURL;}
+ void setActiveDataSource(const OUString& rURL);
+
+ const OUString& getActiveDataTable() const { return aActiveDataTable;}
+ void setActiveDataTable(const OUString& rTable);
+
+ void setFilter(const OUString& rQuery);
+ OUString getFilter() const;
+
+ css::uno::Sequence< OUString> getQueryFields() const;
+ OUString getQueryField() const;
+ void startQueryWith(const OUString& rQuery);
+
+ const css::uno::Reference< css::sdb::XSingleSelectQueryComposer >& getParser() const { return m_xParser; }
+ const css::uno::Reference< css::form::XForm >& getForm() const { return m_xForm; }
+
+
+ static OUString getControlName(sal_Int32 nFormatKey );
+
+ css::uno::Reference< css::awt::XControlModel > loadControlModel(const OUString& rName,
+ bool bForceListBox);
+
+ void CreateMappingDialog(weld::Window* pParent);
+ OUString CreateDBChangeDialog(weld::Window* pParent);
+
+ void DispatchDBChangeDialog();
+
+ void SetView( ::bib::BibView* pView ) { pBibView = pView; }
+
+ void SetToolbar(BibToolBar* pSet);
+
+ const OUString& GetIdentifierMapping();
+ void ResetIdentifierMapping() {sIdentifierMapping.clear();}
+
+ css::uno::Reference< css::form::runtime::XFormController > const & GetFormController();
+ void RegisterInterceptor( const ::bib::BibBeamer* pBibBeamer);
+
+ bool HasActiveConnection() const;
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/formcontrolcontainer.cxx b/extensions/source/bibliography/formcontrolcontainer.cxx
new file mode 100644
index 000000000..c6ec73700
--- /dev/null
+++ b/extensions/source/bibliography/formcontrolcontainer.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "formcontrolcontainer.hxx"
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+
+namespace bib
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::awt;
+
+ FormControlContainer::FormControlContainer( )
+ :OLoadListener( m_aMutex )
+ {
+ }
+
+ FormControlContainer::~FormControlContainer( )
+ {
+ SAL_WARN_IF( isFormConnected(), "extensions.biblio", "FormControlContainer::~FormControlContainer: you should disconnect in your derived class!" );
+ if ( isFormConnected() )
+ disconnectForm();
+ }
+
+ void FormControlContainer::disconnectForm()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ SAL_WARN_IF( !isFormConnected(), "extensions.biblio", "FormControlContainer::connectForm: not connected!" );
+ if ( isFormConnected() )
+ {
+ m_xFormAdapter->dispose();
+ m_xFormAdapter.clear();
+ }
+ }
+
+ void FormControlContainer::connectForm( const Reference< XLoadable >& _rxForm )
+ {
+ SAL_WARN_IF( isFormConnected(), "extensions.biblio", "FormControlContainer::connectForm: already connected!" );
+
+ SAL_WARN_IF( !_rxForm.is(), "extensions.biblio", "FormControlContainer::connectForm: invalid form!" );
+ if ( !isFormConnected() && _rxForm.is() )
+ {
+ m_xFormAdapter = new OLoadListenerAdapter( _rxForm );
+ m_xFormAdapter->Init( this );
+
+ implSetDesignMode( !m_xForm.is() || !m_xForm->isLoaded() );
+ }
+
+ m_xForm = _rxForm;
+ }
+
+ namespace {
+
+ struct ControlModeSwitch
+ {
+ bool bDesign;
+ explicit ControlModeSwitch( bool _bDesign ) : bDesign( _bDesign ) { }
+
+ void operator() ( const Reference< XControl >& _rxControl ) const
+ {
+ if ( _rxControl.is() )
+ _rxControl->setDesignMode( bDesign );
+ }
+ };
+
+ }
+
+ void FormControlContainer::implSetDesignMode( bool _bDesign )
+ {
+ try
+ {
+ Reference< XControlContainer > xControlCont = getControlContainer();
+ if ( xControlCont.is() )
+ {
+ const Sequence<Reference<XControl>> aControls = xControlCont->getControls();
+
+ std::for_each(
+ aControls.begin(),
+ aControls.end(),
+ ControlModeSwitch( _bDesign )
+ );
+ }
+ }
+ catch( const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.biblio", "FormControlContainer::implSetDesignMode" );
+ }
+ }
+
+ void FormControlContainer::_loaded( const css::lang::EventObject& /*_rEvent*/ )
+ {
+ implSetDesignMode( false );
+ }
+
+ void FormControlContainer::_unloading( const css::lang::EventObject& /*_rEvent*/ )
+ {
+ implSetDesignMode( true );
+ }
+
+ void FormControlContainer::_reloading( const css::lang::EventObject& /*_rEvent*/ )
+ {
+ implSetDesignMode( true );
+ }
+
+ void FormControlContainer::_reloaded( const css::lang::EventObject& /*_rEvent*/ )
+ {
+ implSetDesignMode( false );
+ }
+
+
+} // namespace bib
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/formcontrolcontainer.hxx b/extensions/source/bibliography/formcontrolcontainer.hxx
new file mode 100644
index 000000000..b15105ad0
--- /dev/null
+++ b/extensions/source/bibliography/formcontrolcontainer.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 <cppuhelper/basemutex.hxx>
+#include "loadlisteneradapter.hxx"
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <rtl/ref.hxx>
+
+
+namespace bib
+{
+
+ class FormControlContainer
+ :public ::cppu::BaseMutex
+ ,public ::bib::OLoadListener
+ {
+ private:
+ rtl::Reference<OLoadListenerAdapter> m_xFormAdapter;
+ css::uno::Reference< css::form::XLoadable > m_xForm;
+ private:
+ void implSetDesignMode( bool _bDesign );
+
+ protected:
+ FormControlContainer( );
+ virtual ~FormControlContainer( ) override;
+
+ bool isFormConnected() const { return m_xFormAdapter.is(); }
+ void connectForm( const css::uno::Reference< css::form::XLoadable >& _rxForm );
+ void disconnectForm();
+
+ virtual css::uno::Reference< css::awt::XControlContainer >
+ getControlContainer() = 0;
+
+ protected:
+ // XLoadListener equivalents
+ virtual void _loaded( const css::lang::EventObject& _rEvent ) override;
+ virtual void _unloading( const css::lang::EventObject& _rEvent ) override;
+ virtual void _reloading( const css::lang::EventObject& _rEvent ) override;
+ virtual void _reloaded( const css::lang::EventObject& _rEvent ) override;
+
+ };
+
+
+} // namespace bib
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/framectr.cxx b/extensions/source/bibliography/framectr.cxx
new file mode 100644
index 000000000..a273635d9
--- /dev/null
+++ b/extensions/source/bibliography/framectr.cxx
@@ -0,0 +1,869 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/types.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include "framectr.hxx"
+#include "datman.hxx"
+#include <toolkit/helper/vclunohelper.hxx>
+#include "bibconfig.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/form/XConfirmDeleteListener.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/sdbcx/Privilege.hpp>
+#include <com/sun/star/sdbc/XResultSetUpdate.hpp>
+#include <com/sun/star/sdb/FilterDialog.hpp>
+#include <com/sun/star/sdb/RowChangeAction.hpp>
+#include <com/sun/star/frame/CommandGroup.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <comphelper/multicontainer2.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/weld.hxx>
+#include <osl/mutex.hxx>
+
+#include <unordered_map>
+
+using namespace osl;
+using namespace cppu;
+using namespace com::sun::star::sdbc;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::uno;
+using namespace com::sun::star;
+
+namespace {
+
+struct DispatchInfo
+{
+ const char* pCommand;
+ sal_Int16 nGroupId;
+ bool bActiveConnection;
+};
+
+struct CacheDispatchInfo
+{
+ sal_Int16 nGroupId;
+ bool bActiveConnection;
+};
+
+}
+
+// Attention: commands must be sorted by command groups. Implementation is dependent
+// on this!!
+const DispatchInfo SupportedCommandsArray[] =
+{
+ { ".uno:Undo" , frame::CommandGroup::EDIT , false },
+ { ".uno:Cut" , frame::CommandGroup::EDIT , false },
+ { ".uno:Copy" , frame::CommandGroup::EDIT , false },
+ { ".uno:Paste" , frame::CommandGroup::EDIT , false },
+ { ".uno:SelectAll" , frame::CommandGroup::EDIT , false },
+ { ".uno:CloseDoc" , frame::CommandGroup::DOCUMENT , false },
+ { ".uno:StatusBarVisible" , frame::CommandGroup::VIEW , false },
+ { ".uno:AvailableToolbars" , frame::CommandGroup::VIEW , false },
+ { ".uno:Bib/standardFilter" , frame::CommandGroup::DATA , true },
+ { ".uno:Bib/DeleteRecord" , frame::CommandGroup::DATA , true },
+ { ".uno:Bib/InsertRecord" , frame::CommandGroup::DATA , true },
+ { ".uno:Bib/query" , frame::CommandGroup::DATA , true },
+ { ".uno:Bib/autoFilter" , frame::CommandGroup::DATA , true },
+ { ".uno:Bib/source" , frame::CommandGroup::DATA , true },
+ { ".uno:Bib/removeFilter" , frame::CommandGroup::DATA , true },
+ { ".uno:Bib/sdbsource" , frame::CommandGroup::DATA , true },
+ { ".uno:Bib/Mapping" , frame::CommandGroup::DATA , true },
+};
+
+typedef std::unordered_map< OUString, CacheDispatchInfo > CmdToInfoCache;
+
+static const CmdToInfoCache& GetCommandToInfoCache()
+{
+ static CmdToInfoCache aCmdToInfoCache = []() {
+ CmdToInfoCache aCache;
+ for (const auto& command : SupportedCommandsArray)
+ {
+ OUString aCommand(OUString::createFromAscii(command.pCommand));
+
+ CacheDispatchInfo aDispatchInfo;
+ aDispatchInfo.nGroupId = command.nGroupId;
+ aDispatchInfo.bActiveConnection = command.bActiveConnection;
+ aCache.emplace(aCommand, aDispatchInfo);
+ }
+ return aCache;
+ }();
+
+ return aCmdToInfoCache;
+}
+
+
+class BibFrameCtrl_Impl : public cppu::WeakImplHelper < XFrameActionListener >
+{
+public:
+ Mutex aMutex;
+ comphelper::OMultiTypeInterfaceContainerHelper2 aLC;
+
+ BibFrameController_Impl* pController;
+
+ BibFrameCtrl_Impl()
+ : aLC( aMutex )
+ , pController(nullptr)
+ {}
+
+ virtual void SAL_CALL frameAction(const FrameActionEvent& aEvent) override;
+ virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
+};
+
+void BibFrameCtrl_Impl::frameAction(const FrameActionEvent& )
+{
+}
+
+void BibFrameCtrl_Impl::disposing( const lang::EventObject& /*Source*/ )
+{
+ ::SolarMutexGuard aGuard;
+ if ( pController )
+ pController->getFrame()->removeFrameActionListener( this );
+}
+
+BibFrameController_Impl::BibFrameController_Impl( const uno::Reference< awt::XWindow > & xComponent,
+ BibDataManager* pDataManager)
+ :m_xWindow( xComponent )
+ ,m_xDatMan( pDataManager )
+{
+ m_bDisposing = false;
+ m_xImpl = new BibFrameCtrl_Impl;
+ m_xImpl->pController = this;
+}
+
+BibFrameController_Impl::~BibFrameController_Impl()
+{
+ m_xImpl->pController = nullptr;
+ m_xDatMan.clear();
+}
+
+OUString SAL_CALL BibFrameController_Impl::getImplementationName()
+{
+ return "com.sun.star.comp.extensions.Bibliography";
+}
+
+sal_Bool SAL_CALL BibFrameController_Impl::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService( this, sServiceName );
+}
+
+css::uno::Sequence< OUString > SAL_CALL BibFrameController_Impl::getSupportedServiceNames()
+{
+ // return only top level services ...
+ // base services are included there and should be asked by uno-rtti.
+ return { "com.sun.star.frame.Bibliography" };
+}
+
+void BibFrameController_Impl::attachFrame( const uno::Reference< XFrame > & xArg )
+{
+ m_xFrame = xArg;
+ m_xFrame->addFrameActionListener( m_xImpl );
+}
+
+sal_Bool BibFrameController_Impl::attachModel( const uno::Reference< XModel > & /*xModel*/ )
+{
+ return false;
+}
+
+sal_Bool BibFrameController_Impl::suspend( sal_Bool bSuspend )
+{
+ if ( bSuspend )
+ getFrame()->removeFrameActionListener( m_xImpl );
+ else
+ getFrame()->addFrameActionListener( m_xImpl );
+ return true;
+}
+
+uno::Any BibFrameController_Impl::getViewData()
+{
+ return uno::Any();
+}
+
+void BibFrameController_Impl::restoreViewData( const uno::Any& /*Value*/ )
+{
+}
+
+uno::Reference< XFrame > BibFrameController_Impl::getFrame()
+{
+ return m_xFrame;
+}
+
+uno::Reference< XModel > BibFrameController_Impl::getModel()
+{
+ return uno::Reference< XModel > ();
+}
+
+void BibFrameController_Impl::dispose()
+{
+ m_bDisposing = true;
+ lang::EventObject aObject;
+ uno::Reference< XFrame > xFrame = getFrame();
+
+ if (xFrame.is())
+ xFrame->removeFrameActionListener( m_xImpl );
+
+ aObject.Source = static_cast<XController*>(this);
+ m_xImpl->aLC.disposeAndClear(aObject);
+ m_xDatMan.clear();
+ m_aStatusListeners.clear();
+ m_xLastQueriedFocusWin.clear();
+}
+
+void BibFrameController_Impl::addEventListener( const uno::Reference< lang::XEventListener > & aListener )
+{
+ m_xImpl->aLC.addInterface( cppu::UnoType<lang::XEventListener>::get(), aListener );
+}
+
+void BibFrameController_Impl::removeEventListener( const uno::Reference< lang::XEventListener > & aListener )
+{
+ m_xImpl->aLC.removeInterface( cppu::UnoType<lang::XEventListener>::get(), aListener );
+}
+
+uno::Reference< frame::XDispatch > BibFrameController_Impl::queryDispatch( const util::URL& aURL, const OUString& /*aTarget*/, sal_Int32 /*nSearchFlags*/ )
+{
+ if ( !m_bDisposing )
+ {
+ const CmdToInfoCache& rCmdCache = GetCommandToInfoCache();
+ CmdToInfoCache::const_iterator pIter = rCmdCache.find( aURL.Complete );
+ if ( pIter != rCmdCache.end() )
+ {
+ if (( m_xDatMan->HasActiveConnection() ) ||
+ ( !pIter->second.bActiveConnection ))
+ return static_cast<frame::XDispatch*>(this);
+ }
+ }
+
+ return uno::Reference< frame::XDispatch > ();
+}
+
+uno::Sequence<uno::Reference< XDispatch > > BibFrameController_Impl::queryDispatches( const uno::Sequence<DispatchDescriptor>& aDescripts )
+{
+ uno::Sequence< uno::Reference< XDispatch > > aDispatches( aDescripts.getLength() );
+ auto aDispatchesRange = asNonConstRange(aDispatches);
+ for ( sal_Int32 i=0; i<aDescripts.getLength(); ++i )
+ aDispatchesRange[i] = queryDispatch( aDescripts[i].FeatureURL, aDescripts[i].FrameName, aDescripts[i].SearchFlags );
+ return aDispatches;
+}
+
+uno::Sequence< ::sal_Int16 > SAL_CALL BibFrameController_Impl::getSupportedCommandGroups()
+{
+ uno::Sequence< ::sal_Int16 > aDispatchInfo{ frame::CommandGroup::EDIT,
+ frame::CommandGroup::DOCUMENT,
+ frame::CommandGroup::DATA,
+ frame::CommandGroup::VIEW };
+
+ return aDispatchInfo;
+}
+
+uno::Sequence< frame::DispatchInformation > SAL_CALL BibFrameController_Impl::getConfigurableDispatchInformation( ::sal_Int16 nCommandGroup )
+{
+ const CmdToInfoCache& rCmdCache = GetCommandToInfoCache();
+
+ frame::DispatchInformation aDispatchInfo;
+ std::vector< frame::DispatchInformation > aDispatchInfoVector;
+
+ if (( nCommandGroup == frame::CommandGroup::EDIT ) ||
+ ( nCommandGroup == frame::CommandGroup::DOCUMENT ) ||
+ ( nCommandGroup == frame::CommandGroup::DATA ) ||
+ ( nCommandGroup == frame::CommandGroup::VIEW ))
+ {
+ bool bGroupFound = false;
+ for (auto const& item : rCmdCache)
+ {
+ if ( item.second.nGroupId == nCommandGroup )
+ {
+ bGroupFound = true;
+ aDispatchInfo.Command = item.first;
+ aDispatchInfo.GroupId = item.second.nGroupId;
+ aDispatchInfoVector.push_back( aDispatchInfo );
+ }
+ else if ( bGroupFound )
+ break;
+ }
+ }
+
+ return comphelper::containerToSequence( aDispatchInfoVector );
+}
+
+static bool canInsertRecords(const Reference< beans::XPropertySet>& _rxCursorSet)
+{
+ sal_Int32 nPriv = 0;
+ _rxCursorSet->getPropertyValue("Privileges") >>= nPriv;
+ return _rxCursorSet.is() && (nPriv & sdbcx::Privilege::INSERT) != 0;
+}
+
+bool BibFrameController_Impl::SaveModified(const Reference< form::runtime::XFormController>& xController)
+{
+ if (!xController.is())
+ return false;
+
+ Reference< XResultSetUpdate> _xCursor(xController->getModel(), UNO_QUERY);
+
+ if (!_xCursor.is())
+ return false;
+
+ Reference< beans::XPropertySet> _xSet(_xCursor, UNO_QUERY);
+ if (!_xSet.is())
+ return false;
+
+ // need to save?
+ bool bIsNew = ::comphelper::getBOOL(_xSet->getPropertyValue("IsNew"));
+ bool bIsModified = ::comphelper::getBOOL(_xSet->getPropertyValue("IsModified"));
+ bool bResult = !bIsModified;
+ if (bIsModified)
+ {
+ try
+ {
+ if (bIsNew)
+ _xCursor->insertRow();
+ else
+ _xCursor->updateRow();
+ bResult = true;
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.biblio", "");
+ }
+ }
+ return bResult;
+}
+
+static vcl::Window* lcl_GetFocusChild( vcl::Window const * pParent )
+{
+ sal_uInt16 nChildren = pParent->GetChildCount();
+ for( sal_uInt16 nChild = 0; nChild < nChildren; ++nChild)
+ {
+ vcl::Window* pChild = pParent->GetChild( nChild );
+ if(pChild->HasFocus())
+ return pChild;
+ vcl::Window* pSubChild = lcl_GetFocusChild( pChild );
+ if(pSubChild)
+ return pSubChild;
+ }
+ return nullptr;
+}
+
+//class XDispatch
+void BibFrameController_Impl::dispatch(const util::URL& _rURL, const uno::Sequence< beans::PropertyValue >& aArgs)
+{
+ if ( m_bDisposing )
+ return;
+
+ ::SolarMutexGuard aGuard;
+ weld::Window* pParent = Application::GetFrameWeld(m_xWindow);
+ weld::WaitObject aWaitObject(pParent);
+
+ OUString aCommand( _rURL.Path);
+ if(aCommand == "Bib/Mapping")
+ {
+ m_xDatMan->CreateMappingDialog(pParent);
+ }
+ else if(aCommand == "Bib/source")
+ {
+ ChangeDataSource(aArgs);
+ }
+ else if(aCommand == "Bib/sdbsource")
+ {
+ OUString aURL = m_xDatMan->CreateDBChangeDialog(pParent);
+ if(!aURL.isEmpty())
+ {
+ try
+ {
+ uno::Sequence< beans::PropertyValue > aNewDataSource
+ {
+ comphelper::makePropertyValue( {}, OUString() ),
+ comphelper::makePropertyValue( {}, aURL )
+ };
+ ChangeDataSource(aNewDataSource);
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.biblio",
+ "Exception caught while changing the data source");
+ }
+ }
+ }
+ else if(aCommand == "Bib/autoFilter")
+ {
+ sal_uInt16 nCount = m_aStatusListeners.size();
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ BibStatusDispatch *pObj = m_aStatusListeners[n].get();
+ if ( pObj->aURL.Path == "Bib/removeFilter" )
+ {
+ FeatureStateEvent aEvent;
+ aEvent.FeatureURL = pObj->aURL;
+ aEvent.IsEnabled = true;
+ aEvent.Requery = false;
+ aEvent.Source = static_cast<XDispatch *>(this);
+ pObj->xListener->statusChanged( aEvent );
+ //break; because there are more than one
+ }
+ }
+
+ const beans::PropertyValue* pPropertyValue = aArgs.getConstArray();
+ uno::Any aValue=pPropertyValue[0].Value;
+ OUString aQuery;
+ aValue >>= aQuery;
+
+ aValue=pPropertyValue[1].Value;
+ OUString aQueryField;
+ aValue >>= aQueryField;
+ BibConfig* pConfig = BibModul::GetConfig();
+ pConfig->setQueryField(aQueryField);
+ m_xDatMan->startQueryWith(aQuery);
+ }
+ else if(aCommand == "Bib/standardFilter")
+ {
+ try
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ // create the dialog object
+ uno::Reference< ui::dialogs::XExecutableDialog > xDialog = sdb::FilterDialog::createWithQuery(xContext, m_xDatMan->getParser(),
+ Reference<sdbc::XRowSet>(m_xDatMan->getForm(), uno::UNO_QUERY_THROW), m_xWindow);
+ // execute it
+ if ( xDialog->execute( ) )
+ {
+ // the dialog has been executed successfully, and the filter on the query composer
+ // has been changed
+ OUString sNewFilter = m_xDatMan->getParser()->getFilter();
+ m_xDatMan->setFilter( sNewFilter );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.biblio", "BibFrameController_Impl::dispatch" );
+ }
+
+ sal_uInt16 nCount = m_aStatusListeners.size();
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ BibStatusDispatch *pObj = m_aStatusListeners[n].get();
+ if ( pObj->aURL.Path == "Bib/removeFilter" && m_xDatMan->getParser().is())
+ {
+ FeatureStateEvent aEvent;
+ aEvent.FeatureURL = pObj->aURL;
+ aEvent.IsEnabled = !m_xDatMan->getParser()->getFilter().isEmpty();
+ aEvent.Requery = false;
+ aEvent.Source = static_cast<XDispatch *>(this);
+ pObj->xListener->statusChanged( aEvent );
+ }
+ }
+ }
+ else if(aCommand == "Bib/removeFilter")
+ {
+ RemoveFilter();
+ }
+ else if( _rURL.Complete == "slot:5503" || aCommand == "CloseDoc" )
+ {
+ Application::PostUserEvent( LINK( this, BibFrameController_Impl,
+ DisposeHdl ) );
+
+ }
+ else if(aCommand == "Bib/InsertRecord")
+ {
+ Reference<form::runtime::XFormController > xFormCtrl = m_xDatMan->GetFormController();
+ if(SaveModified(xFormCtrl))
+ {
+ try
+ {
+ Reference< sdbc::XResultSet > xCursor( m_xDatMan->getForm(), UNO_QUERY );
+ xCursor->last();
+
+ Reference< XResultSetUpdate > xUpdateCursor( m_xDatMan->getForm(), UNO_QUERY );
+ xUpdateCursor->moveToInsertRow();
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.biblio",
+ "Exception in last() or moveToInsertRow()");
+ }
+ }
+ }
+ else if(aCommand == "Bib/DeleteRecord")
+ {
+ Reference< css::sdbc::XResultSet > xCursor(m_xDatMan->getForm(), UNO_QUERY);
+ Reference< XResultSetUpdate > xUpdateCursor(xCursor, UNO_QUERY);
+ Reference< beans::XPropertySet > xSet(m_xDatMan->getForm(), UNO_QUERY);
+ bool bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue("IsNew"));
+ if(!bIsNew)
+ {
+ sal_uInt32 nCount = 0;
+ xSet->getPropertyValue("RowCount") >>= nCount;
+ // determine next position
+ bool bSuccess = false;
+ bool bLeft = false;
+ bool bRight = false;
+ try
+ {
+ bLeft = xCursor->isLast() && nCount > 1;
+ bRight= !xCursor->isLast();
+ // ask for confirmation
+ Reference< form::XConfirmDeleteListener > xConfirm(m_xDatMan->GetFormController(),UNO_QUERY);
+ if (xConfirm.is())
+ {
+ sdb::RowChangeEvent aEvent;
+ aEvent.Source.set(xCursor, UNO_QUERY);
+ aEvent.Action = sdb::RowChangeAction::DELETE;
+ aEvent.Rows = 1;
+ bSuccess = xConfirm->confirmDelete(aEvent);
+ }
+
+ // delete it
+ if (bSuccess)
+ xUpdateCursor->deleteRow();
+ }
+ catch(const Exception&)
+ {
+ bSuccess = false;
+ }
+ if (bSuccess)
+ {
+ if (bLeft || bRight)
+ xCursor->relative(bRight ? 1 : -1);
+ else
+ {
+ bool bCanInsert = canInsertRecords(xSet);
+ // can another entry be inserted?
+ try
+ {
+ if (bCanInsert)
+ xUpdateCursor->moveToInsertRow();
+ else
+ // move data entry to reset state
+ xCursor->first();
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.biblio",
+ "DeleteRecord: exception caught!");
+ }
+ }
+ }
+ }
+ }
+ else if(aCommand == "Cut")
+ {
+ vcl::Window* pChild = m_xLastQueriedFocusWin.get();
+ if(pChild)
+ {
+ KeyEvent aEvent( 0, KeyFuncType::CUT );
+ pChild->KeyInput( aEvent );
+ }
+ }
+ else if(aCommand == "Copy")
+ {
+ vcl::Window* pChild = m_xLastQueriedFocusWin.get();
+ if(pChild)
+ {
+ KeyEvent aEvent( 0, KeyFuncType::COPY );
+ pChild->KeyInput( aEvent );
+ }
+ }
+ else if(aCommand == "Paste")
+ {
+ vcl::Window* pChild = m_xLastQueriedFocusWin.get();
+ if(pChild)
+ {
+ KeyEvent aEvent( 0, KeyFuncType::PASTE );
+ pChild->KeyInput( aEvent );
+ }
+ }
+}
+IMPL_LINK_NOARG( BibFrameController_Impl, DisposeHdl, void*, void )
+{
+ if (m_xFrame.is())
+ m_xFrame->dispose();
+};
+
+void BibFrameController_Impl::addStatusListener(
+ const uno::Reference< frame::XStatusListener > & aListener,
+ const util::URL& aURL)
+{
+ BibConfig* pConfig = BibModul::GetConfig();
+ // create a new Reference and insert into listener array
+ m_aStatusListeners.push_back( std::make_unique<BibStatusDispatch>( aURL, aListener ) );
+
+ // send first status synchronously
+ FeatureStateEvent aEvent;
+ aEvent.FeatureURL = aURL;
+ aEvent.Requery = false;
+ aEvent.Source = static_cast<XDispatch *>(this);
+ if ( aURL.Path == "StatusBarVisible" )
+ {
+ aEvent.IsEnabled = false;
+ aEvent.State <<= false;
+ }
+ else if ( aURL.Path == "Bib/hierarchical" )
+ {
+ aEvent.IsEnabled = true;
+ aEvent.State <<= OUString();
+ }
+ else if(aURL.Path == "Bib/MenuFilter")
+ {
+ aEvent.IsEnabled = true;
+ aEvent.FeatureDescriptor=m_xDatMan->getQueryField();
+
+ aEvent.State <<= m_xDatMan->getQueryFields();
+
+ }
+ else if ( aURL.Path == "Bib/source")
+ {
+ aEvent.IsEnabled = true;
+ aEvent.FeatureDescriptor=m_xDatMan->getActiveDataTable();
+
+ aEvent.State <<= m_xDatMan->getDataSources();
+ }
+ else if( aURL.Path == "Bib/sdbsource" ||
+ aURL.Path == "Bib/Mapping" ||
+ aURL.Path == "Bib/autoFilter" ||
+ aURL.Path == "Bib/standardFilter" )
+ {
+ aEvent.IsEnabled = true;
+ }
+ else if(aURL.Path == "Bib/query")
+ {
+ aEvent.IsEnabled = true;
+ aEvent.State <<= pConfig->getQueryText();
+ }
+ else if (aURL.Path == "Bib/removeFilter" )
+ {
+ OUString aFilterStr=m_xDatMan->getFilter();
+ aEvent.IsEnabled = !aFilterStr.isEmpty();
+ }
+ else if(aURL.Path == "Cut")
+ {
+ m_xLastQueriedFocusWin = lcl_GetFocusChild( VCLUnoHelper::GetWindow( m_xWindow ) );
+ if (m_xLastQueriedFocusWin)
+ {
+ Reference<css::awt::XTextComponent> xEdit(m_xLastQueriedFocusWin->GetComponentInterface(), css::uno::UNO_QUERY);
+ aEvent.IsEnabled = xEdit && xEdit->isEditable() && !xEdit->getSelectedText().isEmpty();
+ }
+ }
+ if(aURL.Path == "Copy")
+ {
+ m_xLastQueriedFocusWin = lcl_GetFocusChild( VCLUnoHelper::GetWindow( m_xWindow ) );
+ if (m_xLastQueriedFocusWin)
+ {
+ Reference<css::awt::XTextComponent> xEdit(m_xLastQueriedFocusWin->GetComponentInterface(), css::uno::UNO_QUERY);
+ aEvent.IsEnabled = xEdit && !xEdit->getSelectedText().isEmpty();
+ }
+ }
+ else if(aURL.Path == "Paste" )
+ {
+ aEvent.IsEnabled = false;
+ m_xLastQueriedFocusWin = lcl_GetFocusChild( VCLUnoHelper::GetWindow( m_xWindow ) );
+ if (m_xLastQueriedFocusWin)
+ {
+ Reference<css::awt::XTextComponent> xEdit(m_xLastQueriedFocusWin->GetComponentInterface(), css::uno::UNO_QUERY);
+ if (xEdit && !xEdit->isEditable())
+ {
+ uno::Reference< datatransfer::clipboard::XClipboard > xClip = m_xLastQueriedFocusWin->GetClipboard();
+ if(xClip.is())
+ {
+ uno::Reference< datatransfer::XTransferable > xDataObj;
+
+ try
+ {
+ SolarMutexReleaser aReleaser;
+ xDataObj = xClip->getContents();
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ if ( xDataObj.is() )
+ {
+ datatransfer::DataFlavor aFlavor;
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
+ try
+ {
+ uno::Any aData = xDataObj->getTransferData( aFlavor );
+ OUString aText;
+ aData >>= aText;
+ aEvent.IsEnabled = !aText.isEmpty();
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ }
+ }
+ }
+ }
+ else if(aURL.Path == "Bib/DeleteRecord")
+ {
+ Reference< beans::XPropertySet > xSet(m_xDatMan->getForm(), UNO_QUERY);
+ bool bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue("IsNew"));
+ if(!bIsNew)
+ {
+ sal_uInt32 nCount = 0;
+ xSet->getPropertyValue("RowCount") >>= nCount;
+ aEvent.IsEnabled = nCount > 0;
+ }
+ }
+ else if (aURL.Path == "Bib/InsertRecord")
+ {
+ Reference< beans::XPropertySet > xSet(m_xDatMan->getForm(), UNO_QUERY);
+ aEvent.IsEnabled = canInsertRecords(xSet);
+ }
+ aListener->statusChanged( aEvent );
+}
+
+void BibFrameController_Impl::removeStatusListener(
+ const uno::Reference< frame::XStatusListener > & aObject, const util::URL& aURL)
+{
+ // search listener array for given listener
+ // for checking equality always "cast" to XInterface
+ if ( m_bDisposing )
+ return;
+
+ sal_uInt16 nCount = m_aStatusListeners.size();
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ BibStatusDispatch *pObj = m_aStatusListeners[n].get();
+ bool bFlag=pObj->xListener.is();
+ if (!bFlag || (pObj->xListener == aObject &&
+ ( aURL.Complete.isEmpty() || pObj->aURL.Path == aURL.Path )))
+ {
+ m_aStatusListeners.erase( m_aStatusListeners.begin() + n );
+ break;
+ }
+ }
+}
+
+void BibFrameController_Impl::RemoveFilter()
+{
+ OUString aQuery;
+ m_xDatMan->startQueryWith(aQuery);
+
+ sal_uInt16 nCount = m_aStatusListeners.size();
+
+ bool bRemoveFilter=false;
+ bool bQueryText=false;
+
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ BibStatusDispatch *pObj = m_aStatusListeners[n].get();
+ if ( pObj->aURL.Path == "Bib/removeFilter" )
+ {
+ FeatureStateEvent aEvent;
+ aEvent.FeatureURL = pObj->aURL;
+ aEvent.IsEnabled = false;
+ aEvent.Requery = false;
+ aEvent.Source = static_cast<XDispatch *>(this);
+ pObj->xListener->statusChanged( aEvent );
+ bRemoveFilter=true;
+ }
+ else if(pObj->aURL.Path == "Bib/query")
+ {
+ FeatureStateEvent aEvent;
+ aEvent.FeatureURL = pObj->aURL;
+ aEvent.IsEnabled = true;
+ aEvent.Requery = false;
+ aEvent.Source = static_cast<XDispatch *>(this);
+ aEvent.State <<= aQuery;
+ pObj->xListener->statusChanged( aEvent );
+ bQueryText=true;
+ }
+
+ if(bRemoveFilter && bQueryText)
+ break;
+
+ }
+}
+
+void BibFrameController_Impl::ChangeDataSource(const uno::Sequence< beans::PropertyValue >& aArgs)
+{
+ const beans::PropertyValue* pPropertyValue = aArgs.getConstArray();
+ uno::Any aValue=pPropertyValue[0].Value;
+ OUString aDBTableName;
+ aValue >>= aDBTableName;
+
+
+ if(aArgs.getLength() > 1)
+ {
+ uno::Any aDB = pPropertyValue[1].Value;
+ OUString aURL;
+ aDB >>= aURL;
+ m_xDatMan->setActiveDataSource(aURL);
+ aDBTableName = m_xDatMan->getActiveDataTable();
+ }
+ else
+ {
+ Reference<css::form::XLoadable> xLoadable(m_xDatMan);
+ xLoadable->unload();
+ m_xDatMan->setActiveDataTable(aDBTableName);
+ m_xDatMan->updateGridModel();
+ xLoadable->load();
+ }
+
+
+ sal_uInt16 nCount = m_aStatusListeners.size();
+
+ bool bMenuFilter=false;
+ bool bQueryText=false;
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ BibStatusDispatch *pObj = m_aStatusListeners[n].get();
+ if (pObj->aURL.Path == "Bib/MenuFilter")
+ {
+ FeatureStateEvent aEvent;
+ aEvent.FeatureURL = pObj->aURL;
+ aEvent.IsEnabled = true;
+ aEvent.Requery = false;
+ aEvent.Source = static_cast<XDispatch *>(this);
+ aEvent.FeatureDescriptor=m_xDatMan->getQueryField();
+
+ uno::Sequence<OUString> aStringSeq=m_xDatMan->getQueryFields();
+ aEvent.State <<= aStringSeq;
+
+ pObj->xListener->statusChanged( aEvent );
+ bMenuFilter=true;
+ }
+ else if (pObj->aURL.Path == "Bib/query")
+ {
+ FeatureStateEvent aEvent;
+ aEvent.FeatureURL = pObj->aURL;
+ aEvent.IsEnabled = true;
+ aEvent.Requery = false;
+ aEvent.Source = static_cast<XDispatch *>(this);
+ BibConfig* pConfig = BibModul::GetConfig();
+ aEvent.State <<= pConfig->getQueryText();
+ pObj->xListener->statusChanged( aEvent );
+ bQueryText=true;
+ }
+
+ if (bMenuFilter && bQueryText)
+ break;
+
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/framectr.hxx b/extensions/source/bibliography/framectr.hxx
new file mode 100644
index 000000000..be91982cc
--- /dev/null
+++ b/extensions/source/bibliography/framectr.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 <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/frame/XDispatchInformationProvider.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <tools/link.hxx>
+#include <vcl/window.hxx>
+#include <vector>
+#include <memory>
+
+#include "bibmod.hxx"
+class BibDataManager;
+class BibFrameCtrl_Impl;
+namespace com::sun::star{
+ namespace form::runtime {
+ class XFormController;
+ }
+}
+class BibStatusDispatch
+{
+public:
+ css::util::URL aURL;
+ css::uno::Reference< css::frame::XStatusListener > xListener;
+ BibStatusDispatch( const css::util::URL& rURL, const css::uno::Reference< css::frame::XStatusListener >& rRef )
+ : aURL( rURL )
+ , xListener( rRef )
+ {}
+};
+
+typedef std::vector<std::unique_ptr<BibStatusDispatch> > BibStatusDispatchArr;
+
+class BibFrameController_Impl : public cppu::WeakImplHelper <
+ css::lang::XServiceInfo,
+ css::frame::XController,
+ css::frame::XDispatch,
+ css::frame::XDispatchProvider,
+ css::frame::XDispatchInformationProvider
+>
+{
+friend class BibFrameCtrl_Impl;
+ rtl::Reference<BibFrameCtrl_Impl> m_xImpl;
+ BibStatusDispatchArr m_aStatusListeners;
+ css::uno::Reference< css::awt::XWindow > m_xWindow;
+ css::uno::Reference< css::frame::XFrame > m_xFrame;
+ bool m_bDisposing;
+ rtl::Reference<BibDataManager> m_xDatMan;
+ VclPtr<vcl::Window> m_xLastQueriedFocusWin;
+
+ DECL_LINK( DisposeHdl, void*, void );
+
+ static bool SaveModified(const css::uno::Reference< css::form::runtime::XFormController>& xController);
+public:
+ BibFrameController_Impl( const css::uno::Reference< css::awt::XWindow > & xComponent,
+ BibDataManager* pDatMan);
+ virtual ~BibFrameController_Impl() override;
+
+
+ void ChangeDataSource(const css::uno::Sequence< css::beans::PropertyValue >& aArgs);
+ void RemoveFilter();
+
+ // css::lang::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::frame::XController
+ virtual void SAL_CALL attachFrame( const css::uno::Reference< css::frame::XFrame > & xFrame ) override;
+ virtual sal_Bool SAL_CALL attachModel( const css::uno::Reference< css::frame::XModel > & xModel ) override;
+ virtual sal_Bool SAL_CALL suspend( sal_Bool bSuspend ) override;
+ virtual css::uno::Any SAL_CALL getViewData() override;
+ virtual void SAL_CALL restoreViewData( const css::uno::Any& Value ) override;
+ virtual css::uno::Reference< css::frame::XFrame > SAL_CALL getFrame() override;
+ virtual css::uno::Reference< css::frame::XModel > SAL_CALL getModel() override;
+
+ // css::lang::XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener > & aListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener > & aListener ) override;
+
+ // css::frame::XDispatchProvider
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& aDescripts) override;
+
+ //class css::frame::XDispatch
+ virtual void SAL_CALL dispatch(const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs) override;
+ virtual void SAL_CALL addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) override;
+ virtual void SAL_CALL removeStatusListener(const css::uno::Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) override;
+
+ // css::frame::XDispatchInformationProvider
+ virtual css::uno::Sequence< ::sal_Int16 > SAL_CALL getSupportedCommandGroups( ) override;
+ virtual css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL getConfigurableDispatchInformation( ::sal_Int16 CommandGroup ) override;
+ };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/general.cxx b/extensions/source/bibliography/general.cxx
new file mode 100644
index 000000000..ff3625d30
--- /dev/null
+++ b/extensions/source/bibliography/general.cxx
@@ -0,0 +1,876 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/form/XBoundComponent.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <vcl/event.hxx>
+#include <vcl/mnemonic.hxx>
+#include "general.hxx"
+#include "bibresid.hxx"
+#include "datman.hxx"
+#include "bibconfig.hxx"
+#include <strings.hrc>
+#include "bibmod.hxx"
+#include <helpids.h>
+#include <algorithm>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/objsh.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::sdb;
+
+namespace
+{
+/// Tries to split rText into rURL and nPageNumber.
+bool SplitUrlAndPage(const OUString& rText, OUString& rUrl, int& nPageNumber)
+{
+ uno::Reference<uri::XUriReferenceFactory> xUriReferenceFactory
+ = uri::UriReferenceFactory::create(comphelper::getProcessComponentContext());
+ uno::Reference<uri::XUriReference> xUriRef;
+ try
+ {
+ xUriRef = xUriReferenceFactory->parse(rText);
+ }
+ catch (const uno::Exception& rException)
+ {
+ SAL_WARN("extensions.biblio",
+ "SplitUrlAndPage: failed to parse url: " << rException.Message);
+ return false;
+ }
+
+ OUString aPagePrefix("page=");
+ if (!xUriRef->getFragment().startsWith(aPagePrefix))
+ {
+ return false;
+ }
+
+ nPageNumber = o3tl::toInt32(xUriRef->getFragment().subView(aPagePrefix.getLength()));
+ xUriRef->clearFragment();
+ rUrl = xUriRef->getUriReference();
+ return true;
+}
+
+/// Merges rUrl and rPageSB to a URL string.
+OUString MergeUrlAndPage(const OUString& rUrl, const weld::SpinButton& rPageSB)
+{
+ if (!rPageSB.get_sensitive())
+ {
+ return rUrl;
+ }
+
+ uno::Reference<uri::XUriReferenceFactory> xUriReferenceFactory
+ = uri::UriReferenceFactory::create(comphelper::getProcessComponentContext());
+ uno::Reference<uri::XUriReference> xUriRef;
+ try
+ {
+ xUriRef = xUriReferenceFactory->parse(rUrl);
+ }
+ catch (const uno::Exception& rException)
+ {
+ SAL_WARN("extensions.biblio",
+ "MergeUrlAndPage: failed to parse url: " << rException.Message);
+ return rUrl;
+ }
+
+ OUString aFragment("page=" + OUString::number(rPageSB.get_value()));
+ xUriRef->setFragment(aFragment);
+ return xUriRef->getUriReference();
+}
+}
+
+static OUString lcl_GetColumnName( const Mapping* pMapping, sal_uInt16 nIndexPos )
+{
+ BibConfig* pBibConfig = BibModul::GetConfig();
+ OUString sRet = pBibConfig->GetDefColumnName(nIndexPos);
+ if(pMapping)
+ for(const auto & aColumnPair : pMapping->aColumnPairs)
+ {
+ if(aColumnPair.sLogicalColumnName == sRet)
+ {
+ sRet = aColumnPair.sRealColumnName;
+ break;
+ }
+ }
+ return sRet;
+}
+
+BibGeneralPage::BibGeneralPage(vcl::Window* pParent, BibDataManager* pMan)
+ : InterimItemWindow(pParent, "modules/sbibliography/ui/generalpage.ui", "GeneralPage")
+ , BibShortCutHandler(this)
+ , xScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow"))
+ , xGrid(m_xBuilder->weld_widget("grid"))
+ , xIdentifierFT(m_xBuilder->weld_label("shortname"))
+ , xIdentifierED(m_xBuilder->weld_entry("shortnamecontrol"))
+ , xAuthTypeFT(m_xBuilder->weld_label("authtype"))
+ , xAuthTypeLB(m_xBuilder->weld_combo_box("authtypecontrol"))
+ , xYearFT(m_xBuilder->weld_label("year"))
+ , xYearED(m_xBuilder->weld_entry("yearcontrol"))
+ , xAuthorFT(m_xBuilder->weld_label("authors"))
+ , xAuthorED(m_xBuilder->weld_entry("authorscontrol"))
+ , xTitleFT(m_xBuilder->weld_label("title"))
+ , xTitleED(m_xBuilder->weld_entry("titlecontrol"))
+ , xPublisherFT(m_xBuilder->weld_label("publisher"))
+ , xPublisherED(m_xBuilder->weld_entry("publishercontrol"))
+ , xAddressFT(m_xBuilder->weld_label("address"))
+ , xAddressED(m_xBuilder->weld_entry("addresscontrol"))
+ , xISBNFT(m_xBuilder->weld_label("isbn"))
+ , xISBNED(m_xBuilder->weld_entry("isbncontrol"))
+ , xChapterFT(m_xBuilder->weld_label("chapter"))
+ , xChapterED(m_xBuilder->weld_entry("chaptercontrol"))
+ , xPagesFT(m_xBuilder->weld_label("pages"))
+ , xPagesED(m_xBuilder->weld_entry("pagescontrol"))
+ , xEditorFT(m_xBuilder->weld_label("editor"))
+ , xEditorED(m_xBuilder->weld_entry("editorcontrol"))
+ , xEditionFT(m_xBuilder->weld_label("edition"))
+ , xEditionED(m_xBuilder->weld_entry("editioncontrol"))
+ , xBooktitleFT(m_xBuilder->weld_label("booktitle"))
+ , xBooktitleED(m_xBuilder->weld_entry("booktitlecontrol"))
+ , xVolumeFT(m_xBuilder->weld_label("volume"))
+ , xVolumeED(m_xBuilder->weld_entry("volumecontrol"))
+ , xHowpublishedFT(m_xBuilder->weld_label("publicationtype"))
+ , xHowpublishedED(m_xBuilder->weld_entry("publicationtypecontrol"))
+ , xOrganizationsFT(m_xBuilder->weld_label("organization"))
+ , xOrganizationsED(m_xBuilder->weld_entry("organizationcontrol"))
+ , xInstitutionFT(m_xBuilder->weld_label("institution"))
+ , xInstitutionED(m_xBuilder->weld_entry("institutioncontrol"))
+ , xSchoolFT(m_xBuilder->weld_label("university"))
+ , xSchoolED(m_xBuilder->weld_entry("universitycontrol"))
+ , xReportTypeFT(m_xBuilder->weld_label("reporttype"))
+ , xReportTypeED(m_xBuilder->weld_entry("reporttypecontrol"))
+ , xMonthFT(m_xBuilder->weld_label("month"))
+ , xMonthED(m_xBuilder->weld_entry("monthcontrol"))
+ , xJournalFT(m_xBuilder->weld_label("journal"))
+ , xJournalED(m_xBuilder->weld_entry("journalcontrol"))
+ , xNumberFT(m_xBuilder->weld_label("number"))
+ , xNumberED(m_xBuilder->weld_entry("numbercontrol"))
+ , xSeriesFT(m_xBuilder->weld_label("series"))
+ , xSeriesED(m_xBuilder->weld_entry("seriescontrol"))
+ , xAnnoteFT(m_xBuilder->weld_label("annotation"))
+ , xAnnoteED(m_xBuilder->weld_entry("annotationcontrol"))
+ , xNoteFT(m_xBuilder->weld_label("note"))
+ , xNoteED(m_xBuilder->weld_entry("notecontrol"))
+ , xURLFT(m_xBuilder->weld_label("url"))
+ , xURLED(m_xBuilder->weld_entry("urlcontrol"))
+ , xCustom1FT(m_xBuilder->weld_label("custom1"))
+ , xCustom1ED(m_xBuilder->weld_entry("custom1control"))
+ , xCustom2FT(m_xBuilder->weld_label("custom2"))
+ , xCustom2ED(m_xBuilder->weld_entry("custom2control"))
+ , xCustom3FT(m_xBuilder->weld_label("custom3"))
+ , xCustom3ED(m_xBuilder->weld_entry("custom3control"))
+ , xCustom4FT(m_xBuilder->weld_label("custom4"))
+ , xCustom4ED(m_xBuilder->weld_entry("custom4control"))
+ , xCustom5FT(m_xBuilder->weld_label("custom5"))
+ , xCustom5ED(m_xBuilder->weld_entry("custom5control"))
+ , m_xLocalURLFT(m_xBuilder->weld_label("localurl"))
+ , m_xLocalURLED(m_xBuilder->weld_entry("localurlcontrol"))
+ , m_xLocalBrowseButton(m_xBuilder->weld_button("localbrowse"))
+ , m_xLocalPageCB(m_xBuilder->weld_check_button("localpagecb"))
+ , m_xLocalPageSB(m_xBuilder->weld_spin_button("localpagesb"))
+ , pDatMan(pMan)
+{
+ SetStyle(GetStyle() | WB_DIALOGCONTROL);
+
+ BibConfig* pBibConfig = BibModul::GetConfig();
+ BibDBDescriptor aDesc;
+ aDesc.sDataSource = pDatMan->getActiveDataSource();
+ aDesc.sTableOrQuery = pDatMan->getActiveDataTable();
+ aDesc.nCommandType = CommandType::TABLE;
+ const Mapping* pMapping = pBibConfig->GetMapping(aDesc);
+
+ xIdentifierED->connect_key_press(LINK(this, BibGeneralPage, FirstElementKeyInputHdl));
+
+ AddControlWithError(lcl_GetColumnName(pMapping, IDENTIFIER_POS),
+ xIdentifierFT->get_label(), *xIdentifierED,
+ sTableErrorString, HID_BIB_IDENTIFIER_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, AUTHORITYTYPE_POS),
+ xAuthTypeFT->get_label(), *xAuthTypeLB,
+ sTableErrorString, HID_BIB_AUTHORITYTYPE_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, YEAR_POS),
+ xYearFT->get_label(), *xYearED,
+ sTableErrorString, HID_BIB_YEAR_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, AUTHOR_POS),
+ xAuthorFT->get_label(), *xAuthorED,
+ sTableErrorString, HID_BIB_AUTHOR_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, TITLE_POS),
+ xTitleFT->get_label(), *xTitleED,
+ sTableErrorString, HID_BIB_TITLE_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, PUBLISHER_POS),
+ xPublisherFT->get_label(), *xPublisherED,
+ sTableErrorString, HID_BIB_PUBLISHER_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, ADDRESS_POS),
+ xAddressFT->get_label(), *xAddressED,
+ sTableErrorString, HID_BIB_ADDRESS_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, ISBN_POS),
+ xISBNFT->get_label(), *xISBNED,
+ sTableErrorString, HID_BIB_ISBN_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, CHAPTER_POS),
+ xChapterFT->get_label(), *xChapterED,
+ sTableErrorString, HID_BIB_CHAPTER_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, PAGES_POS),
+ xPagesFT->get_label(), *xPagesED,
+ sTableErrorString, HID_BIB_PAGES_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, EDITOR_POS),
+ xEditorFT->get_label(), *xEditorED,
+ sTableErrorString, HID_BIB_EDITOR_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, EDITION_POS),
+ xEditionFT->get_label(), *xEditionED,
+ sTableErrorString, HID_BIB_EDITION_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, BOOKTITLE_POS),
+ xBooktitleFT->get_label(), *xBooktitleED,
+ sTableErrorString, HID_BIB_BOOKTITLE_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, VOLUME_POS),
+ xVolumeFT->get_label(), *xVolumeED,
+ sTableErrorString, HID_BIB_VOLUME_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, HOWPUBLISHED_POS),
+ xHowpublishedFT->get_label(), *xHowpublishedED,
+ sTableErrorString, HID_BIB_HOWPUBLISHED_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, ORGANIZATIONS_POS),
+ xOrganizationsFT->get_label(), *xOrganizationsED,
+ sTableErrorString, HID_BIB_ORGANIZATIONS_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, INSTITUTION_POS),
+ xInstitutionFT->get_label(), *xInstitutionED,
+ sTableErrorString, HID_BIB_INSTITUTION_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, SCHOOL_POS),
+ xSchoolFT->get_label(), *xSchoolED,
+ sTableErrorString, HID_BIB_SCHOOL_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, REPORTTYPE_POS),
+ xReportTypeFT->get_label(), *xReportTypeED,
+ sTableErrorString, HID_BIB_REPORTTYPE_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, MONTH_POS),
+ xMonthFT->get_label(), *xMonthED,
+ sTableErrorString, HID_BIB_MONTH_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, JOURNAL_POS),
+ xJournalFT->get_label(), *xJournalED,
+ sTableErrorString, HID_BIB_JOURNAL_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, NUMBER_POS),
+ xNumberFT->get_label(), *xNumberED,
+ sTableErrorString, HID_BIB_NUMBER_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, SERIES_POS),
+ xSeriesFT->get_label(), *xSeriesED,
+ sTableErrorString, HID_BIB_SERIES_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, ANNOTE_POS),
+ xAnnoteFT->get_label(), *xAnnoteED,
+ sTableErrorString, HID_BIB_ANNOTE_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, NOTE_POS),
+ xNoteFT->get_label(), *xNoteED,
+ sTableErrorString, HID_BIB_NOTE_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, URL_POS),
+ xURLFT->get_label(), *xURLED,
+ sTableErrorString, HID_BIB_URL_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, CUSTOM1_POS),
+ xCustom1FT->get_label(), *xCustom1ED,
+ sTableErrorString, HID_BIB_CUSTOM1_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, CUSTOM2_POS),
+ xCustom2FT->get_label(), *xCustom2ED,
+ sTableErrorString, HID_BIB_CUSTOM2_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, CUSTOM3_POS),
+ xCustom3FT->get_label(), *xCustom3ED,
+ sTableErrorString, HID_BIB_CUSTOM3_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, CUSTOM4_POS),
+ xCustom4FT->get_label(), *xCustom4ED,
+ sTableErrorString, HID_BIB_CUSTOM4_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, CUSTOM5_POS),
+ xCustom5FT->get_label(), *xCustom5ED,
+ sTableErrorString, HID_BIB_CUSTOM5_POS);
+
+ AddControlWithError(lcl_GetColumnName(pMapping, LOCAL_URL_POS),
+ m_xLocalURLFT->get_label(), *m_xLocalURLED,
+ sTableErrorString, HID_BIB_LOCAL_URL_POS);
+
+ m_xLocalBrowseButton->connect_clicked(LINK(this, BibGeneralPage, BrowseHdl));
+ m_xLocalPageCB->connect_toggled(LINK(this, BibGeneralPage, PageNumHdl));
+
+ m_xLocalURLED->connect_key_press(LINK(this, BibGeneralPage, LastElementKeyInputHdl));
+
+ if(!sTableErrorString.isEmpty())
+ sTableErrorString = BibResId(ST_ERROR_PREFIX) + sTableErrorString;
+
+ SetText(BibResId(ST_TYPE_TITLE));
+
+ Size aSize(LogicToPixel(Size(0, 209), MapMode(MapUnit::MapAppFont)));
+ set_height_request(aSize.Height());
+}
+
+IMPL_LINK_NOARG(BibGeneralPage, BrowseHdl, weld::Button&, void)
+{
+ sfx2::FileDialogHelper aFileDlg(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::NONE, GetFrameWeld());
+ OUString aPath = m_xLocalURLED->get_text();
+ if (!aPath.isEmpty())
+ {
+ aFileDlg.SetDisplayDirectory(aPath);
+ }
+ else
+ {
+ OUString aBaseURL;
+ if (SfxObjectShell* pShell = SfxObjectShell::Current())
+ {
+ aBaseURL = pShell->getDocumentBaseURL();
+ }
+ if (!aBaseURL.isEmpty())
+ {
+ aFileDlg.SetDisplayDirectory(aBaseURL);
+ }
+ }
+
+ if (aFileDlg.Execute() != ERRCODE_NONE)
+ {
+ return;
+ }
+
+ weld::Entry& rEntry = *m_xLocalURLED;
+ rEntry.set_text(aFileDlg.GetPath());
+};
+
+IMPL_LINK(BibGeneralPage, PageNumHdl, weld::Toggleable&, rPageCB, void)
+{
+ weld::SpinButton& rPageSB = *m_xLocalPageSB;
+ if (rPageCB.get_active())
+ {
+ rPageSB.set_sensitive(true);
+ rPageSB.set_value(1);
+ }
+ else
+ {
+ rPageSB.set_sensitive(false);
+ }
+}
+
+IMPL_LINK(BibGeneralPage, FirstElementKeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ sal_uInt16 nCode = rKeyEvent.GetKeyCode().GetCode();
+ bool bShift = rKeyEvent.GetKeyCode().IsShift();
+ bool bCtrl = rKeyEvent.GetKeyCode().IsMod1();
+ bool bAlt = rKeyEvent.GetKeyCode().IsMod2();
+ if (KEY_TAB == nCode && bShift && !bCtrl && !bAlt)
+ {
+ SaveChanges();
+ uno::Reference<sdbc::XRowSet> xRowSet(pDatMan->getForm(), UNO_QUERY);
+ if (xRowSet.is() && !xRowSet->isFirst())
+ xRowSet->previous();
+ m_xLocalURLED->grab_focus();
+ m_xLocalURLED->select_region(0, -1);
+ GainFocusHdl(*m_xLocalURLED);
+ return true;
+ }
+ return false;
+}
+
+void BibGeneralPage::SaveChanges()
+{
+ Reference< XForm > xForm = pDatMan->getForm();
+ Reference< beans::XPropertySet > xProps( xForm, UNO_QUERY );
+ Reference< sdbc::XResultSetUpdate > xResUpd( xProps, UNO_QUERY );
+ if (!xResUpd.is() )
+ return;
+
+ Any aModified = xProps->getPropertyValue( "IsModified" );
+ bool bFlag = false;
+ if ( !( aModified >>= bFlag ) || !bFlag )
+ return;
+
+ try
+ {
+ Any aNew = xProps->getPropertyValue( "IsNew" );
+ aNew >>= bFlag;
+ if ( bFlag )
+ xResUpd->insertRow();
+ else
+ xResUpd->updateRow();
+ }
+ catch( const uno::Exception&) {}
+}
+
+IMPL_LINK(BibGeneralPage, LastElementKeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ sal_uInt16 nCode = rKeyEvent.GetKeyCode().GetCode();
+ bool bShift = rKeyEvent.GetKeyCode().IsShift();
+ bool bCtrl = rKeyEvent.GetKeyCode().IsMod1();
+ bool bAlt = rKeyEvent.GetKeyCode().IsMod2();
+ if (KEY_TAB != nCode || bShift || bCtrl || bAlt)
+ return false;
+ SaveChanges();
+ uno::Reference<sdbc::XRowSet> xRowSet(pDatMan->getForm(), UNO_QUERY);
+ if (xRowSet.is())
+ {
+ if (xRowSet->isLast())
+ {
+ uno::Reference<sdbc::XResultSetUpdate> xUpdateCursor(xRowSet, UNO_QUERY);
+ if (xUpdateCursor.is())
+ xUpdateCursor->moveToInsertRow();
+ }
+ else
+ (void)xRowSet->next();
+ }
+ xIdentifierED->grab_focus();
+ xIdentifierED->select_region(0, -1);
+ GainFocusHdl(*xIdentifierED);
+ return true;
+}
+
+BibGeneralPage::~BibGeneralPage()
+{
+ disposeOnce();
+}
+
+class ChangeListener : public cppu::WeakImplHelper<css::beans::XPropertyChangeListener>
+{
+public:
+ explicit ChangeListener(const css::uno::Reference<css::beans::XPropertySet>& rPropSet)
+ : m_xPropSet(rPropSet)
+ , m_bSelfChanging(false)
+ {
+ }
+
+ virtual void SAL_CALL disposing(lang::EventObject const &) override
+ {
+ }
+
+ virtual void start() = 0;
+ virtual void stop()
+ {
+ WriteBack();
+ }
+
+ virtual void WriteBack() = 0;
+
+protected:
+ css::uno::Reference<css::beans::XPropertySet> m_xPropSet;
+ bool m_bSelfChanging;
+};
+
+namespace
+{
+ class EntryChangeListener : public ChangeListener
+ {
+ public:
+ explicit EntryChangeListener(weld::Entry& rEntry, const css::uno::Reference<css::beans::XPropertySet>& rPropSet,
+ BibGeneralPage& rPage)
+ : ChangeListener(rPropSet)
+ , m_rEntry(rEntry)
+ , m_rPage(rPage)
+ {
+ rEntry.connect_focus_out(LINK(this, EntryChangeListener, LoseFocusHdl));
+ setValue(rPropSet->getPropertyValue("Text"));
+ }
+
+ virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override
+ {
+ if (m_bSelfChanging)
+ return;
+ setValue(evt.NewValue);
+ }
+
+ virtual void start() override
+ {
+ m_xPropSet->addPropertyChangeListener("Text", this);
+ }
+
+ virtual void stop() override
+ {
+ m_xPropSet->removePropertyChangeListener("Text", this);
+ ChangeListener::stop();
+ }
+
+ private:
+ weld::Entry& m_rEntry;
+ BibGeneralPage& m_rPage;
+
+ DECL_LINK(LoseFocusHdl, weld::Widget&, void);
+
+ /// Updates the UI widget(s) based on rValue.
+ void setValue(const css::uno::Any& rValue)
+ {
+ OUString sNewName;
+ rValue >>= sNewName;
+ if (&m_rEntry == &m_rPage.GetLocalURLED())
+ {
+ OUString aUrl;
+ int nPageNumber;
+ if (SplitUrlAndPage(sNewName, aUrl, nPageNumber))
+ {
+ m_rEntry.set_text(aUrl);
+ m_rPage.GetLocalPageCB().set_active(true);
+ m_rPage.GetLocalPageSB().set_sensitive(true);
+ m_rPage.GetLocalPageSB().set_value(nPageNumber);
+ }
+ else
+ {
+ m_rEntry.set_text(sNewName);
+ m_rPage.GetLocalPageCB().set_active(false);
+ m_rPage.GetLocalPageSB().set_sensitive(false);
+ m_rPage.GetLocalPageSB().set_value(0);
+ }
+ }
+ else
+ {
+ m_rEntry.set_text(sNewName);
+ }
+
+ m_rEntry.save_value();
+ if (&m_rEntry == &m_rPage.GetLocalURLED())
+ {
+ m_rPage.GetLocalPageSB().save_value();
+ }
+ }
+
+ /// Updates m_xPropSet based on the UI widget(s).
+ virtual void WriteBack() override
+ {
+ bool bLocalURL = &m_rEntry == &m_rPage.GetLocalURLED()
+ && m_rPage.GetLocalPageSB().get_value_changed_from_saved();
+ if (!m_rEntry.get_value_changed_from_saved() && !bLocalURL)
+ return;
+
+ m_bSelfChanging = true;
+
+ OUString aText;
+ if (&m_rEntry == &m_rPage.GetLocalURLED())
+ {
+ aText = MergeUrlAndPage(m_rEntry.get_text(), m_rPage.GetLocalPageSB());
+ }
+ else
+ {
+ aText = m_rEntry.get_text();
+ }
+ m_xPropSet->setPropertyValue("Text", Any(aText));
+
+ css::uno::Reference<css::form::XBoundComponent> xBound(m_xPropSet, css::uno::UNO_QUERY);
+ if (xBound.is())
+ xBound->commit();
+
+ m_bSelfChanging = false;
+ m_rEntry.save_value();
+ if (&m_rEntry == &m_rPage.GetLocalURLED())
+ {
+ m_rPage.GetLocalPageSB().save_value();
+ }
+ }
+
+ };
+
+ IMPL_LINK_NOARG(EntryChangeListener, LoseFocusHdl, weld::Widget&, void)
+ {
+ WriteBack();
+ }
+
+ class ComboBoxChangeListener : public ChangeListener
+ {
+ public:
+ explicit ComboBoxChangeListener(weld::ComboBox& rComboBox, const css::uno::Reference<css::beans::XPropertySet>& rPropSet)
+ : ChangeListener(rPropSet)
+ , m_rComboBox(rComboBox)
+ {
+ rComboBox.connect_changed(LINK(this, ComboBoxChangeListener, ChangeHdl));
+ setValue(rPropSet->getPropertyValue("SelectedItems"));
+ }
+
+ virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override
+ {
+ if (m_bSelfChanging)
+ return;
+ setValue(evt.NewValue);
+ }
+
+ virtual void start() override
+ {
+ m_xPropSet->addPropertyChangeListener("SelectedItems", this);
+ }
+
+ virtual void stop() override
+ {
+ m_xPropSet->removePropertyChangeListener("SelectedItems", this);
+ ChangeListener::stop();
+ }
+
+ private:
+ weld::ComboBox& m_rComboBox;
+
+ DECL_LINK(ChangeHdl, weld::ComboBox&, void);
+
+ void setValue(const css::uno::Any& rValue)
+ {
+ sal_Int16 nSelection = -1;
+ Sequence<sal_Int16> aSelection;
+ rValue >>= aSelection;
+ if (aSelection.hasElements())
+ nSelection = aSelection[0];
+
+ m_rComboBox.set_active(nSelection);
+ m_rComboBox.save_value();
+ }
+
+ virtual void WriteBack() override
+ {
+ if (!m_rComboBox.get_value_changed_from_saved())
+ return;
+ m_bSelfChanging = true;
+
+ Sequence<sal_Int16> aSelection{ o3tl::narrowing<sal_Int16>(m_rComboBox.get_active()) };
+ m_xPropSet->setPropertyValue("SelectedItems", Any(aSelection));
+
+ css::uno::Reference<css::form::XBoundComponent> xBound(m_xPropSet, css::uno::UNO_QUERY);
+ if (xBound.is())
+ xBound->commit();
+
+ m_bSelfChanging = false;
+ m_rComboBox.save_value();
+ }
+ };
+
+ IMPL_LINK_NOARG(ComboBoxChangeListener, ChangeHdl, weld::ComboBox&, void)
+ {
+ WriteBack();
+ }
+}
+
+void BibGeneralPage::dispose()
+{
+ for (auto& listener : maChangeListeners)
+ listener->stop();
+ maChangeListeners.clear();
+
+ SaveChanges();
+
+ xScrolledWindow.reset();
+ xGrid.reset();
+ xIdentifierFT.reset();
+ xIdentifierED.reset();
+ xAuthTypeFT.reset();
+ xAuthTypeLB.reset();
+ xYearFT.reset();
+ xYearED.reset();
+ xAuthorFT.reset();
+ xAuthorED.reset();
+ xTitleFT.reset();
+ xTitleED.reset();
+ xPublisherFT.reset();
+ xPublisherED.reset();
+ xAddressFT.reset();
+ xAddressED.reset();
+ xISBNFT.reset();
+ xISBNED.reset();
+ xChapterFT.reset();
+ xChapterED.reset();
+ xPagesFT.reset();
+ xPagesED.reset();
+ xEditorFT.reset();
+ xEditorED.reset();
+ xEditionFT.reset();
+ xEditionED.reset();
+ xBooktitleFT.reset();
+ xBooktitleED.reset();
+ xVolumeFT.reset();
+ xVolumeED.reset();
+ xHowpublishedFT.reset();
+ xHowpublishedED.reset();
+ xOrganizationsFT.reset();
+ xOrganizationsED.reset();
+ xInstitutionFT.reset();
+ xInstitutionED.reset();
+ xSchoolFT.reset();
+ xSchoolED.reset();
+ xReportTypeFT.reset();
+ xReportTypeED.reset();
+ xMonthFT.reset();
+ xMonthED.reset();
+ xJournalFT.reset();
+ xJournalED.reset();
+ xNumberFT.reset();
+ xNumberED.reset();
+ xSeriesFT.reset();
+ xSeriesED.reset();
+ xAnnoteFT.reset();
+ xAnnoteED.reset();
+ xNoteFT.reset();
+ xNoteED.reset();
+ xURLFT.reset();
+ xURLED.reset();
+ xCustom1FT.reset();
+ xCustom1ED.reset();
+ xCustom2FT.reset();
+ xCustom2ED.reset();
+ xCustom3FT.reset();
+ xCustom3ED.reset();
+ xCustom4FT.reset();
+ xCustom4ED.reset();
+ xCustom5FT.reset();
+ xCustom5ED.reset();
+ m_xLocalURLFT.reset();
+ m_xLocalURLED.reset();
+ m_xLocalBrowseButton.reset();
+ m_xLocalPageCB.reset();
+ m_xLocalPageSB.reset();
+ InterimItemWindow::dispose();
+}
+
+weld::Entry& BibGeneralPage::GetLocalURLED() { return *m_xLocalURLED; }
+
+weld::CheckButton& BibGeneralPage::GetLocalPageCB() { return *m_xLocalPageCB; }
+
+weld::SpinButton& BibGeneralPage::GetLocalPageSB() { return *m_xLocalPageSB; }
+
+bool BibGeneralPage::AddXControl(const OUString& rName, weld::Entry& rEntry)
+{
+ uno::Reference< awt::XControlModel > xCtrModel;
+ try
+ {
+ xCtrModel = pDatMan->loadControlModel(rName, false);
+ if ( xCtrModel.is() )
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xCtrModel, UNO_QUERY );
+
+ if( xPropSet.is())
+ {
+ maChangeListeners.emplace_back(new EntryChangeListener(rEntry, xPropSet, *this));
+ maChangeListeners.back()->start();
+ if (&rEntry == m_xLocalURLED.get())
+ {
+ m_aURLListener = maChangeListeners.back();
+ m_xLocalPageSB->connect_focus_out(LINK(this, BibGeneralPage, LosePageFocusHdl));
+ }
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("BibGeneralPage::AddXControl: something went wrong!");
+ }
+ return xCtrModel.is();
+}
+
+IMPL_LINK_NOARG(BibGeneralPage, LosePageFocusHdl, weld::Widget&, void)
+{
+ m_aURLListener->WriteBack();
+}
+
+IMPL_LINK(BibGeneralPage, GainFocusHdl, weld::Widget&, rWidget, void)
+{
+ int x, y, width, height;
+ if (!rWidget.get_extents_relative_to(*xGrid, x, y, width, height))
+ return;
+
+ int bottom = y + height;
+ int nVScrollPos = xScrolledWindow->vadjustment_get_value();
+ if (y < nVScrollPos || bottom > nVScrollPos + xScrolledWindow->vadjustment_get_page_size())
+ xScrolledWindow->vadjustment_set_value(y);
+
+ int right = x + width;
+ int nHScrollPos = xScrolledWindow->hadjustment_get_value();
+ if (x < nHScrollPos || right > nHScrollPos + xScrolledWindow->hadjustment_get_page_size())
+ xScrolledWindow->hadjustment_set_value(x);
+}
+
+template<class Target> void BibGeneralPage::AddControlWithError(const OUString& rColumnName, const OUString& rColumnUIName,
+ Target& rWidget, OUString& rErrorString, const OString& rHelpId)
+{
+ rWidget.set_help_id(rHelpId);
+ rWidget.connect_focus_in(LINK(this, BibGeneralPage, GainFocusHdl));
+ bool bSuccess = AddXControl(rColumnName, rWidget);
+ if (!bSuccess)
+ {
+ if( !rErrorString.isEmpty() )
+ rErrorString += "\n";
+
+ rErrorString += MnemonicGenerator::EraseAllMnemonicChars(rColumnUIName);
+ }
+}
+
+bool BibGeneralPage::AddXControl(const OUString& rName, weld::ComboBox& rList)
+{
+ uno::Reference< awt::XControlModel > xCtrModel;
+ try
+ {
+ xCtrModel = pDatMan->loadControlModel(rName, true);
+ if ( xCtrModel.is() )
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xCtrModel, UNO_QUERY );
+
+ if( xPropSet.is())
+ {
+ css::uno::Sequence<OUString> aEntries;
+ xPropSet->getPropertyValue("StringItemList") >>= aEntries;
+ for (const OUString& rString : std::as_const(aEntries))
+ rList.append_text(rString);
+
+ sal_Int16 nSelection = -1;
+ Sequence<sal_Int16> aSelection;
+ xPropSet->getPropertyValue("SelectedItems") >>= aSelection;
+ if (aSelection.hasElements())
+ nSelection = aSelection[0];
+
+ rList.set_active(nSelection);
+ rList.save_value();
+
+ maChangeListeners.emplace_back(new ComboBoxChangeListener(rList, xPropSet));
+ maChangeListeners.back()->start();
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("BibGeneralPage::AddXControl: something went wrong!");
+ }
+ return xCtrModel.is();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/general.hxx b/extensions/source/bibliography/general.hxx
new file mode 100644
index 000000000..b4d981955
--- /dev/null
+++ b/extensions/source/bibliography/general.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vcl/InterimItemWindow.hxx>
+#include "bibshortcuthandler.hxx"
+
+
+class BibDataManager;
+#define TYPE_COUNT 22
+#define FIELD_COUNT 31
+
+class ChangeListener;
+
+class BibGeneralPage : public InterimItemWindow
+ , public BibShortCutHandler
+{
+ std::unique_ptr<weld::ScrolledWindow> xScrolledWindow;
+ std::unique_ptr<weld::Widget> xGrid;
+
+ std::unique_ptr<weld::Label> xIdentifierFT;
+ std::unique_ptr<weld::Entry> xIdentifierED;
+
+ std::unique_ptr<weld::Label> xAuthTypeFT;
+ std::unique_ptr<weld::ComboBox> xAuthTypeLB;
+ std::unique_ptr<weld::Label> xYearFT;
+ std::unique_ptr<weld::Entry> xYearED;
+
+ std::unique_ptr<weld::Label> xAuthorFT;
+ std::unique_ptr<weld::Entry> xAuthorED;
+ std::unique_ptr<weld::Label> xTitleFT;
+ std::unique_ptr<weld::Entry> xTitleED;
+
+ std::unique_ptr<weld::Label> xPublisherFT;
+ std::unique_ptr<weld::Entry> xPublisherED;
+ std::unique_ptr<weld::Label> xAddressFT;
+ std::unique_ptr<weld::Entry> xAddressED;
+ std::unique_ptr<weld::Label> xISBNFT;
+ std::unique_ptr<weld::Entry> xISBNED;
+
+ std::unique_ptr<weld::Label> xChapterFT;
+ std::unique_ptr<weld::Entry> xChapterED;
+ std::unique_ptr<weld::Label> xPagesFT;
+ std::unique_ptr<weld::Entry> xPagesED;
+
+ std::unique_ptr<weld::Label> xEditorFT;
+ std::unique_ptr<weld::Entry> xEditorED;
+ std::unique_ptr<weld::Label> xEditionFT;
+ std::unique_ptr<weld::Entry> xEditionED;
+
+ std::unique_ptr<weld::Label> xBooktitleFT;
+ std::unique_ptr<weld::Entry> xBooktitleED;
+ std::unique_ptr<weld::Label> xVolumeFT;
+ std::unique_ptr<weld::Entry> xVolumeED;
+ std::unique_ptr<weld::Label> xHowpublishedFT;
+ std::unique_ptr<weld::Entry> xHowpublishedED;
+
+ std::unique_ptr<weld::Label> xOrganizationsFT;
+ std::unique_ptr<weld::Entry> xOrganizationsED;
+ std::unique_ptr<weld::Label> xInstitutionFT;
+ std::unique_ptr<weld::Entry> xInstitutionED;
+ std::unique_ptr<weld::Label> xSchoolFT;
+ std::unique_ptr<weld::Entry> xSchoolED;
+
+ std::unique_ptr<weld::Label> xReportTypeFT;
+ std::unique_ptr<weld::Entry> xReportTypeED;
+ std::unique_ptr<weld::Label> xMonthFT;
+ std::unique_ptr<weld::Entry> xMonthED;
+
+ std::unique_ptr<weld::Label> xJournalFT;
+ std::unique_ptr<weld::Entry> xJournalED;
+ std::unique_ptr<weld::Label> xNumberFT;
+ std::unique_ptr<weld::Entry> xNumberED;
+ std::unique_ptr<weld::Label> xSeriesFT;
+ std::unique_ptr<weld::Entry> xSeriesED;
+
+ std::unique_ptr<weld::Label> xAnnoteFT;
+ std::unique_ptr<weld::Entry> xAnnoteED;
+ std::unique_ptr<weld::Label> xNoteFT;
+ std::unique_ptr<weld::Entry> xNoteED;
+ std::unique_ptr<weld::Label> xURLFT;
+ std::unique_ptr<weld::Entry> xURLED;
+
+ std::unique_ptr<weld::Label> xCustom1FT;
+ std::unique_ptr<weld::Entry> xCustom1ED;
+ std::unique_ptr<weld::Label> xCustom2FT;
+ std::unique_ptr<weld::Entry> xCustom2ED;
+ std::unique_ptr<weld::Label> xCustom3FT;
+ std::unique_ptr<weld::Entry> xCustom3ED;
+ std::unique_ptr<weld::Label> xCustom4FT;
+ std::unique_ptr<weld::Entry> xCustom4ED;
+ std::unique_ptr<weld::Label> xCustom5FT;
+ std::unique_ptr<weld::Entry> xCustom5ED;
+ std::unique_ptr<weld::Label> m_xLocalURLFT;
+ std::unique_ptr<weld::Entry> m_xLocalURLED;
+ std::unique_ptr<weld::Button> m_xLocalBrowseButton;
+ std::unique_ptr<weld::CheckButton> m_xLocalPageCB;
+ std::unique_ptr<weld::SpinButton> m_xLocalPageSB;
+
+ OUString sTableErrorString;
+
+ std::vector<rtl::Reference<ChangeListener>> maChangeListeners;
+ rtl::Reference<ChangeListener> m_aURLListener;
+
+ BibDataManager* pDatMan;
+
+ bool AddXControl(const OUString& rName, weld::Entry& rEntry);
+ bool AddXControl(const OUString& rName, weld::ComboBox& rList);
+
+ template<class Target> void AddControlWithError(const OUString& rColumnName, const OUString& rColumnUIName,
+ Target& rWidget, OUString& rErrorString, const OString& rHelpId);
+
+ void SaveChanges();
+
+ DECL_LINK(GainFocusHdl, weld::Widget&, void);
+
+ DECL_LINK(FirstElementKeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(LastElementKeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(BrowseHdl, weld::Button&, void);
+ DECL_LINK(PageNumHdl, weld::Toggleable&, void);
+ DECL_LINK(LosePageFocusHdl, weld::Widget&, void);
+
+public:
+ BibGeneralPage(vcl::Window* pParent, BibDataManager* pDatMan);
+ virtual ~BibGeneralPage() override;
+ virtual void dispose() override;
+
+ const OUString& GetErrorString() const
+ {
+ return sTableErrorString;
+ }
+
+ weld::Entry& GetLocalURLED();
+ weld::CheckButton& GetLocalPageCB();
+ weld::SpinButton& GetLocalPageSB();
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/loadlisteneradapter.cxx b/extensions/source/bibliography/loadlisteneradapter.cxx
new file mode 100644
index 000000000..27f4ea2e5
--- /dev/null
+++ b/extensions/source/bibliography/loadlisteneradapter.cxx
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "loadlisteneradapter.hxx"
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+
+
+namespace bib
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::form;
+
+ OComponentListener::~OComponentListener()
+ {
+ ::osl::MutexGuard aGuard( m_rMutex );
+ if ( m_xAdapter.is() )
+ m_xAdapter->dispose();
+ }
+
+
+ void OComponentListener::setAdapter( OComponentAdapterBase* pAdapter )
+ {
+ ::osl::MutexGuard aGuard( m_rMutex );
+ m_xAdapter = pAdapter;
+ }
+
+ OComponentAdapterBase::OComponentAdapterBase( const Reference< XComponent >& _rxComp )
+ :m_xComponent( _rxComp )
+ ,m_pListener( nullptr )
+ ,m_bListening( false )
+ {
+ OSL_ENSURE( m_xComponent.is(), "OComponentAdapterBase::OComponentAdapterBase: invalid component!" );
+ }
+
+
+ void OComponentAdapterBase::Init( OComponentListener* _pListener )
+ {
+ OSL_ENSURE( !m_pListener, "OComponentAdapterBase::Init: already initialized!" );
+ OSL_ENSURE( _pListener, "OComponentAdapterBase::Init: invalid listener!" );
+
+ m_pListener = _pListener;
+ if ( m_pListener )
+ m_pListener->setAdapter( this );
+
+ startComponentListening( );
+ m_bListening = true;
+ }
+
+
+ OComponentAdapterBase::~OComponentAdapterBase()
+ {
+ }
+
+
+ void OComponentAdapterBase::dispose()
+ {
+ if ( !m_bListening )
+ return;
+
+ ::rtl::Reference< OComponentAdapterBase > xPreventDelete(this);
+
+ disposing();
+
+ m_pListener->setAdapter(nullptr);
+
+ m_pListener = nullptr;
+ m_bListening = false;
+
+ m_xComponent = nullptr;
+ }
+
+ // XEventListener
+
+
+ void SAL_CALL OComponentAdapterBase::disposing( const EventObject& )
+ {
+ if (m_pListener)
+ {
+ // disconnect the listener
+ m_pListener->setAdapter( nullptr );
+ m_pListener = nullptr;
+ }
+
+ m_bListening = false;
+ m_xComponent = nullptr;
+ }
+
+ OLoadListenerAdapter::OLoadListenerAdapter( const Reference< XLoadable >& _rxLoadable )
+ :OComponentAdapterBase( Reference< XComponent >( _rxLoadable, UNO_QUERY ) )
+ {
+ }
+
+
+ void OLoadListenerAdapter::startComponentListening()
+ {
+ Reference< XLoadable > xLoadable( getComponent(), UNO_QUERY );
+ OSL_ENSURE( xLoadable.is(), "OLoadListenerAdapter::OLoadListenerAdapter: invalid object!" );
+ if ( xLoadable.is() )
+ xLoadable->addLoadListener( this );
+ }
+
+
+ void SAL_CALL OLoadListenerAdapter::acquire( ) noexcept
+ {
+ OLoadListenerAdapter_Base::acquire();
+ }
+
+
+ void SAL_CALL OLoadListenerAdapter::release( ) noexcept
+ {
+ OLoadListenerAdapter_Base::release();
+ }
+
+
+ void SAL_CALL OLoadListenerAdapter::disposing( const EventObject& _rSource )
+ {
+ OComponentAdapterBase::disposing( _rSource );
+ }
+
+
+ void OLoadListenerAdapter::disposing()
+ {
+ Reference< XLoadable > xLoadable( getComponent(), UNO_QUERY );
+ if ( xLoadable.is() )
+ xLoadable->removeLoadListener( this );
+ }
+
+
+ void SAL_CALL OLoadListenerAdapter::loaded( const EventObject& _rEvent )
+ {
+ if ( getLoadListener( ) )
+ getLoadListener( )->_loaded( _rEvent );
+ }
+
+
+ void SAL_CALL OLoadListenerAdapter::unloading( const EventObject& _rEvent )
+ {
+ if ( getLoadListener( ) )
+ getLoadListener( )->_unloading( _rEvent );
+ }
+
+
+ void SAL_CALL OLoadListenerAdapter::unloaded( const EventObject& )
+ {
+ }
+
+
+ void SAL_CALL OLoadListenerAdapter::reloading( const EventObject& _rEvent )
+ {
+ if ( getLoadListener( ) )
+ getLoadListener( )->_reloading( _rEvent );
+ }
+
+
+ void SAL_CALL OLoadListenerAdapter::reloaded( const EventObject& _rEvent )
+ {
+ if ( getLoadListener( ) )
+ getLoadListener( )->_reloaded( _rEvent );
+ }
+
+
+} // namespace bib
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/loadlisteneradapter.hxx b/extensions/source/bibliography/loadlisteneradapter.hxx
new file mode 100644
index 000000000..1031dedca
--- /dev/null
+++ b/extensions/source/bibliography/loadlisteneradapter.hxx
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <osl/mutex.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <rtl/ref.hxx>
+
+
+namespace bib
+{
+
+
+ class OComponentAdapterBase;
+
+ class OComponentListener
+ {
+ friend class OComponentAdapterBase;
+
+ private:
+ rtl::Reference<OComponentAdapterBase> m_xAdapter;
+ ::osl::Mutex& m_rMutex;
+ protected:
+ explicit OComponentListener( ::osl::Mutex& _rMutex )
+ :m_rMutex( _rMutex )
+ {
+ }
+
+ virtual ~OComponentListener();
+
+ protected:
+ void setAdapter( OComponentAdapterBase* _pAdapter );
+ };
+
+ class OComponentAdapterBase
+ {
+ friend class OComponentListener;
+
+ private:
+ css::uno::Reference< css::lang::XComponent > m_xComponent;
+ OComponentListener* m_pListener;
+ bool m_bListening : 1;
+
+ // impl method for dispose - virtual, 'cause you at least need to remove the listener from the broadcaster
+ virtual void disposing() = 0;
+
+ protected:
+ // attribute access for derivees
+ const css::uno::Reference< css::lang::XComponent >&
+ getComponent() const { return m_xComponent; }
+ OComponentListener* getListener() { return m_pListener; }
+
+ // to be called by derivees which started listening at the component
+ virtual void startComponentListening() = 0;
+
+ virtual ~OComponentAdapterBase();
+
+ public:
+ explicit OComponentAdapterBase(
+ const css::uno::Reference< css::lang::XComponent >& _rxComp
+ );
+
+ // late construction
+ // can be called from within you ctor, to have you're object fully initialized at the moment of
+ // the call (which would not be the case when calling this ctor)
+ void Init( OComponentListener* _pListener );
+
+ // base for ref-counting, implemented by OComponentAdapter
+ virtual void SAL_CALL acquire( ) noexcept = 0;
+ virtual void SAL_CALL release( ) noexcept = 0;
+
+ /// dispose the object - stop listening and such
+ void dispose();
+
+ protected:
+ // XEventListener
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source );
+ };
+
+ class OLoadListener : public OComponentListener
+ {
+ friend class OLoadListenerAdapter;
+
+ protected:
+ explicit OLoadListener( ::osl::Mutex& _rMutex ) : OComponentListener( _rMutex ) { }
+
+ // XLoadListener equivalents
+ virtual void _loaded( const css::lang::EventObject& aEvent ) = 0;
+ virtual void _unloading( const css::lang::EventObject& aEvent ) = 0;
+ virtual void _reloading( const css::lang::EventObject& aEvent ) = 0;
+ virtual void _reloaded( const css::lang::EventObject& aEvent ) = 0;
+ };
+
+ typedef ::cppu::WeakImplHelper< css::form::XLoadListener > OLoadListenerAdapter_Base;
+ class OLoadListenerAdapter
+ :public OLoadListenerAdapter_Base
+ ,public OComponentAdapterBase
+ {
+ protected:
+ OLoadListener* getLoadListener( ) { return static_cast< OLoadListener* >( getListener() ); }
+
+ protected:
+ virtual void disposing() override;
+ virtual void startComponentListening() override;
+
+ public:
+ explicit OLoadListenerAdapter(
+ const css::uno::Reference< css::form::XLoadable >& _rxLoadable
+ );
+
+
+ virtual void SAL_CALL acquire( ) noexcept override;
+ virtual void SAL_CALL release( ) noexcept override;
+
+ protected:
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& _rSource ) override;
+
+ // XLoadListener
+ virtual void SAL_CALL loaded( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL unloading( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL unloaded( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL reloading( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL reloaded( const css::lang::EventObject& aEvent ) override;
+ };
+
+
+} // namespace bib
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/toolbar.cxx b/extensions/source/bibliography/toolbar.cxx
new file mode 100644
index 000000000..fd74a04e6
--- /dev/null
+++ b/extensions/source/bibliography/toolbar.cxx
@@ -0,0 +1,620 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include "datman.hxx"
+#include "toolbar.hxx"
+#include <o3tl/any.hxx>
+#include <svtools/miscopt.hxx>
+#include <svtools/imgdef.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/event.hxx>
+#include <vcl/weldutils.hxx>
+#include <bitmaps.hlst>
+
+#include "bibtools.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+
+// Constants --------------------------------------------------------------
+
+
+BibToolBarListener::BibToolBarListener(BibToolBar *pTB, const OUString& aStr, ToolBoxItemId nId):
+ nIndex(nId),
+ aCommand(aStr),
+ pToolBar(pTB)
+{
+}
+
+BibToolBarListener::~BibToolBarListener()
+{
+}
+
+void BibToolBarListener::statusChanged(const css::frame::FeatureStateEvent& rEvt)
+{
+ if(rEvt.FeatureURL.Complete == aCommand)
+ {
+ SolarMutexGuard aGuard;
+ pToolBar->EnableItem(nIndex,rEvt.IsEnabled);
+
+ css::uno::Any aState=rEvt.State;
+ if(auto bChecked = o3tl::tryAccess<bool>(aState))
+ {
+ pToolBar->CheckItem(nIndex, *bChecked);
+ }
+
+ }
+};
+
+
+BibTBListBoxListener::BibTBListBoxListener(BibToolBar *pTB, const OUString& aStr, ToolBoxItemId nId):
+ BibToolBarListener(pTB,aStr,nId)
+{
+}
+
+BibTBListBoxListener::~BibTBListBoxListener()
+{
+}
+
+void BibTBListBoxListener::statusChanged(const css::frame::FeatureStateEvent& rEvt)
+{
+ if(rEvt.FeatureURL.Complete != GetCommand())
+ return;
+
+ SolarMutexGuard aGuard;
+ pToolBar->EnableSourceList(rEvt.IsEnabled);
+
+ Any aState = rEvt.State;
+ if(auto pStringSeq = o3tl::tryAccess<Sequence<OUString>>(aState))
+ {
+ pToolBar->UpdateSourceList(false);
+ pToolBar->ClearSourceList();
+
+ const OUString* pStringArray = pStringSeq->getConstArray();
+
+ sal_uInt32 nCount = pStringSeq->getLength();
+ OUString aEntry;
+ for( sal_uInt32 i=0; i<nCount; i++ )
+ {
+ aEntry = pStringArray[i];
+ pToolBar->InsertSourceEntry(aEntry);
+ }
+ pToolBar->UpdateSourceList(true);
+ }
+
+ pToolBar->SelectSourceEntry(rEvt.FeatureDescriptor);
+};
+
+BibTBQueryMenuListener::BibTBQueryMenuListener(BibToolBar *pTB, const OUString& aStr, ToolBoxItemId nId):
+ BibToolBarListener(pTB,aStr,nId)
+{
+}
+
+BibTBQueryMenuListener::~BibTBQueryMenuListener()
+{
+}
+
+void BibTBQueryMenuListener::statusChanged(const frame::FeatureStateEvent& rEvt)
+{
+ if(rEvt.FeatureURL.Complete != GetCommand())
+ return;
+
+ SolarMutexGuard aGuard;
+ pToolBar->EnableSourceList(rEvt.IsEnabled);
+
+ uno::Any aState=rEvt.State;
+ auto pStringSeq = o3tl::tryAccess<Sequence<OUString>>(aState);
+ if(!pStringSeq)
+ return;
+
+ pToolBar->ClearFilterMenu();
+
+ const OUString* pStringArray = pStringSeq->getConstArray();
+
+ sal_uInt32 nCount = pStringSeq->getLength();
+ for( sal_uInt32 i=0; i<nCount; i++ )
+ {
+ sal_uInt16 nID = pToolBar->InsertFilterItem(pStringArray[i]);
+ if(pStringArray[i]==rEvt.FeatureDescriptor)
+ {
+ pToolBar->SelectFilterItem(nID);
+ }
+ }
+};
+
+BibTBEditListener::BibTBEditListener(BibToolBar *pTB, const OUString& aStr, ToolBoxItemId nId):
+ BibToolBarListener(pTB,aStr,nId)
+{
+}
+
+BibTBEditListener::~BibTBEditListener()
+{
+}
+
+void BibTBEditListener::statusChanged(const frame::FeatureStateEvent& rEvt)
+{
+ if(rEvt.FeatureURL.Complete == GetCommand())
+ {
+ SolarMutexGuard aGuard;
+ pToolBar->EnableQuery(rEvt.IsEnabled);
+
+ uno::Any aState=rEvt.State;
+ if(auto aStr = o3tl::tryAccess<OUString>(aState))
+ {
+ pToolBar->SetQueryString(*aStr);
+ }
+ }
+}
+
+ComboBoxControl::ComboBoxControl(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "modules/sbibliography/ui/combobox.ui", "ComboBox")
+ , m_xFtSource(m_xBuilder->weld_label("label"))
+ , m_xLBSource(m_xBuilder->weld_combo_box("combobox"))
+{
+ m_xFtSource->set_toolbar_background();
+ m_xLBSource->set_toolbar_background();
+ m_xLBSource->set_size_request(100, -1);
+ SetSizePixel(get_preferred_size());
+}
+
+void ComboBoxControl::dispose()
+{
+ m_xLBSource.reset();
+ m_xFtSource.reset();
+ InterimItemWindow::dispose();
+}
+
+ComboBoxControl::~ComboBoxControl()
+{
+ disposeOnce();
+}
+
+EditControl::EditControl(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "modules/sbibliography/ui/editbox.ui", "EditBox")
+ , m_xFtQuery(m_xBuilder->weld_label("label"))
+ , m_xEdQuery(m_xBuilder->weld_entry("entry"))
+{
+ m_xFtQuery->set_toolbar_background();
+ m_xEdQuery->set_toolbar_background();
+ m_xEdQuery->set_size_request(100, -1);
+ SetSizePixel(get_preferred_size());
+}
+
+void EditControl::dispose()
+{
+ m_xEdQuery.reset();
+ m_xFtQuery.reset();
+ InterimItemWindow::dispose();
+}
+
+EditControl::~EditControl()
+{
+ disposeOnce();
+}
+
+BibToolBar::BibToolBar(vcl::Window* pParent, Link<void*,void> aLink)
+ : ToolBox(pParent, "toolbar", "modules/sbibliography/ui/toolbar.ui")
+ , aIdle("BibToolBar")
+ , xSource(VclPtr<ComboBoxControl>::Create(this))
+ , pLbSource(xSource->get_widget())
+ , xQuery(VclPtr<EditControl>::Create(this))
+ , pEdQuery(xQuery->get_widget())
+ , xBuilder(Application::CreateBuilder(nullptr, "modules/sbibliography/ui/autofiltermenu.ui"))
+ , xPopupMenu(xBuilder->weld_menu("menu"))
+ , nMenuId(0)
+ , aLayoutManager(aLink)
+ , nSymbolsSize(SFX_SYMBOLS_SIZE_SMALL)
+{
+ SvtMiscOptions aSvtMiscOptions;
+ nSymbolsSize = aSvtMiscOptions.GetCurrentSymbolsSize();
+
+ xSource->Show();
+ pLbSource->connect_changed(LINK( this, BibToolBar, SelHdl));
+
+ SvtMiscOptions().AddListenerLink( LINK( this, BibToolBar, OptionsChanged_Impl ) );
+ Application::AddEventListener( LINK( this, BibToolBar, SettingsChanged_Impl ) );
+
+ aIdle.SetInvokeHandler(LINK( this, BibToolBar, SendSelHdl));
+ aIdle.SetPriority(TaskPriority::LOWEST);
+
+ SetDropdownClickHdl( LINK( this, BibToolBar, MenuHdl));
+
+ xQuery->Show();
+
+ nTBC_SOURCE = GetItemId(".uno:Bib/source");
+ nTBC_QUERY = GetItemId(".uno:Bib/query");
+ nTBC_BT_AUTOFILTER = GetItemId(".uno:Bib/autoFilter");
+ nTBC_BT_COL_ASSIGN = GetItemId("TBC_BT_COL_ASSIGN");
+ nTBC_BT_CHANGESOURCE = GetItemId(".uno:Bib/sdbsource");
+ nTBC_BT_FILTERCRIT = GetItemId(".uno:Bib/standardFilter");
+ nTBC_BT_REMOVEFILTER = GetItemId(".uno:Bib/removeFilter");
+
+ SetItemWindow(nTBC_SOURCE, xSource.get());
+ SetItemWindow(nTBC_QUERY , xQuery.get());
+
+ ApplyImageList();
+
+ ::bib::AddToTaskPaneList( this );
+}
+
+BibToolBar::~BibToolBar()
+{
+ disposeOnce();
+}
+
+void BibToolBar::dispose()
+{
+ SvtMiscOptions().RemoveListenerLink( LINK( this, BibToolBar, OptionsChanged_Impl ) );
+ Application::RemoveEventListener( LINK( this, BibToolBar, SettingsChanged_Impl ) );
+ ::bib::RemoveFromTaskPaneList( this );
+ pEdQuery = nullptr;
+ xQuery.disposeAndClear();
+ pLbSource = nullptr;
+ xSource.disposeAndClear();
+ xPopupMenu.reset();
+ xBuilder.reset();
+ ToolBox::dispose();
+}
+
+void BibToolBar::InitListener()
+{
+ ToolBox::ImplToolItems::size_type nCount=GetItemCount();
+
+ uno::Reference< frame::XDispatch > xDisp(xController,UNO_QUERY);
+ uno::Reference< util::XURLTransformer > xTrans( util::URLTransformer::create(comphelper::getProcessComponentContext()) );
+ if( !xTrans.is() )
+ return;
+
+ util::URL aQueryURL;
+ aQueryURL.Complete = ".uno:Bib/MenuFilter";
+ xTrans->parseStrict( aQueryURL);
+ rtl::Reference<BibToolBarListener> pQuery=new BibTBQueryMenuListener(this, aQueryURL.Complete, nTBC_BT_AUTOFILTER);
+ xDisp->addStatusListener(pQuery, aQueryURL);
+
+ for(ToolBox::ImplToolItems::size_type nPos=0;nPos<nCount;nPos++)
+ {
+ ToolBoxItemId nId=GetItemId(nPos);
+ if (!nId)
+ continue;
+
+ util::URL aURL;
+ aURL.Complete = GetItemCommand(nId);
+ if(aURL.Complete.isEmpty())
+ continue;
+
+ xTrans->parseStrict( aURL );
+
+ css::uno::Reference< css::frame::XStatusListener> xListener;
+ if (nId == nTBC_SOURCE)
+ {
+ xListener=new BibTBListBoxListener(this,aURL.Complete,nId);
+ }
+ else if (nId == nTBC_QUERY)
+ {
+ xListener=new BibTBEditListener(this,aURL.Complete,nId);
+ }
+ else
+ {
+ xListener=new BibToolBarListener(this,aURL.Complete,nId);
+ }
+
+ aListenerArr.push_back( xListener );
+ xDisp->addStatusListener(xListener,aURL);
+ }
+}
+
+void BibToolBar::SetXController(const uno::Reference< frame::XController > & xCtr)
+{
+ xController=xCtr;
+ InitListener();
+
+}
+
+void BibToolBar::Select()
+{
+ ToolBoxItemId nId=GetCurItemId();
+
+ if (nId != nTBC_BT_AUTOFILTER)
+ {
+ SendDispatch(nId,Sequence<PropertyValue>() );
+ }
+ else
+ {
+ Sequence<PropertyValue> aPropVal
+ {
+ comphelper::makePropertyValue("QueryText", pEdQuery->get_text()),
+ comphelper::makePropertyValue("QueryField", aQueryField)
+ };
+ SendDispatch(nId,aPropVal);
+ }
+}
+
+void BibToolBar::SendDispatch(ToolBoxItemId nId, const Sequence< PropertyValue >& rArgs)
+{
+ OUString aCommand = GetItemCommand(nId);
+
+ uno::Reference< frame::XDispatchProvider > xDSP( xController, UNO_QUERY );
+
+ if( !xDSP.is() || aCommand.isEmpty() )
+ return;
+
+ uno::Reference< util::XURLTransformer > xTrans( util::URLTransformer::create(comphelper::getProcessComponentContext()) );
+ if( !xTrans.is() )
+ return;
+
+ // load the file
+ util::URL aURL;
+ aURL.Complete = aCommand;
+
+ xTrans->parseStrict( aURL );
+
+ uno::Reference< frame::XDispatch > xDisp = xDSP->queryDispatch( aURL, OUString(), frame::FrameSearchFlag::SELF );
+
+ if ( xDisp.is() )
+ xDisp->dispatch( aURL, rArgs);
+
+}
+
+void BibToolBar::Click()
+{
+ ToolBoxItemId nId = GetCurItemId();
+
+ vcl::Window* pWin = GetParent();
+
+ if (nId == nTBC_BT_COL_ASSIGN )
+ {
+ if (pDatMan)
+ pDatMan->CreateMappingDialog(pWin ? pWin->GetFrameWeld() : nullptr);
+ CheckItem( nId, false );
+ }
+ else if (nId == nTBC_BT_CHANGESOURCE)
+ {
+ if (pDatMan)
+ {
+ OUString sNew = pDatMan->CreateDBChangeDialog(pWin ? pWin->GetFrameWeld() : nullptr);
+ if (!sNew.isEmpty())
+ pDatMan->setActiveDataSource(sNew);
+ }
+ CheckItem( nId, false );
+ }
+}
+
+void BibToolBar::ClearFilterMenu()
+{
+ xPopupMenu->clear();
+ nMenuId=0;
+}
+
+sal_uInt16 BibToolBar::InsertFilterItem(const OUString& rMenuEntry)
+{
+ nMenuId++;
+ xPopupMenu->append_check(OUString::number(nMenuId), rMenuEntry);
+ return nMenuId;
+}
+
+void BibToolBar::SelectFilterItem(sal_uInt16 nId)
+{
+ OString sId = OString::number(nId);
+ xPopupMenu->set_active(sId, true);
+ sSelMenuItem = sId;
+ aQueryField = MnemonicGenerator::EraseAllMnemonicChars(xPopupMenu->get_label(sId));
+}
+
+void BibToolBar::EnableSourceList(bool bFlag)
+{
+ xSource->set_sensitive(bFlag);
+}
+
+void BibToolBar::ClearSourceList()
+{
+ pLbSource->clear();
+}
+
+void BibToolBar::UpdateSourceList(bool bFlag)
+{
+ if (bFlag)
+ pLbSource->thaw();
+ else
+ pLbSource->freeze();
+}
+
+void BibToolBar::InsertSourceEntry(const OUString& aEntry)
+{
+ pLbSource->append_text(aEntry);
+}
+
+void BibToolBar::SelectSourceEntry(const OUString& aStr)
+{
+ pLbSource->set_active_text(aStr);
+}
+
+void BibToolBar::EnableQuery(bool bFlag)
+{
+ xQuery->set_sensitive(bFlag);
+}
+
+void BibToolBar::SetQueryString(const OUString& aStr)
+{
+ pEdQuery->set_text(aStr);
+}
+
+bool BibToolBar::PreNotify( NotifyEvent& rNEvt )
+{
+ bool bResult = true;
+
+ MouseNotifyEvent nSwitch=rNEvt.GetType();
+ if (pEdQuery && pEdQuery->has_focus() && nSwitch == MouseNotifyEvent::KEYINPUT)
+ {
+ const vcl::KeyCode& aKeyCode=rNEvt.GetKeyEvent()->GetKeyCode();
+ sal_uInt16 nKey = aKeyCode.GetCode();
+ if(nKey == KEY_RETURN)
+ {
+ Sequence<PropertyValue> aPropVal
+ {
+ comphelper::makePropertyValue("QueryText", pEdQuery->get_text()),
+ comphelper::makePropertyValue("QueryField", aQueryField)
+ };
+ SendDispatch(nTBC_BT_AUTOFILTER, aPropVal);
+ return bResult;
+ }
+
+ }
+
+ bResult=ToolBox::PreNotify(rNEvt);
+
+ return bResult;
+}
+
+IMPL_LINK_NOARG( BibToolBar, SelHdl, weld::ComboBox&, void )
+{
+ aIdle.Start();
+}
+
+IMPL_LINK_NOARG( BibToolBar, SendSelHdl, Timer*, void )
+{
+ Sequence<PropertyValue> aPropVal
+ {
+ comphelper::makePropertyValue("DataSourceName", MnemonicGenerator::EraseAllMnemonicChars( pLbSource->get_active_text() ))
+ };
+ SendDispatch(nTBC_SOURCE, aPropVal);
+}
+
+IMPL_LINK_NOARG(BibToolBar, MenuHdl, ToolBox*, void)
+{
+ ToolBoxItemId nId = GetCurItemId();
+ if (nId != nTBC_BT_AUTOFILTER)
+ return;
+
+ EndSelection(); // before SetDropMode (SetDropMode calls SetItemImage)
+
+ SetItemDown(nTBC_BT_AUTOFILTER, true);
+
+ tools::Rectangle aRect(GetItemRect(nTBC_BT_AUTOFILTER));
+ weld::Window* pParent = weld::GetPopupParent(*this, aRect);
+ OString sId = xPopupMenu->popup_at_rect(pParent, aRect);
+
+ if (!sId.isEmpty())
+ {
+ xPopupMenu->set_active(sSelMenuItem, false);
+ xPopupMenu->set_active(sId, true);
+ sSelMenuItem = sId;
+ aQueryField = MnemonicGenerator::EraseAllMnemonicChars(xPopupMenu->get_label(sId));
+ Sequence<PropertyValue> aPropVal
+ {
+ comphelper::makePropertyValue("QueryText", pEdQuery->get_text()),
+ comphelper::makePropertyValue("QueryField", aQueryField)
+ };
+ SendDispatch(nTBC_BT_AUTOFILTER, aPropVal);
+ }
+
+ MouseEvent aLeave( Point(), 0, MouseEventModifiers::LEAVEWINDOW | MouseEventModifiers::SYNTHETIC );
+ MouseMove( aLeave );
+ SetItemDown(nTBC_BT_AUTOFILTER, false);
+}
+
+void BibToolBar::statusChanged(const frame::FeatureStateEvent& rEvent)
+{
+ for(uno::Reference<frame::XStatusListener> & rListener : aListenerArr)
+ {
+ rListener->statusChanged(rEvent);
+ }
+}
+
+void BibToolBar::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ ApplyImageList();
+ ToolBox::DataChanged( rDCEvt );
+}
+
+IMPL_LINK_NOARG( BibToolBar, OptionsChanged_Impl, LinkParamNone*, void )
+{
+ bool bRebuildToolBar = false;
+ sal_Int16 eSymbolsSize = SvtMiscOptions().GetCurrentSymbolsSize();
+ if ( nSymbolsSize != eSymbolsSize )
+ {
+ nSymbolsSize = eSymbolsSize;
+ bRebuildToolBar = true;
+ }
+
+ if ( bRebuildToolBar )
+ RebuildToolbar();
+}
+
+IMPL_LINK_NOARG( BibToolBar, SettingsChanged_Impl, VclSimpleEvent&, void )
+{
+ // Check if toolbar button size have changed and we have to use system settings
+ sal_Int16 eSymbolsSize = SvtMiscOptions().GetCurrentSymbolsSize();
+ if ( eSymbolsSize != nSymbolsSize )
+ {
+ nSymbolsSize = eSymbolsSize;
+ RebuildToolbar();
+ }
+}
+
+void BibToolBar::RebuildToolbar()
+{
+ ApplyImageList();
+ // We have to call parent asynchronously as SetSize works also asynchronously!
+ Application::PostUserEvent( aLayoutManager );
+}
+
+void BibToolBar::ApplyImageList()
+{
+ SetItemImage(nTBC_BT_AUTOFILTER, Image(StockImage::Yes, nSymbolsSize == SFX_SYMBOLS_SIZE_SMALL ? OUString(RID_EXTBMP_AUTOFILTER_SC) : OUString(RID_EXTBMP_AUTOFILTER_LC)));
+ SetItemImage(nTBC_BT_FILTERCRIT, Image(StockImage::Yes, nSymbolsSize == SFX_SYMBOLS_SIZE_SMALL ? OUString(RID_EXTBMP_FILTERCRIT_SC) : OUString(RID_EXTBMP_FILTERCRIT_LC)));
+ SetItemImage(nTBC_BT_REMOVEFILTER, Image(StockImage::Yes, nSymbolsSize == SFX_SYMBOLS_SIZE_SMALL ? OUString(RID_EXTBMP_REMOVE_FILTER_SORT_SC) : OUString(RID_EXTBMP_REMOVE_FILTER_SORT_LC)));
+ AdjustToolBox();
+}
+
+void BibToolBar::AdjustToolBox()
+{
+ Size aOldSize = GetSizePixel();
+ Size aSize = CalcWindowSizePixel();
+ if ( !aSize.Width() )
+ aSize.setWidth( aOldSize.Width() );
+ else if ( !aSize.Height() )
+ aSize.setHeight( aOldSize.Height() );
+
+ Size aTbSize = GetSizePixel();
+ if (
+ (aSize.Width() && aSize.Width() != aTbSize.Width()) ||
+ (aSize.Height() && aSize.Height() != aTbSize.Height())
+ )
+ {
+ SetPosSizePixel( GetPosPixel(), aSize );
+ Invalidate();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/bibliography/toolbar.hxx b/extensions/source/bibliography/toolbar.hxx
new file mode 100644
index 000000000..4f800f050
--- /dev/null
+++ b/extensions/source/bibliography/toolbar.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+
+#include <vcl/InterimItemWindow.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/timer.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <vector>
+
+class BibDataManager;
+class BibToolBar;
+
+class BibToolBarListener: public cppu::WeakImplHelper < css::frame::XStatusListener>
+{
+private:
+
+ ToolBoxItemId nIndex;
+ OUString aCommand;
+
+protected:
+
+ VclPtr<BibToolBar> pToolBar;
+
+public:
+
+ BibToolBarListener(BibToolBar *pTB, const OUString& aStr, ToolBoxItemId nId);
+ virtual ~BibToolBarListener() override;
+
+ const OUString& GetCommand() const { return aCommand;}
+
+ // css::lang::XEventListener
+ // we do not hold References to dispatches, so there is nothing to do on disposal
+ virtual void SAL_CALL disposing(const css::lang::EventObject& /*Source*/) override {};
+
+ // css::frame::XStatusListener
+ virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& Event) override;
+
+};
+
+class BibTBListBoxListener: public BibToolBarListener
+{
+public:
+
+ BibTBListBoxListener(BibToolBar *pTB, const OUString& aStr, ToolBoxItemId nId);
+ virtual ~BibTBListBoxListener() override;
+
+ virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& Event) override;
+
+};
+
+class BibTBEditListener: public BibToolBarListener
+{
+public:
+
+ BibTBEditListener(BibToolBar *pTB, const OUString& aStr, ToolBoxItemId nId);
+ virtual ~BibTBEditListener() override;
+
+ virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& Event) override;
+
+};
+
+class BibTBQueryMenuListener: public BibToolBarListener
+{
+public:
+
+ BibTBQueryMenuListener(BibToolBar *pTB, const OUString& aStr, ToolBoxItemId nId);
+ virtual ~BibTBQueryMenuListener() override;
+
+ virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& Event) override;
+
+};
+
+
+typedef std::vector< css::uno::Reference< css::frame::XStatusListener> > BibToolBarListenerArr;
+
+class ComboBoxControl final : public InterimItemWindow
+{
+public:
+ ComboBoxControl(vcl::Window* pParent);
+ virtual ~ComboBoxControl() override;
+ virtual void dispose() override;
+
+ weld::ComboBox* get_widget() { return m_xLBSource.get(); }
+
+ void set_sensitive(bool bSensitive)
+ {
+ m_xFtSource->set_sensitive(bSensitive);
+ m_xLBSource->set_sensitive(bSensitive);
+ Enable(bSensitive);
+ }
+
+private:
+ std::unique_ptr<weld::Label> m_xFtSource;
+ std::unique_ptr<weld::ComboBox> m_xLBSource;
+};
+
+class EditControl final : public InterimItemWindow
+{
+public:
+ EditControl(vcl::Window* pParent);
+ virtual ~EditControl() override;
+ virtual void dispose() override;
+
+ weld::Entry* get_widget() { return m_xEdQuery.get(); }
+
+ void set_sensitive(bool bSensitive)
+ {
+ m_xFtQuery->set_sensitive(bSensitive);
+ m_xEdQuery->set_sensitive(bSensitive);
+ Enable(bSensitive);
+ }
+
+private:
+ std::unique_ptr<weld::Label> m_xFtQuery;
+ std::unique_ptr<weld::Entry> m_xEdQuery;
+};
+
+class BibToolBar: public ToolBox
+{
+ private:
+
+ BibToolBarListenerArr aListenerArr;
+ css::uno::Reference< css::frame::XController > xController;
+ Idle aIdle;
+ VclPtr<ComboBoxControl> xSource;
+ weld::ComboBox* pLbSource;
+ VclPtr<EditControl> xQuery;
+ weld::Entry* pEdQuery;
+ std::unique_ptr<weld::Builder> xBuilder;
+ std::unique_ptr<weld::Menu> xPopupMenu;
+ sal_uInt16 nMenuId;
+ OString sSelMenuItem;
+ OUString aQueryField;
+ Link<void*,void> aLayoutManager;
+ sal_Int16 nSymbolsSize;
+
+ ToolBoxItemId nTBC_SOURCE;
+ ToolBoxItemId nTBC_QUERY;
+ ToolBoxItemId nTBC_BT_AUTOFILTER;
+ ToolBoxItemId nTBC_BT_COL_ASSIGN;
+ ToolBoxItemId nTBC_BT_CHANGESOURCE;
+ ToolBoxItemId nTBC_BT_FILTERCRIT;
+ ToolBoxItemId nTBC_BT_REMOVEFILTER;
+
+ BibDataManager* pDatMan;
+ DECL_LINK( SelHdl, weld::ComboBox&, void );
+ DECL_LINK( SendSelHdl, Timer*, void );
+ DECL_LINK( MenuHdl, ToolBox*, void );
+ DECL_LINK( OptionsChanged_Impl, LinkParamNone*, void );
+ DECL_LINK( SettingsChanged_Impl, VclSimpleEvent&, void );
+
+ void ApplyImageList();
+ void RebuildToolbar();
+
+ protected:
+
+ void DataChanged( const DataChangedEvent& rDCEvt ) override;
+ void InitListener();
+ virtual void Select() override;
+ virtual void Click() override;
+ virtual bool PreNotify( NotifyEvent& rNEvt ) override;
+
+
+ public:
+
+ BibToolBar(vcl::Window* pParent, Link<void*,void> aLink);
+ virtual ~BibToolBar() override;
+ virtual void dispose() override;
+
+ ToolBoxItemId GetChangeSourceId() const { return nTBC_BT_CHANGESOURCE; }
+
+ void SetXController(const css::uno::Reference< css::frame::XController > &);
+
+ void ClearSourceList();
+ void UpdateSourceList(bool bFlag);
+ void EnableSourceList(bool bFlag);
+ void InsertSourceEntry(const OUString& );
+ void SelectSourceEntry(const OUString& );
+
+ void EnableQuery(bool bFlag);
+ void SetQueryString(const OUString& );
+ void AdjustToolBox();
+
+ void ClearFilterMenu();
+ sal_uInt16 InsertFilterItem(const OUString& );
+ void SelectFilterItem(sal_uInt16 nId);
+
+ /// @throws css::uno::RuntimeException
+ void statusChanged(const css::frame::FeatureStateEvent& Event);
+
+ void SetDatMan(BibDataManager& rDatMan) {pDatMan = &rDatMan;}
+ void SendDispatch(ToolBoxItemId nId, const css::uno::Sequence< css::beans::PropertyValue >& rArgs);
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/config/WinUserInfo/WinUserInfoBe.component b/extensions/source/config/WinUserInfo/WinUserInfoBe.component
new file mode 100644
index 000000000..5dd7d21ad
--- /dev/null
+++ b/extensions/source/config/WinUserInfo/WinUserInfoBe.component
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.configuration.backend.WinUserInfoBe"
+ constructor="extensions_WinUserInfoBe_get_implementation">
+ <service name="com.sun.star.configuration.backend.WinUserInfoBe"/>
+ </implementation>
+</component>
diff --git a/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx b/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx
new file mode 100644
index 000000000..2914cf78d
--- /dev/null
+++ b/extensions/source/config/WinUserInfo/WinUserInfoBe.cxx
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "WinUserInfoBe.hxx"
+
+#include <com/sun/star/beans/Optional.hpp>
+#include <comphelper/base64.hxx>
+#include <comphelper/configuration.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <map>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <tools/diagnose_ex.h>
+#include <officecfg/UserProfile.hxx>
+
+#include <Iads.h>
+#include <Adshlp.h>
+#include <Lmcons.h>
+#define SECURITY_WIN32
+#include <Security.h>
+
+#include <systools/win32/comtools.hxx>
+#include <systools/win32/oleauto.hxx>
+
+namespace extensions
+{
+namespace config
+{
+namespace WinUserInfo
+{
+class WinUserInfoBe_Impl
+{
+public:
+ virtual ~WinUserInfoBe_Impl(){};
+ virtual OUString GetGivenName() = 0;
+ virtual OUString GetSn() { return ""; }
+ virtual OUString GetFathersname() { return ""; }
+ virtual OUString GetInitials() { return ""; }
+ virtual OUString GetStreet() { return ""; }
+ virtual OUString GetCity() { return ""; }
+ virtual OUString GetState() { return ""; }
+ virtual OUString GetApartment() { return ""; }
+ virtual OUString GetPostalCode() { return ""; }
+ virtual OUString GetCountry() { return ""; }
+ virtual OUString GetOrganization() { return ""; }
+ virtual OUString GetPosition() { return ""; }
+ virtual OUString GetTitle() { return ""; }
+ virtual OUString GetHomePhone() { return ""; }
+ virtual OUString GetTelephoneNumber() { return ""; }
+ virtual OUString GetFaxNumber() { return ""; }
+ virtual OUString GetMail() { return ""; }
+};
+}
+}
+}
+
+namespace
+{
+constexpr OUStringLiteral givenname(u"givenname");
+constexpr OUStringLiteral sn(u"sn");
+constexpr char fathersname[]("fathersname");
+constexpr OUStringLiteral initials(u"initials");
+constexpr OUStringLiteral street(u"street");
+constexpr OUStringLiteral l(u"l");
+constexpr OUStringLiteral st(u"st");
+constexpr char apartment[]("apartment");
+constexpr OUStringLiteral postalcode(u"postalcode");
+constexpr OUStringLiteral c(u"c");
+constexpr OUStringLiteral o(u"o");
+constexpr char position[]("position");
+constexpr OUStringLiteral title(u"title");
+constexpr OUStringLiteral homephone(u"homephone");
+constexpr OUStringLiteral telephonenumber(u"telephonenumber");
+constexpr OUStringLiteral facsimiletelephonenumber(u"facsimiletelephonenumber");
+constexpr OUStringLiteral mail(u"mail");
+
+// Backend class implementing access to Active Directory user data. It caches its encoded data
+// in a configuration entry, to allow reusing it when user later doesn't have access to AD DC
+// (otherwise the user would get different data when connected vs not connected).
+class ADsUserAccess : public extensions::config::WinUserInfo::WinUserInfoBe_Impl
+{
+public:
+ ADsUserAccess()
+ {
+ try
+ {
+ sal::systools::CoInitializeGuard aCoInitializeGuard(COINIT_APARTMENTTHREADED);
+
+ sal::systools::COMReference<IADsADSystemInfo> pADsys(CLSID_ADSystemInfo, nullptr,
+ CLSCTX_INPROC_SERVER);
+
+ sal::systools::BStr sUserDN;
+ sal::systools::ThrowIfFailed(pADsys->get_UserName(&sUserDN), "get_UserName failed");
+ // If this user is an AD user, then without an active connection to the domain, all the
+ // above will succeed, and m_sUserDN will be correctly initialized, but the following
+ // call to ADsGetObject will fail, and we will attempt reading cached values.
+ m_sUserDN = sUserDN;
+ OUString sLdapUserDN = "LDAP://" + m_sUserDN;
+ sal::systools::COMReference<IADsUser> pUser;
+ sal::systools::ThrowIfFailed(ADsGetObject(o3tl::toW(sLdapUserDN.getStr()), IID_IADsUser,
+ reinterpret_cast<void**>(&pUser)),
+ "ADsGetObject failed");
+ // Fetch all the required information right now, when we know to have access to AD
+ // (later the connection may already be lost)
+ m_aMap[givenname] = Str(pUser, &IADsUser::get_FirstName);
+ m_aMap[sn] = Str(pUser, &IADsUser::get_LastName);
+ m_aMap[initials] = Str(pUser, L"initials");
+ m_aMap[street] = Str(pUser, L"streetAddress");
+ m_aMap[l] = Str(pUser, L"l");
+ m_aMap[st] = Str(pUser, L"st");
+ m_aMap[postalcode] = Str(pUser, L"postalCode");
+ m_aMap[c] = Str(pUser, L"co");
+ m_aMap[o] = Str(pUser, L"company");
+ m_aMap[title] = Str(pUser, &IADsUser::get_Title);
+ m_aMap[homephone] = Str(pUser, L"homePhone");
+ m_aMap[telephonenumber] = Str(pUser, L"TelephoneNumber");
+ m_aMap[facsimiletelephonenumber] = Str(pUser, L"facsimileTelephoneNumber");
+ m_aMap[mail] = Str(pUser, &IADsUser::get_EmailAddress);
+
+ CacheData();
+ }
+ catch (sal::systools::ComError&)
+ {
+ // Maybe we temporarily lost connection to AD; try to get cached data
+ GetCachedData();
+ }
+ }
+
+ virtual OUString GetGivenName() override { return m_aMap[givenname]; }
+ virtual OUString GetSn() override { return m_aMap[sn]; }
+ virtual OUString GetInitials() override { return m_aMap[initials]; }
+ virtual OUString GetStreet() override { return m_aMap[street]; }
+ virtual OUString GetCity() override { return m_aMap[l]; }
+ virtual OUString GetState() override { return m_aMap[st]; }
+ virtual OUString GetPostalCode() override { return m_aMap[postalcode]; }
+ virtual OUString GetCountry() override { return m_aMap[c]; }
+ virtual OUString GetOrganization() override { return m_aMap[o]; }
+ virtual OUString GetTitle() override { return m_aMap[title]; }
+ virtual OUString GetHomePhone() override { return m_aMap[homephone]; }
+ virtual OUString GetTelephoneNumber() override { return m_aMap[telephonenumber]; }
+ virtual OUString GetFaxNumber() override { return m_aMap[facsimiletelephonenumber]; }
+ virtual OUString GetMail() override { return m_aMap[mail]; }
+
+private:
+ typedef HRESULT (__stdcall IADsUser::*getstrfunc)(BSTR*);
+ static OUString Str(IADsUser* pUser, getstrfunc func)
+ {
+ sal::systools::BStr sBstr;
+ if (FAILED((pUser->*func)(&sBstr)))
+ return "";
+ return OUString(sBstr);
+ }
+ static OUString Str(IADsUser* pUser, const wchar_t* property)
+ {
+ sal::systools::BStr sBstrProp{ o3tl::toU(property) };
+ struct AutoVariant : public VARIANT
+ {
+ AutoVariant() { VariantInit(this); }
+ ~AutoVariant() { VariantClear(this); }
+ } varArr;
+ if (FAILED(pUser->GetEx(sBstrProp, &varArr)))
+ return "";
+ SAFEARRAY* sa = V_ARRAY(&varArr);
+ LONG nStart, nEnd;
+ if (FAILED(SafeArrayGetLBound(sa, 1, &nStart)) || FAILED(SafeArrayGetUBound(sa, 1, &nEnd)))
+ return "";
+ AutoVariant varItem;
+ for (LONG i = nStart; i <= nEnd; i++)
+ {
+ if (FAILED(SafeArrayGetElement(sa, &i, &varItem)))
+ continue;
+ if (varItem.vt == VT_BSTR)
+ return OUString(o3tl::toU(V_BSTR(&varItem)));
+ VariantClear(&varItem);
+ }
+ return "";
+ }
+
+ void CacheData()
+ {
+ try
+ {
+ OUString sCachedData = "user=" + m_sUserDN // user DN
+ + "\0" + givenname + "=" + GetGivenName() // 1st name
+ + "\0" + sn + "=" + GetSn() // sn
+ + "\0" + initials + "=" + GetInitials() // initials
+ + "\0" + street + "=" + GetStreet() // street
+ + "\0" + l + "=" + GetCity() // l
+ + "\0" + st + "=" + GetState() // st
+ + "\0" + postalcode + "=" + GetPostalCode() // p.code
+ + "\0" + c + "=" + GetCountry() // c
+ + "\0" + o + "=" + GetOrganization() // o
+ + "\0" + title + "=" + GetTitle() // title
+ + "\0" + homephone + "=" + GetHomePhone() // h.phone
+ + "\0" + telephonenumber + "=" + GetTelephoneNumber() // tel
+ + "\0" + facsimiletelephonenumber + "=" + GetFaxNumber() // fax
+ + "\0" + mail + "=" + GetMail(); // mail
+ const css::uno::Sequence<sal_Int8> seqCachedData(
+ reinterpret_cast<const sal_Int8*>(sCachedData.getStr()),
+ sCachedData.getLength() * sizeof(sal_Unicode));
+ OUStringBuffer sOutBuf;
+ comphelper::Base64::encode(sOutBuf, seqCachedData);
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::UserProfile::WinUserInfo::Cache::set(sOutBuf.makeStringAndClear(), batch);
+ batch->commit();
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.config",
+ "ADsUserAccess: access to configuration data failed:");
+ }
+ }
+
+ void GetCachedData()
+ {
+ if (m_sUserDN.isEmpty())
+ throw css::uno::RuntimeException();
+
+ OUString sCache = officecfg::UserProfile::WinUserInfo::Cache::get();
+
+ if (sCache.isEmpty())
+ throw css::uno::RuntimeException();
+
+ {
+ css::uno::Sequence<sal_Int8> seqCachedData;
+ comphelper::Base64::decode(seqCachedData, sCache);
+ sCache = OUString(reinterpret_cast<const sal_Unicode*>(seqCachedData.getConstArray()),
+ seqCachedData.getLength() / sizeof(sal_Unicode));
+ }
+
+ OUString sUserDN;
+ std::map<const OUString, OUString> aMap;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ const OUString sEntry = sCache.getToken(0, '\0', nIndex);
+ sal_Int32 nEqIndex = 0;
+ const OUString sEntryName = sEntry.getToken(0, '=', nEqIndex);
+ OUString sEntryVal;
+ if (nEqIndex >= 0)
+ sEntryVal = sEntry.copy(nEqIndex);
+ if (sEntryName == "user")
+ sUserDN = sEntryVal;
+ else
+ aMap[sEntryName] = sEntryVal;
+ } while (nIndex >= 0);
+
+ if (sUserDN != m_sUserDN)
+ throw css::uno::RuntimeException();
+ m_aMap = std::move(aMap);
+ }
+
+ OUString m_sUserDN; // used to check if the cached data is for current user
+ std::map<const OUString, OUString> m_aMap;
+};
+
+class SysInfoUserAccess : public extensions::config::WinUserInfo::WinUserInfoBe_Impl
+{
+public:
+ SysInfoUserAccess()
+ {
+ try
+ {
+ ULONG nSize = 0;
+ GetUserNameExW(NameDisplay, nullptr, &nSize);
+ if (GetLastError() != ERROR_MORE_DATA)
+ throw css::uno::RuntimeException();
+ auto pNameBuf(std::make_unique<wchar_t[]>(nSize));
+ if (!GetUserNameExW(NameDisplay, pNameBuf.get(), &nSize))
+ throw css::uno::RuntimeException();
+ m_sName = o3tl::toU(pNameBuf.get());
+ }
+ catch (css::uno::RuntimeException&)
+ {
+ // GetUserNameEx may fail in some cases (e.g., for built-in AD domain
+ // administrator account on non-DC systems), where GetUserName will
+ // still give a name.
+ DWORD nSize = UNLEN + 1;
+ auto pNameBuf(std::make_unique<wchar_t[]>(nSize));
+ if (!GetUserNameW(pNameBuf.get(), &nSize))
+ throw css::uno::RuntimeException();
+ m_sName = o3tl::toU(pNameBuf.get());
+ }
+ }
+
+ virtual OUString GetGivenName() override { return m_sName; }
+
+private:
+ OUString m_sName;
+};
+}
+
+namespace extensions
+{
+namespace config
+{
+namespace WinUserInfo
+{
+WinUserInfoBe::WinUserInfoBe()
+ : WinUserInfoMutexHolder()
+ , BackendBase(mMutex)
+{
+ try
+ {
+ m_pImpl.reset(new ADsUserAccess());
+ }
+ catch (css::uno::RuntimeException&)
+ {
+ m_pImpl.reset(new SysInfoUserAccess);
+ }
+}
+
+WinUserInfoBe::~WinUserInfoBe() {}
+
+void WinUserInfoBe::setPropertyValue(OUString const&, css::uno::Any const&)
+{
+ throw css::lang::IllegalArgumentException("setPropertyValue not supported",
+ static_cast<cppu::OWeakObject*>(this), -1);
+}
+
+css::uno::Any WinUserInfoBe::getPropertyValue(OUString const& PropertyName)
+{
+ OUString sValue;
+ // Only process the first argument of possibly multiple space- or comma-separated arguments
+ OUString sToken = PropertyName.getToken(0, ' ').getToken(0, ',');
+ if (sToken == givenname)
+ {
+ sValue = m_pImpl->GetGivenName();
+ }
+ else if (sToken == sn)
+ {
+ sValue = m_pImpl->GetSn();
+ }
+ else if (sToken == fathersname)
+ {
+ sValue = m_pImpl->GetFathersname();
+ }
+ else if (sToken == initials)
+ {
+ sValue = m_pImpl->GetInitials();
+ }
+ else if (sToken == street)
+ {
+ sValue = m_pImpl->GetStreet();
+ }
+ else if (sToken == l)
+ {
+ sValue = m_pImpl->GetCity();
+ }
+ else if (sToken == st)
+ {
+ sValue = m_pImpl->GetState();
+ }
+ else if (sToken == apartment)
+ {
+ sValue = m_pImpl->GetApartment();
+ }
+ else if (sToken == postalcode)
+ {
+ sValue = m_pImpl->GetPostalCode();
+ }
+ else if (sToken == c)
+ {
+ sValue = m_pImpl->GetCountry();
+ }
+ else if (sToken == o)
+ {
+ sValue = m_pImpl->GetOrganization();
+ }
+ else if (sToken == position)
+ {
+ sValue = m_pImpl->GetPosition();
+ }
+ else if (sToken == title)
+ {
+ sValue = m_pImpl->GetTitle();
+ }
+ else if (sToken == homephone)
+ {
+ sValue = m_pImpl->GetHomePhone();
+ }
+ else if (sToken == telephonenumber)
+ {
+ sValue = m_pImpl->GetTelephoneNumber();
+ }
+ else if (sToken == facsimiletelephonenumber)
+ {
+ sValue = m_pImpl->GetFaxNumber();
+ }
+ else if (sToken == mail)
+ {
+ sValue = m_pImpl->GetMail();
+ }
+ else
+ throw css::beans::UnknownPropertyException(sToken, static_cast<cppu::OWeakObject*>(this));
+
+ return css::uno::Any(css::beans::Optional<css::uno::Any>(
+ !sValue.isEmpty(), sValue.isEmpty() ? css::uno::Any() : css::uno::Any(sValue)));
+}
+
+OUString SAL_CALL WinUserInfoBe::getImplementationName()
+{
+ return "com.sun.star.comp.configuration.backend.WinUserInfoBe";
+}
+
+sal_Bool SAL_CALL WinUserInfoBe::supportsService(const OUString& aServiceName)
+{
+ return cppu::supportsService(this, aServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL WinUserInfoBe::getSupportedServiceNames()
+{
+ return { "com.sun.star.configuration.backend.WinUserInfoBe" };
+}
+}
+}
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_WinUserInfoBe_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new extensions::config::WinUserInfo::WinUserInfoBe());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/config/WinUserInfo/WinUserInfoBe.hxx b/extensions/source/config/WinUserInfo/WinUserInfoBe.hxx
new file mode 100644
index 000000000..30ca088c3
--- /dev/null
+++ b/extensions/source/config/WinUserInfo/WinUserInfoBe.hxx
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <memory>
+
+namespace com
+{
+namespace sun
+{
+namespace star
+{
+namespace uno
+{
+class XComponentContext;
+}
+}
+}
+}
+
+namespace extensions
+{
+namespace config
+{
+namespace WinUserInfo
+{
+class WinUserInfoBe_Impl;
+
+typedef cppu::WeakComponentImplHelper<css::beans::XPropertySet, css::lang::XServiceInfo>
+ BackendBase;
+
+struct WinUserInfoMutexHolder
+{
+ osl::Mutex mMutex;
+};
+/**
+ Implements the PlatformBackend service, a specialization of the
+ XPropertySet service for retrieving Active Directory user profile
+ configuration settings.
+*/
+class WinUserInfoBe : private WinUserInfoMutexHolder, public BackendBase
+{
+public:
+ explicit WinUserInfoBe();
+ virtual ~WinUserInfoBe() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(const OUString& aServiceName) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPropertySet
+ virtual css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override
+ {
+ return css::uno::Reference<css::beans::XPropertySetInfo>();
+ }
+
+ virtual void SAL_CALL setPropertyValue(OUString const&, css::uno::Any const&) override;
+
+ virtual css::uno::Any SAL_CALL getPropertyValue(OUString const& PropertyName) override;
+
+ virtual void SAL_CALL addPropertyChangeListener(
+ OUString const&, css::uno::Reference<css::beans::XPropertyChangeListener> const&) override
+ {
+ }
+
+ virtual void SAL_CALL removePropertyChangeListener(
+ OUString const&, css::uno::Reference<css::beans::XPropertyChangeListener> const&) override
+ {
+ }
+
+ virtual void SAL_CALL addVetoableChangeListener(
+ OUString const&, css::uno::Reference<css::beans::XVetoableChangeListener> const&) override
+ {
+ }
+
+ virtual void SAL_CALL removeVetoableChangeListener(
+ OUString const&, css::uno::Reference<css::beans::XVetoableChangeListener> const&) override
+ {
+ }
+
+private:
+ std::unique_ptr<WinUserInfoBe_Impl> m_pImpl;
+};
+}
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/config/ldap/ldapaccess.cxx b/extensions/source/config/ldap/ldapaccess.cxx
new file mode 100644
index 000000000..7e35408b3
--- /dev/null
+++ b/extensions/source/config/ldap/ldapaccess.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 "ldapaccess.hxx"
+
+#include <osl/diagnose.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <com/sun/star/ldap/LdapConnectionException.hpp>
+
+
+namespace extensions::config::ldap {
+
+
+typedef int LdapErrCode;
+
+struct LdapMessageHolder
+{
+ LdapMessageHolder() : msg(nullptr) {}
+ ~LdapMessageHolder()
+ {
+ if (msg)
+ ldap_msgfree(msg);
+ }
+ LdapMessageHolder(const LdapMessageHolder&) = delete;
+ LdapMessageHolder& operator=(const LdapMessageHolder&) = delete;
+
+ LDAPMessage * msg;
+};
+
+LdapConnection::~LdapConnection()
+{
+ if (isValid()) disconnect();
+}
+
+
+void LdapConnection::disconnect()
+{
+ if (mConnection != nullptr)
+ {
+ ldap_unbind_s(mConnection) ;
+ mConnection = nullptr;
+ }
+}
+
+
+static void checkLdapReturnCode(const char *aOperation,
+ LdapErrCode aRetCode)
+{
+ if (aRetCode == LDAP_SUCCESS) { return ; }
+
+ OUString message;
+
+ if (aOperation != nullptr)
+ {
+ message += OUString::createFromAscii(aOperation) + ": ";
+ }
+ message += OUString::createFromAscii(ldap_err2string(aRetCode)) + " (" ;
+
+#ifndef LDAP_OPT_SIZELIMIT // for use with OpenLDAP
+ char* stub = nullptr;
+ ldap_get_lderrno(aConnection, NULL, &stub) ;
+ if (stub != nullptr)
+ {
+ message += OUString::createFromAscii(stub) ;
+ // It would seem the message returned is actually
+ // not a copy of a string but rather some static
+ // string itself. At any rate freeing it seems to
+ // cause some undue problems at least on Windows.
+ // This call is thus disabled for the moment.
+ //ldap_memfree(stub) ;
+ }
+ else
+#endif
+ { message += "No additional information"; }
+
+ message += ")" ;
+ throw ldap::LdapGenericException(message, nullptr, aRetCode) ;
+}
+
+void LdapConnection::connectSimple(const LdapDefinition& aDefinition)
+{
+ OSL_ENSURE(!isValid(), "Re-connecting to an LDAP connection that is already established");
+ if (isValid()) disconnect();
+
+ mLdapDefinition = aDefinition;
+ connectSimple();
+}
+
+void LdapConnection::connectSimple()
+{
+ if (isValid())
+ return;
+
+ // Connect to the server
+ initConnection() ;
+ // Set Protocol V3
+ int version = LDAP_VERSION3;
+ ldap_set_option(mConnection,
+ LDAP_OPT_PROTOCOL_VERSION,
+ &version);
+
+#ifdef LDAP_X_OPT_CONNECT_TIMEOUT // OpenLDAP doesn't support this and the func
+ /* timeout is specified in milliseconds -> 4 seconds*/
+ int timeout = 4000;
+#ifdef _WIN32
+ ldap_set_optionW( mConnection,
+ LDAP_X_OPT_CONNECT_TIMEOUT,
+ &timeout );
+#else
+ ldap_set_option( mConnection,
+ LDAP_X_OPT_CONNECT_TIMEOUT,
+ &timeout );
+#endif
+#endif
+
+ // Do the bind
+#ifdef _WIN32
+ LdapErrCode retCode = ldap_simple_bind_sW(mConnection,
+ const_cast<PWSTR>(o3tl::toW(mLdapDefinition.mAnonUser.getStr())),
+ const_cast<PWSTR>(o3tl::toW(mLdapDefinition.mAnonCredentials.getStr())) );
+#else
+ LdapErrCode retCode = ldap_simple_bind_s(mConnection,
+ OUStringToOString( mLdapDefinition.mAnonUser, RTL_TEXTENCODING_UTF8 ).getStr(),
+ OUStringToOString( mLdapDefinition.mAnonCredentials, RTL_TEXTENCODING_UTF8 ).getStr()) ;
+#endif
+
+ checkLdapReturnCode("SimpleBind", retCode) ;
+}
+
+void LdapConnection::initConnection()
+{
+ if (mLdapDefinition.mServer.isEmpty())
+ {
+ throw ldap::LdapConnectionException("Cannot initialise connection to LDAP: No server specified.");
+ }
+
+ if (mLdapDefinition.mPort == 0) mLdapDefinition.mPort = LDAP_PORT;
+
+#ifdef _WIN32
+ mConnection = ldap_initW(const_cast<PWSTR>(o3tl::toW(mLdapDefinition.mServer.getStr())),
+ mLdapDefinition.mPort) ;
+#else
+ mConnection = ldap_init(OUStringToOString( mLdapDefinition.mServer, RTL_TEXTENCODING_UTF8 ).getStr(),
+ mLdapDefinition.mPort) ;
+#endif
+ if (mConnection == nullptr)
+ {
+ throw ldap::LdapConnectionException(
+ "Cannot initialise connection to LDAP server "
+ + mLdapDefinition.mServer + ":" + OUString::number(mLdapDefinition.mPort));
+ }
+}
+
+ void LdapConnection::getUserProfile(
+ const OUString& aUser, LdapData * data)
+{
+ OSL_ASSERT(data != nullptr);
+ if (!isValid()) { connectSimple(); }
+
+ OUString aUserDn =findUserDn( aUser );
+
+ LdapMessageHolder result;
+#ifdef _WIN32
+ LdapErrCode retCode = ldap_search_sW(mConnection,
+ const_cast<PWSTR>(o3tl::toW(aUserDn.getStr())),
+ LDAP_SCOPE_BASE,
+ const_cast<PWSTR>( L"(objectclass=*)" ),
+ nullptr,
+ 0, // Attributes + values
+ &result.msg) ;
+#else
+ LdapErrCode retCode = ldap_search_s(mConnection,
+ OUStringToOString( aUserDn, RTL_TEXTENCODING_UTF8 ).getStr(),
+ LDAP_SCOPE_BASE,
+ "(objectclass=*)",
+ nullptr,
+ 0, // Attributes + values
+ &result.msg) ;
+#endif
+ checkLdapReturnCode("getUserProfile", retCode) ;
+
+ BerElement * ptr;
+#ifdef _WIN32
+ PWCHAR attr = ldap_first_attributeW(mConnection, result.msg, &ptr);
+ while (attr) {
+ PWCHAR * values = ldap_get_valuesW(mConnection, result.msg, attr);
+ if (values) {
+ const OUString aAttr( o3tl::toU( attr ) );
+ const OUString aValues( o3tl::toU( *values ) );
+ data->emplace( aAttr, aValues );
+ ldap_value_freeW(values);
+ }
+ attr = ldap_next_attributeW(mConnection, result.msg, ptr);
+#else
+ char * attr = ldap_first_attribute(mConnection, result.msg, &ptr);
+ while (attr) {
+ char ** values = ldap_get_values(mConnection, result.msg, attr);
+ if (values) {
+ data->emplace(
+ OStringToOUString(attr, RTL_TEXTENCODING_ASCII_US),
+ OStringToOUString(*values, RTL_TEXTENCODING_UTF8));
+ ldap_value_free(values);
+ }
+ attr = ldap_next_attribute(mConnection, result.msg, ptr);
+#endif
+ }
+}
+
+ OUString LdapConnection::findUserDn(const OUString& aUser)
+{
+ if (!isValid()) { connectSimple(); }
+
+ if (aUser.isEmpty())
+ {
+ throw lang::IllegalArgumentException(
+ "LdapConnection::findUserDn -User id is empty",
+ nullptr, 0) ;
+ }
+
+ OUString filter = "(&(objectclass="
+ + mLdapDefinition.mUserObjectClass
+ + ")("
+ + mLdapDefinition.mUserUniqueAttr
+ + "="
+ + aUser
+ + "))";
+
+ LdapMessageHolder result;
+#ifdef _WIN32
+ PWCHAR attributes [2] = { const_cast<PWCHAR>( L"1.1" ), nullptr };
+ LdapErrCode retCode = ldap_search_sW(mConnection,
+ const_cast<PWSTR>(o3tl::toW(mLdapDefinition.mBaseDN.getStr())),
+ LDAP_SCOPE_SUBTREE,
+ const_cast<PWSTR>(o3tl::toW(filter.getStr())), attributes, 0, &result.msg) ;
+#else
+ char * attributes [2] = { const_cast<char *>(LDAP_NO_ATTRS), nullptr };
+ LdapErrCode retCode = ldap_search_s(mConnection,
+ OUStringToOString( mLdapDefinition.mBaseDN, RTL_TEXTENCODING_UTF8 ).getStr(),
+ LDAP_SCOPE_SUBTREE,
+ OUStringToOString( filter, RTL_TEXTENCODING_UTF8 ).getStr(), attributes, 0, &result.msg) ;
+#endif
+ checkLdapReturnCode("FindUserDn", retCode) ;
+ OUString userDn ;
+ LDAPMessage *entry = ldap_first_entry(mConnection, result.msg) ;
+
+ if (entry != nullptr)
+ {
+#ifdef _WIN32
+ PWCHAR charsDn = ldap_get_dnW(mConnection, entry) ;
+
+ userDn = OUString( o3tl::toU( charsDn ) );
+ ldap_memfreeW(charsDn) ;
+#else
+ char *charsDn = ldap_get_dn(mConnection, entry) ;
+
+ userDn = OStringToOUString( charsDn, RTL_TEXTENCODING_UTF8 );
+ ldap_memfree(charsDn) ;
+#endif
+ }
+ else
+ {
+ OSL_FAIL( "LdapConnection::findUserDn-could not get DN for User ");
+ }
+
+ return userDn ;
+}
+
+
+} // extensions::config::ldap
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/config/ldap/ldapaccess.hxx b/extensions/source/config/ldap/ldapaccess.hxx
new file mode 100644
index 000000000..36a0708b1
--- /dev/null
+++ b/extensions/source/config/ldap/ldapaccess.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 <sal/config.h>
+
+#include <map>
+
+#ifdef _WIN32
+#if !defined WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <winldap.h>
+#else // !defined _WIN32
+#include <ldap.h>
+#endif // _WIN32
+
+#include <com/sun/star/ldap/LdapGenericException.hpp>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+namespace extensions::config::ldap
+{
+namespace uno = css::uno;
+namespace lang = css::lang;
+namespace ldap = css::ldap;
+
+struct LdapUserProfile;
+
+/** Struct containing the information on LDAP connection */
+struct LdapDefinition
+{
+ /** LDAP server name */
+ OUString mServer;
+ /** LDAP server port number */
+ sal_Int32 mPort;
+ /** Repository base DN */
+ OUString mBaseDN;
+ /** DN to use for "anonymous" connection */
+ OUString mAnonUser;
+ /** Credentials to use for "anonymous" connection */
+ OUString mAnonCredentials;
+ /** User Entity Object Class */
+ OUString mUserObjectClass;
+ /** User Entity Unique Attribute */
+ OUString mUserUniqueAttr;
+
+ LdapDefinition()
+ : mPort(0)
+ {
+ }
+};
+
+typedef std::map<OUString, OUString> LdapData; // key/value pairs
+
+/** Class encapsulating all LDAP functionality */
+class LdapConnection
+{
+ friend struct LdapMessageHolder;
+
+public:
+ /** Default constructor */
+ LdapConnection()
+ : mConnection(nullptr)
+ , mLdapDefinition()
+ {
+ }
+ /** Destructor, releases the connection */
+ ~LdapConnection();
+ /** Make connection to LDAP server
+ @throws ldap::LdapConnectionException
+ @throws ldap::LdapGenericException
+ */
+ void connectSimple(const LdapDefinition& aDefinition);
+
+ /**
+ Gets LdapUserProfile from LDAP repository for specified user
+ @param aUser name of logged on user
+ @param aUserProfileMap Map containing LDAP->00o mapping
+ @param aUserProfile struct for holding OOo values
+
+ @throws css::ldap::LdapGenericException
+ if an LDAP error occurs.
+ */
+ void getUserProfile(const OUString& aUser, LdapData* data);
+
+ /** finds DN of user
+ @return DN of User
+ @throws lang::IllegalArgumentException
+ @throws ldap::LdapConnectionException
+ @throws ldap::LdapGenericException
+ */
+ OUString findUserDn(const OUString& aUser);
+
+private:
+ /// @throws ldap::LdapConnectionException
+ void initConnection();
+ void disconnect();
+ /**
+ Indicates whether the connection is in a valid state.
+ @return sal_True if connection is valid, sal_False otherwise
+ */
+ bool isValid() const { return mConnection != nullptr; }
+
+ /// @throws ldap::LdapConnectionException
+ /// @throws ldap::LdapGenericException
+ void connectSimple();
+
+ /** LDAP connection object */
+ LDAP* mConnection;
+ LdapDefinition mLdapDefinition;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/config/ldap/ldapbe2.component b/extensions/source/config/ldap/ldapbe2.component
new file mode 100644
index 000000000..8f6ea3f80
--- /dev/null
+++ b/extensions/source/config/ldap/ldapbe2.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.configuration.backend.LdapUserProfileBe"
+ constructor="extensions_ldp_LdapUserProfileBe_get_implementation">
+ <service name="com.sun.star.configuration.backend.LdapUserProfileBe"/>
+ </implementation>
+</component>
diff --git a/extensions/source/config/ldap/ldapuserprofilebe.cxx b/extensions/source/config/ldap/ldapuserprofilebe.cxx
new file mode 100644
index 000000000..2012afd8b
--- /dev/null
+++ b/extensions/source/config/ldap/ldapuserprofilebe.cxx
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "ldapaccess.hxx"
+#include "ldapuserprofilebe.hxx"
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <rtl/instance.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <comphelper/scopeguard.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/security.hxx>
+
+
+namespace extensions::config::ldap {
+
+LdapUserProfileBe::LdapUserProfileBe( const uno::Reference<uno::XComponentContext>& xContext)
+: BackendBase(m_aMutex)
+{
+ LdapDefinition aDefinition;
+ OUString loggedOnUser;
+ // true initially to handle reentrant call; will become false if readLdapConfiguration fails
+ bool bHaveLdapConfiguration = true;
+
+ // This whole rigmarole is to prevent an infinite recursion where reading
+ // the configuration for the backend would create another instance of the
+ // backend, which would try and read the configuration which would...
+ {
+ osl::Mutex & aInitMutex = rtl::Static< osl::Mutex, LdapUserProfileBe >::get();
+ osl::MutexGuard aInitGuard(aInitMutex);
+
+ static bool bReentrantCall; // = false
+ OSL_ENSURE(!bReentrantCall, "configuration: Ldap Backend constructor called reentrantly - probably a registration error.");
+
+ if (!bReentrantCall)
+ {
+ bReentrantCall = true ;
+ comphelper::ScopeGuard aReentrantCallGuard([]() { bReentrantCall = false; });
+ // Don't throw on fail: this will crash if LDAP is misconfigured, and user opens
+ // Expert Configuration dialog. Instead, just don't fill data_, which will make the
+ // backend return empty values. This happens in SvtUserOptions::Impl::GetValue_Impl
+ // anyway even in throwing scenario, but doing it here also improves performance
+ // because of avoiding repeated attempts to create the backend.
+ bHaveLdapConfiguration = readLdapConfiguration(
+ xContext, &aDefinition, &loggedOnUser);
+ if (!bHaveLdapConfiguration)
+ SAL_WARN("extensions.config", "LdapUserProfileBackend: LDAP not configured");
+ }
+ }
+
+ if (bHaveLdapConfiguration)
+ {
+ LdapConnection connection;
+ connection.connectSimple(aDefinition);
+ connection.getUserProfile(loggedOnUser, &data_);
+ }
+}
+
+LdapUserProfileBe::~LdapUserProfileBe()
+{
+}
+
+
+bool LdapUserProfileBe::readLdapConfiguration(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ LdapDefinition * definition, OUString * loggedOnUser)
+{
+ OSL_ASSERT(context.is() && definition != nullptr && loggedOnUser != nullptr);
+
+ uno::Reference< XInterface > xIface;
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xCfgProvider(
+ css::configuration::theDefaultProvider::get(context));
+
+ css::beans::NamedValue aPath("nodepath", uno::Any(OUString("org.openoffice.LDAP/UserDirectory")) );
+
+ uno::Sequence< uno::Any > aArgs{ uno::Any(aPath) };
+
+ xIface = xCfgProvider->createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess", aArgs);
+
+ uno::Reference<container::XNameAccess > xAccess(xIface, uno::UNO_QUERY_THROW);
+ xAccess->getByName("ServerDefinition") >>= xIface;
+
+ uno::Reference<container::XNameAccess > xChildAccess(xIface, uno::UNO_QUERY_THROW);
+
+ if (!getLdapStringParam(xChildAccess, "Server", definition->mServer))
+ return false;
+ if (!getLdapStringParam(xChildAccess, "BaseDN", definition->mBaseDN))
+ return false;
+
+ definition->mPort=0;
+ xChildAccess->getByName("Port") >>= definition->mPort ;
+ if (definition->mPort == 0)
+ return false;
+
+ if (!getLdapStringParam(xAccess, "UserObjectClass", definition->mUserObjectClass))
+ return false;
+ if (!getLdapStringParam(xAccess, "UserUniqueAttribute", definition->mUserUniqueAttr))
+ return false;
+
+ getLdapStringParam(xAccess, "SearchUser", definition->mAnonUser);
+ getLdapStringParam(xAccess, "SearchPassword", definition->mAnonCredentials);
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.config", "LdapUserProfileBackend: access to configuration data failed");
+ return false;
+ }
+
+ osl::Security aSecurityContext;
+ if (!aSecurityContext.getUserName(*loggedOnUser))
+ SAL_WARN("extensions.config", "LdapUserProfileBackend - could not get Logged on user from system");
+
+ sal_Int32 nIndex = loggedOnUser->indexOf('/');
+ if (nIndex > 0)
+ *loggedOnUser = loggedOnUser->copy(nIndex+1);
+
+ return true;
+}
+
+
+bool LdapUserProfileBe::getLdapStringParam(
+ uno::Reference<container::XNameAccess> const & xAccess,
+ const OUString& aLdapSetting,
+ OUString& aServerParameter)
+{
+ xAccess->getByName(aLdapSetting) >>= aServerParameter;
+
+ return !aServerParameter.isEmpty();
+}
+
+void LdapUserProfileBe::setPropertyValue(
+ OUString const &, css::uno::Any const &)
+{
+ throw css::lang::IllegalArgumentException(
+ "setPropertyValue not supported",
+ static_cast< cppu::OWeakObject * >(this), -1);
+}
+
+css::uno::Any LdapUserProfileBe::getPropertyValue(
+ OUString const & PropertyName)
+{
+ for (sal_Int32 i = 0;;) {
+ sal_Int32 j = PropertyName.indexOf(',', i);
+ if (j == -1) {
+ j = PropertyName.getLength();
+ }
+ if (j == i) {
+ throw css::beans::UnknownPropertyException(
+ PropertyName, static_cast< cppu::OWeakObject * >(this));
+ }
+ LdapData::iterator k(data_.find(PropertyName.copy(i, j - i)));
+ if (k != data_.end()) {
+ return css::uno::Any(
+ css::beans::Optional< css::uno::Any >(
+ true, css::uno::Any(k->second)));
+ }
+ if (j == PropertyName.getLength()) {
+ break;
+ }
+ i = j + 1;
+ }
+ return css::uno::Any(css::beans::Optional< css::uno::Any >());
+}
+
+
+OUString SAL_CALL LdapUserProfileBe::getImplementationName()
+{
+ return "com.sun.star.comp.configuration.backend.LdapUserProfileBe";
+}
+
+sal_Bool SAL_CALL LdapUserProfileBe::supportsService(const OUString& aServiceName)
+{
+ return cppu::supportsService(this, aServiceName);
+}
+
+uno::Sequence<OUString>
+SAL_CALL LdapUserProfileBe::getSupportedServiceNames()
+{
+ return { "com.sun.star.configuration.backend.LdapUserProfileBe" };
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_ldp_LdapUserProfileBe_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new extensions::config::ldap::LdapUserProfileBe(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/config/ldap/ldapuserprofilebe.hxx b/extensions/source/config/ldap/ldapuserprofilebe.hxx
new file mode 100644
index 000000000..2f0536532
--- /dev/null
+++ b/extensions/source/config/ldap/ldapuserprofilebe.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 <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+#include "ldapaccess.hxx"
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+namespace extensions::config::ldap {
+
+namespace uno = css::uno ;
+namespace lang = css::lang ;
+namespace container = css::container;
+
+struct LdapDefinition;
+
+typedef cppu::WeakComponentImplHelper<css::beans::XPropertySet,
+ lang::XServiceInfo> BackendBase ;
+
+/**
+ Implements the PlatformBackend service, a specialization of the
+ XPropertySet service for retrieving LDAP user profile
+ configuration settings from an LDAP repository.
+ */
+class LdapUserProfileBe : private cppu::BaseMutex, public BackendBase
+{
+ public:
+
+ explicit LdapUserProfileBe(const uno::Reference<uno::XComponentContext>& xContext);
+ virtual ~LdapUserProfileBe() override ;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL
+ getImplementationName( ) override ;
+
+ virtual sal_Bool SAL_CALL
+ supportsService( const OUString& aServiceName ) override ;
+
+ virtual uno::Sequence<OUString> SAL_CALL
+ getSupportedServiceNames( ) override ;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override
+ { return css::uno::Reference< css::beans::XPropertySetInfo >(); }
+
+ virtual void SAL_CALL setPropertyValue(
+ OUString const &, css::uno::Any const &) override;
+
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ OUString const & PropertyName) override;
+
+ virtual void SAL_CALL addPropertyChangeListener(
+ OUString const &,
+ css::uno::Reference< css::beans::XPropertyChangeListener > const &) override
+ {}
+
+ virtual void SAL_CALL removePropertyChangeListener(
+ OUString const &,
+ css::uno::Reference< css::beans::XPropertyChangeListener > const &) override
+ {}
+
+ virtual void SAL_CALL addVetoableChangeListener(
+ OUString const &,
+ css::uno::Reference< css::beans::XVetoableChangeListener > const &) override
+ {}
+
+ virtual void SAL_CALL removeVetoableChangeListener(
+ OUString const &,
+ css::uno::Reference< css::beans::XVetoableChangeListener > const &) override
+ {}
+
+ private:
+ /** Check if LDAP is configured */
+ static bool readLdapConfiguration(
+ uno::Reference<uno::XComponentContext> const & context,
+ LdapDefinition * definition, OUString * loggedOnUser);
+
+ static bool getLdapStringParam(uno::Reference<container::XNameAccess> const & xAccess,
+ const OUString& aLdapSetting,
+ OUString& aServerParameter);
+
+ LdapData data_;
+} ;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/commonpagesdbp.cxx b/extensions/source/dbpilots/commonpagesdbp.cxx
new file mode 100644
index 000000000..2e133e9c5
--- /dev/null
+++ b/extensions/source/dbpilots/commonpagesdbp.cxx
@@ -0,0 +1,449 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "commonpagesdbp.hxx"
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <componentmodule.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdbc/SQLWarning.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/interaction.hxx>
+#include <connectivity/dbtools.hxx>
+#include <sfx2/docfilt.hxx>
+#include <unotools/pathoptions.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <svl/filenotation.hxx>
+#include <osl/diagnose.h>
+
+namespace dbp
+{
+
+
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::task;
+ using namespace ::comphelper;
+
+ OTableSelectionPage::OTableSelectionPage(weld::Container* pPage, OControlWizard* pWizard)
+ : OControlWizardPage(pPage, pWizard, "modules/sabpilot/ui/tableselectionpage.ui", "TableSelectionPage")
+ , m_xTable(m_xBuilder->weld_tree_view("table"))
+ , m_xDatasource(m_xBuilder->weld_tree_view("datasource"))
+ , m_xDatasourceLabel(m_xBuilder->weld_label("datasourcelabel"))
+ , m_xSearchDatabase(m_xBuilder->weld_button("search"))
+ , m_xSourceBox(m_xBuilder->weld_container("sourcebox"))
+ {
+ try
+ {
+ m_xDSContext = getContext().xDatasourceContext;
+ if (m_xDSContext.is())
+ fillListBox(*m_xDatasource, m_xDSContext->getElementNames());
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("OTableSelectionPage::OTableSelectionPage: could not collect the data source names!");
+ }
+
+ m_xDatasource->connect_changed(LINK(this, OTableSelectionPage, OnListboxSelection));
+ m_xTable->connect_changed(LINK(this, OTableSelectionPage, OnListboxSelection));
+ m_xTable->connect_row_activated(LINK(this, OTableSelectionPage, OnListboxDoubleClicked));
+ m_xSearchDatabase->connect_clicked(LINK(this, OTableSelectionPage, OnSearchClicked));
+ }
+
+ OTableSelectionPage::~OTableSelectionPage()
+ {
+ }
+
+ void OTableSelectionPage::Activate()
+ {
+ OControlWizardPage::Activate();
+ m_xDatasource->grab_focus();
+ }
+
+ bool OTableSelectionPage::canAdvance() const
+ {
+ if (!OControlWizardPage::canAdvance())
+ return false;
+
+ if (0 == m_xDatasource->count_selected_rows())
+ return false;
+
+ if (0 == m_xTable->count_selected_rows())
+ return false;
+
+ return true;
+ }
+
+ void OTableSelectionPage::initializePage()
+ {
+ OControlWizardPage::initializePage();
+
+ const OControlWizardContext& rContext = getContext();
+ try
+ {
+ OUString sDataSourceName;
+ rContext.xForm->getPropertyValue("DataSourceName") >>= sDataSourceName;
+
+ Reference< XConnection > xConnection;
+ bool bEmbedded = ::dbtools::isEmbeddedInDatabase( rContext.xForm, xConnection );
+ if ( bEmbedded )
+ {
+ m_xSourceBox->hide();
+ m_xDatasource->append_text(sDataSourceName);
+ }
+ m_xDatasource->select_text(sDataSourceName);
+
+ implFillTables(xConnection);
+
+ OUString sCommand;
+ OSL_VERIFY( rContext.xForm->getPropertyValue("Command") >>= sCommand );
+ sal_Int32 nCommandType = CommandType::TABLE;
+ OSL_VERIFY( rContext.xForm->getPropertyValue("CommandType") >>= nCommandType );
+
+ // search the entry of the given type with the given name
+ for (sal_Int32 nLookup = 0; nLookup < m_xTable->n_children(); ++nLookup)
+ {
+ if (sCommand == m_xTable->get_text(nLookup))
+ {
+ if (m_xTable->get_id(nLookup).toInt32() == nCommandType)
+ {
+ m_xTable->select( nLookup );
+ break;
+ }
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.abpilot", "OTableSelectionPage::initializePage");
+ }
+ }
+
+ bool OTableSelectionPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OControlWizardPage::commitPage(_eReason))
+ return false;
+
+ const OControlWizardContext& rContext = getContext();
+ try
+ {
+ Reference< XConnection > xOldConn;
+ if ( !rContext.bEmbedded )
+ {
+ xOldConn = getFormConnection();
+
+ OUString sDataSource = m_xDatasource->get_selected_text();
+ rContext.xForm->setPropertyValue("DataSourceName", Any( sDataSource ) );
+ }
+ OUString sCommand = m_xTable->get_selected_text();
+ sal_Int32 nCommandType = m_xTable->get_selected_id().toInt32();
+
+ rContext.xForm->setPropertyValue("Command", Any( sCommand ) );
+ rContext.xForm->setPropertyValue("CommandType", Any( nCommandType ) );
+
+ if ( !rContext.bEmbedded )
+ setFormConnection( xOldConn, false );
+
+ if (!updateContext())
+ return false;
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OTableSelectionPage::commitPage");
+ }
+
+ return true;
+ }
+
+ IMPL_LINK_NOARG( OTableSelectionPage, OnSearchClicked, weld::Button&, void )
+ {
+ ::sfx2::FileDialogHelper aFileDlg(
+ ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION,
+ FileDialogFlags::NONE, getDialog()->getDialog());
+ aFileDlg.SetDisplayDirectory( SvtPathOptions().GetWorkPath() );
+
+ std::shared_ptr<const SfxFilter> pFilter = SfxFilter::GetFilterByName("StarOffice XML (Base)");
+ OSL_ENSURE(pFilter,"Filter: StarOffice XML (Base) could not be found!");
+ if ( pFilter )
+ {
+ aFileDlg.AddFilter(pFilter->GetUIName(),pFilter->GetDefaultExtension());
+ }
+
+ if (ERRCODE_NONE == aFileDlg.Execute())
+ {
+ OUString sDataSourceName = aFileDlg.GetPath();
+ ::svt::OFileNotation aFileNotation(sDataSourceName);
+ sDataSourceName = aFileNotation.get(::svt::OFileNotation::N_SYSTEM);
+ m_xDatasource->append_text(sDataSourceName);
+ m_xDatasource->select_text(sDataSourceName);
+ LINK(this, OTableSelectionPage, OnListboxSelection).Call(*m_xDatasource);
+ }
+ }
+
+ IMPL_LINK(OTableSelectionPage, OnListboxDoubleClicked, weld::TreeView&, _rBox, bool)
+ {
+ if (_rBox.count_selected_rows())
+ getDialog()->travelNext();
+ return true;
+ }
+
+ IMPL_LINK(OTableSelectionPage, OnListboxSelection, weld::TreeView&, _rBox, void)
+ {
+ if (m_xDatasource.get() == &_rBox)
+ { // new data source selected
+ implFillTables();
+ }
+
+ updateDialogTravelUI();
+ }
+
+ namespace
+ {
+ void lcl_fillEntries(weld::TreeView& rListBox, const Sequence<OUString>& rNames, const OUString& rImage, sal_Int32 nCommandType)
+ {
+ for (auto const & name : rNames)
+ {
+ rListBox.append(OUString::number(nCommandType), name, rImage);
+ }
+ }
+ }
+
+ void OTableSelectionPage::implFillTables(const Reference< XConnection >& _rxConn)
+ {
+ m_xTable->clear();
+
+ weld::WaitObject aWaitCursor(getDialog()->getDialog());
+
+ // will be the table tables of the selected data source
+ Sequence< OUString > aTableNames;
+ Sequence< OUString > aQueryNames;
+
+ // connect to the data source
+ Any aSQLException;
+ Reference< XConnection > xConn = _rxConn;
+ if ( !xConn.is() )
+ {
+ if (!m_xDSContext.is())
+ return;
+ // connect to the data source
+ try
+ {
+ OUString sCurrentDatasource = m_xDatasource->get_selected_text();
+ if (!sCurrentDatasource.isEmpty())
+ {
+ // obtain the DS object
+ Reference< XCompletedConnection > xDatasource;
+ // check if I know this one otherwise transform it into a file URL
+ if ( !m_xDSContext->hasByName(sCurrentDatasource) )
+ {
+ ::svt::OFileNotation aFileNotation(sCurrentDatasource);
+ sCurrentDatasource = aFileNotation.get(::svt::OFileNotation::N_URL);
+ }
+
+ if (m_xDSContext->getByName(sCurrentDatasource) >>= xDatasource)
+ { // connect
+ // get the default SDB interaction handler
+ Reference< XInteractionHandler > xHandler = getDialog()->getInteractionHandler(getDialog()->getDialog());
+ if (!xHandler.is() )
+ return;
+ xConn = xDatasource->connectWithCompletion(xHandler);
+ setFormConnection( xConn );
+ }
+ else
+ {
+ OSL_FAIL("OTableSelectionPage::implFillTables: invalid data source object returned by the context");
+ }
+ }
+ }
+ catch(const SQLContext& e) { aSQLException <<= e; }
+ catch(const SQLWarning& e) { aSQLException <<= e; }
+ catch(const SQLException& e) { aSQLException <<= e; }
+ catch (const Exception&)
+ {
+ OSL_FAIL("OTableSelectionPage::implFillTables: could not fill the table list!");
+ }
+ }
+
+ // will be the table tables of the selected data source
+ if ( xConn.is() )
+ {
+ try
+ {
+ // get the tables
+ Reference< XTablesSupplier > xSupplTables(xConn, UNO_QUERY);
+ if ( xSupplTables.is() )
+ {
+ Reference< XNameAccess > xTables = xSupplTables->getTables();
+ if (xTables.is())
+ aTableNames = xTables->getElementNames();
+ }
+
+ // and the queries
+ Reference< XQueriesSupplier > xSuppQueries( xConn, UNO_QUERY );
+ if ( xSuppQueries.is() )
+ {
+ Reference< XNameAccess > xQueries = xSuppQueries->getQueries();
+ if ( xQueries.is() )
+ aQueryNames = xQueries->getElementNames();
+ }
+ }
+ catch(const SQLContext& e) { aSQLException <<= e; }
+ catch(const SQLWarning& e) { aSQLException <<= e; }
+ catch(const SQLException& e) { aSQLException <<= e; }
+ catch (const Exception&)
+ {
+ OSL_FAIL("OTableSelectionPage::implFillTables: could not fill the table list!");
+ }
+ }
+
+
+ if ( aSQLException.hasValue() )
+ { // an SQLException (or derivee) was thrown ...
+ Reference< XInteractionRequest > xRequest = new OInteractionRequest(aSQLException);
+ try
+ {
+ // get the default SDB interaction handler
+ Reference< XInteractionHandler > xHandler = getDialog()->getInteractionHandler(getDialog()->getDialog());
+ if ( xHandler.is() )
+ xHandler->handle(xRequest);
+ }
+ catch(const Exception&) { }
+ return;
+ }
+
+ lcl_fillEntries(*m_xTable, aTableNames, BMP_TABLE, CommandType::TABLE);
+ lcl_fillEntries(*m_xTable, aQueryNames, BMP_QUERY, CommandType::QUERY);
+ }
+
+ OMaybeListSelectionPage::OMaybeListSelectionPage(weld::Container* pPage, OControlWizard* pWizard, const OUString& rUIXMLDescription, const OString& rID)
+ : OControlWizardPage(pPage, pWizard, rUIXMLDescription, rID)
+ , m_pYes(nullptr)
+ , m_pNo(nullptr)
+ , m_pList(nullptr)
+ {
+ }
+
+ OMaybeListSelectionPage::~OMaybeListSelectionPage()
+ {
+ }
+
+ void OMaybeListSelectionPage::announceControls(weld::RadioButton& _rYesButton, weld::RadioButton& _rNoButton, weld::ComboBox& _rSelection)
+ {
+ m_pYes = &_rYesButton;
+ m_pNo = &_rNoButton;
+ m_pList = &_rSelection;
+
+ m_pYes->connect_toggled(LINK(this, OMaybeListSelectionPage, OnRadioSelected));
+ m_pNo->connect_toggled(LINK(this, OMaybeListSelectionPage, OnRadioSelected));
+ implEnableWindows();
+ }
+
+ IMPL_LINK(OMaybeListSelectionPage, OnRadioSelected, weld::Toggleable&, rButton, void)
+ {
+ if (!rButton.get_active())
+ return;
+ implEnableWindows();
+ }
+
+ void OMaybeListSelectionPage::implInitialize(const OUString& _rSelection)
+ {
+ DBG_ASSERT(m_pYes, "OMaybeListSelectionPage::implInitialize: no controls announced!");
+ bool bIsSelection = ! _rSelection.isEmpty();
+ m_pYes->set_active(bIsSelection);
+ m_pNo->set_active(!bIsSelection);
+ m_pList->set_sensitive(bIsSelection);
+
+ m_pList->set_active_text(bIsSelection ? _rSelection : OUString());
+ }
+
+ void OMaybeListSelectionPage::implCommit(OUString& _rSelection)
+ {
+ _rSelection = m_pYes->get_active() ? m_pList->get_active_text() : OUString();
+ }
+
+ void OMaybeListSelectionPage::implEnableWindows()
+ {
+ m_pList->set_sensitive(m_pYes->get_active());
+ }
+
+ void OMaybeListSelectionPage::Activate()
+ {
+ OControlWizardPage::Activate();
+
+ DBG_ASSERT(m_pYes, "OMaybeListSelectionPage::Activate: no controls announced!");
+ if (m_pYes->get_active())
+ m_pList->grab_focus();
+ else
+ m_pNo->grab_focus();
+ }
+
+ ODBFieldPage::ODBFieldPage(weld::Container* pPage, OControlWizard* pWizard)
+ : OMaybeListSelectionPage(pPage, pWizard, "modules/sabpilot/ui/optiondbfieldpage.ui", "OptionDBField")
+ , m_xDescription(m_xBuilder->weld_label("explLabel"))
+ , m_xStoreYes(m_xBuilder->weld_radio_button("yesRadiobutton"))
+ , m_xStoreNo(m_xBuilder->weld_radio_button("noRadiobutton"))
+ , m_xStoreWhere(m_xBuilder->weld_combo_box("storeInFieldCombobox"))
+ {
+ SetPageTitle(compmodule::ModuleRes(RID_STR_OPTION_DB_FIELD_TITLE));
+
+ announceControls(*m_xStoreYes, *m_xStoreNo, *m_xStoreWhere);
+ }
+
+ ODBFieldPage::~ODBFieldPage()
+ {
+ }
+
+ void ODBFieldPage::initializePage()
+ {
+ OMaybeListSelectionPage::initializePage();
+
+ // fill the fields page
+ fillListBox(*m_xStoreWhere, getContext().aFieldNames);
+
+ implInitialize(getDBFieldSetting());
+ }
+
+ bool ODBFieldPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OMaybeListSelectionPage::commitPage(_eReason))
+ return false;
+
+ implCommit(getDBFieldSetting());
+
+ return true;
+ }
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/commonpagesdbp.hxx b/extensions/source/dbpilots/commonpagesdbp.hxx
new file mode 100644
index 000000000..1ae742150
--- /dev/null
+++ b/extensions/source/dbpilots/commonpagesdbp.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 "controlwizard.hxx"
+#include <vcl/weld.hxx>
+#include <com/sun/star/sdb/XDatabaseContext.hpp>
+
+namespace dbp
+{
+ class OTableSelectionPage final : public OControlWizardPage
+ {
+ std::unique_ptr<weld::TreeView> m_xTable;
+ std::unique_ptr<weld::TreeView> m_xDatasource;
+ std::unique_ptr<weld::Label> m_xDatasourceLabel;
+ std::unique_ptr<weld::Button> m_xSearchDatabase;
+ std::unique_ptr<weld::Container> m_xSourceBox;
+
+ css::uno::Reference< css::sdb::XDatabaseContext >
+ m_xDSContext;
+
+ public:
+ explicit OTableSelectionPage(weld::Container* pPage, OControlWizard* pParent);
+ virtual ~OTableSelectionPage() override;
+
+ private:
+ // BuilderPage overridables
+ void Activate() override;
+
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+
+ DECL_LINK( OnListboxSelection, weld::TreeView&, void );
+ DECL_LINK( OnListboxDoubleClicked, weld::TreeView&, bool );
+ DECL_LINK( OnSearchClicked, weld::Button&, void );
+
+ void implFillTables(const css::uno::Reference< css::sdbc::XConnection >&
+ _rxConn = css::uno::Reference< css::sdbc::XConnection >());
+
+ // OControlWizardPage overridables
+ virtual bool canAdvance() const override;
+ };
+
+ class OMaybeListSelectionPage : public OControlWizardPage
+ {
+ weld::RadioButton* m_pYes;
+ weld::RadioButton* m_pNo;
+ weld::ComboBox* m_pList;
+
+ public:
+ OMaybeListSelectionPage(weld::Container* pPage, OControlWizard* pWizard, const OUString& rUIXMLDescription, const OString& rID);
+ virtual ~OMaybeListSelectionPage() override;
+
+ protected:
+ DECL_LINK( OnRadioSelected, weld::Toggleable&, void );
+
+ // BuilderPage overridables
+ void Activate() override;
+
+ // own helper
+ void announceControls(
+ weld::RadioButton& _rYesButton,
+ weld::RadioButton& _rNoButton,
+ weld::ComboBox& _rSelection);
+
+ void implEnableWindows();
+
+ void implInitialize(const OUString& _rSelection);
+ void implCommit(OUString& _rSelection);
+ };
+
+ class ODBFieldPage : public OMaybeListSelectionPage
+ {
+ protected:
+ std::unique_ptr<weld::Label> m_xDescription;
+ std::unique_ptr<weld::RadioButton> m_xStoreYes;
+ std::unique_ptr<weld::RadioButton> m_xStoreNo;
+ std::unique_ptr<weld::ComboBox> m_xStoreWhere;
+
+ public:
+ explicit ODBFieldPage(weld::Container* pPage, OControlWizard* pWizard);
+ virtual ~ODBFieldPage() override;
+
+ protected:
+ void setDescriptionText(const OUString& rDesc)
+ {
+ m_xDescription->set_label(rDesc);
+ }
+
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+
+ // own overridables
+ virtual OUString& getDBFieldSetting() = 0;
+ };
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/controlwizard.cxx b/extensions/source/dbpilots/controlwizard.cxx
new file mode 100644
index 000000000..20052c4cb
--- /dev/null
+++ b/extensions/source/dbpilots/controlwizard.cxx
@@ -0,0 +1,663 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "controlwizard.hxx"
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/sdbc/XPreparedStatement.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/XSpreadsheetView.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/SQLWarning.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <comphelper/interaction.hxx>
+#include <vcl/stdtext.hxx>
+#include <connectivity/conncleanup.hxx>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <tools/urlobj.hxx>
+
+#define WIZARD_SIZE_X 60
+#define WIZARD_SIZE_Y 23
+
+namespace dbp
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::drawing;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::sheet;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::task;
+ using namespace ::comphelper;
+ using namespace ::dbtools;
+
+ struct OAccessRegulator
+ {
+ friend class OControlWizardPage;
+
+ protected:
+ OAccessRegulator() { }
+ };
+
+ OControlWizardPage::OControlWizardPage(weld::Container* pPage, OControlWizard* pWizard, const OUString& rUIXMLDescription, const OString& rID)
+ : OControlWizardPage_Base(pPage, pWizard, rUIXMLDescription, rID)
+ , m_pDialog(pWizard)
+ {
+ m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * WIZARD_SIZE_X,
+ m_xContainer->get_text_height() * WIZARD_SIZE_Y);
+ }
+
+ OControlWizardPage::~OControlWizardPage()
+ {
+ }
+
+ OControlWizard* OControlWizardPage::getDialog()
+ {
+ return m_pDialog;
+ }
+
+ const OControlWizard* OControlWizardPage::getDialog() const
+ {
+ return m_pDialog;
+ }
+
+ bool OControlWizardPage::updateContext()
+ {
+ return m_pDialog->updateContext(OAccessRegulator());
+ }
+
+ Reference< XConnection > OControlWizardPage::getFormConnection() const
+ {
+ return m_pDialog->getFormConnection(OAccessRegulator());
+ }
+
+ void OControlWizardPage::setFormConnection( const Reference< XConnection >& _rxConn, bool _bAutoDispose )
+ {
+ m_pDialog->setFormConnection( OAccessRegulator(), _rxConn, _bAutoDispose );
+ }
+
+ const OControlWizardContext& OControlWizardPage::getContext() const
+ {
+ return m_pDialog->getContext();
+ }
+
+ void OControlWizardPage::fillListBox(weld::TreeView& _rList, const Sequence< OUString >& _rItems)
+ {
+ _rList.clear();
+ const OUString* pItems = _rItems.getConstArray();
+ const OUString* pEnd = pItems + _rItems.getLength();
+ sal_Int32 nIndex = 0;
+ for (;pItems < pEnd; ++pItems, ++nIndex)
+ {
+ _rList.append(OUString::number(nIndex), *pItems);
+ }
+ }
+
+ void OControlWizardPage::fillListBox(weld::ComboBox& _rList, const Sequence< OUString >& _rItems)
+ {
+ _rList.clear();
+ const OUString* pItems = _rItems.getConstArray();
+ const OUString* pEnd = pItems + _rItems.getLength();
+ for (;pItems < pEnd; ++pItems)
+ {
+ _rList.append_text(*pItems);
+ }
+ }
+
+ void OControlWizardPage::enableFormDatasourceDisplay()
+ {
+ if (m_xFormContentType)
+ // nothing to do
+ return;
+
+ m_xFrame = m_xBuilder->weld_frame("sourceframe");
+ m_xFrame->show();
+ m_xFormContentType = m_xBuilder->weld_label("contenttype");
+ m_xFormContentTypeLabel = m_xBuilder->weld_label("contenttypelabel");
+ m_xFormDatasource = m_xBuilder->weld_label("datasource");
+ m_xFormDatasourceLabel = m_xBuilder->weld_label("datasourcelabel");
+ m_xFormTable = m_xBuilder->weld_label("formtable");
+ m_xFormTableLabel = m_xBuilder->weld_label("formtablelabel");
+
+ const OControlWizardContext& rContext = getContext();
+ if ( rContext.bEmbedded )
+ {
+ m_xFormDatasourceLabel->hide();
+ m_xFormDatasource->hide();
+ }
+ }
+
+ void OControlWizardPage::initializePage()
+ {
+ if (m_xFormDatasource && m_xFormContentTypeLabel && m_xFormTable)
+ {
+ const OControlWizardContext& rContext = getContext();
+ OUString sDataSource;
+ OUString sCommand;
+ sal_Int32 nCommandType = CommandType::COMMAND;
+ try
+ {
+ rContext.xForm->getPropertyValue("DataSourceName") >>= sDataSource;
+ rContext.xForm->getPropertyValue("Command") >>= sCommand;
+ rContext.xForm->getPropertyValue("CommandType") >>= nCommandType;
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OControlWizardPage::initializePage");
+ }
+
+ INetURLObject aURL( sDataSource );
+ if( aURL.GetProtocol() != INetProtocol::NotValid )
+ sDataSource = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ m_xFormDatasource->set_label(sDataSource);
+ m_xFormTable->set_label(sCommand);
+
+ TranslateId pCommandTypeResourceId;
+ switch (nCommandType)
+ {
+ case CommandType::TABLE:
+ pCommandTypeResourceId = RID_STR_TYPE_TABLE;
+ break;
+
+ case CommandType::QUERY:
+ pCommandTypeResourceId = RID_STR_TYPE_QUERY;
+ break;
+
+ default:
+ pCommandTypeResourceId = RID_STR_TYPE_COMMAND;
+ break;
+ }
+ m_xFormContentType->set_label(compmodule::ModuleRes(pCommandTypeResourceId));
+ }
+
+ OControlWizardPage_Base::initializePage();
+ }
+
+ OControlWizard::OControlWizard(weld::Window* _pParent,
+ const Reference< XPropertySet >& _rxObjectModel, const Reference< XComponentContext >& _rxContext )
+ : WizardMachine(_pParent, WizardButtonFlags::CANCEL | WizardButtonFlags::PREVIOUS | WizardButtonFlags::NEXT | WizardButtonFlags::FINISH)
+ , m_xContext(_rxContext)
+ {
+ m_aContext.xObjectModel = _rxObjectModel;
+ initContext();
+
+ defaultButton(WizardButtonFlags::NEXT);
+ enableButtons(WizardButtonFlags::FINISH, false);
+ }
+
+ OControlWizard::~OControlWizard()
+ {
+ }
+
+ short OControlWizard::run()
+ {
+ // get the class id of the control we're dealing with
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ try
+ {
+ getContext().xObjectModel->getPropertyValue("ClassId") >>= nClassId;
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("OControlWizard::activate: could not obtain the class id!");
+ }
+ if (!approveControl(nClassId))
+ {
+ // TODO: MessageBox or exception
+ return RET_CANCEL;
+ }
+
+ ActivatePage();
+
+ m_xAssistant->set_current_page(0);
+
+ return OControlWizard_Base::run();
+ }
+
+ void OControlWizard::implDetermineShape()
+ {
+ Reference< XIndexAccess > xPageObjects = m_aContext.xDrawPage;
+ DBG_ASSERT(xPageObjects.is(), "OControlWizard::implDetermineShape: invalid page!");
+
+ // for comparing the model
+ Reference< XControlModel > xModelCompare(m_aContext.xObjectModel, UNO_QUERY);
+
+ if (!xPageObjects.is())
+ return;
+
+ // loop through all objects of the page
+ sal_Int32 nObjects = xPageObjects->getCount();
+ Reference< XControlShape > xControlShape;
+ Reference< XControlModel > xControlModel;
+ for (sal_Int32 i=0; i<nObjects; ++i)
+ {
+ if (xPageObjects->getByIndex(i) >>= xControlShape)
+ { // it _is_ a control shape
+ xControlModel = xControlShape->getControl();
+ DBG_ASSERT(xControlModel.is(), "OControlWizard::implDetermineShape: control shape without model!");
+ if (xModelCompare.get() == xControlModel.get())
+ {
+ m_aContext.xObjectShape = xControlShape;
+ break;
+ }
+ }
+ }
+ }
+
+
+ void OControlWizard::implDetermineForm()
+ {
+ Reference< XChild > xModelAsChild(m_aContext.xObjectModel, UNO_QUERY);
+ Reference< XInterface > xControlParent;
+ if (xModelAsChild.is())
+ xControlParent = xModelAsChild->getParent();
+
+ m_aContext.xForm.set(xControlParent, UNO_QUERY);
+ m_aContext.xRowSet.set(xControlParent, UNO_QUERY);
+ DBG_ASSERT(m_aContext.xForm.is() && m_aContext.xRowSet.is(),
+ "OControlWizard::implDetermineForm: missing some interfaces of the control parent!");
+
+ }
+
+
+ void OControlWizard::implDeterminePage()
+ {
+ try
+ {
+ // get the document model
+ Reference< XChild > xControlAsChild(m_aContext.xObjectModel, UNO_QUERY);
+ Reference< XChild > xModelSearch(xControlAsChild->getParent(), UNO_QUERY);
+
+ Reference< XModel > xModel(xModelSearch, UNO_QUERY);
+ while (xModelSearch.is() && !xModel.is())
+ {
+ xModelSearch.set(xModelSearch->getParent(), UNO_QUERY);
+ xModel.set(xModelSearch, UNO_QUERY);
+ }
+
+ Reference< XDrawPage > xPage;
+ if (xModel.is())
+ {
+ m_aContext.xDocumentModel = xModel;
+
+ Reference< XDrawPageSupplier > xPageSupp(xModel, UNO_QUERY);
+ if (xPageSupp.is())
+ { // it's a document with only one page -> Writer
+ xPage = xPageSupp->getDrawPage();
+ }
+ else
+ {
+ // get the controller currently working on this model
+ Reference< XController > xController = xModel->getCurrentController();
+ DBG_ASSERT(xController.is(), "OControlWizard::implDeterminePage: no current controller!");
+
+ // maybe it's a spreadsheet
+ Reference< XSpreadsheetView > xView(xController, UNO_QUERY);
+ if (xView.is())
+ { // okay, it is one
+ Reference< XSpreadsheet > xSheet = xView->getActiveSheet();
+ xPageSupp.set(xSheet, UNO_QUERY);
+ DBG_ASSERT(xPageSupp.is(), "OControlWizard::implDeterminePage: a spreadsheet which is no page supplier!");
+ if (xPageSupp.is())
+ xPage = xPageSupp->getDrawPage();
+ }
+ else
+ { // can be a draw/impress doc only
+ Reference< XDrawView > xDrawView(xController, UNO_QUERY);
+ DBG_ASSERT(xDrawView.is(), "OControlWizard::implDeterminePage: no alternatives left ... can't determine the page!");
+ if (xDrawView.is())
+ xPage = xDrawView->getCurrentPage();
+ }
+ }
+ }
+ else
+ {
+ DBG_ASSERT(xPage.is(), "OControlWizard::implDeterminePage: can't determine the page (no model)!");
+ }
+ m_aContext.xDrawPage = xPage;
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OControlWizard::implDeterminePage");
+ }
+ }
+
+
+ void OControlWizard::implGetDSContext()
+ {
+ try
+ {
+ DBG_ASSERT(m_xContext.is(), "OControlWizard::implGetDSContext: invalid service factory!");
+
+ m_aContext.xDatasourceContext = DatabaseContext::create(m_xContext);
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("OControlWizard::implGetDSContext: invalid database context!");
+ }
+ }
+
+
+ Reference< XConnection > OControlWizard::getFormConnection(const OAccessRegulator&) const
+ {
+ return getFormConnection();
+ }
+
+ Reference< XConnection > OControlWizard::getFormConnection() const
+ {
+ Reference< XConnection > xConn;
+ try
+ {
+ if ( !::dbtools::isEmbeddedInDatabase(m_aContext.xForm,xConn) )
+ m_aContext.xForm->getPropertyValue("ActiveConnection") >>= xConn;
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OControlWizard::getFormConnection");
+ }
+ return xConn;
+ }
+
+
+ void OControlWizard::setFormConnection( const OAccessRegulator& _rAccess, const Reference< XConnection >& _rxConn, bool _bAutoDispose )
+ {
+ try
+ {
+ Reference< XConnection > xOldConn = getFormConnection(_rAccess);
+ if (xOldConn.get() == _rxConn.get())
+ return;
+
+ disposeComponent(xOldConn);
+
+ // set the new connection
+ if ( _bAutoDispose )
+ {
+ // for this, use an AutoDisposer (so the conn is cleaned up when the form dies or gets another connection)
+ Reference< XRowSet > xFormRowSet( m_aContext.xForm, UNO_QUERY );
+ new OAutoConnectionDisposer( xFormRowSet, _rxConn );
+ }
+ else
+ {
+ m_aContext.xForm->setPropertyValue("ActiveConnection", Any( _rxConn ) );
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::setFormConnection");
+ }
+ }
+
+
+ bool OControlWizard::updateContext(const OAccessRegulator&)
+ {
+ return initContext();
+ }
+
+ Reference< XInteractionHandler > OControlWizard::getInteractionHandler(weld::Window* _pWindow) const
+ {
+ Reference< XInteractionHandler > xHandler;
+ try
+ {
+ xHandler.set( InteractionHandler::createWithParent(m_xContext, nullptr), UNO_QUERY_THROW );
+ }
+ catch(const Exception&) { }
+ if (!xHandler.is())
+ {
+ ShowServiceNotAvailableError(_pWindow, u"com.sun.star.task.InteractionHandler", true);
+ }
+ return xHandler;
+ }
+
+ bool OControlWizard::initContext()
+ {
+ DBG_ASSERT(m_aContext.xObjectModel.is(), "OGroupBoxWizard::initContext: have no control model to work with!");
+ if (!m_aContext.xObjectModel.is())
+ return false;
+
+ // reset the context
+ m_aContext.xForm.clear();
+ m_aContext.xRowSet.clear();
+ m_aContext.xDocumentModel.clear();
+ m_aContext.xDrawPage.clear();
+ m_aContext.xObjectShape.clear();
+ m_aContext.aFieldNames.realloc(0);
+
+ m_aContext.xObjectContainer.clear();
+ m_aContext.aTypes.clear();
+ m_aContext.bEmbedded = false;
+
+ Any aSQLException;
+ Reference< XPreparedStatement > xStatement;
+ try
+ {
+ // get the datasource context
+ implGetDSContext();
+
+ // first, determine the form the control belongs to
+ implDetermineForm();
+
+ // need the page, too
+ implDeterminePage();
+
+ // the shape of the control
+ implDetermineShape();
+
+ // get the columns of the object the settings refer to
+ Reference< XNameAccess > xColumns;
+
+ if (m_aContext.xForm.is())
+ {
+ // collect some properties of the form
+ OUString sObjectName = ::comphelper::getString(m_aContext.xForm->getPropertyValue("Command"));
+ sal_Int32 nObjectType = ::comphelper::getINT32(m_aContext.xForm->getPropertyValue("CommandType"));
+
+ // calculate the connection the rowset is working with
+ Reference< XConnection > xConnection;
+ m_aContext.bEmbedded = ::dbtools::isEmbeddedInDatabase( m_aContext.xForm, xConnection );
+ if ( !m_aContext.bEmbedded )
+ xConnection = ::dbtools::connectRowset( m_aContext.xRowSet, m_xContext, nullptr );
+
+ // get the fields
+ if (xConnection.is())
+ {
+ switch (nObjectType)
+ {
+ case 0:
+ {
+ Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
+ if (xSupplyTables.is() && xSupplyTables->getTables().is() && xSupplyTables->getTables()->hasByName(sObjectName))
+ {
+ Reference< XColumnsSupplier > xSupplyColumns;
+ m_aContext.xObjectContainer = xSupplyTables->getTables();
+ m_aContext.xObjectContainer->getByName(sObjectName) >>= xSupplyColumns;
+ DBG_ASSERT(xSupplyColumns.is(), "OControlWizard::initContext: invalid table columns!");
+ xColumns = xSupplyColumns->getColumns();
+ }
+ }
+ break;
+ case 1:
+ {
+ Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY);
+ if (xSupplyQueries.is() && xSupplyQueries->getQueries().is() && xSupplyQueries->getQueries()->hasByName(sObjectName))
+ {
+ Reference< XColumnsSupplier > xSupplyColumns;
+ m_aContext.xObjectContainer = xSupplyQueries->getQueries();
+ m_aContext.xObjectContainer->getByName(sObjectName) >>= xSupplyColumns;
+ DBG_ASSERT(xSupplyColumns.is(), "OControlWizard::initContext: invalid query columns!");
+ xColumns = xSupplyColumns->getColumns();
+ }
+ }
+ break;
+ default:
+ {
+ xStatement = xConnection->prepareStatement(sObjectName);
+
+ // not interested in any results, only in the fields
+ Reference< XPropertySet > xStatementProps(xStatement, UNO_QUERY);
+ xStatementProps->setPropertyValue("MaxRows", Any(sal_Int32(0)));
+
+ // TODO: think about handling local SQLExceptions here ...
+ Reference< XColumnsSupplier > xSupplyCols(xStatement->executeQuery(), UNO_QUERY);
+ if (xSupplyCols.is())
+ xColumns = xSupplyCols->getColumns();
+ }
+ }
+ }
+ }
+
+ if (xColumns.is())
+ {
+ m_aContext.aFieldNames = xColumns->getElementNames();
+ const OUString* pBegin = m_aContext.aFieldNames.getConstArray();
+ const OUString* pEnd = pBegin + m_aContext.aFieldNames.getLength();
+ for(;pBegin != pEnd;++pBegin)
+ {
+ sal_Int32 nFieldType = DataType::OTHER;
+ try
+ {
+ Reference< XPropertySet > xColumn;
+ xColumns->getByName(*pBegin) >>= xColumn;
+ xColumn->getPropertyValue("Type") >>= nFieldType;
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "extensions.dbpilots",
+ "unexpected exception while gathering column information!");
+ }
+ m_aContext.aTypes.emplace(*pBegin,nFieldType);
+ }
+ }
+ }
+ catch(const SQLContext& e) { aSQLException <<= e; }
+ catch(const SQLWarning& e) { aSQLException <<= e; }
+ catch(const SQLException& e) { aSQLException <<= e; }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::initContext: could not retrieve the control context");
+ }
+
+ ::comphelper::disposeComponent(xStatement);
+
+ if (aSQLException.hasValue())
+ { // an SQLException (or derivee) was thrown ...
+
+ // prepend an extra SQLContext explaining what we were doing
+ SQLContext aContext;
+ aContext.Message = compmodule::ModuleRes(RID_STR_COULDNOTOPENTABLE);
+ aContext.NextException = aSQLException;
+
+ // create an interaction handler to display this exception
+ Reference< XInteractionHandler > xHandler = getInteractionHandler(m_xAssistant.get());
+ if ( !xHandler.is() )
+ return false;
+
+ Reference< XInteractionRequest > xRequest = new OInteractionRequest(Any(aContext));
+ try
+ {
+ xHandler->handle(xRequest);
+ }
+ catch(const Exception&) { }
+ return false;
+ }
+
+ return m_aContext.aFieldNames.hasElements();
+ }
+
+
+ void OControlWizard::commitControlSettings(OControlWizardSettings const * _pSettings)
+ {
+ DBG_ASSERT(m_aContext.xObjectModel.is(), "OControlWizard::commitControlSettings: have no control model to work with!");
+ if (!m_aContext.xObjectModel.is())
+ return;
+
+ // the only thing we have at the moment is the label
+ try
+ {
+ Reference< XPropertySetInfo > xInfo = m_aContext.xObjectModel->getPropertySetInfo();
+ if (xInfo.is() && xInfo->hasPropertyByName("Label"))
+ {
+ OUString sControlLabel(_pSettings->sControlLabel);
+ m_aContext.xObjectModel->setPropertyValue(
+ "Label",
+ Any(sControlLabel)
+ );
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::commitControlSettings: could not commit the basic control settings!");
+ }
+ }
+
+
+ void OControlWizard::initControlSettings(OControlWizardSettings* _pSettings)
+ {
+ DBG_ASSERT(m_aContext.xObjectModel.is(), "OControlWizard::initControlSettings: have no control model to work with!");
+ if (!m_aContext.xObjectModel.is())
+ return;
+
+ // initialize some settings from the control model give
+ try
+ {
+ OUString sLabelPropertyName("Label");
+ Reference< XPropertySetInfo > xInfo = m_aContext.xObjectModel->getPropertySetInfo();
+ if (xInfo.is() && xInfo->hasPropertyByName(sLabelPropertyName))
+ {
+ OUString sControlLabel;
+ m_aContext.xObjectModel->getPropertyValue(sLabelPropertyName) >>= sControlLabel;
+ _pSettings->sControlLabel = sControlLabel;
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::initControlSettings: could not retrieve the basic control settings!");
+ }
+ }
+
+
+ bool OControlWizard::needDatasourceSelection()
+ {
+ // lemme see ...
+ return !getContext().aFieldNames.hasElements();
+ // if we got fields, the data source is valid ...
+ }
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/controlwizard.hxx b/extensions/source/dbpilots/controlwizard.hxx
new file mode 100644
index 000000000..cf55c655e
--- /dev/null
+++ b/extensions/source/dbpilots/controlwizard.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 <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <vcl/weld.hxx>
+#include <vcl/wizardmachine.hxx>
+#include "dbptypes.hxx"
+#include <strings.hrc>
+#include <componentmodule.hxx>
+#include "wizardcontext.hxx"
+
+class ResId;
+
+namespace dbp
+{
+ struct OControlWizardSettings
+ {
+ OUString sControlLabel;
+ };
+
+ class OControlWizard;
+ typedef ::vcl::OWizardPage OControlWizardPage_Base;
+ class OControlWizardPage : public OControlWizardPage_Base
+ {
+ OControlWizard* m_pDialog;
+ std::unique_ptr<weld::Label> m_xFormDatasourceLabel;
+ std::unique_ptr<weld::Label> m_xFormDatasource;
+ std::unique_ptr<weld::Label> m_xFormContentTypeLabel;
+ std::unique_ptr<weld::Label> m_xFormContentType;
+ std::unique_ptr<weld::Label> m_xFormTableLabel;
+ std::unique_ptr<weld::Label> m_xFormTable;
+ std::unique_ptr<weld::Frame> m_xFrame;
+
+ protected:
+ OControlWizard* getDialog();
+ const OControlWizard* getDialog() const;
+ const OControlWizardContext& getContext() const;
+ bool updateContext();
+ void setFormConnection(const css::uno::Reference< css::sdbc::XConnection >& _rxConn, bool _bAutoDispose = true );
+ css::uno::Reference< css::sdbc::XConnection >
+ getFormConnection() const;
+ public:
+ OControlWizardPage(weld::Container* pPage, OControlWizard* pWizard, const OUString& rUIXMLDescription, const OString& rID);
+ virtual ~OControlWizardPage() override;
+
+ protected:
+ static void fillListBox(
+ weld::TreeView& _rList,
+ const css::uno::Sequence< OUString >& _rItems);
+ static void fillListBox(
+ weld::ComboBox& _rList,
+ const css::uno::Sequence< OUString >& _rItems);
+
+ protected:
+ void enableFormDatasourceDisplay();
+
+ protected:
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ };
+
+ struct OAccessRegulator;
+
+ typedef ::vcl::WizardMachine OControlWizard_Base;
+ class OControlWizard : public OControlWizard_Base
+ {
+ private:
+ OControlWizardContext m_aContext;
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xContext;
+
+ public:
+ OControlWizard(
+ weld::Window* _pParent,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxObjectModel,
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+ virtual ~OControlWizard() override;
+
+ // make the same base class methods public
+ using OControlWizard_Base::travelNext;
+
+ public:
+ const css::uno::Reference< css::uno::XComponentContext >&
+ getComponentContext() const { return m_xContext; }
+
+ const OControlWizardContext& getContext() const { return m_aContext; }
+ bool updateContext(const OAccessRegulator&);
+ void setFormConnection(const OAccessRegulator&, const css::uno::Reference< css::sdbc::XConnection >& _rxConn, bool _bAutoDispose );
+ css::uno::Reference< css::sdbc::XConnection >
+ getFormConnection(const OAccessRegulator&) const;
+
+ /** returns the com.sun.star.task.InteractionHandler
+ @param _pWindow The window will be used when an error message has to be shown.
+ */
+ css::uno::Reference< css::task::XInteractionHandler > getInteractionHandler(weld::Window* _pWindow) const;
+
+ protected:
+ // initialize the derivees settings (which have to be derived from OControlWizardSettings)
+ // with some common data extracted from the control model
+ void initControlSettings(OControlWizardSettings* _pSettings);
+ // commit the control-relevant settings
+ void commitControlSettings(OControlWizardSettings const * _pSettings);
+
+ bool needDatasourceSelection();
+
+ css::uno::Reference< css::sdbc::XConnection >
+ getFormConnection() const;
+
+ virtual bool approveControl(sal_Int16 _nClassId) = 0;
+
+ virtual short run() override;
+
+ private:
+ bool initContext();
+
+ void implGetDSContext();
+ void implDetermineForm();
+ void implDeterminePage();
+ void implDetermineShape();
+ };
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/dbp.component b/extensions/source/dbpilots/dbp.component
new file mode 100644
index 000000000..5cff1fb3c
--- /dev/null
+++ b/extensions/source/dbpilots/dbp.component
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="org.openoffice.comp.dbp.OGridWizard"
+ constructor="extensions_dbp_OGridWizard_get_implementation">
+ <service name="com.sun.star.sdb.GridControlAutoPilot"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.dbp.OGroupBoxWizard"
+ constructor="extensions_dbp_OGroupBoxWizard_get_implementation">
+ <service name="com.sun.star.sdb.GroupBoxAutoPilot"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.dbp.OListComboWizard"
+ constructor="extensions_dbp_OListComboWizard_get_implementation">
+ <service name="com.sun.star.sdb.ListComboBoxAutoPilot"/>
+ </implementation>
+</component>
diff --git a/extensions/source/dbpilots/dbptools.cxx b/extensions/source/dbpilots/dbptools.cxx
new file mode 100644
index 000000000..1b647e84c
--- /dev/null
+++ b/extensions/source/dbpilots/dbptools.cxx
@@ -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 .
+ */
+
+#include "dbptools.hxx"
+#include <tools/debug.hxx>
+#include <osl/diagnose.h>
+
+
+namespace dbp
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::container;
+
+
+ void disambiguateName(const Reference< XNameAccess >& _rxContainer, OUString& _rElementsName)
+ {
+ DBG_ASSERT(_rxContainer.is(), "::dbp::disambiguateName: invalid container!");
+ if (!_rxContainer.is())
+ return;
+
+ try
+ {
+ OUString sBase(_rElementsName);
+ for (sal_Int32 i=1; i<0x7FFFFFFF; ++i)
+ {
+ _rElementsName = sBase;
+ _rElementsName += OUString::number(i);
+ if (!_rxContainer->hasByName(_rElementsName))
+ return;
+ }
+ // can't do anything ... no free names
+ _rElementsName = sBase;
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("::dbp::disambiguateName: something went (strangely) wrong!");
+ }
+ }
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/dbptools.hxx b/extensions/source/dbpilots/dbptools.hxx
new file mode 100644
index 000000000..d7577f187
--- /dev/null
+++ b/extensions/source/dbpilots/dbptools.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/container/XNameAccess.hpp>
+
+
+namespace dbp
+{
+
+
+ void disambiguateName(
+ const css::uno::Reference< css::container::XNameAccess >& _rxContainer,
+ OUString& _rElementsName);
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/dbptypes.hxx b/extensions/source/dbpilots/dbptypes.hxx
new file mode 100644
index 000000000..8b86fe506
--- /dev/null
+++ b/extensions/source/dbpilots/dbptypes.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+
+#include <map>
+#include <set>
+
+namespace dbp
+{
+typedef std::set<OUString> StringBag;
+typedef std::map<sal_uInt32, OUString> MapInt2String;
+
+} // namespace dbp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/gridwizard.cxx b/extensions/source/dbpilots/gridwizard.cxx
new file mode 100644
index 000000000..3cf1d0324
--- /dev/null
+++ b/extensions/source/dbpilots/gridwizard.cxx
@@ -0,0 +1,446 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include "gridwizard.hxx"
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/awt/MouseWheelBehavior.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include "dbptools.hxx"
+#include <helpids.h>
+
+#define GW_STATE_DATASOURCE_SELECTION 0
+#define GW_STATE_FIELDSELECTION 1
+
+
+namespace dbp
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+
+ OGridWizard::OGridWizard(weld::Window* _pParent,
+ const Reference< XPropertySet >& _rxObjectModel, const Reference< XComponentContext >& _rxContext )
+ : OControlWizard(_pParent, _rxObjectModel, _rxContext)
+ , m_bHadDataSelection(true)
+ {
+ initControlSettings(&m_aSettings);
+
+ m_xPrevPage->set_help_id(HID_GRIDWIZARD_PREVIOUS);
+ m_xNextPage->set_help_id(HID_GRIDWIZARD_NEXT);
+ m_xCancel->set_help_id(HID_GRIDWIZARD_CANCEL);
+ m_xFinish->set_help_id(HID_GRIDWIZARD_FINISH);
+ setTitleBase(compmodule::ModuleRes(RID_STR_GRIDWIZARD_TITLE));
+
+ // if we do not need the data source selection page ...
+ if (!needDatasourceSelection())
+ { // ... skip it!
+ skip();
+ m_bHadDataSelection = false;
+ }
+ }
+
+ bool OGridWizard::approveControl(sal_Int16 _nClassId)
+ {
+ if (FormComponentType::GRIDCONTROL != _nClassId)
+ return false;
+
+ Reference< XGridColumnFactory > xColumnFactory(getContext().xObjectModel, UNO_QUERY);
+ return xColumnFactory.is();
+ }
+
+ void OGridWizard::implApplySettings()
+ {
+ const OControlWizardContext& rContext = getContext();
+
+ // the factory for the columns
+ Reference< XGridColumnFactory > xColumnFactory(rContext.xObjectModel, UNO_QUERY);
+ DBG_ASSERT(xColumnFactory.is(), "OGridWizard::implApplySettings: should never have made it 'til here!");
+ // (if we're here, what the hell happened in approveControl??)
+
+ // the container for the columns
+ Reference< XNameContainer > xColumnContainer(rContext.xObjectModel, UNO_QUERY);
+ DBG_ASSERT(xColumnContainer.is(), "OGridWizard::implApplySettings: no container!");
+
+ if (!xColumnFactory.is() || !xColumnContainer.is())
+ return;
+
+ static constexpr OUStringLiteral s_sMouseWheelBehavior = u"MouseWheelBehavior";
+ static constexpr OUStringLiteral s_sEmptyString = u"";
+
+ // collect "descriptors" for the to-be-created (grid)columns
+ std::vector< OUString > aColumnServiceNames; // service names to be used with the XGridColumnFactory
+ std::vector< OUString > aColumnLabelPostfixes; // postfixes to append to the column labels
+ std::vector< OUString > aFormFieldNames; // data field names
+
+ aColumnServiceNames.reserve(getSettings().aSelectedFields.getLength());
+ aColumnLabelPostfixes.reserve(getSettings().aSelectedFields.getLength());
+ aFormFieldNames.reserve(getSettings().aSelectedFields.getLength());
+
+ // loop through the selected field names
+ const OUString* pSelectedFields = getSettings().aSelectedFields.getConstArray();
+ const OUString* pEnd = pSelectedFields + getSettings().aSelectedFields.getLength();
+ for (;pSelectedFields < pEnd; ++pSelectedFields)
+ {
+ // get the information for the selected column
+ sal_Int32 nFieldType = DataType::OTHER;
+ OControlWizardContext::TNameTypeMap::const_iterator aFind = rContext.aTypes.find(*pSelectedFields);
+ if ( aFind != rContext.aTypes.end() )
+ nFieldType = aFind->second;
+
+ aFormFieldNames.push_back(*pSelectedFields);
+ switch (nFieldType)
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ aColumnServiceNames.push_back(OUString("CheckBox"));
+ aColumnLabelPostfixes.push_back(s_sEmptyString);
+ break;
+
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ aColumnServiceNames.push_back(OUString("NumericField"));
+ aColumnLabelPostfixes.push_back(s_sEmptyString);
+ break;
+
+ case DataType::FLOAT:
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ aColumnServiceNames.push_back(OUString("FormattedField"));
+ aColumnLabelPostfixes.push_back(s_sEmptyString);
+ break;
+
+ case DataType::DATE:
+ aColumnServiceNames.push_back(OUString("DateField"));
+ aColumnLabelPostfixes.push_back(s_sEmptyString);
+ break;
+
+ case DataType::TIME:
+ aColumnServiceNames.push_back(OUString("TimeField"));
+ aColumnLabelPostfixes.push_back(s_sEmptyString);
+ break;
+
+ case DataType::TIMESTAMP:
+ aColumnServiceNames.push_back(OUString("DateField"));
+ aColumnLabelPostfixes.push_back(compmodule::ModuleRes(RID_STR_DATEPOSTFIX));
+
+ aFormFieldNames.push_back(*pSelectedFields);
+ aColumnServiceNames.push_back(OUString("TimeField"));
+ aColumnLabelPostfixes.push_back(compmodule::ModuleRes(RID_STR_TIMEPOSTFIX));
+ break;
+
+ default:
+ aColumnServiceNames.push_back(OUString("TextField"));
+ aColumnLabelPostfixes.push_back(s_sEmptyString);
+ }
+ }
+
+ DBG_ASSERT( aFormFieldNames.size() == aColumnServiceNames.size()
+ && aColumnServiceNames.size() == aColumnLabelPostfixes.size(),
+ "OGridWizard::implApplySettings: inconsistent descriptor sequences!");
+
+ // now loop through the descriptions and create the (grid)columns out of th descriptors
+ {
+ Reference< XNameAccess > xExistenceChecker(xColumnContainer);
+
+ std::vector< OUString >::const_iterator pColumnLabelPostfix = aColumnLabelPostfixes.begin();
+ std::vector< OUString >::const_iterator pFormFieldName = aFormFieldNames.begin();
+
+ for (const auto& rColumnServiceName : aColumnServiceNames)
+ {
+ // create a (grid)column for the (resultset)column
+ try
+ {
+ Reference< XPropertySet > xColumn( xColumnFactory->createColumn(rColumnServiceName), UNO_SET_THROW );
+ Reference< XPropertySetInfo > xColumnPSI( xColumn->getPropertySetInfo(), UNO_SET_THROW );
+
+ OUString sColumnName(rColumnServiceName);
+ disambiguateName(xExistenceChecker, sColumnName);
+
+ // the data field the column should be bound to
+ xColumn->setPropertyValue("DataField", Any(*pFormFieldName));
+ // the label
+ xColumn->setPropertyValue("Label", Any(*pFormFieldName + *pColumnLabelPostfix));
+ // the width (<void/> => column will be auto-sized)
+ xColumn->setPropertyValue("Width", Any());
+
+ if ( xColumnPSI->hasPropertyByName( s_sMouseWheelBehavior ) )
+ xColumn->setPropertyValue( s_sMouseWheelBehavior, Any( MouseWheelBehavior::SCROLL_DISABLED ) );
+
+ // insert the column
+ xColumnContainer->insertByName(sColumnName, Any(xColumn));
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN( "extensions.dbpilots", "OGridWizard::implApplySettings: "
+ "unexpected exception while creating the grid column for field " <<
+ *pFormFieldName );
+ }
+
+ ++pColumnLabelPostfix;
+ ++pFormFieldName;
+ }
+ }
+ }
+
+ std::unique_ptr<BuilderPage> OGridWizard::createPage(WizardState _nState)
+ {
+ OString sIdent(OString::number(_nState));
+ weld::Container* pPageContainer = m_xAssistant->append_page(sIdent);
+
+ switch (_nState)
+ {
+ case GW_STATE_DATASOURCE_SELECTION:
+ return std::make_unique<OTableSelectionPage>(pPageContainer, this);
+ case GW_STATE_FIELDSELECTION:
+ return std::make_unique<OGridFieldsSelection>(pPageContainer, this);
+ }
+
+ return nullptr;
+ }
+
+ vcl::WizardTypes::WizardState OGridWizard::determineNextState( WizardState _nCurrentState ) const
+ {
+ switch (_nCurrentState)
+ {
+ case GW_STATE_DATASOURCE_SELECTION:
+ return GW_STATE_FIELDSELECTION;
+ case GW_STATE_FIELDSELECTION:
+ return WZS_INVALID_STATE;
+ }
+
+ return WZS_INVALID_STATE;
+ }
+
+ void OGridWizard::enterState(WizardState _nState)
+ {
+ OControlWizard::enterState(_nState);
+
+ enableButtons(WizardButtonFlags::PREVIOUS, m_bHadDataSelection ? (GW_STATE_DATASOURCE_SELECTION < _nState) : GW_STATE_FIELDSELECTION < _nState);
+ enableButtons(WizardButtonFlags::NEXT, GW_STATE_FIELDSELECTION != _nState);
+ if (_nState < GW_STATE_FIELDSELECTION)
+ enableButtons(WizardButtonFlags::FINISH, false);
+
+ if (GW_STATE_FIELDSELECTION == _nState)
+ defaultButton(WizardButtonFlags::FINISH);
+ }
+
+
+ bool OGridWizard::leaveState(WizardState _nState)
+ {
+ if (!OControlWizard::leaveState(_nState))
+ return false;
+
+ if (GW_STATE_FIELDSELECTION == _nState)
+ defaultButton(WizardButtonFlags::NEXT);
+
+ return true;
+ }
+
+
+ bool OGridWizard::onFinish()
+ {
+ if ( !OControlWizard::onFinish() )
+ return false;
+
+ implApplySettings();
+
+ return true;
+ }
+
+ OGridFieldsSelection::OGridFieldsSelection(weld::Container* pPage, OGridWizard* pWizard)
+ : OGridPage(pPage, pWizard, "modules/sabpilot/ui/gridfieldsselectionpage.ui", "GridFieldsSelection")
+ , m_xExistFields(m_xBuilder->weld_tree_view("existingfields"))
+ , m_xSelectOne(m_xBuilder->weld_button("fieldright"))
+ , m_xSelectAll(m_xBuilder->weld_button("allfieldsright"))
+ , m_xDeselectOne(m_xBuilder->weld_button("fieldleft"))
+ , m_xDeselectAll(m_xBuilder->weld_button("allfieldsleft"))
+ , m_xSelFields(m_xBuilder->weld_tree_view("selectedfields"))
+ {
+ enableFormDatasourceDisplay();
+
+ m_xSelectOne->connect_clicked(LINK(this, OGridFieldsSelection, OnMoveOneEntry));
+ m_xSelectAll->connect_clicked(LINK(this, OGridFieldsSelection, OnMoveAllEntries));
+ m_xDeselectOne->connect_clicked(LINK(this, OGridFieldsSelection, OnMoveOneEntry));
+ m_xDeselectAll->connect_clicked(LINK(this, OGridFieldsSelection, OnMoveAllEntries));
+
+ m_xExistFields->connect_changed(LINK(this, OGridFieldsSelection, OnEntrySelected));
+ m_xSelFields->connect_changed(LINK(this, OGridFieldsSelection, OnEntrySelected));
+ m_xExistFields->connect_row_activated(LINK(this, OGridFieldsSelection, OnEntryDoubleClicked));
+ m_xSelFields->connect_row_activated(LINK(this, OGridFieldsSelection, OnEntryDoubleClicked));
+ }
+
+ OGridFieldsSelection::~OGridFieldsSelection()
+ {
+ }
+
+ void OGridFieldsSelection::Activate()
+ {
+ OGridPage::Activate();
+ m_xExistFields->grab_focus();
+ }
+
+ bool OGridFieldsSelection::canAdvance() const
+ {
+ return false;
+ // we're the last page in our wizard
+ }
+
+ void OGridFieldsSelection::initializePage()
+ {
+ OGridPage::initializePage();
+
+ const OControlWizardContext& rContext = getContext();
+ fillListBox(*m_xExistFields, rContext.aFieldNames);
+
+ m_xSelFields->clear();
+ const OGridSettings& rSettings = getSettings();
+ const OUString* pSelected = rSettings.aSelectedFields.getConstArray();
+ const OUString* pEnd = pSelected + rSettings.aSelectedFields.getLength();
+ for (; pSelected < pEnd; ++pSelected)
+ {
+ m_xSelFields->append_text(*pSelected);
+ m_xExistFields->remove_text(*pSelected);
+ }
+
+ implCheckButtons();
+ }
+
+ bool OGridFieldsSelection::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OGridPage::commitPage(_eReason))
+ return false;
+
+ OGridSettings& rSettings = getSettings();
+ const sal_Int32 nSelected = m_xSelFields->n_children();
+
+ rSettings.aSelectedFields.realloc(nSelected);
+ OUString* pSelected = rSettings.aSelectedFields.getArray();
+
+ for (sal_Int32 i=0; i<nSelected; ++i, ++pSelected)
+ *pSelected = m_xSelFields->get_text(i);
+
+ return true;
+ }
+
+ void OGridFieldsSelection::implCheckButtons()
+ {
+ m_xSelectOne->set_sensitive(m_xExistFields->count_selected_rows() != 0);
+ m_xSelectAll->set_sensitive(m_xExistFields->n_children() != 0);
+
+ m_xDeselectOne->set_sensitive(m_xSelFields->count_selected_rows() != 0);
+ m_xDeselectAll->set_sensitive(m_xSelFields->n_children() != 0);
+
+ getDialog()->enableButtons(WizardButtonFlags::FINISH, 0 != m_xSelFields->n_children());
+ }
+
+ IMPL_LINK(OGridFieldsSelection, OnEntryDoubleClicked, weld::TreeView&, rList, bool)
+ {
+ weld::Button* pSimulateButton = m_xExistFields.get() == &rList ? m_xSelectOne.get() : m_xDeselectOne.get();
+ if (pSimulateButton->get_sensitive())
+ OnMoveOneEntry(*pSimulateButton);
+ return true;
+ }
+
+ IMPL_LINK_NOARG(OGridFieldsSelection, OnEntrySelected, weld::TreeView&, void)
+ {
+ implCheckButtons();
+ }
+
+ IMPL_LINK(OGridFieldsSelection, OnMoveOneEntry, weld::Button&, rButton, void)
+ {
+ bool bMoveRight = (m_xSelectOne.get() == &rButton);
+ weld::TreeView& rMoveTo = bMoveRight ? *m_xSelFields : *m_xExistFields;
+
+ // the index of the selected entry
+ const sal_Int32 nSelected = bMoveRight ? m_xExistFields->get_selected_index() : m_xSelFields->get_selected_index();
+ // the (original) relative position of the entry
+ int nRelativeIndex = bMoveRight ? m_xExistFields->get_id(nSelected).toInt32() : m_xSelFields->get_id(nSelected).toInt32();
+
+ sal_Int32 nInsertPos = -1;
+ if (!bMoveRight)
+ { // need to determine an insert pos which reflects the original
+ nInsertPos = 0;
+ while (nInsertPos < rMoveTo.n_children())
+ {
+ if (rMoveTo.get_id(nInsertPos).toInt32() > nRelativeIndex)
+ break;
+ ++nInsertPos;
+ }
+ }
+
+ // the text of the entry to move
+ OUString sMovingEntry = bMoveRight ? m_xExistFields->get_text(nSelected) : m_xSelFields->get_text(nSelected);
+
+ // insert the entry preserving it's "relative position" entry data
+ OUString sId(OUString::number(nRelativeIndex));
+ rMoveTo.insert(nullptr, nInsertPos, &sMovingEntry, &sId, nullptr, nullptr, false, nullptr);
+
+ // remove the entry from its old list
+ if (bMoveRight)
+ {
+ sal_Int32 nSelectPos = m_xExistFields->get_selected_index();
+ m_xExistFields->remove(nSelected);
+ if ((nSelectPos != -1) && (nSelectPos < m_xExistFields->n_children()))
+ m_xExistFields->select(nSelectPos);
+
+ m_xExistFields->grab_focus();
+ }
+ else
+ {
+ sal_Int32 nSelectPos = m_xSelFields->get_selected_index();
+ m_xSelFields->remove(nSelected);
+ if ((nSelectPos != -1) && (nSelectPos < m_xSelFields->n_children()))
+ m_xSelFields->select(nSelectPos);
+
+ m_xSelFields->grab_focus();
+ }
+
+ implCheckButtons();
+ }
+
+ IMPL_LINK(OGridFieldsSelection, OnMoveAllEntries, weld::Button&, rButton, void)
+ {
+ bool bMoveRight = (m_xSelectAll.get() == &rButton);
+ m_xExistFields->clear();
+ m_xSelFields->clear();
+ fillListBox(bMoveRight ? *m_xSelFields : *m_xExistFields, getContext().aFieldNames);
+
+ implCheckButtons();
+ }
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/gridwizard.hxx b/extensions/source/dbpilots/gridwizard.hxx
new file mode 100644
index 000000000..774e6cd69
--- /dev/null
+++ b/extensions/source/dbpilots/gridwizard.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 "controlwizard.hxx"
+#include "commonpagesdbp.hxx"
+
+using vcl::WizardTypes::WizardState;
+using vcl::WizardTypes::CommitPageReason;
+
+namespace dbp
+{
+ struct OGridSettings : public OControlWizardSettings
+ {
+ css::uno::Sequence< OUString > aSelectedFields;
+ };
+
+ class OGridWizard final : public OControlWizard
+ {
+ OGridSettings m_aSettings;
+ bool m_bHadDataSelection : 1;
+
+ public:
+ OGridWizard(weld::Window* _pParent,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxObjectModel,
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext);
+
+ OGridSettings& getSettings() { return m_aSettings; }
+
+ private:
+ // OWizardMachine overridables
+ virtual std::unique_ptr<BuilderPage> createPage( WizardState _nState ) override;
+ virtual WizardState determineNextState( WizardState _nCurrentState ) const override;
+ virtual void enterState( WizardState _nState ) override;
+ virtual bool leaveState( WizardState _nState ) override;
+ virtual bool onFinish() override;
+
+ virtual bool approveControl(sal_Int16 _nClassId) override;
+
+ void implApplySettings();
+ };
+
+ class OGridPage : public OControlWizardPage
+ {
+ public:
+ OGridPage(weld::Container* pPage, OGridWizard* pWizard, const OUString& rUIXMLDescription, const OString& rID)
+ : OControlWizardPage(pPage, pWizard, rUIXMLDescription, rID)
+ {
+ }
+ protected:
+ OGridSettings& getSettings() { return static_cast<OGridWizard*>(getDialog())->getSettings(); }
+ };
+
+ class OGridFieldsSelection final : public OGridPage
+ {
+ std::unique_ptr<weld::TreeView> m_xExistFields;
+ std::unique_ptr<weld::Button> m_xSelectOne;
+ std::unique_ptr<weld::Button> m_xSelectAll;
+ std::unique_ptr<weld::Button> m_xDeselectOne;
+ std::unique_ptr<weld::Button> m_xDeselectAll;
+ std::unique_ptr<weld::TreeView> m_xSelFields;
+
+ public:
+ explicit OGridFieldsSelection(weld::Container* pPage, OGridWizard* pWizard);
+ virtual ~OGridFieldsSelection() override;
+
+ private:
+ // BuilderPage overridables
+ virtual void Activate() override;
+
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+ virtual bool canAdvance() const override;
+
+ DECL_LINK(OnMoveOneEntry, weld::Button&, void);
+ DECL_LINK(OnMoveAllEntries, weld::Button&, void);
+ DECL_LINK(OnEntrySelected, weld::TreeView&, void);
+ DECL_LINK(OnEntryDoubleClicked, weld::TreeView&, bool);
+
+ void implCheckButtons();
+ };
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/groupboxwiz.cxx b/extensions/source/dbpilots/groupboxwiz.cxx
new file mode 100644
index 000000000..a093d9154
--- /dev/null
+++ b/extensions/source/dbpilots/groupboxwiz.cxx
@@ -0,0 +1,468 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "groupboxwiz.hxx"
+#include "commonpagesdbp.hxx"
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include "optiongrouplayouter.hxx"
+#include <helpids.h>
+#include <o3tl/safeint.hxx>
+
+#define GBW_STATE_OPTIONLIST 0
+#define GBW_STATE_DEFAULTOPTION 1
+#define GBW_STATE_OPTIONVALUES 2
+#define GBW_STATE_DBFIELD 3
+#define GBW_STATE_FINALIZE 4
+
+namespace dbp
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+
+ OGroupBoxWizard::OGroupBoxWizard(weld::Window* _pParent,
+ const Reference< XPropertySet >& _rxObjectModel, const Reference< XComponentContext >& _rxContext )
+ : OControlWizard(_pParent, _rxObjectModel, _rxContext)
+ , m_bVisitedDefault(false)
+ , m_bVisitedDB(false)
+ {
+ initControlSettings(&m_aSettings);
+
+ m_xPrevPage->set_help_id(HID_GROUPWIZARD_PREVIOUS);
+ m_xNextPage->set_help_id(HID_GROUPWIZARD_NEXT);
+ m_xCancel->set_help_id(HID_GROUPWIZARD_CANCEL);
+ m_xFinish->set_help_id(HID_GROUPWIZARD_FINISH);
+ setTitleBase(compmodule::ModuleRes(RID_STR_GROUPWIZARD_TITLE));
+ }
+
+ bool OGroupBoxWizard::approveControl(sal_Int16 _nClassId)
+ {
+ return FormComponentType::GROUPBOX == _nClassId;
+ }
+
+ std::unique_ptr<BuilderPage> OGroupBoxWizard::createPage(::vcl::WizardTypes::WizardState _nState)
+ {
+ OString sIdent(OString::number(_nState));
+ weld::Container* pPageContainer = m_xAssistant->append_page(sIdent);
+
+ switch (_nState)
+ {
+ case GBW_STATE_OPTIONLIST:
+ return std::make_unique<ORadioSelectionPage>(pPageContainer, this);
+
+ case GBW_STATE_DEFAULTOPTION:
+ return std::make_unique<ODefaultFieldSelectionPage>(pPageContainer, this);
+
+ case GBW_STATE_OPTIONVALUES:
+ return std::make_unique<OOptionValuesPage>(pPageContainer, this);
+
+ case GBW_STATE_DBFIELD:
+ return std::make_unique<OOptionDBFieldPage>(pPageContainer, this);
+
+ case GBW_STATE_FINALIZE:
+ return std::make_unique<OFinalizeGBWPage>(pPageContainer, this);
+ }
+
+ return nullptr;
+ }
+
+ vcl::WizardTypes::WizardState OGroupBoxWizard::determineNextState( ::vcl::WizardTypes::WizardState _nCurrentState ) const
+ {
+ switch (_nCurrentState)
+ {
+ case GBW_STATE_OPTIONLIST:
+ return GBW_STATE_DEFAULTOPTION;
+
+ case GBW_STATE_DEFAULTOPTION:
+ return GBW_STATE_OPTIONVALUES;
+
+ case GBW_STATE_OPTIONVALUES:
+ if (getContext().aFieldNames.hasElements())
+ return GBW_STATE_DBFIELD;
+ else
+ return GBW_STATE_FINALIZE;
+
+ case GBW_STATE_DBFIELD:
+ return GBW_STATE_FINALIZE;
+ }
+
+ return WZS_INVALID_STATE;
+ }
+
+ void OGroupBoxWizard::enterState(::vcl::WizardTypes::WizardState _nState)
+ {
+ // some stuff to do before calling the base class (modifying our settings)
+ switch (_nState)
+ {
+ case GBW_STATE_DEFAULTOPTION:
+ if (!m_bVisitedDefault)
+ { // assume that the first of the radio buttons should be selected
+ DBG_ASSERT(m_aSettings.aLabels.size(), "OGroupBoxWizard::enterState: should never have reached this state!");
+ m_aSettings.sDefaultField = m_aSettings.aLabels[0];
+ }
+ m_bVisitedDefault = true;
+ break;
+
+ case GBW_STATE_DBFIELD:
+ if (!m_bVisitedDB)
+ { // try to generate a default for the DB field
+ // (simply use the first field in the DB names collection)
+ if (getContext().aFieldNames.hasElements())
+ m_aSettings.sDBField = getContext().aFieldNames[0];
+ }
+ m_bVisitedDB = true;
+ break;
+ }
+
+ // setting the def button... to be done before the base class is called, too, 'cause the base class
+ // calls the pages, which are allowed to override our def button behaviour
+ defaultButton(GBW_STATE_FINALIZE == _nState ? WizardButtonFlags::FINISH : WizardButtonFlags::NEXT);
+
+ // allow "finish" on the last page only
+ enableButtons(WizardButtonFlags::FINISH, GBW_STATE_FINALIZE == _nState);
+ // allow previous on all pages but the first one
+ enableButtons(WizardButtonFlags::PREVIOUS, GBW_STATE_OPTIONLIST != _nState);
+ // allow next on all pages but the last one
+ enableButtons(WizardButtonFlags::NEXT, GBW_STATE_FINALIZE != _nState);
+
+ OControlWizard::enterState(_nState);
+ }
+
+ bool OGroupBoxWizard::onFinish()
+ {
+ // commit the basic control settings
+ commitControlSettings(&m_aSettings);
+
+ // create the radio buttons
+ try
+ {
+ OOptionGroupLayouter aLayouter( getComponentContext() );
+ aLayouter.doLayout(getContext(), getSettings());
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.dbpilots",
+ "caught an exception while creating the radio shapes!");
+ }
+
+ return OControlWizard::onFinish();
+ }
+
+ ORadioSelectionPage::ORadioSelectionPage(weld::Container* pPage, OControlWizard* pWizard)
+ : OGBWPage(pPage, pWizard, "modules/sabpilot/ui/groupradioselectionpage.ui", "GroupRadioSelectionPage")
+ , m_xRadioName(m_xBuilder->weld_entry("radiolabels"))
+ , m_xMoveRight(m_xBuilder->weld_button("toright"))
+ , m_xMoveLeft(m_xBuilder->weld_button("toleft"))
+ , m_xExistingRadios(m_xBuilder->weld_tree_view("radiobuttons"))
+ {
+ if (getContext().aFieldNames.hasElements())
+ {
+ enableFormDatasourceDisplay();
+ }
+
+ m_xMoveLeft->connect_clicked(LINK(this, ORadioSelectionPage, OnMoveEntry));
+ m_xMoveRight->connect_clicked(LINK(this, ORadioSelectionPage, OnMoveEntry));
+ m_xRadioName->connect_changed(LINK(this, ORadioSelectionPage, OnNameModified));
+ m_xExistingRadios->connect_changed(LINK(this, ORadioSelectionPage, OnEntrySelected));
+
+ implCheckMoveButtons();
+ m_xExistingRadios->set_selection_mode(SelectionMode::Multiple);
+
+ getDialog()->defaultButton(m_xMoveRight.get());
+ }
+
+ ORadioSelectionPage::~ORadioSelectionPage()
+ {
+ }
+
+ void ORadioSelectionPage::Activate()
+ {
+ OGBWPage::Activate();
+ m_xRadioName->grab_focus();
+ }
+
+ void ORadioSelectionPage::initializePage()
+ {
+ OGBWPage::initializePage();
+
+ m_xRadioName->set_text("");
+
+ // no need to initialize the list of radios here
+ // (we're the only one affecting this special setting, so it will be in the same state as last time this
+ // page was committed)
+
+ implCheckMoveButtons();
+ }
+
+ bool ORadioSelectionPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OGBWPage::commitPage(_eReason))
+ return false;
+
+ // copy the names of the radio buttons to be inserted
+ // and initialize the values
+ OOptionGroupSettings& rSettings = getSettings();
+ rSettings.aLabels.clear();
+ rSettings.aValues.clear();
+ rSettings.aLabels.reserve(m_xExistingRadios->n_children());
+ rSettings.aValues.reserve(m_xExistingRadios->n_children());
+ for (sal_Int32 i=0; i<m_xExistingRadios->n_children(); ++i)
+ {
+ rSettings.aLabels.push_back(m_xExistingRadios->get_text(i));
+ rSettings.aValues.push_back(OUString::number(i + 1));
+ }
+
+ return true;
+ }
+
+ IMPL_LINK( ORadioSelectionPage, OnMoveEntry, weld::Button&, rButton, void )
+ {
+ bool bMoveLeft = (m_xMoveLeft.get() == &rButton);
+ if (bMoveLeft)
+ {
+ while (m_xExistingRadios->count_selected_rows())
+ m_xExistingRadios->remove(m_xExistingRadios->get_selected_index());
+ }
+ else
+ {
+ m_xExistingRadios->append_text(m_xRadioName->get_text());
+ m_xRadioName->set_text("");
+ }
+
+ implCheckMoveButtons();
+
+ //adjust the focus
+ if (bMoveLeft)
+ m_xExistingRadios->grab_focus();
+ else
+ m_xRadioName->grab_focus();
+ }
+
+ IMPL_LINK_NOARG( ORadioSelectionPage, OnEntrySelected, weld::TreeView&, void )
+ {
+ implCheckMoveButtons();
+ }
+
+ IMPL_LINK_NOARG( ORadioSelectionPage, OnNameModified, weld::Entry&, void )
+ {
+ implCheckMoveButtons();
+ }
+
+ bool ORadioSelectionPage::canAdvance() const
+ {
+ return 0 != m_xExistingRadios->n_children();
+ }
+
+ void ORadioSelectionPage::implCheckMoveButtons()
+ {
+ bool bHaveSome = (0 != m_xExistingRadios->n_children());
+ bool bSelectedSome = (0 != m_xExistingRadios->count_selected_rows());
+ bool bUnfinishedInput = !m_xRadioName->get_text().isEmpty();
+
+ m_xMoveLeft->set_sensitive(bSelectedSome);
+ m_xMoveRight->set_sensitive(bUnfinishedInput);
+
+ OControlWizard* pDialogController = getDialog();
+
+ pDialogController->enableButtons(WizardButtonFlags::NEXT, bHaveSome);
+
+ weld::Dialog* pDialog = pDialogController->getDialog();
+
+ if (bUnfinishedInput)
+ {
+ if (!pDialog->is_default_widget(m_xMoveRight.get()))
+ pDialogController->defaultButton(m_xMoveRight.get());
+ }
+ else
+ {
+ if (pDialog->is_default_widget(m_xMoveRight.get()))
+ pDialogController->defaultButton(WizardButtonFlags::NEXT);
+ }
+ }
+
+ ODefaultFieldSelectionPage::ODefaultFieldSelectionPage(weld::Container* pPage, OControlWizard* pWizard)
+ : OMaybeListSelectionPage(pPage, pWizard, "modules/sabpilot/ui/defaultfieldselectionpage.ui", "DefaultFieldSelectionPage")
+ , m_xDefSelYes(m_xBuilder->weld_radio_button("defaultselectionyes"))
+ , m_xDefSelNo(m_xBuilder->weld_radio_button("defaultselectionno"))
+ , m_xDefSelection(m_xBuilder->weld_combo_box("defselectionfield"))
+ {
+ announceControls(*m_xDefSelYes, *m_xDefSelNo, *m_xDefSelection);
+ }
+
+ ODefaultFieldSelectionPage::~ODefaultFieldSelectionPage()
+ {
+ }
+
+ void ODefaultFieldSelectionPage::initializePage()
+ {
+ OMaybeListSelectionPage::initializePage();
+
+ const OOptionGroupSettings& rSettings = getSettings();
+
+ // fill the listbox
+ m_xDefSelection->clear();
+ for (auto const& label : rSettings.aLabels)
+ m_xDefSelection->append_text(label);
+
+ implInitialize(rSettings.sDefaultField);
+ }
+
+ bool ODefaultFieldSelectionPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OMaybeListSelectionPage::commitPage(_eReason))
+ return false;
+
+ OOptionGroupSettings& rSettings = getSettings();
+ implCommit(rSettings.sDefaultField);
+
+ return true;
+ }
+
+ OOptionValuesPage::OOptionValuesPage(weld::Container* pPage, OControlWizard* pWizard)
+ : OGBWPage(pPage, pWizard, "modules/sabpilot/ui/optionvaluespage.ui", "OptionValuesPage")
+ , m_xValue(m_xBuilder->weld_entry("optionvalue"))
+ , m_xOptions(m_xBuilder->weld_tree_view("radiobuttons"))
+ , m_nLastSelection(::vcl::WizardTypes::WizardState(-1))
+ {
+ m_xOptions->connect_changed(LINK(this, OOptionValuesPage, OnOptionSelected));
+ }
+
+ OOptionValuesPage::~OOptionValuesPage()
+ {
+ }
+
+ IMPL_LINK_NOARG( OOptionValuesPage, OnOptionSelected, weld::TreeView&, void )
+ {
+ implTraveledOptions();
+ }
+
+ void OOptionValuesPage::Activate()
+ {
+ OGBWPage::Activate();
+ m_xValue->grab_focus();
+ }
+
+ void OOptionValuesPage::implTraveledOptions()
+ {
+ if (::vcl::WizardTypes::WizardState(-1) != m_nLastSelection)
+ {
+ // save the value for the last option
+ DBG_ASSERT(o3tl::make_unsigned(m_nLastSelection) < m_aUncommittedValues.size(), "OOptionValuesPage::implTraveledOptions: invalid previous selection index!");
+ m_aUncommittedValues[m_nLastSelection] = m_xValue->get_text();
+ }
+
+ m_nLastSelection = m_xOptions->get_selected_index();
+ DBG_ASSERT(o3tl::make_unsigned(m_nLastSelection) < m_aUncommittedValues.size(), "OOptionValuesPage::implTraveledOptions: invalid new selection index!");
+ m_xValue->set_text(m_aUncommittedValues[m_nLastSelection]);
+ }
+
+ void OOptionValuesPage::initializePage()
+ {
+ OGBWPage::initializePage();
+
+ const OOptionGroupSettings& rSettings = getSettings();
+ DBG_ASSERT(rSettings.aLabels.size(), "OOptionValuesPage::initializePage: no options!!");
+ DBG_ASSERT(rSettings.aLabels.size() == rSettings.aValues.size(), "OOptionValuesPage::initializePage: inconsistent data!");
+
+ // fill the list with all available options
+ m_xOptions->clear();
+ m_nLastSelection = -1;
+ for (auto const& label : rSettings.aLabels)
+ m_xOptions->append_text(label);
+
+ // remember the values ... can't set them directly in the settings without the explicit commit call
+ // so we need have a copy of the values
+ m_aUncommittedValues = rSettings.aValues;
+
+ // select the first entry
+ m_xOptions->select(0);
+ implTraveledOptions();
+ }
+
+ bool OOptionValuesPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OGBWPage::commitPage(_eReason))
+ return false;
+
+ OOptionGroupSettings& rSettings = getSettings();
+
+ // commit the current value
+ implTraveledOptions();
+ // copy the uncommitted values
+ rSettings.aValues = m_aUncommittedValues;
+
+ return true;
+ }
+
+ OOptionDBFieldPage::OOptionDBFieldPage(weld::Container* pPage, OControlWizard* pWizard)
+ : ODBFieldPage(pPage, pWizard)
+ {
+ setDescriptionText(compmodule::ModuleRes(RID_STR_GROUPWIZ_DBFIELD));
+ }
+
+ OUString& OOptionDBFieldPage::getDBFieldSetting()
+ {
+ return static_cast<OGroupBoxWizard*>(getDialog())->getSettings().sDBField;
+ }
+
+ OFinalizeGBWPage::OFinalizeGBWPage(weld::Container* pPage, OControlWizard* pWizard)
+ : OGBWPage(pPage, pWizard, "modules/sabpilot/ui/optionsfinalpage.ui", "OptionsFinalPage")
+ , m_xName(m_xBuilder->weld_entry("nameit"))
+ {
+ }
+
+ OFinalizeGBWPage::~OFinalizeGBWPage()
+ {
+ }
+
+ void OFinalizeGBWPage::Activate()
+ {
+ OGBWPage::Activate();
+ m_xName->grab_focus();
+ }
+
+ bool OFinalizeGBWPage::canAdvance() const
+ {
+ return false;
+ }
+
+ void OFinalizeGBWPage::initializePage()
+ {
+ OGBWPage::initializePage();
+
+ const OOptionGroupSettings& rSettings = getSettings();
+ m_xName->set_text(rSettings.sControlLabel);
+ }
+
+ bool OFinalizeGBWPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OGBWPage::commitPage(_eReason))
+ return false;
+
+ getSettings().sControlLabel = m_xName->get_text();
+
+ return true;
+ }
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/groupboxwiz.hxx b/extensions/source/dbpilots/groupboxwiz.hxx
new file mode 100644
index 000000000..18fa12c7f
--- /dev/null
+++ b/extensions/source/dbpilots/groupboxwiz.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "controlwizard.hxx"
+#include "commonpagesdbp.hxx"
+
+using vcl::WizardTypes::WizardState;
+using vcl::WizardTypes::CommitPageReason;
+
+namespace dbp
+{
+
+ struct OOptionGroupSettings : public OControlWizardSettings
+ {
+ std::vector<OUString> aLabels;
+ std::vector<OUString> aValues;
+ OUString sDefaultField;
+ OUString sDBField;
+ };
+
+ class OGroupBoxWizard final : public OControlWizard
+ {
+ OOptionGroupSettings m_aSettings;
+
+ bool m_bVisitedDefault : 1;
+ bool m_bVisitedDB : 1;
+
+ public:
+ OGroupBoxWizard(
+ weld::Window* _pParent,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxObjectModel,
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ OOptionGroupSettings& getSettings() { return m_aSettings; }
+
+ private:
+ // OWizardMachine overridables
+ virtual std::unique_ptr<BuilderPage> createPage( WizardState _nState ) override;
+ virtual WizardState determineNextState( WizardState _nCurrentState ) const override;
+ virtual void enterState( WizardState _nState ) override;
+ virtual bool onFinish() override;
+
+ virtual bool approveControl(sal_Int16 _nClassId) override;
+ };
+
+ class OGBWPage : public OControlWizardPage
+ {
+ public:
+ OGBWPage(weld::Container* pPage, OControlWizard* pWizard, const OUString& rUIXMLDescription, const OString& rID)
+ : OControlWizardPage(pPage, pWizard, rUIXMLDescription, rID)
+ {
+ }
+
+ protected:
+ OOptionGroupSettings& getSettings() { return static_cast<OGroupBoxWizard*>(getDialog())->getSettings(); }
+ };
+
+ class ORadioSelectionPage final : public OGBWPage
+ {
+ std::unique_ptr<weld::Entry> m_xRadioName;
+ std::unique_ptr<weld::Button> m_xMoveRight;
+ std::unique_ptr<weld::Button> m_xMoveLeft;
+ std::unique_ptr<weld::TreeView> m_xExistingRadios;
+
+ public:
+ explicit ORadioSelectionPage(weld::Container* pPage, OControlWizard* pWizard);
+ virtual ~ORadioSelectionPage() override;
+
+ private:
+ // BuilderPage overridables
+ void Activate() override;
+
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+ virtual bool canAdvance() const override;
+
+ DECL_LINK( OnMoveEntry, weld::Button&, void );
+ DECL_LINK( OnEntrySelected, weld::TreeView&, void );
+ DECL_LINK( OnNameModified, weld::Entry&, void );
+
+ void implCheckMoveButtons();
+ };
+
+ class ODefaultFieldSelectionPage final : public OMaybeListSelectionPage
+ {
+ std::unique_ptr<weld::RadioButton> m_xDefSelYes;
+ std::unique_ptr<weld::RadioButton> m_xDefSelNo;
+ std::unique_ptr<weld::ComboBox> m_xDefSelection;
+
+ public:
+ explicit ODefaultFieldSelectionPage(weld::Container* pPage, OControlWizard* pWizard);
+ virtual ~ODefaultFieldSelectionPage() override;
+
+ private:
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+
+ OOptionGroupSettings& getSettings() { return static_cast<OGroupBoxWizard*>(getDialog())->getSettings(); }
+ };
+
+ class OOptionValuesPage final : public OGBWPage
+ {
+ std::unique_ptr<weld::Entry> m_xValue;
+ std::unique_ptr<weld::TreeView> m_xOptions;
+
+ std::vector<OUString> m_aUncommittedValues;
+ ::vcl::WizardTypes::WizardState
+ m_nLastSelection;
+
+ public:
+ explicit OOptionValuesPage(weld::Container* pPage, OControlWizard* pWizard);
+ virtual ~OOptionValuesPage() override;
+
+ private:
+ // BuilderPage overridables
+ void Activate() override;
+
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+
+ void implTraveledOptions();
+
+ DECL_LINK( OnOptionSelected, weld::TreeView&, void );
+ };
+
+ class OOptionDBFieldPage : public ODBFieldPage
+ {
+ public:
+ explicit OOptionDBFieldPage(weld::Container* pPage, OControlWizard* pWizard);
+
+ protected:
+ // ODBFieldPage overridables
+ virtual OUString& getDBFieldSetting() override;
+ };
+
+ class OFinalizeGBWPage final : public OGBWPage
+ {
+ std::unique_ptr<weld::Entry> m_xName;
+
+ public:
+ explicit OFinalizeGBWPage(weld::Container* pPage, OControlWizard* pWizard);
+ virtual ~OFinalizeGBWPage() override;
+
+ private:
+ // BuilderPage overridables
+ void Activate() override;
+
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+ virtual bool canAdvance() const override;
+ };
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/listcombowizard.cxx b/extensions/source/dbpilots/listcombowizard.cxx
new file mode 100644
index 000000000..ae27f9553
--- /dev/null
+++ b/extensions/source/dbpilots/listcombowizard.cxx
@@ -0,0 +1,485 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "listcombowizard.hxx"
+#include "commonpagesdbp.hxx"
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/form/ListSourceType.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <connectivity/dbtools.hxx>
+#include <helpids.h>
+#include <osl/diagnose.h>
+
+
+namespace dbp
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::form;
+ using namespace ::dbtools;
+
+ OListComboWizard::OListComboWizard(weld::Window* _pParent,
+ const Reference< XPropertySet >& _rxObjectModel, const Reference< XComponentContext >& _rxContext )
+ : OControlWizard(_pParent, _rxObjectModel, _rxContext)
+ , m_bListBox(false)
+ , m_bHadDataSelection(true)
+ {
+ initControlSettings(&m_aSettings);
+
+ m_xPrevPage->set_help_id(HID_LISTWIZARD_PREVIOUS);
+ m_xNextPage->set_help_id(HID_LISTWIZARD_NEXT);
+ m_xCancel->set_help_id(HID_LISTWIZARD_CANCEL);
+ m_xFinish->set_help_id(HID_LISTWIZARD_FINISH);
+
+ // if we do not need the data source selection page ...
+ if (!needDatasourceSelection())
+ { // ... skip it!
+ skip();
+ m_bHadDataSelection = false;
+ }
+ }
+
+ bool OListComboWizard::approveControl(sal_Int16 _nClassId)
+ {
+ switch (_nClassId)
+ {
+ case FormComponentType::LISTBOX:
+ m_bListBox = true;
+ setTitleBase(compmodule::ModuleRes(RID_STR_LISTWIZARD_TITLE));
+ return true;
+ case FormComponentType::COMBOBOX:
+ m_bListBox = false;
+ setTitleBase(compmodule::ModuleRes(RID_STR_COMBOWIZARD_TITLE));
+ return true;
+ }
+ return false;
+ }
+
+ std::unique_ptr<BuilderPage> OListComboWizard::createPage(WizardState _nState)
+ {
+ OString sIdent(OString::number(_nState));
+ weld::Container* pPageContainer = m_xAssistant->append_page(sIdent);
+
+ switch (_nState)
+ {
+ case LCW_STATE_DATASOURCE_SELECTION:
+ return std::make_unique<OTableSelectionPage>(pPageContainer, this);
+ case LCW_STATE_TABLESELECTION:
+ return std::make_unique<OContentTableSelection>(pPageContainer, this);
+ case LCW_STATE_FIELDSELECTION:
+ return std::make_unique<OContentFieldSelection>(pPageContainer, this);
+ case LCW_STATE_FIELDLINK:
+ return std::make_unique<OLinkFieldsPage>(pPageContainer, this);
+ case LCW_STATE_COMBODBFIELD:
+ return std::make_unique<OComboDBFieldPage>(pPageContainer, this);
+ }
+
+ return nullptr;
+ }
+
+ vcl::WizardTypes::WizardState OListComboWizard::determineNextState( WizardState _nCurrentState ) const
+ {
+ switch (_nCurrentState)
+ {
+ case LCW_STATE_DATASOURCE_SELECTION:
+ return LCW_STATE_TABLESELECTION;
+ case LCW_STATE_TABLESELECTION:
+ return LCW_STATE_FIELDSELECTION;
+ case LCW_STATE_FIELDSELECTION:
+ return getFinalState();
+ }
+
+ return WZS_INVALID_STATE;
+ }
+
+ void OListComboWizard::enterState(WizardState _nState)
+ {
+ OControlWizard::enterState(_nState);
+
+ enableButtons(WizardButtonFlags::PREVIOUS, m_bHadDataSelection ? (LCW_STATE_DATASOURCE_SELECTION < _nState) : LCW_STATE_TABLESELECTION < _nState);
+ enableButtons(WizardButtonFlags::NEXT, getFinalState() != _nState);
+ if (_nState < getFinalState())
+ enableButtons(WizardButtonFlags::FINISH, false);
+
+ if (getFinalState() == _nState)
+ defaultButton(WizardButtonFlags::FINISH);
+ }
+
+
+ bool OListComboWizard::leaveState(WizardState _nState)
+ {
+ if (!OControlWizard::leaveState(_nState))
+ return false;
+
+ if (getFinalState() == _nState)
+ defaultButton(WizardButtonFlags::NEXT);
+
+ return true;
+ }
+
+
+ void OListComboWizard::implApplySettings()
+ {
+ try
+ {
+ // for quoting identifiers, we need the connection meta data
+ Reference< XConnection > xConn = getFormConnection();
+ DBG_ASSERT(xConn.is(), "OListComboWizard::implApplySettings: no connection, unable to quote!");
+ Reference< XDatabaseMetaData > xMetaData;
+ if (xConn.is())
+ xMetaData = xConn->getMetaData();
+
+ // do some quotings
+ if (xMetaData.is())
+ {
+ OUString sQuoteString = xMetaData->getIdentifierQuoteString();
+ if (isListBox()) // only when we have a listbox this should be not empty
+ getSettings().sLinkedListField = quoteName(sQuoteString, getSettings().sLinkedListField);
+
+ OUString sCatalog, sSchema, sName;
+ ::dbtools::qualifiedNameComponents( xMetaData, getSettings().sListContentTable, sCatalog, sSchema, sName, ::dbtools::EComposeRule::InDataManipulation );
+ getSettings().sListContentTable = ::dbtools::composeTableNameForSelect( xConn, sCatalog, sSchema, sName );
+
+ getSettings().sListContentField = quoteName(sQuoteString, getSettings().sListContentField);
+ }
+
+ // ListSourceType: SQL
+ getContext().xObjectModel->setPropertyValue("ListSourceType", Any(sal_Int32(ListSourceType_SQL)));
+
+ if (isListBox())
+ {
+ // BoundColumn: 1
+ getContext().xObjectModel->setPropertyValue("BoundColumn", Any(sal_Int16(1)));
+
+ // build the statement to set as list source
+ OUString sStatement = "SELECT " +
+ getSettings().sListContentField + ", " + getSettings().sLinkedListField +
+ " FROM " + getSettings().sListContentTable;
+ Sequence< OUString > aListSource { sStatement };
+ getContext().xObjectModel->setPropertyValue("ListSource", Any(aListSource));
+ }
+ else
+ {
+ // build the statement to set as list source
+ OUString sStatement = "SELECT DISTINCT " +
+ getSettings().sListContentField +
+ " FROM " + getSettings().sListContentTable;
+ getContext().xObjectModel->setPropertyValue( "ListSource", Any(sStatement));
+ }
+
+ // the bound field
+ getContext().xObjectModel->setPropertyValue("DataField", Any(getSettings().sLinkedFormField));
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("OListComboWizard::implApplySettings: could not set the property values for the listbox!");
+ }
+ }
+
+
+ bool OListComboWizard::onFinish()
+ {
+ if ( !OControlWizard::onFinish() )
+ return false;
+
+ implApplySettings();
+ return true;
+ }
+
+ Reference< XNameAccess > OLCPage::getTables() const
+ {
+ Reference< XConnection > xConn = getFormConnection();
+ DBG_ASSERT(xConn.is(), "OLCPage::getTables: should have an active connection when reaching this page!");
+
+ Reference< XTablesSupplier > xSuppTables(xConn, UNO_QUERY);
+ Reference< XNameAccess > xTables;
+ if (xSuppTables.is())
+ xTables = xSuppTables->getTables();
+
+ DBG_ASSERT(xTables.is() || !xConn.is(), "OLCPage::getTables: got no tables from the connection!");
+
+ return xTables;
+ }
+
+
+ Sequence< OUString > OLCPage::getTableFields()
+ {
+ Reference< XNameAccess > xTables = getTables();
+ Sequence< OUString > aColumnNames;
+ if (xTables.is())
+ {
+ try
+ {
+ // the list table as XColumnsSupplier
+ Reference< XColumnsSupplier > xSuppCols;
+ xTables->getByName(getSettings().sListContentTable) >>= xSuppCols;
+ DBG_ASSERT(xSuppCols.is(), "OLCPage::getTableFields: no columns supplier!");
+
+ // the columns
+ Reference< XNameAccess > xColumns;
+ if (xSuppCols.is())
+ xColumns = xSuppCols->getColumns();
+
+ // the column names
+ if (xColumns.is())
+ aColumnNames = xColumns->getElementNames();
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OLinkFieldsPage::initializePage: caught an exception while retrieving the columns");
+ }
+ }
+ return aColumnNames;
+ }
+
+ OContentTableSelection::OContentTableSelection(weld::Container* pPage, OListComboWizard* pWizard)
+ : OLCPage(pPage, pWizard, "modules/sabpilot/ui/contenttablepage.ui", "TableSelectionPage")
+ , m_xSelectTable(m_xBuilder->weld_tree_view("table"))
+ {
+ enableFormDatasourceDisplay();
+
+ m_xSelectTable->connect_row_activated(LINK(this, OContentTableSelection, OnTableDoubleClicked));
+ m_xSelectTable->connect_changed(LINK(this, OContentTableSelection, OnTableSelected));
+ }
+
+ OContentTableSelection::~OContentTableSelection()
+ {
+ }
+
+ void OContentTableSelection::Activate()
+ {
+ OLCPage::Activate();
+ m_xSelectTable->grab_focus();
+ }
+
+ bool OContentTableSelection::canAdvance() const
+ {
+ if (!OLCPage::canAdvance())
+ return false;
+
+ return 0 != m_xSelectTable->count_selected_rows();
+ }
+
+ IMPL_LINK_NOARG( OContentTableSelection, OnTableSelected, weld::TreeView&, void )
+ {
+ updateDialogTravelUI();
+ }
+
+ IMPL_LINK( OContentTableSelection, OnTableDoubleClicked, weld::TreeView&, _rListBox, bool )
+ {
+ if (_rListBox.count_selected_rows())
+ getDialog()->travelNext();
+ return true;
+ }
+
+ void OContentTableSelection::initializePage()
+ {
+ OLCPage::initializePage();
+
+ // fill the list with the table name
+ m_xSelectTable->clear();
+ try
+ {
+ Reference< XNameAccess > xTables = getTables();
+ Sequence< OUString > aTableNames;
+ if (xTables.is())
+ aTableNames = xTables->getElementNames();
+ fillListBox(*m_xSelectTable, aTableNames);
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("OContentTableSelection::initializePage: could not retrieve the table names!");
+ }
+
+ m_xSelectTable->select_text(getSettings().sListContentTable);
+ }
+
+
+ bool OContentTableSelection::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OLCPage::commitPage(_eReason))
+ return false;
+
+ OListComboSettings& rSettings = getSettings();
+ rSettings.sListContentTable = m_xSelectTable->get_selected_text();
+ if (rSettings.sListContentTable.isEmpty() && (::vcl::WizardTypes::eTravelBackward != _eReason))
+ // need to select a table
+ return false;
+
+ return true;
+ }
+
+ OContentFieldSelection::OContentFieldSelection(weld::Container* pPage, OListComboWizard* pWizard)
+ : OLCPage(pPage, pWizard, "modules/sabpilot/ui/contentfieldpage.ui", "FieldSelectionPage")
+ , m_xSelectTableField(m_xBuilder->weld_tree_view("selectfield"))
+ , m_xDisplayedField(m_xBuilder->weld_entry("displayfield"))
+ , m_xInfo(m_xBuilder->weld_label("info"))
+ {
+ m_xInfo->set_label(compmodule::ModuleRes( isListBox() ? RID_STR_FIELDINFO_LISTBOX : RID_STR_FIELDINFO_COMBOBOX));
+ m_xSelectTableField->connect_changed(LINK(this, OContentFieldSelection, OnFieldSelected));
+ m_xSelectTableField->connect_row_activated(LINK(this, OContentFieldSelection, OnTableDoubleClicked));
+ }
+
+ OContentFieldSelection::~OContentFieldSelection()
+ {
+ }
+
+ void OContentFieldSelection::initializePage()
+ {
+ OLCPage::initializePage();
+
+ // fill the list of fields
+ fillListBox(*m_xSelectTableField, getTableFields());
+
+ m_xSelectTableField->select_text(getSettings().sListContentField);
+ m_xDisplayedField->set_text(getSettings().sListContentField);
+ }
+
+ bool OContentFieldSelection::canAdvance() const
+ {
+ if (!OLCPage::canAdvance())
+ return false;
+
+ return 0 != m_xSelectTableField->count_selected_rows();
+ }
+
+ IMPL_LINK_NOARG( OContentFieldSelection, OnTableDoubleClicked, weld::TreeView&, bool )
+ {
+ if (m_xSelectTableField->count_selected_rows())
+ getDialog()->travelNext();
+ return true;
+ }
+
+ IMPL_LINK_NOARG( OContentFieldSelection, OnFieldSelected, weld::TreeView&, void )
+ {
+ updateDialogTravelUI();
+ m_xDisplayedField->set_text(m_xSelectTableField->get_selected_text());
+ }
+
+ bool OContentFieldSelection::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OLCPage::commitPage(_eReason))
+ return false;
+
+ getSettings().sListContentField = m_xSelectTableField->get_selected_text();
+
+ return true;
+ }
+
+ OLinkFieldsPage::OLinkFieldsPage(weld::Container* pPage, OListComboWizard* pWizard)
+ : OLCPage(pPage, pWizard, "modules/sabpilot/ui/fieldlinkpage.ui", "FieldLinkPage")
+ , m_xValueListField(m_xBuilder->weld_combo_box("valuefield"))
+ , m_xTableField(m_xBuilder->weld_combo_box("listtable"))
+ {
+ m_xValueListField->connect_changed(LINK(this, OLinkFieldsPage, OnSelectionModified));
+ m_xTableField->connect_changed(LINK(this, OLinkFieldsPage, OnSelectionModified));
+ }
+
+ OLinkFieldsPage::~OLinkFieldsPage()
+ {
+ }
+
+ void OLinkFieldsPage::Activate()
+ {
+ OLCPage::Activate();
+ m_xValueListField->grab_focus();
+ }
+
+ void OLinkFieldsPage::initializePage()
+ {
+ OLCPage::initializePage();
+
+ // fill the value list
+ fillListBox(*m_xValueListField, getContext().aFieldNames);
+ // fill the table field list
+ fillListBox(*m_xTableField, getTableFields());
+
+ // the initial selections
+ m_xValueListField->set_entry_text(getSettings().sLinkedFormField);
+ m_xTableField->set_entry_text(getSettings().sLinkedListField);
+
+ implCheckFinish();
+ }
+
+ bool OLinkFieldsPage::canAdvance() const
+ {
+ // we're on the last page here, no travelNext allowed ...
+ return false;
+ }
+
+ void OLinkFieldsPage::implCheckFinish()
+ {
+ bool bInvalidSelection = (-1 == m_xValueListField->find_text(m_xValueListField->get_active_text()));
+ bInvalidSelection |= (-1 == m_xTableField->find_text(m_xTableField->get_active_text()));
+ getDialog()->enableButtons(WizardButtonFlags::FINISH, !bInvalidSelection);
+ }
+
+ IMPL_LINK_NOARG(OLinkFieldsPage, OnSelectionModified, weld::ComboBox&, void)
+ {
+ implCheckFinish();
+ }
+
+ bool OLinkFieldsPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+ {
+ if (!OLCPage::commitPage(_eReason))
+ return false;
+
+ getSettings().sLinkedFormField = m_xValueListField->get_active_text();
+ getSettings().sLinkedListField = m_xTableField->get_active_text();
+
+ return true;
+ }
+
+ OComboDBFieldPage::OComboDBFieldPage(weld::Container* pPage, OControlWizard* pWizard)
+ : ODBFieldPage(pPage, pWizard)
+ {
+ setDescriptionText(compmodule::ModuleRes(RID_STR_COMBOWIZ_DBFIELD));
+ }
+
+ OUString& OComboDBFieldPage::getDBFieldSetting()
+ {
+ return static_cast<OListComboWizard*>(getDialog())->getSettings().sLinkedFormField;
+ }
+
+ void OComboDBFieldPage::Activate()
+ {
+ ODBFieldPage::Activate();
+ getDialog()->enableButtons(WizardButtonFlags::FINISH, true);
+ }
+
+ bool OComboDBFieldPage::canAdvance() const
+ {
+ // we're on the last page here, no travelNext allowed ...
+ return false;
+ }
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/listcombowizard.hxx b/extensions/source/dbpilots/listcombowizard.hxx
new file mode 100644
index 000000000..7ab6a8a21
--- /dev/null
+++ b/extensions/source/dbpilots/listcombowizard.hxx
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "controlwizard.hxx"
+#include "commonpagesdbp.hxx"
+
+using vcl::WizardTypes::WizardState;
+using vcl::WizardTypes::CommitPageReason;
+
+namespace dbp
+{
+
+#define LCW_STATE_DATASOURCE_SELECTION 0
+#define LCW_STATE_TABLESELECTION 1
+#define LCW_STATE_FIELDSELECTION 2
+#define LCW_STATE_FIELDLINK 3
+#define LCW_STATE_COMBODBFIELD 4
+
+ struct OListComboSettings : public OControlWizardSettings
+ {
+ OUString sListContentTable;
+ OUString sListContentField;
+ OUString sLinkedFormField;
+ OUString sLinkedListField;
+ };
+
+ class OListComboWizard final : public OControlWizard
+ {
+ OListComboSettings m_aSettings;
+ bool m_bListBox : 1;
+ bool m_bHadDataSelection : 1;
+
+ public:
+ OListComboWizard(
+ weld::Window* pParent,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxObjectModel,
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ OListComboSettings& getSettings() { return m_aSettings; }
+
+ bool isListBox() const { return m_bListBox; }
+
+ private:
+ // OWizardMachine overridables
+ virtual std::unique_ptr<BuilderPage> createPage( WizardState _nState ) override;
+ virtual WizardState determineNextState( WizardState _nCurrentState ) const override;
+ virtual void enterState( WizardState _nState ) override;
+ virtual bool leaveState( WizardState _nState ) override;
+ virtual bool onFinish() override;
+
+ virtual bool approveControl(sal_Int16 _nClassId) override;
+
+ WizardState getFinalState() const { return isListBox() ? LCW_STATE_FIELDLINK : LCW_STATE_COMBODBFIELD; }
+
+ void implApplySettings();
+ };
+
+ class OLCPage : public OControlWizardPage
+ {
+ public:
+ OLCPage(weld::Container* pPage, OListComboWizard* pWizard, const OUString& rUIXMLDescription, const OString& rID)
+ : OControlWizardPage(pPage, pWizard, rUIXMLDescription, rID)
+ {
+ }
+
+ protected:
+ OListComboSettings& getSettings() { return static_cast<OListComboWizard*>(getDialog())->getSettings(); }
+ bool isListBox() { return static_cast<OListComboWizard*>(getDialog())->isListBox(); }
+
+ protected:
+ css::uno::Reference< css::container::XNameAccess > getTables() const;
+ css::uno::Sequence< OUString > getTableFields();
+ };
+
+ class OContentTableSelection final : public OLCPage
+ {
+ std::unique_ptr<weld::TreeView> m_xSelectTable;
+
+ public:
+ explicit OContentTableSelection(weld::Container* pPage, OListComboWizard* pWizard);
+ virtual ~OContentTableSelection() override;
+
+ private:
+ // BuilderPage overridables
+ virtual void Activate() override;
+
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+ virtual bool canAdvance() const override;
+
+ DECL_LINK( OnTableDoubleClicked, weld::TreeView&, bool );
+ DECL_LINK( OnTableSelected, weld::TreeView&, void );
+ };
+
+ class OContentFieldSelection final : public OLCPage
+ {
+ std::unique_ptr<weld::TreeView> m_xSelectTableField;
+ std::unique_ptr<weld::Entry> m_xDisplayedField;
+ std::unique_ptr<weld::Label> m_xInfo;
+
+ public:
+ explicit OContentFieldSelection(weld::Container* pPage, OListComboWizard* pWizard);
+ virtual ~OContentFieldSelection() override;
+
+ private:
+ DECL_LINK( OnFieldSelected, weld::TreeView&, void );
+ DECL_LINK( OnTableDoubleClicked, weld::TreeView&, bool );
+
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+ virtual bool canAdvance() const override;
+ };
+
+ class OLinkFieldsPage final : public OLCPage
+ {
+ std::unique_ptr<weld::ComboBox> m_xValueListField;
+ std::unique_ptr<weld::ComboBox> m_xTableField;
+
+ public:
+ explicit OLinkFieldsPage(weld::Container* pPage, OListComboWizard* pWizard);
+ virtual ~OLinkFieldsPage() override;
+
+ private:
+ // BuilderPage overridables
+ virtual void Activate() override;
+
+ // OWizardPage overridables
+ virtual void initializePage() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+ virtual bool canAdvance() const override;
+
+ void implCheckFinish();
+
+ DECL_LINK(OnSelectionModified, weld::ComboBox&, void);
+ };
+
+ class OComboDBFieldPage : public ODBFieldPage
+ {
+ public:
+ explicit OComboDBFieldPage(weld::Container* pPage, OControlWizard* pWizard);
+
+ protected:
+ // BuilderPage overridables
+ virtual void Activate() override;
+
+ // OWizardPage overridables
+ virtual bool canAdvance() const override;
+
+ // ODBFieldPage overridables
+ virtual OUString& getDBFieldSetting() override;
+ };
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/moduledbp.cxx b/extensions/source/dbpilots/moduledbp.cxx
new file mode 100644
index 000000000..0a8f8fd4d
--- /dev/null
+++ b/extensions/source/dbpilots/moduledbp.cxx
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <componentmodule.cxx>
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/optiongrouplayouter.cxx b/extensions/source/dbpilots/optiongrouplayouter.cxx
new file mode 100644
index 000000000..f877a1768
--- /dev/null
+++ b/extensions/source/dbpilots/optiongrouplayouter.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 "optiongrouplayouter.hxx"
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/drawing/ShapeCollection.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/drawing/XShapeGrouper.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include "groupboxwiz.hxx"
+#include "dbptools.hxx"
+#include <tools/diagnose_ex.h>
+
+
+namespace dbp
+{
+
+
+#define BUTTON_HEIGHT 300
+#define HEIGHT 450
+#define OFFSET 300
+#define MIN_WIDTH 600
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::drawing;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::text;
+ using namespace ::com::sun::star::view;
+
+ OOptionGroupLayouter::OOptionGroupLayouter(const Reference< XComponentContext >& _rxContext)
+ :mxContext(_rxContext)
+ {
+ }
+
+
+ void OOptionGroupLayouter::doLayout(const OControlWizardContext& _rContext, const OOptionGroupSettings& _rSettings)
+ {
+ Reference< XShapes > xPageShapes = _rContext.xDrawPage;
+ if (!xPageShapes.is())
+ {
+ OSL_FAIL("OOptionGroupLayouter::OOptionGroupLayouter: missing the XShapes interface for the page!");
+ return;
+ }
+
+ Reference< XMultiServiceFactory > xDocFactory(_rContext.xDocumentModel, UNO_QUERY);
+ if (!xDocFactory.is())
+ {
+ OSL_FAIL("OOptionGroupLayouter::OOptionGroupLayouter: no document service factory!");
+ return;
+ }
+
+ // no. of buttons to create
+ sal_Int32 nRadioButtons = _rSettings.aLabels.size();
+
+ // the shape of the groupbox
+ css::awt::Size aControlShapeSize = _rContext.xObjectShape->getSize();
+ // maybe need to adjust the size if the control shapes
+ sal_Int32 nMinShapeHeight = BUTTON_HEIGHT*(nRadioButtons+1) + BUTTON_HEIGHT + BUTTON_HEIGHT/4;
+ if (aControlShapeSize.Height < nMinShapeHeight)
+ aControlShapeSize.Height = nMinShapeHeight;
+ if (aControlShapeSize.Width < MIN_WIDTH)
+ aControlShapeSize.Width = MIN_WIDTH;
+ _rContext.xObjectShape->setSize(aControlShapeSize);
+
+ // if we're working on a writer document, we need to anchor the shape
+ implAnchorShape(Reference< XPropertySet >(_rContext.xObjectShape, UNO_QUERY));
+
+ // shape collection (for grouping the shapes)
+ Reference< XShapes > xButtonCollection( ShapeCollection::create(mxContext) );
+ // first member : the shape of the control
+ xButtonCollection->add(_rContext.xObjectShape);
+
+ sal_Int32 nTempHeight = (aControlShapeSize.Height - BUTTON_HEIGHT/4) / (nRadioButtons + 1);
+
+ css::awt::Point aShapePosition = _rContext.xObjectShape->getPosition();
+
+ css::awt::Size aButtonSize(aControlShapeSize);
+ aButtonSize.Width = aControlShapeSize.Width - OFFSET;
+ aButtonSize.Height = HEIGHT;
+ css::awt::Point aButtonPosition;
+ aButtonPosition.X = aShapePosition.X + OFFSET;
+
+ OUString sElementsName("RadioGroup");
+ disambiguateName(Reference< XNameAccess >(_rContext.xForm, UNO_QUERY), sElementsName);
+
+ auto aLabelIter = _rSettings.aLabels.cbegin();
+ auto aValueIter = _rSettings.aValues.cbegin();
+ for (sal_Int32 i=0; i<nRadioButtons; ++i, ++aLabelIter, ++aValueIter)
+ {
+ aButtonPosition.Y = aShapePosition.Y + (i+1) * nTempHeight;
+
+ Reference< XPropertySet > xRadioModel(
+ xDocFactory->createInstance("com.sun.star.form.component.RadioButton"),
+ UNO_QUERY);
+
+ // the label
+ xRadioModel->setPropertyValue("Label", Any(*aLabelIter));
+ // the value
+ xRadioModel->setPropertyValue("RefValue", Any(*aValueIter));
+
+ // default selection
+ if (_rSettings.sDefaultField == *aLabelIter)
+ xRadioModel->setPropertyValue("DefaultState", Any(sal_Int16(1)));
+
+ // the connection to the database field
+ if (!_rSettings.sDBField.isEmpty())
+ xRadioModel->setPropertyValue("DataField", Any(_rSettings.sDBField));
+
+ // the name for the model
+ xRadioModel->setPropertyValue("Name", Any(sElementsName));
+
+ // create a shape for the radio button
+ Reference< XControlShape > xRadioShape(
+ xDocFactory->createInstance("com.sun.star.drawing.ControlShape"),
+ UNO_QUERY);
+ Reference< XPropertySet > xShapeProperties(xRadioShape, UNO_QUERY);
+
+ // if we're working on a writer document, we need to anchor the shape
+ implAnchorShape(xShapeProperties);
+
+ // position it
+ xRadioShape->setSize(aButtonSize);
+ xRadioShape->setPosition(aButtonPosition);
+ // knitting with the model
+ xRadioShape->setControl(Reference< XControlModel >(xRadioModel, UNO_QUERY));
+
+ // the name of the shape
+ // tdf#117282 com.sun.star.drawing.ControlShape *has* no property
+ // of type 'Name'. In older versions it was an error that this did
+ // not throw an UnknownPropertyException. Still, it was never set
+ // at the Shape/SdrObject and was lost.
+ // Thus - just do no tset it. It is/stays part of the FormControl
+ // data, so it will be shown in the FormControl dialogs. It is not
+ // shown/used in SdrObject::Name dialog (e.g. context menu/Name...)
+ // if (xShapeProperties.is())
+ // xShapeProperties->setPropertyValue("Name", makeAny(sElementsName));
+
+ // add to the page
+ xPageShapes->add(xRadioShape);
+ // add to the collection (for the later grouping)
+ xButtonCollection->add(xRadioShape);
+
+ // set the GroupBox as "LabelControl" for the RadioButton
+ // (_after_ having inserted the model into the page!)
+ xRadioModel->setPropertyValue("LabelControl", Any(_rContext.xObjectModel));
+ }
+
+ // group the shapes
+ try
+ {
+ Reference< XShapeGrouper > xGrouper(_rContext.xDrawPage, UNO_QUERY);
+ if (xGrouper.is())
+ {
+ Reference< XShapeGroup > xGroupedOptions = xGrouper->group(xButtonCollection);
+ Reference< XSelectionSupplier > xSelector(_rContext.xDocumentModel->getCurrentController(), UNO_QUERY);
+ if (xSelector.is())
+ xSelector->select(Any(xGroupedOptions));
+ }
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.dbpilots",
+ "caught an exception while grouping the shapes!");
+ }
+ }
+
+
+ void OOptionGroupLayouter::implAnchorShape(const Reference< XPropertySet >& _rxShapeProps)
+ {
+ static constexpr OUStringLiteral s_sAnchorPropertyName = u"AnchorType";
+ Reference< XPropertySetInfo > xPropertyInfo;
+ if (_rxShapeProps.is())
+ xPropertyInfo = _rxShapeProps->getPropertySetInfo();
+ if (xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(s_sAnchorPropertyName))
+ _rxShapeProps->setPropertyValue(s_sAnchorPropertyName, Any(TextContentAnchorType_AT_PAGE));
+ }
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/optiongrouplayouter.hxx b/extensions/source/dbpilots/optiongrouplayouter.hxx
new file mode 100644
index 000000000..64a6998c1
--- /dev/null
+++ b/extensions/source/dbpilots/optiongrouplayouter.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 <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+
+namespace dbp
+{
+
+
+ struct OControlWizardContext;
+ struct OOptionGroupSettings;
+
+ class OOptionGroupLayouter final
+ {
+ css::uno::Reference< css::uno::XComponentContext >
+ mxContext;
+
+ public:
+ explicit OOptionGroupLayouter(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ void doLayout(
+ const OControlWizardContext& _rContext,
+ const OOptionGroupSettings& _rSettings
+ );
+
+ private:
+ static void implAnchorShape(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxShapeProps
+ );
+ };
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/unoautopilot.hxx b/extensions/source/dbpilots/unoautopilot.hxx
new file mode 100644
index 000000000..a61df8a6a
--- /dev/null
+++ b/extensions/source/dbpilots/unoautopilot.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svtools/genericunodialog.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <componentmodule.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <vcl/svapp.hxx>
+
+namespace dbp
+{
+ typedef ::svt::OGenericUnoDialog OUnoAutoPilot_Base;
+ template <class TYPE>
+ class OUnoAutoPilot final
+ :public OUnoAutoPilot_Base
+ ,public ::comphelper::OPropertyArrayUsageHelper< OUnoAutoPilot< TYPE > >
+ {
+ public:
+ explicit OUnoAutoPilot(const css::uno::Reference< css::uno::XComponentContext >& _rxORB,
+ OUString aImplementationName,
+ const css::uno::Sequence<OUString>& aSupportedServices)
+ : OUnoAutoPilot_Base(_rxORB),
+ m_ImplementationName(aImplementationName),
+ m_SupportedServices(aSupportedServices)
+ {
+ }
+
+
+ // XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return m_ImplementationName;
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return m_SupportedServices;
+ }
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override
+ {
+ css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+ }
+
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override
+ {
+ return *this->getArrayHelper();
+ }
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override
+ {
+ css::uno::Sequence< css::beans::Property > aProps;
+ describeProperties(aProps);
+ return new ::cppu::OPropertyArrayHelper(aProps);
+ }
+
+ private:
+ // OGenericUnoDialog overridables
+ virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override
+ {
+ return std::make_unique<TYPE>(Application::GetFrameWeld(rParent), m_xObjectModel, m_aContext);
+ }
+
+ virtual void implInitialize(const css::uno::Any& _rValue) override
+ {
+ css::beans::PropertyValue aArgument;
+ if (_rValue >>= aArgument)
+ if (aArgument.Name == "ObjectModel")
+ {
+ aArgument.Value >>= m_xObjectModel;
+ return;
+ }
+
+ OUnoAutoPilot_Base::implInitialize(_rValue);
+ }
+
+ css::uno::Reference< css::beans::XPropertySet > m_xObjectModel;
+ OUString m_ImplementationName;
+ css::uno::Sequence<OUString> m_SupportedServices;
+
+ };
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/wizardcontext.hxx b/extensions/source/dbpilots/wizardcontext.hxx
new file mode 100644
index 000000000..65f4c1410
--- /dev/null
+++ b/extensions/source/dbpilots/wizardcontext.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 <sal/config.h>
+
+#include <map>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdb/XDatabaseContext.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+
+namespace dbp
+{
+
+ struct OControlWizardContext
+ {
+ // the global data source context
+ css::uno::Reference< css::sdb::XDatabaseContext >
+ xDatasourceContext;
+
+ // the control mode
+ css::uno::Reference< css::beans::XPropertySet >
+ xObjectModel;
+ // the form the control model belongs to
+ css::uno::Reference< css::beans::XPropertySet >
+ xForm;
+ // the form as rowset
+ css::uno::Reference< css::sdbc::XRowSet >
+ xRowSet;
+
+ // the model of the document
+ css::uno::Reference< css::frame::XModel >
+ xDocumentModel;
+ // the page where the control mode resides
+ css::uno::Reference< css::drawing::XDrawPage >
+ xDrawPage;
+ // the shape which carries the control
+ css::uno::Reference< css::drawing::XControlShape >
+ xObjectShape;
+
+ // the tables or queries of the data source the form is bound to (if any)
+ css::uno::Reference< css::container::XNameAccess >
+ xObjectContainer;
+ // the column types container of the object the form is bound to (table, query or SQL statement)
+ typedef std::map<OUString, sal_Int32> TNameTypeMap;
+ TNameTypeMap aTypes;
+ // the column names of the object the form is bound to (table, query or SQL statement)
+ css::uno::Sequence< OUString >
+ aFieldNames;
+
+ bool bEmbedded;
+ };
+
+
+} // namespace dbp
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/dbpilots/wizardservices.cxx b/extensions/source/dbpilots/wizardservices.cxx
new file mode 100644
index 000000000..25bcf7942
--- /dev/null
+++ b/extensions/source/dbpilots/wizardservices.cxx
@@ -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 .
+ */
+
+#include <sal/config.h>
+
+#include "unoautopilot.hxx"
+#include "groupboxwiz.hxx"
+#include "listcombowizard.hxx"
+#include "gridwizard.hxx"
+
+// the registration methods
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_dbp_OGroupBoxWizard_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(
+ new ::dbp::OUnoAutoPilot< ::dbp::OGroupBoxWizard>(
+ context,
+ "org.openoffice.comp.dbp.OGroupBoxWizard",
+ { "com.sun.star.sdb.GroupBoxAutoPilot" }
+ ));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_dbp_OListComboWizard_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(
+ new ::dbp::OUnoAutoPilot< ::dbp::OListComboWizard>(
+ context,
+ "org.openoffice.comp.dbp.OListComboWizard",
+ { "com.sun.star.sdb.ListComboBoxAutoPilot" }
+ ));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_dbp_OGridWizard_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(
+ new ::dbp::OUnoAutoPilot< ::dbp::OGridWizard>(
+ context,
+ "org.openoffice.comp.dbp.OGridWizard",
+ { "com.sun.star.sdb.GridControlAutoPilot" }
+ ));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/inc/componentmodule.cxx b/extensions/source/inc/componentmodule.cxx
new file mode 100644
index 000000000..e1b29b2ec
--- /dev/null
+++ b/extensions/source/inc/componentmodule.cxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "componentmodule.hxx"
+#include <unotools/resmgr.hxx>
+
+namespace compmodule
+{
+
+ OUString ModuleRes(TranslateId pId)
+ {
+ return Translate::get(pId, Translate::Create("pcr"));
+ }
+
+
+} // namespace compmodule
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/inc/componentmodule.hxx b/extensions/source/inc/componentmodule.hxx
new file mode 100644
index 000000000..24b5032cd
--- /dev/null
+++ b/extensions/source/inc/componentmodule.hxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <rtl/ustring.hxx>
+#include <unotools/resmgr.hxx>
+
+namespace compmodule
+{
+
+ // specialized ResId, using the resource locale provided by the global module
+ OUString ModuleRes(TranslateId pId);
+
+} // namespace compmodule
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/consolehandler.cxx b/extensions/source/logging/consolehandler.cxx
new file mode 100644
index 000000000..d1455baa3
--- /dev/null
+++ b/extensions/source/logging/consolehandler.cxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "methodguard.hxx"
+#include "loghandler.hxx"
+
+#include <com/sun/star/logging/XConsoleHandler.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <stdio.h>
+
+namespace logging
+{
+ using ::com::sun::star::logging::XConsoleHandler;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::logging::XLogFormatter;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::beans::NamedValue;
+
+ typedef ::cppu::WeakComponentImplHelper < XConsoleHandler
+ , XServiceInfo
+ > ConsoleHandler_Base;
+
+ namespace {
+
+ class ConsoleHandler :public ::cppu::BaseMutex
+ ,public ConsoleHandler_Base
+ {
+ private:
+ LogHandlerHelper m_aHandlerHelper;
+ sal_Int32 m_nThreshold;
+
+ public:
+ ConsoleHandler(const Reference<XComponentContext> &context,
+ const css::uno::Sequence<css::uno::Any> &arguments);
+ virtual ~ConsoleHandler() override;
+
+ private:
+ // XConsoleHandler
+ virtual ::sal_Int32 SAL_CALL getThreshold() override;
+ virtual void SAL_CALL setThreshold( ::sal_Int32 _threshold ) override;
+
+ // XLogHandler
+ virtual OUString SAL_CALL getEncoding() override;
+ virtual void SAL_CALL setEncoding( const OUString& _encoding ) override;
+ virtual Reference< XLogFormatter > SAL_CALL getFormatter() override;
+ virtual void SAL_CALL setFormatter( const Reference< XLogFormatter >& _formatter ) override;
+ virtual ::sal_Int32 SAL_CALL getLevel() override;
+ virtual void SAL_CALL setLevel( ::sal_Int32 _level ) override;
+ virtual void SAL_CALL flush( ) override;
+ virtual sal_Bool SAL_CALL publish( const LogRecord& Record ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& _rServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ public:
+ typedef ComponentMethodGuard< ConsoleHandler > MethodGuard;
+ void enterMethod( MethodGuard::Access );
+ void leaveMethod( MethodGuard::Access );
+ };
+
+ }
+
+ ConsoleHandler::ConsoleHandler(const Reference<XComponentContext> &context,
+ const css::uno::Sequence<css::uno::Any> &arguments)
+ :ConsoleHandler_Base( m_aMutex )
+ ,m_aHandlerHelper( context, m_aMutex, rBHelper )
+ ,m_nThreshold( css::logging::LogLevel::SEVERE )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !arguments.hasElements() )
+ { // create() - nothing to init
+ m_aHandlerHelper.setIsInitialized();
+ return;
+ }
+
+ if ( arguments.getLength() != 1 )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ Sequence< NamedValue > aSettings;
+ if ( !( arguments[0] >>= aSettings ) )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ // createWithSettings( [in] sequence< css::beans::NamedValue > Settings )
+ ::comphelper::NamedValueCollection aTypedSettings( aSettings );
+ m_aHandlerHelper.initFromSettings( aTypedSettings );
+
+ aTypedSettings.get_ensureType( "Threshold", m_nThreshold );
+
+ m_aHandlerHelper.setIsInitialized();
+ }
+
+ ConsoleHandler::~ConsoleHandler()
+ {
+ if ( !rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ void SAL_CALL ConsoleHandler::disposing()
+ {
+ m_aHandlerHelper.setFormatter( nullptr );
+ }
+
+
+ void ConsoleHandler::enterMethod( MethodGuard::Access )
+ {
+ m_aHandlerHelper.enterMethod();
+ }
+
+
+ void ConsoleHandler::leaveMethod( MethodGuard::Access )
+ {
+ m_aMutex.release();
+ }
+
+
+ ::sal_Int32 SAL_CALL ConsoleHandler::getThreshold()
+ {
+ MethodGuard aGuard( *this );
+ return m_nThreshold;
+ }
+
+
+ void SAL_CALL ConsoleHandler::setThreshold( ::sal_Int32 _threshold )
+ {
+ MethodGuard aGuard( *this );
+ m_nThreshold = _threshold;
+ }
+
+
+ OUString SAL_CALL ConsoleHandler::getEncoding()
+ {
+ MethodGuard aGuard( *this );
+ OUString sEncoding;
+ OSL_VERIFY( m_aHandlerHelper.getEncoding( sEncoding ) );
+ return sEncoding;
+ }
+
+
+ void SAL_CALL ConsoleHandler::setEncoding( const OUString& _rEncoding )
+ {
+ MethodGuard aGuard( *this );
+ OSL_VERIFY( m_aHandlerHelper.setEncoding( _rEncoding ) );
+ }
+
+
+ Reference< XLogFormatter > SAL_CALL ConsoleHandler::getFormatter()
+ {
+ MethodGuard aGuard( *this );
+ return m_aHandlerHelper.getFormatter();
+ }
+
+
+ void SAL_CALL ConsoleHandler::setFormatter( const Reference< XLogFormatter >& _rxFormatter )
+ {
+ MethodGuard aGuard( *this );
+ m_aHandlerHelper.setFormatter( _rxFormatter );
+ }
+
+
+ ::sal_Int32 SAL_CALL ConsoleHandler::getLevel()
+ {
+ MethodGuard aGuard( *this );
+ return m_aHandlerHelper.getLevel();
+ }
+
+
+ void SAL_CALL ConsoleHandler::setLevel( ::sal_Int32 _nLevel )
+ {
+ MethodGuard aGuard( *this );
+ m_aHandlerHelper.setLevel( _nLevel );
+ }
+
+
+ void SAL_CALL ConsoleHandler::flush( )
+ {
+ MethodGuard aGuard( *this );
+ fflush( stdout );
+ fflush( stderr );
+ }
+
+
+ sal_Bool SAL_CALL ConsoleHandler::publish( const LogRecord& _rRecord )
+ {
+ MethodGuard aGuard( *this );
+
+ OString sEntry;
+ if ( !m_aHandlerHelper.formatForPublishing( _rRecord, sEntry ) )
+ return false;
+
+ if ( _rRecord.Level >= m_nThreshold )
+ fprintf( stderr, "%s\n", sEntry.getStr() );
+ else
+ fprintf( stdout, "%s\n", sEntry.getStr() );
+
+ return true;
+ }
+
+ OUString SAL_CALL ConsoleHandler::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.ConsoleHandler";
+ }
+
+ sal_Bool SAL_CALL ConsoleHandler::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL ConsoleHandler::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.ConsoleHandler" };
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_ConsoleHandler(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new logging::ConsoleHandler(context, arguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/csvformatter.cxx b/extensions/source/logging/csvformatter.cxx
new file mode 100644
index 000000000..8e749db42
--- /dev/null
+++ b/extensions/source/logging/csvformatter.cxx
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/logging/XCsvLogFormatter.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <sal/macros.h>
+#include <sal/types.h>
+
+#include <stdio.h>
+#include <string_view>
+
+namespace logging
+{
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::logging::LogRecord;
+
+ namespace {
+
+ // formats for csv files as defined by RFC4180
+ class CsvFormatter : public cppu::WeakImplHelper<css::logging::XCsvLogFormatter, css::lang::XServiceInfo>
+ {
+ public:
+ virtual OUString SAL_CALL formatMultiColumn(const Sequence< OUString>& column_data) override;
+
+ CsvFormatter();
+
+ private:
+ // XCsvLogFormatter
+ virtual sal_Bool SAL_CALL getLogEventNo() override;
+ virtual sal_Bool SAL_CALL getLogThread() override;
+ virtual sal_Bool SAL_CALL getLogTimestamp() override;
+ virtual sal_Bool SAL_CALL getLogSource() override;
+ virtual Sequence< OUString > SAL_CALL getColumnnames() override;
+
+ virtual void SAL_CALL setLogEventNo( sal_Bool log_event_no ) override;
+ virtual void SAL_CALL setLogThread( sal_Bool log_thread ) override;
+ virtual void SAL_CALL setLogTimestamp( sal_Bool log_timestamp ) override;
+ virtual void SAL_CALL setLogSource( sal_Bool log_source ) override;
+ virtual void SAL_CALL setColumnnames( const Sequence< OUString>& column_names) override;
+
+ // XLogFormatter
+ virtual OUString SAL_CALL getHead( ) override;
+ virtual OUString SAL_CALL format( const LogRecord& Record ) override;
+ virtual OUString SAL_CALL getTail( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& service_name ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ private:
+ bool m_LogEventNo;
+ bool m_LogThread;
+ bool m_LogTimestamp;
+ bool m_LogSource;
+ bool m_MultiColumn;
+ css::uno::Sequence< OUString > m_Columnnames;
+ };
+
+ }
+} // namespace logging
+
+// private helpers
+namespace
+{
+ const sal_Unicode quote_char = '"';
+ const sal_Unicode comma_char = ',';
+ constexpr OUStringLiteral dos_newline = u"\r\n";
+
+ bool needsQuoting(std::u16string_view str)
+ {
+ return str.find_first_of(u"\",\n\r") != std::u16string_view::npos;
+ };
+
+ void appendEncodedString(OUStringBuffer& buf, const OUString& str)
+ {
+ if(needsQuoting(str))
+ {
+ // each double-quote will get replaced by two double-quotes
+ buf.append(quote_char);
+ const sal_Int32 buf_offset = buf.getLength();
+ const sal_Int32 str_length = str.getLength();
+ buf.append(str);
+ // special treatment for the last character
+ if(quote_char==str[str_length-1])
+ buf.append(quote_char);
+ // iterating backwards because the index at which we insert won't be shifted
+ // when moving that way.
+ for(sal_Int32 i = str_length; i>=0; )
+ {
+ i=str.lastIndexOf(quote_char, --i);
+ if(i!=-1)
+ buf.insert(buf_offset + i, quote_char);
+ }
+ buf.append(quote_char);
+ }
+ else
+ buf.append(str);
+ };
+}
+
+namespace logging
+{
+ CsvFormatter::CsvFormatter()
+ :m_LogEventNo(true),
+ m_LogThread(true),
+ m_LogTimestamp(true),
+ m_LogSource(false),
+ m_MultiColumn(false),
+ m_Columnnames({ "message" })
+ { }
+
+ sal_Bool CsvFormatter::getLogEventNo()
+ {
+ return m_LogEventNo;
+ }
+
+ sal_Bool CsvFormatter::getLogThread()
+ {
+ return m_LogThread;
+ }
+
+ sal_Bool CsvFormatter::getLogTimestamp()
+ {
+ return m_LogTimestamp;
+ }
+
+ sal_Bool CsvFormatter::getLogSource()
+ {
+ return m_LogSource;
+ }
+
+ Sequence< OUString > CsvFormatter::getColumnnames()
+ {
+ return m_Columnnames;
+ }
+
+ void CsvFormatter::setLogEventNo(sal_Bool log_event_no)
+ {
+ m_LogEventNo = log_event_no;
+ }
+
+ void CsvFormatter::setLogThread(sal_Bool log_thread)
+ {
+ m_LogThread = log_thread;
+ }
+
+ void CsvFormatter::setLogTimestamp(sal_Bool log_timestamp)
+ {
+ m_LogTimestamp = log_timestamp;
+ }
+
+ void CsvFormatter::setLogSource(sal_Bool log_source)
+ {
+ m_LogSource = log_source;
+ }
+
+ void CsvFormatter::setColumnnames(const Sequence< OUString >& columnnames)
+ {
+ m_Columnnames = columnnames;
+ m_MultiColumn = (m_Columnnames.getLength()>1);
+ }
+
+ OUString SAL_CALL CsvFormatter::getHead( )
+ {
+ OUStringBuffer buf;
+ if(m_LogEventNo)
+ buf.append("event no,");
+ if(m_LogThread)
+ buf.append("thread,");
+ if(m_LogTimestamp)
+ buf.append("timestamp,");
+ if(m_LogSource)
+ buf.append("class,method,");
+ sal_Int32 columns = m_Columnnames.getLength();
+ for(sal_Int32 i=0; i<columns; i++)
+ {
+ buf.append(m_Columnnames[i]);
+ buf.append(comma_char);
+ }
+ buf.setLength(buf.getLength()-1);
+ buf.append(dos_newline);
+ return buf.makeStringAndClear();
+ }
+
+ OUString SAL_CALL CsvFormatter::format( const LogRecord& record )
+ {
+ OUStringBuffer aLogEntry;
+
+ if(m_LogEventNo)
+ {
+ aLogEntry.append( record.SequenceNumber );
+ aLogEntry.append(comma_char);
+ }
+
+ if(m_LogThread)
+ {
+ aLogEntry.append( record.ThreadID );
+ aLogEntry.append(comma_char);
+ }
+
+ if(m_LogTimestamp)
+ {
+ if ( record.LogTime.Year < -9999 || 9999 < record.LogTime.Year
+ || record.LogTime.Month < 1 || 12 < record.LogTime.Month
+ || record.LogTime.Day < 1 || 31 < record.LogTime.Day
+ || 24 < record.LogTime.Hours
+ || 60 < record.LogTime.Minutes
+ || 60 < record.LogTime.Seconds
+ || 999999999 < record.LogTime.NanoSeconds)
+ {
+ throw css::lang::IllegalArgumentException("invalid date", static_cast<cppu::OWeakObject*>(this), 1);
+ }
+
+ // ISO 8601
+ char buffer[ SAL_N_ELEMENTS("-32768-65535-65535T65535:65535:65535.4294967295") ];
+ const size_t buffer_size = sizeof( buffer );
+ snprintf( buffer, buffer_size, "%04i-%02u-%02uT%02u:%02u:%02u.%09" SAL_PRIuUINT32,
+ static_cast<int>(record.LogTime.Year),
+ static_cast<unsigned int>(record.LogTime.Month),
+ static_cast<unsigned int>(record.LogTime.Day),
+ static_cast<unsigned int>(record.LogTime.Hours),
+ static_cast<unsigned int>(record.LogTime.Minutes),
+ static_cast<unsigned int>(record.LogTime.Seconds),
+ record.LogTime.NanoSeconds );
+ aLogEntry.appendAscii( buffer );
+ aLogEntry.append(comma_char);
+ }
+
+ if(m_LogSource)
+ {
+ appendEncodedString(aLogEntry, record.SourceClassName);
+ aLogEntry.append(comma_char);
+
+ appendEncodedString(aLogEntry, record.SourceMethodName);
+ aLogEntry.append(comma_char);
+ }
+
+ // if the CsvFormatter has multiple columns set via setColumnnames(), the
+ // message of the record is expected to be encoded with formatMultiColumn
+ // if the CsvFormatter has only one column set, the message is expected not
+ // to be encoded
+ if(m_MultiColumn)
+ aLogEntry.append(record.Message);
+ else
+ appendEncodedString(aLogEntry, record.Message);
+
+ aLogEntry.append( dos_newline );
+ return aLogEntry.makeStringAndClear();
+ }
+
+ OUString SAL_CALL CsvFormatter::getTail( )
+ {
+ return OUString();
+ }
+
+ OUString SAL_CALL CsvFormatter::formatMultiColumn(const Sequence< OUString>& column_data)
+ {
+ sal_Int32 columns = column_data.getLength();
+ OUStringBuffer buf;
+ for(int i=0; i<columns; i++)
+ {
+ appendEncodedString(buf, column_data[i]);
+ buf.append(comma_char);
+ }
+ buf.setLength(buf.getLength()-1);
+ return buf.makeStringAndClear();
+ }
+
+ sal_Bool SAL_CALL CsvFormatter::supportsService( const OUString& service_name )
+ {
+ return cppu::supportsService(this, service_name);
+ }
+
+ OUString SAL_CALL CsvFormatter::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.CsvFormatter";
+ }
+
+ Sequence< OUString > SAL_CALL CsvFormatter::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.CsvFormatter" };
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_CsvFormatter(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new logging::CsvFormatter());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/filehandler.cxx b/extensions/source/logging/filehandler.cxx
new file mode 100644
index 000000000..200a3a64b
--- /dev/null
+++ b/extensions/source/logging/filehandler.cxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include "methodguard.hxx"
+#include "loghandler.hxx"
+
+#include <com/sun/star/logging/XLogHandler.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/util/PathSubstitution.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+
+#include <tools/diagnose_ex.h>
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <osl/file.hxx>
+
+#include <memory>
+
+namespace logging
+{
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::logging::XLogFormatter;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::logging::XLogHandler;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::util::PathSubstitution;
+ using ::com::sun::star::util::XStringSubstitution;
+ using ::com::sun::star::beans::NamedValue;
+
+ typedef ::cppu::WeakComponentImplHelper < XLogHandler
+ , XServiceInfo
+ > FileHandler_Base;
+
+ namespace {
+
+ class FileHandler :public ::cppu::BaseMutex
+ ,public FileHandler_Base
+ {
+ private:
+ enum FileValidity
+ {
+ /// never attempted to open the file
+ eUnknown,
+ /// file is valid
+ eValid,
+ /// file is invalid
+ eInvalid
+ };
+
+ Reference<XComponentContext> m_xContext;
+ LogHandlerHelper m_aHandlerHelper;
+ OUString m_sFileURL;
+ std::unique_ptr< ::osl::File > m_pFile;
+ FileValidity m_eFileValidity;
+
+ public:
+ FileHandler(const css::uno::Reference<XComponentContext> &context,
+ const css::uno::Sequence<css::uno::Any> &arguments);
+ virtual ~FileHandler() override;
+
+ private:
+ // XLogHandler
+ virtual OUString SAL_CALL getEncoding() override;
+ virtual void SAL_CALL setEncoding( const OUString& _encoding ) override;
+ virtual Reference< XLogFormatter > SAL_CALL getFormatter() override;
+ virtual void SAL_CALL setFormatter( const Reference< XLogFormatter >& _formatter ) override;
+ virtual ::sal_Int32 SAL_CALL getLevel() override;
+ virtual void SAL_CALL setLevel( ::sal_Int32 _level ) override;
+ virtual void SAL_CALL flush( ) override;
+ virtual sal_Bool SAL_CALL publish( const LogRecord& Record ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& _rServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ public:
+ typedef ComponentMethodGuard< FileHandler > MethodGuard;
+ void enterMethod( MethodGuard::Access );
+ void leaveMethod( MethodGuard::Access );
+
+ private:
+ /** prepares our output file for writing
+ */
+ bool impl_prepareFile_nothrow();
+
+ /// writes the given string to our file
+ void impl_writeString_nothrow( const OString& _rEntry );
+
+ /** does string substitution on a (usually externally provided) file url
+ */
+ void impl_doStringsubstitution_nothrow( OUString& _inout_rURL );
+ };
+
+ }
+
+ FileHandler::FileHandler(const css::uno::Reference<XComponentContext> &context,
+ const css::uno::Sequence<css::uno::Any> &arguments)
+ :FileHandler_Base( m_aMutex )
+ ,m_xContext( context )
+ ,m_aHandlerHelper( context, m_aMutex, rBHelper )
+ ,m_eFileValidity( eUnknown )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( arguments.getLength() != 1 )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ Sequence< NamedValue > aSettings;
+ if ( arguments[0] >>= m_sFileURL )
+ {
+ // create( [in] string URL );
+ impl_doStringsubstitution_nothrow( m_sFileURL );
+ }
+ else if ( arguments[0] >>= aSettings )
+ {
+ // createWithSettings( [in] sequence< css::beans::NamedValue > Settings )
+ ::comphelper::NamedValueCollection aTypedSettings( aSettings );
+ m_aHandlerHelper.initFromSettings( aTypedSettings );
+
+ if ( aTypedSettings.get_ensureType( "FileURL", m_sFileURL ) )
+ impl_doStringsubstitution_nothrow( m_sFileURL );
+ }
+ else
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ m_aHandlerHelper.setIsInitialized();
+ }
+
+ FileHandler::~FileHandler()
+ {
+ if ( !rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ bool FileHandler::impl_prepareFile_nothrow()
+ {
+ if ( m_eFileValidity == eUnknown )
+ {
+ m_pFile.reset( new ::osl::File( m_sFileURL ) );
+ // check whether the log file already exists
+ ::osl::DirectoryItem aFileItem;
+ if (osl::FileBase::E_None == ::osl::DirectoryItem::get(m_sFileURL, aFileItem))
+ {
+ ::osl::FileStatus aStatus(osl_FileStatus_Mask_Validate);
+ if (::osl::FileBase::E_None == aFileItem.getFileStatus(aStatus))
+ ::osl::File::remove(m_sFileURL);
+ }
+
+ ::osl::FileBase::RC res = m_pFile->open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
+ m_eFileValidity = res == ::osl::FileBase::E_None
+ ? eValid
+ : eInvalid;
+ #if OSL_DEBUG_LEVEL > 0
+ if ( m_eFileValidity == eInvalid )
+ {
+ SAL_WARN( "extensions.logging", "FileHandler::impl_prepareFile_nothrow: could not open the designated log file:"
+ "\nURL: " << m_sFileURL
+ << "\nerror code: " << static_cast<sal_Int32>(res) );
+ }
+ #endif
+ if ( m_eFileValidity == eValid )
+ {
+ OString sHead;
+ if ( m_aHandlerHelper.getEncodedHead( sHead ) )
+ impl_writeString_nothrow( sHead );
+ }
+ }
+
+ return m_eFileValidity == eValid;
+ }
+
+
+ void FileHandler::impl_writeString_nothrow( const OString& _rEntry )
+ {
+ OSL_PRECOND(m_pFile, "FileHandler::impl_writeString_nothrow: no file!");
+
+ sal_uInt64 nBytesToWrite( _rEntry.getLength() );
+ sal_uInt64 nBytesWritten( 0 );
+ ::osl::FileBase::RC res =
+ m_pFile->write( _rEntry.getStr(), nBytesToWrite, nBytesWritten );
+ OSL_ENSURE( ( res == ::osl::FileBase::E_None ) && ( nBytesWritten == nBytesToWrite ),
+ "FileHandler::impl_writeString_nothrow: could not write the log entry!" );
+ }
+
+
+ void FileHandler::impl_doStringsubstitution_nothrow( OUString& _inout_rURL )
+ {
+ try
+ {
+ Reference< XStringSubstitution > xStringSubst(PathSubstitution::create(m_xContext));
+ _inout_rURL = xStringSubst->substituteVariables( _inout_rURL, true );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ }
+
+
+ void SAL_CALL FileHandler::disposing()
+ {
+ if ( m_eFileValidity == eValid )
+ {
+ OString sTail;
+ if ( m_aHandlerHelper.getEncodedTail( sTail ) )
+ impl_writeString_nothrow( sTail );
+ }
+
+ m_pFile.reset();
+ m_aHandlerHelper.setFormatter( nullptr );
+ }
+
+
+ void FileHandler::enterMethod( MethodGuard::Access )
+ {
+ m_aHandlerHelper.enterMethod();
+ }
+
+
+ void FileHandler::leaveMethod( MethodGuard::Access )
+ {
+ m_aMutex.release();
+ }
+
+
+ OUString SAL_CALL FileHandler::getEncoding()
+ {
+ MethodGuard aGuard( *this );
+ OUString sEncoding;
+ OSL_VERIFY( m_aHandlerHelper.getEncoding( sEncoding ) );
+ return sEncoding;
+ }
+
+
+ void SAL_CALL FileHandler::setEncoding( const OUString& _rEncoding )
+ {
+ MethodGuard aGuard( *this );
+ OSL_VERIFY( m_aHandlerHelper.setEncoding( _rEncoding ) );
+ }
+
+
+ Reference< XLogFormatter > SAL_CALL FileHandler::getFormatter()
+ {
+ MethodGuard aGuard( *this );
+ return m_aHandlerHelper.getFormatter();
+ }
+
+
+ void SAL_CALL FileHandler::setFormatter( const Reference< XLogFormatter >& _rxFormatter )
+ {
+ MethodGuard aGuard( *this );
+ m_aHandlerHelper.setFormatter( _rxFormatter );
+ }
+
+
+ ::sal_Int32 SAL_CALL FileHandler::getLevel()
+ {
+ MethodGuard aGuard( *this );
+ return m_aHandlerHelper.getLevel();
+ }
+
+
+ void SAL_CALL FileHandler::setLevel( ::sal_Int32 _nLevel )
+ {
+ MethodGuard aGuard( *this );
+ m_aHandlerHelper.setLevel( _nLevel );
+ }
+
+
+ void SAL_CALL FileHandler::flush( )
+ {
+ MethodGuard aGuard( *this );
+ if (!m_pFile)
+ {
+ OSL_PRECOND(false, "FileHandler::flush: no file!");
+ return;
+ }
+ ::osl::FileBase::RC res = m_pFile->sync();
+ OSL_ENSURE(res == ::osl::FileBase::E_None, "FileHandler::flush: Could not sync logfile to filesystem.");
+ }
+
+
+ sal_Bool SAL_CALL FileHandler::publish( const LogRecord& _rRecord )
+ {
+ MethodGuard aGuard( *this );
+
+ if ( !impl_prepareFile_nothrow() )
+ return false;
+
+ OString sEntry;
+ if ( !m_aHandlerHelper.formatForPublishing( _rRecord, sEntry ) )
+ return false;
+
+ impl_writeString_nothrow( sEntry );
+ return true;
+ }
+
+ OUString SAL_CALL FileHandler::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.FileHandler";
+ }
+
+ sal_Bool SAL_CALL FileHandler::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL FileHandler::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.FileHandler" };
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_FileHandler(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new logging::FileHandler(context, arguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/log.component b/extensions/source/logging/log.component
new file mode 100644
index 000000000..ae221bb57
--- /dev/null
+++ b/extensions/source/logging/log.component
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.extensions.ConsoleHandler"
+ constructor="com_sun_star_comp_extensions_ConsoleHandler">
+ <service name="com.sun.star.logging.ConsoleHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.CsvFormatter"
+ constructor="com_sun_star_comp_extensions_CsvFormatter">
+ <service name="com.sun.star.logging.CsvFormatter"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.FileHandler"
+ constructor="com_sun_star_comp_extensions_FileHandler">
+ <service name="com.sun.star.logging.FileHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.LoggerPool"
+ constructor="com_sun_star_comp_extensions_LoggerPool"
+ single-instance="true">
+ <singleton name="com.sun.star.logging.LoggerPool"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.PlainTextFormatter"
+ constructor="com_sun_star_comp_extensions_PlainTextFormatter">
+ <service name="com.sun.star.logging.PlainTextFormatter"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.SimpleTextFormatter"
+ constructor="com_sun_star_comp_extensions_SimpleTextFormatter">
+ <service name="com.sun.star.logging.SimpleTextFormatter"/>
+ </implementation>
+</component>
diff --git a/extensions/source/logging/logger.cxx b/extensions/source/logging/logger.cxx
new file mode 100644
index 000000000..322ad7ab9
--- /dev/null
+++ b/extensions/source/logging/logger.cxx
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "logrecord.hxx"
+#include "loggerconfig.hxx"
+
+#include <com/sun/star/logging/XLogger.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/logging/XLoggerPool.hpp>
+
+#include <cppuhelper/basemutex.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <map>
+
+
+namespace logging
+{
+
+ using ::com::sun::star::logging::XLogger;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::WeakReference;
+ using ::com::sun::star::logging::XLogHandler;
+ using ::com::sun::star::logging::LogRecord;
+
+ namespace {
+
+ class EventLogger : public cppu::BaseMutex,
+ public cppu::WeakImplHelper<css::logging::XLogger>
+ {
+ private:
+ comphelper::OInterfaceContainerHelper2 m_aHandlers;
+ oslInterlockedCount m_nEventNumber;
+
+ // <attributes>
+ sal_Int32 m_nLogLevel;
+ OUString m_sName;
+ // </attributes>
+
+ public:
+ EventLogger( const Reference< XComponentContext >& _rxContext, const OUString& _rName );
+
+ // XLogger
+ virtual OUString SAL_CALL getName() override;
+ virtual ::sal_Int32 SAL_CALL getLevel() override;
+ virtual void SAL_CALL setLevel( ::sal_Int32 _level ) override;
+ virtual void SAL_CALL addLogHandler( const Reference< XLogHandler >& LogHandler ) override;
+ virtual void SAL_CALL removeLogHandler( const Reference< XLogHandler >& LogHandler ) override;
+ virtual sal_Bool SAL_CALL isLoggable( ::sal_Int32 _nLevel ) override;
+ virtual void SAL_CALL log( ::sal_Int32 Level, const OUString& Message ) override;
+ virtual void SAL_CALL logp( ::sal_Int32 Level, const OUString& SourceClass, const OUString& SourceMethod, const OUString& Message ) override;
+
+ protected:
+ virtual ~EventLogger() override;
+
+ private:
+ /** logs the given log record
+ */
+ void impl_ts_logEvent_nothrow( const LogRecord& _rRecord );
+
+ /** non-threadsafe impl-version of isLoggable
+ */
+ bool impl_nts_isLoggable_nothrow( ::sal_Int32 _nLevel );
+ };
+
+ /** administrates a pool of XLogger instances, where a logger is keyed by its name,
+ and subsequent requests for a logger with the same name return the same instance.
+ */
+ class LoggerPool : public cppu::WeakImplHelper<css::logging::XLoggerPool, XServiceInfo>
+ {
+ private:
+ ::osl::Mutex m_aMutex;
+ Reference<XComponentContext> m_xContext;
+ std::map< OUString, WeakReference<XLogger> > m_aLoggerMap;
+
+ public:
+ explicit LoggerPool( const Reference< XComponentContext >& _rxContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& _rServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XLoggerPool
+ virtual Reference< XLogger > SAL_CALL getNamedLogger( const OUString& Name ) override;
+ virtual Reference< XLogger > SAL_CALL getDefaultLogger( ) override;
+ };
+
+ }
+
+ EventLogger::EventLogger( const Reference< XComponentContext >& _rxContext, const OUString& _rName )
+ :m_aHandlers( m_aMutex )
+ ,m_nEventNumber( 0 )
+ ,m_nLogLevel( css::logging::LogLevel::OFF )
+ ,m_sName( _rName )
+ {
+ osl_atomic_increment( &m_refCount );
+ {
+ initializeLoggerFromConfiguration( _rxContext, this );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+ EventLogger::~EventLogger()
+ {
+ }
+
+ bool EventLogger::impl_nts_isLoggable_nothrow( ::sal_Int32 _nLevel )
+ {
+ if ( _nLevel < m_nLogLevel )
+ return false;
+
+ if ( !m_aHandlers.getLength() )
+ return false;
+
+ return true;
+ }
+
+ void EventLogger::impl_ts_logEvent_nothrow( const LogRecord& _rRecord )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !impl_nts_isLoggable_nothrow( _rRecord.Level ) )
+ return;
+
+ m_aHandlers.forEach< XLogHandler >(
+ [&_rRecord] (Reference<XLogHandler> const& rxListener) { rxListener->publish(_rRecord); } );
+ m_aHandlers.forEach< XLogHandler >(
+ [] (Reference<XLogHandler> const& rxListener) { rxListener->flush(); } );
+ }
+
+ OUString SAL_CALL EventLogger::getName()
+ {
+ return m_sName;
+ }
+
+ ::sal_Int32 SAL_CALL EventLogger::getLevel()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_nLogLevel;
+ }
+
+ void SAL_CALL EventLogger::setLevel( ::sal_Int32 _level )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nLogLevel = _level;
+ }
+
+ void SAL_CALL EventLogger::addLogHandler( const Reference< XLogHandler >& _rxLogHandler )
+ {
+ if ( _rxLogHandler.is() )
+ m_aHandlers.addInterface( _rxLogHandler );
+ }
+
+ void SAL_CALL EventLogger::removeLogHandler( const Reference< XLogHandler >& _rxLogHandler )
+ {
+ if ( _rxLogHandler.is() )
+ m_aHandlers.removeInterface( _rxLogHandler );
+ }
+
+ sal_Bool SAL_CALL EventLogger::isLoggable( ::sal_Int32 _nLevel )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return impl_nts_isLoggable_nothrow( _nLevel );
+ }
+
+ void SAL_CALL EventLogger::log( ::sal_Int32 _nLevel, const OUString& _rMessage )
+ {
+ impl_ts_logEvent_nothrow( createLogRecord(
+ m_sName,
+ _rMessage,
+ _nLevel,
+ osl_atomic_increment( &m_nEventNumber )
+ ) );
+ }
+
+ void SAL_CALL EventLogger::logp( ::sal_Int32 _nLevel, const OUString& _rSourceClass, const OUString& _rSourceMethod, const OUString& _rMessage )
+ {
+ impl_ts_logEvent_nothrow( createLogRecord(
+ m_sName,
+ _rSourceClass,
+ _rSourceMethod,
+ _rMessage,
+ _nLevel,
+ osl_atomic_increment( &m_nEventNumber )
+ ) );
+ }
+
+ LoggerPool::LoggerPool( const Reference< XComponentContext >& _rxContext )
+ :m_xContext( _rxContext )
+ {
+ }
+
+ OUString SAL_CALL LoggerPool::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.LoggerPool";
+ }
+
+ sal_Bool SAL_CALL LoggerPool::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL LoggerPool::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.LoggerPool" };
+ }
+
+ Reference< XLogger > SAL_CALL LoggerPool::getNamedLogger( const OUString& _rName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ WeakReference< XLogger >& rLogger( m_aLoggerMap[ _rName ] );
+ Reference< XLogger > xLogger( rLogger );
+ if ( !xLogger.is() )
+ {
+ // never requested before, or already dead
+ xLogger = new EventLogger( m_xContext, _rName );
+ rLogger = xLogger;
+ }
+
+ return xLogger;
+ }
+
+ Reference< XLogger > SAL_CALL LoggerPool::getDefaultLogger( )
+ {
+ return getNamedLogger( "org.openoffice.logging.DefaultLogger" );
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_LoggerPool(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new logging::LoggerPool(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/loggerconfig.cxx b/extensions/source/logging/loggerconfig.cxx
new file mode 100644
index 000000000..25ef76e02
--- /dev/null
+++ b/extensions/source/logging/loggerconfig.cxx
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "loggerconfig.hxx"
+#include <stdio.h>
+#include <string_view>
+
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/logging/XLogHandler.hpp>
+#include <com/sun/star/logging/XLogFormatter.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <osl/process.h>
+
+#include <cppuhelper/component_context.hxx>
+
+
+namespace logging
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::logging::XLogger;
+ using ::com::sun::star::lang::XMultiServiceFactory;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::container::XNameContainer;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::lang::XSingleServiceFactory;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::util::XChangesBatch;
+ using ::com::sun::star::lang::NullPointerException;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::lang::ServiceNotRegisteredException;
+ using ::com::sun::star::beans::NamedValue;
+ using ::com::sun::star::logging::XLogHandler;
+ using ::com::sun::star::logging::XLogFormatter;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::uno::XComponentContext;
+
+ namespace LogLevel = ::com::sun::star::logging::LogLevel;
+
+ namespace
+ {
+
+ typedef void (*SettingTranslation)( const Reference< XLogger >&, const OUString&, Any& );
+
+
+ void lcl_substituteFileHandlerURLVariables_nothrow( const Reference< XLogger >& _rxLogger, OUString& _inout_rFileURL )
+ {
+ struct Variable
+ {
+ std::u16string_view pVariablePattern;
+ OUString sVariableValue;
+ };
+
+ OUString sLoggerName;
+ try { sLoggerName = _rxLogger->getName(); }
+ catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("extensions.logging"); }
+
+ TimeValue aTimeValue;
+ oslDateTime aDateTime;
+ OSL_VERIFY( osl_getSystemTime( &aTimeValue ) );
+ OSL_VERIFY( osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime ) );
+
+ char buffer[ 30 ];
+ const size_t buffer_size = sizeof( buffer );
+
+ snprintf( buffer, buffer_size, "%04i-%02i-%02i",
+ static_cast<int>(aDateTime.Year),
+ static_cast<int>(aDateTime.Month),
+ static_cast<int>(aDateTime.Day) );
+ OUString sDate = OUString::createFromAscii( buffer );
+
+ snprintf( buffer, buffer_size, "%02i-%02i-%02i.%03i",
+ static_cast<int>(aDateTime.Hours),
+ static_cast<int>(aDateTime.Minutes),
+ static_cast<int>(aDateTime.Seconds),
+ ::sal::static_int_cast< sal_Int16 >( aDateTime.NanoSeconds / 10000000 ) );
+ OUString sTime = OUString::createFromAscii( buffer );
+
+ OUString sDateTime = sDate + "." + sTime;
+
+ oslProcessIdentifier aProcessId = 0;
+ oslProcessInfo info;
+ info.Size = sizeof (oslProcessInfo);
+ if ( osl_getProcessInfo ( nullptr, osl_Process_IDENTIFIER, &info ) == osl_Process_E_None)
+ aProcessId = info.Ident;
+ OUString aPID = OUString::number( aProcessId );
+
+ Variable const aVariables[] =
+ {
+ {std::u16string_view(u"$(loggername)"), sLoggerName},
+ {std::u16string_view(u"$(date)"), sDate},
+ {std::u16string_view(u"$(time)"), sTime},
+ {std::u16string_view(u"$(datetime)"), sDateTime},
+ {std::u16string_view(u"$(pid)"), aPID}
+ };
+
+ for (Variable const & aVariable : aVariables)
+ {
+ sal_Int32 nVariableIndex = _inout_rFileURL.indexOf( aVariable.pVariablePattern );
+ if (nVariableIndex >= 0)
+ {
+ _inout_rFileURL = _inout_rFileURL.replaceAt( nVariableIndex, aVariable.pVariablePattern.size(), aVariable.sVariableValue );
+ }
+ }
+ }
+
+
+ void lcl_transformFileHandlerSettings_nothrow( const Reference< XLogger >& _rxLogger, const OUString& _rSettingName, Any& _inout_rSettingValue )
+ {
+ if ( _rSettingName != "FileURL" )
+ // not interested in this setting
+ return;
+
+ OUString sURL;
+ OSL_VERIFY( _inout_rSettingValue >>= sURL );
+ lcl_substituteFileHandlerURLVariables_nothrow( _rxLogger, sURL );
+ _inout_rSettingValue <<= sURL;
+ }
+
+
+ Reference< XInterface > lcl_createInstanceFromSetting_throw(
+ const Reference<XComponentContext>& _rContext,
+ const Reference< XLogger >& _rxLogger,
+ const Reference< XNameAccess >& _rxLoggerSettings,
+ const char* _pServiceNameAsciiNodeName,
+ const char* _pServiceSettingsAsciiNodeName,
+ SettingTranslation _pSettingTranslation = nullptr
+ )
+ {
+ Reference< XInterface > xInstance;
+
+ // read the settings for the to-be-created service
+ Reference< XNameAccess > xServiceSettingsNode( _rxLoggerSettings->getByName(
+ OUString::createFromAscii( _pServiceSettingsAsciiNodeName ) ), UNO_QUERY_THROW );
+
+ Sequence< OUString > aSettingNames( xServiceSettingsNode->getElementNames() );
+ size_t nServiceSettingCount( aSettingNames.getLength() );
+ Sequence< NamedValue > aSettings( nServiceSettingCount );
+ if ( nServiceSettingCount )
+ {
+ const OUString* pSettingNames = aSettingNames.getConstArray();
+ const OUString* pSettingNamesEnd = aSettingNames.getConstArray() + aSettingNames.getLength();
+ NamedValue* pSetting = aSettings.getArray();
+
+ for ( ;
+ pSettingNames != pSettingNamesEnd;
+ ++pSettingNames, ++pSetting
+ )
+ {
+ pSetting->Name = *pSettingNames;
+ pSetting->Value = xServiceSettingsNode->getByName( *pSettingNames );
+
+ if ( _pSettingTranslation )
+ _pSettingTranslation( _rxLogger, pSetting->Name, pSetting->Value );
+ }
+ }
+
+ OUString sServiceName;
+ _rxLoggerSettings->getByName( OUString::createFromAscii( _pServiceNameAsciiNodeName ) ) >>= sServiceName;
+ if ( !sServiceName.isEmpty() )
+ {
+ bool bSuccess = false;
+ if ( aSettings.hasElements() )
+ {
+ Sequence< Any > aConstructionArgs{ Any(aSettings) };
+ xInstance = _rContext->getServiceManager()->createInstanceWithArgumentsAndContext(sServiceName, aConstructionArgs, _rContext);
+ bSuccess = xInstance.is();
+ }
+ else
+ {
+ xInstance = _rContext->getServiceManager()->createInstanceWithContext(sServiceName, _rContext);
+ bSuccess = xInstance.is();
+ }
+
+ if ( !bSuccess )
+ throw ServiceNotRegisteredException( sServiceName );
+ }
+
+ return xInstance;
+ }
+ }
+
+
+ void initializeLoggerFromConfiguration( const Reference<XComponentContext>& _rContext, const Reference< XLogger >& _rxLogger )
+ {
+ try
+ {
+ if ( !_rxLogger.is() )
+ throw NullPointerException();
+
+ Reference< XMultiServiceFactory > xConfigProvider(
+ css::configuration::theDefaultProvider::get(_rContext));
+
+ // write access to the "Settings" node (which includes settings for all loggers)
+ Sequence<Any> aArguments{ Any(NamedValue(
+ "nodepath", Any(OUString("/org.openoffice.Office.Logging/Settings")))) };
+ Reference< XNameContainer > xAllSettings( xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess",
+ aArguments
+ ), UNO_QUERY_THROW );
+
+ OUString sLoggerName( _rxLogger->getName() );
+ if ( !xAllSettings->hasByName( sLoggerName ) )
+ {
+ // no node yet for this logger. Create default settings.
+ Reference< XSingleServiceFactory > xNodeFactory( xAllSettings, UNO_QUERY_THROW );
+ Reference< XInterface > xLoggerSettings( xNodeFactory->createInstance(), css::uno::UNO_SET_THROW );
+ xAllSettings->insertByName( sLoggerName, Any( xLoggerSettings ) );
+ Reference< XChangesBatch > xChanges( xAllSettings, UNO_QUERY_THROW );
+ xChanges->commitChanges();
+ }
+
+ // actually read and forward the settings
+ Reference< XNameAccess > xLoggerSettings( xAllSettings->getByName( sLoggerName ), UNO_QUERY_THROW );
+
+ // the log level
+ sal_Int32 nLogLevel( LogLevel::OFF );
+ OSL_VERIFY( xLoggerSettings->getByName("LogLevel") >>= nLogLevel );
+ _rxLogger->setLevel( nLogLevel );
+
+ // the default handler, if any
+ Reference< XInterface > xUntyped( lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultHandler", "HandlerSettings", &lcl_transformFileHandlerSettings_nothrow ) );
+ if ( !xUntyped.is() )
+ // no handler -> we're done
+ return;
+ Reference< XLogHandler > xHandler( xUntyped, UNO_QUERY_THROW );
+ _rxLogger->addLogHandler( xHandler );
+
+ // The newly created handler might have an own (default) level. Ensure that it uses
+ // the same level as the logger.
+ xHandler->setLevel( nLogLevel );
+
+ // the default formatter for the handler
+ xUntyped = lcl_createInstanceFromSetting_throw( _rContext, _rxLogger, xLoggerSettings, "DefaultFormatter", "FormatterSettings" );
+ if ( !xUntyped.is() )
+ // no formatter -> we're done
+ return;
+ Reference< XLogFormatter > xFormatter( xUntyped, UNO_QUERY_THROW );
+ xHandler->setFormatter( xFormatter );
+
+ // TODO: we could first create the formatter, then the handler. This would allow
+ // passing the formatter as value in the component context, so the handler would
+ // not create an own default formatter
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ }
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/loggerconfig.hxx b/extensions/source/logging/loggerconfig.hxx
new file mode 100644
index 000000000..b08628cf1
--- /dev/null
+++ b/extensions/source/logging/loggerconfig.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/logging/XLogger.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+
+namespace logging
+{
+
+
+ /** initializes the given logger from the configuration
+
+ The configuration node /org.openoffice.Office.Logging/Settings/<logger_name>
+ is examined for this. If it does not yet exist, it will be created.
+
+ The function creates a default handler and a default formatter, as specified in the
+ configuration.
+
+ This function is currently external to the logger instance. Perhaps it can, on the long
+ run, be moved to the logger implementation - not sure if it's the best place.
+ */
+ void initializeLoggerFromConfiguration(
+ const css::uno::Reference<css::uno::XComponentContext>& _rContext,
+ const css::uno::Reference< css::logging::XLogger >& _rxLogger
+ );
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/loghandler.cxx b/extensions/source/logging/loghandler.cxx
new file mode 100644
index 000000000..afc33605b
--- /dev/null
+++ b/extensions/source/logging/loghandler.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "loghandler.hxx"
+
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/logging/PlainTextFormatter.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <rtl/tencinfo.h>
+
+
+namespace logging
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::logging::XLogFormatter;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::logging::PlainTextFormatter;
+
+ namespace LogLevel = ::com::sun::star::logging::LogLevel;
+
+ LogHandlerHelper::LogHandlerHelper( const Reference< XComponentContext >& _rxContext, ::osl::Mutex& _rMutex, ::cppu::OBroadcastHelper& _rBHelper )
+ :m_eEncoding( RTL_TEXTENCODING_UTF8 )
+ ,m_nLevel( LogLevel::SEVERE )
+ ,m_xContext( _rxContext )
+ ,m_rMutex( _rMutex )
+ ,m_rBHelper( _rBHelper )
+ ,m_bInitialized( false )
+ {
+ }
+
+
+ void LogHandlerHelper::initFromSettings( const ::comphelper::NamedValueCollection& _rSettings )
+ {
+ OUString sEncoding;
+ if ( _rSettings.get_ensureType( "Encoding", sEncoding ) )
+ {
+ if ( !setEncoding( sEncoding ) )
+ throw IllegalArgumentException();
+ }
+
+ _rSettings.get_ensureType( "Formatter", m_xFormatter );
+ _rSettings.get_ensureType( "Level", m_nLevel );
+ }
+
+
+ void LogHandlerHelper::enterMethod()
+ {
+ m_rMutex.acquire();
+
+ if ( !m_bInitialized )
+ throw DisposedException("component not initialized" );
+
+ if ( m_rBHelper.bDisposed )
+ throw DisposedException("component already disposed" );
+
+ // fallback settings, in case they weren't passed at construction time
+ if ( !getFormatter().is() )
+ {
+ try
+ {
+ Reference< XLogFormatter > xFormatter( PlainTextFormatter::create( m_xContext ), css::uno::UNO_SET_THROW );
+ setFormatter( xFormatter );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ }
+ }
+
+
+ bool LogHandlerHelper::getEncoding( OUString& _out_rEncoding ) const
+ {
+ const char* pMimeCharset = rtl_getMimeCharsetFromTextEncoding( m_eEncoding );
+ if ( pMimeCharset )
+ {
+ _out_rEncoding = OUString::createFromAscii( pMimeCharset );
+ return true;
+ }
+ _out_rEncoding.clear();
+ return false;
+ }
+
+
+ bool LogHandlerHelper::setEncoding( std::u16string_view _rEncoding )
+ {
+ OString sAsciiEncoding( OUStringToOString( _rEncoding, RTL_TEXTENCODING_ASCII_US ) );
+ rtl_TextEncoding eEncoding = rtl_getTextEncodingFromMimeCharset( sAsciiEncoding.getStr() );
+ if ( eEncoding != RTL_TEXTENCODING_DONTKNOW )
+ {
+ m_eEncoding = eEncoding;
+ return true;
+ }
+ return false;
+ }
+
+
+ bool LogHandlerHelper::formatForPublishing( const LogRecord& _rRecord, OString& _out_rEntry ) const
+ {
+ if ( _rRecord.Level < getLevel() )
+ // not to be published due to low level
+ return false;
+
+ try
+ {
+ Reference< XLogFormatter > xFormatter( getFormatter(), css::uno::UNO_SET_THROW );
+ OUString sEntry( xFormatter->format( _rRecord ) );
+ _out_rEntry = OUStringToOString( sEntry, getTextEncoding() );
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ return false;
+ }
+
+
+ bool LogHandlerHelper::getEncodedHead( OString& _out_rHead ) const
+ {
+ try
+ {
+ Reference< XLogFormatter > xFormatter( getFormatter(), css::uno::UNO_SET_THROW );
+ OUString sHead( xFormatter->getHead() );
+ _out_rHead = OUStringToOString( sHead, getTextEncoding() );
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ return false;
+ }
+
+
+ bool LogHandlerHelper::getEncodedTail( OString& _out_rTail ) const
+ {
+ try
+ {
+ Reference< XLogFormatter > xFormatter( getFormatter(), css::uno::UNO_SET_THROW );
+ OUString sTail( xFormatter->getTail() );
+ _out_rTail = OUStringToOString( sTail, getTextEncoding() );
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.logging");
+ }
+ return false;
+ }
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/loghandler.hxx b/extensions/source/logging/loghandler.hxx
new file mode 100644
index 000000000..02f4fb773
--- /dev/null
+++ b/extensions/source/logging/loghandler.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 <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/logging/XLogFormatter.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/logging/LogRecord.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <cppuhelper/interfacecontainer.hxx>
+#include <rtl/string.hxx>
+
+
+namespace logging
+{
+
+ class LogHandlerHelper
+ {
+ private:
+ // <attributes>
+ rtl_TextEncoding m_eEncoding;
+ sal_Int32 m_nLevel;
+ css::uno::Reference< css::logging::XLogFormatter >
+ m_xFormatter;
+ // <//attributes>
+
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xContext;
+ ::osl::Mutex& m_rMutex;
+ ::cppu::OBroadcastHelper& m_rBHelper;
+ bool m_bInitialized;
+
+ public:
+ LogHandlerHelper(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ ::osl::Mutex& _rMutex,
+ ::cppu::OBroadcastHelper& _rBHelper
+ );
+
+ public:
+ void setIsInitialized() { m_bInitialized = true; }
+
+ bool getEncoding( OUString& _out_rEncoding ) const;
+ bool setEncoding( std::u16string_view _rEncoding );
+
+ rtl_TextEncoding
+ getTextEncoding() const { return m_eEncoding; }
+
+ const css::uno::Reference< css::logging::XLogFormatter >&
+ getFormatter() const { return m_xFormatter; }
+ void
+ setFormatter( const css::uno::Reference< css::logging::XLogFormatter >& _rxFormatter )
+ {
+ m_xFormatter = _rxFormatter;
+ }
+
+ sal_Int32
+ getLevel() const { return m_nLevel; }
+ void
+ setLevel( const sal_Int32 _nLevel )
+ {
+ m_nLevel = _nLevel;
+ }
+
+ /** prepares implementation of a public accessible method of a log handler
+
+ <code>enterMethod</code> does the following things:
+ <ul><li>It acquires the mutex given in the constructor.</li>
+ <li>It checks whether the component is already initialized, and throws an exception if not os.</li>
+ <li>It checks whether the component is already disposed, and throws an exception if not os.</li>
+ <li>It creates a default formatter (PlainTextFormatter), if no formatter exists at this time.</li>
+ </ul>
+ */
+ void enterMethod();
+
+ /** formats a record for publishing it
+
+ The method first checks whether the records log level is greater or equal our own
+ log level. If not, <FALSE/> is returned.
+
+ Second, our formatter is used to create a unicode string from the log record. If an error occurs
+ during this, e.g. if the formatter is <NULL/> or throws an exception during formatting,
+ <FALSE/> is returned.
+
+ Finally, the unicode string is encoded into a byte string, using our encoding setting. Then,
+ <TRUE/> is returned.
+ */
+ bool formatForPublishing( const css::logging::LogRecord& _rRecord, OString& _out_rEntry ) const;
+
+ /** retrieves our formatter's heading, encoded with our encoding
+
+ @return <TRUE/> in case of success, <FALSE/> if any error occurred
+ */
+ bool getEncodedHead( OString& _out_rHead ) const;
+
+ /** retrieves our formatter's tail, encoded with our encoding
+
+ @return <TRUE/> in case of success, <FALSE/> if any error occurred
+ */
+ bool getEncodedTail( OString& _out_rTail ) const;
+
+ /** initializes the instance from a collection of named settings
+
+ The recognized named settings are <code>Encoding</code>, <code>Formatter</code>, and <code>Level</code>,
+ which initialize the respective attributes.
+
+ Settings which are recognized are remove from the given collection. This allows
+ the caller to determine whether or not the collection contained any unsupported
+ items, and react appropriately.
+
+ @throws IllegalArgumentException
+ if one of the values in the collection is of wrong type.
+ */
+ void initFromSettings( const ::comphelper::NamedValueCollection& _rSettings );
+ };
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/logrecord.cxx b/extensions/source/logging/logrecord.cxx
new file mode 100644
index 000000000..26f59e841
--- /dev/null
+++ b/extensions/source/logging/logrecord.cxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "logrecord.hxx"
+
+#include <osl/time.h>
+#include <osl/thread.h>
+#include <osl/thread.hxx>
+#include <osl/diagnose.h>
+
+
+namespace logging
+{
+
+
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::util::DateTime;
+
+ namespace
+ {
+ /** returns a string representation of the current thread
+
+ @todo
+ We need a way to retrieve the current UNO thread ID as string,
+ which is issue #i77342#
+ */
+ OUString getCurrentThreadID()
+ {
+ oslThreadIdentifier nThreadID( osl::Thread::getCurrentIdentifier() );
+ return OUString::number( static_cast<sal_Int64>(nThreadID) );
+ }
+ }
+
+
+ LogRecord createLogRecord( const OUString& _rLoggerName, const OUString& _rClassName,
+ const OUString& _rMethodName, const OUString& _rMessage,
+ sal_Int32 _nLogLevel, oslInterlockedCount _nEventNumber )
+ {
+ TimeValue aTimeValue;
+ osl_getSystemTime( &aTimeValue );
+
+ oslDateTime aDateTime;
+ OSL_VERIFY( osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime ) );
+
+ DateTime aTimeStamp;
+ aTimeStamp.Year = aDateTime.Year;
+ aTimeStamp.Month = aDateTime.Month;
+ aTimeStamp.Day = aDateTime.Day;
+ aTimeStamp.Hours = aDateTime.Hours;
+ aTimeStamp.Minutes = aDateTime.Minutes;
+ aTimeStamp.Seconds = aDateTime.Seconds;
+ aTimeStamp.NanoSeconds = aDateTime.NanoSeconds;
+
+ return LogRecord(
+ _rLoggerName,
+ _rClassName,
+ _rMethodName,
+ _rMessage,
+ aTimeStamp,
+ _nEventNumber,
+ getCurrentThreadID(),
+ _nLogLevel
+ );
+ }
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/logrecord.hxx b/extensions/source/logging/logrecord.hxx
new file mode 100644
index 000000000..ad6e350cf
--- /dev/null
+++ b/extensions/source/logging/logrecord.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 <com/sun/star/logging/LogRecord.hpp>
+
+#include <osl/interlck.h>
+
+
+namespace logging
+{
+
+ css::logging::LogRecord createLogRecord(
+ const OUString& _rLoggerName,
+ const OUString& _rClassName,
+ const OUString& _rMethodName,
+ const OUString& _rMessage,
+ sal_Int32 _nLogLevel,
+ oslInterlockedCount _nEventNumber
+ );
+
+ inline css::logging::LogRecord createLogRecord(
+ const OUString& _rLoggerName,
+ const OUString& _rMessage,
+ sal_Int32 _nLogLevel,
+ oslInterlockedCount _nEventNumber
+ )
+ {
+ return createLogRecord( _rLoggerName, OUString(), OUString(), _rMessage, _nLogLevel, _nEventNumber );
+ }
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/methodguard.hxx b/extensions/source/logging/methodguard.hxx
new file mode 100644
index 000000000..189462eae
--- /dev/null
+++ b/extensions/source/logging/methodguard.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
+
+
+namespace logging
+{
+ template < class COMPONENT >
+ class ComponentMethodGuard
+ {
+ private:
+ COMPONENT& m_rHandler;
+
+ public:
+ class Access
+ {
+ private:
+ friend class ComponentMethodGuard;
+ Access() { }
+ };
+
+ public:
+ explicit ComponentMethodGuard( COMPONENT& _rHandler )
+ :m_rHandler( _rHandler )
+ {
+ m_rHandler.enterMethod( Access() );
+ }
+ ~ComponentMethodGuard()
+ {
+ m_rHandler.leaveMethod( Access() );
+ }
+ };
+
+
+} // namespace logging
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/plaintextformatter.cxx b/extensions/source/logging/plaintextformatter.cxx
new file mode 100644
index 000000000..23392b61c
--- /dev/null
+++ b/extensions/source/logging/plaintextformatter.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 <sal/config.h>
+
+#include <com/sun/star/logging/XLogFormatter.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/thread.h>
+
+#include <stdio.h>
+
+namespace logging
+{
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::logging::LogRecord;
+ using ::com::sun::star::uno::XInterface;
+
+ namespace {
+
+ class PlainTextFormatter : public cppu::WeakImplHelper<css::logging::XLogFormatter, css::lang::XServiceInfo>
+ {
+ public:
+ PlainTextFormatter();
+
+ private:
+ // XLogFormatter
+ virtual OUString SAL_CALL getHead( ) override;
+ virtual OUString SAL_CALL format( const LogRecord& Record ) override;
+ virtual OUString SAL_CALL getTail( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& _rServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+ };
+
+ }
+
+ PlainTextFormatter::PlainTextFormatter()
+ {
+ }
+
+ OUString SAL_CALL PlainTextFormatter::getHead( )
+ {
+ return
+ " event no" // column 1: the event number
+ " "
+ "thread " // column 2: the thread ID
+ " "
+ "date " // column 3: date
+ " "
+ "time " // column 4: time
+ " "
+ "(class/method:) message" // column 5: class/method/message
+ "\n";
+ }
+
+
+ OUString SAL_CALL PlainTextFormatter::format( const LogRecord& _rRecord )
+ {
+ char buffer[ sizeof("-32768-65535-65535 65535:65535:65535.4294967295") ];
+ // reserve enough space for hypothetical max length
+ const int buffer_size = sizeof( buffer );
+ int used = snprintf( buffer, buffer_size, "%10i", static_cast<int>(_rRecord.SequenceNumber) );
+ if ( used >= buffer_size || used < 0 )
+ buffer[ buffer_size - 1 ] = 0;
+
+ OUStringBuffer aLogEntry;
+ aLogEntry.appendAscii( buffer );
+ aLogEntry.append( " " );
+
+ OString sThreadID( OUStringToOString( _rRecord.ThreadID, osl_getThreadTextEncoding() ) );
+ snprintf( buffer, buffer_size, "%8s", sThreadID.getStr() );
+ aLogEntry.appendAscii( buffer );
+ aLogEntry.append( " " );
+
+ snprintf( buffer, buffer_size, "%04" SAL_PRIdINT32 "-%02" SAL_PRIuUINT32 "-%02" SAL_PRIuUINT32 " %02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ".%09" SAL_PRIuUINT32,
+ static_cast<sal_Int32>(_rRecord.LogTime.Year), static_cast<sal_uInt32>(_rRecord.LogTime.Month), static_cast<sal_uInt32>(_rRecord.LogTime.Day),
+ static_cast<sal_uInt32>(_rRecord.LogTime.Hours), static_cast<sal_uInt32>(_rRecord.LogTime.Minutes), static_cast<sal_uInt32>(_rRecord.LogTime.Seconds), _rRecord.LogTime.NanoSeconds );
+ aLogEntry.appendAscii( buffer );
+ aLogEntry.append( " " );
+
+ if ( !(_rRecord.SourceClassName.isEmpty() || _rRecord.SourceMethodName.isEmpty()) )
+ {
+ aLogEntry.append( _rRecord.SourceClassName );
+ aLogEntry.append( "::" );
+ aLogEntry.append( _rRecord.SourceMethodName );
+ aLogEntry.append( ": " );
+ }
+
+ aLogEntry.append( _rRecord.Message );
+ aLogEntry.append( "\n" );
+
+ return aLogEntry.makeStringAndClear();
+ }
+
+
+ OUString SAL_CALL PlainTextFormatter::getTail( )
+ {
+ // no tail
+ return OUString();
+ }
+
+ sal_Bool SAL_CALL PlainTextFormatter::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+
+ OUString SAL_CALL PlainTextFormatter::getImplementationName()
+ {
+ return "com.sun.star.comp.extensions.PlainTextFormatter";
+ }
+
+ Sequence< OUString > SAL_CALL PlainTextFormatter::getSupportedServiceNames()
+ {
+ return { "com.sun.star.logging.PlainTextFormatter" };
+ }
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_extensions_PlainTextFormatter(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new logging::PlainTextFormatter());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/logging/simpletextformatter.cxx b/extensions/source/logging/simpletextformatter.cxx
new file mode 100644
index 000000000..b54fea5a6
--- /dev/null
+++ b/extensions/source/logging/simpletextformatter.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/logging/XLogFormatter.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+namespace logging
+{
+using css::logging::LogRecord;
+using namespace css::uno;
+
+namespace
+{
+class SimpleTextFormatter
+ : public cppu::WeakImplHelper<css::logging::XLogFormatter, css::lang::XServiceInfo>
+{
+public:
+ SimpleTextFormatter();
+
+private:
+ // XLogFormatter
+ virtual OUString SAL_CALL getHead() override;
+ virtual OUString SAL_CALL format(const LogRecord& Record) override;
+ virtual OUString SAL_CALL getTail() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& _rServiceName) override;
+ virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+}
+
+SimpleTextFormatter::SimpleTextFormatter() {}
+
+OUString SAL_CALL SimpleTextFormatter::getHead() { return OUString(); }
+
+OUString SAL_CALL SimpleTextFormatter::format(const LogRecord& _rRecord)
+{
+ OUString aLogEntry;
+ // Highlight warnings
+ if (_rRecord.Level == css::logging::LogLevel::SEVERE)
+ aLogEntry = "ERROR: ";
+ else if (_rRecord.Level == css::logging::LogLevel::WARNING)
+ aLogEntry = "WARNING: ";
+
+ return aLogEntry + _rRecord.Message + "\n";
+}
+
+OUString SAL_CALL SimpleTextFormatter::getTail() { return OUString(); }
+
+sal_Bool SAL_CALL SimpleTextFormatter::supportsService(const OUString& _rServiceName)
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+OUString SAL_CALL SimpleTextFormatter::getImplementationName()
+{
+ return "com.sun.star.comp.extensions.SimpleTextFormatter";
+}
+
+Sequence<OUString> SAL_CALL SimpleTextFormatter::getSupportedServiceNames()
+{
+ return { "com.sun.star.logging.SimpleTextFormatter" };
+}
+
+} // namespace logging
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_extensions_SimpleTextFormatter(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new logging::SimpleTextFormatter());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/GetMetadataForFile.h b/extensions/source/macosx/spotlight/GetMetadataForFile.h
new file mode 100644
index 000000000..51086827e
--- /dev/null
+++ b/extensions/source/macosx/spotlight/GetMetadataForFile.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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
+
+Boolean GetMetadataForFile(
+ void * thisInterface, CFMutableDictionaryRef attributes,
+ CFStringRef contentTypeUTI, CFStringRef pathToFile);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/GetMetadataForFile.m b/extensions/source/macosx/spotlight/GetMetadataForFile.m
new file mode 100644
index 000000000..a1dcdbe61
--- /dev/null
+++ b/extensions/source/macosx/spotlight/GetMetadataForFile.m
@@ -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 <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+#include <Foundation/Foundation.h>
+
+#include "GetMetadataForFile.h"
+#import "OOoSpotlightImporter.h"
+
+/* -----------------------------------------------------------------------------
+ Get metadata attributes from file
+
+ This function's job is to extract useful information your file format supports
+ and return it as a dictionary
+ ----------------------------------------------------------------------------- */
+
+Boolean GetMetadataForFile(void* thisInterface,
+ CFMutableDictionaryRef attributes,
+ CFStringRef contentTypeUTI,
+ CFStringRef pathToFile)
+{
+ (void) thisInterface; /* unused */
+ /* Pull any available metadata from the file at the specified path */
+ /* Return the attribute keys and attribute values in the dict */
+ /* Return TRUE if successful, FALSE if there was no data provided */
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ OOoSpotlightImporter *importer = [OOoSpotlightImporter new];
+
+ Boolean importOK = NO;
+ @try {
+ importOK = [importer importDocument:(NSString*)pathToFile
+ contentType:(NSString*)contentTypeUTI
+ attributes:(NSMutableDictionary*)attributes];
+ }
+ @catch (NSException *exception) {
+ NSLog(@"main: Caught %@: %@", [exception name], [exception reason]);
+ }
+
+ [importer release];
+
+ [pool release];
+
+ return importOK;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/OOoContentDataParser.h b/extensions/source/macosx/spotlight/OOoContentDataParser.h
new file mode 100644
index 000000000..dc07a3166
--- /dev/null
+++ b/extensions/source/macosx/spotlight/OOoContentDataParser.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface OOoContentDataParser : NSObject <NSXMLParserDelegate> {
+ // indicates if we are interested in an element's content
+ BOOL shouldReadCharacters;
+
+ // the MD importer's values
+ NSMutableDictionary *mdiValues;
+
+ // all of the text inside a document
+ NSMutableString *textContent;
+
+ // the current element's content
+ NSMutableString *runningTextContent;
+}
+
+- (void)parseXML:(NSData*)data intoDictionary:(NSMutableDictionary*)dict;
+
+// delegates
+- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict;
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
+
+- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError;
+
+- (void)parserDidEndDocument:(NSXMLParser *)parser;
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/OOoContentDataParser.m b/extensions/source/macosx/spotlight/OOoContentDataParser.m
new file mode 100644
index 000000000..e1f51e5b9
--- /dev/null
+++ b/extensions/source/macosx/spotlight/OOoContentDataParser.m
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <objc/objc-runtime.h>
+
+#import "OOoContentDataParser.h"
+
+@implementation OOoContentDataParser
+
+- (id)init
+{
+ if ((self = [super init]) != nil) {
+ shouldReadCharacters = NO;
+ textContent = nil;
+ runningTextContent = nil;
+
+ return self;
+ }
+
+ return nil;
+}
+
+- (void)parseXML:(NSData*)data intoDictionary:(NSMutableDictionary*)dict
+{
+ mdiValues = dict;
+
+ //NSLog(@"data: %@ %d", data, [data length]);
+
+ //init parser settings
+ shouldReadCharacters = NO;
+
+ NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
+
+ [parser setDelegate:self];
+
+ [parser setShouldResolveExternalEntities:NO];
+ [parser parse];
+
+ [parser release];
+
+ //NSLog(@"finished");
+}
+
+- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
+{
+ (void) parser; // unused
+ (void) namespaceURI; // FIXME this should not be ignored but should be used
+ // instead of text: prefix in the comparison below!
+ (void) qualifiedName; // unused
+ (void) attributeDict; // unused
+ // all text content is stored inside <text:p> elements
+ if ([elementName isEqualToString:@"text:p"] == YES) {
+ runningTextContent = [NSMutableString new];
+ shouldReadCharacters = YES;
+ //NSLog(@"start");
+ } else {
+ return;
+ }
+
+ //NSLog(@"start element %@", elementName);
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
+{
+ (void) parser; // unused
+ (void) elementName; // unused
+ (void) namespaceURI; // unused
+ (void) qName; // unused
+ if (shouldReadCharacters == TRUE) {
+ if (textContent == nil) {
+ textContent = [NSMutableString new];
+ } else if ([runningTextContent isEqualToString:@""] == NO) {
+ // separate by whitespace
+ [textContent appendString:@" "];
+ }
+ //NSLog(@"end");
+
+ [textContent appendString:[NSString stringWithString:runningTextContent]];
+ [runningTextContent release];
+ runningTextContent = nil;
+ }
+ shouldReadCharacters = NO;
+}
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
+{
+ (void) parser; // unused
+ if (shouldReadCharacters == NO) {
+ return;
+ }
+ //NSLog(string);
+
+ [runningTextContent appendString:string];
+
+ //NSLog(@"read: %@", string);
+
+}
+
+- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
+{
+ //NSLog(@"parsing finished with error");
+ NSLog(@"An error occurred parsing the document. (Error %li, Description: %@, Line: %li, Column: %li)", (long) [parseError code],
+ [[parser parserError] localizedDescription], (long) [parser lineNumber],
+ (long) [parser columnNumber]);
+
+ if (runningTextContent != nil) {
+ [runningTextContent release];
+ runningTextContent = nil;
+ }
+ if (textContent != nil) {
+ [textContent release];
+ textContent = nil;
+ }
+}
+
+- (void)parserDidEndDocument:(NSXMLParser *)parser
+{
+ (void) parser; // unused
+ if (textContent != nil && [textContent length] > 0) {
+ [mdiValues setObject:[NSString stringWithString:textContent] forKey:(NSString*)kMDItemTextContent];
+ [textContent release];
+ textContent = nil;
+ }
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/OOoMetaDataParser.h b/extensions/source/macosx/spotlight/OOoMetaDataParser.h
new file mode 100644
index 000000000..85d48b173
--- /dev/null
+++ b/extensions/source/macosx/spotlight/OOoMetaDataParser.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface OOoMetaDataParser : NSObject <NSXMLParserDelegate> {
+ //indicates if content should be read
+ BOOL shouldReadCharacters;
+ //indicates if the current element is a custom metadata tag
+ BOOL isCustom;
+
+ NSMutableDictionary *metaValues;
+ NSMutableString *textCurrentElement;
+ NSString *customAttribute;
+}
+
+- (void)parseXML:(NSData*)data intoDictionary:(NSMutableDictionary*)dict;
+
+//delegates
+- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict;
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
+
+- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError;
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/OOoMetaDataParser.m b/extensions/source/macosx/spotlight/OOoMetaDataParser.m
new file mode 100644
index 000000000..1e7cac685
--- /dev/null
+++ b/extensions/source/macosx/spotlight/OOoMetaDataParser.m
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <objc/objc-runtime.h>
+
+#import "OOoMetaDataParser.h"
+
+static NSSet *singleValueXMLElements;
+static NSSet *multiValueXMLElements;
+static NSDictionary *metaXML2MDIKeys;
+
+@implementation OOoMetaDataParser
+
++ (void)initialize
+{
+ static BOOL isInitialized = NO;
+
+ if (isInitialized == NO) {
+ //set up the meta elements with only one value
+ NSMutableSet *temp = [NSMutableSet new];
+//FIXME these should use namespace URIs and not prefixes
+ [temp addObject:@"dc:title"];
+ [temp addObject:@"dc:description"];
+ [temp addObject:@"meta:user-defined"];
+ singleValueXMLElements = [[NSSet setWithSet:temp] retain];
+
+ //set up the meta elements that can have more than one value
+ [temp removeAllObjects];
+ [temp addObject:@"dc:subject"];
+ [temp addObject:@"meta:keyword"];
+ [temp addObject:@"meta:initial-creator"];
+ [temp addObject:@"dc:creator"];
+ multiValueXMLElements = [[NSSet setWithSet:temp] retain];
+ [temp release];
+
+ //set up the map to store the values with the correct MDI keys
+ NSMutableDictionary *tempDict = [NSMutableDictionary new];
+ [tempDict setObject:(NSString*)kMDItemTitle forKey:@"dc:title"];
+ [tempDict setObject:(NSString*)kMDItemDescription forKey:@"dc:description"];
+ [tempDict setObject:(NSString*)kMDItemKeywords forKey:@"dc:subject"];
+ [tempDict setObject:(NSString*)kMDItemAuthors forKey:@"meta:initial-creator"];
+ [tempDict setObject:(NSString*)kMDItemAuthors forKey:@"dc:creator"];
+ [tempDict setObject:(NSString*)kMDItemKeywords forKey:@"meta:keyword"];
+ [tempDict setObject:@"org_openoffice_opendocument_custominfo1" forKey:@"Info 1"];
+ [tempDict setObject:@"org_openoffice_opendocument_custominfo2" forKey:@"Info 2"];
+ [tempDict setObject:@"org_openoffice_opendocument_custominfo3" forKey:@"Info 3"];
+ [tempDict setObject:@"org_openoffice_opendocument_custominfo4" forKey:@"Info 4"];
+ metaXML2MDIKeys = [[NSDictionary dictionaryWithDictionary:tempDict] retain];
+ [tempDict release];
+
+ isInitialized = YES;
+ }
+}
+
+- (id)init
+{
+ if ((self = [super init]) != nil) {
+ shouldReadCharacters = NO;
+ textCurrentElement = nil;
+
+ return self;
+ }
+
+ return nil;
+}
+
+- (void)parseXML:(NSData*)data intoDictionary:(NSMutableDictionary*)dict
+{
+ metaValues = dict;
+
+ //NSLog(@"data: %@ %d", data, [data length]);
+
+ //init parser settings
+ shouldReadCharacters = NO;
+
+ NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
+
+ [parser setDelegate:self];
+
+ [parser setShouldResolveExternalEntities:NO];
+ [parser parse];
+
+ [parser release];
+
+ //NSLog(@"finished parsing meta");
+}
+
+- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
+{
+ (void) parser; // unused
+ (void) namespaceURI; // FIXME this should not be ignored but should be used
+ // instead of meta: prefix in the comparison below!
+ (void) qualifiedName; // unused
+// NSLog(@"<%@>", elementName);
+ if ([singleValueXMLElements containsObject:elementName] == YES) {
+ shouldReadCharacters = YES;
+ } else if ([multiValueXMLElements containsObject:elementName] == YES) {
+ shouldReadCharacters = YES;
+ } else {
+ //we are not interested in this element
+ shouldReadCharacters = NO;
+ return;
+ }
+
+ if (shouldReadCharacters == YES) {
+ textCurrentElement = [NSMutableString new];
+ isCustom = [elementName isEqualToString:@"meta:user-defined"];
+ if (isCustom == YES) {
+ customAttribute = [[attributeDict objectForKey:@"meta:name"] retain];
+ //NSLog(customAttribute);
+ }
+ }
+
+ //NSLog(@"start element %@", elementName);
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
+{
+ (void) parser; // unused
+ (void) namespaceURI; // unused
+ (void) qName; // unused
+// NSLog(@"</%@>", elementName);
+ if (shouldReadCharacters == YES) {
+ NSString *mdiName = nil;
+ if (isCustom == YES) {
+ mdiName = (NSString*)[metaXML2MDIKeys objectForKey:customAttribute];
+ } else {
+ mdiName = (NSString*)[metaXML2MDIKeys objectForKey:elementName];
+ }
+ //NSLog(@"mdiName: %@", mdiName);
+
+ if (mdiName == nil) {
+ return;
+ }
+
+ if ([singleValueXMLElements containsObject:elementName] == YES) {
+ [metaValues setObject:textCurrentElement forKey:mdiName];
+ } else {
+ // must be multi-value
+ NSMutableArray *arr = [metaValues objectForKey:mdiName];
+ if (arr == nil) {
+ // we have no array yet, create it
+ arr = [[NSMutableArray new] autorelease];
+ // and store it
+ [metaValues setObject:arr forKey:mdiName];
+ }
+ // only store an element once, no need for duplicates
+ if ([arr containsObject:textCurrentElement] == NO) {
+ [arr addObject:textCurrentElement];
+ }
+ }
+ // cleanup part 1
+ [textCurrentElement release];
+ if (isCustom == YES) {
+ [customAttribute release];
+ }
+ }
+
+ //cleanup part 2
+ shouldReadCharacters = NO;
+ isCustom = NO;
+}
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
+{
+ (void) parser; // unused
+// NSLog(@"%@", string);
+ if (shouldReadCharacters == NO) {
+ return;
+ }
+
+ // this delegate method might be called several times for a single element,
+ // so we have to collect the received data
+ [textCurrentElement appendString:string];
+
+ //NSLog(@"chars read: %@", string);
+}
+
+- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
+{
+ //NSLog(@"parsing finished with error");
+ NSLog(@"Error %li, Description: %@, Line: %li, Column: %li", (long) [parseError code],
+ [[parser parserError] localizedDescription], (long) [parser lineNumber],
+ (long) [parser columnNumber]);
+}
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/OOoSpotlightImporter.h b/extensions/source/macosx/spotlight/OOoSpotlightImporter.h
new file mode 100644
index 000000000..947f8dbf1
--- /dev/null
+++ b/extensions/source/macosx/spotlight/OOoSpotlightImporter.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface OOoSpotlightImporter : NSObject
+{
+}
+
+- (BOOL)importDocument:(NSString*)pathToFile
+ contentType:(NSString*)contentTypeUTI
+ attributes:(NSMutableDictionary*)attributes;
+
+- (NSFileHandle*)openZipFileAtPath:(NSString*)pathToFile;
+
+- (NSData*)metaDataFileFromZip:(NSFileHandle*)unzipFile;
+
+- (NSData*)contentDataFileFromZip:(NSFileHandle*)unzipFile;
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/OOoSpotlightImporter.m b/extensions/source/macosx/spotlight/OOoSpotlightImporter.m
new file mode 100644
index 000000000..a144fe259
--- /dev/null
+++ b/extensions/source/macosx/spotlight/OOoSpotlightImporter.m
@@ -0,0 +1,487 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#import <zlib.h>
+
+#import "OOoSpotlightImporter.h"
+#import "OOoMetaDataParser.h"
+#import "OOoContentDataParser.h"
+
+/* a dictionary to hold the UTIs */
+static NSDictionary *uti2kind;
+
+typedef struct {
+ unsigned short min_version;
+ unsigned short general_flag;
+ unsigned short compression;
+ unsigned short lastmod_time;
+ unsigned short lastmod_date;
+ unsigned crc32;
+ unsigned compressed_size;
+ unsigned uncompressed_size;
+ unsigned short filename_size;
+ unsigned short extra_field_size;
+ NSString *filename;
+ NSString *extra_field;
+} LocalFileHeader;
+
+typedef struct {
+ unsigned short creator_version;
+ unsigned short min_version;
+ unsigned short general_flag;
+ unsigned short compression;
+ unsigned short lastmod_time;
+ unsigned short lastmod_date;
+ unsigned crc32;
+ unsigned compressed_size;
+ unsigned uncompressed_size;
+ unsigned short filename_size;
+ unsigned short extra_field_size;
+ unsigned short file_comment_size;
+ unsigned short disk_num;
+ unsigned short internal_attr;
+ unsigned external_attr;
+ unsigned offset;
+ NSString *filename;
+ NSString *extra_field;
+ NSString *file_comment;
+} CentralDirectoryEntry;
+
+typedef struct {
+ unsigned short disk_num;
+ unsigned short cdir_disk;
+ unsigned short disk_entries;
+ unsigned short cdir_entries;
+ unsigned cdir_size;
+ unsigned cdir_offset;
+ unsigned short comment_size;
+ NSString *comment;
+} CentralDirectoryEnd;
+
+#define CDIR_ENTRY_SIG (0x02014b50)
+#define LOC_FILE_HEADER_SIG (0x04034b50)
+#define CDIR_END_SIG (0x06054b50)
+
+static unsigned char readByte(NSFileHandle *file)
+{
+ if (file == nil)
+ return 0;
+ NSData* tmpBuf = [file readDataOfLength: 1];
+ if (tmpBuf == nil)
+ return 0;
+ unsigned char *d = (unsigned char*)[tmpBuf bytes];
+ if (d == nil)
+ return 0;
+ return *d;
+}
+
+static unsigned short readShort(NSFileHandle *file)
+{
+ unsigned short p0 = (unsigned short)readByte(file);
+ unsigned short p1 = (unsigned short)readByte(file);
+ return (unsigned short)(p0|(p1<<8));
+}
+
+static unsigned readInt(NSFileHandle *file)
+{
+ unsigned p0 = (unsigned)readByte(file);
+ unsigned p1 = (unsigned)readByte(file);
+ unsigned p2 = (unsigned)readByte(file);
+ unsigned p3 = (unsigned)readByte(file);
+ return (unsigned)(p0|(p1<<8)|(p2<<16)|(p3<<24));
+}
+
+static bool readCentralDirectoryEnd(NSFileHandle *file, CentralDirectoryEnd *end)
+{
+ unsigned signature = readInt(file);
+ if (signature != CDIR_END_SIG)
+ return false;
+
+ end->disk_num = readShort(file);
+ end->cdir_disk = readShort(file);
+ end->disk_entries = readShort(file);
+ end->cdir_entries = readShort(file);
+ end->cdir_size = readInt(file);
+ end->cdir_offset = readInt(file);
+ end->comment_size = readShort(file);
+ NSData *data = [file readDataOfLength: end->comment_size];
+ end->comment = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ return true;
+}
+
+static bool readCentralDirectoryEntry(NSFileHandle *file, CentralDirectoryEntry *entry)
+{
+ unsigned signature = readInt(file);
+ if (signature != CDIR_ENTRY_SIG)
+ return false;
+
+ entry->creator_version = readShort(file);
+ entry->min_version = readShort(file);
+ entry->general_flag = readShort(file);
+ entry->compression = readShort(file);
+ entry->lastmod_time = readShort(file);
+ entry->lastmod_date = readShort(file);
+ entry->crc32 = readInt(file);
+ entry->compressed_size = readInt(file);
+ entry->uncompressed_size = readInt(file);
+ entry->filename_size = readShort(file);
+ entry->extra_field_size = readShort(file);
+ entry->file_comment_size = readShort(file);
+ entry->disk_num = readShort(file);
+ entry->internal_attr = readShort(file);
+ entry->external_attr = readInt(file);
+ entry->offset = readInt(file);
+ NSData *data = [file readDataOfLength: entry->filename_size];
+ entry->filename = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ data = [file readDataOfLength: entry->extra_field_size];
+ entry->extra_field = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ data = [file readDataOfLength: entry->file_comment_size];
+ entry->file_comment = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ return true;
+}
+
+static bool readLocalFileHeader(NSFileHandle *file, LocalFileHeader *header)
+{
+ unsigned signature = readInt(file);
+ if (signature != LOC_FILE_HEADER_SIG)
+ return false;
+
+ header->min_version = readShort(file);
+ header->general_flag = readShort(file);
+ header->compression = readShort(file);
+ header->lastmod_time = readShort(file);
+ header->lastmod_date = readShort(file);
+ header->crc32 = readInt(file);
+ header->compressed_size = readInt(file);
+ header->uncompressed_size = readInt(file);
+ header->filename_size = readShort(file);
+ header->extra_field_size = readShort(file);
+ NSData *data = [file readDataOfLength: header->filename_size];
+ header->filename = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ data = [file readDataOfLength: header->extra_field_size];
+ header->extra_field = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+ return true;
+}
+
+static bool areHeadersConsistent(const LocalFileHeader *header, const CentralDirectoryEntry *entry)
+{
+ if (header->min_version != entry->min_version)
+ return false;
+ if (header->general_flag != entry->general_flag)
+ return false;
+ if (header->compression != entry->compression)
+ return false;
+ if (!(header->general_flag & 0x08))
+ {
+ if (header->crc32 != entry->crc32)
+ return false;
+ if (header->compressed_size != entry->compressed_size)
+ return false;
+ if (header->uncompressed_size != entry->uncompressed_size)
+ return false;
+ }
+ return true;
+}
+
+static bool findCentralDirectoryEnd(NSFileHandle *file)
+{
+ // Assume the cdir end is in the last 1024 bytes
+ // Scan backward from end of file for the end signature
+
+ [file seekToEndOfFile];
+ unsigned long long fileLength = [file offsetInFile];
+
+ if (fileLength < 10)
+ return false;
+
+ [file seekToFileOffset: (fileLength - 4)];
+
+ unsigned long long limit;
+ if (fileLength > 1024)
+ limit = fileLength - 1024;
+ else
+ limit = 0;
+
+ unsigned long long offset;
+ while ((offset = [file offsetInFile]) > limit)
+ {
+ unsigned signature = readInt(file);
+ if (signature == CDIR_END_SIG)
+ {
+ // Seek back over the CDIR_END_SIG
+ [file seekToFileOffset: offset];
+ return true;
+ }
+ else
+ {
+ // Seek one byte back
+ [file seekToFileOffset: (offset - 1)];
+ }
+ }
+ return false;
+}
+
+static bool isZipFile(NSFileHandle *file)
+{
+ if (!findCentralDirectoryEnd(file))
+ return false;
+ CentralDirectoryEnd end;
+ if (!readCentralDirectoryEnd(file, &end))
+ return false;
+ [file seekToFileOffset: end.cdir_offset];
+ CentralDirectoryEntry entry;
+ if (!readCentralDirectoryEntry(file, &entry))
+ return false;
+ [file seekToFileOffset: entry.offset];
+ LocalFileHeader header;
+ if (!readLocalFileHeader(file, &header))
+ return false;
+ if (!areHeadersConsistent(&header, &entry))
+ return false;
+ return true;
+}
+
+static bool findDataStream(NSFileHandle *file, CentralDirectoryEntry *entry, NSString *name)
+{
+ [file seekToEndOfFile];
+ unsigned long long fileLength = [file offsetInFile];
+ if (!findCentralDirectoryEnd(file))
+ return false;
+ CentralDirectoryEnd end;
+ if (!readCentralDirectoryEnd(file, &end))
+ return false;
+ [file seekToFileOffset: end.cdir_offset];
+ do
+ {
+ if (!readCentralDirectoryEntry(file, entry))
+ return false;
+ if ([entry->filename compare: name] == NSOrderedSame)
+ break;
+ }
+ while ( [file offsetInFile] < fileLength && [file offsetInFile] < end.cdir_offset + end.cdir_size);
+ if ([entry->filename compare: name] != NSOrderedSame)
+ return false;
+ [file seekToFileOffset: entry->offset];
+ LocalFileHeader header;
+ if (!readLocalFileHeader(file, &header))
+ return false;
+ if (!areHeadersConsistent(&header, entry))
+ return false;
+ return true;
+}
+
+static NSData *getUncompressedData(NSFileHandle *file, NSString *name)
+{
+ CentralDirectoryEntry entry;
+ if (!findDataStream(file, &entry, name))
+ return nil;
+ if (!entry.compression)
+ return [file readDataOfLength: entry.compressed_size];
+ else
+ {
+ int ret;
+ z_stream strm;
+
+ /* allocate inflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = 0;
+ strm.next_in = Z_NULL;
+ ret = inflateInit2(&strm,-MAX_WBITS);
+ if (ret != Z_OK)
+ return nil;
+
+ NSData *compressedData = [file readDataOfLength: entry.compressed_size];
+
+ strm.avail_in = [compressedData length];
+ strm.next_in = (Bytef *)[compressedData bytes];
+
+ Bytef *uncompressedData = (Bytef *)malloc(entry.uncompressed_size);
+ if (!uncompressedData)
+ {
+ (void)inflateEnd(&strm);
+ return nil;
+ }
+ strm.avail_out = entry.uncompressed_size;
+ strm.next_out = uncompressedData;
+ ret = inflate(&strm, Z_FINISH);
+ switch (ret)
+ {
+ case Z_NEED_DICT:
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ (void)inflateEnd(&strm);
+ free(uncompressedData);
+ return nil;
+ }
+ (void)inflateEnd(&strm);
+ NSData *returnBuffer = [NSData dataWithBytes:(const void *)uncompressedData length:entry.uncompressed_size];
+ free(uncompressedData);
+ return returnBuffer;
+ }
+}
+
+@implementation OOoSpotlightImporter
+
+/* initialize is only called once the first time this class is loaded */
++ (void)initialize
+{
+ static BOOL isInitialized = NO;
+ if (isInitialized == NO) {
+ NSMutableDictionary *temp = [NSMutableDictionary new];
+ [temp setObject:@"OpenOffice.org 1.0 Text" forKey:@"org.openoffice.text"];
+ [temp setObject:@"OpenDocument Text" forKey:@"org.oasis.opendocument.text"];
+ [temp setObject:@"OpenOffice.org 1.0 Spreadsheet" forKey:@"org.openoffice.spreadsheet"];
+ [temp setObject:@"OpenDocument Spreadsheet" forKey:@"org.oasis.opendocument.spreadsheet"];
+ [temp setObject:@"OpenOffice.org 1.0 Presentation" forKey:@"org.openoffice.presentation"];
+ [temp setObject:@"OpenDocument Presentation" forKey:@"org.oasis.opendocument.presentation"];
+ [temp setObject:@"OpenOffice.org 1.0 Drawing" forKey:@"org.openoffice.graphics"];
+ [temp setObject:@"OpenDocument Drawing" forKey:@"org.oasis.opendocument.graphics"];
+ [temp setObject:@"OpenOffice.org 1.0 Master" forKey:@"org.openoffice.text-master"];
+ [temp setObject:@"OpenDocument Master" forKey:@"org.oasis.opendocument.text-master"];
+ [temp setObject:@"OpenOffice.org 1.0 Formula" forKey:@"org.openoffice.formula"];
+ [temp setObject:@"OpenDocument Formula" forKey:@"org.oasis.opendocument.formula"];
+ [temp setObject:@"OpenOffice.org 1.0 Text Template" forKey:@"org.openoffice.text-template"];
+ [temp setObject:@"OpenDocument Text Template" forKey:@"org.oasis.opendocument.text-template"];
+ [temp setObject:@"OpenOffice.org 1.0 Spreadsheet Template" forKey:@"org.openoffice.spreadsheet-template"];
+ [temp setObject:@"OpenDocument Spreadsheet Template" forKey:@"org.oasis.opendocument.spreadsheet-template"];
+ [temp setObject:@"OpenOffice.org 1.0 Presentation Template" forKey:@"org.openoffice.presentation-template"];
+ [temp setObject:@"OpenDocument Presentation Template" forKey:@"org.oasis.opendocument.presentation-template"];
+ [temp setObject:@"OpenOffice.org 1.0 Drawing Template" forKey:@"org.openoffice.graphics-template"];
+ [temp setObject:@"OpenDocument Drawing Template" forKey:@"org.oasis.opendocument.graphics-template"];
+ [temp setObject:@"OpenOffice.org 1.0 Database" forKey:@"org.openoffice.database"];
+ [temp setObject:@"OpenDocument Chart" forKey:@"org.oasis.opendocument.chart"];
+
+ uti2kind = [[NSDictionary dictionaryWithDictionary:temp] retain];
+ [temp release];
+
+ isInitialized = YES;
+ }
+}
+
+/* importDocument is the real starting point for our plugin */
+- (BOOL)importDocument:(NSString*)pathToFile contentType:(NSString*)contentTypeUTI attributes:(NSMutableDictionary*)attributes
+{
+ //NSLog(contentTypeUTI);
+ //NSLog(pathToFile);
+
+ NSString *itemKind = [uti2kind objectForKey:contentTypeUTI];
+ if (itemKind != nil) {
+ [attributes setObject:itemKind forKey:(NSString*)kMDItemKind];
+ }
+
+ //first check to see if this is a valid zipped file that contains a file "meta.xml"
+ NSFileHandle *unzipFile = [self openZipFileAtPath:pathToFile];
+
+
+ if (unzipFile == nil) {
+ //NSLog(@"zip file not open");
+ return NO;
+ }
+
+ //first get the metadata
+ NSData *metaData = [self metaDataFileFromZip:unzipFile];
+ if (metaData == nil) {
+ [unzipFile closeFile];
+ return YES;
+ }
+
+ [metaData retain];
+
+ OOoMetaDataParser *parser = [OOoMetaDataParser new];
+ if (parser != nil) {
+ //parse and extract the data
+ [parser parseXML:metaData intoDictionary:attributes];
+ }
+
+ [metaData release];
+ [parser release];
+
+ //and now get the content
+ NSData *contentData = [self contentDataFileFromZip:unzipFile];
+ if (contentData == nil) {
+ [unzipFile closeFile];
+ return YES;
+ }
+
+ [contentData retain];
+
+ OOoContentDataParser *parser2 = [OOoContentDataParser new];
+ if (parser2 != nil) {
+ //parse and extract the data
+ [parser2 parseXML:contentData intoDictionary:attributes];
+ }
+
+ [contentData release];
+ [parser2 release];
+
+ [unzipFile closeFile];
+
+ return YES;
+}
+
+/* openZipFileAtPath returns the file as a valid data structure or nil otherwise*/
+- (NSFileHandle*)openZipFileAtPath:(NSString*)pathToFile
+{
+ NSFileHandle* unzipFile = nil;
+
+ if ([pathToFile length] != 0)
+ {
+ unzipFile = [NSFileHandle fileHandleForReadingAtPath: pathToFile];
+ }
+
+ if (unzipFile == nil)
+ {
+ //NSLog(@"Cannot open %s",zipfilename);
+ return nil;
+ }
+
+ if (!isZipFile(unzipFile))
+ {
+ [unzipFile closeFile];
+ return nil;
+ }
+ //NSLog(@"%s opened",zipfilename);
+
+ return unzipFile;
+}
+
+/* metaDataFileFromZip extracts the file meta.xml from the zip file and returns it as an NSData* structure
+ or nil if the metadata is not present */
+- (NSData*) metaDataFileFromZip:(NSFileHandle*)unzipFile
+{
+ if (unzipFile == nil)
+ return nil;
+ return getUncompressedData(unzipFile, @"meta.xml");
+}
+
+/* contentDataFileFromZip extracts the file content.xml from the zip file and returns it as an NSData* structure
+ or nil if the metadata is not present */
+- (NSData*) contentDataFileFromZip:(NSFileHandle*)unzipFile
+{
+ if (unzipFile == nil)
+ return nil;
+ return getUncompressedData(unzipFile, @"content.xml");
+}
+
+
+@end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/.gitignore b/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/.gitignore
new file mode 100644
index 000000000..96c4e5542
--- /dev/null
+++ b/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/.gitignore
@@ -0,0 +1 @@
+project.xcworkspace
diff --git a/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/project.pbxproj b/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..a577eeb1d
--- /dev/null
+++ b/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/project.pbxproj
@@ -0,0 +1,308 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ BE9A7AC823590D9500931013 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9A7AC723590D9500931013 /* main.m */; };
+ BE9A7AD723590E5D00931013 /* OOoSpotlightImporter.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9A7ACE23590E5D00931013 /* OOoSpotlightImporter.m */; };
+ BE9A7AD823590E5D00931013 /* OOoMetaDataParser.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9A7AD223590E5D00931013 /* OOoMetaDataParser.m */; };
+ BE9A7AD923590E5D00931013 /* OOoContentDataParser.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9A7AD323590E5D00931013 /* OOoContentDataParser.m */; };
+ BE9A7ADA23590E5D00931013 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9A7AD423590E5D00931013 /* main.m */; };
+ BE9A7ADB23590E5D00931013 /* GetMetadataForFile.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9A7AD623590E5D00931013 /* GetMetadataForFile.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ BE9A7AC223590D9400931013 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ BE9A7AC423590D9500931013 /* SpotlightImporterTester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SpotlightImporterTester; sourceTree = BUILT_PRODUCTS_DIR; };
+ BE9A7AC723590D9500931013 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ BE9A7ACE23590E5D00931013 /* OOoSpotlightImporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OOoSpotlightImporter.m; path = ../../OOoSpotlightImporter.m; sourceTree = "<group>"; };
+ BE9A7ACF23590E5D00931013 /* OOoSpotlightImporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OOoSpotlightImporter.h; path = ../../OOoSpotlightImporter.h; sourceTree = "<group>"; };
+ BE9A7AD023590E5D00931013 /* GetMetadataForFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GetMetadataForFile.h; path = ../../GetMetadataForFile.h; sourceTree = "<group>"; };
+ BE9A7AD123590E5D00931013 /* OOoMetaDataParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OOoMetaDataParser.h; path = ../../OOoMetaDataParser.h; sourceTree = "<group>"; };
+ BE9A7AD223590E5D00931013 /* OOoMetaDataParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OOoMetaDataParser.m; path = ../../OOoMetaDataParser.m; sourceTree = "<group>"; };
+ BE9A7AD323590E5D00931013 /* OOoContentDataParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OOoContentDataParser.m; path = ../../OOoContentDataParser.m; sourceTree = "<group>"; };
+ BE9A7AD423590E5D00931013 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ../../main.m; sourceTree = "<group>"; };
+ BE9A7AD523590E5D00931013 /* OOoContentDataParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OOoContentDataParser.h; path = ../../OOoContentDataParser.h; sourceTree = "<group>"; };
+ BE9A7AD623590E5D00931013 /* GetMetadataForFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GetMetadataForFile.m; path = ../../GetMetadataForFile.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ BE9A7AC123590D9400931013 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ BE9A7ABB23590D9400931013 = {
+ isa = PBXGroup;
+ children = (
+ BE9A7AC623590D9500931013 /* SpotlightImporterTester */,
+ BE9A7AC523590D9500931013 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ BE9A7AC523590D9500931013 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ BE9A7AC423590D9500931013 /* SpotlightImporterTester */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ BE9A7AC623590D9500931013 /* SpotlightImporterTester */ = {
+ isa = PBXGroup;
+ children = (
+ BE9A7AD023590E5D00931013 /* GetMetadataForFile.h */,
+ BE9A7AD623590E5D00931013 /* GetMetadataForFile.m */,
+ BE9A7AD423590E5D00931013 /* main.m */,
+ BE9A7AD523590E5D00931013 /* OOoContentDataParser.h */,
+ BE9A7AD323590E5D00931013 /* OOoContentDataParser.m */,
+ BE9A7AD123590E5D00931013 /* OOoMetaDataParser.h */,
+ BE9A7AD223590E5D00931013 /* OOoMetaDataParser.m */,
+ BE9A7ACF23590E5D00931013 /* OOoSpotlightImporter.h */,
+ BE9A7ACE23590E5D00931013 /* OOoSpotlightImporter.m */,
+ BE9A7AC723590D9500931013 /* main.m */,
+ );
+ path = SpotlightImporterTester;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ BE9A7AC323590D9400931013 /* SpotlightImporterTester */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = BE9A7ACB23590D9500931013 /* Build configuration list for PBXNativeTarget "SpotlightImporterTester" */;
+ buildPhases = (
+ BE9A7AC023590D9400931013 /* Sources */,
+ BE9A7AC123590D9400931013 /* Frameworks */,
+ BE9A7AC223590D9400931013 /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = SpotlightImporterTester;
+ productName = SpotlightImporterTester;
+ productReference = BE9A7AC423590D9500931013 /* SpotlightImporterTester */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ BE9A7ABC23590D9400931013 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1110;
+ ORGANIZATIONNAME = Collabora;
+ TargetAttributes = {
+ BE9A7AC323590D9400931013 = {
+ CreatedOnToolsVersion = 11.1;
+ };
+ };
+ };
+ buildConfigurationList = BE9A7ABF23590D9400931013 /* Build configuration list for PBXProject "SpotlightImporterTester" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = BE9A7ABB23590D9400931013;
+ productRefGroup = BE9A7AC523590D9500931013 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ BE9A7AC323590D9400931013 /* SpotlightImporterTester */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ BE9A7AC023590D9400931013 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ BE9A7ADA23590E5D00931013 /* main.m in Sources */,
+ BE9A7AD923590E5D00931013 /* OOoContentDataParser.m in Sources */,
+ BE9A7AD823590E5D00931013 /* OOoMetaDataParser.m in Sources */,
+ BE9A7ADB23590E5D00931013 /* GetMetadataForFile.m in Sources */,
+ BE9A7AD723590E5D00931013 /* OOoSpotlightImporter.m in Sources */,
+ BE9A7AC823590D9500931013 /* main.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ BE9A7AC923590D9500931013 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ LO_CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ LO_CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ BE9A7ACA23590D9500931013 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ LO_CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ LO_CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ BE9A7ACC23590D9500931013 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_OBJC_ARC = NO;
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ BE9A7ACD23590D9500931013 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_OBJC_ARC = NO;
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ BE9A7ABF23590D9400931013 /* Build configuration list for PBXProject "SpotlightImporterTester" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ BE9A7AC923590D9500931013 /* Debug */,
+ BE9A7ACA23590D9500931013 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ BE9A7ACB23590D9500931013 /* Build configuration list for PBXNativeTarget "SpotlightImporterTester" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ BE9A7ACC23590D9500931013 /* Debug */,
+ BE9A7ACD23590D9500931013 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = BE9A7ABC23590D9400931013 /* Project object */;
+}
diff --git a/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/xcshareddata/xcschemes/SpotlightImporterTester.xcscheme b/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/xcshareddata/xcschemes/SpotlightImporterTester.xcscheme
new file mode 100644
index 000000000..d2aab1a01
--- /dev/null
+++ b/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester.xcodeproj/xcshareddata/xcschemes/SpotlightImporterTester.xcscheme
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1110"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "BE9A7AC323590D9400931013"
+ BuildableName = "SpotlightImporterTester"
+ BlueprintName = "SpotlightImporterTester"
+ ReferencedContainer = "container:SpotlightImporterTester.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "BE9A7AC323590D9400931013"
+ BuildableName = "SpotlightImporterTester"
+ BlueprintName = "SpotlightImporterTester"
+ ReferencedContainer = "container:SpotlightImporterTester.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <CommandLineArguments>
+ <CommandLineArgument
+ argument = "org.openoffice.database"
+ isEnabled = "YES">
+ </CommandLineArgument>
+ <CommandLineArgument
+ argument = "/tmp/sample.odb"
+ isEnabled = "YES">
+ </CommandLineArgument>
+ </CommandLineArguments>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "BE9A7AC323590D9400931013"
+ BuildableName = "SpotlightImporterTester"
+ BlueprintName = "SpotlightImporterTester"
+ ReferencedContainer = "container:SpotlightImporterTester.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester/main.m b/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester/main.m
new file mode 100644
index 000000000..f9b6bbb5f
--- /dev/null
+++ b/extensions/source/macosx/spotlight/SpotlightImporterTester/SpotlightImporterTester/main.m
@@ -0,0 +1,30 @@
+/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#import <stdio.h>
+
+#import <CoreFoundation/CoreFoundation.h>
+#import <CoreServices/CoreServices.h>
+#import <Foundation/Foundation.h>
+
+#import "GetMetadataForFile.h"
+
+int main(int argc, const char* argv[])
+{
+ @autoreleasepool
+ {
+ if (argc != 3)
+ {
+ fprintf(stderr, "Usage: %s UTI path\n", argv[0]);
+ return 1;
+ }
+ NSMutableDictionary* attributes = [NSMutableDictionary dictionaryWithCapacity:10];
+ NSString* contentTypeUTI = [NSString stringWithUTF8String:argv[1]];
+ NSString* pathToFile = [NSString stringWithUTF8String:argv[2]];
+
+ GetMetadataForFile(NULL, (__bridge CFMutableDictionaryRef)attributes,
+ (__bridge CFStringRef)contentTypeUTI, (__bridge CFStringRef)pathToFile);
+ }
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/main.m b/extensions/source/macosx/spotlight/main.m
new file mode 100644
index 000000000..b1a6381a7
--- /dev/null
+++ b/extensions/source/macosx/spotlight/main.m
@@ -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 .
+ */
+//
+// main.m
+// SpotlightTester
+//
+// Created by Florian Heckl on 10.07.07.
+//
+//==============================================================================
+//
+// DO NOT MODIFY THE CONTENTS OF THIS FILE
+//
+// This file contains the generic CFPlug-in code necessary for your importer
+// To complete your importer implement the function in GetMetadataForFile.c
+//
+//==============================================================================
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFPlugInCOM.h>
+#include <CoreServices/CoreServices.h>
+
+#include "GetMetadataForFile.h"
+
+// constants
+
+
+#define PLUGIN_ID "A3FCC88D-B9A6-4364-8B93-92123C8A2D18"
+
+//
+// Below is the generic glue code for all plug-ins.
+//
+// You should not have to modify this code aside from changing
+// names if you decide to change the names defined in the Info.plist
+//
+
+
+// typedefs
+
+// The layout for an instance of MetaDataImporterPlugIn
+typedef struct
+{
+ MDImporterInterfaceStruct *conduitInterface;
+ CFUUIDRef factoryID;
+ UInt32 refCount;
+} MetadataImporterPluginType;
+
+// prototypes
+// Forward declaration for the IUnknown implementation.
+//
+
+static MetadataImporterPluginType *AllocMetadataImporterPluginType(CFUUIDRef inFactoryID);
+static void DeallocMetadataImporterPluginType(MetadataImporterPluginType *thisInstance);
+static HRESULT MetadataImporterQueryInterface(void *thisInstance,REFIID iid,LPVOID *ppv);
+static ULONG MetadataImporterPluginAddRef(void *thisInstance);
+static ULONG MetadataImporterPluginRelease(void *thisInstance);
+// testInterfaceFtbl definition
+// The TestInterface function table.
+//
+
+static MDImporterInterfaceStruct testInterfaceFtbl = {
+ NULL,
+ MetadataImporterQueryInterface,
+ MetadataImporterPluginAddRef,
+ MetadataImporterPluginRelease,
+ GetMetadataForFile
+};
+
+
+// AllocMetadataImporterPluginType
+// Utility function that allocates a new instance.
+// You can do some initial setup for the importer here if you wish
+// like allocating globals etc...
+//
+MetadataImporterPluginType *AllocMetadataImporterPluginType(CFUUIDRef inFactoryID)
+{
+ MetadataImporterPluginType *theNewInstance;
+
+ theNewInstance = (MetadataImporterPluginType *)malloc(sizeof(MetadataImporterPluginType));
+ memset(theNewInstance,0,sizeof(MetadataImporterPluginType));
+
+ /* Point to the function table */
+ theNewInstance->conduitInterface = &testInterfaceFtbl;
+
+ /* Retain and keep an open instance refcount for each factory. */
+ theNewInstance->factoryID = CFRetain(inFactoryID);
+ CFPlugInAddInstanceForFactory(inFactoryID);
+
+ /* This function returns the IUnknown interface so set the refCount to one. */
+ theNewInstance->refCount = 1;
+ return theNewInstance;
+}
+
+// DeallocSpotlightTesterMDImporterPluginType
+// Utility function that deallocates the instance when
+// the refCount goes to zero.
+// In the current implementation importer interfaces are never deallocated
+// but implement this as this might change in the future
+//
+void DeallocMetadataImporterPluginType(MetadataImporterPluginType *thisInstance)
+{
+ CFUUIDRef theFactoryID;
+
+ theFactoryID = thisInstance->factoryID;
+ free(thisInstance);
+ if (theFactoryID){
+ CFPlugInRemoveInstanceForFactory(theFactoryID);
+ CFRelease(theFactoryID);
+ }
+}
+
+// MetadataImporterQueryInterface
+// Implementation of the IUnknown QueryInterface function.
+//
+HRESULT MetadataImporterQueryInterface(void *thisInstance,REFIID iid,LPVOID *ppv)
+{
+ CFUUIDRef interfaceID;
+
+ interfaceID = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault,iid);
+
+ if (CFEqual(interfaceID,kMDImporterInterfaceID)){
+ /* If the Right interface was requested, bump the ref count,
+ * set the ppv parameter equal to the instance, and
+ * return good status.
+ */
+ ((MetadataImporterPluginType*)thisInstance)->conduitInterface->AddRef(thisInstance);
+ *ppv = thisInstance;
+ CFRelease(interfaceID);
+ return S_OK;
+ }else{
+ if (CFEqual(interfaceID,IUnknownUUID)){
+ /* If the IUnknown interface was requested, same as above. */
+ ((MetadataImporterPluginType*)thisInstance )->conduitInterface->AddRef(thisInstance);
+ *ppv = thisInstance;
+ CFRelease(interfaceID);
+ return S_OK;
+ }else{
+ /* Requested interface unknown, bail with error. */
+ *ppv = NULL;
+ CFRelease(interfaceID);
+ return E_NOINTERFACE;
+ }
+ }
+}
+
+// MetadataImporterPluginAddRef
+// Implementation of reference counting for this type. Whenever an interface
+// is requested, bump the refCount for the instance. NOTE: returning the
+// refcount is a convention but is not required so don't rely on it.
+//
+ULONG MetadataImporterPluginAddRef(void *thisInstance)
+{
+ ((MetadataImporterPluginType *)thisInstance )->refCount += 1;
+ return ((MetadataImporterPluginType*) thisInstance)->refCount;
+}
+
+// SampleCMPluginRelease
+// When an interface is released, decrement the refCount.
+// If the refCount goes to zero, deallocate the instance.
+//
+ULONG MetadataImporterPluginRelease(void *thisInstance)
+{
+ ((MetadataImporterPluginType*)thisInstance)->refCount -= 1;
+ if (((MetadataImporterPluginType*)thisInstance)->refCount == 0){
+ DeallocMetadataImporterPluginType((MetadataImporterPluginType*)thisInstance );
+ return 0;
+ }else{
+ return ((MetadataImporterPluginType*) thisInstance )->refCount;
+ }
+}
+
+// SpotlightTesterMDImporterPluginFactory
+// Implementation of the factory function for this type.
+//
+__attribute__ ((visibility("default")))
+void *
+MetadataImporterPluginFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
+{
+ (void) allocator; /* unused */
+ MetadataImporterPluginType *result;
+ CFUUIDRef uuid;
+
+ /* If correct type is being requested, allocate an
+ * instance of TestType and return the IUnknown interface.
+ */
+ if (CFEqual(typeID,kMDImporterTypeID)){
+ uuid = CFUUIDCreateFromString(kCFAllocatorDefault,CFSTR(PLUGIN_ID));
+ result = AllocMetadataImporterPluginType(uuid);
+ CFRelease(uuid);
+ return result;
+ }
+ /* If the requested type is incorrect, return NULL. */
+ return NULL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/macosx/spotlight/mdimporter/Info.plist b/extensions/source/macosx/spotlight/mdimporter/Info.plist
new file mode 100644
index 000000000..5a60493cb
--- /dev/null
+++ b/extensions/source/macosx/spotlight/mdimporter/Info.plist
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>MDImporter</string>
+ <key>LSItemContentTypes</key>
+ <array>
+ <string>org.openoffice.text</string>
+ <string>org.oasis-open.opendocument.text</string>
+ <string>org.openoffice.spreadsheet</string>
+ <string>org.oasis-open.opendocument.spreadsheet</string>
+ <string>org.openoffice.presentation</string>
+ <string>org.oasis-open.opendocument.presentation</string>
+ <string>org.openoffice.graphics</string>
+ <string>org.oasis-open.opendocument.graphics</string>
+ <string>org.openoffice.text-master</string>
+ <string>org.oasis-open.opendocument.text-master</string>
+ <string>org.openoffice.formula</string>
+ <string>org.oasis-open.opendocument.formula</string>
+ <string>org.openoffice.text-template</string>
+ <string>org.oasis-open.opendocument.text-template</string>
+ <string>org.openoffice.spreadsheet-template</string>
+ <string>org.oasis-open.opendocument.spreadsheet-template</string>
+ <string>org.openoffice.presentation-template</string>
+ <string>org.oasis-open.opendocument.presentation-template</string>
+ <string>org.openoffice.graphics-template</string>
+ <string>org.oasis-open.opendocument.graphics-template</string>
+ <string>org.oasis-open.opendocument.database</string>
+ </array>
+ </dict>
+ </array>
+ <key>CFBundleExecutable</key>
+ <string>OOoSpotlightImporter</string>
+ <key>CFBundleName</key>
+ <string>OOoSpotlightImporter</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>org.libreoffice.mdimporter</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>CFPlugInDynamicRegisterFunction</key>
+ <string></string>
+ <key>CFPlugInDynamicRegistration</key>
+ <string>NO</string>
+ <key>CFPlugInFactories</key>
+ <dict>
+ <key>A3FCC88D-B9A6-4364-8B93-92123C8A2D18</key>
+ <string>MetadataImporterPluginFactory</string>
+ </dict>
+ <key>CFPlugInTypes</key>
+ <dict>
+ <key>8B08C4BF-415B-11D8-B3F9-0003936726FC</key>
+ <array>
+ <string>A3FCC88D-B9A6-4364-8B93-92123C8A2D18</string>
+ </array>
+ </dict>
+ <key>CFPlugInUnloadFunction</key>
+ <string></string>
+
+</dict>
+</plist>
diff --git a/extensions/source/macosx/spotlight/mdimporter/en.lproj/schema.strings b/extensions/source/macosx/spotlight/mdimporter/en.lproj/schema.strings
new file mode 100644
index 000000000..355998783
--- /dev/null
+++ b/extensions/source/macosx/spotlight/mdimporter/en.lproj/schema.strings
@@ -0,0 +1 @@
+ÿþ \ No newline at end of file
diff --git a/extensions/source/macosx/spotlight/mdimporter/schema.xml b/extensions/source/macosx/spotlight/mdimporter/schema.xml
new file mode 100644
index 000000000..f17aa8c6c
--- /dev/null
+++ b/extensions/source/macosx/spotlight/mdimporter/schema.xml
@@ -0,0 +1,413 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<schema version="1.0" xmlns="http://www.apple.com/metadata"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.apple.com/metadata file:///System/Library/Frameworks/CoreServices.framework/Frameworks/Metadata.framework/Resources/MetadataSchema.xsd">
+ <note>
+ OpenOffice.org allows the user to enter 4 pieces of custom information in the document's metadata.
+ These metadata fields are described here for Spotlight's use.
+ </note>
+ <attributes>
+ <attribute name="org_openoffice_opendocument_custominfo1" type="CFString"/>
+ <attribute name="org_openoffice_opendocument_custominfo2" type="CFString"/>
+ <attribute name="org_openoffice_opendocument_custominfo3" type="CFString"/>
+ <attribute name="org_openoffice_opendocument_custominfo4" type="CFString"/>
+ </attributes>
+
+ <types>
+ <type name="org.openoffice.text">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.text">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.openoffice.spreadsheet">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.spreadsheet">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.openoffice.presentation">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.presentation">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.openoffice.graphics">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.graphics">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.openoffice.text-master">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.text-master">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.openoffice.formula">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.formula">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.openoffice.text-template">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.text-template">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.openoffice.spreadsheet-template">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.spreadsheet-template">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.openoffice.presentation-template">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.presentation-template">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.openoffice.graphics-template">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.graphics-template">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+
+ <type name="org.oasis.opendocument.chart">
+ <note>
+ The custom metadata info.
+ </note>
+ <allattrs id="attrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </allattrs>
+ <displayattrs id="dattrs">
+ org_openoffice_opendocument_custominfo1
+ org_openoffice_opendocument_custominfo2
+ org_openoffice_opendocument_custominfo3
+ org_openoffice_opendocument_custominfo4
+ </displayattrs>
+ </type>
+ </types>
+</schema>
+
diff --git a/extensions/source/macosx/spotlight/version.plist b/extensions/source/macosx/spotlight/version.plist
new file mode 100644
index 000000000..d91342841
--- /dev/null
+++ b/extensions/source/macosx/spotlight/version.plist
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<plist version="1.0">
+<dict>
+ <key>BuildVersion</key>
+ <string>266</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>ProjectName</key>
+ <string>DevToolsWizardTemplates</string>
+ <key>SourceVersion</key>
+ <string>3070000</string>
+</dict>
+</plist>
diff --git a/extensions/source/ole/comifaces.hxx b/extensions/source/ole/comifaces.hxx
new file mode 100644
index 000000000..51e955dd6
--- /dev/null
+++ b/extensions/source/ole/comifaces.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 <com/sun/star/uno/XInterface.hpp>
+
+using namespace com::sun::star::uno;
+
+MIDL_INTERFACE("e40a2331-3bc1-11d4-8321-005004526ab4")
+IJScriptValueObject: public IUnknown
+{
+ STDMETHOD( Set)( VARIANT type, VARIANT value)= 0;
+ STDMETHOD( Get)( VARIANT *val)= 0;
+ STDMETHOD( InitOutParam)()= 0;
+ STDMETHOD( InitInOutParam)( VARIANT type, VARIANT value)= 0;
+ STDMETHOD( IsOutParam)( VARIANT_BOOL * flag)= 0;
+ STDMETHOD( IsInOutParam)( VARIANT_BOOL * flag)= 0;
+ STDMETHOD( GetValue)( BSTR* type, VARIANT *value)= 0;
+
+protected:
+ ~IJScriptValueObject() {}
+};
+
+MIDL_INTERFACE("7B5C3410-66FA-11d4-832A-005004526AB4")
+IUnoObjectWrapper: public IUnknown
+{
+ STDMETHOD( getWrapperXInterface)( Reference<XInterface>* pInt)=0;
+ STDMETHOD( getOriginalUnoObject)( Reference<XInterface>* pInt)=0;
+ STDMETHOD( getOriginalUnoStruct)( Any * pStruct)=0;
+
+protected:
+ ~IUnoObjectWrapper() {}
+};
+
+MIDL_INTERFACE("8BB66591-A544-4de9-822C-57AB57BCED1C")
+IUnoTypeWrapper: public IUnknown
+{
+ STDMETHOD(put_Name)(BSTR val) = 0;
+ STDMETHOD(get_Name)(BSTR* pVal) = 0;
+
+protected:
+ ~IUnoTypeWrapper() {}
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/jscriptclasses.cxx b/extensions/source/ole/jscriptclasses.cxx
new file mode 100644
index 000000000..8fc371c4c
--- /dev/null
+++ b/extensions/source/ole/jscriptclasses.cxx
@@ -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 .
+ */
+
+#include "jscriptclasses.hxx"
+
+
+// JScriptValue
+
+JScriptValue::JScriptValue(): m_bOutParam(false), m_bInOutParam(false)
+{
+}
+
+JScriptValue::~JScriptValue()
+{
+}
+
+
+// JScriptValue, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::GetTypeInfoCount(UINT* /*pctinfo*/)
+{
+ return E_NOTIMPL;
+}
+
+// JScriptValue, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::GetTypeInfo( UINT /*iTInfo*/,
+ LCID /*lcid*/,
+ ITypeInfo** /*ppTInfo*/)
+{
+ return E_NOTIMPL;
+}
+
+// JScriptValue, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::GetIDsOfNames( REFIID /*riid*/,
+ LPOLESTR *rgszNames,
+ UINT /*cNames*/,
+ LCID /*lcid*/,
+ DISPID *rgDispId)
+{
+ if( !rgDispId)
+ return E_POINTER;
+
+
+ HRESULT ret= S_OK;
+ CComBSTR name(*rgszNames);
+ name.ToLower();
+
+ if( name == CComBSTR( L"set") )
+ *rgDispId= 1;
+ else if( name == CComBSTR( L"get") )
+ *rgDispId= 2;
+ else if( name == CComBSTR( L"initoutparam") )
+ *rgDispId= 3;
+ else if( name == CComBSTR( L"initinoutparam") )
+ *rgDispId= 4;
+ else
+ ret= DISP_E_UNKNOWNNAME;
+
+ return ret;
+}
+
+// JScriptValue, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::Invoke( DISPID dispIdMember,
+ REFIID /*riid*/,
+ LCID /*lcid*/,
+ WORD wFlags,
+ DISPPARAMS *pDispParams,
+ VARIANT *pVarResult,
+ EXCEPINFO* /*pExcepInfo*/,
+ UINT* /*puArgErr*/)
+{
+ if( pDispParams->cNamedArgs)
+ return DISP_E_NONAMEDARGS;
+
+
+ HRESULT ret= S_OK;
+ switch( dispIdMember)
+ {
+ case 0: // DISPID_VALUE
+ if( wFlags & DISPATCH_PROPERTYGET && pVarResult)
+ {
+ if( FAILED( VariantCopy( pVarResult, &m_varValue)))
+ ret= E_FAIL;
+ }
+ else
+ ret= E_POINTER;
+ break;
+ case 1:
+ if( wFlags & DISPATCH_METHOD)
+ ret= Set( pDispParams->rgvarg[1], pDispParams->rgvarg[0]);
+ if( FAILED( ret))
+ ret= DISP_E_EXCEPTION;
+ break;
+ case 2:
+ if( wFlags & DISPATCH_METHOD)
+ ret= Get( pVarResult);
+ if( FAILED( ret))
+ ret= DISP_E_EXCEPTION;
+ break;
+ case 3:
+ if( wFlags & DISPATCH_METHOD)
+ ret= InitOutParam();
+ if( FAILED( ret))
+ ret= DISP_E_EXCEPTION;
+ break;
+ case 4:
+ if( wFlags & DISPATCH_METHOD)
+ ret= InitInOutParam( pDispParams->rgvarg[1], pDispParams->rgvarg[0]);
+ if( FAILED( ret))
+ ret= DISP_E_EXCEPTION;
+ break;
+ default:
+ ret= DISP_E_MEMBERNOTFOUND;
+ break;
+ }
+
+ return ret;
+}
+
+// JScriptValue, IScriptOutParam-----------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::Set( VARIANT type, VARIANT value)
+{
+ Lock();
+ m_varValue.Clear();
+ HRESULT hr= VariantCopyInd( &m_varValue, &value);
+ VARIANT var;
+ VariantInit( &var);
+ if( SUCCEEDED( hr= VariantChangeType( &var, &type, 0, VT_BSTR)))
+ m_bstrType= var.bstrVal;
+ Unlock();
+ return hr;
+}
+// JScriptValue, IScriptOutParam-----------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::Get( VARIANT *val)
+{
+ Lock();
+ if( !val)
+ return E_POINTER;
+ HRESULT hr= VariantCopy( val, &m_varValue);
+ Unlock();
+ return hr;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::InitOutParam()
+{
+ Lock();
+ m_varValue.Clear();
+ m_bOutParam= true;
+ m_bInOutParam= false;
+ Unlock();
+ return S_OK;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::InitInOutParam( VARIANT type, VARIANT value)
+{
+ Lock();
+ m_bInOutParam= true;
+ m_bOutParam= false;
+ Unlock();
+ return Set( type, value);
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::IsOutParam( VARIANT_BOOL * flag)
+{
+ Lock();
+ if( !flag)
+ return E_POINTER;
+ *flag= m_bOutParam ? VARIANT_TRUE : VARIANT_FALSE;
+ Unlock();
+ return S_OK;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::IsInOutParam( VARIANT_BOOL * flag)
+{
+ Lock();
+ if( !flag)
+ return E_POINTER;
+ *flag= m_bInOutParam ? VARIANT_TRUE : VARIANT_FALSE;
+ Unlock();
+ return S_OK;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptValue::GetValue( BSTR* type, VARIANT *value)
+{
+ Lock();
+ if( !type || !value)
+ return E_POINTER;
+ HRESULT hr;
+ if( SUCCEEDED( hr= m_bstrType.CopyTo( type)))
+ hr= VariantCopy( value, &m_varValue);
+ Unlock();
+ return hr;
+}
+
+
+// JScriptOutValue
+
+
+JScriptOutParam::JScriptOutParam()
+{
+}
+
+JScriptOutParam::~JScriptOutParam()
+{
+}
+
+
+// JScriptOutParam, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptOutParam::GetTypeInfoCount(UINT* /*pctinfo*/)
+{
+ return E_NOTIMPL;
+}
+
+// JScriptOutParam, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptOutParam::GetTypeInfo( UINT /*iTInfo*/,
+ LCID /*lcid*/,
+ ITypeInfo** /*ppTInfo*/)
+{
+ return E_NOTIMPL;
+}
+
+// JScriptOutParam, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptOutParam::GetIDsOfNames( REFIID /*riid*/,
+ LPOLESTR *rgszNames,
+ UINT /*cNames*/,
+ LCID /*lcid*/,
+ DISPID *rgDispId)
+{
+ if( !rgDispId)
+ return E_POINTER;
+
+
+ HRESULT ret= S_OK;
+ CComBSTR name(*rgszNames);
+ name.ToLower();
+
+ if( name == CComBSTR( L"0") )
+ *rgDispId= 1;
+ else
+ ret= DISP_E_UNKNOWNNAME;
+
+ return ret;
+}
+
+// JScriptOutParam, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP JScriptOutParam::Invoke( DISPID dispIdMember,
+ REFIID /*riid*/,
+ LCID /*lcid*/,
+ WORD wFlags,
+ DISPPARAMS *pDispParams,
+ VARIANT *pVarResult,
+ EXCEPINFO* /*pExcepInfo*/,
+ UINT* /*puArgErr*/)
+{
+ HRESULT ret= S_OK;
+ switch( dispIdMember)
+ {
+ case 0: // DISPID_VALUE
+ if( wFlags & DISPATCH_PROPERTYGET && pVarResult)
+ {
+ if( FAILED( VariantCopy( pVarResult, &m_varValue)))
+ ret= E_FAIL;
+ }
+ else if( wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF)
+ {
+ m_varValue.Clear();
+ if( FAILED( VariantCopyInd( &m_varValue, &pDispParams->rgvarg[0])))
+ ret= E_FAIL;
+ }
+ else
+ ret= E_POINTER;
+ break;
+ case 1:
+ if( wFlags & DISPATCH_PROPERTYGET && pVarResult)
+ {
+ if( FAILED( VariantCopy( pVarResult, &m_varValue)))
+ ret= E_FAIL;
+ }
+ else if( wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF)
+ {
+ m_varValue.Clear();
+ if( FAILED( VariantCopyInd( &m_varValue, &pDispParams->rgvarg[0])))
+ ret= E_FAIL;
+ }
+ else
+ ret= E_POINTER;
+ break;
+
+ default:
+ ret= DISP_E_MEMBERNOTFOUND;
+ break;
+ }
+
+ return ret;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/jscriptclasses.hxx b/extensions/source/ole/jscriptclasses.hxx
new file mode 100644
index 000000000..cef993ed0
--- /dev/null
+++ b/extensions/source/ole/jscriptclasses.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "wincrap.hxx"
+
+#include "comifaces.hxx"
+
+
+// Sequences are represented by prepending "[]", e.g. []char, [][]byte, [][][]object, etc.
+
+// To make a JScriptValue object to an out parameter, call
+// "InitOutParam" and to make it a in/out parameter call
+// "InitInOutParam"
+
+// If the object represents an out parameter then the value can after the call
+// be retrieved by "Get".
+
+// From JavaScript the functions Get, Set, InitOutParam and InitInOutParam are
+// used, that is they are accessible through IDispatch. The functions are used
+// by the bridge.
+
+class JScriptValue:
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public IJScriptValueObject,
+ public IDispatch
+{
+public:
+ JScriptValue();
+ virtual ~JScriptValue();
+
+ BEGIN_COM_MAP(JScriptValue)
+ COM_INTERFACE_ENTRY(IDispatch)
+ COM_INTERFACE_ENTRY(IJScriptValueObject)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#endif
+ END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+ // IDispatch -------------------------------------------
+ STDMETHOD( GetTypeInfoCount)(UINT *pctinfo) override;
+
+ STDMETHOD( GetTypeInfo)( UINT iTInfo,
+ LCID lcid,
+ ITypeInfo **ppTInfo) override;
+
+ STDMETHOD( GetIDsOfNames)( REFIID riid,
+ LPOLESTR *rgszNames,
+ UINT cNames,
+ LCID lcid,
+ DISPID *rgDispId) override;
+
+ STDMETHOD( Invoke)( DISPID dispIdMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ DISPPARAMS *pDispParams,
+ VARIANT *pVarResult,
+ EXCEPINFO *pExcepInfo,
+ UINT *puArgErr) override;
+ // IJScriptOutParam --------------------------------------
+
+ STDMETHOD( Set)( VARIANT type, VARIANT value) override;
+ STDMETHOD( Get)( VARIANT *val) override;
+ STDMETHOD( InitOutParam)() override;
+ STDMETHOD( InitInOutParam)( VARIANT type, VARIANT value) override;
+ STDMETHOD( IsOutParam)( VARIANT_BOOL * flag) override;
+ STDMETHOD( IsInOutParam)( VARIANT_BOOL * flag) override;
+ STDMETHOD( GetValue)( BSTR* type, VARIANT *value) override;
+
+
+ CComVariant m_varValue;
+ CComBSTR m_bstrType;
+ bool m_bOutParam: 1;
+ bool m_bInOutParam: 1;
+
+};
+
+// If a class is implemented in JScript, then its method
+class JScriptOutParam:
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public IDispatch
+{
+public:
+ JScriptOutParam();
+ virtual ~JScriptOutParam();
+
+ BEGIN_COM_MAP(JScriptOutParam)
+ COM_INTERFACE_ENTRY(IDispatch)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#endif
+ END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+ // IDispatch -------------------------------------------
+ STDMETHOD( GetTypeInfoCount)(UINT *pctinfo) override;
+
+ STDMETHOD( GetTypeInfo)( UINT iTInfo,
+ LCID lcid,
+ ITypeInfo **ppTInfo) override;
+
+ STDMETHOD( GetIDsOfNames)( REFIID riid,
+ LPOLESTR *rgszNames,
+ UINT cNames,
+ LCID lcid,
+ DISPID *rgDispId) override;
+
+ STDMETHOD( Invoke)( DISPID dispIdMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ DISPPARAMS *pDispParams,
+ VARIANT *pVarResult,
+ EXCEPINFO *pExcepInfo,
+ UINT *puArgErr) override;
+
+
+private:
+ CComVariant m_varValue;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/ole2uno.cxx b/extensions/source/ole/ole2uno.cxx
new file mode 100644
index 000000000..f9eef5125
--- /dev/null
+++ b/extensions/source/ole/ole2uno.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <osl/getglobalmutex.hxx>
+#include <rtl/instance.hxx>
+#include "ole2uno.hxx"
+
+using namespace osl;
+
+namespace {
+
+struct MutexInit
+{
+ Mutex * operator () ()
+ {
+ static Mutex aInstance;
+ return &aInstance;
+ }
+};
+
+}
+
+Mutex * getBridgeMutex()
+{
+ return rtl_Instance< Mutex, MutexInit, ::osl::MutexGuard,
+ ::osl::GetGlobalMutex >::create(
+ MutexInit(), ::osl::GetGlobalMutex());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/ole2uno.hxx b/extensions/source/ole/ole2uno.hxx
new file mode 100644
index 000000000..5fcf2fd96
--- /dev/null
+++ b/extensions/source/ole/ole2uno.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "wincrap.hxx"
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/registry/XRegistryKey.hpp>
+#include <com/sun/star/bridge/XBridgeSupplier2.hpp>
+#include <com/sun/star/bridge/ModelDependent.hpp>
+#include <com/sun/star/reflection/InvocationTargetException.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/factory.hxx>
+#include <sal/types.h>
+#include <typelib/typeclass.h>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+#include <rtl/process.h>
+#include <rtl/uuid.h>
+
+#define UNO_2_OLE_EXCEPTIONCODE 1001
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::registry;
+using namespace com::sun::star::reflection;
+using namespace com::sun::star::beans;
+using namespace osl;
+
+VARTYPE getVarType(const Any& val);
+/* creates a Type object for a given type name.
+
+ The function returns false if the name does not represent
+ a valid type.
+*/
+bool getType(BSTR name, Type& type);
+void o2u_attachCurrentThread();
+
+class BridgeRuntimeError
+{
+public:
+ explicit BridgeRuntimeError(const OUString& sMessage)
+ : message(sMessage)
+ {
+ }
+ OUString message;
+};
+
+Mutex* getBridgeMutex();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/oleautobridge.component b/extensions/source/ole/oleautobridge.component
new file mode 100644
index 000000000..09f7621c2
--- /dev/null
+++ b/extensions/source/ole/oleautobridge.component
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ prefix="oleautobridge" xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.ole.OleClient">
+ <service name="com.sun.star.bridge.OleObjectFactory"/>
+ <service name="com.sun.star.bridge.oleautomation.Factory"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.ole.OleConverter2">
+ <service name="com.sun.star.bridge.OleBridgeSupplier2"/>
+ <service name="com.sun.star.bridge.oleautomation.BridgeSupplier"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.ole.OleConverterVar1">
+ <service name="com.sun.star.bridge.OleBridgeSupplierVar1"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.ole.OleServer">
+ <service name="com.sun.star.bridge.OleApplicationRegistration"/>
+ <service name="com.sun.star.bridge.oleautomation.ApplicationRegistration"/>
+ </implementation>
+</component>
diff --git a/extensions/source/ole/oledll.cxx b/extensions/source/ole/oledll.cxx
new file mode 100644
index 000000000..1275f4dc6
--- /dev/null
+++ b/extensions/source/ole/oledll.cxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#define STRICT
+#define _WIN32_DCOM
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wall"
+#pragma clang diagnostic ignored "-Wattributes"
+#pragma clang diagnostic ignored "-Wdelete-incomplete"
+#pragma clang diagnostic ignored "-Wextra"
+#pragma clang diagnostic ignored "-Wint-to-pointer-cast"
+#pragma clang diagnostic ignored "-Winvalid-noreturn"
+#pragma clang diagnostic ignored "-Wmicrosoft"
+#pragma clang diagnostic ignored "-Wnon-pod-varargs"
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+#include <atlbase.h>
+static CComModule _Module;
+#include <atlcom.h>
+
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+BEGIN_OBJECT_MAP(ObjectMap)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-field-initializers"
+#endif
+END_OBJECT_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+// DLL Entry Point
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
+{
+ if (dwReason == DLL_PROCESS_ATTACH)
+ {
+ _Module.Init(ObjectMap, hInstance);
+ DisableThreadLibraryCalls(hInstance);
+ }
+ else if (dwReason == DLL_PROCESS_DETACH)
+ {
+ _Module.Term();
+ }
+ return TRUE; // ok
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/oleobjw.cxx b/extensions/source/ole/oleobjw.cxx
new file mode 100644
index 000000000..85f410c54
--- /dev/null
+++ b/extensions/source/ole/oleobjw.cxx
@@ -0,0 +1,2513 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ole2uno.hxx"
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/doublecheckedlocking.h>
+#include <osl/thread.h>
+
+#include <memory>
+#include <string_view>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/FailReason.hpp>
+#include <com/sun/star/beans/XMaterialHolder.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/script/XInvocation.hpp>
+#include <com/sun/star/bridge/ModelDependent.hpp>
+
+#include <com/sun/star/bridge/oleautomation/NamedArgument.hpp>
+#include <com/sun/star/bridge/oleautomation/PropertyPutArgument.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <typelib/typedescription.hxx>
+#include <rtl/uuid.h>
+#include <rtl/ustring.hxx>
+
+#include "jscriptclasses.hxx"
+
+#include "oleobjw.hxx"
+#include "unoobjw.hxx"
+#include <stdio.h>
+using namespace osl;
+using namespace cppu;
+using namespace com::sun::star::script;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::bridge;
+using namespace com::sun::star::bridge::oleautomation;
+using namespace com::sun::star::bridge::ModelDependent;
+using namespace ::com::sun::star;
+
+
+#define JSCRIPT_ID_PROPERTY L"_environment"
+#define JSCRIPT_ID L"jscript"
+
+// key: XInterface pointer created by Invocation Adapter Factory
+// value: XInterface pointer to the wrapper class.
+// Entries to the map are made within
+// Any createOleObjectWrapper(IUnknown* pUnknown, const Type& aType);
+// Entries are being deleted if the wrapper class's destructor has been
+// called.
+// Before UNO object is wrapped to COM object this map is checked
+// to see if the UNO object is already a wrapper.
+std::unordered_map<sal_uIntPtr, sal_uIntPtr> AdapterToWrapperMap;
+// key: XInterface of the wrapper object.
+// value: XInterface of the Interface created by the Invocation Adapter Factory.
+// A COM wrapper is responsible for removing the corresponding entry
+// in AdapterToWrapperMap if it is being destroyed. Because the wrapper does not
+// know about its adapted interface it uses WrapperToAdapterMap to get the
+// adapted interface which is then used to locate the entry in AdapterToWrapperMap.
+std::unordered_map<sal_uIntPtr,sal_uIntPtr> WrapperToAdapterMap;
+
+std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > ComPtrToWrapperMap;
+
+IUnknownWrapper::IUnknownWrapper( Reference<XMultiServiceFactory> const & xFactory,
+ sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass):
+ UnoConversionUtilities<IUnknownWrapper>( xFactory, unoWrapperClass, comWrapperClass),
+ m_pxIdlClass( nullptr), m_eJScript( JScriptUndefined),
+ m_bComTlbIndexInit(false), m_bHasDfltMethod(false), m_bHasDfltProperty(false)
+{
+}
+
+
+IUnknownWrapper::~IUnknownWrapper()
+{
+ o2u_attachCurrentThread();
+ MutexGuard guard(getBridgeMutex());
+ XInterface * xIntRoot = static_cast<OWeakObject *>(this);
+#if OSL_DEBUG_LEVEL > 0
+ acquire(); // make sure we don't delete us twice because of Reference
+ OSL_ASSERT( Reference<XInterface>( static_cast<XWeak*>(this), UNO_QUERY).get() == xIntRoot );
+#endif
+
+ // remove entries in global maps
+ auto it= WrapperToAdapterMap.find( reinterpret_cast<sal_uIntPtr>(xIntRoot));
+ if( it != WrapperToAdapterMap.end())
+ {
+ sal_uIntPtr adapter= it->second;
+
+ AdapterToWrapperMap.erase( adapter);
+ WrapperToAdapterMap.erase( it);
+ }
+
+ auto it_c= ComPtrToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(m_spUnknown.p));
+ if(it_c != ComPtrToWrapperMap.end())
+ ComPtrToWrapperMap.erase(it_c);
+}
+
+Any IUnknownWrapper::queryInterface(const Type& t)
+{
+ if (t == cppu::UnoType<XDefaultMethod>::get() && !m_bHasDfltMethod )
+ return Any();
+ if (t == cppu::UnoType<XDefaultProperty>::get() && !m_bHasDfltProperty )
+ return Any();
+ if ( ( t == cppu::UnoType<XInvocation>::get() || t == cppu::UnoType<XAutomationInvocation>::get() ) && !m_spDispatch)
+ return Any();
+ // XDirectInvocation seems to be an oracle replacement for XAutomationInvocation, however it is flawed especially wrt. assumptions about whether to invoke a
+ // Put or Get property, the implementation code has no business guessing that, it's up to the caller to decide that. Worse XDirectInvocation duplicates lots of code.
+ // XAutomationInvocation provides separate calls for put& get
+ // properties. Note: Currently the basic runtime doesn't call put properties directly, it should... after all the basic runtime should know whether it is calling a put or get property.
+ // For the moment for ease of merging we will let the XDirectInvoke and XAuthomationInvocation interfaces stay side by side (and for the moment at least I would prefer the basic
+ // runtime to call XAutomationInvocation instead of XDirectInvoke
+ return WeakImplHelper<XBridgeSupplier2,
+ XInitialization, XAutomationObject, XDefaultProperty, XDefaultMethod, XDirectInvocation, XAutomationInvocation >::queryInterface(t);
+}
+
+Reference<XIntrospectionAccess> SAL_CALL IUnknownWrapper::getIntrospection()
+{
+ Reference<XIntrospectionAccess> ret;
+
+ return ret;
+}
+
+Any SAL_CALL IUnknownWrapper::invokeGetProperty( const OUString& aPropertyName, const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam )
+{
+ Any aResult;
+ try
+ {
+ o2u_attachCurrentThread();
+ ITypeInfo * pInfo = getTypeInfo();
+ FuncDesc aDescGet(pInfo);
+ FuncDesc aDescPut(pInfo);
+ VarDesc aVarDesc(pInfo);
+ getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc);
+ if ( !aDescGet )
+ {
+ OUString msg("[automation bridge]Property \"" + aPropertyName +
+ "\" is not supported");
+ throw UnknownPropertyException(msg);
+ }
+ aResult = invokeWithDispIdComTlb( aDescGet, aPropertyName, aParams, aOutParamIndex, aOutParam );
+ }
+ catch ( const Exception& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::invokeGetProperty ! Message : \n " +
+ e.Message,
+ nullptr, anyEx );
+ }
+ return aResult;
+}
+
+Any SAL_CALL IUnknownWrapper::invokePutProperty( const OUString& aPropertyName, const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam )
+{
+ Any aResult;
+ try
+ {
+ o2u_attachCurrentThread();
+ ITypeInfo * pInfo = getTypeInfo();
+ FuncDesc aDescGet(pInfo);
+ FuncDesc aDescPut(pInfo);
+ VarDesc aVarDesc(pInfo);
+ getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc);
+ if ( !aDescPut )
+ {
+ OUString msg("[automation bridge]Property \"" + aPropertyName +
+ "\" is not supported");
+ throw UnknownPropertyException(msg);
+ }
+ aResult = invokeWithDispIdComTlb( aDescPut, aPropertyName, aParams, aOutParamIndex, aOutParam );
+ }
+ catch ( const Exception& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::invokePutProperty ! Message : \n" +
+ e.Message,
+ nullptr, anyEx );
+ }
+ return aResult;
+}
+
+
+Any SAL_CALL IUnknownWrapper::invoke( const OUString& aFunctionName,
+ const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex,
+ Sequence< Any >& aOutParam )
+{
+ if ( ! m_spDispatch )
+ {
+ throw RuntimeException(
+ "[automation bridge] The object does not have an IDispatch interface");
+ }
+
+ Any ret;
+
+ try
+ {
+ o2u_attachCurrentThread();
+
+ TypeDescription methodDesc;
+ getMethodInfo(aFunctionName, methodDesc);
+ if( methodDesc.is())
+ {
+ ret = invokeWithDispIdUnoTlb(aFunctionName,
+ aParams,
+ aOutParamIndex,
+ aOutParam);
+ }
+ else
+ {
+ ret= invokeWithDispIdComTlb( aFunctionName,
+ aParams,
+ aOutParamIndex,
+ aOutParam);
+ }
+ }
+ catch (const IllegalArgumentException &)
+ {
+ throw;
+ }
+ catch (const CannotConvertException &)
+ {
+ throw;
+ }
+ catch (const BridgeRuntimeError & e)
+ {
+ throw RuntimeException(e.message);
+ }
+ catch (const Exception & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::invoke ! Message : \n" +
+ e.Message,
+ nullptr, anyEx );
+
+ }
+ catch(...)
+ {
+ throw RuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::Invoke !");
+ }
+ return ret;
+}
+
+void SAL_CALL IUnknownWrapper::setValue( const OUString& aPropertyName,
+ const Any& aValue )
+{
+ if ( ! m_spDispatch )
+ {
+ throw RuntimeException(
+ "[automation bridge] The object does not have an IDispatch interface");
+ }
+ try
+ {
+ o2u_attachCurrentThread();
+
+ ITypeInfo * pInfo = getTypeInfo();
+ FuncDesc aDescGet(pInfo);
+ FuncDesc aDescPut(pInfo);
+ VarDesc aVarDesc(pInfo);
+ getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc);
+ //check if there is such a property at all or if it is read only
+ if ( ! aDescPut && ! aDescGet && ! aVarDesc)
+ {
+ OUString msg("[automation bridge]Property \"" + aPropertyName +
+ "\" is not supported");
+ throw UnknownPropertyException(msg);
+ }
+
+ if ( (! aDescPut && aDescGet)
+ || (aVarDesc && aVarDesc->wVarFlags == VARFLAG_FREADONLY) )
+ {
+ //read-only
+ SAL_WARN( "extensions.olebridge", "[automation bridge] Property " << aPropertyName << " is read-only");
+ // ignore silently
+ return;
+ }
+
+ HRESULT hr= S_OK;
+ DISPPARAMS dispparams;
+ CComVariant varArg;
+ CComVariant varRefArg;
+ CComVariant varResult;
+ ExcepInfo excepinfo;
+ unsigned int uArgErr;
+
+ // converting UNO value to OLE variant
+ DISPID dispidPut= DISPID_PROPERTYPUT;
+ dispparams.rgdispidNamedArgs = &dispidPut;
+ dispparams.cArgs = 1;
+ dispparams.cNamedArgs = 1;
+ dispparams.rgvarg = & varArg;
+
+ OSL_ASSERT(aDescPut || aVarDesc);
+
+ VARTYPE vt = 0;
+ DISPID dispid = 0;
+ INVOKEKIND invkind = INVOKE_PROPERTYPUT;
+ //determine the expected type, dispid, invoke kind (DISPATCH_PROPERTYPUT,
+ //DISPATCH_PROPERTYPUTREF)
+ if (aDescPut)
+ {
+ vt = getElementTypeDesc(& aDescPut->lprgelemdescParam[0].tdesc);
+ dispid = aDescPut->memid;
+ invkind = aDescPut->invkind;
+ }
+ else
+ {
+ vt = getElementTypeDesc( & aVarDesc->elemdescVar.tdesc);
+ dispid = aVarDesc->memid;
+ if (vt == VT_UNKNOWN || vt == VT_DISPATCH ||
+ (vt & VT_ARRAY) || (vt & VT_BYREF))
+ {
+ invkind = INVOKE_PROPERTYPUTREF;
+ }
+ }
+
+ // convert the uno argument
+ if (vt & VT_BYREF)
+ {
+ anyToVariant( & varRefArg, aValue, ::sal::static_int_cast< VARTYPE, int >( vt ^ VT_BYREF ) );
+ varArg.vt = vt;
+ if( (vt & VT_TYPEMASK) == VT_VARIANT)
+ varArg.byref = & varRefArg;
+ else if ((vt & VT_TYPEMASK) == VT_DECIMAL)
+ varArg.byref = & varRefArg.decVal;
+ else
+ varArg.byref = & varRefArg.byref;
+ }
+ else
+ {
+ anyToVariant(& varArg, aValue, vt);
+ }
+ // call to IDispatch
+ hr = m_spDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, ::sal::static_int_cast< WORD, INVOKEKIND >( invkind ),
+ &dispparams, & varResult, & excepinfo, &uArgErr);
+
+ // lookup error code
+ switch (hr)
+ {
+ case S_OK:
+ break;
+ case DISP_E_BADPARAMCOUNT:
+ throw RuntimeException();
+ break;
+ case DISP_E_BADVARTYPE:
+ throw RuntimeException();
+ break;
+ case DISP_E_EXCEPTION:
+ throw InvocationTargetException();
+ break;
+ case DISP_E_MEMBERNOTFOUND:
+ throw UnknownPropertyException();
+ break;
+ case DISP_E_NONAMEDARGS:
+ throw RuntimeException();
+ break;
+ case DISP_E_OVERFLOW:
+ throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>(
+ static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr);
+ break;
+ case DISP_E_PARAMNOTFOUND:
+ throw IllegalArgumentException("call to OLE object failed", static_cast<XInterface*>(
+ static_cast<XWeak*>(this)), ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr )) ;
+ break;
+ case DISP_E_TYPEMISMATCH:
+ throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>(
+ static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::UNKNOWN, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr ));
+ break;
+ case DISP_E_UNKNOWNINTERFACE:
+ throw RuntimeException();
+ break;
+ case DISP_E_UNKNOWNLCID:
+ throw RuntimeException();
+ break;
+ case DISP_E_PARAMNOTOPTIONAL:
+ throw CannotConvertException("call to OLE object failed",static_cast<XInterface*>(
+ static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr);
+ break;
+ default:
+ throw RuntimeException();
+ break;
+ }
+ }
+ catch (const CannotConvertException &)
+ {
+ throw;
+ }
+ catch (const UnknownPropertyException &)
+ {
+ throw;
+ }
+ catch (const BridgeRuntimeError& e)
+ {
+ throw RuntimeException(e.message);
+ }
+ catch (const Exception & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::setValue ! Message : \n" +
+ e.Message,
+ nullptr, anyEx );
+
+ }
+ catch (...)
+ {
+ throw RuntimeException(
+ "[automation bridge] unexpected exception in "
+ "IUnknownWrapper::setValue !");
+ }
+}
+
+Any SAL_CALL IUnknownWrapper::getValue( const OUString& aPropertyName )
+{
+ if ( ! m_spDispatch )
+ {
+ throw RuntimeException(
+ "[automation bridge] The object does not have an IDispatch interface");
+ }
+ Any ret;
+ try
+ {
+ o2u_attachCurrentThread();
+ ITypeInfo * pInfo = getTypeInfo();
+ // I was going to implement an XServiceInfo interface to allow the type
+ // of the automation object to be exposed... but it seems
+ // from looking at comments in the code that it is possible for
+ // this object to actually wrap a UNO object ( I guess if automation is
+ // used from MSO to create Openoffice objects ) Therefore, those objects
+ // will more than likely already have their own XServiceInfo interface.
+ // Instead here I chose a name that should be illegal both in COM and
+ // UNO ( from an IDL point of view ) therefore I think this is a safe
+ // hack
+ if ( aPropertyName == "$GetTypeName" )
+ {
+ if ( pInfo && m_sTypeName.getLength() == 0 )
+ {
+ m_sTypeName = "IDispatch";
+ CComBSTR sName;
+
+ if ( SUCCEEDED( pInfo->GetDocumentation( -1, &sName, nullptr, nullptr, nullptr ) ) )
+ {
+ OUString sTmp( o3tl::toU(LPCOLESTR(sName)));
+ if ( sTmp.startsWith("_") )
+ sTmp = sTmp.copy(1);
+ // do we own the memory for pTypeLib, msdn doc is vague
+ // I'll assume we do
+ CComPtr< ITypeLib > pTypeLib;
+ unsigned int index;
+ if ( SUCCEEDED( pInfo->GetContainingTypeLib( &pTypeLib.p, &index )) )
+ {
+ if ( SUCCEEDED( pTypeLib->GetDocumentation( -1, &sName, nullptr, nullptr, nullptr ) ) )
+ {
+ OUString sLibName( o3tl::toU(LPCOLESTR(sName)));
+ m_sTypeName = sLibName + "." + sTmp;
+
+ }
+ }
+ }
+
+ }
+ ret <<= m_sTypeName;
+ return ret;
+ }
+ FuncDesc aDescGet(pInfo);
+ FuncDesc aDescPut(pInfo);
+ VarDesc aVarDesc(pInfo);
+ getPropDesc(aPropertyName, & aDescGet, & aDescPut, & aVarDesc);
+ if ( ! aDescGet && ! aDescPut && ! aVarDesc)
+ {
+ //property not found
+ OUString msg("[automation bridge]Property \"" + aPropertyName +
+ "\" is not supported");
+ throw UnknownPropertyException(msg);
+ }
+ // write-only should not be possible
+ OSL_ASSERT( aDescGet || ! aDescPut);
+
+ HRESULT hr;
+ DISPPARAMS dispparams = {nullptr, nullptr, 0, 0};
+ CComVariant varResult;
+ ExcepInfo excepinfo;
+ unsigned int uArgErr;
+ DISPID dispid;
+ if (aDescGet)
+ dispid = aDescGet->memid;
+ else if (aVarDesc)
+ dispid = aVarDesc->memid;
+ else
+ dispid = aDescPut->memid;
+
+ hr = m_spDispatch->Invoke(dispid,
+ IID_NULL,
+ LOCALE_USER_DEFAULT,
+ DISPATCH_PROPERTYGET,
+ &dispparams,
+ &varResult,
+ &excepinfo,
+ &uArgErr);
+
+ // converting return value and out parameter back to UNO
+ if (hr == S_OK)
+ {
+ // If the com object implements uno interfaces then we have
+ // to convert the attribute into the expected type.
+ TypeDescription attrInfo;
+ getAttributeInfo(aPropertyName, attrInfo);
+ if( attrInfo.is() )
+ variantToAny( &varResult, ret, Type( attrInfo.get()->pWeakRef));
+ else
+ variantToAny(&varResult, ret);
+ }
+
+ // lookup error code
+ switch (hr)
+ {
+ case S_OK:
+ break;
+ case DISP_E_BADPARAMCOUNT:
+ case DISP_E_BADVARTYPE:
+ case DISP_E_EXCEPTION:
+ throw RuntimeException(OUString(o3tl::toU(excepinfo.bstrDescription)));
+ break;
+ case DISP_E_MEMBERNOTFOUND:
+ throw UnknownPropertyException(OUString(o3tl::toU(excepinfo.bstrDescription)));
+ break;
+ default:
+ throw RuntimeException(OUString(o3tl::toU(excepinfo.bstrDescription)));
+ break;
+ }
+ }
+ catch ( const UnknownPropertyException& )
+ {
+ throw;
+ }
+ catch (const BridgeRuntimeError& e)
+ {
+ throw RuntimeException(e.message);
+ }
+ catch (const Exception & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::getValue ! Message : \n" +
+ e.Message,
+ nullptr, anyEx );
+ }
+ catch (...)
+ {
+ throw RuntimeException(
+ "[automation bridge] unexpected exception in "
+ "IUnknownWrapper::getValue !");
+ }
+ return ret;
+}
+
+sal_Bool SAL_CALL IUnknownWrapper::hasMethod( const OUString& aName )
+{
+ if ( ! m_spDispatch )
+ {
+ throw RuntimeException(
+ "[automation bridge] The object does not have an IDispatch interface");
+ }
+ bool ret = false;
+
+ try
+ {
+ o2u_attachCurrentThread();
+ ITypeInfo* pInfo = getTypeInfo();
+ FuncDesc aDesc(pInfo);
+ getFuncDesc(aName, & aDesc);
+ // Automation properties can have arguments. Those are treated as methods and
+ //are called through XInvocation::invoke.
+ if ( ! aDesc)
+ {
+ FuncDesc aDescGet(pInfo);
+ FuncDesc aDescPut(pInfo);
+ VarDesc aVarDesc(pInfo);
+ getPropDesc( aName, & aDescGet, & aDescPut, & aVarDesc);
+ if ((aDescGet && aDescGet->cParams > 0)
+ || (aDescPut && aDescPut->cParams > 0))
+ ret = true;
+ }
+ else
+ ret = true;
+ }
+ catch (const BridgeRuntimeError& e)
+ {
+ throw RuntimeException(e.message);
+ }
+ catch (const Exception & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::hasMethod ! Message : \n" +
+ e.Message,
+ nullptr, anyEx );
+ }
+ catch (...)
+ {
+ throw RuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::hasMethod !");
+ }
+ return ret;
+}
+
+sal_Bool SAL_CALL IUnknownWrapper::hasProperty( const OUString& aName )
+{
+ if ( ! m_spDispatch )
+ {
+ throw RuntimeException("[automation bridge] The object does not have an "
+ "IDispatch interface");
+ }
+ bool ret = false;
+ try
+ {
+ o2u_attachCurrentThread();
+
+ ITypeInfo * pInfo = getTypeInfo();
+ FuncDesc aDescGet(pInfo);
+ FuncDesc aDescPut(pInfo);
+ VarDesc aVarDesc(pInfo);
+ getPropDesc(aName, & aDescGet, & aDescPut, & aVarDesc);
+
+ // we should probably just check the func kind
+ // basic has been modified to handle properties ( 'get' ) props at
+ // least with parameters
+ // additionally you can call invoke(Get|Set)Property on the bridge
+ // you can determine if a property has parameter is hasMethod
+ // returns true for the name
+ if (aVarDesc
+ || aDescPut
+ || aDescGet )
+ {
+ ret = true;
+ }
+ }
+ catch (const BridgeRuntimeError& e)
+ {
+ throw RuntimeException(e.message);
+ }
+ catch (const Exception & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::hasProperty ! Message : \n" +
+ e.Message,
+ nullptr, anyEx );
+
+ }
+ catch (...)
+ {
+ throw RuntimeException("[automation bridge] unexpected exception in "
+ "IUnknownWrapper::hasProperty !");
+ }
+ return ret;
+}
+
+Any SAL_CALL IUnknownWrapper::createBridge( const Any& modelDepObject,
+ const Sequence< sal_Int8 >& /*aProcessId*/, sal_Int16 sourceModelType,
+ sal_Int16 destModelType )
+{
+ Any ret;
+ o2u_attachCurrentThread();
+
+ if (
+ (sourceModelType == UNO) &&
+ (destModelType == OLE) &&
+ (modelDepObject.getValueTypeClass() == TypeClass_INTERFACE)
+ )
+ {
+ Reference<XInterface> xInt( *static_cast<XInterface* const *>(modelDepObject.getValue()));
+ Reference<XInterface> xSelf( static_cast<OWeakObject*>(this));
+
+ if (xInt == xSelf)
+ {
+ VARIANT* pVariant = static_cast<VARIANT*>(CoTaskMemAlloc(sizeof(VARIANT)));
+
+ VariantInit(pVariant);
+ if (m_bOriginalDispatch)
+ {
+ pVariant->vt = VT_DISPATCH;
+ pVariant->pdispVal = m_spDispatch;
+ pVariant->pdispVal->AddRef();
+ }
+ else
+ {
+ pVariant->vt = VT_UNKNOWN;
+ pVariant->punkVal = m_spUnknown;
+ pVariant->punkVal->AddRef();
+ }
+
+ ret.setValue(static_cast<void*>(&pVariant), cppu::UnoType<sal_uIntPtr>::get());
+ }
+ }
+
+ return ret;
+}
+/** @internal
+ @exception IllegalArgumentException
+ @exception CannotConvertException
+ @exception InvocationTargetException
+ @RuntimeException
+*/
+Any IUnknownWrapper::invokeWithDispIdUnoTlb(const OUString& sFunctionName,
+ const Sequence< Any >& Params,
+ Sequence< sal_Int16 >& OutParamIndex,
+ Sequence< Any >& OutParam)
+{
+ Any ret;
+ HRESULT hr= S_OK;
+
+ sal_Int32 parameterCount= Params.getLength();
+ sal_Int32 outParameterCount= 0;
+ typelib_InterfaceMethodTypeDescription* pMethod= nullptr;
+ TypeDescription methodDesc;
+ getMethodInfo(sFunctionName, methodDesc);
+
+ // We need to know whether the IDispatch is from a JScript object.
+ // Then out and in/out parameters have to be treated differently than
+ // with common COM objects.
+ bool bJScriptObject= isJScriptObject();
+ std::unique_ptr<CComVariant[]> sarParams;
+ std::unique_ptr<CComVariant[]> sarParamsRef;
+ CComVariant *pVarParams= nullptr;
+ CComVariant *pVarParamsRef= nullptr;
+ bool bConvRet= true;
+
+ if( methodDesc.is())
+ {
+ pMethod = reinterpret_cast<typelib_InterfaceMethodTypeDescription*>(methodDesc.get());
+ parameterCount = pMethod->nParams;
+ // Create the Array for the array being passed in DISPPARAMS
+ // the array also contains the outparameter (but not the values)
+ if( pMethod->nParams > 0)
+ {
+ sarParams.reset(new CComVariant[ parameterCount]);
+ pVarParams = sarParams.get();
+ }
+
+ // Create the Array for the out an in/out parameter. These values
+ // are referenced by the VT_BYREF VARIANTs in DISPPARAMS.
+ // We need to find out the number of out and in/out parameter.
+ for( sal_Int32 i=0; i < parameterCount; i++)
+ {
+ if( pMethod->pParams[i].bOut)
+ outParameterCount++;
+ }
+
+ if( !bJScriptObject)
+ {
+ sarParamsRef.reset(new CComVariant[outParameterCount]);
+ pVarParamsRef = sarParamsRef.get();
+ // build up the parameters for IDispatch::Invoke
+ sal_Int32 outParamIndex=0;
+ int i = 0;
+ try
+ {
+ for( i= 0; i < parameterCount; i++)
+ {
+ // In parameter
+ if( pMethod->pParams[i].bIn && ! pMethod->pParams[i].bOut)
+ {
+ anyToVariant( &pVarParams[parameterCount - i -1], Params.getConstArray()[i]);
+ }
+ // Out parameter + in/out parameter
+ else if( pMethod->pParams[i].bOut )
+ {
+ CComVariant var;
+ if(pMethod->pParams[i].bIn)
+ {
+ anyToVariant( & var,Params[i]);
+ pVarParamsRef[outParamIndex] = var;
+ }
+
+ switch( pMethod->pParams[i].pTypeRef->eTypeClass)
+ {
+ case typelib_TypeClass_INTERFACE:
+ case typelib_TypeClass_STRUCT:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt= VT_DISPATCH;
+ pVarParamsRef[ outParamIndex].pdispVal= nullptr;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_DISPATCH | VT_BYREF;
+ pVarParams[parameterCount - i -1].ppdispVal= &pVarParamsRef[outParamIndex].pdispVal;
+ break;
+ case typelib_TypeClass_ENUM:
+ case typelib_TypeClass_LONG:
+ case typelib_TypeClass_UNSIGNED_LONG:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_I4;
+ pVarParamsRef[ outParamIndex].lVal = 0;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_I4 | VT_BYREF;
+ pVarParams[parameterCount - i -1].plVal= &pVarParamsRef[outParamIndex].lVal;
+ break;
+ case typelib_TypeClass_SEQUENCE:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_ARRAY| VT_VARIANT;
+ pVarParamsRef[ outParamIndex].parray= nullptr;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_ARRAY| VT_BYREF | VT_VARIANT;
+ pVarParams[parameterCount - i -1].pparray= &pVarParamsRef[outParamIndex].parray;
+ break;
+ case typelib_TypeClass_ANY:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_EMPTY;
+ pVarParamsRef[ outParamIndex].lVal = 0;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_VARIANT | VT_BYREF;
+ pVarParams[parameterCount - i -1].pvarVal = &pVarParamsRef[outParamIndex];
+ break;
+ case typelib_TypeClass_BOOLEAN:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_BOOL;
+ pVarParamsRef[ outParamIndex].boolVal = 0;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_BOOL| VT_BYREF;
+ pVarParams[parameterCount - i -1].pboolVal =
+ & pVarParamsRef[outParamIndex].boolVal;
+ break;
+
+ case typelib_TypeClass_STRING:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_BSTR;
+ pVarParamsRef[ outParamIndex].bstrVal= nullptr;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_BSTR| VT_BYREF;
+ pVarParams[parameterCount - i -1].pbstrVal=
+ & pVarParamsRef[outParamIndex].bstrVal;
+ break;
+
+ case typelib_TypeClass_FLOAT:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_R4;
+ pVarParamsRef[ outParamIndex].fltVal= 0;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_R4| VT_BYREF;
+ pVarParams[parameterCount - i -1].pfltVal =
+ & pVarParamsRef[outParamIndex].fltVal;
+ break;
+ case typelib_TypeClass_DOUBLE:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_R8;
+ pVarParamsRef[ outParamIndex].dblVal= 0;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_R8| VT_BYREF;
+ pVarParams[parameterCount - i -1].pdblVal=
+ & pVarParamsRef[outParamIndex].dblVal;
+ break;
+ case typelib_TypeClass_BYTE:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_UI1;
+ pVarParamsRef[ outParamIndex].bVal= 0;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_UI1| VT_BYREF;
+ pVarParams[parameterCount - i -1].pbVal=
+ & pVarParamsRef[outParamIndex].bVal;
+ break;
+ case typelib_TypeClass_CHAR:
+ case typelib_TypeClass_SHORT:
+ case typelib_TypeClass_UNSIGNED_SHORT:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_I2;
+ pVarParamsRef[ outParamIndex].iVal = 0;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_I2| VT_BYREF;
+ pVarParams[parameterCount - i -1].piVal=
+ & pVarParamsRef[outParamIndex].iVal;
+ break;
+
+ default:
+ if( ! pMethod->pParams[i].bIn)
+ {
+ pVarParamsRef[ outParamIndex].vt = VT_EMPTY;
+ pVarParamsRef[ outParamIndex].lVal = 0;
+ }
+ pVarParams[parameterCount - i -1].vt = VT_VARIANT | VT_BYREF;
+ pVarParams[parameterCount - i -1].pvarVal =
+ & pVarParamsRef[outParamIndex];
+ }
+ outParamIndex++;
+ } // end else if
+ } // end for
+ }
+ catch (IllegalArgumentException & e)
+ {
+ e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, int >( i );
+ throw;
+ }
+ catch (CannotConvertException & e)
+ {
+ e.ArgumentIndex = i;
+ throw;
+ }
+ }
+ else // it is a JScriptObject
+ {
+ int i = 0;
+ try
+ {
+ for( ; i< parameterCount; i++)
+ {
+ // In parameter
+ if( pMethod->pParams[i].bIn && ! pMethod->pParams[i].bOut)
+ {
+ anyToVariant( &pVarParams[parameterCount - i -1], Params.getConstArray()[i]);
+ }
+ // Out parameter + in/out parameter
+ else if( pMethod->pParams[i].bOut )
+ {
+ CComObject<JScriptOutParam>* pParamObject;
+ if( !SUCCEEDED( CComObject<JScriptOutParam>::CreateInstance( &pParamObject)))
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]IUnknownWrapper::"
+ "invokeWithDispIdUnoTlb\n"
+ "Could not create out parameter at index: " +
+ OUString::number(static_cast<sal_Int32>(i)));
+ }
+
+ CComPtr<IUnknown> pUnk(pParamObject->GetUnknown());
+ CComQIPtr<IDispatch> pDisp( pUnk);
+
+ pVarParams[ parameterCount - i -1].vt= VT_DISPATCH;
+ pVarParams[ parameterCount - i -1].pdispVal= pDisp;
+ pVarParams[ parameterCount - i -1].pdispVal->AddRef();
+ // if the param is in/out then put the parameter on index 0
+ if( pMethod->pParams[i].bIn ) // in / out
+ {
+ CComVariant varParam;
+ anyToVariant( &varParam, Params.getConstArray()[i]);
+ CComDispatchDriver dispDriver( pDisp);
+ if(FAILED( dispDriver.PutPropertyByName( L"0", &varParam)))
+ throw BridgeRuntimeError(
+ "[automation bridge]IUnknownWrapper::"
+ "invokeWithDispIdUnoTlb\n"
+ "Could not set property \"0\" for the in/out "
+ "param!");
+
+ }
+ }
+ }
+ }
+ catch (IllegalArgumentException & e)
+ {
+ e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, int >( i );
+ throw;
+ }
+ catch (CannotConvertException & e)
+ {
+ e.ArgumentIndex = i;
+ throw;
+ }
+ }
+ }
+ // No type description Available, that is we have to deal with a COM component,
+ // that does not implements UNO interfaces ( IDispatch based)
+ else
+ {
+ //We should not run into this block, because invokeWithDispIdComTlb should
+ //have been called instead.
+ OSL_ASSERT(false);
+ }
+
+
+ CComVariant varResult;
+ ExcepInfo excepinfo;
+ unsigned int uArgErr;
+ DISPPARAMS dispparams= { pVarParams, nullptr, static_cast<UINT>(parameterCount), 0};
+
+ // Get the DISPID
+ FuncDesc aDesc(getTypeInfo());
+ getFuncDesc(sFunctionName, & aDesc);
+ // invoking OLE method
+ hr = m_spDispatch->Invoke(aDesc->memid,
+ IID_NULL,
+ LOCALE_USER_DEFAULT,
+ DISPATCH_METHOD,
+ &dispparams,
+ &varResult,
+ &excepinfo,
+ &uArgErr);
+
+ // converting return value and out parameter back to UNO
+ if (hr == S_OK)
+ {
+ if( outParameterCount && pMethod)
+ {
+ OutParamIndex.realloc( outParameterCount);
+ auto pOutParamIndex = OutParamIndex.getArray();
+ OutParam.realloc( outParameterCount);
+ auto pOutParam = OutParam.getArray();
+ sal_Int32 outIndex=0;
+ int i = 0;
+ try
+ {
+ for( ; i < parameterCount; i++)
+ {
+ if( pMethod->pParams[i].bOut )
+ {
+ pOutParamIndex[outIndex]= static_cast<sal_Int16>(i);
+ Any outAny;
+ if( !bJScriptObject)
+ {
+ variantToAny( &pVarParamsRef[outIndex], outAny,
+ Type(pMethod->pParams[i].pTypeRef), false);
+ pOutParam[outIndex++]= outAny;
+ }
+ else //JScriptObject
+ {
+ if( pVarParams[i].vt == VT_DISPATCH)
+ {
+ CComDispatchDriver pDisp( pVarParams[i].pdispVal);
+ if( pDisp)
+ {
+ CComVariant varOut;
+ if( SUCCEEDED( pDisp.GetPropertyByName( L"0", &varOut)))
+ {
+ variantToAny( &varOut, outAny,
+ Type(pMethod->pParams[parameterCount - 1 - i].pTypeRef), false);
+ pOutParam[outParameterCount - 1 - outIndex++]= outAny;
+ }
+ else
+ bConvRet= false;
+ }
+ else
+ bConvRet= false;
+ }
+ else
+ bConvRet= false;
+ }
+ }
+ if( !bConvRet) break;
+ }
+ }
+ catch(IllegalArgumentException & e)
+ {
+ e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, int >( i );
+ throw;
+ }
+ catch(CannotConvertException & e)
+ {
+ e.ArgumentIndex = i;
+ throw;
+ }
+ }
+ // return value, no type information available
+ if ( bConvRet)
+ {
+ try
+ {
+ if( pMethod )
+ variantToAny(&varResult, ret, Type( pMethod->pReturnTypeRef), false);
+ else
+ variantToAny(&varResult, ret, false);
+ }
+ catch (IllegalArgumentException & e)
+ {
+ e.Message =
+ "[automation bridge]IUnknownWrapper::invokeWithDispIdUnoTlb\n"
+ "Could not convert return value! \n Message: \n" + e.Message;
+ throw;
+ }
+ catch (CannotConvertException & e)
+ {
+ e.Message =
+ "[automation bridge]IUnknownWrapper::invokeWithDispIdUnoTlb\n"
+ "Could not convert return value! \n Message: \n" + e.Message;
+ throw;
+ }
+ }
+ }
+
+ if( !bConvRet) // conversion of return or out parameter failed
+ throw CannotConvertException("Call to COM object failed. Conversion of return or out value failed",
+ Reference<XInterface>( static_cast<XWeak*>(this), UNO_QUERY ), TypeClass_UNKNOWN,
+ FailReason::UNKNOWN, 0);// lookup error code
+ // conversion of return or out parameter failed
+ switch (hr)
+ {
+ case S_OK:
+ break;
+ case DISP_E_BADPARAMCOUNT:
+ throw IllegalArgumentException();
+ break;
+ case DISP_E_BADVARTYPE:
+ throw RuntimeException();
+ break;
+ case DISP_E_EXCEPTION:
+ throw InvocationTargetException();
+ break;
+ case DISP_E_MEMBERNOTFOUND:
+ throw IllegalArgumentException();
+ break;
+ case DISP_E_NONAMEDARGS:
+ throw IllegalArgumentException();
+ break;
+ case DISP_E_OVERFLOW:
+ throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>(
+ static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr);
+ break;
+ case DISP_E_PARAMNOTFOUND:
+ throw IllegalArgumentException("call to OLE object failed", static_cast<XInterface*>(
+ static_cast<XWeak*>(this)), ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr ));
+ break;
+ case DISP_E_TYPEMISMATCH:
+ throw CannotConvertException("call to OLE object failed",static_cast<XInterface*>(
+ static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::UNKNOWN, uArgErr);
+ break;
+ case DISP_E_UNKNOWNINTERFACE:
+ throw RuntimeException() ;
+ break;
+ case DISP_E_UNKNOWNLCID:
+ throw RuntimeException() ;
+ break;
+ case DISP_E_PARAMNOTOPTIONAL:
+ throw CannotConvertException("call to OLE object failed", static_cast<XInterface*>(
+ static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr);
+ break;
+ default:
+ throw RuntimeException();
+ break;
+ }
+
+ return ret;
+}
+
+
+// XInitialization
+void SAL_CALL IUnknownWrapper::initialize( const Sequence< Any >& aArguments )
+{
+ // 1.parameter is IUnknown
+ // 2.parameter is a boolean which indicates if the COM pointer was an IUnknown or IDispatch
+ // 3.parameter is a Sequence<Type>
+ o2u_attachCurrentThread();
+ OSL_ASSERT(aArguments.getLength() == 3);
+
+ m_spUnknown= *static_cast<IUnknown* const *>(aArguments[0].getValue());
+ m_spUnknown.QueryInterface( & m_spDispatch.p);
+
+ aArguments[1] >>= m_bOriginalDispatch;
+ aArguments[2] >>= m_seqTypes;
+
+ ITypeInfo* pType = nullptr;
+ try
+ {
+ // a COM object implementation that has no TypeInfo is still a legal COM object;
+ // such objects can at least be transported through UNO using the bridge
+ // so we should allow to create wrappers for them as well
+ pType = getTypeInfo();
+ }
+ catch( const BridgeRuntimeError& )
+ {}
+ catch( const Exception& )
+ {}
+
+ if ( pType )
+ {
+ try
+ {
+ // Get Default member
+ CComBSTR defaultMemberName;
+ if ( SUCCEEDED( pType->GetDocumentation(0, &defaultMemberName, nullptr, nullptr, nullptr ) ) )
+ {
+ OUString usName(o3tl::toU(LPCOLESTR(defaultMemberName)));
+ FuncDesc aDescGet(pType);
+ FuncDesc aDescPut(pType);
+ VarDesc aVarDesc(pType);
+ // see if this is a property first ( more likely to be a property then a method )
+ getPropDesc( usName, & aDescGet, & aDescPut, & aVarDesc);
+
+ if ( !aDescGet && !aDescPut )
+ {
+ getFuncDesc( usName, &aDescGet );
+ if ( !aDescGet )
+ throw BridgeRuntimeError( "[automation bridge]IUnknownWrapper::initialize() Failed to get Function or Property desc. for " + usName );
+ }
+ // now for some funny heuristics to make basic understand what to do
+ // a single aDescGet ( that doesn't take any params ) would be
+ // a read only ( defaultmember ) property e.g. this object
+ // should implement XDefaultProperty
+ // a single aDescGet ( that *does* ) take params is basically a
+ // default method e.g. implement XDefaultMethod
+
+ // a DescPut ( I guess we only really support a default param with '1' param ) as a setValue ( but I guess we can leave it through, the object will fail if we don't get it right anyway )
+ if ( aDescPut || ( aDescGet && aDescGet->cParams == 0 ) )
+ m_bHasDfltProperty = true;
+ if ( aDescGet->cParams > 0 )
+ m_bHasDfltMethod = true;
+ if ( m_bHasDfltProperty || m_bHasDfltMethod )
+ m_sDefaultMember = usName;
+ }
+ }
+ catch ( const BridgeRuntimeError & e )
+ {
+ throw RuntimeException( e.message );
+ }
+ catch( const Exception& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "[automation bridge] unexpected exception in IUnknownWrapper::initialize() error message: \n" + e.Message,
+ nullptr, anyEx );
+ }
+ }
+}
+
+
+// XDirectInvocation
+uno::Any SAL_CALL IUnknownWrapper::directInvoke( const OUString& aName, const uno::Sequence< uno::Any >& aParams )
+{
+ Any aResult;
+
+ if ( !m_spDispatch )
+ {
+ throw RuntimeException(
+ "[automation bridge] The object does not have an IDispatch interface");
+ }
+
+ o2u_attachCurrentThread();
+ DISPID dispid;
+ if ( !getDispid( aName, &dispid ) )
+ throw IllegalArgumentException(
+ "[automation bridge] The object does not have a function or property "
+ + aName, Reference<XInterface>(), 0);
+
+ CComVariant varResult;
+ ExcepInfo excepinfo;
+ unsigned int uArgErr = 0;
+ INVOKEKIND pInvkinds[2];
+ pInvkinds[0] = INVOKE_FUNC;
+ pInvkinds[1] = aParams.getLength() ? INVOKE_PROPERTYPUT : INVOKE_PROPERTYGET;
+ HRESULT hInvRes = E_FAIL;
+
+ // try Invoke first, if it does not work, try put/get property
+ for ( sal_Int32 nStep = 0; FAILED( hInvRes ) && nStep < 2; nStep++ )
+ {
+ DISPPARAMS dispparams = {nullptr, nullptr, 0, 0};
+
+ std::unique_ptr<DISPID[]> arDispidNamedArgs;
+ std::unique_ptr<CComVariant[]> ptrArgs;
+ std::unique_ptr<CComVariant[]> ptrRefArgs; // referenced arguments
+ CComVariant * arArgs = nullptr;
+ CComVariant * arRefArgs = nullptr;
+
+ dispparams.cArgs = aParams.getLength();
+
+ // Determine the number of named arguments
+ for ( uno::Any const & any : aParams )
+ if ( any.getValueType() == cppu::UnoType<NamedArgument>::get() )
+ dispparams.cNamedArgs ++;
+
+ // fill the named arguments
+ if ( dispparams.cNamedArgs > 0
+ && ( dispparams.cNamedArgs != 1 || pInvkinds[nStep] != INVOKE_PROPERTYPUT ) )
+ {
+ int nSizeAr = dispparams.cNamedArgs + 1;
+ if ( pInvkinds[nStep] == INVOKE_PROPERTYPUT )
+ nSizeAr = dispparams.cNamedArgs;
+
+ std::unique_ptr<OLECHAR*[]> saNames(new OLECHAR*[nSizeAr]);
+ OLECHAR ** pNames = saNames.get();
+ pNames[0] = const_cast<OLECHAR*>(o3tl::toW(aName.getStr()));
+
+ int cNamedArg = 0;
+ for ( size_t nInd = 0; nInd < dispparams.cArgs; nInd++ )
+ {
+ if (auto v = o3tl::tryAccess<NamedArgument>(aParams[nInd]))
+ {
+ const NamedArgument& arg = *v;
+
+ //We put the parameter names in reverse order into the array,
+ //so we can use the DISPID array for DISPPARAMS::rgdispidNamedArgs
+ //The first name in the array is the method name
+ pNames[nSizeAr - 1 - cNamedArg++] = const_cast<OLECHAR*>(o3tl::toW(arg.Name.getStr()));
+ }
+ }
+
+ arDispidNamedArgs.reset( new DISPID[nSizeAr] );
+ HRESULT hr = getTypeInfo()->GetIDsOfNames( pNames, nSizeAr, arDispidNamedArgs.get() );
+ if ( hr == E_NOTIMPL )
+ hr = m_spDispatch->GetIDsOfNames(IID_NULL, pNames, nSizeAr, LOCALE_USER_DEFAULT, arDispidNamedArgs.get() );
+
+ if ( SUCCEEDED( hr ) )
+ {
+ if ( pInvkinds[nStep] == DISPATCH_PROPERTYPUT )
+ {
+ DISPID* arIDs = arDispidNamedArgs.get();
+ arIDs[0] = DISPID_PROPERTYPUT;
+ dispparams.rgdispidNamedArgs = arIDs;
+ }
+ else
+ {
+ DISPID* arIDs = arDispidNamedArgs.get();
+ dispparams.rgdispidNamedArgs = & arIDs[1];
+ }
+ }
+ else if (hr == DISP_E_UNKNOWNNAME)
+ {
+ throw IllegalArgumentException(
+ "[automation bridge]One of the named arguments is wrong!",
+ Reference<XInterface>(), 0);
+ }
+ else
+ {
+ throw InvocationTargetException(
+ "[automation bridge] ITypeInfo::GetIDsOfNames returned error "
+ + OUString::number(static_cast<sal_Int32>(hr), 16), Reference<XInterface>(), Any());
+ }
+ }
+
+ //Convert arguments
+ ptrArgs.reset(new CComVariant[dispparams.cArgs]);
+ ptrRefArgs.reset(new CComVariant[dispparams.cArgs]);
+ arArgs = ptrArgs.get();
+ arRefArgs = ptrRefArgs.get();
+
+ sal_Int32 nInd = 0;
+ try
+ {
+ sal_Int32 revIndex = 0;
+ for ( nInd = 0; nInd < sal_Int32(dispparams.cArgs); nInd++)
+ {
+ revIndex = dispparams.cArgs - nInd - 1;
+ arRefArgs[revIndex].byref = nullptr;
+ Any anyArg;
+ if ( nInd < aParams.getLength() )
+ anyArg = aParams.getConstArray()[nInd];
+
+ // Property Put arguments
+ if ( anyArg.getValueType() == cppu::UnoType<PropertyPutArgument>::get() )
+ {
+ PropertyPutArgument arg;
+ anyArg >>= arg;
+ anyArg = arg.Value;
+ }
+ // named argument
+ if (anyArg.getValueType() == cppu::UnoType<NamedArgument>::get())
+ {
+ NamedArgument aNamedArgument;
+ anyArg >>= aNamedArgument;
+ anyArg = aNamedArgument.Value;
+ }
+
+ if ( nInd < aParams.getLength() && anyArg.getValueTypeClass() != TypeClass_VOID )
+ {
+ anyToVariant( &arArgs[revIndex], anyArg, VT_VARIANT );
+ }
+ else
+ {
+ arArgs[revIndex].vt = VT_ERROR;
+ arArgs[revIndex].scode = DISP_E_PARAMNOTFOUND;
+ }
+ }
+ }
+ catch (IllegalArgumentException & e)
+ {
+ e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, sal_Int32 >( nInd );
+ throw;
+ }
+ catch (CannotConvertException & e)
+ {
+ e.ArgumentIndex = nInd;
+ throw;
+ }
+
+ dispparams.rgvarg = arArgs;
+ // invoking OLE method
+ hInvRes = m_spDispatch->Invoke( dispid,
+ IID_NULL,
+ LOCALE_USER_DEFAULT,
+ ::sal::static_int_cast< WORD, INVOKEKIND >( pInvkinds[nStep] ),
+ &dispparams,
+ &varResult,
+ &excepinfo,
+ &uArgErr);
+ }
+
+ // converting return value and out parameter back to UNO
+ if ( SUCCEEDED( hInvRes ) )
+ variantToAny( &varResult, aResult, false );
+ else
+ {
+ // map error codes to exceptions
+ OUString message;
+ switch ( hInvRes )
+ {
+ case S_OK:
+ break;
+ case DISP_E_BADPARAMCOUNT:
+ throw IllegalArgumentException("[automation bridge] Wrong "
+ "number of arguments. Object returned DISP_E_BADPARAMCOUNT.",
+ nullptr, 0);
+ break;
+ case DISP_E_BADVARTYPE:
+ throw RuntimeException("[automation bridge] One or more "
+ "arguments have the wrong type. Object returned "
+ "DISP_E_BADVARTYPE.", nullptr);
+ break;
+ case DISP_E_EXCEPTION:
+ message = OUString::Concat("[automation bridge]: ")
+ + std::u16string_view(o3tl::toU(excepinfo.bstrDescription),
+ ::SysStringLen(excepinfo.bstrDescription));
+ throw InvocationTargetException(message, Reference<XInterface>(), Any());
+ break;
+ case DISP_E_MEMBERNOTFOUND:
+ message = "[automation bridge]: A function with the name \""
+ + aName + "\" is not supported. Object returned "
+ "DISP_E_MEMBERNOTFOUND.";
+ throw IllegalArgumentException(message, nullptr, 0);
+ break;
+ case DISP_E_NONAMEDARGS:
+ throw IllegalArgumentException("[automation bridge] Object "
+ "returned DISP_E_NONAMEDARGS",nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr ));
+ break;
+ case DISP_E_OVERFLOW:
+ throw CannotConvertException("[automation bridge] Call failed.",
+ static_cast<XInterface*>(
+ static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr);
+ break;
+ case DISP_E_PARAMNOTFOUND:
+ throw IllegalArgumentException("[automation bridge]Call failed."
+ "Object returned DISP_E_PARAMNOTFOUND.",
+ nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr ));
+ break;
+ case DISP_E_TYPEMISMATCH:
+ throw CannotConvertException("[automation bridge] Call failed. "
+ "Object returned DISP_E_TYPEMISMATCH",
+ static_cast<XInterface*>(
+ static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::UNKNOWN, uArgErr);
+ break;
+ case DISP_E_UNKNOWNINTERFACE:
+ throw RuntimeException("[automation bridge] Call failed. "
+ "Object returned DISP_E_UNKNOWNINTERFACE.",nullptr);
+ break;
+ case DISP_E_UNKNOWNLCID:
+ throw RuntimeException("[automation bridge] Call failed. "
+ "Object returned DISP_E_UNKNOWNLCID.",nullptr);
+ break;
+ case DISP_E_PARAMNOTOPTIONAL:
+ throw CannotConvertException("[automation bridge] Call failed."
+ "Object returned DISP_E_PARAMNOTOPTIONAL",
+ static_cast<XInterface*>(static_cast<XWeak*>(this)),
+ TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr);
+ break;
+ default:
+ throw RuntimeException();
+ break;
+ }
+ }
+
+ return aResult;
+}
+
+sal_Bool SAL_CALL IUnknownWrapper::hasMember( const OUString& aName )
+{
+ if ( ! m_spDispatch )
+ {
+ throw RuntimeException(
+ "[automation bridge] The object does not have an IDispatch interface");
+ }
+
+ o2u_attachCurrentThread();
+ DISPID dispid;
+ return getDispid( aName, &dispid );
+}
+
+
+// UnoConversionUtilities --------------------------------------------------------------------------------
+Reference< XInterface > IUnknownWrapper::createUnoWrapperInstance()
+{
+ if( m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL)
+ {
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+ }
+ else if( m_nUnoWrapperClass == UNO_OBJECT_WRAPPER_REMOTE_OPT)
+ {
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+ }
+ else
+ return Reference<XInterface>();
+}
+Reference<XInterface> IUnknownWrapper::createComWrapperInstance()
+{
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+}
+
+
+void IUnknownWrapper::getMethodInfo(std::u16string_view sName, TypeDescription& methodInfo)
+{
+ TypeDescription desc= getInterfaceMemberDescOfCurrentCall(sName);
+ if( desc.is())
+ {
+ typelib_TypeDescription* pMember= desc.get();
+ if( pMember->eTypeClass == typelib_TypeClass_INTERFACE_METHOD )
+ methodInfo= pMember;
+ }
+}
+
+void IUnknownWrapper::getAttributeInfo(std::u16string_view sName, TypeDescription& attributeInfo)
+{
+ TypeDescription desc= getInterfaceMemberDescOfCurrentCall(sName);
+ if( desc.is())
+ {
+ typelib_TypeDescription* pMember= desc.get();
+ if( pMember->eTypeClass == typelib_TypeClass_INTERFACE_ATTRIBUTE )
+ {
+ attributeInfo= reinterpret_cast<typelib_InterfaceAttributeTypeDescription*>(pMember)->pAttributeTypeRef;
+ }
+ }
+}
+TypeDescription IUnknownWrapper::getInterfaceMemberDescOfCurrentCall(std::u16string_view sName)
+{
+ TypeDescription ret;
+
+ for( auto const & rType : std::as_const(m_seqTypes) )
+ {
+ TypeDescription _curDesc( rType );
+ _curDesc.makeComplete();
+ typelib_InterfaceTypeDescription * pInterface= reinterpret_cast<typelib_InterfaceTypeDescription*>(_curDesc.get());
+ if( pInterface)
+ {
+ typelib_InterfaceMemberTypeDescription* pMember= nullptr;
+ //find the member description of the current call
+ for( int j=0; j < pInterface->nAllMembers; j++)
+ {
+ typelib_TypeDescriptionReference* pTypeRefMember = pInterface->ppAllMembers[j];
+ typelib_TypeDescription* pDescMember= nullptr;
+ TYPELIB_DANGER_GET( &pDescMember, pTypeRefMember);
+
+ typelib_InterfaceMemberTypeDescription* pInterfaceMember=
+ reinterpret_cast<typelib_InterfaceMemberTypeDescription*>(pDescMember);
+ if( OUString( pInterfaceMember->pMemberName) == sName)
+ {
+ pMember= pInterfaceMember;
+ break;
+ }
+ TYPELIB_DANGER_RELEASE( pDescMember);
+ }
+
+ if( pMember)
+ {
+ ret= &pMember->aBase;
+ TYPELIB_DANGER_RELEASE( &pMember->aBase);
+ }
+ }
+ if( ret.is())
+ break;
+ }
+ return ret;
+}
+
+bool IUnknownWrapper::isJScriptObject()
+{
+ if( m_eJScript == JScriptUndefined)
+ {
+ CComDispatchDriver disp( m_spDispatch);
+ if( disp)
+ {
+ CComVariant result;
+ if( SUCCEEDED( disp.GetPropertyByName( JSCRIPT_ID_PROPERTY, &result)))
+ {
+ if(result.vt == VT_BSTR)
+ {
+ CComBSTR name( result.bstrVal);
+ name.ToLower();
+ if( name == CComBSTR(JSCRIPT_ID))
+ m_eJScript= IsJScript;
+ }
+ }
+ }
+ if( m_eJScript == JScriptUndefined)
+ m_eJScript= NoJScript;
+ }
+
+ return m_eJScript != NoJScript;
+}
+
+
+/** @internal
+ The function ultimately calls IDispatch::Invoke on the wrapped COM object.
+ The COM object does not implement UNO Interfaces ( via IDispatch). This
+ is the case when the OleObjectFactory service has been used to create a
+ component.
+ @exception IllegalArgumentException
+ @exception CannotConvertException
+ @InvocationTargetException
+ @RuntimeException
+ @BridgeRuntimeError
+*/
+Any IUnknownWrapper::invokeWithDispIdComTlb(const OUString& sFuncName,
+ const Sequence< Any >& Params,
+ Sequence< sal_Int16 >& OutParamIndex,
+ Sequence< Any >& OutParam)
+{
+ // Get type info for the call. It can be a method call or property put or
+ // property get operation.
+ FuncDesc aFuncDesc(getTypeInfo());
+ getFuncDescForInvoke(sFuncName, Params, & aFuncDesc);
+ return invokeWithDispIdComTlb( aFuncDesc, sFuncName, Params, OutParamIndex, OutParam );
+}
+
+Any IUnknownWrapper::invokeWithDispIdComTlb(FuncDesc& aFuncDesc,
+ const OUString& sFuncName,
+ const Sequence< Any >& Params,
+ Sequence< sal_Int16 >& OutParamIndex,
+ Sequence< Any >& OutParam)
+{
+ Any ret;
+ HRESULT result;
+
+ DISPPARAMS dispparams = {nullptr, nullptr, 0, 0};
+ CComVariant varResult;
+ ExcepInfo excepinfo;
+ unsigned int uArgErr;
+ sal_Int32 i = 0;
+ sal_Int32 nUnoArgs = Params.getLength();
+ DISPID idPropertyPut = DISPID_PROPERTYPUT;
+ std::unique_ptr<DISPID[]> arDispidNamedArgs;
+ std::unique_ptr<CComVariant[]> ptrArgs;
+ std::unique_ptr<CComVariant[]> ptrRefArgs; // referenced arguments
+ CComVariant * arArgs = nullptr;
+ CComVariant * arRefArgs = nullptr;
+ sal_Int32 revIndex = 0;
+
+ //Set the array of DISPIDs for named args if it is a property put operation.
+ //If there are other named arguments another array is set later on.
+ if (aFuncDesc->invkind == INVOKE_PROPERTYPUT
+ || aFuncDesc->invkind == INVOKE_PROPERTYPUTREF)
+ dispparams.rgdispidNamedArgs = & idPropertyPut;
+
+ //Determine the number of named arguments
+ for (int iParam = 0; iParam < nUnoArgs; iParam ++)
+ {
+ const Any & curArg = Params[iParam];
+ if (curArg.getValueType() == cppu::UnoType<NamedArgument>::get())
+ dispparams.cNamedArgs ++;
+ }
+ //In a property put operation a property value is a named argument (DISPID_PROPERTYPUT).
+ //Therefore the number of named arguments is increased by one.
+ //Although named, the argument is not named in an actual language, such as Basic,
+ //therefore it is never a com.sun.star.bridge.oleautomation.NamedArgument
+ if (aFuncDesc->invkind == DISPATCH_PROPERTYPUT
+ || aFuncDesc->invkind == DISPATCH_PROPERTYPUTREF)
+ dispparams.cNamedArgs ++;
+
+ //Determine the number of all arguments and named arguments
+ if (aFuncDesc->cParamsOpt == -1)
+ {
+ //Attribute vararg is set on this method. "Unlimited" number of args
+ //supported. There can be no optional or defaultvalue on any of the arguments.
+ dispparams.cArgs = nUnoArgs;
+ }
+ else
+ {
+ //If there are named arguments, then the dispparams.cArgs
+ //is the number of supplied args, otherwise it is the expected number.
+ if (dispparams.cNamedArgs)
+ dispparams.cArgs = nUnoArgs;
+ else
+ dispparams.cArgs = aFuncDesc->cParams;
+ }
+
+ //check if there are not too many arguments supplied
+ if (::sal::static_int_cast< sal_uInt32, int >( nUnoArgs ) > dispparams.cArgs)
+ {
+ throw IllegalArgumentException(
+ "[automation bridge] There are too many arguments for this method",
+ Reference<XInterface>(), static_cast<sal_Int16>(dispparams.cArgs));
+ }
+
+ //Set up the array of DISPIDs (DISPPARAMS::rgdispidNamedArgs)
+ //for the named arguments.
+ //If there is only one named arg and if it is because of a property put
+ //operation, then we need not set up the DISPID array.
+ if (dispparams.cNamedArgs > 0 &&
+ ! (dispparams.cNamedArgs == 1 &&
+ (aFuncDesc->invkind == INVOKE_PROPERTYPUT ||
+ aFuncDesc->invkind == INVOKE_PROPERTYPUTREF)))
+ {
+ //set up an array containing the member and parameter names
+ //which is then used in ITypeInfo::GetIDsOfNames
+ //First determine the size of the array of names which is passed to
+ //ITypeInfo::GetIDsOfNames. It must hold the method names + the named
+ //args.
+ int nSizeAr = dispparams.cNamedArgs + 1;
+ if (aFuncDesc->invkind == INVOKE_PROPERTYPUT
+ || aFuncDesc->invkind == INVOKE_PROPERTYPUTREF)
+ {
+ nSizeAr = dispparams.cNamedArgs; //counts the DISID_PROPERTYPUT
+ }
+
+ std::unique_ptr<OLECHAR*[]> saNames(new OLECHAR*[nSizeAr]);
+ OLECHAR ** arNames = saNames.get();
+ arNames[0] = const_cast<OLECHAR*>(o3tl::toW(sFuncName.getStr()));
+
+ int cNamedArg = 0;
+ for (size_t iParams = 0; iParams < dispparams.cArgs; iParams ++)
+ {
+ const Any & curArg = Params[iParams];
+ if (auto v = o3tl::tryAccess<NamedArgument>(curArg))
+ {
+ const NamedArgument& arg = *v;
+ //We put the parameter names in reverse order into the array,
+ //so we can use the DISPID array for DISPPARAMS::rgdispidNamedArgs
+ //The first name in the array is the method name
+ arNames[nSizeAr - 1 - cNamedArg++] = const_cast<OLECHAR*>(o3tl::toW(arg.Name.getStr()));
+ }
+ }
+
+ //Prepare the array of DISPIDs for ITypeInfo::GetIDsOfNames
+ //it must be big enough to contain the DISPIDs of the member + parameters
+ arDispidNamedArgs.reset(new DISPID[nSizeAr]);
+ HRESULT hr = getTypeInfo()->GetIDsOfNames(arNames, nSizeAr,
+ arDispidNamedArgs.get());
+ if ( hr == E_NOTIMPL )
+ hr = m_spDispatch->GetIDsOfNames(IID_NULL, arNames, nSizeAr, LOCALE_USER_DEFAULT, arDispidNamedArgs.get() );
+
+ if (hr == S_OK)
+ {
+ // In a "property put" operation, the property value is a named param with the
+ //special DISPID DISPID_PROPERTYPUT
+ if (aFuncDesc->invkind == DISPATCH_PROPERTYPUT
+ || aFuncDesc->invkind == DISPATCH_PROPERTYPUTREF)
+ {
+ //Element at index 0 in the DISPID array must be DISPID_PROPERTYPUT
+ //The first item in the array arDispidNamedArgs is the DISPID for
+ //the method. We replace it with DISPID_PROPERTYPUT.
+ DISPID* arIDs = arDispidNamedArgs.get();
+ arIDs[0] = DISPID_PROPERTYPUT;
+ dispparams.rgdispidNamedArgs = arIDs;
+ }
+ else
+ {
+ //The first item in the array arDispidNamedArgs is the DISPID for
+ //the method. It must be removed
+ DISPID* arIDs = arDispidNamedArgs.get();
+ dispparams.rgdispidNamedArgs = & arIDs[1];
+ }
+ }
+ else if (hr == DISP_E_UNKNOWNNAME)
+ {
+ throw IllegalArgumentException(
+ "[automation bridge]One of the named arguments is wrong!",
+ Reference<XInterface>(), 0);
+ }
+ else
+ {
+ throw InvocationTargetException(
+ "[automation bridge] ITypeInfo::GetIDsOfNames returned error "
+ + OUString::number(static_cast<sal_Int32>(hr), 16), Reference<XInterface>(), Any());
+ }
+ }
+
+ //Convert arguments
+ ptrArgs.reset(new CComVariant[dispparams.cArgs]);
+ ptrRefArgs.reset(new CComVariant[dispparams.cArgs]);
+ arArgs = ptrArgs.get();
+ arRefArgs = ptrRefArgs.get();
+ try
+ {
+ for (i = 0; i < static_cast<sal_Int32>(dispparams.cArgs); i++)
+ {
+ revIndex= dispparams.cArgs - i -1;
+ arRefArgs[revIndex].byref=nullptr;
+ Any anyArg;
+ if ( i < nUnoArgs)
+ anyArg= Params.getConstArray()[i];
+
+ unsigned short paramFlags = PARAMFLAG_FOPT | PARAMFLAG_FIN;
+ VARTYPE varType = VT_VARIANT;
+ if (aFuncDesc->cParamsOpt != -1 || aFuncDesc->cParams != (i + 1))
+ {
+ paramFlags = aFuncDesc->lprgelemdescParam[i].paramdesc.wParamFlags;
+ varType = getElementTypeDesc(&aFuncDesc->lprgelemdescParam[i].tdesc);
+ }
+
+ // Make sure that there is a UNO parameter for every
+ // expected parameter. If there is no UNO parameter where the
+ // called function expects one, then it must be optional. Otherwise
+ // it's a UNO programming error.
+ if (i >= nUnoArgs && !(paramFlags & PARAMFLAG_FOPT))
+ {
+ throw IllegalArgumentException(
+ ("ole automation bridge: The called function expects an argument at position: "
+ + OUString::number(i) + " (index starting at 0)."),
+ Reference<XInterface>(), static_cast<sal_Int16>(i));
+ }
+
+ // Property Put arguments
+ if (anyArg.getValueType() == cppu::UnoType<PropertyPutArgument>::get())
+ {
+ PropertyPutArgument arg;
+ anyArg >>= arg;
+ anyArg = arg.Value;
+ }
+ // named argument
+ if (anyArg.getValueType() == cppu::UnoType<NamedArgument>::get())
+ {
+ NamedArgument aNamedArgument;
+ anyArg >>= aNamedArgument;
+ anyArg = aNamedArgument.Value;
+ }
+ // out param
+ if (paramFlags & PARAMFLAG_FOUT &&
+ ! (paramFlags & PARAMFLAG_FIN) )
+ {
+ VARTYPE type = ::sal::static_int_cast< VARTYPE, int >( varType ^ VT_BYREF );
+ if (i < nUnoArgs)
+ {
+ arRefArgs[revIndex].vt= type;
+ }
+ else
+ {
+ //optional arg
+ arRefArgs[revIndex].vt = VT_ERROR;
+ arRefArgs[revIndex].scode = DISP_E_PARAMNOTFOUND;
+ }
+ if( type == VT_VARIANT )
+ {
+ arArgs[revIndex].vt= VT_VARIANT | VT_BYREF;
+ arArgs[revIndex].byref= &arRefArgs[revIndex];
+ }
+ else
+ {
+ arArgs[revIndex].vt= varType;
+ if (type == VT_DECIMAL)
+ arArgs[revIndex].byref= & arRefArgs[revIndex].decVal;
+ else
+ arArgs[revIndex].byref= & arRefArgs[revIndex].byref;
+ }
+ }
+ // in/out + in byref params
+ else if (varType & VT_BYREF)
+ {
+ VARTYPE type = ::sal::static_int_cast< VARTYPE, int >( varType ^ VT_BYREF );
+ CComVariant var;
+
+ if (i < nUnoArgs && anyArg.getValueTypeClass() != TypeClass_VOID)
+ {
+ anyToVariant( & arRefArgs[revIndex], anyArg, type);
+ }
+ else if (paramFlags & PARAMFLAG_FHASDEFAULT)
+ {
+ //optional arg with default
+ VariantCopy( & arRefArgs[revIndex],
+ & aFuncDesc->lprgelemdescParam[i].paramdesc.
+ pparamdescex->varDefaultValue);
+ }
+ else
+ {
+ //optional arg
+ //e.g: call func(x) in basic : func() ' no arg supplied
+ OSL_ASSERT(paramFlags & PARAMFLAG_FOPT);
+ arRefArgs[revIndex].vt = VT_ERROR;
+ arRefArgs[revIndex].scode = DISP_E_PARAMNOTFOUND;
+ }
+
+ // Set the converted arguments in the array which will be
+ // DISPPARAMS::rgvarg
+ // byref arg VT_XXX |VT_BYREF
+ arArgs[revIndex].vt = varType;
+ if (revIndex == 0 && aFuncDesc->invkind == INVOKE_PROPERTYPUT)
+ {
+ arArgs[revIndex] = arRefArgs[revIndex];
+ }
+ else if (type == VT_DECIMAL)
+ {
+ arArgs[revIndex].byref= & arRefArgs[revIndex].decVal;
+ }
+ else if (type == VT_VARIANT)
+ {
+ if ( ! (paramFlags & PARAMFLAG_FOUT))
+ arArgs[revIndex] = arRefArgs[revIndex];
+ else
+ arArgs[revIndex].byref = & arRefArgs[revIndex];
+ }
+ else
+ {
+ arArgs[revIndex].byref = & arRefArgs[revIndex].byref;
+ arArgs[revIndex].vt = ::sal::static_int_cast< VARTYPE, int >( arRefArgs[revIndex].vt | VT_BYREF );
+ }
+
+ }
+ // in parameter no VT_BYREF except for array, interfaces
+ else
+ { // void any stands for optional param
+ if (i < nUnoArgs && anyArg.getValueTypeClass() != TypeClass_VOID)
+ {
+ anyToVariant( & arArgs[revIndex], anyArg, varType);
+ }
+ //optional arg but no void any supplied
+ //Basic: obj.func() ' first parameter left out because it is optional
+ else if (paramFlags & PARAMFLAG_FHASDEFAULT)
+ {
+ //optional arg with default either as direct arg : VT_XXX or
+ VariantCopy( & arArgs[revIndex],
+ & aFuncDesc->lprgelemdescParam[i].paramdesc.
+ pparamdescex->varDefaultValue);
+ }
+ else if (paramFlags & PARAMFLAG_FOPT)
+ {
+ arArgs[revIndex].vt = VT_ERROR;
+ arArgs[revIndex].scode = DISP_E_PARAMNOTFOUND;
+ }
+ else
+ {
+ arArgs[revIndex].vt = VT_EMPTY;
+ arArgs[revIndex].lVal = 0;
+ }
+ }
+ }
+ }
+ catch (IllegalArgumentException & e)
+ {
+ e.ArgumentPosition = ::sal::static_int_cast< sal_Int16, sal_Int32 >( i );
+ throw;
+ }
+ catch (CannotConvertException & e)
+ {
+ e.ArgumentIndex = i;
+ throw;
+ }
+ dispparams.rgvarg= arArgs;
+ // invoking OLE method
+ result = m_spDispatch->Invoke(aFuncDesc->memid,
+ IID_NULL,
+ LOCALE_USER_DEFAULT,
+ ::sal::static_int_cast< WORD, INVOKEKIND >( aFuncDesc->invkind ),
+ &dispparams,
+ &varResult,
+ &excepinfo,
+ &uArgErr);
+
+ // converting return value and out parameter back to UNO
+ if (result == S_OK)
+ {
+
+ // allocate space for the out param Sequence and indices Sequence
+ int outParamsCount= 0; // includes in/out parameter
+ for (int j = 0; j < aFuncDesc->cParams; j++)
+ {
+ if (aFuncDesc->lprgelemdescParam[j].paramdesc.wParamFlags &
+ PARAMFLAG_FOUT)
+ outParamsCount++;
+ }
+
+ OutParamIndex.realloc(outParamsCount);
+ OutParam.realloc(outParamsCount);
+ // Convert out params
+ if (outParamsCount)
+ {
+ auto pOutParamIndex = OutParamIndex.getArray();
+ auto pOutParam = OutParam.getArray();
+ int outParamIndex=0;
+ for (int paramIndex = 0; paramIndex < nUnoArgs; paramIndex ++)
+ {
+ //Determine the index within the method signature
+ int realParamIndex = paramIndex;
+ int revParamIndex = dispparams.cArgs - paramIndex - 1;
+ if (Params[paramIndex].getValueType()
+ == cppu::UnoType<NamedArgument>::get())
+ {
+ //dispparams.rgdispidNamedArgs contains the mapping from index
+ //of named args list to index of parameter list
+ realParamIndex = dispparams.rgdispidNamedArgs[revParamIndex];
+ }
+
+ // no named arg, always come before named args
+ if (! (aFuncDesc->lprgelemdescParam[realParamIndex].paramdesc.wParamFlags
+ & PARAMFLAG_FOUT))
+ continue;
+ Any outAny;
+ // variantToAny is called with the "reduce range" parameter set to sal_False.
+ // That causes VT_I4 values not to be converted down to a "lower" type. That
+ // feature exist for JScript only because it only uses VT_I4 for integer types.
+ try
+ {
+ variantToAny( & arRefArgs[revParamIndex], outAny, false );
+ }
+ catch (IllegalArgumentException & e)
+ {
+ e.ArgumentPosition = static_cast<sal_Int16>(paramIndex);
+ throw;
+ }
+ catch (CannotConvertException & e)
+ {
+ e.ArgumentIndex = paramIndex;
+ throw;
+ }
+ pOutParam[outParamIndex] = outAny;
+ pOutParamIndex[outParamIndex] = ::sal::static_int_cast< sal_Int16, int >( paramIndex );
+ outParamIndex++;
+ }
+ OutParam.realloc(outParamIndex);
+ OutParamIndex.realloc(outParamIndex);
+ }
+ // Return value
+ variantToAny(&varResult, ret, false);
+ }
+
+ // map error codes to exceptions
+ OUString message;
+ switch (result)
+ {
+ case S_OK:
+ break;
+ case DISP_E_BADPARAMCOUNT:
+ throw IllegalArgumentException("[automation bridge] Wrong "
+ "number of arguments. Object returned DISP_E_BADPARAMCOUNT.",
+ nullptr, 0);
+ break;
+ case DISP_E_BADVARTYPE:
+ throw RuntimeException("[automation bridge] One or more "
+ "arguments have the wrong type. Object returned "
+ "DISP_E_BADVARTYPE.", nullptr);
+ break;
+ case DISP_E_EXCEPTION:
+ message = OUString::Concat("[automation bridge]: ")
+ + std::u16string_view(o3tl::toU(excepinfo.bstrDescription),
+ ::SysStringLen(excepinfo.bstrDescription));
+
+ throw InvocationTargetException(message, Reference<XInterface>(), Any());
+ break;
+ case DISP_E_MEMBERNOTFOUND:
+ message = "[automation bridge]: A function with the name \""
+ + sFuncName + "\" is not supported. Object returned "
+ "DISP_E_MEMBERNOTFOUND.";
+ throw IllegalArgumentException(message, nullptr, 0);
+ break;
+ case DISP_E_NONAMEDARGS:
+ throw IllegalArgumentException("[automation bridge] Object "
+ "returned DISP_E_NONAMEDARGS",nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr ));
+ break;
+ case DISP_E_OVERFLOW:
+ throw CannotConvertException("[automation bridge] Call failed.",
+ static_cast<XInterface*>(
+ static_cast<XWeak*>(this)), TypeClass_UNKNOWN, FailReason::OUT_OF_RANGE, uArgErr);
+ break;
+ case DISP_E_PARAMNOTFOUND:
+ throw IllegalArgumentException("[automation bridge]Call failed."
+ "Object returned DISP_E_PARAMNOTFOUND.",
+ nullptr, ::sal::static_int_cast< sal_Int16, unsigned int >( uArgErr ));
+ break;
+ case DISP_E_TYPEMISMATCH:
+ throw CannotConvertException("[automation bridge] Call failed. "
+ "Object returned DISP_E_TYPEMISMATCH",
+ static_cast<XInterface*>(
+ static_cast<XWeak*>(this)) , TypeClass_UNKNOWN, FailReason::UNKNOWN, uArgErr);
+ break;
+ case DISP_E_UNKNOWNINTERFACE:
+ throw RuntimeException("[automation bridge] Call failed. "
+ "Object returned DISP_E_UNKNOWNINTERFACE.",nullptr);
+ break;
+ case DISP_E_UNKNOWNLCID:
+ throw RuntimeException("[automation bridge] Call failed. "
+ "Object returned DISP_E_UNKNOWNLCID.",nullptr);
+ break;
+ case DISP_E_PARAMNOTOPTIONAL:
+ throw CannotConvertException("[automation bridge] Call failed."
+ "Object returned DISP_E_PARAMNOTOPTIONAL",
+ static_cast<XInterface*>(static_cast<XWeak*>(this)),
+ TypeClass_UNKNOWN, FailReason::NO_DEFAULT_AVAILABLE, uArgErr);
+ break;
+ default:
+ throw RuntimeException();
+ break;
+ }
+
+ return ret;
+}
+
+void IUnknownWrapper::getFuncDescForInvoke(const OUString & sFuncName,
+ const Sequence<Any> & seqArgs,
+ FUNCDESC** pFuncDesc)
+{
+ int nUnoArgs = seqArgs.getLength();
+ const Any * arArgs = seqArgs.getConstArray();
+ ITypeInfo* pInfo = getTypeInfo();
+
+ //If the last of the positional arguments is a PropertyPutArgument
+ //then obtain the type info for the property put operation.
+
+ //The property value is always the last argument, in a positional argument list
+ //or in a list of named arguments. A PropertyPutArgument is actually a named argument
+ //hence it must not be put in an extra NamedArgument structure
+ if (nUnoArgs > 0 &&
+ arArgs[nUnoArgs - 1].getValueType() == cppu::UnoType<PropertyPutArgument>::get())
+ {
+ // DISPATCH_PROPERTYPUT
+ FuncDesc aDescGet(pInfo);
+ FuncDesc aDescPut(pInfo);
+ VarDesc aVarDesc(pInfo);
+ getPropDesc(sFuncName, & aDescGet, & aDescPut, & aVarDesc);
+ if ( ! aDescPut)
+ {
+ throw IllegalArgumentException(
+ "[automation bridge] The object does not have a writeable property: "
+ + sFuncName, Reference<XInterface>(), 0);
+ }
+ *pFuncDesc = aDescPut.Detach();
+ }
+ else
+ { // DISPATCH_METHOD
+ FuncDesc aFuncDesc(pInfo);
+ getFuncDesc(sFuncName, & aFuncDesc);
+ if ( ! aFuncDesc)
+ {
+ // Fallback: DISPATCH_PROPERTYGET can mostly be called as
+ // DISPATCH_METHOD
+ ITypeInfo * pTypeInfo = getTypeInfo();
+ FuncDesc aDescPut(pTypeInfo);
+ VarDesc aVarDesc(pTypeInfo);
+ getPropDesc(sFuncName, & aFuncDesc, & aDescPut, & aVarDesc);
+ if ( ! aFuncDesc )
+ {
+ throw IllegalArgumentException(
+ "[automation bridge] The object does not have a function"
+ " or readable property \""
+ + sFuncName + "\"", Reference<XInterface>(), 0);
+ }
+ }
+ *pFuncDesc = aFuncDesc.Detach();
+ }
+}
+bool IUnknownWrapper::getDispid(const OUString& sFuncName, DISPID * id)
+{
+ OSL_ASSERT(m_spDispatch);
+ LPOLESTR lpsz = const_cast<LPOLESTR> (o3tl::toW(sFuncName.getStr()));
+ HRESULT hr = m_spDispatch->GetIDsOfNames(IID_NULL, &lpsz, 1, LOCALE_USER_DEFAULT, id);
+ return hr == S_OK;
+}
+void IUnknownWrapper::getFuncDesc(const OUString & sFuncName, FUNCDESC ** pFuncDesc)
+
+{
+ OSL_ASSERT( * pFuncDesc == nullptr);
+ buildComTlbIndex();
+ typedef TLBFuncIndexMap::const_iterator cit;
+ //We assume there is only one entry with the function name. A property
+ //would have two entries.
+ cit itIndex= m_mapComFunc.find(sFuncName);
+ if (itIndex == m_mapComFunc.end())
+ {
+ //try case insensitive with IDispatch::GetIDsOfNames
+ DISPID id;
+ if (getDispid(sFuncName, &id))
+ {
+ CComBSTR memberName;
+ unsigned int pcNames=0;
+ // get the case sensitive name
+ if( SUCCEEDED(getTypeInfo()->GetNames( id, & memberName, 1, &pcNames)))
+ {
+ //get the associated index and add an entry to the map
+ //with the name sFuncName which differs in the casing of the letters to
+ //the actual name as obtained from ITypeInfo
+ OUString sRealName(o3tl::toU(LPCOLESTR(memberName)));
+ cit itOrg = m_mapComFunc.find(sRealName);
+ OSL_ASSERT(itOrg != m_mapComFunc.end());
+ // maybe this is a property, if so we need
+ // to store either both id's ( put/get ) or
+ // just the get. Storing both is more consistent
+ std::pair<cit, cit> pItems = m_mapComFunc.equal_range( sRealName );
+ for ( ;pItems.first != pItems.second; ++pItems.first )
+ m_mapComFunc.insert( TLBFuncIndexMap::value_type ( std::make_pair(sFuncName, pItems.first->second ) ));
+ itIndex =
+ m_mapComFunc.find( sFuncName );
+ }
+ }
+ }
+
+#if OSL_DEBUG_LEVEL >= 1
+ // There must only be one entry if sFuncName represents a function or two
+ // if it is a property
+ std::pair<cit, cit> p = m_mapComFunc.equal_range(sFuncName.toAsciiLowerCase());
+ int numEntries = 0;
+ for ( ;p.first != p.second; p.first ++, numEntries ++);
+ OSL_ASSERT( ! (numEntries > 3) );
+#endif
+ if( itIndex != m_mapComFunc.end())
+ {
+ ITypeInfo* pType= getTypeInfo();
+ FUNCDESC * pDesc = nullptr;
+ if (!SUCCEEDED(pType->GetFuncDesc(itIndex->second, & pDesc)))
+ {
+ throw BridgeRuntimeError("[automation bridge] Could not get "
+ "FUNCDESC for " + sFuncName);
+ }
+ if (pDesc->invkind == INVOKE_FUNC)
+ {
+ (*pFuncDesc) = pDesc;
+ }
+ else
+ {
+ pType->ReleaseFuncDesc(pDesc);
+ }
+ }
+ //else no entry found for sFuncName, pFuncDesc will not be filled in
+}
+
+void IUnknownWrapper::getPropDesc(const OUString & sFuncName, FUNCDESC ** pFuncDescGet,
+ FUNCDESC** pFuncDescPut, VARDESC** pVarDesc)
+{
+ OSL_ASSERT( * pFuncDescGet == nullptr && * pFuncDescPut == nullptr);
+ buildComTlbIndex();
+ typedef TLBFuncIndexMap::const_iterator cit;
+ std::pair<cit, cit> p = m_mapComFunc.equal_range(sFuncName);
+ if (p.first == m_mapComFunc.end())
+ {
+ //try case insensitive with IDispatch::GetIDsOfNames
+ DISPID id;
+ if (getDispid(sFuncName, &id))
+ {
+ CComBSTR memberName;
+ unsigned int pcNames=0;
+ // get the case sensitive name
+ if( SUCCEEDED(getTypeInfo()->GetNames( id, & memberName, 1, &pcNames)))
+ {
+ //As opposed to getFuncDesc, we do not add the value because we would
+ // need to find the get and set description for the property. This would
+ //mean to iterate over all FUNCDESCs again.
+ p = m_mapComFunc.equal_range(OUString(o3tl::toU(LPCOLESTR(memberName))));
+ }
+ }
+ }
+
+ for ( int i = 0 ;p.first != p.second; p.first ++, i ++)
+ {
+ // There are a maximum of two entries, property put and property get
+ OSL_ASSERT( ! (i > 2) );
+ ITypeInfo* pType= getTypeInfo();
+ FUNCDESC * pFuncDesc = nullptr;
+ if (SUCCEEDED( pType->GetFuncDesc(p.first->second, & pFuncDesc)))
+ {
+ if (pFuncDesc->invkind == INVOKE_PROPERTYGET)
+ {
+ (*pFuncDescGet) = pFuncDesc;
+ }
+ else if (pFuncDesc->invkind == INVOKE_PROPERTYPUT ||
+ pFuncDesc->invkind == INVOKE_PROPERTYPUTREF)
+ {
+ //a property can have 3 entries, put, put ref, get
+ // If INVOKE_PROPERTYPUTREF or INVOKE_PROPERTYPUT is used
+ //depends on what is found first.
+ if ( * pFuncDescPut)
+ {
+ //we already have found one
+ pType->ReleaseFuncDesc(pFuncDesc);
+ }
+ else
+ {
+ (*pFuncDescPut) = pFuncDesc;
+ }
+ }
+ else
+ {
+ pType->ReleaseFuncDesc(pFuncDesc);
+ }
+ }
+ //ITypeInfo::GetFuncDesc may even provide a funcdesc for a VARDESC
+ // with invkind = INVOKE_FUNC. Since this function should only return
+ //a value for a real property (XInvokation::hasMethod, ..::hasProperty
+ //we need to make sure that sFuncName represents a real property.
+ VARDESC * pVD = nullptr;
+ if (SUCCEEDED(pType->GetVarDesc(p.first->second, & pVD)))
+ (*pVarDesc) = pVD;
+ }
+ //else no entry for sFuncName, pFuncDesc will not be filled in
+}
+
+VARTYPE IUnknownWrapper::getUserDefinedElementType( ITypeInfo* pTypeInfo, const DWORD nHrefType )
+{
+ VARTYPE _type( VT_NULL );
+ if ( pTypeInfo )
+ {
+ CComPtr<ITypeInfo> spRefInfo;
+ pTypeInfo->GetRefTypeInfo( nHrefType, &spRefInfo.p );
+ if ( spRefInfo )
+ {
+ TypeAttr attr( spRefInfo );
+ spRefInfo->GetTypeAttr( &attr );
+ if ( attr->typekind == TKIND_ENUM )
+ {
+ // We use the type of the first enum value.
+ if ( attr->cVars == 0 )
+ {
+ throw BridgeRuntimeError("[automation bridge] Could not obtain type description");
+ }
+ VarDesc var( spRefInfo );
+ spRefInfo->GetVarDesc( 0, &var );
+ _type = var->lpvarValue->vt;
+ }
+ else if ( attr->typekind == TKIND_INTERFACE )
+ {
+ _type = VT_UNKNOWN;
+ }
+ else if ( attr->typekind == TKIND_DISPATCH )
+ {
+ _type = VT_DISPATCH;
+ }
+ else if ( attr->typekind == TKIND_ALIAS )
+ {
+ // TKIND_ALIAS is a type that is an alias for another type. So get that alias type.
+ _type = getUserDefinedElementType( pTypeInfo, attr->tdescAlias.hreftype );
+ }
+ else
+ {
+ throw BridgeRuntimeError( "[automation bridge] Unhandled user defined type." );
+ }
+ }
+ }
+ return _type;
+}
+
+VARTYPE IUnknownWrapper::getElementTypeDesc(const TYPEDESC *desc)
+{
+ VARTYPE _type( VT_NULL );
+
+ if (desc->vt == VT_PTR)
+ {
+ _type = getElementTypeDesc(desc->lptdesc);
+ _type |= VT_BYREF;
+ }
+ else if (desc->vt == VT_SAFEARRAY)
+ {
+ _type = getElementTypeDesc(desc->lptdesc);
+ _type |= VT_ARRAY;
+ }
+ else if (desc->vt == VT_USERDEFINED)
+ {
+ ITypeInfo* thisInfo = getTypeInfo(); //kept by this instance
+ _type = getUserDefinedElementType( thisInfo, desc->hreftype );
+ }
+ else
+ {
+ _type = desc->vt;
+ }
+ return _type;
+}
+
+void IUnknownWrapper::buildComTlbIndex()
+{
+ if ( ! m_bComTlbIndexInit)
+ {
+ MutexGuard guard(getBridgeMutex());
+ {
+ if ( ! m_bComTlbIndexInit)
+ {
+ OUString sError;
+ ITypeInfo* pType= getTypeInfo();
+ TypeAttr typeAttr(pType);
+ if( SUCCEEDED( pType->GetTypeAttr( &typeAttr)))
+ {
+ for( WORD i= 0; i < typeAttr->cFuncs; i++)
+ {
+ FuncDesc funcDesc(pType);
+ if( SUCCEEDED( pType->GetFuncDesc( i, &funcDesc)))
+ {
+ CComBSTR memberName;
+ unsigned int pcNames=0;
+ if( SUCCEEDED(pType->GetNames( funcDesc->memid, & memberName, 1, &pcNames)))
+ {
+ OUString usName(o3tl::toU(LPCOLESTR(memberName)));
+ m_mapComFunc.emplace(usName, i);
+ }
+ else
+ {
+ sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, "
+ "ITypeInfo::GetNames failed.";
+ }
+ }
+ else
+ sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, "
+ "ITypeInfo::GetFuncDesc failed.";
+ }
+
+ //If we create an Object in JScript and a property then it
+ //has VARDESC instead of FUNCDESC
+ for (WORD i = 0; i < typeAttr->cVars; i++)
+ {
+ VarDesc varDesc(pType);
+ if (SUCCEEDED(pType->GetVarDesc(i, & varDesc)))
+ {
+ CComBSTR memberName;
+ unsigned int pcNames = 0;
+ if (SUCCEEDED(pType->GetNames(varDesc->memid, & memberName, 1, &pcNames)))
+ {
+ if (varDesc->varkind == VAR_DISPATCH)
+ {
+ OUString usName(o3tl::toU(LPCOLESTR(memberName)));
+ m_mapComFunc.emplace(usName, i);
+ }
+ }
+ else
+ {
+ sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, "
+ "ITypeInfo::GetNames failed.";
+ }
+ }
+ else
+ sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, "
+ "ITypeInfo::GetVarDesc failed.";
+
+ }
+ }
+ else
+ sError = "[automation bridge] IUnknownWrapper::buildComTlbIndex, "
+ "ITypeInfo::GetTypeAttr failed.";
+
+ if (sError.getLength())
+ {
+ throw BridgeRuntimeError(sError);
+ }
+
+ m_bComTlbIndexInit = true;
+ }
+ }
+ }
+}
+
+ITypeInfo* IUnknownWrapper::getTypeInfo()
+{
+ if( !m_spDispatch)
+ {
+ throw BridgeRuntimeError("The object has no IDispatch interface!");
+ }
+
+ if( !m_spTypeInfo )
+ {
+ MutexGuard guard(getBridgeMutex());
+ if( ! m_spTypeInfo)
+ {
+ CComPtr< ITypeInfo > spType;
+ if( !SUCCEEDED( m_spDispatch->GetTypeInfo( 0, LOCALE_USER_DEFAULT, &spType.p)))
+ {
+ throw BridgeRuntimeError("[automation bridge]The dispatch object does not "
+ "support ITypeInfo!");
+ }
+
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+
+ //If this is a dual interface then TYPEATTR::typekind is usually TKIND_INTERFACE
+ //We need to get the type description for TKIND_DISPATCH
+ TypeAttr typeAttr(spType.p);
+ if( SUCCEEDED(spType->GetTypeAttr( &typeAttr)))
+ {
+ if (typeAttr->typekind == TKIND_INTERFACE &&
+ typeAttr->wTypeFlags & TYPEFLAG_FDUAL)
+ {
+ HREFTYPE refDispatch;
+ if (!SUCCEEDED(spType->GetRefTypeOfImplType(::sal::static_int_cast< UINT, int >( -1 ), &refDispatch)))
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge] Could not obtain type information "
+ "for dispatch interface." );
+ }
+ CComPtr<ITypeInfo> spTypeDisp;
+ if (SUCCEEDED(spType->GetRefTypeInfo(refDispatch, & spTypeDisp)))
+ m_spTypeInfo= spTypeDisp;
+ }
+ else if (typeAttr->typekind == TKIND_DISPATCH)
+ {
+ m_spTypeInfo= spType;
+ }
+ else
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge] Automation object does not "
+ "provide type information.");
+ }
+ }
+ }
+ }
+ return m_spTypeInfo;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/oleobjw.hxx b/extensions/source/ole/oleobjw.hxx
new file mode 100644
index 000000000..d1a1d0ed8
--- /dev/null
+++ b/extensions/source/ole/oleobjw.hxx
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "ole2uno.hxx"
+#include "wincrap.hxx"
+
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/bridge/oleautomation/XAutomationObject.hpp>
+#include <com/sun/star/script/XAutomationInvocation.hpp>
+#include <rtl/ustring.hxx>
+
+#include <com/sun/star/script/XDefaultProperty.hpp>
+#include <com/sun/star/script/XDefaultMethod.hpp>
+#include <com/sun/star/script/XDirectInvocation.hpp>
+
+#include <typelib/typedescription.hxx>
+#include "unoconversionutilities.hxx"
+#include "windata.hxx"
+using namespace cppu;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::bridge;
+using namespace com::sun::star::bridge::oleautomation;
+
+typedef std::unordered_map<OUString, std::pair<DISPID, unsigned short>> DispIdMap;
+
+typedef std::unordered_multimap<OUString, unsigned int> TLBFuncIndexMap;
+
+// This class wraps an IDispatch and maps XInvocation calls to IDispatch calls on the wrapped object.
+// If m_TypeDescription is set then this class represents a UNO interface implemented in a COM component.
+// The interface is not a real interface in terms of an abstract class but is realized through IDispatch.
+class IUnknownWrapper : public WeakImplHelper< XBridgeSupplier2, XInitialization, XAutomationObject, XDefaultProperty, XDefaultMethod, XDirectInvocation, XAutomationInvocation >,
+
+ public UnoConversionUtilities<IUnknownWrapper>
+
+{
+public:
+ IUnknownWrapper(Reference<XMultiServiceFactory> const &xFactory,
+ sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass);
+
+ ~IUnknownWrapper() override;
+
+ //XInterface
+ Any SAL_CALL queryInterface(const Type& t) override;
+
+ // XInvokation
+ virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection( ) override;
+ virtual Any SAL_CALL invoke( const OUString& aFunctionName,
+ const Sequence< Any >& aParams,
+ Sequence< sal_Int16 >& aOutParamIndex,
+ Sequence< Any >& aOutParam ) override;
+ virtual void SAL_CALL setValue( const OUString& aPropertyName,
+ const Any& aValue ) override;
+ virtual Any SAL_CALL getValue( const OUString& aPropertyName ) override;
+ virtual sal_Bool SAL_CALL hasMethod( const OUString& aName ) override;
+ virtual sal_Bool SAL_CALL hasProperty( const OUString& aName ) override;
+
+ // XBridgeSupplier2
+ // This interface is implemented to provide a safe way to obtain the original
+ // IUnknown or IDispatch within the function anyToVariant. The function asks
+ // every UNO object for its XBridgeSupplier2 and if it is available uses it to convert
+ // the object with its own supplier.
+ virtual Any SAL_CALL createBridge( const Any& modelDepObject,
+ const Sequence< sal_Int8 >& aProcessId,
+ sal_Int16 sourceModelType,
+ sal_Int16 destModelType ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+
+ // XDefaultProperty
+ virtual OUString SAL_CALL getDefaultPropertyName( ) override { return m_sDefaultMember; }
+
+ // XDefaultMethod
+ virtual OUString SAL_CALL getDefaultMethodName( ) override { return m_sDefaultMember; }
+
+ virtual css::uno::Any SAL_CALL invokeGetProperty( const OUString& aFunctionName, const css::uno::Sequence< css::uno::Any >& aParams, css::uno::Sequence< ::sal_Int16 >& aOutParamIndex, css::uno::Sequence< css::uno::Any >& aOutParam ) override;
+ virtual css::uno::Any SAL_CALL invokePutProperty( const OUString& aFunctionName, const css::uno::Sequence< css::uno::Any >& aParams, css::uno::Sequence< ::sal_Int16 >& aOutParamIndex, css::uno::Sequence< css::uno::Any >& aOutParam ) override;
+
+ // XDirectInvocation
+ virtual css::uno::Any SAL_CALL directInvoke( const OUString& aName, const css::uno::Sequence< css::uno::Any >& aParams ) override;
+ virtual sal_Bool SAL_CALL hasMember( const OUString& aName ) override;
+
+
+ Any invokeWithDispIdComTlb(FuncDesc& aFuncDesc,
+ const OUString& sFuncName,
+ const Sequence< Any >& Params,
+ Sequence< sal_Int16 >& OutParamIndex,
+ Sequence< Any >& OutParam);
+
+
+protected:
+
+ virtual Any invokeWithDispIdUnoTlb(const OUString& sFunctionName,
+ const Sequence< Any >& Params,
+ Sequence<sal_Int16 >& OutParamIndex,
+ Sequence< Any >& OutParam);
+ // Is used for OleObjectFactory service
+ virtual Any invokeWithDispIdComTlb(const OUString& sFuncName,
+ const Sequence< Any >& Params,
+ Sequence< sal_Int16 >& OutParamIndex,
+ Sequence< Any >& OutParam);
+
+ // UnoConversionUtilities -------------------------------------------------------------------------------
+ virtual Reference<XInterface> createUnoWrapperInstance() override;
+ virtual Reference<XInterface> createComWrapperInstance() override;
+
+ /**Obtains a FUNCDESC structure for a function.
+ Fills the FUNCDESC structure if ITypeInfo provides information for
+ the function of name sFuncName or pFuncDesc will not be filled in.
+ May throw a BridgeRuntimeError.
+ */
+ void getFuncDesc(const OUString & sFuncName, FUNCDESC ** pFuncDesc);
+ /**Obtains a FUNCDESC structures or a VARDESC structure
+ for a property. pFuncDescPut may also contain
+ a structure for a "propertyputref" operation. If pFuncDesc contains a
+ "put ref" or "put" FUNCDESC depends on what was found first in the type
+ description.
+ Fills the FUNCDESC structure if ITypeInfo provides information for
+ the respective property functions or the structures will not be filled in.
+ May throw a BridgeRuntimeError.
+ */
+ void getPropDesc(const OUString & sFuncName, FUNCDESC ** pFuncDescGet,
+ FUNCDESC** pFuncDescPut, VARDESC ** pVarDesc);
+ // These functions are for the case if an object of this class wraps an IDispatch
+ // object that implements UNO interfaces. In that case the member m_seqTypes
+ // is set through XInitialization::initialize.
+ void getMethodInfo(std::u16string_view sName, TypeDescription& methodDescription);
+ // After return attributInfo contains typelib_InterfaceAttributeTypeDescription::pAttributeTypeRef
+ void getAttributeInfo(std::u16string_view sName, TypeDescription& attributeInfo);
+ // used by get MethodInfo
+ TypeDescription getInterfaceMemberDescOfCurrentCall(std::u16string_view sName);
+ /** Returns always a valid ITypeInfo interface or throws a BridgeRuntimeError.
+ The returned interface does not need to be AddRef'ed as long as it is locally
+ used. The interface is kept in the instance of this class.
+ */
+ ITypeInfo* getTypeInfo();
+
+ /** Returns the DISPID for a function or property name. If true is returned then
+ id contains a valid DISPID.
+ */
+
+ bool getDispid(const OUString& sFuncName, DISPID * id);
+
+ VARTYPE getUserDefinedElementType( ITypeInfo* pTypeInfo, const DWORD nHrefType );
+
+ /** Gets the element type in a VARIANT like style. E.g. if desc->lptdesc contains
+ a VT_PTR than it is replaced by VT_BYREF and VT_SAFEARRAY is replaced by VT_ARRAY
+ If the TYPEDESC describes an SAFEARRAY then varType is a combination of VT_ARRAY
+ and the element type.
+ The argument desc must be obtained from FUNCDESC::lprgelemdescParam[i].tdesc where
+ FUNCDESC was obtained from the ITypeInfo belonging to wrapped IDispatch.
+ */
+ VARTYPE getElementTypeDesc( const TYPEDESC *desc);
+ /** Iterates over all functions and put the names and indices into the map
+ m_mapComFunc of type TLBFuncIndexMap.
+ Call the function every time before accessing the map.
+ Throws a BridgeRuntimeError on failure.
+ */
+ void buildComTlbIndex();
+
+ /** Returns a FUNCDESC structure which contains type information about the
+ current XInvocation::invoke call. The FUNCDESC either describes a method,
+ a property put or a property get operation.
+ It uses the types com.sun.star.bridge.oleautomation.PropertyPutArgument
+ which can be
+ contained in the sequence of in-arguments of invoke to determine if the call is
+ a property put or property get operation.
+ If no adequate FUNCDESC was found, an IllegalArgumentException is thrown.
+ Therefore it is safe to assume that the returned FUNCDESC* is not NULL.
+
+ @exception IllegalArgumentException
+ Thrown if no adequate FUNCDESC could be found.
+ */
+ void getFuncDescForInvoke(const OUString & sFuncName,
+ const Sequence<Any> & seqArgs, FUNCDESC** pFuncDesc);
+
+ // Finds out whether the wrapped IDispatch is a JScript Object. This is
+ // done by
+ // asking for the property "_environment". If it has the value "JScript"
+ // (case insensitive) then the IDispatch is considered a JScript object.
+ bool isJScriptObject();
+
+
+ // If UNO interfaces are implemented in JScript objects, VB or C++ COM objects
+ // and those are passed as parameter to a UNO interface function, then
+ // the IDispatch* are wrapped by objects of this class. Assuming that the functions
+ // implemented by the IDispatch object returns another UNO interface then
+ // it has to be wrapped to this type. But this is only possible if an object of this
+ // wrapper class knows what type it is represting. The member m_TypeDescription holds this
+ // information.
+ // m_TypeDescription is only useful when an object wraps an IDispatch object that implements
+ // a UNO interface. The value is set during a call to XInitialization::initialize.
+ Sequence<Type> m_seqTypes;
+ CComPtr<IUnknown> m_spUnknown;
+ CComPtr<IDispatch> m_spDispatch;
+ OUString m_sTypeName; // is "" ( not initialised ), "IDispatch" ( we have no idea ) or "SomeLibrary.SomeTypeName" if we managed to get a type
+ /** This value is set during XInitialization::initialize. It indicates that the COM interface
+ was transported as VT_DISPATCH in a VARIANT rather than a VT_UNKNOWN
+ */
+ bool m_bOriginalDispatch;
+ DispIdMap m_dispIdMap;
+ Reference<XIdlClass>* m_pxIdlClass;
+
+
+ // used by isJScriptObject
+ enum JScriptDetermination{ JScriptUndefined=0, NoJScript, IsJScript};
+ JScriptDetermination m_eJScript;
+ // The map is filled by buildComTlbIndex
+ // It maps Uno Function names to an index which is used in ITypeInfo::GetFuncDesc
+ TLBFuncIndexMap m_mapComFunc;
+ // used for synchronizing the computation of the content for m_mapComFunc
+ bool m_bComTlbIndexInit;
+ // Keeps the ITypeInfo obtained from IDispatch::GetTypeInfo
+ CComPtr< ITypeInfo > m_spTypeInfo;
+ OUString m_sDefaultMember;
+ bool m_bHasDfltMethod;
+ bool m_bHasDfltProperty;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/olethread.cxx b/extensions/source/ole/olethread.cxx
new file mode 100644
index 000000000..1580c0b7d
--- /dev/null
+++ b/extensions/source/ole/olethread.cxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ole2uno.hxx"
+
+#include <comphelper/windowserrorstring.hxx>
+#include <osl/thread.hxx>
+#include <sal/log.hxx>
+
+void o2u_attachCurrentThread()
+{
+ static osl::ThreadData oleThreadData;
+
+ if (!bool(reinterpret_cast<sal_IntPtr>(oleThreadData.getData())))
+ {
+ HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+ if (!SUCCEEDED(hr))
+ { // FIXME: is it a problem that this ends up in STA currently?
+ assert(RPC_E_CHANGED_MODE == hr);
+ // Let's find out explicitly what apartment mode we are in.
+ SAL_WARN("extensions.olebridge", "CoInitializeEx failed"
+ << (hr == RPC_E_CHANGED_MODE ? " (expectedly)" : "")
+ << ": " << WindowsErrorStringFromHRESULT(hr));
+ APTTYPE nAptType;
+ APTTYPEQUALIFIER nAptTypeQualifier;
+ if (SUCCEEDED(CoGetApartmentType(&nAptType, &nAptTypeQualifier)))
+ {
+ SAL_WARN("extensions.olebridge",
+ " Thread is in a "
+ << (nAptType == APTTYPE_STA ? OUString("single-threaded") :
+ (nAptType == APTTYPE_MTA ? OUString("multi-threaded") :
+ (nAptType == APTTYPE_NA ? OUString("neutral") :
+ (nAptType == APTTYPE_MAINSTA ? OUString("main single-threaded") :
+ ("unknown (") + OUString::number(nAptType) + ")"))))
+ << " apartment"
+ << (nAptTypeQualifier == APTTYPEQUALIFIER_NONE ? OUString() :
+ (nAptTypeQualifier == APTTYPEQUALIFIER_IMPLICIT_MTA ? OUString(" (implicit)") :
+ (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_MTA ? OUString(" (on MTA)") :
+ (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_STA ? OUString(" (on STA)") :
+ (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA ? OUString(" (on implicit MTA)") :
+ (nAptTypeQualifier == APTTYPEQUALIFIER_NA_ON_MAINSTA ? OUString(" (on main STA)") :
+ (" (with unknown qualifier (" + OUString::number(nAptTypeQualifier) + "))")))))))
+ << ".");
+ }
+ }
+ oleThreadData.setData(reinterpret_cast<void*>(true));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/servprov.cxx b/extensions/source/ole/servprov.cxx
new file mode 100644
index 000000000..ea69be1d9
--- /dev/null
+++ b/extensions/source/ole/servprov.cxx
@@ -0,0 +1,548 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vector>
+
+#include "ole2uno.hxx"
+#include "unoconversionutilities.hxx"
+#include "servprov.hxx"
+#include "unoobjw.hxx"
+#include "oleobjw.hxx"
+
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <comphelper/automationinvokedzone.hxx>
+#include <comphelper/windowsdebugoutput.hxx>
+#include <comphelper/windowserrorstring.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <ooo/vba/XHelperInterface.hpp>
+#include <sal/log.hxx>
+
+using namespace cppu;
+using namespace osl;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::bridge;
+using namespace com::sun::star::bridge::ModelDependent;
+
+#include <initguid.h>
+
+// GUID used since 5.2 ( src569 m)
+// {82154420-0FBF-11d4-8313-005004526AB4}
+DEFINE_GUID(OID_ServiceManager, 0x82154420, 0xfbf, 0x11d4, 0x83, 0x13, 0x0, 0x50, 0x4, 0x52, 0x6a, 0xb4);
+
+// FIXME: This GUID is just the above OID_ServiceManager with the
+// initial part bumped by one. Is that good enough?
+// {82154421-0FBF-11d4-8313-005004526AB4}
+DEFINE_GUID(OID_LibreOfficeWriterApplication, 0x82154421, 0xfbf, 0x11d4, 0x83, 0x13, 0x0, 0x50, 0x4, 0x52, 0x6a, 0xb4);
+
+// For Calc
+// {82154425-0FBF-11d4-8313-005004526AB4}
+DEFINE_GUID(OID_LibreOfficeCalcApplication, 0x82154425, 0xfbf, 0x11d4, 0x83, 0x13, 0x0, 0x50, 0x4, 0x52, 0x6a, 0xb4);
+
+OneInstanceOleWrapper::OneInstanceOleWrapper( const Reference<XMultiServiceFactory>& smgr,
+ std::function<const Reference<XInterface>()> xInstFunction )
+ : m_refCount(0)
+ , m_xInstFunction(xInstFunction)
+ , m_factoryHandle(0)
+ , m_smgr(smgr)
+{
+ Reference<XInterface> xInt = m_smgr->createInstance("com.sun.star.bridge.oleautomation.BridgeSupplier");
+
+ if (xInt.is())
+ {
+ Any a= xInt->queryInterface( cppu::UnoType<XBridgeSupplier2>::get() );
+ a >>= m_bridgeSupplier;
+ }
+}
+
+OneInstanceOleWrapper::~OneInstanceOleWrapper()
+{
+}
+
+bool OneInstanceOleWrapper::registerClass(GUID const * pGuid)
+{
+ HRESULT hresult;
+
+ o2u_attachCurrentThread();
+
+ hresult = CoRegisterClassObject(
+ *pGuid,
+ this,
+ CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
+ REGCLS_MULTIPLEUSE,
+ &m_factoryHandle);
+
+ SAL_INFO("extensions.olebridge", "CoRegisterClassObject(" << *pGuid << "): " << WindowsErrorStringFromHRESULT(hresult));
+
+ return (hresult == NOERROR);
+}
+
+bool OneInstanceOleWrapper::deregisterClass()
+{
+ return CoRevokeClassObject(m_factoryHandle) == NOERROR;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP OneInstanceOleWrapper::QueryInterface(REFIID riid, void ** ppv)
+{
+ if(IsEqualIID(riid, IID_IUnknown))
+ {
+ AddRef();
+ *ppv = static_cast<IUnknown*>(static_cast<IClassFactory*>(this));
+ return NOERROR;
+ }
+ else if (IsEqualIID(riid, IID_IClassFactory))
+ {
+ AddRef();
+ *ppv = static_cast<IClassFactory*>(this);
+ return NOERROR;
+ }
+
+ *ppv = nullptr;
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) OneInstanceOleWrapper::AddRef()
+{
+ return osl_atomic_increment( &m_refCount);
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) OneInstanceOleWrapper::Release()
+{
+ MutexGuard oGuard( Mutex::getGlobalMutex());
+ ULONG refCount = --m_refCount;
+ if ( m_refCount == 0)
+ {
+ delete this;
+ }
+
+ return refCount;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP OneInstanceOleWrapper::CreateInstance(IUnknown*,
+ REFIID riid, void** ppv)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", "OneInstanceOleWrapper::CreateInstance(" << riid << ")");
+
+ HRESULT ret = ResultFromScode(E_UNEXPECTED);
+
+ const Reference<XInterface>& xInst = m_xInstFunction();
+ if (xInst.is())
+ {
+ Any usrAny(&xInst, cppu::UnoType<decltype(xInst)>::get());
+ sal_uInt8 arId[16];
+ rtl_getGlobalProcessId( arId);
+ Any oleAny = m_bridgeSupplier->createBridge(usrAny,
+ Sequence<sal_Int8>( reinterpret_cast<sal_Int8*>(arId), 16),
+ UNO,
+ OLE);
+
+
+ if (auto v = o3tl::tryAccess<sal_uIntPtr>(oleAny))
+ {
+ VARIANT* pVariant = reinterpret_cast<VARIANT*>(*v);
+
+ if ((pVariant->vt == VT_UNKNOWN) || (pVariant->vt == VT_DISPATCH))
+ {
+ SAL_INFO("extensions.olebridge", "OneInstanceOleWrapper::Createbridge: punkVal=" << pVariant->punkVal);
+ ret = pVariant->punkVal->QueryInterface(riid, ppv);
+ }
+
+ VariantClear(pVariant);
+ CoTaskMemFree(pVariant);
+ }
+ }
+
+ return ret;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP OneInstanceOleWrapper::LockServer(BOOL /*fLock*/)
+{
+ return NOERROR;
+}
+
+OleConverter::OleConverter( const Reference<XMultiServiceFactory> &smgr):
+ UnoConversionUtilities<OleConverter>( smgr)
+
+{
+}
+
+// The XMultiServiceFactory is later set by XInitialization
+OleConverter::OleConverter( const Reference<XMultiServiceFactory>& smgr, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass ):
+ UnoConversionUtilities<OleConverter>( smgr, unoWrapperClass, comWrapperClass )
+
+{
+}
+
+OleConverter::~OleConverter()
+{
+}
+
+// XBridgeSupplier --------------------------------------------------------------
+Any SAL_CALL OleConverter::createBridge(const Any& modelDepObject,
+ const Sequence< sal_Int8 >& ProcessId,
+ sal_Int16 sourceModelType,
+ sal_Int16 destModelType)
+{
+ Any ret;
+ sal_uInt8 arId[16];
+ rtl_getGlobalProcessId( arId );
+
+ Sequence< sal_Int8 > seqProcessId( reinterpret_cast<sal_Int8*>(arId), 16);
+
+ if ( seqProcessId == ProcessId)
+ {
+ if (sourceModelType == UNO)
+ {
+ if (destModelType == UNO)
+ {
+ // same model -> copy value only
+ ret = modelDepObject;
+ }
+ else if (destModelType == OLE)
+ {
+ // convert UNO any into variant
+ VARIANT* pVariant = static_cast<VARIANT*>(CoTaskMemAlloc(sizeof(VARIANT)));
+ VariantInit( pVariant);
+ try
+ {
+ anyToVariant( pVariant, modelDepObject);
+ }
+ catch(...)
+ {
+ CoTaskMemFree(pVariant);
+ throw IllegalArgumentException();
+ }
+ ret.setValue(static_cast<void*>(&pVariant), cppu::UnoType<sal_uIntPtr>::get());
+ }
+ else
+ throw IllegalArgumentException();
+ }
+ else if (sourceModelType == OLE)
+ {
+ auto v = o3tl::tryAccess<sal_uIntPtr>(modelDepObject);
+ if (!v)
+ {
+ throw IllegalArgumentException();
+ }
+ else if (destModelType == OLE)
+ {
+ // same model -> copy value only
+ VARIANT* pVariant = static_cast<VARIANT*>(CoTaskMemAlloc(sizeof(VARIANT)));
+
+ if (NOERROR != VariantCopy(pVariant, reinterpret_cast<VARIANT*>(*v)))
+ {
+ CoTaskMemFree(pVariant);
+ throw(IllegalArgumentException());
+ }
+ else
+ {
+ ret.setValue(static_cast<void*>(&pVariant), cppu::UnoType<sal_uIntPtr>::get());
+ }
+ }
+ else if (destModelType == UNO)
+ {
+ // convert variant into UNO any
+ VARIANT* pVariant = reinterpret_cast<VARIANT*>(*v);
+ try
+ {
+ variantToAny(pVariant, ret);
+ }
+ catch (const CannotConvertException & e)
+ {
+ throw IllegalArgumentException(
+ e.Message, nullptr, -1);
+ }
+ }
+ else
+ throw IllegalArgumentException();
+
+ }
+ else
+ throw IllegalArgumentException();
+ }
+
+ return ret;
+}
+
+OUString OleConverter::getImplementationName()
+{
+ return m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL
+ ? OUString("com.sun.star.comp.ole.OleConverter2")
+ : OUString("com.sun.star.comp.ole.OleConverterVar1");
+}
+
+sal_Bool OleConverter::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> OleConverter::getSupportedServiceNames()
+{
+ if (m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL)
+ {
+ return css::uno::Sequence<OUString>{
+ "com.sun.star.bridge.OleBridgeSupplier2",
+ "com.sun.star.bridge.oleautomation.BridgeSupplier"};
+ }
+ return css::uno::Sequence<OUString>{
+ "com.sun.star.bridge.OleBridgeSupplierVar1"};
+}
+
+// XInitialize ------------------------------------------------------------------------------
+// the first argument is an XMultiServiceFactory if at all
+void SAL_CALL OleConverter::initialize( const Sequence< Any >& aArguments )
+{
+ if( aArguments.getLength() == 1 && aArguments[0].getValueTypeClass() == TypeClass_INTERFACE)
+ {
+ Reference < XInterface > xInt;
+ aArguments[0] >>= xInt;
+ Reference <XMultiServiceFactory> xMulti( xInt, UNO_QUERY);
+ m_smgrRemote= xMulti;
+ }
+}
+
+// UnoConversionUtilities -------------------------------------------------------------------
+Reference< XInterface > OleConverter::createUnoWrapperInstance()
+{
+ if( m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL)
+ {
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+ }
+ else if( m_nUnoWrapperClass == UNO_OBJECT_WRAPPER_REMOTE_OPT)
+ {
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+ }
+ else
+ return Reference<XInterface>();
+}
+
+Reference< XInterface > OleConverter::createComWrapperInstance()
+{
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+}
+
+OleClient::OleClient( const Reference<XMultiServiceFactory>& smgr):
+ UnoConversionUtilities<OleClient>( smgr)
+{
+ Reference<XInterface> xInt;// = m_smgr->createInstance(L"com.sun.star.bridge.OleBridgeSupplier2");
+
+ if (xInt.is())
+ {
+ Any a= xInt->queryInterface(cppu::UnoType<XBridgeSupplier2>::get() );
+ a >>= m_bridgeSupplier;
+ }
+}
+
+OleClient::~OleClient()
+{
+}
+
+Sequence< OUString > SAL_CALL OleClient::getAvailableServiceNames()
+{
+ Sequence< OUString > ret;
+
+ return ret;
+}
+
+OUString OleClient::getImplementationName()
+{
+ return "com.sun.star.comp.ole.OleClient";
+}
+
+sal_Bool OleClient::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> OleClient::getSupportedServiceNames()
+{
+ return css::uno::Sequence<OUString>{
+ "com.sun.star.bridge.OleObjectFactory",
+ "com.sun.star.bridge.oleautomation.Factory"};
+}
+
+Reference<XInterface> SAL_CALL OleClient::createInstance(const OUString& ServiceSpecifier)
+{
+ Reference<XInterface> ret;
+ HRESULT result;
+ IUnknown* pUnknown = nullptr;
+ CLSID classId;
+
+ o2u_attachCurrentThread();
+
+ result = CLSIDFromProgID(
+ o3tl::toW(ServiceSpecifier.getStr()), //Pointer to the ProgID
+ &classId); //Pointer to the CLSID
+
+
+ if (result == NOERROR)
+ {
+ result = CoCreateInstance(
+ classId, //Class identifier (CLSID) of the object
+ nullptr, //Pointer to whether object is or isn't part of an aggregate
+ CLSCTX_SERVER, //Context for running executable code
+ IID_IUnknown, //Reference to the identifier of the interface
+ reinterpret_cast<void**>(&pUnknown)); //Address of output variable that receives
+ // the interface pointer requested in riid
+ }
+
+ if (pUnknown != nullptr)
+ {
+ Any any;
+ CComVariant variant;
+
+ V_VT(&variant) = VT_UNKNOWN;
+ V_UNKNOWN(&variant) = pUnknown;
+ // AddRef for Variant
+ pUnknown->AddRef();
+
+ // When the object is wrapped, then its refcount is increased
+ variantToAny(&variant, any);
+ if (any.getValueTypeClass() == TypeClass_INTERFACE)
+ {
+ any >>= ret;
+ }
+ pUnknown->Release(); // CoCreateInstance
+ }
+
+ return ret;
+}
+
+Reference<XInterface> SAL_CALL OleClient::createInstanceWithArguments(const OUString& ServiceSpecifier, const Sequence< Any >& /*Arguments*/)
+{
+ return createInstance( ServiceSpecifier);
+}
+
+// UnoConversionUtilities -----------------------------------------------------------------------------
+Reference< XInterface > OleClient::createUnoWrapperInstance()
+{
+ if( m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL)
+ {
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+ }
+ else if( m_nUnoWrapperClass == UNO_OBJECT_WRAPPER_REMOTE_OPT)
+ {
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+ }
+ else
+ return Reference< XInterface>();
+}
+// UnoConversionUtilities -----------------------------------------------------------------------------
+Reference< XInterface > OleClient::createComWrapperInstance( )
+{
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+}
+
+OleServer::OleServer( const Reference<XMultiServiceFactory>& smgr):
+ m_smgr( smgr)
+{
+ Reference<XInterface> xInt = m_smgr->createInstance("com.sun.star.bridge.oleautomation.BridgeSupplier");
+
+ if (xInt.is())
+ {
+ Any a= xInt->queryInterface( cppu::UnoType<XBridgeSupplier2>::get() );
+ a >>= m_bridgeSupplier;
+ }
+
+ (void) provideInstance( [&]
+ {
+ return m_smgr;
+ },
+ &OID_ServiceManager );
+
+ (void) provideInstance( [&]
+ {
+ // We want just one SwVbaGlobals for all Automation clients
+ static const Reference<XInterface> xWordGlobals = m_smgr->createInstance("ooo.vba.word.Globals");
+ const Reference<ooo::vba::XHelperInterface> xHelperInterface(xWordGlobals, UNO_QUERY);
+ Any aApplication = xHelperInterface->Application();
+ Reference<XInterface> xApplication;
+ aApplication >>= xApplication;
+ return xApplication;
+ },
+ &OID_LibreOfficeWriterApplication );
+
+ (void) provideInstance( [&]
+ {
+ // Ditto for sc
+ static const Reference<XInterface> xCalcGlobals = m_smgr->createInstance("ooo.vba.excel.Globals");
+ const Reference<ooo::vba::XHelperInterface> xHelperInterface(xCalcGlobals, UNO_QUERY);
+ Any aApplication = xHelperInterface->Application();
+ Reference<XInterface> xApplication;
+ aApplication >>= xApplication;
+ return xApplication;
+ },
+ &OID_LibreOfficeCalcApplication );
+}
+
+OleServer::~OleServer()
+{
+ for (auto const& elem : m_wrapperList)
+ {
+ elem->deregisterClass();
+ elem->Release();
+ }
+ m_wrapperList.clear();
+}
+
+OUString OleServer::getImplementationName()
+{
+ return "com.sun.star.comp.ole.OleServer";
+}
+
+sal_Bool OleServer::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> OleServer::getSupportedServiceNames()
+{
+ return css::uno::Sequence<OUString>{
+ "com.sun.star.bridge.OleApplicationRegistration",
+ "com.sun.star.bridge.oleautomation.ApplicationRegistration"};
+}
+
+bool OleServer::provideInstance(std::function<const Reference<XInterface>()> xInstFunction, GUID const * guid)
+{
+ OneInstanceOleWrapper* pWrapper = new OneInstanceOleWrapper( m_smgr, xInstFunction );
+
+ pWrapper->AddRef();
+ m_wrapperList.push_back(pWrapper);
+
+ return pWrapper->registerClass(guid);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/servprov.hxx b/extensions/source/ole/servprov.hxx
new file mode 100644
index 000000000..8871f28cf
--- /dev/null
+++ b/extensions/source/ole/servprov.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <functional>
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include "ole2uno.hxx"
+#include "unoconversionutilities.hxx"
+
+using namespace com::sun::star::bridge;
+using namespace cppu;
+
+/// @throws Exception
+Reference< XInterface> ConverterProvider_CreateInstance2( const Reference<XMultiServiceFactory> & xSMgr);
+/// @throws Exception
+Reference< XInterface> ConverterProvider_CreateInstanceVar1( const Reference<XMultiServiceFactory> & xSMgr);
+/// @throws Exception
+Reference<XInterface> OleClient_CreateInstance( const Reference<XMultiServiceFactory> & xSMgr);
+/// @throws Exception
+Reference<XInterface> OleServer_CreateInstance( const Reference<XMultiServiceFactory> & xSMgr);
+
+/*****************************************************************************
+
+ OneInstanceOleWrapper
+
+ Provides a single UNO object as OLE object.
+
+ Acts as a COM class factory. When IClassFactory::CreateInstance is being called
+ then it maps the XInstance member it to a COM object.
+
+*****************************************************************************/
+
+class OneInstanceOleWrapper : public IClassFactory
+{
+public:
+
+ OneInstanceOleWrapper( const Reference<XMultiServiceFactory>& smgr,
+ std::function<const Reference<XInterface>()> xInstFunction );
+ virtual ~OneInstanceOleWrapper();
+
+ bool registerClass(GUID const * pGuid);
+ bool deregisterClass();
+
+ /* IUnknown methods */
+ STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj) override;
+ STDMETHOD_(ULONG, AddRef)() override;
+ STDMETHOD_(ULONG, Release)() override;
+
+ /* IClassFactory methods */
+ STDMETHOD(CreateInstance)(IUnknown* punkOuter, REFIID riid, void** ppv) override;
+ STDMETHOD(LockServer)(BOOL fLock) override;
+
+protected:
+ oslInterlockedCount m_refCount;
+ std::function<const Reference<XInterface>()> m_xInstFunction;
+ DWORD m_factoryHandle;
+ Reference<XBridgeSupplier2> m_bridgeSupplier;
+ Reference<XMultiServiceFactory> m_smgr;
+};
+
+// Implementation of the UNO service com.sun.star.bridge.OleBridgeSupplier2.
+
+// This class realizes the service com.sun.star.bridge.OleBridgeSupplier2 and
+// com.sun.star.bridge.OleBridgeSupplierVar1. The class implements XBridgeSupplier2 which
+// interface does not need a Machine Id in its createBridge function anymore,
+// If a UNO interface is to be converted then the member m_nUnoWrapperClass determines
+// what wrapper class is to be used. There are currently InterfaceOleWrapper and
+// UnoObjectWrapperRemoteOpt. The first is used for the OleBridgeSupplier2 and the
+// latter for OleBridgeSupplierVar1.
+// The m_nComWrapperClass specifies the class which is used as wrapper for COM interfaces.
+// Currently there is only one class available (IUnknownWrapper).
+class OleConverter : public WeakImplHelper<XBridgeSupplier2, XInitialization, css::lang::XServiceInfo>,
+ public UnoConversionUtilities<OleConverter>
+{
+public:
+ explicit OleConverter( const Reference<XMultiServiceFactory>& smgr);
+ OleConverter( const Reference<XMultiServiceFactory>& smgr, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass );
+ virtual ~OleConverter() override;
+
+ // XBridgeSupplier2 ---------------------------------------------------
+
+ Any SAL_CALL createBridge(const Any& modelDepObject,
+ const Sequence<sal_Int8>& ProcessId,
+ sal_Int16 sourceModelType,
+ sal_Int16 destModelType) override;
+
+ // XInitialization
+ void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // UnoConversionUtilities
+ Reference< XInterface > createUnoWrapperInstance() override;
+ Reference< XInterface > createComWrapperInstance() override;
+protected:
+
+};
+
+// Implementation of the UNO service com.sun.star.bridge.OleObjectFactory.
+
+class OleClient : public WeakImplHelper<XMultiServiceFactory, css::lang::XServiceInfo>,
+ public UnoConversionUtilities<OleClient>
+{
+public:
+ explicit OleClient( const Reference<XMultiServiceFactory>& smgr);
+ ~OleClient() override;
+
+ // XMultiServiceFactory
+ Reference<XInterface> SAL_CALL createInstance(const OUString& ServiceSpecifier) override;
+ Reference<XInterface> SAL_CALL createInstanceWithArguments(const OUString& ServiceSpecifier, const Sequence< Any >& Arguments) override;
+ Sequence< OUString > SAL_CALL getAvailableServiceNames() override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // UnoConversionUtilities
+ Reference< XInterface > createUnoWrapperInstance() override;
+ Reference< XInterface > createComWrapperInstance() override;
+
+protected:
+ Reference<XBridgeSupplier2> m_bridgeSupplier;
+};
+
+/*****************************************************************************
+
+ OleServer
+
+ Implementation of the UNO service com.sun.star.bridge.OleApplicationRegistration.
+ Register the calling application as OLE automation server for
+ standard OLE object. The objects will be registered while instantiating
+ this implementation and deregistered, if this implementation is destroyed.
+
+*****************************************************************************/
+
+class OleServer : public cppu::WeakImplHelper<css::lang::XServiceInfo>
+{
+public:
+ explicit OleServer( const Reference<XMultiServiceFactory> &smgr);
+ ~OleServer() override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+protected:
+ bool provideInstance(std::function<const Reference<XInterface>()> xInstFunction, GUID const * guid);
+
+ std::list< OneInstanceOleWrapper* > m_wrapperList;
+ Reference< XBridgeSupplier2 > m_bridgeSupplier;
+
+ Reference<XMultiServiceFactory> m_smgr;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/servreg.cxx b/extensions/source/ole/servreg.cxx
new file mode 100644
index 000000000..66928137b
--- /dev/null
+++ b/extensions/source/ole/servreg.cxx
@@ -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 .
+ */
+
+#include <osl/time.h>
+#include "ole2uno.hxx"
+#include "servprov.hxx"
+#include <rtl/ustring.hxx>
+#include <cppuhelper/factory.hxx>
+
+using namespace cppu;
+
+Reference<XInterface> ConverterProvider_CreateInstance2( const Reference<XMultiServiceFactory> & xSMgr)
+{
+ Reference<XInterface> xService = *new OleConverter( xSMgr);
+ return xService;
+}
+
+Reference<XInterface> ConverterProvider_CreateInstanceVar1( const Reference<XMultiServiceFactory> & xSMgr)
+{
+ Reference<XInterface> xService = *new OleConverter( xSMgr, UNO_OBJECT_WRAPPER_REMOTE_OPT, IUNKNOWN_WRAPPER_IMPL);
+ return xService;
+}
+
+Reference<XInterface> OleClient_CreateInstance( const Reference<XMultiServiceFactory> & xSMgr)
+{
+ Reference<XInterface> xService = *new OleClient( xSMgr);
+ return xService;
+}
+
+Reference<XInterface> OleServer_CreateInstance( const Reference<XMultiServiceFactory> & xSMgr)
+{
+ Reference<XInterface > xService = *new OleServer(xSMgr);
+ return xService;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void * oleautobridge_component_getFactory(
+ const char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ )
+{
+ void * pRet = nullptr;
+
+ OUString aImplName( OUString::createFromAscii( pImplName ) );
+ Reference< XSingleServiceFactory > xFactory;
+ Sequence<OUString> seqServiceNames;
+ if (pServiceManager && aImplName == "com.sun.star.comp.ole.OleConverter2")
+ {
+ xFactory= createSingleFactory( static_cast< XMultiServiceFactory*>(pServiceManager),
+ aImplName,
+ ConverterProvider_CreateInstance2, seqServiceNames );
+ }
+ else if (pServiceManager && aImplName == "com.sun.star.comp.ole.OleConverterVar1")
+ {
+ xFactory= createSingleFactory( static_cast<XMultiServiceFactory*>(pServiceManager),
+ aImplName,
+ ConverterProvider_CreateInstanceVar1, seqServiceNames );
+ }
+ else if(pServiceManager && aImplName == "com.sun.star.comp.ole.OleClient")
+ {
+ xFactory= createSingleFactory( static_cast< XMultiServiceFactory*>(pServiceManager),
+ aImplName,
+ OleClient_CreateInstance, seqServiceNames );
+ }
+ else if(pServiceManager && aImplName == "com.sun.star.comp.ole.OleServer")
+ {
+ xFactory= createOneInstanceFactory( static_cast< XMultiServiceFactory*>(pServiceManager),
+ aImplName,
+ OleServer_CreateInstance, seqServiceNames );
+ }
+
+ if (xFactory.is())
+ {
+ xFactory->acquire();
+ pRet = xFactory.get();
+ }
+
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/unoconversionutilities.hxx b/extensions/source/ole/unoconversionutilities.hxx
new file mode 100644
index 000000000..a73a714ab
--- /dev/null
+++ b/extensions/source/ole/unoconversionutilities.hxx
@@ -0,0 +1,2363 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <memory>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/XInvocationAdapterFactory.hpp>
+#include <com/sun/star/script/XInvocationAdapterFactory2.hpp>
+#include <com/sun/star/script/XTypeConverter.hpp>
+#include <com/sun/star/script/FailReason.hpp>
+#include <com/sun/star/bridge/ModelDependent.hpp>
+#include <com/sun/star/bridge/XBridgeSupplier2.hpp>
+#include <com/sun/star/bridge/oleautomation/Date.hpp>
+#include <com/sun/star/bridge/oleautomation/Currency.hpp>
+#include <com/sun/star/bridge/oleautomation/SCode.hpp>
+#include <com/sun/star/bridge/oleautomation/Decimal.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <typelib/typedescription.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include "ole2uno.hxx"
+#include <cppuhelper/weakref.hxx>
+
+#include "unotypewrapper.hxx"
+#include <unordered_map>
+
+// for some reason DECIMAL_NEG (wtypes.h) which contains BYTE is not resolved.
+typedef unsigned char BYTE;
+// classes for wrapping uno objects
+#define INTERFACE_OLE_WRAPPER_IMPL 1
+#define UNO_OBJECT_WRAPPER_REMOTE_OPT 2
+
+#define INVOCATION_SERVICE "com.sun.star.script.Invocation"
+
+
+// classes for wrapping ole objects
+#define IUNKNOWN_WRAPPER_IMPL 1
+
+#define INTERFACE_ADAPTER_FACTORY "com.sun.star.script.InvocationAdapterFactory"
+// COM or JScript objects implementing UNO interfaces have to implement this property
+#define SUPPORTED_INTERFACES_PROP L"_implementedInterfaces"
+// Second property without leading underscore for use in VB
+#define SUPPORTED_INTERFACES_PROP2 L"Bridge_ImplementedInterfaces"
+
+using namespace com::sun::star::script;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::bridge::oleautomation;
+
+extern std::unordered_map<sal_uIntPtr, sal_uIntPtr> AdapterToWrapperMap;
+extern std::unordered_map<sal_uIntPtr, sal_uIntPtr> WrapperToAdapterMap;
+
+//Maps IUnknown pointers to a weak reference of the respective wrapper class (e.g.
+// IUnknownWrapperImpl. It is the responsibility of the wrapper to remove the entry when
+// it is being destroyed.
+// Used to ensure that an Automation object is always mapped to the same UNO objects.
+extern std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > ComPtrToWrapperMap;
+
+// Maps XInterface pointers to a weak reference of its wrapper class (i.e.
+// InterfaceOleWrapper). It is the responsibility of the wrapper to remove the entry when
+// it is being destroyed. It is used to ensure the identity of objects. That is, a UNO interface
+// is mapped to IDispatch which is kept alive in the COM environment. If the same
+// UNO interface is mapped again to COM then the IDispach of the first mapped instance
+// must be returned.
+extern std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > UnoObjToWrapperMap;
+
+// This function tries to the change the type of a value (contained in the Any)
+// to the smallest possible that can hold the value. This is actually done only
+// for types of VT_I4 (see o2u_variantToAny). The reason is the following:
+// JavaScript passes integer values always as VT_I4. If there is a parameter or
+// property of type any then the bridge converts the any's content according
+// to "o2u_variantToAny". Because the VARTYPE is VT_I4 the value would be converted
+// to TypeClass_LONG. Say the method XPropertySet::setPropertyValue( string name, any value)
+// would be called on an object and the property actually is of TypeClass_SHORT.
+// After conversion of the VARIANT parameter the Any would contain type
+// TypeClass_LONG. Because the corereflection does not cast from long to short
+// the "setPropertValue" would fail as the value has not the right type.
+
+// The corereflection does convert small integer types to bigger types.
+// Therefore we can reduce the type if possible and avoid the above mentioned
+// problem.
+
+// The function is not used when elements are to be converted for Sequences.
+
+inline void reduceRange( Any& any)
+{
+ OSL_ASSERT( any.getValueTypeClass() == TypeClass_LONG);
+
+ sal_Int32 value= *o3tl::doAccess<sal_Int32>(any);
+ if( value <= 0x7f && value >= -0x80)
+ {// -128 bis 127
+ sal_Int8 charVal= static_cast<sal_Int8>( value);
+ any.setValue( &charVal, cppu::UnoType<sal_Int8>::get());
+ }
+ else if( value <= 0x7fff && value >= -0x8000)
+ {// -32768 bis 32767
+ sal_Int16 shortVal= static_cast<sal_Int16>( value);
+ any.setValue( &shortVal, cppu::UnoType<sal_Int16>::get());
+ }
+}
+
+// createUnoObjectWrapper gets a wrapper instance by calling createUnoWrapperInstance
+ // and initializes it via XInitialization. The wrapper object is required to implement
+ // XBridgeSupplier so that it can convert itself to IDispatch.
+ // class T: Deriving class ( must implement XInterface )
+/** All methods are allowed to throw at least a BridgeRuntimeError.
+ */
+template< class >
+class UnoConversionUtilities
+{
+public:
+ explicit UnoConversionUtilities( const Reference<XMultiServiceFactory> & smgr):
+ m_nUnoWrapperClass( INTERFACE_OLE_WRAPPER_IMPL),
+ m_nComWrapperClass( IUNKNOWN_WRAPPER_IMPL),
+ m_smgr( smgr)
+ {}
+
+ UnoConversionUtilities( const Reference<XMultiServiceFactory> & xFactory, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass )
+ : m_nUnoWrapperClass(unoWrapperClass),
+ m_nComWrapperClass(comWrapperClass), m_smgr(xFactory)
+ {}
+
+ virtual ~UnoConversionUtilities() {}
+ /** converts only into oleautomation types, that is there is no VT_I1, VT_UI2, VT_UI4
+ a sal_Unicode character is converted into a BSTR.
+ @exception com.sun.star.lang.IllegalArgumentException
+ If the any was inappropriate for conversion.
+ @exception com.sun.star.script.CannotConvertException
+ The any contains a type class for which no conversion is provided.
+ */
+ void anyToVariant(VARIANT* pVariant, const Any& rAny);
+ void anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type);
+
+ /** @exception com.sun.star.lang.IllegalArgumentException
+ If rSeq does not contain a sequence then the exception is thrown.
+ */
+ SAFEARRAY* createUnoSequenceWrapper(const Any& rSeq);
+ /** @exception com.sun.star.lang.IllegalArgumentException
+ If rSeq does not contain a sequence or elemtype has no proper value
+ then the exception is thrown.
+ */
+ SAFEARRAY* createUnoSequenceWrapper(const Any& rSeq, VARTYPE elemtype);
+ /**
+ @exception com.sun.star.lang.IllegalArgumentException
+ If rObj does not contain a struct or interface
+ */
+ void createUnoObjectWrapper(const Any & rObj, VARIANT * pVar);
+ /** @exception CannotConvertException
+ Thrown if the VARIANT contains a type that cannot be coerced in the expected Any.
+ ArgumentIndex is 0.
+ @IllegalArgumentException
+ Thrown if the VARIANT is inappropriate for conversion. ArgumentPosition is -1,
+ */
+ void variantToAny(const VARIANT* pVariant, Any& rAny, bool bReduceValueRange = true);
+ /** This method converts variants arguments in calls from COM -> UNO. Only then
+ the expected UNO type is known.
+ @exception CannotConvertException
+ Thrown if the VARIANT contains a type that cannot be coerced in the expected Any.
+ ArgumentIndex is 0.
+ @IllegalArgumentException
+ Thrown if the VARIANT is inappropriate for conversion. ArgumentPosition is -1,
+ */
+ void variantToAny( const VARIANTARG* pArg, Any& rAny, const Type& ptype, bool bReduceValueRange = true);
+
+ /**
+ @exception IllegalArgumentException
+ -if pVar does not contain VT_UNKNOWN or VT_DISPATCH or
+ pVar is used for a particular UNO type which is not supported by pVar
+ */
+ Any createOleObjectWrapper(VARIANT* pVar, const Type& aType= Type());
+
+ /*
+ Return true means var contained a ValueObject, and it was successfully converted.
+ The result is in any. It an error occurred a BridgeRuntimeError will be thrown.
+ */
+ bool convertValueObject( const VARIANTARG *var, Any& any);
+ void dispatchExObject2Sequence( const VARIANTARG* pvar, Any& anySeq, const Type& type);
+
+ Sequence<Any> createOleArrayWrapperOfDim(SAFEARRAY* pArray, unsigned int dimCount, unsigned int actDim, LONG* index,
+ VARTYPE type, const Type& unotype);
+ Sequence<Any> createOleArrayWrapper(SAFEARRAY* pArray, VARTYPE type, const Type& unotype= Type());
+
+
+ VARTYPE mapTypeClassToVartype( TypeClass type);
+ Reference< XSingleServiceFactory > getInvocationFactory(const Any& anyObject);
+
+
+ virtual Reference< XInterface > createUnoWrapperInstance()=0;
+ virtual Reference< XInterface > createComWrapperInstance()=0;
+
+ static bool isJScriptArray(const VARIANT* pvar);
+
+ Sequence<Type> getImplementedInterfaces(IUnknown* pUnk);
+
+protected:
+ Reference<XInterface> createAdapter(const Sequence<Type>& types, const Reference<XInterface>& receiver);
+
+ // helper function for Sequence conversion
+ void getElementCountAndTypeOfSequence( const Any& rSeq, sal_Int32 dim, Sequence< sal_Int32 >& seqElementCounts, TypeDescription& typeDesc);
+ // helper function for Sequence conversion
+ static bool incrementMultidimensionalIndex(sal_Int32 dimensions, const sal_Int32 * parDimensionLength,
+ sal_Int32 * parMultidimensionalIndex);
+ // helper function for Sequence conversion
+ static size_t getOleElementSize( VARTYPE type);
+
+ static Type getElementTypeOfSequence( const Type& seqType);
+
+ //Provides a typeconverter
+ Reference<XTypeConverter> getTypeConverter();
+
+ // This member determines what class is used to convert a UNO object
+ // or struct to a COM object. It is passed along to the anyToVariant
+ // function in the createBridge function implementation
+ const sal_uInt8 m_nUnoWrapperClass;
+ const sal_uInt8 m_nComWrapperClass;
+
+ // The servicemanager is either a local smgr or remote when the service
+ // com.sun.star.bridge.OleBridgeSupplierVar1 is used. This service can be
+ // created by createInstanceWithArguments where one can supply a service
+ // manager that is to be used.
+ // Local service manager as supplied by the loader when the creator function
+ // of the service is being called.
+ Reference<XMultiServiceFactory> m_smgr;
+ // An explicitly supplied service manager when the service
+ // com.sun.star.bridge.OleBridgeSupplierVar1 is used. That can be a remote
+ // manager.
+ Reference<XMultiServiceFactory> m_smgrRemote;
+ Reference<XSingleServiceFactory> m_xInvocationFactoryLocal;
+ Reference<XSingleServiceFactory> m_xInvocationFactoryRemote;
+
+private:
+ // Holds the type converter which is used for sequence conversion etc.
+ // Use the getTypeConverter function to obtain the interface.
+ Reference<XTypeConverter> m_typeConverter;
+
+
+};
+
+// ask the object for XBridgeSupplier2 and on success bridges
+// the uno object to IUnknown or IDispatch.
+// return true the UNO object supports
+template < class T >
+bool convertSelfToCom( T& unoInterface, VARIANT * pVar)
+{
+ bool ret = false;
+ Reference< XInterface > xInt( unoInterface, UNO_QUERY);
+ if( xInt.is())
+ {
+ Reference< css::bridge::XBridgeSupplier2 > xSupplier( xInt, UNO_QUERY);
+ if( xSupplier.is())
+ {
+ sal_Int8 arId[16];
+ rtl_getGlobalProcessId( reinterpret_cast<sal_uInt8*>(arId));
+ Sequence<sal_Int8> seqId( arId, 16);
+ Any anySource;
+ anySource <<= xInt;
+ Any anyDisp = xSupplier->createBridge(
+ anySource, seqId, css::bridge::ModelDependent::UNO,
+ css::bridge::ModelDependent::OLE);
+
+ // due to global-process-id check this must be in-process pointer
+ if (auto v = o3tl::tryAccess<sal_uIntPtr>(anyDisp))
+ {
+ VARIANT* pvariant= reinterpret_cast<VARIANT*>(*v);
+ HRESULT hr;
+ if (FAILED(hr = VariantCopy(pVar, pvariant)))
+ throw BridgeRuntimeError(
+ "[automation bridge] convertSelfToCom\n"
+ "VariantCopy failed! Error: " +
+ OUString::number(hr));
+ VariantClear( pvariant);
+ CoTaskMemFree( pvariant);
+ ret = true;
+ }
+ }
+ }
+ return ret;
+}
+
+
+// Gets the invocation factory depending on the Type in the Any.
+// The factory can be created by a local or remote multi service factory.
+// In case there is a remote multi service factory available there are
+// some services or types for which the local factory is used. The exceptions
+// are: all structs.
+// Param anyObject - contains the object ( interface, struct) for what we need an invocation object.
+
+template<class T>
+Reference< XSingleServiceFactory > UnoConversionUtilities<T>::getInvocationFactory(const Any& anyObject)
+{
+ Reference< XSingleServiceFactory > retVal;
+ MutexGuard guard( getBridgeMutex());
+ if( anyObject.getValueTypeClass() != TypeClass_STRUCT &&
+ m_smgrRemote.is() )
+ {
+ if( ! m_xInvocationFactoryRemote.is() )
+ m_xInvocationFactoryRemote.set(m_smgrRemote->createInstance( INVOCATION_SERVICE), UNO_QUERY);
+ retVal= m_xInvocationFactoryRemote;
+ }
+ else
+ {
+ if( ! m_xInvocationFactoryLocal.is() )
+ m_xInvocationFactoryLocal.set(m_smgr->createInstance(INVOCATION_SERVICE ), UNO_QUERY);
+ retVal= m_xInvocationFactoryLocal;
+ }
+ return retVal;
+}
+
+template<class T>
+void UnoConversionUtilities<T>::variantToAny( const VARIANTARG* pArg, Any& rAny, const Type& ptype, bool bReduceValueRange /* = sal_True */)
+{
+ try
+ {
+ HRESULT hr;
+ bool bFail = false;
+ bool bCannotConvert = false;
+ CComVariant var;
+
+ // There is no need to support indirect values, since they're not supported by UNO
+ if( FAILED(hr= VariantCopyInd( &var, pArg))) // remove VT_BYREF
+ throw BridgeRuntimeError(
+ "[automation bridge] UnoConversionUtilities<T>::variantToAny \n"
+ "VariantCopyInd failed for reason : " + OUString::number(hr));
+ bool bHandled = convertValueObject( & var, rAny);
+ if( bHandled)
+ OSL_ENSURE( rAny.getValueType() == ptype, "type in Value Object must match the type parameter");
+
+ if( ! bHandled)
+ {
+ // convert into a variant type that is the equivalent to the type
+ // the sequence expects. Thus variantToAny produces the correct type
+ // E.g. An Array object contains VT_I4 and the sequence expects shorts
+ // than the vartype must be changed. The reason is, you can't specify the
+ // type in JavaScript and the script engine determines the type being used.
+ switch( ptype.getTypeClass())
+ {
+ case TypeClass_CHAR: // could be: new Array( 12, 'w', "w")
+ if( var.vt == VT_BSTR)
+ {
+ if(SUCCEEDED( hr= VariantChangeType( &var, &var, 0, VT_BSTR)))
+ rAny.setValue( V_BSTR( &var), ptype);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ }
+ else
+ {
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I2)))
+ rAny.setValue(& var.iVal, ptype);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ }
+ break;
+ case TypeClass_INTERFACE: // could also be an IUnknown
+ case TypeClass_STRUCT:
+ {
+ rAny = createOleObjectWrapper( & var, ptype);
+ break;
+ }
+ case TypeClass_ENUM:
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I4)))
+ rAny.setValue(& var.lVal, ptype);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_SEQUENCE:
+ // There are different ways of receiving a sequence:
+ // 1: JScript, VARTYPE: VT_DISPATCH
+ // 2. VBScript simple arraysVT_VARIANT|VT_BYREF the referenced VARIANT contains
+ // a VT_ARRAY| <type>
+ // 3. VBScript multi dimensional arrays: VT_ARRAY|VT_BYREF
+ if( pArg->vt == VT_DISPATCH)
+ {
+ dispatchExObject2Sequence( pArg, rAny, ptype);
+ }
+ else
+ {
+ if ((var.vt & VT_ARRAY) != 0)
+ {
+ VARTYPE oleType = ::sal::static_int_cast< VARTYPE, int >( var.vt ^ VT_ARRAY );
+ Sequence<Any> unoSeq = createOleArrayWrapper( var.parray, oleType, ptype);
+ Reference<XTypeConverter> conv = getTypeConverter();
+ if (conv.is())
+ {
+ try
+ {
+ Any anySeq(unoSeq);
+ Any convAny = conv->convertTo(anySeq, ptype);
+ rAny = convAny;
+ }
+ catch (const IllegalArgumentException& e)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]com.sun.star.lang.IllegalArgumentException "
+ "in UnoConversionUtilities<T>::variantToAny! Message: " +
+ e.Message);
+ }
+ catch (const CannotConvertException& e)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]com.sun.star.script.CannotConvertException "
+ "in UnoConversionUtilities<T>::variantToAny! Message: " +
+ e.Message);
+ }
+ }
+ }
+ }
+ break;
+ case TypeClass_VOID:
+ rAny.setValue(nullptr,Type());
+ break;
+ case TypeClass_ANY: // Any
+ // There could be a JScript Array that needs special handling
+ // If an Any is expected and this Any must contain a Sequence
+ // then we cannot figure out what element type is required.
+ // Therefore we convert to Sequence< Any >
+ if( pArg->vt == VT_DISPATCH && isJScriptArray( pArg))
+ {
+ dispatchExObject2Sequence( pArg, rAny,
+ cppu::UnoType<Sequence<Any>>::get());
+ }
+ else if (pArg->vt == VT_DECIMAL)
+ {
+ //Decimal maps to hyper in calls from COM -> UNO
+ // It does not matter if we create a sal_uInt64 or sal_Int64,
+ // because the UNO object is called through invocation which
+ //will do a type conversion if necessary
+ if (var.decVal.sign == 0)
+ {
+ // positive value
+ variantToAny( & var, rAny, cppu::UnoType<sal_uInt64>::get(),
+ bReduceValueRange);
+ }
+ else
+ {
+ //negative value
+ variantToAny( & var, rAny, cppu::UnoType<sal_Int64>::get(),
+ bReduceValueRange);
+ }
+ }
+ else
+ {
+ variantToAny( & var, rAny);
+ }
+ break;
+ case TypeClass_BOOLEAN: // VARIANT could be VARIANT_BOOL or other
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_BOOL)))
+ variantToAny( & var, rAny);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_STRING: // UString
+ if(var.vt == VT_NULL)
+ var = CComBSTR("");
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_BSTR)))
+ variantToAny( & var, rAny);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_FLOAT: // float
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_R4)))
+ variantToAny( & var, rAny);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_DOUBLE: // double
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_R8)))
+ variantToAny(& var, rAny);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_BYTE: // BYTE
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I1)))
+ variantToAny( & var, rAny);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_SHORT: // INT16
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_I2)))
+ variantToAny( & var, rAny);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_LONG:
+ if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_I4)))
+ variantToAny( & var, rAny, bReduceValueRange);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_HYPER:
+ if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_DECIMAL)))
+ {
+ if (var.decVal.Lo64 > SAL_CONST_UINT64(0x8000000000000000)
+ || var.decVal.Hi32 > 0
+ || var.decVal.scale > 0)
+ {
+ bFail = true;
+ break;
+ }
+ sal_Int64 value = var.decVal.Lo64;
+ if (var.decVal.sign == DECIMAL_NEG)
+ value |= SAL_CONST_UINT64(0x8000000000000000);
+ rAny <<= value;
+ }
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_UNSIGNED_SHORT: // UINT16
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_UI2)))
+ variantToAny( & var, rAny);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_UNSIGNED_LONG:
+ if(SUCCEEDED(hr = VariantChangeType( & var, &var, 0, VT_UI4)))
+ variantToAny( & var, rAny, bReduceValueRange);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_UNSIGNED_HYPER:
+ if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_DECIMAL)))
+ {
+ if (var.decVal.Hi32 > 0 || var.decVal.scale > 0)
+ {
+ bFail = true;
+ break;
+ }
+ rAny <<= var.decVal.Lo64;
+ }
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ case TypeClass_TYPE:
+ if(SUCCEEDED(hr = VariantChangeType(& var, &var, 0, VT_UNKNOWN)))
+ variantToAny( & var, rAny);
+ else if (hr == DISP_E_TYPEMISMATCH)
+ bCannotConvert = true;
+ else
+ bFail = true;
+ break;
+ default:
+ bCannotConvert = true;
+ break;
+ }
+ }
+ if (bCannotConvert)
+ throw CannotConvertException(
+ "[automation bridge]UnoConversionUtilities<T>::variantToAny \n"
+ "Cannot convert the value of vartype :\"" +
+ OUString::number(static_cast<sal_Int32>(var.vt)) +
+ "\" to the expected UNO type of type class: " +
+ OUString::number(static_cast<sal_Int32>(ptype.getTypeClass())),
+ nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0);
+
+ if (bFail)
+ throw IllegalArgumentException(
+ "[automation bridge]UnoConversionUtilities<T>:variantToAny\n"
+ "The provided VARIANT of type\" " + OUString::number(static_cast<sal_Int32>(var.vt)) +
+ "\" is unappropriate for conversion!", Reference<XInterface>(), -1);
+ }
+ catch (const CannotConvertException &)
+ {
+ throw;
+ }
+ catch (const IllegalArgumentException &)
+ {
+ throw;
+ }
+ catch (const BridgeRuntimeError &)
+ {
+ throw;
+ }
+ catch (const Exception & e)
+ {
+ throw BridgeRuntimeError("[automation bridge] unexpected exception in "
+ "UnoConversionUtilities<T>::variantToAny ! Message : \n" +
+ e.Message);
+ }
+ catch(...)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge] unexpected exception in "
+ "UnoConversionUtilities<T>::variantToAny !");
+ }
+}
+
+// The function only converts Sequences to SAFEARRAYS with elements of the type
+// specified by the parameter type. Everything else is forwarded to
+// anyToVariant(VARIANT* pVariant, const Any& rAny)
+// Param type must not be VT_BYREF
+template<class T>
+void UnoConversionUtilities<T>::anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type)
+{
+ try
+ {
+ HRESULT hr= S_OK;
+
+ OSL_ASSERT( (type & VT_BYREF) == 0);
+ if (type & VT_ARRAY)
+ {
+ type ^= VT_ARRAY;
+ SAFEARRAY* ar= createUnoSequenceWrapper( rAny, type);
+ if( ar)
+ {
+ VariantClear( pVariant);
+ pVariant->vt= ::sal::static_int_cast< VARTYPE, int >( VT_ARRAY | type );
+ pVariant->byref= ar;
+ }
+ }
+ else if(type == VT_VARIANT)
+ {
+ anyToVariant(pVariant, rAny);
+ }
+ else
+ {
+ CComVariant var;
+ anyToVariant( &var, rAny);
+ if(FAILED(hr = VariantChangeType(&var, &var, 0, type)))
+ {
+ if (hr == DISP_E_TYPEMISMATCH)
+ throw CannotConvertException(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n"
+ "Cannot convert the value of type :\"" +
+ rAny.getValueTypeName() +
+ "\" to the expected Automation type of VARTYPE: " +
+ OUString::number(static_cast<sal_Int32>(type)),
+ nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0);
+
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n"
+ "Conversion of any with " +
+ rAny.getValueType().getTypeName() +
+ " to VARIANT with type: " + OUString::number(static_cast<sal_Int32>(type)) +
+ " failed! Error code: " + OUString::number(hr));
+
+ }
+ if(FAILED(hr = VariantCopy(pVariant, &var)))
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n"
+ "VariantCopy failed for reason: " + OUString::number(hr));
+ }
+ }
+ }
+ catch (const IllegalArgumentException &)
+ {
+ throw;
+ }
+ catch (const CannotConvertException &)
+ {
+ throw;
+ }
+ catch (const BridgeRuntimeError&)
+ {
+ throw;
+ }
+ catch(const Exception & e)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n"
+ "Unexpected exception occurred. Message: " + e.Message);
+ }
+ catch(...)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n"
+ "Unexpected exception occurred.");
+ }
+}
+
+template<class T>
+void UnoConversionUtilities<T>::anyToVariant(VARIANT* pVariant, const Any& rAny)
+{
+ try
+ {
+ bool bIllegal = false;
+ switch (rAny.getValueTypeClass())
+ {
+ case TypeClass_INTERFACE:
+ {
+ Reference<XInterface> xInt;
+ if (rAny >>= xInt)
+ {
+ createUnoObjectWrapper(rAny, pVariant);
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ break;
+ }
+ case TypeClass_STRUCT:
+ {
+ if (rAny.getValueType() == cppu::UnoType<Date>::get() )
+ {
+ Date d;
+ if (rAny >>= d)
+ {
+ pVariant->vt = VT_DATE;
+ pVariant->date = d.Value;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ }
+ else if(rAny.getValueType() == cppu::UnoType<Decimal>::get())
+ {
+ Decimal d;
+ if (rAny >>= d)
+ {
+ pVariant->vt = VT_DECIMAL;
+ pVariant->decVal.scale = d.Scale;
+ pVariant->decVal.sign = d.Sign;
+ pVariant->decVal.Lo32 = d.LowValue;
+ pVariant->decVal.Mid32 = d.MiddleValue;
+ pVariant->decVal.Hi32 = d.HighValue;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ }
+ else if (rAny.getValueType() == cppu::UnoType<Currency>::get())
+ {
+ Currency c;
+ if (rAny >>= c)
+ {
+ pVariant->vt = VT_CY;
+ pVariant->cyVal.int64 = c.Value;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ }
+ else if(rAny.getValueType() == cppu::UnoType<SCode>::get())
+ {
+ SCode s;
+ if (rAny >>= s)
+ {
+ pVariant->vt = VT_ERROR;
+ pVariant->scode = s.Value;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ }
+ else
+ {
+ createUnoObjectWrapper(rAny, pVariant);
+ }
+ break;
+ }
+ case TypeClass_SEQUENCE: // sequence ??? SafeArray descriptor
+ {
+ SAFEARRAY* pArray = createUnoSequenceWrapper(rAny);
+ if (pArray)
+ {
+ V_VT(pVariant) = VT_ARRAY | VT_VARIANT;
+ V_ARRAY(pVariant) = pArray;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ break;
+ }
+ case TypeClass_VOID:
+ {
+ HRESULT hr = S_OK;
+ if (FAILED(hr = VariantClear(pVariant)))
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant\n"
+ "VariantClear failed with error:" + OUString::number(hr));
+ }
+ break;
+ }
+ case TypeClass_BOOLEAN:
+ {
+ bool value;
+ if (rAny >>= value)
+ {
+ pVariant->vt = VT_BOOL;
+ pVariant->boolVal = value ? VARIANT_TRUE: VARIANT_FALSE;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ break;
+ }
+ case TypeClass_CHAR:
+ {
+ // Because VT_UI2 does not conform to oleautomation we convert into VT_I2 instead
+ sal_uInt16 value = *o3tl::forceAccess<sal_Unicode>(rAny);
+ pVariant->vt = VT_I2;
+ pVariant->iVal = value;
+ break;
+ }
+ case TypeClass_STRING:
+ {
+ OUString value;
+ if (rAny >>= value)
+ {
+ pVariant->vt = VT_BSTR;
+ pVariant->bstrVal = SysAllocString(o3tl::toW(value.getStr()));
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ break;
+ }
+ case TypeClass_FLOAT:
+ {
+ float value;
+ if (rAny >>= value)
+ {
+ pVariant->vt = VT_R4;
+ pVariant->fltVal = value;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ break;
+ }
+ case TypeClass_DOUBLE:
+ {
+ double value;
+ if (rAny >>= value)
+ {
+ pVariant->vt = VT_R8;
+ pVariant->dblVal = value;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ break;
+ }
+ case TypeClass_BYTE:
+ {
+ // ole automation does not know a signed char but only unsigned char
+ sal_Int8 value;
+ if (rAny >>= value)
+ {
+ pVariant->vt = VT_UI1;
+ pVariant->bVal = value;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ break;
+ }
+ case TypeClass_SHORT: // INT16
+ case TypeClass_UNSIGNED_SHORT: // UINT16
+ {
+ sal_Int16 value;
+ if (rAny >>= value)
+ {
+ pVariant->vt = VT_I2;
+ pVariant->iVal = value;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ break;
+ }
+ case TypeClass_ENUM:
+ {
+ sal_Int32 value = *static_cast<sal_Int32 const *>(rAny.getValue());
+ pVariant->vt = VT_I4;
+ pVariant->lVal= value;
+ break;
+ }
+ case TypeClass_LONG:
+ case TypeClass_UNSIGNED_LONG:
+ {
+ sal_Int32 value;
+ if (rAny >>= value)
+ {
+ pVariant->vt = VT_I4;
+ pVariant->lVal= value;
+ }
+ else
+ {
+ bIllegal = true;
+ }
+ break;
+ }
+ case TypeClass_HYPER:
+ {
+
+ pVariant->vt = VT_DECIMAL;
+ pVariant->decVal.scale = 0;
+ pVariant->decVal.sign = 0;
+ pVariant->decVal.Hi32 = 0;
+
+ sal_Int64 value;
+ rAny >>= value;
+
+ if (value & SAL_CONST_UINT64(0x8000000000000000))
+ pVariant->decVal.sign = DECIMAL_NEG;
+
+ pVariant->decVal.Lo64 = value;
+ break;
+ }
+ case TypeClass_UNSIGNED_HYPER:
+ {
+ pVariant->vt = VT_DECIMAL;
+ pVariant->decVal.scale = 0;
+ pVariant->decVal.sign = 0;
+ pVariant->decVal.Hi32 = 0;
+
+ sal_uInt64 value;
+ rAny >>= value;
+ pVariant->decVal.Lo64 = value;
+ break;
+ }
+ case TypeClass_TYPE:
+ {
+ Type type;
+ rAny >>= type;
+ CComVariant var;
+ if (!createUnoTypeWrapper(type.getTypeName(), & var))
+ throw BridgeRuntimeError(
+ "[automation bridge] UnoConversionUtilities<T>::anyToVariant \n"
+ "Error during conversion of UNO type to Automation object!");
+
+ if (FAILED(VariantCopy(pVariant, &var)))
+ throw BridgeRuntimeError(
+ "[automation bridge] UnoConversionUtilities<T>::anyToVariant \n"
+ "Unexpected error!");
+ break;
+ }
+ default:
+ //TypeClass_SERVICE:
+ //TypeClass_EXCEPTION:
+ //When an InvocationTargetException is thrown when calling XInvocation::invoke
+ //on a UNO object, then the target exception is directly used to create a
+ //EXEPINFO structure
+ //TypeClass_TYPEDEF
+ //TypeClass_ANY:
+ //TypeClass_UNKNOWN:
+ //TypeClass_MODULE:
+ throw CannotConvertException(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant\n"
+ "There is no conversion for this UNO type to an Automation type."
+ "The destination type class is the type class of the UNO "
+ "argument which was to be converted.",
+ Reference<XInterface>(), rAny.getValueTypeClass(),
+ FailReason::TYPE_NOT_SUPPORTED, 0);
+
+ break;
+ }
+ if (bIllegal)
+ {
+ throw IllegalArgumentException(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant\n"
+ "The provided any of type\" " + rAny.getValueType().getTypeName() +
+ "\" is unappropriate for conversion!", Reference<XInterface>(), -1);
+
+ }
+ }
+ catch (const CannotConvertException &)
+ {
+ throw;
+ }
+ catch (const IllegalArgumentException &)
+ {
+ throw;
+ }
+ catch(const BridgeRuntimeError&)
+ {
+ throw;
+ }
+ catch(const Exception & e)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n"
+ "Unexpected exception occurred. Message: " + e.Message);
+ }
+ catch(...)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::anyToVariant \n"
+ "Unexpected exception occurred. " );
+ }
+}
+
+// Creates an SAFEARRAY of the specified element and if necessary
+// creates a SAFEARRAY with multiple dimensions.
+// Used by sal_Bool anyToVariant(VARIANT* pVariant, const Any& rAny, VARTYPE type);
+template<class T>
+SAFEARRAY* UnoConversionUtilities<T>::createUnoSequenceWrapper(const Any& rSeq, VARTYPE elemtype)
+{
+ if (rSeq.getValueTypeClass() != TypeClass_SEQUENCE)
+ throw IllegalArgumentException(
+ "[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper \n"
+ "The any does not contain a sequence!", nullptr, 0);
+ if (elemtype == VT_NULL || elemtype == VT_EMPTY)
+ throw IllegalArgumentException(
+ "[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper \n"
+ "No element type supplied!",nullptr, -1);
+ SAFEARRAY* pArray= nullptr;
+ // Get the dimensions. This is done by examining the type name string
+ // The count of brackets determines the dimensions.
+ OUString sTypeName= rSeq.getValueType().getTypeName();
+ sal_Int32 dims=0;
+ for(sal_Int32 lastIndex=0;(lastIndex= sTypeName.indexOf( L'[', lastIndex)) != -1; lastIndex++,dims++);
+
+ //get the maximum number of elements per dimensions and the typedescription of the elements
+ Sequence<sal_Int32> seqElementCounts( dims);
+ TypeDescription elementTypeDesc;
+ getElementCountAndTypeOfSequence( rSeq, 1, seqElementCounts, elementTypeDesc );
+
+ if( elementTypeDesc.is() )
+ {
+ // set up the SAFEARRAY
+ std::unique_ptr<SAFEARRAYBOUND[]> sarSafeArrayBound(new SAFEARRAYBOUND[dims]);
+ SAFEARRAYBOUND* prgsabound= sarSafeArrayBound.get();
+ for( sal_Int32 i=0; i < dims; i++)
+ {
+ //prgsabound[0] is the right most dimension
+ prgsabound[dims - i - 1].lLbound = 0;
+ prgsabound[dims - i - 1].cElements = seqElementCounts[i];
+ }
+
+ typelib_TypeDescription* rawTypeDesc= elementTypeDesc.get();
+ sal_Int32 elementSize= rawTypeDesc->nSize;
+ size_t oleElementSize= getOleElementSize( elemtype);
+ // SafeArrayCreate clears the memory for the data itself.
+ pArray = SafeArrayCreate(elemtype, dims, prgsabound);
+
+ // convert the Sequence's elements and populate the SAFEARRAY
+ if( pArray)
+ {
+ // Iterate over every Sequence that contains the actual elements
+ void* pSAData;
+ if( SUCCEEDED( SafeArrayAccessData( pArray, &pSAData)))
+ {
+ const sal_Int32* parElementCount= seqElementCounts.getConstArray();
+ uno_Sequence * pMultiSeq= *static_cast<uno_Sequence* const*>(rSeq.getValue());
+ sal_Int32 dimsSeq= dims - 1;
+
+ // arDimSeqIndices contains the current index of a block of data.
+ // E.g. Sequence<Sequence<sal_Int32>> , the index would refer to Sequence<sal_Int32>
+ // In this case arDimSeqIndices would have the size 1. That is the elements are not counted
+ // but the Sequences that contain those elements.
+ // The indices are 0 based
+ std::unique_ptr<sal_Int32[]> sarDimsSeqIndices;
+ sal_Int32* arDimsSeqIndices= nullptr;
+ if( dimsSeq > 0)
+ {
+ sarDimsSeqIndices.reset(new sal_Int32[dimsSeq]);
+ arDimsSeqIndices = sarDimsSeqIndices.get();
+ memset( arDimsSeqIndices, 0, sizeof( sal_Int32 ) * dimsSeq);
+ }
+
+ char* psaCurrentData= static_cast<char*>(pSAData);
+
+ do
+ {
+ // Get the Sequence at the current index , see arDimsSeqIndices
+ uno_Sequence * pCurrentSeq= pMultiSeq;
+ sal_Int32 curDim=1; // 1 based
+ bool skipSeq= false;
+ while( curDim <= dimsSeq )
+ {
+ // get the Sequence at the index if valid
+ if( pCurrentSeq->nElements > arDimsSeqIndices[ curDim - 1] ) // don't point to Nirvana
+ {
+ // size of Sequence is 4
+ sal_Int32 offset= arDimsSeqIndices[ curDim - 1] * 4;
+ pCurrentSeq= *reinterpret_cast<uno_Sequence**>(&pCurrentSeq->elements[ offset]);
+ curDim++;
+ }
+ else
+ {
+ // There is no Sequence at this index, so skip this index
+ skipSeq= true;
+ break;
+ }
+ }
+
+ if( skipSeq)
+ continue;
+
+ // Calculate the current position within the datablock of the SAFEARRAY
+ // for the next Sequence.
+ sal_Int32 memOffset= 0;
+ sal_Int32 dimWeight= parElementCount[ dims - 1]; // size of the rightmost dimension
+ for(sal_Int32 idims=0; idims < dimsSeq; idims++ )
+ {
+ memOffset+= arDimsSeqIndices[dimsSeq - 1 - idims] * dimWeight;
+ // now determine the weight of the dimension to the left of the current.
+ if( dims - 2 - idims >=0)
+ dimWeight*= parElementCount[dims - 2 - idims];
+ }
+ psaCurrentData= static_cast<char*>(pSAData) + memOffset * oleElementSize;
+ // convert the Sequence and put the elements into the Safearray
+ for( sal_Int32 i= 0; i < pCurrentSeq->nElements; i++)
+ {
+ Any unoElement( pCurrentSeq->elements + i * elementSize, rawTypeDesc );
+ // The any is being converted into a VARIANT which value is then copied
+ // to the SAFEARRAY's data block. When copying one has to follow the rules for
+ // copying certain types, as are VT_DISPATCH, VT_UNKNOWN, VT_VARIANT, VT_BSTR.
+ // To increase performance, we just do a memcpy of VARIANT::byref. This is possible
+ // because anyToVariant has already followed the copying rules. To make this
+ // work there must not be a VariantClear.
+ // One Exception is VARIANT because I don't know how VariantCopy works.
+
+ VARIANT var;
+ VariantInit( &var);
+ anyToVariant( &var, unoElement);
+ if( elemtype == VT_VARIANT )
+ {
+ VariantCopy( reinterpret_cast<VARIANT*>(psaCurrentData), &var);
+ VariantClear( &var);
+ }
+ else
+ memcpy( psaCurrentData, &var.byref, oleElementSize);
+
+ psaCurrentData+= oleElementSize;
+ }
+ }
+ while( incrementMultidimensionalIndex( dimsSeq, parElementCount, arDimsSeqIndices));
+
+ SafeArrayUnaccessData( pArray);
+ }
+ }
+ }
+ return pArray;
+}
+
+// Increments a multi dimensional index.
+// Returns true as long as the index has been successfully incremented, false otherwise.
+// False is also returned if an overflow of the most significant dimension occurs. E.g.
+// assume an array with the dimensions (2,2), then the lowest index is (0,0) and the highest
+// index is (1,1). If the function is being called with the index (1,1) then the overflow would
+// occur, with the result (0,0) and a sal_False as return value.
+// Param dimensions - number of dimensions
+// Param parDimensionsLength - The array contains the size of each dimension, that is the
+// size of the array equals the parameter dimensions.
+// The rightmost dimensions is the least significant one
+// ( parDimensionsLengths[ dimensions -1 ] ).
+// Param parMultiDimensionalIndex - The array contains the index. Each dimension index is
+// 0 based.
+template<class T>
+bool UnoConversionUtilities<T>::incrementMultidimensionalIndex(sal_Int32 dimensions,
+ const sal_Int32 * parDimensionLengths,
+ sal_Int32 * parMultidimensionalIndex)
+{
+ if( dimensions < 1)
+ return false;
+
+ bool ret= true;
+ bool carry= true; // to get into the while loop
+
+ sal_Int32 currentDimension= dimensions; //most significant is 1
+ while( carry)
+ {
+ parMultidimensionalIndex[ currentDimension - 1]++;
+ // if carryover, set index to 0 and handle carry on a level above
+ if( parMultidimensionalIndex[ currentDimension - 1] > (parDimensionLengths[ currentDimension - 1] - 1))
+ parMultidimensionalIndex[ currentDimension - 1]= 0;
+ else
+ carry= false;
+
+ currentDimension --;
+ // if dimensions drops below 1 and carry is set than then all indices are 0 again
+ // this is signalled by returning sal_False
+ if( currentDimension < 1 && carry)
+ {
+ carry= false;
+ ret= false;
+ }
+ }
+ return ret;
+}
+
+// Determines the size of a certain OLE type. The function takes
+// only those types into account which are oleautomation types and
+// can have a value ( unless VT_NULL, VT_EMPTY, VT_ARRAY, VT_BYREF).
+// Currently used in createUnoSequenceWrapper to calculate addresses
+// for data within a SAFEARRAY.
+template<class T>
+size_t UnoConversionUtilities<T>::getOleElementSize( VARTYPE type)
+{
+ size_t size;
+ switch( type)
+ {
+ case VT_BOOL: size= sizeof( VARIANT_BOOL);break;
+ case VT_UI1: size= sizeof( unsigned char);break;
+ case VT_R8: size= sizeof( double);break;
+ case VT_R4: size= sizeof( float);break;
+ case VT_I2: size= sizeof( short);break;
+ case VT_I4: size= sizeof( long);break;
+ case VT_BSTR: size= sizeof( BSTR); break;
+ case VT_ERROR: size= sizeof( SCODE); break;
+ case VT_DISPATCH:
+ case VT_UNKNOWN: size= sizeof( IUnknown*); break;
+ case VT_VARIANT: size= sizeof( VARIANT);break;
+ default: size= 0;
+ }
+ return size;
+}
+
+//If a Sequence is being converted into a SAFEARRAY then we possibly have
+// to create a SAFEARRAY with multiple dimensions. This is the case when a
+// Sequence contains Sequences ( Sequence< Sequence < XXX > > ). The leftmost
+// Sequence in the declaration is assumed to represent dimension 1. Because
+// all Sequence elements of a Sequence can have different length, we have to
+// determine the maximum length which is then the length of the respective
+// dimension.
+// getElementCountAndTypeOfSequence determines the length of each dimension and calls itself recursively
+// in the process.
+// param rSeq - an Any that has to contain a Sequence
+// param dim - the dimension for which the number of elements is being determined,
+// must be one.
+// param seqElementCounts - contains the maximum number of elements for each
+// dimension. Index 0 contains the number of dimension one.
+// After return the Sequence contains the maximum number of
+// elements for each dimension.
+// The length of the Sequence must equal the number of dimensions.
+// param typeClass - TypeClass of the element type that is no Sequence, e.g.
+// Sequence< Sequence <Sequence <sal_Int32> > > - type is sal_Int32)
+template<class T>
+void UnoConversionUtilities<T>::getElementCountAndTypeOfSequence( const Any& rSeq, sal_Int32 dim,
+ Sequence< sal_Int32 >& seqElementCounts, TypeDescription& typeDesc)
+{
+ sal_Int32 dimCount= (*static_cast<uno_Sequence* const *>(rSeq.getValue()))->nElements;
+ if( dimCount > seqElementCounts[ dim-1])
+ seqElementCounts.getArray()[ dim-1]= dimCount;
+
+ // we need the element type to construct the any that is
+ // passed into getElementCountAndTypeOfSequence again
+ typelib_TypeDescription* pSeqDesc= nullptr;
+ rSeq.getValueTypeDescription( &pSeqDesc);
+ typelib_TypeDescriptionReference* pElementDescRef= reinterpret_cast<typelib_IndirectTypeDescription*>(pSeqDesc)->pType;
+
+ // if the elements are Sequences then do recursion
+ if( dim < seqElementCounts.getLength() )
+ {
+ uno_Sequence* pSeq = *static_cast<uno_Sequence* const*>(rSeq.getValue());
+ uno_Sequence** arSequences= reinterpret_cast<uno_Sequence**>(pSeq->elements);
+ for( sal_Int32 i=0; i < dimCount; i++)
+ {
+ uno_Sequence* arElement= arSequences[ i];
+ getElementCountAndTypeOfSequence( Any( &arElement, pElementDescRef), dim + 1 , seqElementCounts, typeDesc);
+ }
+ }
+ else
+ {
+ // determine the element type ( e.g. Sequence< Sequence <Sequence <sal_Int32> > > - type is sal_Int32)
+ typeDesc= pElementDescRef;
+ }
+ typelib_typedescription_release( pSeqDesc);
+}
+
+
+template<class T>
+SAFEARRAY* UnoConversionUtilities<T>::createUnoSequenceWrapper(const Any& rSeq)
+{
+ SAFEARRAY* pArray = nullptr;
+ sal_uInt32 n = 0;
+
+ if( rSeq.getValueTypeClass() != TypeClass_SEQUENCE )
+ throw IllegalArgumentException(
+ "[automation bridge]UnoConversionUtilities<T>::createUnoSequenceWrapper\n"
+ "The UNO argument is not a sequence", nullptr, -1);
+
+ uno_Sequence * punoSeq= *static_cast<uno_Sequence* const *>(rSeq.getValue());
+
+ typelib_TypeDescriptionReference* pSeqTypeRef= rSeq.getValueTypeRef();
+ typelib_TypeDescription* pSeqType= nullptr;
+ TYPELIB_DANGER_GET( &pSeqType, pSeqTypeRef);
+ typelib_IndirectTypeDescription * pSeqIndDec= reinterpret_cast<typelib_IndirectTypeDescription*>(pSeqType);
+
+
+ typelib_TypeDescriptionReference * pSeqElementTypeRef= pSeqIndDec->pType;
+ TYPELIB_DANGER_RELEASE( pSeqType);
+
+ typelib_TypeDescription* pSeqElementDesc= nullptr;
+ TYPELIB_DANGER_GET( &pSeqElementDesc, pSeqElementTypeRef);
+ sal_Int32 nElementSize= pSeqElementDesc->nSize;
+ n= punoSeq->nElements;
+
+ SAFEARRAYBOUND rgsabound[1];
+ rgsabound[0].lLbound = 0;
+ rgsabound[0].cElements = n;
+ VARIANT oleElement;
+ LONG safeI[1];
+
+ pArray = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
+
+ Any unoElement;
+ char * pSeqData= punoSeq->elements;
+
+ for (sal_uInt32 i = 0; i < n; i++)
+ {
+ unoElement.setValue( pSeqData + i * nElementSize, pSeqElementDesc);
+ VariantInit(&oleElement);
+
+ anyToVariant(&oleElement, unoElement);
+
+ safeI[0] = i;
+ SafeArrayPutElement(pArray, safeI, &oleElement);
+
+ VariantClear(&oleElement);
+ }
+ TYPELIB_DANGER_RELEASE( pSeqElementDesc);
+
+ return pArray;
+}
+
+/* The argument rObj can contain
+- UNO struct
+- UNO interface
+- UNO interface created by this bridge (adapter factory)
+- UNO interface created by this bridge ( COM Wrapper)
+
+pVar must be initialized.
+*/
+template<class T>
+void UnoConversionUtilities<T>::createUnoObjectWrapper(const Any & rObj, VARIANT * pVar)
+{
+ MutexGuard guard(getBridgeMutex());
+
+ Reference<XInterface> xInt;
+
+ TypeClass tc = rObj.getValueTypeClass();
+ if (tc != TypeClass_INTERFACE && tc != TypeClass_STRUCT)
+ throw IllegalArgumentException(
+ "[automation bridge]UnoConversionUtilities<T>::createUnoObjectWrapper \n"
+ "Cannot create an Automation interface for a UNO type which is not "
+ "a struct or interface!", nullptr, -1);
+
+ if (rObj.getValueTypeClass() == TypeClass_INTERFACE)
+ {
+ if (! (rObj >>= xInt))
+ throw IllegalArgumentException(
+ "[automation bridge] UnoConversionUtilities<T>::createUnoObjectWrapper\n "
+ "Could not create wrapper object for UNO object!", nullptr, -1);
+ //If XInterface is NULL, which is a valid value, then simply return NULL.
+ if ( ! xInt.is())
+ {
+ pVar->vt = VT_UNKNOWN;
+ pVar->punkVal = nullptr;
+ return;
+ }
+ //make sure we have the main XInterface which is used with a map
+ xInt.set(xInt, UNO_QUERY);
+ //If there is already a wrapper for the UNO object then use it
+
+ Reference<XInterface> xIntWrapper;
+ // Does a UNO wrapper exist already ?
+ auto it_uno = UnoObjToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(xInt.get()));
+ if(it_uno != UnoObjToWrapperMap.end())
+ {
+ xIntWrapper = it_uno->second;
+ if (xIntWrapper.is())
+ {
+ convertSelfToCom(xIntWrapper, pVar);
+ return;
+ }
+ }
+ // Is the object a COM wrapper ( either XInvocation, or Adapter object)
+ // or does it supply an IDispatch by its own ?
+ else
+ {
+ Reference<XInterface> xIntComWrapper = xInt;
+
+ // Adapter? then get the COM wrapper to which the adapter delegates its calls
+ auto it = AdapterToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(xInt.get()));
+ if( it != AdapterToWrapperMap.end() )
+ xIntComWrapper= reinterpret_cast<XInterface*>(it->second);
+
+ if (convertSelfToCom(xIntComWrapper, pVar))
+ return;
+ }
+ }
+ // If we have no UNO wrapper nor the IDispatch yet then we have to create
+ // a wrapper. For that we need an XInvocation.
+
+ // create an XInvocation using the invocation service
+ Reference<XInvocation> xInv;
+ Reference<XSingleServiceFactory> xInvFactory= getInvocationFactory(rObj);
+ if (xInvFactory.is())
+ {
+ Sequence<Any> params(2);
+ params.getArray()[0] = rObj;
+ params.getArray()[1] <<= OUString("FromOLE");
+ Reference<XInterface> xInt2 = xInvFactory->createInstanceWithArguments(params);
+ xInv.set(xInt2, UNO_QUERY);
+ }
+
+ if (xInv.is())
+ {
+ Reference<XInterface> xNewWrapper = createUnoWrapperInstance();
+ Reference<css::lang::XInitialization> xInitWrapper(xNewWrapper, UNO_QUERY);
+ if (xInitWrapper.is())
+ {
+ VARTYPE vartype= getVarType( rObj);
+
+ if (xInt.is())
+ {
+ Any params[3];
+ params[0] <<= xInv;
+ params[1] <<= xInt;
+ params[2] <<= vartype;
+ xInitWrapper->initialize( Sequence<Any>(params, 3));
+ }
+ else
+ {
+ Any params[2];
+ params[0] <<= xInv;
+ params[1] <<= vartype;
+ xInitWrapper->initialize( Sequence<Any>(params, 2));
+ }
+
+ // put the newly created object into a map. If the same object will
+ // be mapped again and there is already a wrapper then the old wrapper
+ // will be used.
+ if(xInt.is()) // only interfaces
+ UnoObjToWrapperMap[reinterpret_cast<sal_uIntPtr>(xInt.get())]= xNewWrapper;
+ convertSelfToCom(xNewWrapper, pVar);
+ return;
+ }
+ }
+}
+
+template<class T>
+void UnoConversionUtilities<T>::variantToAny( const VARIANT* pVariant, Any& rAny,
+ bool bReduceValueRange /* = sal_True */)
+{
+ HRESULT hr = S_OK;
+ try
+ {
+ CComVariant var;
+
+ // There is no need to support indirect values, since they're not supported by UNO
+ if( FAILED(hr= VariantCopyInd( &var, pVariant))) // remove VT_BYREF
+ throw BridgeRuntimeError(
+ "[automation bridge] UnoConversionUtilities<T>::variantToAny \n"
+ "VariantCopyInd failed for reason : " + OUString::number(hr));
+
+ if ( ! convertValueObject( & var, rAny))
+ {
+ if ((var.vt & VT_ARRAY) > 0)
+ {
+ VARTYPE oleTypeFlags = ::sal::static_int_cast< VARTYPE, int >( var.vt ^ VT_ARRAY );
+
+ Sequence<Any> unoSeq = createOleArrayWrapper(var.parray, oleTypeFlags);
+ rAny.setValue( &unoSeq, cppu::UnoType<decltype(unoSeq)>::get());
+ }
+ else
+ {
+ switch (var.vt)
+ {
+ case VT_EMPTY:
+ rAny.setValue(nullptr, Type());
+ break;
+ case VT_NULL:
+ rAny.setValue(nullptr, Type());
+ break;
+ case VT_I2:
+ rAny.setValue( & var.iVal, cppu::UnoType<sal_Int16>::get());
+ break;
+ case VT_I4:
+ rAny.setValue( & var.lVal, cppu::UnoType<sal_Int32>::get());
+ // necessary for use in JavaScript ( see "reduceRange")
+ if( bReduceValueRange)
+ reduceRange(rAny);
+ break;
+ case VT_R4:
+ rAny.setValue( & var.fltVal, cppu::UnoType<float>::get());
+ break;
+ case VT_R8:
+ rAny.setValue(& var.dblVal, cppu::UnoType<double>::get());
+ break;
+ case VT_CY:
+ {
+ Currency cy(var.cyVal.int64);
+ rAny <<= cy;
+ break;
+ }
+ case VT_DATE:
+ {
+ Date d(var.date);
+ rAny <<= d;
+ break;
+ }
+ case VT_BSTR:
+ {
+ OUString b(o3tl::toU(var.bstrVal));
+ rAny.setValue( &b, cppu::UnoType<decltype(b)>::get());
+ break;
+ }
+ case VT_UNKNOWN:
+ case VT_DISPATCH:
+ {
+ //check if it is a UNO type
+ CComQIPtr<IUnoTypeWrapper> spType(static_cast<IUnknown*>(var.byref));
+ if (spType)
+ {
+ CComBSTR sName;
+ if (FAILED(spType->get_Name(&sName)))
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::variantToAny \n"
+ "Failed to get the type name from a UnoTypeWrapper!");
+ Type type;
+ if (!getType(sName, type))
+ {
+ throw CannotConvertException(
+ OUString::Concat("[automation bridge]UnoConversionUtilities<T>::variantToAny \n"
+ "A UNO type with the name: ") + o3tl::toU(LPCOLESTR(sName)) +
+ "does not exist!",
+ nullptr, TypeClass_UNKNOWN, FailReason::TYPE_NOT_SUPPORTED,0);
+ }
+ rAny <<= type;
+ }
+ else
+ {
+ rAny = createOleObjectWrapper( & var);
+ }
+ break;
+ }
+ case VT_ERROR:
+ {
+ SCode scode(var.scode);
+ rAny <<= scode;
+ break;
+ }
+ case VT_BOOL:
+ {
+ rAny <<= (var.boolVal == VARIANT_TRUE);
+ break;
+ }
+ case VT_I1:
+ rAny.setValue( & var.cVal, cppu::UnoType<sal_Int8>::get());
+ break;
+ case VT_UI1: // there is no unsigned char in UNO
+ rAny <<= sal_Int8(var.bVal);
+ break;
+ case VT_UI2:
+ rAny.setValue( & var.uiVal, cppu::UnoType<cppu::UnoUnsignedShortType>::get() );
+ break;
+ case VT_UI4:
+ rAny.setValue( & var.ulVal, cppu::UnoType<sal_uInt32>::get());
+ break;
+ case VT_INT:
+ rAny.setValue( & var.intVal, cppu::UnoType<sal_Int32>::get());
+ break;
+ case VT_UINT:
+ rAny.setValue( & var.uintVal, cppu::UnoType<sal_uInt32>::get());
+ break;
+ case VT_VOID:
+ rAny.setValue( nullptr, Type());
+ break;
+ case VT_DECIMAL:
+ {
+ Decimal dec;
+ dec.Scale = var.decVal.scale;
+ dec.Sign = var.decVal.sign;
+ dec.LowValue = var.decVal.Lo32;
+ dec.MiddleValue = var.decVal.Mid32;
+ dec.HighValue = var.decVal.Hi32;
+ rAny <<= dec;
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+ catch (const IllegalArgumentException &)
+ {
+ throw;
+ }
+ catch (const CannotConvertException &)
+ {
+ throw;
+ }
+ catch (const BridgeRuntimeError &)
+ {
+ throw;
+ }
+ catch (const Exception & e)
+ {
+ throw BridgeRuntimeError("[automation bridge] unexpected exception in "
+ "UnoConversionUtilities<T>::variantToAny ! Message : \n" +
+ e.Message);
+ }
+ catch(...)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge] unexpected exception in "
+ "UnoConversionUtilities<T>::variantToAny !");
+ }
+
+}
+// The function converts an IUnknown* into a UNO interface or struct. The
+// IUnknown pointer can constitute different kind of objects:
+// 1. a wrapper of a UNO struct (the wrapper was created by this bridge)
+// 2. a wrapper of a UNO interface (created by this bridge)
+// 3. a dispatch object that implements UNO interfaces
+// 4. a dispatch object.
+
+// If the parameter "aType" has a value then the COM object ( pUnknown) is supposed to
+// implement the interface described by "aType". Moreover it ( pUnknown) can implement
+// several other
+// UNO interfaces in which case it has to support the SUPPORTED_INTERFACES_PROP (see
+// #define) property. That property contains all names of interfaces.
+// "pUnknown" is wrapped by a COM wrapper object that implements XInvocation, e.g.
+// IUnknownWrapper. Additionally an object of type "aType" is created by help
+// of the INTERFACE_ADAPTER_FACTORY (see #define) service. The implementation of
+// "aType" calls on the COM wrapper's XInvocation::invoke. If the COM object supports
+// more than one UNO interfaces, as can be determined by the property
+// SUPPORTED_INTERFACES_PROP, then the INTERFACE_ADAPTER_FACTORY creates an object that
+// implements all these interfaces.
+// This is only done if "pUnknown" is not already a UNO wrapper,
+// that is it is actually NOT a UNO object that was converted to a COM object. If it is an
+// UNO wrapper than the original UNO object is being extracted, queried for "aType" (if
+// it is no struct) and returned.
+template<class T>
+Any UnoConversionUtilities<T>::createOleObjectWrapper(VARIANT* pVar, const Type& aType)
+{
+ //To allow passing "Nothing" in VS 2008 we need to accept VT_EMPTY
+ if (pVar->vt != VT_UNKNOWN && pVar->vt != VT_DISPATCH && pVar->vt != VT_EMPTY)
+ throw IllegalArgumentException(
+ "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n"
+ "The VARIANT does not contain an object type! ", nullptr, -1);
+
+ MutexGuard guard( getBridgeMutex());
+
+ CComPtr<IUnknown> spUnknown;
+ CComPtr<IDispatch> spDispatch;
+
+ if (pVar->vt == VT_UNKNOWN)
+ {
+ spUnknown = pVar->punkVal;
+ if (spUnknown)
+ spUnknown.QueryInterface( & spDispatch.p);
+ }
+ else if (pVar->vt == VT_DISPATCH && pVar->pdispVal != nullptr)
+ {
+ CComPtr<IDispatch> spDispatch2(pVar->pdispVal);
+ if (spDispatch2)
+ spDispatch2.QueryInterface( & spUnknown.p);
+ }
+
+ static Type VOID_TYPE;
+ Any ret;
+ //If no Type is provided and pVar contains IUnknown then we return a XInterface.
+ //If pVar contains an IDispatch then we return a XInvocation.
+ Type desiredType = aType;
+
+ if (aType == VOID_TYPE)
+ {
+ switch (pVar->vt)
+ {
+ case VT_EMPTY:
+ case VT_UNKNOWN:
+ desiredType = cppu::UnoType<XInterface>::get();
+ break;
+ case VT_DISPATCH:
+ desiredType = cppu::UnoType<XInvocation>::get();
+ break;
+ default:
+ desiredType = aType;
+ }
+ }
+
+ // COM pointer are NULL, no wrapper required
+ if (spUnknown == nullptr)
+ {
+ Reference<XInterface> xInt;
+ if( aType.getTypeClass() == TypeClass_INTERFACE)
+ ret.setValue( &xInt, aType);
+ else if( aType.getTypeClass() == TypeClass_STRUCT)
+ ret.setValue( nullptr, aType);
+ else
+ ret <<= xInt;
+ return ret;
+ }
+
+
+ // Check if "spUnknown" is a UNO wrapper, that is a UNO object that has been
+ // passed to COM. Then it supports IUnoObjectWrapper
+ // and we extract the original UNO object.
+ CComQIPtr<IUnoObjectWrapper> spUno( spUnknown);
+ if( spUno)
+ { // it is a wrapper
+ Reference<XInterface> xInt;
+ if( SUCCEEDED( spUno->getOriginalUnoObject( &xInt)))
+ {
+ ret <<= xInt;
+ }
+ else
+ {
+ Any any;
+ if( SUCCEEDED( spUno->getOriginalUnoStruct(&any)))
+ ret= any;
+ }
+ return ret;
+ }
+
+ // "spUnknown" is a real COM object.
+ // Before we create a new wrapper object we check if there is an existing wrapper
+ // There can be two kinds of wrappers, those who wrap dispatch - UNO objects, and those who
+ // wrap ordinary dispatch objects. The dispatch-UNO objects usually are adapted to represent
+ // particular UNO interfaces.
+ Reference<XInterface> xIntWrapper;
+ auto cit_currWrapper= ComPtrToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(spUnknown.p));
+ if(cit_currWrapper != ComPtrToWrapperMap.end())
+ xIntWrapper = cit_currWrapper->second;
+ if (xIntWrapper.is())
+ {
+ //Try to find an adapter for the wrapper
+ //find the proper Adapter. The pointer in the WrapperToAdapterMap are valid as long as
+ //we get a pointer to the wrapper from ComPtrToWrapperMap, because the Adapter hold references
+ //to the wrapper.
+ auto it = WrapperToAdapterMap.find(reinterpret_cast<sal_uIntPtr>(xIntWrapper.get()));
+ if (it == WrapperToAdapterMap.end())
+ {
+ // No adapter available.
+ //The COM component could be a UNO object. Then we need to provide
+ // a proxy that implements all interfaces
+ Sequence<Type> seqTypes= getImplementedInterfaces(spUnknown);
+ Reference<XInterface> xIntAdapter;
+ if (seqTypes.getLength() > 0)
+ {
+ //It is a COM UNO object
+ xIntAdapter = createAdapter(seqTypes, xIntWrapper);
+ }
+ else
+ {
+ // Some ordinary COM object
+ xIntAdapter = xIntWrapper;
+ }
+ // return the wrapper directly, return XInterface or XInvocation
+ ret = xIntWrapper->queryInterface(desiredType);
+ if ( ! ret.hasValue())
+ throw IllegalArgumentException(
+ "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n"
+ "The COM object is not suitable for the UNO type: " +
+ desiredType.getTypeName(), nullptr, -1);
+ }
+ else
+ {
+ //There is an adapter available
+ Reference<XInterface> xIntAdapter(reinterpret_cast<XInterface*>(it->second));
+ ret = xIntAdapter->queryInterface( desiredType);
+ if ( ! ret.hasValue())
+ throw IllegalArgumentException(
+ "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n"
+ "The COM object is not suitable for the UNO type: " +
+ desiredType.getTypeName(), nullptr, -1);
+ }
+
+ return ret;
+ }
+ // No existing wrapper. Therefore create a new proxy.
+ // If the object implements UNO interfaces then get the types.
+ Sequence<Type> seqTypes = getImplementedInterfaces(spUnknown);
+ if (seqTypes.getLength() == 0 &&
+ aType != VOID_TYPE && aType != cppu::UnoType<XInvocation>::get())
+ {
+ seqTypes = Sequence<Type>( & aType, 1);
+ }
+
+ //There is no existing wrapper, therefore we create one for the real COM object
+ Reference<XInterface> xIntNewProxy= createComWrapperInstance();
+ if ( ! xIntNewProxy.is())
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n"
+ "Could not create proxy object for COM object!");
+
+ // initialize the COM wrapper
+ Reference<XInitialization> xInit( xIntNewProxy, UNO_QUERY);
+ OSL_ASSERT( xInit.is());
+
+ Any params[3];
+ params[0] <<= reinterpret_cast<sal_uIntPtr>(spUnknown.p);
+ params[1] <<= (pVar->vt == VT_DISPATCH);
+ params[2] <<= seqTypes;
+
+ xInit->initialize( Sequence<Any>( params, 3));
+ ComPtrToWrapperMap[reinterpret_cast<sal_uInt64>(spUnknown.p)] = xIntNewProxy;
+
+ // we have a wrapper object
+ //The wrapper implements already XInvocation and XInterface. If
+ //param aType is void then the object is supposed to have XInvocation.
+ if (aType == cppu::UnoType<XInvocation>::get()||
+ (aType == VOID_TYPE && seqTypes.getLength() == 0 ))
+ {
+ ret = xIntNewProxy->queryInterface(desiredType);
+ }
+ else
+ {
+ Reference<XInterface> xIntAdapter =
+ createAdapter(seqTypes, xIntNewProxy);
+ ret = xIntAdapter->queryInterface(desiredType);
+ }
+ return ret;
+}
+template<class T>
+Reference<XInterface> UnoConversionUtilities<T>::createAdapter(const Sequence<Type>& seqTypes,
+ const Reference<XInterface>& receiver)
+{
+ Reference< XInterface> xIntAdapterFac;
+ xIntAdapterFac= m_smgr->createInstance(INTERFACE_ADAPTER_FACTORY);
+ // We create an adapter object that does not only implement the required type but also
+ // all types that the COM object pretends to implement. A COM object must therefore
+ // support the property "_implementedInterfaces".
+ Reference<XInterface> xIntAdapted;
+ Reference<XInvocation> xInv(receiver, UNO_QUERY);
+ Reference<XInvocationAdapterFactory2> xAdapterFac( xIntAdapterFac, UNO_QUERY);
+ if( xAdapterFac.is())
+ xIntAdapted= xAdapterFac->createAdapter( xInv, seqTypes);
+
+ if( !xIntAdapted.is())
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge]UnoConversionUtilities<T>::createOleObjectWrapper \n"
+ "Could not create a proxy for COM object! Creation of adapter failed.");
+ }
+
+ // Put the pointer to the wrapper object and the interface pointer of the adapted interface
+ // in a global map. Thus we can determine in a call to createUnoObjectWrapper whether the UNO
+ // object is a wrapped COM object. In that case we extract the original COM object rather than
+ // creating a wrapper around the UNO object.
+ typedef std::unordered_map<sal_uInt64,sal_uInt64>::value_type VALUE;
+ AdapterToWrapperMap.insert( VALUE( reinterpret_cast<sal_uInt64>(xIntAdapted.get()), reinterpret_cast<sal_uInt64>(receiver.get())));
+ WrapperToAdapterMap.insert( VALUE( reinterpret_cast<sal_uInt64>(receiver.get()), reinterpret_cast<sal_uInt64>(xIntAdapted.get())));
+
+ return xIntAdapted;
+}
+// "convertValueObject" converts a JScriptValue object contained in "var" into
+// an any. The type contained in the any is stipulated by a "type value" thas
+// was set within the JScript script on the value object ( see JScriptValue).
+template<class T>
+bool UnoConversionUtilities<T>::convertValueObject( const VARIANTARG *var, Any& any)
+{
+ bool ret = false;
+ try
+ {
+ bool bFail = false;
+ HRESULT hr= S_OK;
+ CComVariant varDisp;
+
+ if(SUCCEEDED(hr = varDisp.ChangeType( VT_DISPATCH, var)))
+ {
+ CComPtr <IJScriptValueObject> spValue;
+ VARIANT_BOOL varBool;
+ CComBSTR bstrType;
+ CComVariant varValue;
+ CComPtr<IDispatch> spDisp( varDisp.pdispVal);
+ if(spDisp)
+ {
+ if(SUCCEEDED( spDisp->QueryInterface( __uuidof( IJScriptValueObject),
+ reinterpret_cast<void**> (&spValue))))
+ {
+ ret = true; // is a ValueObject
+ //If it is an out - param then it does not need to be converted. In/out and
+ // in params does so.
+ if (SUCCEEDED(hr= spValue->IsOutParam( &varBool)))
+ {
+ // if varBool == true then no conversion needed because out param
+ if (varBool == VARIANT_FALSE)
+ {
+ if(SUCCEEDED(hr = spValue->GetValue( & bstrType, & varValue)))
+ {
+ Type type;
+ if (getType(bstrType, type))
+ variantToAny( & varValue, any, type);
+ else
+ bFail = true;
+ }
+ else
+ bFail = true;
+ }
+ }
+ else
+ bFail = true;
+ }
+ }
+ }
+ else if( hr != DISP_E_TYPEMISMATCH && hr != E_NOINTERFACE)
+ bFail = true;
+
+ if (bFail)
+ throw BridgeRuntimeError(
+ "[automation bridge] Conversion of ValueObject failed ");
+ }
+ catch (const BridgeRuntimeError &)
+ {
+ throw;
+ }
+ catch (const Exception & e)
+ {
+ throw BridgeRuntimeError("[automation bridge] unexpected exception in "
+ "UnoConversionUtilities<T>::convertValueObject ! Message : \n" +
+ e.Message);
+ }
+ catch(...)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge] unexpected exception in "
+ "UnoConversionUtilities<T>::convertValueObject !");
+ }
+ return ret;
+}
+
+template<class T>
+void UnoConversionUtilities<T>::dispatchExObject2Sequence( const VARIANTARG* pvar, Any& anySeq, const Type& type)
+{
+ try
+ {
+ if( pvar->vt != VT_DISPATCH)
+ throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n"
+ "Conversion of dispatch object to Sequence failed!");
+ IDispatchEx* pdispEx;
+ HRESULT hr;
+ if( FAILED( hr= pvar->pdispVal->QueryInterface( IID_IDispatchEx,
+ reinterpret_cast<void**>( &pdispEx))))
+ throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n"
+ "Conversion of dispatch object to Sequence failed!");
+
+ DISPID dispid;
+ DISPPARAMS param= {nullptr,nullptr,0,0};
+ CComVariant result;
+
+ OLECHAR const * sLength= L"length";
+
+ // Get the length of the array. Can also be obtained through GetNextDispID. The
+ // method only returns DISPIDs of the array data. Their names are like "0", "1" etc.
+ if( FAILED( hr= pdispEx->GetIDsOfNames(IID_NULL, const_cast<OLECHAR **>(&sLength), 1, LOCALE_USER_DEFAULT, &dispid)))
+ throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n"
+ "Conversion of dispatch object to Sequence failed!");
+ if( FAILED( hr= pdispEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
+ &param, &result, nullptr, nullptr)))
+ throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n"
+ "Conversion of dispatch object to Sequence failed!");
+ if( FAILED( VariantChangeType( &result, &result, 0, VT_I4)))
+ throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n"
+ "Conversion of dispatch object to Sequence failed!");
+ LONG length= result.lVal;
+
+ result.Clear();
+
+ // get a few basic facts about the sequence, and reallocate:
+ // create the Sequences
+ // get the size of the elements
+ typelib_TypeDescription *pDesc= nullptr;
+ type.getDescription( &pDesc);
+
+ typelib_IndirectTypeDescription *pSeqDesc= reinterpret_cast<typelib_IndirectTypeDescription*>(pDesc);
+ typelib_TypeDescriptionReference *pSeqElemDescRef= pSeqDesc->pType; // type of the Sequence' elements
+ Type elemType( pSeqElemDescRef);
+ _typelib_TypeDescription* pSeqElemDesc=nullptr;
+ TYPELIB_DANGER_GET( &pSeqElemDesc, pSeqElemDescRef);
+ sal_uInt32 nelementSize= pSeqElemDesc->nSize;
+ TYPELIB_DANGER_RELEASE( pSeqElemDesc);
+
+ uno_Sequence *p_uno_Seq;
+ uno_sequence_construct( &p_uno_Seq, pDesc, nullptr, length, cpp_acquire);
+
+ typelib_TypeClass typeElement= pSeqDesc->pType->eTypeClass;
+ char *pArray= p_uno_Seq->elements;
+
+ // Get All properties in the object, convert their values to the expected type and
+ // put them into the passed in sequence
+ for( sal_Int32 i= 0; i< length; i++)
+ {
+ OUString ousIndex=OUString::number( i);
+ OLECHAR* sindex = const_cast<OLECHAR *>(o3tl::toW(ousIndex.getStr()));
+
+ if( FAILED( hr= pdispEx->GetIDsOfNames(IID_NULL, &sindex , 1, LOCALE_USER_DEFAULT, &dispid)))
+ {
+ throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n"
+ "Conversion of dispatch object to Sequence failed!");
+ }
+ if( FAILED( hr= pdispEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
+ &param, &result, nullptr, nullptr)))
+ {
+ throw BridgeRuntimeError("[automation bridge] UnoConversionUtilities<T>::dispatchExObject2Sequence \n"
+ "Conversion of dispatch object to Sequence failed!");
+ }
+
+ // If the result is VT_DISPATCH than the Sequence's element type could be Sequence
+ // Look that up in the CoreReflection to make clear.
+ // That requires a recursiv conversion
+ Any any;
+ // Destination address within the out-Sequence "anySeq" where to copy the next converted element
+ void* pDest= pArray + (i * nelementSize);
+
+ if( result.vt & VT_DISPATCH && typeElement == typelib_TypeClass_SEQUENCE)
+ {
+ variantToAny( &result, any, elemType, false);
+ // copy the converted VARIANT, that is a Sequence to the Sequence
+ uno_Sequence * p_unoSeq= *static_cast<uno_Sequence* const *>(any.getValue());
+ // just copy the pointer of the uno_Sequence
+ // nelementSize should be 4 !!!!
+ memcpy( pDest, &p_unoSeq, nelementSize);
+ osl_atomic_increment( &p_unoSeq->nRefCount);
+ }
+ else // Element type is no Sequence -> do one conversion
+ {
+ variantToAny( &result, any, elemType, false);
+ if( typeElement == typelib_TypeClass_ANY)
+ {
+ // copy the converted VARIANT to the Sequence
+ uno_type_assignData( pDest, pSeqElemDescRef , &any, pSeqElemDescRef,cpp_queryInterface,
+ cpp_acquire, cpp_release);
+ }
+ else
+ {
+ // type after conversion must be the element type of the sequence
+ OSL_ENSURE(any.getValueTypeClass() == css::uno::TypeClass(typeElement), "wrong conversion");
+ uno_type_assignData( pDest, pSeqElemDescRef,const_cast<void*>( any.getValue()), any.getValueTypeRef(),
+ cpp_queryInterface, cpp_acquire, cpp_release);
+ }
+ }
+ } // else
+ result.Clear();
+ anySeq.setValue( &p_uno_Seq, pDesc);
+ uno_destructData( &p_uno_Seq, pDesc, cpp_release);
+ typelib_typedescription_release( pDesc);
+ }
+ catch (const BridgeRuntimeError &)
+ {
+ throw;
+ }
+ catch (const Exception & e)
+ {
+ throw BridgeRuntimeError("[automation bridge] unexpected exception in "
+ "UnoConversionUtilities<T>::convertValueObject ! Message : \n" +
+ e.Message);
+ }
+ catch(...)
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge] unexpected exception in "
+ "UnoConversionUtilities<T>::convertValueObject !");
+ }
+}
+
+/* The argument unotype is the type that is expected by the currently called UNO function.
+ For example: []long, [][]long. If the function calls itself recursively then the element type
+ is passed on. For example a two dimensional SAFEARRAY of type VT_I4 is to be converted. Then
+ unotype has to be either void or [][]long. When the function calls itself recursively then
+ it passes the element type which is []long.
+*/
+template<class T>
+Sequence<Any> UnoConversionUtilities<T>::createOleArrayWrapperOfDim(SAFEARRAY* pArray,
+ unsigned int dimCount, unsigned int actDim, LONG* index, VARTYPE type, const Type& unotype)
+{
+ LONG lBound;
+ LONG uBound;
+ LONG nCountElements;
+
+ SafeArrayGetLBound(pArray, actDim, &lBound);
+ SafeArrayGetUBound(pArray, actDim, &uBound);
+ nCountElements= uBound - lBound +1;
+
+ Sequence<Any> anySeq(nCountElements);
+ Any* pUnoArray = anySeq.getArray();
+
+ for (index[actDim - 1] = lBound; index[actDim - 1] <= uBound; index[actDim - 1]++)
+ {
+ if (actDim > 1 )
+ {
+ Sequence<Any> element = createOleArrayWrapperOfDim(pArray, dimCount,
+ actDim - 1, index, type, getElementTypeOfSequence(unotype));
+
+ pUnoArray[index[actDim - 1] - lBound].setValue(&element, cppu::UnoType<decltype(element)>::get());
+ }
+ else
+ {
+ VARIANT variant;
+
+ VariantInit(&variant);
+
+ V_VT(&variant) = type;
+
+ switch (type)
+ {
+ case VT_I2:
+ SafeArrayGetElement(pArray, index, &V_I2(&variant));
+ break;
+ case VT_I4:
+ SafeArrayGetElement(pArray, index, &V_I4(&variant));
+ break;
+ case VT_R4:
+ SafeArrayGetElement(pArray, index, &V_R4(&variant));
+ break;
+ case VT_R8:
+ SafeArrayGetElement(pArray, index, &V_R8(&variant));
+ break;
+ case VT_CY:
+ SafeArrayGetElement(pArray, index, &V_CY(&variant));
+ break;
+ case VT_DATE:
+ SafeArrayGetElement(pArray, index, &V_DATE(&variant));
+ break;
+ case VT_BSTR:
+ SafeArrayGetElement(pArray, index, &V_BSTR(&variant));
+ break;
+ case VT_DISPATCH:
+ SafeArrayGetElement(pArray, index, &V_DISPATCH(&variant));
+ break;
+ case VT_ERROR:
+ SafeArrayGetElement(pArray, index, &V_ERROR(&variant));
+ break;
+ case VT_BOOL:
+ SafeArrayGetElement(pArray, index, &V_BOOL(&variant));
+ break;
+ case VT_VARIANT:
+ SafeArrayGetElement(pArray, index, &variant);
+ break;
+ case VT_UNKNOWN:
+ SafeArrayGetElement(pArray, index, &V_UNKNOWN(&variant));
+ break;
+ case VT_I1:
+ SafeArrayGetElement(pArray, index, &V_I1(&variant));
+ break;
+ case VT_UI1:
+ SafeArrayGetElement(pArray, index, &V_UI1(&variant));
+ break;
+ case VT_UI2:
+ SafeArrayGetElement(pArray, index, &V_UI2(&variant));
+ break;
+ case VT_UI4:
+ SafeArrayGetElement(pArray, index, &V_UI4(&variant));
+ break;
+ default:
+ break;
+ }
+
+ if( unotype.getTypeClass() == TypeClass_VOID)
+ // the function was called without specifying the destination type
+ variantToAny(&variant, pUnoArray[index[actDim - 1] - lBound], false);
+ else
+ variantToAny(&variant, pUnoArray[index[actDim - 1] - lBound],
+ getElementTypeOfSequence(unotype), false);
+
+ VariantClear(&variant);
+ }
+ }
+ return anySeq;
+}
+
+template<class T>
+Type UnoConversionUtilities<T>::getElementTypeOfSequence( const Type& seqType)
+{
+ Type retValue;
+ if( seqType.getTypeClass() != TypeClass_VOID)
+ {
+ OSL_ASSERT( seqType.getTypeClass() == TypeClass_SEQUENCE);
+ typelib_TypeDescription* pDescSeq= nullptr;
+ seqType.getDescription(& pDescSeq);
+ retValue = Type(reinterpret_cast<typelib_IndirectTypeDescription *>(pDescSeq)->pType);
+ typelib_typedescription_release(pDescSeq);
+ }
+ return retValue;
+}
+template<class T>
+Sequence<Any> UnoConversionUtilities<T>::createOleArrayWrapper(SAFEARRAY* pArray, VARTYPE type, const Type& unoType)
+{
+ sal_uInt32 dim = SafeArrayGetDim(pArray);
+
+ Sequence<Any> ret;
+
+ if (dim > 0)
+ {
+ std::unique_ptr<LONG[]> sarIndex(new LONG[dim]);
+ LONG * index = sarIndex.get();
+
+ for (unsigned int i = 0; i < dim; i++)
+ {
+ index[i] = 0;
+ }
+
+ ret = createOleArrayWrapperOfDim(pArray, dim, dim, index, type, unoType);
+ }
+
+ return ret;
+}
+
+// If a VARIANT has the type VT_DISPATCH it can either be a JScript Array
+// or some other object. This function finds out if it is such an array or
+// not. Currently there's no way to make sure it's an array
+// so we assume that when the object has a property "0" then it is an Array.
+// A JScript has property like "0", "1", "2" etc. which represent the
+// value at the corresponding index of the array
+template<class T>
+bool UnoConversionUtilities<T>::isJScriptArray(const VARIANT* rvar)
+{
+ OSL_ENSURE( rvar->vt == VT_DISPATCH, "param is not a VT_DISPATCH");
+ HRESULT hr;
+ OLECHAR const * sindex= L"0";
+ DISPID id;
+ if ( rvar->vt == VT_DISPATCH && rvar->pdispVal )
+ {
+ hr= rvar->pdispVal->GetIDsOfNames(
+ IID_NULL, const_cast<OLECHAR **>(&sindex), 1, LOCALE_USER_DEFAULT,
+ &id);
+
+ if( SUCCEEDED ( hr) )
+ return true;
+ }
+
+ return false;
+}
+
+template<class T>
+VARTYPE UnoConversionUtilities<T>::mapTypeClassToVartype( TypeClass type)
+{
+ VARTYPE ret;
+ switch( type)
+ {
+ case TypeClass_INTERFACE: ret= VT_DISPATCH;
+ break;
+ case TypeClass_STRUCT: ret= VT_DISPATCH;
+ break;
+ case TypeClass_ENUM: ret= VT_I4;
+ break;
+ case TypeClass_SEQUENCE: ret= VT_ARRAY;
+ break;
+ case TypeClass_ANY: ret= VT_VARIANT;
+ break;
+ case TypeClass_BOOLEAN: ret= VT_BOOL;
+ break;
+ case TypeClass_CHAR: ret= VT_I2;
+ break;
+ case TypeClass_STRING: ret= VT_BSTR;
+ break;
+ case TypeClass_FLOAT: ret= VT_R4;
+ break;
+ case TypeClass_DOUBLE: ret= VT_R8;
+ break;
+ case TypeClass_BYTE: ret= VT_UI1;
+ break;
+ case TypeClass_SHORT: ret= VT_I2;
+ break;
+ case TypeClass_LONG: ret= VT_I4;
+ break;
+ case TypeClass_UNSIGNED_SHORT: ret= VT_UI2;
+ break;
+ case TypeClass_UNSIGNED_LONG: ret= VT_UI4;
+ break;
+ default:
+ ret= VT_EMPTY;
+ }
+ return ret;
+}
+
+template<class T>
+Sequence<Type> UnoConversionUtilities<T>::getImplementedInterfaces(IUnknown* pUnk)
+{
+ Sequence<Type> seqTypes;
+ CComDispatchDriver disp( pUnk);
+ if( disp)
+ {
+ CComVariant var;
+ HRESULT hr= S_OK;
+ // There are two different property names possible.
+ if( FAILED( hr= disp.GetPropertyByName( SUPPORTED_INTERFACES_PROP, &var)))
+ {
+ hr= disp.GetPropertyByName( SUPPORTED_INTERFACES_PROP2, &var);
+ }
+ if (SUCCEEDED( hr))
+ {
+ // we expect an array( SafeArray or IDispatch) of Strings.
+ Any anyNames;
+ variantToAny( &var, anyNames, cppu::UnoType<Sequence<Any>>::get());
+ Sequence<Any> seqAny;
+ if( anyNames >>= seqAny)
+ {
+ seqTypes.realloc( seqAny.getLength());
+ auto pseqTypes = seqTypes.getArray();
+ for( sal_Int32 i=0; i < seqAny.getLength(); i++)
+ {
+ OUString typeName;
+ seqAny[i] >>= typeName;
+ pseqTypes[i]= Type( TypeClass_INTERFACE, typeName);
+ }
+ }
+ }
+ }
+ return seqTypes;
+}
+template<class T>
+Reference<XTypeConverter> UnoConversionUtilities<T>::getTypeConverter()
+{
+ if ( ! m_typeConverter.is())
+ {
+ MutexGuard guard(getBridgeMutex());
+ if ( ! m_typeConverter.is())
+ {
+ Reference<XInterface> xIntConverter =
+ m_smgr->createInstance("com.sun.star.script.Converter");
+ if (xIntConverter.is())
+ m_typeConverter.set(xIntConverter, UNO_QUERY);
+ }
+ }
+ return m_typeConverter;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/unoobjw.cxx b/extensions/source/ole/unoobjw.cxx
new file mode 100644
index 000000000..28286bddb
--- /dev/null
+++ b/extensions/source/ole/unoobjw.cxx
@@ -0,0 +1,3437 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// Documentation pointers for recent work:
+//
+// https://www.codeproject.com/Articles/9014/Understanding-COM-Event-Handling
+// https://blogs.msdn.microsoft.com/ericlippert/2005/02/15/why-does-wscript-connectobject-not-always-work/
+
+#include "ole2uno.hxx"
+
+#include <stdio.h>
+#include <list>
+#include <sstream>
+#include <unordered_map>
+#include <vector>
+
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wall"
+#pragma clang diagnostic ignored "-Wattributes"
+#pragma clang diagnostic ignored "-Wdelete-incomplete"
+#pragma clang diagnostic ignored "-Wdynamic-class-memaccess"
+#pragma clang diagnostic ignored "-Wextra"
+#pragma clang diagnostic ignored "-Wint-to-pointer-cast"
+#pragma clang diagnostic ignored "-Winvalid-noreturn"
+#pragma clang diagnostic ignored "-Wmicrosoft"
+#pragma clang diagnostic ignored "-Wnon-pod-varargs"
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#pragma clang diagnostic ignored "-Wnonportable-include-path"
+#pragma clang diagnostic ignored "-Wsequence-point"
+#pragma clang diagnostic ignored "-Wtypename-missing"
+#endif
+#include <atlbase.h>
+#include <atlcom.h>
+#if defined _MSC_VER && defined __clang__
+#pragma clang diagnostic pop
+#endif
+#include <comdef.h>
+
+#include <osl/diagnose.h>
+#include <salhelper/simplereferenceobject.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/beans/MethodConcept.hpp>
+#include <com/sun/star/beans/PropertyConcept.hpp>
+#include <com/sun/star/lang/NoSuchMethodException.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/FailReason.hpp>
+#include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <com/sun/star/reflection/ParamInfo.hpp>
+#include <com/sun/star/beans/XExactName.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+
+#include <com/sun/star/beans/XMaterialHolder.hpp>
+#include <com/sun/star/script/XInvocation2.hpp>
+#include <com/sun/star/script/MemberType.hpp>
+#include <com/sun/star/reflection/XIdlReflection.hpp>
+#include <ooo/vba/XCollection.hpp>
+#include <ooo/vba/XConnectable.hpp>
+#include <ooo/vba/XConnectionPoint.hpp>
+#include <ooo/vba/XSink.hpp>
+#include <ooo/vba/msforms/XCheckBox.hpp>
+#include <osl/interlck.h>
+#include <com/sun/star/uno/genfunc.h>
+#include <comphelper/automationinvokedzone.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/profilezone.hxx>
+#include <comphelper/windowsdebugoutput.hxx>
+#include <comphelper/windowserrorstring.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/safeint.hxx>
+
+#include "comifaces.hxx"
+#include "jscriptclasses.hxx"
+#include "unotypewrapper.hxx"
+#include "oleobjw.hxx"
+#include "unoobjw.hxx"
+#include "servprov.hxx"
+
+using namespace osl;
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+using namespace com::sun::star::script;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::bridge::ModelDependent;
+using namespace com::sun::star::reflection;
+
+std::unordered_map<sal_uIntPtr, WeakReference<XInterface> > UnoObjToWrapperMap;
+static bool writeBackOutParameter(VARIANTARG* pDest, VARIANT* pSource);
+static bool writeBackOutParameter2( VARIANTARG* pDest, VARIANT* pSource);
+static HRESULT mapCannotConvertException(const CannotConvertException &e, unsigned int * puArgErr);
+
+/* Does not throw any exceptions.
+ Param pInfo can be NULL.
+ */
+static void writeExcepinfo(EXCEPINFO * pInfo, const OUString& message)
+{
+ if (pInfo != nullptr)
+ {
+ pInfo->wCode = UNO_2_OLE_EXCEPTIONCODE;
+ pInfo->bstrSource = SysAllocString(L"[automation bridge] ");
+ pInfo->bstrDescription = SysAllocString(o3tl::toW(message.getStr()));
+ }
+}
+
+InterfaceOleWrapper::InterfaceOleWrapper( Reference<XMultiServiceFactory> const & xFactory,
+ sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass):
+ UnoConversionUtilities<InterfaceOleWrapper>( xFactory, unoWrapperClass, comWrapperClass),
+ m_defaultValueType( 0)
+{
+}
+
+InterfaceOleWrapper::~InterfaceOleWrapper()
+{
+ MutexGuard guard(getBridgeMutex());
+ // remove entries in global map
+ auto it = UnoObjToWrapperMap.find( reinterpret_cast<sal_uIntPtr>(m_xOrigin.get()));
+ if(it != UnoObjToWrapperMap.end())
+ UnoObjToWrapperMap.erase(it);
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::QueryInterface(REFIID riid, void ** ppv)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::QueryInterface(" << riid << ")");
+
+ HRESULT ret= S_OK;
+
+ if( !ppv)
+ return E_POINTER;
+
+ if(IsEqualIID(riid, IID_IUnknown))
+ {
+ AddRef();
+ *ppv = static_cast<IUnknown*>(static_cast<IDispatch*>(this));
+ SAL_INFO("extensions.olebridge", " " << *ppv);
+ }
+ else if (IsEqualIID(riid, IID_IDispatch))
+ {
+ AddRef();
+ *ppv = static_cast<IDispatch*>(this);
+ SAL_INFO("extensions.olebridge", " " << *ppv);
+ }
+ else if (IsEqualIID(riid, IID_IProvideClassInfo))
+ {
+ Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY);
+ if (!xConnectable.is())
+ return E_NOINTERFACE;
+ AddRef();
+ *ppv = static_cast<IProvideClassInfo*>(this);
+ SAL_INFO("extensions.olebridge", " " << *ppv);
+ }
+ else if (IsEqualIID(riid, IID_IConnectionPointContainer))
+ {
+ Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY);
+ if (!xConnectable.is())
+ return E_NOINTERFACE;
+ AddRef();
+ *ppv = static_cast<IConnectionPointContainer*>(this);
+ SAL_INFO("extensions.olebridge", " " << *ppv);
+ }
+ else if( IsEqualIID( riid, __uuidof( IUnoObjectWrapper)))
+ {
+ AddRef();
+ *ppv= static_cast<IUnoObjectWrapper*>(this);
+ SAL_INFO("extensions.olebridge", " " << *ppv);
+ }
+ else
+ ret= E_NOINTERFACE;
+ return ret;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) InterfaceOleWrapper::AddRef()
+{
+ acquire();
+ // does not need to guard because one should not rely on the return value of
+ // AddRef anyway
+ return m_refCount;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) InterfaceOleWrapper::Release()
+{
+ ULONG n= m_refCount;
+ release();
+ return n - 1;
+}
+
+// IUnoObjectWrapper --------------------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::getWrapperXInterface( Reference<XInterface>* pXInt)
+{
+ pXInt->set( static_cast<XWeak*>( this), UNO_QUERY);
+ return pXInt->is() ? S_OK : E_FAIL;
+}
+COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::getOriginalUnoObject( Reference<XInterface>* pXInt)
+{
+ *pXInt= m_xOrigin;
+ return m_xOrigin.is() ? S_OK : E_FAIL;
+}
+COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::getOriginalUnoStruct( Any * pStruct)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ HRESULT ret= E_FAIL;
+ if( !m_xOrigin.is())
+ {
+ Reference<XMaterialHolder> xMatHolder( m_xInvocation, UNO_QUERY);
+ if( xMatHolder.is())
+ {
+ Any any = xMatHolder->getMaterial();
+ if( any.getValueTypeClass() == TypeClass_STRUCT)
+ {
+ *pStruct= any;
+ ret= S_OK;
+ }
+ }
+ }
+ return ret;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::GetTypeInfoCount( UINT *pctinfo )
+{
+ SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetTypeInfoCount");
+
+ if (!pctinfo)
+ return E_POINTER;
+
+ *pctinfo = 1;
+
+ return S_OK;
+}
+
+namespace {
+
+class CXTypeInfo : public ITypeInfo,
+ public CComObjectRoot
+{
+public:
+ enum class Kind { COCLASS, MAIN, OUTGOING };
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ BEGIN_COM_MAP(CXTypeInfo)
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+ COM_INTERFACE_ENTRY(ITypeInfo)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+ DECLARE_NOT_AGGREGATABLE(CXTypeInfo)
+
+ virtual ~CXTypeInfo() {}
+
+ void InitForCoclass(Reference<XInterface> xOrigin,
+ const OUString& sImplementationName,
+ const IID& rIID,
+ Reference<XMultiServiceFactory> xMSF);
+ void InitForClassItself(Reference<XInterface> xOrigin,
+ const OUString& sImplementationName,
+ const IID& rIID,
+ Reference<XMultiServiceFactory> xMSF);
+ void InitForOutgoing(Reference<XInterface> xOrigin,
+ const OUString& sInterfaceName,
+ const IID& rIID,
+ Reference<XMultiServiceFactory> xMSF,
+ Type aType);
+ virtual HRESULT STDMETHODCALLTYPE GetTypeAttr(TYPEATTR **ppTypeAttr) override;
+ virtual HRESULT STDMETHODCALLTYPE GetTypeComp(ITypeComp **ppTComp) override;
+ virtual HRESULT STDMETHODCALLTYPE GetFuncDesc(UINT index,
+ FUNCDESC **ppFuncDesc) override;
+ virtual HRESULT STDMETHODCALLTYPE GetVarDesc(UINT index,
+ VARDESC **ppVarDesc) override;
+ virtual HRESULT STDMETHODCALLTYPE GetNames(MEMBERID memid,
+ BSTR *rgBstrNames,
+ UINT cMaxNames,
+ UINT *pcNames) override;
+ virtual HRESULT STDMETHODCALLTYPE GetRefTypeOfImplType(UINT index,
+ HREFTYPE *pRefType) override;
+ virtual HRESULT STDMETHODCALLTYPE GetImplTypeFlags(UINT index,
+ INT *pImplTypeFlags) override;
+ virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(LPOLESTR *rgszNames,
+ UINT cNames,
+ MEMBERID *pMemId) override;
+ virtual HRESULT STDMETHODCALLTYPE Invoke(PVOID pvInstance,
+ MEMBERID memid,
+ WORD wFlags,
+ DISPPARAMS *pDispParams,
+ VARIANT *pVarResult,
+ EXCEPINFO *pExcepInfo,
+ UINT *puArgErr) override;
+ virtual HRESULT STDMETHODCALLTYPE GetDocumentation(MEMBERID memid,
+ BSTR *pBstrName,
+ BSTR *pBstrDocString,
+ DWORD *pdwHelpContext,
+ BSTR *pBstrHelpFile) override;
+ virtual HRESULT STDMETHODCALLTYPE GetDllEntry(MEMBERID memid,
+ INVOKEKIND invKind,
+ BSTR *pBstrDllName,
+ BSTR *pBstrName,
+ WORD *pwOrdinal) override;
+ virtual HRESULT STDMETHODCALLTYPE GetRefTypeInfo(HREFTYPE hRefType,
+ ITypeInfo **ppTInfo) override;
+ virtual HRESULT STDMETHODCALLTYPE AddressOfMember(MEMBERID memid,
+ INVOKEKIND invKind,
+ PVOID *ppv) override;
+ virtual HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter,
+ REFIID riid,
+ PVOID *ppvObj) override;
+ virtual HRESULT STDMETHODCALLTYPE GetMops(MEMBERID memid,
+ BSTR *pBstrMops) override;
+ virtual HRESULT STDMETHODCALLTYPE GetContainingTypeLib(ITypeLib **ppTLib,
+ UINT *pIndex) override;
+ virtual void STDMETHODCALLTYPE ReleaseTypeAttr(TYPEATTR *pTypeAttr) override;
+ virtual void STDMETHODCALLTYPE ReleaseFuncDesc(FUNCDESC *pFuncDesc) override;
+ virtual void STDMETHODCALLTYPE ReleaseVarDesc(VARDESC *pVarDesc) override;
+
+private:
+ Kind meKind;
+ Reference<XInterface> mxOrigin;
+ OUString msImplementationName;
+ OUString msInterfaceName;
+ IID maIID;
+ Reference<XMultiServiceFactory> mxMSF;
+ Type maType;
+};
+
+class CXTypeLib : public ITypeLib,
+ public CComObjectRoot
+{
+public:
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ BEGIN_COM_MAP(CXTypeLib)
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+ COM_INTERFACE_ENTRY(ITypeLib)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+ DECLARE_NOT_AGGREGATABLE(CXTypeLib)
+
+ virtual ~CXTypeLib() {}
+
+ void Init(Reference<XInterface> xOrigin,
+ const OUString& sImplementationName,
+ Reference<XMultiServiceFactory> xMSF)
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXTypeLib::Init for " << sImplementationName);
+ mxOrigin = xOrigin;
+ msImplementationName = sImplementationName;
+ mxMSF = xMSF;
+ }
+
+ virtual UINT STDMETHODCALLTYPE GetTypeInfoCount() override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeInfoCount");
+ return 1;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT,
+ ITypeInfo **) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeInfo: E_NOTIMPL");
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetTypeInfoType(UINT,
+ TYPEKIND *) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeInfoType: E_NOTIMPL");
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetTypeInfoOfGuid(REFGUID guid,
+ ITypeInfo **ppTInfo) override
+ {
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeLib::GetTypeInfoOfGuid(" << guid << ")");
+ if (!ppTInfo)
+ return E_POINTER;
+
+ Reference<ooo::vba::XConnectable> xConnectable(mxOrigin, UNO_QUERY);
+ if (!xConnectable.is())
+ return TYPE_E_ELEMENTNOTFOUND;
+
+ IID aIID;
+ if (SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(xConnectable->getIID().pData->buffer), &aIID)))
+ {
+ if (IsEqualIID(guid, aIID))
+ {
+ HRESULT ret;
+
+ CComObject<CXTypeInfo>* pTypeInfo;
+
+ ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
+ if (FAILED(ret))
+ return ret;
+
+ pTypeInfo->AddRef();
+
+ pTypeInfo->InitForCoclass(mxOrigin, msImplementationName, aIID, mxMSF);
+
+ *ppTInfo = pTypeInfo;
+
+ return S_OK;
+ }
+ }
+
+#if 0
+ ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint();
+
+ IID aIID;
+ if (SUCCEEDED(IIDFromString((LPOLESTR)aTypeAndIID.IID.pData->buffer, &aIID)))
+ {
+ HRESULT ret;
+
+ CComObject<CXTypeInfo>* pTypeInfo;
+
+ ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
+ if (FAILED(ret))
+ return ret;
+
+ pTypeInfo->AddRef();
+
+ pTypeInfo->InitForOutgoing(mxOrigin, msImplementationName, aIID, mxMSF);
+
+ *ppTInfo = pTypeInfo;
+
+ return S_OK;
+ }
+#else
+ SAL_WARN("extensions.olebridge", "Not implemented: GetTypeInfoOfGuid(" << guid << ")");
+#endif
+
+ return TYPE_E_ELEMENTNOTFOUND;
+
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetLibAttr(TLIBATTR **) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetLibAttr: E_NOTIMPL");
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetTypeComp(ITypeComp **) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetTypeComp: E_NOTIMPL");
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetDocumentation(INT,
+ BSTR *,
+ BSTR *,
+ DWORD *,
+ BSTR *) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXTypeLib::GetDocumentation: E_NOTIMPL");
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE IsName(LPOLESTR,
+ ULONG,
+ BOOL *) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXTypeLib:IsName: E_NOTIMPL");
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE FindName(LPOLESTR,
+ ULONG,
+ ITypeInfo **,
+ MEMBERID *,
+ USHORT *) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXTypeLib::FindName: E_NOTIMPL");
+ return E_NOTIMPL;
+ }
+
+ virtual void STDMETHODCALLTYPE ReleaseTLibAttr(TLIBATTR *) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXTypeLib::ReleaseTLibAttr: E_NOTIMPL");
+ }
+
+private:
+ Reference<XInterface> mxOrigin;
+ OUString msImplementationName;
+ Reference<XMultiServiceFactory> mxMSF;
+};
+
+}
+
+void CXTypeInfo::InitForCoclass(Reference<XInterface> xOrigin,
+ const OUString& sImplementationName,
+ const IID& rIID,
+ Reference<XMultiServiceFactory> xMSF)
+{
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::InitForCoclass(" << sImplementationName << "," << rIID << ")");
+ meKind = Kind::COCLASS;
+ mxOrigin = xOrigin;
+ msImplementationName = sImplementationName;
+ maIID = rIID;
+ mxMSF = xMSF;
+}
+
+void CXTypeInfo::InitForClassItself(Reference<XInterface> xOrigin,
+ const OUString& sImplementationName,
+ const IID& rIID,
+ Reference<XMultiServiceFactory> xMSF)
+{
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::InitForClassItself(" << sImplementationName << "," << rIID << ")");
+ meKind = Kind::MAIN;
+ mxOrigin = xOrigin;
+ msImplementationName = sImplementationName;
+ maIID = rIID;
+ mxMSF = xMSF;
+}
+
+void CXTypeInfo::InitForOutgoing(Reference<XInterface> xOrigin,
+ const OUString& sInterfaceName,
+ const IID& rIID,
+ Reference<XMultiServiceFactory> xMSF,
+ Type aType)
+{
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::InitForOutgoing(" << sInterfaceName << "," << rIID << ")");
+ meKind = Kind::OUTGOING;
+ mxOrigin = xOrigin;
+ msInterfaceName = sInterfaceName;
+ maIID = rIID;
+ mxMSF = xMSF;
+ maType = aType;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetTypeAttr(TYPEATTR **ppTypeAttr)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetTypeAttr");
+
+ if (!ppTypeAttr)
+ return E_POINTER;
+
+ assert(!IsEqualIID(maIID, IID_NULL));
+
+ TYPEATTR *pTypeAttr = new TYPEATTR;
+ memset(pTypeAttr, 0, sizeof(*pTypeAttr));
+
+ pTypeAttr->guid = maIID;
+
+ if (meKind == Kind::COCLASS)
+ {
+ pTypeAttr->typekind = TKIND_COCLASS;
+ pTypeAttr->cFuncs = 0;
+ pTypeAttr->cVars = 0;
+ pTypeAttr->cImplTypes = 3;
+ pTypeAttr->cbSizeVft = 0;
+ pTypeAttr->cbAlignment = 8;
+ pTypeAttr->wTypeFlags = TYPEFLAG_FCANCREATE;
+ }
+ else if (meKind == Kind::MAIN)
+ {
+ pTypeAttr->typekind = TKIND_DISPATCH;
+ pTypeAttr->cFuncs = 10; // FIXME, dummy
+ pTypeAttr->cVars = 0;
+ pTypeAttr->cImplTypes = 1;
+ // FIXME: I think this is always supposed to be as if just for the seven methods in
+ // IDIspatch?
+ pTypeAttr->cbSizeVft = 7 * sizeof(void*);
+ pTypeAttr->cbAlignment = 8;
+ pTypeAttr->wTypeFlags = TYPEFLAG_FHIDDEN|TYPEFLAG_FDISPATCHABLE;
+ }
+ else if (meKind == Kind::OUTGOING)
+ {
+ pTypeAttr->typekind = TKIND_DISPATCH;
+
+ Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF));
+ assert(xRefl.is());
+
+ Reference<XIdlClass> xClass = xRefl->forName(maType.getTypeName());
+ assert(xClass.is());
+
+ auto aMethods = xClass->getMethods();
+ assert(xClass->getTypeClass() == TypeClass_INTERFACE &&
+ aMethods.getLength() > 0);
+
+ // Drop the three XInterface methods, add the three corresponding IUnknown ones plus the
+ // four IDispatch ones on top of that.
+ pTypeAttr->cFuncs = aMethods.getLength() - 3 + 3 + 4;
+ pTypeAttr->cVars = 0;
+ pTypeAttr->cImplTypes = 1;
+ // FIXME: I think this, too, is always supposed to be as if just for the seven methods in
+ // IDIspatch?
+ pTypeAttr->cbSizeVft = 7 * sizeof(void*);
+ pTypeAttr->cbAlignment = 8;
+ pTypeAttr->wTypeFlags = TYPEFLAG_FHIDDEN|TYPEFLAG_FNONEXTENSIBLE|TYPEFLAG_FDISPATCHABLE;
+ }
+ else
+ assert(false);
+
+ pTypeAttr->lcid = LOCALE_USER_DEFAULT;
+ pTypeAttr->memidConstructor = MEMBERID_NIL;
+ pTypeAttr->memidDestructor = MEMBERID_NIL;
+ // FIXME: Is this correct, just the vtable pointer, right?
+ pTypeAttr->cbSizeInstance = sizeof(void*);
+ pTypeAttr->wMajorVerNum = 0;
+ pTypeAttr->wMinorVerNum = 0;
+ pTypeAttr->idldescType.wIDLFlags = IDLFLAG_NONE;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetTypeAttr: " << pTypeAttr);
+
+ *ppTypeAttr = pTypeAttr;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetTypeComp(ITypeComp **)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetTypeComp: E_NOTIMPL");
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetFuncDesc(UINT index,
+ FUNCDESC **ppFuncDesc)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ if (!ppFuncDesc)
+ return E_POINTER;
+
+ if (meKind != Kind::OUTGOING)
+ return E_NOTIMPL;
+
+ if (index <= 6)
+ {
+ *ppFuncDesc = new FUNCDESC;
+ (*ppFuncDesc)->memid = 0x60000000 + index;
+ (*ppFuncDesc)->lprgscode = nullptr;
+ (*ppFuncDesc)->lprgelemdescParam = nullptr;
+ (*ppFuncDesc)->funckind = FUNC_DISPATCH;
+ (*ppFuncDesc)->invkind = INVOKE_FUNC;
+ (*ppFuncDesc)->callconv = CC_STDCALL;
+ switch (index)
+ {
+ case 0: // QueryInterface
+ (*ppFuncDesc)->cParams = 2;
+ (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
+ (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
+ break;
+ case 1: // AddRef
+ (*ppFuncDesc)->cParams = 0;
+ (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
+ (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_UI4;
+ break;
+ case 2: // Release
+ (*ppFuncDesc)->cParams = 1;
+ (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
+ (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_UI4;
+ break;
+ case 3: // GetTypeInfoCount
+ (*ppFuncDesc)->cParams = 1;
+ (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
+ (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
+ break;
+ case 4: // GetTypeInfo
+ (*ppFuncDesc)->cParams = 3;
+ (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
+ (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
+ break;
+ case 5: // GetIDsOfNames
+ (*ppFuncDesc)->cParams = 5;
+ (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
+ (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
+ break;
+ case 6: // Invoke
+ (*ppFuncDesc)->cParams = 8;
+ (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr;
+ (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID;
+ break;
+ }
+ (*ppFuncDesc)->cParamsOpt = 0;
+ (*ppFuncDesc)->oVft = index * sizeof(void*);
+ (*ppFuncDesc)->cScodes = 0;
+ (*ppFuncDesc)->wFuncFlags = FUNCFLAG_FRESTRICTED;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetFuncDesc(" << index << "): S_OK: " << *ppFuncDesc);
+
+ return S_OK;
+ }
+
+ Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF));
+ assert(xRefl.is());
+
+ Reference<XIdlClass> xClass = xRefl->forName(maType.getTypeName());
+ assert(xClass.is());
+
+ auto aMethods = xClass->getMethods();
+ assert(xClass->getTypeClass() == TypeClass_INTERFACE &&
+ aMethods.getLength() > 0);
+
+ if (index > o3tl::make_unsigned(aMethods.getLength() - 3 + 3 + 4))
+ return E_INVALIDARG;
+
+ *ppFuncDesc = new FUNCDESC;
+
+ (*ppFuncDesc)->memid = index - 6;
+ (*ppFuncDesc)->lprgscode = nullptr;
+ (*ppFuncDesc)->lprgelemdescParam = nullptr;
+ (*ppFuncDesc)->funckind = FUNC_DISPATCH;
+ (*ppFuncDesc)->invkind = INVOKE_FUNC;
+ (*ppFuncDesc)->callconv = CC_STDCALL;
+ (*ppFuncDesc)->cParams = aMethods[index - 4]->getParameterInfos().getLength();
+ (*ppFuncDesc)->cParamsOpt = 0;
+ (*ppFuncDesc)->oVft = index * sizeof(void*);
+ (*ppFuncDesc)->cScodes = 0;
+ (*ppFuncDesc)->elemdescFunc.tdesc.lptdesc = nullptr; // ???
+ (*ppFuncDesc)->elemdescFunc.tdesc.vt = VT_VOID; // ???
+ (*ppFuncDesc)->wFuncFlags = 0;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetFuncDesc(" << index << "): S_OK: " << *ppFuncDesc);
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetVarDesc(UINT,
+ VARDESC **)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetVarDesc: E_NOTIMPL");
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetNames(MEMBERID memid,
+ BSTR *rgBstrNames,
+ UINT cMaxNames,
+ UINT *pcNames)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetNames(" << memid << ")");
+ assert(meKind != Kind::COCLASS);
+
+ if (!rgBstrNames)
+ return E_POINTER;
+
+ if (!pcNames)
+ return E_POINTER;
+
+ if (memid < 1)
+ return E_INVALIDARG;
+
+ if (cMaxNames < 1)
+ return E_INVALIDARG;
+
+ if (meKind == Kind::MAIN)
+ {
+ SAL_WARN("extensions.olebridge", "GetNames() for MAIN not implemented");
+ return E_NOTIMPL;
+ }
+
+ Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF));
+ assert(xRefl.is());
+
+ Reference<XIdlClass> xClass = xRefl->forName(maType.getTypeName());
+ assert(xClass.is());
+
+ auto aMethods = xClass->getMethods();
+ assert(xClass->getTypeClass() == TypeClass_INTERFACE &&
+ aMethods.getLength() > 0);
+
+ // Subtract the three XInterface methods. Memid for the first following method is 1.
+ if (memid > aMethods.getLength() - 3)
+ return E_INVALIDARG;
+
+ SAL_INFO("extensions.olebridge", "..." << this << "@CXTypeInfo::GetNames(" << memid << "): " << aMethods[memid + 2]->getName());
+ rgBstrNames[0] = SysAllocString(reinterpret_cast<LPOLESTR>(aMethods[memid + 2]->getName().pData->buffer));
+ *pcNames = 1;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetRefTypeOfImplType(UINT index,
+ HREFTYPE *pRefType)
+{
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetRefTypeOfImplType(" << index << ")");
+
+ if (!pRefType)
+ return E_POINTER;
+
+ assert(index == 0 || index == 1);
+
+ *pRefType = 1000+index;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetImplTypeFlags(UINT index,
+ INT *pImplTypeFlags)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetImplTypeFlags(" << index << ")");
+
+ if (!pImplTypeFlags)
+ return E_POINTER;
+
+ assert(meKind == Kind::COCLASS);
+ assert(index == 0 || index == 1);
+
+ if (index == 0)
+ *pImplTypeFlags = IMPLTYPEFLAG_FDEFAULT;
+ else if (index == 1)
+ *pImplTypeFlags = IMPLTYPEFLAG_FDEFAULT|IMPLTYPEFLAG_FSOURCE;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetIDsOfNames(LPOLESTR *,
+ UINT,
+ MEMBERID *)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetIDsOfNames: E_NOTIMPL");
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::Invoke(PVOID,
+ MEMBERID,
+ WORD,
+ DISPPARAMS *,
+ VARIANT *,
+ EXCEPINFO *,
+ UINT *)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::Invoke: E_NOTIMPL");
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetDocumentation(MEMBERID memid,
+ BSTR *pBstrName,
+ BSTR *pBstrDocString,
+ DWORD *pdwHelpContext,
+ BSTR *pBstrHelpFile)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetDocumentation(" << memid << ")");
+
+ if (pBstrName)
+ {
+ if (memid == MEMBERID_NIL)
+ {
+ *pBstrName = SysAllocString(o3tl::toW(msImplementationName.getStr()));
+ }
+ else if (memid == DISPID_VALUE)
+ {
+ // MEMBERIDs are the same as DISPIDs, apparently?
+ *pBstrName = SysAllocString(L"Value");
+ }
+ else
+ {
+ // FIXME: Shouldn't we be able to know the names of the members of UNO interfaces?
+ *pBstrName = SysAllocString(o3tl::toW(OUString("UnknownNameOfMember#" + OUString::number(memid)).getStr()));
+ }
+ }
+ if (pBstrDocString)
+ *pBstrDocString = SysAllocString(L"");
+ if (pdwHelpContext)
+ *pdwHelpContext = 0;
+ if (pBstrHelpFile)
+ *pBstrHelpFile = nullptr;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetDllEntry(MEMBERID,
+ INVOKEKIND,
+ BSTR *,
+ BSTR *,
+ WORD *)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetDllEntry: E_NOTIMPL");
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetRefTypeInfo(HREFTYPE hRefType,
+ ITypeInfo **ppTInfo)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetRefTypeInfo(" << hRefType << ")");
+
+ if (!ppTInfo)
+ return E_POINTER;
+
+ // FIXME: Is it correct to assume that the only interfaces on which GetRefTypeInfo() would be
+ // called are those that implement ooo::vba::XConnectable?
+
+ Reference<ooo::vba::XConnectable> xConnectable(mxOrigin, UNO_QUERY);
+ if (!xConnectable.is())
+ return E_NOTIMPL;
+
+ ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint();
+
+ IID aIID;
+ if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(aTypeAndIID.IID.pData->buffer), &aIID)))
+ return E_NOTIMPL;
+
+ HRESULT ret;
+
+ CComObject<CXTypeInfo>* pTypeInfo;
+
+ ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
+ if (FAILED(ret))
+ return ret;
+
+ pTypeInfo->AddRef();
+
+ pTypeInfo->InitForOutgoing(mxOrigin, aTypeAndIID.Type.getTypeName(), aIID, mxMSF, aTypeAndIID.Type);
+
+ *ppTInfo = pTypeInfo;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::AddressOfMember(MEMBERID,
+ INVOKEKIND,
+ PVOID *)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::AddressOfMember: E_NOTIMPL");
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::CreateInstance(IUnknown *,
+ REFIID,
+ PVOID *)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::CreateInstance: E_NOTIMPL");
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetMops(MEMBERID,
+ BSTR *)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::GetMops: E_NOTIMPL");
+ return E_NOTIMPL;
+}
+
+// This is not actually called any more by my vbscript test after I added the IProvideClassInfo
+// thing... so all the CXTypeLib stuff is dead code at the moment.
+
+HRESULT STDMETHODCALLTYPE CXTypeInfo::GetContainingTypeLib(ITypeLib **ppTLib,
+ UINT *pIndex)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::GetContainingTypeLib");
+
+ if (!ppTLib || !pIndex)
+ return E_POINTER;
+
+ HRESULT ret;
+
+ CComObject<CXTypeLib>* pTypeLib;
+
+ ret = CComObject<CXTypeLib>::CreateInstance(&pTypeLib);
+ if (FAILED(ret))
+ return ret;
+
+ pTypeLib->AddRef();
+
+ pTypeLib->Init(mxOrigin, msImplementationName, mxMSF);
+
+ *ppTLib = pTypeLib;
+
+ return S_OK;
+}
+
+void STDMETHODCALLTYPE CXTypeInfo::ReleaseTypeAttr(TYPEATTR *pTypeAttr)
+{
+ SAL_INFO("extensions.olebridge", this << "@CXTypeInfo::ReleaseTypeAttr(" << pTypeAttr << ")");
+
+ delete pTypeAttr;
+}
+
+void STDMETHODCALLTYPE CXTypeInfo::ReleaseFuncDesc(FUNCDESC *pFuncDesc)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::ReleaseFuncDesc(" << pFuncDesc << ")");
+
+ delete pFuncDesc;
+}
+
+void STDMETHODCALLTYPE CXTypeInfo::ReleaseVarDesc(VARDESC *)
+{
+ SAL_WARN("extensions.olebridge", this << "@CXTypeInfo::ReleaseVarDesc: E_NOTIMPL");
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::GetTypeInfo(UINT iTInfo, LCID, ITypeInfo ** ppTInfo)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetTypeInfo(" << iTInfo << ")");
+
+ if (!ppTInfo)
+ return E_POINTER;
+
+ if (iTInfo != 0)
+ return E_NOTIMPL;
+
+ // FIXME: This is surely incorrect. Why is being able to handle GetTypeInfo() here coupled to
+ // being a source for outgoing events, i.e. implementing XConnectable? What would break if we
+ // would use XInterfaceWithIID and its getIID instead?
+
+ Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY);
+ if (!xConnectable.is())
+ return E_NOTIMPL;
+
+ OUString sIID = xConnectable->GetIIDForClassItselfNotCoclass();
+ IID aIID;
+ if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(sIID.pData->buffer), &aIID)))
+ return E_NOTIMPL;
+
+ HRESULT ret;
+
+ CComObject<CXTypeInfo>* pTypeInfo;
+
+ ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
+ if (FAILED(ret))
+ return ret;
+
+ pTypeInfo->AddRef();
+
+ pTypeInfo->InitForClassItself(m_xOrigin, m_sImplementationName, aIID, m_smgr);
+
+ *ppTInfo = pTypeInfo;
+
+ return S_OK;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::GetIDsOfNames(REFIID /*riid*/,
+ LPOLESTR * rgszNames,
+ UINT cNames,
+ LCID /*lcid*/,
+ DISPID * rgdispid )
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ if( ! rgdispid)
+ return E_POINTER;
+
+ SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetIDsOfNames:");
+ for (unsigned int i = 0; i < cNames; ++i)
+ {
+ // Initialise returned rgdispid values.
+ rgdispid[i] = DISPID_UNKNOWN;
+
+ SAL_INFO("extensions.olebridge", " " << OUString(o3tl::toU(rgszNames[i])));
+ }
+
+ HRESULT ret = DISP_E_UNKNOWNNAME;
+ try
+ {
+ MutexGuard guard( getBridgeMutex());
+
+ // FIXME: Handle the cNames > 1 case? Note that the rest of the names mean the names of *arguments*.
+
+ if( ! _wcsicmp( *rgszNames, JSCRIPT_VALUE_FUNC) ||
+ ! _wcsicmp( *rgszNames, BRIDGE_VALUE_FUNC))
+ {
+ *rgdispid= DISPID_JSCRIPT_VALUE_FUNC;
+ return S_OK;
+ }
+ else if( ! _wcsicmp( *rgszNames, GET_STRUCT_FUNC) ||
+ ! _wcsicmp( *rgszNames, BRIDGE_GET_STRUCT_FUNC))
+ {
+ *rgdispid= DISPID_GET_STRUCT_FUNC;
+ return S_OK;
+ }
+ else if( ! _wcsicmp( *rgszNames, BRIDGE_CREATE_TYPE_FUNC))
+ {
+ *rgdispid= DISPID_CREATE_TYPE_FUNC;
+ return S_OK;
+ }
+
+ if (m_xInvocation.is() && (cNames > 0))
+ {
+ OUString name(o3tl::toU(rgszNames[0]));
+ NameToIdMap::iterator iter = m_nameToDispIdMap.find(name);
+
+ bool bIsMethod = false;
+
+ OUString exactName = name;
+
+ if (iter == m_nameToDispIdMap.end())
+ {
+ if (m_xExactName.is())
+ {
+ exactName = m_xExactName->getExactName(name);
+ if (exactName.isEmpty())
+ exactName = name;
+ }
+
+ MemberInfo d(0, exactName);
+
+ if (m_xInvocation->hasProperty(exactName))
+ {
+ d.flags |= DISPATCH_PROPERTYGET;
+ d.flags |= DISPATCH_PROPERTYPUT;
+ d.flags |= DISPATCH_PROPERTYPUTREF;
+ }
+
+ if (m_xInvocation->hasMethod(exactName))
+ {
+ d.flags |= DISPATCH_METHOD;
+ bIsMethod = true;
+ }
+
+ if (d.flags != 0)
+ {
+ m_MemberInfos.push_back(d);
+ iter = m_nameToDispIdMap.emplace(exactName, static_cast<DISPID>(m_MemberInfos.size())).first;
+
+ if (exactName != name)
+ {
+ iter = m_nameToDispIdMap.emplace(name, static_cast<DISPID>(m_MemberInfos.size())).first;
+ }
+ }
+ }
+
+ if (iter == m_nameToDispIdMap.end())
+ {
+ ret = DISP_E_UNKNOWNNAME;
+ SAL_INFO("extensions.olebridge", " " << name << ": UNKNOWN");
+ }
+ else
+ {
+ rgdispid[0] = (*iter).second;
+ SAL_INFO("extensions.olebridge", " " << name << ": " << rgdispid[0]);
+
+ if (bIsMethod && cNames > 1)
+ {
+ Reference<XIdlMethod> xIdlMethod;
+ Reference<XIntrospectionAccess> xIntrospectionAccess = m_xInvocation->getIntrospection();
+ try
+ {
+ if (xIntrospectionAccess.is())
+ xIdlMethod = xIntrospectionAccess->getMethod(exactName, MethodConcept::ALL);
+ }
+ catch (const NoSuchMethodException&)
+ {
+ }
+ if (xIdlMethod.is())
+ {
+ auto aParamInfos = xIdlMethod->getParameterInfos();
+ for (unsigned int i = 1; i < cNames; ++i)
+ {
+ bool bFound = false;
+ for (int j = 0; j < aParamInfos.getLength(); ++j)
+ {
+ if (aParamInfos[j].aName.equalsIgnoreAsciiCase(o3tl::toU(rgszNames[i])))
+ {
+ rgdispid[i] = j;
+ bFound = true;
+ SAL_INFO("extensions.olebridge", " " << OUString(o3tl::toU(rgszNames[i])) << ": " << rgdispid[i]);
+ break;
+ }
+ }
+ if (!bFound)
+ SAL_INFO("extensions.olebridge", " " << OUString(o3tl::toU(rgszNames[i])) << ": NOT FOUND");
+ }
+ }
+ }
+
+ // Return value should be S_OK only if *all* the names were found.
+ unsigned int i;
+ for (i = 0; i < cNames; ++i)
+ if (rgdispid[i] == DISPID_UNKNOWN)
+ break;
+ if (i == cNames)
+ ret = S_OK;
+ }
+ }
+ }
+ catch(const BridgeRuntimeError&)
+ {
+ OSL_ASSERT(false);
+ }
+ catch(const Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+ catch(...)
+ {
+ OSL_ASSERT(false);
+ }
+
+ return ret;
+}
+
+// Note: What the comments here say about JScript possibly holds for Automation clients in general,
+// like VBScript ones, too. Or not. Hard to say. What is the relevance of JScript nowadays anyway,
+// and can LO really be used from JScript code on web pages any longer?
+
+// "convertDispparamsArgs" converts VARIANTS to their respecting Any counterparts
+// The parameters "id", "wFlags" and "pdispparams" equal those as used in
+// IDispatch::Invoke. The function handles special JavaScript
+// cases where a VARIANT of type VT_DISPATCH is ambiguous and could represent
+// an object, array ( JavaScript Array object), out parameter and in/out ( JavaScript Array object)
+// parameter (JavaScript Array object)
+// Because all those VT_DISPATCH objects need a different conversion
+// we have to find out what the object is supposed to be. The function does this
+// by either using type information or by help of a specialized ValueObject object.
+
+// A. Type Information
+
+// With the help of type information the kind of parameter can be exactly determined
+// and an appropriate conversion can be chosen. A problem arises if a method expects
+// an Any. Then the type info does not tell what the type of the value, that is kept
+// by the any, should be. In this situation the decision whether the param is a
+// sequence or an object is made upon the fact if the object has a property "0"
+// ( see function "isJScriptArray"). Since this is unsafe it is recommended to use
+// the JScript value objects within a JScript script on such an occasion.
+
+// B. JavaScript Value Object ( class JScriptValue )
+
+// A JScriptValue (ValueObject) object is a COM object in that it implements IDispatch and the
+// IJScriptValue object interface. Such objects are provided by all UNO wrapper
+// objects used within a JScript script. To obtain an instance one has to call
+// "_GetValueObject() or Bridge_GetValueObject()" on a UNO wrapper object (class InterfaceOleWrapper).
+// A value object is appropriately initialized within the script and passed as
+// parameter to a UNO object method or property. The convertDispparamsArgs function
+// can easily find out that a param is such an object by querying for the
+// IJScriptValue interface. By this interface one the type and kind ( out, in/out)
+// can be determined and the right conversion can be applied.
+// Using ValueObjects we spare us the effort of acquiring and examining type information
+// in order to figure out what the an IDispatch parameter is meant for.
+
+// Normal JScript object parameter can be mixed with JScriptValue object. If an
+// VARIANT contains a VT_DISPATCH that is no JScriptValue than the type information
+// is used to find out about the required type.
+void InterfaceOleWrapper::convertDispparamsArgs(DISPID id,
+ unsigned short /*wFlags*/, DISPPARAMS* pdispparams, Sequence<Any>& rSeq)
+{
+ // Parameters come in in reverse order in pdispparams. There might be less parameters than
+ // expected. In that case, assume they are "optional" (but can't be marked as such in UNO IDL),
+ // and fill in the rest with empty Anys. There might also be more than expected. In that case,
+ // assume the oovbaapi UNO IDL hasn't kept up with added optional parameters in MSO, and just
+ // ignore the extra ones, as long as they are empty.
+
+ // An example: incoming parameters: <12, 13, "foo/bar.tem">
+ //
+ // Expected parameters: (string filename, int something, int somethingElse, Any whatever, Any
+ // whateverElse)
+ //
+ // Here the existing incoming parameters are placed in reverse order in the first three outgoing
+ // parameters, and the rest of the outgoing parameters are kept as empty Anys.
+ //
+ // Another example: incoming parameters: <EMPTY, TRUE>
+ //
+ // Expected parameters: (bool flag)
+ //
+ // Here the TRUE is passed as the sole outgoing parameter, and the incoming EMPTY is ignored.
+ //
+ // Still an example: incoming parameters: <"foo.doc", TRUE>
+ //
+ // Expected parameters: (bool flag)
+ //
+ // This throws an error as the incoming string parameter presumably should do something important,
+ // but there is no corresponding outgoing parameter.
+
+ HRESULT hr = S_OK;
+ const int countIncomingArgs = pdispparams->cArgs;
+
+ //Get type information for the current call
+ InvocationInfo info;
+ if( ! getInvocationInfoForCall( id, info))
+ throw BridgeRuntimeError(
+ "[automation bridge]InterfaceOleWrapper::convertDispparamsArgs \n"
+ "Could not obtain type information for current call.");
+
+ // Size rSeq according to the number of expected parameters.
+ const int expectedArgs = info.aParamTypes.getLength() + (info.eMemberType == MemberType_PROPERTY ? 1 : 0);
+ rSeq.realloc( expectedArgs );
+ Any* pParams = rSeq.getArray();
+
+ Any anyParam;
+
+ int outgoingArgIndex = 0;
+
+ // Go through incoming parameters in reverse order, i.e. in the order as declared in IDL
+ for (int i = std::max(countIncomingArgs, expectedArgs) - 1; i >= 0; i--)
+ {
+ // Ignore too many parameters if they are VT_EMPTY anyway
+ if ( outgoingArgIndex >= expectedArgs && pdispparams->rgvarg[i].vt == VT_EMPTY )
+ continue;
+
+ // But otherwise too many parameters is an error
+ if ( outgoingArgIndex >= expectedArgs )
+ throw BridgeRuntimeError( "[automation bridge] Too many parameters" );
+
+ if (info.eMemberType == MemberType_METHOD &&
+ info.aParamModes[ outgoingArgIndex ] == ParamMode_OUT)
+ {
+ outgoingArgIndex++;
+ continue;
+ }
+
+ if (i < countIncomingArgs)
+ {
+ // A missing (and hopefully optional) arg (in the middle of the argument list) is passed
+ // as an empty Any.
+ if (pdispparams->rgvarg[i].vt == VT_ERROR && pdispparams->rgvarg[i].scode == DISP_E_PARAMNOTFOUND)
+ {
+ Any aEmpty;
+ pParams[ outgoingArgIndex ] = aEmpty;
+ outgoingArgIndex++;
+ continue;
+ }
+
+ if(convertValueObject( & pdispparams->rgvarg[i], anyParam))
+ { //a param is a ValueObject and could be converted
+ pParams[ outgoingArgIndex ] = anyParam;
+ outgoingArgIndex++;
+ continue;
+ }
+ }
+ else
+ {
+ // A missing arg. Let's hope it is de facto optional (there is no way in UNO IDL to mark
+ // a parameter as optional). The corresponding slot in pParams is already a void Any.
+ // Here we don't increase outgoingArgIndex!
+ continue;
+ }
+
+ // If the param is an out, in/out parameter in
+ // JScript (Array object, with value at index 0) then we
+ // extract Array[0] and put the value into varParam. At the end of the loop varParam
+ // is converted if it contains a value otherwise the VARIANT from
+ // DISPPARAMS is converted.
+ CComVariant varParam;
+
+ // Check for JScript out and in/out paramsobjects (VT_DISPATCH).
+ // To find them out we use typeinformation of the function being called.
+
+ // No idea how this stuff, originally written for JScript, works for other Automation
+ // clients.
+
+ if( pdispparams->rgvarg[i].vt == VT_DISPATCH )
+ {
+ if( info.eMemberType == MemberType_METHOD && info.aParamModes[ outgoingArgIndex ] == ParamMode_INOUT)
+ {
+ // INOUT-param
+ // Index ( property) "0" contains the actual IN-param. The object is a JScript
+ // Array object.
+ // Get the IN-param at index "0"
+ IDispatch* pdisp= pdispparams->rgvarg[i].pdispVal;
+
+ OLECHAR const * sindex= L"0";
+ DISPID id2;
+ DISPPARAMS noParams= {nullptr,nullptr,0,0};
+ if(SUCCEEDED( hr= pdisp->GetIDsOfNames( IID_NULL, const_cast<OLECHAR **>(&sindex), 1, LOCALE_USER_DEFAULT, &id2)))
+ hr= pdisp->Invoke( id2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
+ & noParams, & varParam, nullptr, nullptr);
+ if( FAILED( hr))
+ {
+ throw BridgeRuntimeError(
+ "[automation bridge] Could not determine "
+ "if the object has a member \"0\". Error: " +
+ OUString::number(hr));
+ }
+ }
+ }
+
+ if( varParam.vt == VT_EMPTY) // then it was no in/out parameter
+ varParam= pdispparams->rgvarg[i];
+
+ if(info.eMemberType == MemberType_METHOD)
+ variantToAny( & varParam, anyParam,
+ info.aParamTypes[ outgoingArgIndex ]);
+ else if(info.eMemberType == MemberType_PROPERTY)
+ variantToAny( & varParam, anyParam, info.aType);
+ else
+ OSL_ASSERT(false);
+
+ if (outgoingArgIndex < expectedArgs)
+ pParams[ outgoingArgIndex ]= anyParam;
+ outgoingArgIndex++;
+ }// end for / iterating over all parameters
+}
+
+bool InterfaceOleWrapper::getInvocationInfoForCall( DISPID id, InvocationInfo& info)
+{
+ bool bTypesAvailable= false;
+
+ if( !m_xInvocation.is() )return false;
+ Reference<XInvocation2> inv2( m_xInvocation, UNO_QUERY);
+ if( inv2.is())
+ {
+ // We need the name of the property or method to get its type information.
+ // The name can be identified through the param "id"
+ // that is kept as value in the map m_nameToDispIdMap.
+ // Problem: the Windows JScript engine sometimes changes small letters to capital
+ // letters as happens in xidlclass_obj.createObject( var) // in JScript.
+ // IDispatch::GetIdsOfNames is then called with "CreateObject" !!!
+ // m_nameToDispIdMap can contain several names for one DISPID but only one is
+ // the exact one. If there's no m_xExactName and therefore no exact name then
+ // there's only one entry in the map.
+ OUString sMemberName;
+
+ auto ci1 = std::find_if(m_nameToDispIdMap.cbegin(), m_nameToDispIdMap.cend(),
+ [&id](const NameToIdMap::value_type& nameToDispId) { return nameToDispId.second == id; }); // item is a pair<OUString, DISPID>
+ if (ci1 != m_nameToDispIdMap.cend())
+ sMemberName= (*ci1).first;
+ // Get information for the current call ( property or method).
+ // There could be similar names which only differ in the cases
+ // of letters. First we assume that the name which was passed into
+ // GetIDsOfNames is correct. If we won't get information with that
+ // name then we have the invocation service use the XExactName interface.
+ bool validInfo= true;
+ InvocationInfo invInfo;
+ try{
+ invInfo= inv2->getInfoForName( sMemberName, false);
+ }
+ catch(const IllegalArgumentException&)
+ {
+ validInfo= false;
+ }
+
+ if( ! validInfo)
+ {
+ invInfo= inv2->getInfoForName( sMemberName, true);
+ }
+ if( invInfo.aName.pData)
+ {
+ bTypesAvailable= true;
+ info= invInfo;
+ }
+ }
+ return bTypesAvailable;
+}
+
+// XBridgeSupplier2 ---------------------------------------------------
+// only bridges itself ( this instance of InterfaceOleWrapper)from UNO to IDispatch
+// If sourceModelType is UNO than any UNO interface implemented by InterfaceOleWrapper
+// can bridged to IDispatch ( if destModelType == OLE). The IDispatch is
+// implemented by this class.
+Any SAL_CALL InterfaceOleWrapper::createBridge(const Any& modelDepObject,
+ const Sequence<sal_Int8>& /*ProcessId*/,
+ sal_Int16 sourceModelType,
+ sal_Int16 destModelType)
+{
+
+ Any retAny;
+ if( sourceModelType == UNO && destModelType == OLE &&
+ modelDepObject.getValueTypeClass() == TypeClass_INTERFACE )
+ {
+ Reference<XInterface> xInt;
+ if( modelDepObject >>= xInt )
+ {
+ if( xInt == Reference<XInterface>( static_cast<XWeak*>( this), UNO_QUERY))
+ {
+ VARIANT *pVar= static_cast<VARIANT*>(CoTaskMemAlloc( sizeof( VARIANT)));
+ if( pVar)
+ {
+ pVar->vt= VT_DISPATCH;
+ pVar->pdispVal= this;
+ AddRef();
+
+ retAny<<= reinterpret_cast< sal_uIntPtr >( pVar);
+ }
+ }
+ }
+ }
+
+ return retAny;
+}
+
+// XInitialization --------------------------------------------------
+void SAL_CALL InterfaceOleWrapper::initialize( const Sequence< Any >& aArguments )
+{
+ switch( aArguments.getLength() )
+ {
+ case 2: // the object wraps a UNO struct
+ aArguments[0] >>= m_xInvocation;
+ aArguments[1] >>= m_defaultValueType;
+ break;
+ case 3: // the object wraps a UNO interface
+ aArguments[0] >>= m_xInvocation;
+ aArguments[1] >>= m_xOrigin;
+ aArguments[2] >>= m_defaultValueType;
+
+ Reference<XServiceInfo> xServiceInfo(m_xOrigin, UNO_QUERY);
+ if (xServiceInfo.is())
+ m_sImplementationName = xServiceInfo->getImplementationName();
+
+ SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::initialize for "
+ << (m_sImplementationName.isEmpty()?"an unknown implementation":m_sImplementationName));
+ break;
+ }
+
+ m_xExactName.set( m_xInvocation, UNO_QUERY);
+}
+
+Reference< XInterface > InterfaceOleWrapper::createUnoWrapperInstance()
+{
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+}
+
+Reference<XInterface> InterfaceOleWrapper::createComWrapperInstance()
+{
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+}
+
+// "getType" is used in convertValueObject to map the string denoting the type
+// to an actual Type object.
+bool getType( const BSTR name, Type & type)
+{
+ bool ret = false;
+ typelib_TypeDescription * pDesc= nullptr;
+ OUString str(o3tl::toU(name));
+ typelib_typedescription_getByName( &pDesc, str.pData );
+ if( pDesc)
+ {
+ type = Type( pDesc->pWeakRef );
+ typelib_typedescription_release( pDesc);
+ ret = true;
+ }
+ return ret;
+}
+
+static bool writeBackOutParameter2( VARIANTARG* pDest, VARIANT* pSource)
+{
+ bool ret = false;
+ HRESULT hr;
+
+ // Handle JScriptValue objects and JScript out params ( Array object )
+ CComVariant varDest( *pDest);
+
+ if( SUCCEEDED( varDest.ChangeType(VT_DISPATCH)))
+ {
+ CComPtr<IDispatch> spDispDest(varDest.pdispVal);
+
+ // special Handling for a JScriptValue object
+ CComQIPtr<IJScriptValueObject> spValueDest(spDispDest);
+ if (spValueDest)
+ {
+ VARIANT_BOOL varBool= VARIANT_FALSE;
+ if ((SUCCEEDED(hr = spValueDest->IsOutParam(&varBool))
+ && varBool == VARIANT_TRUE)
+ || (SUCCEEDED(hr = spValueDest->IsInOutParam(&varBool))
+ && varBool == VARIANT_TRUE))
+ {
+ if( SUCCEEDED( spValueDest->Set( CComVariant(), *pSource)))
+ ret= true;
+ }
+ }
+ else if (pDest->vt == VT_DISPATCH)// VT_DISPATCH -> JScript out param
+ {
+ // We use IDispatchEx because its GetDispID function causes the creation
+ // of a property if it does not exist already. This is convenient for
+ // out parameters in JScript. Then the user must not specify property "0"
+ // explicitly
+ CComQIPtr<IDispatchEx> spDispEx( spDispDest);
+ if( spDispEx)
+ {
+ CComBSTR nullProp(L"0");
+ DISPID dwDispID;
+ if( SUCCEEDED( spDispEx->GetDispID( nullProp, fdexNameEnsure, &dwDispID)))
+ {
+ DISPPARAMS dispparams = {nullptr, nullptr, 1, 1};
+ dispparams.rgvarg = pSource;
+ DISPID dispidPut = DISPID_PROPERTYPUT;
+ dispparams.rgdispidNamedArgs = &dispidPut;
+
+ if (pSource->vt == VT_UNKNOWN || pSource->vt == VT_DISPATCH ||
+ (pSource->vt & VT_ARRAY) || (pSource->vt & VT_BYREF))
+ hr = spDispEx->InvokeEx(dwDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF,
+ &dispparams, nullptr, nullptr, nullptr);
+ else
+ hr= spDispEx->InvokeEx(dwDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT,
+ &dispparams, nullptr, nullptr, nullptr);
+ if( SUCCEEDED(hr))
+ ret= true;
+ }
+ }
+ }
+ else
+ ret= writeBackOutParameter( pDest, pSource);
+ }
+ else // The param can't be a JScript out-parameter ( an Array object), it could be a VBScript
+ { // param. The function checks itself for correct VBScript params
+ ret= writeBackOutParameter( pDest, pSource);
+ }
+ return ret;
+}
+
+// VisualBasic Script passes arguments as VT_VARIANT | VT_BYREF be it in or out parameter.
+// Thus we are in charge of freeing an eventual value contained by the inner VARIANT
+// Please note: VariantCopy doesn't free a VT_BYREF value
+// The out parameters are expected to have always a valid type
+static bool writeBackOutParameter(VARIANTARG* pDest, VARIANT* pSource)
+{
+ HRESULT hr;
+ bool ret = false;
+ // Out parameter must be VT_BYREF
+ if ((V_VT(pDest) & VT_BYREF) != 0 )
+ {
+ VARTYPE oleTypeFlags = V_VT(pSource);
+
+ // if caller accept VARIANT as out parameter, any value must be converted
+ if (V_VT(pDest) == (VT_VARIANT | VT_BYREF))
+ {
+ // When the user provides a VARIANT rather than a concrete type
+ // we just copy the source to the out, in/out parameter
+ // VT_DISPATCH, VT_UNKNOWN, VT_ARRAY, VT_BSTR in the VARIANT that
+ // is contained in pDest are released by VariantCopy
+ VariantCopy(V_VARIANTREF(pDest), pSource);
+ ret = true;
+ }
+ else
+ {
+ // variantarg and variant must have same type
+ if ((V_VT(pDest) & oleTypeFlags) == oleTypeFlags)
+ {
+ if ((oleTypeFlags & VT_ARRAY) != 0)
+ {
+ // In / Out Param
+ if( *V_ARRAYREF(pDest) != nullptr)
+ hr= SafeArrayCopyData( V_ARRAY(pSource), *V_ARRAYREF(pDest));
+ else
+ // Out Param
+ hr= SafeArrayCopy(V_ARRAY(pSource), V_ARRAYREF(pDest));
+ if( SUCCEEDED( hr))
+ ret = true;
+ }
+ else
+ {
+ // copy base type
+ switch (V_VT(pSource))
+ {
+ case VT_I2:
+ {
+ *V_I2REF(pDest) = V_I2(pSource);
+ ret = true;
+ break;
+ }
+ case VT_I4:
+ *V_I4REF(pDest) = V_I4(pSource);
+ ret = true;
+ break;
+ case VT_R4:
+ *V_R4REF(pDest) = V_R4(pSource);
+ ret = true;
+ break;
+ case VT_R8:
+ *V_R8REF(pDest) = V_R8(pSource);
+ ret = true;
+ break;
+ case VT_CY:
+ *V_CYREF(pDest) = V_CY(pSource);
+ ret = true;
+ break;
+ case VT_DATE:
+ *V_DATEREF(pDest) = V_DATE(pSource);
+ ret = true;
+ break;
+ case VT_BSTR:
+ SysFreeString( *pDest->pbstrVal);
+
+ *V_BSTRREF(pDest) = SysAllocString(V_BSTR(pSource));
+ ret = true;
+ break;
+ case VT_DISPATCH:
+ if (*V_DISPATCHREF(pDest) != nullptr)
+ (*V_DISPATCHREF(pDest))->Release();
+
+ *V_DISPATCHREF(pDest) = V_DISPATCH(pSource);
+
+ if (*V_DISPATCHREF(pDest) != nullptr)
+ (*V_DISPATCHREF(pDest))->AddRef();
+
+ ret = true;
+ break;
+ case VT_ERROR:
+ *V_ERRORREF(pDest) = V_ERROR(pSource);
+ ret = true;
+ break;
+ case VT_BOOL:
+ *V_BOOLREF(pDest) = V_BOOL(pSource);
+ ret = true;
+ break;
+ case VT_UNKNOWN:
+ if (*V_UNKNOWNREF(pDest) != nullptr)
+ (*V_UNKNOWNREF(pDest))->Release();
+
+ *V_UNKNOWNREF(pDest) = V_UNKNOWN(pSource);
+
+ if (*V_UNKNOWNREF(pDest) != nullptr)
+ (*V_UNKNOWNREF(pDest))->AddRef();
+
+ ret = true;
+ break;
+ case VT_I1:
+ *V_I1REF(pDest) = V_I1(pSource);
+ ret = true;
+ break;
+ case VT_UI1:
+ *V_UI1REF(pDest) = V_UI1(pSource);
+ ret = true;
+ break;
+ case VT_UI2:
+ *V_UI2REF(pDest) = V_UI2(pSource);
+ ret = true;
+ break;
+ case VT_UI4:
+ *V_UI4REF(pDest) = V_UI4(pSource);
+ ret = true;
+ break;
+ case VT_INT:
+ *V_INTREF(pDest) = V_INT(pSource);
+ ret = true;
+ break;
+ case VT_UINT:
+ *V_UINTREF(pDest) = V_UINT(pSource);
+ ret = true;
+ break;
+ case VT_DECIMAL:
+ memcpy(pDest->pdecVal, pSource, sizeof(DECIMAL));
+ ret = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Handling of special cases
+ // Destination and source types are different
+ if( pDest->vt == (VT_BSTR | VT_BYREF)
+ && pSource->vt == VT_I2)
+ {
+ // When the user provides a String as out our in/out parameter
+ // and the type is char (TypeClass_CHAR) then we convert to a BSTR
+ // instead of VT_I2 as is done otherwise
+ OLECHAR buff[]= {0,0};
+ buff[0]= pSource->iVal;
+
+ SysFreeString( *pDest->pbstrVal);
+ *pDest->pbstrVal= SysAllocString( buff);
+ ret = true;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP InterfaceOleWrapper::Invoke(DISPID dispidMember,
+ REFIID /*riid*/,
+ LCID /*lcid*/,
+ WORD wFlags,
+ DISPPARAMS * pdispparams,
+ VARIANT * pvarResult,
+ EXCEPINFO * pexcepinfo,
+ UINT * puArgErr )
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ OUString sParams;
+#if defined SAL_LOG_INFO
+ sParams += "[";
+ for (unsigned int i = 0; i < pdispparams->cArgs; ++i)
+ {
+ if (i > 0)
+ sParams += ",";
+ std::stringstream aStringStream;
+ aStringStream << pdispparams->rgvarg[i];
+ sParams += OUString::createFromAscii(aStringStream.str().c_str());
+ }
+ sParams += "]";
+#endif
+ SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::Invoke(" << dispidMember << "," << sParams << ")");
+
+ comphelper::ProfileZone aZone("COM Bridge");
+ HRESULT ret = S_OK;
+
+ try
+ {
+ bool bHandled= false;
+ ret= InvokeGeneral( dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo,
+ puArgErr, bHandled);
+ if( bHandled)
+ return ret;
+
+ if ((dispidMember > 0) && (o3tl::make_unsigned(dispidMember) <= m_MemberInfos.size()) && m_xInvocation.is())
+ {
+ MemberInfo d = m_MemberInfos[dispidMember - 1];
+ DWORD flags = wFlags & d.flags;
+
+ if (flags != 0)
+ {
+ if ((flags & DISPATCH_METHOD) != 0)
+ {
+ std::unique_ptr<DISPPARAMS> pNewDispParams;
+ std::vector<VARIANTARG> vNewArgs;
+
+ if (pdispparams->cNamedArgs > 0)
+ {
+ // Convert named arguments to positional ones.
+
+ // An example:
+ //
+ // Function declaration (in pseudo-code):
+ // int foo(int A, int B, optional int C, optional int D, optional int E, optional int F, optional int G)
+ //
+ // Corresponding parameter numbers (DISPIDs):
+ // 0 1 2 3 4 5 6
+ //
+ // Actual call:
+ // foo(10, 20, E:=50, D:=40, F:=60)
+ //
+ // That is, A and B are passed positionally, D, E, and F as named arguments,
+ // and the optional C and G parameters are left out.
+ //
+ // Incoming DISPPARAMS:
+ // cArgs=5, cNamedArgs=3
+ // rgvarg: [60, 40, 50, 20, 10]
+ // rgdispidNamedArgs: [5, 3, 4]
+ //
+ // We calculate nLowestNamedArgDispid = 3 and nHighestNamedArgDispid = 5.
+ //
+ // Result of conversion, no named args:
+ // cArgs=6, cNamedArgs=0
+ // rgvarg: [60, 50, 40, DISP_E_PARAMNOTFOUND, 20, 10]
+
+ // First find the lowest and highest DISPID of the named arguments.
+ DISPID nLowestNamedArgDispid = 1000000;
+ DISPID nHighestNamedArgDispid = -1;
+ for (unsigned int i = 0; i < pdispparams->cNamedArgs; ++i)
+ {
+ if (pdispparams->rgdispidNamedArgs[i] < nLowestNamedArgDispid)
+ nLowestNamedArgDispid = pdispparams->rgdispidNamedArgs[i];
+ if (pdispparams->rgdispidNamedArgs[i] > nHighestNamedArgDispid)
+ nHighestNamedArgDispid = pdispparams->rgdispidNamedArgs[i];
+ }
+
+ // Make sure named arguments don't overlap with positional ones. The lowest
+ // DISPID of the named arguments should be >= the number of positional
+ // arguments.
+ if (nLowestNamedArgDispid < static_cast<DISPID>(pdispparams->cArgs - pdispparams->cNamedArgs))
+ return DISP_E_NONAMEDARGS;
+
+ // Do the actual conversion.
+ pNewDispParams.reset(new DISPPARAMS);
+ vNewArgs.resize(nHighestNamedArgDispid + 1);
+ pNewDispParams->rgvarg = vNewArgs.data();
+ pNewDispParams->rgdispidNamedArgs = nullptr;
+ pNewDispParams->cArgs = nHighestNamedArgDispid + 1;
+ pNewDispParams->cNamedArgs = 0;
+
+ // Initialise all parameter slots as missing
+ for (int i = 0; i < nHighestNamedArgDispid; ++i)
+ {
+ pNewDispParams->rgvarg[i].vt = VT_ERROR;
+ pNewDispParams->rgvarg[i].scode = DISP_E_PARAMNOTFOUND;
+ }
+
+ // Then set the value of those actually present.
+ for (unsigned int i = 0; i < pdispparams->cNamedArgs; ++i)
+ pNewDispParams->rgvarg[nHighestNamedArgDispid - pdispparams->rgdispidNamedArgs[i]] = pdispparams->rgvarg[i];
+
+ const int nFirstUnnamedArg = pdispparams->cNamedArgs + (nLowestNamedArgDispid-(pdispparams->cArgs - pdispparams->cNamedArgs));
+
+ for (unsigned int i = pdispparams->cNamedArgs; i < pdispparams->cArgs; ++i)
+ pNewDispParams->rgvarg[nFirstUnnamedArg + (i-pdispparams->cNamedArgs)] = pdispparams->rgvarg[i];
+
+ pdispparams = pNewDispParams.get();
+ }
+
+ Sequence<Any> params;
+
+ convertDispparamsArgs(dispidMember, wFlags, pdispparams , params );
+
+ ret= doInvoke(pdispparams, pvarResult,
+ pexcepinfo, puArgErr, d.name, params);
+ }
+ else if ((flags & DISPATCH_PROPERTYGET) != 0)
+ {
+ ret= doGetProperty( pdispparams, pvarResult,
+ pexcepinfo, d.name);
+ }
+ else if ((flags & DISPATCH_PROPERTYPUT) != 0 || (flags & DISPATCH_PROPERTYPUTREF) != 0)
+ {
+ if (pdispparams->cArgs != 1)
+ ret = DISP_E_BADPARAMCOUNT;
+ else
+ {
+ Sequence<Any> params;
+ convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
+ if(params.getLength() > 0)
+ ret= doSetProperty( pdispparams, pvarResult, pexcepinfo, puArgErr, d.name, params);
+ else
+ ret = DISP_E_BADVARTYPE;
+ }
+ }
+ }
+ else
+ ret= DISP_E_MEMBERNOTFOUND;
+ }
+ else
+ ret = DISP_E_MEMBERNOTFOUND;
+ }
+ catch(const BridgeRuntimeError& e)
+ {
+ writeExcepinfo(pexcepinfo, e.message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch(const Exception& e)
+ {
+ OUString message= "InterfaceOleWrapper::Invoke : \n" +
+ e.Message;
+ writeExcepinfo(pexcepinfo, message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch(...)
+ {
+ writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::Invoke : \nUnexpected exception");
+ ret = DISP_E_EXCEPTION;
+ }
+
+ return ret;
+}
+
+HRESULT InterfaceOleWrapper::doInvoke( DISPPARAMS * pdispparams, VARIANT * pvarResult,
+ EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString& name, Sequence<Any>& params)
+{
+
+
+ HRESULT ret= S_OK;
+ try
+ {
+ Sequence<sal_Int16> outIndex;
+ Sequence<Any> outParams;
+ Any returnValue;
+
+ if (pdispparams->cNamedArgs > 0)
+ return DISP_E_NONAMEDARGS;
+
+ // invoke method and take care of exceptions
+ returnValue = m_xInvocation->invoke(name,
+ params,
+ outIndex,
+ outParams);
+
+ // try to write back out parameter
+ if (outIndex.getLength() > 0)
+ {
+ const sal_Int16* pOutIndex = outIndex.getConstArray();
+ const Any* pOutParams = outParams.getConstArray();
+
+ for (sal_Int32 i = 0; i < outIndex.getLength(); i++)
+ {
+ CComVariant variant;
+ // Currently a Sequence is converted to an SafeArray of VARIANTs.
+ anyToVariant( &variant, pOutParams[i]);
+
+ // out parameter need special handling if they are VT_DISPATCH
+ // and used in JScript
+ int outindex= pOutIndex[i];
+ writeBackOutParameter2(&(pdispparams->rgvarg[pdispparams->cArgs - 1 - outindex]),
+ &variant );
+ }
+ }
+
+ // write back return value
+ if (pvarResult != nullptr)
+ anyToVariant(pvarResult, returnValue);
+ }
+ catch(const IllegalArgumentException & e) //XInvocation::invoke
+ {
+ writeExcepinfo(pexcepinfo, e.Message);
+ ret = DISP_E_TYPEMISMATCH;
+ }
+ catch(const CannotConvertException & e) //XInvocation::invoke
+ {
+ writeExcepinfo(pexcepinfo, e.Message);
+ ret = mapCannotConvertException( e, puArgErr);
+ }
+ catch(const InvocationTargetException & e) //XInvocation::invoke
+ {
+ const Any& org = e.TargetException;
+ Exception excTarget;
+ org >>= excTarget;
+ OUString message=
+ org.getValueType().getTypeName() + ": " + excTarget.Message;
+ writeExcepinfo(pexcepinfo, message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch(const NoSuchMethodException & e) //XInvocation::invoke
+ {
+ writeExcepinfo(pexcepinfo, e.Message);
+ ret = DISP_E_MEMBERNOTFOUND;
+ }
+ catch(const BridgeRuntimeError & e)
+ {
+ writeExcepinfo(pexcepinfo, e.message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch(const Exception & e)
+ {
+ OUString message= "InterfaceOleWrapper::doInvoke : \n" +
+ e.Message;
+ writeExcepinfo(pexcepinfo, message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch( ... )
+ {
+ writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::doInvoke : \nUnexpected exception");
+ ret = DISP_E_EXCEPTION;
+ }
+ return ret;
+}
+
+HRESULT InterfaceOleWrapper::doGetProperty( DISPPARAMS * /*pdispparams*/, VARIANT * pvarResult,
+ EXCEPINFO * pexcepinfo, OUString& name)
+{
+ HRESULT ret= S_OK;
+
+ try
+ {
+ Any returnValue = m_xInvocation->getValue( name);
+ // write back return value
+ if (pvarResult)
+ anyToVariant(pvarResult, returnValue);
+ }
+ catch(const UnknownPropertyException& e) //XInvocation::getValue
+ {
+ writeExcepinfo(pexcepinfo, e.Message);
+ ret = DISP_E_MEMBERNOTFOUND;
+ }
+ catch(const BridgeRuntimeError& e)
+ {
+ writeExcepinfo(pexcepinfo, e.message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch(const Exception& e)
+ {
+ OUString message= "InterfaceOleWrapper::doGetProperty : \n" +
+ e.Message;
+ writeExcepinfo(pexcepinfo, message);
+ }
+ catch( ... )
+ {
+ writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::doInvoke : \nUnexpected exception");
+ ret = DISP_E_EXCEPTION;
+ }
+ return ret;
+}
+
+HRESULT InterfaceOleWrapper::doSetProperty( DISPPARAMS * /*pdispparams*/, VARIANT * /*pvarResult*/,
+ EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString& name, Sequence<Any> const & params)
+{
+ HRESULT ret= S_OK;
+
+ try
+ {
+ m_xInvocation->setValue( name, params.getConstArray()[0]);
+ }
+ catch(const UnknownPropertyException &)
+ {
+ ret = DISP_E_MEMBERNOTFOUND;
+ }
+ catch(const CannotConvertException &e)
+ {
+ ret= mapCannotConvertException( e, puArgErr);
+ }
+ catch(const InvocationTargetException &e)
+ {
+ if (pexcepinfo != nullptr)
+ {
+ Any org = e.TargetException;
+
+ pexcepinfo->wCode = UNO_2_OLE_EXCEPTIONCODE;
+ pexcepinfo->bstrSource = SysAllocString(L"any ONE component");
+ pexcepinfo->bstrDescription = SysAllocString(
+ o3tl::toW(org.getValueType().getTypeName().getStr()));
+ }
+ ret = DISP_E_EXCEPTION;
+ }
+ catch( ... )
+ {
+ ret= DISP_E_EXCEPTION;
+ }
+ return ret;
+}
+
+namespace {
+
+class CXEnumVariant : public IEnumVARIANT,
+ public CComObjectRoot
+{
+public:
+ CXEnumVariant()
+ : mnIndex(1) // ooo::vba::XCollection index starts at one
+ {
+ }
+
+ virtual ~CXEnumVariant()
+ {
+ }
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ BEGIN_COM_MAP(CXEnumVariant)
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+ COM_INTERFACE_ENTRY(IEnumVARIANT)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+ DECLARE_NOT_AGGREGATABLE(CXEnumVariant)
+
+ // Creates and initializes the enumerator
+ void Init(InterfaceOleWrapper* pInterfaceOleWrapper,
+ const Reference<ooo::vba::XCollection > xCollection)
+ {
+ mpInterfaceOleWrapper = pInterfaceOleWrapper;
+ mxCollection = xCollection;
+ }
+
+ // IEnumVARIANT
+ virtual HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT **) override
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Clone: E_NOTIMPL");
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Next(ULONG const celt,
+ VARIANT *rgVar,
+ ULONG *pCeltFetched) override
+ {
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ if (pCeltFetched)
+ *pCeltFetched = 0;
+
+ if (celt == 0)
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): E_INVALIDARG");
+ return E_INVALIDARG;
+ }
+
+ if (rgVar == nullptr || (celt != 1 && pCeltFetched == nullptr))
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): E_FAIL");
+ return E_FAIL;
+ }
+
+ for (ULONG i = 0; i < celt; i++)
+ VariantInit(&rgVar[i]);
+
+ ULONG nLeft = celt;
+ ULONG nReturned = 0;
+ while (nLeft > 0)
+ {
+ if (mnIndex > mxCollection->getCount())
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): got " << nReturned << ": S_FALSE");
+ return S_FALSE;
+ }
+ Any aIndex;
+ aIndex <<= mnIndex;
+ Any aElement = mxCollection->Item(aIndex, Any());
+ mpInterfaceOleWrapper->anyToVariant(rgVar, aElement);
+ // rgVar->pdispVal->AddRef(); ??
+ if (pCeltFetched)
+ (*pCeltFetched)++;
+ rgVar++;
+ nReturned++;
+ mnIndex++;
+ nLeft--;
+ }
+ SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Next(" << celt << "): S_OK");
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Reset() override
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Reset: S_OK");
+ mnIndex = 1;
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE STDMETHODCALLTYPE Skip(ULONG const celt) override
+ {
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ ULONG nLeft = celt;
+ ULONG nSkipped = 0;
+ while (nLeft > 0)
+ {
+ if (mnIndex > mxCollection->getCount())
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Skip(" << celt << "): skipped " << nSkipped << ": S_FALSE");
+ return S_FALSE;
+ }
+ mnIndex++;
+ nLeft--;
+ }
+ SAL_INFO("extensions.olebridge", this << "@CXEnumVariant::Skip(" << celt << "): S_OK");
+ return S_OK;
+ }
+
+private:
+ InterfaceOleWrapper* mpInterfaceOleWrapper;
+ Reference<ooo::vba::XCollection> mxCollection;
+ sal_Int32 mnIndex;
+};
+
+class Sink : public cppu::WeakImplHelper<ooo::vba::XSink>
+{
+public:
+ Sink(IUnknown* pUnkSink,
+ Reference<XMultiServiceFactory> xMSF,
+ ooo::vba::TypeAndIID aTypeAndIID,
+ InterfaceOleWrapper* pInterfaceOleWrapper);
+
+ // XSink
+ void SAL_CALL Call( const OUString& Method, Sequence< Any >& Arguments ) override;
+
+private:
+ IUnknown* mpUnkSink;
+ Reference<XMultiServiceFactory> mxMSF;
+ ooo::vba::TypeAndIID maTypeAndIID;
+ InterfaceOleWrapper* mpInterfaceOleWrapper;
+};
+
+}
+
+Sink::Sink(IUnknown* pUnkSink,
+ Reference<XMultiServiceFactory> xMSF,
+ ooo::vba::TypeAndIID aTypeAndIID,
+ InterfaceOleWrapper* pInterfaceOleWrapper) :
+ mpUnkSink(pUnkSink),
+ mxMSF(xMSF),
+ maTypeAndIID(aTypeAndIID),
+ mpInterfaceOleWrapper(pInterfaceOleWrapper)
+{
+ mpUnkSink->AddRef();
+}
+
+void SAL_CALL
+Sink::Call( const OUString& Method, Sequence< Any >& Arguments )
+{
+ SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << ", " << Arguments.getLength() << " arguments)");
+
+ IDispatch* pDispatch;
+ HRESULT nResult = mpUnkSink->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(&pDispatch));
+ if (!SUCCEEDED(nResult))
+ {
+ SAL_WARN("extensions.olebridge", "Sink::Call: Not IDispatch: " << WindowsErrorStringFromHRESULT(nResult));
+ return;
+ }
+
+ Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(mxMSF));
+ assert(xRefl.is());
+
+ Reference<XIdlClass> xClass = xRefl->forName(maTypeAndIID.Type.getTypeName());
+ assert(xClass.is());
+
+ auto aMethods = xClass->getMethods();
+ assert(xClass->getTypeClass() == TypeClass_INTERFACE &&
+ aMethods.getLength() > 0);
+
+ int nMemId = 1;
+ auto ArgumentsRange = asNonConstRange(Arguments);
+ // Skip the three XInterface methods
+ for (int i = 3; i < aMethods.getLength(); i++)
+ {
+ if (aMethods[i]->getName() == Method)
+ {
+ // FIXME: Handle mismatch in type of actual argument and parameter of the method.
+
+ // FIXME: Handle mismatch in number of arguments passed and actual number of parameters
+ // of the method.
+
+ auto aParamInfos = aMethods[i]->getParameterInfos();
+
+ assert(Arguments.getLength() == aParamInfos.getLength());
+
+ DISPPARAMS aDispParams;
+ aDispParams.rgdispidNamedArgs = nullptr;
+ aDispParams.cArgs = Arguments.getLength();
+ aDispParams.cNamedArgs = 0;
+ aDispParams.rgvarg = new VARIANT[aDispParams.cArgs];
+ for (unsigned j = 0; j < aDispParams.cArgs; j++)
+ {
+ VariantInit(aDispParams.rgvarg+j);
+ // Note: Reverse order of arguments in Arguments and aDispParams.rgvarg!
+ const unsigned nIncomingArgIndex = aDispParams.cArgs - j - 1;
+ mpInterfaceOleWrapper->anyToVariant(aDispParams.rgvarg+j, Arguments[nIncomingArgIndex]);
+
+ // Handle OUT and INOUT arguments. For instance, the second ('Cancel') parameter to
+ // DocumentBeforeClose() should be a VT_BYREF|VT_BOOL parameter. Need to handle that
+ // here.
+
+ if (aParamInfos[nIncomingArgIndex].aMode == ParamMode_OUT ||
+ aParamInfos[nIncomingArgIndex].aMode == ParamMode_INOUT)
+ {
+ switch (aDispParams.rgvarg[j].vt)
+ {
+ case VT_I2:
+ aDispParams.rgvarg[j].byref = new SHORT(aDispParams.rgvarg[j].iVal);
+ aDispParams.rgvarg[j].vt |= VT_BYREF;
+ break;
+ case VT_I4:
+ aDispParams.rgvarg[j].byref = new LONG(aDispParams.rgvarg[j].lVal);
+ aDispParams.rgvarg[j].vt |= VT_BYREF;
+ break;
+ case VT_BSTR:
+ aDispParams.rgvarg[j].byref = new BSTR(aDispParams.rgvarg[j].bstrVal);
+ aDispParams.rgvarg[j].vt |= VT_BYREF;
+ break;
+ case VT_BOOL:
+ aDispParams.rgvarg[j].byref = new VARIANT_BOOL(aDispParams.rgvarg[j].boolVal);
+ aDispParams.rgvarg[j].vt |= VT_BYREF;
+ break;
+ default:
+ assert(false && "Not handled yet");
+ break;
+ }
+ }
+ }
+
+ VARIANT aVarResult;
+ VariantInit(&aVarResult);
+ UINT uArgErr;
+
+ // In the case of a VBScript client, which uses "late binding", calling Invoke on the
+ // sink it provides will cause a callback to our CXTypeInfo::GetNames for the given
+ // member id, and in that we will tell it the name of the corresponding method, and the
+ // client will know what event handler to invoke based on that name.
+ //
+ // As the outgoing interfaces used (ooo::vba::word::XApplicationOutgoing and others) are
+ // totally not stable and not published in any way, there can be no client that would
+ // have done "compile-time binding" and where the sink would actually be an object with
+ // a vtbl corresponding to the outgoing interface. Late binding clients that work like
+ // VBScript is all we support.
+ SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << "): Calling Invoke(" << nMemId << ")");
+
+ nResult = pDispatch->Invoke(nMemId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &aDispParams, &aVarResult, nullptr, &uArgErr);
+ SAL_INFO("extensions.olebridge", "Sink::Call(" << Method << "): Invoke() returned");
+
+ SAL_WARN_IF(!SUCCEEDED(nResult), "extensions.olebridge", "Call to " << Method << " failed: " << WindowsErrorStringFromHRESULT(nResult));
+
+ // Undo VT_BYREF magic done above. Copy out parameters back to the Anys in Arguments
+ for (unsigned j = 0; j < aDispParams.cArgs; j++)
+ {
+ const unsigned nIncomingArgIndex = aDispParams.cArgs - j - 1;
+ if (aParamInfos[nIncomingArgIndex].aMode == ParamMode_OUT ||
+ aParamInfos[nIncomingArgIndex].aMode == ParamMode_INOUT)
+ {
+ switch (aDispParams.rgvarg[j].vt)
+ {
+ case VT_BYREF|VT_I2:
+ {
+ SHORT *pI = static_cast<SHORT*>(aDispParams.rgvarg[j].byref);
+ ArgumentsRange[nIncomingArgIndex] <<= static_cast<sal_Int16>(*pI);
+ delete pI;
+ }
+ break;
+ case VT_BYREF|VT_I4:
+ {
+ LONG *pL = static_cast<LONG*>(aDispParams.rgvarg[j].byref);
+ ArgumentsRange[nIncomingArgIndex] <<= static_cast<sal_Int32>(*pL);
+ delete pL;
+ }
+ break;
+ case VT_BYREF|VT_BSTR:
+ {
+ BSTR *pBstr = static_cast<BSTR*>(aDispParams.rgvarg[j].byref);
+ ArgumentsRange[nIncomingArgIndex] <<= OUString(o3tl::toU(*pBstr));
+ // Undo SysAllocString() done in anyToVariant()
+ SysFreeString(*pBstr);
+ delete pBstr;
+ }
+ break;
+ case VT_BYREF|VT_BOOL:
+ {
+ VARIANT_BOOL *pBool = static_cast<VARIANT_BOOL*>(aDispParams.rgvarg[j].byref);
+ ArgumentsRange[nIncomingArgIndex] <<= (*pBool != VARIANT_FALSE);
+ delete pBool;
+ }
+ break;
+ default:
+ assert(false && "Not handled yet");
+ break;
+ }
+ }
+ else
+ {
+ switch (aDispParams.rgvarg[j].vt)
+ {
+ case VT_BSTR:
+ // Undo SysAllocString() done in anyToVariant()
+ SysFreeString(aDispParams.rgvarg[j].bstrVal);
+ break;
+ }
+ }
+ }
+
+ delete[] aDispParams.rgvarg;
+ return;
+ }
+ nMemId++;
+ }
+ SAL_WARN("extensions.olebridge", "Sink::Call: Unknown method '" << Method << "'");
+}
+
+namespace {
+
+class CXEnumConnections : public IEnumConnections,
+ public CComObjectRoot
+{
+public:
+ CXEnumConnections()
+ {
+ }
+
+ virtual ~CXEnumConnections()
+ {
+ }
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ BEGIN_COM_MAP(CXEnumConnections)
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+ COM_INTERFACE_ENTRY(IEnumConnections)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+ DECLARE_NOT_AGGREGATABLE(CXEnumConnections)
+
+ void Init(std::vector<IUnknown*>& rUnknowns, std::vector<DWORD>& rCookies)
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Init");
+ SAL_WARN_IF(rUnknowns.size() != rCookies.size(), "extensions.olebridge", "Vectors of different size");
+ mvUnknowns = rUnknowns;
+ mvCookies = rCookies;
+ mnIndex = 0;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Next(ULONG cConnections,
+ LPCONNECTDATA rgcd,
+ ULONG *pcFetched) override
+ {
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ if (!rgcd)
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): E_POINTER");
+ return E_POINTER;
+ }
+
+ if (pcFetched && cConnections != 1)
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): E_INVALIDARG");
+ return E_INVALIDARG;
+ }
+
+ ULONG nFetched = 0;
+ while (nFetched < cConnections && mnIndex < mvUnknowns.size())
+ {
+ rgcd[nFetched].pUnk = mvUnknowns[mnIndex];
+ rgcd[nFetched].pUnk->AddRef();
+ rgcd[nFetched].dwCookie = mvCookies[mnIndex];
+ ++nFetched;
+ ++mnIndex;
+ }
+ if (nFetched != cConnections)
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): S_FALSE");
+ if (pcFetched)
+ *pcFetched = nFetched;
+ return S_FALSE;
+ }
+ SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Next(" << cConnections << "): S_OK");
+ if (pcFetched)
+ *pcFetched = nFetched;
+
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Skip(ULONG cConnections) override
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Skip(" << cConnections << "): E_NOTIMPL");
+
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Reset() override
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Reset: E_NOTIMPL");
+
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Clone(IEnumConnections** /* ppEnum */) override
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXEnumConnections::Clone: E_NOTIMPL");
+
+ return E_NOTIMPL;
+ }
+
+private:
+ std::vector<IUnknown*> mvUnknowns;
+ std::vector<DWORD> mvCookies;
+ ULONG mnIndex;
+};
+
+class CXConnectionPoint : public IConnectionPoint,
+ public CComObjectRoot
+{
+public:
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ BEGIN_COM_MAP(CXConnectionPoint)
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+ COM_INTERFACE_ENTRY(IConnectionPoint)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+ END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+ DECLARE_NOT_AGGREGATABLE(CXConnectionPoint)
+
+ virtual ~CXConnectionPoint() {}
+
+ void Init(InterfaceOleWrapper* pInterfaceOleWrapper,
+ Reference<ooo::vba::XConnectionPoint>& xCP,
+ Reference<XMultiServiceFactory>& xMSF,
+ ooo::vba::TypeAndIID aTypeAndIID)
+ {
+ SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Init for " << pInterfaceOleWrapper->getImplementationName());
+
+ IUnknown *pUnknown;
+ if (SUCCEEDED(QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnknown))))
+ {
+ // In case QI for IUnknown returns a different pointer, but nah, it doesn't
+ SAL_INFO("extensions.olebridge", " (IUnknown@" << pUnknown << ")");
+ }
+
+ mpInterfaceOleWrapper = pInterfaceOleWrapper;
+ mxCP = xCP;
+ mxMSF = xMSF;
+ maTypeAndIID = aTypeAndIID;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetConnectionInterface(IID *pIID) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXConnectionPoint::GetConnectionInterface(" << *pIID << "): E_NOTIMPL");
+
+ // FIXME: Needed?
+
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetConnectionPointContainer(IConnectionPointContainer **) override
+ {
+ SAL_WARN("extensions.olebridge", this << "@CXConnectionPoint::GetConnectionInterface: E_NOTIMPL");
+
+ // FIXME: Needed?
+
+ return E_NOTIMPL;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Advise(IUnknown *pUnkSink,
+ DWORD *pdwCookie) override
+ {
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Advise(" << pUnkSink << ")");
+
+ if (!pdwCookie)
+ return E_POINTER;
+
+ Reference<ooo::vba::XSink> xSink(new Sink(pUnkSink, mxMSF, maTypeAndIID, mpInterfaceOleWrapper));
+
+ mvISinks.push_back(pUnkSink);
+ *pdwCookie = mvISinks.size();
+
+ mvCookies.push_back(mxCP->Advise(xSink));
+
+ mvXSinks.push_back(xSink);
+
+ SAL_INFO("extensions.olebridge", " *pdwCookie: " << *pdwCookie);
+
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE Unadvise(DWORD dwCookie) override
+ {
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::Unadvise(" << dwCookie << ")");
+
+ if (dwCookie == 0 || dwCookie > mvISinks.size())
+ return E_POINTER;
+
+ mvISinks[dwCookie-1] = nullptr;
+
+ mxCP->Unadvise(mvCookies[dwCookie-1]);
+
+ mvXSinks[dwCookie-1] = Reference<ooo::vba::XSink>();
+
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE EnumConnections(IEnumConnections **ppEnum) override
+ {
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ HRESULT nResult;
+
+ SAL_INFO("extensions.olebridge", this << "@CXConnectionPoint::EnumConnections...");
+
+ if (!ppEnum)
+ {
+ SAL_INFO("extensions.olebridge", "..." << this << "@CXConnectionPoint::EnumConnections: E_POINTER");
+ return E_POINTER;
+ }
+
+ CComObject<CXEnumConnections>* pEnumConnections;
+
+ nResult = CComObject<CXEnumConnections>::CreateInstance(&pEnumConnections);
+ if (FAILED(nResult))
+ {
+ SAL_INFO("extensions.olebridge", "..." << this << "@CXConnectionPoint::EnumConnections: " << WindowsErrorStringFromHRESULT(nResult));
+ return nResult;
+ }
+
+ pEnumConnections->AddRef();
+
+ pEnumConnections->Init(mvISinks, mvCookies);
+ *ppEnum = pEnumConnections;
+
+ SAL_INFO("extensions.olebridge", "..." << this << "@CXConnectionPoint::EnumConnections: S_OK");
+
+ return S_OK;
+ }
+
+ InterfaceOleWrapper* mpInterfaceOleWrapper;
+ std::vector<IUnknown*> mvISinks;
+ std::vector<Reference<ooo::vba::XSink>> mvXSinks;
+ std::vector<DWORD> mvCookies;
+ Reference<XMultiServiceFactory> mxMSF;
+ Reference<ooo::vba::XConnectionPoint> mxCP;
+ ooo::vba::TypeAndIID maTypeAndIID;
+};
+
+}
+
+HRESULT InterfaceOleWrapper::InvokeGeneral( DISPID dispidMember, unsigned short wFlags,
+ DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo,
+ unsigned int * /*puArgErr*/, bool& bHandled)
+{
+ HRESULT ret= S_OK;
+ try
+ {
+// DISPID_VALUE | The DEFAULT Value is required in JScript when the situation
+// is that we put an object into an Array object ( out parameter). We have to return
+// IDispatch otherwise the object cannot be accessed from the Script.
+ if( dispidMember == DISPID_VALUE && (wFlags & DISPATCH_PROPERTYGET) != 0
+ && m_defaultValueType != VT_EMPTY && pvarResult != nullptr)
+ {
+ // Special case hack: If it is a ScVbaCheckBox, return the boolean value
+ Reference<ooo::vba::msforms::XCheckBox> xCheckBox(m_xOrigin, UNO_QUERY);
+ if (xCheckBox.is())
+ {
+ bHandled = true;
+ Any aValue = xCheckBox->getValue();
+ anyToVariant(pvarResult, aValue);
+ return S_OK;
+ }
+
+ bHandled= true;
+ if( m_defaultValueType == VT_DISPATCH)
+ {
+ pvarResult->vt= VT_DISPATCH;
+ pvarResult->pdispVal= this;
+ AddRef();
+ ret= S_OK;
+ }
+ }
+
+ // function: _GetValueObject
+ else if( dispidMember == DISPID_JSCRIPT_VALUE_FUNC)
+ {
+ bHandled= true;
+ if( !pvarResult)
+ return E_POINTER;
+ CComObject< JScriptValue>* pValue;
+ if( SUCCEEDED( CComObject<JScriptValue>::CreateInstance( &pValue)))
+ {
+ pValue->AddRef();
+ pvarResult->vt= VT_DISPATCH;
+ pvarResult->pdispVal= CComQIPtr<IDispatch>(pValue->GetUnknown());
+ ret= S_OK;
+ }
+ else
+ ret= DISP_E_EXCEPTION;
+ }
+ else if( dispidMember == DISPID_GET_STRUCT_FUNC)
+ {
+ bHandled= true;
+ bool bStruct= false;
+
+
+ Reference<XIdlReflection> xRefl = theCoreReflection::get(comphelper::getComponentContext(m_smgr));
+ // the first parameter is in DISPPARAMS rgvargs contains the name of the struct.
+ CComVariant arg;
+ if( pdispparams->cArgs == 1 && SUCCEEDED( arg.ChangeType( VT_BSTR, &pdispparams->rgvarg[0])) )
+ {
+ Reference<XIdlClass> classStruct= xRefl->forName(OUString(o3tl::toU(arg.bstrVal)));
+ if( classStruct.is())
+ {
+ Any anyStruct;
+ classStruct->createObject( anyStruct);
+ CComVariant var;
+ anyToVariant( &var, anyStruct );
+
+ if( var.vt == VT_DISPATCH)
+ {
+ VariantCopy( pvarResult, & var);
+ bStruct= true;
+ }
+ }
+ }
+ ret= bStruct ? S_OK : DISP_E_EXCEPTION;
+ }
+ else if (dispidMember == DISPID_CREATE_TYPE_FUNC)
+ {
+ bHandled= true;
+ if( !pvarResult)
+ return E_POINTER;
+ // the first parameter is in DISPPARAMS rgvargs contains the name of the struct.
+ CComVariant arg;
+ if( pdispparams->cArgs != 1)
+ return DISP_E_BADPARAMCOUNT;
+ if (FAILED( arg.ChangeType( VT_BSTR, &pdispparams->rgvarg[0])))
+ return DISP_E_BADVARTYPE;
+
+ //check if the provided name represents a valid type
+ Type type;
+ if (!getType(arg.bstrVal, type))
+ {
+ writeExcepinfo(pexcepinfo, OUString::Concat("[automation bridge] A UNO type with the name ") +
+ o3tl::toU(arg.bstrVal) + " does not exist!");
+ return DISP_E_EXCEPTION;
+ }
+
+ if (!createUnoTypeWrapper(arg.bstrVal, pvarResult))
+ {
+ writeExcepinfo(pexcepinfo, "[automation bridge] InterfaceOleWrapper::InvokeGeneral\n"
+ "Could not initialize UnoTypeWrapper object!");
+ return DISP_E_EXCEPTION;
+ }
+ }
+ else if (dispidMember == DISPID_NEWENUM)
+ {
+ bHandled = true;
+ if( !pvarResult)
+ return E_POINTER;
+
+ Reference< ooo::vba::XCollection> xCollection(m_xOrigin, UNO_QUERY);
+ if (!xCollection.is())
+ return DISP_E_MEMBERNOTFOUND;
+
+ CComObject<CXEnumVariant>* pEnumVar;
+
+ ret = CComObject<CXEnumVariant>::CreateInstance(&pEnumVar);
+ if (FAILED(ret))
+ return ret;
+
+ pEnumVar->AddRef();
+
+ pEnumVar->Init(this, xCollection);
+
+ pvarResult->vt = VT_UNKNOWN;
+ pvarResult->punkVal = nullptr;
+
+ ret = pEnumVar->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&pvarResult->punkVal));
+ if (FAILED(ret))
+ {
+ pEnumVar->Release();
+ return ret;
+ }
+ }
+ }
+ catch(const BridgeRuntimeError & e)
+ {
+ writeExcepinfo(pexcepinfo, e.message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch(const Exception & e)
+ {
+ OUString message= "InterfaceOleWrapper::InvokeGeneral : \n" +
+ e.Message;
+ writeExcepinfo(pexcepinfo, message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch( ... )
+ {
+ writeExcepinfo(pexcepinfo, "InterfaceOleWrapper::InvokeGeneral : \nUnexpected exception");
+ ret = DISP_E_EXCEPTION;
+ }
+ return ret;
+}
+
+STDMETHODIMP InterfaceOleWrapper::GetDispID(BSTR /*bstrName*/, DWORD /*grfdex*/, DISPID __RPC_FAR* /*pid*/)
+{
+ return ResultFromScode(E_NOTIMPL);
+}
+
+STDMETHODIMP InterfaceOleWrapper::InvokeEx(
+ /* [in] */ DISPID /*id*/,
+ /* [in] */ LCID /*lcid*/,
+ /* [in] */ WORD /*wFlags*/,
+ /* [in] */ DISPPARAMS __RPC_FAR* /*pdp*/,
+ /* [out] */ VARIANT __RPC_FAR* /*pvarRes*/,
+ /* [out] */ EXCEPINFO __RPC_FAR* /*pei*/,
+ /* [unique][in] */ IServiceProvider __RPC_FAR* /*pspCaller*/)
+{
+ return ResultFromScode(E_NOTIMPL);
+}
+
+STDMETHODIMP InterfaceOleWrapper::DeleteMemberByName(
+ /* [in] */ BSTR /*bstr*/,
+ /* [in] */ DWORD /*grfdex*/)
+{
+ return ResultFromScode(E_NOTIMPL);
+}
+
+STDMETHODIMP InterfaceOleWrapper::DeleteMemberByDispID(DISPID /*id*/)
+{
+ return ResultFromScode(E_NOTIMPL);
+}
+
+STDMETHODIMP InterfaceOleWrapper::GetMemberProperties(
+ /* [in] */ DISPID /*id*/,
+ /* [in] */ DWORD /*grfdexFetch*/,
+ /* [out] */ DWORD __RPC_FAR* /*pgrfdex*/)
+{
+ return ResultFromScode(E_NOTIMPL);
+}
+
+STDMETHODIMP InterfaceOleWrapper::GetMemberName(
+ /* [in] */ DISPID /*id*/,
+ /* [out] */ BSTR __RPC_FAR* /*pbstrName*/)
+{
+ return ResultFromScode(E_NOTIMPL);
+}
+
+STDMETHODIMP InterfaceOleWrapper::GetNextDispID(
+ /* [in] */ DWORD /*grfdex*/,
+ /* [in] */ DISPID /*id*/,
+ /* [out] */ DISPID __RPC_FAR* /*pid*/)
+{
+ return ResultFromScode(E_NOTIMPL);
+}
+
+STDMETHODIMP InterfaceOleWrapper::GetNameSpaceParent(
+ /* [out] */ IUnknown __RPC_FAR *__RPC_FAR* /*ppunk*/)
+{
+ return ResultFromScode(E_NOTIMPL);
+}
+
+// IProvideClassInfo
+HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::GetClassInfo (
+ /* [out] */ ITypeInfo **ppTI)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::GetClassInfo");
+
+ if (!ppTI)
+ return E_POINTER;
+
+ Reference<ooo::vba::XInterfaceWithIID> xIID(m_xOrigin, UNO_QUERY);
+ if (!xIID.is())
+ return E_NOTIMPL;
+
+ OUString sIID = xIID->getIID();
+ IID aIID;
+ if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(sIID.pData->buffer), &aIID)))
+ return E_NOTIMPL;
+
+ HRESULT ret;
+
+ CComObject<CXTypeInfo>* pTypeInfo;
+
+ ret = CComObject<CXTypeInfo>::CreateInstance(&pTypeInfo);
+ if (FAILED(ret))
+ return ret;
+
+ pTypeInfo->AddRef();
+
+ pTypeInfo->InitForCoclass(m_xOrigin, m_sImplementationName, aIID, m_smgr);
+
+ *ppTI = pTypeInfo;
+
+ return S_OK;
+}
+
+// IConnectionPointContainer
+HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::EnumConnectionPoints(
+ /* [out] */ IEnumConnectionPoints **)
+{
+ SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::EnumConnectionPoints");
+ return ResultFromScode(E_NOTIMPL);
+}
+
+HRESULT STDMETHODCALLTYPE InterfaceOleWrapper::FindConnectionPoint(
+ /* [in] */ REFIID riid,
+ /* [out] */ IConnectionPoint **ppCP)
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ SAL_INFO("extensions.olebridge", this << "@InterfaceOleWrapper::FindConnectionPoint(" << riid << ")");
+
+ if (!ppCP)
+ return E_POINTER;
+
+ Reference<ooo::vba::XConnectable> xConnectable(m_xOrigin, UNO_QUERY);
+
+ // We checked already
+ assert(xConnectable.is());
+ if (!xConnectable.is())
+ return E_NOTIMPL;
+
+ ooo::vba::TypeAndIID aTypeAndIID = xConnectable->GetConnectionPoint();
+
+ IID aIID;
+ if (!SUCCEEDED(IIDFromString(reinterpret_cast<LPOLESTR>(aTypeAndIID.IID.pData->buffer), &aIID)))
+ return E_INVALIDARG;
+
+ if (!IsEqualIID(riid, aIID))
+ return E_INVALIDARG;
+
+ Reference<ooo::vba::XConnectionPoint> xCP = xConnectable->FindConnectionPoint();
+ if (!xCP.is())
+ return E_INVALIDARG;
+
+ HRESULT ret;
+
+ CComObject<CXConnectionPoint>* pConnectionPoint;
+
+ ret = CComObject<CXConnectionPoint>::CreateInstance(&pConnectionPoint);
+ if (FAILED(ret))
+ return ret;
+
+ pConnectionPoint->AddRef();
+
+ pConnectionPoint->Init(this, xCP, m_smgr, aTypeAndIID);
+
+ *ppCP = pConnectionPoint;
+
+ return S_OK;
+}
+
+// UnoObjectWrapperRemoteOpt ---------------------------------------------------
+
+UnoObjectWrapperRemoteOpt::UnoObjectWrapperRemoteOpt( Reference<XMultiServiceFactory> const & aFactory,
+ sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass):
+InterfaceOleWrapper( aFactory, unoWrapperClass, comWrapperClass),
+m_currentId(1)
+
+{
+}
+UnoObjectWrapperRemoteOpt::~UnoObjectWrapperRemoteOpt()
+{
+}
+
+// UnoConversionUtilities
+Reference< XInterface > UnoObjectWrapperRemoteOpt::createUnoWrapperInstance()
+{
+ Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt(
+ m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
+ return Reference<XInterface>( xWeak, UNO_QUERY);
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP UnoObjectWrapperRemoteOpt::GetIDsOfNames ( REFIID /*riid*/, LPOLESTR * rgszNames, UINT cNames,
+ LCID /*lcid*/, DISPID * rgdispid )
+{
+ MutexGuard guard( getBridgeMutex());
+
+ if( ! rgdispid)
+ return E_POINTER;
+ HRESULT ret = E_UNEXPECTED;
+
+ // _GetValueObject
+ if( ! wcscmp( *rgszNames, JSCRIPT_VALUE_FUNC))
+ {
+ *rgdispid= DISPID_JSCRIPT_VALUE_FUNC;
+ return S_OK;
+ }
+ else if( ! wcscmp( *rgszNames, GET_STRUCT_FUNC))
+ {
+ *rgdispid= DISPID_GET_STRUCT_FUNC;
+ return S_OK;
+ }
+
+ if (m_xInvocation.is() && (cNames > 0))
+ {
+ OUString name(o3tl::toU(rgszNames[0]));
+ // has this name been determined as "bad"
+ BadNameMap::iterator badIter= m_badNameMap.find( name);
+ if( badIter == m_badNameMap.end() )
+ {
+ // name has not been bad before( member exists
+ typedef NameToIdMap::iterator ITnames;
+ std::pair< ITnames, bool > pair_id= m_nameToDispIdMap.emplace(name, m_currentId++);
+ // new ID inserted ?
+ if( pair_id.second )
+ {// yes, now create MemberInfo and ad to IdToMemberInfoMap
+ MemberInfo d(0, name);
+ m_idToMemberInfoMap.emplace(m_currentId - 1, d);
+ }
+
+ *rgdispid = pair_id.first->second;
+ ret = S_OK;
+ }
+ else
+ ret= DISP_E_UNKNOWNNAME;
+ }
+ return ret;
+}
+
+COM_DECLSPEC_NOTHROW STDMETHODIMP UnoObjectWrapperRemoteOpt::Invoke ( DISPID dispidMember, REFIID /*riid*/, LCID /*lcid*/, WORD wFlags,
+ DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo,
+ UINT * puArgErr )
+{
+ comphelper::Automation::AutomationInvokedZone aAutomationActive;
+
+ HRESULT ret = S_OK;
+ try
+ {
+ bool bHandled= false;
+ ret= InvokeGeneral( dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo,
+ puArgErr, bHandled);
+ if( bHandled)
+ return ret;
+
+ if ( dispidMember > 0 && m_xInvocation.is())
+ {
+
+ IdToMemberInfoMap::iterator it_MemberInfo= m_idToMemberInfoMap.find( dispidMember);
+ if( it_MemberInfo != m_idToMemberInfoMap.end() )
+ {
+ MemberInfo& info= it_MemberInfo->second;
+
+ Sequence<Any> params; // holds converted any s
+ if( ! info.flags )
+ { // DISPID called for the first time
+ if( wFlags == DISPATCH_METHOD )
+ {
+ convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
+
+ if( FAILED( ret= doInvoke( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, info.name, params))
+ && ret == DISP_E_MEMBERNOTFOUND)
+ {
+ // try to get the exact name
+ OUString exactName;
+ if (m_xExactName.is())
+ {
+ exactName = m_xExactName->getExactName( info.name);
+ // invoke again
+ if( !exactName.isEmpty() )
+ {
+ if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, exactName, params)))
+ info.name= exactName;
+ }
+ }
+ }
+ if( SUCCEEDED( ret ) )
+ info.flags= DISPATCH_METHOD;
+ }
+ else if( wFlags == DISPATCH_PROPERTYPUT || wFlags == DISPATCH_PROPERTYPUTREF)
+ {
+ convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
+ if( FAILED( ret= doSetProperty( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, info.name, params))
+ && ret == DISP_E_MEMBERNOTFOUND)
+ {
+ // try to get the exact name
+ OUString exactName;
+ if (m_xExactName.is())
+ {
+ exactName = m_xExactName->getExactName( info.name);
+ // invoke again
+ if( !exactName.isEmpty() )
+ {
+ if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, exactName, params)))
+ info.name= exactName;
+ }
+ }
+ }
+ if( SUCCEEDED( ret ) )
+ info.flags= DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYGET;
+ }
+ else if( wFlags == DISPATCH_PROPERTYGET)
+ {
+ if( FAILED( ret= doGetProperty( pdispparams, pvarResult,
+ pexcepinfo, info.name))
+ && ret == DISP_E_MEMBERNOTFOUND)
+ {
+ // try to get the exact name
+ OUString exactName;
+ if (m_xExactName.is())
+ {
+ exactName = m_xExactName->getExactName( info.name);
+ // invoke again
+ if( !exactName.isEmpty() )
+ {
+ if( SUCCEEDED( ret= doGetProperty( pdispparams, pvarResult,
+ pexcepinfo, exactName)))
+ info.name= exactName;
+ }
+ }
+ }
+ if( SUCCEEDED( ret ) )
+ info.flags= DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT;
+ }
+ else if( wFlags & DISPATCH_METHOD &&
+ (wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF))
+ {
+
+ OUString exactName;
+ // convert params for DISPATCH_METHOD or DISPATCH_PROPERTYPUT
+ convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
+ // try first as method
+ if( FAILED( ret= doInvoke( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, info.name, params))
+ && ret == DISP_E_MEMBERNOTFOUND)
+ {
+ // try to get the exact name
+ if (m_xExactName.is())
+ {
+ exactName = m_xExactName->getExactName( info.name);
+ // invoke again
+ if( !exactName.isEmpty() )
+ {
+ if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, exactName, params)))
+ info.name= exactName;
+ }
+ }
+ }
+ if( SUCCEEDED( ret ) )
+ info.flags= DISPATCH_METHOD;
+
+ // try as property
+ if( FAILED( ret) && pdispparams->cArgs == 1)
+ {
+ if( FAILED( ret= doSetProperty( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, info.name, params))
+ && ret == DISP_E_MEMBERNOTFOUND)
+ {
+ // try to get the exact name
+ if( !exactName.isEmpty() )
+ {
+ if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, exactName, params)))
+ info.name= exactName;
+ }
+ }
+ if( SUCCEEDED( ret ) )
+ info.flags= DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYGET;
+ }
+ }
+ else if( wFlags & DISPATCH_METHOD && wFlags & DISPATCH_PROPERTYGET)
+ {
+ OUString exactName;
+ convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
+
+ if( FAILED( ret= doInvoke( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, info.name, params))
+ && ret == DISP_E_MEMBERNOTFOUND)
+ {
+ // try to get the exact name
+ if (m_xExactName.is())
+ {
+ exactName = m_xExactName->getExactName( info.name);
+ // invoke again
+ if( !exactName.isEmpty() )
+ {
+ if( SUCCEEDED( ret= doInvoke( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, exactName, params)))
+ info.name= exactName;
+ }
+ }
+ }
+ if( SUCCEEDED( ret ) )
+ info.flags= DISPATCH_METHOD;
+
+ // try as property
+ if( FAILED( ret) && pdispparams->cArgs == 1)
+ {
+ if( FAILED( ret= doGetProperty( pdispparams, pvarResult,
+ pexcepinfo, info.name))
+ && ret == DISP_E_MEMBERNOTFOUND)
+ {
+ if( !exactName.isEmpty() )
+ {
+ if( SUCCEEDED( ret= doSetProperty( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, exactName, params)))
+ info.name= exactName;
+ }
+ }
+ if( SUCCEEDED( ret ) )
+ info.flags= DISPATCH_PROPERTYGET;
+ }
+ }
+
+ // update information about this member
+ if( ret == DISP_E_MEMBERNOTFOUND)
+ {
+ // Remember the name as not existing
+ // and remove the MemberInfo
+ m_badNameMap[info.name]= false;
+ m_idToMemberInfoMap.erase( it_MemberInfo);
+ }
+ } // if( ! info.flags )
+ else // IdToMemberInfoMap contains a MemberInfo
+ {
+ if( wFlags & DISPATCH_METHOD && info.flags == DISPATCH_METHOD)
+ {
+ convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
+ ret= doInvoke( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, info.name, params);
+ }
+ else if( (wFlags & DISPATCH_PROPERTYPUT || wFlags & DISPATCH_PROPERTYPUTREF ) &&
+ info.flags & DISPATCH_PROPERTYPUT)
+ {
+ convertDispparamsArgs(dispidMember, wFlags, pdispparams, params );
+ ret= doSetProperty( pdispparams, pvarResult,
+ pexcepinfo, puArgErr, info.name, params);
+ }
+ else if( (wFlags & DISPATCH_PROPERTYGET) && ( info.flags & DISPATCH_PROPERTYGET))
+ {
+ ret= doGetProperty( pdispparams, pvarResult,
+ pexcepinfo, info.name);
+ }
+ else
+ {
+ ret= DISP_E_MEMBERNOTFOUND;
+ }
+ }
+ }// if( it_MemberInfo != m_idToMemberInfoMap.end() )
+ else
+ ret= DISP_E_MEMBERNOTFOUND;
+ }
+ }
+ catch(const BridgeRuntimeError& e)
+ {
+ writeExcepinfo(pexcepinfo, e.message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch(const Exception& e)
+ {
+ OUString message= "UnoObjectWrapperRemoteOpt::Invoke : \n" +
+ e.Message;
+ writeExcepinfo(pexcepinfo, message);
+ ret = DISP_E_EXCEPTION;
+ }
+ catch(...)
+ {
+ writeExcepinfo(pexcepinfo, "UnoObjectWrapperRemoteOpt::Invoke : \nUnexpected exception");
+ ret = DISP_E_EXCEPTION;
+ }
+
+ return ret;
+}
+
+HRESULT UnoObjectWrapperRemoteOpt::methodInvoke( DISPID /*dispidMember*/, DISPPARAMS * /*pdispparams*/, VARIANT * /*pvarResult*/,
+ EXCEPINFO * /*pexcepinfo*/, unsigned int * /*puArgErr*/, Sequence<Any> const &)
+{
+ return S_OK;
+}
+
+// The returned HRESULT is only appropriate for IDispatch::Invoke
+static HRESULT mapCannotConvertException(const CannotConvertException &e, unsigned int * puArgErr)
+{
+ HRESULT ret;
+ bool bWriteIndex= true;
+
+ switch ( e.Reason)
+ {
+ case FailReason::OUT_OF_RANGE:
+ ret = DISP_E_OVERFLOW;
+ break;
+ case FailReason::IS_NOT_NUMBER:
+ ret = DISP_E_TYPEMISMATCH;
+ break;
+ case FailReason::IS_NOT_ENUM:
+ ret = DISP_E_TYPEMISMATCH;
+ break;
+ case FailReason::IS_NOT_BOOL:
+ ret = DISP_E_TYPEMISMATCH;
+ break;
+ case FailReason::NO_SUCH_INTERFACE:
+ ret = DISP_E_TYPEMISMATCH;
+ break;
+ case FailReason::SOURCE_IS_NO_DERIVED_TYPE:
+ ret = DISP_E_TYPEMISMATCH;
+ break;
+ case FailReason::TYPE_NOT_SUPPORTED:
+ ret = DISP_E_TYPEMISMATCH;
+ break;
+ case FailReason::INVALID:
+ ret = DISP_E_TYPEMISMATCH;
+ break;
+ case FailReason::NO_DEFAULT_AVAILABLE:
+ ret = DISP_E_BADPARAMCOUNT;
+ break;
+ case FailReason::UNKNOWN:
+ ret = E_UNEXPECTED;
+ break;
+ default:
+ ret = E_UNEXPECTED;
+ bWriteIndex= false;
+ break;
+ }
+
+ if( bWriteIndex && puArgErr != nullptr)
+ *puArgErr = e.ArgumentIndex;
+ return ret;
+}
+
+// The function maps the TypeClass of the any to VARTYPE: If
+// the Any contains STRUCT or INTERFACE then the return value
+// is VT_DISPATCH. The function is used from o2u_createUnoObjectWrapper
+// and the result is put into the constructor of the uno - wrapper
+// object. If a client asks the object for DISPID_VALUE and this
+// function returned VT_DISPATCH then the IDispatch of the same
+// object is being returned.
+// See InterfaceOleWrapper::Invoke, InterfaceOleWrapper::m_defaultValueType
+VARTYPE getVarType( const Any& value)
+{
+ VARTYPE ret= VT_EMPTY;
+
+ switch ( value.getValueTypeClass())
+ {
+ case TypeClass_STRUCT: ret= VT_DISPATCH; break;
+ case TypeClass_INTERFACE: ret= VT_DISPATCH; break;
+ default: break;
+ }
+ return ret;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/unoobjw.hxx b/extensions/source/ole/unoobjw.hxx
new file mode 100644
index 000000000..845724d81
--- /dev/null
+++ b/extensions/source/ole/unoobjw.hxx
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/bridge/XBridgeSupplier2.hpp>
+#include <com/sun/star/beans/XExactName.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/script/InvocationInfo.hpp>
+#include <salhelper/simplereferenceobject.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include "comifaces.hxx"
+#include "ole2uno.hxx"
+#include "unoconversionutilities.hxx"
+
+#define JSCRIPT_VALUE_FUNC L"_GetValueObject"
+#define GET_STRUCT_FUNC L"_GetStruct"
+#define BRIDGE_VALUE_FUNC L"Bridge_GetValueObject"
+#define BRIDGE_GET_STRUCT_FUNC L"Bridge_GetStruct"
+#define BRIDGE_CREATE_TYPE_FUNC L"Bridge_CreateType"
+
+#define DISPID_JSCRIPT_VALUE_FUNC -10l
+#define DISPID_GET_STRUCT_FUNC -102
+#define DISPID_CREATE_TYPE_FUNC -103
+
+using namespace cppu;
+using namespace com::sun::star::bridge;
+using namespace com::sun::star::script;
+
+struct MemberInfo
+{
+ MemberInfo() : flags(0), name() {}
+ MemberInfo(WORD f, const OUString& n) : flags(f), name(n) {}
+
+ WORD flags;
+ OUString name;
+};
+
+typedef std::unordered_map
+<
+ OUString,
+ DISPID
+> NameToIdMap;
+
+typedef std::unordered_map
+<
+ OUString,
+ bool
+> BadNameMap;
+
+typedef std::unordered_map
+<
+ DISPID,
+ MemberInfo
+> IdToMemberInfoMap;
+
+// An InterfaceOleWrapper object can wrap either a UNO struct or a UNO
+// interface as a COM IDispatchEx and IUnoObjectWrapper.
+
+class InterfaceOleWrapper : public WeakImplHelper<XBridgeSupplier2, XInitialization>,
+ public IDispatchEx,
+ public IProvideClassInfo,
+ public IConnectionPointContainer,
+ public UnoConversionUtilities<InterfaceOleWrapper>,
+ public IUnoObjectWrapper
+{
+public:
+ InterfaceOleWrapper(Reference<XMultiServiceFactory> const & xFactory, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass);
+ ~InterfaceOleWrapper() override;
+
+ // IUnknown
+ STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj) override;
+ STDMETHOD_(ULONG, AddRef)() override;
+ STDMETHOD_(ULONG, Release)() override;
+
+ // IDispatch
+ STDMETHOD( GetTypeInfoCount )( UINT * pctinfo ) override;
+ STDMETHOD( GetTypeInfo )( UINT itinfo, LCID lcid, ITypeInfo ** pptinfo ) override;
+ STDMETHOD( GetIDsOfNames )( REFIID riid, LPOLESTR * rgszNames, UINT cNames,
+ LCID lcid, DISPID * rgdispid ) override;
+ STDMETHOD( Invoke )( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
+ DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo,
+ UINT * puArgErr ) override;
+
+ // IDispatchEx
+ virtual HRESULT STDMETHODCALLTYPE GetDispID(
+ /* [in] */ BSTR bstrName,
+ /* [in] */ DWORD grfdex,
+ /* [out] */ DISPID __RPC_FAR *pid) override;
+
+ virtual /* [local] */ HRESULT STDMETHODCALLTYPE InvokeEx(
+ /* [in] */ DISPID id,
+ /* [in] */ LCID lcid,
+ /* [in] */ WORD wFlags,
+ /* [in] */ DISPPARAMS __RPC_FAR *pdp,
+ /* [out] */ VARIANT __RPC_FAR *pvarRes,
+ /* [out] */ EXCEPINFO __RPC_FAR *pei,
+ /* [unique][in] */ IServiceProvider __RPC_FAR *pspCaller) override;
+
+ virtual HRESULT STDMETHODCALLTYPE DeleteMemberByName(
+ /* [in] */ BSTR bstr,
+ /* [in] */ DWORD grfdex) override;
+
+ virtual HRESULT STDMETHODCALLTYPE DeleteMemberByDispID(
+ /* [in] */ DISPID id) override;
+
+ virtual HRESULT STDMETHODCALLTYPE GetMemberProperties(
+ /* [in] */ DISPID id,
+ /* [in] */ DWORD grfdexFetch,
+ /* [out] */ DWORD __RPC_FAR *pgrfdex) override;
+
+ virtual HRESULT STDMETHODCALLTYPE GetMemberName(
+ /* [in] */ DISPID id,
+ /* [out] */ BSTR __RPC_FAR *pbstrName) override;
+
+ virtual HRESULT STDMETHODCALLTYPE GetNextDispID(
+ /* [in] */ DWORD grfdex,
+ /* [in] */ DISPID id,
+ /* [out] */ DISPID __RPC_FAR *pid) override;
+
+ virtual HRESULT STDMETHODCALLTYPE GetNameSpaceParent(
+ /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppunk) override;
+
+ // IProvideClassInfo
+ virtual HRESULT STDMETHODCALLTYPE GetClassInfo(
+ /* [out] */ ITypeInfo **ppTI) override;
+
+ // IConnectionPointContainer
+ virtual HRESULT STDMETHODCALLTYPE EnumConnectionPoints(
+ /* [out] */ IEnumConnectionPoints **ppEnum) override;
+ virtual HRESULT STDMETHODCALLTYPE FindConnectionPoint(
+ /* [in] */ REFIID riid,
+ /* [out] */ IConnectionPoint **ppCP) override;
+
+ // XBridgeSupplier2
+ virtual Any SAL_CALL createBridge(const Any& modelDepObject,
+ const Sequence<sal_Int8>& ProcessId,
+ sal_Int16 sourceModelType,
+ sal_Int16 destModelType) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+
+ // IUnoObjectWrapper
+ STDMETHOD( getWrapperXInterface)( Reference<XInterface>* pXInt) override;
+ STDMETHOD( getOriginalUnoObject)( Reference<XInterface>* pXInt) override;
+ STDMETHOD( getOriginalUnoStruct)( Any * pStruct) override;
+
+ // UnoConversionUtility
+ virtual Reference< XInterface > createUnoWrapperInstance() override;
+ virtual Reference< XInterface > createComWrapperInstance() override;
+
+ const OUString& getImplementationName() const
+ {
+ return m_sImplementationName;
+ }
+
+protected:
+ virtual HRESULT doInvoke( DISPPARAMS * pdispparams, VARIANT * pvarResult,
+ EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString & name, Sequence<Any>& params);
+
+ virtual HRESULT doGetProperty( DISPPARAMS * pdispparams, VARIANT * pvarResult,
+ EXCEPINFO * pexcepinfo, OUString & name );
+
+ virtual HRESULT doSetProperty( DISPPARAMS * pdispparams, VARIANT * pvarResult,
+ EXCEPINFO * pexcepinfo, unsigned int * puArgErr, OUString & name, Sequence<Any> const & params);
+
+ virtual HRESULT InvokeGeneral( DISPID dispidMember, unsigned short wFlags,
+ DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo,
+ unsigned int * puArgErr, bool& bHandled);
+
+ void convertDispparamsArgs( DISPID id, unsigned short wFlags, DISPPARAMS* pdispparams,
+ Sequence<Any>& rSeq);
+
+ bool getInvocationInfoForCall(DISPID id, InvocationInfo& info);
+
+ Reference<XInvocation> m_xInvocation;
+ Reference<XExactName> m_xExactName;
+ Reference<XInterface> m_xOrigin;
+ NameToIdMap m_nameToDispIdMap;
+ std::vector<MemberInfo> m_MemberInfos;
+ // This member is used to determine the default value
+ // denoted by DISPID_VALUE (0). For proper results in JavaScript
+ // we have to return the default value when we write an object
+ // as out parameter. That is, we get a JScript Array as parameter
+ // and put a wrapped object on index null. The array object tries
+ // to detect the default value. The wrapped object must then return
+ // its own IDispatch* otherwise we cannot access it within the script.
+ // see InterfaceOleWrapper::Invoke
+ VARTYPE m_defaultValueType;
+
+ // The name of the implementation. Can be empty if unknown.
+ OUString m_sImplementationName;
+};
+
+/*****************************************************************************
+
+ UnoObjectWrapperRemoteOpt = Uno Object Wrapper Remote Optimized
+
+ This is the UNO wrapper used in the service com.sun.star.bridge.OleBridgeSupplierVar1.
+ Key features:
+ DISPIDs are passed out blindly. That is in GetIDsOfNames is no name checking carried out.
+ Only if Invoke fails the name is being checked. Moreover Invoke tries to figure out
+ if a call is made to a property or method if the flags are DISPATCH_METHOD | DISPATCH_PROPERTYPUT.
+ If something has been found out about a property or member than it is saved
+ in a MemberInfo structure hold by an IdToMemberInfoMap stl map.
+
+*****************************************************************************/
+class UnoObjectWrapperRemoteOpt: public InterfaceOleWrapper
+{
+public:
+ UnoObjectWrapperRemoteOpt( Reference<XMultiServiceFactory> const & aFactory, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass);
+ ~UnoObjectWrapperRemoteOpt() override;
+
+ STDMETHOD( GetIDsOfNames )( REFIID riid, LPOLESTR * rgszNames, UINT cNames,
+ LCID lcid, DISPID * rgdispid ) override;
+ STDMETHOD( Invoke )( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
+ DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo,
+ UINT * puArgErr ) override;
+
+ // UnoConversionUtility
+ // If UNO interfaces are converted in methods of this class then
+ // they are always wrapped with instances of this class
+ virtual Reference< XInterface > createUnoWrapperInstance() override;
+
+protected:
+
+ static HRESULT methodInvoke( DISPID dispidMember, DISPPARAMS * pdispparams, VARIANT * pvarResult,
+ EXCEPINFO * pexcepinfo, unsigned int * puArgErr, Sequence<Any> const & params);
+ // In GetIDsOfNames are blindly passed out, that is without verifying
+ // the name. If two names are passed in during different calls to
+ // GetIDsOfNames and the names differ only in their cases then different
+ // id's are passed out ( e.g. "doSomethingMethod" or "dosomethingmethod").
+ // In Invoke the DISPID is remapped to the name passed to GetIDsOfNames
+ // and the name is used as parameter for XInvocation::invoke. If invoke
+ // fails because of a wrong name, then m_xExactName ( XExactName) is used
+ // to verify the name. The correct name is then inserted to m_MemberInfos
+ // ( vector<MemberInfo> ). During the next call to Invoke the right name
+ // is used. .
+
+
+ BadNameMap m_badNameMap;
+
+ IdToMemberInfoMap m_idToMemberInfoMap;
+
+ DISPID m_currentId;
+
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/unotypewrapper.cxx b/extensions/source/ole/unotypewrapper.cxx
new file mode 100644
index 000000000..2824e8ef7
--- /dev/null
+++ b/extensions/source/ole/unotypewrapper.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 "unotypewrapper.hxx"
+#include <rtl/ustring.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+bool createUnoTypeWrapper(BSTR sTypeName, VARIANT * pVar)
+{
+ bool ret = false;
+ OSL_ASSERT(sTypeName && pVar);
+ CComObject< UnoTypeWrapper>* pObj;
+ VariantClear(pVar);
+ if( SUCCEEDED( CComObject<UnoTypeWrapper>::CreateInstance( &pObj)))
+ {
+ pObj->AddRef();
+ pVar->vt= VT_DISPATCH;
+ pVar->pdispVal= CComQIPtr<IDispatch>(pObj->GetUnknown());
+ //now set the value, e.i. the name of the type
+ CComQIPtr<IUnoTypeWrapper> spType(pVar->pdispVal);
+ OSL_ASSERT(spType);
+ if (SUCCEEDED(spType->put_Name(sTypeName)))
+ {
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+
+bool createUnoTypeWrapper(const OUString& sTypeName, VARIANT * pVar)
+{
+ CComBSTR bstr(o3tl::toW(sTypeName.getStr()));
+ return createUnoTypeWrapper(bstr, pVar);
+}
+
+UnoTypeWrapper::UnoTypeWrapper()
+{
+}
+
+UnoTypeWrapper::~UnoTypeWrapper()
+{
+}
+
+
+// UnoTypeWrapper, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::GetTypeInfoCount(UINT* /*pctinfo*/)
+{
+ return E_NOTIMPL;
+}
+
+// UnoTypeWrapper, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::GetTypeInfo( UINT /*iTInfo*/,
+ LCID /*lcid*/,
+ ITypeInfo** /*ppTInfo*/)
+{
+ return E_NOTIMPL;
+}
+
+// UnoTypeWrapper, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::GetIDsOfNames( REFIID /*riid*/,
+ LPOLESTR *rgszNames,
+ UINT /*cNames*/,
+ LCID /*lcid*/,
+ DISPID *rgDispId)
+{
+ if( !rgDispId)
+ return E_POINTER;
+
+ HRESULT ret= S_OK;
+ CComBSTR name(*rgszNames);
+ name.ToLower();
+
+ if( name == CComBSTR( L"name") )
+ *rgDispId= DISPID_VALUE;
+ else
+ ret= DISP_E_UNKNOWNNAME;
+
+ return ret;
+}
+
+// UnoTypeWrapper, IDispatch --------------------------------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::Invoke( DISPID dispIdMember,
+ REFIID /*riid*/,
+ LCID /*lcid*/,
+ WORD wFlags,
+ DISPPARAMS *pDispParams,
+ VARIANT *pVarResult,
+ EXCEPINFO* /*pExcepInfo*/,
+ UINT* /*puArgErr*/)
+{
+ if (pDispParams == nullptr)
+ return DISP_E_EXCEPTION;
+
+ if( pDispParams->cNamedArgs)
+ return DISP_E_NONAMEDARGS;
+
+
+ HRESULT ret= S_OK;
+ switch( dispIdMember)
+ {
+ case DISPID_VALUE: // DISPID_VALUE
+ if (wFlags & DISPATCH_PROPERTYGET)
+ {
+ if (pVarResult == nullptr)
+ {
+ ret = E_POINTER;
+ break;
+ }
+ get_Name( & pVarResult->bstrVal);
+ pVarResult->vt = VT_BSTR;
+ }
+ break;
+ default:
+ ret= DISP_E_MEMBERNOTFOUND;
+ break;
+ }
+
+ return ret;
+}
+
+// IUnoTypeWrapper-----------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::put_Name(BSTR val)
+{
+ Lock();
+ m_sName = val;
+ Unlock();
+ return S_OK;
+}
+
+// (UnoTypeWrapper-----------------------
+COM_DECLSPEC_NOTHROW STDMETHODIMP UnoTypeWrapper::get_Name(BSTR *pVal)
+{
+ Lock();
+ if( !pVal)
+ return E_POINTER;
+ *pVal = m_sName.Copy();
+ Unlock();
+ return S_OK;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/unotypewrapper.hxx b/extensions/source/ole/unotypewrapper.hxx
new file mode 100644
index 000000000..a7515448c
--- /dev/null
+++ b/extensions/source/ole/unotypewrapper.hxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "wincrap.hxx"
+
+#include "comifaces.hxx"
+
+/* creates a UnoTypWrapper and sets the Name property to the value
+ specified by sTypeName.
+ Returns true if the object could be created and initialized.
+ */
+bool createUnoTypeWrapper(BSTR sTypeName, VARIANT * pVariant);
+bool createUnoTypeWrapper(const OUString& sTypeName, VARIANT * pVar);
+
+class UnoTypeWrapper:
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public IUnoTypeWrapper,
+ public IDispatch
+{
+public:
+ UnoTypeWrapper();
+ virtual ~UnoTypeWrapper();
+
+ BEGIN_COM_MAP(UnoTypeWrapper)
+ COM_INTERFACE_ENTRY(IDispatch)
+ COM_INTERFACE_ENTRY(IUnoTypeWrapper)
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winconsistent-missing-override"
+#endif
+ END_COM_MAP()
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+ // IDispatch -------------------------------------------
+ STDMETHOD( GetTypeInfoCount)(UINT *pctinfo) override;
+
+ STDMETHOD( GetTypeInfo)( UINT iTInfo,
+ LCID lcid,
+ ITypeInfo **ppTInfo) override;
+
+ STDMETHOD( GetIDsOfNames)( REFIID riid,
+ LPOLESTR *rgszNames,
+ UINT cNames,
+ LCID lcid,
+ DISPID *rgDispId) override;
+
+ STDMETHOD( Invoke)( DISPID dispIdMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ DISPPARAMS *pDispParams,
+ VARIANT *pVarResult,
+ EXCEPINFO *pExcepInfo,
+ UINT *puArgErr) override;
+ // IUnoTypeWrapper --------------------------------------
+ STDMETHOD(put_Name)(BSTR val) override;
+ STDMETHOD(get_Name)(BSTR* pVal) override;
+
+ CComBSTR m_sName;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/wincrap.hxx b/extensions/source/ole/wincrap.hxx
new file mode 100644
index 000000000..4da57d53c
--- /dev/null
+++ b/extensions/source/ole/wincrap.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
+
+/* wrap all includes that need to be wrapped by prewin.h/postwin.h here */
+
+#define STRICT
+
+#define _WIN32_DCOM
+#if OSL_DEBUG_LEVEL > 0
+//#define _ATL_DEBUG_INTERFACES
+#endif
+
+#include <dispex.h>
+
+#include <prewin.h>
+#include <list>
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wall"
+#pragma clang diagnostic ignored "-Wattributes"
+#pragma clang diagnostic ignored "-Wdelete-incomplete"
+#pragma clang diagnostic ignored "-Wextra"
+#pragma clang diagnostic ignored "-Wint-to-pointer-cast"
+#pragma clang diagnostic ignored "-Winvalid-noreturn"
+#pragma clang diagnostic ignored "-Wmicrosoft"
+#pragma clang diagnostic ignored "-Wnon-pod-varargs"
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+// from oleobjw.hxx
+#include <atlbase.h>
+// from jscriptclasses.hxx
+extern CComModule _Module;
+#include <atlcom.h>
+
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+// from unoobjw.cxx
+#include <olectl.h>
+
+#include <postwin.h>
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/ole/windata.hxx b/extensions/source/ole/windata.hxx
new file mode 100644
index 000000000..edf3c1d45
--- /dev/null
+++ b/extensions/source/ole/windata.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <oleidl.h>
+
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wall"
+#pragma clang diagnostic ignored "-Wextra"
+#pragma clang diagnostic ignored "-Wignored-attributes"
+#pragma clang diagnostic ignored "-Wint-to-pointer-cast"
+#pragma clang diagnostic ignored "-Winvalid-noreturn"
+#pragma clang diagnostic ignored "-Wmicrosoft"
+#pragma clang diagnostic ignored "-Wnon-pod-varargs"
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+#include <atlbase.h>
+
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+#include <osl/diagnose.h>
+
+//Wrapper for VARDESC
+class VarDesc
+{
+ VARDESC* operator = (const VarDesc*);
+ VarDesc(const VarDesc&);
+// Construction
+public:
+ CComPtr< ITypeInfo > m_pTypeInfo;
+ VARDESC* m_pVarDesc;
+
+ explicit VarDesc(ITypeInfo* pTypeInfo) :
+ m_pTypeInfo(pTypeInfo),
+ m_pVarDesc(nullptr)
+ {
+ OSL_ASSERT(pTypeInfo);
+ }
+ ~VarDesc()
+ {
+ if (m_pVarDesc != nullptr)
+ {
+ m_pTypeInfo->ReleaseVarDesc(m_pVarDesc);
+ }
+ }
+
+ VARDESC* operator->()
+ {
+ return m_pVarDesc;
+ }
+
+ VARDESC** operator&()
+ {
+ return &m_pVarDesc;
+ }
+
+ operator VARDESC* ()
+ {
+ return m_pVarDesc;
+ }
+};
+
+//Wrapper for FUNCDESC structure
+class FuncDesc
+{
+ FUNCDESC* operator = (const FuncDesc &);
+ FuncDesc(const FuncDesc&);
+ CComPtr<ITypeInfo> m_pTypeInfo;
+ FUNCDESC * m_pFuncDesc;
+
+public:
+
+ explicit FuncDesc(ITypeInfo * pTypeInfo) :
+ m_pTypeInfo(pTypeInfo),
+ m_pFuncDesc(nullptr)
+ {
+ OSL_ASSERT(pTypeInfo);
+ }
+ ~FuncDesc()
+ {
+ ReleaseFUNCDESC();
+ }
+
+ FUNCDESC* operator -> ()
+ {
+ return m_pFuncDesc;
+ }
+
+ FUNCDESC** operator & ()
+ {
+ return & m_pFuncDesc;
+ }
+
+ operator FUNCDESC* ()
+ {
+ return m_pFuncDesc;
+ }
+
+ FUNCDESC* operator = (FUNCDESC* pDesc)
+ {
+ ReleaseFUNCDESC();
+ m_pFuncDesc = pDesc;
+ return m_pFuncDesc;
+ }
+ FUNCDESC* Detach()
+ {
+ FUNCDESC* pDesc = m_pFuncDesc;
+ m_pFuncDesc = nullptr;
+ return pDesc;
+ }
+
+ void ReleaseFUNCDESC()
+ {
+ if (m_pFuncDesc != nullptr)
+ {
+ m_pTypeInfo->ReleaseFuncDesc(m_pFuncDesc);
+ }
+ m_pFuncDesc = nullptr;
+ }
+};
+//Wrapper for EXCEPINFO structure
+class ExcepInfo : public EXCEPINFO
+{
+ EXCEPINFO* operator = (const ExcepInfo& );
+ ExcepInfo(const ExcepInfo &);
+public:
+ ExcepInfo()
+ : EXCEPINFO{}
+ {
+ }
+ ~ExcepInfo()
+ {
+ if (bstrSource != nullptr)
+ ::SysFreeString(bstrSource);
+ if (bstrDescription != nullptr)
+ ::SysFreeString(bstrDescription);
+ if (bstrHelpFile != nullptr)
+ ::SysFreeString(bstrHelpFile);
+ }
+};
+
+//Wrapper for TYPEATTR
+class TypeAttr
+{
+ TYPEATTR* operator = (const TypeAttr &);
+ TypeAttr(const TypeAttr &);
+public:
+ CComPtr< ITypeInfo > m_pTypeInfo;
+ TYPEATTR* m_pTypeAttr;
+
+ explicit TypeAttr(ITypeInfo* pTypeInfo) :
+ m_pTypeInfo( pTypeInfo ),
+ m_pTypeAttr( nullptr )
+ {
+ OSL_ASSERT(pTypeInfo);
+ }
+ ~TypeAttr() noexcept
+ {
+ if (m_pTypeAttr != nullptr)
+ {
+ m_pTypeInfo->ReleaseTypeAttr(m_pTypeAttr);
+ }
+ }
+
+ TYPEATTR** operator&() noexcept
+ {
+ return &m_pTypeAttr;
+ }
+
+ TYPEATTR* operator->() noexcept
+ {
+ return m_pTypeAttr;
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/MasterDetailLinkDialog.cxx b/extensions/source/propctrlr/MasterDetailLinkDialog.cxx
new file mode 100644
index 000000000..5f38856fc
--- /dev/null
+++ b/extensions/source/propctrlr/MasterDetailLinkDialog.cxx
@@ -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 .
+ */
+
+#include <sal/log.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <vcl/svapp.hxx>
+#include "MasterDetailLinkDialog.hxx"
+#include "formlinkdialog.hxx"
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+
+ MasterDetailLinkDialog::MasterDetailLinkDialog(const Reference< XComponentContext >& _rxContext )
+ :OGenericUnoDialog( _rxContext )
+ {
+ }
+
+ Sequence<sal_Int8> SAL_CALL MasterDetailLinkDialog::getImplementationId( )
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+
+ OUString SAL_CALL MasterDetailLinkDialog::getImplementationName()
+ {
+ return "org.openoffice.comp.form.ui.MasterDetailLinkDialog";
+ }
+
+
+ css::uno::Sequence<OUString> SAL_CALL MasterDetailLinkDialog::getSupportedServiceNames()
+ {
+ return { "com.sun.star.form.MasterDetailLinkDialog" };
+ }
+
+
+ Reference<XPropertySetInfo> SAL_CALL MasterDetailLinkDialog::getPropertySetInfo()
+ {
+ Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+ }
+
+
+ ::cppu::IPropertyArrayHelper& MasterDetailLinkDialog::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+
+ ::cppu::IPropertyArrayHelper* MasterDetailLinkDialog::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties(aProps);
+ return new ::cppu::OPropertyArrayHelper(aProps);
+ }
+
+ std::unique_ptr<weld::DialogController> MasterDetailLinkDialog::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent)
+ {
+ return std::make_unique<FormLinkDialog>(Application::GetFrameWeld(rParent), m_xDetail,
+ m_xMaster, m_aContext, m_sExplanation,
+ m_sDetailLabel, m_sMasterLabel);
+ }
+
+ void MasterDetailLinkDialog::implInitialize(const Any& _rValue)
+ {
+ PropertyValue aProperty;
+ if (_rValue >>= aProperty)
+ {
+ if (aProperty.Name == "Detail")
+ {
+ if ( ! (aProperty.Value >>= m_xDetail) )
+ SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property Detail");
+ return;
+ }
+ else if (aProperty.Name == "Master")
+ {
+ if ( ! (aProperty.Value >>= m_xMaster) )
+ SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property Master");
+ return;
+ }
+ else if (aProperty.Name == "Explanation")
+ {
+ if ( ! (aProperty.Value >>= m_sExplanation) )
+ SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property Explanation");
+ return;
+ }
+ else if (aProperty.Name == "DetailLabel")
+ {
+ if ( ! (aProperty.Value >>= m_sDetailLabel) )
+ SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property DetailLabel");
+ return;
+ }
+ else if (aProperty.Name == "MasterLabel")
+ {
+ if ( ! (aProperty.Value >>= m_sMasterLabel) )
+ SAL_WARN("extensions.propctrlr", "implInitialize: unable to get property MasterLabel");
+ return;
+ }
+ }
+ MasterDetailLinkDialog_DBase::implInitialize(_rValue);
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_MasterDetailLinkDialog_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::MasterDetailLinkDialog(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/MasterDetailLinkDialog.hxx b/extensions/source/propctrlr/MasterDetailLinkDialog.hxx
new file mode 100644
index 000000000..97911436a
--- /dev/null
+++ b/extensions/source/propctrlr/MasterDetailLinkDialog.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 <comphelper/proparrhlp.hxx>
+#include <svtools/genericunodialog.hxx>
+
+namespace pcr
+{
+
+
+ class MasterDetailLinkDialog;
+ typedef ::svt::OGenericUnoDialog MasterDetailLinkDialog_DBase;
+ typedef ::comphelper::OPropertyArrayUsageHelper< MasterDetailLinkDialog > MasterDetailLinkDialog_PBase;
+
+ class MasterDetailLinkDialog : public MasterDetailLinkDialog_DBase
+ ,public MasterDetailLinkDialog_PBase
+ {
+ public:
+ explicit MasterDetailLinkDialog(const css::uno::Reference< css::uno::XComponentContext >& _rxContext);
+
+ private:
+ // XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ // OGenericUnoDialog overridables
+ virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override;
+ virtual void implInitialize(const css::uno::Any& _rValue) override;
+
+ css::uno::Reference< css::beans::XPropertySet> m_xDetail;
+ css::uno::Reference< css::beans::XPropertySet> m_xMaster;
+ OUString m_sExplanation;
+ OUString m_sDetailLabel;
+ OUString m_sMasterLabel;
+ };
+
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/browserline.cxx b/extensions/source/propctrlr/browserline.cxx
new file mode 100644
index 000000000..814c24f4b
--- /dev/null
+++ b/extensions/source/propctrlr/browserline.cxx
@@ -0,0 +1,405 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "browserline.hxx"
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/inspection/PropertyLineElement.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/graphic/XGraphicProvider.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/string.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::inspection::XPropertyControl;
+ using ::com::sun::star::inspection::XPropertyControlContext;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::graphic::GraphicProvider;
+ using ::com::sun::star::graphic::XGraphicProvider;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::graphic::XGraphic;
+
+ namespace PropertyLineElement = ::com::sun::star::inspection::PropertyLineElement;
+
+ OBrowserLine::OBrowserLine(const OUString& rEntryName, weld::Container* pParent, weld::SizeGroup* pLabelGroup,
+ weld::Container* pInitialControlParent)
+ : m_sEntryName(rEntryName)
+ , m_xBuilder(Application::CreateBuilder(pParent, "modules/spropctrlr/ui/browserline.ui"))
+ , m_xContainer(m_xBuilder->weld_container("BrowserLine"))
+ , m_xFtTitle(m_xBuilder->weld_label("label"))
+ , m_xBrowseButton(m_xBuilder->weld_button("browse"))
+ , m_xAdditionalBrowseButton(m_xBuilder->weld_button("morebrowse"))
+ , m_pInitialControlParent(pInitialControlParent) // controls start with this as their parent and need to be moved into m_xContainer
+ , m_pParent(pParent)
+ , m_pControlWindow( nullptr )
+ , m_pBrowseButton(nullptr)
+ , m_pAdditionalBrowseButton( nullptr )
+ , m_pClickListener( nullptr )
+ , m_nNameWidth(0)
+ , m_nEnableFlags( 0xFFFF )
+ , m_bIndentTitle( false )
+ , m_bReadOnly( false )
+ {
+ pLabelGroup->add_widget(m_xFtTitle.get());
+ }
+
+ OBrowserLine::~OBrowserLine()
+ {
+ implHideBrowseButton(true);
+ implHideBrowseButton(false);
+ m_pParent->move(m_xContainer.get(), nullptr);
+ }
+
+ void OBrowserLine::IndentTitle( bool _bIndent )
+ {
+ if ( m_bIndentTitle != _bIndent )
+ {
+ m_bIndentTitle = _bIndent;
+ }
+ }
+
+ void OBrowserLine::SetComponentHelpIds(const OString& rHelpId)
+ {
+ if (m_pControlWindow)
+ m_pControlWindow->set_help_id(rHelpId);
+
+ if ( m_pBrowseButton )
+ {
+ m_pBrowseButton->set_help_id(rHelpId);
+
+ if ( m_pAdditionalBrowseButton )
+ {
+ m_pAdditionalBrowseButton->set_help_id(rHelpId);
+ }
+ }
+ }
+
+ void OBrowserLine::setControl( const Reference< XPropertyControl >& rxControl )
+ {
+ m_xControl = rxControl;
+ auto xWindow = m_xControl->getControlWindow();
+ if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xWindow.get()))
+ m_pControlWindow = pTunnel->getWidget();
+ else
+ m_pControlWindow = nullptr;
+ DBG_ASSERT( m_pControlWindow, "OBrowserLine::setControl: setting NULL controls/windows is not allowed!" );
+
+ if ( m_pControlWindow )
+ {
+ m_pInitialControlParent->move(m_pControlWindow, m_xContainer.get());
+ m_pControlWindow->set_grid_left_attach(1);
+ m_xFtTitle->set_mnemonic_widget(m_pControlWindow);
+ m_pControlWindow->show();
+ }
+ }
+
+ bool OBrowserLine::GrabFocus()
+ {
+ bool bRes=false;
+
+ if (m_pControlWindow && m_pControlWindow->get_sensitive())
+ {
+ m_pControlWindow->grab_focus();
+ bRes = true;
+ }
+ else if ( m_pAdditionalBrowseButton && m_pAdditionalBrowseButton->get_sensitive() )
+ {
+ m_pAdditionalBrowseButton->grab_focus();
+ bRes = true;
+ }
+ else if ( m_pBrowseButton && m_pBrowseButton->get_sensitive() )
+ {
+ m_pBrowseButton->grab_focus();
+ bRes = true;
+ }
+ return bRes;
+ }
+
+ void OBrowserLine::Show(bool bFlag)
+ {
+ m_xFtTitle->set_visible(bFlag);
+ if (m_pControlWindow)
+ m_pControlWindow->set_visible( bFlag );
+ if ( m_pBrowseButton )
+ m_pBrowseButton->set_visible( bFlag );
+ if ( m_pAdditionalBrowseButton )
+ m_pAdditionalBrowseButton->set_visible( bFlag );
+ }
+
+ void OBrowserLine::Hide()
+ {
+ Show(false);
+ }
+
+ void OBrowserLine::SetTitle(const OUString& rNewTitle )
+ {
+ if ( GetTitle() == rNewTitle )
+ return;
+ m_xFtTitle->set_label( rNewTitle );
+ if (m_pControlWindow)
+ m_pControlWindow->set_accessible_name(rNewTitle);
+ if ( m_pBrowseButton )
+ m_pBrowseButton->set_accessible_name( rNewTitle );
+ FullFillTitleString();
+ }
+
+ void OBrowserLine::FullFillTitleString()
+ {
+ OUStringBuffer aText(m_xFtTitle->get_label());
+
+ int n10DotsWidth = m_xFtTitle->get_pixel_size("..........").Width();
+ int nTextWidth = m_xFtTitle->get_pixel_size(OUString::unacquired(aText)).Width();
+ int nDiff = m_nNameWidth - nTextWidth;
+ int nExtraChars = (nDiff * 10) / n10DotsWidth;
+ for (int i = 0; i < nExtraChars; ++i)
+ aText.append(".");
+
+ // for Issue 69452
+ if (AllSettings::GetLayoutRTL())
+ {
+ sal_Unicode const cRTL_mark = 0x200F;
+ aText.append( cRTL_mark );
+ }
+
+ m_xFtTitle->set_label(aText.makeStringAndClear());
+ }
+
+ OUString OBrowserLine::GetTitle() const
+ {
+ OUString sDisplayName = m_xFtTitle->get_label();
+
+ // for Issue 69452
+ if (AllSettings::GetLayoutRTL())
+ {
+ sal_Unicode const cRTL_mark = 0x200F;
+ sDisplayName = comphelper::string::stripEnd(sDisplayName, cRTL_mark);
+ }
+
+ sDisplayName = comphelper::string::stripEnd(sDisplayName, '.');
+
+ return sDisplayName;
+ }
+
+ void OBrowserLine::SetReadOnly( bool _bReadOnly )
+ {
+ if ( m_bReadOnly != _bReadOnly )
+ {
+ m_bReadOnly = _bReadOnly;
+ implUpdateEnabledDisabled();
+ }
+ }
+
+ namespace
+ {
+ void implSetBitIfAffected(sal_uInt16& nEnabledBits, sal_Int16 _nAffectedMask, sal_Int16 _nTestBit, bool _bSet)
+ {
+ if ( _nAffectedMask & _nTestBit )
+ {
+ if ( _bSet )
+ nEnabledBits |= _nTestBit;
+ else
+ nEnabledBits &= ~_nTestBit;
+ }
+ }
+
+ void implEnable(weld::Widget* pWindow, bool bEnable)
+ {
+ // tdf#138131 get_sensitive comparison as bodge for
+ // vcl's recursive Enable behavior
+ if (pWindow && pWindow->get_sensitive() != bEnable)
+ pWindow->set_sensitive(bEnable);
+ }
+
+ void implEnable(weld::Widget* pWindow, sal_uInt16 nEnabledBits, sal_uInt16 nMatchBits)
+ {
+ bool bEnable = ((nEnabledBits & nMatchBits) == nMatchBits);
+ implEnable(pWindow, bEnable);
+ }
+ }
+
+ void OBrowserLine::implUpdateEnabledDisabled()
+ {
+ implEnable( m_xFtTitle.get(), m_nEnableFlags, PropertyLineElement::CompleteLine );
+ if ( m_pControlWindow )
+ implEnable( m_pControlWindow, m_nEnableFlags, PropertyLineElement::CompleteLine | PropertyLineElement::InputControl );
+
+ if ( m_bReadOnly )
+ {
+ implEnable( m_pBrowseButton, false );
+ implEnable( m_pAdditionalBrowseButton, false );
+ }
+ else
+ {
+ implEnable( m_pBrowseButton, m_nEnableFlags, PropertyLineElement::CompleteLine | PropertyLineElement::PrimaryButton );
+ implEnable( m_pAdditionalBrowseButton, m_nEnableFlags, PropertyLineElement::CompleteLine | PropertyLineElement::SecondaryButton );
+ }
+ }
+
+ void OBrowserLine::EnablePropertyLine( bool _bEnable )
+ {
+ implSetBitIfAffected( m_nEnableFlags, PropertyLineElement::CompleteLine, PropertyLineElement::CompleteLine, _bEnable );
+ implUpdateEnabledDisabled();
+ }
+
+
+ void OBrowserLine::EnablePropertyControls( sal_Int16 _nControls, bool _bEnable )
+ {
+ implSetBitIfAffected( m_nEnableFlags, _nControls, PropertyLineElement::InputControl, _bEnable );
+ implSetBitIfAffected( m_nEnableFlags, _nControls, PropertyLineElement::PrimaryButton, _bEnable );
+ implSetBitIfAffected( m_nEnableFlags, _nControls, PropertyLineElement::SecondaryButton, _bEnable );
+ implUpdateEnabledDisabled();
+ }
+
+ weld::Button& OBrowserLine::impl_ensureButton(bool bPrimary)
+ {
+ weld::Button* pButton;
+ if (bPrimary)
+ pButton = m_pBrowseButton;
+ else
+ pButton = m_pAdditionalBrowseButton;
+
+ if (!pButton )
+ {
+ if (bPrimary)
+ pButton = m_pBrowseButton = m_xBrowseButton.get();
+ else
+ pButton = m_pAdditionalBrowseButton = m_xAdditionalBrowseButton.get();
+ pButton->connect_focus_in(LINK(this, OBrowserLine, OnButtonFocus));
+ pButton->connect_clicked(LINK(this, OBrowserLine, OnButtonClicked));
+ }
+
+ pButton->show();
+
+ return *pButton;
+ }
+
+ void OBrowserLine::ShowBrowseButton( const OUString& rImageURL, bool bPrimary )
+ {
+ weld::Button& rButton( impl_ensureButton( bPrimary ) );
+
+ OSL_PRECOND( !rImageURL.isEmpty(), "OBrowserLine::ShowBrowseButton: use the other version if you don't have an image!" );
+ Reference<XGraphic> xGraphic;
+ try
+ {
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ Reference< XGraphicProvider > xGraphicProvider( GraphicProvider::create(xContext) );
+
+ Sequence aMediaProperties{ comphelper::makePropertyValue("URL", rImageURL) };
+
+ xGraphic = Reference<XGraphic>(xGraphicProvider->queryGraphic(aMediaProperties), css::uno::UNO_SET_THROW);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ rButton.set_image(xGraphic);
+ }
+
+ void OBrowserLine::ShowBrowseButton(const css::uno::Reference<css::graphic::XGraphic>& rGraphic, bool bPrimary)
+ {
+ weld::Button& rButton( impl_ensureButton( bPrimary ) );
+ rButton.set_image(rGraphic);
+ }
+
+ void OBrowserLine::ShowBrowseButton( bool bPrimary )
+ {
+ impl_ensureButton(bPrimary);
+ }
+
+ void OBrowserLine::implHideBrowseButton(bool bPrimary)
+ {
+ if (bPrimary)
+ {
+ if (m_pBrowseButton)
+ {
+ m_pBrowseButton->hide();
+ m_pBrowseButton->connect_focus_in(Link<weld::Widget&, void>());
+ m_pBrowseButton = nullptr;
+ }
+ }
+ else
+ {
+ if (m_pAdditionalBrowseButton)
+ {
+ m_pAdditionalBrowseButton->hide();
+ m_pAdditionalBrowseButton->connect_focus_in(Link<weld::Widget&, void>());
+ m_pAdditionalBrowseButton = nullptr;
+ }
+ }
+ }
+
+ void OBrowserLine::HideBrowseButton(bool bPrimary)
+ {
+ implHideBrowseButton(bPrimary);
+ }
+
+ void OBrowserLine::SetTitleWidth(sal_uInt16 nWidth)
+ {
+ int nMinDotsWidth = m_xFtTitle->get_pixel_size("...").Width();
+ if (m_nNameWidth != nWidth + nMinDotsWidth)
+ m_nNameWidth = nWidth + nMinDotsWidth;
+ FullFillTitleString();
+ }
+
+ void OBrowserLine::SetClickListener( IButtonClickListener* _pListener )
+ {
+ m_pClickListener = _pListener;
+ }
+
+ IMPL_LINK(OBrowserLine, OnButtonClicked, weld::Button&, rButton, void)
+ {
+ if ( m_pClickListener )
+ m_pClickListener->buttonClicked(this, &rButton == m_pBrowseButton);
+ }
+
+ IMPL_LINK_NOARG( OBrowserLine, OnButtonFocus, weld::Widget&, void )
+ {
+ if ( m_xControl.is() )
+ {
+ try
+ {
+ Reference< XPropertyControlContext > xContext( m_xControl->getControlContext(), css::uno::UNO_SET_THROW );
+ xContext->focusGained( m_xControl );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+ }
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/browserline.hxx b/extensions/source/propctrlr/browserline.hxx
new file mode 100644
index 000000000..db33b7a55
--- /dev/null
+++ b/extensions/source/propctrlr/browserline.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/inspection/XPropertyControl.hpp>
+#include <vcl/weld.hxx>
+
+namespace com::sun::star::inspection::PropertyLineElement
+{
+ const sal_Int16 CompleteLine = 0x4000;
+}
+
+
+namespace pcr
+{
+
+
+ class OBrowserLine;
+
+
+ class IButtonClickListener
+ {
+ public:
+ virtual void buttonClicked( OBrowserLine* pLine, bool bPrimary ) = 0;
+
+ protected:
+ ~IButtonClickListener() {}
+ };
+
+
+ class OBrowserLine
+ {
+ private:
+ OUString m_sEntryName;
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<weld::Label> m_xFtTitle;
+ std::unique_ptr<weld::Button> m_xBrowseButton;
+ std::unique_ptr<weld::Button> m_xAdditionalBrowseButton;
+ css::uno::Reference< css::inspection::XPropertyControl >
+ m_xControl;
+ weld::Container* m_pInitialControlParent;
+ weld::Container* m_pParent;
+ weld::Widget* m_pControlWindow;
+ weld::Button* m_pBrowseButton;
+ weld::Button* m_pAdditionalBrowseButton;
+ IButtonClickListener* m_pClickListener;
+ sal_uInt16 m_nNameWidth;
+ sal_uInt16 m_nEnableFlags;
+ bool m_bIndentTitle;
+ bool m_bReadOnly;
+
+ public:
+ OBrowserLine(const OUString& rEntryName, weld::Container* pParent, weld::SizeGroup* pLabelGroup,
+ weld::Container* pInitialControlParent);
+ ~OBrowserLine();
+
+ void setControl( const css::uno::Reference< css::inspection::XPropertyControl >& rxControl );
+ const css::uno::Reference< css::inspection::XPropertyControl >& getControl() const
+ {
+ return m_xControl;
+ }
+ weld::Widget* getControlWindow() const
+ {
+ return m_pControlWindow;
+ }
+
+ const OUString& GetEntryName() const { return m_sEntryName; }
+
+ void SetComponentHelpIds(const OString& rHelpId);
+
+ void SetTitle(const OUString& rString );
+ void FullFillTitleString();
+ OUString GetTitle() const;
+ void SetTitleWidth(sal_uInt16);
+
+ int GetRowHeight() const { return m_xContainer->get_preferred_size().Height(); }
+ void Show(bool bFlag=true);
+ void Hide();
+
+ bool GrabFocus();
+ void ShowBrowseButton( const OUString& rImageURL, bool bPrimary );
+ void ShowBrowseButton( const css::uno::Reference<css::graphic::XGraphic>& rGraphic, bool bPrimary );
+ void ShowBrowseButton( bool bPrimary );
+ void HideBrowseButton( bool bPrimary );
+
+ void EnablePropertyControls( sal_Int16 nControls, bool bEnable );
+ void EnablePropertyLine( bool bEnable );
+
+ void SetReadOnly( bool bReadOnly );
+
+ void SetClickListener( IButtonClickListener* pListener );
+
+ void IndentTitle( bool bIndent );
+
+ private:
+ DECL_LINK( OnButtonClicked, weld::Button&, void );
+ DECL_LINK( OnButtonFocus, weld::Widget&, void );
+
+ void implHideBrowseButton(bool bPrimary);
+ void implUpdateEnabledDisabled();
+
+ weld::Button& impl_ensureButton(bool bPrimary);
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/browserlistbox.cxx b/extensions/source/propctrlr/browserlistbox.cxx
new file mode 100644
index 000000000..04e9e44b3
--- /dev/null
+++ b/extensions/source/propctrlr/browserlistbox.cxx
@@ -0,0 +1,817 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "browserlistbox.hxx"
+#include "pcrcommon.hxx"
+#include "proplinelistener.hxx"
+#include "propcontrolobserver.hxx"
+#include "linedescriptor.hxx"
+#include "inspectorhelpwindow.hxx"
+
+#include <sal/log.hxx>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/asyncnotification.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+
+
+namespace pcr
+{
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::inspection::XPropertyControlContext;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::inspection::XPropertyControl;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::lang::XComponent;
+ using ::com::sun::star::uno::UNO_QUERY;
+
+ namespace PropertyControlType = ::com::sun::star::inspection::PropertyControlType;
+
+ namespace {
+
+ enum ControlEventType
+ {
+ FOCUS_GAINED,
+ VALUE_CHANGED,
+ ACTIVATE_NEXT
+ };
+
+ struct ControlEvent : public ::comphelper::AnyEvent
+ {
+ Reference< XPropertyControl > xControl;
+ ControlEventType eType;
+
+ ControlEvent( const Reference< XPropertyControl >& _rxControl, ControlEventType _eType )
+ :xControl( _rxControl )
+ ,eType( _eType )
+ {
+ }
+ };
+
+ class SharedNotifier
+ {
+ private:
+ static ::osl::Mutex& getMutex();
+ static ::rtl::Reference< ::comphelper::AsyncEventNotifier > s_pNotifier;
+
+ public:
+ SharedNotifier(const SharedNotifier&) = delete;
+ SharedNotifier& operator=(const SharedNotifier&) = delete;
+ static const ::rtl::Reference< ::comphelper::AsyncEventNotifier >&
+ getNotifier();
+ };
+
+ }
+
+ ::rtl::Reference< ::comphelper::AsyncEventNotifier > SharedNotifier::s_pNotifier;
+
+
+ ::osl::Mutex& SharedNotifier::getMutex()
+ {
+ static ::osl::Mutex s_aMutex;
+ return s_aMutex;
+ }
+
+
+ const ::rtl::Reference< ::comphelper::AsyncEventNotifier >& SharedNotifier::getNotifier()
+ {
+ ::osl::MutexGuard aGuard( getMutex() );
+ if ( !s_pNotifier.is() )
+ {
+ s_pNotifier.set(
+ new ::comphelper::AsyncEventNotifier("browserlistbox"));
+ s_pNotifier->launch();
+ //TODO: a protocol is missing how to join with the launched
+ // thread before exit(3), to ensure the thread is no longer
+ // relying on any infrastructure while that infrastructure is
+ // being shut down in atexit handlers
+ }
+ return s_pNotifier;
+ }
+
+
+ /** implementation for of <type scope="css::inspection">XPropertyControlContext</type>
+ which forwards all events to a non-UNO version of this interface
+ */
+ typedef ::cppu::WeakImplHelper< XPropertyControlContext > PropertyControlContext_Impl_Base;
+ class PropertyControlContext_Impl :public PropertyControlContext_Impl_Base
+ ,public ::comphelper::IEventProcessor
+ {
+ public:
+ enum NotificationMode
+ {
+ eSynchronously,
+ eAsynchronously
+ };
+
+ private:
+ OBrowserListBox* m_pContext;
+ NotificationMode m_eMode;
+
+ public:
+ /** creates an instance
+ @param _rContextImpl
+ the instance to delegate events to
+ */
+ explicit PropertyControlContext_Impl( OBrowserListBox& _rContextImpl );
+
+ /** disposes the context.
+
+ When you call this method, all subsequent callbacks to the
+ <type scope="css::inspection">XPropertyControlContext</type> methods
+ will throw a <type scope="css::lang">DisposedException</type>.
+ */
+ void dispose();
+
+ /** sets the notification mode, so that notifications received from the controls are
+ forwarded to our OBrowserListBox either synchronously or asynchronously
+ @param _eMode
+ the new notification mode
+ */
+ void setNotificationMode( NotificationMode _eMode );
+
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ protected:
+ virtual ~PropertyControlContext_Impl() override;
+
+ // XPropertyControlObserver
+ virtual void SAL_CALL focusGained( const Reference< XPropertyControl >& Control ) override;
+ virtual void SAL_CALL valueChanged( const Reference< XPropertyControl >& Control ) override;
+ // XPropertyControlContext
+ virtual void SAL_CALL activateNextControl( const Reference< XPropertyControl >& CurrentControl ) override;
+
+ // IEventProcessor
+ virtual void processEvent( const ::comphelper::AnyEvent& _rEvent ) override;
+
+ private:
+ /** processes the given event, i.e. notifies it to our OBrowserListBox
+ @param _rEvent
+ the event no notify
+ @precond
+ our mutex (well, the SolarMutex) is locked
+ */
+ void impl_processEvent_throw( const ::comphelper::AnyEvent& _rEvent );
+
+ /** checks whether the instance is already disposed
+ */
+ bool impl_isDisposed_nothrow() const { return m_pContext == nullptr; }
+
+ /** notifies the given event originating from the given control
+ @throws DisposedException
+ @param _rxControl
+ @param _eType
+ */
+ void impl_notify_throw( const Reference< XPropertyControl >& _rxControl, ControlEventType _eType );
+ };
+
+ PropertyControlContext_Impl::PropertyControlContext_Impl( OBrowserListBox& _rContextImpl )
+ : m_pContext( &_rContextImpl )
+ , m_eMode( eAsynchronously )
+ {
+ }
+
+ PropertyControlContext_Impl::~PropertyControlContext_Impl()
+ {
+ if ( !impl_isDisposed_nothrow() )
+ dispose();
+ }
+
+ void PropertyControlContext_Impl::dispose()
+ {
+ SolarMutexGuard aGuard;
+ if ( impl_isDisposed_nothrow() )
+ return;
+
+ SharedNotifier::getNotifier()->removeEventsForProcessor( this );
+ m_pContext = nullptr;
+ }
+
+ void PropertyControlContext_Impl::setNotificationMode( NotificationMode _eMode )
+ {
+ SolarMutexGuard aGuard;
+ m_eMode = _eMode;
+ }
+
+ void PropertyControlContext_Impl::impl_notify_throw( const Reference< XPropertyControl >& _rxControl, ControlEventType _eType )
+ {
+ ::comphelper::AnyEventRef pEvent;
+
+ {
+ SolarMutexGuard aGuard;
+ if ( impl_isDisposed_nothrow() )
+ throw DisposedException( OUString(), *this );
+ pEvent = new ControlEvent( _rxControl, _eType );
+
+ if ( m_eMode == eSynchronously )
+ {
+ impl_processEvent_throw( *pEvent );
+ return;
+ }
+ }
+
+ SharedNotifier::getNotifier()->addEvent( pEvent, this );
+ }
+
+ void SAL_CALL PropertyControlContext_Impl::focusGained( const Reference< XPropertyControl >& Control )
+ {
+ impl_notify_throw( Control, FOCUS_GAINED );
+ }
+
+ void SAL_CALL PropertyControlContext_Impl::valueChanged( const Reference< XPropertyControl >& Control )
+ {
+ impl_notify_throw( Control, VALUE_CHANGED );
+ }
+
+ void SAL_CALL PropertyControlContext_Impl::activateNextControl( const Reference< XPropertyControl >& CurrentControl )
+ {
+ impl_notify_throw( CurrentControl, ACTIVATE_NEXT );
+ }
+
+ void SAL_CALL PropertyControlContext_Impl::acquire() noexcept
+ {
+ PropertyControlContext_Impl_Base::acquire();
+ }
+
+ void SAL_CALL PropertyControlContext_Impl::release() noexcept
+ {
+ PropertyControlContext_Impl_Base::release();
+ }
+
+ void PropertyControlContext_Impl::processEvent( const ::comphelper::AnyEvent& _rEvent )
+ {
+ SolarMutexGuard aGuard;
+ if ( impl_isDisposed_nothrow() )
+ return;
+
+ try
+ {
+ impl_processEvent_throw( _rEvent );
+ }
+ catch( const Exception& )
+ {
+ // can't handle otherwise, since our caller (the notification thread) does not allow
+ // for exceptions (it could itself abort only)
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ void PropertyControlContext_Impl::impl_processEvent_throw( const ::comphelper::AnyEvent& _rEvent )
+ {
+ const ControlEvent& rControlEvent = static_cast< const ControlEvent& >( _rEvent );
+ switch ( rControlEvent.eType )
+ {
+ case FOCUS_GAINED:
+ m_pContext->focusGained( rControlEvent.xControl );
+ break;
+ case VALUE_CHANGED:
+ m_pContext->valueChanged( rControlEvent.xControl );
+ break;
+ case ACTIVATE_NEXT:
+ m_pContext->activateNextControl( rControlEvent.xControl );
+ break;
+ }
+ }
+
+ OBrowserListBox::OBrowserListBox(weld::Builder& rBuilder, weld::Container* pContainer)
+ : m_xScrolledWindow(rBuilder.weld_scrolled_window("scrolledwindow"))
+ , m_xLinesPlayground(rBuilder.weld_container("playground"))
+ , m_xSizeGroup(rBuilder.create_size_group())
+ , m_xHelpWindow(new InspectorHelpWindow(rBuilder))
+ , m_pInitialControlParent(pContainer)
+ , m_pLineListener(nullptr)
+ , m_pControlObserver( nullptr )
+ , m_nTheNameSize(0)
+ , m_nRowHeight(0)
+ , m_pControlContextImpl( new PropertyControlContext_Impl( *this ) )
+ {
+ m_xScrolledWindow->set_size_request(-1, m_xScrolledWindow->get_text_height() * 20);
+ }
+
+ OBrowserListBox::~OBrowserListBox()
+ {
+ OSL_ENSURE( !IsModified(), "OBrowserListBox::~OBrowserListBox: still modified - should have been committed before!" );
+
+ // doing the commit here, while we, as well as our owner, as well as some other components,
+ // are already "half dead" (means within their dtor) is potentially dangerous.
+ // By definition, CommitModified has to be called (if necessary) before destruction
+ m_pControlContextImpl->dispose();
+ m_pControlContextImpl.clear();
+
+ Clear();
+ }
+
+ bool OBrowserListBox::IsModified() const
+ {
+ bool bModified = false;
+
+ if (m_xScrolledWindow->get_visible() && m_xActiveControl.is())
+ bModified = m_xActiveControl->isModified();
+
+ return bModified;
+ }
+
+ void OBrowserListBox::CommitModified( )
+ {
+ if ( !(IsModified() && m_xActiveControl.is()) )
+ return;
+
+ // for the time of this commit, notify all events synchronously
+ // #i63814#
+ m_pControlContextImpl->setNotificationMode( PropertyControlContext_Impl::eSynchronously );
+ try
+ {
+ m_xActiveControl->notifyModifiedValue();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ m_pControlContextImpl->setNotificationMode( PropertyControlContext_Impl::eAsynchronously );
+ }
+
+ void OBrowserListBox::SetListener( IPropertyLineListener* _pListener )
+ {
+ m_pLineListener = _pListener;
+ }
+
+ void OBrowserListBox::SetObserver( IPropertyControlObserver* _pObserver )
+ {
+ m_pControlObserver = _pObserver;
+ }
+
+ void OBrowserListBox::EnableHelpSection( bool _bEnable )
+ {
+ m_xHelpWindow->Show( _bEnable );
+ }
+
+ bool OBrowserListBox::HasHelpSection() const
+ {
+ return m_xHelpWindow->IsVisible();
+ }
+
+ void OBrowserListBox::SetHelpText( const OUString& _rHelpText )
+ {
+ OSL_ENSURE( HasHelpSection(), "OBrowserListBox::SetHelpText: help section not visible!" );
+ m_xHelpWindow->SetText( _rHelpText );
+ }
+
+ void OBrowserListBox::UpdatePlayGround()
+ {
+ for (auto& line : m_aLines)
+ line.pLine->SetTitleWidth(m_nTheNameSize);
+ }
+
+ void OBrowserListBox::SetPropertyValue(const OUString& _rEntryName, const Any& _rValue, bool _bUnknownValue )
+ {
+ ListBoxLines::iterator line = std::find_if(m_aLines.begin(), m_aLines.end(),
+ [&_rEntryName](const ListBoxLine& rLine) { return rLine.aName == _rEntryName; });
+
+ if ( line != m_aLines.end() )
+ {
+ if ( _bUnknownValue )
+ {
+ Reference< XPropertyControl > xControl( line->pLine->getControl() );
+ OSL_ENSURE( xControl.is(), "OBrowserListBox::SetPropertyValue: illegal control!" );
+ if ( xControl.is() )
+ xControl->setValue( Any() );
+ }
+ else
+ impl_setControlAsPropertyValue( *line, _rValue );
+ }
+ }
+
+ sal_uInt16 OBrowserListBox::GetPropertyPos( std::u16string_view _rEntryName ) const
+ {
+ sal_uInt16 nPos = 0;
+ for (auto const& line : m_aLines)
+ {
+ if ( line.aName == _rEntryName )
+ {
+ return nPos;
+ }
+ ++nPos;
+ }
+
+ return EDITOR_LIST_ENTRY_NOTFOUND;
+ }
+
+ bool OBrowserListBox::impl_getBrowserLineForName( const OUString& _rEntryName, BrowserLinePointer& _out_rpLine ) const
+ {
+ ListBoxLines::const_iterator line = std::find_if(m_aLines.begin(), m_aLines.end(),
+ [&_rEntryName](const ListBoxLine& rLine) { return rLine.aName == _rEntryName; });
+
+ if ( line != m_aLines.end() )
+ _out_rpLine = line->pLine;
+ else
+ _out_rpLine.reset();
+ return bool(_out_rpLine);
+ }
+
+ void OBrowserListBox::EnablePropertyControls( const OUString& _rEntryName, sal_Int16 _nControls, bool _bEnable )
+ {
+ BrowserLinePointer pLine;
+ if ( impl_getBrowserLineForName( _rEntryName, pLine ) )
+ pLine->EnablePropertyControls( _nControls, _bEnable );
+ }
+
+ void OBrowserListBox::EnablePropertyLine( const OUString& _rEntryName, bool _bEnable )
+ {
+ BrowserLinePointer pLine;
+ if ( impl_getBrowserLineForName( _rEntryName, pLine ) )
+ pLine->EnablePropertyLine( _bEnable );
+ }
+
+ Reference< XPropertyControl > OBrowserListBox::GetPropertyControl( const OUString& _rEntryName )
+ {
+ BrowserLinePointer pLine;
+ if ( impl_getBrowserLineForName( _rEntryName, pLine ) )
+ return pLine->getControl();
+ return nullptr;
+ }
+
+ void OBrowserListBox::InsertEntry(const OLineDescriptor& rPropertyData, sal_uInt16 _nPos)
+ {
+ // create a new line
+ BrowserLinePointer pBrowserLine = std::make_shared<OBrowserLine>(rPropertyData.sName, m_xLinesPlayground.get(),
+ m_xSizeGroup.get(), m_pInitialControlParent);
+
+ // check that the name is unique
+ for (auto const& line : m_aLines)
+ {
+ if (line.aName == rPropertyData.sName)
+ {
+ // already have another line for this name!
+ assert(false);
+ }
+ }
+
+ ListBoxLine aNewLine( rPropertyData.sName, pBrowserLine, rPropertyData.xPropertyHandler );
+ ListBoxLines::size_type nInsertPos = _nPos;
+ if ( _nPos >= m_aLines.size() )
+ {
+ nInsertPos = m_aLines.size();
+ m_aLines.push_back( aNewLine );
+ }
+ else
+ m_aLines.insert( m_aLines.begin() + _nPos, aNewLine );
+
+ pBrowserLine->SetTitleWidth(m_nTheNameSize);
+
+ // initialize the entry
+ ChangeEntry(rPropertyData, nInsertPos);
+
+ m_nRowHeight = std::max(m_nRowHeight, pBrowserLine->GetRowHeight() + 6); // 6 is spacing of the "playground" in browserpage.ui
+ m_xScrolledWindow->vadjustment_set_step_increment(m_nRowHeight);
+ }
+
+ void OBrowserListBox::ShowEntry(sal_uInt16 nPos)
+ {
+ if (nPos == 0)
+ {
+ // special case the simple entry 0 situation
+ m_xScrolledWindow->vadjustment_set_value(0);
+ return;
+ }
+
+ if (nPos >= m_aLines.size())
+ return;
+
+ unsigned const nWinHeight = m_xScrolledWindow->vadjustment_get_page_size();
+
+ auto nThumbPos = m_xScrolledWindow->vadjustment_get_value();
+ int const nWinTop = nThumbPos;
+ int const nWinBottom = nWinTop + nWinHeight;
+
+ auto nCtrlPosY = nPos * m_nRowHeight;
+
+ int const nSelectedItemTop = nCtrlPosY;
+ int const nSelectedItemBottom = nCtrlPosY + m_nRowHeight;
+ bool const shouldScrollDown = nSelectedItemBottom >= nWinBottom;
+ bool const shouldScrollUp = nSelectedItemTop <= nWinTop;
+ bool const isNeedToScroll = shouldScrollDown || shouldScrollUp;
+
+ if (!isNeedToScroll)
+ return;
+
+ if (shouldScrollDown)
+ {
+ int nOffset = nSelectedItemBottom - nWinBottom;
+ nThumbPos += nOffset;
+ }
+ else
+ {
+ int nOffset = nWinTop - nSelectedItemTop;
+ nThumbPos -= nOffset;
+ if(nThumbPos < 0)
+ nThumbPos = 0;
+ }
+ m_xScrolledWindow->vadjustment_set_value(nThumbPos);
+ }
+
+ void OBrowserListBox::buttonClicked( OBrowserLine* _pLine, bool _bPrimary )
+ {
+ DBG_ASSERT( _pLine, "OBrowserListBox::buttonClicked: invalid browser line!" );
+ if ( _pLine && m_pLineListener )
+ {
+ m_pLineListener->Clicked( _pLine->GetEntryName(), _bPrimary );
+ }
+ }
+
+ void OBrowserListBox::impl_setControlAsPropertyValue( const ListBoxLine& _rLine, const Any& _rPropertyValue )
+ {
+ Reference< XPropertyControl > xControl( _rLine.pLine->getControl() );
+ try
+ {
+ if ( _rPropertyValue.getValueType().equals( _rLine.pLine->getControl()->getValueType() ) )
+ {
+ xControl->setValue( _rPropertyValue );
+ }
+ else
+ {
+ SAL_WARN_IF( !_rLine.xHandler.is(), "extensions.propctrlr",
+ "OBrowserListBox::impl_setControlAsPropertyValue: no handler -> no conversion (property: '"
+ << _rLine.pLine->GetEntryName() << "')!" );
+ if ( _rLine.xHandler.is() )
+ {
+ Any aControlValue = _rLine.xHandler->convertToControlValue(
+ _rLine.pLine->GetEntryName(), _rPropertyValue, xControl->getValueType() );
+ xControl->setValue( aControlValue );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ Any OBrowserListBox::impl_getControlAsPropertyValue( const ListBoxLine& _rLine )
+ {
+ Reference< XPropertyControl > xControl( _rLine.pLine->getControl() );
+ Any aPropertyValue;
+ try
+ {
+ SAL_WARN_IF( !_rLine.xHandler.is(), "extensions.propctrlr",
+ "OBrowserListBox::impl_getControlAsPropertyValue: no handler -> no conversion (property: '"
+ << _rLine.pLine->GetEntryName() << "')!" );
+ if ( _rLine.xHandler.is() )
+ aPropertyValue = _rLine.xHandler->convertToPropertyValue( _rLine.pLine->GetEntryName(), xControl->getValue() );
+ else
+ aPropertyValue = xControl->getValue();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return aPropertyValue;
+ }
+
+ sal_uInt16 OBrowserListBox::impl_getControlPos( const Reference< XPropertyControl >& _rxControl ) const
+ {
+ sal_uInt16 nPos = 0;
+ for (auto const& search : m_aLines)
+ {
+ if ( search.pLine->getControl().get() == _rxControl.get() )
+ return nPos;
+ ++nPos;
+ }
+ OSL_FAIL( "OBrowserListBox::impl_getControlPos: invalid control - not part of any of our lines!" );
+ return sal_uInt16(-1);
+ }
+
+
+ void OBrowserListBox::focusGained( const Reference< XPropertyControl >& _rxControl )
+ {
+ DBG_TESTSOLARMUTEX();
+
+ DBG_ASSERT( _rxControl.is(), "OBrowserListBox::focusGained: invalid event source!" );
+ if ( !_rxControl.is() )
+ return;
+
+ if ( m_pControlObserver )
+ m_pControlObserver->focusGained( _rxControl );
+
+ m_xActiveControl = _rxControl;
+ ShowEntry( impl_getControlPos( m_xActiveControl ) );
+ }
+
+
+ void OBrowserListBox::valueChanged( const Reference< XPropertyControl >& _rxControl )
+ {
+ DBG_TESTSOLARMUTEX();
+
+ DBG_ASSERT( _rxControl.is(), "OBrowserListBox::valueChanged: invalid event source!" );
+ if ( !_rxControl.is() )
+ return;
+
+ if ( m_pControlObserver )
+ m_pControlObserver->valueChanged( _rxControl );
+
+ if ( m_pLineListener )
+ {
+ const ListBoxLine& rLine = m_aLines[ impl_getControlPos( _rxControl ) ];
+ m_pLineListener->Commit(
+ rLine.pLine->GetEntryName(),
+ impl_getControlAsPropertyValue( rLine )
+ );
+ }
+ }
+
+
+ void OBrowserListBox::activateNextControl( const Reference< XPropertyControl >& _rxCurrentControl )
+ {
+ DBG_TESTSOLARMUTEX();
+
+ sal_uInt16 nLine = impl_getControlPos( _rxCurrentControl );
+
+ // cycle forwards, 'til we've the next control which can grab the focus
+ ++nLine;
+ while ( static_cast< size_t >( nLine ) < m_aLines.size() )
+ {
+ if ( m_aLines[nLine].pLine->GrabFocus() )
+ break;
+ ++nLine;
+ }
+
+ // wrap around?
+ if ( ( static_cast< size_t >( nLine ) >= m_aLines.size() ) && ( !m_aLines.empty() ) )
+ m_aLines[0].pLine->GrabFocus();
+ }
+
+
+ namespace
+ {
+
+ void lcl_implDisposeControl_nothrow( const Reference< XPropertyControl >& _rxControl )
+ {
+ if ( !_rxControl.is() )
+ return;
+ try
+ {
+ _rxControl->setControlContext( nullptr );
+ Reference< XComponent > xControlComponent( _rxControl, UNO_QUERY );
+ if ( xControlComponent.is() )
+ xControlComponent->dispose();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+ }
+
+ void OBrowserListBox::Clear()
+ {
+ for (auto const& line : m_aLines)
+ {
+ // hide the line
+ line.pLine->Hide();
+ // reset the listener
+ lcl_implDisposeControl_nothrow( line.pLine->getControl() );
+ }
+
+ clearContainer( m_aLines );
+ }
+
+ bool OBrowserListBox::RemoveEntry( const OUString& _rName )
+ {
+ ListBoxLines::iterator it = std::find_if(m_aLines.begin(), m_aLines.end(),
+ [&_rName](const ListBoxLine& rLine) { return rLine.aName == _rName; });
+
+ if ( it == m_aLines.end() )
+ return false;
+
+ m_aLines.erase( it );
+
+ return true;
+ }
+
+ void OBrowserListBox::ChangeEntry( const OLineDescriptor& rPropertyData, ListBoxLines::size_type nPos )
+ {
+ OSL_PRECOND( rPropertyData.Control.is(), "OBrowserListBox::ChangeEntry: invalid control!" );
+ if ( !rPropertyData.Control.is() )
+ return;
+
+ if ( nPos == EDITOR_LIST_REPLACE_EXISTING )
+ nPos = GetPropertyPos( rPropertyData.sName );
+
+ if ( nPos >= m_aLines.size() )
+ return;
+
+ // the current line and control
+ ListBoxLine& rLine = m_aLines[nPos];
+
+ // the old control and some data about it
+ Reference< XPropertyControl > xControl = rLine.pLine->getControl();
+
+ // clean up the old control
+ lcl_implDisposeControl_nothrow( xControl );
+
+ // set the new control at the line
+ rLine.pLine->setControl( rPropertyData.Control );
+ xControl = rLine.pLine->getControl();
+
+ if ( xControl.is() )
+ xControl->setControlContext( m_pControlContextImpl );
+
+ // the initial property value
+ if ( rPropertyData.bUnknownValue )
+ xControl->setValue( Any() );
+ else
+ impl_setControlAsPropertyValue( rLine, rPropertyData.aValue );
+
+ rLine.pLine->SetTitle(rPropertyData.DisplayName);
+ rLine.xHandler = rPropertyData.xPropertyHandler;
+
+ if ( rPropertyData.HasPrimaryButton )
+ {
+ if ( !rPropertyData.PrimaryButtonImageURL.isEmpty() )
+ rLine.pLine->ShowBrowseButton( rPropertyData.PrimaryButtonImageURL, true );
+ else if ( rPropertyData.PrimaryButtonImage.is() )
+ rLine.pLine->ShowBrowseButton( rPropertyData.PrimaryButtonImage, true );
+ else
+ rLine.pLine->ShowBrowseButton( true );
+
+ if ( rPropertyData.HasSecondaryButton )
+ {
+ if ( !rPropertyData.SecondaryButtonImageURL.isEmpty() )
+ rLine.pLine->ShowBrowseButton( rPropertyData.SecondaryButtonImageURL, false );
+ else if ( rPropertyData.SecondaryButtonImage.is() )
+ rLine.pLine->ShowBrowseButton( rPropertyData.SecondaryButtonImage, false );
+ else
+ rLine.pLine->ShowBrowseButton( false );
+ }
+ else
+ rLine.pLine->HideBrowseButton( false );
+
+ rLine.pLine->SetClickListener( this );
+ }
+ else
+ {
+ rLine.pLine->HideBrowseButton( true );
+ rLine.pLine->HideBrowseButton( false );
+ }
+
+ DBG_ASSERT( ( rPropertyData.IndentLevel == 0 ) || ( rPropertyData.IndentLevel == 1 ),
+ "OBrowserListBox::ChangeEntry: unsupported indent level!" );
+ rLine.pLine->IndentTitle( rPropertyData.IndentLevel > 0 );
+
+ rLine.pLine->SetComponentHelpIds(
+ HelpIdUrl::getHelpId( rPropertyData.HelpURL )
+ );
+
+ if ( rPropertyData.bReadOnly )
+ {
+ rLine.pLine->SetReadOnly( true );
+
+ // user controls (i.e. the ones not provided by the usual
+ // XPropertyControlFactory) have no chance to know that they should be read-only,
+ // since XPropertyHandler::describePropertyLine does not transport this
+ // information.
+ // So, we manually switch this to read-only.
+ if ( xControl.is() && ( xControl->getControlType() == PropertyControlType::Unknown ) )
+ {
+ weld::Widget* pWindow = rLine.pLine->getControlWindow();
+ weld::Entry* pControlWindowAsEdit = dynamic_cast<weld::Entry*>(pWindow);
+ if (pControlWindowAsEdit)
+ pControlWindowAsEdit->set_editable(false);
+ else
+ pWindow->set_sensitive(false);
+ }
+ }
+
+ sal_uInt16 nTextWidth = m_xLinesPlayground->get_pixel_size(rPropertyData.DisplayName).Width();
+ if (m_nTheNameSize< nTextWidth)
+ {
+ m_nTheNameSize = nTextWidth;
+ UpdatePlayGround();
+ }
+ }
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/browserlistbox.hxx b/extensions/source/propctrlr/browserlistbox.hxx
new file mode 100644
index 000000000..1a7232612
--- /dev/null
+++ b/extensions/source/propctrlr/browserlistbox.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "browserline.hxx"
+
+#include <com/sun/star/inspection/XPropertyControl.hpp>
+#include <com/sun/star/inspection/XPropertyHandler.hpp>
+#include <vcl/weld.hxx>
+#include <rtl/ref.hxx>
+
+#include <memory>
+#include <vector>
+
+#define EDITOR_LIST_REPLACE_EXISTING \
+ std::numeric_limits<ListBoxLines::size_type>::max()
+
+namespace pcr
+{
+
+
+ class IPropertyLineListener;
+ class IPropertyControlObserver;
+ struct OLineDescriptor;
+ class InspectorHelpWindow;
+ class PropertyControlContext_Impl;
+
+
+ // administrative structures for OBrowserListBox
+
+ typedef std::shared_ptr< OBrowserLine > BrowserLinePointer;
+ struct ListBoxLine
+ {
+ OUString aName;
+ BrowserLinePointer pLine;
+ css::uno::Reference< css::inspection::XPropertyHandler >
+ xHandler;
+
+ ListBoxLine( const OUString& rName, const BrowserLinePointer& _pLine, const css::uno::Reference< css::inspection::XPropertyHandler >& _rxHandler )
+ : aName( rName ),
+ pLine( _pLine ),
+ xHandler( _rxHandler )
+ {
+ }
+ };
+ typedef std::vector< ListBoxLine > ListBoxLines;
+
+
+ class OBrowserListBox final : public IButtonClickListener
+ {
+ std::unique_ptr<weld::ScrolledWindow> m_xScrolledWindow;
+ std::unique_ptr<weld::Container> m_xLinesPlayground;
+ std::unique_ptr<weld::SizeGroup> m_xSizeGroup;
+ std::unique_ptr<InspectorHelpWindow> m_xHelpWindow;
+ weld::Container* m_pInitialControlParent;
+ ListBoxLines m_aLines;
+ IPropertyLineListener* m_pLineListener;
+ IPropertyControlObserver* m_pControlObserver;
+ css::uno::Reference< css::inspection::XPropertyControl >
+ m_xActiveControl;
+ sal_uInt16 m_nTheNameSize;
+ int m_nRowHeight;
+ ::rtl::Reference< PropertyControlContext_Impl >
+ m_pControlContextImpl;
+
+ void UpdatePlayGround();
+ void ShowEntry(sal_uInt16 nPos);
+
+ public:
+ explicit OBrowserListBox(weld::Builder& rBuilder, weld::Container* pContainer);
+ ~OBrowserListBox();
+
+ void SetListener( IPropertyLineListener* _pListener );
+ void SetObserver( IPropertyControlObserver* _pObserver );
+
+ void EnableHelpSection( bool _bEnable );
+ bool HasHelpSection() const;
+ void SetHelpText( const OUString& _rHelpText );
+
+ void Clear();
+
+ void InsertEntry( const OLineDescriptor&, sal_uInt16 nPos );
+ bool RemoveEntry( const OUString& _rName );
+ void ChangeEntry( const OLineDescriptor&, ListBoxLines::size_type nPos );
+
+ void SetPropertyValue( const OUString& rEntryName, const css::uno::Any& rValue, bool _bUnknownValue );
+ sal_uInt16 GetPropertyPos( std::u16string_view rEntryName ) const;
+ css::uno::Reference< css::inspection::XPropertyControl >
+ GetPropertyControl( const OUString& rEntryName );
+ void EnablePropertyControls( const OUString& _rEntryName, sal_Int16 _nControls, bool _bEnable );
+ void EnablePropertyLine( const OUString& _rEntryName, bool _bEnable );
+
+ bool IsModified( ) const;
+ void CommitModified( );
+
+ /// @throws css::uno::RuntimeException
+ void focusGained( const css::uno::Reference< css::inspection::XPropertyControl >& Control );
+ /// @throws css::uno::RuntimeException
+ void valueChanged( const css::uno::Reference< css::inspection::XPropertyControl >& Control );
+ /// @throws css::uno::RuntimeException
+ void activateNextControl( const css::uno::Reference< css::inspection::XPropertyControl >& CurrentControl );
+
+ private:
+ // IButtonClickListener
+ void buttonClicked( OBrowserLine* _pLine, bool _bPrimary ) override;
+
+ /** retrieves the index of a given control in our line list
+ @param _rxControl
+ The control to lookup. Must denote a control of one of the lines in ->m_aLines
+ */
+ sal_uInt16 impl_getControlPos( const css::uno::Reference< css::inspection::XPropertyControl >& _rxControl ) const;
+
+ /** sets the given property value at the given control, after converting it as necessary
+ @param _rLine
+ The line whose at which the value is to be set.
+ @param _rPropertyValue
+ the property value to set. If it's not compatible with the control value,
+ it will be converted, using <member>XPropertyHandler::convertToControlValue</member>
+ */
+ static void impl_setControlAsPropertyValue( const ListBoxLine& _rLine, const css::uno::Any& _rPropertyValue );
+
+ /** retrieves the value for the given control, as a property value, after converting it as necessary
+ @param _rLine
+ The line whose at which the value is to be set.
+ */
+ static css::uno::Any
+ impl_getControlAsPropertyValue( const ListBoxLine& _rLine );
+
+ /** retrieves the ->BrowserLinePointer for a given entry name
+ @param _rEntryName
+ the name whose line is to be looked up
+ @param _out_rpLine
+ contains, upon return, the found browser line, if any
+ @return
+ <TRUE/> if and only if a non-<NULL/> line for the given entry name could be
+ found.
+ */
+ bool impl_getBrowserLineForName( const OUString& _rEntryName, BrowserLinePointer& _out_rpLine ) const;
+ };
+
+
+} // namespace pcr
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/browserpage.cxx b/extensions/source/propctrlr/browserpage.cxx
new file mode 100644
index 000000000..5502418bd
--- /dev/null
+++ b/extensions/source/propctrlr/browserpage.cxx
@@ -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 .
+ */
+
+#include <vcl/svapp.hxx>
+#include "browserpage.hxx"
+
+namespace pcr
+{
+ OBrowserPage::OBrowserPage(weld::Container* pParent, weld::Container* pInitialControlContainer)
+ : m_pParent(pParent)
+ , m_xBuilder(Application::CreateBuilder(pParent, "modules/spropctrlr/ui/browserpage.ui"))
+ , m_xContainer(m_xBuilder->weld_container("BrowserPage"))
+ , m_xListBox(new OBrowserListBox(*m_xBuilder, pInitialControlContainer))
+ {
+ }
+
+ OBrowserPage::~OBrowserPage()
+ {
+ if (m_pParent)
+ detach();
+ assert(!m_pParent);
+ }
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/browserpage.hxx b/extensions/source/propctrlr/browserpage.hxx
new file mode 100644
index 000000000..0db6cc3c7
--- /dev/null
+++ b/extensions/source/propctrlr/browserpage.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <o3tl/deleter.hxx>
+#include "browserlistbox.hxx"
+
+namespace pcr
+{
+ class OBrowserPage
+ {
+ private:
+ weld::Container* m_pParent;
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<OBrowserListBox, o3tl::default_delete<OBrowserListBox>> m_xListBox;
+
+ public:
+ // TODO inherit from BuilderPage
+ explicit OBrowserPage(weld::Container* pParent, weld::Container* pContainer);
+ ~OBrowserPage();
+
+ void SetHelpId(const OString& rHelpId) { m_xContainer->set_help_id(rHelpId); }
+
+ OBrowserListBox& getListBox() { return *m_xListBox; }
+ const OBrowserListBox& getListBox() const { return *m_xListBox; }
+
+ void detach()
+ {
+ assert(m_pParent && "already attached");
+ m_pParent->move(m_xContainer.get(), nullptr);
+ m_pParent = nullptr;
+ }
+
+ void reattach(weld::Container* pNewParent)
+ {
+ assert(!m_pParent && "already attached");
+ m_pParent = pNewParent;
+ m_pParent->move(m_xContainer.get(), pNewParent);
+ }
+ };
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/browserview.cxx b/extensions/source/propctrlr/browserview.cxx
new file mode 100644
index 000000000..e7b7cb690
--- /dev/null
+++ b/extensions/source/propctrlr/browserview.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 "browserview.hxx"
+#include "propertyeditor.hxx"
+#include <helpids.h>
+#include <memory>
+
+namespace pcr
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+
+ OPropertyBrowserView::OPropertyBrowserView(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Builder& rBuilder)
+ : m_xPropBox(new OPropertyEditor(rContext, rBuilder))
+ , m_nActivePage(0)
+ {
+ m_xPropBox->SetHelpId(HID_FM_PROPDLG_TABCTR);
+ m_xPropBox->setPageActivationHandler(LINK(this, OPropertyBrowserView, OnPageActivation));
+ }
+
+ IMPL_LINK(OPropertyBrowserView, OnPageActivation, const OString&, rNewPage, void)
+ {
+ m_nActivePage = rNewPage.toUInt32();
+ m_aPageActivationHandler.Call(nullptr);
+ }
+
+ OPropertyBrowserView::~OPropertyBrowserView()
+ {
+ sal_uInt16 nTmpPage = m_xPropBox->GetCurPage();
+ if (nTmpPage)
+ m_nActivePage = nTmpPage;
+ }
+
+ void OPropertyBrowserView::activatePage(sal_uInt16 _nPage)
+ {
+ m_nActivePage = _nPage;
+ getPropertyBox().SetPage(m_nActivePage);
+ }
+
+ css::awt::Size OPropertyBrowserView::getMinimumSize() const
+ {
+ ::Size aSize = m_xPropBox->get_preferred_size();
+ return css::awt::Size(aSize.Width(), aSize.Height());
+ }
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/browserview.hxx b/extensions/source/propctrlr/browserview.hxx
new file mode 100644
index 000000000..7a0711fb1
--- /dev/null
+++ b/extensions/source/propctrlr/browserview.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <o3tl/deleter.hxx>
+#include <vcl/weld.hxx>
+
+namespace pcr
+{
+ class OPropertyEditor;
+ class OPropertyBrowserView final
+ {
+ std::unique_ptr<OPropertyEditor, o3tl::default_delete<OPropertyEditor>> m_xPropBox;
+ sal_uInt16 m_nActivePage;
+ Link<LinkParamNone*,void> m_aPageActivationHandler;
+
+ public:
+ explicit OPropertyBrowserView(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Builder& rBuilder);
+ ~OPropertyBrowserView();
+
+ OPropertyEditor& getPropertyBox() { return *m_xPropBox; }
+
+ // page handling
+ sal_uInt16 getActivePage() const { return m_nActivePage; }
+ void activatePage(sal_uInt16 _nPage);
+
+ void setPageActivationHandler(const Link<LinkParamNone*,void>& _rHdl) { m_aPageActivationHandler = _rHdl; }
+
+ css::awt::Size getMinimumSize() const;
+
+ private:
+ DECL_LINK(OnPageActivation, const OString&, void);
+ };
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/buttonnavigationhandler.cxx b/extensions/source/propctrlr/buttonnavigationhandler.cxx
new file mode 100644
index 000000000..618d9db46
--- /dev/null
+++ b/extensions/source/propctrlr/buttonnavigationhandler.cxx
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "buttonnavigationhandler.hxx"
+#include "formstrings.hxx"
+#include "formmetadata.hxx"
+#include "pushbuttonnavigation.hxx"
+
+#include <com/sun/star/form/inspection/FormComponentPropertyHandler.hpp>
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::inspection;
+
+ ButtonNavigationHandler::ButtonNavigationHandler( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandlerComponent( _rxContext )
+ {
+
+ m_xSlaveHandler = css::form::inspection::FormComponentPropertyHandler::create( m_xContext );
+ }
+
+
+ ButtonNavigationHandler::~ButtonNavigationHandler( )
+ {
+ }
+
+
+ OUString ButtonNavigationHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.ButtonNavigationHandler";
+ }
+
+
+ Sequence< OUString > ButtonNavigationHandler::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.inspection.ButtonNavigationHandler" };
+ }
+
+
+ void SAL_CALL ButtonNavigationHandler::inspect( const Reference< XInterface >& _rxIntrospectee )
+ {
+ PropertyHandlerComponent::inspect( _rxIntrospectee );
+ m_xSlaveHandler->inspect( _rxIntrospectee );
+ }
+
+
+ PropertyState SAL_CALL ButtonNavigationHandler::getPropertyState( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+ PropertyState eState = PropertyState_DIRECT_VALUE;
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_BUTTONTYPE:
+ {
+ PushButtonNavigation aHelper( m_xComponent );
+ eState = aHelper.getCurrentButtonTypeState();
+ }
+ break;
+ case PROPERTY_ID_TARGET_URL:
+ {
+ PushButtonNavigation aHelper( m_xComponent );
+ eState = aHelper.getCurrentTargetURLState();
+ }
+ break;
+
+ default:
+ OSL_FAIL( "ButtonNavigationHandler::getPropertyState: cannot handle this property!" );
+ break;
+ }
+
+ return eState;
+ }
+
+
+ Any SAL_CALL ButtonNavigationHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ Any aReturn;
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_BUTTONTYPE:
+ {
+ PushButtonNavigation aHelper( m_xComponent );
+ aReturn = aHelper.getCurrentButtonType();
+ }
+ break;
+
+ case PROPERTY_ID_TARGET_URL:
+ {
+ PushButtonNavigation aHelper( m_xComponent );
+ aReturn = aHelper.getCurrentTargetURL();
+ }
+ break;
+
+ default:
+ OSL_FAIL( "ButtonNavigationHandler::getPropertyValue: cannot handle this property!" );
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ void SAL_CALL ButtonNavigationHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_BUTTONTYPE:
+ {
+ PushButtonNavigation aHelper( m_xComponent );
+ aHelper.setCurrentButtonType( _rValue );
+ }
+ break;
+
+ case PROPERTY_ID_TARGET_URL:
+ {
+ PushButtonNavigation aHelper( m_xComponent );
+ aHelper.setCurrentTargetURL( _rValue );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "ButtonNavigationHandler::setPropertyValue: cannot handle this id!" );
+ }
+ }
+
+
+ bool ButtonNavigationHandler::isNavigationCapableButton( const Reference< XPropertySet >& _rxComponent )
+ {
+ Reference< XPropertySetInfo > xPSI;
+ if ( _rxComponent.is() )
+ xPSI = _rxComponent->getPropertySetInfo();
+
+ return xPSI.is()
+ && xPSI->hasPropertyByName( PROPERTY_TARGET_URL )
+ && xPSI->hasPropertyByName( PROPERTY_BUTTONTYPE );
+ }
+
+
+ Sequence< Property > ButtonNavigationHandler::doDescribeSupportedProperties() const
+ {
+ std::vector< Property > aProperties;
+
+ if ( isNavigationCapableButton( m_xComponent ) )
+ {
+ addStringPropertyDescription( aProperties, PROPERTY_TARGET_URL );
+ implAddPropertyDescription( aProperties, PROPERTY_BUTTONTYPE, ::cppu::UnoType<sal_Int32>::get() );
+ }
+
+ if ( aProperties.empty() )
+ return Sequence< Property >();
+ return comphelper::containerToSequence(aProperties);
+ }
+
+
+ Sequence< OUString > SAL_CALL ButtonNavigationHandler::getActuatingProperties( )
+ {
+ Sequence< OUString > aActuating{ PROPERTY_BUTTONTYPE, PROPERTY_TARGET_URL };
+ return aActuating;
+ }
+
+
+ InteractiveSelectionResult SAL_CALL ButtonNavigationHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ InteractiveSelectionResult eReturn( InteractiveSelectionResult_Cancelled );
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_TARGET_URL:
+ eReturn = m_xSlaveHandler->onInteractivePropertySelection( _rPropertyName, _bPrimary, _rData, _rxInspectorUI );
+ break;
+ default:
+ eReturn = PropertyHandlerComponent::onInteractivePropertySelection( _rPropertyName, _bPrimary, _rData, _rxInspectorUI );
+ break;
+ }
+
+ return eReturn;
+ }
+
+
+ void SAL_CALL ButtonNavigationHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& /*_rNewValue*/, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool /*_bFirstTimeInit*/ )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) );
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_BUTTONTYPE:
+ {
+ PushButtonNavigation aHelper( m_xComponent );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_TARGET_URL, aHelper.currentButtonTypeIsOpenURL() );
+ }
+ break;
+
+ case PROPERTY_ID_TARGET_URL:
+ {
+ PushButtonNavigation aHelper( m_xComponent );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_TARGET_FRAME, aHelper.hasNonEmptyCurrentTargetURL() );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "ButtonNavigationHandler::actuatingPropertyChanged: cannot handle this id!" );
+ }
+ }
+
+
+ LineDescriptor SAL_CALL ButtonNavigationHandler::describePropertyLine( const OUString& _rPropertyName, const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ LineDescriptor aReturn;
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_TARGET_URL:
+ aReturn = m_xSlaveHandler->describePropertyLine( _rPropertyName, _rxControlFactory );
+ break;
+ default:
+ aReturn = PropertyHandlerComponent::describePropertyLine( _rPropertyName, _rxControlFactory );
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_ButtonNavigationHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::ButtonNavigationHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/buttonnavigationhandler.hxx b/extensions/source/propctrlr/buttonnavigationhandler.hxx
new file mode 100644
index 000000000..c5e01e1df
--- /dev/null
+++ b/extensions/source/propctrlr/buttonnavigationhandler.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "propertyhandler.hxx"
+
+
+namespace pcr
+{
+
+ /** a property handler for any virtual string properties
+ */
+ class ButtonNavigationHandler : public PropertyHandlerComponent
+ {
+ private:
+ css::uno::Reference< css::inspection::XPropertyHandler >
+ m_xSlaveHandler;
+
+ public:
+ explicit ButtonNavigationHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ protected:
+ virtual ~ButtonNavigationHandler() override;
+
+ static bool isNavigationCapableButton( const css::uno::Reference< css::beans::XPropertySet >& _rxComponent );
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
+
+ // XPropertyHandler overriables
+ virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override;
+ virtual css::inspection::InteractiveSelectionResult
+ SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override;
+ virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+
+ // PropertyHandler overridables
+ virtual css::uno::Sequence< css::beans::Property >
+ doDescribeSupportedProperties() const override;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/cellbindinghandler.cxx b/extensions/source/propctrlr/cellbindinghandler.cxx
new file mode 100644
index 000000000..633a3574b
--- /dev/null
+++ b/extensions/source/propctrlr/cellbindinghandler.cxx
@@ -0,0 +1,487 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "cellbindinghandler.hxx"
+#include "formstrings.hxx"
+#include "formmetadata.hxx"
+#include "cellbindinghelper.hxx"
+
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::table;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::inspection;
+ using namespace ::com::sun::star::form::binding;
+ using namespace ::comphelper;
+
+ CellBindingPropertyHandler::CellBindingPropertyHandler( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandlerComponent( _rxContext )
+ ,m_pCellExchangeConverter( new DefaultEnumRepresentation( *m_pInfoService, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_ID_CELL_EXCHANGE_TYPE ) )
+ {
+ }
+
+
+ OUString CellBindingPropertyHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.CellBindingPropertyHandler";
+ }
+
+
+ Sequence< OUString > CellBindingPropertyHandler::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.inspection.CellBindingPropertyHandler" };
+ }
+
+
+ void CellBindingPropertyHandler::onNewComponent()
+ {
+ PropertyHandlerComponent::onNewComponent();
+
+ Reference< XModel > xDocument( impl_getContextDocument_nothrow() );
+ DBG_ASSERT( xDocument.is(), "CellBindingPropertyHandler::onNewComponent: no document!" );
+ if ( CellBindingHelper::isSpreadsheetDocument( xDocument ) )
+ m_pHelper.reset( new CellBindingHelper( m_xComponent, xDocument ) );
+ }
+
+
+ CellBindingPropertyHandler::~CellBindingPropertyHandler( )
+ {
+ }
+
+
+ Sequence< OUString > SAL_CALL CellBindingPropertyHandler::getActuatingProperties( )
+ {
+ Sequence< OUString > aInterestingProperties{ PROPERTY_LIST_CELL_RANGE,
+ PROPERTY_BOUND_CELL,
+ PROPERTY_CONTROLSOURCE };
+ return aInterestingProperties;
+ }
+
+
+ void SAL_CALL CellBindingPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) );
+ OSL_PRECOND(m_pHelper,
+ "CellBindingPropertyHandler::actuatingPropertyChanged: inconsistency!");
+ // if we survived impl_getPropertyId_throwRuntime, we should have a helper, since no helper implies no properties
+
+ OSL_PRECOND( _rxInspectorUI.is(), "FormComponentPropertyHandler::actuatingPropertyChanged: no access to the UI!" );
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ std::vector< PropertyId > aDependentProperties;
+
+ switch ( nActuatingPropId )
+ {
+ // ----- BoundCell -----
+ case PROPERTY_ID_BOUND_CELL:
+ {
+ // the SQL-data-binding related properties need to be enabled if and only if
+ // there is *no* valid cell binding
+ Reference< XValueBinding > xBinding;
+ _rNewValue >>= xBinding;
+
+ if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_CELL_EXCHANGE_TYPE ) )
+ _rxInspectorUI->enablePropertyUI( PROPERTY_CELL_EXCHANGE_TYPE, xBinding.is() );
+ if ( impl_componentHasProperty_throw( PROPERTY_CONTROLSOURCE ) )
+ _rxInspectorUI->enablePropertyUI( PROPERTY_CONTROLSOURCE, !xBinding.is() );
+
+ if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_FILTERPROPOSAL ) )
+ _rxInspectorUI->enablePropertyUI( PROPERTY_FILTERPROPOSAL, !xBinding.is() );
+ if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_EMPTY_IS_NULL ) )
+ _rxInspectorUI->enablePropertyUI( PROPERTY_EMPTY_IS_NULL, !xBinding.is() );
+
+ aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN );
+
+ if ( !xBinding.is() && m_pHelper->getCurrentBinding().is() )
+ {
+ // ensure that the "transfer selection as" property is reset. Since we can't remember
+ // it at the object itself, but derive it from the binding only, we have to normalize
+ // it now that there *is* no binding anymore.
+ setPropertyValue( PROPERTY_CELL_EXCHANGE_TYPE, Any( sal_Int16(0) ) );
+ }
+ }
+ break;
+
+ // ----- CellRange -----
+ case PROPERTY_ID_LIST_CELL_RANGE:
+ {
+ // the list source related properties need to be enabled if and only if
+ // there is *no* valid external list source for the control
+ Reference< XListEntrySource > xSource;
+ _rNewValue >>= xSource;
+
+ _rxInspectorUI->enablePropertyUI( PROPERTY_STRINGITEMLIST, !xSource.is() );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_LISTSOURCE, !xSource.is() );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_LISTSOURCETYPE, !xSource.is() );
+
+ aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN );
+
+ // also reset the list entries if the cell range is reset
+ // #i28319#
+ if ( !_bFirstTimeInit )
+ {
+ try
+ {
+ if ( !xSource.is() )
+ {
+ setPropertyValue( PROPERTY_STRINGITEMLIST, Any( Sequence< OUString >() ) );
+ setPropertyValue( PROPERTY_TYPEDITEMLIST, Any( Sequence< Any >() ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION(
+ "extensions.propctrlr",
+ "ListCellRange: caught an exception while resetting the string items!");
+ }
+ }
+ }
+ break; // case PROPERTY_ID_LIST_CELL_RANGE
+
+ // ----- DataField -----
+ case PROPERTY_ID_CONTROLSOURCE:
+ {
+ OUString sControlSource;
+ _rNewValue >>= sControlSource;
+ if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_BOUND_CELL ) )
+ _rxInspectorUI->enablePropertyUI( PROPERTY_BOUND_CELL, sControlSource.isEmpty() );
+ }
+ break; // case PROPERTY_ID_CONTROLSOURCE
+
+ default:
+ OSL_FAIL( "CellBindingPropertyHandler::actuatingPropertyChanged: did not register for this property!" );
+ }
+
+ for (auto const& dependentProperty : aDependentProperties)
+ {
+ impl_updateDependentProperty_nothrow( dependentProperty, _rxInspectorUI );
+ }
+ }
+
+
+ void CellBindingPropertyHandler::impl_updateDependentProperty_nothrow( PropertyId _nPropId, const Reference< XObjectInspectorUI >& _rxInspectorUI ) const
+ {
+ try
+ {
+ switch ( _nPropId )
+ {
+ // ----- BoundColumn -----
+ case PROPERTY_ID_BOUNDCOLUMN:
+ {
+ CellBindingPropertyHandler* pNonConstThis = const_cast< CellBindingPropertyHandler* >( this );
+ Reference< XValueBinding > xBinding( pNonConstThis->getPropertyValue( PROPERTY_BOUND_CELL ), UNO_QUERY );
+ Reference< XListEntrySource > xListSource( pNonConstThis->getPropertyValue( PROPERTY_LIST_CELL_RANGE ), UNO_QUERY );
+
+ if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_BOUNDCOLUMN ) )
+ _rxInspectorUI->enablePropertyUI( PROPERTY_BOUNDCOLUMN, !xBinding.is() && !xListSource.is() );
+ }
+ break; // case PROPERTY_ID_BOUNDCOLUMN
+
+ } // switch
+
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingPropertyHandler::impl_updateDependentProperty_nothrow" );
+ }
+ }
+
+
+ Any SAL_CALL CellBindingPropertyHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ OSL_ENSURE(m_pHelper, "CellBindingPropertyHandler::getPropertyValue: inconsistency!");
+ // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties
+
+ Any aReturn;
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_BOUND_CELL:
+ {
+ Reference< XValueBinding > xBinding( m_pHelper->getCurrentBinding() );
+ if ( !CellBindingHelper::isCellBinding( xBinding ) )
+ xBinding.clear();
+
+ aReturn <<= xBinding;
+ }
+ break;
+
+ case PROPERTY_ID_LIST_CELL_RANGE:
+ {
+ Reference< XListEntrySource > xSource( m_pHelper->getCurrentListSource() );
+ if ( !CellBindingHelper::isCellRangeListSource( xSource ) )
+ xSource.clear();
+
+ aReturn <<= xSource;
+ }
+ break;
+
+ case PROPERTY_ID_CELL_EXCHANGE_TYPE:
+ {
+ Reference< XValueBinding > xBinding( m_pHelper->getCurrentBinding() );
+ aReturn <<= static_cast<sal_Int16>( CellBindingHelper::isCellIntegerBinding( xBinding ) ? 1 : 0 );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "CellBindingPropertyHandler::getPropertyValue: cannot handle this!" );
+ break;
+ }
+ return aReturn;
+ }
+
+
+ void SAL_CALL CellBindingPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ OSL_ENSURE(m_pHelper, "CellBindingPropertyHandler::setPropertyValue: inconsistency!");
+ // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties
+
+ try
+ {
+ Any aOldValue = getPropertyValue( _rPropertyName );
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_BOUND_CELL:
+ {
+ Reference< XValueBinding > xBinding;
+ _rValue >>= xBinding;
+ m_pHelper->setBinding( xBinding );
+ }
+ break;
+
+ case PROPERTY_ID_LIST_CELL_RANGE:
+ {
+ Reference< XListEntrySource > xSource;
+ _rValue >>= xSource;
+ m_pHelper->setListSource( xSource );
+ }
+ break;
+
+ case PROPERTY_ID_CELL_EXCHANGE_TYPE:
+ {
+ sal_Int16 nExchangeType = 0;
+ OSL_VERIFY( _rValue >>= nExchangeType );
+
+ Reference< XValueBinding > xBinding = m_pHelper->getCurrentBinding( );
+ if ( xBinding.is() )
+ {
+ bool bNeedIntegerBinding = ( nExchangeType == 1 );
+ if ( bNeedIntegerBinding != CellBindingHelper::isCellIntegerBinding( xBinding ) )
+ {
+ CellAddress aAddress;
+ if ( m_pHelper->getAddressFromCellBinding( xBinding, aAddress ) )
+ {
+ xBinding = m_pHelper->createCellBindingFromAddress( aAddress, bNeedIntegerBinding );
+ m_pHelper->setBinding( xBinding );
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "CellBindingPropertyHandler::setPropertyValue: cannot handle this!" );
+ break;
+ }
+
+ impl_setContextDocumentModified_nothrow();
+
+ Any aNewValue( getPropertyValue( _rPropertyName ) );
+ firePropertyChange( _rPropertyName, nPropId, aOldValue, aNewValue );
+ // TODO/UNOize: can't we make this a part of the base class, for all those "virtual"
+ // properties? Base class'es |setPropertyValue| could call some |doSetPropertyValue|,
+ // and handle the listener notification itself
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingPropertyHandler::setPropertyValue" );
+ }
+ }
+
+
+ Any SAL_CALL CellBindingPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Any aPropertyValue;
+
+ OSL_ENSURE(
+ m_pHelper,
+ "CellBindingPropertyHandler::convertToPropertyValue: we have no SupportedProperties!");
+ if (!m_pHelper)
+ return aPropertyValue;
+
+ PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) );
+
+ OUString sControlValue;
+ OSL_VERIFY( _rControlValue >>= sControlValue );
+ switch( nPropId )
+ {
+ case PROPERTY_ID_LIST_CELL_RANGE:
+ aPropertyValue <<= m_pHelper->createCellListSourceFromStringAddress( sControlValue );
+ break;
+
+ case PROPERTY_ID_BOUND_CELL:
+ {
+ // if we have the possibility of an integer binding, then we must preserve
+ // this property's value (e.g. if the current binding is an integer binding, then
+ // the newly created one must be, too)
+ bool bIntegerBinding = false;
+ if ( m_pHelper->isCellIntegerBindingAllowed() )
+ {
+ sal_Int16 nCurrentBindingType = 0;
+ getPropertyValue( PROPERTY_CELL_EXCHANGE_TYPE ) >>= nCurrentBindingType;
+ bIntegerBinding = ( nCurrentBindingType != 0 );
+ }
+ aPropertyValue <<= m_pHelper->createCellBindingFromStringAddress( sControlValue, bIntegerBinding );
+ }
+ break;
+
+ case PROPERTY_ID_CELL_EXCHANGE_TYPE:
+ m_pCellExchangeConverter->getValueFromDescription( sControlValue, aPropertyValue );
+ break;
+
+ default:
+ OSL_FAIL( "CellBindingPropertyHandler::convertToPropertyValue: cannot handle this!" );
+ break;
+ }
+
+ return aPropertyValue;
+ }
+
+
+ Any SAL_CALL CellBindingPropertyHandler::convertToControlValue( const OUString& _rPropertyName,
+ const Any& _rPropertyValue, const Type& /*_rControlValueType*/ )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Any aControlValue;
+
+ OSL_ENSURE(
+ m_pHelper,
+ "CellBindingPropertyHandler::convertToControlValue: we have no SupportedProperties!");
+ if (!m_pHelper)
+ return aControlValue;
+
+ PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) );
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_BOUND_CELL:
+ {
+ Reference< XValueBinding > xBinding;
+ bool bSuccess = _rPropertyValue >>= xBinding;
+ OSL_ENSURE( bSuccess, "CellBindingPropertyHandler::convertToControlValue: invalid value (1)!" );
+
+ // the only value binding we support so far is linking to spreadsheet cells
+ aControlValue <<= m_pHelper->getStringAddressFromCellBinding( xBinding );
+ }
+ break;
+
+ case PROPERTY_ID_LIST_CELL_RANGE:
+ {
+ Reference< XListEntrySource > xSource;
+ bool bSuccess = _rPropertyValue >>= xSource;
+ OSL_ENSURE( bSuccess, "CellBindingPropertyHandler::convertToControlValue: invalid value (2)!" );
+
+ // the only value binding we support so far is linking to spreadsheet cells
+ aControlValue <<= m_pHelper->getStringAddressFromCellListSource( xSource );
+ }
+ break;
+
+ case PROPERTY_ID_CELL_EXCHANGE_TYPE:
+ aControlValue <<= m_pCellExchangeConverter->getDescriptionForValue( _rPropertyValue );
+ break;
+
+ default:
+ OSL_FAIL( "CellBindingPropertyHandler::convertToControlValue: cannot handle this!" );
+ break;
+ }
+
+ return aControlValue;
+ }
+
+
+ Sequence< Property > CellBindingPropertyHandler::doDescribeSupportedProperties() const
+ {
+ std::vector< Property > aProperties;
+
+ bool bAllowCellLinking = m_pHelper && m_pHelper->isCellBindingAllowed();
+ bool bAllowCellIntLinking = m_pHelper && m_pHelper->isCellIntegerBindingAllowed();
+ bool bAllowListCellRange = m_pHelper && m_pHelper->isListCellRangeAllowed();
+ if ( bAllowCellLinking || bAllowListCellRange || bAllowCellIntLinking )
+ {
+ sal_Int32 nPos = ( bAllowCellLinking ? 1 : 0 )
+ + ( bAllowListCellRange ? 1 : 0 )
+ + ( bAllowCellIntLinking ? 1 : 0 );
+ aProperties.resize( nPos );
+
+ if ( bAllowCellLinking )
+ {
+ aProperties[ --nPos ] = Property( PROPERTY_BOUND_CELL, PROPERTY_ID_BOUND_CELL,
+ ::cppu::UnoType<OUString>::get(), 0 );
+ }
+ if ( bAllowCellIntLinking )
+ {
+ aProperties[ --nPos ] = Property( PROPERTY_CELL_EXCHANGE_TYPE, PROPERTY_ID_CELL_EXCHANGE_TYPE,
+ ::cppu::UnoType<sal_Int16>::get(), 0 );
+ }
+ if ( bAllowListCellRange )
+ {
+ aProperties[ --nPos ] = Property( PROPERTY_LIST_CELL_RANGE, PROPERTY_ID_LIST_CELL_RANGE,
+ ::cppu::UnoType<OUString>::get(), 0 );
+ }
+ }
+
+ if ( aProperties.empty() )
+ return Sequence< Property >();
+ return comphelper::containerToSequence(aProperties);
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_CellBindingPropertyHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::CellBindingPropertyHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/cellbindinghandler.hxx b/extensions/source/propctrlr/cellbindinghandler.hxx
new file mode 100644
index 000000000..4e81b0624
--- /dev/null
+++ b/extensions/source/propctrlr/cellbindinghandler.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "propertyhandler.hxx"
+
+#include <rtl/ref.hxx>
+
+#include <memory>
+
+
+namespace pcr
+{
+
+
+ class CellBindingHelper;
+ class IPropertyEnumRepresentation;
+
+ class CellBindingPropertyHandler : public PropertyHandlerComponent
+ {
+ private:
+ std::unique_ptr< CellBindingHelper > m_pHelper;
+ ::rtl::Reference< IPropertyEnumRepresentation > m_pCellExchangeConverter;
+
+ public:
+ explicit CellBindingPropertyHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ protected:
+ virtual ~CellBindingPropertyHandler() override;
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
+
+ // XPropertyHandler overriables
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override;
+ virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override;
+
+ // PropertyHandler overridables
+ virtual css::uno::Sequence< css::beans::Property >
+ doDescribeSupportedProperties() const override;
+ virtual void onNewComponent() override;
+
+ private:
+ /** updates a property (UI) whose state depends on more than one other property
+
+ ->actuatingPropertyChanged is called for certain properties in whose changes
+ we expressed interes (->getActuatingProperty). Now such a property change can
+ result in simple UI updates, for instance another property being enabled or disabled.
+
+ However, it can also result in a more complex change: The current (UI) state might
+ depend on the value of more than one other property. Those dependent properties (their
+ UI, more precisely) are updated in this method.
+
+ @param _nPropid
+ the ->PropertyId of the dependent property whose UI state is to be updated
+
+ @param _rxInspectorUI
+ provides access to the property browser UI. Must not be <NULL/>.
+ */
+ void impl_updateDependentProperty_nothrow( PropertyId _nPropId, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) const;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/cellbindinghelper.cxx b/extensions/source/propctrlr/cellbindinghelper.cxx
new file mode 100644
index 000000000..3c66fc8c9
--- /dev/null
+++ b/extensions/source/propctrlr/cellbindinghelper.cxx
@@ -0,0 +1,540 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "cellbindinghelper.hxx"
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <unotools/transliterationwrapper.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+#include "formstrings.hxx"
+
+#include <algorithm>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::sheet;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::drawing;
+ using namespace ::com::sun::star::table;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::i18n;
+ using namespace ::com::sun::star::form::binding;
+
+ namespace
+ {
+
+ struct StringCompare
+ {
+ private:
+ OUString m_sReference;
+
+ public:
+ explicit StringCompare( const OUString& _rReference ) : m_sReference( _rReference ) { }
+
+ bool operator()( std::u16string_view _rCompare )
+ {
+ return ( _rCompare == m_sReference );
+ }
+ };
+ }
+
+
+ CellBindingHelper::CellBindingHelper( const Reference< XPropertySet >& _rxControlModel, const Reference< XModel >& _rxContextDocument )
+ :m_xControlModel( _rxControlModel )
+ {
+ OSL_ENSURE( m_xControlModel.is(), "CellBindingHelper::CellBindingHelper: invalid control model!" );
+
+ m_xDocument.set(_rxContextDocument, css::uno::UNO_QUERY);
+ OSL_ENSURE( m_xDocument.is(), "CellBindingHelper::CellBindingHelper: This is no spreadsheet document!" );
+
+ OSL_ENSURE( isSpreadsheetDocumentWhichSupplies( SERVICE_ADDRESS_CONVERSION ),
+ "CellBindingHelper::CellBindingHelper: the document cannot convert address representations!" );
+ }
+
+
+ bool CellBindingHelper::isSpreadsheetDocument( const Reference< XModel >& _rxContextDocument )
+ {
+ return Reference< XSpreadsheetDocument >::query( _rxContextDocument ).is();
+ }
+
+
+ sal_Int16 CellBindingHelper::getControlSheetIndex( Reference< XSpreadsheet >& _out_rxSheet ) const
+ {
+ sal_Int16 nSheetIndex = -1;
+ // every sheet has a draw page, and every draw page has a forms collection.
+ // Our control, OTOH, belongs to a forms collection. Match these...
+ try
+ {
+ // for determining the draw page, we need the forms collection which
+ // the object belongs to. This is the first object up the hierarchy which is
+ // *no* XForm (and, well, no XGridColumnFactory)
+ Reference< XChild > xCheck( m_xControlModel, UNO_QUERY );
+ Reference< XForm > xParentAsForm; if ( xCheck.is() ) xParentAsForm.set(xCheck->getParent(), css::uno::UNO_QUERY);
+ Reference< XGridColumnFactory > xParentAsGrid; if ( xCheck.is() ) xParentAsGrid.set(xCheck->getParent(), css::uno::UNO_QUERY);
+
+ while ( ( xParentAsForm.is() || xParentAsGrid.is() ) && xCheck.is() )
+ {
+ xCheck.set(xCheck->getParent(), css::uno::UNO_QUERY);
+ xParentAsForm.set(xCheck.is() ? xCheck->getParent() : Reference< XForm >(), css::uno::UNO_QUERY);
+ xParentAsGrid.set(xCheck.is() ? xCheck->getParent() : Reference< XGridColumnFactory >(), css::uno::UNO_QUERY);
+ }
+ Reference< XInterface > xFormsCollection( xCheck.is() ? xCheck->getParent() : Reference< XInterface >() );
+
+ // now iterate through the sheets
+ Reference< XIndexAccess > xSheets( m_xDocument->getSheets(), UNO_QUERY );
+ if ( xSheets.is() && xFormsCollection.is() )
+ {
+ for ( sal_Int32 i = 0; i < xSheets->getCount(); ++i )
+ {
+ Reference< XDrawPageSupplier > xSuppPage( xSheets->getByIndex( i ), UNO_QUERY_THROW );
+ Reference< XFormsSupplier > xSuppForms( xSuppPage->getDrawPage(), UNO_QUERY_THROW );
+
+ if ( xSuppForms->getForms() == xFormsCollection )
+ { // found it
+ nSheetIndex = static_cast<sal_Int16>(i);
+ _out_rxSheet.set( xSuppPage, UNO_QUERY_THROW );
+ break;
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ return nSheetIndex;
+ }
+
+
+ bool CellBindingHelper::convertStringAddress( const OUString& _rAddressDescription, CellAddress& /* [out] */ _rAddress ) const
+ {
+ Any aAddress;
+ return doConvertAddressRepresentations(
+ PROPERTY_UI_REPRESENTATION,
+ Any( _rAddressDescription ),
+ PROPERTY_ADDRESS,
+ aAddress,
+ false
+ )
+ && ( aAddress >>= _rAddress );
+ }
+
+
+ bool CellBindingHelper::doConvertAddressRepresentations( const OUString& _rInputProperty, const Any& _rInputValue,
+ const OUString& _rOutputProperty, Any& _rOutputValue, bool _bIsRange ) const
+ {
+ bool bSuccess = false;
+
+ Reference< XPropertySet > xConverter(
+ createDocumentDependentInstance(
+ _bIsRange ? OUString(SERVICE_RANGEADDRESS_CONVERSION) : OUString(SERVICE_ADDRESS_CONVERSION),
+ OUString(),
+ Any()
+ ),
+ UNO_QUERY
+ );
+ OSL_ENSURE( xConverter.is(), "CellBindingHelper::doConvertAddressRepresentations: could not get a converter service!" );
+ if ( xConverter.is() )
+ {
+ try
+ {
+ Reference< XSpreadsheet > xSheet;
+ xConverter->setPropertyValue( PROPERTY_REFERENCE_SHEET, Any( static_cast<sal_Int32>(getControlSheetIndex( xSheet )) ) );
+ xConverter->setPropertyValue( _rInputProperty, _rInputValue );
+ _rOutputValue = xConverter->getPropertyValue( _rOutputProperty );
+ bSuccess = true;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::doConvertAddressRepresentations" );
+ }
+ }
+
+ return bSuccess;
+ }
+
+
+ bool CellBindingHelper::convertStringAddress( const OUString& _rAddressDescription,
+ CellRangeAddress& /* [out] */ _rAddress ) const
+ {
+ Any aAddress;
+ return doConvertAddressRepresentations(
+ PROPERTY_UI_REPRESENTATION,
+ Any( _rAddressDescription ),
+ PROPERTY_ADDRESS,
+ aAddress,
+ true
+ )
+ && ( aAddress >>= _rAddress );
+ }
+
+
+ Reference< XValueBinding > CellBindingHelper::createCellBindingFromAddress( const CellAddress& _rAddress, bool _bSupportIntegerExchange ) const
+ {
+ Reference< XValueBinding > xBinding( createDocumentDependentInstance(
+ _bSupportIntegerExchange ? OUString(SERVICE_SHEET_CELL_INT_BINDING) : OUString(SERVICE_SHEET_CELL_BINDING),
+ PROPERTY_BOUND_CELL,
+ Any( _rAddress )
+ ), UNO_QUERY );
+
+ return xBinding;
+ }
+
+
+ Reference< XValueBinding > CellBindingHelper::createCellBindingFromStringAddress( const OUString& _rAddress, bool _bSupportIntegerExchange ) const
+ {
+ Reference< XValueBinding > xBinding;
+ if ( !m_xDocument.is() )
+ // very bad ...
+ return xBinding;
+
+ // get the UNO representation of the address
+ CellAddress aAddress;
+ if ( _rAddress.isEmpty() || !convertStringAddress( _rAddress, aAddress ) )
+ return xBinding;
+
+ return createCellBindingFromAddress( aAddress, _bSupportIntegerExchange );
+ }
+
+
+ Reference< XListEntrySource > CellBindingHelper::createCellListSourceFromStringAddress( const OUString& _rAddress ) const
+ {
+ Reference< XListEntrySource > xSource;
+
+ CellRangeAddress aRangeAddress;
+ if ( _rAddress.isEmpty() || !convertStringAddress( _rAddress, aRangeAddress ) )
+ return xSource;
+
+ // create a range object for this address
+ xSource.set(createDocumentDependentInstance(
+ SERVICE_SHEET_CELLRANGE_LISTSOURCE,
+ PROPERTY_LIST_CELL_RANGE,
+ Any( aRangeAddress )
+ ), css::uno::UNO_QUERY);
+
+ return xSource;
+ }
+
+
+ Reference< XInterface > CellBindingHelper::createDocumentDependentInstance( const OUString& _rService, const OUString& _rArgumentName,
+ const Any& _rArgumentValue ) const
+ {
+ Reference< XInterface > xReturn;
+
+ Reference< XMultiServiceFactory > xDocumentFactory( m_xDocument, UNO_QUERY );
+ OSL_ENSURE( xDocumentFactory.is(), "CellBindingHelper::createDocumentDependentInstance: no document service factory!" );
+ if ( xDocumentFactory.is() )
+ {
+ try
+ {
+ if ( !_rArgumentName.isEmpty() )
+ {
+ Sequence aArgs{ Any(NamedValue(_rArgumentName, _rArgumentValue)) };
+ xReturn = xDocumentFactory->createInstanceWithArguments( _rService, aArgs );
+ }
+ else
+ {
+ xReturn = xDocumentFactory->createInstance( _rService );
+ }
+ }
+ catch ( const Exception& )
+ {
+ OSL_FAIL( "CellBindingHelper::createDocumentDependentInstance: could not create the binding at the document!" );
+ }
+ }
+ return xReturn;
+ }
+
+
+ bool CellBindingHelper::getAddressFromCellBinding(
+ const Reference< XValueBinding >& _rxBinding, CellAddress& _rAddress ) const
+ {
+ OSL_PRECOND( !_rxBinding.is() || isCellBinding( _rxBinding ), "CellBindingHelper::getAddressFromCellBinding: this is no cell binding!" );
+
+ bool bReturn = false;
+ if ( !m_xDocument.is() )
+ // very bad ...
+ return bReturn;
+
+ try
+ {
+ Reference< XPropertySet > xBindingProps( _rxBinding, UNO_QUERY );
+ OSL_ENSURE( xBindingProps.is() || !_rxBinding.is(), "CellBindingHelper::getAddressFromCellBinding: no property set for the binding!" );
+ if ( xBindingProps.is() )
+ {
+ bReturn = ( xBindingProps->getPropertyValue( PROPERTY_BOUND_CELL ) >>= _rAddress );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::getAddressFromCellBinding" );
+ }
+
+ return bReturn;
+ }
+
+
+ OUString CellBindingHelper::getStringAddressFromCellBinding( const Reference< XValueBinding >& _rxBinding ) const
+ {
+ CellAddress aAddress;
+ OUString sAddress;
+ if ( getAddressFromCellBinding( _rxBinding, aAddress ) )
+ {
+ Any aStringAddress;
+ doConvertAddressRepresentations( PROPERTY_ADDRESS, Any( aAddress ),
+ PROPERTY_UI_REPRESENTATION, aStringAddress, false );
+
+ aStringAddress >>= sAddress;
+ }
+
+ return sAddress;
+ }
+
+
+ OUString CellBindingHelper::getStringAddressFromCellListSource( const Reference< XListEntrySource >& _rxSource ) const
+ {
+ OSL_PRECOND( !_rxSource.is() || isCellRangeListSource( _rxSource ), "CellBindingHelper::getStringAddressFromCellListSource: this is no cell list source!" );
+
+ OUString sAddress;
+ if ( !m_xDocument.is() )
+ // very bad ...
+ return sAddress;
+
+ try
+ {
+ Reference< XPropertySet > xSourceProps( _rxSource, UNO_QUERY );
+ OSL_ENSURE( xSourceProps.is() || !_rxSource.is(), "CellBindingHelper::getStringAddressFromCellListSource: no property set for the list source!" );
+ if ( xSourceProps.is() )
+ {
+ CellRangeAddress aRangeAddress;
+ xSourceProps->getPropertyValue( PROPERTY_LIST_CELL_RANGE ) >>= aRangeAddress;
+
+ Any aStringAddress;
+ doConvertAddressRepresentations( PROPERTY_ADDRESS, Any( aRangeAddress ),
+ PROPERTY_UI_REPRESENTATION, aStringAddress, true );
+ aStringAddress >>= sAddress;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::getStringAddressFromCellListSource" );
+ }
+
+ return sAddress;
+ }
+
+
+ bool CellBindingHelper::isSpreadsheetDocumentWhichSupplies( const OUString& _rService ) const
+ {
+ bool bYesItIs = false;
+
+ Reference< XServiceInfo > xSI( m_xDocument, UNO_QUERY );
+ if ( xSI.is() && xSI->supportsService( SERVICE_SPREADSHEET_DOCUMENT ) )
+ {
+ Reference< XMultiServiceFactory > xDocumentFactory( m_xDocument, UNO_QUERY );
+ OSL_ENSURE( xDocumentFactory.is(), "CellBindingHelper::isSpreadsheetDocumentWhichSupplies: spreadsheet document, but no factory?" );
+
+ if ( xDocumentFactory.is() )
+ {
+ const Sequence<OUString> aAvailableServices = xDocumentFactory->getAvailableServiceNames( );
+
+ bYesItIs = std::any_of(
+ aAvailableServices.begin(),
+ aAvailableServices.end(),
+ StringCompare( _rService )
+ );
+ }
+ }
+
+ return bYesItIs;
+ }
+
+
+ bool CellBindingHelper::isListCellRangeAllowed( ) const
+ {
+ bool bAllow( false );
+
+ Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY );
+ if ( xSink.is() )
+ {
+ bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_SHEET_CELLRANGE_LISTSOURCE );
+ }
+
+ return bAllow;
+ }
+
+
+ bool CellBindingHelper::isCellIntegerBindingAllowed( ) const
+ {
+ bool bAllow( true );
+
+ // first, we only offer this for controls which allow bindings in general
+ Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
+ if ( !xBindable.is() )
+ bAllow = false;
+
+ // then, we must live in a spreadsheet document which can provide the special
+ // service needed for exchanging integer values
+ if ( bAllow )
+ bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_SHEET_CELL_INT_BINDING );
+
+ // then, we only offer this for list boxes
+ if ( bAllow )
+ {
+ try
+ {
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ m_xControlModel->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId;
+ if ( FormComponentType::LISTBOX != nClassId )
+ bAllow = false;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::isCellIntegerBindingAllowed" );
+ // are there really control models which survive isCellBindingAllowed, but don't have a ClassId
+ // property?
+ bAllow = false;
+ }
+ }
+
+ return bAllow;
+ }
+
+
+ bool CellBindingHelper::isCellBindingAllowed( ) const
+ {
+ bool bAllow( false );
+
+ Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
+ if ( xBindable.is() )
+ {
+ // the control can potentially be bound to an external value
+ // Does it live within a Calc document, and is able to supply CellBindings?
+ bAllow = isSpreadsheetDocumentWhichSupplies( SERVICE_SHEET_CELL_BINDING );
+ }
+
+ // disallow for some types
+ // TODO: shouldn't the XBindableValue supply a list of supported types, and we can distinguish
+ // using this list? The current behavior below is somewhat hackish...
+ if ( bAllow )
+ {
+ try
+ {
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ m_xControlModel->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId;
+ if ( ( FormComponentType::DATEFIELD == nClassId ) || ( FormComponentType::TIMEFIELD == nClassId ) )
+ bAllow = false;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "CellBindingHelper::isCellBindingAllowed" );
+ bAllow = false;
+ }
+ }
+ return bAllow;
+ }
+
+
+ bool CellBindingHelper::isCellBinding( const Reference< XValueBinding >& _rxBinding )
+ {
+ return doesComponentSupport( _rxBinding, SERVICE_SHEET_CELL_BINDING );
+ }
+
+
+ bool CellBindingHelper::isCellIntegerBinding( const Reference< XValueBinding >& _rxBinding )
+ {
+ return doesComponentSupport( _rxBinding, SERVICE_SHEET_CELL_INT_BINDING );
+ }
+
+
+ bool CellBindingHelper::isCellRangeListSource( const Reference< XListEntrySource >& _rxSource )
+ {
+ return doesComponentSupport( _rxSource, SERVICE_SHEET_CELLRANGE_LISTSOURCE );
+ }
+
+
+ bool CellBindingHelper::doesComponentSupport( const Reference< XInterface >& _rxComponent, const OUString& _rService )
+ {
+ Reference< XServiceInfo > xSI( _rxComponent, UNO_QUERY );
+ bool bDoes = xSI.is() && xSI->supportsService( _rService );
+ return bDoes;
+ }
+
+
+ Reference< XValueBinding > CellBindingHelper::getCurrentBinding( ) const
+ {
+ Reference< XValueBinding > xBinding;
+ Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
+ if ( xBindable.is() )
+ xBinding = xBindable->getValueBinding();
+ return xBinding;
+ }
+
+
+ Reference< XListEntrySource > CellBindingHelper::getCurrentListSource( ) const
+ {
+ Reference< XListEntrySource > xSource;
+ Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY );
+ if ( xSink.is() )
+ xSource = xSink->getListEntrySource();
+ return xSource;
+ }
+
+
+ void CellBindingHelper::setBinding( const Reference< XValueBinding >& _rxBinding )
+ {
+ Reference< XBindableValue > xBindable( m_xControlModel, UNO_QUERY );
+ OSL_PRECOND( xBindable.is(), "CellBindingHelper::setBinding: the object is not bindable!" );
+ if ( xBindable.is() )
+ xBindable->setValueBinding( _rxBinding );
+ }
+
+
+ void CellBindingHelper::setListSource( const Reference< XListEntrySource >& _rxSource )
+ {
+ Reference< XListEntrySink > xSink( m_xControlModel, UNO_QUERY );
+ OSL_PRECOND( xSink.is(), "CellBindingHelper::setListSource: the object is no list entry sink!" );
+ if ( xSink.is() )
+ xSink->setListEntrySource( _rxSource );
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/cellbindinghelper.hxx b/extensions/source/propctrlr/cellbindinghelper.hxx
new file mode 100644
index 000000000..a2c85cd65
--- /dev/null
+++ b/extensions/source/propctrlr/cellbindinghelper.hxx
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/form/binding/XListEntrySource.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+
+
+namespace pcr
+{
+
+ /** encapsulates functionality related to binding a form control to a spreadsheet cell
+ */
+ class CellBindingHelper final
+ {
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xControlModel; // the model we work for
+ css::uno::Reference< css::sheet::XSpreadsheetDocument >
+ m_xDocument; // the document where the model lives
+
+ public:
+ /** ctor
+ @param _rxControlModel
+ the control model which is or will be bound
+ */
+ CellBindingHelper(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel,
+ const css::uno::Reference< css::frame::XModel >& _rxContextDocument
+ );
+
+ /** determines whether the given model is a spreadsheet document model
+
+ <p>If this method returns <FALSE/>, you cannot instantiate a CellBindingHelper with
+ the document, since then no of its functionality will be available.</p>
+ */
+ static bool isSpreadsheetDocument(
+ const css::uno::Reference< css::frame::XModel >& _rxContextDocument
+ );
+
+ /** gets a cell binding for the given address
+ @precond
+ isCellBindingAllowed returns <TRUE/>
+ */
+ css::uno::Reference< css::form::binding::XValueBinding >
+ createCellBindingFromStringAddress(
+ const OUString& _rAddress,
+ bool _bSupportIntegerExchange
+ ) const;
+
+ /** creates a cell binding (supporting integer exchange, if requested) for
+ the given address object
+ */
+ css::uno::Reference< css::form::binding::XValueBinding >
+ createCellBindingFromAddress(
+ const css::table::CellAddress& _rAddress,
+ bool _bSupportIntegerExchange
+ ) const;
+
+ /** gets a cell range list source binding for the given address
+ */
+ css::uno::Reference< css::form::binding::XListEntrySource >
+ createCellListSourceFromStringAddress( const OUString& _rAddress ) const;
+
+ /** creates a string representation for the given value binding's address
+
+ <p>If the sheet of the bound cell is the same as the sheet which our control belongs
+ to, then the sheet name is omitted in the resulting string representation.</p>
+
+ @precond
+ The binding is a valid cell binding, or <NULL/>
+ @see isCellBinding
+ */
+ OUString getStringAddressFromCellBinding(
+ const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding
+ ) const;
+
+ /** creates an address object for the given value binding's address
+
+ @precond
+ The binding is a valid cell binding, or <NULL/>
+ @return
+ <FALSE/> if and only if an error occurred and no valid address could be obtained
+ @see isCellBinding
+ */
+ bool getAddressFromCellBinding(
+ const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding,
+ css::table::CellAddress& _rAddress
+ ) const;
+
+ /** creates a string representation for the given list source's range address
+
+ <p>If the sheet of the cell range which acts as list source is the same as the
+ sheet which our control belongs to, then the sheet name is omitted in the
+ resulting string representation.</p>
+
+ @precond
+ The object is a valid cell range list source, or <NULL/>
+ @see isCellRangeListSource
+ */
+ OUString getStringAddressFromCellListSource(
+ const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource
+ ) const;
+
+ /** returns the current binding of our control model, if any.
+ */
+ css::uno::Reference< css::form::binding::XValueBinding >
+ getCurrentBinding( ) const;
+
+ /** returns the current external list source of the control model, if any
+ */
+ css::uno::Reference< css::form::binding::XListEntrySource >
+ getCurrentListSource( ) const;
+
+ /** sets a new binding for our control model
+ @precond
+ the control model is bindable (which is implied by <member>isCellBindingAllowed</member>
+ returning <TRUE/>)
+ */
+ void setBinding(
+ const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding
+ );
+
+ /** sets a list source for our control model
+ @precond
+ the control model is a list sink (which is implied by <member>isListCellRangeAllowed</member>
+ returning <TRUE/>)
+ */
+ void setListSource(
+ const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource
+ );
+
+ /** checks whether it's possible to bind the control model to a spreadsheet cell
+ */
+ bool isCellBindingAllowed( ) const;
+
+ /** checks whether it's possible to bind the control model to a spreadsheet cell,
+ with exchanging integer values
+ */
+ bool isCellIntegerBindingAllowed( ) const;
+
+ /** checks whether it's possible to bind the control model to range of spreadsheet cells
+ supplying the list entries
+ */
+ bool isListCellRangeAllowed( ) const;
+
+ /** checks whether a given binding is a spreadsheet cell binding
+ */
+ static bool isCellBinding(
+ const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding
+ );
+
+ /** checks whether a given binding is a spreadsheet cell binding, exchanging
+ integer values
+ */
+ static bool isCellIntegerBinding(
+ const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding
+ );
+
+ /** checks whether a given list source is a spreadsheet cell list source
+ */
+ static bool isCellRangeListSource(
+ const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource
+ );
+
+ /** retrieves the index of the sheet which our control belongs to
+ @return the index of the sheet which our control belongs to or -1, if an error occurred
+ */
+ sal_Int16 getControlSheetIndex(
+ css::uno::Reference< css::sheet::XSpreadsheet >& _out_rxSheet
+ ) const;
+
+ private:
+ /** creates an address object from a string representation of a cell address
+ */
+ bool convertStringAddress(
+ const OUString& _rAddressDescription,
+ css::table::CellAddress& /* [out] */ _rAddress
+ ) const;
+
+ /** creates an address range object from a string representation of a cell range address
+ */
+ bool convertStringAddress(
+ const OUString& _rAddressDescription,
+ css::table::CellRangeAddress& /* [out] */ _rAddress
+ ) const;
+
+ /** determines if our document is a spreadsheet document, *and* can supply
+ the given service
+ */
+ bool isSpreadsheetDocumentWhichSupplies( const OUString& _rService ) const;
+
+ /** checks whether a given component supports a given service
+ */
+ static bool doesComponentSupport(
+ const css::uno::Reference< css::uno::XInterface >& _rxComponent,
+ const OUString& _rService
+ );
+
+ /** uses the document (it's factory interface, respectively) to create a component instance
+ @param _rService
+ the service name
+ @param _rArgumentName
+ the name of the single argument to pass during creation. May be empty, in this case
+ no arguments are passed
+ @param _rArgumentValue
+ the value of the instantiation argument. Not evaluated if <arg>_rArgumentName</arg>
+ is empty.
+ */
+ css::uno::Reference< css::uno::XInterface >
+ createDocumentDependentInstance(
+ const OUString& _rService,
+ const OUString& _rArgumentName,
+ const css::uno::Any& _rArgumentValue
+ ) const;
+
+ /** converts an address representation into another one
+
+ @param _rInputProperty
+ the input property name for the conversion service
+ @param _rInputValue
+ the input property value for the conversion service
+ @param _rOutputProperty
+ the output property name for the conversion service
+ @param _rOutputValue
+ the output property value for the conversion service
+ @param _bIsRange
+ if <TRUE/>, the RangeAddressConversion service will be used, else
+ the AddressConversion service
+
+ @return
+ <TRUE/> if any only if the conversion was successful
+
+ @see css::table::CellAddressConversion
+ @see css::table::CellRangeAddressConversion
+ */
+ bool doConvertAddressRepresentations(
+ const OUString& _rInputProperty,
+ const css::uno::Any& _rInputValue,
+ const OUString& _rOutputProperty,
+ css::uno::Any& _rOutputValue,
+ bool _bIsRange
+ ) const;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/commoncontrol.cxx b/extensions/source/propctrlr/commoncontrol.cxx
new file mode 100644
index 000000000..243bbd813
--- /dev/null
+++ b/extensions/source/propctrlr/commoncontrol.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 "commoncontrol.hxx"
+#include <tools/diagnose_ex.h>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::inspection::XPropertyControlContext;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::inspection::XPropertyControl;
+
+ CommonBehaviourControlHelper::CommonBehaviourControlHelper( sal_Int16 _nControlType, XPropertyControl& _rAntiImpl )
+ :m_nControlType( _nControlType )
+ ,m_rAntiImpl( _rAntiImpl )
+ ,m_bModified( false )
+ {
+ }
+
+
+ CommonBehaviourControlHelper::~CommonBehaviourControlHelper()
+ {
+ }
+
+ void CommonBehaviourControlHelper::setControlContext( const Reference< XPropertyControlContext >& _controlcontext )
+ {
+ m_xContext = _controlcontext;
+ }
+
+ void CommonBehaviourControlHelper::notifyModifiedValue( )
+ {
+ if ( isModified() && m_xContext.is() )
+ {
+ try
+ {
+ m_xContext->valueChanged( &m_rAntiImpl );
+ m_bModified = false;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+ }
+
+ void CommonBehaviourControlHelper::editChanged()
+ {
+ setModified();
+ }
+
+ IMPL_LINK_NOARG( CommonBehaviourControlHelper, EditModifiedHdl, weld::Entry&, void )
+ {
+ editChanged();
+ }
+
+ IMPL_LINK_NOARG( CommonBehaviourControlHelper, ModifiedHdl, weld::ComboBox&, void )
+ {
+ setModified();
+ // notify as soon as the Data source is changed, don't wait until we lose focus
+ // because the Content dropdown cannot be populated after it is popped up
+ // and going from Data source direct to Content may give focus-lost to
+ // Content after the popup attempt is made
+ notifyModifiedValue();
+ }
+
+ IMPL_LINK_NOARG( CommonBehaviourControlHelper, MetricModifiedHdl, weld::MetricSpinButton&, void )
+ {
+ setModified();
+ }
+
+ IMPL_LINK_NOARG( CommonBehaviourControlHelper, FormattedModifiedHdl, weld::FormattedSpinButton&, void )
+ {
+ setModified();
+ }
+
+ IMPL_LINK_NOARG( CommonBehaviourControlHelper, TimeModifiedHdl, weld::FormattedSpinButton&, void )
+ {
+ setModified();
+ }
+
+ IMPL_LINK_NOARG( CommonBehaviourControlHelper, DateModifiedHdl, SvtCalendarBox&, void )
+ {
+ setModified();
+ }
+
+ IMPL_LINK_NOARG( CommonBehaviourControlHelper, ColorModifiedHdl, ColorListBox&, void )
+ {
+ setModified();
+ }
+
+ IMPL_LINK_NOARG( CommonBehaviourControlHelper, GetFocusHdl, weld::Widget&, void )
+ {
+ try
+ {
+ if ( m_xContext.is() )
+ m_xContext->focusGained( &m_rAntiImpl );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ IMPL_LINK_NOARG( CommonBehaviourControlHelper, LoseFocusHdl, weld::Widget&, void )
+ {
+ // TODO/UNOize: should this be outside the default control's implementations? If somebody
+ // has an own control implementation, which does *not* do this - would this be allowed?
+ // If not, then we must move this logic out of here.
+ notifyModifiedValue();
+ }
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/commoncontrol.hxx b/extensions/source/propctrlr/commoncontrol.hxx
new file mode 100644
index 000000000..746f2f56f
--- /dev/null
+++ b/extensions/source/propctrlr/commoncontrol.hxx
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/inspection/XPropertyControl.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <tools/link.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+
+class NotifyEvent;
+class ColorListBox;
+class SvtCalendarBox;
+
+namespace pcr
+{
+
+ //= CommonBehaviourControlHelper
+
+ /** A helper class for implementing the <type scope="css::inspection">XPropertyControl</type>
+ or one of its derived interfaces.
+
+ This class is used as a base class the CommonBehaviourControl template.
+ */
+ class CommonBehaviourControlHelper
+ {
+ private:
+ sal_Int16 m_nControlType;
+ css::uno::Reference< css::inspection::XPropertyControlContext >
+ m_xContext;
+ css::inspection::XPropertyControl&
+ m_rAntiImpl;
+ bool m_bModified;
+
+ public:
+ /** creates the instance
+ @param nControlType
+ the type of the control - one of the <type scope="css::inspection">PropertyControlType</type>
+ constants
+ @param pAntiImpl
+ Reference to the instance as whose "impl-class" we act i.e. the CommonBehaviourControl<> template,
+ which is why we hold it without acquiring it/
+ */
+ CommonBehaviourControlHelper(
+ sal_Int16 nControlType,
+ css::inspection::XPropertyControl& rAntiImpl);
+
+ virtual ~CommonBehaviourControlHelper();
+
+ virtual void setModified() { m_bModified = true; }
+
+ virtual void editChanged();
+
+ // XPropertyControl
+ /// @throws css::uno::RuntimeException
+ ::sal_Int16 getControlType() const { return m_nControlType; }
+ /// @throws css::uno::RuntimeException
+ const css::uno::Reference< css::inspection::XPropertyControlContext >& getControlContext() const { return m_xContext; }
+ /// @throws css::uno::RuntimeException
+ void setControlContext( const css::uno::Reference< css::inspection::XPropertyControlContext >& controlcontext );
+ /// @throws css::uno::RuntimeException
+ bool isModified( ) const { return m_bModified; }
+ /// @throws css::uno::RuntimeException
+ void notifyModifiedValue( );
+
+ virtual weld::Widget* getWidget() = 0;
+
+ /// may be used by derived classes, they forward the event to the PropCtrListener
+ DECL_LINK( ModifiedHdl, weld::ComboBox&, void );
+ DECL_LINK( ColorModifiedHdl, ColorListBox&, void );
+ DECL_LINK( EditModifiedHdl, weld::Entry&, void );
+ DECL_LINK( MetricModifiedHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( FormattedModifiedHdl, weld::FormattedSpinButton&, void );
+ DECL_LINK( TimeModifiedHdl, weld::FormattedSpinButton&, void );
+ DECL_LINK( DateModifiedHdl, SvtCalendarBox&, void );
+ DECL_LINK( GetFocusHdl, weld::Widget&, void );
+ DECL_LINK( LoseFocusHdl, weld::Widget&, void );
+ };
+
+
+ //= CommonBehaviourControl
+
+ /** implements a base class for <type scope="css::inspection">XPropertyControl</type>
+ implementations
+
+ @param TControlInterface
+ an interface class which is derived from (or identical to) <type scope="css::inspection">XPropertyControl</type>
+ @param TControlWindow
+ a class which is derived from weld::Widget
+ */
+ template < class TControlInterface, class TControlWindow >
+ class CommonBehaviourControl :public ::cppu::BaseMutex
+ ,public ::cppu::WeakComponentImplHelper< TControlInterface >
+ ,public CommonBehaviourControlHelper
+ {
+ protected:
+ typedef ::cppu::WeakComponentImplHelper< TControlInterface > ComponentBaseClass;
+
+ inline CommonBehaviourControl(sal_Int16 nControlType,
+ std::unique_ptr<weld::Builder> xBuilder,
+ std::unique_ptr<TControlWindow> xWidget,
+ bool bReadOnly);
+
+ virtual ~CommonBehaviourControl() override
+ {
+ clear_widgetry();
+ }
+
+ // XPropertyControl - delegated to ->m_aImplControl
+ virtual ::sal_Int16 SAL_CALL getControlType() override
+ { return CommonBehaviourControlHelper::getControlType(); }
+ virtual css::uno::Reference< css::inspection::XPropertyControlContext > SAL_CALL getControlContext() override
+ { return CommonBehaviourControlHelper::getControlContext(); }
+ virtual void SAL_CALL setControlContext( const css::uno::Reference< css::inspection::XPropertyControlContext >& controlcontext ) override
+ { CommonBehaviourControlHelper::setControlContext( controlcontext ); }
+ virtual css::uno::Reference< css::awt::XWindow > SAL_CALL getControlWindow() override
+ { return new weld::TransportAsXWindow(getWidget()); }
+ virtual sal_Bool SAL_CALL isModified( ) override
+ { return CommonBehaviourControlHelper::isModified(); }
+ virtual void SAL_CALL notifyModifiedValue( ) override
+ { CommonBehaviourControlHelper::notifyModifiedValue(); }
+
+ void clear_widgetry()
+ {
+ if (!m_xControlWindow)
+ return;
+ weld::Widget* pWidget = getWidget();
+ std::unique_ptr<weld::Container> xParent(pWidget->weld_parent());
+ xParent->move(pWidget, nullptr);
+ m_xControlWindow.reset();
+ m_xBuilder.reset();
+ }
+
+ // XComponent
+ virtual void SAL_CALL disposing() override
+ {
+ clear_widgetry();
+ }
+
+ TControlWindow* getTypedControlWindow()
+ { return m_xControlWindow.get(); }
+ const TControlWindow* getTypedControlWindow() const
+ { return m_xControlWindow.get(); }
+
+ virtual void SetModifyHandler()
+ {
+ m_xControlWindow->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xControlWindow->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+ }
+
+ /** checks whether the instance is already disposed
+ @throws DisposedException
+ if the instance is already disposed
+ */
+ inline void impl_checkDisposed_throw();
+ protected:
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ private:
+ std::unique_ptr<TControlWindow> m_xControlWindow;
+ };
+
+ //= CommonBehaviourControl - implementation
+ template< class TControlInterface, class TControlWindow >
+ inline CommonBehaviourControl< TControlInterface, TControlWindow >::CommonBehaviourControl(sal_Int16 nControlType,
+ std::unique_ptr<weld::Builder> xBuilder,
+ std::unique_ptr<TControlWindow> xWidget,
+ bool bReadOnly)
+ : ComponentBaseClass( m_aMutex )
+ , CommonBehaviourControlHelper( nControlType, *this )
+ , m_xBuilder(std::move(xBuilder))
+ , m_xControlWindow(std::move(xWidget))
+ {
+ if (bReadOnly)
+ {
+ // disable widget by default, entries will override to enable the widget but set it non-editable
+ m_xControlWindow->set_sensitive(false);
+ }
+ }
+
+ template< class TControlInterface, class TControlWindow >
+ inline void CommonBehaviourControl< TControlInterface, TControlWindow >::impl_checkDisposed_throw()
+ {
+ if ( ComponentBaseClass::rBHelper.bDisposed )
+ throw css::lang::DisposedException( OUString(), *this );
+ }
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/composeduiupdate.cxx b/extensions/source/propctrlr/composeduiupdate.cxx
new file mode 100644
index 000000000..0e32ef19a
--- /dev/null
+++ b/extensions/source/propctrlr/composeduiupdate.cxx
@@ -0,0 +1,786 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "composeduiupdate.hxx"
+#include "pcrcommon.hxx"
+
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/inspection/PropertyLineElement.hpp>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <algorithm>
+#include <map>
+#include <set>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::lang::NullPointerException;
+ using ::com::sun::star::inspection::XPropertyHandler;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::inspection::XObjectInspectorUI;
+ using ::com::sun::star::inspection::XPropertyControl;
+ using ::com::sun::star::inspection::XPropertyControlObserver;
+
+ namespace PropertyLineElement = ::com::sun::star::inspection::PropertyLineElement;
+
+ namespace
+ {
+ struct HandlerLess
+ {
+ bool operator()( const Reference< XPropertyHandler >& lhs, const Reference< XPropertyHandler >& rhs) const
+ {
+ return lhs.get() < rhs.get();
+ }
+ };
+
+
+ typedef std::set< OUString > StringBag;
+ typedef std::map< sal_Int16, StringBag > MapIntToStringBag;
+ }
+
+
+ // callbacks for CachedInspectorUI
+
+ typedef void (ComposedPropertyUIUpdate::*FNotifySingleUIChange)();
+
+ typedef ::cppu::WeakImplHelper < css::inspection::XObjectInspectorUI
+ > CachedInspectorUI_Base;
+
+ namespace {
+
+ struct CachedInspectorUI : public CachedInspectorUI_Base
+ {
+ private:
+ ::osl::Mutex m_aMutex;
+ bool m_bDisposed;
+ ComposedPropertyUIUpdate&
+ m_rMaster;
+ FNotifySingleUIChange m_pUIChangeNotification;
+
+ // enablePropertyUI cache
+ StringBag aEnabledProperties;
+ StringBag aDisabledProperties;
+
+ // show/hidePropertyUI cache
+ StringBag aShownProperties;
+ StringBag aHiddenProperties;
+
+ // rebuildPropertyUI cache
+ StringBag aRebuiltProperties;
+
+ // showCategory cache
+ StringBag aShownCategories;
+ StringBag aHiddenCategories;
+
+ // enablePropertyUIElements cache
+ MapIntToStringBag aEnabledElements;
+ MapIntToStringBag aDisabledElements;
+
+ public:
+ typedef StringBag& (CachedInspectorUI::*FGetStringBag)();
+
+ // enablePropertyUI cache
+ StringBag& getEnabledProperties() { return aEnabledProperties; }
+ StringBag& getDisabledProperties() { return aDisabledProperties; }
+
+ // show/hidePropertyUI cache
+ StringBag& getShownProperties() { return aShownProperties; }
+ StringBag& getHiddenProperties() { return aHiddenProperties; }
+
+ // rebuildPropertyUI cache
+ StringBag& getRebuiltProperties() { return aRebuiltProperties; }
+
+ // showCategory cache
+ StringBag& getShownCategories() { return aShownCategories; }
+ StringBag& getHiddenCategories() { return aHiddenCategories; }
+
+ // enablePropertyUIElements
+ StringBag& getEnabledInputControls() { return aEnabledElements[ PropertyLineElement::InputControl ]; }
+ StringBag& getDisabledInputControls() { return aDisabledElements[ PropertyLineElement::InputControl ]; }
+ StringBag& getEnabledPrimaryButtons() { return aEnabledElements[ PropertyLineElement::PrimaryButton ]; }
+ StringBag& getDisabledPrimaryButtons() { return aDisabledElements[ PropertyLineElement::PrimaryButton ]; }
+ StringBag& getEnabledSecondaryButtons() { return aEnabledElements[ PropertyLineElement::SecondaryButton ]; }
+ StringBag& getDisabledSecondaryButtons() { return aDisabledElements[ PropertyLineElement::SecondaryButton ]; }
+
+ public:
+ CachedInspectorUI( ComposedPropertyUIUpdate& _rMaster, FNotifySingleUIChange _pUIChangeNotification );
+ CachedInspectorUI(const CachedInspectorUI&) = delete;
+ CachedInspectorUI& operator=(const CachedInspectorUI&) = delete;
+
+ /// disposes the instance
+ void dispose();
+
+ // XObjectInspectorUI overridables
+ virtual void SAL_CALL enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable ) override;
+ virtual void SAL_CALL enablePropertyUIElements( const OUString& _rPropertyName, ::sal_Int16 _nElements, sal_Bool _bEnable ) override;
+ virtual void SAL_CALL rebuildPropertyUI( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL showPropertyUI( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL hidePropertyUI( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL showCategory( const OUString& _rCategory, sal_Bool _bShow ) override;
+ virtual Reference< XPropertyControl > SAL_CALL getPropertyControl( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL registerControlObserver( const Reference< XPropertyControlObserver >& Observer ) override;
+ virtual void SAL_CALL revokeControlObserver( const Reference< XPropertyControlObserver >& Observer ) override;
+ virtual void SAL_CALL setHelpSectionText( const OUString& HelpText ) override;
+
+ protected:
+ virtual ~CachedInspectorUI() override;
+
+ /// throws an exception if the component is already disposed
+ void checkDisposed() const;
+
+ private:
+ void impl_markElementEnabledOrDisabled( const OUString& _rPropertyName, sal_Int16 _nElementIdOrZero, bool _bEnable );
+
+ /** calls <member>m_pUIChangeNotification</member> at <member>m_rMaster</member>
+ */
+ void impl_notifySingleUIChange() const;
+
+ private:
+ class MethodGuard;
+ friend class MethodGuard;
+ class MethodGuard : public ::osl::MutexGuard
+ {
+ public:
+ explicit MethodGuard( CachedInspectorUI& rInstance )
+ : ::osl::MutexGuard( rInstance.m_aMutex )
+ {
+ rInstance.checkDisposed();
+ }
+ };
+ };
+
+ }
+
+ CachedInspectorUI::CachedInspectorUI( ComposedPropertyUIUpdate& _rMaster, FNotifySingleUIChange _pUIChangeNotification )
+ :m_bDisposed( false )
+ ,m_rMaster( _rMaster )
+ ,m_pUIChangeNotification( _pUIChangeNotification )
+ {
+ }
+
+
+ CachedInspectorUI::~CachedInspectorUI()
+ {
+ }
+
+
+ void CachedInspectorUI::dispose()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_bDisposed = true;
+
+ clearContainer( aEnabledProperties );
+ clearContainer( aDisabledProperties );
+ clearContainer( aRebuiltProperties );
+ clearContainer( aShownProperties );
+ clearContainer( aHiddenProperties );
+ clearContainer( aShownCategories );
+ clearContainer( aHiddenCategories );
+ clearContainer( aEnabledElements );
+ clearContainer( aDisabledElements );
+ }
+
+
+ void CachedInspectorUI::checkDisposed() const
+ {
+ if (m_bDisposed)
+ throw DisposedException();
+ }
+
+
+ namespace
+ {
+ void lcl_markStringKeyPositiveOrNegative( const OUString& _rKeyName, StringBag& _rPositives, StringBag& _rNegatives, bool _bMarkPositive )
+ {
+ if ( _bMarkPositive )
+ {
+ _rPositives.insert( _rKeyName );
+ // if the same key has been remember as in the "negative" list before, clear this information, since it's overruled
+ _rNegatives.erase( _rKeyName );
+ }
+ else
+ _rNegatives.insert( _rKeyName );
+ }
+ }
+
+
+ void CachedInspectorUI::enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable )
+ {
+ MethodGuard aGuard( *this );
+ if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) )
+ return;
+
+ lcl_markStringKeyPositiveOrNegative( _rPropertyName, aEnabledProperties, aDisabledProperties, _bEnable );
+ impl_notifySingleUIChange();
+ }
+
+
+ void CachedInspectorUI::impl_markElementEnabledOrDisabled( const OUString& _rPropertyName, sal_Int16 _nElementIdOrZero, bool _bEnable )
+ {
+ if ( _nElementIdOrZero == 0 )
+ return;
+
+ lcl_markStringKeyPositiveOrNegative(
+ _rPropertyName,
+ aEnabledElements[ _nElementIdOrZero ],
+ aDisabledElements[ _nElementIdOrZero ],
+ _bEnable
+ );
+ }
+
+
+ void CachedInspectorUI::impl_notifySingleUIChange() const
+ {
+ (m_rMaster.*m_pUIChangeNotification)();
+ }
+
+
+ void CachedInspectorUI::enablePropertyUIElements( const OUString& _rPropertyName, sal_Int16 _nElements, sal_Bool _bEnable )
+ {
+ MethodGuard aGuard( *this );
+ if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) )
+ return;
+
+ impl_markElementEnabledOrDisabled( _rPropertyName, _nElements & PropertyLineElement::InputControl, _bEnable );
+ impl_markElementEnabledOrDisabled( _rPropertyName, _nElements & PropertyLineElement::PrimaryButton, _bEnable );
+ impl_markElementEnabledOrDisabled( _rPropertyName, _nElements & PropertyLineElement::SecondaryButton, _bEnable );
+
+ impl_notifySingleUIChange();
+ }
+
+
+ void CachedInspectorUI::rebuildPropertyUI( const OUString& _rPropertyName )
+ {
+ MethodGuard aGuard( *this );
+ if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) )
+ return;
+
+ aRebuiltProperties.insert( _rPropertyName );
+
+ impl_notifySingleUIChange();
+ }
+
+
+ void CachedInspectorUI::showPropertyUI( const OUString& _rPropertyName )
+ {
+ MethodGuard aGuard( *this );
+ if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) )
+ return;
+
+ aShownProperties.insert( _rPropertyName );
+ // if the same category has been hidden before, clear this information, since it's overruled
+ aHiddenProperties.erase( _rPropertyName );
+
+ impl_notifySingleUIChange();
+ }
+
+
+ void CachedInspectorUI::hidePropertyUI( const OUString& _rPropertyName )
+ {
+ MethodGuard aGuard( *this );
+ if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) )
+ return;
+
+ aHiddenProperties.insert( _rPropertyName );
+ impl_notifySingleUIChange();
+ }
+
+
+ void CachedInspectorUI::showCategory( const OUString& _rCategory, sal_Bool _bShow )
+ {
+ MethodGuard aGuard( *this );
+
+ lcl_markStringKeyPositiveOrNegative( _rCategory, aShownCategories, aHiddenCategories, _bShow );
+ impl_notifySingleUIChange();
+ }
+
+
+ Reference< XPropertyControl > SAL_CALL CachedInspectorUI::getPropertyControl( const OUString& _rPropertyName )
+ {
+ MethodGuard aGuard( *this );
+ if ( !m_rMaster.shouldContinuePropertyHandling( _rPropertyName ) )
+ return Reference< XPropertyControl >();
+
+ return m_rMaster.getDelegatorUI()->getPropertyControl( _rPropertyName );
+ }
+
+
+ void SAL_CALL CachedInspectorUI::registerControlObserver( const Reference< XPropertyControlObserver >& Observer )
+ {
+ OSL_FAIL( "CachedInspectorUI::registerControlObserver: not expected to be called!" );
+ // CachedInspectorUI is used as context for the controls, and we don't expect them to
+ // register listeners themself
+ m_rMaster.getDelegatorUI()->registerControlObserver( Observer );
+ }
+
+
+ void SAL_CALL CachedInspectorUI::revokeControlObserver( const Reference< XPropertyControlObserver >& Observer )
+ {
+ OSL_FAIL( "CachedInspectorUI::revokeControlObserver: not expected to be called!" );
+ // CachedInspectorUI is used as context for the controls, and we don't expect them to
+ // register listeners themself
+ m_rMaster.getDelegatorUI()->revokeControlObserver( Observer );
+ }
+
+
+ void SAL_CALL CachedInspectorUI::setHelpSectionText( const OUString& HelpText )
+ {
+ m_rMaster.getDelegatorUI()->setHelpSectionText( HelpText );
+ }
+
+
+ // HandlerMap
+
+ typedef std::map < Reference< XPropertyHandler >
+ , ::rtl::Reference< CachedInspectorUI >
+ , HandlerLess
+ > ImplMapHandlerToUI;
+ struct MapHandlerToUI
+ {
+ ImplMapHandlerToUI aHandlers;
+ };
+
+ ComposedPropertyUIUpdate::ComposedPropertyUIUpdate( const Reference< XObjectInspectorUI >& _rxDelegatorUI,
+ IPropertyExistenceCheck* _pPropertyCheck )
+ :m_pCollectedUIs( new MapHandlerToUI )
+ ,m_xDelegatorUI( _rxDelegatorUI )
+ ,m_nSuspendCounter( 0 )
+ ,m_pPropertyCheck( _pPropertyCheck )
+ {
+ if ( !m_xDelegatorUI.is() )
+ throw NullPointerException();
+ }
+
+
+ ComposedPropertyUIUpdate::~ComposedPropertyUIUpdate( )
+ {
+ }
+
+
+ Reference< XObjectInspectorUI > ComposedPropertyUIUpdate::getUIForPropertyHandler( const Reference< XPropertyHandler >& _rxHandler )
+ {
+ impl_checkDisposed();
+
+ ::rtl::Reference< CachedInspectorUI >& rUI = m_pCollectedUIs->aHandlers[ _rxHandler ];
+ if ( !rUI.is() )
+ rUI = new CachedInspectorUI( *this, &ComposedPropertyUIUpdate::callback_inspectorUIChanged_throw );
+ return rUI;
+ }
+
+
+ namespace
+ {
+
+ // an STL-compatible structure which collects strings from a CachedInspectorUI instances
+ struct StringBagCollector
+ {
+ private:
+ StringBag& m_rBag;
+ CachedInspectorUI::FGetStringBag m_pGetter;
+
+ public:
+ StringBagCollector( StringBag& _rBag, CachedInspectorUI::FGetStringBag _pGetter ) :m_rBag( _rBag ), m_pGetter( _pGetter ) { }
+
+ void operator()( const ImplMapHandlerToUI::value_type& _rUI )
+ {
+ StringBag& rBag( ((_rUI.second.get())->*m_pGetter)() );
+ m_rBag.insert( rBag.begin(), rBag.end() );
+ }
+
+ static void collectAll( StringBag& _rAll, const ImplMapHandlerToUI& _rMap, CachedInspectorUI::FGetStringBag _pGetter )
+ {
+ std::for_each( _rMap.begin(), _rMap.end(), StringBagCollector( _rAll, _pGetter ) );
+ }
+ };
+
+
+ // an STL-compatible structure which cleans a certain string bag in a CachedInspectorUI instances
+ struct StringBagClearer
+ {
+ private:
+ CachedInspectorUI::FGetStringBag m_pGetter;
+
+ public:
+ explicit StringBagClearer( CachedInspectorUI::FGetStringBag _pGetter ) :m_pGetter( _pGetter ) { }
+
+ void operator()( const ImplMapHandlerToUI::value_type& _rUI )
+ {
+ clearContainer( ((_rUI.second.get())->*m_pGetter)() );
+ }
+
+ static void clearAll( const ImplMapHandlerToUI& _rMap, CachedInspectorUI::FGetStringBag _pGetter )
+ {
+ std::for_each( _rMap.begin(), _rMap.end(), StringBagClearer( _pGetter ) );
+ }
+ };
+
+ // a typedef for a ->XObjectInspectorUI member function taking a string
+ typedef void ( SAL_CALL XObjectInspectorUI::*FPropertyUISetter )( const OUString& );
+
+
+ // an STL-compatible struct which calls a certain member method (taking a string) at a
+ // given ->XObjectInspectorUI instance
+ struct PropertyUIOperator
+ {
+ private:
+ Reference< XObjectInspectorUI > m_xUpdater;
+ FPropertyUISetter m_pSetter;
+
+ public:
+ PropertyUIOperator( const Reference< XObjectInspectorUI >& _rxInspectorUI, FPropertyUISetter _pSetter )
+ :m_xUpdater( _rxInspectorUI )
+ ,m_pSetter( _pSetter )
+ {
+ }
+
+ void operator()( const OUString& _rPropertyName )
+ {
+ ((m_xUpdater.get())->*m_pSetter)( _rPropertyName );
+ }
+
+ static void forEach( const StringBag& _rProperties, const Reference< XObjectInspectorUI >& _rxDelegatorUI, FPropertyUISetter _pSetter )
+ {
+ std::for_each( _rProperties.begin(), _rProperties.end(), PropertyUIOperator( _rxDelegatorUI, _pSetter ) );
+ }
+ };
+
+
+ // an interface which encapsulates access to a single aspect of the ->XObjectInspectorUI,
+ // where this aspect is given by a string key, and has a boolean value.
+ class IStringKeyBooleanUIUpdate
+ {
+ public:
+ virtual void updateUIForKey( const OUString& _rKey, bool _bFlag ) const = 0;
+
+ virtual ~IStringKeyBooleanUIUpdate() { }
+ };
+
+
+ // FPropertyUIFlagSetter
+
+ /** an implementation of the ->IStringKeyBooleanUIUpdate interface which,
+ for a fixed ->XObjectInspectorUI instance and a fixed UI element (->PropertyLineElement),
+ updates this element for a given property with a given boolean flag
+ (->XObjectInspectorUI::enablePropertyUIElements)
+ */
+ class EnablePropertyUIElement : public IStringKeyBooleanUIUpdate
+ {
+ private:
+ Reference< XObjectInspectorUI > m_xUIUpdate;
+ sal_Int16 m_nElement;
+
+ public:
+ EnablePropertyUIElement( const Reference< XObjectInspectorUI >& _rxUIUpdate, sal_Int16 _nElement )
+ :m_xUIUpdate( _rxUIUpdate )
+ ,m_nElement( _nElement )
+ {
+ }
+ // IStringKeyBooleanUIUpdate
+ virtual void updateUIForKey( const OUString& _rKey, bool _bFlag ) const override;
+ };
+
+
+ void EnablePropertyUIElement::updateUIForKey( const OUString& _rKey, bool _bFlag ) const
+ {
+ m_xUIUpdate->enablePropertyUIElements( _rKey, m_nElement, _bFlag );
+ }
+
+
+ // a ->XObjectInspectorUI method taking a string and a boolean
+ typedef void ( SAL_CALL XObjectInspectorUI::*FPropertyUIFlagSetter )( const OUString&, sal_Bool );
+
+
+ // an implementation of the ->IStringKeyBooleanUIUpdate interface which calls
+ // an arbitrary ->XObjectInspectorUI method taking a string and a boolean flag
+ class DefaultStringKeyBooleanUIUpdate : public IStringKeyBooleanUIUpdate
+ {
+ private:
+ Reference< XObjectInspectorUI > m_xUIUpdate;
+ FPropertyUIFlagSetter m_pSetter;
+
+ public:
+ DefaultStringKeyBooleanUIUpdate( const Reference< XObjectInspectorUI >& _rxUIUpdate, FPropertyUIFlagSetter _pSetter );
+ // IStringKeyBooleanUIUpdate
+ virtual void updateUIForKey( const OUString& _rKey, bool _bFlag ) const override;
+ };
+
+
+ DefaultStringKeyBooleanUIUpdate::DefaultStringKeyBooleanUIUpdate( const Reference< XObjectInspectorUI >& _rxUIUpdate, FPropertyUIFlagSetter _pSetter )
+ :m_xUIUpdate( _rxUIUpdate )
+ ,m_pSetter( _pSetter )
+ {
+ }
+
+
+ void DefaultStringKeyBooleanUIUpdate::updateUIForKey( const OUString& _rKey, bool _bFlag ) const
+ {
+ ((m_xUIUpdate.get())->*m_pSetter)( _rKey, _bFlag );
+ }
+
+
+ // an STL-compatible structure which applies a ->IStringKeyBooleanUIUpdate::updateUIForKey
+ // operation with a fixed boolean value, for a given string value
+ struct BooleanUIAspectUpdate
+ {
+ private:
+ const IStringKeyBooleanUIUpdate& m_rUpdater;
+ bool m_bFlag;
+
+ public:
+ BooleanUIAspectUpdate( const IStringKeyBooleanUIUpdate& _rUpdater, bool _bFlag )
+ :m_rUpdater( _rUpdater )
+ ,m_bFlag( _bFlag )
+ {
+ }
+
+ void operator()( const OUString& _rPropertyName )
+ {
+ m_rUpdater.updateUIForKey( _rPropertyName, m_bFlag );
+ }
+
+ static void forEach( const StringBag& _rProperties, const IStringKeyBooleanUIUpdate& _rUpdater, bool _bFlag )
+ {
+ std::for_each( _rProperties.begin(), _rProperties.end(), BooleanUIAspectUpdate( _rUpdater, _bFlag ) );
+ }
+ };
+
+
+ // BooleanUIAspectUpdate
+
+ // an STL-compatible structure subtracting a given string from a fixed ->StringBag
+ struct StringBagComplement
+ {
+ private:
+ StringBag& m_rMinuend;
+
+ public:
+ explicit StringBagComplement( StringBag& _rMinuend ) :m_rMinuend( _rMinuend ) { }
+
+ void operator()( const OUString& _rPropertyToSubtract )
+ {
+ m_rMinuend.erase( _rPropertyToSubtract );
+ }
+
+ static void subtract( StringBag& _rMinuend, const StringBag& _rSubtrahend )
+ {
+ std::for_each( _rSubtrahend.begin(), _rSubtrahend.end(), StringBagComplement( _rMinuend ) );
+ }
+ };
+
+
+ // BooleanUIAspectUpdate
+
+ void lcl_fireUIStateFlag(
+ const IStringKeyBooleanUIUpdate& _rUIUpdate,
+ const ImplMapHandlerToUI& _rHandlerUIs,
+ CachedInspectorUI::FGetStringBag _pGetPositives,
+ CachedInspectorUI::FGetStringBag _pGetNegatives
+ )
+ {
+ // all strings which are in the "positive" list of one handler
+ StringBag aAllPositives;
+ StringBagCollector::collectAll( aAllPositives, _rHandlerUIs, _pGetPositives );
+
+ // all strings which are in the "negative" list of one handler
+ StringBag aAllNegatives;
+ StringBagCollector::collectAll( aAllNegatives, _rHandlerUIs, _pGetNegatives );
+
+ // propagate the "negative" flags to the delegator UI
+ BooleanUIAspectUpdate::forEach( aAllNegatives, _rUIUpdate, false );
+
+ // propagate the "positive" flags to the delegator UI, for all elements where _no_
+ // "negative" flag exists
+ StringBagComplement::subtract( aAllPositives, aAllNegatives );
+ BooleanUIAspectUpdate::forEach( aAllPositives, _rUIUpdate, true );
+
+ // the "positive" request can be cleared no, only negative requests
+ // (such as "disable a property" or "hide a category") need to be preserved for the next round
+ StringBagClearer::clearAll( _rHandlerUIs, _pGetPositives );
+ }
+ }
+
+
+ void ComposedPropertyUIUpdate::impl_fireEnablePropertyUI_throw()
+ {
+ lcl_fireUIStateFlag(
+ DefaultStringKeyBooleanUIUpdate( m_xDelegatorUI, &XObjectInspectorUI::enablePropertyUI ),
+ m_pCollectedUIs->aHandlers,
+ &CachedInspectorUI::getEnabledProperties,
+ &CachedInspectorUI::getDisabledProperties
+ );
+ }
+
+
+ void ComposedPropertyUIUpdate::impl_fireRebuildPropertyUI_throw()
+ {
+ // collect all properties for which a rebuild request has been made
+ StringBag aAllRebuilt;
+ StringBagCollector::collectAll( aAllRebuilt, m_pCollectedUIs->aHandlers, &CachedInspectorUI::getRebuiltProperties );
+
+ // rebuild all those properties
+ PropertyUIOperator::forEach( aAllRebuilt, m_xDelegatorUI, &XObjectInspectorUI::rebuildPropertyUI );
+
+ // clear the "properties to rebuild" at all handlers, since the request has been fulfilled now.
+ StringBagClearer::clearAll( m_pCollectedUIs->aHandlers, &CachedInspectorUI::getRebuiltProperties );
+ }
+
+
+ void ComposedPropertyUIUpdate::impl_fireShowHidePropertyUI_throw()
+ {
+ // all properties which have been shown by at least one handler
+ StringBag aAllShown;
+ StringBagCollector::collectAll( aAllShown, m_pCollectedUIs->aHandlers, &CachedInspectorUI::getShownProperties );
+ // all properties which have been hidden by at least one handler
+ StringBag aAllHidden;
+ StringBagCollector::collectAll( aAllHidden, m_pCollectedUIs->aHandlers, &CachedInspectorUI::getHiddenProperties );
+
+ // hide properties as necessary
+ PropertyUIOperator::forEach( aAllHidden, m_xDelegatorUI, &XObjectInspectorUI::hidePropertyUI );
+
+ // for those properties which are hidden, ignore all "show" requests which other handlers might have had
+ StringBagComplement::subtract( aAllShown, aAllHidden );
+
+ // show properties
+ PropertyUIOperator::forEach( aAllShown, m_xDelegatorUI, &XObjectInspectorUI::showPropertyUI );
+ }
+
+
+ void ComposedPropertyUIUpdate::impl_fireShowCategory_throw()
+ {
+ lcl_fireUIStateFlag(
+ DefaultStringKeyBooleanUIUpdate( m_xDelegatorUI, &XObjectInspectorUI::showCategory ),
+ m_pCollectedUIs->aHandlers,
+ &CachedInspectorUI::getShownCategories,
+ &CachedInspectorUI::getHiddenCategories
+ );
+ }
+
+
+ void ComposedPropertyUIUpdate::impl_fireEnablePropertyUIElements_throw()
+ {
+ lcl_fireUIStateFlag(
+ EnablePropertyUIElement( m_xDelegatorUI, PropertyLineElement::InputControl ),
+ m_pCollectedUIs->aHandlers,
+ &CachedInspectorUI::getEnabledInputControls,
+ &CachedInspectorUI::getDisabledInputControls
+ );
+
+ lcl_fireUIStateFlag(
+ EnablePropertyUIElement( m_xDelegatorUI, PropertyLineElement::PrimaryButton ),
+ m_pCollectedUIs->aHandlers,
+ &CachedInspectorUI::getEnabledPrimaryButtons,
+ &CachedInspectorUI::getDisabledPrimaryButtons
+ );
+
+ lcl_fireUIStateFlag(
+ EnablePropertyUIElement( m_xDelegatorUI, PropertyLineElement::SecondaryButton ),
+ m_pCollectedUIs->aHandlers,
+ &CachedInspectorUI::getEnabledSecondaryButtons,
+ &CachedInspectorUI::getDisabledSecondaryButtons
+ );
+ }
+
+
+ void ComposedPropertyUIUpdate::impl_fireAll_throw()
+ {
+ OSL_PRECOND( !impl_isDisposed(), "ComposedPropertyUIUpdate::impl_fireAll_throw: already disposed, this will crash!" );
+
+ impl_fireEnablePropertyUI_throw();
+ impl_fireShowHidePropertyUI_throw();
+ impl_fireRebuildPropertyUI_throw();
+ impl_fireShowCategory_throw();
+ impl_fireEnablePropertyUIElements_throw();
+ }
+
+
+ void ComposedPropertyUIUpdate::suspendAutoFire()
+ {
+ impl_checkDisposed();
+ osl_atomic_increment( &m_nSuspendCounter );
+ }
+
+
+ void ComposedPropertyUIUpdate::resumeAutoFire()
+ {
+ impl_checkDisposed();
+ if ( 0 == osl_atomic_decrement( &m_nSuspendCounter ) )
+ impl_fireAll_throw();
+ }
+
+
+ void ComposedPropertyUIUpdate::impl_checkDisposed() const
+ {
+ if ( impl_isDisposed() )
+ throw DisposedException();
+ }
+
+
+ void ComposedPropertyUIUpdate::callback_inspectorUIChanged_throw()
+ {
+ if ( 0 == m_nSuspendCounter )
+ impl_fireAll_throw();
+ }
+
+
+ Reference< XObjectInspectorUI > const & ComposedPropertyUIUpdate::getDelegatorUI() const
+ {
+ impl_checkDisposed();
+ return m_xDelegatorUI;
+ }
+
+
+ void ComposedPropertyUIUpdate::dispose()
+ {
+ if ( impl_isDisposed() )
+ return;
+
+ OSL_ENSURE( m_nSuspendCounter == 0, "ComposedPropertyUIUpdate::dispose: still suspended, the changes will be lost!" );
+
+ for (auto const& singleUI : m_pCollectedUIs->aHandlers)
+ {
+ singleUI.second->dispose();
+ }
+ m_pCollectedUIs.reset();
+ m_xDelegatorUI.set( nullptr );
+ }
+
+
+ bool ComposedPropertyUIUpdate::shouldContinuePropertyHandling( const OUString& _rName ) const
+ {
+ if ( !m_pPropertyCheck )
+ return true;
+ if ( m_pPropertyCheck->hasPropertyByName( _rName ) )
+ return true;
+ return false;
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/composeduiupdate.hxx b/extensions/source/propctrlr/composeduiupdate.hxx
new file mode 100644
index 000000000..5e356af1f
--- /dev/null
+++ b/extensions/source/propctrlr/composeduiupdate.hxx
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/inspection/XPropertyHandler.hpp>
+
+#include <memory>
+
+
+namespace pcr
+{
+
+ struct MapHandlerToUI;
+
+ /** callback for a ComposedPropertyUIUpdate checking a given property for existence
+ */
+ class SAL_NO_VTABLE IPropertyExistenceCheck
+ {
+ public:
+ /// @throws css::uno::RuntimeException
+ virtual bool hasPropertyByName( const OUString& _rName ) = 0;
+
+ protected:
+ ~IPropertyExistenceCheck() {}
+ };
+
+ /** helper class composing requests to a ->XObjectInspectorUI interface, coming
+ from multiple sources
+
+ Usually, a handler tells the browser UI to enable to disable, or show or hide, certain
+ elements. Now when multiple handlers do this, their instructions must be combined:
+ If one handler disables a certain element, but others enable it, it must in the
+ result still be disabled. Similar for showing/hiding elements.
+
+ ->ComposedPropertyUIUpdate implements this combination. It does so by providing a dedicated
+ ->XObjectInspectorUI instance for every participating handler, and remembering the UI
+ state on a per-handler basis. Upon request (->fire), the combined UI state is
+ forwarded to another ->XObjectInspectorUI instance, the so-called delegator UI.
+ */
+ class ComposedPropertyUIUpdate
+ {
+ private:
+ std::unique_ptr< MapHandlerToUI > m_pCollectedUIs;
+ css::uno::Reference< css::inspection::XObjectInspectorUI >
+ m_xDelegatorUI;
+ oslInterlockedCount m_nSuspendCounter;
+ IPropertyExistenceCheck* m_pPropertyCheck;
+
+ public:
+ /** constructs a ->ComposedPropertyUIUpdate instance
+ @param _rxDelegatorUI
+ a ->XObjectInspectorUI instance to which composed UI requests should be forwarded. Must
+ not be <NULL/>.
+ @param _pPropertyCheck
+ an instance checking properties for existence. If this is not <NULL/>, it will be invoked
+ whenever one of the ->XObjectInspectorUI methods is called, to check the passed property
+ name.<br/>
+ Beware of lifetime issues. The instance pointed to by <arg>_pPropertyCheck</arg> must
+ live at least as long as the ->ComposedPropertyUIUpdate instance you're going to create.
+ @throws css::lang::NullPointerException
+ if ->_rxDelegatorUI is <NULL/>
+ */
+ ComposedPropertyUIUpdate(
+ const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxDelegatorUI,
+ IPropertyExistenceCheck* _pPropertyCheck );
+ ~ComposedPropertyUIUpdate();
+
+ /** returns the delegator UI
+ @throw css::lang::DisposedException
+ */
+ css::uno::Reference< css::inspection::XObjectInspectorUI > const & getDelegatorUI() const;
+
+ /** returns a ->XObjectInspectorUI instance belonging to a given property handler
+
+ In every call to an ->XPropertyHandler method which requires a ->XObjectInspectorUI,
+ the same UI instance should be used. The instance here will cache all requests passed
+ to it, and ->ComposedPropertyUIUpdate::fire will use the combination of all
+ cached UI states of all handlers to update the delegator UI.
+ */
+ css::uno::Reference< css::inspection::XObjectInspectorUI >
+ getUIForPropertyHandler( const css::uno::Reference< css::inspection::XPropertyHandler >& _rxHandler );
+
+ /** Suspends automatic firing of UI changes
+
+ normally, as soon as any of the property handlers does a request for an
+ arbitrary UI change, the set of collected UI changes is evaluated, and the combined
+ UI state is fired to the delegator UI.
+
+ You can disable this automatic firing by calling ->suspendAutoFire. As longs as auto
+ firing is suspended, only explicit ->fire calls trigger the notification to the
+ delegator UI.
+
+ Note that calls to ->suspendAutoFire are cumulative, that is, if you make multiple calls
+ they must be accompanied by an equal number of calls to ->resumeAutoFire, to enable
+ auto-firing again.
+
+ @seealso resumeAutoFire
+ */
+ void suspendAutoFire();
+
+ /** Suspends automatic firing of UI changes
+
+ @seealso suspendAutoFire
+ */
+ void resumeAutoFire();
+
+ /** disposes the instance, so it becomes non-functional.
+
+ All cached handlers and cached ->XObjectInspectorUI instances will be released,
+ the latter will also be disposed, so that if anybody still holds a reference to them
+ and tries to operate them will get a DisposedException.
+ */
+ void dispose();
+
+ /** invokes m_pPropertyCheck to check whether a given property should be handled
+ */
+ bool shouldContinuePropertyHandling( const OUString& _rName ) const;
+
+ private:
+ /// determines whether the instance is already disposed
+ bool impl_isDisposed() const { return !m_pCollectedUIs; }
+
+ /// throws an exception if the component is already disposed
+ void impl_checkDisposed() const;
+
+ /** fires the collected UI changes to our delegator UI
+
+ All operations for any elements are forwarded:
+ <ul><li>If an element has been hidden at least once, it's also hidden at the delegator UI.</li>
+ <li>If an element has been shown at least once, and never been hidden, it's also
+ shown at the delegator UI.</li>
+ <li>If an element has never been shown or hidden, it's also not touched at the delegator UI.</li>
+ <li>The same holds if you replace "hidden" in the last three items with "disabled",
+ and "shown" with "enabled".</li>
+ <li>If an element should have been rebuilt (->XObjectInspectorUI::rebuiltPropertyUI)
+ at least once, it's rebuilt at the delegator UI, too.<br/>
+ After that, the request to rebuild the UI for this property is cleared, so subsequent
+ calls to ->fire will not trigger a new rebuilt request.
+ </ul>
+
+ @precond
+ instance is not disposed
+ */
+ void impl_fireAll_throw();
+
+ /// fires the combination of ->XObjectInspectorUI::enablePropertyUI calls
+ void impl_fireEnablePropertyUI_throw();
+
+ /// fires the combination of ->XObjectInspectorUI::enablePropertyUIElements calls
+ void impl_fireEnablePropertyUIElements_throw();
+
+ /// fires the combination of ->XObjectInspectorUI::rebuildPropertyUI calls
+ void impl_fireRebuildPropertyUI_throw();
+
+ /// fires the combination of ->XObjectInspectorUI::showPropertyUI and ->XObjectInspectorUI::hidePropertyUI calls
+ void impl_fireShowHidePropertyUI_throw();
+
+ /// fires the combination of ->XObjectInspectorUI::showCategory calls
+ void impl_fireShowCategory_throw();
+
+ /** callback for when a single property handler requested any change in the inspector UI
+ */
+ void callback_inspectorUIChanged_throw();
+
+ private:
+ ComposedPropertyUIUpdate( const ComposedPropertyUIUpdate& ) = delete;
+ ComposedPropertyUIUpdate& operator=( const ComposedPropertyUIUpdate& ) = delete;
+ };
+
+ class ComposedUIAutoFireGuard
+ {
+ private:
+ ComposedPropertyUIUpdate& m_rUIUpdate;
+ public:
+ explicit ComposedUIAutoFireGuard( ComposedPropertyUIUpdate& _rUIUpdate )
+ :m_rUIUpdate( _rUIUpdate )
+ {
+ m_rUIUpdate.suspendAutoFire();
+ }
+ ~ComposedUIAutoFireGuard() COVERITY_NOEXCEPT_FALSE
+ {
+ m_rUIUpdate.resumeAutoFire();
+ }
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/controlfontdialog.cxx b/extensions/source/propctrlr/controlfontdialog.cxx
new file mode 100644
index 000000000..08088e074
--- /dev/null
+++ b/extensions/source/propctrlr/controlfontdialog.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 <com/sun/star/beans/PropertyAttribute.hpp>
+#include "controlfontdialog.hxx"
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/svapp.hxx>
+#include "fontdialog.hxx"
+#include "formstrings.hxx"
+#include "pcrcommon.hxx"
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+
+ OControlFontDialog::OControlFontDialog(const Reference< XComponentContext >& _rxContext )
+ :OGenericUnoDialog( _rxContext )
+ ,m_pItemPoolDefaults(nullptr)
+ {
+ registerProperty(PROPERTY_INTROSPECTEDOBJECT, OWN_PROPERTY_ID_INTROSPECTEDOBJECT,
+ PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT,
+ &m_xControlModel, cppu::UnoType<decltype(m_xControlModel)>::get());
+ }
+
+
+ OControlFontDialog::~OControlFontDialog()
+ {
+ if (m_xDialog)
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ if (m_xDialog)
+ {
+ destroyDialog();
+ ControlCharacterDialog::destroyItemSet(m_pFontItems, m_pItemPool, m_pItemPoolDefaults);
+ }
+ }
+ }
+
+
+ Sequence<sal_Int8> SAL_CALL OControlFontDialog::getImplementationId( )
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+
+ OUString SAL_CALL OControlFontDialog::getImplementationName()
+ {
+ return "org.openoffice.comp.form.ui.OControlFontDialog";
+ }
+
+
+ css::uno::Sequence<OUString> SAL_CALL OControlFontDialog::getSupportedServiceNames()
+ {
+ return { "com.sun.star.form.ControlFontDialog" };
+ }
+
+ void OControlFontDialog::initialize( const Sequence< Any >& aArguments )
+ {
+ Reference<XPropertySet> xGridModel;
+ if (aArguments.getLength() == 1 && (aArguments[0] >>= xGridModel))
+ {
+ Sequence aNewArguments{ Any(comphelper::makePropertyValue("IntrospectedObject",
+ xGridModel)) };
+ OControlFontDialog_DBase::initialize(aNewArguments);
+ }
+ else
+ OControlFontDialog_DBase::initialize(aArguments);
+ }
+
+
+ Reference<XPropertySetInfo> SAL_CALL OControlFontDialog::getPropertySetInfo()
+ {
+ Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+ }
+
+
+ ::cppu::IPropertyArrayHelper& OControlFontDialog::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+
+ ::cppu::IPropertyArrayHelper* OControlFontDialog::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties(aProps);
+ return new ::cppu::OPropertyArrayHelper(aProps);
+ }
+
+ std::unique_ptr<weld::DialogController> OControlFontDialog::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent)
+ {
+ ControlCharacterDialog::createItemSet(m_pFontItems, m_pItemPool, m_pItemPoolDefaults);
+
+ OSL_ENSURE(m_xControlModel.is(), "OControlFontDialog::createDialog: no introspectee set!");
+ if (m_xControlModel.is())
+ ControlCharacterDialog::translatePropertiesToItems(m_xControlModel, m_pFontItems.get());
+ // TODO: we need a mechanism to prevent that somebody creates us, sets an introspectee, executes us,
+ // sets a new introspectee and re-executes us. In this case, the dialog returned here (upon the first
+ // execute) will be re-used upon the second execute, and thus it won't be initialized correctly.
+
+ return std::make_unique<ControlCharacterDialog>(Application::GetFrameWeld(rParent), *m_pFontItems);
+ }
+
+ void OControlFontDialog::executedDialog(sal_Int16 _nExecutionResult)
+ {
+ OSL_ENSURE(m_xDialog, "OControlFontDialog::executedDialog: no dialog anymore?!!");
+ if (m_xDialog && (RET_OK == _nExecutionResult) && m_xControlModel.is())
+ {
+ const SfxItemSet* pOutput = static_cast<ControlCharacterDialog*>(m_xDialog.get())->GetOutputItemSet();
+ if (pOutput)
+ ControlCharacterDialog::translateItemsToProperties( *pOutput, m_xControlModel );
+ }
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_OControlFontDialog_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::OControlFontDialog(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/controlfontdialog.hxx b/extensions/source/propctrlr/controlfontdialog.hxx
new file mode 100644
index 000000000..8dc520102
--- /dev/null
+++ b/extensions/source/propctrlr/controlfontdialog.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 <comphelper/proparrhlp.hxx>
+#include <svtools/genericunodialog.hxx>
+
+class SfxItemSet;
+class SfxItemPool;
+class SfxPoolItem;
+
+namespace pcr
+{
+
+ class OControlFontDialog;
+ typedef ::svt::OGenericUnoDialog OControlFontDialog_DBase;
+ typedef ::comphelper::OPropertyArrayUsageHelper< OControlFontDialog > OControlFontDialog_PBase;
+
+ class OControlFontDialog
+ :public OControlFontDialog_DBase
+ ,public OControlFontDialog_PBase
+ {
+ protected:
+ // <properties>
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xControlModel;
+ // </properties>
+
+ std::unique_ptr<SfxItemSet> m_pFontItems; // item set for the dialog
+ rtl::Reference<SfxItemPool> m_pItemPool; // item pool for the item set for the dialog
+ std::vector<SfxPoolItem*>*
+ m_pItemPoolDefaults; // pool defaults
+
+ public:
+ explicit OControlFontDialog(const css::uno::Reference< css::uno::XComponentContext >& _rxContext);
+ virtual ~OControlFontDialog() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ protected:
+ // OGenericUnoDialog overridables
+ virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override;
+ virtual void executedDialog(sal_Int16 _nExecutionResult) override;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/controltype.hxx b/extensions/source/propctrlr/controltype.hxx
new file mode 100644
index 000000000..89a400a76
--- /dev/null
+++ b/extensions/source/propctrlr/controltype.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 <sal/types.h>
+
+
+namespace pcr::ControlType
+{
+ const sal_Int16 FIXEDLINE = sal_Int16(100);
+ const sal_Int16 FORMATTEDFIELD = sal_Int16(101);
+ const sal_Int16 PROGRESSBAR = sal_Int16(102);
+
+ // need only those which are not already covered as FormComponentType
+
+} // namespace pcr::ControlType
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/defaultforminspection.cxx b/extensions/source/propctrlr/defaultforminspection.cxx
new file mode 100644
index 000000000..1fe8ee60e
--- /dev/null
+++ b/extensions/source/propctrlr/defaultforminspection.cxx
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "defaultforminspection.hxx"
+#include "pcrcommon.hxx"
+#include <helpids.h>
+#include <strings.hrc>
+#include "modulepcr.hxx"
+#include "formmetadata.hxx"
+
+#include <com/sun/star/ucb/AlreadyInitializedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <sal/macros.h>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::inspection::PropertyCategoryDescriptor;
+ using ::com::sun::star::ucb::AlreadyInitializedException;
+ using ::com::sun::star::lang::IllegalArgumentException;
+
+ DefaultFormComponentInspectorModel::DefaultFormComponentInspectorModel( bool _bUseFormFormComponentHandlers )
+ :m_bUseFormComponentHandlers( _bUseFormFormComponentHandlers )
+ ,m_bConstructed( false )
+ ,m_pInfoService( new OPropertyInfoService )
+ {
+ }
+
+
+ DefaultFormComponentInspectorModel::~DefaultFormComponentInspectorModel()
+ {
+ }
+
+
+ OUString SAL_CALL DefaultFormComponentInspectorModel::getImplementationName( )
+ {
+ return "org.openoffice.comp.extensions.DefaultFormComponentInspectorModel";
+ }
+
+
+ Sequence< OUString > SAL_CALL DefaultFormComponentInspectorModel::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.inspection.DefaultFormComponentInspectorModel" };
+ }
+
+
+ Sequence< Any > SAL_CALL DefaultFormComponentInspectorModel::getHandlerFactories()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // service names for all our handlers
+ static struct
+ {
+ const char* serviceName;
+ bool isFormOnly;
+ } const aFactories[] = {
+
+ // a generic handler for form component properties (must precede the ButtonNavigationHandler)
+ { "com.sun.star.form.inspection.FormComponentPropertyHandler", false },
+
+ // generic virtual edit properties
+ { "com.sun.star.form.inspection.EditPropertyHandler", false },
+
+ // a handler which virtualizes the ButtonType property, to provide additional types like
+ // "move to next record"
+ { "com.sun.star.form.inspection.ButtonNavigationHandler", false },
+
+ // a handler for script events bound to form components or dialog elements
+ { "com.sun.star.form.inspection.EventHandler", false },
+
+ // a handler which introduces virtual properties for binding controls to spreadsheet cells
+ { "com.sun.star.form.inspection.CellBindingPropertyHandler", false },
+
+ // properties related to binding to an XForms DOM node
+ { "com.sun.star.form.inspection.XMLFormsPropertyHandler", true },
+
+ // properties related to the XSD data against which a control content is validated
+ { "com.sun.star.form.inspection.XSDValidationPropertyHandler", true },
+
+ // a handler which cares for XForms submissions
+ { "com.sun.star.form.inspection.SubmissionPropertyHandler", true },
+
+ // a handler which cares for geometry properties of form controls
+ { "com.sun.star.form.inspection.FormGeometryHandler", true }
+ };
+
+ sal_Int32 nFactories = SAL_N_ELEMENTS( aFactories );
+ Sequence< Any > aReturn( nFactories );
+ Any* pReturn = aReturn.getArray();
+ for ( sal_Int32 i = 0; i < nFactories; ++i )
+ {
+ if ( aFactories[i].isFormOnly && !m_bUseFormComponentHandlers )
+ continue;
+ *pReturn++ <<= OUString::createFromAscii( aFactories[i].serviceName );
+ }
+ aReturn.realloc( pReturn - aReturn.getArray() );
+
+ return aReturn;
+ }
+
+
+ Sequence< PropertyCategoryDescriptor > SAL_CALL DefaultFormComponentInspectorModel::describeCategories( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ static struct
+ {
+ const char* programmaticName;
+ TranslateId uiNameResId;
+ rtl::OUStringConstExpr helpId;
+ } const aCategories[] = {
+ { "General", RID_STR_PROPPAGE_DEFAULT, HID_FM_PROPDLG_TAB_GENERAL },
+ { "Data", RID_STR_PROPPAGE_DATA, HID_FM_PROPDLG_TAB_DATA },
+ { "Events", RID_STR_EVENTS, HID_FM_PROPDLG_TAB_EVT }
+ };
+
+ sal_Int32 nCategories = SAL_N_ELEMENTS( aCategories );
+ Sequence< PropertyCategoryDescriptor > aReturn( nCategories );
+ PropertyCategoryDescriptor* pReturn = aReturn.getArray();
+ for ( sal_Int32 i=0; i<nCategories; ++i, ++pReturn )
+ {
+ pReturn->ProgrammaticName = OUString::createFromAscii( aCategories[i].programmaticName );
+ pReturn->UIName = PcrRes( aCategories[i].uiNameResId );
+ pReturn->HelpURL = HelpIdUrl::getHelpURL( aCategories[i].helpId.asView() );
+ }
+
+ return aReturn;
+ }
+
+
+ ::sal_Int32 SAL_CALL DefaultFormComponentInspectorModel::getPropertyOrderIndex( const OUString& _rPropertyName )
+ {
+ sal_Int32 nPropertyId( m_pInfoService->getPropertyId( _rPropertyName ) );
+ if ( nPropertyId == -1 )
+ {
+ if ( _rPropertyName.indexOf( ';' ) != -1 )
+ // it's an event. Just give it an arbitrary number - events will be on a separate
+ // page, and by definition, if two properties have the same OrderIndex, then
+ // they will be ordered as they appear in the handler's getSupportedProperties.
+ return 1000;
+ return 0;
+ }
+ return m_pInfoService->getPropertyPos( nPropertyId );
+ }
+
+
+ void SAL_CALL DefaultFormComponentInspectorModel::initialize( const Sequence< Any >& _arguments )
+ {
+ if ( m_bConstructed )
+ throw AlreadyInitializedException();
+
+ StlSyntaxSequence< Any > arguments( _arguments );
+ if ( arguments.empty() )
+ { // constructor: "createDefault()"
+ m_bConstructed = true;
+ return;
+ }
+
+ if ( arguments.size() == 2 )
+ { // constructor: "createWithHelpSection( long, long )"
+ sal_Int32 nMinHelpTextLines( 0 ), nMaxHelpTextLines( 0 );
+ if ( !( arguments[0] >>= nMinHelpTextLines ) || !( arguments[1] >>= nMaxHelpTextLines ) )
+ throw IllegalArgumentException( OUString(), *this, 0 );
+ createWithHelpSection( nMinHelpTextLines, nMaxHelpTextLines );
+ return;
+ }
+
+ throw IllegalArgumentException( OUString(), *this, 0 );
+ }
+
+
+ void DefaultFormComponentInspectorModel::createWithHelpSection( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines )
+ {
+ if ( ( _nMinHelpTextLines <= 0 ) || ( _nMaxHelpTextLines <= 0 ) || ( _nMinHelpTextLines > _nMaxHelpTextLines ) )
+ throw IllegalArgumentException( OUString(), *this, 0 );
+
+ enableHelpSectionProperties( _nMinHelpTextLines, _nMaxHelpTextLines );
+ m_bConstructed = true;
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_DefaultFormComponentInspectorModel_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::DefaultFormComponentInspectorModel(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/defaultforminspection.hxx b/extensions/source/propctrlr/defaultforminspection.hxx
new file mode 100644
index 000000000..e118e9da6
--- /dev/null
+++ b/extensions/source/propctrlr/defaultforminspection.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 "inspectormodelbase.hxx"
+
+#include <memory>
+
+
+namespace pcr
+{
+
+
+ class OPropertyInfoService;
+
+ class DefaultFormComponentInspectorModel final : public ImplInspectorModel
+ {
+ bool m_bUseFormComponentHandlers;
+ bool m_bConstructed;
+
+ /// access to property meta data
+ std::unique_ptr< OPropertyInfoService > m_pInfoService;
+
+ virtual ~DefaultFormComponentInspectorModel() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XObjectInspectorModel
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL getHandlerFactories() override;
+ virtual css::uno::Sequence< css::inspection::PropertyCategoryDescriptor > SAL_CALL describeCategories( ) override;
+ virtual ::sal_Int32 SAL_CALL getPropertyOrderIndex( const OUString& PropertyName ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ public:
+ explicit DefaultFormComponentInspectorModel( bool _bUseFormFormComponentHandlers = true );
+
+ private:
+ // Service constructors
+ void createWithHelpSection( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines );
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/defaulthelpprovider.cxx b/extensions/source/propctrlr/defaulthelpprovider.cxx
new file mode 100644
index 000000000..8f1e22b82
--- /dev/null
+++ b/extensions/source/propctrlr/defaulthelpprovider.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 "defaulthelpprovider.hxx"
+#include "pcrcommon.hxx"
+
+#include <com/sun/star/ucb/AlreadyInitializedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/window.hxx>
+#include <tools/diagnose_ex.h>
+#include <cppuhelper/supportsservice.hxx>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::inspection::XPropertyControl;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::inspection::XObjectInspectorUI;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::ucb::AlreadyInitializedException;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::awt::XWindow;
+
+ DefaultHelpProvider::DefaultHelpProvider()
+ :m_bConstructed( false )
+ {
+ }
+
+
+ DefaultHelpProvider::~DefaultHelpProvider()
+ {
+ }
+
+
+ Sequence< OUString > SAL_CALL DefaultHelpProvider::getSupportedServiceNames()
+ {
+ return { "com.sun.star.inspection.DefaultHelpProvider" };
+ }
+
+ OUString SAL_CALL DefaultHelpProvider::getImplementationName()
+ {
+ return "org.openoffice.comp.extensions.DefaultHelpProvider";
+ }
+
+ sal_Bool SAL_CALL DefaultHelpProvider::supportsService(const OUString& aServiceName)
+ {
+ return cppu::supportsService(this, aServiceName);
+ }
+
+
+ void SAL_CALL DefaultHelpProvider::focusGained( const Reference< XPropertyControl >& Control )
+ {
+ if ( !m_xInspectorUI.is() )
+ throw RuntimeException( OUString(), *this );
+
+ try
+ {
+ m_xInspectorUI->setHelpSectionText( impl_getHelpText_nothrow( Control ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ void SAL_CALL DefaultHelpProvider::valueChanged( const Reference< XPropertyControl >& )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL DefaultHelpProvider::initialize( const Sequence< Any >& _arguments )
+ {
+ if ( m_bConstructed )
+ throw AlreadyInitializedException();
+
+ StlSyntaxSequence< Any > arguments( _arguments );
+ if ( arguments.size() == 1 )
+ { // constructor: "create( XObjectInspectorUI )"
+ Reference< XObjectInspectorUI > xUI( arguments[0], UNO_QUERY );
+ create( xUI );
+ return;
+ }
+
+ throw IllegalArgumentException( OUString(), *this, 0 );
+ }
+
+
+ void DefaultHelpProvider::create( const Reference< XObjectInspectorUI >& _rxUI )
+ {
+ if ( !_rxUI.is() )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+
+ try
+ {
+ m_xInspectorUI = _rxUI;
+ m_xInspectorUI->registerControlObserver( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ m_bConstructed = true;
+ }
+
+
+ vcl::Window* DefaultHelpProvider::impl_getVclControlWindow_nothrow( const Reference< XPropertyControl >& _rxControl )
+ {
+ vcl::Window* pControlWindow = nullptr;
+ OSL_PRECOND( _rxControl.is(), "DefaultHelpProvider::impl_getVclControlWindow_nothrow: illegal control!" );
+ if ( !_rxControl.is() )
+ return pControlWindow;
+
+ try
+ {
+ Reference< XWindow > xControlWindow( _rxControl->getControlWindow(), css::uno::UNO_SET_THROW );
+ pControlWindow = VCLUnoHelper::GetWindow( xControlWindow );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ return pControlWindow;
+ }
+
+
+ OUString DefaultHelpProvider::impl_getHelpText_nothrow( const Reference< XPropertyControl >& _rxControl )
+ {
+ OUString sHelpText;
+ OSL_PRECOND( _rxControl.is(), "DefaultHelpProvider::impl_getHelpText_nothrow: illegal control!" );
+ if ( !_rxControl.is() )
+ return sHelpText;
+
+ vcl::Window* pControlWindow( impl_getVclControlWindow_nothrow( _rxControl ) );
+ OSL_ENSURE( pControlWindow, "DefaultHelpProvider::impl_getHelpText_nothrow: could not determine the VCL window!" );
+ if ( !pControlWindow )
+ return sHelpText;
+
+ sHelpText = pControlWindow->GetHelpText();
+ return sHelpText;
+ }
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_DefaultHelpProvider_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::DefaultHelpProvider());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/defaulthelpprovider.hxx b/extensions/source/propctrlr/defaulthelpprovider.hxx
new file mode 100644
index 000000000..f647937dd
--- /dev/null
+++ b/extensions/source/propctrlr/defaulthelpprovider.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 <com/sun/star/inspection/XPropertyControlObserver.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+namespace vcl { class Window; }
+
+
+namespace pcr
+{
+
+
+ //= DefaultHelpProvider
+
+ typedef ::cppu::WeakImplHelper < css::inspection::XPropertyControlObserver
+ , css::lang::XInitialization
+ , css::lang::XServiceInfo
+ > DefaultHelpProvider_Base;
+ class DefaultHelpProvider final : public DefaultHelpProvider_Base
+ {
+ private:
+ bool m_bConstructed;
+ css::uno::Reference< css::inspection::XObjectInspectorUI >
+ m_xInspectorUI;
+
+ public:
+ DefaultHelpProvider();
+
+ private:
+ virtual ~DefaultHelpProvider() 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;
+
+ // XPropertyControlObserver
+ virtual void SAL_CALL focusGained( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) override;
+ virtual void SAL_CALL valueChanged( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // Service constructors
+ void create( const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxUI );
+
+ static vcl::Window* impl_getVclControlWindow_nothrow( const css::uno::Reference< css::inspection::XPropertyControl >& _rxControl );
+ static OUString impl_getHelpText_nothrow( const css::uno::Reference< css::inspection::XPropertyControl >& _rxControl );
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/editpropertyhandler.cxx b/extensions/source/propctrlr/editpropertyhandler.cxx
new file mode 100644
index 000000000..22ad04988
--- /dev/null
+++ b/extensions/source/propctrlr/editpropertyhandler.cxx
@@ -0,0 +1,308 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "editpropertyhandler.hxx"
+#include "formstrings.hxx"
+#include "formmetadata.hxx"
+
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <tools/diagnose_ex.h>
+
+#define TEXTTYPE_SINGLELINE 0
+#define TEXTTYPE_MULTILINE 1
+#define TEXTTYPE_RICHTEXT 2
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::inspection;
+
+
+ //= EditPropertyHandler
+
+
+ EditPropertyHandler::EditPropertyHandler( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandlerComponent( _rxContext )
+ {
+ }
+
+
+ EditPropertyHandler::~EditPropertyHandler( )
+ {
+ }
+
+
+ OUString EditPropertyHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.EditPropertyHandler";
+ }
+
+
+ Sequence< OUString > EditPropertyHandler::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.inspection.EditPropertyHandler" };
+ }
+
+
+ Any SAL_CALL EditPropertyHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ Any aReturn;
+ try
+ {
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_SHOW_SCROLLBARS:
+ {
+ bool bHasVScroll = false;
+ m_xComponent->getPropertyValue( PROPERTY_VSCROLL ) >>= bHasVScroll;
+ bool bHasHScroll = false;
+ m_xComponent->getPropertyValue( PROPERTY_HSCROLL ) >>= bHasHScroll;
+
+ aReturn <<= static_cast<sal_Int32>( ( bHasVScroll ? 2 : 0 ) + ( bHasHScroll ? 1 : 0 ) );
+ }
+ break;
+
+ case PROPERTY_ID_TEXTTYPE:
+ {
+ sal_Int32 nTextType = TEXTTYPE_SINGLELINE;
+ bool bRichText = false;
+ OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_RICHTEXT ) >>= bRichText );
+ if ( bRichText )
+ nTextType = TEXTTYPE_RICHTEXT;
+ else
+ {
+ bool bMultiLine = false;
+ OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_MULTILINE ) >>= bMultiLine );
+ if ( bMultiLine )
+ nTextType = TEXTTYPE_MULTILINE;
+ else
+ nTextType = TEXTTYPE_SINGLELINE;
+ }
+ aReturn <<= nTextType;
+ }
+ break;
+
+
+ default:
+ OSL_FAIL( "EditPropertyHandler::getPropertyValue: cannot handle this property!" );
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EditPropertyHandler::getPropertyValue" );
+ }
+
+ return aReturn;
+ }
+
+
+ void SAL_CALL EditPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ try
+ {
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_SHOW_SCROLLBARS:
+ {
+ sal_Int32 nScrollbars = 0;
+ _rValue >>= nScrollbars;
+
+ bool bHasVScroll = 0 != ( nScrollbars & 2 );
+ bool bHasHScroll = 0 != ( nScrollbars & 1 );
+
+ m_xComponent->setPropertyValue( PROPERTY_VSCROLL, Any( bHasVScroll ) );
+ m_xComponent->setPropertyValue( PROPERTY_HSCROLL, Any( bHasHScroll ) );
+ }
+ break;
+
+ case PROPERTY_ID_TEXTTYPE:
+ {
+ bool bMultiLine = false;
+ bool bRichText = false;
+ sal_Int32 nTextType = TEXTTYPE_SINGLELINE;
+ OSL_VERIFY( _rValue >>= nTextType );
+ switch ( nTextType )
+ {
+ case TEXTTYPE_SINGLELINE: bMultiLine = bRichText = false; break;
+ case TEXTTYPE_MULTILINE: bMultiLine = true; bRichText = false; break;
+ case TEXTTYPE_RICHTEXT: bMultiLine = true; bRichText = true; break;
+ default:
+ OSL_FAIL( "EditPropertyHandler::setPropertyValue: invalid text type!" );
+ }
+
+ m_xComponent->setPropertyValue( PROPERTY_MULTILINE, Any( bMultiLine ) );
+ m_xComponent->setPropertyValue( PROPERTY_RICHTEXT, Any( bRichText ) );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "EditPropertyHandler::setPropertyValue: cannot handle this id!" );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EditPropertyHandler::setPropertyValue" );
+ }
+ }
+
+
+ bool EditPropertyHandler::implHaveBothScrollBarProperties() const
+ {
+ // have a "Scrollbars" property if the object supports both "HScroll" and "VScroll"
+ Reference< XPropertySetInfo > xPSI;
+ if ( m_xComponent.is() )
+ xPSI = m_xComponent->getPropertySetInfo();
+
+ return xPSI.is()
+ && xPSI->hasPropertyByName( PROPERTY_HSCROLL )
+ && xPSI->hasPropertyByName( PROPERTY_VSCROLL );
+ }
+
+
+ bool EditPropertyHandler::implHaveTextTypeProperty() const
+ {
+ // have a "Scrollbars" property if the object supports both "HScroll" and "VScroll"
+ Reference< XPropertySetInfo > xPSI;
+ if ( m_xComponent.is() )
+ xPSI = m_xComponent->getPropertySetInfo();
+
+ return xPSI.is()
+ && xPSI->hasPropertyByName( PROPERTY_RICHTEXT )
+ && xPSI->hasPropertyByName( PROPERTY_MULTILINE );
+ }
+
+
+ Sequence< Property > EditPropertyHandler::doDescribeSupportedProperties() const
+ {
+ std::vector< Property > aProperties;
+
+ if ( implHaveBothScrollBarProperties() )
+ addInt32PropertyDescription( aProperties, PROPERTY_SHOW_SCROLLBARS );
+
+ if ( implHaveTextTypeProperty() )
+ addInt32PropertyDescription( aProperties, PROPERTY_TEXTTYPE );
+
+ if ( aProperties.empty() )
+ return Sequence< Property >();
+ return comphelper::containerToSequence(aProperties);
+ }
+
+
+ Sequence< OUString > SAL_CALL EditPropertyHandler::getSupersededProperties( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ std::vector< OUString > aSuperseded;
+ if ( implHaveBothScrollBarProperties() )
+ {
+ aSuperseded.push_back( PROPERTY_HSCROLL );
+ aSuperseded.push_back( PROPERTY_VSCROLL );
+ }
+ if ( implHaveTextTypeProperty() )
+ {
+ aSuperseded.push_back( PROPERTY_RICHTEXT );
+ aSuperseded.push_back( PROPERTY_MULTILINE );
+ }
+ if ( aSuperseded.empty() )
+ return Sequence< OUString >();
+ return comphelper::containerToSequence(aSuperseded);
+ }
+
+
+ Sequence< OUString > SAL_CALL EditPropertyHandler::getActuatingProperties( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ std::vector< OUString > aInterestingActuatingProps;
+ if ( implHaveTextTypeProperty() )
+ aInterestingActuatingProps.push_back( PROPERTY_TEXTTYPE );
+ aInterestingActuatingProps.push_back( PROPERTY_MULTILINE );
+ return comphelper::containerToSequence(aInterestingActuatingProps);
+ }
+
+
+ void SAL_CALL EditPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) );
+ switch ( nActuatingPropId )
+ {
+ case PROPERTY_ID_TEXTTYPE:
+ {
+ sal_Int32 nTextType = TEXTTYPE_SINGLELINE;
+ getPropertyValue( PROPERTY_TEXTTYPE ) >>= nTextType;
+
+ if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_WORDBREAK ) )
+ _rxInspectorUI->enablePropertyUI( PROPERTY_WORDBREAK, nTextType == TEXTTYPE_RICHTEXT );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_MAXTEXTLEN, nTextType != TEXTTYPE_RICHTEXT );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_ECHO_CHAR, nTextType == TEXTTYPE_SINGLELINE );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_FONT, nTextType != TEXTTYPE_RICHTEXT );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_ALIGN, nTextType != TEXTTYPE_RICHTEXT );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_DEFAULT_TEXT, nTextType != TEXTTYPE_RICHTEXT );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_SHOW_SCROLLBARS, nTextType != TEXTTYPE_SINGLELINE );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_LINEEND_FORMAT, nTextType != TEXTTYPE_SINGLELINE );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_VERTICAL_ALIGN, nTextType == TEXTTYPE_SINGLELINE );
+
+ _rxInspectorUI->showCategory( "Data", nTextType != TEXTTYPE_RICHTEXT );
+ }
+ break;
+
+ case PROPERTY_ID_MULTILINE:
+ {
+ bool bIsMultiline = false;
+ _rNewValue >>= bIsMultiline;
+
+ _rxInspectorUI->enablePropertyUI( PROPERTY_SHOW_SCROLLBARS, bIsMultiline );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_ECHO_CHAR, !bIsMultiline );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_LINEEND_FORMAT, bIsMultiline );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "EditPropertyHandler::actuatingPropertyChanged: cannot handle this id!" );
+ }
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_EditPropertyHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::EditPropertyHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/editpropertyhandler.hxx b/extensions/source/propctrlr/editpropertyhandler.hxx
new file mode 100644
index 000000000..33ad8df98
--- /dev/null
+++ b/extensions/source/propctrlr/editpropertyhandler.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 "propertyhandler.hxx"
+
+
+namespace pcr
+{
+
+
+ //= EditPropertyHandler
+
+ class EditPropertyHandler;
+ /** a property handler for any virtual string properties
+ */
+ class EditPropertyHandler : public PropertyHandlerComponent
+ {
+ public:
+ explicit EditPropertyHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ protected:
+ virtual ~EditPropertyHandler() override;
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
+
+ // XPropertyHandler overriables
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupersededProperties( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) override;
+
+ // PropertyHandler overridables
+ virtual css::uno::Sequence< css::beans::Property >
+ doDescribeSupportedProperties() const override;
+ private:
+ bool implHaveBothScrollBarProperties() const;
+ bool implHaveTextTypeProperty() const;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/eformshelper.cxx b/extensions/source/propctrlr/eformshelper.cxx
new file mode 100644
index 000000000..620368e9d
--- /dev/null
+++ b/extensions/source/propctrlr/eformshelper.cxx
@@ -0,0 +1,762 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string_view>
+
+#include "eformshelper.hxx"
+#include "formstrings.hxx"
+#include <strings.hrc>
+#include "modulepcr.hxx"
+#include "propeventtranslation.hxx"
+#include "formbrowsertools.hxx"
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/xforms/XFormsUIHelper1.hpp>
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+#include <o3tl/functional.hxx>
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::form::binding;
+ using namespace ::com::sun::star::xsd;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::form;
+
+
+ //= file-local helpers
+
+ namespace
+ {
+
+ OUString composeModelElementUIName( std::u16string_view _rModelName, std::u16string_view _rElementName )
+ {
+ OUString a = OUString::Concat("[")
+ + _rModelName + "] "
+ + _rElementName;
+ return a;
+ }
+ }
+
+
+ //= EFormsHelper
+
+
+ EFormsHelper::EFormsHelper( ::osl::Mutex& _rMutex, const Reference< XPropertySet >& _rxControlModel, const Reference< frame::XModel >& _rxContextDocument )
+ :m_xControlModel( _rxControlModel )
+ ,m_aPropertyListeners( _rMutex )
+ {
+ OSL_ENSURE( _rxControlModel.is(), "EFormsHelper::EFormsHelper: invalid control model!" );
+ m_xBindableControl.set(_rxControlModel, css::uno::UNO_QUERY);
+
+ m_xDocument.set(_rxContextDocument, css::uno::UNO_QUERY);
+ OSL_ENSURE( m_xDocument.is(), "EFormsHelper::EFormsHelper: invalid document!" );
+
+ }
+
+
+ bool EFormsHelper::isEForm( const Reference< frame::XModel >& _rxContextDocument )
+ {
+ try
+ {
+ Reference< xforms::XFormsSupplier > xDocument( _rxContextDocument, UNO_QUERY );
+ if ( !xDocument.is() )
+ return false;
+
+ return xDocument->getXForms().is();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::isEForm" );
+ }
+ return false;
+ }
+
+
+ bool EFormsHelper::canBindToDataType( sal_Int32 _nDataType ) const
+ {
+ if ( !m_xBindableControl.is() )
+ // cannot bind at all
+ return false;
+
+ // some types cannot be bound, independent from the control type
+ if ( ( DataTypeClass::hexBinary == _nDataType )
+ || ( DataTypeClass::base64Binary == _nDataType )
+ || ( DataTypeClass::QName == _nDataType )
+ || ( DataTypeClass::NOTATION == _nDataType )
+ )
+ return false;
+
+ bool bCan = false;
+ try
+ {
+ // classify the control model
+ sal_Int16 nControlType = FormComponentType::CONTROL;
+ OSL_VERIFY( m_xControlModel->getPropertyValue( PROPERTY_CLASSID ) >>= nControlType );
+
+ // some lists
+ sal_Int16 const nNumericCompatibleTypes[] = { DataTypeClass::DECIMAL, DataTypeClass::FLOAT, DataTypeClass::DOUBLE, 0 };
+ sal_Int16 const nDateCompatibleTypes[] = { DataTypeClass::DATE, 0 };
+ sal_Int16 const nTimeCompatibleTypes[] = { DataTypeClass::TIME, 0 };
+ sal_Int16 const nCheckboxCompatibleTypes[] = { DataTypeClass::BOOLEAN, DataTypeClass::STRING, DataTypeClass::anyURI, 0 };
+ sal_Int16 const nRadiobuttonCompatibleTypes[] = { DataTypeClass::STRING, DataTypeClass::anyURI, 0 };
+ sal_Int16 const nFormattedCompatibleTypes[] = { DataTypeClass::DECIMAL, DataTypeClass::FLOAT, DataTypeClass::DOUBLE, DataTypeClass::DATETIME, DataTypeClass::DATE, DataTypeClass::TIME, 0 };
+
+ sal_Int16 const * pCompatibleTypes = nullptr;
+ switch ( nControlType )
+ {
+ case FormComponentType::SPINBUTTON:
+ case FormComponentType::NUMERICFIELD:
+ pCompatibleTypes = nNumericCompatibleTypes;
+ break;
+ case FormComponentType::DATEFIELD:
+ pCompatibleTypes = nDateCompatibleTypes;
+ break;
+ case FormComponentType::TIMEFIELD:
+ pCompatibleTypes = nTimeCompatibleTypes;
+ break;
+ case FormComponentType::CHECKBOX:
+ pCompatibleTypes = nCheckboxCompatibleTypes;
+ break;
+ case FormComponentType::RADIOBUTTON:
+ pCompatibleTypes = nRadiobuttonCompatibleTypes;
+ break;
+
+ case FormComponentType::TEXTFIELD:
+ {
+ // both the normal text field, and the formatted field, claim to be a TEXTFIELD
+ // need to distinguish by service name
+ Reference< XServiceInfo > xSI( m_xControlModel, UNO_QUERY );
+ OSL_ENSURE( xSI.is(), "EFormsHelper::canBindToDataType: a control model which has no service info?" );
+ if ( xSI.is() )
+ {
+ if ( xSI->supportsService( SERVICE_COMPONENT_FORMATTEDFIELD ) )
+ {
+ pCompatibleTypes = nFormattedCompatibleTypes;
+ break;
+ }
+ }
+ [[fallthrough]];
+ }
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ // edit fields and list/combo boxes can be bound to anything
+ bCan = true;
+ }
+
+ if ( !bCan && pCompatibleTypes )
+ {
+ if ( _nDataType == -1 )
+ {
+ // the control can be bound to at least one type, and exactly this is being asked for
+ bCan = true;
+ }
+ else
+ {
+ while ( *pCompatibleTypes && !bCan )
+ bCan = ( *pCompatibleTypes++ == _nDataType );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::canBindToDataType" );
+ }
+
+ return bCan;
+ }
+
+
+ bool EFormsHelper::isListEntrySink() const
+ {
+ bool bIs = false;
+ try
+ {
+ Reference< XListEntrySink > xAsSink( m_xControlModel, UNO_QUERY );
+ bIs = xAsSink.is();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::isListEntrySink" );
+ }
+ return bIs;
+ }
+
+
+ void EFormsHelper::impl_switchBindingListening_throw( bool _bDoListening, const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ Reference< XPropertySet > xBindingProps;
+ if ( m_xBindableControl.is() )
+ xBindingProps.set(m_xBindableControl->getValueBinding(), css::uno::UNO_QUERY);
+ if ( !xBindingProps.is() )
+ return;
+
+ if ( _bDoListening )
+ {
+ xBindingProps->addPropertyChangeListener( OUString(), _rxListener );
+ }
+ else
+ {
+ xBindingProps->removePropertyChangeListener( OUString(), _rxListener );
+ }
+ }
+
+
+ void EFormsHelper::registerBindingListener( const Reference< XPropertyChangeListener >& _rxBindingListener )
+ {
+ if ( !_rxBindingListener.is() )
+ return;
+ impl_toggleBindingPropertyListening_throw( true, _rxBindingListener );
+ }
+
+
+ void EFormsHelper::impl_toggleBindingPropertyListening_throw( bool _bDoListen, const Reference< XPropertyChangeListener >& _rxConcreteListenerOrNull )
+ {
+ if ( !_bDoListen )
+ {
+ ::comphelper::OInterfaceIteratorHelper3 aListenerIterator(m_aPropertyListeners);
+ while ( aListenerIterator.hasMoreElements() )
+ {
+ PropertyEventTranslation* pTranslator = dynamic_cast< PropertyEventTranslation* >( aListenerIterator.next().get() );
+ OSL_ENSURE( pTranslator, "EFormsHelper::impl_toggleBindingPropertyListening_throw: invalid listener element in my container!" );
+ if ( !pTranslator )
+ continue;
+
+ Reference< XPropertyChangeListener > xEventSourceTranslator( pTranslator );
+ if ( _rxConcreteListenerOrNull.is() )
+ {
+ if ( pTranslator->getDelegator() == _rxConcreteListenerOrNull )
+ {
+ impl_switchBindingListening_throw( false, xEventSourceTranslator );
+ m_aPropertyListeners.removeInterface( xEventSourceTranslator );
+ break;
+ }
+ }
+ else
+ {
+ impl_switchBindingListening_throw( false, xEventSourceTranslator );
+ }
+ }
+ }
+ else
+ {
+ if ( _rxConcreteListenerOrNull.is() )
+ {
+ Reference< XPropertyChangeListener > xEventSourceTranslator( new PropertyEventTranslation( _rxConcreteListenerOrNull, m_xBindableControl ) );
+ m_aPropertyListeners.addInterface( xEventSourceTranslator );
+ impl_switchBindingListening_throw( true, xEventSourceTranslator );
+ }
+ else
+ {
+ ::comphelper::OInterfaceIteratorHelper3 aListenerIterator(m_aPropertyListeners);
+ while ( aListenerIterator.hasMoreElements() )
+ impl_switchBindingListening_throw( true, aListenerIterator.next() );
+ }
+ }
+ }
+
+
+ void EFormsHelper::revokeBindingListener( const Reference< XPropertyChangeListener >& _rxBindingListener )
+ {
+ impl_toggleBindingPropertyListening_throw( false, _rxBindingListener );
+ }
+
+
+ void EFormsHelper::getFormModelNames( std::vector< OUString >& /* [out] */ _rModelNames ) const
+ {
+ if ( !m_xDocument.is() )
+ return;
+
+ try
+ {
+ _rModelNames.resize( 0 );
+
+ Reference< XNameContainer > xForms( m_xDocument->getXForms() );
+ OSL_ENSURE( xForms.is(), "EFormsHelper::getFormModelNames: invalid forms container!" );
+ if ( xForms.is() )
+ {
+ const Sequence< OUString > aModelNames = xForms->getElementNames();
+ _rModelNames.resize( aModelNames.getLength() );
+ std::copy( aModelNames.begin(), aModelNames.end(), _rModelNames.begin() );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getFormModelNames" );
+ }
+ }
+
+
+ void EFormsHelper::getBindingNames( const OUString& _rModelName, std::vector< OUString >& /* [out] */ _rBindingNames ) const
+ {
+ _rBindingNames.resize( 0 );
+ try
+ {
+ Reference< xforms::XModel > xModel( getFormModelByName( _rModelName ) );
+ if ( xModel.is() )
+ {
+ Reference< XNameAccess > xBindings( xModel->getBindings(), UNO_QUERY );
+ OSL_ENSURE( xBindings.is(), "EFormsHelper::getBindingNames: invalid bindings container obtained from the model!" );
+ if ( xBindings.is() )
+ {
+ const Sequence< OUString > aNames = xBindings->getElementNames();
+ _rBindingNames.resize( aNames.getLength() );
+ std::copy( aNames.begin(), aNames.end(), _rBindingNames.begin() );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getBindingNames" );
+ }
+ }
+
+
+ Reference< xforms::XModel > EFormsHelper::getFormModelByName( const OUString& _rModelName ) const
+ {
+ Reference< xforms::XModel > xReturn;
+ try
+ {
+ Reference< XNameContainer > xForms( m_xDocument->getXForms() );
+ OSL_ENSURE( xForms.is(), "EFormsHelper::getFormModelByName: invalid forms container!" );
+ if ( xForms.is() )
+ OSL_VERIFY( xForms->getByName( _rModelName ) >>= xReturn );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getFormModelByName" );
+ }
+ return xReturn;
+ }
+
+
+ Reference< xforms::XModel > EFormsHelper::getCurrentFormModel() const
+ {
+ Reference< xforms::XModel > xModel;
+ try
+ {
+ Reference< XPropertySet > xBinding( getCurrentBinding() );
+ if ( xBinding.is() )
+ {
+ OSL_VERIFY( xBinding->getPropertyValue( PROPERTY_MODEL ) >>= xModel );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentFormModel" );
+ }
+ return xModel;
+ }
+
+
+ OUString EFormsHelper::getCurrentFormModelName() const
+ {
+ OUString sModelName;
+ try
+ {
+ Reference< xforms::XModel > xFormsModel( getCurrentFormModel() );
+ if ( xFormsModel.is() )
+ sModelName = xFormsModel->getID();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentFormModel" );
+ }
+ return sModelName;
+ }
+
+
+ Reference< XPropertySet > EFormsHelper::getCurrentBinding() const
+ {
+ Reference< XPropertySet > xBinding;
+
+ try
+ {
+ if ( m_xBindableControl.is() )
+ xBinding.set(m_xBindableControl->getValueBinding(), css::uno::UNO_QUERY);
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentBinding" );
+ }
+
+ return xBinding;
+ }
+
+
+ OUString EFormsHelper::getCurrentBindingName() const
+ {
+ OUString sBindingName;
+ try
+ {
+ Reference< XPropertySet > xBinding( getCurrentBinding() );
+ if ( xBinding.is() )
+ xBinding->getPropertyValue( PROPERTY_BINDING_ID ) >>= sBindingName;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentBindingName" );
+ }
+ return sBindingName;
+ }
+
+
+ Reference< XListEntrySource > EFormsHelper::getCurrentListSourceBinding() const
+ {
+ Reference< XListEntrySource > xReturn;
+ try
+ {
+ Reference< XListEntrySink > xAsSink( m_xControlModel, UNO_QUERY );
+ OSL_ENSURE( xAsSink.is(), "EFormsHelper::getCurrentListSourceBinding: you should have used isListEntrySink before!" );
+ if ( xAsSink.is() )
+ xReturn = xAsSink->getListEntrySource();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getCurrentListSourceBinding" );
+ }
+ return xReturn;
+ }
+
+
+ void EFormsHelper::setListSourceBinding( const Reference< XListEntrySource >& _rxListSource )
+ {
+ try
+ {
+ Reference< XListEntrySink > xAsSink( m_xControlModel, UNO_QUERY );
+ OSL_ENSURE( xAsSink.is(), "EFormsHelper::setListSourceBinding: you should have used isListEntrySink before!" );
+ if ( xAsSink.is() )
+ xAsSink->setListEntrySource( _rxListSource );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::setListSourceBinding" );
+ }
+ }
+
+
+ void EFormsHelper::setBinding( const Reference< css::beans::XPropertySet >& _rxBinding )
+ {
+ if ( !m_xBindableControl.is() )
+ return;
+
+ try
+ {
+ Reference< XPropertySet > xOldBinding( m_xBindableControl->getValueBinding(), UNO_QUERY );
+
+ Reference< XValueBinding > xBinding( _rxBinding, UNO_QUERY );
+ OSL_ENSURE( xBinding.is() || !_rxBinding.is(), "EFormsHelper::setBinding: invalid binding!" );
+
+ impl_toggleBindingPropertyListening_throw( false, nullptr );
+ m_xBindableControl->setValueBinding( xBinding );
+ impl_toggleBindingPropertyListening_throw( true, nullptr );
+
+ std::set< OUString > aSet;
+ firePropertyChanges( xOldBinding, _rxBinding, aSet );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::setBinding" );
+ }
+ }
+
+
+ Reference< XPropertySet > EFormsHelper::getOrCreateBindingForModel( const OUString& _rTargetModel, const OUString& _rBindingName ) const
+ {
+ OSL_ENSURE( !_rBindingName.isEmpty(), "EFormsHelper::getOrCreateBindingForModel: invalid binding name!" );
+ return implGetOrCreateBinding( _rTargetModel, _rBindingName );
+ }
+
+
+ Reference< XPropertySet > EFormsHelper::implGetOrCreateBinding( const OUString& _rTargetModel, const OUString& _rBindingName ) const
+ {
+ OSL_ENSURE( !( _rTargetModel.isEmpty() && !_rBindingName.isEmpty() ), "EFormsHelper::implGetOrCreateBinding: no model, but a binding name?" );
+
+ Reference< XPropertySet > xBinding;
+ try
+ {
+ OUString sTargetModel( _rTargetModel );
+ // determine the model which the binding should belong to
+ if ( sTargetModel.isEmpty() )
+ {
+ std::vector< OUString > aModelNames;
+ getFormModelNames( aModelNames );
+ if ( !aModelNames.empty() )
+ sTargetModel = *aModelNames.begin();
+ OSL_ENSURE( !sTargetModel.isEmpty(), "EFormsHelper::implGetOrCreateBinding: unable to obtain a default model!" );
+ }
+ Reference< xforms::XModel > xModel( getFormModelByName( sTargetModel ) );
+ Reference< XNameAccess > xBindingNames( xModel.is() ? xModel->getBindings() : Reference< XSet >(), UNO_QUERY );
+ if ( xBindingNames.is() )
+ {
+ // get or create the binding instance
+ if ( !_rBindingName.isEmpty() )
+ {
+ if ( xBindingNames->hasByName( _rBindingName ) )
+ OSL_VERIFY( xBindingNames->getByName( _rBindingName ) >>= xBinding );
+ else
+ {
+ xBinding = xModel->createBinding( );
+ if ( xBinding.is() )
+ {
+ xBinding->setPropertyValue( PROPERTY_BINDING_ID, Any( _rBindingName ) );
+ xModel->getBindings()->insert( Any( xBinding ) );
+ }
+ }
+ }
+ else
+ {
+ xBinding = xModel->createBinding( );
+ if ( xBinding.is() )
+ {
+ // find a nice name for it
+ OUString sBaseName(PcrRes(RID_STR_BINDING_NAME) + " ");
+ OUString sNewName;
+ sal_Int32 nNumber = 1;
+ do
+ {
+ sNewName = sBaseName + OUString::number( nNumber++ );
+ }
+ while ( xBindingNames->hasByName( sNewName ) );
+ Reference< XNamed > xName( xBinding, UNO_QUERY_THROW );
+ xName->setName( sNewName );
+ // and insert into the model
+ xModel->getBindings()->insert( Any( xBinding ) );
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ return xBinding;
+ }
+
+
+ namespace
+ {
+
+ struct PropertyBagInserter
+ {
+ private:
+ PropertyBag& m_rProperties;
+
+ public:
+ explicit PropertyBagInserter( PropertyBag& rProperties ) : m_rProperties( rProperties ) { }
+
+ void operator()( const Property& _rProp )
+ {
+ m_rProperties.insert( _rProp );
+ }
+ };
+
+
+ Reference< XPropertySetInfo > collectPropertiesGetInfo( const Reference< XPropertySet >& _rxProps, PropertyBag& _rBag )
+ {
+ Reference< XPropertySetInfo > xInfo;
+ if ( _rxProps.is() )
+ xInfo = _rxProps->getPropertySetInfo();
+ if ( xInfo.is() )
+ {
+ const Sequence< Property > aProperties = xInfo->getProperties();
+ std::for_each( aProperties.begin(), aProperties.end(),
+ PropertyBagInserter( _rBag )
+ );
+ }
+ return xInfo;
+ }
+ }
+
+
+ OUString EFormsHelper::getModelElementUIName( const EFormsHelper::ModelElementType _eType, const Reference< XPropertySet >& _rxElement )
+ {
+ OUString sUIName;
+ try
+ {
+ // determine the model which the element belongs to
+ Reference< xforms::XFormsUIHelper1 > xHelper;
+ if ( _rxElement.is() )
+ _rxElement->getPropertyValue( PROPERTY_MODEL ) >>= xHelper;
+ OSL_ENSURE( xHelper.is(), "EFormsHelper::getModelElementUIName: invalid element or model!" );
+ if ( xHelper.is() )
+ {
+ OUString sElementName = ( _eType == Submission ) ? xHelper->getSubmissionName( _rxElement, true ) : xHelper->getBindingName( _rxElement, true );
+ Reference< xforms::XModel > xModel( xHelper, UNO_QUERY_THROW );
+ sUIName = composeModelElementUIName( xModel->getID(), sElementName );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getModelElementUIName" );
+ }
+
+ return sUIName;
+ }
+
+
+ Reference< XPropertySet > EFormsHelper::getModelElementFromUIName( const EFormsHelper::ModelElementType _eType, const OUString& _rUIName ) const
+ {
+ const MapStringToPropertySet& rMapUINameToElement( ( _eType == Submission ) ? m_aSubmissionUINames : m_aBindingUINames );
+ MapStringToPropertySet::const_iterator pos = rMapUINameToElement.find( _rUIName );
+ OSL_ENSURE( pos != rMapUINameToElement.end(), "EFormsHelper::getModelElementFromUIName: didn't find it!" );
+
+ return ( pos != rMapUINameToElement.end() ) ? pos->second : Reference< XPropertySet >();
+ }
+
+
+ void EFormsHelper::getAllElementUINames( const ModelElementType _eType, std::vector< OUString >& /* [out] */ _rElementNames, bool _bPrepentEmptyEntry )
+ {
+ MapStringToPropertySet& rMapUINameToElement( ( _eType == Submission ) ? m_aSubmissionUINames : m_aBindingUINames );
+ rMapUINameToElement.clear();
+ _rElementNames.resize( 0 );
+
+ if ( _bPrepentEmptyEntry )
+ rMapUINameToElement[ OUString() ].clear();
+
+ try
+ {
+ // obtain the model names
+ std::vector< OUString > aModels;
+ getFormModelNames( aModels );
+ _rElementNames.reserve( aModels.size() * 2 ); // heuristics
+
+ // for every model, obtain the element
+ for (auto const& modelName : aModels)
+ {
+ Reference< xforms::XModel > xModel = getFormModelByName(modelName);
+ OSL_ENSURE( xModel.is(), "EFormsHelper::getAllElementUINames: inconsistency in the models!" );
+ Reference< xforms::XFormsUIHelper1 > xHelper( xModel, UNO_QUERY );
+
+ Reference< XIndexAccess > xElements;
+ if ( xModel.is() )
+ xElements.set(( _eType == Submission ) ? xModel->getSubmissions() : xModel->getBindings(), css::uno::UNO_QUERY);
+ if ( !xElements.is() )
+ break;
+
+ sal_Int32 nElementCount = xElements->getCount();
+ for ( sal_Int32 i = 0; i < nElementCount; ++i )
+ {
+ Reference< XPropertySet > xElement( xElements->getByIndex( i ), UNO_QUERY );
+ OSL_ENSURE( xElement.is(), "EFormsHelper::getAllElementUINames: empty element!" );
+ if ( !xElement.is() )
+ continue;
+#if OSL_DEBUG_LEVEL > 0
+ {
+ Reference< xforms::XModel > xElementsModel;
+ xElement->getPropertyValue( PROPERTY_MODEL ) >>= xElementsModel;
+ OSL_ENSURE( xElementsModel == xModel, "EFormsHelper::getAllElementUINames: inconsistency in the model-element relationship!" );
+ if ( xElementsModel != xModel )
+ xElement->setPropertyValue( PROPERTY_MODEL, Any( xModel ) );
+ }
+#endif
+ OUString sElementName = ( _eType == Submission ) ? xHelper->getSubmissionName( xElement, true ) : xHelper->getBindingName( xElement, true );
+ OUString sUIName = composeModelElementUIName( modelName, sElementName );
+
+ OSL_ENSURE( rMapUINameToElement.find( sUIName ) == rMapUINameToElement.end(), "EFormsHelper::getAllElementUINames: duplicate name!" );
+ rMapUINameToElement.emplace( sUIName, xElement );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::getAllElementUINames" );
+ }
+
+ _rElementNames.resize( rMapUINameToElement.size() );
+ std::transform( rMapUINameToElement.begin(), rMapUINameToElement.end(), _rElementNames.begin(),
+ ::o3tl::select1st< MapStringToPropertySet::value_type >() );
+ }
+
+
+ void EFormsHelper::firePropertyChange( const OUString& _rName, const Any& _rOldValue, const Any& _rNewValue ) const
+ {
+ if ( m_aPropertyListeners.getLength() == 0 )
+ return;
+
+ if ( _rOldValue == _rNewValue )
+ return;
+
+ try
+ {
+ PropertyChangeEvent aEvent;
+
+ aEvent.Source = m_xBindableControl.get();
+ aEvent.PropertyName = _rName;
+ aEvent.OldValue = _rOldValue;
+ aEvent.NewValue = _rNewValue;
+
+ const_cast< EFormsHelper* >( this )->m_aPropertyListeners.notifyEach( &XPropertyChangeListener::propertyChange, aEvent );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::firePropertyChange" );
+ }
+ }
+
+
+ void EFormsHelper::firePropertyChanges( const Reference< XPropertySet >& _rxOldProps, const Reference< XPropertySet >& _rxNewProps, std::set< OUString >& _rFilter ) const
+ {
+ if ( m_aPropertyListeners.getLength() == 0 )
+ return;
+
+ try
+ {
+ PropertyBag aProperties;
+ Reference< XPropertySetInfo > xOldInfo = collectPropertiesGetInfo( _rxOldProps, aProperties );
+ Reference< XPropertySetInfo > xNewInfo = collectPropertiesGetInfo( _rxNewProps, aProperties );
+
+ for (auto const& property : aProperties)
+ {
+ if ( _rFilter.find( property.Name ) != _rFilter.end() )
+ continue;
+
+ Any aOldValue( nullptr, property.Type );
+ if ( xOldInfo.is() && xOldInfo->hasPropertyByName( property.Name ) )
+ aOldValue = _rxOldProps->getPropertyValue( property.Name );
+
+ Any aNewValue( nullptr, property.Type );
+ if ( xNewInfo.is() && xNewInfo->hasPropertyByName( property.Name ) )
+ aNewValue = _rxNewProps->getPropertyValue( property.Name );
+
+ firePropertyChange( property.Name, aOldValue, aNewValue );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsHelper::firePropertyChanges" );
+ }
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/eformshelper.hxx b/extensions/source/propctrlr/eformshelper.hxx
new file mode 100644
index 000000000..e88cb7491
--- /dev/null
+++ b/extensions/source/propctrlr/eformshelper.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 "pcrcommon.hxx"
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <com/sun/star/xforms/XFormsSupplier.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XListEntrySource.hpp>
+#include <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+
+#include <vector>
+#include <set>
+#include <map>
+
+
+namespace pcr
+{
+
+
+ typedef std::map< OUString, css::uno::Reference< css::beans::XPropertySet >, std::less< OUString > >
+ MapStringToPropertySet;
+
+
+ //= EFormsHelper
+
+ class EFormsHelper
+ {
+ protected:
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xControlModel;
+ css::uno::Reference< css::form::binding::XBindableValue >
+ m_xBindableControl;
+ css::uno::Reference< css::xforms::XFormsSupplier >
+ m_xDocument;
+ PropertyChangeListeners
+ m_aPropertyListeners;
+ MapStringToPropertySet
+ m_aSubmissionUINames; // only filled upon request
+ MapStringToPropertySet
+ m_aBindingUINames; // only filled upon request
+
+ public:
+ EFormsHelper(
+ ::osl::Mutex& _rMutex,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel,
+ const css::uno::Reference< css::frame::XModel >& _rxContextDocument
+ );
+
+ /** determines whether the given document is an eForm
+
+ If this method returns <FALSE/>, you cannot instantiate an EFormsHelper with
+ this document, since none of its functionality will be available then.
+ */
+ static bool
+ isEForm(
+ const css::uno::Reference< css::frame::XModel >& _rxContextDocument
+ );
+
+ /** registers a listener to be notified when any aspect of the binding changes.
+
+ The listener will be registered at the current binding of the control model. If the binding
+ changes (see <method>setBinding</method>), the listener will be revoked from the old binding,
+ registered at the new binding, and for all properties which differ between both bindings,
+ the listener will be notified.
+ @see revokeBindingListener
+ */
+ void registerBindingListener(
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxBindingListener
+ );
+
+ /** revokes the binding listener which has previously been registered
+ @see registerBindingListener
+ */
+ void revokeBindingListener(
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxBindingListener
+ );
+
+ /** checks whether it's possible to bind the control model to a given XSD data type
+
+ @param _nDataType
+ the data type which should be bound. If this is -1, <TRUE/> is returned if the control model
+ can be bound to <em>any</em> data type.
+ */
+ bool canBindToDataType( sal_Int32 _nDataType = -1 ) const;
+
+ /** checks whether the control model can be bound to any XSD data type
+ */
+ bool canBindToAnyDataType() const { return canBindToDataType(); }
+
+ /** checks whether the control model is a source for list entries, as supplied by XML data bindings
+ */
+ bool isListEntrySink() const;
+
+ /** retrieves the names of all XForms models in the document the control lives in
+ */
+ void getFormModelNames( std::vector< OUString >& /* [out] */ _rModelNames ) const;
+
+ /** retrieves the names of all bindings for a given model
+ @see getFormModelNames
+ */
+ void getBindingNames( const OUString& _rModelName, std::vector< OUString >& /* [out] */ _rBindingNames ) const;
+
+ /// retrieves the XForms model (within the control model's document) with the given name
+ css::uno::Reference< css::xforms::XModel >
+ getFormModelByName( const OUString& _rModelName ) const;
+
+ /** retrieves the model which the active binding of the control model belongs to
+ */
+ css::uno::Reference< css::xforms::XModel >
+ getCurrentFormModel() const;
+
+ /** retrieves the name of the model which the active binding of the control model belongs to
+ */
+ OUString
+ getCurrentFormModelName() const;
+
+ /** retrieves the binding instance which is currently attached to the control model
+ */
+ css::uno::Reference< css::beans::XPropertySet >
+ getCurrentBinding() const;
+
+ /** retrieves the name of the binding instance which is currently attached to the control model
+ */
+ OUString
+ getCurrentBindingName() const;
+
+ /** sets a new binding at the control model
+ */
+ void setBinding( const css::uno::Reference< css::beans::XPropertySet >& _rxBinding );
+
+ /** retrieves the binding instance which is currently used as list source for the control model
+ @see isListEntrySink
+ */
+ css::uno::Reference< css::form::binding::XListEntrySource >
+ getCurrentListSourceBinding() const;
+
+ /** sets a new list source at the control model
+ @see isListEntrySink
+ */
+ void setListSourceBinding( const css::uno::Reference< css::form::binding::XListEntrySource >& _rxListSource );
+
+ /** retrieves a given binding for a given model, or creates a new one
+
+ @param _rTargetModel
+ the name of the model to create a binding for. Must not be empty
+ @param _rBindingName
+ the name of the binding to retrieve. If the model denoted by <arg>_rTargetModel</arg> does not
+ have a binding with this name, a new binding is created and returned.
+ */
+ css::uno::Reference< css::beans::XPropertySet >
+ getOrCreateBindingForModel( const OUString& _rTargetModel, const OUString& _rBindingName ) const;
+
+ /** types of sub-elements of a model
+ */
+ enum ModelElementType
+ {
+ Submission,
+ Binding
+ };
+
+ /** retrieves the name of a model's sub-element, as to be shown in the UI
+ @see getModelElementFromUIName
+ @see getAllElementUINames
+ */
+ static OUString
+ getModelElementUIName(
+ const ModelElementType _eType,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxElement
+ );
+
+ /** retrieves the submission object for an UI name
+
+ Note that <member>getAllElementUINames</member> must have been called before, for the given element type
+
+ @see getModelElementUIName
+ @see getAllElementUINames
+ */
+ css::uno::Reference< css::beans::XPropertySet >
+ getModelElementFromUIName(
+ const ModelElementType _eType,
+ const OUString& _rUIName
+ ) const;
+
+ /** retrieves the UI names of all elements of all models in our document
+ @param _eType
+ the type of elements for which the names should be retrieved
+ @param _rElementNames
+ the array of element names
+ @see getModelElementUIName
+ @see getModelElementFromUIName
+ */
+ void getAllElementUINames(
+ const ModelElementType _eType,
+ std::vector< OUString >& /* [out] */ _rElementNames,
+ bool _bPrepentEmptyEntry
+ );
+
+ protected:
+ void firePropertyChanges(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxOldProps,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxNewProps,
+ std::set< OUString >& _rFilter
+ ) const;
+
+ /** fires a change in a single property, if the property value changed, and if we have a listener
+ interested in property changes
+ */
+ void firePropertyChange(
+ const OUString& _rName,
+ const css::uno::Any& _rOldValue,
+ const css::uno::Any& _rNewValue
+ ) const;
+
+ private:
+ void impl_switchBindingListening_throw( bool _bDoListening, const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener );
+
+ /// implementation for both <member>getOrCreateBindingForModel</member>
+ css::uno::Reference< css::beans::XPropertySet >
+ implGetOrCreateBinding( const OUString& _rTargetModel, const OUString& _rBindingName ) const;
+
+ void
+ impl_toggleBindingPropertyListening_throw( bool _bDoListen, const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxConcreteListenerOrNull );
+
+ private:
+ EFormsHelper( const EFormsHelper& ) = delete;
+ EFormsHelper& operator=( const EFormsHelper& ) = delete;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/eformspropertyhandler.cxx b/extensions/source/propctrlr/eformspropertyhandler.cxx
new file mode 100644
index 000000000..42ca7ed1a
--- /dev/null
+++ b/extensions/source/propctrlr/eformspropertyhandler.cxx
@@ -0,0 +1,598 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "eformspropertyhandler.hxx"
+#include "formstrings.hxx"
+#include "formmetadata.hxx"
+#include <propctrlr.h>
+#include "eformshelper.hxx"
+#include "handlerhelper.hxx"
+
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+
+namespace pcr
+{
+
+
+ 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::xforms;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::ui::dialogs;
+ using namespace ::com::sun::star::form::binding;
+ using namespace ::com::sun::star::inspection;
+
+
+ //= EFormsPropertyHandler
+
+
+ EFormsPropertyHandler::EFormsPropertyHandler( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandlerComponent( _rxContext )
+ ,m_bSimulatingModelChange( false )
+ {
+ }
+
+
+ EFormsPropertyHandler::~EFormsPropertyHandler( )
+ {
+ }
+
+
+ OUString EFormsPropertyHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.EFormsPropertyHandler";
+ }
+
+
+ Sequence< OUString > EFormsPropertyHandler::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.inspection.XMLFormsPropertyHandler" };
+ }
+
+
+ OUString EFormsPropertyHandler::getModelNamePropertyValue() const
+ {
+ OUString sModelName = m_pHelper->getCurrentFormModelName();
+ if ( sModelName.isEmpty() )
+ sModelName = m_sBindingLessModelName;
+ return sModelName;
+ }
+
+
+ Any SAL_CALL EFormsPropertyHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ OSL_ENSURE(
+ m_pHelper,
+ "EFormsPropertyHandler::getPropertyValue: we don't have any SupportedProperties!");
+ // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties
+
+ Any aReturn;
+ try
+ {
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_LIST_BINDING:
+ aReturn <<= m_pHelper->getCurrentListSourceBinding();
+ break;
+
+ case PROPERTY_ID_XML_DATA_MODEL:
+ aReturn <<= getModelNamePropertyValue();
+ break;
+
+ case PROPERTY_ID_BINDING_NAME:
+ aReturn <<= m_pHelper->getCurrentBindingName();
+ break;
+
+ case PROPERTY_ID_BIND_EXPRESSION:
+ case PROPERTY_ID_XSD_CONSTRAINT:
+ case PROPERTY_ID_XSD_CALCULATION:
+ case PROPERTY_ID_XSD_REQUIRED:
+ case PROPERTY_ID_XSD_RELEVANT:
+ case PROPERTY_ID_XSD_READONLY:
+ {
+ Reference< XPropertySet > xBindingProps( m_pHelper->getCurrentBinding() );
+ if ( xBindingProps.is() )
+ {
+ aReturn = xBindingProps->getPropertyValue( _rPropertyName );
+ DBG_ASSERT( aReturn.getValueType().equals( ::cppu::UnoType<OUString>::get() ),
+ "EFormsPropertyHandler::getPropertyValue: invalid BindingExpression value type!" );
+ }
+ else
+ aReturn <<= OUString();
+ }
+ break;
+
+ default:
+ OSL_FAIL( "EFormsPropertyHandler::getPropertyValue: cannot handle this property!" );
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsPropertyHandler::getPropertyValue: caught exception!"
+ "(have been asked for the \"" <<_rPropertyName << "\" property.)");
+ }
+ return aReturn;
+ }
+
+
+ void SAL_CALL EFormsPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ OSL_ENSURE(
+ m_pHelper,
+ "EFormsPropertyHandler::setPropertyValue: we don't have any SupportedProperties!");
+ // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties
+
+ try
+ {
+ Any aOldValue = getPropertyValue( _rPropertyName );
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_LIST_BINDING:
+ {
+ Reference< XListEntrySource > xSource;
+ OSL_VERIFY( _rValue >>= xSource );
+ m_pHelper->setListSourceBinding( xSource );
+ }
+ break;
+
+ case PROPERTY_ID_XML_DATA_MODEL:
+ {
+ OSL_VERIFY( _rValue >>= m_sBindingLessModelName );
+
+ // if the model changed, reset the binding to NULL
+ if ( m_pHelper->getCurrentFormModelName() != m_sBindingLessModelName )
+ {
+ OUString sOldBindingName = m_pHelper->getCurrentBindingName();
+ m_pHelper->setBinding( nullptr );
+ firePropertyChange( PROPERTY_BINDING_NAME, PROPERTY_ID_BINDING_NAME,
+ Any( sOldBindingName ), Any( OUString() ) );
+ }
+ }
+ break;
+
+ case PROPERTY_ID_BINDING_NAME:
+ {
+ OUString sNewBindingName;
+ OSL_VERIFY( _rValue >>= sNewBindingName );
+
+ bool bPreviouslyEmptyModel = !m_pHelper->getCurrentFormModel().is();
+
+ Reference< XPropertySet > xNewBinding;
+ if ( !sNewBindingName.isEmpty() )
+ // obtain the binding with this name, for the current model
+ xNewBinding = m_pHelper->getOrCreateBindingForModel( getModelNamePropertyValue(), sNewBindingName );
+
+ m_pHelper->setBinding( xNewBinding );
+
+ if ( bPreviouslyEmptyModel )
+ { // simulate a property change for the model property
+ // This is because we "simulate" the Model property by remembering the
+ // value ourself. Other instances might, however, not know this value,
+ // but prefer to retrieve it somewhere else - e.g. from the EFormsHelper
+
+ // The really correct solution would be if *all* property handlers
+ // obtain a "current property value" for *all* properties from a central
+ // instance. Then, handler A could ask it for the value of property
+ // X, and this request would be re-routed to handler B, which ultimately
+ // knows the current value.
+ // However, there's no such mechanism in place currently.
+ m_bSimulatingModelChange = true;
+ firePropertyChange( PROPERTY_XML_DATA_MODEL, PROPERTY_ID_XML_DATA_MODEL,
+ Any( OUString() ), Any( getModelNamePropertyValue() ) );
+ m_bSimulatingModelChange = false;
+ }
+ }
+ break;
+
+ case PROPERTY_ID_BIND_EXPRESSION:
+ {
+ Reference< XPropertySet > xBinding( m_pHelper->getCurrentBinding() );
+ OSL_ENSURE( xBinding.is(), "You should not reach this without an active binding!" );
+ if ( xBinding.is() )
+ xBinding->setPropertyValue( PROPERTY_BIND_EXPRESSION, _rValue );
+ }
+ break;
+
+ case PROPERTY_ID_XSD_REQUIRED:
+ case PROPERTY_ID_XSD_RELEVANT:
+ case PROPERTY_ID_XSD_READONLY:
+ case PROPERTY_ID_XSD_CONSTRAINT:
+ case PROPERTY_ID_XSD_CALCULATION:
+ {
+ Reference< XPropertySet > xBindingProps( m_pHelper->getCurrentBinding() );
+ DBG_ASSERT( xBindingProps.is(), "EFormsPropertyHandler::setPropertyValue: how can I set a property if there's no binding?" );
+ if ( xBindingProps.is() )
+ {
+ DBG_ASSERT( _rValue.getValueType().equals( ::cppu::UnoType<OUString>::get() ),
+ "EFormsPropertyHandler::setPropertyValue: invalid value type!" );
+ xBindingProps->setPropertyValue( _rPropertyName, _rValue );
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "EFormsPropertyHandler::setPropertyValue: cannot handle this property!" );
+ break;
+ }
+
+ impl_setContextDocumentModified_nothrow();
+
+ Any aNewValue( getPropertyValue( _rPropertyName ) );
+ firePropertyChange( _rPropertyName, nPropId, aOldValue, aNewValue );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsPropertyHandler::setPropertyValue" );
+ }
+ }
+
+
+ void EFormsPropertyHandler::onNewComponent()
+ {
+ PropertyHandlerComponent::onNewComponent();
+
+ Reference< frame::XModel > xDocument( impl_getContextDocument_nothrow() );
+ DBG_ASSERT( xDocument.is(), "EFormsPropertyHandler::onNewComponent: no document!" );
+ if ( EFormsHelper::isEForm( xDocument ) )
+ m_pHelper.reset( new EFormsHelper( m_aMutex, m_xComponent, xDocument ) );
+ else
+ m_pHelper.reset();
+ }
+
+
+ Sequence< Property > EFormsPropertyHandler::doDescribeSupportedProperties() const
+ {
+ std::vector< Property > aProperties;
+
+ if (m_pHelper)
+ {
+ if ( m_pHelper->canBindToAnyDataType() )
+ {
+ aProperties.reserve( 7 );
+ addStringPropertyDescription( aProperties, PROPERTY_XML_DATA_MODEL );
+ addStringPropertyDescription( aProperties, PROPERTY_BINDING_NAME );
+ addStringPropertyDescription( aProperties, PROPERTY_BIND_EXPRESSION );
+ addStringPropertyDescription( aProperties, PROPERTY_XSD_REQUIRED );
+ addStringPropertyDescription( aProperties, PROPERTY_XSD_RELEVANT );
+ addStringPropertyDescription( aProperties, PROPERTY_XSD_READONLY );
+ addStringPropertyDescription( aProperties, PROPERTY_XSD_CONSTRAINT );
+ addStringPropertyDescription( aProperties, PROPERTY_XSD_CALCULATION );
+ }
+ if ( m_pHelper->isListEntrySink() )
+ {
+ implAddPropertyDescription( aProperties, PROPERTY_LIST_BINDING,
+ cppu::UnoType<XListEntrySource>::get() );
+ }
+ }
+
+ if ( aProperties.empty() )
+ return Sequence< Property >();
+ return comphelper::containerToSequence(aProperties);
+ }
+
+
+ Any SAL_CALL EFormsPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Any aReturn;
+
+ OSL_ENSURE(
+ m_pHelper,
+ "EFormsPropertyHandler::convertToPropertyValue: we have no SupportedProperties!");
+ if (!m_pHelper)
+ return aReturn;
+
+ PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) );
+
+ OUString sControlValue;
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_LIST_BINDING:
+ {
+ OSL_VERIFY( _rControlValue >>= sControlValue );
+ Reference< XListEntrySource > xListSource( m_pHelper->getModelElementFromUIName( EFormsHelper::Binding, sControlValue ), UNO_QUERY );
+ OSL_ENSURE( xListSource.is() || !m_pHelper->getModelElementFromUIName( EFormsHelper::Binding, sControlValue ).is(),
+ "EFormsPropertyHandler::convertToPropertyValue: there's a binding which is no ListEntrySource!" );
+ aReturn <<= xListSource;
+ }
+ break;
+
+ default:
+ aReturn = PropertyHandlerComponent::convertToPropertyValue( _rPropertyName, _rControlValue );
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ Any SAL_CALL EFormsPropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Any aReturn;
+
+ OSL_ENSURE(m_pHelper,
+ "EFormsPropertyHandler::convertToControlValue: we have no SupportedProperties!");
+ if (!m_pHelper)
+ return aReturn;
+
+ PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) );
+
+ OSL_ENSURE( _rControlValueType.getTypeClass() == TypeClass_STRING,
+ "EFormsPropertyHandler::convertToControlValue: all our controls should use strings for value exchange!" );
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_LIST_BINDING:
+ {
+ Reference< XPropertySet > xListSourceBinding( _rPropertyValue, UNO_QUERY );
+ if ( xListSourceBinding.is() )
+ aReturn <<= EFormsHelper::getModelElementUIName( EFormsHelper::Binding, xListSourceBinding );
+ }
+ break;
+
+ default:
+ aReturn = PropertyHandlerComponent::convertToControlValue( _rPropertyName, _rPropertyValue, _rControlValueType );
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ Sequence< OUString > SAL_CALL EFormsPropertyHandler::getActuatingProperties( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pHelper)
+ return Sequence< OUString >();
+
+ std::vector< OUString > aInterestedInActuations( 2 );
+ aInterestedInActuations[ 0 ] = PROPERTY_XML_DATA_MODEL;
+ aInterestedInActuations[ 1 ] = PROPERTY_BINDING_NAME;
+ return comphelper::containerToSequence(aInterestedInActuations);
+ }
+
+
+ Sequence< OUString > SAL_CALL EFormsPropertyHandler::getSupersededProperties( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pHelper)
+ return Sequence< OUString >();
+
+ Sequence<OUString> aReturn { PROPERTY_INPUT_REQUIRED };
+ return aReturn;
+ }
+
+ LineDescriptor SAL_CALL EFormsPropertyHandler::describePropertyLine( const OUString& _rPropertyName,
+ const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !_rxControlFactory.is() )
+ throw NullPointerException();
+ if (!m_pHelper)
+ throw RuntimeException();
+
+ LineDescriptor aDescriptor;
+ sal_Int16 nControlType = PropertyControlType::TextField;
+ std::vector< OUString > aListEntries;
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_LIST_BINDING:
+ nControlType = PropertyControlType::ListBox;
+ m_pHelper->getAllElementUINames(EFormsHelper::Binding, aListEntries, true);
+ break;
+
+ case PROPERTY_ID_XML_DATA_MODEL:
+ nControlType = PropertyControlType::ListBox;
+ m_pHelper->getFormModelNames( aListEntries );
+ break;
+
+ case PROPERTY_ID_BINDING_NAME:
+ {
+ nControlType = PropertyControlType::ComboBox;
+ OUString sCurrentModel( getModelNamePropertyValue() );
+ if ( !sCurrentModel.isEmpty() )
+ m_pHelper->getBindingNames( sCurrentModel, aListEntries );
+ }
+ break;
+
+ case PROPERTY_ID_BIND_EXPRESSION: aDescriptor.PrimaryButtonId = UID_PROP_DLG_BIND_EXPRESSION; break;
+ case PROPERTY_ID_XSD_REQUIRED: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_REQUIRED; break;
+ case PROPERTY_ID_XSD_RELEVANT: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_RELEVANT; break;
+ case PROPERTY_ID_XSD_READONLY: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_READONLY; break;
+ case PROPERTY_ID_XSD_CONSTRAINT: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_CONSTRAINT; break;
+ case PROPERTY_ID_XSD_CALCULATION: aDescriptor.PrimaryButtonId = UID_PROP_DLG_XSD_CALCULATION; break;
+
+ default:
+ OSL_FAIL( "EFormsPropertyHandler::describePropertyLine: cannot handle this property!" );
+ break;
+ }
+
+ switch ( nControlType )
+ {
+ case PropertyControlType::ListBox:
+ aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory, std::move(aListEntries), false, true );
+ break;
+ case PropertyControlType::ComboBox:
+ aDescriptor.Control = PropertyHandlerHelper::createComboBoxControl( _rxControlFactory, std::move(aListEntries), true );
+ break;
+ default:
+ aDescriptor.Control = _rxControlFactory->createPropertyControl( nControlType, false );
+ break;
+ }
+
+ aDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( nPropId );
+ aDescriptor.Category = "Data";
+ aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) );
+ return aDescriptor;
+ }
+
+ InteractiveSelectionResult SAL_CALL EFormsPropertyHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool /*_bPrimary*/, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_ENSURE(m_pHelper, "EFormsPropertyHandler::onInteractivePropertySelection: we do not "
+ "have any SupportedProperties!");
+ if (!m_pHelper)
+ return InteractiveSelectionResult_Cancelled;
+
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+ OSL_ENSURE( ( PROPERTY_ID_BINDING_NAME == nPropId )
+ || ( PROPERTY_ID_BIND_EXPRESSION == nPropId )
+ || ( PROPERTY_ID_XSD_REQUIRED == nPropId )
+ || ( PROPERTY_ID_XSD_RELEVANT == nPropId )
+ || ( PROPERTY_ID_XSD_READONLY == nPropId )
+ || ( PROPERTY_ID_XSD_CONSTRAINT == nPropId )
+ || ( PROPERTY_ID_XSD_CALCULATION == nPropId ), "EFormsPropertyHandler::onInteractivePropertySelection: unexpected!" );
+
+ try
+ {
+ Reference< XExecutableDialog > xDialog;
+ xDialog.set( m_xContext->getServiceManager()->createInstanceWithContext( "com.sun.star.xforms.ui.dialogs.AddCondition", m_xContext ), UNO_QUERY );
+ Reference< XPropertySet > xDialogProps( xDialog, UNO_QUERY_THROW );
+
+ // the model for the dialog to work with
+ Reference< xforms::XModel > xModel( m_pHelper->getCurrentFormModel() );
+ // the binding for the dialog to work with
+ Reference< XPropertySet > xBinding( m_pHelper->getCurrentBinding() );
+ // the aspect of the binding which the dialog should modify
+ const OUString& sFacetName( _rPropertyName );
+
+ OSL_ENSURE( xModel.is() && xBinding.is() && !sFacetName.isEmpty(),
+ "EFormsPropertyHandler::onInteractivePropertySelection: something is missing for the dialog initialization!" );
+ if ( !xModel.is() || !xBinding.is() || sFacetName.isEmpty() )
+ return InteractiveSelectionResult_Cancelled;
+
+ xDialogProps->setPropertyValue("FormModel", Any( xModel ) );
+ xDialogProps->setPropertyValue("Binding", Any( xBinding ) );
+ xDialogProps->setPropertyValue("FacetName", Any( sFacetName ) );
+
+ if ( !xDialog->execute() )
+ // cancelled
+ return InteractiveSelectionResult_Cancelled;
+
+ _rData = xDialogProps->getPropertyValue("ConditionValue");
+ return InteractiveSelectionResult_ObtainedValue;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EFormsPropertyHandler::onInteractivePropertySelection" );
+ }
+
+ // something went wrong here ...(but has been asserted already)
+ return InteractiveSelectionResult_Cancelled;
+ }
+
+
+ void SAL_CALL EFormsPropertyHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyHandlerComponent::addPropertyChangeListener( _rxListener );
+ if (m_pHelper)
+ m_pHelper->registerBindingListener( _rxListener );
+ }
+
+
+ void SAL_CALL EFormsPropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (m_pHelper)
+ m_pHelper->revokeBindingListener( _rxListener );
+ PropertyHandlerComponent::removePropertyChangeListener( _rxListener );
+ }
+
+
+ void SAL_CALL EFormsPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) );
+ OSL_PRECOND(m_pHelper, "EFormsPropertyHandler::actuatingPropertyChanged: inconsistency!");
+ // if we survived impl_getPropertyId_throwRuntime, we should have a helper, since no helper implies no properties
+
+ DBG_ASSERT( _rxInspectorUI.is(), "EFormsPropertyHandler::actuatingPropertyChanged: invalid callback!" );
+ if ( !_rxInspectorUI.is() )
+ return;
+
+ switch ( nActuatingPropId )
+ {
+ case PROPERTY_ID_XML_DATA_MODEL:
+ {
+ if ( m_bSimulatingModelChange )
+ break;
+ OUString sDataModelName;
+ OSL_VERIFY( _rNewValue >>= sDataModelName );
+ bool bBoundToSomeModel = !sDataModelName.isEmpty();
+ _rxInspectorUI->rebuildPropertyUI( PROPERTY_BINDING_NAME );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_BINDING_NAME, bBoundToSomeModel );
+ [[fallthrough]];
+ }
+
+ case PROPERTY_ID_BINDING_NAME:
+ {
+ bool bHaveABinding = !m_pHelper->getCurrentBindingName().isEmpty();
+ _rxInspectorUI->enablePropertyUI( PROPERTY_BIND_EXPRESSION, bHaveABinding );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_REQUIRED, bHaveABinding );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_RELEVANT, bHaveABinding );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_READONLY, bHaveABinding );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_CONSTRAINT, bHaveABinding );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_CALCULATION, bHaveABinding );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_XSD_DATA_TYPE, bHaveABinding );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "EFormsPropertyHandler::actuatingPropertyChanged: cannot handle this property!" );
+ break;
+ }
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_EFormsPropertyHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::EFormsPropertyHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/eformspropertyhandler.hxx b/extensions/source/propctrlr/eformspropertyhandler.hxx
new file mode 100644
index 000000000..8945c24f4
--- /dev/null
+++ b/extensions/source/propctrlr/eformspropertyhandler.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 "propertyhandler.hxx"
+
+#include <memory>
+
+
+namespace pcr
+{
+
+
+ class EFormsHelper;
+
+ //= EFormsPropertyHandler
+
+ class EFormsPropertyHandler;
+ class EFormsPropertyHandler : public PropertyHandlerComponent
+ {
+ private:
+ std::unique_ptr< EFormsHelper > m_pHelper;
+ /** current value of the Model property, if there is no binding, yet
+ */
+ OUString m_sBindingLessModelName;
+ /** are we currently simulating a propertyChange event of the Model property?
+ */
+ bool m_bSimulatingModelChange;
+
+ public:
+ explicit EFormsPropertyHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ protected:
+ virtual ~EFormsPropertyHandler() override;
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
+
+ // XPropertyHandler overriables
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Sequence< OUString >
+ SAL_CALL getActuatingProperties( ) override;
+ virtual css::uno::Sequence< OUString >
+ SAL_CALL getSupersededProperties( ) override;
+ virtual css::inspection::LineDescriptor
+ SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+ virtual css::inspection::InteractiveSelectionResult
+ SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) override;
+ virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override;
+ virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+
+ // PropertyHandler overridables
+ virtual css::uno::Sequence< css::beans::Property >
+ doDescribeSupportedProperties() const override;
+ virtual void onNewComponent() override;
+
+ protected:
+ /** returns the value of the PROPERTY_XML_DATA_MODEL property.
+
+ An extra method is necessary here, which respects both the value set at our helper,
+ and <member>m_sBindingLessModelName</member>
+ */
+ OUString getModelNamePropertyValue() const;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/enumrepresentation.hxx b/extensions/source/propctrlr/enumrepresentation.hxx
new file mode 100644
index 000000000..b3ba04c9a
--- /dev/null
+++ b/extensions/source/propctrlr/enumrepresentation.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 <com/sun/star/uno/Any.hxx>
+#include <rtl/ustring.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+#include <vector>
+
+
+namespace pcr
+{
+
+
+ //= IPropertyEnumRepresentation
+
+ class SAL_NO_VTABLE IPropertyEnumRepresentation : public salhelper::SimpleReferenceObject
+ {
+ public:
+ /** retrieves all descriptions of all possible values of the enumeration property
+ */
+ virtual std::vector< OUString > getDescriptions(
+ ) const = 0;
+
+ /** converts a given description into a property value
+ */
+ virtual void getValueFromDescription(
+ const OUString& _rDescription,
+ css::uno::Any& _out_rValue
+ ) const = 0;
+
+ /** converts a given property value into a description
+ */
+ virtual OUString getDescriptionForValue(
+ const css::uno::Any& _rEnumValue
+ ) const = 0;
+
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/eventhandler.cxx b/extensions/source/propctrlr/eventhandler.cxx
new file mode 100644
index 000000000..d07f524bf
--- /dev/null
+++ b/extensions/source/propctrlr/eventhandler.cxx
@@ -0,0 +1,1112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "eventhandler.hxx"
+#include <helpids.h>
+#include <propctrlr.h>
+#include "formbrowsertools.hxx"
+#include <strings.hrc>
+#include "formstrings.hxx"
+#include "handlerhelper.hxx"
+#include "modulepcr.hxx"
+#include "pcrcommon.hxx"
+#include "propertycontrolextender.hxx"
+
+#include <com/sun/star/awt/XTabControllerModel.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/runtime/FormController.hpp>
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/script/XScriptEventsSupplier.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/evtmethodhelper.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <svx/svxdlg.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <map>
+#include <algorithm>
+#include <iterator>
+#include <string_view>
+
+namespace pcr
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::TypeClass_STRING;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::beans::theIntrospection;
+ using ::com::sun::star::beans::XPropertyChangeListener;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::beans::PropertyState;
+ using ::com::sun::star::beans::PropertyState_DIRECT_VALUE;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::script::ScriptEventDescriptor;
+ using ::com::sun::star::script::XScriptEventsSupplier;
+ using ::com::sun::star::lang::NullPointerException;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::script::XEventAttacherManager;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::beans::XIntrospection;
+ using ::com::sun::star::beans::XIntrospectionAccess;
+ using ::com::sun::star::container::XNameContainer;
+ using ::com::sun::star::awt::XTabControllerModel;
+ using ::com::sun::star::form::XForm;
+ using ::com::sun::star::form::runtime::FormController;
+ using ::com::sun::star::form::runtime::XFormController;
+ using ::com::sun::star::beans::UnknownPropertyException;
+ using ::com::sun::star::container::NoSuchElementException;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::container::XNameReplace;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::inspection::LineDescriptor;
+ using ::com::sun::star::inspection::XPropertyControlFactory;
+ using ::com::sun::star::inspection::InteractiveSelectionResult;
+ using ::com::sun::star::inspection::InteractiveSelectionResult_Cancelled;
+ using ::com::sun::star::inspection::InteractiveSelectionResult_Success;
+ using ::com::sun::star::inspection::XObjectInspectorUI;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::frame::XFrame;
+ using ::com::sun::star::frame::XModel;
+ using ::com::sun::star::frame::XController;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using com::sun::star::uri::UriReferenceFactory;
+ using com::sun::star::uri::XUriReferenceFactory;
+ using com::sun::star::uri::XVndSunStarScriptUrlReference;
+ using ::com::sun::star::lang::XEventListener;
+
+ namespace PropertyControlType = css::inspection::PropertyControlType;
+ namespace PropertyAttribute = css::beans::PropertyAttribute;
+ namespace FormComponentType = css::form::FormComponentType;
+
+ EventDescription::EventDescription( EventId _nId, const char* _pListenerNamespaceAscii, const char* _pListenerClassAsciiName,
+ const char* _pListenerMethodAsciiName, TranslateId pDisplayNameResId, const OUString& _sHelpId, const OString& _sUniqueBrowseId )
+ :sDisplayName(PcrRes( pDisplayNameResId ))
+ ,sListenerMethodName( OUString::createFromAscii( _pListenerMethodAsciiName ) )
+ ,sHelpId( _sHelpId )
+ ,sUniqueBrowseId( _sUniqueBrowseId )
+ ,nId( _nId )
+ {
+ OUStringBuffer aQualifiedListenerClass;
+ aQualifiedListenerClass.append( "com.sun.star." );
+ aQualifiedListenerClass.appendAscii( _pListenerNamespaceAscii );
+ aQualifiedListenerClass.append( "." );
+ aQualifiedListenerClass.appendAscii( _pListenerClassAsciiName );
+ sListenerClassName = aQualifiedListenerClass.makeStringAndClear();
+ }
+
+ namespace
+ {
+ #define DESCRIBE_EVENT( map, asciinamespace, asciilistener, asciimethod, id_postfix ) \
+ map.emplace( \
+ asciimethod, \
+ EventDescription( ++nEventId, asciinamespace, asciilistener, asciimethod, RID_STR_EVT_##id_postfix, HID_EVT_##id_postfix, UID_BRWEVT_##id_postfix ) )
+
+ bool lcl_getEventDescriptionForMethod( const OUString& _rMethodName, EventDescription& _out_rDescription )
+ {
+ static EventMap s_aKnownEvents = []() {
+ EventMap aMap;
+ sal_Int32 nEventId = 0;
+
+ DESCRIBE_EVENT(aMap, "form", "XApproveActionListener", "approveAction", APPROVEACTIONPERFORMED);
+ DESCRIBE_EVENT(aMap, "awt", "XActionListener", "actionPerformed", ACTIONPERFORMED);
+ DESCRIBE_EVENT(aMap, "form", "XChangeListener", "changed", CHANGED);
+ DESCRIBE_EVENT(aMap, "awt", "XTextListener", "textChanged", TEXTCHANGED);
+ DESCRIBE_EVENT(aMap, "awt", "XItemListener", "itemStateChanged", ITEMSTATECHANGED);
+ DESCRIBE_EVENT(aMap, "awt", "XFocusListener", "focusGained", FOCUSGAINED);
+ DESCRIBE_EVENT(aMap, "awt", "XFocusListener", "focusLost", FOCUSLOST);
+ DESCRIBE_EVENT(aMap, "awt", "XKeyListener", "keyPressed", KEYTYPED);
+ DESCRIBE_EVENT(aMap, "awt", "XKeyListener", "keyReleased", KEYUP);
+ DESCRIBE_EVENT(aMap, "awt", "XMouseListener", "mouseEntered", MOUSEENTERED);
+ DESCRIBE_EVENT(aMap, "awt", "XMouseMotionListener", "mouseDragged", MOUSEDRAGGED);
+ DESCRIBE_EVENT(aMap, "awt", "XMouseMotionListener", "mouseMoved", MOUSEMOVED);
+ DESCRIBE_EVENT(aMap, "awt", "XMouseListener", "mousePressed", MOUSEPRESSED);
+ DESCRIBE_EVENT(aMap, "awt", "XMouseListener", "mouseReleased", MOUSERELEASED);
+ DESCRIBE_EVENT(aMap, "awt", "XMouseListener", "mouseExited", MOUSEEXITED);
+ DESCRIBE_EVENT(aMap, "form", "XResetListener", "approveReset", APPROVERESETTED);
+ DESCRIBE_EVENT(aMap, "form", "XResetListener", "resetted", RESETTED);
+ DESCRIBE_EVENT(aMap, "form", "XSubmitListener", "approveSubmit", SUBMITTED);
+ DESCRIBE_EVENT(aMap, "form", "XUpdateListener", "approveUpdate", BEFOREUPDATE);
+ DESCRIBE_EVENT(aMap, "form", "XUpdateListener", "updated", AFTERUPDATE);
+ DESCRIBE_EVENT(aMap, "form", "XLoadListener", "loaded", LOADED);
+ DESCRIBE_EVENT(aMap, "form", "XLoadListener", "reloading", RELOADING);
+ DESCRIBE_EVENT(aMap, "form", "XLoadListener", "reloaded", RELOADED);
+ DESCRIBE_EVENT(aMap, "form", "XLoadListener", "unloading", UNLOADING);
+ DESCRIBE_EVENT(aMap, "form", "XLoadListener", "unloaded", UNLOADED);
+ DESCRIBE_EVENT(aMap, "form", "XConfirmDeleteListener", "confirmDelete", CONFIRMDELETE);
+ DESCRIBE_EVENT(aMap, "sdb", "XRowSetApproveListener", "approveRowChange", APPROVEROWCHANGE);
+ DESCRIBE_EVENT(aMap, "sdbc", "XRowSetListener", "rowChanged", ROWCHANGE);
+ DESCRIBE_EVENT(aMap, "sdb", "XRowSetApproveListener", "approveCursorMove", POSITIONING);
+ DESCRIBE_EVENT(aMap, "sdbc", "XRowSetListener", "cursorMoved", POSITIONED);
+ DESCRIBE_EVENT(aMap, "form", "XDatabaseParameterListener", "approveParameter", APPROVEPARAMETER);
+ DESCRIBE_EVENT(aMap, "sdb", "XSQLErrorListener", "errorOccured", ERROROCCURRED);
+ DESCRIBE_EVENT(aMap, "awt", "XAdjustmentListener", "adjustmentValueChanged", ADJUSTMENTVALUECHANGED);
+
+ return aMap;
+ }();
+
+ EventMap::const_iterator pos = s_aKnownEvents.find( _rMethodName );
+ if ( pos == s_aKnownEvents.end() )
+ return false;
+
+ _out_rDescription = pos->second;
+ return true;
+ }
+
+ OUString lcl_getEventPropertyName( std::u16string_view _rListenerClassName, std::u16string_view _rMethodName )
+ {
+ return _rListenerClassName + OUStringChar(';') + _rMethodName;
+ }
+
+ ScriptEventDescriptor lcl_getAssignedScriptEvent( const EventDescription& _rEvent, const std::vector< ScriptEventDescriptor >& _rAllAssignedMacros )
+ {
+ ScriptEventDescriptor aScriptEvent;
+ // for the case there is actually no event assigned, initialize at least ListenerType and MethodName,
+ // so this ScriptEventDescriptor properly describes the given event
+ aScriptEvent.ListenerType = _rEvent.sListenerClassName;
+ aScriptEvent.EventMethod = _rEvent.sListenerMethodName;
+
+ for ( const ScriptEventDescriptor& rSED : _rAllAssignedMacros )
+ {
+ if ( rSED.ListenerType != _rEvent.sListenerClassName
+ || rSED.EventMethod != _rEvent.sListenerMethodName
+ )
+ continue;
+
+ if ( rSED.ScriptCode.isEmpty()
+ || rSED.ScriptType.isEmpty()
+ )
+ {
+ OSL_FAIL( "lcl_getAssignedScriptEvent: me thinks this should not happen!" );
+ continue;
+ }
+
+ aScriptEvent = rSED;
+
+ if ( aScriptEvent.ScriptType != "StarBasic" )
+ continue;
+
+ // this is an old-style macro specification:
+ // [document|application]:Library.Module.Function
+ // we need to translate this to the new-style macro specification
+ // vnd.sun.star.script:Library.Module.Function?language=Basic&location=[document|application]
+
+ sal_Int32 nPrefixLen = aScriptEvent.ScriptCode.indexOf( ':' );
+ OSL_ENSURE( nPrefixLen > 0, "lcl_getAssignedScriptEvent: illegal location!" );
+ std::u16string_view sLocation = aScriptEvent.ScriptCode.subView( 0, nPrefixLen );
+ std::u16string_view sMacroPath = aScriptEvent.ScriptCode.subView( nPrefixLen + 1 );
+
+ aScriptEvent.ScriptCode =
+ OUString::Concat("vnd.sun.star.script:") +
+ sMacroPath +
+ "?language=Basic&location=" +
+ sLocation;
+
+ // also, this new-style spec requires the script code to be "Script" instead of "StarBasic"
+ aScriptEvent.ScriptType = "Script";
+ }
+ return aScriptEvent;
+ }
+
+ OUString lcl_getQualifiedKnownListenerName( const ScriptEventDescriptor& _rFormComponentEventDescriptor )
+ {
+ EventDescription aKnownEvent;
+ if ( lcl_getEventDescriptionForMethod( _rFormComponentEventDescriptor.EventMethod, aKnownEvent ) )
+ return aKnownEvent.sListenerClassName;
+ OSL_FAIL( "lcl_getQualifiedKnownListenerName: unknown method name!" );
+ // somebody assigned an script to a form component event which we don't know
+ // Speaking strictly, this is not really an error - it is possible to do
+ // this programmatically -, but it should rarely happen, since it's not possible
+ // via UI
+ return _rFormComponentEventDescriptor.ListenerType;
+ }
+
+ typedef std::set< Type, TypeLessByName > TypeBag;
+
+ void lcl_addListenerTypesFor_throw( const Reference< XInterface >& _rxComponent,
+ const Reference< XIntrospection >& _rxIntrospection, TypeBag& _out_rTypes )
+ {
+ if ( !_rxComponent.is() )
+ return;
+ OSL_PRECOND( _rxIntrospection.is(), "lcl_addListenerTypesFor_throw: this will crash!" );
+
+ Reference< XIntrospectionAccess > xIntrospectionAccess(
+ _rxIntrospection->inspect( Any( _rxComponent ) ), UNO_SET_THROW );
+
+ const Sequence< Type > aListeners( xIntrospectionAccess->getSupportedListeners() );
+
+ std::copy( aListeners.begin(), aListeners.end(),
+ std::insert_iterator< TypeBag >( _out_rTypes, _out_rTypes.begin() ) );
+ }
+ }
+
+ typedef ::cppu::WeakImplHelper < css::container::XNameReplace
+ > EventHolder_Base;
+
+ namespace {
+
+ /* A UNO component holding assigned event descriptions, for use with a SvxMacroAssignDlg */
+ class EventHolder : public EventHolder_Base
+ {
+ private:
+ typedef std::unordered_map< OUString, ScriptEventDescriptor > EventMap;
+ typedef std::map< EventId, OUString > EventMapIndexAccess;
+
+ EventMap m_aEventNameAccess;
+ EventMapIndexAccess m_aEventIndexAccess;
+
+ public:
+ EventHolder( );
+
+ void addEvent( EventId _nId, const OUString& _rEventName, const ScriptEventDescriptor& _rScriptEvent );
+
+ /** effectively the same as getByName, but instead of converting the ScriptEventDescriptor to the weird
+ format used by the macro assignment dialog, it is returned directly
+ */
+ ScriptEventDescriptor getNormalizedDescriptorByName( const OUString& _rEventName ) const;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& _rName, const Any& aElement ) override;
+ virtual Any SAL_CALL getByName( const OUString& _rName ) override;
+ virtual Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& _rName ) override;
+ virtual Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+
+ protected:
+ virtual ~EventHolder( ) override;
+
+ private:
+ ScriptEventDescriptor const & impl_getDescriptor_throw( const OUString& _rEventName ) const;
+ };
+
+ }
+
+ EventHolder::EventHolder()
+ {
+ }
+
+ EventHolder::~EventHolder()
+ {
+ m_aEventNameAccess.clear();
+ m_aEventIndexAccess.clear();
+ }
+
+ void EventHolder::addEvent( EventId _nId, const OUString& _rEventName, const ScriptEventDescriptor& _rScriptEvent )
+ {
+ std::pair< EventMap::iterator, bool > insertionResult =
+ m_aEventNameAccess.emplace( _rEventName, _rScriptEvent );
+ OSL_ENSURE( insertionResult.second, "EventHolder::addEvent: there already was a MacroURL for this event!" );
+ m_aEventIndexAccess[ _nId ] = _rEventName;
+ }
+
+ ScriptEventDescriptor EventHolder::getNormalizedDescriptorByName( const OUString& _rEventName ) const
+ {
+ return impl_getDescriptor_throw( _rEventName );
+ }
+
+ ScriptEventDescriptor const & EventHolder::impl_getDescriptor_throw( const OUString& _rEventName ) const
+ {
+ EventMap::const_iterator pos = m_aEventNameAccess.find( _rEventName );
+ if ( pos == m_aEventNameAccess.end() )
+ throw NoSuchElementException( OUString(), *const_cast< EventHolder* >( this ) );
+ return pos->second;
+ }
+
+ void SAL_CALL EventHolder::replaceByName( const OUString& _rName, const Any& _rElement )
+ {
+ EventMap::iterator pos = m_aEventNameAccess.find( _rName );
+ if ( pos == m_aEventNameAccess.end() )
+ throw NoSuchElementException( OUString(), *this );
+
+ Sequence< PropertyValue > aScriptDescriptor;
+ OSL_VERIFY( _rElement >>= aScriptDescriptor );
+
+ ::comphelper::NamedValueCollection aExtractor( aScriptDescriptor );
+
+ pos->second.ScriptType = aExtractor.getOrDefault( "EventType", OUString() );
+ pos->second.ScriptCode = aExtractor.getOrDefault( "Script", OUString() );
+ }
+
+ Any SAL_CALL EventHolder::getByName( const OUString& _rName )
+ {
+ ScriptEventDescriptor aDescriptor( impl_getDescriptor_throw( _rName ) );
+
+ Sequence< PropertyValue > aScriptDescriptor{
+ comphelper::makePropertyValue("EventType", aDescriptor.ScriptType),
+ comphelper::makePropertyValue("Script", aDescriptor.ScriptCode)
+ };
+
+ return Any( aScriptDescriptor );
+ }
+
+ Sequence< OUString > SAL_CALL EventHolder::getElementNames( )
+ {
+ Sequence< OUString > aReturn( m_aEventIndexAccess.size() );
+ OUString* pReturn = aReturn.getArray();
+
+ // SvxMacroAssignDlg has a weird API: It expects a XNameReplace, means a container whose
+ // main access method is by name. In its UI, it shows the possible events in exactly the
+ // order in which XNameAccess::getElementNames returns them.
+ // However, SvxMacroAssignDlg *also* takes an index for the initial selection, which is
+ // relative to the sequence returned by XNameAccess::getElementNames.
+ // This is IMO weird, since it mixes index access with name access, which decreases efficiency
+ // of the implementation.
+ // Well, it means we're forced to return the events in getElementNames in exactly the same as they
+ // appear in the property browser UI.
+ for (auto const& elem : m_aEventIndexAccess)
+ {
+ *pReturn = elem.second;
+ ++pReturn;
+ }
+ return aReturn;
+ }
+
+ sal_Bool SAL_CALL EventHolder::hasByName( const OUString& _rName )
+ {
+ EventMap::const_iterator pos = m_aEventNameAccess.find( _rName );
+ return pos != m_aEventNameAccess.end();
+ }
+
+ Type SAL_CALL EventHolder::getElementType( )
+ {
+ return cppu::UnoType<Sequence< PropertyValue >>::get();
+ }
+
+ sal_Bool SAL_CALL EventHolder::hasElements( )
+ {
+ return !m_aEventNameAccess.empty();
+ }
+
+
+ EventHandler::EventHandler( const Reference< XComponentContext >& _rxContext )
+ :EventHandler_Base( m_aMutex )
+ ,m_xContext( _rxContext )
+ ,m_aPropertyListeners( m_aMutex )
+ ,m_bEventsMapInitialized( false )
+ ,m_bIsDialogElement( false )
+ ,m_nGridColumnType( -1 )
+ {
+ }
+
+ EventHandler::~EventHandler()
+ {
+ }
+
+ OUString SAL_CALL EventHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.EventHandler";
+ }
+
+ sal_Bool SAL_CALL EventHandler::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL EventHandler::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.inspection.EventHandler" };
+ }
+
+ void SAL_CALL EventHandler::inspect( const Reference< XInterface >& _rxIntrospectee )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !_rxIntrospectee.is() )
+ throw NullPointerException();
+
+ m_xComponent.set( _rxIntrospectee, UNO_QUERY_THROW );
+
+ m_bEventsMapInitialized = false;
+ EventMap().swap(m_aEvents);
+
+ m_bIsDialogElement = false;
+ m_nGridColumnType = -1;
+ try
+ {
+ Reference< XPropertySetInfo > xPSI( m_xComponent->getPropertySetInfo() );
+ m_bIsDialogElement = xPSI.is()
+ && xPSI->hasPropertyByName( PROPERTY_WIDTH )
+ && xPSI->hasPropertyByName( PROPERTY_HEIGHT )
+ && xPSI->hasPropertyByName( PROPERTY_POSITIONX )
+ && xPSI->hasPropertyByName( PROPERTY_POSITIONY );
+
+ Reference< XChild > xAsChild( _rxIntrospectee, UNO_QUERY );
+ if ( xAsChild.is() && !Reference< XForm >( _rxIntrospectee, UNO_QUERY ).is() )
+ {
+ if ( FormComponentType::GRIDCONTROL == classifyComponent( xAsChild->getParent() ) )
+ {
+ m_nGridColumnType = classifyComponent( _rxIntrospectee );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ Any SAL_CALL EventHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName );
+
+ std::vector< ScriptEventDescriptor > aEvents;
+ impl_getComponentScriptEvents_nothrow( aEvents );
+
+ ScriptEventDescriptor aPropertyValue;
+ for ( const ScriptEventDescriptor& rSCD : aEvents )
+ {
+ if ( rEvent.sListenerClassName == rSCD.ListenerType
+ && rEvent.sListenerMethodName == rSCD.EventMethod
+ )
+ {
+ aPropertyValue = rSCD;
+ break;
+ }
+ }
+
+ return Any( aPropertyValue );
+ }
+
+ void SAL_CALL EventHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName );
+
+ ScriptEventDescriptor aNewScriptEvent;
+ OSL_VERIFY( _rValue >>= aNewScriptEvent );
+
+ ScriptEventDescriptor aOldScriptEvent;
+ OSL_VERIFY( getPropertyValue( _rPropertyName ) >>= aOldScriptEvent );
+ if ( aOldScriptEvent == aNewScriptEvent )
+ return;
+
+ if ( m_bIsDialogElement )
+ impl_setDialogElementScriptEvent_nothrow( aNewScriptEvent );
+ else
+ impl_setFormComponentScriptEvent_nothrow( aNewScriptEvent );
+
+ PropertyHandlerHelper::setContextDocumentModified( m_xContext );
+
+ PropertyChangeEvent aEvent;
+ aEvent.Source = m_xComponent;
+ aEvent.PropertyHandle = rEvent.nId;
+ aEvent.PropertyName = _rPropertyName;
+ aEvent.OldValue <<= aOldScriptEvent;
+ aEvent.NewValue <<= aNewScriptEvent;
+ m_aPropertyListeners.notifyEach( &XPropertyChangeListener::propertyChange, aEvent );
+ }
+
+ Any SAL_CALL EventHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OUString sNewScriptCode;
+ OSL_VERIFY( _rControlValue >>= sNewScriptCode );
+
+ std::vector< ScriptEventDescriptor > aAllAssignedEvents;
+ impl_getComponentScriptEvents_nothrow( aAllAssignedEvents );
+
+ const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName );
+ ScriptEventDescriptor aAssignedScript = lcl_getAssignedScriptEvent( rEvent, aAllAssignedEvents );
+
+ OSL_ENSURE( sNewScriptCode.isEmpty(), "EventHandler::convertToPropertyValue: cannot convert a non-empty display name!" );
+ // Usually, there is no possibility for the user to change the content of an event binding directly in the
+ // input field, this instead is done with the macro assignment dialog.
+ // The only exception is the user pressing "DEL" while the control has the focus, in this case, we reset the
+ // control content to an empty string. So this is the only scenario where this method is allowed to be called.
+
+ // Strictly, we would be able to convert the display value to a property value,
+ // using the "name (location, language)" format we used in convertToControlValue. However,
+ // there is no need for this code...
+
+ aAssignedScript.ScriptCode = sNewScriptCode;
+ return Any( aAssignedScript );
+ }
+
+ Any SAL_CALL EventHandler::convertToControlValue( const OUString& /*_rPropertyName*/, const Any& _rPropertyValue, const Type& _rControlValueType )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ ScriptEventDescriptor aScriptEvent;
+ OSL_VERIFY( _rPropertyValue >>= aScriptEvent );
+
+ OSL_ENSURE( _rControlValueType.getTypeClass() == TypeClass_STRING,
+ "EventHandler::convertToControlValue: unexpected ControlValue type class!" );
+
+ OUString sScript( aScriptEvent.ScriptCode );
+ if ( !sScript.isEmpty() )
+ {
+ // format is: "name (location, language)"
+ try
+ {
+ // parse
+ Reference< XUriReferenceFactory > xUriRefFac = UriReferenceFactory::create( m_xContext );
+ Reference< XVndSunStarScriptUrlReference > xScriptUri( xUriRefFac->parse( sScript ), UNO_QUERY_THROW );
+
+ OUStringBuffer aComposeBuffer;
+
+ // name
+ aComposeBuffer.append( xScriptUri->getName() );
+
+ // location
+ const OUString sLocation = xScriptUri->getParameter( "location" );
+ const OUString sLanguage = xScriptUri->getParameter( "language" );
+
+ if ( !(sLocation.isEmpty() && sLanguage.isEmpty()) )
+ {
+ aComposeBuffer.append( " (" );
+
+ // location
+ OSL_ENSURE( !sLocation.isEmpty(), "EventHandler::convertToControlValue: unexpected: no location!" );
+ if ( !sLocation.isEmpty() )
+ {
+ aComposeBuffer.append( sLocation );
+ aComposeBuffer.append( ", " );
+ }
+
+ // language
+ if ( !sLanguage.isEmpty() )
+ {
+ aComposeBuffer.append( sLanguage );
+ }
+
+ aComposeBuffer.append( ')' );
+ }
+
+ sScript = aComposeBuffer.makeStringAndClear();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ return Any( sScript );
+ }
+
+ PropertyState SAL_CALL EventHandler::getPropertyState( const OUString& /*_rPropertyName*/ )
+ {
+ return PropertyState_DIRECT_VALUE;
+ }
+
+ void SAL_CALL EventHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !_rxListener.is() )
+ throw NullPointerException();
+ m_aPropertyListeners.addInterface( _rxListener );
+ }
+
+ void SAL_CALL EventHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_aPropertyListeners.removeInterface( _rxListener );
+ }
+
+ Sequence< Property > SAL_CALL EventHandler::getSupportedProperties()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_bEventsMapInitialized )
+ {
+ m_bEventsMapInitialized = true;
+ try
+ {
+ std::vector< Type > aListeners;
+ impl_getComponentListenerTypes_nothrow( aListeners );
+
+ OUString sListenerClassName;
+
+ // loop through all listeners and all methods, and see which we can present at the UI
+ for ( const Type& rListener : aListeners )
+ {
+ // the programmatic name of the listener, to be used as "property" name
+ sListenerClassName = rListener.getTypeName();
+ OSL_ENSURE( !sListenerClassName.isEmpty(), "EventHandler::getSupportedProperties: strange - no listener name ..." );
+ if ( sListenerClassName.isEmpty() )
+ continue;
+
+ // loop through all methods
+ const Sequence<OUString> aEventMethods = comphelper::getEventMethodsForType( rListener );
+ for (const OUString& rMethod : aEventMethods)
+ {
+ EventDescription aEvent;
+ if ( !lcl_getEventDescriptionForMethod( rMethod, aEvent ) )
+ continue;
+
+ if ( !impl_filterMethod_nothrow( aEvent ) )
+ continue;
+
+ m_aEvents.emplace(
+ lcl_getEventPropertyName( sListenerClassName, rMethod ), aEvent );
+ }
+ }
+
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ // sort them by ID - this is the relative ordering in the UI
+ std::map< EventId, Property > aOrderedProperties;
+ for (auto const& event : m_aEvents)
+ {
+ aOrderedProperties[ event.second.nId ] = Property(
+ event.first, event.second.nId,
+ ::cppu::UnoType<OUString>::get(),
+ PropertyAttribute::BOUND );
+ }
+
+ return comphelper::mapValuesToSequence( aOrderedProperties );
+ }
+
+ Sequence< OUString > SAL_CALL EventHandler::getSupersededProperties( )
+ {
+ // none
+ return Sequence< OUString >( );
+ }
+
+ Sequence< OUString > SAL_CALL EventHandler::getActuatingProperties( )
+ {
+ // none
+ return Sequence< OUString >( );
+ }
+
+ LineDescriptor SAL_CALL EventHandler::describePropertyLine( const OUString& _rPropertyName,
+ const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ if ( !_rxControlFactory.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ LineDescriptor aDescriptor;
+
+ aDescriptor.Control = _rxControlFactory->createPropertyControl( PropertyControlType::TextField, true );
+ new PropertyControlExtender( aDescriptor.Control );
+
+ const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName );
+ aDescriptor.DisplayName = rEvent.sDisplayName;
+ aDescriptor.HelpURL = HelpIdUrl::getHelpURL( rEvent.sHelpId );
+ aDescriptor.PrimaryButtonId = OStringToOUString(rEvent.sUniqueBrowseId, RTL_TEXTENCODING_UTF8);
+ aDescriptor.HasPrimaryButton = true;
+ aDescriptor.Category = "Events";
+ return aDescriptor;
+ }
+
+ sal_Bool SAL_CALL EventHandler::isComposable( const OUString& /*_rPropertyName*/ )
+ {
+ return false;
+ }
+
+ InteractiveSelectionResult SAL_CALL EventHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool /*_bPrimary*/, Any& /*_rData*/, const Reference< XObjectInspectorUI >& _rxInspectorUI )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ const EventDescription& rForEvent = impl_getEventForName_throw( _rPropertyName );
+
+ std::vector< ScriptEventDescriptor > aAllAssignedEvents;
+ impl_getComponentScriptEvents_nothrow( aAllAssignedEvents );
+
+ // SvxMacroAssignDlg-compatible structure holding all event/assignments
+ ::rtl::Reference< EventHolder > pEventHolder( new EventHolder );
+
+ for (auto const& event : m_aEvents)
+ {
+ // the script which is assigned to the current event (if any)
+ ScriptEventDescriptor aAssignedScript = lcl_getAssignedScriptEvent( event.second, aAllAssignedEvents );
+ pEventHolder->addEvent( event.second.nId, event.second.sListenerMethodName, aAssignedScript );
+ }
+
+ // the initial selection in the dialog
+ const Sequence< OUString > aNames( pEventHolder->getElementNames() );
+ const OUString* pChosenEvent = std::find( aNames.begin(), aNames.end(), rForEvent.sListenerMethodName );
+ sal_uInt16 nInitialSelection = static_cast<sal_uInt16>( pChosenEvent - aNames.begin() );
+
+ // the dialog
+ SvxAbstractDialogFactory* pFactory = SvxAbstractDialogFactory::Create();
+
+ ScopedVclPtr<VclAbstractDialog> pDialog( pFactory->CreateSvxMacroAssignDlg(
+ PropertyHandlerHelper::getDialogParentFrame( m_xContext ),
+ impl_getContextFrame_nothrow(),
+ m_bIsDialogElement,
+ pEventHolder,
+ nInitialSelection
+ ) );
+
+ if ( !pDialog )
+ return InteractiveSelectionResult_Cancelled;
+
+ // DF definite problem here
+ // OK & Cancel seem to be both returning 0
+ if ( pDialog->Execute() == RET_CANCEL )
+ return InteractiveSelectionResult_Cancelled;
+
+ try
+ {
+ for (auto const& event : m_aEvents)
+ {
+ ScriptEventDescriptor aScriptDescriptor( pEventHolder->getNormalizedDescriptorByName( event.second.sListenerMethodName ) );
+
+ // set the new "property value"
+ setPropertyValue(
+ lcl_getEventPropertyName( event.second.sListenerClassName, event.second.sListenerMethodName ),
+ Any( aScriptDescriptor )
+ );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ return InteractiveSelectionResult_Success;
+ }
+
+ void SAL_CALL EventHandler::actuatingPropertyChanged( const OUString& /*_rActuatingPropertyName*/, const Any& /*_rNewValue*/, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/, sal_Bool /*_bFirstTimeInit*/ )
+ {
+ OSL_FAIL( "EventHandler::actuatingPropertyChanged: no actuating properties -> no callback (well, this is how it *should* be!)" );
+ }
+
+ IMPLEMENT_FORWARD_XCOMPONENT( EventHandler, EventHandler_Base )
+
+ void SAL_CALL EventHandler::disposing()
+ {
+ EventMap().swap(m_aEvents);
+ m_xComponent.clear();
+ }
+
+ sal_Bool SAL_CALL EventHandler::suspend( sal_Bool /*_bSuspend*/ )
+ {
+ return true;
+ }
+
+ Reference< XFrame > EventHandler::impl_getContextFrame_nothrow() const
+ {
+ Reference< XFrame > xContextFrame;
+
+ try
+ {
+ Reference< XModel > xContextDocument( PropertyHandlerHelper::getContextDocument(m_xContext), UNO_QUERY_THROW );
+ Reference< XController > xController( xContextDocument->getCurrentController(), UNO_SET_THROW );
+ xContextFrame.set( xController->getFrame(), UNO_SET_THROW );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ return xContextFrame;
+ }
+
+ sal_Int32 EventHandler::impl_getComponentIndexInParent_throw() const
+ {
+ Reference< XChild > xChild( m_xComponent, UNO_QUERY_THROW );
+ Reference< XIndexAccess > xParentAsIndexAccess( xChild->getParent(), UNO_QUERY_THROW );
+
+ // get the index of the inspected object within its parent container
+ sal_Int32 nElements = xParentAsIndexAccess->getCount();
+ for ( sal_Int32 i=0; i<nElements; ++i )
+ {
+ Reference< XInterface > xElement( xParentAsIndexAccess->getByIndex( i ), UNO_QUERY_THROW );
+ if ( xElement == m_xComponent )
+ return i;
+ }
+ throw NoSuchElementException();
+ }
+
+ void EventHandler::impl_getFormComponentScriptEvents_nothrow( std::vector < ScriptEventDescriptor >& _out_rEvents ) const
+ {
+ _out_rEvents.clear();
+ try
+ {
+ Reference< XChild > xChild( m_xComponent, UNO_QUERY_THROW );
+ Reference< XEventAttacherManager > xEventManager( xChild->getParent(), UNO_QUERY_THROW );
+ comphelper::sequenceToContainer(_out_rEvents, xEventManager->getScriptEvents( impl_getComponentIndexInParent_throw() ));
+
+ // the form component script API has unqualified listener names, but for normalization
+ // purpose, we want fully qualified ones
+ for ( ScriptEventDescriptor& rSED : _out_rEvents)
+ {
+ rSED.ListenerType = lcl_getQualifiedKnownListenerName( rSED );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ void EventHandler::impl_getComponentListenerTypes_nothrow( std::vector< Type >& _out_rTypes ) const
+ {
+ _out_rTypes.clear();
+ try
+ {
+ // we use a set to avoid duplicates
+ TypeBag aListeners;
+
+ Reference< XIntrospection > xIntrospection = theIntrospection::get( m_xContext );
+
+ // --- model listeners
+ lcl_addListenerTypesFor_throw(
+ m_xComponent, xIntrospection, aListeners );
+
+ // --- "secondary component" (usually: "control" listeners)
+ {
+ Reference< XInterface > xSecondaryComponent( impl_getSecondaryComponentForEventInspection_throw() );
+ lcl_addListenerTypesFor_throw( xSecondaryComponent, xIntrospection, aListeners );
+ ::comphelper::disposeComponent( xSecondaryComponent );
+ }
+
+ // now that they're disambiguated, copy these types into our member
+ _out_rTypes.insert( _out_rTypes.end(), aListeners.begin(), aListeners.end() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ void EventHandler::impl_getDialogElementScriptEvents_nothrow( std::vector < ScriptEventDescriptor >& _out_rEvents ) const
+ {
+ _out_rEvents.clear();
+ try
+ {
+ Reference< XScriptEventsSupplier > xEventsSupplier( m_xComponent, UNO_QUERY_THROW );
+ Reference< XNameContainer > xEvents( xEventsSupplier->getEvents(), UNO_SET_THROW );
+ Sequence< OUString > aEventNames( xEvents->getElementNames() );
+
+ sal_Int32 nEventCount = aEventNames.getLength();
+ _out_rEvents.resize( nEventCount );
+
+ for( sal_Int32 i = 0; i < nEventCount; ++i )
+ OSL_VERIFY( xEvents->getByName( aEventNames[i] ) >>= _out_rEvents[i] );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ Reference< XInterface > EventHandler::impl_getSecondaryComponentForEventInspection_throw( ) const
+ {
+ Reference< XInterface > xReturn;
+
+ // if it's a form, create a form controller for the additional events
+ Reference< XForm > xComponentAsForm( m_xComponent, UNO_QUERY );
+ if ( xComponentAsForm.is() )
+ {
+ Reference< XTabControllerModel > xComponentAsTCModel( m_xComponent, UNO_QUERY_THROW );
+ Reference< XFormController > xController = FormController::create( m_xContext );
+ xController->setModel( xComponentAsTCModel );
+
+ xReturn = xController;
+ }
+ else
+ {
+ OUString sControlService;
+ OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_DEFAULTCONTROL ) >>= sControlService );
+
+ xReturn = m_xContext->getServiceManager()->createInstanceWithContext( sControlService, m_xContext );
+ }
+ return xReturn;
+ }
+
+ const EventDescription& EventHandler::impl_getEventForName_throw( const OUString& _rPropertyName ) const
+ {
+ EventMap::const_iterator pos = m_aEvents.find( _rPropertyName );
+ if ( pos == m_aEvents.end() )
+ throw UnknownPropertyException(_rPropertyName);
+ return pos->second;
+ }
+
+ namespace
+ {
+ bool lcl_endsWith( const OUString& _rText, const OUString& _rCheck )
+ {
+ sal_Int32 nTextLen = _rText.getLength();
+ sal_Int32 nCheckLen = _rCheck.getLength();
+ if ( nCheckLen > nTextLen )
+ return false;
+
+ return _rText.indexOf( _rCheck ) == ( nTextLen - nCheckLen );
+ }
+ }
+
+ void EventHandler::impl_setFormComponentScriptEvent_nothrow( const ScriptEventDescriptor& _rScriptEvent )
+ {
+ try
+ {
+ OUString sScriptCode( _rScriptEvent.ScriptCode );
+ OUString sScriptType( _rScriptEvent.ScriptType );
+ bool bResetScript = sScriptCode.isEmpty();
+
+ sal_Int32 nObjectIndex = impl_getComponentIndexInParent_throw();
+ Reference< XChild > xChild( m_xComponent, UNO_QUERY_THROW );
+ Reference< XEventAttacherManager > xEventManager( xChild->getParent(), UNO_QUERY_THROW );
+ std::vector< ScriptEventDescriptor > aEvents;
+ comphelper::sequenceToContainer( aEvents, xEventManager->getScriptEvents( nObjectIndex ) );
+
+ // is there already a registered script for this event?
+ sal_Int32 eventCount = aEvents.size(), event = 0;
+ for ( event = 0; event < eventCount; ++event )
+ {
+ ScriptEventDescriptor* pEvent = &aEvents[event];
+ if ( ( pEvent->EventMethod == _rScriptEvent.EventMethod )
+ && ( lcl_endsWith( _rScriptEvent.ListenerType, pEvent->ListenerType ) )
+ // (strange enough, the events we get from getScriptEvents are not fully qualified)
+ )
+ {
+ // yes
+ if ( !bResetScript )
+ {
+ // set to something non-empty -> overwrite
+ pEvent->ScriptCode = sScriptCode;
+ pEvent->ScriptType = sScriptType;
+ }
+ else
+ {
+ // set to empty -> remove from vector
+ aEvents.erase(aEvents.begin() + event );
+ --eventCount;
+ }
+ break;
+ }
+ }
+ if ( ( event >= eventCount ) && !bResetScript )
+ {
+ // no, did not find it -> append
+ aEvents.push_back( _rScriptEvent );
+ }
+
+ xEventManager->revokeScriptEvents( nObjectIndex );
+ xEventManager->registerScriptEvents( nObjectIndex, comphelper::containerToSequence(aEvents) );
+
+ PropertyHandlerHelper::setContextDocumentModified( m_xContext );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ void EventHandler::impl_setDialogElementScriptEvent_nothrow( const ScriptEventDescriptor& _rScriptEvent )
+ {
+ try
+ {
+ OUString sScriptCode( _rScriptEvent.ScriptCode );
+ bool bResetScript = sScriptCode.isEmpty();
+
+ Reference< XScriptEventsSupplier > xEventsSupplier( m_xComponent, UNO_QUERY_THROW );
+ Reference< XNameContainer > xEvents( xEventsSupplier->getEvents(), UNO_SET_THROW );
+
+ OUString sCompleteName =
+ _rScriptEvent.ListenerType +
+ "::" +
+ _rScriptEvent.EventMethod;
+
+ bool bExists = xEvents->hasByName( sCompleteName );
+
+ if ( bResetScript )
+ {
+ if ( bExists )
+ xEvents->removeByName( sCompleteName );
+ }
+ else
+ {
+ Any aNewValue; aNewValue <<= _rScriptEvent;
+
+ if ( bExists )
+ xEvents->replaceByName( sCompleteName, aNewValue );
+ else
+ xEvents->insertByName( sCompleteName, aNewValue );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ bool EventHandler::impl_filterMethod_nothrow( const EventDescription& _rEvent ) const
+ {
+ // some (control-triggered) events do not make sense for certain grid control columns. However,
+ // our mechanism to retrieve control-triggered events does not know about this, so we do some
+ // late filtering here.
+ switch ( m_nGridColumnType )
+ {
+ case FormComponentType::COMBOBOX:
+ if ( UID_BRWEVT_ACTIONPERFORMED == _rEvent.sUniqueBrowseId )
+ return false;
+ break;
+ case FormComponentType::LISTBOX:
+ if ( ( UID_BRWEVT_CHANGED == _rEvent.sUniqueBrowseId )
+ || ( UID_BRWEVT_ACTIONPERFORMED == _rEvent.sUniqueBrowseId )
+ )
+ return false;
+ break;
+ }
+
+ return true;
+ }
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_EventHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::EventHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/eventhandler.hxx b/extensions/source/propctrlr/eventhandler.hxx
new file mode 100644
index 000000000..4506f1b06
--- /dev/null
+++ b/extensions/source/propctrlr/eventhandler.hxx
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "pcrcommon.hxx"
+
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/inspection/XPropertyHandler.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <unotools/resmgr.hxx>
+
+#include <unordered_map>
+
+namespace pcr
+{
+
+
+ //= EventDescription
+
+ typedef sal_Int32 EventId;
+ struct EventDescription
+ {
+ public:
+ OUString sDisplayName;
+ OUString sListenerClassName;
+ OUString sListenerMethodName;
+ OUString sHelpId;
+ OString sUniqueBrowseId;
+ EventId nId;
+
+ EventDescription()
+ :nId( 0 )
+ {
+ }
+
+ EventDescription(
+ EventId _nId,
+ const char* _pListenerNamespaceAscii,
+ const char* _pListenerClassAsciiName,
+ const char* _pListenerMethodAsciiName,
+ TranslateId pDisplayNameResId,
+ const OUString& _sHelpId,
+ const OString& _sUniqueBrowseId );
+ };
+
+ typedef std::unordered_map< OUString, EventDescription > EventMap;
+
+
+ //= EventHandler
+
+ typedef ::cppu::WeakComponentImplHelper < css::inspection::XPropertyHandler
+ , css::lang::XServiceInfo
+ > EventHandler_Base;
+ class EventHandler final : public EventHandler_Base
+ {
+ private:
+ mutable ::osl::Mutex m_aMutex;
+
+ /// the context in which the instance was created
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ /// the properties of the object we're handling
+ css::uno::Reference< css::beans::XPropertySet > m_xComponent;
+ /// our XPropertyChangeListener(s)
+ PropertyChangeListeners m_aPropertyListeners;
+ /// cache of the events we found at our introspectee
+ EventMap m_aEvents;
+ /// has m_aEvents been initialized?
+ bool m_bEventsMapInitialized;
+ /// is our introspectee a dialog element?
+ bool m_bIsDialogElement;
+ // TODO: move different handling into different derived classes?
+ /// (FormComponent) type of the grid column being inspected, or -1 if we're not inspecting a grid column
+ sal_Int16 m_nGridColumnType;
+
+ public:
+ explicit EventHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ virtual ~EventHandler() override;
+
+ private:
+ // XPropertyHandler overridables
+ virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override;
+ virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override;
+ virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual css::uno::Sequence< css::beans::Property > SAL_CALL getSupportedProperties() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupersededProperties( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override;
+ virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+ virtual sal_Bool SAL_CALL isComposable( const OUString& _rPropertyName ) override;
+ virtual css::inspection::InteractiveSelectionResult SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override;
+ virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override;
+
+ // XComponent
+ DECLARE_XCOMPONENT()
+ virtual void SAL_CALL disposing() 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;
+
+ /** returns the script events associated with our introspectee
+ @param _out_rEvents
+ Takes, upon successful return, the events currently associated with the introspectee
+ @precond
+ Our introspectee is a form component
+ */
+ void impl_getFormComponentScriptEvents_nothrow(
+ std::vector< css::script::ScriptEventDescriptor >& _out_rEvents
+ ) const;
+
+ /** returns the script events associated with our introspectee
+ @param _out_rEvents
+ Takes, upon successful return, the events currently associated with the introspectee
+ @precond
+ Our introspectee is a dialog element
+ */
+ void impl_getDialogElementScriptEvents_nothrow(
+ std::vector< css::script::ScriptEventDescriptor >& _out_rEvents
+ ) const;
+
+ /** returns the script events associated with our introspectee
+ @param _out_rEvents
+ Takes, the events currently associated with the introspectee
+ */
+ void impl_getComponentScriptEvents_nothrow(
+ std::vector< css::script::ScriptEventDescriptor >& _out_rEvents
+ ) const
+ {
+ if ( m_bIsDialogElement )
+ impl_getDialogElementScriptEvents_nothrow( _out_rEvents );
+ else
+ impl_getFormComponentScriptEvents_nothrow( _out_rEvents );
+ }
+
+ /** returns the types of the listeners which can be registered at our introspectee
+ @param _out_rTypes
+ Takes, upon successful return, the types of possible listeners at the introspectee
+ */
+ void impl_getComponentListenerTypes_nothrow(
+ std::vector< css::uno::Type >& _out_rTypes
+ ) const;
+
+ /** returns a secondary component to be used for event inspection
+
+ In the UI, we want to mix events for the control model with events for the control.
+ Since our introspectee is a model, this method creates a control for it (if possible).
+
+ @return
+ the secondary component whose events should be mixed with the introspectee's events
+ The caller takes the ownership of the component (if not <NULL/>).
+
+ @throws
+ if an unexpected error occurs during creation of the secondary component.
+ A <NULL/> component to be returned is not unexpected, but allowed
+
+ @precond
+ ->m_xComponent is not <NULL/>
+ */
+ css::uno::Reference< css::uno::XInterface >
+ impl_getSecondaryComponentForEventInspection_throw( ) const;
+
+ /** returns the event description for the given (programmatic) property name
+ @param _rPropertyName
+ the name whose event description should be looked up
+ @return
+ the event description for the property name
+ @throws css::beans::UnknownPropertyException
+ if our introspectee does not have an event with the given logical name (see ->getSupportedProperties)
+ */
+ const EventDescription&
+ impl_getEventForName_throw( const OUString& _rPropertyName ) const;
+
+ /** returns the index of our component within its parent, if this parent can be
+ obtained (XChild::getParent) and supports an ->XIndexAccess interface
+ */
+ sal_Int32 impl_getComponentIndexInParent_throw() const;
+
+ /** sets a given script event as event handler at a form component
+
+ @param _rScriptEvent
+ the script event to set
+ */
+ void impl_setFormComponentScriptEvent_nothrow( const css::script::ScriptEventDescriptor& _rScriptEvent );
+
+ /** sets a given script event as event handler at a dialog component
+
+ @param _rScriptEvent
+ the script event to set
+ */
+ void impl_setDialogElementScriptEvent_nothrow( const css::script::ScriptEventDescriptor& _rScriptEvent );
+
+ /** returns the frame associated with our context document
+ */
+ css::uno::Reference< css::frame::XFrame >
+ impl_getContextFrame_nothrow() const;
+
+ /** approves or denies a certain method to be included in the UI
+ @return
+ <TRUE/> if and only if the given method is allowed.
+ */
+ bool impl_filterMethod_nothrow( const EventDescription& _rEvent ) const;
+
+ EventHandler( const EventHandler& ) = delete;
+ EventHandler& operator=( const EventHandler& ) = delete;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/fontdialog.cxx b/extensions/source/propctrlr/fontdialog.cxx
new file mode 100644
index 000000000..133367953
--- /dev/null
+++ b/extensions/source/propctrlr/fontdialog.cxx
@@ -0,0 +1,572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "fontdialog.hxx"
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/unohelp.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/types.hxx>
+#include <comphelper/extract.hxx>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include "formstrings.hxx"
+#include "fontitemids.hxx"
+#include <editeng/charreliefitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/flstitem.hxx>
+#include <svtools/ctrltool.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <svx/svxids.hrc>
+#include <svx/svxdlg.hxx>
+#include <svx/dialogs.hrc>
+#include <svx/flagsdef.hxx>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+
+
+ //= OFontPropertyExtractor
+
+ namespace {
+
+ class OFontPropertyExtractor
+ {
+ protected:
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xPropValueAccess;
+ css::uno::Reference< css::beans::XPropertyState >
+ m_xPropStateAccess;
+
+ public:
+ explicit OFontPropertyExtractor( const css::uno::Reference< css::beans::XPropertySet >&
+ _rxProps );
+
+ public:
+ bool getCheckFontProperty(const OUString& _rPropName, css::uno::Any& _rValue);
+ OUString getStringFontProperty(const OUString& _rPropName, const OUString& _rDefault);
+ sal_Int16 getInt16FontProperty(const OUString& _rPropName, const sal_Int16 _nDefault);
+ sal_Int32 getInt32FontProperty(const OUString& _rPropName, const sal_Int32 _nDefault);
+ float getFloatFontProperty(const OUString& _rPropName, const float _nDefault);
+
+ void invalidateItem(
+ const OUString& _rPropName,
+ sal_uInt16 _nItemId,
+ SfxItemSet& _rSet,
+ bool _bForceInvalidation = false);
+ };
+
+ }
+
+ OFontPropertyExtractor::OFontPropertyExtractor(const Reference< XPropertySet >& _rxProps)
+ :m_xPropValueAccess(_rxProps)
+ ,m_xPropStateAccess(_rxProps, UNO_QUERY)
+ {
+ OSL_ENSURE(m_xPropValueAccess.is(), "OFontPropertyExtractor::OFontPropertyExtractor: invalid property set!");
+ }
+
+
+ bool OFontPropertyExtractor::getCheckFontProperty(const OUString& _rPropName, Any& _rValue)
+ {
+ _rValue = m_xPropValueAccess->getPropertyValue(_rPropName);
+ if (m_xPropStateAccess.is())
+ return PropertyState_DEFAULT_VALUE == m_xPropStateAccess->getPropertyState(_rPropName);
+
+ return false;
+ }
+
+
+ OUString OFontPropertyExtractor::getStringFontProperty(const OUString& _rPropName, const OUString& _rDefault)
+ {
+ Any aValue;
+ if (getCheckFontProperty(_rPropName, aValue))
+ return _rDefault;
+
+ return ::comphelper::getString(aValue);
+ }
+
+
+ sal_Int16 OFontPropertyExtractor::getInt16FontProperty(const OUString& _rPropName, const sal_Int16 _nDefault)
+ {
+ Any aValue;
+ if (getCheckFontProperty(_rPropName, aValue))
+ return _nDefault;
+
+ sal_Int32 nValue(_nDefault);
+ ::cppu::enum2int(nValue, aValue);
+ return static_cast<sal_Int16>(nValue);
+ }
+
+
+ sal_Int32 OFontPropertyExtractor::getInt32FontProperty(const OUString& _rPropName, const sal_Int32 _nDefault)
+ {
+ Any aValue;
+ if (getCheckFontProperty(_rPropName, aValue))
+ return _nDefault;
+
+ sal_Int32 nValue(_nDefault);
+ ::cppu::enum2int(nValue, aValue);
+ return nValue;
+ }
+
+
+ float OFontPropertyExtractor::getFloatFontProperty(const OUString& _rPropName, const float _nDefault)
+ {
+ Any aValue;
+ if (getCheckFontProperty(_rPropName, aValue))
+ return _nDefault;
+
+ return ::comphelper::getFloat(aValue);
+ }
+
+
+ void OFontPropertyExtractor::invalidateItem(const OUString& _rPropName, sal_uInt16 _nItemId, SfxItemSet& _rSet, bool _bForceInvalidation)
+ {
+ if ( _bForceInvalidation
+ || ( m_xPropStateAccess.is()
+ && (PropertyState_AMBIGUOUS_VALUE == m_xPropStateAccess->getPropertyState(_rPropName))
+ )
+ )
+ _rSet.InvalidateItem(_nItemId);
+ }
+
+ //= ControlCharacterDialog
+ ControlCharacterDialog::ControlCharacterDialog(weld::Window* pParent, const SfxItemSet& _rCoreSet)
+ : SfxTabDialogController(pParent, "modules/spropctrlr/ui/controlfontdialog.ui", "ControlFontDialog", &_rCoreSet)
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ AddTabPage("font", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_NAME), nullptr );
+ AddTabPage("fonteffects", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_EFFECTS), nullptr );
+ }
+
+ ControlCharacterDialog::~ControlCharacterDialog()
+ {
+ }
+
+ void ControlCharacterDialog::translatePropertiesToItems(const Reference< XPropertySet >& _rxModel, SfxItemSet* _pSet)
+ {
+ OSL_ENSURE(_pSet && _rxModel.is(), "ControlCharacterDialog::translatePropertiesToItems: invalid arguments!");
+ if (!_pSet || !_rxModel.is())
+ return;
+
+ try
+ {
+ OFontPropertyExtractor aPropExtractor(_rxModel);
+
+ // some items, which may be in default state, have to be filled with non-void information
+ vcl::Font aDefaultVCLFont = Application::GetDefaultDevice()->GetSettings().GetStyleSettings().GetAppFont();
+ css::awt::FontDescriptor aDefaultFont = VCLUnoHelper::CreateFontDescriptor(aDefaultVCLFont);
+
+ // get the current properties
+ OUString aFontName = aPropExtractor.getStringFontProperty(PROPERTY_FONT_NAME, aDefaultFont.Name);
+ OUString aFontStyleName = aPropExtractor.getStringFontProperty(PROPERTY_FONT_STYLENAME, aDefaultFont.StyleName);
+ sal_Int16 nFontFamily = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_FAMILY, aDefaultFont.Family);
+ sal_Int16 nFontCharset = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_CHARSET, aDefaultFont.CharSet);
+ float nFontHeight = aPropExtractor.getFloatFontProperty(PROPERTY_FONT_HEIGHT, static_cast<float>(aDefaultFont.Height));
+ float nFontWeight = aPropExtractor.getFloatFontProperty(PROPERTY_FONT_WEIGHT, aDefaultFont.Weight);
+ css::awt::FontSlant nFontSlant = static_cast<css::awt::FontSlant>(aPropExtractor.getInt16FontProperty(PROPERTY_FONT_SLANT, static_cast<sal_Int16>(aDefaultFont.Slant)));
+ sal_Int16 nFontLineStyle = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_UNDERLINE, aDefaultFont.Underline);
+ sal_Int16 nFontStrikeout = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_STRIKEOUT, aDefaultFont.Strikeout);
+
+ sal_Int32 nTextLineColor = aPropExtractor.getInt32FontProperty(PROPERTY_TEXTLINECOLOR, sal_uInt32(COL_AUTO));
+ sal_Int16 nFontRelief = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_RELIEF, static_cast<sal_Int16>(aDefaultVCLFont.GetRelief()));
+ sal_Int16 nFontEmphasisMark = aPropExtractor.getInt16FontProperty(PROPERTY_FONT_EMPHASIS_MARK, static_cast<sal_uInt16>(aDefaultVCLFont.GetEmphasisMark()));
+
+ Any aValue;
+ bool bWordLineMode = aPropExtractor.getCheckFontProperty(PROPERTY_WORDLINEMODE, aValue) ? aDefaultFont.WordLineMode : ::cppu::any2bool(aValue);
+ sal_Int32 nColor32 = aPropExtractor.getInt32FontProperty(PROPERTY_TEXTCOLOR, 0);
+
+ // build SfxItems with the values
+ SvxFontItem aFontItem(static_cast<FontFamily>(nFontFamily), aFontName, aFontStyleName, PITCH_DONTKNOW, nFontCharset, CFID_FONT);
+
+ nFontHeight = static_cast<float>(o3tl::convert(nFontHeight, o3tl::Length::pt, o3tl::Length::twip));
+
+ SvxFontHeightItem aSvxFontHeightItem(static_cast<sal_uInt32>(nFontHeight),100,CFID_HEIGHT);
+
+ FontWeight eWeight=vcl::unohelper::ConvertFontWeight(nFontWeight);
+ FontItalic eItalic=vcl::unohelper::ConvertFontSlant(nFontSlant);
+ FontLineStyle eUnderline=static_cast<FontLineStyle>(nFontLineStyle);
+ FontStrikeout eStrikeout=static_cast<FontStrikeout>(nFontStrikeout);
+
+ SvxWeightItem aWeightItem(eWeight,CFID_WEIGHT);
+ SvxPostureItem aPostureItem(eItalic,CFID_POSTURE);
+
+ SvxCrossedOutItem aCrossedOutItem(eStrikeout,CFID_STRIKEOUT);
+ SvxWordLineModeItem aWordLineModeItem(bWordLineMode, CFID_WORDLINEMODE);
+
+ SvxUnderlineItem aUnderlineItem(eUnderline,CFID_UNDERLINE);
+ aUnderlineItem.SetColor(Color(ColorTransparency, nTextLineColor));
+
+ SvxColorItem aSvxColorItem(Color(ColorTransparency, nColor32),CFID_CHARCOLOR);
+ SvxLanguageItem aLanguageItem(Application::GetSettings().GetUILanguageTag().getLanguageType(), CFID_LANGUAGE);
+
+ // the 2 CJK props
+ SvxCharReliefItem aFontReliefItem(static_cast<FontRelief>(nFontRelief), CFID_RELIEF);
+ SvxEmphasisMarkItem aEmphasisMarkitem(static_cast<FontEmphasisMark>(nFontEmphasisMark), CFID_EMPHASIS);
+
+ _pSet->Put(aFontItem);
+ _pSet->Put(aSvxFontHeightItem);
+ _pSet->Put(aWeightItem);
+ _pSet->Put(aPostureItem);
+ _pSet->Put(aLanguageItem);
+ _pSet->Put(aUnderlineItem);
+ _pSet->Put(aCrossedOutItem);
+ _pSet->Put(aWordLineModeItem);
+ _pSet->Put(aSvxColorItem);
+ _pSet->Put(aFontReliefItem);
+ _pSet->Put(aEmphasisMarkitem);
+
+ aPropExtractor.invalidateItem(PROPERTY_FONT_NAME, CFID_FONT, *_pSet);
+ aPropExtractor.invalidateItem(PROPERTY_FONT_HEIGHT, CFID_HEIGHT, *_pSet);
+ aPropExtractor.invalidateItem(PROPERTY_FONT_WEIGHT, CFID_WEIGHT, *_pSet, css::awt::FontWeight::DONTKNOW == nFontWeight);
+ aPropExtractor.invalidateItem(PROPERTY_FONT_SLANT, CFID_POSTURE, *_pSet, css::awt::FontSlant_DONTKNOW == nFontSlant);
+ aPropExtractor.invalidateItem(PROPERTY_FONT_UNDERLINE, CFID_UNDERLINE, *_pSet, css::awt::FontUnderline::DONTKNOW == nFontLineStyle);
+ aPropExtractor.invalidateItem(PROPERTY_FONT_STRIKEOUT, CFID_STRIKEOUT, *_pSet, css::awt::FontStrikeout::DONTKNOW == nFontStrikeout);
+ aPropExtractor.invalidateItem(PROPERTY_WORDLINEMODE, CFID_WORDLINEMODE, *_pSet);
+ aPropExtractor.invalidateItem(PROPERTY_TEXTCOLOR, CFID_CHARCOLOR, *_pSet);
+ aPropExtractor.invalidateItem(PROPERTY_FONT_RELIEF, CFID_RELIEF, *_pSet);
+ aPropExtractor.invalidateItem(PROPERTY_FONT_EMPHASIS_MARK, CFID_EMPHASIS, *_pSet);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "ControlCharacterDialog::translatePropertiesToItems");
+ }
+
+ _pSet->DisableItem(SID_ATTR_CHAR_CJK_FONT);
+ _pSet->DisableItem(SID_ATTR_CHAR_CJK_FONTHEIGHT);
+ _pSet->DisableItem(SID_ATTR_CHAR_CJK_LANGUAGE);
+ _pSet->DisableItem(SID_ATTR_CHAR_CJK_POSTURE);
+ _pSet->DisableItem(SID_ATTR_CHAR_CJK_WEIGHT);
+
+ _pSet->DisableItem(SID_ATTR_CHAR_CASEMAP);
+ _pSet->DisableItem(SID_ATTR_CHAR_CONTOUR);
+ _pSet->DisableItem(SID_ATTR_CHAR_SHADOWED);
+ }
+
+ namespace
+ {
+ void lcl_pushBackPropertyValue( std::vector< NamedValue >& _out_properties, const OUString& _name, const Any& _value )
+ {
+ _out_properties.push_back( NamedValue( _name, _value ) );
+ }
+ }
+
+ void ControlCharacterDialog::translateItemsToProperties( const SfxItemSet& _rSet, std::vector< NamedValue >& _out_properties )
+ {
+ _out_properties.clear();
+
+ try
+ {
+
+ // font name
+ SfxItemState eState = _rSet.GetItemState(CFID_FONT);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxFontItem& rFontItem =
+ static_cast<const SvxFontItem&>(_rSet.Get(CFID_FONT));
+
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_NAME , Any(rFontItem.GetFamilyName()));
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_STYLENAME, Any(rFontItem.GetStyleName()));
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_FAMILY , Any(static_cast<sal_Int16>(rFontItem.GetFamily())));
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_CHARSET , Any(static_cast<sal_Int16>(rFontItem.GetCharSet())));
+ }
+
+
+ // font height
+ eState = _rSet.GetItemState(CFID_HEIGHT);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxFontHeightItem& rSvxFontHeightItem =
+ static_cast<const SvxFontHeightItem&>(_rSet.Get(CFID_HEIGHT));
+
+ float nHeight = static_cast<float>(o3tl::convert(rSvxFontHeightItem.GetHeight(), o3tl::Length::twip, o3tl::Length::pt));
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_HEIGHT,Any(nHeight));
+
+ }
+
+
+ // font weight
+ eState = _rSet.GetItemState(CFID_WEIGHT);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxWeightItem& rWeightItem =
+ static_cast<const SvxWeightItem&>(_rSet.Get(CFID_WEIGHT));
+
+ float nWeight = vcl::unohelper::ConvertFontWeight(rWeightItem.GetWeight());
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_WEIGHT,Any(nWeight));
+ }
+
+
+ // font slant
+ eState = _rSet.GetItemState(CFID_POSTURE);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxPostureItem& rPostureItem =
+ static_cast<const SvxPostureItem&>(_rSet.Get(CFID_POSTURE));
+
+ css::awt::FontSlant eSlant = vcl::unohelper::ConvertFontSlant(rPostureItem.GetPosture());
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_SLANT, Any(static_cast<sal_Int16>(eSlant)));
+ }
+
+
+ // font underline
+ eState = _rSet.GetItemState(CFID_UNDERLINE);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxUnderlineItem& rUnderlineItem =
+ static_cast<const SvxUnderlineItem&>(_rSet.Get(CFID_UNDERLINE));
+
+ sal_Int16 nUnderline = static_cast<sal_Int16>(rUnderlineItem.GetLineStyle());
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_UNDERLINE,Any(nUnderline));
+
+ // the text line color is transported in this item, too
+ Color nColor = rUnderlineItem.GetColor();
+
+ Any aUnoColor;
+ if (COL_AUTO != nColor)
+ aUnoColor <<= nColor;
+
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_TEXTLINECOLOR, aUnoColor );
+ }
+
+
+ // font strikeout
+ eState = _rSet.GetItemState(CFID_STRIKEOUT);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxCrossedOutItem& rCrossedOutItem =
+ static_cast<const SvxCrossedOutItem&>(_rSet.Get(CFID_STRIKEOUT));
+
+ sal_Int16 nStrikeout = static_cast<sal_Int16>(rCrossedOutItem.GetStrikeout());
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_STRIKEOUT,Any(nStrikeout));
+ }
+
+
+ // font wordline mode
+ eState = _rSet.GetItemState(CFID_WORDLINEMODE);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxWordLineModeItem& rWordLineModeItem =
+ static_cast<const SvxWordLineModeItem&>(_rSet.Get(CFID_WORDLINEMODE));
+
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_WORDLINEMODE, css::uno::Any(rWordLineModeItem.GetValue()));
+ }
+
+
+ // text color
+ eState = _rSet.GetItemState(CFID_CHARCOLOR);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxColorItem& rColorItem =
+ static_cast<const SvxColorItem&>(_rSet.Get(CFID_CHARCOLOR));
+
+ Color nColor = rColorItem.GetValue();
+
+ Any aUnoColor;
+ if (COL_AUTO != nColor)
+ aUnoColor <<= nColor;
+
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_TEXTCOLOR, aUnoColor );
+ }
+
+
+ // font relief
+ eState = _rSet.GetItemState(CFID_RELIEF);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxCharReliefItem& rReliefItem =
+ static_cast<const SvxCharReliefItem&>(_rSet.Get(CFID_RELIEF));
+
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_RELIEF, Any(static_cast<sal_Int16>(rReliefItem.GetValue())) );
+ }
+
+
+ // font emphasis mark
+ eState = _rSet.GetItemState(CFID_EMPHASIS);
+
+ if ( eState == SfxItemState::SET )
+ {
+ const SvxEmphasisMarkItem& rEmphMarkItem =
+ static_cast<const SvxEmphasisMarkItem&>(_rSet.Get(CFID_EMPHASIS));
+
+ lcl_pushBackPropertyValue( _out_properties, PROPERTY_FONT_EMPHASIS_MARK, Any(static_cast<sal_Int16>(rEmphMarkItem.GetEmphasisMark())) );
+ }
+ }
+ catch (const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ void ControlCharacterDialog::translateItemsToProperties( const SfxItemSet& _rSet, const Reference< XPropertySet >& _rxModel)
+ {
+ OSL_ENSURE( _rxModel.is(), "ControlCharacterDialog::translateItemsToProperties: invalid arguments!" );
+ if ( !_rxModel.is())
+ return;
+
+ std::vector< NamedValue > aPropertyValues;
+ translateItemsToProperties( _rSet, aPropertyValues );
+ try
+ {
+ for ( const NamedValue& rNV : aPropertyValues )
+ _rxModel->setPropertyValue( rNV.Name, rNV.Value );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ void ControlCharacterDialog::createItemSet(std::unique_ptr<SfxItemSet>& _rpSet, rtl::Reference<SfxItemPool>& _rpPool, std::vector<SfxPoolItem*>*& _rpDefaults)
+ {
+ // just to be sure...
+ _rpSet = nullptr;
+ _rpPool = nullptr;
+ _rpDefaults = nullptr;
+
+ // create and initialize the defaults
+ _rpDefaults = new std::vector<SfxPoolItem*>(CFID_LAST_ITEM_ID - CFID_FIRST_ITEM_ID + 1);
+
+ vcl::Font aDefaultVCLFont = Application::GetDefaultDevice()->GetSettings().GetStyleSettings().GetAppFont();
+
+ SfxPoolItem** pCounter = _rpDefaults->data(); // want to modify this without affecting the out param _rppDefaults
+ *pCounter++ = new SvxFontItem(aDefaultVCLFont.GetFamilyType(), aDefaultVCLFont.GetFamilyName(), aDefaultVCLFont.GetStyleName(), aDefaultVCLFont.GetPitch(), aDefaultVCLFont.GetCharSet(), CFID_FONT);
+ *pCounter++ = new SvxFontHeightItem(aDefaultVCLFont.GetFontHeight(), 100, CFID_HEIGHT);
+ *pCounter++ = new SvxWeightItem(aDefaultVCLFont.GetWeight(), CFID_WEIGHT);
+ *pCounter++ = new SvxPostureItem(aDefaultVCLFont.GetItalic(), CFID_POSTURE);
+ *pCounter++ = new SvxLanguageItem(Application::GetSettings().GetUILanguageTag().getLanguageType(), CFID_LANGUAGE);
+ *pCounter++ = new SvxUnderlineItem(aDefaultVCLFont.GetUnderline(), CFID_UNDERLINE);
+ *pCounter++ = new SvxCrossedOutItem(aDefaultVCLFont.GetStrikeout(), CFID_STRIKEOUT);
+ *pCounter++ = new SvxWordLineModeItem(aDefaultVCLFont.IsWordLineMode(), CFID_WORDLINEMODE);
+ *pCounter++ = new SvxColorItem(aDefaultVCLFont.GetColor(), CFID_CHARCOLOR);
+ *pCounter++ = new SvxCharReliefItem(aDefaultVCLFont.GetRelief(), CFID_RELIEF);
+ *pCounter++ = new SvxEmphasisMarkItem(aDefaultVCLFont.GetEmphasisMark(), CFID_EMPHASIS);
+
+ *pCounter++ = new SvxFontItem(aDefaultVCLFont.GetFamilyType(), aDefaultVCLFont.GetFamilyName(), aDefaultVCLFont.GetStyleName(), aDefaultVCLFont.GetPitch(), aDefaultVCLFont.GetCharSet(), CFID_CJK_FONT);
+ *pCounter++ = new SvxFontHeightItem(aDefaultVCLFont.GetFontHeight(), 100, CFID_CJK_HEIGHT);
+ *pCounter++ = new SvxWeightItem(aDefaultVCLFont.GetWeight(), CFID_CJK_WEIGHT);
+ *pCounter++ = new SvxPostureItem(aDefaultVCLFont.GetItalic(), CFID_CJK_POSTURE);
+ *pCounter++ = new SvxLanguageItem(Application::GetSettings().GetUILanguageTag().getLanguageType(), CFID_CJK_LANGUAGE);
+
+ *pCounter++ = new SvxCaseMapItem(SvxCaseMap::NotMapped, CFID_CASEMAP);
+ *pCounter++ = new SvxContourItem(false, CFID_CONTOUR);
+ *pCounter++ = new SvxShadowedItem(false, CFID_SHADOWED);
+
+ *pCounter++ = new SvxFontListItem (new FontList(Application::GetDefaultDevice()), CFID_FONTLIST);
+
+ // create the pool
+ static SfxItemInfo const aItemInfos[CFID_LAST_ITEM_ID - CFID_FIRST_ITEM_ID + 1] =
+ {
+ { SID_ATTR_CHAR_FONT, false },
+ { SID_ATTR_CHAR_FONTHEIGHT, false },
+ { SID_ATTR_CHAR_WEIGHT, false },
+ { SID_ATTR_CHAR_POSTURE, false },
+ { SID_ATTR_CHAR_LANGUAGE, false },
+ { SID_ATTR_CHAR_UNDERLINE, false },
+ { SID_ATTR_CHAR_STRIKEOUT, false },
+ { SID_ATTR_CHAR_WORDLINEMODE, false },
+ { SID_ATTR_CHAR_COLOR, false },
+ { SID_ATTR_CHAR_RELIEF, false },
+ { SID_ATTR_CHAR_EMPHASISMARK, false },
+ { 0, false },
+ { 0, false },
+ { 0, false },
+ { 0, false },
+ { 0, false },
+ { 0, false },
+ { 0, false },
+ { 0, false },
+ { SID_ATTR_CHAR_FONTLIST, false }
+ };
+
+ _rpPool = new SfxItemPool("PCRControlFontItemPool", CFID_FIRST_ITEM_ID, CFID_LAST_ITEM_ID,
+ aItemInfos, _rpDefaults);
+ _rpPool->FreezeIdRanges();
+
+ // and, finally, the set
+ _rpSet.reset(new SfxItemSet(*_rpPool));
+ }
+
+ void ControlCharacterDialog::destroyItemSet(std::unique_ptr<SfxItemSet>& _rpSet, rtl::Reference<SfxItemPool>& _rpPool, std::vector<SfxPoolItem*>*& _rpDefaults)
+ {
+ // from the pool, get and remember the font list (needs to be deleted)
+ const SvxFontListItem& rFontListItem = static_cast<const SvxFontListItem&>(_rpPool->GetDefaultItem(CFID_FONTLIST));
+ const FontList* pFontList = rFontListItem.GetFontList();
+
+ // _first_ delete the set (referring the pool)
+ _rpSet.reset();
+
+ // delete the pool
+ _rpPool->ReleaseDefaults(true);
+ // the "true" means delete the items, too
+ _rpPool = nullptr;
+
+ // reset the defaults ptr
+ _rpDefaults = nullptr;
+ // no need to explicitly delete the defaults, this has been done by the ReleaseDefaults
+
+ delete pFontList;
+ }
+
+ void ControlCharacterDialog::PageCreated(const OString& rId, SfxTabPage& rPage)
+ {
+ SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool()));
+ if (rId == "font")
+ {
+ aSet.Put (static_cast<const SvxFontListItem&>(GetInputSetImpl()->Get(CFID_FONTLIST)));
+ aSet.Put (SfxUInt16Item(SID_DISABLE_CTL,DISABLE_HIDE_LANGUAGE));
+ rPage.PageCreated(aSet);
+ }
+ }
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/fontdialog.hxx b/extensions/source/propctrlr/fontdialog.hxx
new file mode 100644
index 000000000..eaba29db0
--- /dev/null
+++ b/extensions/source/propctrlr/fontdialog.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 <sfx2/tabdlg.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+
+
+namespace pcr
+{
+
+
+ //= ControlCharacterDialog
+ class ControlCharacterDialog : public SfxTabDialogController
+ {
+ public:
+ ControlCharacterDialog(weld::Window* pParent, const SfxItemSet& rCoreSet);
+ virtual ~ControlCharacterDialog() override;
+
+ /// creates an item set to be used with this dialog
+ static void createItemSet(std::unique_ptr<SfxItemSet>& _rpSet, rtl::Reference<SfxItemPool>& _rpPool, std::vector<SfxPoolItem*>*& _rpDefaults);
+
+ /// destroys an item previously created with <method>createItemSet</method>
+ static void destroyItemSet(std::unique_ptr<SfxItemSet>& _rpSet, rtl::Reference<SfxItemPool>& _rpPool, std::vector<SfxPoolItem*>*& _rpDefaults);
+
+ /// fills the given item set with values obtained from the given property set
+ static void translatePropertiesToItems(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxModel,
+ SfxItemSet* _pSet);
+
+ /** fills the given property set with values obtained from the given item set
+ */
+ static void translateItemsToProperties(
+ const SfxItemSet& _rSet,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxModel);
+
+ /** fills the given property set with values obtained from the given item set
+ */
+ static void translateItemsToProperties(
+ const SfxItemSet& _rSet,
+ std::vector< css::beans::NamedValue >& _out_properties );
+
+ protected:
+ virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override;
+ };
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/fontitemids.hxx b/extensions/source/propctrlr/fontitemids.hxx
new file mode 100644
index 000000000..c9b3a455f
--- /dev/null
+++ b/extensions/source/propctrlr/fontitemids.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
+
+#define CFID_FONT 1
+#define CFID_HEIGHT 2
+#define CFID_WEIGHT 3
+#define CFID_POSTURE 4
+#define CFID_LANGUAGE 5
+#define CFID_UNDERLINE 6
+#define CFID_STRIKEOUT 7
+#define CFID_WORDLINEMODE 8
+#define CFID_CHARCOLOR 9
+#define CFID_RELIEF 10
+#define CFID_EMPHASIS 11
+
+#define CFID_CJK_FONT 12
+#define CFID_CJK_HEIGHT 13
+#define CFID_CJK_WEIGHT 14
+#define CFID_CJK_POSTURE 15
+#define CFID_CJK_LANGUAGE 16
+#define CFID_CASEMAP 17
+#define CFID_CONTOUR 18
+#define CFID_SHADOWED 19
+
+#define CFID_FONTLIST 20
+
+#define CFID_FIRST_ITEM_ID CFID_FONT
+#define CFID_LAST_ITEM_ID CFID_FONTLIST
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formbrowsertools.cxx b/extensions/source/propctrlr/formbrowsertools.cxx
new file mode 100644
index 000000000..b31aa0ce4
--- /dev/null
+++ b/extensions/source/propctrlr/formbrowsertools.cxx
@@ -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 .
+ */
+
+#include "formbrowsertools.hxx"
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <osl/diagnose.h>
+#include <strings.hrc>
+#include "modulepcr.hxx"
+#include "formstrings.hxx"
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+
+
+ OUString GetUIHeadlineName(sal_Int16 nClassId, const Any& aUnoObj)
+ {
+ OUString sClassName;
+ switch (nClassId)
+ {
+ case FormComponentType::TEXTFIELD:
+ {
+ Reference< XInterface > xIFace;
+ aUnoObj >>= xIFace;
+ sClassName = PcrRes(RID_STR_PROPTITLE_EDIT);
+ if (xIFace.is())
+ { // we have a chance to check if it's a formatted field model
+ Reference< XServiceInfo > xInfo(xIFace, UNO_QUERY);
+ if (xInfo.is() && (xInfo->supportsService(SERVICE_COMPONENT_FORMATTEDFIELD)))
+ sClassName = PcrRes(RID_STR_PROPTITLE_FORMATTED);
+ else if (!xInfo.is())
+ {
+ // couldn't distinguish between formatted and edit with the service name, so try with the properties
+ Reference< XPropertySet > xProps(xIFace, UNO_QUERY);
+ if (xProps.is())
+ {
+ Reference< XPropertySetInfo > xPropsInfo = xProps->getPropertySetInfo();
+ if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(PROPERTY_FORMATSSUPPLIER))
+ sClassName = PcrRes(RID_STR_PROPTITLE_FORMATTED);
+ }
+ }
+ }
+ }
+ break;
+
+ case FormComponentType::COMMANDBUTTON:
+ sClassName = PcrRes(RID_STR_PROPTITLE_PUSHBUTTON); break;
+ case FormComponentType::RADIOBUTTON:
+ sClassName = PcrRes(RID_STR_PROPTITLE_RADIOBUTTON); break;
+ case FormComponentType::CHECKBOX:
+ sClassName = PcrRes(RID_STR_PROPTITLE_CHECKBOX); break;
+ case FormComponentType::LISTBOX:
+ sClassName = PcrRes(RID_STR_PROPTITLE_LISTBOX); break;
+ case FormComponentType::COMBOBOX:
+ sClassName = PcrRes(RID_STR_PROPTITLE_COMBOBOX); break;
+ case FormComponentType::GROUPBOX:
+ sClassName = PcrRes(RID_STR_PROPTITLE_GROUPBOX); break;
+ case FormComponentType::IMAGEBUTTON:
+ sClassName = PcrRes(RID_STR_PROPTITLE_IMAGEBUTTON); break;
+ case FormComponentType::FIXEDTEXT:
+ sClassName = PcrRes(RID_STR_PROPTITLE_FIXEDTEXT); break;
+ case FormComponentType::GRIDCONTROL:
+ sClassName = PcrRes(RID_STR_PROPTITLE_DBGRID); break;
+ case FormComponentType::FILECONTROL:
+ sClassName = PcrRes(RID_STR_PROPTITLE_FILECONTROL); break;
+
+ case FormComponentType::DATEFIELD:
+ sClassName = PcrRes(RID_STR_PROPTITLE_DATEFIELD); break;
+ case FormComponentType::TIMEFIELD:
+ sClassName = PcrRes(RID_STR_PROPTITLE_TIMEFIELD); break;
+ case FormComponentType::NUMERICFIELD:
+ sClassName = PcrRes(RID_STR_PROPTITLE_NUMERICFIELD); break;
+ case FormComponentType::CURRENCYFIELD:
+ sClassName = PcrRes(RID_STR_PROPTITLE_CURRENCYFIELD); break;
+ case FormComponentType::PATTERNFIELD:
+ sClassName = PcrRes(RID_STR_PROPTITLE_PATTERNFIELD); break;
+ case FormComponentType::IMAGECONTROL:
+ sClassName = PcrRes(RID_STR_PROPTITLE_IMAGECONTROL); break;
+ case FormComponentType::HIDDENCONTROL:
+ sClassName = PcrRes(RID_STR_PROPTITLE_HIDDENCONTROL); break;
+
+ case FormComponentType::CONTROL:
+ default:
+ sClassName = PcrRes(RID_STR_PROPTITLE_UNKNOWNCONTROL); break;
+ }
+
+ return sClassName;
+ }
+
+
+ sal_Int16 classifyComponent( const Reference< XInterface >& _rxComponent )
+ {
+ Reference< XPropertySet > xComponentProps( _rxComponent, UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xPSI( xComponentProps->getPropertySetInfo(), UNO_SET_THROW );
+
+ sal_Int16 nControlType( FormComponentType::CONTROL );
+ if ( xPSI->hasPropertyByName( PROPERTY_CLASSID ) )
+ {
+ OSL_VERIFY( xComponentProps->getPropertyValue( PROPERTY_CLASSID ) >>= nControlType );
+ }
+ return nControlType;
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formbrowsertools.hxx b/extensions/source/propctrlr/formbrowsertools.hxx
new file mode 100644
index 000000000..08d48747c
--- /dev/null
+++ b/extensions/source/propctrlr/formbrowsertools.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 <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/beans/Property.hpp>
+#include <rtl/ustring.hxx>
+
+#include <set>
+
+
+namespace pcr
+{
+
+
+ OUString GetUIHeadlineName(sal_Int16 _nClassId, const css::uno::Any& _rUnoObject);
+ sal_Int16 classifyComponent( const css::uno::Reference< css::uno::XInterface >& _rxComponent );
+
+
+ struct FindPropertyByHandle
+ {
+ private:
+ sal_Int32 m_nId;
+
+ public:
+ explicit FindPropertyByHandle( sal_Int32 _nId ) : m_nId ( _nId ) { }
+ bool operator()( const css::beans::Property& _rProp ) const
+ {
+ return m_nId == _rProp.Handle;
+ }
+ };
+
+
+ struct FindPropertyByName
+ {
+ private:
+ OUString m_sName;
+
+ public:
+ explicit FindPropertyByName( const OUString& _rName ) : m_sName( _rName ) { }
+ bool operator()( const css::beans::Property& _rProp ) const
+ {
+ return m_sName == _rProp.Name;
+ }
+ };
+
+
+ struct PropertyLessByName
+ {
+ bool operator() (const css::beans::Property& _rLhs, const css::beans::Property& _rRhs) const
+ {
+ return _rLhs.Name < _rRhs.Name;
+ }
+ };
+
+
+ struct TypeLessByName
+ {
+ bool operator() (const css::uno::Type& _rLhs, const css::uno::Type& _rRhs) const
+ {
+ return _rLhs.getTypeName() < _rRhs.getTypeName();
+ }
+ };
+
+
+ typedef std::set< css::beans::Property, PropertyLessByName > PropertyBag;
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formcomponenthandler.cxx b/extensions/source/propctrlr/formcomponenthandler.cxx
new file mode 100644
index 000000000..d5f397662
--- /dev/null
+++ b/extensions/source/propctrlr/formcomponenthandler.cxx
@@ -0,0 +1,3305 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "controltype.hxx"
+#include "modulepcr.hxx"
+#include <propctrlr.h>
+#include <helpids.h>
+#include "fontdialog.hxx"
+#include "formcomponenthandler.hxx"
+#include "formlinkdialog.hxx"
+#include "formmetadata.hxx"
+#include <strings.hrc>
+#include <showhide.hrc>
+#include <yesno.hrc>
+#include "formstrings.hxx"
+#include "handlerhelper.hxx"
+#include "listselectiondlg.hxx"
+#include "pcrcommon.hxx"
+#include "selectlabeldialog.hxx"
+#include "standardcontrol.hxx"
+#include "taborder.hxx"
+#include "usercontrol.hxx"
+
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/sdb/OrderDialog.hpp>
+#include <com/sun/star/sdb/FilterDialog.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/form/ListSourceType.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/awt/XTabControllerModel.hpp>
+#include <com/sun/star/form/FormSubmitEncoding.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/inspection/PropertyLineElement.hpp>
+#include <com/sun/star/resource/XStringResourceManager.hpp>
+#include <com/sun/star/resource/MissingResourceException.hpp>
+#include <com/sun/star/report/XReportDefinition.hpp>
+#include <com/sun/star/graphic/GraphicObject.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+
+#include <comphelper/extract.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbconversion.hxx>
+#include <connectivity/dbexception.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <svl/ctloptions.hxx>
+#include <svtools/colrdlg.hxx>
+#include <svl/filenotation.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/numformat.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <svl/numuno.hxx>
+#include <svl/urihelper.hxx>
+#include <svx/dialogs.hrc>
+#include <svx/numinf.hxx>
+#include <svx/svxdlg.hxx>
+#include <svx/svxids.hrc>
+#include <vcl/graph.hxx>
+#include <vcl/unohelp.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+
+#include <limits>
+#include <memory>
+#include <string_view>
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star;
+ using namespace uno;
+ using namespace lang;
+ using namespace beans;
+ using namespace frame;
+ using namespace script;
+ using namespace form;
+ using namespace util;
+ using namespace awt;
+ using namespace sdb;
+ using namespace sdbc;
+ using namespace sdbcx;
+ using namespace report;
+ using namespace container;
+ using namespace ui::dialogs;
+ using namespace inspection;
+ using namespace ::dbtools;
+
+ namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+
+
+ //= FormComponentPropertyHandler
+
+#define PROPERTY_ID_ROWSET 1
+
+ FormComponentPropertyHandler::FormComponentPropertyHandler( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandlerComponent( _rxContext )
+ ,::comphelper::OPropertyContainer(PropertyHandlerComponent::rBHelper)
+ ,m_sDefaultValueString( PcrRes(RID_STR_STANDARD) )
+ ,m_eComponentClass( eUnknown )
+ ,m_bComponentIsSubForm( false )
+ ,m_bHaveListSource( false )
+ ,m_bHaveCommand( false )
+ ,m_nClassId( 0 )
+ {
+ registerProperty(PROPERTY_ROWSET,PROPERTY_ID_ROWSET,0,&m_xRowSet,cppu::UnoType<decltype(m_xRowSet)>::get());
+ }
+
+
+ FormComponentPropertyHandler::~FormComponentPropertyHandler()
+ {
+ }
+
+ IMPLEMENT_FORWARD_XINTERFACE2(FormComponentPropertyHandler,PropertyHandlerComponent,::comphelper::OPropertyContainer)
+
+ OUString FormComponentPropertyHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.FormComponentPropertyHandler";
+ }
+
+
+ Sequence< OUString > FormComponentPropertyHandler::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.inspection.FormComponentPropertyHandler" };
+ }
+
+ namespace {
+
+ // TODO: -> export from toolkit
+ struct LanguageDependentProp
+ {
+ const char* pPropName;
+ sal_Int32 nPropNameLength;
+ };
+
+ }
+
+ const LanguageDependentProp aLanguageDependentProp[] =
+ {
+ { "Text", 4 },
+ { "Label", 5 },
+ { "Title", 5 },
+ { "HelpText", 8 },
+ { "CurrencySymbol", 14 },
+ { "StringItemList", 14 },
+ { nullptr, 0 }
+ };
+
+ namespace
+ {
+ bool lcl_isLanguageDependentProperty( const OUString& aName )
+ {
+ bool bRet = false;
+
+ const LanguageDependentProp* pLangDepProp = aLanguageDependentProp;
+ while( pLangDepProp->pPropName != nullptr )
+ {
+ if( aName.equalsAsciiL( pLangDepProp->pPropName, pLangDepProp->nPropNameLength ))
+ {
+ bRet = true;
+ break;
+ }
+ pLangDepProp++;
+ }
+ return bRet;
+ }
+
+ Reference< resource::XStringResourceResolver > lcl_getStringResourceResolverForProperty
+ ( const Reference< XPropertySet >& _xComponent, const OUString& _rPropertyName,
+ const Any& _rPropertyValue )
+ {
+ Reference< resource::XStringResourceResolver > xRet;
+ const TypeClass eType = _rPropertyValue.getValueType().getTypeClass();
+ if ( (eType == TypeClass_STRING || eType == TypeClass_SEQUENCE) &&
+ lcl_isLanguageDependentProperty( _rPropertyName ) )
+ {
+ Reference< resource::XStringResourceResolver > xStringResourceResolver;
+ try
+ {
+ xStringResourceResolver.set( _xComponent->getPropertyValue( "ResourceResolver" ),UNO_QUERY);
+ if( xStringResourceResolver.is() &&
+ xStringResourceResolver->getLocales().hasElements() )
+ {
+ xRet = xStringResourceResolver;
+ }
+ }
+ catch(const UnknownPropertyException&)
+ {
+ // nii
+ }
+ }
+
+ return xRet;
+ }
+ }
+
+
+ Any FormComponentPropertyHandler::impl_getPropertyValue_throw( const OUString& _rPropertyName ) const
+ {
+ const PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ // tdf#117159 crash with chart in database report
+ if (!m_xComponent)
+ return Any();
+
+ Any aPropertyValue( m_xComponent->getPropertyValue( _rPropertyName ) );
+
+ Reference< resource::XStringResourceResolver > xStringResourceResolver
+ = lcl_getStringResourceResolverForProperty( m_xComponent, _rPropertyName, aPropertyValue );
+ if( xStringResourceResolver.is() )
+ {
+ TypeClass eType = aPropertyValue.getValueType().getTypeClass();
+ if( eType == TypeClass_STRING )
+ {
+ OUString aPropStr;
+ aPropertyValue >>= aPropStr;
+ if( aPropStr.getLength() > 1 )
+ {
+ OUString aPureIdStr = aPropStr.copy( 1 );
+ if( xStringResourceResolver->hasEntryForId( aPureIdStr ) )
+ {
+ OUString aResourceStr = xStringResourceResolver->resolveString( aPureIdStr );
+ aPropertyValue <<= aResourceStr;
+ }
+ }
+ }
+ // StringItemList?
+ else if( eType == TypeClass_SEQUENCE )
+ {
+ Sequence< OUString > aStrings;
+ aPropertyValue >>= aStrings;
+
+ std::vector< OUString > aResolvedStrings;
+ aResolvedStrings.reserve( aStrings.getLength() );
+ try
+ {
+ for ( const OUString& rIdStr : std::as_const(aStrings) )
+ {
+ OUString aPureIdStr = rIdStr.copy( 1 );
+ if( xStringResourceResolver->hasEntryForId( aPureIdStr ) )
+ aResolvedStrings.push_back(xStringResourceResolver->resolveString( aPureIdStr ));
+ else
+ aResolvedStrings.push_back(rIdStr);
+ }
+ }
+ catch( const resource::MissingResourceException & )
+ {}
+ aPropertyValue <<= comphelper::containerToSequence(aResolvedStrings);
+ }
+ }
+ else
+ impl_normalizePropertyValue_nothrow( aPropertyValue, nPropId );
+
+ return aPropertyValue;
+ }
+
+ Any SAL_CALL FormComponentPropertyHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ if( _rPropertyName == PROPERTY_ROWSET )
+ return ::comphelper::OPropertyContainer::getPropertyValue( _rPropertyName );
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return impl_getPropertyValue_throw( _rPropertyName );
+ }
+
+ void SAL_CALL FormComponentPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ if( _rPropertyName == PROPERTY_ROWSET )
+ {
+ ::comphelper::OPropertyContainer::setPropertyValue( _rPropertyName, _rValue );
+ return;
+ }
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) ); // check if property is known by the handler
+
+ Reference< graphic::XGraphicObject > xGrfObj;
+ if ( PROPERTY_ID_IMAGE_URL == nPropId && ( _rValue >>= xGrfObj ) )
+ {
+ DBG_ASSERT( xGrfObj.is(), "FormComponentPropertyHandler::setPropertyValue() xGrfObj is invalid");
+ m_xComponent->setPropertyValue(PROPERTY_GRAPHIC, uno::Any(xGrfObj->getGraphic()));
+ }
+ else if ( PROPERTY_ID_FONT == nPropId )
+ {
+ // special handling, the value is a faked value we generated ourself in impl_executeFontDialog_nothrow
+ Sequence< NamedValue > aFontPropertyValues;
+ if( ! (_rValue >>= aFontPropertyValues) )
+ SAL_WARN("extensions.propctrlr", "setPropertyValue: unable to get property " << PROPERTY_ID_FONT);
+
+ for ( const NamedValue& fontPropertyValue : std::as_const(aFontPropertyValues) )
+ m_xComponent->setPropertyValue( fontPropertyValue.Name, fontPropertyValue.Value );
+ }
+ else
+ {
+ Any aValue = _rValue;
+
+ Reference< resource::XStringResourceResolver > xStringResourceResolver
+ = lcl_getStringResourceResolverForProperty( m_xComponent, _rPropertyName, _rValue );
+ if( xStringResourceResolver.is() )
+ {
+ Reference< resource::XStringResourceManager >
+ xStringResourceManager( xStringResourceResolver, UNO_QUERY );
+ if( xStringResourceManager.is() )
+ {
+ Any aPropertyValue( m_xComponent->getPropertyValue( _rPropertyName ) );
+ TypeClass eType = aPropertyValue.getValueType().getTypeClass();
+ if( eType == TypeClass_STRING )
+ {
+ OUString aPropStr;
+ aPropertyValue >>= aPropStr;
+ if( aPropStr.getLength() > 1 )
+ {
+ OUString aPureIdStr = aPropStr.copy( 1 );
+ OUString aValueStr;
+ _rValue >>= aValueStr;
+ xStringResourceManager->setString( aPureIdStr, aValueStr );
+ aValue = aPropertyValue; // set value to force modified
+ }
+ }
+ // StringItemList?
+ else if( eType == TypeClass_SEQUENCE )
+ {
+ static const char aDot[] = ".";
+
+ // Put strings into resource using new ids
+ Sequence< OUString > aNewStrings;
+ _rValue >>= aNewStrings;
+
+ const sal_Int32 nNewCount = aNewStrings.getLength();
+
+ // Create new Ids
+ std::unique_ptr<OUString[]> pNewPureIds(new OUString[nNewCount]);
+ Any aNameAny = m_xComponent->getPropertyValue(PROPERTY_NAME);
+ OUString sControlName;
+ aNameAny >>= sControlName;
+ OUString aIdStrBase = aDot
+ + sControlName
+ + aDot
+ + _rPropertyName;
+ sal_Int32 i;
+ for ( i = 0; i < nNewCount; ++i )
+ {
+ sal_Int32 nUniqueId = xStringResourceManager->getUniqueNumericId();
+ OUString aPureIdStr = OUString::number( nUniqueId ) + aIdStrBase;
+ pNewPureIds[i] = aPureIdStr;
+ // Force usage of next Unique Id
+ xStringResourceManager->setString( aPureIdStr, OUString() );
+ }
+
+ // Move strings to new Ids for all locales
+ const Sequence< Locale > aLocaleSeq = xStringResourceManager->getLocales();
+ Sequence< OUString > aOldIdStrings;
+ aPropertyValue >>= aOldIdStrings;
+ try
+ {
+ const OUString* pOldIdStrings = aOldIdStrings.getConstArray();
+ sal_Int32 nOldIdCount = aOldIdStrings.getLength();
+ for ( i = 0; i < nNewCount; ++i )
+ {
+ OUString aOldPureIdStr;
+ if( i < nOldIdCount )
+ {
+ OUString aOldIdStr = pOldIdStrings[i];
+ aOldPureIdStr = aOldIdStr.copy( 1 );
+ }
+ OUString aNewPureIdStr = pNewPureIds[i];
+
+ for ( const Locale& rLocale : aLocaleSeq )
+ {
+ OUString aResourceStr;
+ if( !aOldPureIdStr.isEmpty() )
+ {
+ if( xStringResourceManager->hasEntryForIdAndLocale( aOldPureIdStr, rLocale ) )
+ {
+ aResourceStr = xStringResourceManager->
+ resolveStringForLocale( aOldPureIdStr, rLocale );
+ }
+ }
+ xStringResourceManager->setStringForLocale( aNewPureIdStr, aResourceStr, rLocale );
+ }
+ }
+ }
+ catch( const resource::MissingResourceException & )
+ {}
+
+
+ // Set new strings for current locale and create
+ // new Id sequence as new property value
+ Sequence< OUString > aNewIdStrings;
+ aNewIdStrings.realloc( nNewCount );
+ OUString* pNewIdStrings = aNewIdStrings.getArray();
+ for ( i = 0; i < nNewCount; ++i )
+ {
+ const OUString& aPureIdStr = pNewPureIds[i];
+ const OUString& aStr = aNewStrings[i];
+ xStringResourceManager->setString( aPureIdStr, aStr );
+
+ pNewIdStrings[i] = "&" + aPureIdStr;
+ }
+ aValue <<= aNewIdStrings;
+
+ // Remove old ids from resource for all locales
+ for( const OUString& rIdStr : std::as_const(aOldIdStrings) )
+ {
+ OUString aPureIdStr = rIdStr.copy( 1 );
+ for ( const Locale& rLocale : aLocaleSeq )
+ {
+ try
+ {
+ xStringResourceManager->removeIdForLocale( aPureIdStr, rLocale );
+ }
+ catch( const resource::MissingResourceException & )
+ {}
+ }
+ }
+ }
+ }
+ }
+
+ m_xComponent->setPropertyValue( _rPropertyName, aValue );
+ }
+ }
+
+ Any SAL_CALL FormComponentPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+ Property aProperty( impl_getPropertyFromId_throw( nPropId ) );
+
+ Any aPropertyValue( _rControlValue );
+ if ( !aPropertyValue.hasValue() )
+ {
+ if ( ( aProperty.Attributes & PropertyAttribute::MAYBEVOID ) == 0 )
+ // default construct an instance of the proper type
+ aPropertyValue = Any( nullptr, aProperty.Type );
+ // nothing to do
+ return aPropertyValue;
+ }
+
+ /// care for the special "default" string, translate it to VOID
+ if ( m_aPropertiesWithDefListEntry.find( _rPropertyName ) != m_aPropertiesWithDefListEntry.end() )
+ {
+ // it's a control with a string list
+ OUString sStringValue;
+ if ( _rControlValue >>= sStringValue )
+ { // note that ColorListBoxes might transfer values either as string or as css.util.Color,
+ // so this check here is important
+ if ( sStringValue == m_sDefaultValueString )
+ return Any();
+ }
+ }
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_DATASOURCE:
+ {
+ OUString sControlValue;
+ if( ! (_rControlValue >>= sControlValue) )
+ SAL_WARN("extensions.propctrlr", "convertToPropertyValue: unable to get property " << PROPERTY_ID_DATASOURCE);
+
+ if ( !sControlValue.isEmpty() )
+ {
+ Reference< XDatabaseContext > xDatabaseContext = sdb::DatabaseContext::create( m_xContext );
+ if ( !xDatabaseContext->hasByName( sControlValue ) )
+ {
+ ::svt::OFileNotation aTransformer(sControlValue);
+ aPropertyValue <<= aTransformer.get( ::svt::OFileNotation::N_URL );
+ }
+ }
+ }
+ break; // case PROPERTY_ID_DATASOURCE
+
+ case PROPERTY_ID_SHOW_POSITION:
+ case PROPERTY_ID_SHOW_NAVIGATION:
+ case PROPERTY_ID_SHOW_RECORDACTIONS:
+ case PROPERTY_ID_SHOW_FILTERSORT:
+ {
+ OUString sControlValue;
+ if( ! (_rControlValue >>= sControlValue) )
+ SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for Show/Hide");
+
+ static_assert(SAL_N_ELEMENTS(RID_RSC_ENUM_SHOWHIDE) == 2, "FormComponentPropertyHandler::convertToPropertyValue: broken resource for Show/Hide!");
+ bool bShow = sControlValue == PcrRes(RID_RSC_ENUM_SHOWHIDE[1]);
+
+ aPropertyValue <<= bShow;
+ }
+ break;
+
+ case PROPERTY_ID_TARGET_URL:
+ case PROPERTY_ID_IMAGE_URL:
+ {
+ OUString sControlValue;
+ if( ! (_rControlValue >>= sControlValue) )
+ SAL_WARN("extensions.propctrlr", "convertToPropertyValue: unable to get property for URLs");
+ // Don't convert a placeholder
+ if ( nPropId == PROPERTY_ID_IMAGE_URL && sControlValue == PcrRes(RID_EMBED_IMAGE_PLACEHOLDER) )
+ aPropertyValue <<= sControlValue;
+ else
+ {
+ INetURLObject aDocURL( impl_getDocumentURL_nothrow() );
+ aPropertyValue <<= URIHelper::SmartRel2Abs( aDocURL, sControlValue, Link<OUString *, bool>(), false, true );
+ }
+ }
+ break;
+
+ case PROPERTY_ID_DATEMIN:
+ case PROPERTY_ID_DATEMAX:
+ case PROPERTY_ID_DEFAULT_DATE:
+ case PROPERTY_ID_DATE:
+ {
+ util::Date aDate;
+ if( ! (_rControlValue >>= aDate) )
+ SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for date");
+ aPropertyValue <<= aDate;
+ }
+ break;
+
+ case PROPERTY_ID_TIMEMIN:
+ case PROPERTY_ID_TIMEMAX:
+ case PROPERTY_ID_DEFAULT_TIME:
+ case PROPERTY_ID_TIME:
+ {
+ util::Time aTime;
+ if( ! (_rControlValue >>= aTime) )
+ SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for time");
+ aPropertyValue <<= aTime;
+ }
+ break;
+
+ case PROPERTY_ID_WRITING_MODE:
+ {
+ aPropertyValue = PropertyHandlerComponent::convertToPropertyValue( _rPropertyName, _rControlValue );
+
+ sal_Int16 nNormalizedValue( 2 );
+ if( ! (aPropertyValue >>= nNormalizedValue) )
+ SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for " << PROPERTY_ID_WRITING_MODE);
+
+ sal_Int16 nWritingMode = WritingMode2::CONTEXT;
+ switch ( nNormalizedValue )
+ {
+ case 0: nWritingMode = WritingMode2::LR_TB; break;
+ case 1: nWritingMode = WritingMode2::RL_TB; break;
+ case 2: nWritingMode = WritingMode2::CONTEXT; break;
+ default:
+ OSL_FAIL( "FormComponentPropertyHandler::convertToPropertyValue: unexpected 'normalized value' for WritingMode!" );
+ nWritingMode = WritingMode2::CONTEXT;
+ break;
+ }
+
+ aPropertyValue <<= nWritingMode;
+ }
+ break;
+
+ default:
+ aPropertyValue = PropertyHandlerComponent::convertToPropertyValue( _rPropertyName, _rControlValue );
+ break; // default
+
+ } // switch ( nPropId )
+
+ return aPropertyValue;
+ }
+
+ Any SAL_CALL FormComponentPropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ sal_Int32 nPropId = m_pInfoService->getPropertyId( _rPropertyName );
+ DBG_ASSERT( nPropId != -1, "FormComponentPropertyHandler::convertToPropertyValue: not one of my properties!!" );
+
+ impl_getPropertyFromId_throw( nPropId );
+
+ Any aControlValue( _rPropertyValue );
+ if ( !aControlValue.hasValue() )
+ {
+ // if the property is represented with a list box or color list box, we need to
+ // translate this into the string "Default"
+ if ( m_aPropertiesWithDefListEntry.find( _rPropertyName ) != m_aPropertiesWithDefListEntry.end() )
+ aControlValue <<= m_sDefaultValueString;
+
+ return aControlValue;
+ }
+
+ switch ( nPropId )
+ {
+
+ case PROPERTY_ID_SHOW_POSITION:
+ case PROPERTY_ID_SHOW_NAVIGATION:
+ case PROPERTY_ID_SHOW_RECORDACTIONS:
+ case PROPERTY_ID_SHOW_FILTERSORT:
+ {
+ static_assert(SAL_N_ELEMENTS(RID_RSC_ENUM_SHOWHIDE) == 2, "FormComponentPropertyHandler::convertToPropertyValue: broken resource for Show/Hide!");
+ OUString sControlValue = ::comphelper::getBOOL(_rPropertyValue)
+ ? PcrRes(RID_RSC_ENUM_SHOWHIDE[1])
+ : PcrRes(RID_RSC_ENUM_SHOWHIDE[0]);
+ aControlValue <<= sControlValue;
+ }
+ break;
+
+
+ case PROPERTY_ID_DATASOURCE:
+ {
+ OSL_ENSURE( _rControlValueType.getTypeClass() == TypeClass_STRING,
+ "FormComponentPropertyHandler::convertToControlValue: wrong ControlValueType!" );
+
+ OUString sDataSource;
+ _rPropertyValue >>= sDataSource;
+ if ( !sDataSource.isEmpty() )
+ {
+ ::svt::OFileNotation aTransformer( sDataSource );
+ sDataSource = aTransformer.get( ::svt::OFileNotation::N_SYSTEM );
+ }
+ aControlValue <<= sDataSource;
+ }
+ break;
+
+
+ case PROPERTY_ID_CONTROLLABEL:
+ {
+ OUString sControlValue;
+
+ Reference< XPropertySet > xSet;
+ _rPropertyValue >>= xSet;
+ Reference< XPropertySetInfo > xPSI;
+ if ( xSet.is() )
+ xPSI = xSet->getPropertySetInfo();
+ if ( xPSI.is() && xPSI->hasPropertyByName( PROPERTY_LABEL ) )
+ {
+ OUString sLabel;
+ if( ! (xSet->getPropertyValue( PROPERTY_LABEL) >>= sLabel) )
+ SAL_WARN("extensions.propctrlr", "convertToPropertyValue: unable to get property " << PROPERTY_LABEL);
+ sControlValue = "<" + sLabel + ">";
+ }
+
+ aControlValue <<= sControlValue;
+ }
+ break;
+
+
+ case PROPERTY_ID_DATEMIN:
+ case PROPERTY_ID_DATEMAX:
+ case PROPERTY_ID_DEFAULT_DATE:
+ case PROPERTY_ID_DATE:
+ {
+ sal_Int32 nDate = 0;
+ if( ! (_rPropertyValue >>= nDate) )
+ SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for dates");
+ aControlValue <<= DBTypeConversion::toDate( nDate );
+ }
+ break;
+
+ case PROPERTY_ID_TIMEMIN:
+ case PROPERTY_ID_TIMEMAX:
+ case PROPERTY_ID_DEFAULT_TIME:
+ case PROPERTY_ID_TIME:
+ {
+ sal_Int64 nTime = 0;
+ if( ! (_rPropertyValue >>= nTime) )
+ SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property for times");
+ aControlValue <<= DBTypeConversion::toTime( nTime );
+ }
+ break;
+
+ case PROPERTY_ID_WRITING_MODE:
+ {
+ sal_Int16 nWritingMode( WritingMode2::CONTEXT );
+ if( ! (_rPropertyValue >>= nWritingMode) )
+ SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property " << PROPERTY_ID_WRITING_MODE);
+
+ sal_Int16 nNormalized = 2;
+ switch ( nWritingMode )
+ {
+ case WritingMode2::LR_TB: nNormalized = 0; break;
+ case WritingMode2::RL_TB: nNormalized = 1; break;
+ case WritingMode2::CONTEXT: nNormalized = 2; break;
+ default:
+ OSL_FAIL( "FormComponentPropertyHandler::convertToControlValue: unsupported API value for WritingMode!" );
+ nNormalized = 2;
+ break;
+ }
+
+ aControlValue = PropertyHandlerComponent::convertToControlValue( _rPropertyName, Any( nNormalized ), _rControlValueType );
+ }
+ break;
+
+ case PROPERTY_ID_FONT:
+ {
+ FontDescriptor aFont;
+ if( ! (_rPropertyValue >>= aFont) )
+ SAL_WARN("extensions.propctrlr", "convertToControlValue: unable to get property " << PROPERTY_ID_FONT);
+
+ OUStringBuffer displayName;
+ if ( aFont.Name.isEmpty() )
+ {
+ displayName.append( PcrRes(RID_STR_FONT_DEFAULT) );
+ }
+ else
+ {
+ // font name
+ displayName.append( aFont.Name );
+ displayName.append( ", " );
+
+ // font style
+ ::FontWeight eWeight = vcl::unohelper::ConvertFontWeight( aFont.Weight );
+ TranslateId pStyleResID = RID_STR_FONTSTYLE_REGULAR;
+ if ( aFont.Slant == FontSlant_ITALIC )
+ {
+ if ( eWeight > WEIGHT_NORMAL )
+ pStyleResID = RID_STR_FONTSTYLE_BOLD_ITALIC;
+ else
+ pStyleResID = RID_STR_FONTSTYLE_ITALIC;
+ }
+ else
+ {
+ if ( eWeight > WEIGHT_NORMAL )
+ pStyleResID = RID_STR_FONTSTYLE_BOLD;
+ }
+ displayName.append(PcrRes(pStyleResID));
+
+ // font size
+ if ( aFont.Height )
+ {
+ displayName.append( ", " );
+ displayName.append( sal_Int32( aFont.Height ) );
+ }
+ }
+
+ aControlValue <<= displayName.makeStringAndClear();
+ }
+ break;
+
+ default:
+ aControlValue = PropertyHandlerComponent::convertToControlValue( _rPropertyName, _rPropertyValue, _rControlValueType );
+ break;
+
+ } // switch ( nPropId )
+
+ return aControlValue;
+ }
+
+ PropertyState SAL_CALL FormComponentPropertyHandler::getPropertyState( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_xPropertyState.is() )
+ return m_xPropertyState->getPropertyState( _rPropertyName );
+ return PropertyState_DIRECT_VALUE;
+ }
+
+ void SAL_CALL FormComponentPropertyHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyHandlerComponent::addPropertyChangeListener( _rxListener );
+ if ( m_xComponent.is() )
+ m_xComponent->addPropertyChangeListener( OUString(), _rxListener );
+ }
+
+ void SAL_CALL FormComponentPropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_xComponent.is() )
+ m_xComponent->removePropertyChangeListener( OUString(), _rxListener );
+ PropertyHandlerComponent::removePropertyChangeListener( _rxListener );
+ }
+
+ Sequence< Property > FormComponentPropertyHandler::doDescribeSupportedProperties() const
+ {
+ if ( !m_xComponentPropertyInfo.is() )
+ return Sequence< Property >();
+
+ std::vector< Property > aProperties;
+
+ Sequence< Property > aAllProperties( m_xComponentPropertyInfo->getProperties() );
+ aProperties.reserve( aAllProperties.getLength() );
+
+ // filter the properties
+ PropertyId nPropId( 0 );
+ OUString sDisplayName;
+
+ for ( Property & rProperty : asNonConstRange(aAllProperties) )
+ {
+ nPropId = m_pInfoService->getPropertyId( rProperty.Name );
+ if ( nPropId == -1 )
+ continue;
+ rProperty.Handle = nPropId;
+
+ sDisplayName = m_pInfoService->getPropertyTranslation( nPropId );
+ if ( sDisplayName.isEmpty() )
+ continue;
+
+ sal_uInt32 nPropertyUIFlags = m_pInfoService->getPropertyUIFlags( nPropId );
+ bool bIsVisibleForForms = ( nPropertyUIFlags & PROP_FLAG_FORM_VISIBLE ) != 0;
+ bool bIsVisibleForDialogs = ( nPropertyUIFlags & PROP_FLAG_DIALOG_VISIBLE ) != 0;
+
+ // depending on whether we're working for a form or a UNO dialog, some
+ // properties are not displayed
+ if ( ( m_eComponentClass == eFormControl && !bIsVisibleForForms )
+ || ( m_eComponentClass == eDialogControl && !bIsVisibleForDialogs )
+ )
+ continue;
+
+ // some generic sanity checks
+ if ( impl_shouldExcludeProperty_nothrow( rProperty ) )
+ continue;
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_BORDER:
+ case PROPERTY_ID_TABSTOP:
+ // BORDER and TABSTOP are normalized (see impl_normalizePropertyValue_nothrow)
+ // to not allow VOID values
+ rProperty.Attributes &= ~PropertyAttribute::MAYBEVOID;
+ break;
+
+ case PROPERTY_ID_LISTSOURCE:
+ // no cursor source if no Base is installed.
+ if ( SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ const_cast< FormComponentPropertyHandler* >( this )->m_bHaveListSource = true;
+ break;
+
+ case PROPERTY_ID_COMMAND:
+ // no cursor source if no Base is installed.
+ if ( SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ const_cast< FormComponentPropertyHandler* >( this )->m_bHaveCommand = true;
+ break;
+ } // switch ( nPropId )
+
+ aProperties.push_back( rProperty );
+ }
+
+ if ( aProperties.empty() )
+ return Sequence< Property >();
+ return comphelper::containerToSequence(aProperties);
+ }
+
+ Sequence< OUString > SAL_CALL FormComponentPropertyHandler::getSupersededProperties( )
+ {
+ return Sequence< OUString >( );
+ }
+
+ Sequence< OUString > SAL_CALL FormComponentPropertyHandler::getActuatingProperties( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return
+ {
+ PROPERTY_DATASOURCE,
+ PROPERTY_COMMAND,
+ PROPERTY_COMMANDTYPE,
+ PROPERTY_LISTSOURCE,
+ PROPERTY_LISTSOURCETYPE,
+ PROPERTY_SUBMIT_ENCODING,
+ PROPERTY_REPEAT,
+ PROPERTY_TABSTOP,
+ PROPERTY_BORDER,
+ PROPERTY_CONTROLSOURCE,
+ PROPERTY_DROPDOWN,
+ PROPERTY_IMAGE_URL,
+ PROPERTY_TARGET_URL,
+ PROPERTY_STRINGITEMLIST,
+ PROPERTY_BUTTONTYPE,
+ PROPERTY_ESCAPE_PROCESSING,
+ PROPERTY_TRISTATE,
+ PROPERTY_DECIMAL_ACCURACY,
+ PROPERTY_SHOWTHOUSANDSEP,
+ PROPERTY_FORMATKEY,
+ PROPERTY_EMPTY_IS_NULL,
+ PROPERTY_TOGGLE
+ };
+ }
+
+ LineDescriptor SAL_CALL FormComponentPropertyHandler::describePropertyLine( const OUString& _rPropertyName,
+ const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ if ( !_rxControlFactory.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+ Property aProperty( impl_getPropertyFromId_throw( nPropId ) );
+
+
+ // for the MultiLine property, we have different UI translations depending on the control
+ // type
+ if ( nPropId == PROPERTY_ID_MULTILINE )
+ {
+ if ( ( m_nClassId == FormComponentType::FIXEDTEXT )
+ || ( m_nClassId == FormComponentType::COMMANDBUTTON )
+ || ( m_nClassId == FormComponentType::RADIOBUTTON )
+ || ( m_nClassId == FormComponentType::CHECKBOX )
+ )
+ nPropId = PROPERTY_ID_WORDBREAK;
+ }
+
+ OUString sDisplayName = m_pInfoService->getPropertyTranslation( nPropId );
+ if ( sDisplayName.isEmpty() )
+ {
+ OSL_FAIL( "FormComponentPropertyHandler::describePropertyLine: did getSupportedProperties not work properly?" );
+ throw UnknownPropertyException();
+ }
+
+
+ LineDescriptor aDescriptor;
+ aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) );
+ aDescriptor.DisplayName = sDisplayName;
+
+ // for the moment, assume a text field
+ sal_Int16 nControlType = PropertyControlType::TextField;
+ bool bReadOnly = false;
+ aDescriptor.Control.clear();
+
+
+ bool bNeedDefaultStringIfVoidAllowed = false;
+
+ TypeClass eType = aProperty.Type.getTypeClass();
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_DEFAULT_SELECT_SEQ:
+ case PROPERTY_ID_SELECTEDITEMS:
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_SELECTION;
+ break;
+
+ case PROPERTY_ID_FILTER:
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_FILTER;
+ break;
+
+ case PROPERTY_ID_SORT:
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_ORDER;
+ break;
+
+ case PROPERTY_ID_MASTERFIELDS:
+ case PROPERTY_ID_DETAILFIELDS:
+ nControlType = PropertyControlType::StringListField;
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_FORMLINKFIELDS;
+ break;
+
+ case PROPERTY_ID_COMMAND:
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_SQLCOMMAND;
+ break;
+
+ case PROPERTY_ID_TABINDEX:
+ {
+ Reference< XControlContainer > xControlContext( impl_getContextControlContainer_nothrow() );
+ if ( xControlContext.is() )
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_TABINDEX;
+ nControlType = PropertyControlType::NumericField;
+ };
+ break;
+
+ case PROPERTY_ID_FONT:
+ bReadOnly = true;
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_FONT_TYPE;
+ break;
+
+ case PROPERTY_ID_TARGET_URL:
+ case PROPERTY_ID_IMAGE_URL:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/urlcontrol.ui", m_xContext));
+ auto pURLBox = std::make_unique<SvtURLBox>(xBuilder->weld_combo_box("urlcontrol"));
+ rtl::Reference<OFileUrlControl> pControl = new OFileUrlControl(std::move(pURLBox), std::move(xBuilder), false);
+ pControl->SetModifyHandler();
+ aDescriptor.Control = pControl;
+
+ aDescriptor.PrimaryButtonId = PROPERTY_ID_TARGET_URL == nPropId
+ ? OUString(UID_PROP_DLG_ATTR_TARGET_URL)
+ : OUString(UID_PROP_DLG_IMAGE_URL);
+ break;
+ }
+
+ case PROPERTY_ID_ECHO_CHAR:
+ nControlType = PropertyControlType::CharacterField;
+ break;
+
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ case PROPERTY_ID_FILLCOLOR:
+ case PROPERTY_ID_SYMBOLCOLOR:
+ case PROPERTY_ID_BORDERCOLOR:
+ case PROPERTY_ID_GRIDLINECOLOR:
+ case PROPERTY_ID_HEADERBACKGROUNDCOLOR:
+ case PROPERTY_ID_HEADERTEXTCOLOR:
+ case PROPERTY_ID_ACTIVESELECTIONBACKGROUNDCOLOR:
+ case PROPERTY_ID_ACTIVESELECTIONTEXTCOLOR:
+ case PROPERTY_ID_INACTIVESELECTIONBACKGROUNDCOLOR:
+ case PROPERTY_ID_INACTIVESELECTIONTEXTCOLOR:
+ nControlType = PropertyControlType::ColorListBox;
+
+ switch( nPropId )
+ {
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_BACKGROUNDCOLOR; break;
+ case PROPERTY_ID_FILLCOLOR:
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_FILLCOLOR; break;
+ case PROPERTY_ID_SYMBOLCOLOR:
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_SYMBOLCOLOR; break;
+ case PROPERTY_ID_BORDERCOLOR:
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_BORDERCOLOR; break;
+ case PROPERTY_ID_GRIDLINECOLOR:
+ aDescriptor.PrimaryButtonId = HID_PROP_GRIDLINECOLOR; break;
+ case PROPERTY_ID_HEADERBACKGROUNDCOLOR:
+ aDescriptor.PrimaryButtonId = HID_PROP_HEADERBACKGROUNDCOLOR; break;
+ case PROPERTY_ID_HEADERTEXTCOLOR:
+ aDescriptor.PrimaryButtonId = HID_PROP_HEADERTEXTCOLOR; break;
+ case PROPERTY_ID_ACTIVESELECTIONBACKGROUNDCOLOR:
+ aDescriptor.PrimaryButtonId = HID_PROP_ACTIVESELECTIONBACKGROUNDCOLOR; break;
+ case PROPERTY_ID_ACTIVESELECTIONTEXTCOLOR:
+ aDescriptor.PrimaryButtonId = HID_PROP_ACTIVESELECTIONTEXTCOLOR; break;
+ case PROPERTY_ID_INACTIVESELECTIONBACKGROUNDCOLOR:
+ aDescriptor.PrimaryButtonId = HID_PROP_INACTIVESELECTIONBACKGROUNDCOLOR; break;
+ case PROPERTY_ID_INACTIVESELECTIONTEXTCOLOR:
+ aDescriptor.PrimaryButtonId = HID_PROP_INACTIVESELECTIONTEXTCOLOR; break;
+ }
+ break;
+
+ case PROPERTY_ID_LABEL:
+ case PROPERTY_ID_URL:
+ nControlType = PropertyControlType::MultiLineTextField;
+ break;
+
+ case PROPERTY_ID_DEFAULT_TEXT:
+ {
+ if (FormComponentType::FILECONTROL == m_nClassId)
+ nControlType = PropertyControlType::TextField;
+ else
+ nControlType = PropertyControlType::MultiLineTextField;
+ }
+ break;
+
+ case PROPERTY_ID_TEXT:
+ if ( impl_componentHasProperty_throw( PROPERTY_MULTILINE ) )
+ nControlType = PropertyControlType::MultiLineTextField;
+ break;
+
+ case PROPERTY_ID_CONTROLLABEL:
+ bReadOnly = true;
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_CONTROLLABEL;
+ break;
+
+ case PROPERTY_ID_FORMATKEY:
+ case PROPERTY_ID_EFFECTIVE_MIN:
+ case PROPERTY_ID_EFFECTIVE_MAX:
+ case PROPERTY_ID_EFFECTIVE_DEFAULT:
+ case PROPERTY_ID_EFFECTIVE_VALUE:
+ {
+ // and the supplier is really available
+ Reference< XNumberFormatsSupplier > xSupplier;
+ m_xComponent->getPropertyValue( PROPERTY_FORMATSSUPPLIER ) >>= xSupplier;
+ if (xSupplier.is())
+ {
+ Reference< XUnoTunnel > xTunnel(xSupplier,UNO_QUERY);
+ DBG_ASSERT(xTunnel.is(), "FormComponentPropertyHandler::describePropertyLine : xTunnel is invalid!");
+ if (auto pSupplier = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>(xTunnel))
+ {
+ bool bIsFormatKey = (PROPERTY_ID_FORMATKEY == nPropId);
+
+ bReadOnly = bIsFormatKey;
+
+ if ( bIsFormatKey )
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/formattedsample.ui", m_xContext));
+ auto pContainer = xBuilder->weld_container("formattedsample");
+ rtl::Reference<OFormatSampleControl> pControl = new OFormatSampleControl(std::move(pContainer), std::move(xBuilder), false);
+ pControl->SetModifyHandler();
+
+ pControl->SetFormatSupplier(pSupplier);
+
+ aDescriptor.Control = pControl;
+
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_NUMBER_FORMAT;
+ }
+ else
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/formattedcontrol.ui", m_xContext));
+ auto pSpinButton = xBuilder->weld_formatted_spin_button("formattedcontrol");
+ rtl::Reference<OFormattedNumericControl> pControl = new OFormattedNumericControl(std::move(pSpinButton), std::move(xBuilder), false);
+ pControl->SetModifyHandler();
+
+ FormatDescription aDesc;
+ aDesc.pSupplier = pSupplier;
+ Any aFormatKeyValue = m_xComponent->getPropertyValue(PROPERTY_FORMATKEY);
+ if ( !( aFormatKeyValue >>= aDesc.nKey ) )
+ aDesc.nKey = 0;
+
+ pControl->SetFormatDescription( aDesc );
+
+ aDescriptor.Control = pControl;
+ }
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_ID_DATEMIN:
+ case PROPERTY_ID_DATEMAX:
+ case PROPERTY_ID_DEFAULT_DATE:
+ case PROPERTY_ID_DATE:
+ nControlType = PropertyControlType::DateField;
+ break;
+
+ case PROPERTY_ID_TIMEMIN:
+ case PROPERTY_ID_TIMEMAX:
+ case PROPERTY_ID_DEFAULT_TIME:
+ case PROPERTY_ID_TIME:
+ nControlType = PropertyControlType::TimeField;
+ break;
+
+ case PROPERTY_ID_VALUEMIN:
+ case PROPERTY_ID_VALUEMAX:
+ case PROPERTY_ID_DEFAULT_VALUE:
+ case PROPERTY_ID_VALUE:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/formattedcontrol.ui", m_xContext));
+ auto pSpinButton = xBuilder->weld_formatted_spin_button("formattedcontrol");
+ rtl::Reference<OFormattedNumericControl> pControl = new OFormattedNumericControl(std::move(pSpinButton), std::move(xBuilder), false);
+ pControl->SetModifyHandler();
+ aDescriptor.Control = pControl;
+
+ // we don't set a formatter so the control uses a default (which uses the application
+ // language and a default numeric format)
+ // but we set the decimal digits
+ pControl->SetDecimalDigits(
+ ::comphelper::getINT16( m_xComponent->getPropertyValue( PROPERTY_DECIMAL_ACCURACY ) )
+ );
+
+ // and the default value for the property
+ try
+ {
+ if (m_xPropertyState.is() && ((PROPERTY_ID_VALUEMIN == nPropId) || (PROPERTY_ID_VALUEMAX == nPropId)))
+ {
+ double nDefault = 0;
+ if ( m_xPropertyState->getPropertyDefault( aProperty.Name ) >>= nDefault )
+ pControl->SetDefaultValue(nDefault);
+ }
+ }
+ catch (const Exception&)
+ {
+ // just ignore it
+ }
+
+ break;
+ }
+
+ default:
+ if ( TypeClass_BYTE <= eType && eType <= TypeClass_DOUBLE )
+ {
+ sal_Int16 nDigits = 0;
+ sal_Int16 nValueUnit = -1;
+ sal_Int16 nDisplayUnit = -1;
+ if ( m_eComponentClass == eFormControl )
+ {
+ if ( ( nPropId == PROPERTY_ID_WIDTH )
+ || ( nPropId == PROPERTY_ID_ROWHEIGHT )
+ || ( nPropId == PROPERTY_ID_HEIGHT )
+ )
+ {
+ nValueUnit = MeasureUnit::MM_10TH;
+ nDisplayUnit = impl_getDocumentMeasurementUnit_throw();
+ nDigits = 2;
+ }
+ }
+
+ Optional< double > aValueNotPresent( false, 0 );
+ aDescriptor.Control = PropertyHandlerHelper::createNumericControl(
+ _rxControlFactory, nDigits, aValueNotPresent, aValueNotPresent );
+
+ Reference< XNumericControl > xNumericControl( aDescriptor.Control, UNO_QUERY_THROW );
+ if ( nValueUnit != -1 )
+ xNumericControl->setValueUnit( nValueUnit );
+ if ( nDisplayUnit != -1 )
+ xNumericControl->setDisplayUnit( nDisplayUnit );
+ }
+ break;
+ }
+
+ if ( eType == TypeClass_SEQUENCE )
+ nControlType = PropertyControlType::StringListField;
+
+ // boolean values
+ if ( eType == TypeClass_BOOLEAN )
+ {
+ if ( ( nPropId == PROPERTY_ID_SHOW_POSITION )
+ || ( nPropId == PROPERTY_ID_SHOW_NAVIGATION )
+ || ( nPropId == PROPERTY_ID_SHOW_RECORDACTIONS )
+ || ( nPropId == PROPERTY_ID_SHOW_FILTERSORT )
+ )
+ {
+ aDescriptor.Control = PropertyHandlerHelper::createListBoxControl(_rxControlFactory, RID_RSC_ENUM_SHOWHIDE, SAL_N_ELEMENTS(RID_RSC_ENUM_SHOWHIDE), false);
+ }
+ else
+ aDescriptor.Control = PropertyHandlerHelper::createListBoxControl(_rxControlFactory, RID_RSC_ENUM_YESNO, SAL_N_ELEMENTS(RID_RSC_ENUM_YESNO), false);
+ bNeedDefaultStringIfVoidAllowed = true;
+ }
+
+
+ // enum properties
+ sal_uInt32 nPropertyUIFlags = m_pInfoService->getPropertyUIFlags( nPropId );
+ bool bIsEnumProperty = ( nPropertyUIFlags & PROP_FLAG_ENUM ) != 0;
+ if ( bIsEnumProperty || ( PROPERTY_ID_TARGET_FRAME == nPropId ) )
+ {
+ std::vector< OUString > aEnumValues = m_pInfoService->getPropertyEnumRepresentations( nPropId );
+ std::vector< OUString >::const_iterator pStart = aEnumValues.begin();
+ std::vector< OUString >::const_iterator pEnd = aEnumValues.end();
+
+ // for a checkbox: if "ambiguous" is not allowed, remove this from the sequence
+ if ( ( PROPERTY_ID_DEFAULT_STATE == nPropId )
+ || ( PROPERTY_ID_STATE == nPropId )
+ )
+ {
+ if ( impl_componentHasProperty_throw( PROPERTY_TRISTATE ) )
+ {
+ if ( !::comphelper::getBOOL( m_xComponent->getPropertyValue( PROPERTY_TRISTATE ) ) )
+ { // remove the last sequence element
+ if ( pEnd > pStart )
+ --pEnd;
+ }
+ }
+ else
+ --pEnd;
+ }
+
+ if ( PROPERTY_ID_LISTSOURCETYPE == nPropId )
+ if ( FormComponentType::COMBOBOX == m_nClassId )
+ // remove the first sequence element -> value list not possible for combo boxes
+ ++pStart;
+
+ // copy the sequence
+ std::vector< OUString > aListEntries( pEnd - pStart );
+ std::copy( pStart, pEnd, aListEntries.begin() );
+
+ // create the control
+ if ( PROPERTY_ID_TARGET_FRAME == nPropId )
+ aDescriptor.Control = PropertyHandlerHelper::createComboBoxControl( _rxControlFactory, std::move(aListEntries), false );
+ else
+ {
+ aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory, std::move(aListEntries), false, false );
+ bNeedDefaultStringIfVoidAllowed = true;
+ }
+ }
+
+
+ switch( nPropId )
+ {
+ case PROPERTY_ID_REPEAT_DELAY:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/numericfield.ui", m_xContext));
+ auto pSpinButton = xBuilder->weld_metric_spin_button("numericfield", FieldUnit::MILLISECOND);
+ rtl::Reference<ONumericControl> pControl = new ONumericControl(std::move(pSpinButton), std::move(xBuilder), bReadOnly);
+ pControl->SetModifyHandler();
+ pControl->setMinValue( Optional< double >( true, 0 ) );
+ pControl->setMaxValue( Optional< double >( true, std::numeric_limits< double >::max() ) );
+ aDescriptor.Control = pControl;
+ }
+ break;
+
+ case PROPERTY_ID_TABINDEX:
+ case PROPERTY_ID_BOUNDCOLUMN:
+ case PROPERTY_ID_VISIBLESIZE:
+ case PROPERTY_ID_MAXTEXTLEN:
+ case PROPERTY_ID_LINEINCREMENT:
+ case PROPERTY_ID_BLOCKINCREMENT:
+ case PROPERTY_ID_SPININCREMENT:
+ {
+ Optional< double > aMinValue( true, 0 );
+ Optional< double > aMaxValue( true, 0x7FFFFFFF );
+
+ if ( nPropId == PROPERTY_ID_MAXTEXTLEN || nPropId == PROPERTY_ID_BOUNDCOLUMN )
+ aMinValue.Value = -1;
+ else if ( nPropId == PROPERTY_ID_VISIBLESIZE )
+ aMinValue.Value = 1;
+ else
+ aMinValue.Value = 0;
+
+ aDescriptor.Control = PropertyHandlerHelper::createNumericControl(
+ _rxControlFactory, 0, aMinValue, aMaxValue );
+ }
+ break;
+
+ case PROPERTY_ID_DECIMAL_ACCURACY:
+ {
+ Optional< double > aMinValue( true, 0 );
+ Optional< double > aMaxValue( true, 20 );
+
+ aDescriptor.Control = PropertyHandlerHelper::createNumericControl(
+ _rxControlFactory, 0, aMinValue, aMaxValue );
+ }
+ break;
+
+
+ // DataSource
+ case PROPERTY_ID_DATASOURCE:
+ {
+ aDescriptor.PrimaryButtonId = UID_PROP_DLG_ATTR_DATASOURCE;
+
+ std::vector< OUString > aListEntries;
+
+ Reference< XDatabaseContext > xDatabaseContext = sdb::DatabaseContext::create( m_xContext );
+ const Sequence< OUString > aDatasources = xDatabaseContext->getElementNames();
+ aListEntries.resize( aDatasources.getLength() );
+ std::copy( aDatasources.begin(), aDatasources.end(), aListEntries.begin() );
+ aDescriptor.Control = PropertyHandlerHelper::createComboBoxControl(
+ _rxControlFactory, std::move(aListEntries), true );
+ }
+ break;
+
+ case PROPERTY_ID_CONTROLSOURCE:
+ {
+ std::vector< OUString > aFieldNames;
+ impl_initFieldList_nothrow( aFieldNames );
+ aDescriptor.Control = PropertyHandlerHelper::createComboBoxControl(
+ _rxControlFactory, std::move(aFieldNames), false );
+ }
+ break;
+
+ case PROPERTY_ID_COMMAND:
+ impl_describeCursorSource_nothrow( aDescriptor, _rxControlFactory );
+ break;
+
+ case PROPERTY_ID_LISTSOURCE:
+ impl_describeListSourceUI_throw( aDescriptor, _rxControlFactory );
+ break;
+ }
+
+ if ( !aDescriptor.Control.is() )
+ aDescriptor.Control = _rxControlFactory->createPropertyControl( nControlType, bReadOnly );
+
+ if ( ( aProperty.Attributes & PropertyAttribute::MAYBEVOID ) != 0 )
+ {
+ // insert the string "Default" string, if necessary
+ if (bNeedDefaultStringIfVoidAllowed)
+ {
+ Reference< XStringListControl > xStringList( aDescriptor.Control, UNO_QUERY_THROW );
+ xStringList->prependListEntry( m_sDefaultValueString );
+ m_aPropertiesWithDefListEntry.insert( _rPropertyName );
+ }
+ }
+
+ if ( !aDescriptor.PrimaryButtonId.isEmpty() )
+ aDescriptor.HasPrimaryButton = true;
+ if ( !aDescriptor.SecondaryButtonId.isEmpty() )
+ aDescriptor.HasSecondaryButton = true;
+
+ bool bIsDataProperty = ( nPropertyUIFlags & PROP_FLAG_DATA_PROPERTY ) != 0;
+ aDescriptor.Category = bIsDataProperty ? std::u16string_view(u"Data") : std::u16string_view(u"General");
+ return aDescriptor;
+ }
+
+ InteractiveSelectionResult SAL_CALL FormComponentPropertyHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool /*_bPrimary*/, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ InteractiveSelectionResult eResult = InteractiveSelectionResult_Cancelled;
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_DEFAULT_SELECT_SEQ:
+ case PROPERTY_ID_SELECTEDITEMS:
+ if ( impl_dialogListSelection_nothrow( _rPropertyName, aGuard ) )
+ eResult = InteractiveSelectionResult_Success;
+ break;
+
+ case PROPERTY_ID_FILTER:
+ case PROPERTY_ID_SORT:
+ {
+ OUString sClause;
+ if ( impl_dialogFilterOrSort_nothrow( PROPERTY_ID_FILTER == nPropId, sClause, aGuard ) )
+ {
+ _rData <<= sClause;
+ eResult = InteractiveSelectionResult_ObtainedValue;
+ }
+ }
+ break;
+
+ case PROPERTY_ID_MASTERFIELDS:
+ case PROPERTY_ID_DETAILFIELDS:
+ if ( impl_dialogLinkedFormFields_nothrow( aGuard ) )
+ eResult = InteractiveSelectionResult_Success;
+ break;
+
+ case PROPERTY_ID_FORMATKEY:
+ if ( impl_dialogFormatting_nothrow( _rData, aGuard ) )
+ eResult = InteractiveSelectionResult_ObtainedValue;
+ break;
+
+ case PROPERTY_ID_IMAGE_URL:
+ if ( impl_browseForImage_nothrow( _rData, aGuard ) )
+ eResult = InteractiveSelectionResult_ObtainedValue;
+ break;
+
+ case PROPERTY_ID_TARGET_URL:
+ if ( impl_browseForTargetURL_nothrow( _rData, aGuard ) )
+ eResult = InteractiveSelectionResult_ObtainedValue;
+ break;
+
+ case PROPERTY_ID_FONT:
+ if ( impl_executeFontDialog_nothrow( _rData, aGuard ) )
+ eResult = InteractiveSelectionResult_ObtainedValue;
+ break;
+
+ case PROPERTY_ID_DATASOURCE:
+ if ( impl_browseForDatabaseDocument_throw( _rData, aGuard ) )
+ eResult = InteractiveSelectionResult_ObtainedValue;
+ break;
+
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ case PROPERTY_ID_FILLCOLOR:
+ case PROPERTY_ID_SYMBOLCOLOR:
+ case PROPERTY_ID_BORDERCOLOR:
+ case PROPERTY_ID_GRIDLINECOLOR:
+ case PROPERTY_ID_HEADERBACKGROUNDCOLOR:
+ case PROPERTY_ID_HEADERTEXTCOLOR:
+ case PROPERTY_ID_ACTIVESELECTIONBACKGROUNDCOLOR:
+ case PROPERTY_ID_ACTIVESELECTIONTEXTCOLOR:
+ case PROPERTY_ID_INACTIVESELECTIONBACKGROUNDCOLOR:
+ case PROPERTY_ID_INACTIVESELECTIONTEXTCOLOR:
+ if ( impl_dialogColorChooser_throw( nPropId, _rData, aGuard ) )
+ eResult = InteractiveSelectionResult_ObtainedValue;
+ break;
+
+ case PROPERTY_ID_CONTROLLABEL:
+ if ( impl_dialogChooseLabelControl_nothrow( _rData, aGuard ) )
+ eResult = InteractiveSelectionResult_ObtainedValue;
+ break;
+
+ case PROPERTY_ID_TABINDEX:
+ if ( impl_dialogChangeTabOrder_nothrow( aGuard ) )
+ eResult = InteractiveSelectionResult_Success;
+ break;
+
+ case PROPERTY_ID_COMMAND:
+ case PROPERTY_ID_LISTSOURCE:
+ if ( impl_doDesignSQLCommand_nothrow( _rxInspectorUI, nPropId ) )
+ eResult = InteractiveSelectionResult_Pending;
+ break;
+ default:
+ OSL_FAIL( "FormComponentPropertyHandler::onInteractivePropertySelection: request for a property which does not have dedicated UI!" );
+ break;
+ }
+ return eResult;
+ }
+
+ namespace
+ {
+ void lcl_rebuildAndResetCommand( const Reference< XObjectInspectorUI >& _rxInspectorUI, const Reference< XPropertyHandler >& _rxHandler )
+ {
+ OSL_PRECOND( _rxInspectorUI.is(), "lcl_rebuildAndResetCommand: invalid BrowserUI!" );
+ OSL_PRECOND( _rxHandler.is(), "lcl_rebuildAndResetCommand: invalid handler!" );
+ _rxInspectorUI->rebuildPropertyUI( PROPERTY_COMMAND );
+ _rxHandler->setPropertyValue( PROPERTY_COMMAND, Any( OUString() ) );
+ }
+ }
+
+ void SAL_CALL FormComponentPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nActuatingPropId( impl_getPropertyId_nothrow( _rActuatingPropertyName ) );
+
+ std::vector< PropertyId > aDependentProperties;
+
+ switch ( nActuatingPropId )
+ {
+ // ----- EscapeProcessing -----
+ case PROPERTY_ID_ESCAPE_PROCESSING:
+ aDependentProperties.push_back( PROPERTY_ID_FILTER );
+ aDependentProperties.push_back( PROPERTY_ID_SORT );
+ break; // case PROPERTY_ID_ESCAPE_PROCESSING
+
+ // ----- CommandType -----
+ case PROPERTY_ID_COMMANDTYPE:
+ // available commands (tables or queries) might have changed
+ if ( !_bFirstTimeInit && m_bHaveCommand )
+ lcl_rebuildAndResetCommand( _rxInspectorUI, this );
+ aDependentProperties.push_back( PROPERTY_ID_COMMAND );
+ break; // case PROPERTY_ID_COMMANDTYPE
+
+ // ----- DataSourceName -----
+ case PROPERTY_ID_DATASOURCE:
+ // reset the connection, now that we have a new data source
+ m_xRowSetConnection.clear();
+
+ // available list source values (tables or queries) might have changed
+ if ( !_bFirstTimeInit && m_bHaveListSource )
+ _rxInspectorUI->rebuildPropertyUI( PROPERTY_LISTSOURCE );
+
+ // available commands (tables or queries) might have changed
+ if ( !_bFirstTimeInit && m_bHaveCommand )
+ lcl_rebuildAndResetCommand( _rxInspectorUI, this );
+
+ // Command also depends on DataSource
+ aDependentProperties.push_back( PROPERTY_ID_COMMAND );
+ [[fallthrough]];
+
+ // ----- Command -----
+ case PROPERTY_ID_COMMAND:
+ aDependentProperties.push_back( PROPERTY_ID_FILTER );
+ aDependentProperties.push_back( PROPERTY_ID_SORT );
+ if ( m_bComponentIsSubForm )
+ aDependentProperties.push_back( PROPERTY_ID_DETAILFIELDS );
+ break;
+
+ // ----- ListSourceType -----
+ case PROPERTY_ID_LISTSOURCETYPE:
+ if ( !_bFirstTimeInit && m_bHaveListSource )
+ // available list source values (tables or queries) might have changed
+ _rxInspectorUI->rebuildPropertyUI( PROPERTY_LISTSOURCE );
+ aDependentProperties.push_back( PROPERTY_ID_STRINGITEMLIST );
+ aDependentProperties.push_back( PROPERTY_ID_TYPEDITEMLIST );
+ aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN );
+ [[fallthrough]];
+
+ // ----- StringItemList -----
+ case PROPERTY_ID_STRINGITEMLIST:
+ aDependentProperties.push_back( PROPERTY_ID_TYPEDITEMLIST );
+ aDependentProperties.push_back( PROPERTY_ID_SELECTEDITEMS );
+ aDependentProperties.push_back( PROPERTY_ID_DEFAULT_SELECT_SEQ );
+ break;
+
+ // ----- ListSource -----
+ case PROPERTY_ID_LISTSOURCE:
+ aDependentProperties.push_back( PROPERTY_ID_STRINGITEMLIST );
+ aDependentProperties.push_back( PROPERTY_ID_TYPEDITEMLIST );
+ break;
+
+ // ----- DataField -----
+ case PROPERTY_ID_CONTROLSOURCE:
+ {
+ OUString sControlSource;
+ _rNewValue >>= sControlSource;
+ if ( impl_componentHasProperty_throw( PROPERTY_FILTERPROPOSAL ) )
+ _rxInspectorUI->enablePropertyUI( PROPERTY_FILTERPROPOSAL, !sControlSource.isEmpty() );
+ if ( impl_componentHasProperty_throw( PROPERTY_EMPTY_IS_NULL ) )
+ _rxInspectorUI->enablePropertyUI( PROPERTY_EMPTY_IS_NULL, !sControlSource.isEmpty() );
+
+ aDependentProperties.push_back( PROPERTY_ID_BOUNDCOLUMN );
+ aDependentProperties.push_back( PROPERTY_ID_SCALEIMAGE );
+ aDependentProperties.push_back( PROPERTY_ID_SCALE_MODE );
+ aDependentProperties.push_back( PROPERTY_ID_INPUT_REQUIRED );
+ }
+ break;
+
+ case PROPERTY_ID_EMPTY_IS_NULL:
+ aDependentProperties.push_back( PROPERTY_ID_INPUT_REQUIRED );
+ break;
+
+ // ----- SubmitEncoding -----
+ case PROPERTY_ID_SUBMIT_ENCODING:
+ {
+ FormSubmitEncoding eEncoding = FormSubmitEncoding_URL;
+ if( ! (_rNewValue >>= eEncoding) )
+ SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_SUBMIT_ENCODING);
+ _rxInspectorUI->enablePropertyUI( PROPERTY_SUBMIT_METHOD, eEncoding == FormSubmitEncoding_URL );
+ }
+ break;
+
+ // ----- Repeat -----
+ case PROPERTY_ID_REPEAT:
+ {
+ bool bIsRepeating = false;
+ if( ! (_rNewValue >>= bIsRepeating) )
+ SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_REPEAT);
+ _rxInspectorUI->enablePropertyUI( PROPERTY_REPEAT_DELAY, bIsRepeating );
+ }
+ break;
+
+ // ----- TabStop -----
+ case PROPERTY_ID_TABSTOP:
+ {
+ if ( !impl_componentHasProperty_throw( PROPERTY_TABINDEX ) )
+ break;
+ bool bHasTabStop = false;
+ _rNewValue >>= bHasTabStop;
+ _rxInspectorUI->enablePropertyUI( PROPERTY_TABINDEX, bHasTabStop );
+ }
+ break;
+
+ // ----- Border -----
+ case PROPERTY_ID_BORDER:
+ {
+ sal_Int16 nBordeType = VisualEffect::NONE;
+ if( ! (_rNewValue >>= nBordeType) )
+ SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_BORDER);
+ _rxInspectorUI->enablePropertyUI( PROPERTY_BORDERCOLOR, nBordeType == VisualEffect::FLAT );
+ }
+ break;
+
+ // ----- DropDown -----
+ case PROPERTY_ID_DROPDOWN:
+ {
+ if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_LINECOUNT ) )
+ {
+ bool bDropDown = true;
+ _rNewValue >>= bDropDown;
+ _rxInspectorUI->enablePropertyUI( PROPERTY_LINECOUNT, bDropDown );
+ }
+ }
+ break;
+
+ // ----- ImageURL -----
+ case PROPERTY_ID_IMAGE_URL:
+ {
+ if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_IMAGEPOSITION ) )
+ {
+ OUString sImageURL;
+ if( ! (_rNewValue >>= sImageURL) )
+ SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_IMAGE_URL);
+ _rxInspectorUI->enablePropertyUI( PROPERTY_IMAGEPOSITION, !sImageURL.isEmpty() );
+ }
+
+ aDependentProperties.push_back( PROPERTY_ID_SCALEIMAGE );
+ aDependentProperties.push_back( PROPERTY_ID_SCALE_MODE );
+ }
+ break;
+
+ // ----- ButtonType -----
+ case PROPERTY_ID_BUTTONTYPE:
+ {
+ FormButtonType eButtonType( FormButtonType_PUSH );
+ if( ! (_rNewValue >>= eButtonType) )
+ SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_BUTTONTYPE);
+ _rxInspectorUI->enablePropertyUI( PROPERTY_TARGET_URL, FormButtonType_URL == eButtonType );
+ [[fallthrough]];
+ }
+
+ // ----- TargetURL -----
+ case PROPERTY_ID_TARGET_URL:
+ aDependentProperties.push_back( PROPERTY_ID_TARGET_FRAME );
+ break; // case PROPERTY_ID_TARGET_URL
+
+ // ----- TriState -----
+ case PROPERTY_ID_TRISTATE:
+ if ( !_bFirstTimeInit )
+ _rxInspectorUI->rebuildPropertyUI( m_eComponentClass == eFormControl ? OUString(PROPERTY_DEFAULT_STATE) : OUString(PROPERTY_STATE) );
+ break; // case PROPERTY_ID_TRISTATE
+
+ // ----- DecimalAccuracy -----
+ case PROPERTY_ID_DECIMAL_ACCURACY:
+ // ----- ShowThousandsSeparator -----
+ case PROPERTY_ID_SHOWTHOUSANDSEP:
+ {
+ bool bAccuracy = (PROPERTY_ID_DECIMAL_ACCURACY == nActuatingPropId);
+ sal_uInt16 nNewDigits = 0;
+ if ( bAccuracy )
+ {
+ if( ! (_rNewValue >>= nNewDigits) )
+ SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_DECIMAL_ACCURACY);
+ }
+ else
+ {
+ bool bUseSep = false;
+ if( ! (_rNewValue >>= bUseSep) )
+ SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_SHOWTHOUSANDSEP);
+ }
+
+ // propagate the changes to the min/max/default fields
+ OUString aAffectedProps[] = { OUString(PROPERTY_VALUE), OUString(PROPERTY_DEFAULT_VALUE), OUString(PROPERTY_VALUEMIN), OUString(PROPERTY_VALUEMAX) };
+ for (const OUString & aAffectedProp : aAffectedProps)
+ {
+ Reference< XPropertyControl > xControl;
+ try
+ {
+ xControl = _rxInspectorUI->getPropertyControl( aAffectedProp );
+ }
+ catch( const UnknownPropertyException& ) {}
+ if ( xControl.is() )
+ {
+ OFormattedNumericControl* pControl = dynamic_cast< OFormattedNumericControl* >( xControl.get() );
+ DBG_ASSERT( pControl, "FormComponentPropertyHandler::actuatingPropertyChanged: invalid control!" );
+ if (pControl)
+ {
+ if ( bAccuracy )
+ pControl->SetDecimalDigits( nNewDigits );
+ }
+ }
+ }
+ }
+ break;
+
+ // ----- FormatKey -----
+ case PROPERTY_ID_FORMATKEY:
+ {
+ FormatDescription aNewDesc;
+
+ Reference< XNumberFormatsSupplier > xSupplier;
+ if( ! (m_xComponent->getPropertyValue( PROPERTY_FORMATSSUPPLIER ) >>= xSupplier) )
+ SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_FORMATKEY);
+
+ Reference< XUnoTunnel > xTunnel( xSupplier, UNO_QUERY );
+ DBG_ASSERT(xTunnel.is(), "FormComponentPropertyHandler::actuatingPropertyChanged: xTunnel is invalid!");
+ if ( xTunnel.is() )
+ {
+ SvNumberFormatsSupplierObj* pSupplier = reinterpret_cast<SvNumberFormatsSupplierObj*>(xTunnel->getSomething(SvNumberFormatsSupplierObj::getUnoTunnelId()));
+ // the same again
+
+ aNewDesc.pSupplier = pSupplier;
+ if ( !( _rNewValue >>= aNewDesc.nKey ) )
+ aNewDesc.nKey = 0;
+
+ // give each control which has to know this an own copy of the description
+ OUString aFormattedPropertyControls[] = {
+ OUString(PROPERTY_EFFECTIVE_MIN), OUString(PROPERTY_EFFECTIVE_MAX), OUString(PROPERTY_EFFECTIVE_DEFAULT), OUString(PROPERTY_EFFECTIVE_VALUE)
+ };
+ for (const OUString & aFormattedPropertyControl : aFormattedPropertyControls)
+ {
+ Reference< XPropertyControl > xControl;
+ try
+ {
+ xControl = _rxInspectorUI->getPropertyControl( aFormattedPropertyControl );
+ }
+ catch( const UnknownPropertyException& ) {}
+ if ( xControl.is() )
+ {
+ OFormattedNumericControl* pControl = dynamic_cast< OFormattedNumericControl* >( xControl.get() );
+ DBG_ASSERT( pControl, "FormComponentPropertyHandler::actuatingPropertyChanged: invalid control!" );
+ if ( pControl )
+ pControl->SetFormatDescription( aNewDesc );
+ }
+ }
+ }
+ }
+ break;
+
+ case PROPERTY_ID_TOGGLE:
+ {
+ bool bIsToggleButton = false;
+ if( ! (_rNewValue >>= bIsToggleButton) )
+ SAL_WARN("extensions.propctrlr", "actuatingPropertyChanged: unable to get property " << PROPERTY_ID_TOGGLE);
+ _rxInspectorUI->enablePropertyUI( PROPERTY_DEFAULT_STATE, bIsToggleButton );
+ }
+ break;
+ case -1:
+ throw RuntimeException();
+ break;
+ default:
+ OSL_FAIL( "FormComponentPropertyHandler::actuatingPropertyChanged: did not register for this property!" );
+ break;
+
+ } // switch ( nActuatingPropId )
+
+ for (auto const& dependentProperty : aDependentProperties)
+ {
+ if ( impl_isSupportedProperty_nothrow(dependentProperty) )
+ impl_updateDependentProperty_nothrow(dependentProperty, _rxInspectorUI);
+ }
+ }
+
+ void FormComponentPropertyHandler::impl_updateDependentProperty_nothrow( PropertyId _nPropId, const Reference< XObjectInspectorUI >& _rxInspectorUI ) const
+ {
+ try
+ {
+ switch ( _nPropId )
+ {
+ // ----- StringItemList -----
+ case PROPERTY_ID_STRINGITEMLIST:
+ {
+ ListSourceType eLSType = ListSourceType_VALUELIST;
+ if( ! (impl_getPropertyValue_throw( PROPERTY_LISTSOURCETYPE ) >>= eLSType) )
+ SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_LISTSOURCETYPE);
+
+ OUString sListSource;
+ {
+ Sequence< OUString > aListSource;
+ Any aListSourceValue( impl_getPropertyValue_throw( PROPERTY_LISTSOURCE ) );
+ if ( aListSourceValue >>= aListSource )
+ {
+ if ( aListSource.hasElements() )
+ sListSource = aListSource[0];
+ }
+ else
+ if( ! (aListSourceValue >>= sListSource) )
+ SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_LISTSOURCE);
+ }
+
+ bool bIsEnabled = ( ( eLSType == ListSourceType_VALUELIST )
+ || ( sListSource.isEmpty() )
+ );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_STRINGITEMLIST, bIsEnabled );
+ }
+ break; // case PROPERTY_ID_STRINGITEMLIST
+
+ // ----- TypedItemList -----
+ case PROPERTY_ID_TYPEDITEMLIST:
+ {
+ /* TODO: anything? */
+ }
+ break; // case PROPERTY_ID_TYPEDITEMLIST
+
+ // ----- BoundColumn -----
+ case PROPERTY_ID_BOUNDCOLUMN:
+ {
+ ListSourceType eLSType = ListSourceType_VALUELIST;
+ if( ! (impl_getPropertyValue_throw( PROPERTY_LISTSOURCETYPE ) >>= eLSType) )
+ SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_LISTSOURCETYPE);
+
+ _rxInspectorUI->enablePropertyUI( PROPERTY_BOUNDCOLUMN,
+ ( eLSType != ListSourceType_VALUELIST )
+ );
+ }
+ break; // case PROPERTY_ID_BOUNDCOLUMN
+
+ // ----- ScaleImage, ScaleMode -----
+ case PROPERTY_ID_SCALEIMAGE:
+ case PROPERTY_ID_SCALE_MODE:
+ {
+ OUString sControlSource;
+ if ( impl_isSupportedProperty_nothrow( PROPERTY_ID_CONTROLSOURCE ) )
+ impl_getPropertyValue_throw( PROPERTY_CONTROLSOURCE ) >>= sControlSource;
+
+ OUString sImageURL;
+ impl_getPropertyValue_throw( PROPERTY_IMAGE_URL ) >>= sImageURL;
+
+ _rxInspectorUI->enablePropertyUI( impl_getPropertyNameFromId_nothrow( _nPropId ),
+ ( !sControlSource.isEmpty() ) || ( !sImageURL.isEmpty() )
+ );
+ }
+ break; // case PROPERTY_ID_SCALEIMAGE, PROPERTY_ID_SCALE_MODE
+
+ // ----- InputRequired -----
+ case PROPERTY_ID_INPUT_REQUIRED:
+ {
+ OUString sControlSource;
+ if( ! (impl_getPropertyValue_throw( PROPERTY_CONTROLSOURCE ) >>= sControlSource) )
+ SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_CONTROLSOURCE);
+
+ bool bEmptyIsNULL = false;
+ bool bHasEmptyIsNULL = impl_componentHasProperty_throw( PROPERTY_EMPTY_IS_NULL );
+ if ( bHasEmptyIsNULL )
+ if( ! (impl_getPropertyValue_throw( PROPERTY_EMPTY_IS_NULL ) >>= bEmptyIsNULL) )
+ SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_EMPTY_IS_NULL);
+
+ // if the control is not bound to a DB field, there is no sense in having the "Input required"
+ // property
+ // Also, if an empty input of this control are *not* written as NULL, but as empty strings,
+ // then "Input required" does not make sense, too (since there's always an input, even if the control
+ // is empty).
+ _rxInspectorUI->enablePropertyUI( PROPERTY_INPUT_REQUIRED,
+ ( !sControlSource.isEmpty() ) && ( !bHasEmptyIsNULL || bEmptyIsNULL )
+ );
+ }
+ break;
+
+ // ----- SelectedItems, DefaultSelection -----
+ case PROPERTY_ID_SELECTEDITEMS:
+ case PROPERTY_ID_DEFAULT_SELECT_SEQ:
+ {
+ Sequence< OUString > aEntries;
+ impl_getPropertyValue_throw( PROPERTY_STRINGITEMLIST ) >>= aEntries;
+ bool isEnabled = aEntries.hasElements();
+
+ if ( ( m_nClassId == FormComponentType::LISTBOX ) && ( m_eComponentClass == eFormControl ) )
+ {
+ ListSourceType eLSType = ListSourceType_VALUELIST;
+ impl_getPropertyValue_throw( PROPERTY_LISTSOURCETYPE ) >>= eLSType;
+ isEnabled &= ( eLSType == ListSourceType_VALUELIST );
+ }
+ _rxInspectorUI->enablePropertyUIElements( impl_getPropertyNameFromId_nothrow( _nPropId ),
+ PropertyLineElement::PrimaryButton, isEnabled );
+ }
+ break; // case PROPERTY_ID_DEFAULT_SELECT_SEQ
+
+ // ----- TargetFrame ------
+ case PROPERTY_ID_TARGET_FRAME:
+ {
+ OUString sTargetURL;
+ impl_getPropertyValue_throw( PROPERTY_TARGET_URL ) >>= sTargetURL;
+ FormButtonType eButtonType( FormButtonType_URL );
+ if ( 0 != m_nClassId )
+ {
+ if( ! (impl_getPropertyValue_throw( PROPERTY_BUTTONTYPE ) >>= eButtonType) )
+ SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_BUTTONTYPE);
+ }
+ // if m_nClassId is 0, then we're inspecting a form. In this case, eButtonType is always
+ // FormButtonType_URL here
+ _rxInspectorUI->enablePropertyUI( PROPERTY_TARGET_FRAME,
+ ( eButtonType == FormButtonType_URL ) && ( !sTargetURL.isEmpty() )
+ );
+ }
+ break;
+
+ // ----- Order ------
+ case PROPERTY_ID_SORT:
+ // ----- Filter ------
+ case PROPERTY_ID_FILTER:
+ {
+ Reference< XConnection > xConnection;
+ bool bAllowEmptyDS = ::dbtools::isEmbeddedInDatabase( m_xComponent, xConnection );
+
+ // if there's no escape processing, we cannot enter any values for this property
+ bool bDoEscapeProcessing( false );
+ impl_getPropertyValue_throw( PROPERTY_ESCAPE_PROCESSING ) >>= bDoEscapeProcessing;
+ _rxInspectorUI->enablePropertyUI(
+ impl_getPropertyNameFromId_nothrow( _nPropId ),
+ bDoEscapeProcessing
+ );
+
+ // also care for the browse button - enabled if we have escape processing, and a valid
+ // data source signature
+ _rxInspectorUI->enablePropertyUIElements(
+ impl_getPropertyNameFromId_nothrow( _nPropId ),
+ PropertyLineElement::PrimaryButton,
+ impl_hasValidDataSourceSignature_nothrow( m_xComponent, bAllowEmptyDS )
+ && bDoEscapeProcessing
+ );
+ }
+ break; // case PROPERTY_ID_FILTER:
+
+ // ----- Command -----
+ case PROPERTY_ID_COMMAND:
+ {
+ sal_Int32 nCommandType( CommandType::COMMAND );
+ if( ! (impl_getPropertyValue_throw( PROPERTY_COMMANDTYPE ) >>= nCommandType) )
+ SAL_WARN("extensions.propctrlr", "impl_updateDependentProperty_nothrow: unable to get property " << PROPERTY_COMMANDTYPE);
+
+ impl_ensureRowsetConnection_nothrow();
+ Reference< XConnection > xConnection = m_xRowSetConnection.getTyped();
+ bool bAllowEmptyDS = false;
+ if ( !xConnection.is() )
+ bAllowEmptyDS = ::dbtools::isEmbeddedInDatabase( m_xComponent, xConnection );
+
+ bool doEnable = ( nCommandType == CommandType::COMMAND )
+ && ( m_xRowSetConnection.is()
+ || xConnection.is()
+ || impl_hasValidDataSourceSignature_nothrow( m_xComponent, bAllowEmptyDS)
+ );
+
+ _rxInspectorUI->enablePropertyUIElements(
+ PROPERTY_COMMAND,
+ PropertyLineElement::PrimaryButton,
+ doEnable
+ );
+ }
+ break; // case PROPERTY_ID_COMMAND
+
+ // ----- DetailFields -----
+ case PROPERTY_ID_DETAILFIELDS:
+ {
+ Reference< XConnection > xConnection;
+ bool bAllowEmptyDS = ::dbtools::isEmbeddedInDatabase( m_xComponent, xConnection );
+
+ // both our current form, and its parent form, need to have a valid
+ // data source signature
+ bool bDoEnableMasterDetailFields =
+ impl_hasValidDataSourceSignature_nothrow( m_xComponent, bAllowEmptyDS )
+ && impl_hasValidDataSourceSignature_nothrow( Reference< XPropertySet >( m_xObjectParent, UNO_QUERY ), bAllowEmptyDS );
+
+ // in opposite to the other properties, here in real *two* properties are
+ // affected
+ _rxInspectorUI->enablePropertyUIElements( PROPERTY_DETAILFIELDS, PropertyLineElement::PrimaryButton, bDoEnableMasterDetailFields );
+ _rxInspectorUI->enablePropertyUIElements( PROPERTY_MASTERFIELDS, PropertyLineElement::PrimaryButton, bDoEnableMasterDetailFields );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "FormComponentPropertyHandler::impl_updateDependentProperty_nothrow: unexpected property to update!" );
+ break;
+
+ } // switch
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_updateDependentProperty_nothrow" );
+ }
+ }
+
+ void SAL_CALL FormComponentPropertyHandler::disposing()
+ {
+ PropertyHandlerComponent::disposing();
+ if ( m_xCommandDesigner.is() && m_xCommandDesigner->isActive() )
+ m_xCommandDesigner->dispose();
+ }
+
+ sal_Bool SAL_CALL FormComponentPropertyHandler::suspend( sal_Bool _bSuspend )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( _bSuspend )
+ if ( m_xCommandDesigner.is() && m_xCommandDesigner->isActive() )
+ return m_xCommandDesigner->suspend();
+ return true;
+ }
+
+ void FormComponentPropertyHandler::onNewComponent()
+ {
+ PropertyHandlerComponent::onNewComponent();
+ if ( !m_xComponentPropertyInfo.is() && m_xComponent.is() )
+ throw NullPointerException();
+
+ m_xPropertyState.set( m_xComponent, UNO_QUERY );
+ m_eComponentClass = eUnknown;
+ m_bComponentIsSubForm = m_bHaveListSource = m_bHaveCommand = false;
+ m_nClassId = 0;
+
+ try
+ {
+ // component class
+ m_eComponentClass = eUnknown;
+
+ if ( impl_componentHasProperty_throw( PROPERTY_WIDTH )
+ && impl_componentHasProperty_throw( PROPERTY_HEIGHT )
+ && impl_componentHasProperty_throw( PROPERTY_POSITIONX )
+ && impl_componentHasProperty_throw( PROPERTY_POSITIONY )
+ && impl_componentHasProperty_throw( PROPERTY_STEP )
+ && impl_componentHasProperty_throw( PROPERTY_TABINDEX )
+ )
+ {
+ m_eComponentClass = eDialogControl;
+ }
+ else
+ {
+ m_eComponentClass = eFormControl;
+ }
+
+
+ // (database) sub form?
+ Reference< XForm > xAsForm( m_xComponent, UNO_QUERY );
+ if ( xAsForm.is() )
+ {
+ Reference< XForm > xFormsParent( xAsForm->getParent(), css::uno::UNO_QUERY );
+ m_bComponentIsSubForm = xFormsParent.is();
+ }
+
+
+ // ClassId
+ Reference< XChild > xCompAsChild( m_xComponent, UNO_QUERY );
+ if ( xCompAsChild.is() )
+ m_xObjectParent = xCompAsChild->getParent();
+
+
+ // ClassId
+ impl_classifyControlModel_throw();
+ }
+ catch( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::onNewComponent" );
+ }
+ }
+
+ void FormComponentPropertyHandler::impl_classifyControlModel_throw( )
+ {
+ if ( impl_componentHasProperty_throw( PROPERTY_CLASSID ) )
+ {
+ if( ! (m_xComponent->getPropertyValue( PROPERTY_CLASSID ) >>= m_nClassId) )
+ SAL_WARN("extensions.propctrlr", "impl_classifyControlModel_throw: unable to get property " << PROPERTY_CLASSID);
+ }
+ else if ( eDialogControl == m_eComponentClass )
+ {
+ Reference< XServiceInfo > xServiceInfo( m_xComponent, UNO_QUERY );
+ if ( xServiceInfo.is() )
+ {
+ // it's a control model, and can tell about it's supported services
+ m_nClassId = FormComponentType::CONTROL;
+
+ const char* aControlModelServiceNames[] =
+ {
+ "UnoControlButtonModel",
+ "UnoControlCheckBoxModel",
+ "UnoControlComboBoxModel",
+ "UnoControlCurrencyFieldModel",
+ "UnoControlDateFieldModel",
+ "UnoControlEditModel",
+ "UnoControlFileControlModel",
+ "UnoControlFixedTextModel",
+ "UnoControlGroupBoxModel",
+ "UnoControlImageControlModel",
+ "UnoControlListBoxModel",
+ "UnoControlNumericFieldModel",
+ "UnoControlPatternFieldModel",
+ "UnoControlRadioButtonModel",
+ "UnoControlScrollBarModel",
+ "UnoControlSpinButtonModel",
+ "UnoControlTimeFieldModel",
+
+ "UnoControlFixedLineModel",
+ "UnoControlFormattedFieldModel",
+ "UnoControlProgressBarModel"
+ };
+ const sal_Int16 nClassIDs[] =
+ {
+ FormComponentType::COMMANDBUTTON,
+ FormComponentType::CHECKBOX,
+ FormComponentType::COMBOBOX,
+ FormComponentType::CURRENCYFIELD,
+ FormComponentType::DATEFIELD,
+ FormComponentType::TEXTFIELD,
+ FormComponentType::FILECONTROL,
+ FormComponentType::FIXEDTEXT,
+ FormComponentType::GROUPBOX,
+ FormComponentType::IMAGECONTROL,
+ FormComponentType::LISTBOX,
+ FormComponentType::NUMERICFIELD,
+ FormComponentType::PATTERNFIELD,
+ FormComponentType::RADIOBUTTON,
+ FormComponentType::SCROLLBAR,
+ FormComponentType::SPINBUTTON,
+ FormComponentType::TIMEFIELD,
+
+ ControlType::FIXEDLINE,
+ ControlType::FORMATTEDFIELD,
+ ControlType::PROGRESSBAR
+ };
+
+ sal_Int32 nKnownControlTypes = SAL_N_ELEMENTS( aControlModelServiceNames );
+ OSL_ENSURE( nKnownControlTypes == SAL_N_ELEMENTS( nClassIDs ),
+ "FormComponentPropertyHandler::impl_classifyControlModel_throw: inconsistence" );
+
+ for ( sal_Int32 i = 0; i < nKnownControlTypes; ++i )
+ {
+ OUString sServiceName = "com.sun.star.awt." +
+ OUString::createFromAscii( aControlModelServiceNames[ i ] );
+
+ if ( xServiceInfo->supportsService( sServiceName ) )
+ {
+ m_nClassId = nClassIDs[ i ];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ void FormComponentPropertyHandler::impl_normalizePropertyValue_nothrow( Any& _rValue, PropertyId _nPropId ) const
+ {
+ switch ( _nPropId )
+ {
+ case PROPERTY_ID_TABSTOP:
+ if ( !_rValue.hasValue() )
+ {
+ switch ( m_nClassId )
+ {
+ case FormComponentType::COMMANDBUTTON:
+ case FormComponentType::RADIOBUTTON:
+ case FormComponentType::CHECKBOX:
+ case FormComponentType::TEXTFIELD:
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ case FormComponentType::FILECONTROL:
+ case FormComponentType::DATEFIELD:
+ case FormComponentType::TIMEFIELD:
+ case FormComponentType::NUMERICFIELD:
+ case ControlType::FORMATTEDFIELD:
+ case FormComponentType::CURRENCYFIELD:
+ case FormComponentType::PATTERNFIELD:
+ _rValue <<= true;
+ break;
+ default:
+ _rValue <<= false;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ bool FormComponentPropertyHandler::isReportModel() const
+ {
+ Reference<XModel> xModel(impl_getContextDocument_nothrow());
+ Reference<XReportDefinition> xReportDef(xModel, css::uno::UNO_QUERY);
+ return xReportDef.is();
+ }
+
+ bool FormComponentPropertyHandler::impl_shouldExcludeProperty_nothrow( const Property& _rProperty ) const
+ {
+ OSL_ENSURE( _rProperty.Handle == m_pInfoService->getPropertyId( _rProperty.Name ),
+ "FormComponentPropertyHandler::impl_shouldExcludeProperty_nothrow: inconsistency in the property!" );
+
+ if ( _rProperty.Handle == PROPERTY_ID_CONTROLLABEL )
+ // prevent that this is caught below
+ return false;
+
+ if ( ( _rProperty.Type.getTypeClass() == TypeClass_INTERFACE )
+ || ( _rProperty.Type.getTypeClass() == TypeClass_UNKNOWN )
+ )
+ return true;
+
+ if ( ( _rProperty.Attributes & PropertyAttribute::TRANSIENT ) && ( m_eComponentClass != eDialogControl ) )
+ // strange enough, dialog controls declare a lot of their properties as transient
+ return true;
+
+ if ( _rProperty.Attributes & PropertyAttribute::READONLY )
+ return true;
+
+ switch ( _rProperty.Handle )
+ {
+ case PROPERTY_ID_MASTERFIELDS:
+ case PROPERTY_ID_DETAILFIELDS:
+ if ( !m_bComponentIsSubForm )
+ // no master and detail fields for forms which are no sub forms
+ return true;
+ break;
+
+ case PROPERTY_ID_DATASOURCE:
+ {
+ // don't show DataSource if the component is part of an embedded form document
+ Reference< XConnection > xConn;
+ if ( isEmbeddedInDatabase( m_xComponent, xConn ) )
+ return true;
+ }
+ break;
+
+ case PROPERTY_ID_TEXT:
+ // don't show the "Text" property of formatted fields
+ if ( ControlType::FORMATTEDFIELD == m_nClassId )
+ return true;
+ break;
+
+ case PROPERTY_ID_FORMATKEY:
+ case PROPERTY_ID_EFFECTIVE_MIN:
+ case PROPERTY_ID_EFFECTIVE_MAX:
+ case PROPERTY_ID_EFFECTIVE_DEFAULT:
+ case PROPERTY_ID_EFFECTIVE_VALUE:
+ // only if the set has a formats supplier, too
+ if ( !impl_componentHasProperty_throw( PROPERTY_FORMATSSUPPLIER ) )
+ return true;
+ // (form) date and time fields also have a formats supplier, but the format itself
+ // is reflected in another property
+ if ( ( FormComponentType::DATEFIELD == m_nClassId )
+ || ( FormComponentType::TIMEFIELD == m_nClassId )
+ )
+ return true;
+ break;
+
+ case PROPERTY_ID_SCALEIMAGE:
+ if ( impl_componentHasProperty_throw( PROPERTY_SCALE_MODE ) )
+ // ScaleImage is superseded by ScaleMode
+ return true;
+ break;
+
+ case PROPERTY_ID_WRITING_MODE:
+ if ( !SvtCTLOptions().IsCTLFontEnabled() )
+ return true;
+ break;
+ }
+
+ sal_uInt32 nPropertyUIFlags = m_pInfoService->getPropertyUIFlags( _rProperty.Handle );
+
+ // don't show experimental properties unless allowed to do so
+ if ( ( nPropertyUIFlags & PROP_FLAG_EXPERIMENTAL ) != 0 )
+ return true;
+
+ // no data properties if no Base is installed.
+ if ( ( nPropertyUIFlags & PROP_FLAG_DATA_PROPERTY ) != 0 )
+ if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ return true;
+
+ if ((nPropertyUIFlags & PROP_FLAG_REPORT_INVISIBLE) != 0 && isReportModel())
+ return true;
+
+ return false;
+ }
+
+ Reference< XRowSet > FormComponentPropertyHandler::impl_getRowSet_throw( ) const
+ {
+ Reference< XRowSet > xRowSet = m_xRowSet;
+ if ( !xRowSet.is() )
+ {
+ xRowSet.set( m_xComponent, UNO_QUERY );
+ if ( !xRowSet.is() )
+ {
+ xRowSet.set( m_xObjectParent, UNO_QUERY );
+ if ( !xRowSet.is() )
+ {
+ // are we inspecting a grid column?
+ if (Reference< XGridColumnFactory >( m_xObjectParent, UNO_QUERY) .is())
+ { // yes
+ Reference< XChild > xParentAsChild( m_xObjectParent, UNO_QUERY );
+ if ( xParentAsChild.is() )
+ xRowSet.set( xParentAsChild->getParent(), UNO_QUERY );
+ }
+ }
+ if ( !xRowSet.is() )
+ xRowSet = m_xRowSet;
+ }
+ DBG_ASSERT( xRowSet.is(), "FormComponentPropertyHandler::impl_getRowSet_throw: could not obtain the rowset for the introspectee!" );
+ }
+ return xRowSet;
+ }
+
+
+ Reference< XRowSet > FormComponentPropertyHandler::impl_getRowSet_nothrow( ) const
+ {
+ Reference< XRowSet > xReturn;
+ try
+ {
+ xReturn = impl_getRowSet_throw();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_getRowSet_nothrow" );
+ }
+ return xReturn;
+ }
+
+
+ void FormComponentPropertyHandler::impl_initFieldList_nothrow( std::vector< OUString >& _rFieldNames ) const
+ {
+ clearContainer( _rFieldNames );
+ try
+ {
+ weld::WaitObject aWaitCursor(impl_getDefaultDialogFrame_nothrow());
+
+ // get the form of the control we're inspecting
+ Reference< XPropertySet > xFormSet( impl_getRowSet_throw(), UNO_QUERY );
+ if ( !xFormSet.is() )
+ return;
+
+ OUString sObjectName;
+ if( ! (xFormSet->getPropertyValue( PROPERTY_COMMAND ) >>= sObjectName) )
+ SAL_WARN("extensions.propctrlr", "impl_initFieldList_nothrow: unable to get property " << PROPERTY_COMMAND);
+ // when there is no command we don't need to ask for columns
+ if ( !sObjectName.isEmpty() && impl_ensureRowsetConnection_nothrow() )
+ {
+ OUString aDatabaseName;
+ if( ! (xFormSet->getPropertyValue( PROPERTY_DATASOURCE ) >>= aDatabaseName) )
+ SAL_WARN("extensions.propctrlr", "impl_initFieldList_nothrow: unable to get property " << PROPERTY_DATASOURCE);
+ sal_Int32 nObjectType = CommandType::COMMAND;
+ if( ! (xFormSet->getPropertyValue( PROPERTY_COMMANDTYPE ) >>= nObjectType) )
+ SAL_WARN("extensions.propctrlr", "impl_initFieldList_nothrow: unable to get property " << PROPERTY_COMMANDTYPE);
+
+ const Sequence<OUString> aNames = ::dbtools::getFieldNamesByCommandDescriptor( m_xRowSetConnection, nObjectType, sObjectName );
+ _rFieldNames.insert( _rFieldNames.end(), aNames.begin(), aNames.end() );
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_initFieldList_nothrow" );
+ }
+ }
+
+ void FormComponentPropertyHandler::impl_displaySQLError_nothrow( const ::dbtools::SQLExceptionInfo& _rErrorDescriptor ) const
+ {
+ auto pTopLevel = impl_getDefaultDialogFrame_nothrow();
+ ::dbtools::showError(_rErrorDescriptor, pTopLevel ? pTopLevel->GetXWindow() : nullptr, m_xContext);
+ }
+
+ bool FormComponentPropertyHandler::impl_ensureRowsetConnection_nothrow() const
+ {
+ if ( !m_xRowSetConnection.is() )
+ {
+ uno::Reference<sdbc::XConnection> xConnection;
+ Any any = m_xContext->getValueByName( "ActiveConnection" );
+ any >>= xConnection;
+ m_xRowSetConnection.reset(xConnection,::dbtools::SharedConnection::NoTakeOwnership);
+ }
+ if ( m_xRowSetConnection.is() )
+ return true;
+
+ Reference< XRowSet > xRowSet( impl_getRowSet_throw() );
+ Reference< XPropertySet > xRowSetProps( xRowSet, UNO_QUERY );
+
+ // connect the row set - this is delegated to elsewhere - while observing errors
+ SQLExceptionInfo aError;
+ try
+ {
+ if ( xRowSetProps.is() )
+ {
+ weld::WaitObject aWaitCursor(impl_getDefaultDialogFrame_nothrow());
+ m_xRowSetConnection = ::dbtools::ensureRowSetConnection( xRowSet, m_xContext, nullptr );
+ }
+ }
+ catch ( const SQLException& ) { aError = SQLExceptionInfo( ::cppu::getCaughtException() ); }
+ catch ( const WrappedTargetException& e ) { aError = SQLExceptionInfo( e.TargetException ); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("extensions.propctrlr"); }
+
+ // report errors, if necessary
+ if ( aError.isValid() )
+ {
+ OUString sDataSourceName;
+ try
+ {
+ xRowSetProps->getPropertyValue( PROPERTY_DATASOURCE ) >>= sDataSourceName;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_ensureRowsetConnection_nothrow: caught an exception during error handling!" );
+ }
+ // additional info about what happened
+ INetURLObject aParser( sDataSourceName );
+ if ( aParser.GetProtocol() != INetProtocol::NotValid )
+ sDataSourceName = aParser.getBase( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+ OUString sInfo(PcrRes(RID_STR_UNABLETOCONNECT).replaceAll("$name$", sDataSourceName));
+ SQLContext aContext;
+ aContext.Message = sInfo;
+ aContext.NextException = aError.get();
+ impl_displaySQLError_nothrow( aContext );
+ }
+
+ return m_xRowSetConnection.is();
+ }
+
+
+ void FormComponentPropertyHandler::impl_describeCursorSource_nothrow( LineDescriptor& _out_rProperty, const Reference< XPropertyControlFactory >& _rxControlFactory ) const
+ {
+ try
+ {
+ weld::WaitObject aWaitCursor(impl_getDefaultDialogFrame_nothrow());
+
+
+ // Set the UI data
+ _out_rProperty.DisplayName = m_pInfoService->getPropertyTranslation( PROPERTY_ID_COMMAND );
+
+ _out_rProperty.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( PROPERTY_ID_COMMAND ) );
+ _out_rProperty.PrimaryButtonId = UID_PROP_DLG_SQLCOMMAND;
+
+
+ sal_Int32 nCommandType = CommandType::COMMAND;
+ impl_getPropertyValue_throw( PROPERTY_COMMANDTYPE ) >>= nCommandType;
+
+ switch ( nCommandType )
+ {
+ case CommandType::TABLE:
+ case CommandType::QUERY:
+ {
+ std::vector< OUString > aNames;
+ if ( impl_ensureRowsetConnection_nothrow() )
+ {
+ if ( nCommandType == CommandType::TABLE )
+ impl_fillTableNames_throw( aNames );
+ else
+ impl_fillQueryNames_throw( aNames );
+ }
+ _out_rProperty.Control = PropertyHandlerHelper::createComboBoxControl( _rxControlFactory, std::move(aNames), true );
+ }
+ break;
+
+ default:
+ _out_rProperty.Control = _rxControlFactory->createPropertyControl( PropertyControlType::MultiLineTextField, false );
+ break;
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_describeCursorSource_nothrow");
+ }
+ }
+
+
+ void FormComponentPropertyHandler::impl_fillTableNames_throw( std::vector< OUString >& _out_rNames ) const
+ {
+ OSL_PRECOND( m_xRowSetConnection.is(), "FormComponentPropertyHandler::impl_fillTableNames_throw: need a connection!" );
+ _out_rNames.resize( 0 );
+
+ Reference< XTablesSupplier > xSupplyTables( m_xRowSetConnection, UNO_QUERY );
+ Reference< XNameAccess > xTableNames;
+ if ( xSupplyTables.is() )
+ xTableNames = xSupplyTables->getTables();
+ DBG_ASSERT( xTableNames.is(), "FormComponentPropertyHandler::impl_fillTableNames_throw: no way to obtain the tables of the connection!" );
+ if ( !xTableNames.is() )
+ return;
+
+ const Sequence<OUString> aNames = xTableNames->getElementNames();
+ _out_rNames.insert( _out_rNames.end(), aNames.begin(), aNames.end() );
+ }
+
+
+ void FormComponentPropertyHandler::impl_fillQueryNames_throw( std::vector< OUString >& _out_rNames ) const
+ {
+ OSL_PRECOND( m_xRowSetConnection.is(), "FormComponentPropertyHandler::impl_fillQueryNames_throw: need a connection!" );
+ _out_rNames.resize( 0 );
+
+ Reference< XQueriesSupplier > xSupplyQueries( m_xRowSetConnection, UNO_QUERY );
+ Reference< XNameAccess > xQueryNames;
+ if ( xSupplyQueries.is() )
+ {
+ xQueryNames = xSupplyQueries->getQueries();
+ impl_fillQueryNames_throw(xQueryNames,_out_rNames);
+ }
+ }
+
+ void FormComponentPropertyHandler::impl_fillQueryNames_throw( const Reference< XNameAccess >& _xQueryNames,std::vector< OUString >& _out_rNames,std::u16string_view _sName ) const
+ {
+ DBG_ASSERT( _xQueryNames.is(), "FormComponentPropertyHandler::impl_fillQueryNames_throw: no way to obtain the queries of the connection!" );
+ if ( !_xQueryNames.is() )
+ return;
+
+ bool bAdd = !_sName.empty();
+
+ const Sequence<OUString> aQueryNames =_xQueryNames->getElementNames();
+ for ( const OUString& rQueryName : aQueryNames )
+ {
+ OUStringBuffer sTemp;
+ if ( bAdd )
+ {
+ sTemp.append(_sName);
+ sTemp.append("/");
+ }
+ sTemp.append(rQueryName);
+ Reference< XNameAccess > xSubQueries(_xQueryNames->getByName(rQueryName),UNO_QUERY);
+ if ( xSubQueries.is() )
+ impl_fillQueryNames_throw(xSubQueries,_out_rNames,sTemp.makeStringAndClear());
+ else
+ _out_rNames.push_back( sTemp.makeStringAndClear() );
+ }
+ }
+
+
+ void FormComponentPropertyHandler::impl_describeListSourceUI_throw( LineDescriptor& _out_rDescriptor, const Reference< XPropertyControlFactory >& _rxControlFactory ) const
+ {
+ OSL_PRECOND( m_xComponent.is(), "FormComponentPropertyHandler::impl_describeListSourceUI_throw: no component!" );
+
+
+ // read out ListSourceTypes
+ Any aListSourceType( m_xComponent->getPropertyValue( PROPERTY_LISTSOURCETYPE ) );
+
+ sal_Int32 nListSourceType = sal_Int32(ListSourceType_VALUELIST);
+ ::cppu::enum2int( nListSourceType, aListSourceType );
+ ListSourceType eListSourceType = static_cast<ListSourceType>(nListSourceType);
+
+ _out_rDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( PROPERTY_ID_LISTSOURCE );
+ _out_rDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( PROPERTY_ID_LISTSOURCE ) );
+
+
+ // set enums
+ switch( eListSourceType )
+ {
+ case ListSourceType_VALUELIST:
+ _out_rDescriptor.Control = _rxControlFactory->createPropertyControl( PropertyControlType::StringListField, false );
+ break;
+
+ case ListSourceType_TABLEFIELDS:
+ case ListSourceType_TABLE:
+ case ListSourceType_QUERY:
+ {
+ std::vector< OUString > aListEntries;
+ if ( impl_ensureRowsetConnection_nothrow() )
+ {
+ if ( eListSourceType == ListSourceType_QUERY )
+ impl_fillQueryNames_throw( aListEntries );
+ else
+ impl_fillTableNames_throw( aListEntries );
+ }
+ _out_rDescriptor.Control = PropertyHandlerHelper::createComboBoxControl( _rxControlFactory, std::move(aListEntries), false );
+ }
+ break;
+ case ListSourceType_SQL:
+ case ListSourceType_SQLPASSTHROUGH:
+ impl_ensureRowsetConnection_nothrow();
+ _out_rDescriptor.HasPrimaryButton = m_xRowSetConnection.is();
+ break;
+ default: break;
+ }
+ }
+
+ bool FormComponentPropertyHandler::impl_dialogListSelection_nothrow( const OUString& _rProperty, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ OSL_PRECOND(m_pInfoService, "FormComponentPropertyHandler::impl_dialogListSelection_"
+ "nothrow: no property meta data!");
+
+ OUString sPropertyUIName( m_pInfoService->getPropertyTranslation( m_pInfoService->getPropertyId( _rProperty ) ) );
+ ListSelectionDialog aDialog(impl_getDefaultDialogFrame_nothrow(), m_xComponent, _rProperty, sPropertyUIName);
+ _rClearBeforeDialog.clear();
+ return ( RET_OK == aDialog.run() );
+ }
+
+ bool FormComponentPropertyHandler::impl_dialogFilterOrSort_nothrow( bool _bFilter, OUString& _out_rSelectedClause, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ OSL_PRECOND( Reference< XRowSet >( m_xComponent, UNO_QUERY ).is(),
+ "FormComponentPropertyHandler::impl_dialogFilterOrSort_nothrow: to be called for forms only!" );
+
+ _out_rSelectedClause.clear();
+ bool bSuccess = false;
+ SQLExceptionInfo aErrorInfo;
+ try
+ {
+ if ( !impl_ensureRowsetConnection_nothrow() )
+ return false;
+
+ // get a composer for the statement which the form is currently based on
+ Reference< XSingleSelectQueryComposer > xComposer( ::dbtools::getCurrentSettingsComposer( m_xComponent, m_xContext, nullptr ) );
+ OSL_ENSURE( xComposer.is(), "FormComponentPropertyHandler::impl_dialogFilterOrSort_nothrow: could not obtain a composer!" );
+ if ( !xComposer.is() )
+ return false;
+
+ OUString sPropertyUIName( m_pInfoService->getPropertyTranslation( _bFilter ? PROPERTY_ID_FILTER : PROPERTY_ID_SORT ) );
+
+ // create the dialog
+ Reference< XExecutableDialog > xDialog;
+ if ( _bFilter)
+ {
+ xDialog.set( sdb::FilterDialog::createDefault(m_xContext) );
+ }
+ else
+ {
+ xDialog.set( sdb::OrderDialog::createDefault(m_xContext) );
+ }
+
+
+ // initialize the dialog
+ Reference< XPropertySet > xDialogProps( xDialog, UNO_QUERY_THROW );
+ xDialogProps->setPropertyValue("QueryComposer", Any( xComposer ) );
+ xDialogProps->setPropertyValue("RowSet", Any( m_xComponent ) );
+ if (auto pTopLevel = impl_getDefaultDialogFrame_nothrow())
+ xDialogProps->setPropertyValue("ParentWindow", Any(pTopLevel->GetXWindow()));
+ xDialogProps->setPropertyValue("Title", Any( sPropertyUIName ) );
+
+ _rClearBeforeDialog.clear();
+ bSuccess = ( xDialog->execute() != 0 );
+ if ( bSuccess )
+ _out_rSelectedClause = _bFilter ? xComposer->getFilter() : xComposer->getOrder();
+ }
+ catch (const SQLContext& e) { aErrorInfo = e; }
+ catch (const SQLWarning& e) { aErrorInfo = e; }
+ catch (const SQLException& e) { aErrorInfo = e; }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_dialogFilterOrSort_nothrow" );
+ }
+
+ if ( aErrorInfo.isValid() )
+ impl_displaySQLError_nothrow( aErrorInfo );
+
+ return bSuccess;
+ }
+
+
+ bool FormComponentPropertyHandler::impl_dialogLinkedFormFields_nothrow( ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ Reference< XForm > xDetailForm( m_xComponent, UNO_QUERY );
+ Reference< XForm > xMasterForm( m_xObjectParent, UNO_QUERY );
+ uno::Reference<beans::XPropertySet> xMasterProp(m_xObjectParent,uno::UNO_QUERY);
+ OSL_PRECOND( xDetailForm.is() && xMasterForm.is(), "FormComponentPropertyHandler::impl_dialogLinkedFormFields_nothrow: no forms!" );
+ if ( !xDetailForm.is() || !xMasterForm.is() )
+ return false;
+
+ FormLinkDialog aDialog(impl_getDefaultDialogFrame_nothrow(), m_xComponent, xMasterProp, m_xContext);
+ _rClearBeforeDialog.clear();
+ return ( RET_OK == aDialog.run() );
+ }
+
+ bool FormComponentPropertyHandler::impl_dialogFormatting_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ bool bChanged = false;
+ try
+ {
+ // create the itemset for the dialog
+ SfxItemSet aCoreSet(
+ SfxGetpApp()->GetPool(),
+ svl::Items<
+ SID_ATTR_NUMBERFORMAT_VALUE, SID_ATTR_NUMBERFORMAT_INFO>);
+ // ripped this somewhere ... don't understand it :(
+
+ // get the number formats supplier
+ Reference< XNumberFormatsSupplier > xSupplier;
+ m_xComponent->getPropertyValue( PROPERTY_FORMATSSUPPLIER ) >>= xSupplier;
+
+ DBG_ASSERT(xSupplier.is(), "FormComponentPropertyHandler::impl_dialogFormatting_nothrow: invalid call !" );
+ Reference< XUnoTunnel > xTunnel( xSupplier, UNO_QUERY_THROW );
+ SvNumberFormatsSupplierObj* pSupplier =
+ reinterpret_cast< SvNumberFormatsSupplierObj* >( xTunnel->getSomething( SvNumberFormatsSupplierObj::getUnoTunnelId() ) );
+ DBG_ASSERT( pSupplier != nullptr, "FormComponentPropertyHandler::impl_dialogFormatting_nothrow: invalid call !" );
+
+ sal_Int32 nFormatKey = 0;
+ impl_getPropertyValue_throw( PROPERTY_FORMATKEY ) >>= nFormatKey;
+ aCoreSet.Put( SfxUInt32Item( SID_ATTR_NUMBERFORMAT_VALUE, nFormatKey ) );
+
+ SvNumberFormatter* pFormatter = pSupplier->GetNumberFormatter();
+ double dPreviewVal = OFormatSampleControl::getPreviewValue(pFormatter,nFormatKey);
+ SvxNumberInfoItem aFormatter( pFormatter, dPreviewVal, PcrRes(RID_STR_TEXT_FORMAT), SID_ATTR_NUMBERFORMAT_INFO );
+ aCoreSet.Put( aFormatter );
+
+ // a tab dialog with a single page
+ SfxSingleTabDialogController aDialog(impl_getDefaultDialogFrame_nothrow(), &aCoreSet,
+ "cui/ui/formatnumberdialog.ui", "FormatNumberDialog");
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( RID_SVXPAGE_NUMBERFORMAT );
+ if ( !fnCreatePage )
+ throw RuntimeException(); // caught below
+
+ aDialog.SetTabPage((*fnCreatePage)(aDialog.get_content_area(), &aDialog, &aCoreSet));
+
+ _rClearBeforeDialog.clear();
+ if ( RET_OK == aDialog.run() )
+ {
+ const SfxItemSet* pResult = aDialog.GetOutputItemSet();
+
+ if (const SvxNumberInfoItem* pInfoItem = pResult->GetItem( SID_ATTR_NUMBERFORMAT_INFO ))
+ {
+ for (sal_uInt32 key : pInfoItem->GetDelFormats())
+ pFormatter->DeleteEntry(key);
+ }
+
+ if ( const SfxUInt32Item* pItem = pResult->GetItemIfSet( SID_ATTR_NUMBERFORMAT_VALUE, false ) )
+ {
+ _out_rNewValue <<= static_cast<sal_Int32>( pItem->GetValue() );
+ bChanged = true;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_dialogFormatting_nothrow" );
+ }
+ return bChanged;
+ }
+
+ bool FormComponentPropertyHandler::impl_browseForImage_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ bool bIsLink = true;// reflect the legacy behavior
+ OUString aStrTrans = m_pInfoService->getPropertyTranslation( PROPERTY_ID_IMAGE_URL );
+
+ weld::Window* pWin = impl_getDefaultDialogFrame_nothrow();
+ ::sfx2::FileDialogHelper aFileDlg(
+ ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW,
+ FileDialogFlags::Graphic, pWin);
+ aFileDlg.SetContext(sfx2::FileDialogHelper::FormsInsertImage);
+ aFileDlg.SetTitle(aStrTrans);
+ // non-linked images ( e.g. those located in the document
+ // stream ) only if document is available
+ bool bHandleNonLink;
+ {
+ Reference< XModel > xModel( impl_getContextDocument_nothrow() );
+ bHandleNonLink = xModel.is();
+ // Not implemented in reports
+ if (bHandleNonLink)
+ {
+ Reference< XReportDefinition > xReportDef( xModel, css::uno::UNO_QUERY );
+ bHandleNonLink = !xReportDef.is();
+ }
+ }
+
+ Reference< XFilePickerControlAccess > xController(aFileDlg.GetFilePicker(), UNO_QUERY);
+ DBG_ASSERT(xController.is(), "FormComponentPropertyHandler::impl_browseForImage_nothrow: missing the controller interface on the file picker!");
+ if (xController.is())
+ {
+ // do a preview by default
+ xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, css::uno::Any(true));
+
+ xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, css::uno::Any(bIsLink));
+ xController->enableControl(ExtendedFilePickerElementIds::CHECKBOX_LINK, bHandleNonLink );
+
+ }
+
+ OUString sCurValue;
+ if( ! (impl_getPropertyValue_throw( PROPERTY_IMAGE_URL ) >>= sCurValue) )
+ SAL_WARN("extensions.propctrlr", "impl_browseForImage_nothrow: unable to get property " << PROPERTY_IMAGE_URL);
+ if (!sCurValue.isEmpty())
+ {
+ aFileDlg.SetDisplayDirectory( sCurValue );
+ // TODO: need to set the display directory _and_ the default name
+ }
+
+ _rClearBeforeDialog.clear();
+ bool bSuccess = ( ERRCODE_NONE == aFileDlg.Execute() );
+ if ( bSuccess )
+ {
+ if ( bHandleNonLink && xController.is() )
+ {
+ xController->getValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0) >>= bIsLink;
+ }
+ if ( !bIsLink )
+ {
+ Graphic aGraphic;
+ aFileDlg.GetGraphic(aGraphic);
+
+ Reference< graphic::XGraphicObject > xGrfObj = graphic::GraphicObject::create( m_xContext );
+ xGrfObj->setGraphic( aGraphic.GetXGraphic() );
+
+ _out_rNewValue <<= xGrfObj;
+
+ }
+ else
+ _out_rNewValue <<= aFileDlg.GetPath();
+ }
+ return bSuccess;
+ }
+
+ bool FormComponentPropertyHandler::impl_browseForTargetURL_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ weld::Window* pWin = impl_getDefaultDialogFrame_nothrow();
+ ::sfx2::FileDialogHelper aFileDlg(
+ ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION,
+ FileDialogFlags::NONE, pWin);
+
+ OUString sURL;
+ if( ! (impl_getPropertyValue_throw( PROPERTY_TARGET_URL ) >>= sURL) )
+ SAL_WARN("extensions.propctrlr", "impl_browseForTargetURL_nothrow: unable to get property " << PROPERTY_TARGET_URL);
+ INetURLObject aParser( sURL );
+ if ( INetProtocol::File == aParser.GetProtocol() )
+ // set the initial directory only for file-URLs. Everything else
+ // is considered to be potentially expensive
+ aFileDlg.SetDisplayDirectory( sURL );
+
+ _rClearBeforeDialog.clear();
+ bool bSuccess = ( ERRCODE_NONE == aFileDlg.Execute() );
+ if ( bSuccess )
+ _out_rNewValue <<= aFileDlg.GetPath();
+ return bSuccess;
+ }
+
+ bool FormComponentPropertyHandler::impl_executeFontDialog_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ bool bSuccess = false;
+
+ // create an item set for use with the dialog
+ std::unique_ptr<SfxItemSet> pSet;
+ rtl::Reference<SfxItemPool> pPool;
+ std::vector<SfxPoolItem*>* pDefaults = nullptr;
+ ControlCharacterDialog::createItemSet(pSet, pPool, pDefaults);
+ ControlCharacterDialog::translatePropertiesToItems(m_xComponent, pSet.get());
+
+ { // do this in an own block. The dialog needs to be destroyed before we call
+ // destroyItemSet
+ ControlCharacterDialog aDlg(impl_getDefaultDialogFrame_nothrow(), *pSet);
+ _rClearBeforeDialog.clear();
+ if (RET_OK == aDlg.run())
+ {
+ const SfxItemSet* pOut = aDlg.GetOutputItemSet();
+ if ( pOut )
+ {
+ std::vector< NamedValue > aFontPropertyValues;
+ ControlCharacterDialog::translateItemsToProperties( *pOut, aFontPropertyValues );
+ _out_rNewValue <<= comphelper::containerToSequence(aFontPropertyValues);
+ bSuccess = true;
+ }
+ }
+ }
+
+ ControlCharacterDialog::destroyItemSet(pSet, pPool, pDefaults);
+ return bSuccess;
+ }
+
+
+ bool FormComponentPropertyHandler::impl_browseForDatabaseDocument_throw( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ weld::Window* pWin = impl_getDefaultDialogFrame_nothrow();
+ ::sfx2::FileDialogHelper aFileDlg(
+ ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION, FileDialogFlags::NONE,
+ "sdatabase", SfxFilterFlags::NONE, SfxFilterFlags::NONE, pWin);
+
+ OUString sDataSource;
+ if( ! (impl_getPropertyValue_throw( PROPERTY_DATASOURCE ) >>= sDataSource) )
+ SAL_WARN("extensions.propctrlr", "impl_browseForDatabaseDocument_throw: unable to get property " << PROPERTY_DATASOURCE);
+ INetURLObject aParser( sDataSource );
+ if ( INetProtocol::File == aParser.GetProtocol() )
+ // set the initial directory only for file-URLs. Everything else
+ // is considered to be potentially expensive
+ aFileDlg.SetDisplayDirectory( sDataSource );
+
+ std::shared_ptr<const SfxFilter> pFilter = SfxFilter::GetFilterByName("StarOffice XML (Base)");
+ OSL_ENSURE(pFilter,"Filter: StarOffice XML (Base) could not be found!");
+ if ( pFilter )
+ {
+ aFileDlg.SetCurrentFilter(pFilter->GetUIName());
+ //aFileDlg.AddFilter(pFilter->GetFilterName(),pFilter->GetDefaultExtension());
+ }
+
+ _rClearBeforeDialog.clear();
+ bool bSuccess = ( ERRCODE_NONE == aFileDlg.Execute() );
+ if ( bSuccess )
+ _out_rNewValue <<= aFileDlg.GetPath();
+ return bSuccess;
+ }
+
+ bool FormComponentPropertyHandler::impl_dialogColorChooser_throw( sal_Int32 _nColorPropertyId, Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ ::Color aColor;
+ if( ! (impl_getPropertyValue_throw( impl_getPropertyNameFromId_nothrow( _nColorPropertyId )) >>= aColor) )
+ SAL_WARN("extensions.propctrlr", "impl_dialogColorChooser_throw: unable to get property " << _nColorPropertyId);
+ SvColorDialog aColorDlg;
+ aColorDlg.SetColor( aColor );
+
+ _rClearBeforeDialog.clear();
+ weld::Window* pParent = impl_getDefaultDialogFrame_nothrow();
+ if (!aColorDlg.Execute(pParent))
+ return false;
+
+ _out_rNewValue <<= aColorDlg.GetColor();
+ return true;
+ }
+
+ bool FormComponentPropertyHandler::impl_dialogChooseLabelControl_nothrow( Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ weld::Window* pParent = impl_getDefaultDialogFrame_nothrow();
+ OSelectLabelDialog dlgSelectLabel(pParent, m_xComponent);
+ _rClearBeforeDialog.clear();
+ bool bSuccess = (RET_OK == dlgSelectLabel.run());
+ if ( bSuccess )
+ _out_rNewValue <<= dlgSelectLabel.GetSelected();
+ return bSuccess;
+ }
+
+
+ Reference< XControlContainer > FormComponentPropertyHandler::impl_getContextControlContainer_nothrow() const
+ {
+ Reference< XControlContainer > xControlContext;
+ Any any = m_xContext->getValueByName( "ControlContext" );
+ any >>= xControlContext;
+ return xControlContext;
+ }
+
+
+ bool FormComponentPropertyHandler::impl_dialogChangeTabOrder_nothrow( ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const
+ {
+ OSL_PRECOND( impl_getContextControlContainer_nothrow().is(), "FormComponentPropertyHandler::impl_dialogChangeTabOrder_nothrow: invalid control context!" );
+
+ Reference< XTabControllerModel > xTabControllerModel( impl_getRowSet_nothrow(), UNO_QUERY );
+ TabOrderDialog aDialog(impl_getDefaultDialogFrame_nothrow(), xTabControllerModel,
+ impl_getContextControlContainer_nothrow(), m_xContext);
+ _rClearBeforeDialog.clear();
+ return RET_OK == aDialog.run();
+ }
+
+ namespace
+ {
+
+ //- ISQLCommandPropertyUI
+
+ class ISQLCommandPropertyUI : public ISQLCommandAdapter
+ {
+ public:
+ /** returns the empty-string-terminated list of names of properties
+ whose UI is to be disabled while the SQL command property is
+ being edited.
+ */
+ virtual OUString* getPropertiesToDisable() = 0;
+ };
+
+
+ //- SQLCommandPropertyUI
+
+ class SQLCommandPropertyUI : public ISQLCommandPropertyUI
+ {
+ protected:
+ explicit SQLCommandPropertyUI( const Reference< XPropertySet >& _rxObject )
+ : m_xObject(_rxObject)
+ {
+ if ( !m_xObject.is() )
+ throw NullPointerException();
+ }
+
+ protected:
+ Reference< XPropertySet > m_xObject;
+ };
+
+
+ //- FormSQLCommandUI - declaration
+
+ class FormSQLCommandUI : public SQLCommandPropertyUI
+ {
+ public:
+ explicit FormSQLCommandUI( const Reference< XPropertySet >& _rxForm );
+
+ // ISQLCommandAdapter
+ virtual OUString getSQLCommand() const override;
+ virtual bool getEscapeProcessing() const override;
+ virtual void setSQLCommand( const OUString& _rCommand ) const override;
+ virtual void setEscapeProcessing( const bool _bEscapeProcessing ) const override;
+
+ // ISQLCommandPropertyUI
+ virtual OUString* getPropertiesToDisable() override;
+ };
+
+
+ //- FormSQLCommandUI - implementation
+
+
+ FormSQLCommandUI::FormSQLCommandUI( const Reference< XPropertySet >& _rxForm )
+ :SQLCommandPropertyUI( _rxForm )
+ {
+ }
+
+
+ OUString FormSQLCommandUI::getSQLCommand() const
+ {
+ OUString sCommand;
+ if( ! (m_xObject->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand) )
+ SAL_WARN("extensions.propctrlr", "getSQLCommand: unable to get property " << PROPERTY_COMMAND);
+ return sCommand;
+ }
+
+
+ bool FormSQLCommandUI::getEscapeProcessing() const
+ {
+ bool bEscapeProcessing( false );
+ if( ! (m_xObject->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bEscapeProcessing) )
+ SAL_WARN("extensions.propctrlr", "getSQLCommand: unable to get property " << PROPERTY_ESCAPE_PROCESSING);
+ return bEscapeProcessing;
+ }
+
+
+ void FormSQLCommandUI::setSQLCommand( const OUString& _rCommand ) const
+ {
+ m_xObject->setPropertyValue( PROPERTY_COMMAND, Any( _rCommand ) );
+ }
+
+
+ void FormSQLCommandUI::setEscapeProcessing( const bool _bEscapeProcessing ) const
+ {
+ m_xObject->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, Any( _bEscapeProcessing ) );
+ }
+
+
+ OUString* FormSQLCommandUI::getPropertiesToDisable()
+ {
+ static OUString s_aCommandProps[] = {
+ OUString(PROPERTY_DATASOURCE),
+ OUString(PROPERTY_COMMAND),
+ OUString(PROPERTY_COMMANDTYPE),
+ OUString(PROPERTY_ESCAPE_PROCESSING),
+ OUString()
+ };
+ return s_aCommandProps;
+ }
+
+ //- ValueListCommandUI - declaration
+
+ class ValueListCommandUI : public SQLCommandPropertyUI
+ {
+ public:
+ explicit ValueListCommandUI( const Reference< XPropertySet >& _rxListOrCombo );
+
+ // ISQLCommandAdapter
+ virtual OUString getSQLCommand() const override;
+ virtual bool getEscapeProcessing() const override;
+ virtual void setSQLCommand( const OUString& _rCommand ) const override;
+ virtual void setEscapeProcessing( const bool _bEscapeProcessing ) const override;
+
+ // ISQLCommandPropertyUI
+ virtual OUString* getPropertiesToDisable() override;
+ private:
+ mutable bool m_bPropertyValueIsList;
+ };
+
+
+ //- ValueListCommandUI - implementation
+
+
+ ValueListCommandUI::ValueListCommandUI( const Reference< XPropertySet >& _rxListOrCombo )
+ :SQLCommandPropertyUI( _rxListOrCombo )
+ ,m_bPropertyValueIsList( false )
+ {
+ }
+
+
+ OUString ValueListCommandUI::getSQLCommand() const
+ {
+ OUString sValue;
+ m_bPropertyValueIsList = false;
+
+ // for combo boxes, the property is a mere string
+ Any aValue( m_xObject->getPropertyValue( PROPERTY_LISTSOURCE ) );
+ if ( aValue >>= sValue )
+ return sValue;
+
+ Sequence< OUString > aValueList;
+ if ( aValue >>= aValueList )
+ {
+ m_bPropertyValueIsList = true;
+ if ( aValueList.hasElements() )
+ sValue = aValueList[0];
+ return sValue;
+ }
+
+ OSL_FAIL( "ValueListCommandUI::getSQLCommand: unexpected property type!" );
+ return sValue;
+ }
+
+
+ bool ValueListCommandUI::getEscapeProcessing() const
+ {
+ ListSourceType eType = ListSourceType_SQL;
+ if( ! (m_xObject->getPropertyValue( PROPERTY_LISTSOURCETYPE ) >>= eType) )
+ SAL_WARN("extensions.propctrlr", "getEscapeProcessing: unable to get property " << PROPERTY_LISTSOURCETYPE);
+ OSL_ENSURE( ( eType == ListSourceType_SQL ) || ( eType == ListSourceType_SQLPASSTHROUGH ),
+ "ValueListCommandUI::getEscapeProcessing: unexpected list source type!" );
+ return ( eType == ListSourceType_SQL );
+ }
+
+
+ void ValueListCommandUI::setSQLCommand( const OUString& _rCommand ) const
+ {
+ Any aValue;
+ if ( m_bPropertyValueIsList )
+ aValue <<= Sequence< OUString >( &_rCommand, 1 );
+ else
+ aValue <<= _rCommand;
+ m_xObject->setPropertyValue( PROPERTY_LISTSOURCE, aValue );
+ }
+
+
+ void ValueListCommandUI::setEscapeProcessing( const bool _bEscapeProcessing ) const
+ {
+ m_xObject->setPropertyValue( PROPERTY_LISTSOURCETYPE, Any(
+ _bEscapeProcessing ? ListSourceType_SQL : ListSourceType_SQLPASSTHROUGH ) );
+ }
+
+
+ OUString* ValueListCommandUI::getPropertiesToDisable()
+ {
+ static OUString s_aListSourceProps[] = {
+ OUString(PROPERTY_LISTSOURCETYPE),
+ OUString(PROPERTY_LISTSOURCE),
+ OUString()
+ };
+ return s_aListSourceProps;
+ }
+ }
+
+
+ bool FormComponentPropertyHandler::impl_doDesignSQLCommand_nothrow( const Reference< XObjectInspectorUI >& _rxInspectorUI, PropertyId _nDesignForProperty )
+ {
+ try
+ {
+ if ( m_xCommandDesigner.is() )
+ {
+ if ( m_xCommandDesigner->isActive() )
+ {
+ m_xCommandDesigner->raise();
+ return true;
+ }
+ m_xCommandDesigner->dispose();
+ m_xCommandDesigner.set( nullptr );
+ }
+
+ if ( !impl_ensureRowsetConnection_nothrow() )
+ return false;
+
+ Reference< XPropertySet > xComponentProperties( m_xComponent, UNO_SET_THROW );
+
+ ::rtl::Reference< ISQLCommandPropertyUI > xCommandUI;
+ switch ( _nDesignForProperty )
+ {
+ case PROPERTY_ID_COMMAND:
+ xCommandUI = new FormSQLCommandUI( xComponentProperties );
+ break;
+ case PROPERTY_ID_LISTSOURCE:
+ xCommandUI = new ValueListCommandUI( xComponentProperties );
+ break;
+ default:
+ OSL_FAIL( "FormComponentPropertyHandler::OnDesignerClosed: invalid property id!" );
+ return false;
+ }
+
+ m_xCommandDesigner.set( new SQLCommandDesigner( m_xContext, xCommandUI, m_xRowSetConnection, LINK( this, FormComponentPropertyHandler, OnDesignerClosed ) ) );
+
+ DBG_ASSERT( _rxInspectorUI.is(), "FormComponentPropertyHandler::OnDesignerClosed: no access to the property browser ui!" );
+ if ( m_xCommandDesigner->isActive() && _rxInspectorUI.is() )
+ {
+ m_xBrowserUI = _rxInspectorUI;
+ // disable everything which would affect this property
+ const OUString* pToDisable = xCommandUI->getPropertiesToDisable();
+ while ( !pToDisable->isEmpty() )
+ {
+ m_xBrowserUI->enablePropertyUIElements( *pToDisable++, PropertyLineElement::All, false );
+ }
+
+ // but enable the browse button for the property itself - so it can be used to raise the query designer
+ OUString sPropertyName( impl_getPropertyNameFromId_nothrow( _nDesignForProperty ) );
+ m_xBrowserUI->enablePropertyUIElements( sPropertyName, PropertyLineElement::PrimaryButton, true );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return m_xCommandDesigner.is();
+ }
+
+
+ IMPL_LINK_NOARG( FormComponentPropertyHandler, OnDesignerClosed, SQLCommandDesigner&, void )
+ {
+ OSL_ENSURE( m_xBrowserUI.is() && m_xCommandDesigner.is(), "FormComponentPropertyHandler::OnDesignerClosed: too many NULLs!" );
+ if ( !(m_xBrowserUI.is() && m_xCommandDesigner.is()) )
+ return;
+
+ try
+ {
+ ::rtl::Reference< ISQLCommandPropertyUI > xCommandUI(
+ dynamic_cast< ISQLCommandPropertyUI* >( m_xCommandDesigner->getPropertyAdapter().get() ) );
+ if ( !xCommandUI.is() )
+ throw NullPointerException();
+
+ const OUString* pToEnable = xCommandUI->getPropertiesToDisable();
+ while ( !pToEnable->isEmpty() )
+ {
+ m_xBrowserUI->enablePropertyUIElements( *pToEnable++, PropertyLineElement::All, true );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ bool FormComponentPropertyHandler::impl_hasValidDataSourceSignature_nothrow( const Reference< XPropertySet >& _xFormProperties, bool _bAllowEmptyDataSourceName )
+ {
+ bool bHas = false;
+ if ( _xFormProperties.is() )
+ {
+ try
+ {
+ OUString sPropertyValue;
+ // first, we need the name of an existent data source
+ if ( _xFormProperties->getPropertySetInfo()->hasPropertyByName(PROPERTY_DATASOURCE) )
+ _xFormProperties->getPropertyValue( PROPERTY_DATASOURCE ) >>= sPropertyValue;
+ bHas = ( !sPropertyValue.isEmpty() ) || _bAllowEmptyDataSourceName;
+
+ // then, the command should not be empty
+ if ( bHas )
+ {
+ if ( _xFormProperties->getPropertySetInfo()->hasPropertyByName(PROPERTY_COMMAND) )
+ _xFormProperties->getPropertyValue( PROPERTY_COMMAND ) >>= sPropertyValue;
+ bHas = !sPropertyValue.isEmpty();
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormComponentPropertyHandler::impl_hasValidDataSourceSignature_nothrow" );
+ }
+ }
+ return bHas;
+ }
+
+ OUString FormComponentPropertyHandler::impl_getDocumentURL_nothrow() const
+ {
+ OUString sURL;
+ try
+ {
+ Reference< XModel > xDocument( impl_getContextDocument_nothrow() );
+ if ( xDocument.is() )
+ sURL = xDocument->getURL();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return sURL;
+ }
+
+ ::cppu::IPropertyArrayHelper* FormComponentPropertyHandler::createArrayHelper( ) const
+ {
+ uno::Sequence< beans::Property > aProps;
+ describeProperties(aProps);
+ return new ::cppu::OPropertyArrayHelper(aProps);
+
+ }
+
+ ::cppu::IPropertyArrayHelper & FormComponentPropertyHandler::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+ uno::Reference< beans::XPropertySetInfo > SAL_CALL FormComponentPropertyHandler::getPropertySetInfo( )
+ {
+ return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_FormComponentPropertyHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::FormComponentPropertyHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formcomponenthandler.hxx b/extensions/source/propctrlr/formcomponenthandler.hxx
new file mode 100644
index 000000000..bc7367abb
--- /dev/null
+++ b/extensions/source/propctrlr/formcomponenthandler.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "propertyhandler.hxx"
+#include "sqlcommanddesign.hxx"
+#include <comphelper/uno3.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/propertycontainer.hxx>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <connectivity/dbtools.hxx>
+
+#include <set>
+
+
+namespace pcr
+{
+
+
+ //= ComponentClassification
+
+ enum ComponentClassification
+ {
+ eFormControl,
+ eDialogControl,
+ eUnknown
+ };
+
+
+ //= FormComponentPropertyHandler
+
+ class FormComponentPropertyHandler;
+ typedef ::comphelper::OPropertyArrayUsageHelper<FormComponentPropertyHandler> FormComponentPropertyHandler_PROP;
+ /** default ->XPropertyHandler for all form components.
+ */
+ class FormComponentPropertyHandler : public PropertyHandlerComponent,
+ public ::comphelper::OPropertyContainer,
+ public FormComponentPropertyHandler_PROP
+ {
+ private:
+ /// access to property states
+ css::uno::Reference< css::beans::XPropertyState > m_xPropertyState;
+ /// the parent of our component
+ css::uno::Reference< css::uno::XInterface > m_xObjectParent;
+
+ /// the database connection. Owned by us if and only if we created it ourself.
+ mutable ::dbtools::SharedConnection m_xRowSetConnection;
+ css::uno::Reference< css::sdbc::XRowSet > m_xRowSet;
+ /** helper component encapsulating the handling for the QueryDesign component for
+ interactively designing an SQL command
+ */
+ ::rtl::Reference< SQLCommandDesigner > m_xCommandDesigner;
+ css::uno::Reference< css::inspection::XObjectInspectorUI > m_xBrowserUI;
+
+ /// the string indicating a "default" (VOID) value in list-like controls
+ OUString m_sDefaultValueString;
+ /// all properties to whose control's we added ->m_sDefaultValueString
+ std::set< OUString > m_aPropertiesWithDefListEntry;
+ /// type of our component
+ ComponentClassification m_eComponentClass;
+ /// is our component a (database) sub form?
+ bool m_bComponentIsSubForm : 1;
+ /// our component has a "ListSource" property
+ bool m_bHaveListSource : 1;
+ /// our component has a "Command" property
+ bool m_bHaveCommand : 1;
+ /// the class id of the component - if applicable
+ sal_Int16 m_nClassId;
+
+ public:
+ explicit FormComponentPropertyHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ DECLARE_XINTERFACE( )
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+
+ protected:
+ virtual ~FormComponentPropertyHandler() override;
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
+
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ // XPropertyHandler overridables
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override;
+ virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override;
+ virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupersededProperties() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties() override;
+ virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+ virtual css::inspection::InteractiveSelectionResult
+ SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override;
+ virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override;
+
+ // XComponent
+ virtual void SAL_CALL disposing() override;
+
+ // PropertyHandler
+ virtual css::uno::Sequence< css::beans::Property >
+ doDescribeSupportedProperties() const override;
+ virtual void onNewComponent() override;
+
+ private:
+ /** classifies our component, in case it's a control model, by ClassId
+
+ Note that UNO dialog controls are also classified, though they don't have the ClassId property
+ */
+ void impl_classifyControlModel_throw();
+
+ bool isReportModel() const;
+
+ /** const-version of ->getPropertyValue
+ */
+ css::uno::Any impl_getPropertyValue_throw( const OUString& _rPropertyName ) const;
+
+ // some property values are faked, and not used in the way they're provided by our component
+ void impl_normalizePropertyValue_nothrow( css::uno::Any& _rValue, PropertyId _nPropId ) const;
+
+ /** determines whether we should exclude a given property from our "supported properties"
+ */
+ bool impl_shouldExcludeProperty_nothrow( const css::beans::Property& _rProperty ) const;
+
+ /** initializes the list of field names, if we're handling a control which supports the
+ DataField property
+ */
+ void impl_initFieldList_nothrow( std::vector< OUString >& rFieldNames ) const;
+
+ /** obtains the RowSet to which our component belongs
+
+ If the component is a RowSet itself, it's returned directly. Else, the parent
+ is examined for the XRowSet interface. If the parent is no XRowSet, then
+ a check is made whether our component is a grid control column, and if so,
+ the parent of the grid control is examined for the XRowSet interface.
+
+ Normally, at least one of those methods should succeed.
+ */
+ css::uno::Reference< css::sdbc::XRowSet > impl_getRowSet_throw( ) const;
+
+ /** nothrow-version of ->impl_getRowSet_throw
+ */
+ css::uno::Reference< css::sdbc::XRowSet > impl_getRowSet_nothrow( ) const;
+
+ /** connects the row set belonging to our introspected data aware form component,
+ and remembers the connection in ->m_xRowSetConnection.
+
+ If the row set already is connected, ->m_xRowSetConnection will be set, too, but
+ not take the ownership of the connection.
+
+ If ->m_xRowSetConnection is already set, nothing happens, so if you want to
+ force creation of a connection, you need to clear ->m_xRowSetConnection.
+ */
+ bool impl_ensureRowsetConnection_nothrow() const;
+
+ /** fills an ->LineDescriptor with information to represent a cursor source
+ of our form - that is, a table, a query, or an SQL statement.
+
+ As an example, if our form has currently a CommandType of TABLE, then the
+ value list in the LineDescriptor will contain a list of all tables
+ of the data source which the form is bound to.
+
+ @seealso impl_fillTableNames_throw
+ @seealso impl_fillQueryNames_throw
+ */
+ void impl_describeCursorSource_nothrow(
+ css::inspection::LineDescriptor& _out_rProperty,
+ const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory
+ ) const;
+
+ /** describes the UI for selecting a table name
+
+ @precond
+ m_xRowSetConnection is not <NULL/>
+ */
+ void impl_fillTableNames_throw( std::vector< OUString >& _out_rNames ) const;
+
+ /** describes the UI for selecting a query name
+
+ @precond
+ m_xRowSetConnection is not <NULL/>
+ */
+ void impl_fillQueryNames_throw( std::vector< OUString >& _out_rNames ) const;
+
+ /** describes the UI for selecting a query name
+
+ @precond
+ m_xRowSetConnection is not <NULL/>
+ */
+ void impl_fillQueryNames_throw( const css::uno::Reference< css::container::XNameAccess >& _xQueryNames
+ ,std::vector< OUString >& _out_rNames
+ ,std::u16string_view _sName = std::u16string_view() ) const;
+
+ /** describes the UI for selecting a ListSource (for list-like form controls)
+ @precond
+ ->m_xRowSetConnection is not <NULL/>
+ @precond
+ ->m_xComponent is not <NULL/>
+ */
+ void impl_describeListSourceUI_throw(
+ css::inspection::LineDescriptor& _out_rDescriptor,
+ const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory
+ ) const;
+
+ /** displays a database-related error to the user
+ */
+ void impl_displaySQLError_nothrow( const ::dbtools::SQLExceptionInfo& _rErrorDescriptor ) const;
+
+ /** let's the user chose a selection of entries from a string list, and stores this
+ selection in the given property
+ @return
+ <TRUE/> if and only if the user successfully changed the property
+ */
+ bool impl_dialogListSelection_nothrow( const OUString& _rProperty, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** executes a dialog for choosing a filter or sort criterion for a database form
+ @param _bFilter
+ <TRUE/> if the Filter property should be used, <FALSE/> if it's the Order
+ property
+ @param _out_rSelectedClause
+ the filter or order clause as chosen by the user
+ @precond
+ we're really inspecting a database form (well, a RowSet at least)
+ @return
+ <TRUE/> if and only if the user successfully chose a clause
+ */
+ bool impl_dialogFilterOrSort_nothrow( bool _bFilter, OUString& _out_rSelectedClause, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** executes a dialog which allows the user to choose the columns linking
+ a sub to a master form, and sets the respective MasterFields / SlaveFields
+ properties at the form.
+ @precond
+ we're inspecting (sub) database form
+ @return
+ <TRUE/> if and only if the user successfully enter master and slave fields
+ */
+ bool impl_dialogLinkedFormFields_nothrow( ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** executes a dialog which allows the user to modify the FormatKey
+ property of our component, by choosing a (number) format.
+ @precond
+ Our component actually has a FormatKey property.
+ @param _out_rNewValue
+ the new property value, if the user chose a new formatting
+ @return
+ <TRUE/> if and only if a new formatting has been chosen by the user.
+ In this case, ->_out_rNewValue is filled with the new property value
+ */
+ bool impl_dialogFormatting_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** executes a dialog which allows to the user to change the ImageURL property
+ of our component by browsing for an image file.
+ @precond
+ our component actually has an ImageURL property
+ @param _out_rNewValue
+ the new property value, if the user chose a new image url
+ @return
+ <TRUE/> if and only if a new image URL has been chosen by the user.
+ In this case, ->_out_rNewValue is filled with the new property value
+ */
+ bool impl_browseForImage_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** executes a dialog which allows the user to change the TargetURL property of
+ our component
+ @precond
+ our component actually has a TargetURL property
+ @param _out_rNewValue
+ the new property value, if the user chose a new TargetURL
+ @return
+ <TRUE/> if and only if a new TargetURL has been chosen by the user.
+ In this case, ->_out_rNewValue is filled with the new property value
+ */
+ bool impl_browseForTargetURL_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** executes a dialog which allows the user to change the font, plus related properties,
+ of our component
+ @precond
+ our component actually has a Font property
+ @param _out_rNewValue
+ a value describing the new font, as <code>Sequence&lt; NamedValue &gt;</code>
+ @return
+ <TRUE/> if and only if the user successfully changed the font of our component
+ */
+ bool impl_executeFontDialog_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** allows the user browsing for a database document
+ @precond
+ our component actually has a DataSource property
+ @param _out_rNewValue
+ the new property value, if the user chose a new DataSource
+ @return
+ <TRUE/> if and only if a new DataSource has been chosen by the user.
+ In this case, ->_out_rNewValue is filled with the new property value
+ */
+ bool impl_browseForDatabaseDocument_throw( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** raises a dialog which allows the user to choose a color
+ @param _nColorPropertyId
+ the ID of the color property
+ @param _out_rNewValue
+ the chosen color value
+ @return
+ <TRUE/> if and only if a color was chosen by the user
+ */
+ bool impl_dialogColorChooser_throw( sal_Int32 _nColorPropertyId, css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** raises a dialog which allows the user to choose a label control for our component
+ @param _out_rNewValue
+ the chosen label control, if any
+ @return
+ <TRUE/> if and only if a label control was chosen by the user
+ */
+ bool impl_dialogChooseLabelControl_nothrow( css::uno::Any& _out_rNewValue, ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** raises a dialog which lets the user chose the tab order of controls of a form
+ @precond
+ we have a view control container in which our controls live
+ @return
+ <TRUE/> if and only if the user successfully changed the tab order
+ @seealso impl_getContextControlContainer_nothrow
+ */
+ bool impl_dialogChangeTabOrder_nothrow( ::osl::ClearableMutexGuard& _rClearBeforeDialog ) const;
+
+ /** retrieves the context for controls, whose model(s) we're inspecting
+
+ If we're inspecting a control model, this is usually part of a set of controls
+ and control models, where the controls live in a certain context (a ->XControlContainer).
+ If we know this context, we can enable additional special functionality.
+
+ The ->XComponentContext in which we were created is examined for a value
+ named "ControlContext", and this value is returned.
+ */
+ css::uno::Reference< css::awt::XControlContainer >
+ impl_getContextControlContainer_nothrow() const;
+
+ /** opens a query design window for interactively designing the SQL command of a
+ database form
+ @param _rxUIUpdate
+ access to the property browser UI
+ @param _nDesignForProperty
+ the ID for the property for which the designer is opened
+ @return
+ <TRUE/> if the window was successfully opened, or was previously open,
+ <FALSE/> otherwise
+ */
+ bool impl_doDesignSQLCommand_nothrow(
+ const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI,
+ PropertyId _nDesignForProperty
+ );
+
+ /** updates a property (UI) whose state depends on more than one other property
+
+ ->actuatingPropertyChanged is called for certain properties in whose changes
+ we expressed interes (->getActuatingProperty). Now such a property change can
+ result in simple UI updates, for instance another property being enabled or disabled.
+
+ However, it can also result in a more complex change: The current (UI) state might
+ depend on the value of more than one other property. Those dependent properties (their
+ UI, more precisely) are updated in this method.
+
+ @param _nPropid
+ the ->PropertyId of the dependent property whose UI state is to be updated
+
+ @param _rxInspectorUI
+ provides access to the property browser UI. Must not be <NULL/>.
+ */
+ void impl_updateDependentProperty_nothrow( PropertyId _nPropId, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) const;
+
+ /** determines whether the given form has a valid data source signature.
+
+ Valid here means that the DataSource property denotes an existing data source, and the
+ Command property is not empty. No check is made whether the value of the Command property
+ denotes an existent object, since this would be way too expensive.
+
+ @param _xFormProperties
+ the form to check. Must not be <NULL/>.
+ @param _bAllowEmptyDataSourceName
+ determine whether an empty data source name is allowed (<TRUE/>), and should not
+ lead to rejection
+ */
+ static bool impl_hasValidDataSourceSignature_nothrow(
+ const css::uno::Reference< css::beans::XPropertySet >& _xFormProperties,
+ bool _bAllowEmptyDataSourceName );
+
+ /** returns the URL of our context document
+ @return
+ */
+ OUString impl_getDocumentURL_nothrow() const;
+
+ private:
+ DECL_LINK( OnDesignerClosed, SQLCommandDesigner&, void );
+
+ private:
+ FormComponentPropertyHandler( const FormComponentPropertyHandler& ) = delete;
+ FormComponentPropertyHandler& operator=( const FormComponentPropertyHandler& ) = delete;
+
+ private:
+ using ::comphelper::OPropertyContainer::addPropertyChangeListener;
+ using ::comphelper::OPropertyContainer::removePropertyChangeListener;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formcontroller.cxx b/extensions/source/propctrlr/formcontroller.cxx
new file mode 100644
index 000000000..6e94aa0d1
--- /dev/null
+++ b/extensions/source/propctrlr/formcontroller.cxx
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "formcontroller.hxx"
+#include "pcrcommon.hxx"
+#include "formstrings.hxx"
+#include "defaultforminspection.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <cppuhelper/typeprovider.hxx>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::TypeClass_INTERFACE;
+ using ::com::sun::star::uno::TypeClass_STRING;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::inspection::XObjectInspectorModel;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::util::VetoException;
+ using ::com::sun::star::beans::PropertyVetoException;
+ using ::com::sun::star::uno::UNO_QUERY;
+
+ namespace PropertyAttribute = css::beans::PropertyAttribute;
+
+
+ //= FormController
+
+
+ FormController::FormController( const Reference< XComponentContext >& _rxContext,
+ const OUString& sImplementationName,
+ const css::uno::Sequence<OUString>& aSupportedServiceNames,
+ bool _bUseFormFormComponentHandlers )
+ :OPropertyBrowserController( _rxContext )
+ ,FormController_PropertyBase1( m_aBHelper )
+ ,m_sImplementationName( sImplementationName )
+ ,m_aSupportedServiceNames( aSupportedServiceNames )
+ {
+ osl_atomic_increment( &m_refCount );
+ {
+ Reference< XObjectInspectorModel > xModel(
+ *(new DefaultFormComponentInspectorModel( _bUseFormFormComponentHandlers )),
+ UNO_QUERY_THROW
+ );
+ setInspectorModel( xModel );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ FormController::~FormController()
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( FormController, OPropertyBrowserController, FormController_PropertyBase1 )
+
+
+ Sequence< Type > SAL_CALL FormController::getTypes( )
+ {
+ ::cppu::OTypeCollection aTypes(
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get(),
+ cppu::UnoType<XFastPropertySet>::get(),
+ OPropertyBrowserController::getTypes());
+ return aTypes.getTypes();
+ }
+
+
+ IMPLEMENT_GET_IMPLEMENTATION_ID( FormController )
+
+
+ OUString SAL_CALL FormController::getImplementationName( )
+ {
+ return m_sImplementationName;
+ }
+
+
+ Sequence< OUString > SAL_CALL FormController::getSupportedServiceNames( )
+ {
+ Sequence< OUString > aSupported( m_aSupportedServiceNames );
+ aSupported.realloc( aSupported.getLength() + 1 );
+ aSupported.getArray()[ aSupported.getLength() - 1 ] = "com.sun.star.inspection.ObjectInspector";
+ return aSupported;
+ }
+
+
+ Reference< XPropertySetInfo > SAL_CALL FormController::getPropertySetInfo( )
+ {
+ return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
+ }
+
+
+ ::cppu::IPropertyArrayHelper& SAL_CALL FormController::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+
+ ::cppu::IPropertyArrayHelper* FormController::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps{
+ Property(
+ PROPERTY_CURRENTPAGE,
+ OWN_PROPERTY_ID_CURRENTPAGE,
+ ::cppu::UnoType<OUString>::get(),
+ PropertyAttribute::TRANSIENT
+ ),
+ Property(
+ PROPERTY_INTROSPECTEDOBJECT,
+ OWN_PROPERTY_ID_INTROSPECTEDOBJECT,
+ cppu::UnoType<XPropertySet>::get(),
+ PropertyAttribute::TRANSIENT | PropertyAttribute::CONSTRAINED
+ )
+ };
+ return new ::cppu::OPropertyArrayHelper( aProps );
+ }
+
+
+ sal_Bool SAL_CALL FormController::convertFastPropertyValue( Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue )
+ {
+ switch ( nHandle )
+ {
+ case OWN_PROPERTY_ID_INTROSPECTEDOBJECT:
+ if ( rValue.getValueTypeClass() != TypeClass_INTERFACE )
+ throw IllegalArgumentException();
+ break;
+ case OWN_PROPERTY_ID_CURRENTPAGE:
+ if ( rValue.getValueTypeClass() != TypeClass_STRING )
+ throw IllegalArgumentException();
+ break;
+ }
+
+ getFastPropertyValue( rOldValue, nHandle );
+ rConvertedValue = rValue;
+ return true;
+ }
+
+
+ void SAL_CALL FormController::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+ {
+ switch ( _nHandle )
+ {
+ case OWN_PROPERTY_ID_INTROSPECTEDOBJECT:
+ {
+ Reference< XObjectInspectorModel > xModel( getInspectorModel() );
+ if ( xModel.is() )
+ {
+ try
+ {
+ m_xCurrentInspectee.set( _rValue, UNO_QUERY );
+ Sequence< Reference< XInterface > > aObjects;
+ if ( m_xCurrentInspectee.is() )
+ {
+ aObjects = { m_xCurrentInspectee };
+ }
+
+ Reference< XObjectInspector > xInspector( *this, UNO_QUERY_THROW );
+ xInspector->inspect( aObjects );
+ }
+ catch( const VetoException& e )
+ {
+ throw PropertyVetoException( e.Message, e.Context );
+ }
+ }
+ }
+ break;
+ case OWN_PROPERTY_ID_CURRENTPAGE:
+ restoreViewData( _rValue );
+ break;
+ }
+ }
+
+
+ void SAL_CALL FormController::getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const
+ {
+ switch ( nHandle )
+ {
+ case OWN_PROPERTY_ID_INTROSPECTEDOBJECT:
+ rValue <<= m_xCurrentInspectee;
+ break;
+
+ case OWN_PROPERTY_ID_CURRENTPAGE:
+ rValue = const_cast< FormController* >( this )->getViewData();
+ break;
+ }
+ }
+
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_FormController_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::FormController( context,
+ "org.openoffice.comp.extensions.FormController",
+ { "com.sun.star.form.PropertyBrowserController" },
+ true ) );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_DialogController_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::FormController( context,
+ "org.openoffice.comp.extensions.DialogController",
+ { "com.sun.star.awt.PropertyBrowserController" },
+ false ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formcontroller.hxx b/extensions/source/propctrlr/formcontroller.hxx
new file mode 100644
index 000000000..8c285160e
--- /dev/null
+++ b/extensions/source/propctrlr/formcontroller.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 "propcontroller.hxx"
+
+#include <cppuhelper/propshlp.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/uno3.hxx>
+
+
+namespace pcr
+{
+
+
+ //= FormController
+
+ class FormController;
+ typedef ::cppu::OPropertySetHelper FormController_PropertyBase1;
+ typedef ::comphelper::OPropertyArrayUsageHelper< FormController > FormController_PropertyBase2;
+
+ /** Legacy implementation of com.sun.star.form.PropertyBrowserController
+
+ Nowadays only a wrapper around an ObjectInspector using a
+ DefaultFormComponentInspectorModel.
+ */
+ class FormController :public OPropertyBrowserController
+ ,public FormController_PropertyBase1
+ ,public FormController_PropertyBase2
+ {
+ private:
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xCurrentInspectee;
+ OUString m_sImplementationName;
+ css::uno::Sequence<OUString> m_aSupportedServiceNames;
+ public:
+ FormController(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ const OUString& sImplementName,
+ const css::uno::Sequence<OUString>& aSupportedServiceNames,
+ bool _bUseFormFormComponentHandlers
+ );
+
+ protected:
+ virtual ~FormController() override;
+
+ DECLARE_XINTERFACE()
+ DECLARE_XTYPEPROVIDER()
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XPropertySet and friends
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(
+ css::uno::Any & rConvertedValue, css::uno::Any & rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue
+ ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle, const css::uno::Any& rValue
+ ) override;
+ virtual void SAL_CALL getFastPropertyValue(
+ css::uno::Any& rValue, sal_Int32 nHandle
+ ) const override;
+ private:
+ using FormController_PropertyBase1::getFastPropertyValue;
+ };
+
+
+ //= DialogController
+
+ /** Legacy implementation of com.sun.star.awt.PropertyBrowserController
+ */
+ class DialogController
+ {
+ private:
+ DialogController( const DialogController& ) = delete;
+ DialogController& operator=( const DialogController& ) = delete;
+ };
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formgeometryhandler.cxx b/extensions/source/propctrlr/formgeometryhandler.cxx
new file mode 100644
index 000000000..26c432405
--- /dev/null
+++ b/extensions/source/propctrlr/formgeometryhandler.cxx
@@ -0,0 +1,820 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "propertyhandler.hxx"
+#include "formmetadata.hxx"
+#include "formstrings.hxx"
+#include "handlerhelper.hxx"
+#include "cellbindinghelper.hxx"
+
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/container/XMap.hpp>
+#include <com/sun/star/inspection/XNumericControl.hpp>
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/interfacecontainer.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <comphelper/componentbase.hxx>
+#include <rtl/ref.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::awt::XControlModel;
+ using ::com::sun::star::drawing::XControlShape;
+ using ::com::sun::star::container::XMap;
+ using ::com::sun::star::inspection::LineDescriptor;
+ using ::com::sun::star::inspection::XPropertyControlFactory;
+ using ::com::sun::star::lang::NullPointerException;
+ using ::com::sun::star::beans::Optional;
+ using ::com::sun::star::inspection::XNumericControl;
+ using ::com::sun::star::drawing::XShape;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::beans::XPropertyChangeListener;
+ using ::com::sun::star::text::TextContentAnchorType;
+ using ::com::sun::star::text::TextContentAnchorType_AT_PARAGRAPH;
+ using ::com::sun::star::text::TextContentAnchorType_AS_CHARACTER;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::inspection::XObjectInspectorUI;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::sheet::XSpreadsheet;
+ using ::com::sun::star::table::XColumnRowRange;
+ using ::com::sun::star::table::XTableColumns;
+ using ::com::sun::star::table::XTableRows;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::form::XGridColumnFactory;
+
+ namespace MeasureUnit = css::util::MeasureUnit;
+
+ #define ANCHOR_TO_SHEET 0
+ #define ANCHOR_TO_CELL 1
+
+
+ //= BroadcastHelperBase
+
+ namespace {
+
+ class BroadcastHelperBase
+ {
+ protected:
+ explicit BroadcastHelperBase( ::osl::Mutex& _rMutex )
+ :maBHelper( _rMutex )
+ {
+ }
+
+ protected:
+ ::cppu::OBroadcastHelper& getBroadcastHelper() { return maBHelper; }
+
+ private:
+ ::cppu::OBroadcastHelper maBHelper;
+ };
+
+ }
+
+ //= ShapeGeometryChangeNotifier - declaration
+
+ /** helper class to work around the ...unfortunate implementation of property change broadcasts
+ in the XShape implementation, which broadcasts way too generous and unspecified
+ */
+ typedef ::comphelper::ComponentBase ShapeGeometryChangeNotifier_CBase;
+ typedef ::cppu::WeakImplHelper < css::beans::XPropertyChangeListener
+ > ShapeGeometryChangeNotifier_IBase;
+
+ namespace {
+
+ class ShapeGeometryChangeNotifier :public BroadcastHelperBase
+ ,public ShapeGeometryChangeNotifier_CBase
+ ,public ShapeGeometryChangeNotifier_IBase
+ {
+ public:
+ ShapeGeometryChangeNotifier( ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rParentMutex, const Reference< XShape >& _shape )
+ :BroadcastHelperBase( _rParentMutex )
+ ,ShapeGeometryChangeNotifier_CBase( BroadcastHelperBase::getBroadcastHelper(), ::comphelper::ComponentBase::NoInitializationNeeded() )
+ ,m_rParent( _rParent )
+ ,m_aPropertyChangeListeners( _rParentMutex )
+ ,m_xShape( _shape )
+ {
+ ENSURE_OR_THROW( m_xShape.is(), "illegal shape!" );
+ impl_init_nothrow();
+ }
+
+ // property change broadcasting
+ void addPropertyChangeListener( const Reference< XPropertyChangeListener >& _listener )
+ {
+ m_aPropertyChangeListeners.addInterface( _listener );
+ }
+ void removePropertyChangeListener( const Reference< XPropertyChangeListener >& _listener )
+ {
+ m_aPropertyChangeListeners.removeInterface( _listener );
+ }
+
+ // XComponent equivalent
+ void dispose()
+ {
+ ::osl::MutexGuard aGuard( getMutex() );
+ impl_dispose_nothrow();
+ }
+
+ // XInterface
+ virtual void SAL_CALL acquire( ) noexcept override
+ {
+ m_rParent.acquire();
+ }
+
+ virtual void SAL_CALL release( ) noexcept override
+ {
+ m_rParent.release();
+ }
+
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const PropertyChangeEvent& _event ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& _event ) override;
+
+ protected:
+ virtual ~ShapeGeometryChangeNotifier() override
+ {
+ if ( !getBroadcastHelper().bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+ protected:
+ ::cppu::OBroadcastHelper& getBroadcastHelper() { return BroadcastHelperBase::getBroadcastHelper(); }
+
+ private:
+ void impl_init_nothrow();
+ void impl_dispose_nothrow();
+
+ private:
+ ::cppu::OWeakObject& m_rParent;
+ ::comphelper::OInterfaceContainerHelper2 m_aPropertyChangeListeners;
+ Reference< XShape > m_xShape;
+ };
+
+ }
+
+ //= FormGeometryHandler - declaration
+
+ namespace {
+
+ class FormGeometryHandler;
+
+ }
+
+ /** a property handler for any virtual string properties
+ */
+
+ namespace {
+
+ class FormGeometryHandler : public PropertyHandlerComponent
+ {
+ public:
+ explicit FormGeometryHandler(
+ const Reference< XComponentContext >& _rxContext
+ );
+
+ protected:
+ virtual ~FormGeometryHandler() override;
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
+
+ // XPropertyHandler overriables
+ virtual Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) override;
+ virtual LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual Sequence< OUString > SAL_CALL getActuatingProperties( ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& _rOldValue, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override;
+
+ // OComponentHandler overridables
+ virtual void SAL_CALL disposing() override;
+
+ // PropertyHandler overridables
+ virtual Sequence< Property > doDescribeSupportedProperties() const override;
+
+ protected:
+ virtual void onNewComponent() override;
+
+ private:
+ bool impl_haveTextAnchorType_nothrow() const;
+ bool impl_haveSheetAnchorType_nothrow() const;
+ void impl_setSheetAnchorType_nothrow( const sal_Int32 _nAnchorType ) const;
+
+ private:
+ Reference< XControlShape > m_xAssociatedShape;
+ Reference< XPropertySet > m_xShapeProperties;
+ ::rtl::Reference< ShapeGeometryChangeNotifier > m_xChangeNotifier;
+ };
+
+ }
+
+ //= FormGeometryHandler - implementation
+
+
+ FormGeometryHandler::FormGeometryHandler( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandlerComponent( _rxContext )
+ {
+ }
+
+
+ FormGeometryHandler::~FormGeometryHandler( )
+ {
+ if ( !rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+
+ }
+
+
+ void FormGeometryHandler::onNewComponent()
+ {
+ if ( m_xChangeNotifier.is() )
+ {
+ m_xChangeNotifier->dispose();
+ m_xChangeNotifier.clear();
+ }
+ m_xAssociatedShape.clear();
+ m_xShapeProperties.clear();
+
+ PropertyHandlerComponent::onNewComponent();
+
+ try
+ {
+ Reference< XControlModel > xControlModel( m_xComponent, UNO_QUERY );
+ if ( xControlModel.is() )
+ {
+ // do not ask the map for shapes for grid control columns...
+ Reference< XChild > xCompChild( m_xComponent, UNO_QUERY_THROW );
+ Reference< XGridColumnFactory > xCheckGrid( xCompChild->getParent(), UNO_QUERY );
+ if ( !xCheckGrid.is() )
+ {
+ Reference< XMap > xControlMap;
+ Any any = m_xContext->getValueByName( "ControlShapeAccess" );
+ any >>= xControlMap;
+ m_xAssociatedShape.set( xControlMap->get( Any( xControlModel ) ), UNO_QUERY_THROW );
+ m_xShapeProperties.set( m_xAssociatedShape, UNO_QUERY_THROW );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ if ( m_xAssociatedShape.is() )
+ m_xChangeNotifier = new ShapeGeometryChangeNotifier( *this, m_aMutex, m_xAssociatedShape );
+ }
+
+
+ OUString FormGeometryHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.FormGeometryHandler";
+ }
+
+
+ Sequence< OUString > FormGeometryHandler::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.inspection.FormGeometryHandler" };
+ }
+
+
+ Any SAL_CALL FormGeometryHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ ENSURE_OR_THROW2( m_xAssociatedShape.is(), "internal error: properties, but no shape!", *this );
+ ENSURE_OR_THROW2( m_xShapeProperties.is(), "internal error: no shape properties!", *this );
+
+ Any aReturn;
+ try
+ {
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_POSITIONX:
+ aReturn <<= m_xAssociatedShape->getPosition().X;
+ break;
+ case PROPERTY_ID_POSITIONY:
+ aReturn <<= m_xAssociatedShape->getPosition().Y;
+ break;
+ case PROPERTY_ID_WIDTH:
+ aReturn <<= m_xAssociatedShape->getSize().Width;
+ break;
+ case PROPERTY_ID_HEIGHT:
+ aReturn <<= m_xAssociatedShape->getSize().Height;
+ break;
+ case PROPERTY_ID_TEXT_ANCHOR_TYPE:
+ aReturn = m_xShapeProperties->getPropertyValue( PROPERTY_ANCHOR_TYPE );
+ OSL_ENSURE( aReturn.hasValue(), "FormGeometryHandler::getPropertyValue: illegal anchor type!" );
+ break;
+ case PROPERTY_ID_SHEET_ANCHOR_TYPE:
+ {
+ Reference< XSpreadsheet > xAnchorSheet( m_xShapeProperties->getPropertyValue( PROPERTY_ANCHOR ), UNO_QUERY );
+ aReturn <<= sal_Int32( xAnchorSheet.is() ? ANCHOR_TO_SHEET : ANCHOR_TO_CELL );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "FormGeometryHandler::getPropertyValue: huh?" );
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return aReturn;
+ }
+
+
+ void SAL_CALL FormGeometryHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ ENSURE_OR_THROW2( m_xAssociatedShape.is(), "internal error: properties, but no shape!", *this );
+ ENSURE_OR_THROW2( m_xShapeProperties.is(), "internal error: properties, but no shape!", *this );
+
+ try
+ {
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_POSITIONX:
+ case PROPERTY_ID_POSITIONY:
+ {
+ sal_Int32 nPosition(0);
+ OSL_VERIFY( _rValue >>= nPosition );
+
+ css::awt::Point aPos( m_xAssociatedShape->getPosition() );
+ if ( nPropId == PROPERTY_ID_POSITIONX )
+ aPos.X = nPosition;
+ else
+ aPos.Y = nPosition;
+ m_xAssociatedShape->setPosition( aPos );
+ }
+ break;
+
+ case PROPERTY_ID_WIDTH:
+ case PROPERTY_ID_HEIGHT:
+ {
+ sal_Int32 nSize(0);
+ OSL_VERIFY( _rValue >>= nSize );
+
+ css::awt::Size aSize( m_xAssociatedShape->getSize() );
+ if ( nPropId == PROPERTY_ID_WIDTH )
+ aSize.Width = nSize;
+ else
+ aSize.Height = nSize;
+ m_xAssociatedShape->setSize( aSize );
+ }
+ break;
+
+ case PROPERTY_ID_TEXT_ANCHOR_TYPE:
+ m_xShapeProperties->setPropertyValue( PROPERTY_ANCHOR_TYPE, _rValue );
+ break;
+
+ case PROPERTY_ID_SHEET_ANCHOR_TYPE:
+ {
+ sal_Int32 nSheetAnchorType = 0;
+ OSL_VERIFY( _rValue >>= nSheetAnchorType );
+ impl_setSheetAnchorType_nothrow( nSheetAnchorType );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "FormGeometryHandler::getPropertyValue: huh?" );
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ LineDescriptor SAL_CALL FormGeometryHandler::describePropertyLine( const OUString& _rPropertyName,
+ const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ LineDescriptor aLineDesc( PropertyHandler::describePropertyLine( _rPropertyName, _rxControlFactory ) );
+ try
+ {
+ bool bIsSize = false;
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_WIDTH:
+ case PROPERTY_ID_HEIGHT:
+ bIsSize = true;
+ [[fallthrough]];
+ case PROPERTY_ID_POSITIONX:
+ case PROPERTY_ID_POSITIONY:
+ {
+ Optional< double > aZero( true, 0 );
+ Optional< double > aValueNotPresent( false, 0 );
+ aLineDesc.Control = PropertyHandlerHelper::createNumericControl(
+ _rxControlFactory, 2, bIsSize ? aZero : aValueNotPresent, aValueNotPresent );
+
+ Reference< XNumericControl > xNumericControl( aLineDesc.Control, UNO_QUERY_THROW );
+ xNumericControl->setValueUnit( MeasureUnit::MM_100TH );
+ xNumericControl->setDisplayUnit( impl_getDocumentMeasurementUnit_throw() );
+ }
+ break;
+
+ case PROPERTY_ID_TEXT_ANCHOR_TYPE:
+ case PROPERTY_ID_SHEET_ANCHOR_TYPE:
+ // default handling from PropertyHandler is sufficient
+ break;
+
+ default:
+ OSL_FAIL( "FormGeometryHandler::describePropertyLine: huh?" );
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return aLineDesc;
+ }
+
+
+ void SAL_CALL FormGeometryHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _listener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_xChangeNotifier.is(), "FormGeometryHandler::addPropertyChangeListener: no notified, implies no shape!?" );
+ if ( m_xChangeNotifier.is() )
+ m_xChangeNotifier->addPropertyChangeListener( _listener );
+ }
+
+
+ void SAL_CALL FormGeometryHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _listener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_xChangeNotifier.is(), "FormGeometryHandler::removePropertyChangeListener: no notified, implies no shape!?" );
+ if ( m_xChangeNotifier.is() )
+ m_xChangeNotifier->removePropertyChangeListener( _listener );
+ }
+
+
+ Sequence< OUString > SAL_CALL FormGeometryHandler::getActuatingProperties( )
+ {
+ Sequence< OUString > aInterestedIn { PROPERTY_TEXT_ANCHOR_TYPE };
+ return aInterestedIn;
+ }
+
+
+ void SAL_CALL FormGeometryHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool /*_bFirstTimeInit*/ )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nActuatingPropId( impl_getPropertyId_nothrow( _rActuatingPropertyName ) );
+
+ switch ( nActuatingPropId )
+ {
+ case PROPERTY_ID_TEXT_ANCHOR_TYPE:
+ {
+ TextContentAnchorType eAnchorType( TextContentAnchorType_AT_PARAGRAPH );
+ OSL_VERIFY( _rNewValue >>= eAnchorType );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_POSITIONX, eAnchorType != TextContentAnchorType_AS_CHARACTER );
+ }
+ break;
+ case -1:
+ throw RuntimeException();
+ break;
+ default:
+ OSL_FAIL( "FormGeometryHandler::actuatingPropertyChanged: not registered for this property!" );
+ break;
+ }
+ }
+
+
+ Sequence< Property > FormGeometryHandler::doDescribeSupportedProperties() const
+ {
+ if ( !m_xAssociatedShape.is() )
+ return Sequence< Property >();
+
+ std::vector< Property > aProperties;
+
+ addInt32PropertyDescription( aProperties, PROPERTY_POSITIONX );
+ addInt32PropertyDescription( aProperties, PROPERTY_POSITIONY );
+ addInt32PropertyDescription( aProperties, PROPERTY_WIDTH );
+ addInt32PropertyDescription( aProperties, PROPERTY_HEIGHT );
+
+ if ( impl_haveTextAnchorType_nothrow() )
+ implAddPropertyDescription( aProperties, PROPERTY_TEXT_ANCHOR_TYPE, ::cppu::UnoType< TextContentAnchorType >::get() );
+
+ if ( impl_haveSheetAnchorType_nothrow() )
+ addInt32PropertyDescription( aProperties, PROPERTY_SHEET_ANCHOR_TYPE );
+
+ return comphelper::containerToSequence(aProperties);
+ }
+
+
+ void SAL_CALL FormGeometryHandler::disposing()
+ {
+ PropertyHandlerComponent::disposing();
+
+ if ( m_xChangeNotifier.is() )
+ {
+ m_xChangeNotifier->dispose();
+ m_xChangeNotifier.clear();
+ }
+ }
+
+
+ bool FormGeometryHandler::impl_haveTextAnchorType_nothrow() const
+ {
+ ENSURE_OR_THROW( m_xShapeProperties.is(), "not to be called without shape properties" );
+ try
+ {
+ Reference< XPropertySetInfo > xPSI( m_xShapeProperties->getPropertySetInfo(), UNO_SET_THROW );
+ if ( xPSI->hasPropertyByName( PROPERTY_ANCHOR_TYPE ) )
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return false;
+ }
+
+
+ bool FormGeometryHandler::impl_haveSheetAnchorType_nothrow() const
+ {
+ ENSURE_OR_THROW( m_xShapeProperties.is(), "not to be called without shape properties" );
+ try
+ {
+ Reference< XPropertySetInfo > xPSI( m_xShapeProperties->getPropertySetInfo(), UNO_SET_THROW );
+ if ( !xPSI->hasPropertyByName( PROPERTY_ANCHOR ) )
+ return false;
+ Reference< XServiceInfo > xSI( m_xAssociatedShape, UNO_QUERY_THROW );
+ if ( xSI->supportsService("com.sun.star.sheet.Shape") )
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return false;
+ }
+
+
+ namespace
+ {
+ sal_Int32 lcl_getLowerBoundRowOrColumn( const Reference< XIndexAccess >& _rxRowsOrColumns, const bool _bRows,
+ const css::awt::Point& _rRelativePosition )
+ {
+ sal_Int32 nAccumulated = 0;
+
+ const sal_Int32& rRelativePos = _bRows ? _rRelativePosition.Y : _rRelativePosition.X;
+
+ sal_Int32 nElements = _rxRowsOrColumns->getCount();
+ sal_Int32 currentPos = 0;
+ for ( currentPos=0; currentPos<nElements; ++currentPos )
+ {
+ Reference< XPropertySet > xRowOrColumn( _rxRowsOrColumns->getByIndex( currentPos ), UNO_QUERY_THROW );
+
+ bool bIsVisible = true;
+ OSL_VERIFY( xRowOrColumn->getPropertyValue( PROPERTY_IS_VISIBLE ) >>= bIsVisible );
+ if ( !bIsVisible )
+ continue;
+
+ sal_Int32 nHeightOrWidth( 0 );
+ OSL_VERIFY( xRowOrColumn->getPropertyValue( _bRows ? OUString(PROPERTY_HEIGHT) : OUString(PROPERTY_WIDTH) ) >>= nHeightOrWidth );
+
+ if ( nAccumulated + nHeightOrWidth > rRelativePos )
+ break;
+
+ nAccumulated += nHeightOrWidth;
+ }
+
+ return currentPos;
+ }
+ }
+
+
+ void FormGeometryHandler::impl_setSheetAnchorType_nothrow( const sal_Int32 _nAnchorType ) const
+ {
+ ENSURE_OR_THROW( m_xShapeProperties.is(), "illegal to be called without shape properties." );
+ try
+ {
+ CellBindingHelper aHelper( m_xComponent, impl_getContextDocument_nothrow() );
+ // find the sheet which the control belongs to
+ Reference< XSpreadsheet > xSheet;
+ aHelper.getControlSheetIndex( xSheet );
+
+ switch ( _nAnchorType )
+ {
+ case ANCHOR_TO_SHEET:
+ OSL_ENSURE( xSheet.is(),
+ "FormGeometryHandler::impl_setSheetAnchorType_nothrow: sheet not found!" );
+ if ( xSheet.is() )
+ {
+ css::awt::Point aPreservePosition( m_xAssociatedShape->getPosition() );
+ m_xShapeProperties->setPropertyValue( PROPERTY_ANCHOR, Any( xSheet ) );
+ m_xAssociatedShape->setPosition( aPreservePosition );
+ }
+ break;
+
+ case ANCHOR_TO_CELL:
+ {
+ Reference< XColumnRowRange > xColsRows( xSheet, UNO_QUERY_THROW );
+
+ // get the current anchor
+ Reference< XSpreadsheet > xCurrentAnchor;
+ OSL_VERIFY( m_xShapeProperties->getPropertyValue( PROPERTY_ANCHOR ) >>= xCurrentAnchor );
+ OSL_ENSURE( xCurrentAnchor.is(), "FormGeometryHandler::impl_setSheetAnchorType_nothrow: only to be called when currently anchored to a sheet!" );
+
+ // get the current position
+ css::awt::Point aRelativePosition( m_xAssociatedShape->getPosition() );
+
+ Reference< XTableColumns > xCols( xColsRows->getColumns(), UNO_SET_THROW );
+ sal_Int32 nNewAnchorCol = lcl_getLowerBoundRowOrColumn( xCols, false, aRelativePosition );
+
+ Reference< XTableRows > xRows( xColsRows->getRows(), UNO_SET_THROW );
+ sal_Int32 nNewAnchorRow = lcl_getLowerBoundRowOrColumn( xRows, true, aRelativePosition );
+
+ Any aNewAnchorCell( xSheet->getCellByPosition( nNewAnchorCol, nNewAnchorRow ) );
+ m_xShapeProperties->setPropertyValue( PROPERTY_ANCHOR, aNewAnchorCell );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "FormGeometryHandler::impl_setSheetAnchorType_nothrow: illegal anchor type!" );
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ //= ShapeGeometryChangeNotifier - implementation
+
+ namespace
+ {
+ struct EventTranslation
+ {
+ OUString sPropertyName;
+ Any aNewPropertyValue;
+
+ EventTranslation( const OUString& _propertyName, const Any& _newPropertyValue )
+ :sPropertyName( _propertyName )
+ ,aNewPropertyValue( _newPropertyValue )
+ {
+ }
+ };
+ }
+
+
+ void SAL_CALL ShapeGeometryChangeNotifier::propertyChange( const PropertyChangeEvent& _event )
+ {
+ ::comphelper::ComponentMethodGuard aGuard( *this );
+
+ std::vector< EventTranslation > aEventTranslations;
+ aEventTranslations.reserve(2);
+
+ if ( _event.PropertyName == "Position" )
+ {
+ css::awt::Point aPos = m_xShape->getPosition();
+ aEventTranslations.push_back( EventTranslation( PROPERTY_POSITIONX, Any( aPos.X ) ) );
+ aEventTranslations.push_back( EventTranslation( PROPERTY_POSITIONY, Any( aPos.Y ) ) );
+ }
+ else if ( _event.PropertyName == "Size" )
+ {
+ css::awt::Size aSize = m_xShape->getSize();
+ aEventTranslations.push_back( EventTranslation( PROPERTY_WIDTH, Any( aSize.Width ) ) );
+ aEventTranslations.push_back( EventTranslation( PROPERTY_HEIGHT, Any( aSize.Height ) ) );
+ }
+ else if ( _event.PropertyName == PROPERTY_ANCHOR_TYPE )
+ {
+ aEventTranslations.push_back( EventTranslation( PROPERTY_TEXT_ANCHOR_TYPE, _event.NewValue ) );
+ }
+ else if ( _event.PropertyName == PROPERTY_ANCHOR )
+ {
+ aEventTranslations.push_back( EventTranslation( PROPERTY_SHEET_ANCHOR_TYPE, _event.NewValue ) );
+ }
+
+ PropertyChangeEvent aTranslatedEvent( _event );
+ aTranslatedEvent.Source = m_rParent;
+
+ aGuard.clear();
+ for (auto const& eventTranslation : aEventTranslations)
+ {
+ aTranslatedEvent.PropertyName = eventTranslation.sPropertyName;
+ aTranslatedEvent.NewValue = eventTranslation.aNewPropertyValue;
+ m_aPropertyChangeListeners.notifyEach( &XPropertyChangeListener::propertyChange, aTranslatedEvent );
+ }
+ }
+
+
+ void SAL_CALL ShapeGeometryChangeNotifier::disposing( const EventObject& /*_event*/ )
+ {
+ ::comphelper::ComponentMethodGuard aGuard( *this );
+ impl_dispose_nothrow();
+ }
+
+
+ void ShapeGeometryChangeNotifier::impl_init_nothrow()
+ {
+ osl_atomic_increment( &m_refCount );
+ try
+ {
+ Reference< XPropertySet > xShapeProperties( m_xShape, UNO_QUERY_THROW );
+ xShapeProperties->addPropertyChangeListener( OUString(), this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ void ShapeGeometryChangeNotifier::impl_dispose_nothrow()
+ {
+ try
+ {
+ Reference< XPropertySet > xShapeProperties( m_xShape, UNO_QUERY_THROW );
+ xShapeProperties->removePropertyChangeListener( OUString(), this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ getBroadcastHelper().bDisposed = true;
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_FormGeometryHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::FormGeometryHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formlinkdialog.cxx b/extensions/source/propctrlr/formlinkdialog.cxx
new file mode 100644
index 000000000..319ade956
--- /dev/null
+++ b/extensions/source/propctrlr/formlinkdialog.cxx
@@ -0,0 +1,630 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "formlinkdialog.hxx"
+
+#include "modulepcr.hxx"
+#include <strings.hrc>
+#include "formstrings.hxx"
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/svapp.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbexception.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbcx/XKeysSupplier.hpp>
+#include <com/sun/star/sdbcx/KeyType.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::container;
+
+
+ //= FieldLinkRow
+
+ class FieldLinkRow
+ {
+ private:
+ std::unique_ptr<weld::ComboBox> m_xDetailColumn;
+ std::unique_ptr<weld::ComboBox> m_xMasterColumn;
+
+ Link<FieldLinkRow&,void> m_aLinkChangeHandler;
+
+ public:
+ FieldLinkRow(std::unique_ptr<weld::ComboBox> xDetailColumn,
+ std::unique_ptr<weld::ComboBox> xMasterColumn);
+
+
+ void SetLinkChangeHandler( const Link<FieldLinkRow&,void>& _rHdl ) { m_aLinkChangeHandler = _rHdl; }
+
+ enum LinkParticipant
+ {
+ eDetailField,
+ eMasterField
+ };
+ /** retrieves the selected field name for either the master or the detail field
+ @return <TRUE/> if and only a valid field is selected
+ */
+ bool GetFieldName( LinkParticipant _eWhich, OUString& /* [out] */ _rName ) const;
+ void SetFieldName( LinkParticipant _eWhich, const OUString& _rName );
+
+ void fillList( LinkParticipant _eWhich, const Sequence< OUString >& _rFieldNames );
+
+ void Show()
+ {
+ m_xDetailColumn->show();
+ m_xMasterColumn->show();
+ }
+
+ private:
+ DECL_LINK( OnFieldNameChanged, weld::ComboBox&, void );
+ };
+
+
+ FieldLinkRow::FieldLinkRow(std::unique_ptr<weld::ComboBox> xDetailColumn,
+ std::unique_ptr<weld::ComboBox> xMasterColumn)
+ : m_xDetailColumn(std::move(xDetailColumn))
+ , m_xMasterColumn(std::move(xMasterColumn))
+ {
+ m_xDetailColumn->connect_changed( LINK( this, FieldLinkRow, OnFieldNameChanged ) );
+ m_xMasterColumn->connect_changed( LINK( this, FieldLinkRow, OnFieldNameChanged ) );
+ }
+
+ void FieldLinkRow::fillList( LinkParticipant _eWhich, const Sequence< OUString >& _rFieldNames )
+ {
+ weld::ComboBox* pBox = ( _eWhich == eDetailField ) ? m_xDetailColumn.get() : m_xMasterColumn.get();
+
+ const OUString* pFieldName = _rFieldNames.getConstArray();
+ const OUString* pFieldNameEnd = pFieldName + _rFieldNames.getLength();
+ for ( ; pFieldName != pFieldNameEnd; ++pFieldName )
+ pBox->append_text( *pFieldName );
+ }
+
+ bool FieldLinkRow::GetFieldName( LinkParticipant _eWhich, OUString& /* [out] */ _rName ) const
+ {
+ const weld::ComboBox* pBox = ( _eWhich == eDetailField ) ? m_xDetailColumn.get() : m_xMasterColumn.get();
+ _rName = pBox->get_active_text();
+ return !_rName.isEmpty();
+ }
+
+ void FieldLinkRow::SetFieldName( LinkParticipant _eWhich, const OUString& _rName )
+ {
+ weld::ComboBox* pBox = ( _eWhich == eDetailField ) ? m_xDetailColumn.get() : m_xMasterColumn.get();
+ pBox->set_entry_text( _rName );
+ }
+
+ IMPL_LINK_NOARG( FieldLinkRow, OnFieldNameChanged, weld::ComboBox&, void )
+ {
+ m_aLinkChangeHandler.Call( *this );
+ }
+
+ //= FormLinkDialog
+
+ FormLinkDialog::FormLinkDialog(weld::Window* _pParent, const Reference< XPropertySet >& _rxDetailForm,
+ const Reference< XPropertySet >& _rxMasterForm, const Reference< XComponentContext >& _rxContext,
+ const OUString& _sExplanation,
+ const OUString& _sDetailLabel,
+ const OUString& _sMasterLabel)
+ : GenericDialogController(_pParent, "modules/spropctrlr/ui/formlinksdialog.ui", "FormLinks")
+ , m_xContext ( _rxContext )
+ , m_xDetailForm( _rxDetailForm )
+ , m_xMasterForm( _rxMasterForm )
+ , m_sDetailLabel(_sDetailLabel)
+ , m_sMasterLabel(_sMasterLabel)
+ , m_xExplanation(m_xBuilder->weld_label("explanationLabel"))
+ , m_xDetailLabel(m_xBuilder->weld_label("detailLabel"))
+ , m_xMasterLabel(m_xBuilder->weld_label("masterLabel"))
+ , m_xRow1(std::make_unique<FieldLinkRow>(m_xBuilder->weld_combo_box("detailCombobox1"),
+ m_xBuilder->weld_combo_box("masterCombobox1")))
+ , m_xRow2(std::make_unique<FieldLinkRow>(m_xBuilder->weld_combo_box("detailCombobox2"),
+ m_xBuilder->weld_combo_box("masterCombobox2")))
+ , m_xRow3(std::make_unique<FieldLinkRow>(m_xBuilder->weld_combo_box("detailCombobox3"),
+ m_xBuilder->weld_combo_box("masterCombobox3")))
+ , m_xRow4(std::make_unique<FieldLinkRow>(m_xBuilder->weld_combo_box("detailCombobox4"),
+ m_xBuilder->weld_combo_box("masterCombobox4")))
+ , m_xOK(m_xBuilder->weld_button("ok"))
+ , m_xSuggest(m_xBuilder->weld_button("suggestButton"))
+ {
+ m_xRow1->Show();
+ m_xRow2->Show();
+ m_xRow3->Show();
+ m_xRow4->Show();
+ m_xDialog->set_size_request(600, -1);
+
+ if ( !_sExplanation.isEmpty() )
+ m_xExplanation->set_label(_sExplanation);
+
+ m_xSuggest->connect_clicked(LINK(this, FormLinkDialog, OnSuggest));
+ m_xRow1->SetLinkChangeHandler( LINK( this, FormLinkDialog, OnFieldChanged ) );
+ m_xRow2->SetLinkChangeHandler( LINK( this, FormLinkDialog, OnFieldChanged ) );
+ m_xRow3->SetLinkChangeHandler( LINK( this, FormLinkDialog, OnFieldChanged ) );
+ m_xRow4->SetLinkChangeHandler( LINK( this, FormLinkDialog, OnFieldChanged ) );
+
+ Application::PostUserEvent(LINK(this, FormLinkDialog, OnInitialize));
+
+ updateOkButton();
+ }
+
+ FormLinkDialog::~FormLinkDialog()
+ {
+ }
+
+ void FormLinkDialog::commitLinkPairs()
+ {
+ // collect the field lists from the rows
+ std::vector< OUString > aDetailFields; aDetailFields.reserve( 4 );
+ std::vector< OUString > aMasterFields; aMasterFields.reserve( 4 );
+
+ const FieldLinkRow* aRows[] = {
+ m_xRow1.get(), m_xRow2.get(), m_xRow3.get(), m_xRow4.get()
+ };
+
+ for (const FieldLinkRow* aRow : aRows)
+ {
+ OUString sDetailField, sMasterField;
+ aRow->GetFieldName( FieldLinkRow::eDetailField, sDetailField );
+ aRow->GetFieldName( FieldLinkRow::eMasterField, sMasterField );
+ if ( sDetailField.isEmpty() && sMasterField.isEmpty() )
+ continue;
+
+ aDetailFields.push_back( sDetailField );
+ aMasterFields.push_back( sMasterField );
+ }
+
+ // and set as property values
+ try
+ {
+ if ( m_xDetailForm.is() )
+ {
+ m_xDetailForm->setPropertyValue( PROPERTY_DETAILFIELDS, Any( Sequence< OUString >( aDetailFields.data(), aDetailFields.size() ) ) );
+ m_xDetailForm->setPropertyValue( PROPERTY_MASTERFIELDS, Any( Sequence< OUString >( aMasterFields.data(), aMasterFields.size() ) ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("extensions.propctrlr",
+ "caught an exception while setting the properties!");
+ }
+ }
+
+ short FormLinkDialog::run()
+ {
+ short nResult = GenericDialogController::run();
+
+ if ( RET_OK == nResult )
+ commitLinkPairs();
+
+ return nResult;
+ }
+
+ void FormLinkDialog::initializeFieldLists()
+ {
+ Sequence< OUString > sDetailFields;
+ getFormFields( m_xDetailForm, sDetailFields );
+
+ Sequence< OUString > sMasterFields;
+ getFormFields( m_xMasterForm, sMasterFields );
+
+ FieldLinkRow* aRows[] = {
+ m_xRow1.get(), m_xRow2.get(), m_xRow3.get(), m_xRow4.get()
+ };
+ for (FieldLinkRow* aRow : aRows)
+ {
+ aRow->fillList( FieldLinkRow::eDetailField, sDetailFields );
+ aRow->fillList( FieldLinkRow::eMasterField, sMasterFields );
+ }
+
+ }
+
+
+ void FormLinkDialog::initializeColumnLabels()
+ {
+ // label for the detail form
+ OUString sDetailType = getFormDataSourceType( m_xDetailForm );
+ if ( sDetailType.isEmpty() )
+ {
+ if ( m_sDetailLabel.isEmpty() )
+ {
+ m_sDetailLabel = PcrRes(STR_DETAIL_FORM);
+ }
+ sDetailType = m_sDetailLabel;
+ }
+ m_xDetailLabel->set_label( sDetailType );
+
+ // label for the master form
+ OUString sMasterType = getFormDataSourceType( m_xMasterForm );
+ if ( sMasterType.isEmpty() )
+ {
+ if ( m_sMasterLabel.isEmpty() )
+ {
+ m_sMasterLabel = PcrRes(STR_MASTER_FORM);
+ }
+ sMasterType = m_sMasterLabel;
+ }
+ m_xMasterLabel->set_label( sMasterType );
+ }
+
+ void FormLinkDialog::initializeFieldRowsFrom( std::vector< OUString >& _rDetailFields, std::vector< OUString >& _rMasterFields )
+ {
+ // our UI does allow 4 fields max
+ _rDetailFields.resize( 4 );
+ _rMasterFields.resize( 4 );
+
+ FieldLinkRow* aRows[] = {
+ m_xRow1.get(), m_xRow2.get(), m_xRow3.get(), m_xRow4.get()
+ };
+ for ( sal_Int32 i = 0; i < 4; ++i )
+ {
+ aRows[ i ]->SetFieldName( FieldLinkRow::eDetailField, _rDetailFields[i] );
+ aRows[ i ]->SetFieldName( FieldLinkRow::eMasterField, _rMasterFields[i] );
+ }
+ }
+
+
+ void FormLinkDialog::initializeLinks()
+ {
+ try
+ {
+ Sequence< OUString > aDetailFields;
+ Sequence< OUString > aMasterFields;
+
+ if ( m_xDetailForm.is() )
+ {
+ m_xDetailForm->getPropertyValue( PROPERTY_DETAILFIELDS ) >>= aDetailFields;
+ m_xDetailForm->getPropertyValue( PROPERTY_MASTERFIELDS ) >>= aMasterFields;
+ }
+
+ std::vector< OUString > aDetailFields1;
+ comphelper::sequenceToContainer(aDetailFields1, aDetailFields);
+ std::vector< OUString > aMasterFields1;
+ comphelper::sequenceToContainer(aMasterFields1, aMasterFields);
+ initializeFieldRowsFrom( aDetailFields1, aMasterFields1 );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::initializeLinks" );
+ }
+ }
+
+
+ void FormLinkDialog::updateOkButton()
+ {
+ // in all rows, there must be either two valid selections, or none at all
+ // If there is at least one row with exactly one valid selection, then the
+ // OKButton needs to be disabled
+ bool bEnable = true;
+
+ const FieldLinkRow* aRows[] = {
+ m_xRow1.get(), m_xRow2.get(), m_xRow3.get(), m_xRow4.get()
+ };
+
+ for ( sal_Int32 i = 0; ( i < 4 ) && bEnable; ++i )
+ {
+ OUString sNotInterestedInRightNow;
+ if ( aRows[ i ]->GetFieldName( FieldLinkRow::eDetailField, sNotInterestedInRightNow )
+ != aRows[ i ]->GetFieldName( FieldLinkRow::eMasterField, sNotInterestedInRightNow )
+ )
+ bEnable = false;
+ }
+
+ m_xOK->set_sensitive(bEnable);
+ }
+
+ OUString FormLinkDialog::getFormDataSourceType( const Reference< XPropertySet >& _rxForm )
+ {
+ OUString sReturn;
+ if ( !_rxForm.is() )
+ return sReturn;
+
+ try
+ {
+ sal_Int32 nCommandType = CommandType::COMMAND;
+ OUString sCommand;
+
+ _rxForm->getPropertyValue( PROPERTY_COMMANDTYPE ) >>= nCommandType;
+ _rxForm->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand;
+
+ if ( ( nCommandType == CommandType::TABLE )
+ || ( nCommandType == CommandType::QUERY )
+ )
+ sReturn = sCommand;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::getFormDataSourceType" );
+ }
+ return sReturn;
+ }
+
+ void FormLinkDialog::getFormFields( const Reference< XPropertySet >& _rxForm, Sequence< OUString >& /* [out] */ _rNames ) const
+ {
+ _rNames.realloc( 0 );
+
+ ::dbtools::SQLExceptionInfo aErrorInfo;
+ OUString sCommand;
+ try
+ {
+ weld::WaitObject aWaitCursor(m_xDialog.get());
+
+ OSL_ENSURE( _rxForm.is(), "FormLinkDialog::getFormFields: invalid form!" );
+
+ sal_Int32 nCommandType = CommandType::COMMAND;
+
+ _rxForm->getPropertyValue( PROPERTY_COMMANDTYPE ) >>= nCommandType;
+ _rxForm->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand;
+
+ Reference< XConnection > xConnection;
+ ensureFormConnection( _rxForm, xConnection );
+
+ _rNames = ::dbtools::getFieldNamesByCommandDescriptor(
+ xConnection,
+ nCommandType,
+ sCommand,
+ &aErrorInfo
+ );
+ }
+ catch (const SQLContext& e) { aErrorInfo = e; }
+ catch (const SQLWarning& e) { aErrorInfo = e; }
+ catch (const SQLException& e ) { aErrorInfo = e; }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::getFormFields: caught a non-SQL exception!" );
+ }
+
+ if ( !aErrorInfo.isValid() )
+ return;
+
+ OUString sErrorMessage;
+ {
+ sErrorMessage = PcrRes(STR_ERROR_RETRIEVING_COLUMNS);
+ sErrorMessage = sErrorMessage.replaceFirst("#", sCommand);
+ }
+
+ SQLContext aContext;
+ aContext.Message = sErrorMessage;
+ aContext.NextException = aErrorInfo.get();
+ ::dbtools::showError(aContext, m_xDialog->GetXWindow(), m_xContext);
+ }
+
+ void FormLinkDialog::ensureFormConnection( const Reference< XPropertySet >& _rxFormProps, Reference< XConnection >& /* [out] */ _rxConnection ) const
+ {
+ OSL_PRECOND( _rxFormProps.is(), "FormLinkDialog::ensureFormConnection: invalid form!" );
+ if ( !_rxFormProps.is() )
+ return;
+ if ( _rxFormProps->getPropertySetInfo()->hasPropertyByName(PROPERTY_ACTIVE_CONNECTION) )
+ _rxConnection.set(_rxFormProps->getPropertyValue(PROPERTY_ACTIVE_CONNECTION),UNO_QUERY);
+
+ if ( !_rxConnection.is() )
+ _rxConnection = ::dbtools::connectRowset( Reference< XRowSet >( _rxFormProps, UNO_QUERY ), m_xContext, nullptr );
+ }
+
+
+ void FormLinkDialog::getConnectionMetaData( const Reference< XPropertySet >& _rxFormProps, Reference< XDatabaseMetaData >& /* [out] */ _rxMeta )
+ {
+ if ( _rxFormProps.is() )
+ {
+ Reference< XConnection > xConnection;
+ if ( !::dbtools::isEmbeddedInDatabase( _rxFormProps, xConnection ) )
+ _rxFormProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConnection;
+ if ( xConnection.is() )
+ _rxMeta = xConnection->getMetaData();
+ }
+ }
+
+
+ Reference< XPropertySet > FormLinkDialog::getCanonicUnderlyingTable( const Reference< XPropertySet >& _rxFormProps ) const
+ {
+ Reference< XPropertySet > xTable;
+ try
+ {
+ Reference< XTablesSupplier > xTablesInForm( ::dbtools::getCurrentSettingsComposer( _rxFormProps, m_xContext, nullptr ), UNO_QUERY );
+ Reference< XNameAccess > xTables;
+ if ( xTablesInForm.is() )
+ xTables = xTablesInForm->getTables();
+ Sequence< OUString > aTableNames;
+ if ( xTables.is() )
+ aTableNames = xTables->getElementNames();
+
+ if ( aTableNames.getLength() == 1 )
+ {
+ xTables->getByName( aTableNames[ 0 ] ) >>= xTable;
+ OSL_ENSURE( xTable.is(), "FormLinkDialog::getCanonicUnderlyingTable: invalid table!" );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::getCanonicUnderlyingTable" );
+ }
+ return xTable;
+ }
+
+
+ bool FormLinkDialog::getExistingRelation( const Reference< XPropertySet >& _rxLHS, const Reference< XPropertySet >& /*_rxRHS*/,
+ // TODO: fix the usage of _rxRHS. This is issue #i81956#.
+ std::vector< OUString >& _rLeftFields, std::vector< OUString >& _rRightFields )
+ {
+ try
+ {
+ Reference< XKeysSupplier > xSuppKeys( _rxLHS, UNO_QUERY );
+ Reference< XIndexAccess > xKeys;
+ if ( xSuppKeys.is() )
+ xKeys = xSuppKeys->getKeys();
+
+ if ( xKeys.is() )
+ {
+ Reference< XPropertySet > xKey;
+ Reference< XColumnsSupplier > xKeyColSupp( xKey, UNO_QUERY );
+ Reference< XIndexAccess > xKeyColumns;
+ Reference< XPropertySet > xKeyColumn;
+ OUString sColumnName, sRelatedColumnName;
+
+ const sal_Int32 keyCount = xKeys->getCount();
+ for ( sal_Int32 key = 0; key < keyCount; ++key )
+ {
+ xKeys->getByIndex( key ) >>= xKey;
+ sal_Int32 nKeyType = 0;
+ xKey->getPropertyValue("Type") >>= nKeyType;
+ if ( nKeyType != KeyType::FOREIGN )
+ continue;
+
+ xKeyColumns.clear();
+ xKeyColSupp.set(xKey, css::uno::UNO_QUERY);
+ if ( xKeyColSupp.is() )
+ xKeyColumns.set(xKeyColSupp->getColumns(), css::uno::UNO_QUERY);
+ OSL_ENSURE( xKeyColumns.is(), "FormLinkDialog::getExistingRelation: could not obtain the columns for the key!" );
+
+ if ( !xKeyColumns.is() )
+ continue;
+
+ const sal_Int32 columnCount = xKeyColumns->getCount();
+ _rLeftFields.resize( columnCount );
+ _rRightFields.resize( columnCount );
+ for ( sal_Int32 column = 0; column < columnCount; ++column )
+ {
+ xKeyColumn.clear();
+ xKeyColumns->getByIndex( column ) >>= xKeyColumn;
+ OSL_ENSURE( xKeyColumn.is(), "FormLinkDialog::getExistingRelation: invalid key column!" );
+ if ( xKeyColumn.is() )
+ {
+ xKeyColumn->getPropertyValue( PROPERTY_NAME ) >>= sColumnName;
+ xKeyColumn->getPropertyValue("RelatedColumn") >>= sRelatedColumnName;
+
+ _rLeftFields[ column ] = sColumnName;
+ _rRightFields[ column ] = sRelatedColumnName;
+ }
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::getExistingRelation" );
+ }
+
+ return ( !_rLeftFields.empty() ) && ( !_rLeftFields[ 0 ].isEmpty() );
+ }
+
+
+ void FormLinkDialog::initializeSuggest()
+ {
+ if ( !m_xDetailForm.is() || !m_xMasterForm.is() )
+ return;
+
+ try
+ {
+ // only show the button when both forms are based on the same data source
+ OUString sMasterDS, sDetailDS;
+ m_xMasterForm->getPropertyValue( PROPERTY_DATASOURCE ) >>= sMasterDS;
+ m_xDetailForm->getPropertyValue( PROPERTY_DATASOURCE ) >>= sDetailDS;
+ bool bEnable = ( sMasterDS == sDetailDS );
+
+ // only show the button when the connection supports relations
+ if ( bEnable )
+ {
+ Reference< XDatabaseMetaData > xMeta;
+ getConnectionMetaData( m_xDetailForm, xMeta );
+ OSL_ENSURE( xMeta.is(), "FormLinkDialog::initializeSuggest: unable to retrieve the meta data for the connection!" );
+ try
+ {
+ bEnable = xMeta.is() && xMeta->supportsIntegrityEnhancementFacility();
+ }
+ catch(const Exception&)
+ {
+ bEnable = false;
+ }
+ }
+
+ // only enable the button if there is a "canonic" table underlying both forms
+ Reference< XPropertySet > xDetailTable, xMasterTable;
+ if ( bEnable )
+ {
+ xDetailTable = getCanonicUnderlyingTable( m_xDetailForm );
+ xMasterTable = getCanonicUnderlyingTable( m_xMasterForm );
+ bEnable = xDetailTable.is() && xMasterTable.is();
+ }
+
+ // only enable the button if there is a relation between both tables
+ m_aRelationDetailColumns.clear();
+ m_aRelationMasterColumns.clear();
+ if ( bEnable )
+ {
+ bEnable = getExistingRelation( xDetailTable, xMasterTable, m_aRelationDetailColumns, m_aRelationMasterColumns );
+ SAL_WARN_IF( m_aRelationMasterColumns.size() != m_aRelationDetailColumns.size(),
+ "extensions.propctrlr",
+ "FormLinkDialog::initializeSuggest: nonsense!" );
+ if ( m_aRelationMasterColumns.empty() )
+ { // okay, there is no relation "pointing" (via a foreign key) from the detail table to the master table
+ // but perhaps the other way round (would make less sense, but who knows ...)
+ bEnable = getExistingRelation( xMasterTable, xDetailTable, m_aRelationMasterColumns, m_aRelationDetailColumns );
+ }
+ }
+
+ // only enable the button if the relation contains at most 4 field pairs
+ if ( bEnable )
+ {
+ bEnable = ( m_aRelationMasterColumns.size() <= 4 );
+ }
+
+ m_xSuggest->set_sensitive(bEnable);
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "FormLinkDialog::initializeSuggest" );
+ }
+ }
+
+ IMPL_LINK_NOARG( FormLinkDialog, OnSuggest, weld::Button&, void )
+ {
+ initializeFieldRowsFrom( m_aRelationDetailColumns, m_aRelationMasterColumns );
+ }
+
+ IMPL_LINK_NOARG( FormLinkDialog, OnFieldChanged, FieldLinkRow&, void )
+ {
+ updateOkButton();
+ }
+
+ IMPL_LINK_NOARG( FormLinkDialog, OnInitialize, void*, void )
+ {
+ initializeColumnLabels();
+ initializeFieldLists();
+ initializeLinks();
+ initializeSuggest();
+ }
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formlinkdialog.hxx b/extensions/source/propctrlr/formlinkdialog.hxx
new file mode 100644
index 000000000..80cc086cc
--- /dev/null
+++ b/extensions/source/propctrlr/formlinkdialog.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <memory>
+
+
+namespace pcr
+{
+ class FieldLinkRow;
+
+ //= FormLinkDialog
+
+ class FormLinkDialog : public weld::GenericDialogController
+ {
+ private:
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xContext;
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xDetailForm;
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xMasterForm;
+
+ std::vector< OUString > m_aRelationDetailColumns;
+ std::vector< OUString > m_aRelationMasterColumns;
+
+ OUString m_sDetailLabel;
+ OUString m_sMasterLabel;
+
+ std::unique_ptr<weld::Label> m_xExplanation;
+ std::unique_ptr<weld::Label> m_xDetailLabel;
+ std::unique_ptr<weld::Label> m_xMasterLabel;
+ std::unique_ptr<FieldLinkRow> m_xRow1;
+ std::unique_ptr<FieldLinkRow> m_xRow2;
+ std::unique_ptr<FieldLinkRow> m_xRow3;
+ std::unique_ptr<FieldLinkRow> m_xRow4;
+ std::unique_ptr<weld::Button> m_xOK;
+ std::unique_ptr<weld::Button> m_xSuggest;
+
+ public:
+ FormLinkDialog(
+ weld::Window* _pParent,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxDetailForm,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxMasterForm,
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ const OUString& _sExplanation = OUString(),
+ const OUString& _sDetailLabel = OUString(),
+ const OUString& _sMasterLabel = OUString()
+ );
+
+ virtual ~FormLinkDialog() override;
+
+ // Dialog overridables
+ virtual short run() override;
+
+ private:
+ DECL_LINK( OnSuggest, weld::Button&, void );
+ DECL_LINK( OnFieldChanged, FieldLinkRow&, void );
+ DECL_LINK( OnInitialize, void*, void);
+
+ void updateOkButton();
+ void initializeFieldLists();
+ void initializeColumnLabels();
+ void initializeLinks();
+ void initializeSuggest();
+ void commitLinkPairs();
+
+ void initializeFieldRowsFrom(
+ std::vector< OUString >& _rDetailFields,
+ std::vector< OUString >& _rMasterFields
+ );
+
+ static OUString getFormDataSourceType(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxForm
+ );
+
+ void getFormFields(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxForm,
+ css::uno::Sequence< OUString >& /* [out] */ _rNames
+ ) const;
+
+ void ensureFormConnection(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxFormProps,
+ css::uno::Reference< css::sdbc::XConnection >& /* [out] */ _rxConnection
+ ) const;
+
+ static void getConnectionMetaData(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxFormProps,
+ css::uno::Reference< css::sdbc::XDatabaseMetaData >& /* [out] */ _rxMeta
+ );
+
+ css::uno::Reference< css::beans::XPropertySet >
+ getCanonicUnderlyingTable( const css::uno::Reference< css::beans::XPropertySet >& _rxFormProps ) const;
+ static bool getExistingRelation(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxLHS,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxRHS,
+ std::vector< OUString >& /* [out] */ _rLeftFields,
+ std::vector< OUString >& /* [out] */ _rRightFields
+ );
+ };
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formmetadata.cxx b/extensions/source/propctrlr/formmetadata.cxx
new file mode 100644
index 000000000..13a7b32e7
--- /dev/null
+++ b/extensions/source/propctrlr/formmetadata.cxx
@@ -0,0 +1,694 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "formmetadata.hxx"
+#include "formstrings.hxx"
+#include "modulepcr.hxx"
+#include <command.hrc>
+#include <helpids.h>
+#include <strings.hrc>
+#include <stringarrays.hrc>
+#include <comphelper/extract.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sal/macros.h>
+#include <algorithm>
+
+namespace pcr
+{
+ using namespace ::com::sun::star::uno;
+
+
+ //= OPropertyInfoImpl
+
+ struct OPropertyInfoImpl
+ {
+ OUString sName;
+ OUString sTranslation;
+ OUString sHelpId;
+ sal_Int32 nId;
+ sal_uInt16 nPos;
+ sal_uInt32 nUIFlags;
+
+ OPropertyInfoImpl(
+ const OUString& rName,
+ sal_Int32 _nId,
+ const OUString& aTranslation,
+ sal_uInt16 nPosId,
+ const OUString&,
+ sal_uInt32 _nUIFlags);
+ };
+
+
+ OPropertyInfoImpl::OPropertyInfoImpl(const OUString& _rName, sal_Int32 _nId,
+ const OUString& aString, sal_uInt16 nP, const OUString& sHid, sal_uInt32 _nUIFlags)
+ :sName(_rName)
+ ,sTranslation(aString)
+ ,sHelpId(sHid)
+ ,nId(_nId)
+ ,nPos(nP)
+ ,nUIFlags(_nUIFlags)
+ {
+ }
+
+ namespace {
+
+ // Compare PropertyInfo
+ struct PropertyInfoLessByName
+ {
+ bool operator()( const OPropertyInfoImpl& _rLHS, const OPropertyInfoImpl& _rRHS )
+ {
+ return _rLHS.sName.compareTo( _rRHS.sName ) < 0;
+ }
+ };
+
+ }
+
+ //= OPropertyInfoService
+
+#define DEF_INFO( ident, uinameres, pos, helpid, flags ) \
+ OPropertyInfoImpl( PROPERTY_##ident, PROPERTY_ID_##ident, \
+ PcrRes( RID_STR_##uinameres ), pos, HID_PROP_##helpid, flags )
+
+#define DEF_INFO_1( ident, uinameres, pos, helpid, flag1 ) \
+ DEF_INFO( ident, uinameres, pos, helpid, PROP_FLAG_##flag1 )
+
+#define DEF_INFO_2( ident, uinameres, pos, helpid, flag1, flag2 ) \
+ DEF_INFO( ident, uinameres, pos, helpid, PROP_FLAG_##flag1 | PROP_FLAG_##flag2 )
+
+#define DEF_INFO_3( ident, uinameres, pos, helpid, flag1, flag2, flag3 ) \
+ DEF_INFO( ident, uinameres, pos, helpid, PROP_FLAG_##flag1 | PROP_FLAG_##flag2 | PROP_FLAG_##flag3 )
+
+#define DEF_INFO_4( ident, uinameres, pos, helpid, flag1, flag2, flag3, flag4 ) \
+ DEF_INFO( ident, uinameres, pos, helpid, PROP_FLAG_##flag1 | PROP_FLAG_##flag2 | PROP_FLAG_##flag3 | PROP_FLAG_##flag4 )
+
+ sal_uInt16 OPropertyInfoService::s_nCount = 0;
+ OPropertyInfoImpl* OPropertyInfoService::s_pPropertyInfos = nullptr;
+
+ const OPropertyInfoImpl* OPropertyInfoService::getPropertyInfo()
+ {
+ if ( s_pPropertyInfos )
+ return s_pPropertyInfos;
+
+ static OPropertyInfoImpl aPropertyInfos[] =
+ {
+ /*
+ DEF_INFO_?( propname and id, resource id, pos, help id, flags ),
+ */
+ DEF_INFO_3( NAME, NAME, 0, NAME, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( TITLE, TITLE, 1, TITLE, FORM_VISIBLE, DIALOG_VISIBLE ),
+ DEF_INFO_3( LABEL, LABEL, 2, LABEL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( CONTROLLABEL, LABELCONTROL, 3, CONTROLLABEL, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( WRITING_MODE, WRITING_MODE, 4, WRITING_MODE, FORM_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( GROUP_NAME, GROUP_NAME, 5, GROUP_NAME, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( TEXT, TEXT, 6, TEXT, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( MAXTEXTLEN, MAXTEXTLEN, 7, MAXTEXTLEN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( EDITMASK, EDITMASK, 8, EDITMASK, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( LITERALMASK, LITERALMASK, 9, LITERALMASK, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( STRICTFORMAT, STRICTFORMAT, 10, STRICTFORMAT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( ENABLED, ENABLED, 11, ENABLED, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( ENABLE_VISIBLE, ENABLE_VISIBLE, 12, ENABLE_VISIBLE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( READONLY, READONLY, 13, READONLY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( PRINTABLE, PRINTABLE, 14, PRINTABLE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( STEP, STEP, 15, STEP, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( WHEEL_BEHAVIOR, WHEEL_BEHAVIOR, 16, WHEEL_BEHAVIOR, FORM_VISIBLE | PROP_FLAG_REPORT_INVISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( TABSTOP, TABSTOP, 17, TABSTOP, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( TABINDEX, TABINDEX, 18, TABINDEX, FORM_VISIBLE, DIALOG_VISIBLE ),
+
+ DEF_INFO_2( BOUND_CELL, BOUND_CELL, 19, BOUND_CELL, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_3( CELL_EXCHANGE_TYPE,CELL_EXCHANGE_TYPE, 20, CELL_EXCHANGE_TYPE,FORM_VISIBLE, DATA_PROPERTY, ENUM ),
+ DEF_INFO_2( LIST_CELL_RANGE, LIST_CELL_RANGE, 21, LIST_CELL_RANGE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_3( CONTROLSOURCE, CONTROLSOURCE, 22, CONTROLSOURCE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( EMPTY_IS_NULL, EMPTY_IS_NULL, 23, EMPTY_IS_NULL, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( INPUT_REQUIRED, INPUT_REQUIRED, 24, INPUT_REQUIRED, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( REFVALUE, REFVALUE, 25, REFVALUE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( UNCHECKEDREFVALUE, UNCHECKEDREFVALUE, 26, UNCHECKEDREFVALUE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( DATASOURCE, DATASOURCE, 27, DATASOURCE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_4( COMMANDTYPE, CURSORSOURCETYPE, 28, CURSORSOURCETYPE, FORM_VISIBLE, DATA_PROPERTY, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( COMMAND, CURSORSOURCE, 29, CURSORSOURCE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( ESCAPE_PROCESSING, ESCAPE_PROCESSING, 30, ESCAPE_PROCESSING, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( FILTER, FILTER, 31, FILTER, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( SORT, SORT_CRITERIA, 32, SORT_CRITERIA, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_2( MASTERFIELDS, MASTERFIELDS, 33, MASTERFIELDS, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( DETAILFIELDS, SLAVEFIELDS, 34, SLAVEFIELDS, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_3( ALLOWADDITIONS, ALLOW_ADDITIONS, 35, ALLOW_ADDITIONS, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( ALLOWEDITS, ALLOW_EDITS, 36, ALLOW_EDITS, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( ALLOWDELETIONS, ALLOW_DELETIONS, 37, ALLOW_DELETIONS, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( INSERTONLY, DATAENTRY, 38, DATAENTRY, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_4( NAVIGATION, NAVIGATION, 39, NAVIGATION, FORM_VISIBLE, DATA_PROPERTY, ENUM, COMPOSEABLE ),
+ DEF_INFO_4( CYCLE, CYCLE, 40, CYCLE, FORM_VISIBLE, DATA_PROPERTY, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( FILTERPROPOSAL, FILTERPROPOSAL, 41, FILTERPROPOSAL, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_4( LISTSOURCETYPE, LISTSOURCETYPE, 42, LISTSOURCETYPE, FORM_VISIBLE, DATA_PROPERTY, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( LISTSOURCE, LISTSOURCE, 43, LISTSOURCE, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+ DEF_INFO_3( BOUNDCOLUMN, BOUNDCOLUMN, 44, BOUNDCOLUMN, FORM_VISIBLE, DATA_PROPERTY, COMPOSEABLE ),
+
+ // <!----------------->
+ // XML node binding
+ DEF_INFO_2( LIST_BINDING, LIST_BINDING, 45, LIST_BINDING, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XML_DATA_MODEL, XML_DATA_MODEL, 46, XML_DATA_MODEL, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( BINDING_NAME, BINDING_NAME, 47, BINDING_NAME, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( BIND_EXPRESSION, BIND_EXPRESSION, 48, BIND_EXPRESSION, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_REQUIRED, XSD_REQUIRED, 49, XSD_REQUIRED, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_RELEVANT, XSD_RELEVANT, 50, XSD_RELEVANT, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_READONLY, XSD_READONLY, 51, XSD_READONLY, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_CONSTRAINT, XSD_CONSTRAINT, 52, XSD_CONSTRAINT, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_CALCULATION, XSD_CALCULATION, 53, XSD_CALCULATION, FORM_VISIBLE, DATA_PROPERTY ),
+
+ // data type
+ DEF_INFO_2( XSD_DATA_TYPE, XSD_DATA_TYPE, 54, XSD_DATA_TYPE, FORM_VISIBLE, DATA_PROPERTY ),
+ // data types facets
+ // common
+ DEF_INFO_3( XSD_WHITESPACES, XSD_WHITESPACES, 55, XSD_WHITESPACES, FORM_VISIBLE, DATA_PROPERTY, ENUM ),
+ DEF_INFO_2( XSD_PATTERN, XSD_PATTERN, 56, XSD_PATTERN, FORM_VISIBLE, DATA_PROPERTY ),
+ // string
+ DEF_INFO_2( XSD_LENGTH, XSD_LENGTH, 57, XSD_LENGTH, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_LENGTH, XSD_MIN_LENGTH, 58, XSD_MIN_LENGTH, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MAX_LENGTH, XSD_MAX_LENGTH, 59, XSD_MAX_LENGTH, FORM_VISIBLE, DATA_PROPERTY ),
+ // decimal
+ DEF_INFO_2( XSD_TOTAL_DIGITS, XSD_TOTAL_DIGITS, 60, XSD_TOTAL_DIGITS, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_FRACTION_DIGITS,XSD_FRACTION_DIGITS,61,XSD_FRACTION_DIGITS,FORM_VISIBLE, DATA_PROPERTY ),
+ // int value types (year, month, day)
+ DEF_INFO_2( XSD_MAX_INCLUSIVE_INT, XSD_MAX_INCLUSIVE, 62, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MAX_EXCLUSIVE_INT, XSD_MAX_EXCLUSIVE, 63, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_INCLUSIVE_INT, XSD_MIN_INCLUSIVE, 64, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_EXCLUSIVE_INT, XSD_MIN_EXCLUSIVE, 65, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ // double value types (double, float, decimal)
+ DEF_INFO_2( XSD_MAX_INCLUSIVE_DOUBLE, XSD_MAX_INCLUSIVE, 66, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MAX_EXCLUSIVE_DOUBLE, XSD_MAX_EXCLUSIVE, 67, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_INCLUSIVE_DOUBLE, XSD_MIN_INCLUSIVE, 68, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_EXCLUSIVE_DOUBLE, XSD_MIN_EXCLUSIVE, 69, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ // date value type
+ DEF_INFO_2( XSD_MAX_INCLUSIVE_DATE, XSD_MAX_INCLUSIVE, 70, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MAX_EXCLUSIVE_DATE, XSD_MAX_EXCLUSIVE, 71, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_INCLUSIVE_DATE, XSD_MIN_INCLUSIVE, 72, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_EXCLUSIVE_DATE, XSD_MIN_EXCLUSIVE, 73, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ // time value type
+ DEF_INFO_2( XSD_MAX_INCLUSIVE_TIME, XSD_MAX_INCLUSIVE, 74, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MAX_EXCLUSIVE_TIME, XSD_MAX_EXCLUSIVE, 75, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_INCLUSIVE_TIME, XSD_MIN_INCLUSIVE, 76, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_EXCLUSIVE_TIME, XSD_MIN_EXCLUSIVE, 77, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ // dateTime value type
+ DEF_INFO_2( XSD_MAX_INCLUSIVE_DATE_TIME, XSD_MAX_INCLUSIVE, 78, XSD_MAX_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MAX_EXCLUSIVE_DATE_TIME, XSD_MAX_EXCLUSIVE, 79, XSD_MAX_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_INCLUSIVE_DATE_TIME, XSD_MIN_INCLUSIVE, 80, XSD_MIN_INCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ DEF_INFO_2( XSD_MIN_EXCLUSIVE_DATE_TIME, XSD_MIN_EXCLUSIVE, 81, XSD_MIN_EXCLUSIVE, FORM_VISIBLE, DATA_PROPERTY ),
+ // <!----------------->
+
+ DEF_INFO_2( HIDDEN_VALUE, VALUE, 82, HIDDEN_VALUE, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( VALUE, VALUE, 83, VALUE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( VALUEMIN, VALUEMIN, 84, VALUEMIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( VALUEMAX, VALUEMAX, 85, VALUEMAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( VALUESTEP, VALUESTEP, 86, VALUESTEP, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( DEFAULT_VALUE, DEFAULTVALUE, 87, DEFAULT_LONG_VALUE,FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( DECIMAL_ACCURACY, DECIMAL_ACCURACY, 88, DECIMAL_ACCURACY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SHOWTHOUSANDSEP, SHOWTHOUSANDSEP, 89, SHOWTHOUSANDSEP, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_3( CURRENCYSYMBOL, CURRENCYSYMBOL, 90, CURRENCYSYMBOL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( CURRSYM_POSITION, CURRSYM_POSITION, 91, CURRSYM_POSITION, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_2( DATE, DATE, 92, DATE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( DATEMIN, DATEMIN, 93, DATEMIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( DATEMAX, DATEMAX, 94, DATEMAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_4( DATEFORMAT, DATEFORMAT, 95, DATEFORMAT, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_2( DEFAULT_DATE, DEFAULTDATE, 96, DEFAULT_DATE, FORM_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_2( TIME, TIME, 97, TIME, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( TIMEMIN, TIMEMIN, 98, TIMEMIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( TIMEMAX, TIMEMAX, 99, TIMEMAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_4( TIMEFORMAT, TIMEFORMAT, 100, TIMEFORMAT, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_2( DEFAULT_TIME, DEFAULTTIME, 101, DEFAULT_TIME, FORM_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_1( EFFECTIVE_VALUE, VALUE, 102, VALUE, DIALOG_VISIBLE ),
+ DEF_INFO_3( EFFECTIVE_MIN, VALUEMIN, 103, EFFECTIVEMIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( EFFECTIVE_MAX, VALUEMAX, 104, EFFECTIVEMAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( EFFECTIVE_DEFAULT, DEFAULTVALUE, 105, EFFECTIVEDEFAULT, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( FORMATKEY, FORMATKEY, 106, FORMATKEY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_3( PROGRESSVALUE, PROGRESSVALUE, 107, PROGRESSVALUE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( PROGRESSVALUE_MIN, PROGRESSVALUE_MIN, 108, PROGRESSVALUE_MIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( PROGRESSVALUE_MAX, PROGRESSVALUE_MAX, 109, PROGRESSVALUE_MAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_2( SCROLLVALUE, SCROLLVALUE, 110, SCROLLVALUE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SCROLLVALUE_MIN, SCROLLVALUE_MIN, 111, SCROLLVALUE_MIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SCROLLVALUE_MAX, SCROLLVALUE_MAX, 112, SCROLLVALUE_MAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SCROLL_WIDTH, SCROLL_WIDTH, 113, SCROLL_WIDTH, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SCROLL_HEIGHT, SCROLL_HEIGHT, 114, SCROLL_HEIGHT, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SCROLL_TOP, SCROLL_TOP, 115, SCROLL_TOP, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SCROLL_LEFT, SCROLL_LEFT, 116, SCROLL_LEFT, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( DEFAULT_SCROLLVALUE,DEFAULT_SCROLLVALUE,117,DEFAULT_SCROLLVALUE,FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( LINEINCREMENT, LINEINCREMENT, 118, LINEINCREMENT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( BLOCKINCREMENT, BLOCKINCREMENT, 119, BLOCKINCREMENT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_2( SPINVALUE, VALUE, 120, SPINVALUE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SPINVALUE_MIN, VALUEMIN, 121, SPINVALUE_MIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SPINVALUE_MAX, VALUEMAX, 122, SPINVALUE_MAX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( DEFAULT_SPINVALUE,DEFAULTVALUE, 123, DEFAULT_SPINVALUE, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SPININCREMENT, VALUESTEP, 124, SPININCREMENT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_3( SPIN, SPIN, 125, SPIN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( REPEAT, REPEAT, 126, REPEAT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( REPEAT_DELAY, REPEAT_DELAY, 127, REPEAT_DELAY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( VISIBLESIZE, VISIBLESIZE, 128, VISIBLESIZE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_4( ORIENTATION, ORIENTATION, 129, ORIENTATION, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( FOCUSONCLICK, FOCUSONCLICK, 130, FOCUSONCLICK, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( TOGGLE, TOGGLE, 131, TOGGLE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( DEFAULT_STATE, DEFAULT_STATE, 132, DEFAULT_STATE, FORM_VISIBLE, ENUM, COMPOSEABLE ),
+
+ DEF_INFO_3( TEXT_ANCHOR_TYPE, ANCHOR_TYPE, 133, ANCHOR_TYPE, FORM_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( SHEET_ANCHOR_TYPE, ANCHOR_TYPE, 134, ANCHOR_TYPE, FORM_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( POSITIONX, POSITIONX, 135, POSITIONX, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( POSITIONY, POSITIONY, 136, POSITIONY, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( WIDTH, WIDTH, 137, WIDTH, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( HEIGHT, HEIGHT, 138, HEIGHT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_1( LISTINDEX, LISTINDEX, 139, LISTINDEX, FORM_VISIBLE ),
+ DEF_INFO_3( STRINGITEMLIST, STRINGITEMLIST, 140, STRINGITEMLIST, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( DEFAULT_TEXT, DEFAULTTEXT, 141, DEFAULTVALUE, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( FONT, FONT, 142, FONT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_4( VISUALEFFECT, VISUALEFFECT, 143, VISUALEFFECT, FORM_VISIBLE, DIALOG_VISIBLE, ENUM_ONE, COMPOSEABLE ),
+ DEF_INFO_4( ALIGN, ALIGN, 144, ALIGN, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_4( VERTICAL_ALIGN, VERTICAL_ALIGN, 145, VERTICAL_ALIGN, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( ROWHEIGHT, ROWHEIGHT, 146, ROWHEIGHT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( BACKGROUNDCOLOR, BACKGROUNDCOLOR, 147, BACKGROUNDCOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SYMBOLCOLOR, SYMBOLCOLOR, 148, SYMBOLCOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( FILLCOLOR, FILLCOLOR, 149, FILLCOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( LINECOLOR, LINECOLOR, 150, LINECOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_4( BORDER, BORDER, 151, BORDER, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( BORDERCOLOR, BORDERCOLOR, 152, BORDERCOLOR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( ICONSIZE, ICONSIZE, 153, ICONSIZE, FORM_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_2( SHOW_POSITION, SHOW_POSITION, 154, SHOW_POSITION, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SHOW_NAVIGATION, SHOW_NAVIGATION, 155, SHOW_NAVIGATION, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SHOW_RECORDACTIONS,SHOW_RECORDACTIONS, 156, SHOW_RECORDACTIONS,FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SHOW_FILTERSORT, SHOW_FILTERSORT, 157, SHOW_FILTERSORT, FORM_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_3( DROPDOWN, DROPDOWN, 158, DROPDOWN, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( LINECOUNT, LINECOUNT, 159, LINECOUNT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( AUTOCOMPLETE, AUTOCOMPLETE, 160, AUTOCOMPLETE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( MULTILINE, MULTILINE, 161, MULTILINE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( WORDBREAK, WORDBREAK, 162, WORDBREAK, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( TEXTTYPE, TEXTTYPE, 163, TEXTTYPE, FORM_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( LINEEND_FORMAT, LINEEND_FORMAT, 164, LINEEND_FORMAT, FORM_VISIBLE, ENUM_ONE, COMPOSEABLE ),
+ DEF_INFO_3( MULTISELECTION, MULTISELECTION, 165, MULTISELECTION, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_4( SHOW_SCROLLBARS, SHOW_SCROLLBARS, 166, SHOW_SCROLLBARS, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( HSCROLL, HSCROLL, 167, HSCROLL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( VSCROLL, VSCROLL, 168, VSCROLL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( BUTTONTYPE, BUTTONTYPE, 169, BUTTONTYPE, FORM_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_2( XFORMS_BUTTONTYPE, BUTTONTYPE, 170, BUTTONTYPE, FORM_VISIBLE, ENUM ),
+ DEF_INFO_1( SUBMISSION_ID, SUBMISSION_ID, 171, SUBMISSION_ID, FORM_VISIBLE ),
+ DEF_INFO_2( PUSHBUTTONTYPE, PUSHBUTTONTYPE, 172, PUSHBUTTONTYPE, DIALOG_VISIBLE, ENUM ),
+ DEF_INFO_2( TARGET_URL, TARGET_URL, 173, TARGET_URL, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_1( TARGET_FRAME, TARGET_FRAME, 174, TARGET_FRAME, FORM_VISIBLE ),
+ DEF_INFO_2( SUBMIT_ACTION, SUBMIT_ACTION, 175, SUBMIT_ACTION, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SUBMIT_TARGET, SUBMIT_TARGET, 176, SUBMIT_TARGET, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SUBMIT_ENCODING, SUBMIT_ENCODING, 177, SUBMIT_ENCODING, FORM_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( SUBMIT_METHOD, SUBMIT_METHOD, 178, SUBMIT_METHOD, FORM_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( STATE, STATE, 179, STATE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( DEFAULTBUTTON, DEFAULT_BUTTON, 180, DEFAULT_BUTTON, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( IMAGE_URL, IMAGE_URL, 181, IMAGE_URL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_4( IMAGEPOSITION, IMAGEPOSITION, 182, IMAGEPOSITION, FORM_VISIBLE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_3( SCALEIMAGE, SCALEIMAGE, 183, SCALEIMAGE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_4( SCALE_MODE, SCALEIMAGE, 184, SCALEIMAGE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE, ENUM ),
+ DEF_INFO_2( DEFAULT_SELECT_SEQ,DEFAULT_SELECT_SEQ, 185, DEFAULT_SELECT_SEQ,FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SELECTEDITEMS, SELECTEDITEMS, 186, SELECTEDITEMS, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( ECHO_CHAR, ECHO_CHAR, 187, ECHO_CHAR, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( HIDEINACTIVESELECTION, HIDEINACTIVESELECTION, 188, HIDEINACTIVESELECTION, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( TRISTATE, TRISTATE, 189, TRISTATE, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( HASNAVIGATION, NAVIGATION, 190, NAVIGATIONBAR, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( RECORDMARKER, RECORDMARKER, 191, RECORDMARKER, FORM_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( TAG, TAG, 192, TAG, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( HELPTEXT, HELPTEXT, 193, HELPTEXT, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( HELPURL, HELPURL, 194, HELPURL, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SELECTION_TYPE, SELECTION_TYPE, 195, SELECTION_TYPE, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_2( ROOT_DISPLAYED, ROOT_DISPLAYED, 196, ROOT_DISPLAYED, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SHOWS_HANDLES, SHOWS_HANDLES, 197, SHOWS_HANDLES, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SHOWS_ROOT_HANDLES, SHOWS_ROOT_HANDLES, 198, SHOWS_ROOT_HANDLES, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( EDITABLE, EDITABLE, 199, EDITABLE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( INVOKES_STOP_NOT_EDITING, INVOKES_STOP_NOT_EDITING, 200, INVOKES_STOP_NOT_EDITING, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( DECORATION, DECORATION, 201, DECORATION, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( NOLABEL, NOLABEL, 202, NOLABEL, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_3( SELECTIONMODEL, SELECTIONMODEL, 203, SELECTIONMODEL, DIALOG_VISIBLE, ENUM, COMPOSEABLE ),
+ DEF_INFO_2( USEGRIDLINE, USEGRIDLINE, 204, USEGRIDLINE, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( GRIDLINECOLOR, GRIDLINECOLOR, 205, GRIDLINECOLOR, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SHOWCOLUMNHEADER, SHOWCOLUMNHEADER, 206, SHOWCOLUMNHEADER, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( SHOWROWHEADER, SHOWROWHEADER, 207, SHOWROWHEADER, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( HEADERBACKGROUNDCOLOR, HEADERBACKGROUNDCOLOR, 208, HEADERBACKGROUNDCOLOR, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( HEADERTEXTCOLOR, HEADERTEXTCOLOR, 209, HEADERTEXTCOLOR, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( ACTIVESELECTIONBACKGROUNDCOLOR, ACTIVESELECTIONBACKGROUNDCOLOR, 210, ACTIVESELECTIONBACKGROUNDCOLOR, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( ACTIVESELECTIONTEXTCOLOR, ACTIVESELECTIONTEXTCOLOR, 211, ACTIVESELECTIONTEXTCOLOR, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( INACTIVESELECTIONBACKGROUNDCOLOR, INACTIVESELECTIONBACKGROUNDCOLOR, 212, INACTIVESELECTIONBACKGROUNDCOLOR, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( INACTIVESELECTIONTEXTCOLOR, INACTIVESELECTIONTEXTCOLOR, 213, INACTIVESELECTIONTEXTCOLOR, DIALOG_VISIBLE, COMPOSEABLE ),
+ DEF_INFO_2( URL, URL, 214, URL, DIALOG_VISIBLE, COMPOSEABLE ),
+
+ DEF_INFO_3( AUTOGROW, AUTOGROW, 215, AUTOGROW, FORM_VISIBLE, DIALOG_VISIBLE, COMPOSEABLE)
+ };
+
+ s_pPropertyInfos = aPropertyInfos;
+ s_nCount = SAL_N_ELEMENTS(aPropertyInfos);
+
+ // sort
+ std::sort( s_pPropertyInfos, s_pPropertyInfos + s_nCount, PropertyInfoLessByName() );
+
+#if OSL_DEBUG_LEVEL > 0
+ for ( const OPropertyInfoImpl* pCheck = s_pPropertyInfos; pCheck != s_pPropertyInfos + s_nCount - 1; ++pCheck )
+ {
+ OSL_ENSURE( pCheck->sName != ( pCheck + 1 )->sName, "OPropertyInfoService::getPropertyInfo: duplicate entry in the table!" );
+ }
+#endif
+
+ return s_pPropertyInfos;
+ }
+
+
+ sal_Int32 OPropertyInfoService::getPropertyId(const OUString& _rName) const
+ {
+ const OPropertyInfoImpl* pInfo = getPropertyInfo(_rName);
+ return pInfo ? pInfo->nId : -1;
+ }
+
+
+ OUString OPropertyInfoService::getPropertyTranslation(sal_Int32 _nId) const
+ {
+ const OPropertyInfoImpl* pInfo = getPropertyInfo(_nId);
+ return pInfo ? pInfo->sTranslation : OUString();
+ }
+
+ OUString OPropertyInfoService::getPropertyHelpId(sal_Int32 _nId) const
+ {
+ const OPropertyInfoImpl* pInfo = getPropertyInfo(_nId);
+ return pInfo ? pInfo->sHelpId : OUString();
+ }
+
+ sal_Int16 OPropertyInfoService::getPropertyPos(sal_Int32 _nId) const
+ {
+ const OPropertyInfoImpl* pInfo = getPropertyInfo(_nId);
+ return pInfo ? pInfo->nPos : 0xFFFF;
+ }
+
+ sal_uInt32 OPropertyInfoService::getPropertyUIFlags(sal_Int32 _nId) const
+ {
+ const OPropertyInfoImpl* pInfo = getPropertyInfo(_nId);
+ return pInfo ? pInfo->nUIFlags : 0;
+ }
+
+ std::vector< OUString > OPropertyInfoService::getPropertyEnumRepresentations(sal_Int32 _nId) const
+ {
+ OSL_ENSURE( ( ( getPropertyUIFlags( _nId ) & PROP_FLAG_ENUM ) != 0 ) || ( _nId == PROPERTY_ID_TARGET_FRAME ),
+ "OPropertyInfoService::getPropertyEnumRepresentations: this is no enum property!" );
+
+ if (_nId == PROPERTY_ID_SUBMIT_METHOD)
+ {
+ return { "Get", "Post" };
+ }
+ const TranslateId* pStringItemsResId = nullptr;
+ int nElements = 0;
+ switch ( _nId )
+ {
+ case PROPERTY_ID_IMAGEPOSITION:
+ pStringItemsResId = RID_RSC_ENUM_IMAGE_POSITION;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_IMAGE_POSITION);
+ break;
+ case PROPERTY_ID_BORDER:
+ pStringItemsResId = RID_RSC_ENUM_BORDER_TYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_BORDER_TYPE);
+ break;
+ case PROPERTY_ID_ICONSIZE:
+ pStringItemsResId = RID_RSC_ENUM_ICONSIZE_TYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_ICONSIZE_TYPE);
+ break;
+ case PROPERTY_ID_COMMANDTYPE:
+ pStringItemsResId = RID_RSC_ENUM_COMMAND_TYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_COMMAND_TYPE);
+ break;
+ case PROPERTY_ID_LISTSOURCETYPE:
+ pStringItemsResId = RID_RSC_ENUM_LISTSOURCE_TYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_LISTSOURCE_TYPE);
+ break;
+ case PROPERTY_ID_ALIGN:
+ pStringItemsResId = RID_RSC_ENUM_ALIGNMENT;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_ALIGNMENT);
+ break;
+ case PROPERTY_ID_VERTICAL_ALIGN:
+ pStringItemsResId = RID_RSC_ENUM_VERTICAL_ALIGN;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_VERTICAL_ALIGN);
+ break;
+ case PROPERTY_ID_BUTTONTYPE:
+ pStringItemsResId = RID_RSC_ENUM_BUTTONTYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_BUTTONTYPE);
+ break;
+ case PROPERTY_ID_PUSHBUTTONTYPE:
+ pStringItemsResId = RID_RSC_ENUM_PUSHBUTTONTYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_PUSHBUTTONTYPE);
+ break;
+ case PROPERTY_ID_SUBMIT_ENCODING:
+ pStringItemsResId = RID_RSC_ENUM_SUBMIT_ENCODING;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SUBMIT_ENCODING);
+ break;
+ case PROPERTY_ID_DATEFORMAT:
+ pStringItemsResId = RID_RSC_ENUM_DATEFORMAT_LIST;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_DATEFORMAT_LIST);
+ break;
+ case PROPERTY_ID_TIMEFORMAT:
+ pStringItemsResId = RID_RSC_ENUM_TIMEFORMAT_LIST;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_TIMEFORMAT_LIST);
+ break;
+ case PROPERTY_ID_DEFAULT_STATE:
+ case PROPERTY_ID_STATE:
+ pStringItemsResId = RID_RSC_ENUM_CHECKED;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_CHECKED);
+ break;
+ case PROPERTY_ID_CYCLE:
+ pStringItemsResId = RID_RSC_ENUM_CYCLE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_CYCLE);
+ break;
+ case PROPERTY_ID_NAVIGATION:
+ pStringItemsResId = RID_RSC_ENUM_NAVIGATION;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_NAVIGATION);
+ break;
+ case PROPERTY_ID_TARGET_FRAME:
+ pStringItemsResId = RID_RSC_ENUM_SUBMIT_TARGET;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SUBMIT_TARGET);
+ break;
+ case PROPERTY_ID_ORIENTATION:
+ pStringItemsResId = RID_RSC_ENUM_ORIENTATION;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_ORIENTATION);
+ break;
+ case PROPERTY_ID_CELL_EXCHANGE_TYPE:
+ pStringItemsResId = RID_RSC_ENUM_CELL_EXCHANGE_TYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_CELL_EXCHANGE_TYPE);
+ break;
+ case PROPERTY_ID_SHOW_SCROLLBARS:
+ pStringItemsResId = RID_RSC_ENUM_SCROLLBARS;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SCROLLBARS);
+ break;
+ case PROPERTY_ID_VISUALEFFECT:
+ pStringItemsResId = RID_RSC_ENUM_VISUALEFFECT;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_VISUALEFFECT);
+ break;
+ case PROPERTY_ID_TEXTTYPE:
+ pStringItemsResId = RID_RSC_ENUM_TEXTTYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_TEXTTYPE);
+ break;
+ case PROPERTY_ID_LINEEND_FORMAT:
+ pStringItemsResId = RID_RSC_ENUM_LINEEND_FORMAT;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_LINEEND_FORMAT);
+ break;
+ case PROPERTY_ID_XSD_WHITESPACES:
+ pStringItemsResId = RID_RSC_ENUM_WHITESPACE_HANDLING;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_WHITESPACE_HANDLING);
+ break;
+ case PROPERTY_ID_SELECTION_TYPE:
+ case PROPERTY_ID_SELECTIONMODEL:
+ pStringItemsResId = RID_RSC_ENUM_SELECTION_TYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SELECTION_TYPE);
+ break;
+ case PROPERTY_ID_SCALE_MODE:
+ pStringItemsResId = RID_RSC_ENUM_SCALE_MODE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SCALE_MODE);
+ break;
+ case PROPERTY_ID_WRITING_MODE:
+ pStringItemsResId = RID_RSC_ENUM_WRITING_MODE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_WRITING_MODE);
+ break;
+ case PROPERTY_ID_WHEEL_BEHAVIOR:
+ pStringItemsResId = RID_RSC_ENUM_WHEEL_BEHAVIOR;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_WHEEL_BEHAVIOR);
+ break;
+ case PROPERTY_ID_TEXT_ANCHOR_TYPE:
+ pStringItemsResId = RID_RSC_ENUM_TEXT_ANCHOR_TYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_TEXT_ANCHOR_TYPE);
+ break;
+ case PROPERTY_ID_SHEET_ANCHOR_TYPE:
+ pStringItemsResId = RID_RSC_ENUM_SHEET_ANCHOR_TYPE;
+ nElements = SAL_N_ELEMENTS(RID_RSC_ENUM_SHEET_ANCHOR_TYPE);
+ break;
+ default:
+ OSL_FAIL( "OPropertyInfoService::getPropertyEnumRepresentations: unknown enum property!" );
+ break;
+ }
+
+ std::vector< OUString > aReturn;
+
+ aReturn.reserve(nElements);
+ for (int i = 0; i < nElements; ++i)
+ {
+ aReturn.push_back(PcrRes(pStringItemsResId[i]));
+ }
+
+ return aReturn;
+ }
+
+ bool OPropertyInfoService::isComposeable( const OUString& _rPropertyName ) const
+ {
+ sal_Int32 nId = getPropertyId( _rPropertyName );
+ if ( nId == -1 )
+ return false;
+
+ sal_uInt32 nFlags = getPropertyUIFlags( nId );
+ return ( nFlags & PROP_FLAG_COMPOSEABLE ) != 0;
+ }
+
+
+ const OPropertyInfoImpl* OPropertyInfoService::getPropertyInfo(const OUString& _rName)
+ {
+ // Initialization
+ if(!s_pPropertyInfos)
+ getPropertyInfo();
+ OPropertyInfoImpl aSearch(_rName, 0, OUString(), 0, "", 0);
+
+ const OPropertyInfoImpl* pInfo = std::lower_bound(
+ s_pPropertyInfos, s_pPropertyInfos + s_nCount, aSearch, PropertyInfoLessByName() );
+
+ if ( pInfo == s_pPropertyInfos + s_nCount )
+ return nullptr;
+
+ if ( pInfo->sName != _rName )
+ return nullptr;
+
+ return pInfo;
+ }
+
+
+ const OPropertyInfoImpl* OPropertyInfoService::getPropertyInfo(sal_Int32 _nId)
+ {
+ // Initialization
+ if(!s_pPropertyInfos)
+ getPropertyInfo();
+
+ // TODO: a real structure which allows quick access by name as well as by id
+ for (sal_uInt16 i = 0; i < s_nCount; i++)
+ if (s_pPropertyInfos[i].nId == _nId)
+ return &s_pPropertyInfos[i];
+
+ return nullptr;
+ }
+
+
+ //= DefaultEnumRepresentation
+
+
+ DefaultEnumRepresentation::DefaultEnumRepresentation( const IPropertyInfoService& _rInfo, const Type& _rType, sal_Int32 _nPropertyId )
+ :m_rMetaData( _rInfo )
+ ,m_aType( _rType )
+ ,m_nPropertyId( _nPropertyId )
+ {
+ }
+
+
+ DefaultEnumRepresentation::~DefaultEnumRepresentation()
+ {
+ }
+
+
+ std::vector< OUString > DefaultEnumRepresentation::getDescriptions() const
+ {
+ return m_rMetaData.getPropertyEnumRepresentations( m_nPropertyId );
+ }
+
+
+ void DefaultEnumRepresentation::getValueFromDescription( const OUString& _rDescription, Any& _out_rValue ) const
+ {
+ sal_uInt32 nPropertyUIFlags = m_rMetaData.getPropertyUIFlags( m_nPropertyId );
+ std::vector< OUString > aEnumStrings = m_rMetaData.getPropertyEnumRepresentations( m_nPropertyId );
+ std::vector< OUString >::const_iterator pos = std::find( aEnumStrings.begin(), aEnumStrings.end(), _rDescription );
+ if ( pos != aEnumStrings.end() )
+ {
+ sal_Int32 nPos = pos - aEnumStrings.begin();
+ if ( ( nPropertyUIFlags & PROP_FLAG_ENUM_ONE ) == PROP_FLAG_ENUM_ONE )
+ // enum value starting with 1
+ ++nPos;
+
+ switch ( m_aType.getTypeClass() )
+ {
+ case TypeClass_ENUM:
+ _out_rValue = ::cppu::int2enum( nPos, m_aType );
+ break;
+
+ case TypeClass_SHORT:
+ _out_rValue <<= static_cast<sal_Int16>(nPos);
+ break;
+
+ case TypeClass_UNSIGNED_SHORT:
+ _out_rValue <<= static_cast<sal_uInt16>(nPos);
+ break;
+
+ case TypeClass_UNSIGNED_LONG:
+ _out_rValue <<= static_cast<sal_uInt32>(nPos);
+ break;
+
+ default:
+ _out_rValue <<= nPos;
+ break;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "DefaultEnumRepresentation::getValueFromDescription: could not translate the enum string!" );
+ _out_rValue.clear();
+ }
+ }
+
+
+ OUString DefaultEnumRepresentation::getDescriptionForValue( const Any& _rEnumValue ) const
+ {
+ OUString sReturn;
+ sal_Int32 nIntValue = -1;
+ OSL_VERIFY( ::cppu::enum2int( nIntValue, _rEnumValue ) );
+
+ sal_uInt32 nUIFlags = m_rMetaData.getPropertyUIFlags( m_nPropertyId );
+ if ( ( nUIFlags & PROP_FLAG_ENUM_ONE ) == PROP_FLAG_ENUM_ONE )
+ // enum value starting with 1
+ --nIntValue;
+
+ std::vector< OUString > aEnumStrings = m_rMetaData.getPropertyEnumRepresentations( m_nPropertyId );
+ if ( ( nIntValue >= 0 ) && ( o3tl::make_unsigned(nIntValue) < aEnumStrings.size() ) )
+ {
+ sReturn = aEnumStrings[ nIntValue ];
+ }
+ else
+ {
+ OSL_FAIL( "DefaultEnumRepresentation::getDescriptionForValue: could not translate an enum value" );
+ }
+ return sReturn;
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formmetadata.hxx b/extensions/source/propctrlr/formmetadata.hxx
new file mode 100644
index 000000000..0f7aa67d2
--- /dev/null
+++ b/extensions/source/propctrlr/formmetadata.hxx
@@ -0,0 +1,345 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "propertyinfo.hxx"
+#include "enumrepresentation.hxx"
+
+
+namespace pcr
+{
+
+
+ struct OPropertyInfoImpl;
+
+
+ //= OPropertyInfoService
+
+ class OPropertyInfoService final
+ :public IPropertyInfoService
+ {
+ static sal_uInt16 s_nCount;
+ static OPropertyInfoImpl* s_pPropertyInfos;
+ // TODO: a real structure which allows quick access by name as well as by id
+
+ public:
+ // IPropertyInfoService
+ virtual sal_Int32 getPropertyId(const OUString& _rName) const override;
+ virtual OUString getPropertyTranslation(sal_Int32 _nId) const override;
+ virtual OUString getPropertyHelpId(sal_Int32 _nId) const override;
+ virtual sal_Int16 getPropertyPos(sal_Int32 _nId) const override;
+ virtual sal_uInt32 getPropertyUIFlags(sal_Int32 _nId) const override;
+ virtual std::vector< OUString > getPropertyEnumRepresentations(sal_Int32 _nId) const override;
+
+ bool isComposeable( const OUString& _rPropertyName ) const;
+
+ private:
+ static const OPropertyInfoImpl* getPropertyInfo();
+
+ static const OPropertyInfoImpl* getPropertyInfo(const OUString& _rName);
+ static const OPropertyInfoImpl* getPropertyInfo(sal_Int32 _nId);
+ };
+
+
+ //= DefaultEnumRepresentation
+
+ /** an implementation of the IPropertyEnumRepresentation
+
+ To be used with properties which, in formmetadata.cxx, are declared as ENUM.
+ */
+ class DefaultEnumRepresentation : public IPropertyEnumRepresentation
+ {
+ private:
+ const IPropertyInfoService& m_rMetaData;
+ css::uno::Type m_aType;
+ const sal_Int32 m_nPropertyId;
+
+ public:
+ /** constructs an instance
+
+ @param _rInfo
+ An instance implementing IPropertyInfoService. Must live at least as
+ long as the DefaultEnumRepresentation should live.
+ */
+ DefaultEnumRepresentation( const IPropertyInfoService& _rInfo, const css::uno::Type& _rType, sal_Int32 _nPropertyId );
+
+ protected:
+ virtual ~DefaultEnumRepresentation() override;
+
+ protected:
+ // IPropertyEnumRepresentation implementqation
+ virtual std::vector< OUString >
+ getDescriptions() const override;
+ virtual void getValueFromDescription( const OUString& _rDescription, css::uno::Any& _out_rValue ) const override;
+ virtual OUString getDescriptionForValue( const css::uno::Any& _rEnumValue ) const override;
+
+ private:
+ DefaultEnumRepresentation( const DefaultEnumRepresentation& ) = delete;
+ DefaultEnumRepresentation& operator=( const DefaultEnumRepresentation& ) = delete;
+ };
+
+
+ //= UI flags (for all browsable properties)
+
+
+#define PROP_FLAG_NONE 0x00000000 // no special flag
+#define PROP_FLAG_FORM_VISIBLE 0x00000001 // the property is visible when inspecting a form object
+#define PROP_FLAG_DIALOG_VISIBLE 0x00000002 // the property is visible when inspecting a dialog object
+#define PROP_FLAG_DATA_PROPERTY 0x00000004 // the property is to appear on the "Data" page
+#define PROP_FLAG_ENUM 0x00000020 // the property is some kind of enum property, i.e. its
+ // value is chosen from a fixed list of possible values
+#define PROP_FLAG_ENUM_ONE 0x00000060 // the property is an enum property starting with 1
+ // (note that this includes PROP_FLAG_ENUM)
+#define PROP_FLAG_COMPOSEABLE 0x00000080 // the property is "composeable", i.e. an intersection of property
+ // sets should expose it, if all elements do
+#define PROP_FLAG_EXPERIMENTAL 0x00000100 // the property is experimental, i.e. should not appear in the
+ // UI, unless experimental properties are enabled by a configuration
+ // option
+#define PROP_FLAG_REPORT_INVISIBLE 0x00000200 // the property should not appear in the Report Designer UI
+
+
+ //= property ids (for all browsable properties)
+
+
+ #define PROPERTY_ID_NAME 1
+ #define PROPERTY_ID_LABEL 2
+ #define PROPERTY_ID_CONTROLLABEL 3
+ #define PROPERTY_ID_MAXTEXTLEN 4
+ #define PROPERTY_ID_EDITMASK 5
+ #define PROPERTY_ID_LITERALMASK 6
+ #define PROPERTY_ID_STRICTFORMAT 7
+ #define PROPERTY_ID_ENABLED 8
+ #define PROPERTY_ID_READONLY 9
+ #define PROPERTY_ID_PRINTABLE 10
+ #define PROPERTY_ID_CONTROLSOURCE 11
+ #define PROPERTY_ID_TABSTOP 12
+ #define PROPERTY_ID_TABINDEX 13
+ #define PROPERTY_ID_DATASOURCE 14
+ #define PROPERTY_ID_COMMAND 15
+ #define PROPERTY_ID_COMMANDTYPE 16
+ #define PROPERTY_ID_FILTER 17
+ #define PROPERTY_ID_SORT 18
+ #define PROPERTY_ID_INSERTONLY 19
+ #define PROPERTY_ID_ALLOWADDITIONS 20
+ #define PROPERTY_ID_ALLOWEDITS 21
+ #define PROPERTY_ID_ALLOWDELETIONS 22
+ #define PROPERTY_ID_GROUP_NAME 23
+ #define PROPERTY_ID_NAVIGATION 24
+ #define PROPERTY_ID_CYCLE 25
+ #define PROPERTY_ID_HIDDEN_VALUE 26
+ #define PROPERTY_ID_VALUEMIN 27
+ #define PROPERTY_ID_VALUEMAX 28
+ #define PROPERTY_ID_VALUESTEP 29
+ #define PROPERTY_ID_DEFAULT_VALUE 30
+ #define PROPERTY_ID_DECIMAL_ACCURACY 31
+ #define PROPERTY_ID_SHOWTHOUSANDSEP 32
+ #define PROPERTY_ID_REFVALUE 33
+ #define PROPERTY_ID_CURRENCYSYMBOL 34
+ #define PROPERTY_ID_CURRSYM_POSITION 35
+ #define PROPERTY_ID_DATEMIN 36
+ #define PROPERTY_ID_DATEMAX 37
+ #define PROPERTY_ID_DATEFORMAT 38
+ #define PROPERTY_ID_SELECTEDITEMS 39
+ #define PROPERTY_ID_DEFAULT_DATE 40
+ #define PROPERTY_ID_TIMEMIN 41
+ #define PROPERTY_ID_TIMEMAX 42
+ #define PROPERTY_ID_TIMEFORMAT 43
+ #define PROPERTY_ID_DEFAULT_TIME 44
+ #define PROPERTY_ID_EFFECTIVE_MIN 45
+ #define PROPERTY_ID_EFFECTIVE_MAX 46
+ #define PROPERTY_ID_EFFECTIVE_DEFAULT 47
+ #define PROPERTY_ID_FORMATKEY 48
+ #define PROPERTY_ID_CLASSID 50
+ #define PROPERTY_ID_HEIGHT 51
+ #define PROPERTY_ID_WIDTH 52
+ #define PROPERTY_ID_BOUNDCOLUMN 53
+ #define PROPERTY_ID_LISTSOURCETYPE 54
+ #define PROPERTY_ID_LISTSOURCE 55
+ #define PROPERTY_ID_LISTINDEX 56
+ #define PROPERTY_ID_STRINGITEMLIST 57
+ #define PROPERTY_ID_DEFAULT_TEXT 58
+ #define PROPERTY_ID_FONT 59
+ #define PROPERTY_ID_ALIGN 60
+ #define PROPERTY_ID_ROWHEIGHT 61
+ #define PROPERTY_ID_BACKGROUNDCOLOR 62
+ #define PROPERTY_ID_FILLCOLOR 63
+ #define PROPERTY_ID_ESCAPE_PROCESSING 64
+ #define PROPERTY_ID_LINECOLOR 65
+ #define PROPERTY_ID_BORDER 66
+ #define PROPERTY_ID_DROPDOWN 67
+ #define PROPERTY_ID_AUTOCOMPLETE 68
+ #define PROPERTY_ID_LINECOUNT 69
+ #define PROPERTY_ID_WORDBREAK 70
+ #define PROPERTY_ID_MULTILINE 71
+ #define PROPERTY_ID_MULTISELECTION 72
+ #define PROPERTY_ID_AUTOLINEBREAK 73
+ #define PROPERTY_ID_HSCROLL 74
+ #define PROPERTY_ID_VSCROLL 75
+ #define PROPERTY_ID_SPIN 76
+ #define PROPERTY_ID_BUTTONTYPE 77
+ #define PROPERTY_ID_TARGET_URL 78
+ #define PROPERTY_ID_TARGET_FRAME 79
+ #define PROPERTY_ID_SUBMIT_ACTION 80
+ #define PROPERTY_ID_SUBMIT_TARGET 81
+ #define PROPERTY_ID_SUBMIT_METHOD 82
+ #define PROPERTY_ID_SUBMIT_ENCODING 83
+ #define PROPERTY_ID_DEFAULT_STATE 84
+ #define PROPERTY_ID_DEFAULTBUTTON 85
+ #define PROPERTY_ID_IMAGE_URL 86
+ #define PROPERTY_ID_DEFAULT_SELECT_SEQ 87
+ #define PROPERTY_ID_ECHO_CHAR 88
+ #define PROPERTY_ID_EMPTY_IS_NULL 89
+ #define PROPERTY_ID_TRISTATE 90
+ #define PROPERTY_ID_MASTERFIELDS 91
+ #define PROPERTY_ID_DETAILFIELDS 92
+ #define PROPERTY_ID_RECORDMARKER 93
+ #define PROPERTY_ID_FILTERPROPOSAL 94
+ #define PROPERTY_ID_TAG 95
+ #define PROPERTY_ID_HELPTEXT 96
+ #define PROPERTY_ID_HELPURL 97
+ #define PROPERTY_ID_HASNAVIGATION 98
+ #define PROPERTY_ID_POSITIONX 99
+ #define PROPERTY_ID_POSITIONY 100
+ #define PROPERTY_ID_TITLE 101
+ #define PROPERTY_ID_STEP 102
+ #define PROPERTY_ID_PROGRESSVALUE 103
+ #define PROPERTY_ID_PROGRESSVALUE_MIN 104
+ #define PROPERTY_ID_PROGRESSVALUE_MAX 105
+ #define PROPERTY_ID_SCROLLVALUE 106
+ #define PROPERTY_ID_SCROLLVALUE_MAX 107
+ #define PROPERTY_ID_LINEINCREMENT 108
+ #define PROPERTY_ID_BLOCKINCREMENT 109
+ #define PROPERTY_ID_VISIBLESIZE 110
+ #define PROPERTY_ID_ORIENTATION 111
+ #define PROPERTY_ID_IMAGEPOSITION 112
+ #define PROPERTY_ID_DATE 113
+ #define PROPERTY_ID_STATE 114
+ #define PROPERTY_ID_TIME 115
+ #define PROPERTY_ID_VALUE 116
+ #define PROPERTY_ID_SCALEIMAGE 117
+ #define PROPERTY_ID_PUSHBUTTONTYPE 118
+ #define PROPERTY_ID_EFFECTIVE_VALUE 119
+ #define PROPERTY_ID_TEXT 120
+ #define PROPERTY_ID_BOUND_CELL 121
+ #define PROPERTY_ID_LIST_CELL_RANGE 122
+ #define PROPERTY_ID_CELL_EXCHANGE_TYPE 123
+ #define PROPERTY_ID_SCROLLVALUE_MIN 124
+ #define PROPERTY_ID_DEFAULT_SCROLLVALUE 125
+ #define PROPERTY_ID_REPEAT_DELAY 126
+ #define PROPERTY_ID_SYMBOLCOLOR 127
+ #define PROPERTY_ID_SPINVALUE 128
+ #define PROPERTY_ID_SPINVALUE_MIN 129
+ #define PROPERTY_ID_SPINVALUE_MAX 130
+ #define PROPERTY_ID_DEFAULT_SPINVALUE 131
+ #define PROPERTY_ID_SPININCREMENT 132
+ #define PROPERTY_ID_REPEAT 133
+ #define PROPERTY_ID_SHOW_SCROLLBARS 134
+ #define PROPERTY_ID_ICONSIZE 135
+ #define PROPERTY_ID_SHOW_POSITION 136
+ #define PROPERTY_ID_SHOW_NAVIGATION 137
+ #define PROPERTY_ID_SHOW_RECORDACTIONS 138
+ #define PROPERTY_ID_SHOW_FILTERSORT 139
+ #define PROPERTY_ID_TEXTTYPE 140
+ #define PROPERTY_ID_LINEEND_FORMAT 141
+ #define PROPERTY_ID_TOGGLE 142
+ #define PROPERTY_ID_FOCUSONCLICK 143
+ #define PROPERTY_ID_HIDEINACTIVESELECTION 144
+ #define PROPERTY_ID_VISUALEFFECT 145
+ #define PROPERTY_ID_BORDERCOLOR 146
+ #define PROPERTY_ID_XML_DATA_MODEL 147
+ #define PROPERTY_ID_BIND_EXPRESSION 148
+ #define PROPERTY_ID_XSD_REQUIRED 149
+ #define PROPERTY_ID_XSD_RELEVANT 150
+ #define PROPERTY_ID_XSD_READONLY 151
+ #define PROPERTY_ID_XSD_CONSTRAINT 152
+ #define PROPERTY_ID_XSD_CALCULATION 153
+ #define PROPERTY_ID_XSD_DATA_TYPE 154
+ #define PROPERTY_ID_XSD_WHITESPACES 155
+ #define PROPERTY_ID_XSD_PATTERN 156
+ #define PROPERTY_ID_XSD_LENGTH 157
+ #define PROPERTY_ID_XSD_MIN_LENGTH 158
+ #define PROPERTY_ID_XSD_MAX_LENGTH 159
+ #define PROPERTY_ID_XSD_TOTAL_DIGITS 160
+ #define PROPERTY_ID_XSD_FRACTION_DIGITS 161
+ #define PROPERTY_ID_XSD_MAX_INCLUSIVE_INT 162
+ #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_INT 163
+ #define PROPERTY_ID_XSD_MIN_INCLUSIVE_INT 164
+ #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_INT 165
+ #define PROPERTY_ID_XSD_MAX_INCLUSIVE_DOUBLE 166
+ #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_DOUBLE 167
+ #define PROPERTY_ID_XSD_MIN_INCLUSIVE_DOUBLE 168
+ #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_DOUBLE 169
+ #define PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE 170
+ #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE 171
+ #define PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE 172
+ #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE 173
+ #define PROPERTY_ID_XSD_MAX_INCLUSIVE_TIME 174
+ #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_TIME 175
+ #define PROPERTY_ID_XSD_MIN_INCLUSIVE_TIME 176
+ #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_TIME 177
+ #define PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE_TIME 178
+ #define PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE_TIME 179
+ #define PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE_TIME 180
+ #define PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE_TIME 181
+ #define PROPERTY_ID_UNCHECKEDREFVALUE 182
+ #define PROPERTY_ID_SUBMISSION_ID 183
+ #define PROPERTY_ID_XFORMS_BUTTONTYPE 184
+ #define PROPERTY_ID_LIST_BINDING 185
+ #define PROPERTY_ID_VERTICAL_ALIGN 186
+ #define PROPERTY_ID_BINDING_NAME 187
+ #define PROPERTY_ID_DECORATION 188
+ #define PROPERTY_ID_SELECTION_TYPE 189
+ #define PROPERTY_ID_ROOT_DISPLAYED 190
+ #define PROPERTY_ID_SHOWS_HANDLES 191
+ #define PROPERTY_ID_SHOWS_ROOT_HANDLES 192
+ #define PROPERTY_ID_EDITABLE 193
+ #define PROPERTY_ID_INVOKES_STOP_NOT_EDITING 194
+ #define PROPERTY_ID_NOLABEL 195
+ #define PROPERTY_ID_SCALE_MODE 196
+ #define PROPERTY_ID_INPUT_REQUIRED 197
+ #define PROPERTY_ID_WRITING_MODE 198
+ #define PROPERTY_ID_ENABLE_VISIBLE 199
+ #define PROPERTY_ID_WHEEL_BEHAVIOR 200
+ #define PROPERTY_ID_TEXT_ANCHOR_TYPE 201
+ #define PROPERTY_ID_SHEET_ANCHOR_TYPE 202
+ #define PROPERTY_ID_SCROLL_WIDTH 203
+ #define PROPERTY_ID_SCROLL_HEIGHT 204
+ #define PROPERTY_ID_SCROLL_TOP 205
+ #define PROPERTY_ID_SCROLL_LEFT 206
+ #define PROPERTY_ID_TYPEDITEMLIST 207
+ #define PROPERTY_ID_SELECTIONMODEL 208
+ #define PROPERTY_ID_USEGRIDLINE 209
+ #define PROPERTY_ID_GRIDLINECOLOR 210
+ #define PROPERTY_ID_SHOWCOLUMNHEADER 211
+ #define PROPERTY_ID_SHOWROWHEADER 212
+ #define PROPERTY_ID_HEADERBACKGROUNDCOLOR 213
+ #define PROPERTY_ID_HEADERTEXTCOLOR 214
+ #define PROPERTY_ID_ACTIVESELECTIONBACKGROUNDCOLOR 215
+ #define PROPERTY_ID_ACTIVESELECTIONTEXTCOLOR 216
+ #define PROPERTY_ID_INACTIVESELECTIONBACKGROUNDCOLOR 217
+ #define PROPERTY_ID_INACTIVESELECTIONTEXTCOLOR 218
+ #define PROPERTY_ID_URL 219
+ #define PROPERTY_ID_AUTOGROW 220
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/formstrings.hxx b/extensions/source/propctrlr/formstrings.hxx
new file mode 100644
index 000000000..df151ab95
--- /dev/null
+++ b/extensions/source/propctrlr/formstrings.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 incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <rtl/ustring.hxx>
+
+inline constexpr OUStringLiteral PROPERTY_DEFAULTCONTROL = u"DefaultControl";
+inline constexpr OUStringLiteral PROPERTY_INTROSPECTEDOBJECT = u"IntrospectedObject";
+inline constexpr OUStringLiteral PROPERTY_CURRENTPAGE = u"CurrentPage";
+inline constexpr OUStringLiteral PROPERTY_CONTROLCONTEXT = u"ControlContext";
+
+// properties
+inline constexpr OUStringLiteral PROPERTY_CLASSID = u"ClassId";
+inline constexpr OUStringLiteral PROPERTY_CONTROLLABEL = u"LabelControl";
+inline constexpr OUStringLiteral PROPERTY_LABEL = u"Label";
+inline constexpr OUStringLiteral PROPERTY_TABINDEX = u"TabIndex";
+inline constexpr OUStringLiteral PROPERTY_WHEEL_BEHAVIOR = u"MouseWheelBehavior";
+inline constexpr OUStringLiteral PROPERTY_TAG = u"Tag";
+inline constexpr OUStringLiteral PROPERTY_NAME = u"Name";
+inline constexpr OUStringLiteral PROPERTY_GROUP_NAME = u"GroupName";
+inline constexpr OUStringLiteral PROPERTY_VALUE = u"Value";
+inline constexpr OUStringLiteral PROPERTY_TEXT = u"Text";
+inline constexpr OUStringLiteral PROPERTY_NAVIGATION = u"NavigationBarMode";
+inline constexpr OUStringLiteral PROPERTY_CYCLE = u"Cycle";
+inline constexpr OUStringLiteral PROPERTY_CONTROLSOURCE = u"DataField";
+inline constexpr OUStringLiteral PROPERTY_INPUT_REQUIRED = u"InputRequired";
+inline constexpr OUStringLiteral PROPERTY_ENABLED = u"Enabled";
+inline constexpr OUStringLiteral PROPERTY_ENABLE_VISIBLE = u"EnableVisible";
+inline constexpr OUStringLiteral PROPERTY_READONLY = u"ReadOnly";
+inline constexpr OUStringLiteral PROPERTY_FILTER = u"Filter";
+inline constexpr OUStringLiteral PROPERTY_WIDTH = u"Width";
+inline constexpr OUStringLiteral PROPERTY_MULTILINE = u"MultiLine";
+inline constexpr OUStringLiteral PROPERTY_WORDBREAK = u"WordBreak";
+inline constexpr OUStringLiteral PROPERTY_TARGET_URL = u"TargetURL";
+inline constexpr OUStringLiteral PROPERTY_TARGET_FRAME = u"TargetFrame";
+inline constexpr OUStringLiteral PROPERTY_MAXTEXTLEN = u"MaxTextLen";
+inline constexpr OUStringLiteral PROPERTY_EDITMASK = u"EditMask";
+inline constexpr OUStringLiteral PROPERTY_SPIN = u"Spin";
+inline constexpr OUStringLiteral PROPERTY_TRISTATE = u"TriState";
+inline constexpr OUStringLiteral PROPERTY_HIDDEN_VALUE = u"HiddenValue";
+inline constexpr OUStringLiteral PROPERTY_BUTTONTYPE = u"ButtonType";
+inline constexpr OUStringLiteral PROPERTY_XFORMS_BUTTONTYPE = u"XFormsButtonType";
+inline constexpr OUStringLiteral PROPERTY_STRINGITEMLIST = u"StringItemList";
+inline constexpr OUStringLiteral PROPERTY_TYPEDITEMLIST = u"TypedItemList";
+inline constexpr OUStringLiteral PROPERTY_DEFAULT_TEXT = u"DefaultText";
+inline constexpr OUStringLiteral PROPERTY_DEFAULT_STATE = u"DefaultState";
+inline constexpr OUStringLiteral PROPERTY_FORMATKEY = u"FormatKey";
+inline constexpr OUStringLiteral PROPERTY_FORMATSSUPPLIER = u"FormatsSupplier";
+inline constexpr OUStringLiteral PROPERTY_SUBMIT_ACTION = u"SubmitAction";
+inline constexpr OUStringLiteral PROPERTY_SUBMIT_TARGET = u"SubmitTarget";
+inline constexpr OUStringLiteral PROPERTY_SUBMIT_METHOD = u"SubmitMethod";
+inline constexpr OUStringLiteral PROPERTY_SUBMIT_ENCODING = u"SubmitEncoding";
+inline constexpr OUStringLiteral PROPERTY_IMAGE_URL = u"ImageURL";
+inline constexpr OUStringLiteral PROPERTY_GRAPHIC = u"Graphic";
+inline constexpr OUStringLiteral PROPERTY_EMPTY_IS_NULL = u"ConvertEmptyToNull";
+inline constexpr OUStringLiteral PROPERTY_LISTSOURCETYPE = u"ListSourceType";
+inline constexpr OUStringLiteral PROPERTY_LISTSOURCE = u"ListSource";
+inline constexpr OUStringLiteral PROPERTY_DEFAULT_SELECT_SEQ = u"DefaultSelection";
+inline constexpr OUStringLiteral PROPERTY_MULTISELECTION = u"MultiSelection";
+inline constexpr OUStringLiteral PROPERTY_ALIGN = u"Align";
+inline constexpr OUStringLiteral PROPERTY_VERTICAL_ALIGN = u"VerticalAlign";
+inline constexpr OUStringLiteral PROPERTY_DEFAULT_DATE = u"DefaultDate";
+inline constexpr OUStringLiteral PROPERTY_DEFAULT_TIME = u"DefaultTime";
+inline constexpr OUStringLiteral PROPERTY_DEFAULT_VALUE = u"DefaultValue";
+inline constexpr OUStringLiteral PROPERTY_DECIMAL_ACCURACY = u"DecimalAccuracy";
+inline constexpr OUStringLiteral PROPERTY_REFVALUE = u"RefValue";
+inline constexpr OUStringLiteral PROPERTY_UNCHECKEDREFVALUE = u"SecondaryRefValue";
+inline constexpr OUStringLiteral PROPERTY_VALUEMIN = u"ValueMin";
+inline constexpr OUStringLiteral PROPERTY_VALUEMAX = u"ValueMax";
+inline constexpr OUStringLiteral PROPERTY_STRICTFORMAT = u"StrictFormat";
+inline constexpr OUStringLiteral PROPERTY_ALLOWADDITIONS = u"AllowInserts";
+inline constexpr OUStringLiteral PROPERTY_ALLOWEDITS = u"AllowUpdates";
+inline constexpr OUStringLiteral PROPERTY_ALLOWDELETIONS = u"AllowDeletes";
+inline constexpr OUStringLiteral PROPERTY_MASTERFIELDS = u"MasterFields";
+inline constexpr OUStringLiteral PROPERTY_LITERALMASK = u"LiteralMask";
+inline constexpr OUStringLiteral PROPERTY_VALUESTEP = u"ValueStep";
+inline constexpr OUStringLiteral PROPERTY_SHOWTHOUSANDSEP = u"ShowThousandsSeparator";
+inline constexpr OUStringLiteral PROPERTY_CURRENCYSYMBOL = u"CurrencySymbol";
+inline constexpr OUStringLiteral PROPERTY_DATEFORMAT = u"DateFormat";
+inline constexpr OUStringLiteral PROPERTY_DATEMIN = u"DateMin";
+inline constexpr OUStringLiteral PROPERTY_DATEMAX = u"DateMax";
+inline constexpr OUStringLiteral PROPERTY_TIMEFORMAT = u"TimeFormat";
+inline constexpr OUStringLiteral PROPERTY_TIMEMIN = u"TimeMin";
+inline constexpr OUStringLiteral PROPERTY_TIMEMAX = u"TimeMax";
+inline constexpr OUStringLiteral PROPERTY_LINECOUNT = u"LineCount";
+inline constexpr OUStringLiteral PROPERTY_BOUNDCOLUMN = u"BoundColumn";
+inline constexpr OUStringLiteral PROPERTY_BACKGROUNDCOLOR = u"BackgroundColor";
+inline constexpr OUStringLiteral PROPERTY_FILLCOLOR = u"FillColor";
+inline constexpr OUStringLiteral PROPERTY_TEXTCOLOR = u"TextColor";
+inline constexpr OUStringLiteral PROPERTY_LINECOLOR = u"LineColor";
+inline constexpr OUStringLiteral PROPERTY_BORDER = u"Border";
+inline constexpr OUStringLiteral PROPERTY_ICONSIZE = u"IconSize";
+inline constexpr OUStringLiteral PROPERTY_DROPDOWN = u"Dropdown";
+inline constexpr OUStringLiteral PROPERTY_HSCROLL = u"HScroll";
+inline constexpr OUStringLiteral PROPERTY_VSCROLL = u"VScroll";
+inline constexpr OUStringLiteral PROPERTY_SHOW_SCROLLBARS = u"ShowScrollbars";
+inline constexpr OUStringLiteral PROPERTY_TABSTOP = u"Tabstop";
+inline constexpr OUStringLiteral PROPERTY_AUTOCOMPLETE = u"Autocomplete";
+inline constexpr OUStringLiteral PROPERTY_PRINTABLE = u"Printable";
+inline constexpr OUStringLiteral PROPERTY_ECHO_CHAR = u"EchoChar";
+inline constexpr OUStringLiteral PROPERTY_ROWHEIGHT = u"RowHeight";
+inline constexpr OUStringLiteral PROPERTY_HELPTEXT = u"HelpText";
+inline constexpr OUStringLiteral PROPERTY_FONT = u"FontDescriptor";
+inline constexpr OUStringLiteral PROPERTY_FONT_NAME = u"FontName";
+inline constexpr OUStringLiteral PROPERTY_FONT_STYLENAME = u"FontStyleName";
+inline constexpr OUStringLiteral PROPERTY_FONT_FAMILY = u"FontFamily";
+inline constexpr OUStringLiteral PROPERTY_FONT_CHARSET = u"FontCharset";
+inline constexpr OUStringLiteral PROPERTY_FONT_HEIGHT = u"FontHeight";
+inline constexpr OUStringLiteral PROPERTY_FONT_WEIGHT = u"FontWeight";
+inline constexpr OUStringLiteral PROPERTY_FONT_SLANT = u"FontSlant";
+inline constexpr OUStringLiteral PROPERTY_FONT_UNDERLINE = u"FontUnderline";
+inline constexpr OUStringLiteral PROPERTY_FONT_STRIKEOUT = u"FontStrikeout";
+inline constexpr OUStringLiteral PROPERTY_FONT_RELIEF = u"FontRelief";
+inline constexpr OUStringLiteral PROPERTY_FONT_EMPHASIS_MARK = u"FontEmphasisMark";
+inline constexpr OUStringLiteral PROPERTY_TEXTLINECOLOR = u"TextLineColor";
+inline constexpr OUStringLiteral PROPERTY_HELPURL = u"HelpURL";
+inline constexpr OUStringLiteral PROPERTY_RECORDMARKER = u"HasRecordMarker";
+inline constexpr OUStringLiteral PROPERTY_EFFECTIVE_DEFAULT = u"EffectiveDefault";
+inline constexpr OUStringLiteral PROPERTY_EFFECTIVE_MIN = u"EffectiveMin";
+inline constexpr OUStringLiteral PROPERTY_EFFECTIVE_MAX = u"EffectiveMax";
+inline constexpr OUStringLiteral PROPERTY_FILTERPROPOSAL = u"UseFilterValueProposal";
+inline constexpr OUStringLiteral PROPERTY_CURRSYM_POSITION = u"PrependCurrencySymbol";
+inline constexpr OUStringLiteral PROPERTY_COMMAND = u"Command";
+inline constexpr OUStringLiteral PROPERTY_COMMANDTYPE = u"CommandType";
+inline constexpr OUStringLiteral PROPERTY_INSERTONLY = u"IgnoreResult";
+inline constexpr OUStringLiteral PROPERTY_ESCAPE_PROCESSING = u"EscapeProcessing";
+inline constexpr OUStringLiteral PROPERTY_TITLE = u"Title";
+inline constexpr OUStringLiteral PROPERTY_SORT = u"Order";
+inline constexpr OUStringLiteral PROPERTY_DATASOURCE = u"DataSourceName";
+inline constexpr OUStringLiteral PROPERTY_DETAILFIELDS = u"DetailFields";
+inline constexpr OUStringLiteral PROPERTY_DEFAULTBUTTON = u"DefaultButton";
+inline constexpr OUStringLiteral PROPERTY_LISTINDEX = u"ListIndex";
+inline constexpr OUStringLiteral PROPERTY_HEIGHT = u"Height";
+inline constexpr OUStringLiteral PROPERTY_HASNAVIGATION = u"HasNavigationBar";
+inline constexpr OUStringLiteral PROPERTY_POSITIONX = u"PositionX";
+inline constexpr OUStringLiteral PROPERTY_POSITIONY = u"PositionY";
+inline constexpr OUStringLiteral PROPERTY_AUTOGROW = u"AutoGrow";
+inline constexpr OUStringLiteral PROPERTY_STEP = u"Step";
+inline constexpr OUStringLiteral PROPERTY_WORDLINEMODE = u"FontWordLineMode";
+inline constexpr OUStringLiteral PROPERTY_PROGRESSVALUE = u"ProgressValue";
+inline constexpr OUStringLiteral PROPERTY_PROGRESSVALUE_MIN = u"ProgressValueMin";
+inline constexpr OUStringLiteral PROPERTY_PROGRESSVALUE_MAX = u"ProgressValueMax";
+inline constexpr OUStringLiteral PROPERTY_SCROLLVALUE = u"ScrollValue";
+inline constexpr OUStringLiteral PROPERTY_DEFAULT_SCROLLVALUE = u"DefaultScrollValue";
+inline constexpr OUStringLiteral PROPERTY_SCROLLVALUE_MIN = u"ScrollValueMin";
+inline constexpr OUStringLiteral PROPERTY_SCROLLVALUE_MAX = u"ScrollValueMax";
+inline constexpr OUStringLiteral PROPERTY_SCROLL_WIDTH = u"ScrollWidth";
+inline constexpr OUStringLiteral PROPERTY_SCROLL_HEIGHT = u"ScrollHeight";
+inline constexpr OUStringLiteral PROPERTY_SCROLL_TOP = u"ScrollTop";
+inline constexpr OUStringLiteral PROPERTY_SCROLL_LEFT = u"ScrollLeft";
+inline constexpr OUStringLiteral PROPERTY_LINEINCREMENT = u"LineIncrement";
+inline constexpr OUStringLiteral PROPERTY_BLOCKINCREMENT = u"BlockIncrement";
+inline constexpr OUStringLiteral PROPERTY_VISIBLESIZE = u"VisibleSize";
+inline constexpr OUStringLiteral PROPERTY_ORIENTATION = u"Orientation";
+inline constexpr OUStringLiteral PROPERTY_IMAGEPOSITION = u"ImagePosition";
+inline constexpr OUStringLiteral PROPERTY_ACTIVE_CONNECTION = u"ActiveConnection";
+inline constexpr OUStringLiteral PROPERTY_ACTIVECOMMAND = u"ActiveCommand";
+inline constexpr OUStringLiteral PROPERTY_DATE = u"Date";
+inline constexpr OUStringLiteral PROPERTY_STATE = u"State";
+inline constexpr OUStringLiteral PROPERTY_TIME = u"Time";
+inline constexpr OUStringLiteral PROPERTY_SCALEIMAGE = u"ScaleImage";
+inline constexpr OUStringLiteral PROPERTY_SCALE_MODE = u"ScaleMode";
+inline constexpr OUStringLiteral PROPERTY_PUSHBUTTONTYPE = u"PushButtonType";
+inline constexpr OUStringLiteral PROPERTY_EFFECTIVE_VALUE = u"EffectiveValue";
+inline constexpr OUStringLiteral PROPERTY_SELECTEDITEMS = u"SelectedItems";
+inline constexpr OUStringLiteral PROPERTY_REPEAT = u"Repeat";
+inline constexpr OUStringLiteral PROPERTY_REPEAT_DELAY = u"RepeatDelay";
+inline constexpr OUStringLiteral PROPERTY_SYMBOLCOLOR = u"SymbolColor";
+inline constexpr OUStringLiteral PROPERTY_SPINVALUE = u"SpinValue";
+inline constexpr OUStringLiteral PROPERTY_SPINVALUE_MIN = u"SpinValueMin";
+inline constexpr OUStringLiteral PROPERTY_SPINVALUE_MAX = u"SpinValueMax";
+inline constexpr OUStringLiteral PROPERTY_DEFAULT_SPINVALUE = u"DefaultSpinValue";
+inline constexpr OUStringLiteral PROPERTY_SPININCREMENT = u"SpinIncrement";
+inline constexpr OUStringLiteral PROPERTY_SHOW_POSITION = u"ShowPosition";
+inline constexpr OUStringLiteral PROPERTY_SHOW_NAVIGATION = u"ShowNavigation";
+inline constexpr OUStringLiteral PROPERTY_SHOW_RECORDACTIONS = u"ShowRecordActions";
+inline constexpr OUStringLiteral PROPERTY_SHOW_FILTERSORT = u"ShowFilterSort";
+inline constexpr OUStringLiteral PROPERTY_LINEEND_FORMAT = u"LineEndFormat";
+inline constexpr OUStringLiteral PROPERTY_DECORATION = u"Decoration";
+inline constexpr OUStringLiteral PROPERTY_NOLABEL = u"NoLabel";
+inline constexpr OUStringLiteral PROPERTY_URL = u"URL";
+
+inline constexpr OUStringLiteral PROPERTY_SELECTION_TYPE = u"SelectionType";
+inline constexpr OUStringLiteral PROPERTY_ROOT_DISPLAYED = u"RootDisplayed";
+inline constexpr OUStringLiteral PROPERTY_SHOWS_HANDLES = u"ShowsHandles";
+inline constexpr OUStringLiteral PROPERTY_SHOWS_ROOT_HANDLES = u"ShowsRootHandles";
+inline constexpr OUStringLiteral PROPERTY_EDITABLE = u"Editable";
+inline constexpr OUStringLiteral PROPERTY_INVOKES_STOP_NOT_EDITING = u"InvokesStopNodeEditing";
+
+inline constexpr OUStringLiteral PROPERTY_TOGGLE = u"Toggle";
+inline constexpr OUStringLiteral PROPERTY_FOCUSONCLICK = u"FocusOnClick";
+inline constexpr OUStringLiteral PROPERTY_HIDEINACTIVESELECTION = u"HideInactiveSelection";
+inline constexpr OUStringLiteral PROPERTY_VISUALEFFECT = u"VisualEffect";
+inline constexpr OUStringLiteral PROPERTY_BORDERCOLOR = u"BorderColor";
+
+inline constexpr OUStringLiteral PROPERTY_ADDRESS = u"Address";
+inline constexpr OUStringLiteral PROPERTY_REFERENCE_SHEET = u"ReferenceSheet";
+inline constexpr OUStringLiteral PROPERTY_UI_REPRESENTATION = u"UserInterfaceRepresentation";
+
+inline constexpr OUStringLiteral PROPERTY_XML_DATA_MODEL = u"XMLDataModel";
+inline constexpr OUStringLiteral PROPERTY_BINDING_NAME = u"BindingName";
+inline constexpr OUStringLiteral PROPERTY_BIND_EXPRESSION = u"BindingExpression";
+inline constexpr OUStringLiteral PROPERTY_LIST_BINDING = u"ListBinding";
+inline constexpr OUStringLiteral PROPERTY_XSD_REQUIRED = u"RequiredExpression";
+inline constexpr OUStringLiteral PROPERTY_XSD_RELEVANT = u"RelevantExpression";
+inline constexpr OUStringLiteral PROPERTY_XSD_READONLY = u"ReadonlyExpression";
+inline constexpr OUStringLiteral PROPERTY_XSD_CONSTRAINT = u"ConstraintExpression";
+inline constexpr OUStringLiteral PROPERTY_XSD_CALCULATION = u"CalculateExpression";
+inline constexpr OUStringLiteral PROPERTY_XSD_DATA_TYPE = u"Type";
+inline constexpr OUStringLiteral PROPERTY_XSD_WHITESPACES = u"WhiteSpace";
+inline constexpr OUStringLiteral PROPERTY_XSD_PATTERN = u"Pattern";
+inline constexpr OUStringLiteral PROPERTY_XSD_LENGTH = u"Length";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_LENGTH = u"MinLength";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_LENGTH = u"MaxLength";
+inline constexpr OUStringLiteral PROPERTY_XSD_TOTAL_DIGITS = u"TotalDigits";
+inline constexpr OUStringLiteral PROPERTY_XSD_FRACTION_DIGITS = u"FractionDigits";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_INT = u"MaxInclusiveInt";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_INT = u"MaxExclusiveInt";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_INT = u"MinInclusiveInt";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_INT = u"MinExclusiveInt";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_DOUBLE = u"MaxInclusiveDouble";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_DOUBLE = u"MaxExclusiveDouble";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_DOUBLE = u"MinInclusiveDouble";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_DOUBLE = u"MinExclusiveDouble";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_DATE = u"MaxInclusiveDate";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_DATE = u"MaxExclusiveDate";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_DATE = u"MinInclusiveDate";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_DATE = u"MinExclusiveDate";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_TIME = u"MaxInclusiveTime";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_TIME = u"MaxExclusiveTime";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_TIME = u"MinInclusiveTime";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_TIME = u"MinExclusiveTime";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_INCLUSIVE_DATE_TIME = u"MaxInclusiveDateTime";
+inline constexpr OUStringLiteral PROPERTY_XSD_MAX_EXCLUSIVE_DATE_TIME = u"MaxExclusiveDateTime";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_INCLUSIVE_DATE_TIME = u"MinInclusiveDateTime";
+inline constexpr OUStringLiteral PROPERTY_XSD_MIN_EXCLUSIVE_DATE_TIME = u"MinExclusiveDateTime";
+inline constexpr OUStringLiteral PROPERTY_SUBMISSION_ID = u"SubmissionID";
+inline constexpr OUStringLiteral PROPERTY_BINDING_ID = u"BindingID";
+inline constexpr OUStringLiteral PROPERTY_WRITING_MODE = u"WritingMode";
+inline constexpr OUStringLiteral PROPERTY_TEXT_ANCHOR_TYPE = u"TextAnchorType";
+inline constexpr OUStringLiteral PROPERTY_SHEET_ANCHOR_TYPE = u"SheetAnchorType";
+inline constexpr OUStringLiteral PROPERTY_ANCHOR_TYPE = u"AnchorType";
+inline constexpr OUStringLiteral PROPERTY_ANCHOR = u"Anchor";
+inline constexpr OUStringLiteral PROPERTY_IS_VISIBLE = u"IsVisible";
+
+inline constexpr OUStringLiteral PROPERTY_MODEL = u"Model";
+
+inline constexpr OUStringLiteral PROPERTY_CELL_EXCHANGE_TYPE = u"ExchangeSelectionIndex";
+inline constexpr OUStringLiteral PROPERTY_BOUND_CELL = u"BoundCell";
+inline constexpr OUStringLiteral PROPERTY_LIST_CELL_RANGE = u"CellRange";
+inline constexpr OUStringLiteral PROPERTY_TEXTTYPE = u"TextType";
+inline constexpr OUStringLiteral PROPERTY_RICHTEXT = u"RichText";
+inline constexpr OUStringLiteral PROPERTY_ROWSET = u"RowSet";
+inline constexpr OUStringLiteral PROPERTY_SELECTIONMODEL = u"SelectionModel";
+inline constexpr OUStringLiteral PROPERTY_USEGRIDLINE = u"UseGridLines";
+inline constexpr OUStringLiteral PROPERTY_GRIDLINECOLOR = u"GridLineColor";
+inline constexpr OUStringLiteral PROPERTY_SHOWCOLUMNHEADER = u"ShowColumnHeader";
+inline constexpr OUStringLiteral PROPERTY_SHOWROWHEADER = u"ShowRowHeader";
+inline constexpr OUStringLiteral PROPERTY_HEADERBACKGROUNDCOLOR = u"HeaderBackgroundColor";
+inline constexpr OUStringLiteral PROPERTY_HEADERTEXTCOLOR = u"HeaderTextColor";
+inline constexpr OUStringLiteral PROPERTY_ACTIVESELECTIONBACKGROUNDCOLOR = u"ActiveSelectionBackgroundColor";
+inline constexpr OUStringLiteral PROPERTY_ACTIVESELECTIONTEXTCOLOR = u"ActiveSelectionTextColor";
+inline constexpr OUStringLiteral PROPERTY_INACTIVESELECTIONBACKGROUNDCOLOR = u"InactiveSelectionBackgroundColor";
+inline constexpr OUStringLiteral PROPERTY_INACTIVESELECTIONTEXTCOLOR = u"InactiveSelectionTextColor";
+
+// services
+inline constexpr OUStringLiteral SERVICE_COMPONENT_GROUPBOX = u"com.sun.star.form.component.GroupBox";
+inline constexpr OUStringLiteral SERVICE_COMPONENT_FIXEDTEXT = u"com.sun.star.form.component.FixedText";
+inline constexpr OUStringLiteral SERVICE_COMPONENT_FORMATTEDFIELD = u"com.sun.star.form.component.FormattedField";
+
+inline constexpr OUStringLiteral SERVICE_TEXT_DOCUMENT = u"com.sun.star.text.TextDocument";
+inline constexpr OUStringLiteral SERVICE_WEB_DOCUMENT = u"com.sun.star.text.WebDocument";
+inline constexpr OUStringLiteral SERVICE_SPREADSHEET_DOCUMENT = u"com.sun.star.sheet.SpreadsheetDocument";
+inline constexpr OUStringLiteral SERVICE_DRAWING_DOCUMENT = u"com.sun.star.drawing.DrawingDocument";
+inline constexpr OUStringLiteral SERVICE_PRESENTATION_DOCUMENT = u"com.sun.star.presentation.PresentationDocument";
+
+inline constexpr OUStringLiteral SERVICE_SHEET_CELL_BINDING = u"com.sun.star.table.CellValueBinding";
+inline constexpr OUStringLiteral SERVICE_SHEET_CELL_INT_BINDING = u"com.sun.star.table.ListPositionCellBinding";
+inline constexpr OUStringLiteral SERVICE_SHEET_CELLRANGE_LISTSOURCE = u"com.sun.star.table.CellRangeListSource";
+inline constexpr OUStringLiteral SERVICE_ADDRESS_CONVERSION = u"com.sun.star.table.CellAddressConversion";
+inline constexpr OUStringLiteral SERVICE_RANGEADDRESS_CONVERSION = u"com.sun.star.table.CellRangeAddressConversion";
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/genericpropertyhandler.cxx b/extensions/source/propctrlr/genericpropertyhandler.cxx
new file mode 100644
index 000000000..8320e5154
--- /dev/null
+++ b/extensions/source/propctrlr/genericpropertyhandler.cxx
@@ -0,0 +1,619 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "enumrepresentation.hxx"
+#include "genericpropertyhandler.hxx"
+#include "handlerhelper.hxx"
+
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/reflection/XEnumTypeDescription.hpp>
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <com/sun/star/inspection/XHyperlinkControl.hpp>
+#include <com/sun/star/awt/XActionListener.hpp>
+#include <com/sun/star/script/Converter.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/extract.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/types.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+
+namespace pcr
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::reflection;
+ using namespace ::com::sun::star::inspection;
+ using ::com::sun::star::awt::XActionListener;
+ using ::com::sun::star::awt::ActionEvent;
+
+ namespace {
+
+ class EnumRepresentation : public IPropertyEnumRepresentation
+ {
+ private:
+ Reference< XEnumTypeDescription > m_xTypeDescription;
+ Type m_aEnumType;
+
+ public:
+ EnumRepresentation( const Reference< XComponentContext >& _rxContext, const Type& _rEnumType );
+ EnumRepresentation(const EnumRepresentation&) = delete;
+ EnumRepresentation& operator=(const EnumRepresentation&) = delete;
+
+ // IPropertyEnumRepresentation implementqation
+ virtual std::vector< OUString >
+ getDescriptions() const override;
+ virtual void getValueFromDescription( const OUString& _rDescription, css::uno::Any& _out_rValue ) const override;
+ virtual OUString getDescriptionForValue( const css::uno::Any& _rEnumValue ) const override;
+
+ private:
+ void impl_getValues( Sequence< sal_Int32 >& _out_rValues ) const;
+ };
+
+ }
+
+ EnumRepresentation::EnumRepresentation( const Reference< XComponentContext >& _rxContext, const Type& _rEnumType )
+ :m_aEnumType( _rEnumType )
+ {
+ try
+ {
+ if ( _rxContext.is() )
+ {
+ Reference< XHierarchicalNameAccess > xTypeDescProv(
+ _rxContext->getValueByName("/singletons/com.sun.star.reflection.theTypeDescriptionManager"),
+ UNO_QUERY_THROW );
+
+ m_xTypeDescription.set( xTypeDescProv->getByHierarchicalName( m_aEnumType.getTypeName() ), UNO_QUERY_THROW );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EnumRepresentation::EnumRepresentation" );
+ }
+ }
+
+ std::vector< OUString > EnumRepresentation::getDescriptions() const
+ {
+ Sequence< OUString > aNames;
+ try
+ {
+ if ( m_xTypeDescription.is() )
+ aNames = m_xTypeDescription->getEnumNames();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EnumRepresentation::getDescriptions" );
+ }
+
+ return std::vector< OUString >( std::cbegin(aNames), std::cend(aNames) );
+ }
+
+ void EnumRepresentation::impl_getValues( Sequence< sal_Int32 >& _out_rValues ) const
+ {
+ _out_rValues.realloc( 0 );
+ try
+ {
+ if ( m_xTypeDescription.is() )
+ _out_rValues = m_xTypeDescription->getEnumValues();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "EnumRepresentation::impl_getValues" );
+ }
+ }
+
+ void EnumRepresentation::getValueFromDescription( const OUString& _rDescription, Any& _out_rValue ) const
+ {
+ std::vector< OUString > aDescriptions( getDescriptions() );
+
+ sal_Int32 index = std::find( aDescriptions.begin(), aDescriptions.end(),
+ _rDescription ) - aDescriptions.begin();
+
+ Sequence< sal_Int32 > aValues;
+ impl_getValues( aValues );
+
+ if ( ( index >= 0 ) && ( index < aValues.getLength() ) )
+ _out_rValue = ::cppu::int2enum( aValues[ index ], m_aEnumType );
+ else
+ {
+ OSL_FAIL( "EnumRepresentation::getValueFromDescription: cannot convert!" );
+ _out_rValue.clear();
+ }
+ }
+
+ OUString EnumRepresentation::getDescriptionForValue( const Any& _rEnumValue ) const
+ {
+ OUString sDescription;
+
+ sal_Int32 nAsInt = 0;
+ OSL_VERIFY( ::cppu::enum2int( nAsInt, _rEnumValue ) );
+
+ Sequence< sal_Int32 > aValues;
+ impl_getValues( aValues );
+
+ sal_Int32 index = std::find( std::cbegin(aValues), std::cend(aValues), nAsInt ) - std::cbegin(aValues);
+
+ std::vector< OUString > aDescriptions( getDescriptions() );
+ if ( ( index >= 0 ) && ( o3tl::make_unsigned(index) < aDescriptions.size() ) )
+ sDescription = aDescriptions[ index ];
+ else
+ {
+ OSL_FAIL( "EnumRepresentation::getDescriptionForValue: cannot convert!" );
+ }
+ return sDescription;
+ }
+
+ typedef ::cppu::WeakImplHelper < XActionListener
+ > UrlClickHandler_Base;
+
+ namespace {
+
+ class UrlClickHandler : public UrlClickHandler_Base
+ {
+ Reference<XComponentContext> m_xContext;
+ public:
+ UrlClickHandler( const Reference<XComponentContext>& _rContext, const Reference< XHyperlinkControl >& _rxControl );
+
+ protected:
+ virtual ~UrlClickHandler() override;
+
+ // XActionListener
+ virtual void SAL_CALL actionPerformed( const ActionEvent& rEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+
+ protected:
+ void impl_dispatch_throw( const OUString& _rURL );
+ };
+
+ }
+
+ UrlClickHandler::UrlClickHandler( const Reference<XComponentContext>& _rContext, const Reference< XHyperlinkControl >& _rxControl )
+ :m_xContext( _rContext )
+ {
+ if ( !_rxControl.is() )
+ throw NullPointerException();
+
+ osl_atomic_increment( &m_refCount );
+ {
+ _rxControl->addActionListener( this );
+ }
+ osl_atomic_decrement( &m_refCount );
+ OSL_ENSURE( m_refCount > 0, "UrlClickHandler::UrlClickHandler: leaking!" );
+
+ }
+
+ UrlClickHandler::~UrlClickHandler()
+ {
+ }
+
+ void SAL_CALL UrlClickHandler::actionPerformed( const ActionEvent& rEvent )
+ {
+ Reference< XPropertyControl > xControl( rEvent.Source, UNO_QUERY_THROW );
+ Any aControlValue( xControl->getValue() );
+
+ OUString sURL;
+ if ( aControlValue.hasValue() && !( aControlValue >>= sURL ) )
+ throw RuntimeException( OUString(), *this );
+
+ if ( sURL.isEmpty() )
+ return;
+
+ impl_dispatch_throw( sURL );
+ }
+
+ void SAL_CALL UrlClickHandler::disposing( const EventObject& /*Source*/ )
+ {
+ // not interested in
+ }
+
+ void UrlClickHandler::impl_dispatch_throw( const OUString& _rURL )
+ {
+ Reference< XURLTransformer > xTransformer( URLTransformer::create(m_xContext) );
+ URL aURL; aURL.Complete = ".uno:OpenHyperlink";
+ xTransformer->parseStrict( aURL );
+
+ Reference< XDesktop2 > xDispProv = Desktop::create( m_xContext );
+ Reference< XDispatch > xDispatch( xDispProv->queryDispatch( aURL, OUString(), 0 ), UNO_SET_THROW );
+
+ Sequence aDispatchArgs{ comphelper::makePropertyValue("URL", _rURL) };
+
+ xDispatch->dispatch( aURL, aDispatchArgs );
+ }
+
+
+ GenericPropertyHandler::GenericPropertyHandler( const Reference< XComponentContext >& _rxContext )
+ :GenericPropertyHandler_Base( m_aMutex )
+ ,m_xContext( _rxContext )
+ ,m_aPropertyListeners( m_aMutex )
+ ,m_bPropertyMapInitialized( false )
+ {
+ m_xTypeConverter = Converter::create(_rxContext);
+ }
+
+ GenericPropertyHandler::~GenericPropertyHandler()
+ {
+ }
+
+ OUString SAL_CALL GenericPropertyHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.GenericPropertyHandler";
+ }
+
+ sal_Bool SAL_CALL GenericPropertyHandler::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL GenericPropertyHandler::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.inspection.GenericPropertyHandler" };
+ }
+
+ void SAL_CALL GenericPropertyHandler::inspect( const Reference< XInterface >& _rxIntrospectee )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !_rxIntrospectee.is() )
+ throw NullPointerException();
+
+ // revoke old property change listeners
+ ::comphelper::OInterfaceIteratorHelper2 iterRemove( m_aPropertyListeners );
+ ::comphelper::OInterfaceIteratorHelper2 iterReAdd( m_aPropertyListeners ); // this holds a copy of the container ...
+ while ( iterRemove.hasMoreElements() )
+ m_xComponent->removePropertyChangeListener( OUString(), static_cast< XPropertyChangeListener* >( iterRemove.next() ) );
+
+ m_xComponentIntrospectionAccess.clear();
+ m_xComponent.clear();
+ m_xPropertyState.clear();
+
+ // create an introspection adapter for the component
+ Reference< XIntrospection > xIntrospection = theIntrospection::get( m_xContext );
+
+ Reference< XIntrospectionAccess > xIntrospectionAccess( xIntrospection->inspect( Any( _rxIntrospectee ) ) );
+ if ( !xIntrospectionAccess.is() )
+ throw RuntimeException("The introspection service could not handle the given component.", *this );
+
+ m_xComponent.set( xIntrospectionAccess->queryAdapter( cppu::UnoType<XPropertySet>::get() ), UNO_QUERY_THROW );
+ // now that we survived so far, remember m_xComponentIntrospectionAccess
+ m_xComponentIntrospectionAccess = xIntrospectionAccess;
+ m_xPropertyState.set(m_xComponent, css::uno::UNO_QUERY);
+
+ m_bPropertyMapInitialized = false;
+ m_aProperties.clear();
+
+ // re-add the property change listeners
+ while ( iterReAdd.hasMoreElements() )
+ m_xComponent->addPropertyChangeListener( OUString(), static_cast< XPropertyChangeListener* >( iterReAdd.next() ) );
+ }
+
+ Any SAL_CALL GenericPropertyHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_xComponent.is() )
+ throw UnknownPropertyException(_rPropertyName);
+
+ return m_xComponent->getPropertyValue( _rPropertyName );
+ }
+
+ void SAL_CALL GenericPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_xComponent.is() )
+ throw UnknownPropertyException(_rPropertyName);
+
+ m_xComponent->setPropertyValue( _rPropertyName, _rValue );
+ }
+
+ ::rtl::Reference< IPropertyEnumRepresentation > GenericPropertyHandler::impl_getEnumConverter( const Type& _rEnumType )
+ {
+ ::rtl::Reference< IPropertyEnumRepresentation >& rConverter = m_aEnumConverters[ _rEnumType ];
+ if ( !rConverter.is() )
+ rConverter = new EnumRepresentation( m_xContext, _rEnumType );
+ return rConverter;
+ }
+
+ Any SAL_CALL GenericPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_ensurePropertyMap();
+
+ PropertyMap::const_iterator pos = m_aProperties.find( _rPropertyName );
+ if ( pos == m_aProperties.end() )
+ throw UnknownPropertyException(_rPropertyName);
+
+ Any aPropertyValue;
+ if ( !_rControlValue.hasValue() )
+ // NULL is converted to NULL
+ return aPropertyValue;
+
+ if ( pos->second.Type.getTypeClass() == TypeClass_ENUM )
+ {
+ OUString sControlValue;
+ OSL_VERIFY( _rControlValue >>= sControlValue );
+ impl_getEnumConverter( pos->second.Type )->getValueFromDescription( sControlValue, aPropertyValue );
+ }
+ else
+ aPropertyValue = PropertyHandlerHelper::convertToPropertyValue( m_xContext, m_xTypeConverter, pos->second, _rControlValue );
+
+ return aPropertyValue;
+ }
+
+ Any SAL_CALL GenericPropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_ensurePropertyMap();
+
+ PropertyMap::const_iterator pos = m_aProperties.find( _rPropertyName );
+ if ( pos == m_aProperties.end() )
+ throw UnknownPropertyException(_rPropertyName);
+
+ Any aControlValue;
+ if ( !_rPropertyValue.hasValue() )
+ // NULL is converted to NULL
+ return aControlValue;
+
+ if ( pos->second.Type.getTypeClass() == TypeClass_ENUM )
+ {
+ aControlValue <<= impl_getEnumConverter( pos->second.Type )->getDescriptionForValue( _rPropertyValue );
+ }
+ else
+ aControlValue = PropertyHandlerHelper::convertToControlValue( m_xContext, m_xTypeConverter, _rPropertyValue, _rControlValueType );
+ return aControlValue;
+ }
+
+ PropertyState SAL_CALL GenericPropertyHandler::getPropertyState( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyState eState = PropertyState_DIRECT_VALUE;
+ if ( m_xPropertyState.is() )
+ eState = m_xPropertyState->getPropertyState( _rPropertyName );
+ return eState;
+ }
+
+ void SAL_CALL GenericPropertyHandler::addPropertyChangeListener(const Reference< XPropertyChangeListener >& _rxListener)
+ {
+ if ( !_rxListener.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_aPropertyListeners.addInterface( _rxListener );
+ if ( m_xComponent.is() )
+ {
+ try
+ {
+ m_xComponent->addPropertyChangeListener( OUString(), _rxListener );
+ }
+ catch( const UnknownPropertyException& )
+ {
+ OSL_FAIL( "GenericPropertyHandler::addPropertyChangeListener:\nThe inspected component does not allow registering for all properties at once! This violates the interface contract!" );
+ }
+ }
+ }
+
+ void SAL_CALL GenericPropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_xComponent.is() )
+ {
+ try
+ {
+ m_xComponent->removePropertyChangeListener( OUString(), _rxListener );
+ }
+ catch( const UnknownPropertyException& )
+ {
+ OSL_FAIL( "GenericPropertyHandler::removePropertyChangeListener:\nThe inspected component does not allow de-registering for all properties at once! This violates the interface contract!" );
+ }
+ }
+ m_aPropertyListeners.removeInterface( _rxListener );
+ }
+
+ void GenericPropertyHandler::impl_ensurePropertyMap()
+ {
+ if ( m_bPropertyMapInitialized )
+ return;
+
+ m_bPropertyMapInitialized = true;
+ try
+ {
+ Reference< XPropertySetInfo > xPSI;
+ if ( m_xComponent.is() )
+ xPSI = m_xComponent->getPropertySetInfo();
+ Sequence< Property > aProperties;
+ if ( xPSI.is() )
+ aProperties = xPSI->getProperties();
+ DBG_ASSERT( aProperties.hasElements(), "GenericPropertyHandler::getSupportedProperties: no properties!" );
+
+ for ( auto const & property : std::as_const(aProperties) )
+ {
+ switch ( property.Type.getTypeClass() )
+ {
+ case TypeClass_BOOLEAN:
+ case TypeClass_BYTE:
+ case TypeClass_SHORT:
+ case TypeClass_UNSIGNED_SHORT:
+ case TypeClass_LONG:
+ case TypeClass_UNSIGNED_LONG:
+ case TypeClass_HYPER:
+ case TypeClass_UNSIGNED_HYPER:
+ case TypeClass_FLOAT:
+ case TypeClass_DOUBLE:
+ case TypeClass_ENUM:
+ case TypeClass_STRING:
+ // allowed, we can handle this type
+ break;
+
+ case TypeClass_SEQUENCE:
+ {
+ TypeClass eElementTypeClass = ::comphelper::getSequenceElementType( property.Type ).getTypeClass();
+ if ( ( eElementTypeClass != TypeClass_STRING )
+ && ( eElementTypeClass != TypeClass_BYTE )
+ && ( eElementTypeClass != TypeClass_SHORT )
+ && ( eElementTypeClass != TypeClass_UNSIGNED_SHORT )
+ && ( eElementTypeClass != TypeClass_LONG )
+ && ( eElementTypeClass != TypeClass_UNSIGNED_LONG )
+ )
+ // can only handle the above
+ continue;
+ }
+ break;
+
+ default:
+ // next property, we don't support this type
+ continue;
+ }
+
+ m_aProperties.emplace( property.Name, property );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "GenericPropertyHandler::impl_ensurePropertyMap" );
+ }
+ }
+
+ Sequence< Property > SAL_CALL GenericPropertyHandler::getSupportedProperties()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_ensurePropertyMap();
+
+ return comphelper::mapValuesToSequence( m_aProperties );
+ }
+
+ Sequence< OUString > SAL_CALL GenericPropertyHandler::getSupersededProperties( )
+ {
+ // no superseded properties at all. This handler offers the very basic PropertyHandler
+ // functionality, so it's much more likely that other handlers want to supersede
+ // *our* properties...
+ return Sequence< OUString >( );
+ }
+
+ Sequence< OUString > SAL_CALL GenericPropertyHandler::getActuatingProperties( )
+ {
+ // This basic PropertyHandler implementation is too dumb^Wgeneric to know
+ // anything about property dependencies
+ return Sequence< OUString >( );
+ }
+
+ LineDescriptor SAL_CALL GenericPropertyHandler::describePropertyLine( const OUString& _rPropertyName,
+ const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ if ( !_rxControlFactory.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_ensurePropertyMap();
+
+ PropertyMap::const_iterator pos = m_aProperties.find( _rPropertyName );
+ if ( pos == m_aProperties.end() )
+ throw UnknownPropertyException(_rPropertyName);
+
+ LineDescriptor aDescriptor;
+ aDescriptor.DisplayName = _rPropertyName;
+ switch ( pos->second.Type.getTypeClass() )
+ {
+ case TypeClass_ENUM:
+ aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory,
+ impl_getEnumConverter( pos->second.Type )->getDescriptions(),
+ PropertyHandlerHelper::requiresReadOnlyControl( pos->second.Attributes ),
+ false );
+ break;
+ case TypeClass_STRING:
+ {
+ // some special handling for URL properties
+ bool bIsURLProperty = _rPropertyName.endsWith( "URL" );
+ if ( bIsURLProperty )
+ {
+ aDescriptor.Control = _rxControlFactory->createPropertyControl(
+ PropertyControlType::HyperlinkField, PropertyHandlerHelper::requiresReadOnlyControl( pos->second.Attributes ) );
+
+ Reference< XHyperlinkControl > xControl( aDescriptor.Control, UNO_QUERY_THROW );
+ new UrlClickHandler( m_xContext, xControl );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ // fallback
+ if ( !aDescriptor.Control.is() )
+ PropertyHandlerHelper::describePropertyLine( pos->second, aDescriptor, _rxControlFactory );
+
+ aDescriptor.Category = "General";
+ return aDescriptor;
+ }
+
+ sal_Bool SAL_CALL GenericPropertyHandler::isComposable( const OUString& /*_rPropertyName*/ )
+ {
+ return false;
+ }
+
+ InteractiveSelectionResult SAL_CALL GenericPropertyHandler::onInteractivePropertySelection( const OUString& /*_rPropertyName*/, sal_Bool /*_bPrimary*/, Any& /*_rData*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/ )
+ {
+ OSL_FAIL( "GenericPropertyHandler::onInteractivePropertySelection: I'm too dumb to know anything about property browse buttons!" );
+ return InteractiveSelectionResult_Cancelled;
+ }
+
+ void SAL_CALL GenericPropertyHandler::actuatingPropertyChanged( const OUString& /*_rActuatingPropertyName*/, const Any& /*_rNewValue*/, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/, sal_Bool /*_bFirstTimeInit*/ )
+ {
+ OSL_FAIL( "GenericPropertyHandler::actuatingPropertyChanged: no no no, I did not register for any actuating properties!" );
+ }
+
+ sal_Bool SAL_CALL GenericPropertyHandler::suspend( sal_Bool /*_bSuspend*/ )
+ {
+ return true;
+ }
+
+ void SAL_CALL GenericPropertyHandler::disposing()
+ {
+ m_aPropertyListeners.clear();
+ // not disposeAndClear: the listeners are (virtually) listeners at our introspectee, not
+ // at this handler instance
+ }
+
+ IMPLEMENT_FORWARD_XCOMPONENT( GenericPropertyHandler, GenericPropertyHandler_Base );
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_GenericPropertyHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::GenericPropertyHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/genericpropertyhandler.hxx b/extensions/source/propctrlr/genericpropertyhandler.hxx
new file mode 100644
index 000000000..b2114be3c
--- /dev/null
+++ b/extensions/source/propctrlr/genericpropertyhandler.hxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "pcrcommontypes.hxx"
+#include "pcrcommon.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+#include <com/sun/star/inspection/XPropertyHandler.hpp>
+#include <com/sun/star/script/XTypeConverter.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/interfacecontainer2.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <map>
+
+
+namespace pcr
+{
+
+
+ struct TypeLess
+ {
+ bool operator()( const css::uno::Type& _rLHS, const css::uno::Type& _rRHS ) const
+ {
+ return _rLHS.getTypeName() < _rRHS.getTypeName();
+ }
+ };
+
+ class IPropertyEnumRepresentation;
+
+ //= GenericPropertyHandler
+
+ typedef ::cppu::WeakComponentImplHelper < css::inspection::XPropertyHandler
+ , css::lang::XServiceInfo
+ > GenericPropertyHandler_Base;
+ class GenericPropertyHandler final : public GenericPropertyHandler_Base
+ {
+ mutable ::osl::Mutex m_aMutex;
+
+ /// the service factory for creating services
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ /// need this to keep alive as long as m_xComponent lives
+ css::uno::Reference< css::beans::XIntrospectionAccess > m_xComponentIntrospectionAccess;
+ /// the properties of the object we're handling
+ css::uno::Reference< css::beans::XPropertySet > m_xComponent;
+ /// cached interface of ->m_xComponent
+ css::uno::Reference< css::beans::XPropertyState > m_xPropertyState;
+ /// type converter, needed on various occasions
+ css::uno::Reference< css::script::XTypeConverter > m_xTypeConverter;
+ /// cache of our supported properties
+ PropertyMap m_aProperties;
+ /// property change listeners
+ ::comphelper::OInterfaceContainerHelper2 m_aPropertyListeners;
+ std::map< css::uno::Type, ::rtl::Reference< IPropertyEnumRepresentation >, TypeLess >
+ m_aEnumConverters;
+
+ /// has ->m_aProperties been initialized?
+ bool m_bPropertyMapInitialized : 1;
+
+ public:
+ explicit GenericPropertyHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ virtual ~GenericPropertyHandler() override;
+
+ private:
+ // XPropertyHandler overridables
+ virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override;
+ virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override;
+ virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual css::uno::Sequence< css::beans::Property >
+ SAL_CALL getSupportedProperties() override;
+ virtual css::uno::Sequence< OUString >
+ SAL_CALL getSupersededProperties() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties() override;
+ virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+ virtual sal_Bool SAL_CALL isComposable( const OUString& _rPropertyName ) override;
+ virtual css::inspection::InteractiveSelectionResult
+ SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override;
+ virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override;
+
+ // XComponent
+ DECLARE_XCOMPONENT()
+ virtual void SAL_CALL disposing() 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;
+
+ /** ensures that ->m_aProperties is initialized
+ @precond
+ our mutex is locked
+ */
+ void impl_ensurePropertyMap();
+
+ /** retrieves the enum converter for the given ENUM type
+ */
+ ::rtl::Reference< IPropertyEnumRepresentation >
+ impl_getEnumConverter( const css::uno::Type& _rEnumType );
+
+ GenericPropertyHandler( const GenericPropertyHandler& ) = delete;
+ GenericPropertyHandler& operator=( const GenericPropertyHandler& ) = delete;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/handlerhelper.cxx b/extensions/source/propctrlr/handlerhelper.cxx
new file mode 100644
index 000000000..89f149b92
--- /dev/null
+++ b/extensions/source/propctrlr/handlerhelper.cxx
@@ -0,0 +1,314 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "handlerhelper.hxx"
+#include <yesno.hrc>
+#include "modulepcr.hxx"
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/inspection/StringRepresentation.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/inspection/LineDescriptor.hpp>
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <com/sun/star/inspection/XStringListControl.hpp>
+#include <com/sun/star/inspection/XNumericControl.hpp>
+#include <tools/diagnose_ex.h>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+
+#include <algorithm>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::inspection;
+
+
+ //= PropertyHandlerHelper
+
+
+ void PropertyHandlerHelper::describePropertyLine( const Property& _rProperty,
+ LineDescriptor& /* [out] */ _out_rDescriptor, const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ // display the pure property name - no L10N
+ _out_rDescriptor.DisplayName = _rProperty.Name;
+
+ OSL_PRECOND( _rxControlFactory.is(), "PropertyHandlerHelper::describePropertyLine: no factory -> no control!" );
+ if ( !_rxControlFactory.is() )
+ return;
+
+ bool bReadOnlyControl = requiresReadOnlyControl( _rProperty.Attributes );
+
+ // special handling for booleans (this will become a list)
+ if ( _rProperty.Type.getTypeClass() == TypeClass_BOOLEAN )
+ {
+ _out_rDescriptor.Control = createListBoxControl(_rxControlFactory, RID_RSC_ENUM_YESNO, SAL_N_ELEMENTS(RID_RSC_ENUM_YESNO), bReadOnlyControl);
+ return;
+ }
+
+ sal_Int16 nControlType = PropertyControlType::TextField;
+ switch ( _rProperty.Type.getTypeClass() )
+ {
+ case TypeClass_BYTE:
+ case TypeClass_SHORT:
+ case TypeClass_UNSIGNED_SHORT:
+ case TypeClass_LONG:
+ case TypeClass_UNSIGNED_LONG:
+ case TypeClass_HYPER:
+ case TypeClass_UNSIGNED_HYPER:
+ case TypeClass_FLOAT:
+ case TypeClass_DOUBLE:
+ nControlType = PropertyControlType::NumericField;
+ break;
+
+ case TypeClass_SEQUENCE:
+ nControlType = PropertyControlType::StringListField;
+ break;
+
+ default:
+ OSL_FAIL( "PropertyHandlerHelper::describePropertyLine: don't know how to represent this at the UI!" );
+ [[fallthrough]];
+
+ case TypeClass_STRING:
+ nControlType = PropertyControlType::TextField;
+ break;
+ }
+
+ // create a control
+ _out_rDescriptor.Control = _rxControlFactory->createPropertyControl( nControlType, bReadOnlyControl );
+ }
+
+
+ namespace
+ {
+ Reference< XPropertyControl > lcl_implCreateListLikeControl(
+ const Reference< XPropertyControlFactory >& _rxControlFactory,
+ std::vector< OUString >&& _rInitialListEntries,
+ bool _bReadOnlyControl,
+ bool _bSorted,
+ bool _bTrueIfListBoxFalseIfComboBox
+ )
+ {
+ Reference< XStringListControl > xListControl(
+ _rxControlFactory->createPropertyControl(
+ _bTrueIfListBoxFalseIfComboBox ? PropertyControlType::ListBox : PropertyControlType::ComboBox, _bReadOnlyControl
+ ),
+ UNO_QUERY_THROW
+ );
+
+ if ( _bSorted )
+ std::sort( _rInitialListEntries.begin(), _rInitialListEntries.end() );
+
+ for (auto const& initialEntry : _rInitialListEntries)
+ xListControl->appendListEntry(initialEntry);
+ return xListControl;
+ }
+ }
+
+ Reference< XPropertyControl > PropertyHandlerHelper::createListBoxControl( const Reference< XPropertyControlFactory >& _rxControlFactory,
+ std::vector< OUString >&& _rInitialListEntries, bool _bReadOnlyControl, bool _bSorted )
+ {
+ return lcl_implCreateListLikeControl(_rxControlFactory, std::move(_rInitialListEntries), _bReadOnlyControl, _bSorted, true);
+ }
+
+ Reference< XPropertyControl > PropertyHandlerHelper::createListBoxControl( const Reference< XPropertyControlFactory >& _rxControlFactory,
+ const TranslateId* pTransIds, size_t nElements, bool _bReadOnlyControl )
+ {
+ std::vector<OUString> aInitialListEntries;
+ for (size_t i = 0; i < nElements; ++i)
+ aInitialListEntries.push_back(PcrRes(pTransIds[i]));
+ return lcl_implCreateListLikeControl(_rxControlFactory, std::move(aInitialListEntries), _bReadOnlyControl, /*_bSorted*/false, true);
+ }
+
+ Reference< XPropertyControl > PropertyHandlerHelper::createComboBoxControl( const Reference< XPropertyControlFactory >& _rxControlFactory,
+ std::vector< OUString >&& _rInitialListEntries, bool _bSorted )
+ {
+ return lcl_implCreateListLikeControl( _rxControlFactory, std::move(_rInitialListEntries), /*_bReadOnlyControl*/false, _bSorted, false );
+ }
+
+
+ Reference< XPropertyControl > PropertyHandlerHelper::createNumericControl( const Reference< XPropertyControlFactory >& _rxControlFactory,
+ sal_Int16 _nDigits, const Optional< double >& _rMinValue, const Optional< double >& _rMaxValue )
+ {
+ Reference< XNumericControl > xNumericControl(
+ _rxControlFactory->createPropertyControl( PropertyControlType::NumericField, /*_bReadOnlyControl*/false ),
+ UNO_QUERY_THROW
+ );
+
+ xNumericControl->setDecimalDigits( _nDigits );
+ xNumericControl->setMinValue( _rMinValue );
+ xNumericControl->setMaxValue( _rMaxValue );
+
+ return xNumericControl;
+ }
+
+
+ Any PropertyHandlerHelper::convertToPropertyValue( const Reference< XComponentContext >& _rxContext,const Reference< XTypeConverter >& _rxTypeConverter,
+ const Property& _rProperty, const Any& _rControlValue )
+ {
+ Any aPropertyValue( _rControlValue );
+ if ( !aPropertyValue.hasValue() )
+ // NULL is converted to NULL
+ return aPropertyValue;
+
+ if ( aPropertyValue.getValueType().equals( _rProperty.Type ) )
+ // nothing to do, type is already as desired
+ return aPropertyValue;
+
+ if ( _rControlValue.getValueType().getTypeClass() == TypeClass_STRING )
+ {
+ OUString sControlValue;
+ _rControlValue >>= sControlValue;
+
+ Reference< XStringRepresentation > xConversionHelper = StringRepresentation::create( _rxContext,_rxTypeConverter );
+ aPropertyValue = xConversionHelper->convertToPropertyValue( sControlValue, _rProperty.Type );
+ }
+ else
+ {
+ try
+ {
+ if ( _rxTypeConverter.is() )
+ aPropertyValue = _rxTypeConverter->convertTo( _rControlValue, _rProperty.Type );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("extensions.propctrlr",
+ "caught an exception while converting via TypeConverter!");
+ }
+ }
+
+ return aPropertyValue;
+ }
+
+
+ Any PropertyHandlerHelper::convertToControlValue( const Reference< XComponentContext >& _rxContext,const Reference< XTypeConverter >& _rxTypeConverter,
+ const Any& _rPropertyValue, const Type& _rControlValueType )
+ {
+ Any aControlValue( _rPropertyValue );
+ if ( !aControlValue.hasValue() )
+ // NULL is converted to NULL
+ return aControlValue;
+
+ if ( _rControlValueType.getTypeClass() == TypeClass_STRING )
+ {
+ Reference< XStringRepresentation > xConversionHelper = StringRepresentation::create( _rxContext,_rxTypeConverter );
+ aControlValue <<= xConversionHelper->convertToControlValue( _rPropertyValue );
+ }
+ else
+ {
+ try
+ {
+ if ( _rxTypeConverter.is() )
+ aControlValue = _rxTypeConverter->convertTo( _rPropertyValue, _rControlValueType );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("extensions.propctrlr",
+ "caught an exception while converting via TypeConverter!");
+ }
+ }
+
+ return aControlValue;
+ }
+
+
+ void PropertyHandlerHelper::setContextDocumentModified( const Reference<XComponentContext> & rContext )
+ {
+ try
+ {
+ Reference< XModifiable > xDocumentModifiable( getContextDocument_throw(rContext), UNO_QUERY_THROW );
+ xDocumentModifiable->setModified( true );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ Reference< XInterface > PropertyHandlerHelper::getContextDocument( const Reference<XComponentContext> & rContext )
+ {
+ Reference< XInterface > xI;
+ try
+ {
+ xI = getContextDocument_throw( rContext );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PropertyHandler::getContextValueByName" );
+ }
+ return xI;
+ }
+
+ Reference< XInterface > PropertyHandlerHelper::getContextDocument_throw( const Reference<XComponentContext> & rContext )
+ {
+ Reference< XInterface > xI;
+ Any aReturn = rContext->getValueByName( "ContextDocument" );
+ aReturn >>= xI;
+ return xI;
+ }
+
+ weld::Window* PropertyHandlerHelper::getDialogParentFrame(const Reference<XComponentContext>& rContext)
+ {
+ weld::Window* pInspectorWindow = nullptr;
+ try
+ {
+ Reference< XWindow > xInspectorWindow(rContext->getValueByName( "DialogParentWindow" ), UNO_QUERY_THROW);
+ pInspectorWindow = Application::GetFrameWeld(xInspectorWindow);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return pInspectorWindow;
+ }
+
+ std::unique_ptr<weld::Builder> PropertyHandlerHelper::makeBuilder(const OUString& rUIFile, const Reference<XComponentContext>& rContext)
+ {
+ Reference<XWindow> xWindow(rContext->getValueByName("BuilderParent"), UNO_QUERY_THROW);
+ weld::TransportAsXWindow& rTunnel = dynamic_cast<weld::TransportAsXWindow&>(*xWindow);
+ return Application::CreateBuilder(rTunnel.getWidget(), rUIFile);
+ }
+
+ void PropertyHandlerHelper::setBuilderParent(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Widget* pParent)
+ {
+ Reference<css::container::XNameContainer> xName(rContext, UNO_QUERY_THROW);
+ Reference<XWindow> xWindow(new weld::TransportAsXWindow(pParent));
+ xName->insertByName("BuilderParent", Any(xWindow));
+ }
+
+ void PropertyHandlerHelper::clearBuilderParent(const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ {
+ Reference<css::container::XNameContainer> xName(rContext, UNO_QUERY_THROW);
+ xName->removeByName("BuilderParent");
+ }
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/handlerhelper.hxx b/extensions/source/propctrlr/handlerhelper.hxx
new file mode 100644
index 000000000..555b8ee82
--- /dev/null
+++ b/extensions/source/propctrlr/handlerhelper.hxx
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/script/XTypeConverter.hpp>
+#include <com/sun/star/inspection/XPropertyControlFactory.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/Optional.hpp>
+#include <unotools/resmgr.hxx>
+
+#include <memory>
+#include <vector>
+
+namespace weld { class Builder; class Widget; class Window; }
+namespace com::sun::star {
+ namespace inspection {
+ struct LineDescriptor;
+ }
+}
+
+namespace pcr
+{
+
+
+ //= PropertyHandlerHelper
+
+ class PropertyHandlerHelper
+ {
+ public:
+ /** helper for implementing XPropertyHandler::describePropertyLine in a generic way
+ */
+ static void describePropertyLine(
+ const css::beans::Property& _rProperty,
+ css::inspection::LineDescriptor& /* [out] */ _out_rDescriptor,
+ const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory
+ );
+
+ /** helper for implementing XPropertyHandler::convertToPropertyValue
+ */
+ static css::uno::Any convertToPropertyValue(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ const css::uno::Reference< css::script::XTypeConverter >& _rxTypeConverter,
+ const css::beans::Property& _rProperty,
+ const css::uno::Any& _rControlValue
+ );
+
+ /// helper for implementing XPropertyHandler::convertToControlValue
+ static css::uno::Any convertToControlValue(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ const css::uno::Reference< css::script::XTypeConverter >& _rxTypeConverter,
+ const css::uno::Any& _rPropertyValue,
+ const css::uno::Type& _rControlValueType
+ );
+
+ /** creates an <member scope="css::inspection">PropertyControlType::ListBox</member>-type control
+ and fills it with initial values
+
+ @param _rxControlFactory
+ A control factory. Must not be <NULL/>.
+
+ @param _rInitialListEntries
+ the initial values of the control
+
+ @param _bReadOnlyControl
+ determines whether the control should be read-only
+
+ @param _bSorted
+ determines whether the list entries should be sorted
+
+ @return
+ the newly created control
+ */
+ static css::uno::Reference< css::inspection::XPropertyControl >
+ createListBoxControl(
+ const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory,
+ std::vector< OUString >&& _rInitialListEntries,
+ bool _bReadOnlyControl,
+ bool _bSorted
+ );
+
+ /** creates an <member scope="css::inspection">PropertyControlType::ListBox</member>-type control
+ and fills it with initial values.
+
+ @param _rxControlFactory
+ A control factory. Must not be <NULL/>.
+
+ @param pTransIds
+ the initial translation ids for the value of the control
+
+ @param nElements
+ the count of initial values of the control
+
+ @param _bReadOnlyControl
+ determines whether the control should be read-only
+
+ @return
+ the newly created control
+ */
+ static css::uno::Reference< css::inspection::XPropertyControl >
+ createListBoxControl(
+ const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory,
+ const TranslateId* pTransIds, size_t nElements,
+ bool _bReadOnlyControl
+ );
+
+ /** creates an <member scope="css::inspection">PropertyControlType::ComboBox</member>-type control
+ and fills it with initial values
+
+ @param _rxControlFactory
+ A control factory. Must not be <NULL/>.
+
+ @param _rInitialListEntries
+ the initial values of the control
+
+ @param _bSorted
+ determines whether the list entries should be sorted
+
+ @return
+ the newly created control
+ */
+ static css::uno::Reference< css::inspection::XPropertyControl >
+ createComboBoxControl(
+ const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory,
+ std::vector< OUString >&& _rInitialListEntries,
+ bool _bSorted
+ );
+
+ /** creates an <member scope="css::inspection">PropertyControlType::NumericField</member>-type control
+ and initializes it
+
+ @param _rxControlFactory
+ A control factory. Must not be <NULL/>.
+ @param _nDigits
+ number of decimal digits for the control
+ (<member scope="css::inspection">XNumericControl::DecimalDigits</member>)
+ @param _rMinValue
+ minimum value which can be entered in the control
+ (<member scope="css::inspection">XNumericControl::MinValue</member>)
+ @param _rMaxValue
+ maximum value which can be entered in the control
+ (<member scope="css::inspection">XNumericControl::MaxValue</member>)
+
+ @return
+ the newly created control
+ */
+ static css::uno::Reference< css::inspection::XPropertyControl >
+ createNumericControl(
+ const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory,
+ sal_Int16 _nDigits,
+ const css::beans::Optional< double >& _rMinValue,
+ const css::beans::Optional< double >& _rMaxValue
+ );
+
+ /** marks the document passed in our UNO context as modified
+
+ The method looks up a value called "ContextDocument" in the given UNO component context,
+ queries it for the ->css::util::XModifiable interface, and calls its
+ setModified method. If either of those steps fails, this is asserted in a non-product
+ version, and silently ignore otherwise.
+
+ @param _rContext
+ the component context which was used to create the component calling this method
+ */
+ static void setContextDocumentModified(
+ const css::uno::Reference< css::uno::XComponentContext > & _rContext
+ );
+
+ static css::uno::Reference< css::uno::XInterface > getContextDocument( const css::uno::Reference<css::uno::XComponentContext> & _rContext );
+
+ /// @throws css::uno::RuntimeException
+ static css::uno::Reference< css::uno::XInterface > getContextDocument_throw( const css::uno::Reference<css::uno::XComponentContext> & _rContext );
+
+ /** gets the window of the ObjectInspector in which a property handler lives
+
+ The method looks up a value called "DialogParentWindow" in the given UNO component context,
+ queries it for XWindow, and returns the respective weld::Window*. If either of those steps fails,
+ this is asserted in a non-product version, and silently ignore otherwise.
+
+ @param _rContext
+ the component context which was used to create the component calling this method
+ */
+ static weld::Window* getDialogParentFrame( const css::uno::Reference< css::uno::XComponentContext > & _rContext );
+
+
+ /** determines whether given PropertyAttributes require a to-be-created
+ <type scope="css::inspection">XPropertyControl</type> to be read-only
+
+ @param _nPropertyAttributes
+ the attributes of the property which should be reflected by a to-be-created
+ <type scope="css::inspection">XPropertyControl</type>
+ */
+ static bool requiresReadOnlyControl( sal_Int16 _nPropertyAttributes )
+ {
+ return ( _nPropertyAttributes & css::beans::PropertyAttribute::READONLY ) != 0;
+ }
+
+ static std::unique_ptr<weld::Builder> makeBuilder(const OUString& rUIFile, const css::uno::Reference<css::uno::XComponentContext>& rContext);
+
+ static void setBuilderParent(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Widget* pParent);
+
+ static void clearBuilderParent(const css::uno::Reference<css::uno::XComponentContext>& rContext);
+
+ private:
+ PropertyHandlerHelper( const PropertyHandlerHelper& ) = delete;
+ PropertyHandlerHelper& operator=( const PropertyHandlerHelper& ) = delete;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/inspectorhelpwindow.cxx b/extensions/source/propctrlr/inspectorhelpwindow.cxx
new file mode 100644
index 000000000..a57c4ffed
--- /dev/null
+++ b/extensions/source/propctrlr/inspectorhelpwindow.cxx
@@ -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 .
+ */
+#include "inspectorhelpwindow.hxx"
+
+
+namespace pcr
+{
+ //= InspectorHelpWindow
+ InspectorHelpWindow::InspectorHelpWindow(weld::Builder& rBuilder)
+ : m_xHelpFrame(rBuilder.weld_widget("helpframe"))
+ , m_xHelpText(rBuilder.weld_text_view("helptext"))
+ {
+ }
+
+ InspectorHelpWindow::~InspectorHelpWindow()
+ {
+ }
+
+ void InspectorHelpWindow::SetText(const OUString& rStr)
+ {
+ m_xHelpText->set_text(rStr);
+ }
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/inspectorhelpwindow.hxx b/extensions/source/propctrlr/inspectorhelpwindow.hxx
new file mode 100644
index 000000000..98b8efbab
--- /dev/null
+++ b/extensions/source/propctrlr/inspectorhelpwindow.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 <vcl/weld.hxx>
+
+namespace pcr
+{
+ //= InspectorHelpWindow
+ class InspectorHelpWindow
+ {
+ private:
+ std::unique_ptr<weld::Widget> m_xHelpFrame;
+ std::unique_ptr<weld::TextView> m_xHelpText;
+
+ public:
+ explicit InspectorHelpWindow(weld::Builder& rBuilder);
+ ~InspectorHelpWindow();
+
+ void SetText(const OUString& rStr);
+
+ void Show(bool bShow) { m_xHelpFrame->set_visible(bShow); }
+ bool IsVisible() const { return m_xHelpFrame->get_visible(); }
+ };
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/inspectormodelbase.cxx b/extensions/source/propctrlr/inspectormodelbase.cxx
new file mode 100644
index 000000000..f8780ff99
--- /dev/null
+++ b/extensions/source/propctrlr/inspectormodelbase.cxx
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "inspectormodelbase.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <comphelper/propertycontainerhelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+
+namespace pcr
+{
+
+
+#define MODEL_PROPERTY_ID_HAS_HELP_SECTION 2000
+#define MODEL_PROPERTY_ID_MIN_HELP_TEXT_LINES 2001
+#define MODEL_PROPERTY_ID_MAX_HELP_TEXT_LINES 2002
+#define MODEL_PROPERTY_ID_IS_READ_ONLY 2003
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::beans::Property;
+
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+
+
+ //= InspectorModelProperties
+
+ /** helper class for implementing the property set related functionality
+ of an ImplInspectorModel
+ */
+ class InspectorModelProperties : public ::comphelper::OPropertyContainerHelper
+ {
+ private:
+ ::osl::Mutex& m_rMutex;
+ bool m_bHasHelpSection;
+ sal_Int32 m_nMinHelpTextLines;
+ sal_Int32 m_nMaxHelpTextLines;
+ bool m_bIsReadOnly;
+ std::unique_ptr< ::cppu::IPropertyArrayHelper >
+ m_pPropertyInfo;
+
+ public:
+ explicit InspectorModelProperties( ::osl::Mutex& _rMutex );
+
+ using ::comphelper::OPropertyContainerHelper::convertFastPropertyValue;
+ using ::comphelper::OPropertyContainerHelper::setFastPropertyValue;
+ using ::comphelper::OPropertyContainerHelper::getFastPropertyValue;
+
+ public:
+ bool hasHelpSection() const { return m_bHasHelpSection; }
+ bool isReadOnly() const { return m_bIsReadOnly; }
+ sal_Int32 getMinHelpTextLines() const { return m_nMinHelpTextLines; }
+ sal_Int32 getMaxHelpTextLines() const { return m_nMaxHelpTextLines; }
+
+ css::uno::Reference< css::beans::XPropertySetInfo >
+ getPropertySetInfo();
+ ::cppu::IPropertyArrayHelper&
+ getInfoHelper();
+
+ void constructWithHelpSection( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines );
+ };
+
+
+ //= InspectorModelProperties
+
+
+ InspectorModelProperties::InspectorModelProperties( ::osl::Mutex& _rMutex )
+ :m_rMutex( _rMutex )
+ ,m_bHasHelpSection( false )
+ ,m_nMinHelpTextLines( 3 )
+ ,m_nMaxHelpTextLines( 8 )
+ ,m_bIsReadOnly( false )
+ {
+ registerProperty(
+ "HasHelpSection",
+ MODEL_PROPERTY_ID_HAS_HELP_SECTION,
+ PropertyAttribute::READONLY,
+ &m_bHasHelpSection, cppu::UnoType<decltype(m_bHasHelpSection)>::get()
+ );
+ registerProperty(
+ "MinHelpTextLines",
+ MODEL_PROPERTY_ID_MIN_HELP_TEXT_LINES,
+ PropertyAttribute::READONLY,
+ &m_nMinHelpTextLines, cppu::UnoType<decltype(m_nMinHelpTextLines)>::get()
+ );
+ registerProperty(
+ "MaxHelpTextLines",
+ MODEL_PROPERTY_ID_MAX_HELP_TEXT_LINES,
+ PropertyAttribute::READONLY,
+ &m_nMaxHelpTextLines, cppu::UnoType<decltype(m_nMaxHelpTextLines)>::get()
+ );
+ registerProperty(
+ "IsReadOnly",
+ MODEL_PROPERTY_ID_IS_READ_ONLY,
+ PropertyAttribute::BOUND,
+ &m_bIsReadOnly, cppu::UnoType<decltype(m_bIsReadOnly)>::get()
+ );
+ }
+
+
+ void InspectorModelProperties::constructWithHelpSection( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines )
+ {
+ m_bHasHelpSection = true;
+ m_nMinHelpTextLines = _nMinHelpTextLines;
+ m_nMaxHelpTextLines = _nMaxHelpTextLines;
+ // no need to notify this, those properties are not bound. Also, the method should
+ // only be used during construction phase, where we don't expect to have any listeners.
+ }
+
+
+ ::cppu::IPropertyArrayHelper& InspectorModelProperties::getInfoHelper()
+ {
+ ::osl::MutexGuard aGuard( m_rMutex );
+ if (m_pPropertyInfo == nullptr)
+ {
+ Sequence< Property > aProperties;
+ describeProperties( aProperties );
+
+ m_pPropertyInfo.reset( new ::cppu::OPropertyArrayHelper( aProperties ) );
+ }
+ return *m_pPropertyInfo;
+ }
+
+
+ Reference< XPropertySetInfo > InspectorModelProperties::getPropertySetInfo()
+ {
+ return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
+ }
+
+
+ //= ImplInspectorModel
+
+ ImplInspectorModel::ImplInspectorModel()
+ :ImplInspectorModel_PBase( GetBroadcastHelper() )
+ ,m_pProperties( new InspectorModelProperties( m_aMutex ) )
+ {
+ }
+
+
+ ImplInspectorModel::~ImplInspectorModel()
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( ImplInspectorModel, ImplInspectorModel_Base, ImplInspectorModel_PBase )
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( ImplInspectorModel, ImplInspectorModel_Base, ImplInspectorModel_PBase )
+
+
+ Reference< XPropertySetInfo > SAL_CALL ImplInspectorModel::getPropertySetInfo( )
+ {
+ return m_pProperties->getPropertySetInfo();
+ }
+
+
+ ::cppu::IPropertyArrayHelper& SAL_CALL ImplInspectorModel::getInfoHelper()
+ {
+ return m_pProperties->getInfoHelper();
+ }
+
+
+ sal_Bool SAL_CALL ImplInspectorModel::convertFastPropertyValue( Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue )
+ {
+ return m_pProperties->convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue );
+ }
+
+
+ void SAL_CALL ImplInspectorModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+ {
+ m_pProperties->setFastPropertyValue( nHandle, rValue );
+ }
+
+
+ void SAL_CALL ImplInspectorModel::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+ {
+ m_pProperties->getFastPropertyValue( rValue, nHandle );
+ }
+
+
+ sal_Bool SAL_CALL ImplInspectorModel::getHasHelpSection()
+ {
+ return m_pProperties->hasHelpSection();
+ }
+
+
+ ::sal_Int32 SAL_CALL ImplInspectorModel::getMinHelpTextLines()
+ {
+ return m_pProperties->getMinHelpTextLines();
+ }
+
+
+ ::sal_Int32 SAL_CALL ImplInspectorModel::getMaxHelpTextLines()
+ {
+ return m_pProperties->getMaxHelpTextLines();
+ }
+
+
+ sal_Bool SAL_CALL ImplInspectorModel::getIsReadOnly()
+ {
+ return m_pProperties->isReadOnly();
+ }
+
+
+ void SAL_CALL ImplInspectorModel::setIsReadOnly( sal_Bool IsReadOnly )
+ {
+ setFastPropertyValue( MODEL_PROPERTY_ID_IS_READ_ONLY, Any( IsReadOnly ) );
+ }
+
+ sal_Bool SAL_CALL ImplInspectorModel::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+
+ void ImplInspectorModel::enableHelpSectionProperties( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines )
+ {
+ m_pProperties->constructWithHelpSection( _nMinHelpTextLines, _nMaxHelpTextLines );
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/inspectormodelbase.hxx b/extensions/source/propctrlr/inspectormodelbase.hxx
new file mode 100644
index 000000000..527668b2f
--- /dev/null
+++ b/extensions/source/propctrlr/inspectormodelbase.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/inspection/XObjectInspectorModel.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/propshlp.hxx>
+
+#include <comphelper/broadcasthelper.hxx>
+#include <comphelper/uno3.hxx>
+
+#include <memory>
+
+
+namespace pcr
+{
+
+
+ class InspectorModelProperties;
+
+ //= ImplInspectorModel
+
+ typedef ::cppu::WeakImplHelper < css::inspection::XObjectInspectorModel
+ , css::lang::XInitialization
+ , css::lang::XServiceInfo
+ > ImplInspectorModel_Base;
+ typedef ::cppu::OPropertySetHelper ImplInspectorModel_PBase;
+
+ class ImplInspectorModel
+ :public ::comphelper::OMutexAndBroadcastHelper
+ ,public ImplInspectorModel_Base
+ ,public ImplInspectorModel_PBase
+ {
+ std::unique_ptr< InspectorModelProperties > m_pProperties;
+
+ protected:
+ virtual ~ImplInspectorModel() override;
+
+ public:
+ ImplInspectorModel();
+
+ DECLARE_XINTERFACE()
+ DECLARE_XTYPEPROVIDER()
+
+ // css::beans::XPropertySet and friends
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any & rConvertedValue, css::uno::Any & rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+
+ // css::inspection::XObjectInspectorModel
+ virtual sal_Bool SAL_CALL getHasHelpSection() override;
+ virtual ::sal_Int32 SAL_CALL getMinHelpTextLines() override;
+ virtual ::sal_Int32 SAL_CALL getMaxHelpTextLines() override;
+ virtual sal_Bool SAL_CALL getIsReadOnly() override;
+ virtual void SAL_CALL setIsReadOnly( sal_Bool IsReadOnly ) override;
+
+ // css::lang::XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ protected:
+ void enableHelpSectionProperties( sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines );
+
+ private:
+ using ImplInspectorModel_PBase::getFastPropertyValue;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/linedescriptor.hxx b/extensions/source/propctrlr/linedescriptor.hxx
new file mode 100644
index 000000000..a73eda7cf
--- /dev/null
+++ b/extensions/source/propctrlr/linedescriptor.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <com/sun/star/inspection/LineDescriptor.hpp>
+#include <com/sun/star/inspection/XPropertyHandler.hpp>
+
+namespace pcr
+{
+ //= OLineDescriptor
+ struct OLineDescriptor : public css::inspection::LineDescriptor
+ {
+ OUString sName; // the name of the property
+ css::uno::Reference< css::inspection::XPropertyHandler >
+ xPropertyHandler; // the handler for this property
+ css::uno::Any aValue; // the current value of the property
+
+ bool bUnknownValue : 1; // is the property value currently "unknown"? (PropertyState_AMBIGUOUS)
+ bool bReadOnly : 1;
+
+ OLineDescriptor()
+ :bUnknownValue( false )
+ ,bReadOnly( false )
+ {
+ }
+
+ void assignFrom( const css::inspection::LineDescriptor& _rhs )
+ {
+ css::inspection::LineDescriptor::operator=( _rhs );
+ }
+ };
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/listselectiondlg.cxx b/extensions/source/propctrlr/listselectiondlg.cxx
new file mode 100644
index 000000000..18ab1d778
--- /dev/null
+++ b/extensions/source/propctrlr/listselectiondlg.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 "listselectiondlg.hxx"
+
+#include "formstrings.hxx"
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace pcr
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+
+ ListSelectionDialog::ListSelectionDialog(weld::Window* pParent, const Reference< XPropertySet >& _rxListBox,
+ const OUString& _rPropertyName, const OUString& _rPropertyUIName)
+ : GenericDialogController(pParent, "modules/spropctrlr/ui/listselectdialog.ui", "ListSelectDialog")
+ , m_xListBox ( _rxListBox )
+ , m_sPropertyName( _rPropertyName )
+ , m_xFrame(m_xBuilder->weld_frame("frame"))
+ , m_xEntries(m_xBuilder->weld_tree_view("treeview"))
+ {
+ OSL_PRECOND( m_xListBox.is(), "ListSelectionDialog::ListSelectionDialog: invalid list box!" );
+
+ m_xEntries->set_size_request(m_xEntries->get_approximate_digit_width() * 40, m_xEntries->get_height_rows(9));
+
+ m_xDialog->set_title(_rPropertyUIName);
+ m_xFrame->set_label(_rPropertyUIName);
+
+ initialize( );
+ }
+
+ ListSelectionDialog::~ListSelectionDialog()
+ {
+ }
+
+ short ListSelectionDialog::run()
+ {
+ short nResult = GenericDialogController::run();
+
+ if ( RET_OK == nResult )
+ commitSelection();
+
+ return nResult;
+ }
+
+
+ void ListSelectionDialog::initialize( )
+ {
+ if ( !m_xListBox.is() )
+ return;
+
+ try
+ {
+ // initialize the multi-selection flag
+ bool bMultiSelection = false;
+ OSL_VERIFY( m_xListBox->getPropertyValue( PROPERTY_MULTISELECTION ) >>= bMultiSelection );
+ m_xEntries->set_selection_mode(bMultiSelection ? SelectionMode::Single : SelectionMode::Multiple);
+
+ // fill the list box with all entries
+ Sequence< OUString > aListEntries;
+ OSL_VERIFY( m_xListBox->getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aListEntries );
+ fillEntryList( aListEntries );
+
+ // select entries according to the property
+ Sequence< sal_Int16 > aSelection;
+ OSL_VERIFY( m_xListBox->getPropertyValue( m_sPropertyName ) >>= aSelection );
+ selectEntries( aSelection );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "ListSelectionDialog::initialize" );
+ }
+ }
+
+ void ListSelectionDialog::commitSelection()
+ {
+ if ( !m_xListBox.is() )
+ return;
+
+ std::vector< sal_Int16 > aSelection;
+ collectSelection( aSelection );
+
+ try
+ {
+ m_xListBox->setPropertyValue( m_sPropertyName, Any( comphelper::containerToSequence(aSelection) ) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "ListSelectionDialog::commitSelection" );
+ }
+ }
+
+ void ListSelectionDialog::fillEntryList( const Sequence< OUString >& _rListEntries )
+ {
+ m_xEntries->freeze();
+ m_xEntries->clear();
+ for (auto const & entry : _rListEntries)
+ m_xEntries->append_text(entry);
+ m_xEntries->thaw();
+ }
+
+ void ListSelectionDialog::collectSelection( std::vector< sal_Int16 >& /* [out] */ _rSelection )
+ {
+ auto aSelection = m_xEntries->get_selected_rows();
+ _rSelection.resize(aSelection.size());
+ for (auto row : aSelection)
+ _rSelection.push_back(row);
+ }
+
+ void ListSelectionDialog::selectEntries( const Sequence< sal_Int16 >& /* [in ] */ _rSelection )
+ {
+ m_xEntries->unselect_all();
+ for (auto const & selection : _rSelection)
+ m_xEntries->select(selection);
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/listselectiondlg.hxx b/extensions/source/propctrlr/listselectiondlg.hxx
new file mode 100644
index 000000000..51469c091
--- /dev/null
+++ b/extensions/source/propctrlr/listselectiondlg.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 <vcl/weld.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+namespace pcr
+{
+ class ListSelectionDialog : public weld::GenericDialogController
+ {
+ private:
+ css::uno::Reference<css::beans::XPropertySet> m_xListBox;
+ OUString m_sPropertyName;
+ std::unique_ptr<weld::Frame> m_xFrame;
+ std::unique_ptr<weld::TreeView> m_xEntries;
+
+ public:
+ ListSelectionDialog(
+ weld::Window* _pParent,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxListBox,
+ const OUString& _rPropertyName,
+ const OUString& _rPropertyUIName
+ );
+ virtual ~ListSelectionDialog() override;
+
+ virtual short run() override;
+
+ private:
+ void initialize( );
+ void commitSelection();
+
+ void fillEntryList ( const css::uno::Sequence< OUString >& _rListEntries );
+
+ void selectEntries ( const css::uno::Sequence< sal_Int16 >& /* [in ] */ _rSelection );
+ void collectSelection( std::vector< sal_Int16 >& /* [out] */ _rSelection );
+ };
+
+} // namespacepcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/modulepcr.cxx b/extensions/source/propctrlr/modulepcr.cxx
new file mode 100644
index 000000000..da8336e10
--- /dev/null
+++ b/extensions/source/propctrlr/modulepcr.cxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "modulepcr.hxx"
+
+#include <unotools/resmgr.hxx>
+
+namespace pcr
+{
+OUString PcrRes(TranslateId aId) { return Translate::get(aId, Translate::Create("pcr")); }
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/modulepcr.hxx b/extensions/source/propctrlr/modulepcr.hxx
new file mode 100644
index 000000000..d0b854893
--- /dev/null
+++ b/extensions/source/propctrlr/modulepcr.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
+
+#include <rtl/ustring.hxx>
+#include <unotools/resmgr.hxx>
+
+namespace pcr
+{
+OUString PcrRes(TranslateId pId);
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/newdatatype.cxx b/extensions/source/propctrlr/newdatatype.cxx
new file mode 100644
index 000000000..09c774d3a
--- /dev/null
+++ b/extensions/source/propctrlr/newdatatype.cxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "newdatatype.hxx"
+
+namespace pcr
+{
+
+
+ //= NewDataTypeDialog
+
+
+ NewDataTypeDialog::NewDataTypeDialog(weld::Window* pParent, const OUString& _rNameBase, const std::vector< OUString >& _rProhibitedNames)
+ : GenericDialogController(pParent, "modules/spropctrlr/ui/datatypedialog.ui", "DataTypeDialog")
+ , m_aProhibitedNames( _rProhibitedNames.begin(), _rProhibitedNames.end() )
+ , m_xName(m_xBuilder->weld_entry("entry"))
+ , m_xOK(m_xBuilder->weld_button("ok"))
+ {
+ m_xName->connect_changed(LINK(this, NewDataTypeDialog, OnNameModified));
+
+ // find an initial name
+ // for this, first remove trailing digits
+ sal_Int32 nStripUntil = _rNameBase.getLength();
+ while ( nStripUntil > 0 )
+ {
+ sal_Unicode nChar = _rNameBase[ --nStripUntil ];
+ if ( ( nChar < '0' ) || ( nChar > '9' ) )
+ {
+ if ( nChar == ' ' )
+ --nStripUntil; // strip the space, too
+ break;
+ }
+ }
+
+ OUString sNameBase = OUString::Concat(_rNameBase.subView( 0, nStripUntil ? nStripUntil + 1 : 0 )) + " ";
+ OUString sInitialName;
+ sal_Int32 nPostfixNumber = 1;
+ do
+ {
+ sInitialName = sNameBase + OUString::number(nPostfixNumber++);
+ }
+ while ( m_aProhibitedNames.find( sInitialName ) != m_aProhibitedNames.end() );
+
+ m_xName->set_text(sInitialName);
+ OnNameModified(*m_xName);
+ }
+
+ NewDataTypeDialog::~NewDataTypeDialog()
+ {
+ }
+
+ IMPL_LINK_NOARG(NewDataTypeDialog, OnNameModified, weld::Entry&, void)
+ {
+ OUString sCurrentName = GetName();
+ bool bNameIsOK = ( !sCurrentName.isEmpty() )
+ && ( m_aProhibitedNames.find( sCurrentName ) == m_aProhibitedNames.end() );
+
+ m_xOK->set_sensitive(bNameIsOK);
+ }
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/newdatatype.hxx b/extensions/source/propctrlr/newdatatype.hxx
new file mode 100644
index 000000000..e6d46f1af
--- /dev/null
+++ b/extensions/source/propctrlr/newdatatype.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 <vcl/weld.hxx>
+
+#include <set>
+#include <vector>
+
+
+namespace pcr
+{
+ //= NewDataTypeDialog
+ class NewDataTypeDialog : public weld::GenericDialogController
+ {
+ private:
+ std::set<OUString> m_aProhibitedNames;
+
+ std::unique_ptr<weld::Entry> m_xName;
+ std::unique_ptr<weld::Button> m_xOK;
+ public:
+ NewDataTypeDialog(weld::Window* _pParent, const OUString& _rNameBase,
+ const std::vector< OUString >& _rProhibitedNames );
+ virtual ~NewDataTypeDialog() override;
+
+ OUString GetName() const { return m_xName->get_text(); }
+
+ private:
+ DECL_LINK(OnNameModified, weld::Entry&, void);
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/objectinspectormodel.cxx b/extensions/source/propctrlr/objectinspectormodel.cxx
new file mode 100644
index 000000000..adad6e66f
--- /dev/null
+++ b/extensions/source/propctrlr/objectinspectormodel.cxx
@@ -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 .
+ */
+
+#include "pcrcommon.hxx"
+#include "inspectormodelbase.hxx"
+
+#include <com/sun/star/ucb/AlreadyInitializedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::inspection::PropertyCategoryDescriptor;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::ucb::AlreadyInitializedException;
+
+
+ //= ObjectInspectorModel
+
+ namespace {
+
+ class ObjectInspectorModel : public ImplInspectorModel
+ {
+ private:
+ Sequence< Any > m_aFactories;
+
+ public:
+ ObjectInspectorModel();
+
+ // XObjectInspectorModel
+ virtual Sequence< Any > SAL_CALL getHandlerFactories() override;
+ virtual Sequence< PropertyCategoryDescriptor > SAL_CALL describeCategories( ) override;
+ virtual ::sal_Int32 SAL_CALL getPropertyOrderIndex( const OUString& PropertyName ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ protected:
+ void createDefault();
+ void createWithHandlerFactories( const Sequence< Any >& _rFactories );
+ void createWithHandlerFactoriesAndHelpSection( const Sequence< Any >& _rFactories, sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines );
+
+ private:
+ /** checks a given condition to be <TRUE/>, and throws an IllegalArgumentException if not
+ */
+ void impl_verifyArgument_throw( bool _bCondition, sal_Int16 _nArgumentPosition );
+ };
+
+ }
+
+ //= ObjectInspectorModel
+
+ ObjectInspectorModel::ObjectInspectorModel()
+ {
+ }
+
+
+ Sequence< Any > SAL_CALL ObjectInspectorModel::getHandlerFactories()
+ {
+ return m_aFactories;
+ }
+
+
+ Sequence< PropertyCategoryDescriptor > SAL_CALL ObjectInspectorModel::describeCategories( )
+ {
+ // no category info provided by this default implementation
+ return Sequence< PropertyCategoryDescriptor >( );
+ }
+
+
+ ::sal_Int32 SAL_CALL ObjectInspectorModel::getPropertyOrderIndex( const OUString& /*PropertyName*/ )
+ {
+ // no ordering provided by this default implementation
+ return 0;
+ }
+
+
+ void SAL_CALL ObjectInspectorModel::initialize( const Sequence< Any >& _arguments )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_aFactories.hasElements() )
+ throw AlreadyInitializedException();
+
+ StlSyntaxSequence< Any > arguments( _arguments );
+ if ( arguments.empty() )
+ { // constructor: "createDefault()"
+ createDefault();
+ return;
+ }
+
+ Sequence< Any > factories;
+ impl_verifyArgument_throw( arguments[0] >>= factories, 1 );
+
+ if ( arguments.size() == 1 )
+ { // constructor: "createWithHandlerFactories( any[] )"
+ createWithHandlerFactories( factories );
+ return;
+ }
+
+ if ( arguments.size() == 3 )
+ { // constructor: "createWithHandlerFactoriesAndHelpSection( any[], long, long )"
+ sal_Int32 nMinHelpTextLines( 0 ), nMaxHelpTextLines( 0 );
+ impl_verifyArgument_throw( arguments[1] >>= nMinHelpTextLines, 2 );
+ impl_verifyArgument_throw( arguments[2] >>= nMaxHelpTextLines, 3 );
+ createWithHandlerFactoriesAndHelpSection( factories, nMinHelpTextLines, nMaxHelpTextLines );
+ return;
+ }
+
+ impl_verifyArgument_throw( false, 2 );
+ }
+
+
+ OUString SAL_CALL ObjectInspectorModel::getImplementationName( )
+ {
+ return "org.openoffice.comp.extensions.ObjectInspectorModel";
+ }
+
+
+ Sequence< OUString > SAL_CALL ObjectInspectorModel::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.inspection.ObjectInspectorModel" };
+ }
+
+
+ void ObjectInspectorModel::createDefault()
+ {
+ m_aFactories = { Any(OUString( "com.sun.star.inspection.GenericPropertyHandler" )) };
+ }
+
+
+ void ObjectInspectorModel::createWithHandlerFactories( const Sequence< Any >& _rFactories )
+ {
+ impl_verifyArgument_throw( _rFactories.hasElements(), 1 );
+ m_aFactories = _rFactories;
+ }
+
+
+ void ObjectInspectorModel::createWithHandlerFactoriesAndHelpSection( const Sequence< Any >& _rFactories, sal_Int32 _nMinHelpTextLines, sal_Int32 _nMaxHelpTextLines )
+ {
+ impl_verifyArgument_throw( _rFactories.hasElements(), 1 );
+ impl_verifyArgument_throw( _nMinHelpTextLines >= 1, 2 );
+ impl_verifyArgument_throw( _nMaxHelpTextLines >= 1, 3 );
+ impl_verifyArgument_throw( _nMinHelpTextLines <= _nMaxHelpTextLines, 2 );
+
+ m_aFactories = _rFactories;
+ enableHelpSectionProperties( _nMinHelpTextLines, _nMaxHelpTextLines );
+ }
+
+
+ void ObjectInspectorModel::impl_verifyArgument_throw( bool _bCondition, sal_Int16 _nArgumentPosition )
+ {
+ if ( !_bCondition )
+ throw IllegalArgumentException( OUString(), *this, _nArgumentPosition );
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_ObjectInspectorModel_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::ObjectInspectorModel());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/pcr.component b/extensions/source/propctrlr/pcr.component
new file mode 100644
index 000000000..3d0214ded
--- /dev/null
+++ b/extensions/source/propctrlr/pcr.component
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="StringRepresentation"
+ constructor="extensions_propctrlr_StringRepresentation_get_implementation">
+ <service name="com.sun.star.inspection.StringRepresentation"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.ButtonNavigationHandler"
+ constructor="extensions_propctrlr_ButtonNavigationHandler_get_implementation">
+ <service name="com.sun.star.form.inspection.ButtonNavigationHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.CellBindingPropertyHandler"
+ constructor="extensions_propctrlr_CellBindingPropertyHandler_get_implementation">
+ <service name="com.sun.star.form.inspection.CellBindingPropertyHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.EFormsPropertyHandler"
+ constructor="extensions_propctrlr_EFormsPropertyHandler_get_implementation">
+ <service name="com.sun.star.form.inspection.XMLFormsPropertyHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.EditPropertyHandler"
+ constructor="extensions_propctrlr_EditPropertyHandler_get_implementation">
+ <service name="com.sun.star.form.inspection.EditPropertyHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.EventHandler"
+ constructor="extensions_propctrlr_EventHandler_get_implementation">
+ <service name="com.sun.star.form.inspection.EventHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.FormComponentPropertyHandler"
+ constructor="extensions_propctrlr_FormComponentPropertyHandler_get_implementation">
+ <service name="com.sun.star.form.inspection.FormComponentPropertyHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.FormGeometryHandler"
+ constructor="extensions_propctrlr_FormGeometryHandler_get_implementation">
+ <service name="com.sun.star.form.inspection.FormGeometryHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.GenericPropertyHandler"
+ constructor="extensions_propctrlr_GenericPropertyHandler_get_implementation">
+ <service name="com.sun.star.inspection.GenericPropertyHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.SubmissionPropertyHandler"
+ constructor="extensions_propctrlr_SubmissionPropertyHandler_get_implementation">
+ <service name="com.sun.star.form.inspection.SubmissionPropertyHandler"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.extensions.XSDValidationPropertyHandler"
+ constructor="extensions_propctrlr_XSDValidationPropertyHandler_get_implementation">
+ <service name="com.sun.star.form.inspection.XSDValidationPropertyHandler"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.extensions.DefaultFormComponentInspectorModel"
+ constructor="extensions_propctrlr_DefaultFormComponentInspectorModel_get_implementation">
+ <service name="com.sun.star.form.inspection.DefaultFormComponentInspectorModel"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.extensions.DefaultHelpProvider"
+ constructor="extensions_propctrlr_DefaultHelpProvider_get_implementation">
+ <service name="com.sun.star.inspection.DefaultHelpProvider"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.extensions.DialogController"
+ constructor="extensions_propctrlr_DialogController_get_implementation">
+ <service name="com.sun.star.awt.PropertyBrowserController"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.extensions.FormController"
+ constructor="extensions_propctrlr_FormController_get_implementation">
+ <service name="com.sun.star.form.PropertyBrowserController"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.extensions.ObjectInspector"
+ constructor="extensions_propctrlr_OPropertyBrowserController_get_implementation">
+ <service name="com.sun.star.inspection.ObjectInspector"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.extensions.ObjectInspectorModel"
+ constructor="extensions_propctrlr_ObjectInspectorModel_get_implementation">
+ <service name="com.sun.star.inspection.ObjectInspectorModel"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.form.ui.MasterDetailLinkDialog"
+ constructor="extensions_propctrlr_MasterDetailLinkDialog_get_implementation">
+ <service name="com.sun.star.form.MasterDetailLinkDialog"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.form.ui.OControlFontDialog"
+ constructor="extensions_propctrlr_OControlFontDialog_get_implementation">
+ <service name="com.sun.star.form.ControlFontDialog"/>
+ </implementation>
+ <implementation name="org.openoffice.comp.form.ui.OTabOrderDialog"
+ constructor="extensions_propcrltr_OTabOrderDialog_get_implementation">
+ <service name="com.sun.star.form.ui.TabOrderDialog"/>
+ <service name="com.sun.star.form.TabOrderDialog"/>
+ </implementation>
+</component>
diff --git a/extensions/source/propctrlr/pcrcommon.cxx b/extensions/source/propctrlr/pcrcommon.cxx
new file mode 100644
index 000000000..4151e6827
--- /dev/null
+++ b/extensions/source/propctrlr/pcrcommon.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 "pcrcommon.hxx"
+
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <rtl/ustrbuf.hxx>
+#include <tools/urlobj.hxx>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::util;
+
+
+ //= HelpIdUrl
+
+
+ OString HelpIdUrl::getHelpId( std::u16string_view _rHelpURL )
+ {
+ INetURLObject aHID( _rHelpURL );
+ if ( aHID.GetProtocol() == INetProtocol::Hid )
+ return OUStringToOString( aHID.GetURLPath(), RTL_TEXTENCODING_UTF8 );
+ else
+ return OUStringToOString( _rHelpURL, RTL_TEXTENCODING_UTF8 );
+ }
+
+
+ OUString HelpIdUrl::getHelpURL( std::u16string_view sHelpId )
+ {
+ OUStringBuffer aBuffer;
+ INetURLObject aHID( sHelpId );
+ if ( aHID.GetProtocol() == INetProtocol::NotValid )
+ aBuffer.append( INET_HID_SCHEME );
+ aBuffer.append( sHelpId );
+ return aBuffer.makeStringAndClear();
+ }
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/pcrcommon.hxx b/extensions/source/propctrlr/pcrcommon.hxx
new file mode 100644
index 000000000..f9ec2838c
--- /dev/null
+++ b/extensions/source/propctrlr/pcrcommon.hxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 EDITOR_LIST_APPEND (SAL_MAX_UINT16)
+#define EDITOR_LIST_ENTRY_NOTFOUND (SAL_MAX_UINT16)
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <comphelper/interfacecontainer3.hxx>
+
+namespace pcr
+{
+
+
+ #define OWN_PROPERTY_ID_INTROSPECTEDOBJECT 0x0010
+ #define OWN_PROPERTY_ID_CURRENTPAGE 0x0011
+ #define OWN_PROPERTY_ID_CONTROLCONTEXT 0x0012
+ #define OWN_PROPERTY_ID_TABBINGMODEL 0x0013
+
+
+ //= types
+
+ typedef ::comphelper::OInterfaceContainerHelper3 < css::beans::XPropertyChangeListener
+ > PropertyChangeListeners;
+
+
+ //= helper
+
+ // small helper to make the "swap" call on an STL container a single-line call, which
+ // in its canonic form "aFoo.swap( Container() )" doesn't compile with GCC
+ template< class CONTAINER >
+ void clearContainer( CONTAINER& _rContainer )
+ {
+ CONTAINER().swap(_rContainer);
+ }
+
+
+ //= HelpIdUrl
+
+ /// small helper to translate help ids into help urls
+ class HelpIdUrl
+ {
+ public:
+ static OString getHelpId( std::u16string_view _rHelpURL );
+ static OUString getHelpURL( std::u16string_view );
+ };
+
+
+ //= StlSyntaxSequence
+
+ template< class ELEMENT >
+ class StlSyntaxSequence : public css::uno::Sequence< ELEMENT >
+ {
+ private:
+ typedef css::uno::Sequence< ELEMENT > UnoBase;
+
+ public:
+ StlSyntaxSequence() : UnoBase() { }
+ explicit StlSyntaxSequence( const UnoBase& rSeq ) : UnoBase( rSeq ) { }
+ explicit StlSyntaxSequence( sal_Int32 len ) : UnoBase( len ) { }
+
+ typedef const ELEMENT* const_iterator;
+ typedef ELEMENT* iterator;
+
+ const_iterator begin() const { return UnoBase::getConstArray(); }
+ const_iterator end() const { return UnoBase::getConstArray() + UnoBase::getLength(); }
+
+ iterator begin() { return UnoBase::getArray(); }
+ iterator end() { return UnoBase::getArray() + UnoBase::getLength(); }
+
+ sal_Int32 size() const { return UnoBase::getLength(); }
+ bool empty() const { return !UnoBase::hasElements(); }
+ };
+
+
+ //= UNO helpers
+
+#define DECLARE_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;
+
+#define IMPLEMENT_FORWARD_XCOMPONENT( classname, baseclass ) \
+ void SAL_CALL classname::dispose( ) \
+ { \
+ baseclass::WeakComponentImplHelperBase::dispose(); \
+ } \
+ void SAL_CALL classname::addEventListener( const css::uno::Reference< css::lang::XEventListener >& Listener ) \
+ { \
+ baseclass::WeakComponentImplHelperBase::addEventListener( Listener ); \
+ } \
+ void SAL_CALL classname::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& Listener ) \
+ { \
+ baseclass::WeakComponentImplHelperBase::removeEventListener( Listener ); \
+ } \
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/pcrcommontypes.hxx b/extensions/source/propctrlr/pcrcommontypes.hxx
new file mode 100644
index 000000000..e2aa3570e
--- /dev/null
+++ b/extensions/source/propctrlr/pcrcommontypes.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/Property.hpp>
+#include <rtl/ustring.hxx>
+
+#include <unordered_map>
+
+namespace pcr
+{
+typedef std::unordered_map<OUString, css::beans::Property> PropertyMap;
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/pcrstrings.hxx b/extensions/source/propctrlr/pcrstrings.hxx
new file mode 100644
index 000000000..3bb0a888c
--- /dev/null
+++ b/extensions/source/propctrlr/pcrstrings.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 <rtl/ustring.hxx>
+
+namespace pcr
+{
+
+
+ // properties
+ constexpr OUStringLiteral PROPERTY_TABBINGMODEL = u"TabbingModel";
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/pcrunodialogs.cxx b/extensions/source/propctrlr/pcrunodialogs.cxx
new file mode 100644
index 000000000..9083cb284
--- /dev/null
+++ b/extensions/source/propctrlr/pcrunodialogs.cxx
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <vcl/svapp.hxx>
+#include "pcrunodialogs.hxx"
+#include "formstrings.hxx"
+#include "pcrstrings.hxx"
+#include "taborder.hxx"
+#include "pcrcommon.hxx"
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+
+
+ //= OTabOrderDialog
+
+
+ OTabOrderDialog::OTabOrderDialog( const Reference< XComponentContext >& _rxContext )
+ :OGenericUnoDialog( _rxContext )
+ {
+ registerProperty( PROPERTY_CONTROLCONTEXT, OWN_PROPERTY_ID_CONTROLCONTEXT,
+ PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT,
+ &m_xControlContext, cppu::UnoType<decltype(m_xControlContext)>::get() );
+
+ registerProperty( PROPERTY_TABBINGMODEL, OWN_PROPERTY_ID_TABBINGMODEL,
+ PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT,
+ &m_xTabbingModel, cppu::UnoType<decltype(m_xTabbingModel)>::get() );
+ }
+
+ OTabOrderDialog::~OTabOrderDialog()
+ {
+ if (m_xDialog)
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (m_xDialog)
+ destroyDialog();
+ }
+ }
+
+ Sequence<sal_Int8> SAL_CALL OTabOrderDialog::getImplementationId( )
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+ OUString SAL_CALL OTabOrderDialog::getImplementationName()
+ {
+ return "org.openoffice.comp.form.ui.OTabOrderDialog";
+ }
+
+
+ css::uno::Sequence<OUString> SAL_CALL OTabOrderDialog::getSupportedServiceNames()
+ {
+ return { "com.sun.star.form.ui.TabOrderDialog", "com.sun.star.form.TabOrderDialog" };
+ }
+
+
+ Reference<XPropertySetInfo> SAL_CALL OTabOrderDialog::getPropertySetInfo()
+ {
+ Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+ }
+
+ ::cppu::IPropertyArrayHelper& OTabOrderDialog::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+ ::cppu::IPropertyArrayHelper* OTabOrderDialog::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties( aProps );
+ return new ::cppu::OPropertyArrayHelper( aProps );
+ }
+
+ std::unique_ptr<weld::DialogController> OTabOrderDialog::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent)
+ {
+ return std::make_unique<TabOrderDialog>(Application::GetFrameWeld(rParent), m_xTabbingModel, m_xControlContext, m_aContext);
+ }
+
+ void OTabOrderDialog::initialize( const Sequence< Any >& aArguments )
+ {
+ Reference<css::awt::XTabControllerModel> xTabbingModel;
+ Reference<css::awt::XControlContainer> xControlContext;
+ Reference<css::awt::XWindow> xParentWindow;
+ if (aArguments.getLength() == 3 && (aArguments[0] >>= xTabbingModel) && (aArguments[1] >>= xControlContext) && (aArguments[2] >>= xParentWindow))
+ {
+ Sequence< Any > aNewArguments{
+ Any(NamedValue(
+ "TabbingModel",
+ Any( xTabbingModel )
+ )),
+ Any(NamedValue(
+ "ControlContext",
+ Any( xControlContext )
+ )),
+ Any(NamedValue(
+ "ParentWindow",
+ Any( xParentWindow )
+ ))
+ };
+ OTabOrderDialog_DBase::initialize(aNewArguments);
+ }
+ else
+ OTabOrderDialog_DBase::initialize(aArguments);
+ }
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propcrltr_OTabOrderDialog_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::OTabOrderDialog(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/pcrunodialogs.hxx b/extensions/source/propctrlr/pcrunodialogs.hxx
new file mode 100644
index 000000000..87c99db35
--- /dev/null
+++ b/extensions/source/propctrlr/pcrunodialogs.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 <svtools/genericunodialog.hxx>
+#include <com/sun/star/awt/XTabControllerModel.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <comphelper/proparrhlp.hxx>
+
+
+namespace pcr
+{
+
+
+ //= OTabOrderDialog
+
+ class OTabOrderDialog;
+ typedef ::svt::OGenericUnoDialog OTabOrderDialog_DBase;
+ typedef ::comphelper::OPropertyArrayUsageHelper< OTabOrderDialog > OTabOrderDialog_PBase;
+
+ class OTabOrderDialog
+ :public OTabOrderDialog_DBase
+ ,public OTabOrderDialog_PBase
+ {
+ protected:
+ // <properties>
+ css::uno::Reference< css::awt::XTabControllerModel >
+ m_xTabbingModel;
+ css::uno::Reference< css::awt::XControlContainer >
+ m_xControlContext;
+ // </properties>
+
+ public:
+ explicit OTabOrderDialog( const css::uno::Reference< css::uno::XComponentContext >& _rxContext );
+ virtual ~OTabOrderDialog() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ protected:
+ // OGenericUnoDialog overridables
+ virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override;
+ };
+
+
+} // namespacepcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propcontroller.cxx b/extensions/source/propctrlr/propcontroller.cxx
new file mode 100644
index 000000000..6bbcdcf5d
--- /dev/null
+++ b/extensions/source/propctrlr/propcontroller.cxx
@@ -0,0 +1,1652 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "propcontroller.hxx"
+#include "handlerhelper.hxx"
+#include "standardcontrol.hxx"
+#include "linedescriptor.hxx"
+#include <strings.hrc>
+#include "propertyeditor.hxx"
+#include "modulepcr.hxx"
+#include "formstrings.hxx"
+#include "formbrowsertools.hxx"
+#include "propertycomposer.hxx"
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <com/sun/star/ucb/AlreadyInitializedException.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+#include <osl/mutex.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <algorithm>
+#include <sal/log.hxx>
+
+namespace pcr
+{
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::inspection;
+ using namespace ::com::sun::star::ucb;
+ using namespace ::comphelper;
+
+ //= OPropertyBrowserController
+ OPropertyBrowserController::OPropertyBrowserController( const Reference< XComponentContext >& _rxContext )
+ :m_xContext(_rxContext)
+ ,m_aDisposeListeners( m_aMutex )
+ ,m_aControlObservers( m_aMutex )
+ ,m_bContainerFocusListening( false )
+ ,m_bSuspendingPropertyHandlers( false )
+ ,m_bConstructed( false )
+ ,m_bBindingIntrospectee( false )
+ {
+ }
+
+ OPropertyBrowserController::~OPropertyBrowserController()
+ {
+ // stop listening for property changes
+ acquire();
+ stopInspection( true );
+ }
+
+ IMPLEMENT_FORWARD_REFCOUNT( OPropertyBrowserController, OPropertyBrowserController_Base )
+
+ Any SAL_CALL OPropertyBrowserController::queryInterface( const Type& _rType )
+ {
+ Any aReturn = OPropertyBrowserController_Base::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = ::cppu::queryInterface(
+ _rType,
+ static_cast< XObjectInspectorUI* >( this )
+ );
+ return aReturn;
+ }
+
+
+ void OPropertyBrowserController::startContainerWindowListening()
+ {
+ if (m_bContainerFocusListening)
+ return;
+
+ if (m_xFrame.is())
+ {
+ Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
+ if (xContainerWindow.is())
+ {
+ xContainerWindow->addFocusListener(this);
+ m_bContainerFocusListening = true;
+ }
+ }
+
+ DBG_ASSERT(m_bContainerFocusListening, "OPropertyBrowserController::startContainerWindowListening: unable to start listening (inconsistence)!");
+ }
+
+
+ void OPropertyBrowserController::stopContainerWindowListening()
+ {
+ if (!m_bContainerFocusListening)
+ return;
+
+ if (m_xFrame.is())
+ {
+ Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
+ if (xContainerWindow.is())
+ {
+ xContainerWindow->removeFocusListener(this);
+ m_bContainerFocusListening = false;
+ }
+ }
+
+ DBG_ASSERT(!m_bContainerFocusListening, "OPropertyBrowserController::stopContainerWindowListening: unable to stop listening (inconsistence)!");
+ }
+
+
+ Reference< XObjectInspectorModel > SAL_CALL OPropertyBrowserController::getInspectorModel()
+ {
+ return m_xModel;
+ }
+
+
+ void OPropertyBrowserController::impl_initializeView_nothrow()
+ {
+ OSL_PRECOND( haveView(), "OPropertyBrowserController::impl_initializeView_nothrow: not to be called when we have no view!" );
+ if ( !haveView() )
+ return;
+
+ if ( !m_xModel.is() )
+ // allowed
+ return;
+
+ try
+ {
+ getPropertyBox().EnableHelpSection( m_xModel->getHasHelpSection() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ bool OPropertyBrowserController::impl_isReadOnlyModel_throw() const
+ {
+ if ( !m_xModel.is() )
+ return false;
+
+ return m_xModel->getIsReadOnly();
+ }
+
+
+ void OPropertyBrowserController::impl_startOrStopModelListening_nothrow( bool _bDoListen ) const
+ {
+ try
+ {
+ Reference< XPropertySet > xModelProperties( m_xModel, UNO_QUERY );
+ if ( !xModelProperties.is() )
+ // okay, so the model doesn't want to change its properties
+ // dynamically - fine with us
+ return;
+
+ void (SAL_CALL XPropertySet::*pListenerOperation)( const OUString&, const Reference< XPropertyChangeListener >& )
+ = _bDoListen ? &XPropertySet::addPropertyChangeListener : &XPropertySet::removePropertyChangeListener;
+
+ (xModelProperties.get()->*pListenerOperation)(
+ OUString( "IsReadOnly" ),
+ const_cast< OPropertyBrowserController* >( this )
+ );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ void OPropertyBrowserController::impl_bindToNewModel_nothrow( const Reference< XObjectInspectorModel >& _rxInspectorModel )
+ {
+ impl_startOrStopModelListening_nothrow( false );
+ m_xModel = _rxInspectorModel;
+ impl_startOrStopModelListening_nothrow( true );
+
+ // initialize the view, if we already have one
+ if ( haveView() )
+ impl_initializeView_nothrow();
+
+ // inspect again, if we already have inspectees
+ if ( !m_aInspectedObjects.empty() )
+ impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects) );
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::setInspectorModel( const Reference< XObjectInspectorModel >& _inspectorModel )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_xModel == _inspectorModel )
+ return;
+
+ impl_bindToNewModel_nothrow( _inspectorModel );
+ }
+
+
+ Reference< XObjectInspectorUI > SAL_CALL OPropertyBrowserController::getInspectorUI()
+ {
+ // we're derived from this interface, though we do not expose it in queryInterface and getTypes.
+ return this;
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::inspect( const Sequence< Reference< XInterface > >& _rObjects )
+ {
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_bSuspendingPropertyHandlers || !suspendAll_nothrow() )
+ { // we already are trying to suspend the component (this is somewhere up the stack)
+ // OR one of our property handlers raised a veto against closing. Well, we *need* to close
+ // it in order to inspect another object.
+ throw VetoException();
+ }
+ if ( m_bBindingIntrospectee )
+ throw VetoException();
+
+ m_bBindingIntrospectee = true;
+ impl_rebindToInspectee_nothrow( InterfaceArray( _rObjects.begin(), _rObjects.end() ) );
+ m_bBindingIntrospectee = false;
+
+ }
+
+
+ Reference< XDispatch > SAL_CALL OPropertyBrowserController::queryDispatch( const URL& /*URL*/, const OUString& /*TargetFrameName*/, ::sal_Int32 /*SearchFlags*/ )
+ {
+ // we don't have any dispatches at all, right now
+ return Reference< XDispatch >();
+ }
+
+
+ Sequence< Reference< XDispatch > > SAL_CALL OPropertyBrowserController::queryDispatches( const Sequence< DispatchDescriptor >& Requests )
+ {
+ Sequence< Reference< XDispatch > > aReturn;
+ sal_Int32 nLen = Requests.getLength();
+ aReturn.realloc( nLen );
+
+ Reference< XDispatch >* pReturn = aReturn.getArray();
+ const Reference< XDispatch >* pReturnEnd = aReturn.getArray() + nLen;
+ const DispatchDescriptor* pDescripts = Requests.getConstArray();
+
+ for ( ; pReturn != pReturnEnd; ++ pReturn, ++pDescripts )
+ *pReturn = queryDispatch( pDescripts->FeatureURL, pDescripts->FrameName, pDescripts->SearchFlags );
+
+ return aReturn;
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::initialize( const Sequence< Any >& _arguments )
+ {
+ if ( m_bConstructed )
+ throw AlreadyInitializedException();
+
+ StlSyntaxSequence< Any > arguments( _arguments );
+ if ( arguments.empty() )
+ { // constructor: "createDefault()"
+ m_bConstructed = true;
+ return;
+ }
+
+ Reference< XObjectInspectorModel > xModel;
+ if ( arguments.size() == 1 )
+ { // constructor: "createWithModel( XObjectInspectorModel )"
+ if ( !( arguments[0] >>= xModel ) )
+ throw IllegalArgumentException( OUString(), *this, 0 );
+ createWithModel( xModel );
+ return;
+ }
+
+ throw IllegalArgumentException( OUString(), *this, 0 );
+ }
+
+
+ void OPropertyBrowserController::createWithModel( const Reference< XObjectInspectorModel >& _rxModel )
+ {
+ osl_atomic_increment( &m_refCount );
+ {
+ setInspectorModel( _rxModel );
+ }
+ osl_atomic_decrement( &m_refCount );
+
+ m_bConstructed = true;
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::attachFrame( const Reference< XFrame >& _rxFrame )
+ {
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (_rxFrame.is() && haveView())
+ throw RuntimeException("Unable to attach to a second frame.",*this);
+
+ // revoke as focus listener from the old container window
+ stopContainerWindowListening();
+
+ m_xPropView.reset();
+ m_xBuilder.reset();
+
+ m_xFrame = _rxFrame;
+ if (!m_xFrame.is())
+ return;
+
+ // TODO: this construction perhaps should be done outside. Don't know the exact meaning of attachFrame.
+ // Maybe it is intended to only announce the frame to the controller, and the instance doing this
+ // announcement is responsible for calling setComponent, too.
+ Reference<XWindow> xContainerWindow = m_xFrame->getContainerWindow();
+
+ OUString sUIFile("modules/spropctrlr/ui/formproperties.ui");
+ std::unique_ptr<weld::Builder> xBuilder;
+
+ if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xContainerWindow.get()))
+ {
+ xBuilder = Application::CreateBuilder(pTunnel->getWidget(), sUIFile);
+ }
+ else
+ {
+ VclPtr<vcl::Window> pParentWin = VCLUnoHelper::GetWindow(xContainerWindow);
+ if (!pParentWin)
+ throw RuntimeException("The frame is invalid. Unable to extract the container window.",*this);
+ xBuilder = Application::CreateInterimBuilder(pParentWin, sUIFile, true);
+ }
+
+ Construct(xContainerWindow, std::move(xBuilder));
+
+ startContainerWindowListening();
+
+ UpdateUI();
+ }
+
+ sal_Bool SAL_CALL OPropertyBrowserController::attachModel( const Reference< XModel >& _rxModel )
+ {
+ Reference< XObjectInspectorModel > xModel( _rxModel, UNO_QUERY );
+ if ( !xModel.is() )
+ return false;
+
+ setInspectorModel( xModel );
+ return getInspectorModel() == _rxModel;
+ }
+
+
+ bool OPropertyBrowserController::suspendAll_nothrow()
+ {
+ // if there is a handle inside its "onInteractivePropertySelection" method,
+ // then veto
+ // Normally, we could expect every handler to do this itself, but being
+ // realistic, it's safer to handle this here in general.
+ if ( m_xInteractiveHandler.is() )
+ return false;
+
+ m_bSuspendingPropertyHandlers = true;
+ bool bHandlerVeto = !suspendPropertyHandlers_nothrow( true );
+ m_bSuspendingPropertyHandlers = false;
+ return !bHandlerVeto;
+ }
+
+
+ bool OPropertyBrowserController::suspendPropertyHandlers_nothrow( bool _bSuspend )
+ {
+ PropertyHandlerArray aAllHandlers; // will contain every handler exactly once
+ for (auto const& propertyHandler : m_aPropertyHandlers)
+ {
+ if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) != aAllHandlers.end() )
+ // already visited this particular handler (m_aPropertyHandlers usually contains
+ // the same handler more than once)
+ continue;
+ aAllHandlers.push_back(propertyHandler.second);
+ }
+
+ for (auto const& handler : aAllHandlers)
+ {
+ try
+ {
+ if ( !handler->suspend( _bSuspend ) )
+ if ( _bSuspend )
+ // if we're not suspending, but reactivating, ignore the error
+ return false;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::suspendPropertyHandlers_nothrow" );
+ }
+ }
+ return true;
+ }
+
+
+ sal_Bool SAL_CALL OPropertyBrowserController::suspend( sal_Bool _bSuspend )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_ENSURE( haveView(), "OPropertyBrowserController::suspend: don't have a view anymore!" );
+
+ if ( !_bSuspend )
+ { // this means a "suspend" is to be "revoked"
+ suspendPropertyHandlers_nothrow( false );
+ // we ourself cannot revoke our suspend
+ return false;
+ }
+
+ if ( !suspendAll_nothrow() )
+ return false;
+
+ // commit the editor's content
+ if ( haveView() )
+ getPropertyBox().CommitModified();
+
+ // stop listening
+ stopContainerWindowListening();
+
+ // outta here
+ return true;
+ }
+
+
+ Any SAL_CALL OPropertyBrowserController::getViewData( )
+ {
+ return Any( m_sPageSelection );
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::restoreViewData( const Any& Data )
+ {
+ OUString sPageSelection;
+ if ( ( Data >>= sPageSelection ) && !sPageSelection.isEmpty() )
+ {
+ m_sPageSelection = sPageSelection;
+ selectPageFromViewData();
+ }
+ }
+
+ Reference< XModel > SAL_CALL OPropertyBrowserController::getModel( )
+ {
+ // have no model
+ return Reference< XModel >();
+ }
+
+ Reference< XFrame > SAL_CALL OPropertyBrowserController::getFrame( )
+ {
+ return m_xFrame;
+ }
+
+ void SAL_CALL OPropertyBrowserController::dispose()
+ {
+ SolarMutexGuard aSolarGuard;
+
+ // stop inspecting the current object
+ stopInspection( false );
+
+ // say our dispose listeners goodbye
+ css::lang::EventObject aEvt;
+ aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
+ m_aDisposeListeners.disposeAndClear(aEvt);
+ m_aControlObservers.disposeAndClear(aEvt);
+
+ m_xPropView.reset();
+ m_xBuilder.reset();
+
+ if ( m_xView.is() )
+ m_xView->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
+ m_xView.clear( );
+
+ m_aInspectedObjects.clear();
+ impl_bindToNewModel_nothrow( nullptr );
+ }
+
+ void SAL_CALL OPropertyBrowserController::addEventListener( const Reference< XEventListener >& _rxListener )
+ {
+ m_aDisposeListeners.addInterface(_rxListener);
+ }
+
+ void SAL_CALL OPropertyBrowserController::removeEventListener( const Reference< XEventListener >& _rxListener )
+ {
+ m_aDisposeListeners.removeInterface(_rxListener);
+ }
+
+ OUString SAL_CALL OPropertyBrowserController::getImplementationName( )
+ {
+ return "org.openoffice.comp.extensions.ObjectInspector";
+ }
+
+ sal_Bool SAL_CALL OPropertyBrowserController::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+
+ Sequence< OUString > SAL_CALL OPropertyBrowserController::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.inspection.ObjectInspector" };
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::focusGained( const FocusEvent& _rSource )
+ {
+ Reference< XWindow > xSourceWindow(_rSource.Source, UNO_QUERY);
+ Reference< XWindow > xContainerWindow;
+ if (m_xFrame.is())
+ xContainerWindow = m_xFrame->getContainerWindow();
+
+ if ( xContainerWindow.get() == xSourceWindow.get() )
+ { // our container window got the focus
+ if ( haveView() )
+ getPropertyBox().GrabFocus();
+ }
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::focusLost( const FocusEvent& /*_rSource*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::disposing( const EventObject& _rSource )
+ {
+ if ( m_xView.is() && ( m_xView == _rSource.Source ) )
+ {
+ m_xView = nullptr;
+ m_xPropView.reset();
+ m_xBuilder.reset();
+ }
+
+ auto it = std::find_if(m_aInspectedObjects.begin(), m_aInspectedObjects.end(),
+ [&_rSource](const InterfaceArray::value_type& rxObj) { return rxObj == _rSource.Source; });
+ if (it != m_aInspectedObjects.end())
+ m_aInspectedObjects.erase(it);
+ }
+
+
+ IMPL_LINK_NOARG(OPropertyBrowserController, OnPageActivation, LinkParamNone*, void)
+ {
+ updateViewDataFromActivePage();
+ }
+
+
+ void OPropertyBrowserController::updateViewDataFromActivePage()
+ {
+ if (!haveView())
+ return;
+
+ OUString sOldSelection = m_sPageSelection;
+ m_sPageSelection.clear();
+
+ const sal_uInt16 nCurrentPage = m_xPropView->getActivePage();
+ if ( sal_uInt16(-1) != nCurrentPage )
+ {
+ for (auto const& pageId : m_aPageIds)
+ {
+ if ( nCurrentPage == pageId.second )
+ {
+ m_sPageSelection = pageId.first;
+ break;
+ }
+ }
+ }
+
+ if ( !m_sPageSelection.isEmpty() )
+ m_sLastValidPageSelection = m_sPageSelection;
+ else if ( !sOldSelection.isEmpty() )
+ m_sLastValidPageSelection = sOldSelection;
+ }
+
+
+ sal_uInt16 OPropertyBrowserController::impl_getPageIdForCategory_nothrow( const OUString& _rCategoryName ) const
+ {
+ sal_uInt16 nPageId = sal_uInt16(-1);
+ HashString2Int16::const_iterator pagePos = m_aPageIds.find( _rCategoryName );
+ if ( pagePos != m_aPageIds.end() )
+ nPageId = pagePos->second;
+ return nPageId;
+ }
+
+ void OPropertyBrowserController::selectPageFromViewData()
+ {
+ sal_uInt16 nNewPage = impl_getPageIdForCategory_nothrow( m_sPageSelection );
+
+ if ( haveView() && ( nNewPage != sal_uInt16(-1) ) )
+ m_xPropView->activatePage( nNewPage );
+
+ // just in case ...
+ updateViewDataFromActivePage();
+ }
+
+ void OPropertyBrowserController::Construct(const Reference<XWindow>& rContainerWindow, std::unique_ptr<weld::Builder> xBuilder)
+ {
+ DBG_ASSERT(!haveView(), "OPropertyBrowserController::Construct: already have a view!");
+ assert(xBuilder && "OPropertyBrowserController::Construct: invalid parent window!");
+
+ m_xBuilder = std::move(xBuilder);
+
+ m_xPropView.reset(new OPropertyBrowserView(m_xContext, *m_xBuilder));
+ m_xPropView->setPageActivationHandler(LINK(this, OPropertyBrowserController, OnPageActivation));
+
+ // add as dispose listener for our view. The view is disposed by the frame we're plugged into,
+ // and this disposal _deletes_ the view, so it would be deadly if we use our m_xPropView member
+ // after that
+ m_xView = rContainerWindow;
+ if (m_xView.is())
+ m_xView->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
+
+ getPropertyBox().SetLineListener(this);
+ getPropertyBox().SetControlObserver(this);
+ impl_initializeView_nothrow();
+ }
+
+ void SAL_CALL OPropertyBrowserController::propertyChange( const PropertyChangeEvent& _rEvent )
+ {
+ if ( _rEvent.Source == m_xModel )
+ {
+ if ( _rEvent.PropertyName == "IsReadOnly" )
+ // this is a huge cudgel, admitted.
+ // The problem is that in case we were previously read-only, all our controls
+ // were created read-only, too. We cannot simply switch them to not-read-only.
+ // Even if they had an API for this, we do not know whether they were
+ // originally created read-only, or if they are read-only just because
+ // the model was.
+ impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects) );
+ return;
+ }
+
+ if ( m_sCommittingProperty == _rEvent.PropertyName )
+ return;
+
+ if ( !haveView() )
+ return;
+
+ Any aNewValue( _rEvent.NewValue );
+ if ( impl_hasPropertyHandlerFor_nothrow( _rEvent.PropertyName ) )
+ {
+ // forward the new value to the property box, to reflect the change in the UI
+ aNewValue = impl_getPropertyValue_throw( _rEvent.PropertyName );
+
+ // check whether the state is ambiguous. This is interesting in case we display the properties
+ // for multiple objects at once: In this case, we'll get a notification from one of the objects,
+ // but need to care for the "composed" value, which can be "ambiguous".
+ PropertyHandlerRef xHandler( impl_getHandlerForProperty_throw( _rEvent.PropertyName ), UNO_SET_THROW );
+ PropertyState ePropertyState( xHandler->getPropertyState( _rEvent.PropertyName ) );
+ bool bAmbiguousValue = ( PropertyState_AMBIGUOUS_VALUE == ePropertyState );
+
+ getPropertyBox().SetPropertyValue( _rEvent.PropertyName, aNewValue, bAmbiguousValue );
+ }
+
+ // if it's an actuating property, then update the UI for any dependent
+ // properties
+ if ( impl_isActuatingProperty_nothrow( _rEvent.PropertyName ) )
+ impl_broadcastPropertyChange_nothrow( _rEvent.PropertyName, aNewValue, _rEvent.OldValue, false );
+ }
+
+ Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::createPropertyControl( ::sal_Int16 ControlType, sal_Bool bCreateReadOnly )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XPropertyControl > xControl;
+
+ // read-only-ness
+ bCreateReadOnly |= impl_isReadOnlyModel_throw() ? 1 : 0;
+
+ switch ( ControlType )
+ {
+ case PropertyControlType::MultiLineTextField:
+ case PropertyControlType::StringListField:
+ {
+ bool bMultiLineTextField = ControlType == PropertyControlType::MultiLineTextField;
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/multiline.ui", m_xContext));
+ auto pContainer = xBuilder->weld_container("multiline");
+ rtl::Reference<OMultilineEditControl> pControl = new OMultilineEditControl(std::move(pContainer), std::move(xBuilder),
+ bMultiLineTextField ? eMultiLineText : eStringList, bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ case PropertyControlType::ListBox:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/listbox.ui", m_xContext));
+ auto pComboBox = xBuilder->weld_combo_box("listbox");
+ rtl::Reference<OListboxControl> pControl = new OListboxControl(std::move(pComboBox), std::move(xBuilder), bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ case PropertyControlType::ComboBox:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/combobox.ui", m_xContext));
+ auto pComboBox = xBuilder->weld_combo_box("combobox");
+ rtl::Reference<OComboboxControl> pControl = new OComboboxControl(std::move(pComboBox), std::move(xBuilder), bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ case PropertyControlType::TextField:
+ case PropertyControlType::CharacterField:
+ {
+ bool bCharacterField = ControlType == PropertyControlType::CharacterField;
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/textfield.ui", m_xContext));
+ auto pEntry = xBuilder->weld_entry("textfield");
+ rtl::Reference<OEditControl> pControl = new OEditControl(std::move(pEntry), std::move(xBuilder), bCharacterField, bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ case PropertyControlType::NumericField:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/numericfield.ui", m_xContext));
+ auto pSpinButton = xBuilder->weld_metric_spin_button("numericfield", FieldUnit::NONE);
+ rtl::Reference<ONumericControl> pControl = new ONumericControl(std::move(pSpinButton), std::move(xBuilder), bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ case PropertyControlType::DateTimeField:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/datetimefield.ui", m_xContext));
+ auto pContainer = xBuilder->weld_container("datetimefield");
+ rtl::Reference<ODateTimeControl> pControl = new ODateTimeControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ case PropertyControlType::DateField:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/datefield.ui", m_xContext));
+ auto pContainer = xBuilder->weld_container("datefield");
+ rtl::Reference<ODateControl> pControl = new ODateControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ case PropertyControlType::TimeField:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/timefield.ui", m_xContext));
+ auto pTimeSpinButton = xBuilder->weld_formatted_spin_button("timefield");
+ rtl::Reference<OTimeControl> pControl = new OTimeControl(std::move(pTimeSpinButton), std::move(xBuilder), bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ case PropertyControlType::ColorListBox:
+ {
+ auto lambda = [this]{ return PropertyHandlerHelper::getDialogParentFrame(m_xContext); };
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/colorlistbox.ui", m_xContext));
+ auto pMenuButton = xBuilder->weld_menu_button("colorlistbox");
+ rtl::Reference<OColorControl> pControl = new OColorControl(std::make_unique<ColorListBox>(std::move(pMenuButton), lambda), std::move(xBuilder), bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ case PropertyControlType::HyperlinkField:
+ {
+ std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/hyperlinkfield.ui", m_xContext));
+ auto pContainer = xBuilder->weld_container("hyperlinkfield");
+ rtl::Reference<OHyperlinkControl> pControl = new OHyperlinkControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly);
+ pControl->SetModifyHandler();
+ xControl = pControl;
+ break;
+ }
+
+ default:
+ throw IllegalArgumentException( OUString(), *this, 1 );
+ }
+
+ return xControl;
+ }
+
+
+ void OPropertyBrowserController::impl_toggleInspecteeListening_nothrow( bool _bOn )
+ {
+ for (auto const& inspectedObject : m_aInspectedObjects)
+ {
+ try
+ {
+ Reference< XComponent > xComp( inspectedObject, UNO_QUERY );
+ if ( xComp.is() )
+ {
+ if ( _bOn )
+ xComp->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
+ else
+ xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+ }
+
+
+ void OPropertyBrowserController::stopInspection( bool _bCommitModified )
+ {
+ if ( haveView() )
+ {
+ if ( _bCommitModified )
+ // commit the editor's content
+ getPropertyBox().CommitModified();
+
+ // hide the property box so that it does not flicker
+ getPropertyBox().Hide();
+
+ // clear the property box
+ getPropertyBox().ClearAll();
+ }
+
+ // destroy the view first
+ if ( haveView() )
+ {
+ // remove the pages
+ for (auto const& pageId : m_aPageIds)
+ getPropertyBox().RemovePage( pageId.second );
+ clearContainer( m_aPageIds );
+ }
+
+ clearContainer( m_aProperties );
+
+ // de-register as dispose-listener from our inspected objects
+ impl_toggleInspecteeListening_nothrow( false );
+
+ // handlers are obsolete, so is our "composer" for their UI requests
+ if (m_pUIRequestComposer)
+ m_pUIRequestComposer->dispose();
+ m_pUIRequestComposer.reset();
+
+ // clean up the property handlers
+ PropertyHandlerArray aAllHandlers; // will contain every handler exactly once
+ for (auto const& propertyHandler : m_aPropertyHandlers)
+ if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) == aAllHandlers.end() )
+ aAllHandlers.push_back( propertyHandler.second );
+
+ for (auto const& handler : aAllHandlers)
+ {
+ try
+ {
+ handler->removePropertyChangeListener( this );
+ handler->dispose();
+ }
+ catch( const DisposedException& )
+ {
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+ clearContainer( m_aPropertyHandlers );
+ clearContainer( m_aDependencyHandlers );
+ }
+
+
+ bool OPropertyBrowserController::impl_hasPropertyHandlerFor_nothrow( const OUString& _rPropertyName ) const
+ {
+ PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
+ return ( handlerPos != m_aPropertyHandlers.end() );
+ }
+
+
+ OPropertyBrowserController::PropertyHandlerRef const & OPropertyBrowserController::impl_getHandlerForProperty_throw( const OUString& _rPropertyName ) const
+ {
+ PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
+ if ( handlerPos == m_aPropertyHandlers.end() )
+ throw RuntimeException();
+ return handlerPos->second;
+ }
+
+
+ Any OPropertyBrowserController::impl_getPropertyValue_throw( const OUString& _rPropertyName )
+ {
+ PropertyHandlerRef handler = impl_getHandlerForProperty_throw( _rPropertyName );
+ return handler->getPropertyValue( _rPropertyName );
+ }
+
+
+ void OPropertyBrowserController::impl_rebindToInspectee_nothrow( InterfaceArray&& _rObjects )
+ {
+ try
+ {
+ // stop inspecting the old object(s)
+ stopInspection( true );
+
+ // inspect the new object(s)
+ m_aInspectedObjects = std::move(_rObjects);
+ doInspection();
+
+ // update the user interface
+ UpdateUI();
+ }
+
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
+ }
+ }
+
+
+ void OPropertyBrowserController::doInspection()
+ {
+ try
+ {
+
+ // obtain the properties of the object
+ std::vector< Property > aProperties;
+
+ PropertyHandlerArray aPropertyHandlers;
+ getPropertyHandlers( m_aInspectedObjects, aPropertyHandlers );
+
+ PropertyHandlerArray::iterator aHandler( aPropertyHandlers.begin() );
+ while ( aHandler != aPropertyHandlers.end() )
+ {
+ DBG_ASSERT( aHandler->get(), "OPropertyBrowserController::doInspection: invalid handler!" );
+
+ StlSyntaxSequence< Property > aThisHandlersProperties( (*aHandler)->getSupportedProperties() );
+
+ if ( aThisHandlersProperties.empty() )
+ {
+ // this handler doesn't know anything about the current inspectee -> ignore it
+ (*aHandler)->dispose();
+ aHandler = aPropertyHandlers.erase( aHandler );
+ continue;
+ }
+
+ // append these properties to our "all properties" array
+ aProperties.reserve( aProperties.size() + aThisHandlersProperties.size() );
+ for (const auto & aThisHandlersProperty : aThisHandlersProperties)
+ {
+ auto noPrevious = std::none_of(
+ aProperties.begin(),
+ aProperties.end(),
+ FindPropertyByName( aThisHandlersProperty.Name )
+ );
+ if ( noPrevious )
+ {
+ aProperties.push_back( aThisHandlersProperty );
+ continue;
+ }
+
+ // there already was another (previous) handler which supported this property.
+ // Don't add it to aProperties, again.
+
+ // Also, ensure that handlers which previously expressed interest in *changes*
+ // of this property are not notified.
+ // This is 'cause we have a new handler which is responsible for this property,
+ // which means it can give it a completely different meaning than the previous
+ // handler for this property is prepared for.
+ std::pair< PropertyHandlerMultiRepository::iterator, PropertyHandlerMultiRepository::iterator >
+ aDepHandlers = m_aDependencyHandlers.equal_range( aThisHandlersProperty.Name );
+ m_aDependencyHandlers.erase( aDepHandlers.first, aDepHandlers.second );
+ }
+
+ // determine the superseded properties
+ StlSyntaxSequence< OUString > aSupersededByThisHandler( (*aHandler)->getSupersededProperties() );
+ for (const auto & superseded : aSupersededByThisHandler)
+ {
+ std::vector< Property >::iterator existent = std::find_if(
+ aProperties.begin(),
+ aProperties.end(),
+ FindPropertyByName( superseded )
+ );
+ if ( existent != aProperties.end() )
+ // one of the properties superseded by this handler was supported by a previous
+ // one -> erase
+ aProperties.erase( existent );
+ }
+
+ // be notified of changes which this handler is responsible for
+ (*aHandler)->addPropertyChangeListener( this );
+
+ // remember this handler for every of the properties which it is responsible
+ // for
+ for (const auto & aThisHandlersProperty : aThisHandlersProperties)
+ {
+ m_aPropertyHandlers[ aThisHandlersProperty.Name ] = *aHandler;
+ // note that this implies that if two handlers support the same property,
+ // the latter wins
+ }
+
+ // see if the handler expresses interest in any actuating properties
+ StlSyntaxSequence< OUString > aInterestingActuations( (*aHandler)->getActuatingProperties() );
+ for (const auto & aInterestingActuation : aInterestingActuations)
+ {
+ m_aDependencyHandlers.emplace( aInterestingActuation, *aHandler );
+ }
+
+ ++aHandler;
+ }
+
+ // create a new composer for UI requests coming from the handlers
+ m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( getInspectorUI(), this ) );
+
+ // sort the properties by relative position, as indicated by the model
+ sal_Int32 nPos = 0;
+ for (auto const& sourceProps : aProperties)
+ {
+ sal_Int32 nRelativePropertyOrder = nPos;
+ if ( m_xModel.is() )
+ nRelativePropertyOrder = m_xModel->getPropertyOrderIndex( sourceProps.Name );
+ m_aProperties.emplace(nRelativePropertyOrder, sourceProps);
+ ++nPos;
+ }
+
+ // be notified when one of our inspectees dies
+ impl_toggleInspecteeListening_nothrow( true );
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
+ }
+ }
+
+
+ css::awt::Size SAL_CALL OPropertyBrowserController::getMinimumSize()
+ {
+ css::awt::Size aSize;
+ if( m_xPropView )
+ return m_xPropView->getMinimumSize();
+ else
+ return aSize;
+ }
+
+
+ css::awt::Size SAL_CALL OPropertyBrowserController::getPreferredSize()
+ {
+ return getMinimumSize();
+ }
+
+
+ css::awt::Size SAL_CALL OPropertyBrowserController::calcAdjustedSize( const css::awt::Size& _rNewSize )
+ {
+ awt::Size aMinSize = getMinimumSize( );
+ awt::Size aAdjustedSize( _rNewSize );
+ if ( aAdjustedSize.Width < aMinSize.Width )
+ aAdjustedSize.Width = aMinSize.Width;
+ if ( aAdjustedSize.Height < aMinSize.Height )
+ aAdjustedSize.Height = aMinSize.Height;
+ return aAdjustedSize;
+ }
+
+
+ void OPropertyBrowserController::describePropertyLine( const Property& _rProperty, OLineDescriptor& _rDescriptor )
+ {
+ try
+ {
+ PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rProperty.Name );
+ if ( handler == m_aPropertyHandlers.end() )
+ throw RuntimeException(); // caught below
+
+ _rDescriptor.assignFrom( handler->second->describePropertyLine( _rProperty.Name, this ) );
+
+
+ _rDescriptor.xPropertyHandler = handler->second;
+ _rDescriptor.sName = _rProperty.Name;
+ _rDescriptor.aValue = _rDescriptor.xPropertyHandler->getPropertyValue( _rProperty.Name );
+
+ if ( _rDescriptor.DisplayName.isEmpty() )
+ {
+ #ifdef DBG_UTIL
+ SAL_WARN( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine: handler did not provide a display name for '"
+ <<_rProperty.Name << "'!" );
+ #endif
+ _rDescriptor.DisplayName = _rProperty.Name;
+ }
+
+ PropertyState ePropertyState( _rDescriptor.xPropertyHandler->getPropertyState( _rProperty.Name ) );
+ if ( PropertyState_AMBIGUOUS_VALUE == ePropertyState )
+ {
+ _rDescriptor.bUnknownValue = true;
+ _rDescriptor.aValue.clear();
+ }
+
+ _rDescriptor.bReadOnly = impl_isReadOnlyModel_throw();
+
+ // for ui-testing try and distinguish different instances of the controls
+ auto xWindow = _rDescriptor.Control->getControlWindow();
+ if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xWindow.get()))
+ {
+ weld::Widget* m_pControlWindow = pTunnel->getWidget();
+ if (m_pControlWindow)
+ m_pControlWindow->set_buildable_name(m_pControlWindow->get_buildable_name() + "-" + _rDescriptor.DisplayName.toUtf8());
+ }
+
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine" );
+ }
+ }
+
+
+ void OPropertyBrowserController::impl_buildCategories_throw()
+ {
+ OSL_PRECOND( m_aPageIds.empty(), "OPropertyBrowserController::impl_buildCategories_throw: duplicate call!" );
+
+ StlSyntaxSequence< PropertyCategoryDescriptor > aCategories;
+ if ( m_xModel.is() )
+ aCategories = StlSyntaxSequence< PropertyCategoryDescriptor >(m_xModel->describeCategories());
+
+ for (auto const& category : aCategories)
+ {
+ OSL_ENSURE( m_aPageIds.find( category.ProgrammaticName ) == m_aPageIds.end(),
+ "OPropertyBrowserController::impl_buildCategories_throw: duplicate programmatic name!" );
+
+ m_aPageIds[ category.ProgrammaticName ] =
+ getPropertyBox().AppendPage( category.UIName, HelpIdUrl::getHelpId( category.HelpURL ) );
+ }
+ }
+
+
+ void OPropertyBrowserController::UpdateUI()
+ {
+ try
+ {
+ if ( !haveView() )
+ // too early, will return later
+ return;
+
+ // create our tab pages
+ impl_buildCategories_throw();
+ // (and allow for pages to be actually unused)
+ std::set< sal_uInt16 > aUsedPages;
+
+ // when building the UI below, remember which properties are actuating,
+ // to allow for an initial actuatingPropertyChanged call
+ std::vector< OUString > aActuatingProperties;
+ std::vector< Any > aActuatingPropertyValues;
+
+ // ask the handlers to describe the property UI, and insert the resulting
+ // entries into our list boxes
+ for (auto const& property : m_aProperties)
+ {
+ OLineDescriptor aDescriptor;
+ describePropertyLine( property.second, aDescriptor );
+
+ bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( property.second.Name );
+
+ SAL_WARN_IF( aDescriptor.Category.isEmpty(), "extensions.propctrlr",
+ "OPropertyBrowserController::UpdateUI: empty category provided for property '"
+ << property.second.Name << "'!");
+ // finally insert this property control
+ sal_uInt16 nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
+ if ( nTargetPageId == sal_uInt16(-1) )
+ {
+ // this category does not yet exist. This is allowed, as an inspector model might be lazy, and not provide
+ // any category information of its own. In this case, we have a fallback ...
+ m_aPageIds[ aDescriptor.Category ] =
+ getPropertyBox().AppendPage( aDescriptor.Category, OString() );
+ nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
+ }
+
+ getPropertyBox().InsertEntry( aDescriptor, nTargetPageId );
+ aUsedPages.insert( nTargetPageId );
+
+ // if it's an actuating property, remember it
+ if ( bIsActuatingProperty )
+ {
+ aActuatingProperties.push_back( property.second.Name );
+ aActuatingPropertyValues.push_back( impl_getPropertyValue_throw( property.second.Name ) );
+ }
+ }
+
+ // update any dependencies for the actuating properties which we encountered
+ {
+ std::vector< Any >::const_iterator aPropertyValue = aActuatingPropertyValues.begin();
+ for (auto const& actuatingProperty : aActuatingProperties)
+ {
+ impl_broadcastPropertyChange_nothrow( actuatingProperty, *aPropertyValue, *aPropertyValue, true );
+ ++aPropertyValue;
+ }
+ }
+
+ // remove any unused pages (which we did not encounter properties for)
+ HashString2Int16 aSurvivingPageIds;
+ for (auto const& pageId : m_aPageIds)
+ {
+ if ( aUsedPages.find( pageId.second ) == aUsedPages.end() )
+ getPropertyBox().RemovePage( pageId.second );
+ else
+ aSurvivingPageIds.insert(pageId);
+ }
+ m_aPageIds.swap( aSurvivingPageIds );
+
+ getPropertyBox().Show();
+
+ // activate the first page
+ if ( !m_aPageIds.empty() )
+ {
+ Sequence< PropertyCategoryDescriptor > aCategories( m_xModel->describeCategories() );
+ if ( aCategories.hasElements() )
+ m_xPropView->activatePage( m_aPageIds[ aCategories[0].ProgrammaticName ] );
+ else
+ // allowed: if we default-created the pages ...
+ m_xPropView->activatePage( m_aPageIds.begin()->second );
+ }
+
+ // activate the previously active page (if possible)
+ if ( !m_sLastValidPageSelection.isEmpty() )
+ m_sPageSelection = m_sLastValidPageSelection;
+ selectPageFromViewData();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ void OPropertyBrowserController::Clicked( const OUString& _rName, bool _bPrimary )
+ {
+ try
+ {
+ // since the browse buttons do not get the focus when clicked with the mouse,
+ // we need to commit the changes in the current property field
+ getPropertyBox().CommitModified();
+
+ PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rName );
+ DBG_ASSERT( handler != m_aPropertyHandlers.end(), "OPropertyBrowserController::Clicked: a property without handler? This will crash!" );
+
+ ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
+
+ Any aData;
+ m_xInteractiveHandler = handler->second;
+ InteractiveSelectionResult eResult =
+ handler->second->onInteractivePropertySelection( _rName, _bPrimary, aData,
+ m_pUIRequestComposer->getUIForPropertyHandler( handler->second ) );
+
+ switch ( eResult )
+ {
+ case InteractiveSelectionResult_Cancelled:
+ case InteractiveSelectionResult_Success:
+ // okay, nothing to do
+ break;
+ case InteractiveSelectionResult_ObtainedValue:
+ handler->second->setPropertyValue( _rName, aData );
+ break;
+ case InteractiveSelectionResult_Pending:
+ // also okay, we expect that the handler has disabled the UI as necessary
+ break;
+ default:
+ OSL_FAIL( "OPropertyBrowserController::Clicked: unknown result value!" );
+ break;
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ m_xInteractiveHandler = nullptr;
+ }
+
+
+ bool OPropertyBrowserController::hasPropertyByName( const OUString& _rName )
+ {
+ for (auto const& property : m_aProperties)
+ if ( property.second.Name == _rName )
+ return true;
+ return false;
+ }
+
+
+ void OPropertyBrowserController::Commit( const OUString& rName, const Any& _rValue )
+ {
+ try
+ {
+ OUString sPlcHolder = PcrRes(RID_EMBED_IMAGE_PLACEHOLDER);
+ bool bIsPlaceHolderValue = false;
+
+ if ( rName == PROPERTY_IMAGE_URL )
+ {
+ // if the prop value is the PlaceHolder
+ // can ignore it
+ OUString sVal;
+ _rValue >>= sVal;
+ if ( sVal == sPlcHolder )
+ bIsPlaceHolderValue = true;
+ }
+ m_sCommittingProperty = rName;
+
+ bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( rName );
+
+ Any aOldValue;
+ if ( bIsActuatingProperty )
+ aOldValue = impl_getPropertyValue_throw( rName );
+
+ // do we have a dedicated handler for this property, which we can delegate some tasks to?
+ PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
+
+
+ // set the value ( only if it's not a placeholder )
+ if ( !bIsPlaceHolderValue )
+ handler->setPropertyValue( rName, _rValue );
+
+
+ // re-retrieve the value
+ Any aNormalizedValue = handler->getPropertyValue( rName );
+
+ // care for any inter-property dependencies
+ if ( bIsActuatingProperty )
+ impl_broadcastPropertyChange_nothrow( rName, aNormalizedValue, aOldValue, false );
+
+ // and display it again. This ensures proper formatting
+ getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
+ }
+ catch(const PropertyVetoException& eVetoException)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xPropView->getPropertyBox().getWidget(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ eVetoException.Message));
+ xInfoBox->run();
+ PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
+ Any aNormalizedValue = handler->getPropertyValue( rName );
+ getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
+ }
+
+ m_sCommittingProperty.clear();
+ }
+
+
+ void OPropertyBrowserController::focusGained( const Reference< XPropertyControl >& Control )
+ {
+ m_aControlObservers.notifyEach( &XPropertyControlObserver::focusGained, Control );
+ }
+
+
+ void OPropertyBrowserController::valueChanged( const Reference< XPropertyControl >& Control )
+ {
+ m_aControlObservers.notifyEach( &XPropertyControlObserver::valueChanged, Control );
+ }
+
+
+ namespace
+ {
+ Reference< XPropertyHandler > lcl_createHandler( const Reference<XComponentContext>& _rContext, const Any& _rFactoryDescriptor )
+ {
+ Reference< XPropertyHandler > xHandler;
+
+ OUString sServiceName;
+ Reference< XSingleServiceFactory > xServiceFac;
+ Reference< XSingleComponentFactory > xComponentFac;
+
+ if ( _rFactoryDescriptor >>= sServiceName )
+ xHandler.set( _rContext->getServiceManager()->createInstanceWithContext( sServiceName, _rContext ), UNO_QUERY );
+ else if ( _rFactoryDescriptor >>= xServiceFac )
+ xHandler.set(xServiceFac->createInstance(), css::uno::UNO_QUERY);
+ else if ( _rFactoryDescriptor >>= xComponentFac )
+ xHandler.set(xComponentFac->createInstanceWithContext( _rContext ), css::uno::UNO_QUERY);
+ OSL_ENSURE(xHandler.is(),"lcl_createHandler: Can not create handler");
+ return xHandler;
+ }
+ }
+
+
+ void OPropertyBrowserController::getPropertyHandlers( const InterfaceArray& _rObjects, PropertyHandlerArray& _rHandlers )
+ {
+ _rHandlers.resize( 0 );
+ if ( _rObjects.empty() )
+ return;
+
+ Sequence< Any > aHandlerFactories;
+ if ( m_xModel.is() )
+ aHandlerFactories = m_xModel->getHandlerFactories();
+
+ for ( auto const & handlerFactory : std::as_const(aHandlerFactories) )
+ {
+ if ( _rObjects.size() == 1 )
+ { // we're inspecting only one object -> one handler
+ Reference< XPropertyHandler > xHandler( lcl_createHandler( m_xContext, handlerFactory ) );
+ if ( xHandler.is() )
+ {
+ xHandler->inspect( _rObjects[0] );
+ _rHandlers.push_back( xHandler );
+ }
+ }
+ else
+ {
+ // create a single handler for every single object
+ std::vector< Reference< XPropertyHandler > > aSingleHandlers( _rObjects.size() );
+ std::vector< Reference< XPropertyHandler > >::iterator pHandler = aSingleHandlers.begin();
+
+ for (auto const& elem : _rObjects)
+ {
+ *pHandler = lcl_createHandler( m_xContext, handlerFactory );
+ if ( pHandler->is() )
+ {
+ (*pHandler)->inspect(elem);
+ ++pHandler;
+ }
+ }
+ aSingleHandlers.resize( pHandler - aSingleHandlers.begin() );
+
+ // then create a handler which composes information out of those single handlers
+ if ( !aSingleHandlers.empty() )
+ _rHandlers.push_back( new PropertyComposer( std::move(aSingleHandlers) ) );
+ }
+ }
+
+ // note that the handlers will not be used by our caller, if they indicate that there are no
+ // properties they feel responsible for
+ }
+
+
+ bool OPropertyBrowserController::impl_findObjectProperty_nothrow( const OUString& _rName, OrderedPropertyMap::const_iterator* _pProperty )
+ {
+ OrderedPropertyMap::const_iterator search = std::find_if(m_aProperties.begin(), m_aProperties.end(),
+ [&_rName](const OrderedPropertyMap::value_type& rEntry) { return rEntry.second.Name == _rName; });
+ if ( _pProperty )
+ *_pProperty = search;
+ return ( search != m_aProperties.end() );
+ }
+
+
+ void OPropertyBrowserController::rebuildPropertyUI( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !haveView() )
+ throw RuntimeException();
+
+ OrderedPropertyMap::const_iterator propertyPos;
+ if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
+ return;
+
+ OLineDescriptor aDescriptor;
+ try
+ {
+ describePropertyLine( propertyPos->second, aDescriptor );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::rebuildPropertyUI" );
+ }
+
+ getPropertyBox().ChangeEntry( aDescriptor );
+ }
+
+
+ void OPropertyBrowserController::enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !haveView() )
+ throw RuntimeException();
+
+ if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
+ return;
+
+ getPropertyBox().EnablePropertyLine( _rPropertyName, _bEnable );
+ }
+
+
+ void OPropertyBrowserController::enablePropertyUIElements( const OUString& _rPropertyName, sal_Int16 _nElements, sal_Bool _bEnable )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !haveView() )
+ throw RuntimeException();
+
+ if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
+ return;
+
+ getPropertyBox().EnablePropertyControls( _rPropertyName, _nElements, _bEnable );
+ }
+
+
+ void OPropertyBrowserController::showPropertyUI( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !haveView() )
+ throw RuntimeException();
+
+ // look up the property in our object properties
+ OrderedPropertyMap::const_iterator propertyPos;
+ if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
+ return;
+
+ if ( getPropertyBox().GetPropertyPos( _rPropertyName ) != EDITOR_LIST_ENTRY_NOTFOUND )
+ {
+ rebuildPropertyUI( _rPropertyName );
+ return;
+ }
+
+ OLineDescriptor aDescriptor;
+ describePropertyLine( propertyPos->second, aDescriptor );
+
+ // look for the position to insert the property
+
+ // side note: The methods GetPropertyPos and InsertEntry of the OPropertyEditor work
+ // only on the current page. This implies that it's impossible to use this method here
+ // to show property lines which are *not* on the current page.
+ // This is sufficient for now, but should be changed in the future.
+
+ // by definition, the properties in m_aProperties are in the order in which they appear in the UI
+ // So all we need is a predecessor of pProperty in m_aProperties
+ sal_uInt16 nUIPos = EDITOR_LIST_ENTRY_NOTFOUND;
+ do
+ {
+ if ( propertyPos != m_aProperties.begin() )
+ --propertyPos;
+ nUIPos = getPropertyBox().GetPropertyPos( propertyPos->second.Name );
+ }
+ while ( ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND ) && ( propertyPos != m_aProperties.begin() ) );
+
+ if ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND )
+ // insert at the very top
+ nUIPos = 0;
+ else
+ // insert right after the predecessor we found
+ ++nUIPos;
+
+ getPropertyBox().InsertEntry(
+ aDescriptor, impl_getPageIdForCategory_nothrow( aDescriptor.Category ), nUIPos );
+ }
+
+
+ void OPropertyBrowserController::hidePropertyUI( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !haveView() )
+ throw RuntimeException();
+
+ if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
+ return;
+
+ getPropertyBox().RemoveEntry( _rPropertyName );
+ }
+
+
+ void OPropertyBrowserController::showCategory( const OUString& rCategory, sal_Bool bShow )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !haveView() )
+ throw RuntimeException();
+
+ sal_uInt16 nPageId = impl_getPageIdForCategory_nothrow( rCategory );
+ OSL_ENSURE( nPageId != sal_uInt16(-1), "OPropertyBrowserController::showCategory: invalid category!" );
+
+ getPropertyBox().ShowPropertyPage( nPageId, bShow );
+ }
+
+
+ Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::getPropertyControl( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !haveView() )
+ throw RuntimeException();
+
+ Reference< XPropertyControl > xControl( getPropertyBox().GetPropertyControl( _rPropertyName ) );
+ return xControl;
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::registerControlObserver( const Reference< XPropertyControlObserver >& Observer )
+ {
+ m_aControlObservers.addInterface( Observer );
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::revokeControlObserver( const Reference< XPropertyControlObserver >& Observer )
+ {
+ m_aControlObservers.removeInterface( Observer );
+ }
+
+
+ void SAL_CALL OPropertyBrowserController::setHelpSectionText( const OUString& _rHelpText )
+ {
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !haveView() )
+ throw DisposedException();
+
+ if ( !getPropertyBox().HasHelpSection() )
+ throw NoSupportException();
+
+ getPropertyBox().SetHelpText( _rHelpText );
+ }
+
+
+ void OPropertyBrowserController::impl_broadcastPropertyChange_nothrow( const OUString& _rPropertyName, const Any& _rNewValue, const Any& _rOldValue, bool _bFirstTimeInit ) const
+ {
+ // are there one or more handlers which are interested in the actuation?
+ std::pair< PropertyHandlerMultiRepository::const_iterator, PropertyHandlerMultiRepository::const_iterator > aInterestedHandlers =
+ m_aDependencyHandlers.equal_range( _rPropertyName );
+ if ( aInterestedHandlers.first == aInterestedHandlers.second )
+ // none of our handlers is interested in this
+ return;
+
+ ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
+ try
+ {
+ // collect the responses from all interested handlers
+ PropertyHandlerMultiRepository::const_iterator handler = aInterestedHandlers.first;
+ while ( handler != aInterestedHandlers.second )
+ {
+ handler->second->actuatingPropertyChanged( _rPropertyName, _rNewValue, _rOldValue,
+ m_pUIRequestComposer->getUIForPropertyHandler( handler->second ),
+ _bFirstTimeInit );
+ ++handler;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_OPropertyBrowserController_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::OPropertyBrowserController(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propcontroller.hxx b/extensions/source/propctrlr/propcontroller.hxx
new file mode 100644
index 000000000..a56a83979
--- /dev/null
+++ b/extensions/source/propctrlr/propcontroller.hxx
@@ -0,0 +1,368 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "composeduiupdate.hxx"
+#include "proplinelistener.hxx"
+#include "propcontrolobserver.hxx"
+#include "browserview.hxx"
+
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/awt/XLayoutConstrains.hpp>
+#include <com/sun/star/inspection/XPropertyControlFactory.hpp>
+#include <com/sun/star/inspection/XObjectInspector.hpp>
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/inspection/XPropertyHandler.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <comphelper/interfacecontainer2.hxx>
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/broadcasthelper.hxx>
+#include <vcl/weld.hxx>
+
+#include <map>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+namespace pcr
+{
+ class OPropertyEditor;
+ struct OLineDescriptor;
+
+ typedef ::cppu::WeakImplHelper < css::lang::XServiceInfo
+ , css::awt::XFocusListener
+ , css::awt::XLayoutConstrains
+ , css::beans::XPropertyChangeListener
+ , css::inspection::XPropertyControlFactory
+ , css::inspection::XObjectInspector
+ , css::lang::XInitialization
+ > OPropertyBrowserController_Base;
+
+ class OPropertyBrowserController
+ :public ::comphelper::OMutexAndBroadcastHelper
+ ,public OPropertyBrowserController_Base
+ ,public css::inspection::XObjectInspectorUI
+ // that's intentionally *not* part of the OPropertyBrowserController_Base
+ // We do not want this to be available in queryInterface, getTypes, and the like.
+ ,public IPropertyLineListener
+ ,public IPropertyControlObserver
+ ,public IPropertyExistenceCheck
+ {
+ private:
+ typedef std::multimap< sal_Int32, css::beans::Property > OrderedPropertyMap;
+ typedef std::vector< css::uno::Reference< css::uno::XInterface > >
+ InterfaceArray;
+
+ protected:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ private:
+ css::uno::Reference< css::frame::XFrame > m_xFrame;
+ css::uno::Reference< css::awt::XWindow > m_xView;
+
+ ::comphelper::OInterfaceContainerHelper2 m_aDisposeListeners;
+ ::comphelper::OInterfaceContainerHelper2 m_aControlObservers;
+ // meta data about the properties
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<OPropertyBrowserView> m_xPropView;
+
+ OUString m_sPageSelection;
+ OUString m_sLastValidPageSelection;
+
+ typedef css::uno::Reference< css::inspection::XPropertyHandler >
+ PropertyHandlerRef;
+ typedef std::vector< PropertyHandlerRef > PropertyHandlerArray;
+ typedef std::unordered_map< OUString, PropertyHandlerRef >
+ PropertyHandlerRepository;
+ typedef std::unordered_multimap< OUString, PropertyHandlerRef >
+ PropertyHandlerMultiRepository;
+ PropertyHandlerRepository m_aPropertyHandlers;
+ PropertyHandlerMultiRepository m_aDependencyHandlers;
+ PropertyHandlerRef m_xInteractiveHandler;
+
+ std::unique_ptr< ComposedPropertyUIUpdate > m_pUIRequestComposer;
+
+ /// our InspectorModel
+ css::uno::Reference< css::inspection::XObjectInspectorModel >
+ m_xModel;
+ /// the object(s) we're currently inspecting
+ InterfaceArray m_aInspectedObjects;
+ /// the properties of the currently inspected object(s)
+ OrderedPropertyMap m_aProperties;
+ /// the property we're just committing
+ OUString m_sCommittingProperty;
+
+ typedef std::unordered_map< OUString, sal_uInt16 > HashString2Int16;
+ HashString2Int16 m_aPageIds;
+
+ bool m_bContainerFocusListening;
+ bool m_bSuspendingPropertyHandlers;
+ bool m_bConstructed;
+ bool m_bBindingIntrospectee;
+
+ protected:
+ DECLARE_XINTERFACE()
+
+ // 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;
+
+ // XController
+ virtual void SAL_CALL attachFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) override;
+ virtual sal_Bool SAL_CALL attachModel( const css::uno::Reference< css::frame::XModel >& xModel ) override;
+ virtual sal_Bool SAL_CALL suspend( sal_Bool bSuspend ) override;
+ virtual css::uno::Any SAL_CALL getViewData( ) override;
+ virtual void SAL_CALL restoreViewData( const css::uno::Any& Data ) override;
+ virtual css::uno::Reference< css::frame::XModel > SAL_CALL getModel( ) override;
+ virtual css::uno::Reference< css::frame::XFrame > SAL_CALL getFrame( ) 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;
+
+ // XFocusListener
+ virtual void SAL_CALL focusGained( const css::awt::FocusEvent& _rSource ) override;
+ virtual void SAL_CALL focusLost( const css::awt::FocusEvent& _rSource ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XLayoutConstrains
+ virtual css::awt::Size SAL_CALL getMinimumSize( ) override;
+ virtual css::awt::Size SAL_CALL getPreferredSize( ) override;
+ virtual css::awt::Size SAL_CALL calcAdjustedSize( const css::awt::Size& rNewSize ) override;
+
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& _rEvent ) override;
+
+ /** XPropertyControlFactory
+ */
+ virtual css::uno::Reference< css::inspection::XPropertyControl > SAL_CALL createPropertyControl( ::sal_Int16 ControlType, sal_Bool CreateReadOnly ) override;
+
+ public:
+ explicit OPropertyBrowserController(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext);
+
+ protected:
+ virtual ~OPropertyBrowserController() override;
+
+ // IPropertyLineListener
+ virtual void Clicked( const OUString& _rName, bool _bPrimary ) override;
+ virtual void Commit( const OUString& _rName, const css::uno::Any& _rVal ) override;
+
+ // IPropertyControlObserver
+ virtual void focusGained( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) override;
+ virtual void valueChanged( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) override;
+
+ // IPropertyExistenceCheck
+ virtual bool hasPropertyByName( const OUString& _rName ) override;
+
+ // XObjectInspectorUI
+ virtual void SAL_CALL enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable ) override;
+ virtual void SAL_CALL enablePropertyUIElements( const OUString& _rPropertyName, ::sal_Int16 _nElements, sal_Bool _bEnable ) override;
+ virtual void SAL_CALL rebuildPropertyUI( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL showPropertyUI( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL hidePropertyUI( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL showCategory( const OUString& _rCategory, sal_Bool _bShow ) override;
+ virtual css::uno::Reference< css::inspection::XPropertyControl > SAL_CALL getPropertyControl( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL registerControlObserver( const css::uno::Reference< css::inspection::XPropertyControlObserver >& Observer ) override;
+ virtual void SAL_CALL revokeControlObserver( const css::uno::Reference< css::inspection::XPropertyControlObserver >& Observer ) override;
+ virtual void SAL_CALL setHelpSectionText( const OUString& HelpText ) override;
+
+ // XObjectInspector
+ virtual css::uno::Reference< css::inspection::XObjectInspectorModel > SAL_CALL getInspectorModel() override;
+ virtual void SAL_CALL setInspectorModel( const css::uno::Reference< css::inspection::XObjectInspectorModel >& _inspectormodel ) override;
+ virtual css::uno::Reference< css::inspection::XObjectInspectorUI > SAL_CALL getInspectorUI() override;
+ virtual void SAL_CALL inspect( const css::uno::Sequence< css::uno::Reference< css::uno::XInterface > >& Objects ) 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ private:
+ void UpdateUI();
+
+ void startContainerWindowListening();
+ void stopContainerWindowListening();
+
+ // stop the inspection
+ void stopInspection( bool _bCommitModified );
+
+ bool haveView() const { return bool(m_xPropView); }
+ OPropertyEditor& getPropertyBox() { return m_xPropView->getPropertyBox(); }
+
+ // does the inspection of the objects as indicated by our model
+ void doInspection();
+
+ // bind the browser to m_xIntrospecteeAsProperty
+ void impl_rebindToInspectee_nothrow( InterfaceArray&& _rObjects );
+
+ /** retrieves special property handlers for our introspectee
+ */
+ void getPropertyHandlers( const InterfaceArray& _rObjects, PropertyHandlerArray& _rHandlers );
+
+ /** called when a property changed, to broadcast any handlers which might have
+ registered for this property
+
+ @param _bFirstTimeInit
+ if set to <FALSE/>, this is a real change in the property value, not just a call
+ for purposes of initialization.
+ */
+ void impl_broadcastPropertyChange_nothrow( const OUString& _rPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, bool _bFirstTimeInit ) const;
+
+ /** determines whether the given property is an actuating property, that is, at least one
+ handler expressed interest in changes to this property's value.
+ */
+ bool impl_isActuatingProperty_nothrow( const OUString& _rPropertyName ) const
+ {
+ return ( m_aDependencyHandlers.find( _rPropertyName ) != m_aDependencyHandlers.end() );
+ }
+
+ /** retrieves the value of the given property, by asking the appropriate XPropertyHandler
+ @param _rPropertyName
+ the name whose handler is to be obtained. Must be the name of a property
+ for which a handler is registered.
+ @throws
+ RuntimeException if there is no handler for the given property
+ @return
+ the value of this property
+ */
+ css::uno::Any
+ impl_getPropertyValue_throw( const OUString& _rPropertyName );
+
+ /// calls XPropertyHandler::suspend for all our property handlers
+ bool suspendPropertyHandlers_nothrow( bool _bSuspend );
+
+ /// suspends the complete inspector
+ bool suspendAll_nothrow();
+
+ /** selects a page according to our current view data
+ */
+ void selectPageFromViewData();
+
+ /** updates our view data from the currently active page
+ */
+ void updateViewDataFromActivePage();
+
+ /// describes the UI for the given property
+ void describePropertyLine( const css::beans::Property& _rPropertyName, OLineDescriptor& _rDescriptor );
+
+ /** retrieves the position of the property given by name in m_aProperties
+ @return
+ <TRUE/> if and only if the property could be found. In this case, <arg>_pProperty</arg> (if
+ not <NULL/> contains the iterator pointing to this property.
+ */
+ bool impl_findObjectProperty_nothrow( const OUString& _rName, OrderedPropertyMap::const_iterator* _pProperty = nullptr );
+
+ void Construct(const css::uno::Reference<css::awt::XWindow>& rContainerWindow, std::unique_ptr<weld::Builder> xBuilder);
+
+ /** retrieves the property handler for a given property name
+ @param _rPropertyName
+ the name whose handler is to be obtained. Must be the name of a property
+ for which a handler is registered.
+ @throws
+ RuntimeException if there is no handler for the given property
+ @return
+ the handler which is responsible for the given property
+ */
+ PropertyHandlerRef const &
+ impl_getHandlerForProperty_throw( const OUString& _rPropertyName ) const;
+
+ /** determines whether we have a handler for the given property
+ @param _rPropertyName
+ the name of the property for which the existence of a handler should be checked
+ */
+ bool
+ impl_hasPropertyHandlerFor_nothrow( const OUString& _rPropertyName ) const;
+
+ /** builds up m_aPageIds from InspectorModel::describeCategories, and insert all the
+ respective tab pages into our view
+ @precond
+ m_aPageIds is empty
+ @throws css::uno::RuntimeException
+ if one of the callees of this method throws this exception
+ */
+ void
+ impl_buildCategories_throw();
+
+ /** retrieves the id of the tab page which represents a given category.
+ @param _rCategoryName
+ the programmatic name of a category.
+ @return
+ the id of the tab page, or <code>(sal_uInt16)-1</code> if there
+ is no tab page for the given category
+ */
+ sal_uInt16
+ impl_getPageIdForCategory_nothrow( const OUString& _rCategoryName ) const;
+
+ /** adds or removes ourself as XEventListener to/from all our inspectees
+ */
+ void impl_toggleInspecteeListening_nothrow( bool _bOn );
+
+ /** binds the instance to a new model
+ */
+ void impl_bindToNewModel_nothrow( const css::uno::Reference< css::inspection::XObjectInspectorModel >& _rxInspectorModel );
+
+ /** initializes our view, as indicated by the model's view-relevant properties
+
+ It's allowed to call this method when no model exists, yet. In this case, nothing
+ happens.
+ */
+ void impl_initializeView_nothrow();
+
+ /** determines whether the view should be readonly.
+
+ Effectively, this means that the method simply checks the IsReadOnly attribute of the model.
+ If there is no model, <FALSE/> is returned.
+
+ @throws css::uno::RuntimeException
+ in case asking the model for its IsReadOnly attribute throws a css::uno::RuntimeException
+ itself.
+ */
+ bool impl_isReadOnlyModel_throw() const;
+
+ /** starts or stops listening at the model
+ */
+ void impl_startOrStopModelListening_nothrow( bool _bDoListen ) const;
+
+ private:
+ DECL_LINK(OnPageActivation, LinkParamNone*, void);
+
+ private:
+ // constructors
+ void createWithModel( const css::uno::Reference< css::inspection::XObjectInspectorModel >& _rxModel );
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propcontrolobserver.hxx b/extensions/source/propctrlr/propcontrolobserver.hxx
new file mode 100644
index 000000000..93e11053d
--- /dev/null
+++ b/extensions/source/propctrlr/propcontrolobserver.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <com/sun/star/inspection/XPropertyControl.hpp>
+
+
+namespace pcr
+{
+
+
+ //= IPropertyControlObserver
+
+ /** non-UNO version of the XPropertyControlObserver
+ */
+ class IPropertyControlObserver
+ {
+ public:
+ virtual void focusGained( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) = 0;
+ virtual void valueChanged( const css::uno::Reference< css::inspection::XPropertyControl >& Control ) = 0;
+
+ protected:
+ ~IPropertyControlObserver() {}
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propertycomposer.cxx b/extensions/source/propctrlr/propertycomposer.cxx
new file mode 100644
index 000000000..d77280d9f
--- /dev/null
+++ b/extensions/source/propctrlr/propertycomposer.cxx
@@ -0,0 +1,484 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "propertycomposer.hxx"
+
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <comphelper/sequence.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+#include <iterator>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::inspection;
+
+
+ //= helper
+
+ namespace
+ {
+
+ struct SetPropertyValue
+ {
+ OUString sPropertyName;
+ const Any& rValue;
+ SetPropertyValue( const OUString& _rPropertyName, const Any& _rValue ) : sPropertyName( _rPropertyName ), rValue( _rValue ) { }
+ void operator()( const Reference< XPropertyHandler >& _rHandler )
+ {
+ _rHandler->setPropertyValue( sPropertyName, rValue );
+ }
+ };
+
+
+ template < class BagType >
+ void putIntoBag( const Sequence< typename BagType::value_type >& _rArray, BagType& /* [out] */ _rBag )
+ {
+ std::copy( _rArray.begin(), _rArray.end(),
+ std::insert_iterator< BagType >( _rBag, _rBag.begin() ) );
+ }
+
+
+ template < class BagType >
+ void copyBagToArray( const BagType& /* [out] */ _rBag, Sequence< typename BagType::value_type >& _rArray )
+ {
+ _rArray.realloc( _rBag.size() );
+ std::copy( _rBag.begin(), _rBag.end(), _rArray.getArray() );
+ }
+ }
+
+
+ //= PropertyComposer
+
+
+ // TODO: there are various places where we determine the first handler in our array which
+ // supports a given property id. This is, at the moment, done with searching all handlers,
+ // which is O( n * k ) at worst (n being the number of handlers, k being the maximum number
+ // of supported properties per handler). Shouldn't we cache this? So that it is O( log k )?
+
+
+ PropertyComposer::PropertyComposer( std::vector< Reference< XPropertyHandler > >&& _rSlaveHandlers )
+ :PropertyComposer_Base ( m_aMutex )
+ ,m_aSlaveHandlers ( std::move(_rSlaveHandlers) )
+ ,m_aPropertyListeners ( m_aMutex )
+ ,m_bSupportedPropertiesAreKnown ( false )
+ {
+ if ( m_aSlaveHandlers.empty() )
+ throw IllegalArgumentException();
+
+ osl_atomic_increment( &m_refCount );
+ {
+ Reference< XPropertyChangeListener > xMeMyselfAndI( this );
+ for (auto const& slaveHandler : m_aSlaveHandlers)
+ {
+ if ( !slaveHandler.is() )
+ throw NullPointerException();
+ slaveHandler->addPropertyChangeListener( xMeMyselfAndI );
+ }
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ void SAL_CALL PropertyComposer::inspect( const Reference< XInterface >& _rxIntrospectee )
+ {
+ MethodGuard aGuard( *this );
+
+ for (auto const& slaveHandler : m_aSlaveHandlers)
+ {
+ slaveHandler->inspect( _rxIntrospectee );
+ }
+ }
+
+
+ Any SAL_CALL PropertyComposer::getPropertyValue( const OUString& _rPropertyName )
+ {
+ MethodGuard aGuard( *this );
+ return m_aSlaveHandlers[0]->getPropertyValue( _rPropertyName );
+ }
+
+
+ void SAL_CALL PropertyComposer::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ MethodGuard aGuard( *this );
+ std::for_each( m_aSlaveHandlers.begin(), m_aSlaveHandlers.end(), SetPropertyValue( _rPropertyName, _rValue ) );
+ }
+
+
+ Any SAL_CALL PropertyComposer::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue )
+ {
+ MethodGuard aGuard( *this );
+ return m_aSlaveHandlers[0]->convertToPropertyValue( _rPropertyName, _rControlValue );
+ }
+
+
+ Any SAL_CALL PropertyComposer::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType )
+ {
+ MethodGuard aGuard( *this );
+ return m_aSlaveHandlers[0]->convertToControlValue( _rPropertyName, _rPropertyValue, _rControlValueType );
+ }
+
+
+ PropertyState SAL_CALL PropertyComposer::getPropertyState( const OUString& _rPropertyName )
+ {
+ MethodGuard aGuard( *this );
+
+ // assume DIRECT for the moment. This will stay this way if *all* slaves
+ // tell the property has DIRECT state, and if *all* values equal
+ PropertyState eState = PropertyState_DIRECT_VALUE;
+
+ // check the master state
+ Reference< XPropertyHandler > xPrimary( *m_aSlaveHandlers.begin() );
+ Any aPrimaryValue = xPrimary->getPropertyValue( _rPropertyName );
+ eState = xPrimary->getPropertyState( _rPropertyName );
+
+ // loop through the secondary sets
+ PropertyState eSecondaryState = PropertyState_DIRECT_VALUE;
+ for ( HandlerArray::const_iterator loop = m_aSlaveHandlers.begin() + 1;
+ loop != m_aSlaveHandlers.end();
+ ++loop
+ )
+ {
+ // the secondary state
+ eSecondaryState = (*loop)->getPropertyState( _rPropertyName );
+
+ // the secondary value
+ Any aSecondaryValue( (*loop)->getPropertyValue( _rPropertyName ) );
+
+ if ( ( PropertyState_AMBIGUOUS_VALUE == eSecondaryState ) // secondary is ambiguous
+ || ( aPrimaryValue != aSecondaryValue ) // unequal values
+ )
+ {
+ eState = PropertyState_AMBIGUOUS_VALUE;
+ break;
+ }
+ }
+
+ return eState;
+ }
+
+
+ void SAL_CALL PropertyComposer::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ MethodGuard aGuard( *this );
+ m_aPropertyListeners.addInterface( _rxListener );
+ }
+
+
+ void SAL_CALL PropertyComposer::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ MethodGuard aGuard( *this );
+ m_aPropertyListeners.removeInterface( _rxListener );
+ }
+
+
+ Sequence< Property > SAL_CALL PropertyComposer::getSupportedProperties()
+ {
+ MethodGuard aGuard( *this );
+
+ if ( !m_bSupportedPropertiesAreKnown )
+ {
+ // we support a property if and only if all of our slaves support it
+
+ // initially, use all the properties of an arbitrary handler (we take the first one)
+ putIntoBag( (*m_aSlaveHandlers.begin())->getSupportedProperties(), m_aSupportedProperties );
+
+ // now intersect with the properties of *all* other handlers
+ for ( HandlerArray::const_iterator loop = m_aSlaveHandlers.begin() + 1;
+ loop != m_aSlaveHandlers.end();
+ ++loop
+ )
+ {
+ // the properties supported by the current handler
+ PropertyBag aThisRound;
+ putIntoBag( (*loop)->getSupportedProperties(), aThisRound );
+
+ // the intersection of those properties with all we already have
+ PropertyBag aIntersection;
+ std::set_intersection( aThisRound.begin(), aThisRound.end(), m_aSupportedProperties.begin(), m_aSupportedProperties.end(),
+ std::insert_iterator< PropertyBag >( aIntersection, aIntersection.begin() ), PropertyLessByName() );
+
+ m_aSupportedProperties.swap( aIntersection );
+ if ( m_aSupportedProperties.empty() )
+ break;
+ }
+
+ // remove those properties which are not composable
+ for ( PropertyBag::iterator check = m_aSupportedProperties.begin();
+ check != m_aSupportedProperties.end();
+ )
+ {
+ bool bIsComposable = isComposable( check->Name );
+ if ( !bIsComposable )
+ {
+ check = m_aSupportedProperties.erase( check );
+ }
+ else
+ ++check;
+ }
+
+ m_bSupportedPropertiesAreKnown = true;
+ }
+
+ return comphelper::containerToSequence( m_aSupportedProperties );
+ }
+
+
+ static void uniteStringArrays( const PropertyComposer::HandlerArray& _rHandlers, Sequence< OUString > (SAL_CALL XPropertyHandler::*pGetter)( ),
+ Sequence< OUString >& /* [out] */ _rUnion )
+ {
+ std::set< OUString > aUnitedBag;
+
+ Sequence< OUString > aThisRound;
+ for (auto const& handler : _rHandlers)
+ {
+ aThisRound = (handler.get()->*pGetter)();
+ putIntoBag( aThisRound, aUnitedBag );
+ }
+
+ copyBagToArray( aUnitedBag, _rUnion );
+ }
+
+
+ Sequence< OUString > SAL_CALL PropertyComposer::getSupersededProperties( )
+ {
+ MethodGuard aGuard( *this );
+
+ // we supersede those properties which are superseded by at least one of our slaves
+ Sequence< OUString > aSuperseded;
+ uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getSupersededProperties, aSuperseded );
+ return aSuperseded;
+ }
+
+
+ Sequence< OUString > SAL_CALL PropertyComposer::getActuatingProperties( )
+ {
+ MethodGuard aGuard( *this );
+
+ // we're interested in those properties which at least one handler wants to have
+ Sequence< OUString > aActuating;
+ uniteStringArrays( m_aSlaveHandlers, &XPropertyHandler::getActuatingProperties, aActuating );
+ return aActuating;
+ }
+
+
+ LineDescriptor SAL_CALL PropertyComposer::describePropertyLine( const OUString& _rPropertyName,
+ const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ MethodGuard aGuard( *this );
+ return m_aSlaveHandlers[0]->describePropertyLine( _rPropertyName, _rxControlFactory );
+ }
+
+
+ sal_Bool SAL_CALL PropertyComposer::isComposable( const OUString& _rPropertyName )
+ {
+ MethodGuard aGuard( *this );
+ return m_aSlaveHandlers[0]->isComposable( _rPropertyName );
+ }
+
+
+ InteractiveSelectionResult SAL_CALL PropertyComposer::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, Any& _rData, const Reference< XObjectInspectorUI >& _rxInspectorUI )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ MethodGuard aGuard( *this );
+
+ impl_ensureUIRequestComposer( _rxInspectorUI );
+ ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
+
+ // ask the first of the handlers
+ InteractiveSelectionResult eResult = (*m_aSlaveHandlers.begin())->onInteractivePropertySelection(
+ _rPropertyName,
+ _bPrimary,
+ _rData,
+ m_pUIRequestComposer->getUIForPropertyHandler( *m_aSlaveHandlers.begin() )
+ );
+
+ switch ( eResult )
+ {
+ case InteractiveSelectionResult_Cancelled:
+ // fine
+ break;
+
+ case InteractiveSelectionResult_Success:
+ case InteractiveSelectionResult_Pending:
+ OSL_FAIL( "PropertyComposer::onInteractivePropertySelection: no chance to forward the new value to the other handlers!" );
+ // This means that we cannot know the new property value, which either has already been set
+ // at the first component ("Success"), or will be set later on once the asynchronous input
+ // is finished ("Pending"). So, we also cannot forward this new property value to the other
+ // handlers.
+ // We would need to be a listener at the property at the first component, but even this wouldn't
+ // be sufficient, since the property handler is free to change *any* property during a dedicated
+ // property UI.
+ eResult = InteractiveSelectionResult_Cancelled;
+ break;
+
+ case InteractiveSelectionResult_ObtainedValue:
+ // OK. Our own caller will pass this as setPropertyValue, and we will then pass it to
+ // all slave handlers
+ break;
+
+ default:
+ OSL_FAIL( "OPropertyBrowserController::onInteractivePropertySelection: unknown result value!" );
+ break;
+ }
+
+ return eResult;
+ }
+
+
+ void PropertyComposer::impl_ensureUIRequestComposer( const Reference< XObjectInspectorUI >& _rxInspectorUI )
+ {
+ OSL_ENSURE(!m_pUIRequestComposer
+ || m_pUIRequestComposer->getDelegatorUI().get() == _rxInspectorUI.get(),
+ "PropertyComposer::impl_ensureUIRequestComposer: somebody's changing the horse "
+ "in the mid of the race!");
+
+ if (!m_pUIRequestComposer)
+ m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( _rxInspectorUI, this ) );
+ }
+
+
+ void SAL_CALL PropertyComposer::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& _rOldValue, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ MethodGuard aGuard( *this );
+
+ impl_ensureUIRequestComposer( _rxInspectorUI );
+ ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
+
+ // ask all handlers which expressed interest in this particular property, and "compose" their
+ // commands for the UIUpdater
+ for (auto const& slaveHandler : m_aSlaveHandlers)
+ {
+ // TODO: make this cheaper (cache it?)
+ const StlSyntaxSequence< OUString > aThisHandlersActuatingProps( slaveHandler->getActuatingProperties() );
+ for (const auto & aThisHandlersActuatingProp : aThisHandlersActuatingProps)
+ {
+ if ( aThisHandlersActuatingProp == _rActuatingPropertyName )
+ {
+ slaveHandler->actuatingPropertyChanged( _rActuatingPropertyName, _rNewValue, _rOldValue,
+ m_pUIRequestComposer->getUIForPropertyHandler(slaveHandler),
+ _bFirstTimeInit );
+ break;
+ }
+ }
+ }
+ }
+
+
+ IMPLEMENT_FORWARD_XCOMPONENT( PropertyComposer, PropertyComposer_Base )
+
+
+ void SAL_CALL PropertyComposer::disposing()
+ {
+ MethodGuard aGuard( *this );
+
+ // dispose our slave handlers
+ for (auto const& slaveHandler : m_aSlaveHandlers)
+ {
+ slaveHandler->removePropertyChangeListener( this );
+ slaveHandler->dispose();
+ }
+
+ clearContainer( m_aSlaveHandlers );
+
+ if (m_pUIRequestComposer)
+ m_pUIRequestComposer->dispose();
+ m_pUIRequestComposer.reset();
+ }
+
+
+ void SAL_CALL PropertyComposer::propertyChange( const PropertyChangeEvent& evt )
+ {
+ if ( !impl_isSupportedProperty_nothrow( evt.PropertyName ) )
+ // A slave handler might fire events for more properties than we support. Ignore those.
+ return;
+
+ PropertyChangeEvent aTranslatedEvent( evt );
+ try
+ {
+ aTranslatedEvent.NewValue = getPropertyValue( evt.PropertyName );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ m_aPropertyListeners.notifyEach( &XPropertyChangeListener::propertyChange, aTranslatedEvent );
+ }
+
+
+ void SAL_CALL PropertyComposer::disposing( const EventObject& Source )
+ {
+ MethodGuard aGuard( *this );
+ m_aPropertyListeners.disposeAndClear( Source );
+ }
+
+
+ sal_Bool SAL_CALL PropertyComposer::suspend( sal_Bool _bSuspend )
+ {
+ MethodGuard aGuard( *this );
+ for ( PropertyComposer::HandlerArray::const_iterator loop = m_aSlaveHandlers.begin();
+ loop != m_aSlaveHandlers.end();
+ ++loop
+ )
+ {
+ if ( !(*loop)->suspend( _bSuspend ) )
+ {
+ if ( _bSuspend && ( loop != m_aSlaveHandlers.begin() ) )
+ {
+ // if we tried to suspend, but one of the slave handlers vetoed,
+ // re-activate the handlers which actually did *not* veto
+ // the suspension
+ do
+ {
+ --loop;
+ (*loop)->suspend( false );
+ }
+ while ( loop != m_aSlaveHandlers.begin() );
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ bool PropertyComposer::hasPropertyByName( const OUString& _rName )
+ {
+ return impl_isSupportedProperty_nothrow( _rName );
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propertycomposer.hxx b/extensions/source/propctrlr/propertycomposer.hxx
new file mode 100644
index 000000000..5bcc58e23
--- /dev/null
+++ b/extensions/source/propctrlr/propertycomposer.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 "pcrcommon.hxx"
+#include "composeduiupdate.hxx"
+#include "formbrowsertools.hxx"
+
+#include <com/sun/star/inspection/XPropertyHandler.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+#include <memory>
+#include <vector>
+#include <set>
+
+
+namespace pcr
+{
+
+
+ //= PropertyComposer
+
+ typedef ::cppu::WeakComponentImplHelper < css::inspection::XPropertyHandler
+ , css::beans::XPropertyChangeListener
+ > PropertyComposer_Base;
+ /** implements an <type>XPropertyHandler</type> which composes its information
+ from a set of other property handlers
+ */
+ class PropertyComposer :public ::cppu::BaseMutex
+ ,public PropertyComposer_Base
+ ,public IPropertyExistenceCheck
+ {
+ public:
+ typedef std::vector< css::uno::Reference< css::inspection::XPropertyHandler > >
+ HandlerArray;
+
+ private:
+ HandlerArray m_aSlaveHandlers;
+ std::unique_ptr< ComposedPropertyUIUpdate > m_pUIRequestComposer;
+ PropertyChangeListeners m_aPropertyListeners;
+ bool m_bSupportedPropertiesAreKnown;
+ PropertyBag m_aSupportedProperties;
+
+ public:
+ /** constructs an <type>XPropertyHandler</type> which composes its information from a set
+ of other property handlers
+
+ @param _rSlaveHandlers
+ the set of slave handlers to invoke. Must not be <NULL/>
+ */
+ explicit PropertyComposer( std::vector< css::uno::Reference< css::inspection::XPropertyHandler > >&& _rSlaveHandlers );
+
+ public:
+ // XPropertyHandler overridables
+ virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override;
+ virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override;
+ virtual css::beans::PropertyState
+ SAL_CALL getPropertyState( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual css::uno::Sequence< css::beans::Property >
+ SAL_CALL getSupportedProperties() override;
+ virtual css::uno::Sequence< OUString >
+ SAL_CALL getSupersededProperties( ) override;
+ virtual css::uno::Sequence< OUString >
+ SAL_CALL getActuatingProperties( ) override;
+ virtual css::inspection::LineDescriptor
+ SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+ virtual sal_Bool SAL_CALL isComposable( const OUString& _rPropertyName ) override;
+ virtual css::inspection::InteractiveSelectionResult
+ SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override;
+ virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override;
+
+ // XComponent
+ DECLARE_XCOMPONENT()
+ virtual void SAL_CALL disposing() override;
+
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // IPropertyExistenceCheck
+ virtual bool hasPropertyByName( const OUString& _rName ) override;
+
+ private:
+ /** ensures that m_pUIRequestComposer exists
+ */
+ void impl_ensureUIRequestComposer( const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI );
+
+ /** checks whether a given property exists in <member>m_aSupportedProperties</member>
+ */
+ bool impl_isSupportedProperty_nothrow( const OUString& _rPropertyName )
+ {
+ css::beans::Property aDummy; aDummy.Name = _rPropertyName;
+ return m_aSupportedProperties.find( aDummy ) != m_aSupportedProperties.end();
+ }
+
+ private:
+ class MethodGuard;
+ friend class MethodGuard;
+ class MethodGuard : public ::osl::MutexGuard
+ {
+ public:
+ explicit MethodGuard( PropertyComposer& _rInstance )
+ : ::osl::MutexGuard( _rInstance.m_aMutex )
+ {
+ if ( _rInstance.m_aSlaveHandlers.empty() )
+ throw css::lang::DisposedException( OUString(), _rInstance );
+ }
+ };
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propertycontrolextender.cxx b/extensions/source/propctrlr/propertycontrolextender.cxx
new file mode 100644
index 000000000..142e288dd
--- /dev/null
+++ b/extensions/source/propctrlr/propertycontrolextender.cxx
@@ -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 .
+ */
+
+
+#include "propertycontrolextender.hxx"
+
+#include <com/sun/star/awt/KeyFunction.hpp>
+
+#include <tools/diagnose_ex.h>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::awt::XWindow;
+ using ::com::sun::star::awt::KeyEvent;
+ using ::com::sun::star::inspection::XPropertyControl;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::inspection::XPropertyControlContext;
+
+ namespace KeyFunction = ::com::sun::star::awt::KeyFunction;
+
+
+ //= PropertyControlExtender_Data
+
+ struct PropertyControlExtender_Data
+ {
+ Reference< XPropertyControl > xControl;
+ Reference< XWindow > xControlWindow;
+ };
+
+
+ //= PropertyControlExtender
+
+
+ PropertyControlExtender::PropertyControlExtender( const Reference< XPropertyControl >& _rxObservedControl )
+ :m_pData( new PropertyControlExtender_Data )
+ {
+ try
+ {
+ m_pData->xControl.set( _rxObservedControl, UNO_SET_THROW );
+ m_pData->xControlWindow.set( m_pData->xControl->getControlWindow(), UNO_SET_THROW );
+ m_pData->xControlWindow->addKeyListener( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ PropertyControlExtender::~PropertyControlExtender()
+ {
+ }
+
+
+ void SAL_CALL PropertyControlExtender::keyPressed( const KeyEvent& _event )
+ {
+ OSL_ENSURE( _event.Source == m_pData->xControlWindow, "PropertyControlExtender::keyPressed: where does this come from?" );
+ if ( ( _event.KeyFunc != KeyFunction::DELETE )
+ || ( _event.Modifiers != 0 )
+ )
+ return;
+
+ try
+ {
+ Reference< XPropertyControl > xControl( m_pData->xControl, UNO_SET_THROW );
+
+ // reset the value
+ xControl->setValue( Any() );
+
+ // and notify the change
+ // don't use XPropertyControl::notifyModifiedValue. It only notifies when the control content
+ // is recognized as being modified by the user, which is not the case, since we just modified
+ // it programmatically.
+ Reference< XPropertyControlContext > xControlContext( xControl->getControlContext(), UNO_SET_THROW );
+ xControlContext->valueChanged( xControl );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ void SAL_CALL PropertyControlExtender::keyReleased( const KeyEvent& /*_event*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL PropertyControlExtender::disposing( const EventObject& Source )
+ {
+ OSL_ENSURE( Source.Source == m_pData->xControlWindow, "PropertyControlExtender::disposing: where does this come from?" );
+ (void)Source.Source;
+ m_pData->xControlWindow.clear();
+ m_pData->xControl.clear();
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propertycontrolextender.hxx b/extensions/source/propctrlr/propertycontrolextender.hxx
new file mode 100644
index 000000000..ed7c4a06c
--- /dev/null
+++ b/extensions/source/propctrlr/propertycontrolextender.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 <com/sun/star/awt/XKeyListener.hpp>
+#include <com/sun/star/inspection/XPropertyControl.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <memory>
+
+
+namespace pcr
+{
+
+
+ //= PropertyControlExtender
+
+ struct PropertyControlExtender_Data;
+ typedef ::cppu::WeakImplHelper < css::awt::XKeyListener
+ > PropertyControlExtender_Base;
+ class PropertyControlExtender : public PropertyControlExtender_Base
+ {
+ public:
+ explicit PropertyControlExtender(
+ const css::uno::Reference< css::inspection::XPropertyControl >& _rxObservedControl
+ );
+
+ // XKeyListener
+ virtual void SAL_CALL keyPressed( const css::awt::KeyEvent& e ) override;
+ virtual void SAL_CALL keyReleased( const css::awt::KeyEvent& e ) override;
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ protected:
+ virtual ~PropertyControlExtender() override;
+
+ private:
+ std::unique_ptr< PropertyControlExtender_Data > m_pData;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propertyeditor.cxx b/extensions/source/propctrlr/propertyeditor.cxx
new file mode 100644
index 000000000..2fcf55649
--- /dev/null
+++ b/extensions/source/propctrlr/propertyeditor.cxx
@@ -0,0 +1,380 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "handlerhelper.hxx"
+#include "propertyeditor.hxx"
+#include "browserpage.hxx"
+#include "linedescriptor.hxx"
+
+#include <tools/debug.hxx>
+#include <osl/diagnose.h>
+
+namespace pcr
+{
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::inspection::XPropertyControl;
+ using ::com::sun::star::uno::Reference;
+
+ OPropertyEditor::OPropertyEditor(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Builder& rBuilder)
+ : m_xContainer(rBuilder.weld_container("box"))
+ , m_xTabControl(rBuilder.weld_notebook("tabcontrol"))
+ , m_xControlHoldingParent(rBuilder.weld_container("controlparent")) // controls initially have this parent before they are moved
+ , m_xContext(rContext)
+ , m_pListener(nullptr)
+ , m_pObserver(nullptr)
+ , m_nNextId(1)
+ , m_bHasHelpSection(false)
+ {
+ PropertyHandlerHelper::setBuilderParent(rContext, m_xControlHoldingParent.get());
+
+ m_xTabControl->connect_leave_page(LINK(this, OPropertyEditor, OnPageDeactivate));
+ m_xTabControl->connect_enter_page(LINK(this, OPropertyEditor, OnPageActivate));
+ }
+
+ OPropertyEditor::~OPropertyEditor()
+ {
+ PropertyHandlerHelper::clearBuilderParent(m_xContext);
+ ClearAll();
+ }
+
+ void OPropertyEditor::ClearAll()
+ {
+ m_nNextId=1;
+
+ m_aPropertyPageIds.clear();
+ m_aShownPages.clear();
+ m_aHiddenPages.clear();
+
+ int nCount = m_xTabControl->get_n_pages();
+ for (int i = nCount - 1; i >= 0; --i)
+ {
+ OString sID = m_xTabControl->get_page_ident(i);
+ m_xTabControl->remove_page(sID);
+ }
+
+ assert(m_xTabControl->get_n_pages() == 0);
+ }
+
+ Size OPropertyEditor::get_preferred_size() const
+ {
+ return m_xTabControl->get_preferred_size();
+ }
+
+ void OPropertyEditor::CommitModified()
+ {
+ // commit all of my pages, if necessary
+ for (const auto& page : m_aShownPages)
+ {
+ OBrowserPage* pPage = page.second.xPage.get();
+ if (pPage && pPage->getListBox().IsModified() )
+ pPage->getListBox().CommitModified();
+ }
+ }
+
+ OBrowserPage* OPropertyEditor::getPage(const OUString& rPropertyName)
+ {
+ OBrowserPage* pPage = nullptr;
+ MapStringToPageId::const_iterator aPropertyPageIdPos = m_aPropertyPageIds.find(rPropertyName);
+ if (aPropertyPageIdPos != m_aPropertyPageIds.end())
+ pPage = getPage(aPropertyPageIdPos->second);
+ return pPage;
+ }
+
+ const OBrowserPage* OPropertyEditor::getPage( const OUString& _rPropertyName ) const
+ {
+ return const_cast< OPropertyEditor* >( this )->getPage( _rPropertyName );
+ }
+
+ OBrowserPage* OPropertyEditor::getPage(sal_uInt16 rPageId)
+ {
+ OBrowserPage* pPage = nullptr;
+ auto aPagePos = m_aShownPages.find(rPageId);
+ if (aPagePos != m_aShownPages.end())
+ pPage = aPagePos->second.xPage.get();
+ return pPage;
+ }
+
+ const OBrowserPage* OPropertyEditor::getPage(sal_uInt16 rPageId) const
+ {
+ return const_cast<OPropertyEditor*>(this)->getPage(rPageId);
+ }
+
+ sal_uInt16 OPropertyEditor::AppendPage(const OUString& rText, const OString& rHelpId)
+ {
+ // obtain a new id
+ sal_uInt16 nId = m_nNextId++;
+ // insert the id
+ OString sIdent = OString::number(nId);
+ m_xTabControl->append_page(sIdent, rText);
+
+ // create a new page
+ auto xPage = std::make_unique<OBrowserPage>(m_xTabControl->get_page(sIdent), m_xControlHoldingParent.get());
+ // some knittings
+ xPage->getListBox().SetListener(m_pListener);
+ xPage->getListBox().SetObserver(m_pObserver);
+ xPage->getListBox().EnableHelpSection(m_bHasHelpSection);
+ xPage->SetHelpId(rHelpId);
+
+ m_aShownPages[nId] = PropertyPage(m_xTabControl->get_n_pages() - 1, rText, std::move(xPage));
+
+ // immediately activate the page
+ m_xTabControl->set_current_page(sIdent);
+
+ return nId;
+ }
+
+ void OPropertyEditor::SetHelpId( const OString& rHelpId )
+ {
+ m_xTabControl->set_help_id(rHelpId);
+ }
+
+ void OPropertyEditor::RemovePage(sal_uInt16 nID)
+ {
+ auto aPagePos = m_aShownPages.find(nID);
+ if (aPagePos == m_aShownPages.end())
+ return;
+
+ m_aShownPages.erase(aPagePos);
+ OString sIdent(OString::number(nID));
+ m_xTabControl->remove_page(sIdent);
+ }
+
+ void OPropertyEditor::SetPage(sal_uInt16 nId)
+ {
+ m_xTabControl->set_current_page(OString::number(nId));
+ }
+
+ sal_uInt16 OPropertyEditor::GetCurPage() const
+ {
+ return m_xTabControl->get_current_page_ident().toUInt32();
+ }
+
+ void OPropertyEditor::forEachPage( PageOperation _pOperation )
+ {
+ int nCount = m_xTabControl->get_n_pages();
+ for (int i = 0; i < nCount; ++i)
+ {
+ sal_uInt16 nID = m_xTabControl->get_page_ident(i).toUInt32();
+ OBrowserPage* pPage = getPage(nID);
+ if (!pPage)
+ continue;
+ (this->*_pOperation)( *pPage, nullptr );
+ }
+ }
+
+ void OPropertyEditor::setPageLineListener( OBrowserPage& rPage, const void* )
+ {
+ rPage.getListBox().SetListener( m_pListener );
+ }
+
+ void OPropertyEditor::SetLineListener(IPropertyLineListener* pListener)
+ {
+ m_pListener = pListener;
+ forEachPage( &OPropertyEditor::setPageLineListener );
+ }
+
+ void OPropertyEditor::setPageControlObserver( OBrowserPage& rPage, const void* )
+ {
+ rPage.getListBox().SetObserver( m_pObserver );
+ }
+
+ void OPropertyEditor::SetControlObserver( IPropertyControlObserver* _pObserver )
+ {
+ m_pObserver = _pObserver;
+ forEachPage( &OPropertyEditor::setPageControlObserver );
+ }
+
+ void OPropertyEditor::EnableHelpSection( bool bEnable )
+ {
+ m_bHasHelpSection = bEnable;
+ forEachPage( &OPropertyEditor::enableHelpSection );
+ }
+
+ void OPropertyEditor::SetHelpText( const OUString& rHelpText )
+ {
+ int nCount = m_xTabControl->get_n_pages();
+ for (int i = 0; i < nCount; ++i)
+ {
+ sal_uInt16 nID = m_xTabControl->get_page_ident(i).toUInt32();
+ OBrowserPage* pPage = getPage(nID);
+ if (!pPage)
+ continue;
+ setHelpSectionText( *pPage, &rHelpText );
+ }
+ }
+
+ void OPropertyEditor::enableHelpSection( OBrowserPage& rPage, const void* )
+ {
+ rPage.getListBox().EnableHelpSection( m_bHasHelpSection );
+ }
+
+ void OPropertyEditor::setHelpSectionText( OBrowserPage& rPage, const void* pPointerToOUString )
+ {
+ OSL_ENSURE( pPointerToOUString, "OPropertyEditor::setHelpSectionText: invalid argument!" );
+ if ( !pPointerToOUString )
+ return;
+
+ const OUString& rText( *static_cast<const OUString*>(pPointerToOUString) );
+ rPage.getListBox().SetHelpText( rText );
+ }
+
+ void OPropertyEditor::InsertEntry( const OLineDescriptor& rData, sal_uInt16 nPageId, sal_uInt16 nPos )
+ {
+ // let the current page handle this
+ OBrowserPage* pPage = getPage(nPageId);
+ DBG_ASSERT( pPage, "OPropertyEditor::InsertEntry: don't have such a page!" );
+ if ( !pPage )
+ return;
+
+ pPage->getListBox().InsertEntry( rData, nPos );
+
+ OSL_ENSURE( m_aPropertyPageIds.find( rData.sName ) == m_aPropertyPageIds.end(),
+ "OPropertyEditor::InsertEntry: property already present in the map!" );
+ m_aPropertyPageIds.emplace( rData.sName, nPageId );
+ }
+
+ void OPropertyEditor::RemoveEntry( const OUString& rName )
+ {
+ OBrowserPage* pPage = getPage( rName );
+ if ( pPage )
+ {
+ OSL_VERIFY( pPage->getListBox().RemoveEntry( rName ) );
+
+ OSL_ENSURE( m_aPropertyPageIds.find( rName ) != m_aPropertyPageIds.end(),
+ "OPropertyEditor::RemoveEntry: property not present in the map!" );
+ m_aPropertyPageIds.erase( rName );
+ }
+ }
+
+ void OPropertyEditor::ChangeEntry( const OLineDescriptor& rData )
+ {
+ OBrowserPage* pPage = getPage( rData.sName );
+ if ( pPage )
+ pPage->getListBox().ChangeEntry( rData, EDITOR_LIST_REPLACE_EXISTING );
+ }
+
+ void OPropertyEditor::SetPropertyValue( const OUString& rEntryName, const Any& _rValue, bool _bUnknownValue )
+ {
+ OBrowserPage* pPage = getPage( rEntryName );
+ if ( pPage )
+ pPage->getListBox().SetPropertyValue( rEntryName, _rValue, _bUnknownValue );
+ }
+
+ sal_uInt16 OPropertyEditor::GetPropertyPos( const OUString& rEntryName ) const
+ {
+ sal_uInt16 nVal=EDITOR_LIST_ENTRY_NOTFOUND;
+ const OBrowserPage* pPage = getPage( rEntryName );
+ if ( pPage )
+ nVal = pPage->getListBox().GetPropertyPos( rEntryName );
+ return nVal;
+ }
+
+ void OPropertyEditor::ShowPropertyPage(sal_uInt16 nPageId, bool bShow)
+ {
+ assert((m_aHiddenPages.find(nPageId) != m_aHiddenPages.end() ||
+ m_aShownPages.find(nPageId) != m_aShownPages.end()) && "page doesn't exist");
+ OString sIdent(OString::number(nPageId));
+ if (!bShow)
+ {
+ auto aPagePos = m_aShownPages.find(nPageId);
+ if (aPagePos != m_aShownPages.end())
+ {
+ aPagePos->second.xPage->detach();
+ m_xTabControl->remove_page(sIdent);
+
+ m_aHiddenPages[nPageId] = std::move(aPagePos->second);
+ m_aShownPages.erase(aPagePos);
+ }
+ }
+ else
+ {
+ auto aPagePos = m_aHiddenPages.find(nPageId);
+ if (aPagePos != m_aHiddenPages.end())
+ {
+ m_xTabControl->insert_page(sIdent, aPagePos->second.sLabel, aPagePos->second.nPos);
+ aPagePos->second.xPage->reattach(m_xTabControl->get_page(sIdent));
+
+ m_aShownPages[nPageId] = std::move(aPagePos->second);
+ m_aHiddenPages.erase(aPagePos);
+ }
+ }
+ }
+
+ void OPropertyEditor::EnablePropertyControls( const OUString& rEntryName, sal_Int16 nControls, bool bEnable )
+ {
+ for (const auto& rPage : m_aShownPages)
+ {
+ OBrowserPage* pPage = rPage.second.xPage.get();
+ if (pPage)
+ pPage->getListBox().EnablePropertyControls( rEntryName, nControls, bEnable );
+ }
+ }
+
+ void OPropertyEditor::EnablePropertyLine( const OUString& rEntryName, bool bEnable )
+ {
+ for (const auto& rPage : m_aShownPages)
+ {
+ OBrowserPage* pPage = rPage.second.xPage.get();
+ if (pPage)
+ pPage->getListBox().EnablePropertyLine( rEntryName, bEnable );
+ }
+ }
+
+ Reference< XPropertyControl > OPropertyEditor::GetPropertyControl(const OUString& rEntryName)
+ {
+ Reference< XPropertyControl > xControl;
+ // let the current page handle this
+ OBrowserPage* pPage = getPage(m_xTabControl->get_current_page_ident().toUInt32());
+ if (pPage)
+ xControl = pPage->getListBox().GetPropertyControl(rEntryName);
+ return xControl;
+ }
+
+ IMPL_LINK(OPropertyEditor, OnPageActivate, const OString&, rNewPage, void)
+ {
+ m_aPageActivationHandler.Call(rNewPage);
+ }
+
+ IMPL_LINK(OPropertyEditor, OnPageDeactivate, const OString&, rIdent, bool)
+ {
+ // commit the data on the current (to-be-deactivated) tab page
+ // (79404)
+ OBrowserPage* pCurrentPage = getPage(rIdent.toUInt32());
+ if (!pCurrentPage)
+ return true;
+
+ if (pCurrentPage->getListBox().IsModified())
+ pCurrentPage->getListBox().CommitModified();
+
+ return true;
+ }
+
+ OPropertyEditor::PropertyPage::PropertyPage()
+ : nPos(0)
+ {
+ }
+
+ OPropertyEditor::PropertyPage::PropertyPage(sal_uInt16 nPagePos, const OUString& rLabel, std::unique_ptr<OBrowserPage> pPage)
+ : nPos(nPagePos), sLabel(rLabel), xPage(std::move(pPage))
+ {
+ }
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propertyeditor.hxx b/extensions/source/propctrlr/propertyeditor.hxx
new file mode 100644
index 000000000..1f28510ab
--- /dev/null
+++ b/extensions/source/propctrlr/propertyeditor.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "browserpage.hxx"
+#include "pcrcommon.hxx"
+
+#include <com/sun/star/inspection/XPropertyControl.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <vcl/weld.hxx>
+#include <map>
+
+namespace pcr
+{
+ class IPropertyLineListener;
+ class IPropertyControlObserver;
+ class OBrowserPage;
+ struct OLineDescriptor;
+ class OBrowserListBox;
+
+ //= OPropertyEditor
+ class OPropertyEditor final
+ {
+ private:
+ typedef std::map< OUString, sal_uInt16 > MapStringToPageId;
+ struct PropertyPage
+ {
+ sal_uInt16 nPos;
+ OUString sLabel;
+ std::unique_ptr<OBrowserPage> xPage;
+ PropertyPage();
+ PropertyPage(sal_uInt16 nPagePos, const OUString& rLabel, std::unique_ptr<OBrowserPage> pPage);
+ };
+
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<weld::Notebook> m_xTabControl;
+ // controls initially have this parent before they are moved
+ std::unique_ptr<weld::Container> m_xControlHoldingParent;
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ IPropertyLineListener* m_pListener;
+ IPropertyControlObserver* m_pObserver;
+ sal_uInt16 m_nNextId;
+ Link<const OString&,void> m_aPageActivationHandler;
+ bool m_bHasHelpSection;
+
+ MapStringToPageId m_aPropertyPageIds;
+ std::map<sal_uInt16, PropertyPage> m_aShownPages;
+ std::map<sal_uInt16, PropertyPage> m_aHiddenPages;
+
+ public:
+ explicit OPropertyEditor(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Builder& rBuilder);
+ ~OPropertyEditor();
+
+ void SetLineListener( IPropertyLineListener* );
+ void SetControlObserver( IPropertyControlObserver* );
+
+ void EnableHelpSection( bool _bEnable );
+ bool HasHelpSection() const { return m_bHasHelpSection; }
+ void SetHelpText( const OUString& _rHelpText );
+
+ void SetHelpId( const OString& sHelpId );
+ sal_uInt16 AppendPage( const OUString& r, const OString& _rHelpId );
+ void SetPage( sal_uInt16 );
+ void RemovePage(sal_uInt16 nID);
+ sal_uInt16 GetCurPage() const;
+ void ClearAll();
+
+ void SetPropertyValue(const OUString& _rEntryName, const css::uno::Any& _rValue, bool _bUnknownValue );
+ sal_uInt16 GetPropertyPos(const OUString& rEntryName ) const;
+ css::uno::Reference< css::inspection::XPropertyControl >
+ GetPropertyControl( const OUString& rEntryName );
+ void EnablePropertyLine( const OUString& _rEntryName, bool _bEnable );
+ void EnablePropertyControls( const OUString& _rEntryName, sal_Int16 _nControls, bool _bEnable );
+
+ void ShowPropertyPage( sal_uInt16 _nPageId, bool _bShow );
+
+ void InsertEntry( const OLineDescriptor&, sal_uInt16 _nPageId, sal_uInt16 nPos = EDITOR_LIST_APPEND );
+ void RemoveEntry( const OUString& _rName );
+ void ChangeEntry( const OLineDescriptor& );
+
+ void setPageActivationHandler(const Link<const OString&,void>& _rHdl) { m_aPageActivationHandler = _rHdl; }
+
+ Size get_preferred_size() const;
+
+ weld::Widget* getWidget() const { return m_xTabControl.get(); }
+
+ void Show() { m_xTabControl->show(); }
+ void Hide() { m_xTabControl->hide(); }
+ void GrabFocus() { m_xTabControl->grab_focus(); }
+
+ void CommitModified();
+
+ private:
+ OBrowserPage* getPage( sal_uInt16 _rPageId );
+ const OBrowserPage* getPage( sal_uInt16 _rPageId ) const;
+
+ OBrowserPage* getPage( const OUString& _rPropertyName );
+ const OBrowserPage* getPage( const OUString& _rPropertyName ) const;
+
+ typedef void (OPropertyEditor::*PageOperation)( OBrowserPage&, const void* );
+ void forEachPage( PageOperation _pOperation );
+
+ void setPageLineListener( OBrowserPage& _rPage, const void* );
+ void setPageControlObserver( OBrowserPage& _rPage, const void* );
+ void enableHelpSection( OBrowserPage& _rPage, const void* );
+ static void setHelpSectionText( OBrowserPage& _rPage, const void* _pPointerToOUString );
+
+ DECL_LINK(OnPageDeactivate, const OString&, bool);
+ DECL_LINK(OnPageActivate, const OString&, void);
+ };
+
+
+} // namespace pcr
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propertyhandler.cxx b/extensions/source/propctrlr/propertyhandler.cxx
new file mode 100644
index 000000000..53cf29fc6
--- /dev/null
+++ b/extensions/source/propctrlr/propertyhandler.cxx
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "propertyhandler.hxx"
+#include "formmetadata.hxx"
+#include "formbrowsertools.hxx"
+#include "handlerhelper.hxx"
+#include "formstrings.hxx"
+
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/script/Converter.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/debug.hxx>
+#include <unotools/confignode.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <rtl/ref.hxx>
+
+#include <algorithm>
+
+namespace pcr
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::inspection;
+ using namespace ::comphelper;
+
+
+ PropertyHandler::PropertyHandler( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandler_Base( m_aMutex )
+ ,m_bSupportedPropertiesAreKnown( false )
+ ,m_aPropertyListeners( m_aMutex )
+ ,m_xContext( _rxContext )
+ ,m_pInfoService ( new OPropertyInfoService )
+ {
+
+ m_xTypeConverter = Converter::create(_rxContext);
+ }
+
+ PropertyHandler::~PropertyHandler()
+ {
+ }
+
+ void SAL_CALL PropertyHandler::inspect( const Reference< XInterface >& _rxIntrospectee )
+ {
+ if ( !_rxIntrospectee.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XPropertySet > xNewComponent( _rxIntrospectee, UNO_QUERY );
+ if ( xNewComponent == m_xComponent )
+ return;
+
+ // remove all old property change listeners
+ ::comphelper::OInterfaceIteratorHelper3 removeListener(m_aPropertyListeners);
+ ::comphelper::OInterfaceIteratorHelper3 readdListener(m_aPropertyListeners); // will copy the container as needed
+ while ( removeListener.hasMoreElements() )
+ removePropertyChangeListener( removeListener.next() );
+ OSL_ENSURE( m_aPropertyListeners.getLength() == 0, "PropertyHandler::inspect: derived classes are expected to forward the removePropertyChangeListener call to their base class (me)!" );
+
+ // remember the new component, and give derived classes the chance to react on it
+ m_xComponent = xNewComponent;
+ onNewComponent();
+
+ // add the listeners, again
+ while ( readdListener.hasMoreElements() )
+ addPropertyChangeListener( readdListener.next() );
+ }
+
+ void PropertyHandler::onNewComponent()
+ {
+ if ( m_xComponent.is() )
+ m_xComponentPropertyInfo = m_xComponent->getPropertySetInfo();
+ else
+ m_xComponentPropertyInfo.clear();
+
+ m_bSupportedPropertiesAreKnown = false;
+ m_aSupportedProperties.realloc( 0 );
+ }
+
+ Sequence< Property > SAL_CALL PropertyHandler::getSupportedProperties()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_bSupportedPropertiesAreKnown )
+ {
+ m_aSupportedProperties = StlSyntaxSequence<css::beans::Property>(doDescribeSupportedProperties());
+ m_bSupportedPropertiesAreKnown = true;
+ }
+ return m_aSupportedProperties;
+ }
+
+ Sequence< OUString > SAL_CALL PropertyHandler::getSupersededProperties( )
+ {
+ return Sequence< OUString >();
+ }
+
+ Sequence< OUString > SAL_CALL PropertyHandler::getActuatingProperties( )
+ {
+ return Sequence< OUString >();
+ }
+
+ Any SAL_CALL PropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId = m_pInfoService->getPropertyId( _rPropertyName );
+ Property aProperty( impl_getPropertyFromName_throw( _rPropertyName ) );
+
+ Any aPropertyValue;
+ if ( !_rControlValue.hasValue() )
+ // NULL is converted to NULL
+ return aPropertyValue;
+
+ if ( ( m_pInfoService->getPropertyUIFlags( nPropId ) & PROP_FLAG_ENUM ) != 0 )
+ {
+ OUString sControlValue;
+ OSL_VERIFY( _rControlValue >>= sControlValue );
+ ::rtl::Reference< IPropertyEnumRepresentation > aEnumConversion(
+ new DefaultEnumRepresentation( *m_pInfoService, aProperty.Type, nPropId ) );
+ // TODO/UNOize: cache those converters?
+ aEnumConversion->getValueFromDescription( sControlValue, aPropertyValue );
+ }
+ else
+ aPropertyValue = PropertyHandlerHelper::convertToPropertyValue(
+ m_xContext, m_xTypeConverter, aProperty, _rControlValue );
+ return aPropertyValue;
+ }
+
+ Any SAL_CALL PropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId = m_pInfoService->getPropertyId( _rPropertyName );
+
+ if ( ( m_pInfoService->getPropertyUIFlags( nPropId ) & PROP_FLAG_ENUM ) != 0 )
+ {
+ DBG_ASSERT( _rControlValueType.getTypeClass() == TypeClass_STRING, "PropertyHandler::convertToControlValue: ENUM, but not STRING?" );
+
+ ::rtl::Reference< IPropertyEnumRepresentation > aEnumConversion(
+ new DefaultEnumRepresentation( *m_pInfoService, _rPropertyValue.getValueType(), nPropId ) );
+ // TODO/UNOize: cache those converters?
+ return Any( aEnumConversion->getDescriptionForValue( _rPropertyValue ) );
+ }
+
+ return PropertyHandlerHelper::convertToControlValue(
+ m_xContext, m_xTypeConverter, _rPropertyValue, _rControlValueType );
+ }
+
+ PropertyState SAL_CALL PropertyHandler::getPropertyState( const OUString& /*_rPropertyName*/ )
+ {
+ return PropertyState_DIRECT_VALUE;
+ }
+
+ LineDescriptor SAL_CALL PropertyHandler::describePropertyLine( const OUString& _rPropertyName,
+ const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ if ( !_rxControlFactory.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+ const Property& rProperty( impl_getPropertyFromId_throw( nPropId ) );
+
+ LineDescriptor aDescriptor;
+ if ( ( m_pInfoService->getPropertyUIFlags( nPropId ) & PROP_FLAG_ENUM ) != 0 )
+ {
+ aDescriptor.Control = PropertyHandlerHelper::createListBoxControl(
+ _rxControlFactory, m_pInfoService->getPropertyEnumRepresentations( nPropId ),
+ PropertyHandlerHelper::requiresReadOnlyControl( rProperty.Attributes ), false );
+ }
+ else
+ PropertyHandlerHelper::describePropertyLine( rProperty, aDescriptor, _rxControlFactory );
+
+ aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) );
+ aDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( nPropId );
+
+ if ( ( m_pInfoService->getPropertyUIFlags( nPropId ) & PROP_FLAG_DATA_PROPERTY ) != 0 )
+ aDescriptor.Category = "Data";
+ else
+ aDescriptor.Category = "General";
+ return aDescriptor;
+ }
+
+ sal_Bool SAL_CALL PropertyHandler::isComposable( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_pInfoService->isComposeable( _rPropertyName );
+ }
+
+ InteractiveSelectionResult SAL_CALL PropertyHandler::onInteractivePropertySelection( const OUString& /*_rPropertyName*/, sal_Bool /*_bPrimary*/, Any& /*_rData*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/ )
+ {
+ OSL_FAIL( "PropertyHandler::onInteractivePropertySelection: not implemented!" );
+ return InteractiveSelectionResult_Cancelled;
+ }
+
+ void SAL_CALL PropertyHandler::actuatingPropertyChanged( const OUString& /*_rActuatingPropertyName*/, const Any& /*_rNewValue*/, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/, sal_Bool /*_bFirstTimeInit*/ )
+ {
+ OSL_FAIL( "PropertyHandler::actuatingPropertyChanged: not implemented!" );
+ }
+
+ void SAL_CALL PropertyHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !_rxListener.is() )
+ throw NullPointerException();
+ m_aPropertyListeners.addInterface( _rxListener );
+ }
+
+ void SAL_CALL PropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_aPropertyListeners.removeInterface( _rxListener );
+ }
+
+ sal_Bool SAL_CALL PropertyHandler::suspend( sal_Bool /*_bSuspend*/ )
+ {
+ return true;
+ }
+
+ IMPLEMENT_FORWARD_XCOMPONENT( PropertyHandler, PropertyHandler_Base )
+
+ void SAL_CALL PropertyHandler::disposing()
+ {
+ m_xComponent.clear();
+ m_aPropertyListeners.clear();
+ m_xTypeConverter.clear();
+ m_aSupportedProperties.realloc( 0 );
+ }
+
+ void PropertyHandler::firePropertyChange( const OUString& _rPropName, PropertyId _nPropId, const Any& _rOldValue, const Any& _rNewValue )
+ {
+ PropertyChangeEvent aEvent;
+ aEvent.Source = m_xComponent;
+ aEvent.PropertyHandle = _nPropId;
+ aEvent.PropertyName = _rPropName;
+ aEvent.OldValue = _rOldValue;
+ aEvent.NewValue = _rNewValue;
+ m_aPropertyListeners.notifyEach( &XPropertyChangeListener::propertyChange, aEvent );
+ }
+
+ const Property* PropertyHandler::impl_getPropertyFromId_nothrow( PropertyId _nPropId ) const
+ {
+ const_cast< PropertyHandler* >( this )->getSupportedProperties();
+ const Property* pFound = std::find_if( m_aSupportedProperties.begin(), m_aSupportedProperties.end(),
+ FindPropertyByHandle( _nPropId )
+ );
+ if ( pFound != m_aSupportedProperties.end() )
+ return pFound;
+ return nullptr;
+ }
+
+ const Property& PropertyHandler::impl_getPropertyFromId_throw( PropertyId _nPropId ) const
+ {
+ const Property* pProperty = impl_getPropertyFromId_nothrow( _nPropId );
+ if ( !pProperty )
+ throw UnknownPropertyException();
+
+ return *pProperty;
+ }
+
+ const Property& PropertyHandler::impl_getPropertyFromName_throw( const OUString& _rPropertyName ) const
+ {
+ const_cast< PropertyHandler* >( this )->getSupportedProperties();
+ StlSyntaxSequence< Property >::const_iterator pFound = std::find_if( m_aSupportedProperties.begin(), m_aSupportedProperties.end(),
+ FindPropertyByName( _rPropertyName )
+ );
+ if ( pFound == m_aSupportedProperties.end() )
+ throw UnknownPropertyException(_rPropertyName);
+
+ return *pFound;
+ }
+
+ void PropertyHandler::implAddPropertyDescription( std::vector< Property >& _rProperties, const OUString& _rPropertyName, const Type& _rType, sal_Int16 _nAttribs ) const
+ {
+ _rProperties.push_back( Property(
+ _rPropertyName,
+ m_pInfoService->getPropertyId( _rPropertyName ),
+ _rType,
+ _nAttribs
+ ) );
+ }
+
+ weld::Window* PropertyHandler::impl_getDefaultDialogFrame_nothrow() const
+ {
+ return PropertyHandlerHelper::getDialogParentFrame(m_xContext);
+ }
+
+ PropertyId PropertyHandler::impl_getPropertyId_throwUnknownProperty( const OUString& _rPropertyName ) const
+ {
+ PropertyId nPropId = m_pInfoService->getPropertyId( _rPropertyName );
+ if ( nPropId == -1 )
+ throw UnknownPropertyException(_rPropertyName);
+ return nPropId;
+ }
+
+ PropertyId PropertyHandler::impl_getPropertyId_throwRuntime( const OUString& _rPropertyName ) const
+ {
+ PropertyId nPropId = m_pInfoService->getPropertyId( _rPropertyName );
+ if ( nPropId == -1 )
+ throw RuntimeException();
+ return nPropId;
+ }
+
+ PropertyId PropertyHandler::impl_getPropertyId_nothrow( const OUString& _rPropertyName ) const
+ {
+ return m_pInfoService->getPropertyId( _rPropertyName );
+ }
+
+ void PropertyHandler::impl_setContextDocumentModified_nothrow() const
+ {
+ Reference< XModifiable > xModifiable( impl_getContextDocument_nothrow(), UNO_QUERY );
+ if ( xModifiable.is() )
+ xModifiable->setModified( true );
+ }
+
+ bool PropertyHandler::impl_componentHasProperty_throw( const OUString& _rPropName ) const
+ {
+ return m_xComponentPropertyInfo.is() && m_xComponentPropertyInfo->hasPropertyByName( _rPropName );
+ }
+
+ sal_Int16 PropertyHandler::impl_getDocumentMeasurementUnit_throw() const
+ {
+ FieldUnit eUnit = FieldUnit::NONE;
+
+ Reference< XServiceInfo > xDocumentSI( impl_getContextDocument_nothrow(), UNO_QUERY );
+ OSL_ENSURE( xDocumentSI.is(), "PropertyHandlerHelper::impl_getDocumentMeasurementUnit_throw: No context document - where do I live?" );
+ if ( xDocumentSI.is() )
+ {
+ // determine the application type we live in
+ OUString sConfigurationLocation;
+ OUString sConfigurationProperty;
+ if ( xDocumentSI->supportsService( SERVICE_WEB_DOCUMENT ) )
+ { // writer
+ sConfigurationLocation = "/org.openoffice.Office.WriterWeb/Layout/Other";
+ sConfigurationProperty = "MeasureUnit";
+ }
+ else if ( xDocumentSI->supportsService( SERVICE_TEXT_DOCUMENT ) )
+ { // writer
+ sConfigurationLocation = "/org.openoffice.Office.Writer/Layout/Other";
+ sConfigurationProperty = "MeasureUnit";
+ }
+ else if ( xDocumentSI->supportsService( SERVICE_SPREADSHEET_DOCUMENT ) )
+ { // calc
+ sConfigurationLocation = "/org.openoffice.Office.Calc/Layout/Other/MeasureUnit";
+ sConfigurationProperty = "Metric";
+ }
+ else if ( xDocumentSI->supportsService( SERVICE_DRAWING_DOCUMENT ) )
+ {
+ sConfigurationLocation = "/org.openoffice.Office.Draw/Layout/Other/MeasureUnit";
+ sConfigurationProperty = "Metric";
+ }
+ else if ( xDocumentSI->supportsService( SERVICE_PRESENTATION_DOCUMENT ) )
+ {
+ sConfigurationLocation = "/org.openoffice.Office.Impress/Layout/Other/MeasureUnit";
+ sConfigurationProperty = "Metric";
+ }
+
+ // read the measurement unit from the configuration
+ if ( !(sConfigurationLocation.isEmpty() || sConfigurationProperty.isEmpty()) )
+ {
+ ::utl::OConfigurationTreeRoot aConfigTree( ::utl::OConfigurationTreeRoot::createWithComponentContext(
+ m_xContext, sConfigurationLocation, -1, ::utl::OConfigurationTreeRoot::CM_READONLY ) );
+ sal_Int32 nUnitAsInt = sal_Int32(FieldUnit::NONE);
+ aConfigTree.getNodeValue( sConfigurationProperty ) >>= nUnitAsInt;
+
+ // if this denotes a valid (and accepted) unit, then use it
+ if ( ( nUnitAsInt > sal_Int32(FieldUnit::NONE) ) && ( nUnitAsInt <= sal_Int32(FieldUnit::MM_100TH) ) )
+ eUnit = static_cast< FieldUnit >( nUnitAsInt );
+ }
+ }
+
+ if ( FieldUnit::NONE == eUnit )
+ {
+ MeasurementSystem eSystem = SvtSysLocale().GetLocaleData().getMeasurementSystemEnum();
+ eUnit = MeasurementSystem::Metric == eSystem ? FieldUnit::CM : FieldUnit::INCH;
+ }
+
+ return VCLUnoHelper::ConvertToMeasurementUnit( eUnit, 1 );
+ }
+
+ PropertyHandlerComponent::PropertyHandlerComponent( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandler( _rxContext )
+ {
+ }
+
+ IMPLEMENT_FORWARD_XINTERFACE2( PropertyHandlerComponent, PropertyHandler, PropertyHandlerComponent_Base )
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( PropertyHandlerComponent, PropertyHandler, PropertyHandlerComponent_Base )
+
+ sal_Bool SAL_CALL PropertyHandlerComponent::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propertyhandler.hxx b/extensions/source/propctrlr/propertyhandler.hxx
new file mode 100644
index 000000000..3491a89be
--- /dev/null
+++ b/extensions/source/propctrlr/propertyhandler.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "pcrcommon.hxx"
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/PropertyState.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/script/XTypeConverter.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/inspection/XPropertyHandler.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <comphelper/uno3.hxx>
+
+#include <memory>
+#include <vector>
+
+namespace com::sun::star {
+ namespace inspection {
+ struct LineDescriptor;
+ class XPropertyControlFactory;
+ }
+}
+
+namespace weld { class Window; }
+
+namespace pcr
+{
+
+
+ typedef sal_Int32 PropertyId;
+
+
+ //= PropertyHandler
+
+ class OPropertyInfoService;
+ typedef ::cppu::WeakComponentImplHelper < css::inspection::XPropertyHandler
+ > PropertyHandler_Base;
+ /** the base class for property handlers
+ */
+ class PropertyHandler : public ::cppu::BaseMutex
+ , public PropertyHandler_Base
+ {
+ private:
+ /// cache for getSupportedProperties
+ mutable StlSyntaxSequence< css::beans::Property >
+ m_aSupportedProperties;
+ mutable bool m_bSupportedPropertiesAreKnown;
+
+ private:
+ /// the property listener which has been registered
+ PropertyChangeListeners m_aPropertyListeners;
+
+ protected:
+ /// the context in which the instance was created
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ /// the component we're inspecting
+ css::uno::Reference< css::beans::XPropertySet > m_xComponent;
+ /// info about our component's properties
+ css::uno::Reference< css::beans::XPropertySetInfo > m_xComponentPropertyInfo;
+ /// type converter, needed on various occasions
+ css::uno::Reference< css::script::XTypeConverter > m_xTypeConverter;
+ /// access to property meta data
+ std::unique_ptr< OPropertyInfoService > m_pInfoService;
+
+ protected:
+ explicit PropertyHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+ virtual ~PropertyHandler() override;
+
+ // default implementations for XPropertyHandler
+ virtual void SAL_CALL inspect( const css::uno::Reference< css::uno::XInterface >& _rxIntrospectee ) override;
+ virtual css::uno::Sequence< css::beans::Property > SAL_CALL getSupportedProperties() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupersededProperties( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getActuatingProperties( ) override;
+ virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override;
+ virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override;
+ virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& _rPropertyName ) override;
+ virtual css::inspection::LineDescriptor SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+ virtual sal_Bool SAL_CALL isComposable( const OUString& _rPropertyName ) override;
+ virtual css::inspection::InteractiveSelectionResult SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) override;
+
+ // XComponent
+ DECLARE_XCOMPONENT()
+ virtual void SAL_CALL disposing() override;
+
+ // own overridables
+ virtual css::uno::Sequence< css::beans::Property >
+ doDescribeSupportedProperties() const = 0;
+
+ /// called when XPropertyHandler::inspect has been called, and we thus have a new component to inspect
+ virtual void onNewComponent();
+
+ protected:
+ /** fires the change in a property value to our listener (if any)
+ @see addPropertyChangeListener
+ */
+ void firePropertyChange( const OUString& _rPropName, PropertyId _nPropId,
+ const css::uno::Any& _rOldValue, const css::uno::Any& _rNewValue );
+
+ /** retrieves a window which can be used as parent for dialogs
+ */
+ weld::Window* impl_getDefaultDialogFrame_nothrow() const;
+
+ /** retrieves the property id for a given property name
+ @throw css::beans::UnknownPropertyException
+ if the property name is not known to our ->m_pInfoService
+ */
+ PropertyId impl_getPropertyId_throwUnknownProperty( const OUString& _rPropertyName ) const;
+
+ /** retrieves the property id for a given property name
+ @throw css::uno::RuntimeException
+ if the property name is not known to our ->m_pInfoService
+ */
+ PropertyId impl_getPropertyId_throwRuntime( const OUString& _rPropertyName ) const;
+
+
+ /** retrieves the property id for a given property name
+ @returns -1
+ if the property name is not known to our ->m_pInfoService
+ */
+ PropertyId impl_getPropertyId_nothrow( const OUString& _rPropertyName ) const;
+
+ // helper for implementing doDescribeSupportedProperties
+ /** adds a description for the given string property to the given property vector
+ Most probably to be called from within getSupportedProperties
+ */
+ inline void addStringPropertyDescription(
+ std::vector< css::beans::Property >& _rProperties,
+ const OUString& _rPropertyName
+ ) const;
+
+ /** adds a description for the given int32 property to the given property vector
+ */
+ inline void addInt32PropertyDescription(
+ std::vector< css::beans::Property >& _rProperties,
+ const OUString& _rPropertyName,
+ sal_Int16 _nAttribs = 0
+ ) const;
+
+ /** adds a description for the given int16 property to the given property vector
+ */
+ inline void addInt16PropertyDescription(
+ std::vector< css::beans::Property >& _rProperties,
+ const OUString& _rPropertyName,
+ sal_Int16 _nAttribs = 0
+ ) const;
+
+ /** adds a description for the given double property to the given property vector
+ */
+ inline void addDoublePropertyDescription(
+ std::vector< css::beans::Property >& _rProperties,
+ const OUString& _rPropertyName,
+ sal_Int16 _nAttribs
+ ) const;
+
+ /** adds a description for the given date property to the given property vector
+ */
+ inline void addDatePropertyDescription(
+ std::vector< css::beans::Property >& _rProperties,
+ const OUString& _rPropertyName,
+ sal_Int16 _nAttribs
+ ) const;
+
+ /** adds a description for the given time property to the given property vector
+ */
+ inline void addTimePropertyDescription(
+ std::vector< css::beans::Property >& _rProperties,
+ const OUString& _rPropertyName,
+ sal_Int16 _nAttribs
+ ) const;
+
+ /** adds a description for the given DateTime property to the given property vector
+ */
+ inline void addDateTimePropertyDescription(
+ std::vector< css::beans::Property >& _rProperties,
+ const OUString& _rPropertyName,
+ sal_Int16 _nAttribs
+ ) const;
+
+ /// adds a Property, given by name only, to a given vector of Properties
+ void implAddPropertyDescription(
+ std::vector< css::beans::Property >& _rProperties,
+ const OUString& _rPropertyName,
+ const css::uno::Type& _rType,
+ sal_Int16 _nAttribs = 0
+ ) const;
+
+
+ // helper for accessing and maintaining meta data about our supported properties
+
+ /** retrieves a property given by handle
+
+ @return
+ a pointer to the descriptor for the given properties, if it is one of our
+ supported properties, <NULL/> else.
+
+ @see doDescribeSupportedProperties
+ @see impl_getPropertyFromId_throw
+ */
+ const css::beans::Property*
+ impl_getPropertyFromId_nothrow( PropertyId _nPropId ) const;
+
+ /** retrieves a property given by handle
+
+ @throws UnknownPropertyException
+ if the handler does not support a property with the given handle
+
+ @seealso doDescribeSupportedProperties
+ @see impl_getPropertyFromId_nothrow
+ */
+ const css::beans::Property&
+ impl_getPropertyFromId_throw( PropertyId _nPropId ) const;
+
+ /** determines whether a given property id is part of our supported properties
+ @see getSupportedProperties
+ @see doDescribeSupportedProperties
+ */
+ bool impl_isSupportedProperty_nothrow( PropertyId _nPropId ) const
+ {
+ return impl_getPropertyFromId_nothrow( _nPropId ) != nullptr;
+ }
+
+ /** retrieves a property given by name
+
+ @throws UnknownPropertyException
+ if the handler does not support a property with the given name
+
+ @seealso doDescribeSupportedProperties
+ */
+ const css::beans::Property&
+ impl_getPropertyFromName_throw( const OUString& _rPropertyName ) const;
+
+ /** get the name of a property given by handle
+ */
+ inline OUString
+ impl_getPropertyNameFromId_nothrow( PropertyId _nPropId ) const;
+
+ /** returns the value of the ContextDocument property in the ComponentContext which was used to create
+ this handler.
+ */
+ css::uno::Reference< css::frame::XModel >
+ impl_getContextDocument_nothrow() const
+ {
+ return css::uno::Reference< css::frame::XModel >(
+ m_xContext->getValueByName( "ContextDocument" ), css::uno::UNO_QUERY );
+ }
+
+ /** marks the context document as modified
+
+ @see impl_getContextDocument_nothrow
+ */
+ void impl_setContextDocumentModified_nothrow() const;
+
+ /// determines whether our component has a given property
+ bool impl_componentHasProperty_throw( const OUString& _rPropName ) const;
+
+ /** determines the default measure unit for the document in which our component lives
+ */
+ sal_Int16 impl_getDocumentMeasurementUnit_throw() const;
+
+ private:
+ PropertyHandler( const PropertyHandler& ) = delete;
+ PropertyHandler& operator=( const PropertyHandler& ) = delete;
+ };
+
+
+ inline void PropertyHandler::addStringPropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName ) const
+ {
+ implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<OUString>::get() );
+ }
+
+ inline void PropertyHandler::addInt32PropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const
+ {
+ implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<sal_Int32>::get(), _nAttribs );
+ }
+
+ inline void PropertyHandler::addInt16PropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const
+ {
+ implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<sal_Int16>::get(), _nAttribs );
+ }
+
+ inline void PropertyHandler::addDoublePropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const
+ {
+ implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<double>::get(), _nAttribs );
+ }
+
+ inline void PropertyHandler::addDatePropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const
+ {
+ implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<css::util::Date>::get(), _nAttribs );
+ }
+
+ inline void PropertyHandler::addTimePropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const
+ {
+ implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<css::util::Time>::get(), _nAttribs );
+ }
+
+ inline void PropertyHandler::addDateTimePropertyDescription( std::vector< css::beans::Property >& _rProperties, const OUString& _rPropertyName, sal_Int16 _nAttribs ) const
+ {
+ implAddPropertyDescription( _rProperties, _rPropertyName, ::cppu::UnoType<css::util::DateTime>::get(), _nAttribs );
+ }
+
+ inline OUString PropertyHandler::impl_getPropertyNameFromId_nothrow( PropertyId _nPropId ) const
+ {
+ const css::beans::Property* pProp = impl_getPropertyFromId_nothrow( _nPropId );
+ return pProp ? pProp->Name : OUString();
+ }
+
+
+ //= PropertyHandlerComponent
+
+ typedef ::cppu::ImplHelper1 < css::lang::XServiceInfo
+ > PropertyHandlerComponent_Base;
+ /** PropertyHandler implementation which additionally supports XServiceInfo
+ */
+ class PropertyHandlerComponent :public PropertyHandler
+ ,public PropertyHandlerComponent_Base
+ {
+ protected:
+ explicit PropertyHandlerComponent(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ DECLARE_XINTERFACE()
+ DECLARE_XTYPEPROVIDER()
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override = 0;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) final override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override = 0;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propertyinfo.hxx b/extensions/source/propctrlr/propertyinfo.hxx
new file mode 100644
index 000000000..592557c2a
--- /dev/null
+++ b/extensions/source/propctrlr/propertyinfo.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+#include <vector>
+
+
+namespace pcr
+{
+
+
+ //= IPropertyInfoService
+
+ class SAL_NO_VTABLE IPropertyInfoService
+ {
+ public:
+ virtual sal_Int32 getPropertyId(const OUString& _rName) const = 0;
+ virtual OUString getPropertyTranslation(sal_Int32 _nId) const = 0;
+ virtual OUString getPropertyHelpId(sal_Int32 _nId) const = 0;
+ virtual sal_Int16 getPropertyPos(sal_Int32 _nId) const = 0;
+ virtual sal_uInt32 getPropertyUIFlags(sal_Int32 _nId) const = 0;
+ virtual std::vector< OUString > getPropertyEnumRepresentations(sal_Int32 _nId) const = 0;
+
+ virtual ~IPropertyInfoService() { }
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propeventtranslation.cxx b/extensions/source/propctrlr/propeventtranslation.cxx
new file mode 100644
index 000000000..736c1e06f
--- /dev/null
+++ b/extensions/source/propctrlr/propeventtranslation.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 "propeventtranslation.hxx"
+
+#include <com/sun/star/lang/DisposedException.hpp>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::beans::XPropertyChangeListener;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::lang::DisposedException;
+
+
+ //= PropertyEventTranslation
+
+
+ PropertyEventTranslation::PropertyEventTranslation( const Reference< XPropertyChangeListener >& _rxDelegator,
+ const Reference< XInterface >& _rxTranslatedEventSource )
+ :m_xDelegator( _rxDelegator )
+ ,m_xTranslatedEventSource( _rxTranslatedEventSource )
+ {
+ if ( !m_xDelegator.is() )
+ throw RuntimeException();
+ }
+
+
+ void SAL_CALL PropertyEventTranslation::propertyChange( const PropertyChangeEvent& evt )
+ {
+ if ( !m_xDelegator.is() )
+ throw DisposedException();
+
+ if ( !m_xTranslatedEventSource.is() )
+ m_xDelegator->propertyChange( evt );
+ else
+ {
+ PropertyChangeEvent aTranslatedEvent( evt );
+ aTranslatedEvent.Source = m_xTranslatedEventSource;
+ m_xDelegator->propertyChange( aTranslatedEvent );
+ }
+ }
+
+
+ void SAL_CALL PropertyEventTranslation::disposing( const EventObject& Source )
+ {
+ if ( !m_xDelegator.is() )
+ throw DisposedException();
+
+ if ( !m_xTranslatedEventSource.is() )
+ m_xDelegator->disposing( Source );
+ else
+ {
+ EventObject aTranslatedEvent( Source );
+ aTranslatedEvent.Source = m_xTranslatedEventSource;
+ m_xDelegator->disposing( aTranslatedEvent );
+ }
+
+ m_xDelegator.clear();
+ m_xTranslatedEventSource.clear();
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/propeventtranslation.hxx b/extensions/source/propctrlr/propeventtranslation.hxx
new file mode 100644
index 000000000..43155b8c6
--- /dev/null
+++ b/extensions/source/propctrlr/propeventtranslation.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <cppuhelper/implbase.hxx>
+
+
+namespace pcr
+{
+
+
+ //= PropertyEventTranslation
+
+ typedef ::cppu::WeakImplHelper < css::beans::XPropertyChangeListener
+ > PropertyEventTranslation_Base;
+
+ class PropertyEventTranslation : public PropertyEventTranslation_Base
+ {
+ css::uno::Reference< css::beans::XPropertyChangeListener >
+ m_xDelegator;
+ css::uno::Reference< css::uno::XInterface >
+ m_xTranslatedEventSource;
+
+ public:
+ /** constructs the object
+ @throws NullPointerException
+ if <arg>_rxDelegator</arg> is <NULL/>
+ */
+ PropertyEventTranslation(
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxDelegator,
+ const css::uno::Reference< css::uno::XInterface >& _rxTranslatedEventSource
+ );
+
+ const css::uno::Reference< css::beans::XPropertyChangeListener >&
+ getDelegator() const { return m_xDelegator; }
+
+ protected:
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override;
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ private:
+ PropertyEventTranslation( const PropertyEventTranslation& ) = delete;
+ PropertyEventTranslation& operator=( const PropertyEventTranslation& ) = delete;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/proplinelistener.hxx b/extensions/source/propctrlr/proplinelistener.hxx
new file mode 100644
index 000000000..6931a1f33
--- /dev/null
+++ b/extensions/source/propctrlr/proplinelistener.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+namespace pcr
+{
+
+
+ class IPropertyLineListener
+ {
+ public:
+ virtual void Clicked( const OUString& _rName, bool _bPrimary ) = 0;
+ virtual void Commit( const OUString& _rName, const css::uno::Any& _rVal ) = 0;
+
+ protected:
+ ~IPropertyLineListener() {}
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/pushbuttonnavigation.cxx b/extensions/source/propctrlr/pushbuttonnavigation.cxx
new file mode 100644
index 000000000..449ffd583
--- /dev/null
+++ b/extensions/source/propctrlr/pushbuttonnavigation.cxx
@@ -0,0 +1,298 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "pushbuttonnavigation.hxx"
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include "formstrings.hxx"
+#include <comphelper/extract.hxx>
+#include <comphelper/property.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+
+
+ namespace
+ {
+ const sal_Int32 s_nFirstVirtualButtonType = 1 + sal_Int32(FormButtonType_URL);
+
+ const char* pNavigationURLs[] =
+ {
+ ".uno:FormController/moveToFirst",
+ ".uno:FormController/moveToPrev",
+ ".uno:FormController/moveToNext",
+ ".uno:FormController/moveToLast",
+ ".uno:FormController/saveRecord",
+ ".uno:FormController/undoRecord",
+ ".uno:FormController/moveToNew",
+ ".uno:FormController/deleteRecord",
+ ".uno:FormController/refreshForm",
+ nullptr
+ };
+
+ sal_Int32 lcl_getNavigationURLIndex( std::u16string_view _rNavURL )
+ {
+ const char** pLookup = pNavigationURLs;
+ while ( *pLookup )
+ {
+ if ( o3tl::equalsAscii( _rNavURL, *pLookup ) )
+ return pLookup - pNavigationURLs;
+ ++pLookup;
+ }
+ return -1;
+ }
+
+ const char* lcl_getNavigationURL( sal_Int32 _nButtonTypeIndex )
+ {
+ const char** pLookup = pNavigationURLs;
+ while ( _nButtonTypeIndex-- && *pLookup++ )
+ ;
+ OSL_ENSURE( *pLookup, "lcl_getNavigationURL: invalid index!" );
+ return *pLookup;
+ }
+ }
+
+
+ //= PushButtonNavigation
+
+
+ PushButtonNavigation::PushButtonNavigation( const Reference< XPropertySet >& _rxControlModel )
+ :m_xControlModel( _rxControlModel )
+ ,m_bIsPushButton( false )
+ {
+ OSL_ENSURE( m_xControlModel.is(), "PushButtonNavigation::PushButtonNavigation: invalid control model!" );
+
+ try
+ {
+ m_bIsPushButton = ::comphelper::hasProperty( PROPERTY_BUTTONTYPE, m_xControlModel );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::PushButtonNavigation" );
+ }
+ }
+
+
+ FormButtonType PushButtonNavigation::implGetCurrentButtonType() const
+ {
+ sal_Int32 nButtonType = sal_Int32(FormButtonType_PUSH);
+ if ( !m_xControlModel.is() )
+ return static_cast<FormButtonType>(nButtonType);
+ OSL_VERIFY( ::cppu::enum2int( nButtonType, m_xControlModel->getPropertyValue( PROPERTY_BUTTONTYPE ) ) );
+
+ if ( nButtonType == sal_Int32(FormButtonType_URL) )
+ {
+ // there's a chance that this is a "virtual" button type
+ // (which are realized by special URLs)
+ OUString sTargetURL;
+ m_xControlModel->getPropertyValue( PROPERTY_TARGET_URL ) >>= sTargetURL;
+
+ sal_Int32 nNavigationURLIndex = lcl_getNavigationURLIndex( sTargetURL );
+ if ( nNavigationURLIndex >= 0)
+ // it actually *is* a virtual button type
+ nButtonType = s_nFirstVirtualButtonType + nNavigationURLIndex;
+ }
+ return static_cast<FormButtonType>(nButtonType);
+ }
+
+
+ Any PushButtonNavigation::getCurrentButtonType() const
+ {
+ OSL_ENSURE( m_bIsPushButton, "PushButtonNavigation::getCurrentButtonType: not expected to be called for forms!" );
+ Any aReturn;
+
+ try
+ {
+ aReturn <<= implGetCurrentButtonType();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::getCurrentButtonType" );
+ }
+ return aReturn;
+ }
+
+
+ void PushButtonNavigation::setCurrentButtonType( const Any& _rValue ) const
+ {
+ OSL_ENSURE( m_bIsPushButton, "PushButtonNavigation::setCurrentButtonType: not expected to be called for forms!" );
+ if ( !m_xControlModel.is() )
+ return;
+
+ try
+ {
+ sal_Int32 nButtonType = sal_Int32(FormButtonType_PUSH);
+ OSL_VERIFY( ::cppu::enum2int( nButtonType, _rValue ) );
+ OUString sTargetURL;
+
+ bool bIsVirtualButtonType = nButtonType >= s_nFirstVirtualButtonType;
+ if ( bIsVirtualButtonType )
+ {
+ const char* pURL = lcl_getNavigationURL( nButtonType - s_nFirstVirtualButtonType );
+ sTargetURL = OUString::createFromAscii( pURL );
+
+ nButtonType = sal_Int32(FormButtonType_URL);
+ }
+
+ m_xControlModel->setPropertyValue( PROPERTY_BUTTONTYPE, Any( static_cast< FormButtonType >( nButtonType ) ) );
+ m_xControlModel->setPropertyValue( PROPERTY_TARGET_URL, Any( sTargetURL ) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::setCurrentButtonType" );
+ }
+ }
+
+
+ PropertyState PushButtonNavigation::getCurrentButtonTypeState( ) const
+ {
+ OSL_ENSURE( m_bIsPushButton, "PushButtonNavigation::getCurrentButtonTypeState: not expected to be called for forms!" );
+ PropertyState eState = PropertyState_DIRECT_VALUE;
+
+ try
+ {
+ Reference< XPropertyState > xStateAccess( m_xControlModel, UNO_QUERY );
+ if ( xStateAccess.is() )
+ {
+ // let's see what the model says about the ButtonType property
+ eState = xStateAccess->getPropertyState( PROPERTY_BUTTONTYPE );
+ if ( eState == PropertyState_DIRECT_VALUE )
+ {
+ sal_Int32 nRealButtonType = sal_Int32(FormButtonType_PUSH);
+ OSL_VERIFY( ::cppu::enum2int( nRealButtonType, m_xControlModel->getPropertyValue( PROPERTY_BUTTONTYPE ) ) );
+ // perhaps it's one of the virtual button types?
+ if ( sal_Int32(FormButtonType_URL) == nRealButtonType )
+ {
+ // yes, it is -> rely on the state of the URL property
+ eState = xStateAccess->getPropertyState( PROPERTY_TARGET_URL );
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::getCurrentButtonTypeState" );
+ }
+
+ return eState;
+ }
+
+
+ Any PushButtonNavigation::getCurrentTargetURL() const
+ {
+ Any aReturn;
+ if ( !m_xControlModel.is() )
+ return aReturn;
+
+ try
+ {
+ aReturn = m_xControlModel->getPropertyValue( PROPERTY_TARGET_URL );
+ if ( m_bIsPushButton )
+ {
+ FormButtonType nCurrentButtonType = implGetCurrentButtonType();
+ bool bIsVirtualButtonType = nCurrentButtonType >= FormButtonType(s_nFirstVirtualButtonType);
+ if ( bIsVirtualButtonType )
+ {
+ // pretend (to the user) that there's no URL set - since
+ // virtual button types imply a special (technical) URL which
+ // the user should not see
+ aReturn <<= OUString();
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::getCurrentTargetURL" );
+ }
+ return aReturn;
+ }
+
+
+ void PushButtonNavigation::setCurrentTargetURL( const Any& _rValue ) const
+ {
+ if ( !m_xControlModel.is() )
+ return;
+
+ try
+ {
+ m_xControlModel->setPropertyValue( PROPERTY_TARGET_URL, _rValue );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::setCurrentTargetURL" );
+ }
+ }
+
+
+ PropertyState PushButtonNavigation::getCurrentTargetURLState( ) const
+ {
+ PropertyState eState = PropertyState_DIRECT_VALUE;
+
+ try
+ {
+ Reference< XPropertyState > xStateAccess( m_xControlModel, UNO_QUERY );
+ if ( xStateAccess.is() )
+ {
+ eState = xStateAccess->getPropertyState( PROPERTY_TARGET_URL );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "PushButtonNavigation::setCurrentTargetURL" );
+ }
+
+ return eState;
+ }
+
+
+ bool PushButtonNavigation::currentButtonTypeIsOpenURL() const
+ {
+ FormButtonType nButtonType( FormButtonType_PUSH );
+ try
+ {
+ nButtonType = implGetCurrentButtonType();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return nButtonType == FormButtonType_URL;
+ }
+
+
+ bool PushButtonNavigation::hasNonEmptyCurrentTargetURL() const
+ {
+ OUString sTargetURL;
+ OSL_VERIFY( getCurrentTargetURL() >>= sTargetURL );
+ return !sTargetURL.isEmpty();
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/pushbuttonnavigation.hxx b/extensions/source/propctrlr/pushbuttonnavigation.hxx
new file mode 100644
index 000000000..7248fb27d
--- /dev/null
+++ b/extensions/source/propctrlr/pushbuttonnavigation.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyState.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+
+
+namespace pcr
+{
+
+
+ //= PushButtonNavigation
+
+ class PushButtonNavigation final
+ {
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xControlModel;
+ bool m_bIsPushButton;
+
+ public:
+ /** ctor
+ @param _rxControlModel
+ the control model which is or will be bound
+ */
+ explicit PushButtonNavigation(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel
+ );
+
+ /** returns the current value of the "ButtonType" property, taking into account
+ the "virtual" button types such as "move-to-next-record button".
+ */
+ css::uno::Any
+ getCurrentButtonType() const;
+
+ /** sets the current value of the "ButtonType" property, taking into account
+ the "virtual" button types such as "move-to-next-record button".
+ */
+ void setCurrentButtonType( const css::uno::Any& _rValue ) const;
+
+ /** retrieves the current state of the "ButtonType" property, taking into account
+ the "virtual" button types such as "move-to-next-record button".
+ */
+ css::beans::PropertyState
+ getCurrentButtonTypeState( ) const;
+
+ /** returns the current value of the "TargetURL" property, taking into account
+ that some URLs are special values caused by "virtual" ButtonTypes
+ */
+ css::uno::Any
+ getCurrentTargetURL() const;
+
+ /** sets the current value of the "TargetURL" property, taking into account
+ that some URLs are special values caused by "virtual" ButtonTypes
+ */
+ void setCurrentTargetURL( const css::uno::Any& _rValue ) const;
+
+ /** retrieves the current state of the "TargetURL" property, taking into account
+ that some URLs are special values caused by "virtual" ButtonTypes
+ */
+ css::beans::PropertyState
+ getCurrentTargetURLState( ) const;
+
+ /** determines whether the current button type is FormButtonType_URL
+ */
+ bool currentButtonTypeIsOpenURL() const;
+
+ /** determines whether the TargetURL property does currently denote a non-empty string
+ */
+ bool hasNonEmptyCurrentTargetURL() const;
+
+ private:
+ css::form::FormButtonType implGetCurrentButtonType() const;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/selectlabeldialog.cxx b/extensions/source/propctrlr/selectlabeldialog.cxx
new file mode 100644
index 000000000..c8c0a82a3
--- /dev/null
+++ b/extensions/source/propctrlr/selectlabeldialog.cxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "selectlabeldialog.hxx"
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include "formbrowsertools.hxx"
+#include "formstrings.hxx"
+#include "modulepcr.hxx"
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::lang;
+
+
+ // OSelectLabelDialog
+ OSelectLabelDialog::OSelectLabelDialog(weld::Window* pParent, Reference< XPropertySet > const & _xControlModel)
+ : GenericDialogController(pParent, "modules/spropctrlr/ui/labelselectiondialog.ui", "LabelSelectionDialog")
+ , m_xControlModel(_xControlModel)
+ , m_bLastSelected(false)
+ , m_bHaveAssignableControl(false)
+ , m_xMainDesc(m_xBuilder->weld_label("label"))
+ , m_xControlTree(m_xBuilder->weld_tree_view("control"))
+ , m_xScratchIter(m_xControlTree->make_iterator())
+ , m_xNoAssignment(m_xBuilder->weld_check_button("noassignment"))
+ {
+ m_xControlTree->connect_changed(LINK(this, OSelectLabelDialog, OnEntrySelected));
+ m_xControlTree->set_size_request(-1, m_xControlTree->get_height_rows(8));
+
+ // fill the description
+ OUString sDescription = m_xMainDesc->get_label();
+ sal_Int16 nClassID = FormComponentType::CONTROL;
+ if (::comphelper::hasProperty(PROPERTY_CLASSID, m_xControlModel))
+ nClassID = ::comphelper::getINT16(m_xControlModel->getPropertyValue(PROPERTY_CLASSID));
+
+ sDescription = sDescription.replaceAll("$controlclass$",
+ GetUIHeadlineName(nClassID, Any(m_xControlModel)));
+ OUString sName = ::comphelper::getString(m_xControlModel->getPropertyValue(PROPERTY_NAME));
+ sDescription = sDescription.replaceAll("$controlname$", sName);
+ m_xMainDesc->set_label(sDescription);
+
+ // search for the root of the form hierarchy
+ Reference< XChild > xCont(m_xControlModel, UNO_QUERY);
+ Reference< XInterface > xSearch( xCont.is() ? xCont->getParent() : Reference< XInterface > ());
+ Reference< XResultSet > xParentAsResultSet(xSearch, UNO_QUERY);
+ while (xParentAsResultSet.is())
+ {
+ xCont.set(xSearch, UNO_QUERY);
+ xSearch = xCont.is() ? xCont->getParent() : Reference< XInterface > ();
+ xParentAsResultSet.set(xSearch, UNO_QUERY);
+ }
+
+ // and insert all entries below this root into the listbox
+ if (xSearch.is())
+ {
+ // check which service the allowed components must support
+ sal_Int16 nClassId = 0;
+ try { nClassId = ::comphelper::getINT16(m_xControlModel->getPropertyValue(PROPERTY_CLASSID)); } catch(...) { }
+ m_sRequiredService = (FormComponentType::RADIOBUTTON == nClassId) ? OUString(SERVICE_COMPONENT_GROUPBOX) : OUString(SERVICE_COMPONENT_FIXEDTEXT);
+ m_aRequiredControlImage = (FormComponentType::RADIOBUTTON == nClassId) ? OUString(RID_EXTBMP_GROUPBOX) : OUString(RID_EXTBMP_FIXEDTEXT);
+
+ // calc the currently set label control (so InsertEntries can calc m_xInitialSelection)
+ Any aCurrentLabelControl( m_xControlModel->getPropertyValue(PROPERTY_CONTROLLABEL) );
+ DBG_ASSERT((aCurrentLabelControl.getValueTypeClass() == TypeClass_INTERFACE) || !aCurrentLabelControl.hasValue(),
+
+ "OSelectLabelDialog::OSelectLabelDialog : invalid ControlLabel property !");
+ if (aCurrentLabelControl.hasValue())
+ aCurrentLabelControl >>= m_xInitialLabelControl;
+
+ // insert the root
+ OUString sRootName(PcrRes(RID_STR_FORMS));
+ m_xControlTree->insert(nullptr, -1, &sRootName, nullptr,
+ nullptr, nullptr, false, m_xScratchIter.get());
+ m_xControlTree->set_image(*m_xScratchIter, RID_EXTBMP_FORMS);
+
+ // build the tree
+ m_xInitialSelection.reset();
+ m_bHaveAssignableControl = false;
+ std::unique_ptr<weld::TreeIter> xRoot = m_xControlTree->make_iterator();
+ m_xControlTree->get_iter_first(*xRoot);
+ InsertEntries(xSearch, *xRoot);
+ m_xControlTree->expand_row(*xRoot);
+ }
+
+ if (m_xInitialSelection)
+ {
+ m_xControlTree->scroll_to_row(*m_xInitialSelection);
+ m_xControlTree->select(*m_xInitialSelection);
+ }
+ else
+ {
+ m_xControlTree->scroll_to_row(0);
+ m_xControlTree->unselect_all();
+ m_xNoAssignment->set_active(true);
+ }
+
+ if (!m_bHaveAssignableControl)
+ { // no controls which can be assigned
+ m_xNoAssignment->set_active(true);
+ m_xNoAssignment->set_sensitive(false);
+ }
+
+ m_xLastSelected = m_xControlTree->make_iterator(nullptr);
+
+ m_xNoAssignment->connect_toggled(LINK(this, OSelectLabelDialog, OnNoAssignmentClicked));
+ OnNoAssignmentClicked(*m_xNoAssignment);
+ }
+
+ OSelectLabelDialog::~OSelectLabelDialog()
+ {
+ }
+
+ sal_Int32 OSelectLabelDialog::InsertEntries(const Reference< XInterface > & _xContainer, const weld::TreeIter& rContainerEntry)
+ {
+ Reference< XIndexAccess > xContainer(_xContainer, UNO_QUERY);
+ if (!xContainer.is())
+ return 0;
+
+ sal_Int32 nChildren = 0;
+ OUString sName;
+ Reference< XPropertySet > xAsSet;
+ for (sal_Int32 i=0; i<xContainer->getCount(); ++i)
+ {
+ xContainer->getByIndex(i) >>= xAsSet;
+ if (!xAsSet.is())
+ {
+ SAL_INFO("extensions.propctrlr", "OSelectLabelDialog::InsertEntries : strange : a form component which isn't a property set !");
+ continue;
+ }
+
+ if (!::comphelper::hasProperty(PROPERTY_NAME, xAsSet))
+ // we need at least a name for displaying ...
+ continue;
+ sName = ::comphelper::getString(xAsSet->getPropertyValue(PROPERTY_NAME));
+
+ // we need to check if the control model supports the required service
+ Reference< XServiceInfo > xInfo(xAsSet, UNO_QUERY);
+ if (!xInfo.is())
+ continue;
+
+ if (!xInfo->supportsService(m_sRequiredService))
+ { // perhaps it is a container
+ Reference< XIndexAccess > xCont(xAsSet, UNO_QUERY);
+ if (xCont.is() && xCont->getCount())
+ { // yes -> step down
+ m_xControlTree->insert(&rContainerEntry, -1, &sName, nullptr,
+ nullptr, nullptr, false, m_xScratchIter.get());
+ m_xControlTree->set_image(*m_xScratchIter, RID_EXTBMP_FORM);
+ auto xIter = m_xControlTree->make_iterator(&rContainerEntry);
+ m_xControlTree->iter_nth_child(*xIter, nChildren);
+ sal_Int32 nContChildren = InsertEntries(xCont, *xIter);
+ if (nContChildren)
+ {
+ m_xControlTree->expand_row(*xIter);
+ ++nChildren;
+ }
+ else
+ { // oops, no valid children -> remove the entry
+ m_xControlTree->remove(*xIter);
+ }
+ }
+ continue;
+ }
+
+ // get the label
+ if (!::comphelper::hasProperty(PROPERTY_LABEL, xAsSet))
+ continue;
+
+ OUString sDisplayName =
+ ::comphelper::getString(xAsSet->getPropertyValue(PROPERTY_LABEL)) +
+ " (" + sName + ")";
+
+ // all requirements met -> insert
+ m_xUserData.emplace_back(new Reference<XPropertySet>(xAsSet));
+ OUString sId(weld::toId(m_xUserData.back().get()));
+ m_xControlTree->insert(&rContainerEntry, -1, &sDisplayName, &sId, nullptr, nullptr, false, m_xScratchIter.get());
+ m_xControlTree->set_image(*m_xScratchIter, m_aRequiredControlImage);
+
+ if (m_xInitialLabelControl == xAsSet)
+ {
+ m_xInitialSelection = m_xControlTree->make_iterator(&rContainerEntry);
+ m_xControlTree->iter_nth_child(*m_xInitialSelection, nChildren);
+ }
+
+ ++nChildren;
+ m_bHaveAssignableControl = true;
+ }
+
+ return nChildren;
+ }
+
+ IMPL_LINK(OSelectLabelDialog, OnEntrySelected, weld::TreeView&, rLB, void)
+ {
+ DBG_ASSERT(&rLB == m_xControlTree.get(), "OSelectLabelDialog::OnEntrySelected : where did this come from ?");
+ std::unique_ptr<weld::TreeIter> xIter = m_xControlTree->make_iterator();
+ bool bSelected = m_xControlTree->get_selected(xIter.get());
+ OUString sData = bSelected ? m_xControlTree->get_id(*xIter) : OUString();
+ if (!sData.isEmpty())
+ m_xSelectedControl.set(*weld::fromId<Reference<XPropertySet>*>(sData));
+ m_xNoAssignment->set_active(sData.isEmpty());
+ }
+
+ IMPL_LINK(OSelectLabelDialog, OnNoAssignmentClicked, weld::Toggleable&, rButton, void)
+ {
+ DBG_ASSERT(&rButton == m_xNoAssignment.get(), "OSelectLabelDialog::OnNoAssignmentClicked : where did this come from ?");
+
+ if (m_xNoAssignment->get_active())
+ {
+ m_bLastSelected = m_xControlTree->get_selected(m_xLastSelected.get());
+ }
+ else
+ {
+ DBG_ASSERT(m_bHaveAssignableControl, "OSelectLabelDialog::OnNoAssignmentClicked");
+ // search the first assignable entry
+ auto xSearch = m_xControlTree->make_iterator(nullptr);
+ bool bSearch = m_xControlTree->get_iter_first(*xSearch);
+ while (bSearch)
+ {
+ if (m_xControlTree->get_id(*xSearch).toInt64())
+ break;
+ bSearch = m_xControlTree->iter_next(*xSearch);
+ }
+ // and select it
+ if (bSearch)
+ {
+ m_xControlTree->copy_iterator(*xSearch, *m_xLastSelected);
+ m_xControlTree->select(*m_xLastSelected);
+ m_bLastSelected = true;
+ }
+ }
+
+ if (m_bLastSelected)
+ {
+ if (!m_xNoAssignment->get_active())
+ m_xControlTree->select(*m_xLastSelected);
+ else
+ m_xControlTree->unselect(*m_xLastSelected);
+ }
+ }
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/selectlabeldialog.hxx b/extensions/source/propctrlr/selectlabeldialog.hxx
new file mode 100644
index 000000000..9affa3512
--- /dev/null
+++ b/extensions/source/propctrlr/selectlabeldialog.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 <vcl/weld.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+namespace pcr
+{
+ // OSelectLabelDialog
+ class OSelectLabelDialog final : public weld::GenericDialogController
+ {
+ css::uno::Reference< css::beans::XPropertySet > m_xControlModel;
+ OUString m_sRequiredService;
+ OUString m_aRequiredControlImage;
+ std::unique_ptr<weld::TreeIter> m_xInitialSelection;
+ // the entry data of the listbox entries
+ std::vector<std::unique_ptr<css::uno::Reference<css::beans::XPropertySet>>> m_xUserData;
+ css::uno::Reference< css::beans::XPropertySet > m_xInitialLabelControl;
+
+ css::uno::Reference< css::beans::XPropertySet > m_xSelectedControl;
+ std::unique_ptr<weld::TreeIter> m_xLastSelected;
+ bool m_bLastSelected;
+ bool m_bHaveAssignableControl;
+
+ std::unique_ptr<weld::Label> m_xMainDesc;
+ std::unique_ptr<weld::TreeView> m_xControlTree;
+ std::unique_ptr<weld::TreeIter> m_xScratchIter;
+ std::unique_ptr<weld::CheckButton> m_xNoAssignment;
+
+ public:
+ OSelectLabelDialog(weld::Window* pParent, css::uno::Reference< css::beans::XPropertySet > const & _xControlModel);
+ virtual ~OSelectLabelDialog() override;
+
+ css::uno::Reference< css::beans::XPropertySet > GetSelected() const { return m_xNoAssignment->get_active() ? css::uno::Reference< css::beans::XPropertySet > () : m_xSelectedControl; }
+
+ private:
+ sal_Int32 InsertEntries(const css::uno::Reference< css::uno::XInterface >& _xContainer, const weld::TreeIter& rContainerEntry);
+
+ DECL_LINK(OnEntrySelected, weld::TreeView&, void);
+ DECL_LINK(OnNoAssignmentClicked, weld::Toggleable&, void);
+ };
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/sqlcommanddesign.cxx b/extensions/source/propctrlr/sqlcommanddesign.cxx
new file mode 100644
index 000000000..73e205be4
--- /dev/null
+++ b/extensions/source/propctrlr/sqlcommanddesign.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 "sqlcommanddesign.hxx"
+#include "formstrings.hxx"
+#include <command.hrc>
+#include "modulepcr.hxx"
+#include "unourl.hxx"
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XTitle.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <tools/diagnose_ex.h>
+#include <osl/diagnose.h>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::frame::XFrame;
+ using ::com::sun::star::awt::XTopWindow;
+ using ::com::sun::star::awt::XWindow;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::lang::XComponent;
+ using ::com::sun::star::frame::XComponentLoader;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::frame::XTitle;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::lang::NullPointerException;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::frame::XFrames;
+ using ::com::sun::star::util::XCloseable;
+ using ::com::sun::star::lang::XMultiServiceFactory;
+ using ::com::sun::star::frame::XDispatchProvider;
+ using ::com::sun::star::frame::XDispatch;
+ using ::com::sun::star::frame::Desktop;
+ using ::com::sun::star::frame::XDesktop2;
+
+ namespace FrameSearchFlag = ::com::sun::star::frame::FrameSearchFlag;
+ namespace CommandType = ::com::sun::star::sdb::CommandType;
+
+
+ //= ISQLCommandAdapter
+
+
+ ISQLCommandAdapter::~ISQLCommandAdapter()
+ {
+ }
+
+
+ //= SQLCommandDesigner
+
+
+ SQLCommandDesigner::SQLCommandDesigner( const Reference< XComponentContext >& _rxContext,
+ const ::rtl::Reference< ISQLCommandAdapter >& _rxPropertyAdapter,
+ const ::dbtools::SharedConnection& _rConnection, const Link<SQLCommandDesigner&,void>& _rCloseLink )
+ :m_xContext( _rxContext )
+ ,m_xConnection( _rConnection )
+ ,m_xObjectAdapter( _rxPropertyAdapter )
+ ,m_aCloseLink( _rCloseLink )
+ {
+ if ( m_xContext.is() )
+ m_xORB = m_xContext->getServiceManager();
+ if ( !m_xORB.is() || !_rxPropertyAdapter.is() || !m_xConnection.is() )
+ throw NullPointerException();
+
+ impl_doOpenDesignerFrame_nothrow();
+ }
+
+
+ SQLCommandDesigner::~SQLCommandDesigner()
+ {
+ }
+
+
+ void SAL_CALL SQLCommandDesigner::propertyChange( const PropertyChangeEvent& Event )
+ {
+ OSL_ENSURE( m_xDesigner.is() && ( Event.Source == m_xDesigner ), "SQLCommandDesigner::propertyChange: where did this come from?" );
+
+ if ( !(m_xDesigner.is() && ( Event.Source == m_xDesigner )) )
+ return;
+
+ try
+ {
+ if ( PROPERTY_ACTIVECOMMAND == Event.PropertyName )
+ {
+ OUString sCommand;
+ OSL_VERIFY( Event.NewValue >>= sCommand );
+ m_xObjectAdapter->setSQLCommand( sCommand );
+ }
+ else if ( PROPERTY_ESCAPE_PROCESSING == Event.PropertyName )
+ {
+ bool bEscapeProcessing( false );
+ OSL_VERIFY( Event.NewValue >>= bEscapeProcessing );
+ m_xObjectAdapter->setEscapeProcessing( bEscapeProcessing );
+ }
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ // not allowed to leave, so silence it
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ void SAL_CALL SQLCommandDesigner::disposing( const EventObject& Source )
+ {
+ if ( m_xDesigner.is() && ( Source.Source == m_xDesigner ) )
+ {
+ m_aCloseLink.Call( *this );
+ m_xDesigner.clear();
+ }
+ }
+
+
+ void SQLCommandDesigner::dispose()
+ {
+ if ( impl_isDisposed() )
+ return;
+
+ if ( isActive() )
+ impl_closeDesigner_nothrow();
+
+ m_xConnection.clear();
+ m_xContext.clear();
+ m_xORB.clear();
+ }
+
+
+ void SQLCommandDesigner::impl_checkDisposed_throw() const
+ {
+ if ( impl_isDisposed() )
+ throw DisposedException();
+ }
+
+
+ void SQLCommandDesigner::raise() const
+ {
+ impl_checkDisposed_throw();
+ impl_raise_nothrow();
+ }
+
+
+ bool SQLCommandDesigner::suspend() const
+ {
+ impl_checkDisposed_throw();
+ return impl_trySuspendDesigner_nothrow();
+ }
+
+
+ void SQLCommandDesigner::impl_raise_nothrow() const
+ {
+ OSL_PRECOND( isActive(), "SQLCommandDesigner::impl_raise_nothrow: not active!" );
+ if ( !isActive() )
+ return;
+
+ try
+ {
+ // activate the frame for this component
+ Reference< XFrame > xFrame( m_xDesigner->getFrame(), css::uno::UNO_SET_THROW );
+ Reference< XWindow > xWindow( xFrame->getContainerWindow(), css::uno::UNO_SET_THROW );
+ Reference< XTopWindow > xTopWindow( xWindow, UNO_QUERY_THROW );
+
+ xTopWindow->toFront();
+ xWindow->setFocus();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ void SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow()
+ {
+ OSL_PRECOND( !isActive(),
+ "SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow: already active!" );
+ OSL_PRECOND( m_xConnection.is(), "SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow: this will crash!" );
+ osl_atomic_increment(&m_refCount);
+
+ try
+ {
+ // for various reasons, we don't want the new frame to appear in the desktop's frame list
+ // thus, we create a blank frame at the desktop, remove it from the desktop's frame list
+ // immediately, and then load the component into this blank (and now parent-less) frame
+ Reference< XComponentLoader > xLoader( impl_createEmptyParentlessTask_nothrow(), UNO_QUERY_THROW );
+ const bool bEscapeProcessing = m_xObjectAdapter->getEscapeProcessing();
+ Sequence< PropertyValue > aArgs{
+ comphelper::makePropertyValue(PROPERTY_ACTIVE_CONNECTION, m_xConnection.getTyped()),
+ comphelper::makePropertyValue(PROPERTY_COMMAND, m_xObjectAdapter->getSQLCommand()),
+ comphelper::makePropertyValue(PROPERTY_COMMANDTYPE, CommandType::COMMAND),
+ comphelper::makePropertyValue(PROPERTY_ESCAPE_PROCESSING, bEscapeProcessing),
+ comphelper::makePropertyValue("GraphicalDesign", bEscapeProcessing)
+ };
+
+ Reference< XComponent > xQueryDesign = xLoader->loadComponentFromURL(
+ ".component:DB/QueryDesign",
+ "_self",
+ FrameSearchFlag::TASKS | FrameSearchFlag::CREATE,
+ aArgs
+ );
+
+ // remember this newly loaded component - we need to care for it e.g. when we're suspended
+ m_xDesigner.set(xQueryDesign, css::uno::UNO_QUERY);
+ OSL_ENSURE( m_xDesigner.is() || !xQueryDesign.is(), "SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow: the component is expected to be a controller!" );
+ if ( m_xDesigner.is() )
+ {
+ Reference< XPropertySet > xQueryDesignProps( m_xDesigner, UNO_QUERY );
+ OSL_ENSURE( xQueryDesignProps.is(), "SQLCommandDesigner::impl_doOpenDesignerFrame_nothrow: the controller should have properties!" );
+ if ( xQueryDesignProps.is() )
+ {
+ xQueryDesignProps->addPropertyChangeListener( PROPERTY_ACTIVECOMMAND, this );
+ xQueryDesignProps->addPropertyChangeListener( PROPERTY_ESCAPE_PROCESSING, this );
+ }
+ }
+
+ // get the frame which we just opened and set its title
+ Reference< XTitle> xTitle(xQueryDesign,UNO_QUERY);
+ if ( xTitle.is() )
+ {
+ OUString sDisplayName = PcrRes(RID_RSC_ENUM_COMMAND_TYPE[CommandType::COMMAND]);
+ xTitle->setTitle(sDisplayName);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ m_xDesigner.clear();
+ }
+ osl_atomic_decrement(&m_refCount);
+ }
+
+
+ Reference< XFrame > SQLCommandDesigner::impl_createEmptyParentlessTask_nothrow( ) const
+ {
+ OSL_PRECOND( m_xORB.is(), "SQLCommandDesigner::impl_createEmptyParentlessTask_nothrow: this will crash!" );
+
+ Reference< XFrame > xFrame;
+ try
+ {
+ Reference< XDesktop2 > xDesktop = Desktop::create(m_xContext);
+
+ Reference< XFrames > xDesktopFramesCollection( xDesktop->getFrames(), css::uno::UNO_SET_THROW );
+ xFrame = xDesktop->findFrame( "_blank", FrameSearchFlag::CREATE );
+ OSL_ENSURE( xFrame.is(), "SQLCommandDesigner::impl_createEmptyParentlessTask_nothrow: could not create an empty frame!" );
+ xDesktopFramesCollection->remove( xFrame );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return xFrame;
+ }
+
+
+ void SQLCommandDesigner::impl_closeDesigner_nothrow()
+ {
+ OSL_PRECOND( isActive(), "SQLCommandDesigner::impl_closeDesigner_nothrow: invalid call!" );
+ // close it
+ try
+ {
+ // do not listen anymore...
+ Reference< XPropertySet > xProps( m_xDesigner, UNO_QUERY );
+ if ( xProps.is() )
+ xProps->removePropertyChangeListener( PROPERTY_ACTIVECOMMAND, this );
+
+ // we need to close the frame via the "user interface", by dispatching a close command,
+ // instead of calling XCloseable::close directly. The latter method would also close
+ // the frame, but not care for things like shutting down the office when the last
+ // frame is gone ...
+ const UnoURL aCloseURL( ".uno:CloseDoc",
+ Reference< XMultiServiceFactory >( m_xORB, UNO_QUERY ) );
+
+ Reference< XDispatchProvider > xProvider( m_xDesigner->getFrame(), UNO_QUERY_THROW );
+ Reference< XDispatch > xDispatch( xProvider->queryDispatch( aCloseURL, "_top", FrameSearchFlag::SELF ) );
+ OSL_ENSURE( xDispatch.is(), "SQLCommandDesigner::impl_closeDesigner_nothrow: no dispatcher for the CloseDoc command!" );
+ if ( xDispatch.is() )
+ {
+ xDispatch->dispatch( aCloseURL, Sequence< PropertyValue >( ) );
+ }
+ else
+ {
+ // fallback: use the XCloseable::close (with all possible disadvantages)
+ Reference< XCloseable > xClose( m_xDesigner->getFrame(), UNO_QUERY );
+ if ( xClose.is() )
+ xClose->close( true );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+
+ m_xDesigner.clear();
+ }
+
+
+ bool SQLCommandDesigner::impl_trySuspendDesigner_nothrow() const
+ {
+ OSL_PRECOND( isActive(), "SQLCommandDesigner::impl_trySuspendDesigner_nothrow: no active designer, this will crash!" );
+ bool bAllow = true;
+ try
+ {
+ bAllow = m_xDesigner->suspend( true );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ return bAllow;
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/sqlcommanddesign.hxx b/extensions/source/propctrlr/sqlcommanddesign.hxx
new file mode 100644
index 000000000..a72ba11b0
--- /dev/null
+++ b/extensions/source/propctrlr/sqlcommanddesign.hxx
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <connectivity/dbtools.hxx>
+#include <tools/link.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+
+namespace pcr
+{
+
+
+ class ISQLCommandAdapter;
+
+ //= SQLCommandDesigner
+
+ typedef ::cppu::WeakImplHelper < css::beans::XPropertyChangeListener
+ > SQLCommandDesigner_Base;
+ /** encapsulates the code for calling and managing a query design frame, used
+ for interactively designing the Command property of a ->RowSet
+ */
+ class SQLCommandDesigner final : public SQLCommandDesigner_Base
+ {
+ private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::lang::XMultiComponentFactory > m_xORB;
+ ::dbtools::SharedConnection m_xConnection;
+ css::uno::Reference< css::frame::XController > m_xDesigner;
+ ::rtl::Reference< ISQLCommandAdapter > m_xObjectAdapter;
+ Link<SQLCommandDesigner&,void> m_aCloseLink;
+
+ public:
+ /** creates the instance, and immediately opens the SQL command design frame
+
+ @param _rxContext
+ our component context. Must not be <NULL/>, and must provide a non-<NULL/> XMultiComponentFactory
+ @param _rxPropertyAdapter
+ an adapter to the object's SQL command related properties
+ @param _rConnection
+ the current connection of ->_rxRowSet. Must not be <NULL/>.
+ @param _rCloseLink
+ link to call when the component has been closed
+ @throws css::lang::NullPointerException
+ if any of the arguments (except ->_rCloseLink) is <NULL/>, or if the component context
+ does not provide a valid component factory.
+ */
+ SQLCommandDesigner(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ const ::rtl::Reference< ISQLCommandAdapter >& _rxPropertyAdapter,
+ const ::dbtools::SharedConnection& _rConnection,
+ const Link<SQLCommandDesigner&,void>& _rCloseLink
+ );
+
+ /** determines whether the SQL Command designer is currently active, i.e.
+ if there currently exists a frame which allows the user entering the SQL command
+ */
+ bool isActive() const { return m_xDesigner.is(); }
+
+ /** returns the property adapter used by the instance
+ */
+ const ::rtl::Reference< ISQLCommandAdapter >& getPropertyAdapter() const { return m_xObjectAdapter; }
+
+ /** raises the designer window to top
+ @precond
+ the designer is active (->isActive)
+ @precond
+ the instance is not disposed
+ */
+ void raise() const;
+
+ /** suspends the designer
+ @precond
+ the designer is active (->isActive)
+ @precond
+ the instance is not disposed
+ */
+ bool suspend() const;
+
+ /** disposes the instance so that it becomes non-functional
+ */
+ void dispose();
+
+ private:
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ virtual ~SQLCommandDesigner() override;
+
+ /** opens a new frame for interactively designing an SQL command
+ @precond
+ the designer is not currently active (see ->isActive)
+ @precond
+ ->m_xConnection is not <NULL/>
+ */
+ void impl_doOpenDesignerFrame_nothrow();
+
+ /** impl-version of ->raise
+ */
+ void impl_raise_nothrow() const;
+
+ /** determines whether we are already disposed
+ */
+ bool impl_isDisposed() const
+ {
+ return !m_xContext.is();
+ }
+ /** checks whether we are already disposed
+ @throws css::lang::DisposedException
+ if we in fact are disposed
+ */
+ void impl_checkDisposed_throw() const;
+
+ /** create an empty top-level frame, which does not belong to the desktop's frame list
+ @precond
+ ->m_xORB is not <NULL/>
+ */
+ css::uno::Reference< css::frame::XFrame >
+ impl_createEmptyParentlessTask_nothrow() const;
+
+ /** closes the component denoted by m_xDesigner
+ @precond
+ our designer component is actually active (->isActive)
+ @precond
+ we're not disposed already
+ */
+ void impl_closeDesigner_nothrow();
+
+ /** suspends our designer component
+ @precond
+ the designer component is actually active (->isActive)
+ @return
+ <TRUE/> if the suspension was successful, <FALSE/> if it was vetoed
+ */
+ bool impl_trySuspendDesigner_nothrow() const;
+
+ SQLCommandDesigner( const SQLCommandDesigner& ) = delete;
+ SQLCommandDesigner& operator=( const SQLCommandDesigner& ) = delete;
+ };
+
+
+ //= ISQLCommandAdapter
+
+ /** an adapter to forward changed SQL command property values to a component
+ */
+ class ISQLCommandAdapter : public salhelper::SimpleReferenceObject
+ {
+ public:
+ /// retrieves the current SQL command of the component
+ virtual OUString getSQLCommand() const = 0;
+ /// retrieves the current value of the EscapeProcessing property of the component
+ virtual bool getEscapeProcessing() const = 0;
+
+ /// sets a new SQL command
+ virtual void setSQLCommand( const OUString& _rCommand ) const = 0;
+ /// sets a new EscapeProcessing property value
+ virtual void setEscapeProcessing( const bool _bEscapeProcessing ) const = 0;
+
+ virtual ~ISQLCommandAdapter() override;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/standardcontrol.cxx b/extensions/source/propctrlr/standardcontrol.cxx
new file mode 100644
index 000000000..95b4143c7
--- /dev/null
+++ b/extensions/source/propctrlr/standardcontrol.cxx
@@ -0,0 +1,865 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "standardcontrol.hxx"
+#include "pcrcommon.hxx"
+
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/Color.hpp>
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <comphelper/string.hxx>
+#include <o3tl/float_int_conversion.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+// ugly dependencies for the OColorControl
+#include <svx/svxids.hrc>
+
+#include <tools/datetime.hxx>
+#include <unotools/datetime.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <limits>
+#include <memory>
+
+namespace pcr
+{
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::inspection;
+
+ //= OTimeControl
+ OTimeControl::OTimeControl(std::unique_ptr<weld::FormattedSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : OTimeControl_Base(PropertyControlType::TimeField, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ , m_xFormatter(new weld::TimeFormatter(*getTypedControlWindow()))
+ {
+ m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration);
+ }
+
+ void SAL_CALL OTimeControl::setValue( const Any& _rValue )
+ {
+ util::Time aUNOTime;
+ if ( !( _rValue >>= aUNOTime ) )
+ {
+ getTypedControlWindow()->set_text("");
+ m_xFormatter->SetTime(tools::Time(tools::Time::EMPTY));
+ }
+ else
+ {
+ m_xFormatter->SetTime(::tools::Time(aUNOTime));
+ }
+ }
+
+ Any SAL_CALL OTimeControl::getValue()
+ {
+ Any aPropValue;
+ if ( !getTypedControlWindow()->get_text().isEmpty() )
+ {
+ aPropValue <<= m_xFormatter->GetTime().GetUNOTime();
+ }
+ return aPropValue;
+ }
+
+ Type SAL_CALL OTimeControl::getValueType()
+ {
+ return ::cppu::UnoType<util::Time>::get();
+ }
+
+ //= ODateControl
+ ODateControl::ODateControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : ODateControl_Base(PropertyControlType::DateField, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ , m_xEntry(m_xBuilder->weld_entry("entry"))
+ , m_xCalendarBox(std::make_unique<SvtCalendarBox>(m_xBuilder->weld_menu_button("button"), false))
+ {
+ m_xEntryFormatter.reset(new weld::DateFormatter(*m_xEntry));
+
+ m_xEntryFormatter->SetStrictFormat(true);
+ m_xEntryFormatter->SetMin(::Date(1, 1, 1600));
+ m_xEntryFormatter->SetMax(::Date(1, 1, 9999));
+
+ m_xEntryFormatter->SetExtDateFormat(ExtDateFieldFormat::SystemShortYYYY);
+ m_xEntryFormatter->EnableEmptyField(true);
+
+ m_xCalendarBox->connect_activated(LINK(this, ODateControl, ActivateHdl));
+
+ m_xCalendarBox->get_button().connect_toggled(LINK(this, ODateControl, ToggleHdl));
+ }
+
+ void SAL_CALL ODateControl::disposing()
+ {
+ m_xEntryFormatter.reset();
+ m_xEntry.reset();
+ m_xCalendarBox.reset();
+ ODateControl_Base::disposing();
+ }
+
+ void SAL_CALL ODateControl::setValue( const Any& _rValue )
+ {
+ util::Date aUNODate;
+ if ( !( _rValue >>= aUNODate ) )
+ {
+ m_xEntry->set_text(OUString());
+ }
+ else
+ {
+ ::Date aDate( aUNODate.Day, aUNODate.Month, aUNODate.Year );
+ m_xEntryFormatter->SetDate(aDate);
+ }
+ }
+
+ IMPL_LINK_NOARG(ODateControl, ActivateHdl, SvtCalendarBox&, void)
+ {
+ m_xEntryFormatter->SetDate(m_xCalendarBox->get_date());
+ setModified();
+ m_xEntry->grab_focus();
+ }
+
+ IMPL_LINK(ODateControl, ToggleHdl, weld::Toggleable&, rToggle, void)
+ {
+ if (!rToggle.get_active())
+ return;
+ ::Date aDate = m_xEntryFormatter->GetDate();
+ if (aDate.IsEmpty())
+ {
+ // with an empty date preselect today in the calendar
+ aDate = ::Date(::Date::SYSTEM);
+ }
+ m_xCalendarBox->set_date(aDate);
+ }
+
+ Any SAL_CALL ODateControl::getValue()
+ {
+ Any aPropValue;
+ if (!m_xEntry->get_text().isEmpty())
+ {
+ ::Date aDate(m_xEntryFormatter->GetDate());
+ aPropValue <<= aDate.GetUNODate();
+ }
+ return aPropValue;
+ }
+
+ Type SAL_CALL ODateControl::getValueType()
+ {
+ return ::cppu::UnoType<util::Date>::get();
+ }
+
+ //= OEditControl
+ OEditControl::OEditControl(std::unique_ptr<weld::Entry> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bPW, bool bReadOnly)
+ : OEditControl_Base( bPW ? PropertyControlType::CharacterField : PropertyControlType::TextField, std::move(xBuilder), std::move(xWidget), bReadOnly )
+ {
+ m_bIsPassword = bPW;
+
+ auto pWidget = getTypedControlWindow();
+ pWidget->set_sensitive(true);
+ pWidget->set_editable(!bReadOnly);
+
+ if (m_bIsPassword)
+ pWidget->set_max_length( 1 );
+ }
+
+ void SAL_CALL OEditControl::setValue( const Any& _rValue )
+ {
+ OUString sText;
+ if ( m_bIsPassword )
+ {
+ sal_Int16 nValue = 0;
+ _rValue >>= nValue;
+ if ( nValue )
+ {
+ sText = OUString(static_cast<sal_Unicode>(nValue));
+ }
+ }
+ else
+ _rValue >>= sText;
+
+ getTypedControlWindow()->set_text( sText );
+ }
+
+ Any SAL_CALL OEditControl::getValue()
+ {
+ Any aPropValue;
+
+ OUString sText( getTypedControlWindow()->get_text() );
+ if ( m_bIsPassword )
+ {
+ if ( !sText.isEmpty() )
+ aPropValue <<= static_cast<sal_Int16>(sText[0]);
+ }
+ else
+ aPropValue <<= sText;
+
+ return aPropValue;
+ }
+
+ Type SAL_CALL OEditControl::getValueType()
+ {
+ return m_bIsPassword ? ::cppu::UnoType<sal_Int16>::get() : ::cppu::UnoType<OUString>::get();
+ }
+
+ void OEditControl::setModified()
+ {
+ OEditControl_Base::setModified();
+
+ // for password controls, we fire a commit for every single change
+ if ( m_bIsPassword )
+ notifyModifiedValue();
+ }
+
+ static sal_Int64 ImplCalcLongValue( double nValue, sal_uInt16 nDigits )
+ {
+ double n = nValue;
+ for ( sal_uInt16 d = 0; d < nDigits; ++d )
+ n *= 10;
+
+ return o3tl::saturating_cast<sal_Int64>(n);
+ }
+
+ static double ImplCalcDoubleValue(sal_Int64 nValue, sal_uInt16 nDigits )
+ {
+ double n = nValue;
+ for ( sal_uInt16 d = 0; d < nDigits; ++d )
+ n /= 10;
+ return n;
+ }
+
+ ODateTimeControl::ODateTimeControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : ODateTimeControl_Base(PropertyControlType::DateTimeField, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ , m_xDate(std::make_unique<SvtCalendarBox>(m_xBuilder->weld_menu_button("datefield")))
+ , m_xTime(m_xBuilder->weld_formatted_spin_button("timefield"))
+ , m_xFormatter(new weld::TimeFormatter(*m_xTime))
+ {
+ m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration);
+ }
+
+ void SAL_CALL ODateTimeControl::setValue( const Any& _rValue )
+ {
+ if ( !_rValue.hasValue() )
+ {
+ m_xDate->set_date(::Date(::Date::SYSTEM));
+ m_xTime->set_text("");
+ m_xFormatter->SetTime(tools::Time(tools::Time::EMPTY));
+ }
+ else
+ {
+ util::DateTime aUNODateTime;
+ OSL_VERIFY( _rValue >>= aUNODateTime );
+
+ ::DateTime aDateTime( ::DateTime::EMPTY );
+ ::utl::typeConvert( aUNODateTime, aDateTime );
+
+ m_xDate->set_date(aDateTime);
+ m_xFormatter->SetTime(aDateTime);
+ }
+ }
+
+ Any SAL_CALL ODateTimeControl::getValue()
+ {
+ Any aPropValue;
+ if (!m_xTime->get_text().isEmpty())
+ {
+ ::DateTime aDateTime(m_xDate->get_date(), m_xFormatter->GetTime());
+
+ util::DateTime aUNODateTime;
+ ::utl::typeConvert( aDateTime, aUNODateTime );
+
+ aPropValue <<= aUNODateTime;
+ }
+ return aPropValue;
+ }
+
+ Type SAL_CALL ODateTimeControl::getValueType()
+ {
+ return ::cppu::UnoType<util::DateTime>::get();
+ }
+
+ //= OHyperlinkControl
+ OHyperlinkControl::OHyperlinkControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : OHyperlinkControl_Base(PropertyControlType::HyperlinkField, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ , m_xEntry(m_xBuilder->weld_entry("entry"))
+ , m_xButton(m_xBuilder->weld_button("button"))
+ , m_aActionListeners(m_aMutex)
+ {
+ auto pWidget = getTypedControlWindow();
+ pWidget->set_sensitive(true);
+ m_xEntry->set_editable(!bReadOnly);
+
+ m_xButton->connect_clicked(LINK(this, OHyperlinkControl, OnHyperlinkClicked));
+ }
+
+ Any SAL_CALL OHyperlinkControl::getValue()
+ {
+ OUString sText = m_xEntry->get_text();
+ return Any( sText );
+ }
+
+ void SAL_CALL OHyperlinkControl::setValue( const Any& _value )
+ {
+ OUString sText;
+ _value >>= sText;
+ m_xEntry->set_text( sText );
+ }
+
+ Type SAL_CALL OHyperlinkControl::getValueType()
+ {
+ return ::cppu::UnoType<OUString>::get();
+ }
+
+ void SAL_CALL OHyperlinkControl::addActionListener( const Reference< XActionListener >& listener )
+ {
+ if ( listener.is() )
+ m_aActionListeners.addInterface( listener );
+ }
+
+ void SAL_CALL OHyperlinkControl::removeActionListener( const Reference< XActionListener >& listener )
+ {
+ m_aActionListeners.removeInterface( listener );
+ }
+
+ void SAL_CALL OHyperlinkControl::disposing()
+ {
+ m_xButton.reset();
+ m_xEntry.reset();
+ OHyperlinkControl_Base::disposing();
+
+ EventObject aEvent( *this );
+ m_aActionListeners.disposeAndClear( aEvent );
+ }
+
+ IMPL_LINK_NOARG( OHyperlinkControl, OnHyperlinkClicked, weld::Button&, void )
+ {
+ ActionEvent aEvent( *this, "clicked" );
+ m_aActionListeners.forEach< XActionListener >(
+ [&aEvent] (uno::Reference<awt::XActionListener> const& xListener)
+ { return xListener->actionPerformed(aEvent); });
+ }
+
+ //= ONumericControl
+ ONumericControl::ONumericControl(std::unique_ptr<weld::MetricSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : ONumericControl_Base(PropertyControlType::NumericField, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ , m_eValueUnit( FieldUnit::NONE )
+ , m_nFieldToUNOValueFactor( 1 )
+ {
+ Optional< double > value( getMaxValue() );
+ value.Value = -value.Value;
+ setMinValue( value );
+ }
+
+ ::sal_Int16 SAL_CALL ONumericControl::getDecimalDigits()
+ {
+ return getTypedControlWindow()->get_digits();
+ }
+
+ void SAL_CALL ONumericControl::setDecimalDigits( ::sal_Int16 decimaldigits )
+ {
+ weld::MetricSpinButton* pControlWindow = getTypedControlWindow();
+ sal_Int64 min, max;
+ pControlWindow->get_range(min, max, FieldUnit::NONE);
+ pControlWindow->set_digits(decimaldigits);
+ pControlWindow->set_range(min, max, FieldUnit::NONE);
+ }
+
+ Optional< double > SAL_CALL ONumericControl::getMinValue()
+ {
+ Optional< double > aReturn( true, 0 );
+
+ sal_Int64 minValue = getTypedControlWindow()->get_min(FieldUnit::NONE);
+ if ( minValue == std::numeric_limits<sal_Int64>::min() )
+ aReturn.IsPresent = false;
+ else
+ aReturn.Value = static_cast<double>(minValue);
+
+ return aReturn;
+ }
+
+ void SAL_CALL ONumericControl::setMinValue( const Optional< double >& _minvalue )
+ {
+ if ( !_minvalue.IsPresent )
+ getTypedControlWindow()->set_min( std::numeric_limits<sal_Int64>::min(), FieldUnit::NONE );
+ else
+ getTypedControlWindow()->set_min( impl_apiValueToFieldValue_nothrow( _minvalue.Value ) , m_eValueUnit);
+ }
+
+ Optional< double > SAL_CALL ONumericControl::getMaxValue()
+ {
+ Optional< double > aReturn( true, 0 );
+
+ sal_Int64 maxValue = getTypedControlWindow()->get_max(FieldUnit::NONE);
+ if ( maxValue == std::numeric_limits<sal_Int64>::max() )
+ aReturn.IsPresent = false;
+ else
+ aReturn.Value = static_cast<double>(maxValue);
+
+ return aReturn;
+ }
+
+ void SAL_CALL ONumericControl::setMaxValue( const Optional< double >& _maxvalue )
+ {
+ if ( !_maxvalue.IsPresent )
+ getTypedControlWindow()->set_max( std::numeric_limits<sal_Int64>::max(), FieldUnit::NONE );
+ else
+ getTypedControlWindow()->set_max( impl_apiValueToFieldValue_nothrow( _maxvalue.Value ), m_eValueUnit );
+ }
+
+ ::sal_Int16 SAL_CALL ONumericControl::getDisplayUnit()
+ {
+ return VCLUnoHelper::ConvertToMeasurementUnit( getTypedControlWindow()->get_unit(), 1 );
+ }
+
+ void SAL_CALL ONumericControl::setDisplayUnit( ::sal_Int16 _displayunit )
+ {
+ if ( ( _displayunit < MeasureUnit::MM_100TH ) || ( _displayunit > MeasureUnit::PERCENT ) )
+ throw IllegalArgumentException();
+ if ( ( _displayunit == MeasureUnit::MM_100TH )
+ || ( _displayunit == MeasureUnit::MM_10TH )
+ || ( _displayunit == MeasureUnit::INCH_1000TH )
+ || ( _displayunit == MeasureUnit::INCH_100TH )
+ || ( _displayunit == MeasureUnit::INCH_10TH )
+ || ( _displayunit == MeasureUnit::PERCENT )
+ )
+ throw IllegalArgumentException();
+
+ sal_Int16 nDummyFactor = 1;
+ FieldUnit eFieldUnit = VCLUnoHelper::ConvertToFieldUnit( _displayunit, nDummyFactor );
+ if ( nDummyFactor != 1 )
+ // everything which survived the checks above should result in a factor of 1, i.e.,
+ // it should have a direct counterpart as FieldUnit
+ throw RuntimeException();
+ getTypedControlWindow()->set_unit(eFieldUnit);
+ }
+
+ ::sal_Int16 SAL_CALL ONumericControl::getValueUnit()
+ {
+ return VCLUnoHelper::ConvertToMeasurementUnit( m_eValueUnit, m_nFieldToUNOValueFactor );
+ }
+
+ void SAL_CALL ONumericControl::setValueUnit( ::sal_Int16 _valueunit )
+ {
+ if ( ( _valueunit < MeasureUnit::MM_100TH ) || ( _valueunit > MeasureUnit::PERCENT ) )
+ throw IllegalArgumentException();
+ m_eValueUnit = VCLUnoHelper::ConvertToFieldUnit( _valueunit, m_nFieldToUNOValueFactor );
+ }
+
+ void SAL_CALL ONumericControl::setValue( const Any& _rValue )
+ {
+ if ( !_rValue.hasValue() )
+ {
+ getTypedControlWindow()->set_text( "" );
+ }
+ else
+ {
+ double nValue( 0 );
+ OSL_VERIFY( _rValue >>= nValue );
+ auto nControlValue = impl_apiValueToFieldValue_nothrow( nValue );
+ getTypedControlWindow()->set_value( nControlValue, m_eValueUnit );
+ }
+ }
+
+ sal_Int64 ONumericControl::impl_apiValueToFieldValue_nothrow( double _nApiValue ) const
+ {
+ sal_Int64 nControlValue = ImplCalcLongValue( _nApiValue, getTypedControlWindow()->get_digits() );
+ nControlValue /= m_nFieldToUNOValueFactor;
+ return nControlValue;
+ }
+
+ double ONumericControl::impl_fieldValueToApiValue_nothrow(sal_Int64 nFieldValue) const
+ {
+ double nApiValue = ImplCalcDoubleValue( nFieldValue, getTypedControlWindow()->get_digits() );
+ nApiValue *= m_nFieldToUNOValueFactor;
+ return nApiValue;
+ }
+
+ Any SAL_CALL ONumericControl::getValue()
+ {
+ Any aPropValue;
+ if ( !getTypedControlWindow()->get_text().isEmpty() )
+ {
+ double nValue = impl_fieldValueToApiValue_nothrow( getTypedControlWindow()->get_value( m_eValueUnit ) );
+ aPropValue <<= nValue;
+ }
+ return aPropValue;
+ }
+
+ Type SAL_CALL ONumericControl::getValueType()
+ {
+ return ::cppu::UnoType<double>::get();
+ }
+
+ //= OColorControl
+ OColorControl::OColorControl(std::unique_ptr<ColorListBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : OColorControl_Base(PropertyControlType::ColorListBox, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ {
+ getTypedControlWindow()->SetSlotId(SID_FM_CTL_PROPERTIES);
+ }
+
+ void SAL_CALL OColorControl::setValue( const Any& _rValue )
+ {
+ css::util::Color nColor = sal_uInt32(COL_TRANSPARENT);
+ if (_rValue.hasValue())
+ _rValue >>= nColor;
+ getTypedControlWindow()->SelectEntry(::Color(ColorTransparency, nColor));
+ }
+
+ Any SAL_CALL OColorControl::getValue()
+ {
+ Any aPropValue;
+ ::Color aRgbCol = getTypedControlWindow()->GetSelectEntryColor();
+ if (aRgbCol == COL_TRANSPARENT)
+ return aPropValue;
+ aPropValue <<= aRgbCol;
+ return aPropValue;
+ }
+
+ Type SAL_CALL OColorControl::getValueType()
+ {
+ return ::cppu::UnoType<sal_Int32>::get();
+ }
+
+ void OColorControl::setModified()
+ {
+ OColorControl_Base::setModified();
+
+ // fire a commit
+ notifyModifiedValue();
+ }
+
+ //= OListboxControl
+ OListboxControl::OListboxControl(std::unique_ptr<weld::ComboBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : OListboxControl_Base(PropertyControlType::ListBox, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ {
+ }
+
+ Any SAL_CALL OListboxControl::getValue()
+ {
+ OUString sControlValue( getTypedControlWindow()->get_active_text() );
+
+ Any aPropValue;
+ if ( !sControlValue.isEmpty() )
+ aPropValue <<= sControlValue;
+ return aPropValue;
+ }
+
+ Type SAL_CALL OListboxControl::getValueType()
+ {
+ return ::cppu::UnoType<OUString>::get();
+ }
+
+ void SAL_CALL OListboxControl::setValue( const Any& _rValue )
+ {
+ if ( !_rValue.hasValue() )
+ getTypedControlWindow()->set_active(-1);
+ else
+ {
+ OUString sSelection;
+ _rValue >>= sSelection;
+
+ if (getTypedControlWindow()->find_text(sSelection) == -1)
+ getTypedControlWindow()->insert_text(0, sSelection);
+
+ if (sSelection != getTypedControlWindow()->get_active_text())
+ getTypedControlWindow()->set_active_text(sSelection);
+ }
+ }
+
+ void SAL_CALL OListboxControl::clearList()
+ {
+ getTypedControlWindow()->clear();
+ }
+
+ void SAL_CALL OListboxControl::prependListEntry( const OUString& NewEntry )
+ {
+ getTypedControlWindow()->insert_text(0, NewEntry);
+ }
+
+ void SAL_CALL OListboxControl::appendListEntry( const OUString& NewEntry )
+ {
+ getTypedControlWindow()->append_text(NewEntry);
+ }
+
+ Sequence< OUString > SAL_CALL OListboxControl::getListEntries()
+ {
+ const sal_Int32 nCount = getTypedControlWindow()->get_count();
+ Sequence< OUString > aRet(nCount);
+ OUString* pIter = aRet.getArray();
+ for (sal_Int32 i = 0; i < nCount ; ++i,++pIter)
+ *pIter = getTypedControlWindow()->get_text(i);
+
+ return aRet;
+ }
+
+ void OListboxControl::setModified()
+ {
+ OListboxControl_Base::setModified();
+
+ // fire a commit
+ notifyModifiedValue();
+ }
+
+ //= OComboboxControl
+ OComboboxControl::OComboboxControl(std::unique_ptr<weld::ComboBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : OComboboxControl_Base(PropertyControlType::ComboBox, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ {
+ getTypedControlWindow()->connect_changed( LINK( this, OComboboxControl, OnEntrySelected ) );
+ }
+
+ void SAL_CALL OComboboxControl::setValue( const Any& _rValue )
+ {
+ OUString sText;
+ _rValue >>= sText;
+ weld::ComboBox* pControlWindow = getTypedControlWindow();
+ // tdf#138701 leave current cursor valid if the contents won't change
+ if (pControlWindow->get_active_text() != sText)
+ pControlWindow->set_entry_text(sText);
+ }
+
+ Any SAL_CALL OComboboxControl::getValue()
+ {
+ return Any( getTypedControlWindow()->get_active_text() );
+ }
+
+ Type SAL_CALL OComboboxControl::getValueType()
+ {
+ return ::cppu::UnoType<OUString>::get();
+ }
+
+ void SAL_CALL OComboboxControl::clearList()
+ {
+ getTypedControlWindow()->clear();
+ }
+
+ void SAL_CALL OComboboxControl::prependListEntry( const OUString& NewEntry )
+ {
+ getTypedControlWindow()->insert_text(0, NewEntry);
+ }
+
+ void SAL_CALL OComboboxControl::appendListEntry( const OUString& NewEntry )
+ {
+ getTypedControlWindow()->append_text( NewEntry );
+ }
+
+ Sequence< OUString > SAL_CALL OComboboxControl::getListEntries( )
+ {
+ const sal_Int32 nCount = getTypedControlWindow()->get_count();
+ Sequence< OUString > aRet(nCount);
+ OUString* pIter = aRet.getArray();
+ for (sal_Int32 i = 0; i < nCount ; ++i,++pIter)
+ *pIter = getTypedControlWindow()->get_text(i);
+
+ return aRet;
+ }
+
+ IMPL_LINK_NOARG( OComboboxControl, OnEntrySelected, weld::ComboBox&, void )
+ {
+ // fire a commit
+ notifyModifiedValue();
+ }
+
+ namespace
+ {
+
+ StlSyntaxSequence< OUString > lcl_convertMultiLineToList( std::u16string_view _rCompsedTextWithLineBreaks )
+ {
+ sal_Int32 nLines = comphelper::string::getTokenCount(_rCompsedTextWithLineBreaks, '\n');
+ StlSyntaxSequence< OUString > aStrings( nLines );
+ if (nLines)
+ {
+ StlSyntaxSequence< OUString >::iterator stringItem = aStrings.begin();
+ sal_Int32 nIdx {0};
+ do
+ {
+ *stringItem = o3tl::getToken(_rCompsedTextWithLineBreaks, 0, '\n', nIdx );
+ ++stringItem;
+ }
+ while (nIdx>0);
+ }
+ return aStrings;
+ }
+
+ OUString lcl_convertListToMultiLine( const StlSyntaxSequence< OUString >& _rStrings )
+ {
+ OUStringBuffer sMultiLineText;
+ for ( StlSyntaxSequence< OUString >::const_iterator item = _rStrings.begin();
+ item != _rStrings.end();
+ )
+ {
+ sMultiLineText.append(*item);
+ if ( ++item != _rStrings.end() )
+ sMultiLineText.append("\n");
+ }
+ return sMultiLineText.makeStringAndClear();
+ }
+
+
+ OUString lcl_convertListToDisplayText( const StlSyntaxSequence< OUString >& _rStrings )
+ {
+ OUStringBuffer aComposed;
+ for ( StlSyntaxSequence< OUString >::const_iterator strings = _rStrings.begin();
+ strings != _rStrings.end();
+ ++strings
+ )
+ {
+ if ( strings != _rStrings.begin() )
+ aComposed.append( ';' );
+ aComposed.append( '\"' );
+ aComposed.append( *strings );
+ aComposed.append( '\"' );
+ }
+ return aComposed.makeStringAndClear();
+ }
+ }
+
+ void OMultilineEditControl::CheckEntryTextViewMisMatch()
+ {
+ // if there are newlines or something else which the entry cannot show, then make
+ // just the multiline dropdown editable as the canonical source for text
+ m_xEntry->set_sensitive(m_xEntry->get_text() == m_xTextView->get_text());
+ }
+
+ void OMultilineEditControl::SetStringListValue(const StlSyntaxSequence<OUString>& rStrings)
+ {
+ m_xEntry->set_text(lcl_convertListToDisplayText(rStrings));
+ m_xTextView->set_text(lcl_convertListToMultiLine(rStrings));
+ CheckEntryTextViewMisMatch();
+ }
+
+ StlSyntaxSequence<OUString> OMultilineEditControl::GetStringListValue() const
+ {
+ return lcl_convertMultiLineToList(m_xTextView->get_text());
+ }
+
+ void OMultilineEditControl::SetTextValue(const OUString& rText)
+ {
+ OSL_PRECOND( m_nOperationMode == eMultiLineText, "OMultilineEditControl::SetTextValue: illegal call!" );
+
+ m_xTextView->set_text(rText);
+ m_xEntry->set_text(rText);
+ CheckEntryTextViewMisMatch();
+ }
+
+ OUString OMultilineEditControl::GetTextValue() const
+ {
+ OSL_PRECOND( m_nOperationMode == eMultiLineText, "OMultilineEditControl::GetTextValue: illegal call!" );
+ return m_xTextView->get_text();
+ }
+
+ //= OMultilineEditControl
+ OMultilineEditControl::OMultilineEditControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, MultiLineOperationMode eMode, bool bReadOnly)
+ : OMultilineEditControl_Base(eMode == eMultiLineText ? PropertyControlType::MultiLineTextField : PropertyControlType::StringListField,
+ std::move(xBuilder), std::move(xWidget), bReadOnly)
+ , m_nOperationMode(eMode)
+ , m_xEntry(m_xBuilder->weld_entry("entry"))
+ , m_xButton(m_xBuilder->weld_menu_button("button"))
+ , m_xPopover(m_xBuilder->weld_widget("popover"))
+ , m_xTextView(m_xBuilder->weld_text_view("textview"))
+ , m_xOk(m_xBuilder->weld_button("ok"))
+ {
+ m_xButton->set_popover(m_xPopover.get());
+ m_xTextView->set_size_request(m_xTextView->get_approximate_digit_width() * 30, m_xTextView->get_height_rows(8));
+ m_xOk->connect_clicked(LINK(this, OMultilineEditControl, ButtonHandler));
+ }
+
+ IMPL_LINK_NOARG(OMultilineEditControl, TextViewModifiedHdl, weld::TextView&, void)
+ {
+ // tdf#139070 during editing update the entry to look like how it will
+ // look once editing is finished so that the default behaviour of vcl
+ // to strip newlines and the default behaviour of gtk to show a newline
+ // symbol is suppressed
+ OUString sText = m_xTextView->get_text();
+ auto aSeq = lcl_convertMultiLineToList(sText);
+ if (aSeq.getLength() > 1)
+ m_xEntry->set_text(lcl_convertListToDisplayText(aSeq));
+ else
+ m_xEntry->set_text(sText);
+ CheckEntryTextViewMisMatch();
+ setModified();
+ }
+
+ void OMultilineEditControl::editChanged()
+ {
+ m_xTextView->set_text(m_xEntry->get_text());
+ CheckEntryTextViewMisMatch();
+ setModified();
+ }
+
+ IMPL_LINK_NOARG(OMultilineEditControl, ButtonHandler, weld::Button&, void)
+ {
+ m_xButton->set_active(false);
+ notifyModifiedValue();
+ }
+
+ void SAL_CALL OMultilineEditControl::setValue( const Any& _rValue )
+ {
+ impl_checkDisposed_throw();
+
+ switch (m_nOperationMode)
+ {
+ case eMultiLineText:
+ {
+ OUString sText;
+ if ( !( _rValue >>= sText ) && _rValue.hasValue() )
+ throw IllegalTypeException();
+ SetTextValue(sText);
+ break;
+ }
+ case eStringList:
+ {
+ Sequence< OUString > aStringLines;
+ if ( !( _rValue >>= aStringLines ) && _rValue.hasValue() )
+ throw IllegalTypeException();
+ SetStringListValue( StlSyntaxSequence<OUString>(aStringLines) );
+ break;
+ }
+ }
+ }
+
+ Any SAL_CALL OMultilineEditControl::getValue()
+ {
+ impl_checkDisposed_throw();
+
+ Any aValue;
+ switch (m_nOperationMode)
+ {
+ case eMultiLineText:
+ aValue <<= GetTextValue();
+ break;
+ case eStringList:
+ aValue <<= GetStringListValue();
+ break;
+ }
+ return aValue;
+ }
+
+ Type SAL_CALL OMultilineEditControl::getValueType()
+ {
+ if (m_nOperationMode == eMultiLineText)
+ return ::cppu::UnoType<OUString>::get();
+ return cppu::UnoType<Sequence< OUString >>::get();
+ }
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/standardcontrol.hxx b/extensions/source/propctrlr/standardcontrol.hxx
new file mode 100644
index 000000000..fcd194886
--- /dev/null
+++ b/extensions/source/propctrlr/standardcontrol.hxx
@@ -0,0 +1,412 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "commoncontrol.hxx"
+#include "pcrcommon.hxx"
+
+#include <com/sun/star/inspection/XNumericControl.hpp>
+#include <com/sun/star/inspection/XStringListControl.hpp>
+#include <com/sun/star/inspection/XHyperlinkControl.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <svtools/ctrlbox.hxx>
+#include <svx/colorbox.hxx>
+
+namespace pcr
+{
+ //= OTimeControl
+ typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::FormattedSpinButton> OTimeControl_Base;
+ class OTimeControl : public OTimeControl_Base
+ {
+ std::unique_ptr<weld::TimeFormatter> m_xFormatter;
+ public:
+ OTimeControl(std::unique_ptr<weld::FormattedSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ virtual void SAL_CALL disposing() override
+ {
+ m_xFormatter.reset();
+ OTimeControl_Base::disposing();
+ }
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ virtual void SetModifyHandler() override
+ {
+ OTimeControl_Base::SetModifyHandler();
+ getTypedControlWindow()->connect_value_changed( LINK( this, CommonBehaviourControlHelper, TimeModifiedHdl ) );
+ }
+
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+ };
+
+ //= ODateControl
+ typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Container> ODateControl_Base;
+ class ODateControl : public ODateControl_Base
+ {
+ std::unique_ptr<weld::Entry> m_xEntry;
+ std::unique_ptr<SvtCalendarBox> m_xCalendarBox;
+ std::unique_ptr<weld::DateFormatter> m_xEntryFormatter;
+
+ DECL_LINK(ActivateHdl, SvtCalendarBox&, void);
+ DECL_LINK(ToggleHdl, weld::Toggleable&, void);
+
+ public:
+ ODateControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ virtual void SetModifyHandler() override
+ {
+ ODateControl_Base::SetModifyHandler();
+
+ m_xEntry->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xEntryFormatter->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+ m_xCalendarBox->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xCalendarBox->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+
+ m_xEntryFormatter->connect_changed(LINK(this, CommonBehaviourControlHelper, EditModifiedHdl));
+ }
+
+ virtual void SAL_CALL disposing() override;
+
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+ };
+
+ //= OEditControl
+ typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Entry> OEditControl_Base;
+ class OEditControl final : public OEditControl_Base
+ {
+ bool m_bIsPassword : 1;
+
+ public:
+ OEditControl(std::unique_ptr<weld::Entry> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bPassWord, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ virtual void SetModifyHandler() override
+ {
+ OEditControl_Base::SetModifyHandler();
+ getTypedControlWindow()->connect_changed( LINK( this, CommonBehaviourControlHelper, EditModifiedHdl ) );
+ }
+
+ private:
+ // CommonBehaviourControlHelper::modified
+ virtual void setModified() override;
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+ };
+
+ //= ODateTimeControl
+ typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Container> ODateTimeControl_Base;
+ class ODateTimeControl : public ODateTimeControl_Base
+ {
+ private:
+ std::unique_ptr<SvtCalendarBox> m_xDate;
+ std::unique_ptr<weld::FormattedSpinButton> m_xTime;
+ std::unique_ptr<weld::TimeFormatter> m_xFormatter;
+
+ public:
+ ODateTimeControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ virtual void SetModifyHandler() override
+ {
+ m_xDate->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xDate->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+ m_xTime->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xTime->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+
+ m_xDate->connect_selected( LINK( this, CommonBehaviourControlHelper, DateModifiedHdl ) );
+ m_xTime->connect_value_changed( LINK( this, CommonBehaviourControlHelper, TimeModifiedHdl ) );
+ }
+
+ virtual void SAL_CALL disposing() override
+ {
+ m_xFormatter.reset();
+ m_xTime.reset();
+ m_xDate.reset();
+ ODateTimeControl_Base::disposing();
+ }
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+ };
+
+ //= OHyperlinkControl
+ typedef CommonBehaviourControl<css::inspection::XHyperlinkControl, weld::Container> OHyperlinkControl_Base;
+ class OHyperlinkControl final : public OHyperlinkControl_Base
+ {
+ private:
+ std::unique_ptr<weld::Entry> m_xEntry;
+ std::unique_ptr<weld::Button> m_xButton;
+
+ ::comphelper::OInterfaceContainerHelper2 m_aActionListeners;
+
+ public:
+ OHyperlinkControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ virtual void SetModifyHandler() override
+ {
+ m_xEntry->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xEntry->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+ m_xButton->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xButton->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+
+ m_xEntry->connect_changed( LINK( this, CommonBehaviourControlHelper, EditModifiedHdl ) );
+ }
+
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+
+ // XHyperlinkControl
+ virtual void SAL_CALL addActionListener( const css::uno::Reference< css::awt::XActionListener >& listener ) override;
+ virtual void SAL_CALL removeActionListener( const css::uno::Reference< css::awt::XActionListener >& listener ) override;
+
+ private:
+ // XComponent
+ virtual void SAL_CALL disposing() override;
+
+ DECL_LINK(OnHyperlinkClicked, weld::Button&, void);
+ };
+
+ //= ONumericControl
+ typedef CommonBehaviourControl<css::inspection::XNumericControl, weld::MetricSpinButton> ONumericControl_Base;
+ class ONumericControl : public ONumericControl_Base
+ {
+ private:
+ FieldUnit m_eValueUnit;
+ sal_Int16 m_nFieldToUNOValueFactor;
+
+ public:
+ ONumericControl(std::unique_ptr<weld::MetricSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ // XNumericControl
+ virtual ::sal_Int16 SAL_CALL getDecimalDigits() override;
+ virtual void SAL_CALL setDecimalDigits( ::sal_Int16 _decimaldigits ) override;
+ virtual css::beans::Optional< double > SAL_CALL getMinValue() override;
+ virtual void SAL_CALL setMinValue( const css::beans::Optional< double >& _minvalue ) override;
+ virtual css::beans::Optional< double > SAL_CALL getMaxValue() override;
+ virtual void SAL_CALL setMaxValue( const css::beans::Optional< double >& _maxvalue ) override;
+ virtual ::sal_Int16 SAL_CALL getDisplayUnit() override;
+ virtual void SAL_CALL setDisplayUnit( ::sal_Int16 _displayunit ) override;
+ virtual ::sal_Int16 SAL_CALL getValueUnit() override;
+ virtual void SAL_CALL setValueUnit( ::sal_Int16 _valueunit ) override;
+
+ virtual void SetModifyHandler() override
+ {
+ ONumericControl_Base::SetModifyHandler();
+ weld::MetricSpinButton* pSpinButton = getTypedControlWindow();
+ pSpinButton->connect_value_changed( LINK( this, CommonBehaviourControlHelper, MetricModifiedHdl ) );
+ pSpinButton->get_widget().connect_changed( LINK( this, CommonBehaviourControlHelper, EditModifiedHdl ) );
+ }
+
+ private:
+ virtual weld::Widget* getWidget() override { return &getTypedControlWindow()->get_widget(); }
+
+ /** converts an API value (<code>double</code>, as passed into <code>set[Max|Min|]Value) into
+ a <code>int</code> value which can be passed to our NumericField.
+
+ The conversion respects our decimal digits as well as our value factor (<member>m_nFieldToUNOValueFactor</member>).
+ */
+ sal_Int64 impl_apiValueToFieldValue_nothrow( double nApiValue ) const;
+
+ /** converts a control value, as obtained from our Numeric field, into a value which can passed
+ to outer callers via our UNO API.
+ */
+ double impl_fieldValueToApiValue_nothrow(sal_Int64 nFieldValue) const;
+ };
+
+ //= OColorControl
+ typedef CommonBehaviourControl<css::inspection::XPropertyControl, ColorListBox> OColorControl_Base;
+ class OColorControl : public OColorControl_Base
+ {
+ public:
+ OColorControl(std::unique_ptr<ColorListBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ virtual void SetModifyHandler() override
+ {
+ OColorControl_Base::SetModifyHandler();
+ getTypedControlWindow()->SetSelectHdl(LINK(this, CommonBehaviourControlHelper, ColorModifiedHdl));
+ }
+
+ protected:
+ // CommonBehaviourControlHelper::setModified
+ virtual void setModified() override;
+
+ private:
+ virtual weld::Widget* getWidget() override { return &getTypedControlWindow()->get_widget(); }
+ };
+
+ //= OListboxControl
+ typedef CommonBehaviourControl<css::inspection::XStringListControl, weld::ComboBox> OListboxControl_Base;
+ class OListboxControl : public OListboxControl_Base
+ {
+ public:
+ OListboxControl(std::unique_ptr<weld::ComboBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ // XStringListControl
+ virtual void SAL_CALL clearList( ) override;
+ virtual void SAL_CALL prependListEntry( const OUString& NewEntry ) override;
+ virtual void SAL_CALL appendListEntry( const OUString& NewEntry ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getListEntries( ) override;
+
+ virtual void SetModifyHandler() override
+ {
+ OListboxControl_Base::SetModifyHandler();
+ getTypedControlWindow()->connect_changed(LINK(this, CommonBehaviourControlHelper, ModifiedHdl));
+ }
+
+ protected:
+ // CommonBehaviourControlHelper::setModified
+ virtual void setModified() override;
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+ };
+
+ //= OComboboxControl
+ typedef CommonBehaviourControl< css::inspection::XStringListControl, weld::ComboBox > OComboboxControl_Base;
+ class OComboboxControl final : public OComboboxControl_Base
+ {
+ public:
+ OComboboxControl(std::unique_ptr<weld::ComboBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ // XStringListControl
+ virtual void SAL_CALL clearList( ) override;
+ virtual void SAL_CALL prependListEntry( const OUString& NewEntry ) override;
+ virtual void SAL_CALL appendListEntry( const OUString& NewEntry ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getListEntries( ) override;
+
+ virtual void SetModifyHandler() override
+ {
+ OComboboxControl_Base::SetModifyHandler();
+ getTypedControlWindow()->connect_changed(LINK(this, CommonBehaviourControlHelper, ModifiedHdl));
+ }
+
+ // CommonBehaviourControlHelper::setModified
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+
+ private:
+ DECL_LINK( OnEntrySelected, weld::ComboBox&, void );
+ };
+
+
+ //= DropDownEditControl
+
+ enum MultiLineOperationMode
+ {
+ eStringList,
+ eMultiLineText
+ };
+
+ //= OMultilineEditControl
+ typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Container> OMultilineEditControl_Base;
+ class OMultilineEditControl : public OMultilineEditControl_Base
+ {
+ private:
+ MultiLineOperationMode m_nOperationMode;
+ std::unique_ptr<weld::Entry> m_xEntry;
+ std::unique_ptr<weld::MenuButton> m_xButton;
+ std::unique_ptr<weld::Widget> m_xPopover;
+ std::unique_ptr<weld::TextView> m_xTextView;
+ std::unique_ptr<weld::Button> m_xOk;
+
+ void SetTextValue(const OUString& rText);
+ OUString GetTextValue() const;
+
+ void SetStringListValue( const StlSyntaxSequence< OUString >& _rStrings );
+ StlSyntaxSequence< OUString >
+ GetStringListValue() const;
+
+ DECL_LINK(ButtonHandler, weld::Button&, void);
+ DECL_LINK(TextViewModifiedHdl, weld::TextView&, void);
+
+ void CheckEntryTextViewMisMatch();
+
+ public:
+ OMultilineEditControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, MultiLineOperationMode eMode, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+
+ virtual void editChanged() override;
+
+ virtual void SetModifyHandler() override
+ {
+ m_xEntry->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xEntry->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+ m_xButton->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xButton->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+
+ m_xEntry->connect_changed( LINK( this, CommonBehaviourControlHelper, EditModifiedHdl ) );
+ m_xTextView->connect_changed( LINK( this, OMultilineEditControl, TextViewModifiedHdl ) );
+ }
+
+ virtual void SAL_CALL disposing() override
+ {
+ m_xOk.reset();
+ m_xTextView.reset();
+ m_xButton.reset();
+ m_xEntry.reset();
+ OMultilineEditControl_Base::disposing();
+ }
+
+ };
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/stringrepresentation.cxx b/extensions/source/propctrlr/stringrepresentation.cxx
new file mode 100644
index 000000000..143ab1363
--- /dev/null
+++ b/extensions/source/propctrlr/stringrepresentation.cxx
@@ -0,0 +1,604 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/inspection/XStringRepresentation.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/XTypeConverter.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/reflection/XConstantsTypeDescription.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <connectivity/dbconversion.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <yesno.hrc>
+#include <comphelper/types.hxx>
+#include <o3tl/string_view.hxx>
+#include "modulepcr.hxx"
+
+#include <algorithm>
+
+namespace pcr{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+class StringRepresentation:
+ public ::cppu::WeakImplHelper<
+ lang::XServiceInfo,
+ inspection::XStringRepresentation,
+ lang::XInitialization>
+{
+public:
+ explicit StringRepresentation(uno::Reference< uno::XComponentContext > const & context);
+ StringRepresentation (const StringRepresentation&) = delete;
+ StringRepresentation& operator=(const StringRepresentation&) = delete;
+
+ // lang::XServiceInfo:
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString & ServiceName) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // inspection::XStringRepresentation:
+ virtual OUString SAL_CALL convertToControlValue(const uno::Any & PropertyValue) override;
+ virtual uno::Any SAL_CALL convertToPropertyValue(const OUString & ControlValue, const uno::Type & ControlValueType) override;
+
+ // lang::XInitialization:
+ virtual void SAL_CALL initialize(const uno::Sequence< uno::Any > & aArguments) override;
+
+private:
+ virtual ~StringRepresentation() override {}
+
+ /** converts a generic value into a string representation
+
+ If you want to convert values whose string representation does not depend
+ on a concrete property, use this version
+
+ @return <TRUE/>
+ if and only if the value could be converted
+ */
+ static bool convertGenericValueToString(
+ const uno::Any& _rValue,
+ OUString& _rStringRep
+ );
+
+ /** converts string representation into generic value
+
+ If you want to convert values whose string representation does not depend
+ on a concrete property, use this version
+
+ @return <TRUE/>
+ if and only if the value could be converted
+ */
+ static bool convertStringToGenericValue(
+ const OUString& _rStringRep,
+ uno::Any& _rValue,
+ const uno::Type& _rTargetType
+ );
+
+ /** uses the simple convert method from the type converter
+ *
+ * \param _rValue the value to be converted
+ * \return the converted string.
+ */
+ OUString convertSimpleToString( const uno::Any& _rValue );
+
+ /** converts a string into his constant value if it exists, otherwise the type converter is used.
+ * \param _rValue the value to be converted
+ * \param _ePropertyType the type of the property to be converted into
+ * \return the converted value
+ */
+ uno::Any convertStringToSimple( const OUString& _rValue,const uno::TypeClass& _ePropertyType );
+
+ uno::Reference< uno::XComponentContext > m_xContext;
+ uno::Reference< script::XTypeConverter > m_xTypeConverter;
+ uno::Reference< reflection::XConstantsTypeDescription > m_xTypeDescription;
+ uno::Sequence< OUString > m_aValues;
+ uno::Sequence< uno::Reference< reflection::XConstantTypeDescription> > m_aConstants;
+
+};
+
+}
+
+StringRepresentation::StringRepresentation(uno::Reference< uno::XComponentContext > const & context) :
+ m_xContext(context)
+{}
+
+// com.sun.star.uno.XServiceInfo:
+OUString SAL_CALL StringRepresentation::getImplementationName()
+{
+ return "StringRepresentation";
+}
+
+sal_Bool SAL_CALL StringRepresentation::supportsService(OUString const & serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+uno::Sequence< OUString > SAL_CALL StringRepresentation::getSupportedServiceNames()
+{
+ return { "com.sun.star.inspection.StringRepresentation" };
+}
+
+// inspection::XStringRepresentation:
+OUString SAL_CALL StringRepresentation::convertToControlValue(const uno::Any & PropertyValue)
+{
+ OUString sReturn;
+ if ( !convertGenericValueToString( PropertyValue, sReturn ) )
+ {
+ sReturn = convertSimpleToString( PropertyValue );
+#ifdef DBG_UTIL
+ if ( sReturn.isEmpty() && PropertyValue.hasValue() )
+ {
+ SAL_WARN( "extensions.propctrlr", "StringRepresentation::convertPropertyValueToStringRepresentation: cannot convert values of type '"
+ << PropertyValue.getValueType().getTypeName()
+ << "'!" );
+ }
+#endif
+ }
+
+ return sReturn;
+}
+
+uno::Any SAL_CALL StringRepresentation::convertToPropertyValue(const OUString & ControlValue, const uno::Type & ControlValueType)
+{
+ uno::Any aReturn;
+
+ uno::TypeClass ePropertyType = ControlValueType.getTypeClass();
+ switch ( ePropertyType )
+ {
+ case uno::TypeClass_FLOAT:
+ case uno::TypeClass_DOUBLE:
+ case uno::TypeClass_BYTE:
+ case uno::TypeClass_SHORT:
+ case uno::TypeClass_LONG:
+ case uno::TypeClass_HYPER:
+ case uno::TypeClass_UNSIGNED_SHORT:
+ case uno::TypeClass_UNSIGNED_LONG:
+ case uno::TypeClass_UNSIGNED_HYPER:
+ try
+ {
+ aReturn = convertStringToSimple(ControlValue, ePropertyType);
+ }
+ catch( const script::CannotConvertException& ) { }
+ catch( const lang::IllegalArgumentException& ) { }
+ break;
+
+ default:
+ #if OSL_DEBUG_LEVEL > 0
+ bool bCanConvert =
+ #endif
+ convertStringToGenericValue( ControlValue, aReturn, ControlValueType );
+
+ #if OSL_DEBUG_LEVEL > 0
+ // could not convert ...
+ if ( !bCanConvert && !ControlValue.isEmpty() )
+ {
+ SAL_WARN( "extensions.propctrlr", "StringRepresentation::convertStringRepresentationToPropertyValue: cannot convert into values of type '"
+ << ControlValueType.getTypeName() << "'!" );
+ }
+ #endif
+ }
+
+ return aReturn;
+}
+
+namespace {
+
+// This comparison functor assumes an underlying set of constants with pairwise
+// unequal values that are all of UNO SHORT or LONG type:
+struct CompareConstants {
+ bool operator ()(
+ css::uno::Reference< css::reflection::XConstantTypeDescription > const &
+ c1,
+ css::uno::Reference< css::reflection::XConstantTypeDescription > const &
+ c2) const
+ {
+ return c1->getConstantValue().get<sal_Int32>()
+ < c2->getConstantValue().get<sal_Int32>();
+ }
+};
+
+}
+
+// lang::XInitialization:
+void SAL_CALL StringRepresentation::initialize(const uno::Sequence< uno::Any > & aArguments)
+{
+ sal_Int32 nLength = aArguments.getLength();
+ if ( !nLength )
+ return;
+
+ const uno::Any* pIter = aArguments.getConstArray();
+ m_xTypeConverter.set(*pIter++,uno::UNO_QUERY);
+ if ( nLength != 3 )
+ return;
+
+ OUString sConstantName;
+ *pIter++ >>= sConstantName;
+ *pIter >>= m_aValues;
+
+ if ( !m_xContext.is() )
+ return;
+
+ uno::Reference< container::XHierarchicalNameAccess > xTypeDescProv(
+ m_xContext->getValueByName("/singletons/com.sun.star.reflection.theTypeDescriptionManager"),
+ uno::UNO_QUERY_THROW );
+
+ m_xTypeDescription.set( xTypeDescProv->getByHierarchicalName( sConstantName ), uno::UNO_QUERY_THROW );
+ uno::Sequence<
+ uno::Reference< reflection::XConstantTypeDescription > >
+ cs(m_xTypeDescription->getConstants());
+ auto [begin, end] = asNonConstRange(cs);
+ std::sort(begin, end, CompareConstants());
+ m_aConstants = cs;
+}
+
+OUString StringRepresentation::convertSimpleToString( const uno::Any& _rValue )
+{
+ OUString sReturn;
+ if ( m_xTypeConverter.is() && _rValue.hasValue() )
+ {
+ try
+ {
+ if ( m_aConstants.hasElements() )
+ {
+ sal_Int16 nConstantValue = 0;
+ if ( _rValue >>= nConstantValue )
+ {
+ const uno::Reference< reflection::XConstantTypeDescription>* pIter = m_aConstants.getConstArray();
+ const uno::Reference< reflection::XConstantTypeDescription>* pEnd = pIter + m_aConstants.getLength();
+ for(sal_Int32 i = 0;pIter != pEnd;++pIter,++i)
+ {
+ if ( (*pIter)->getConstantValue() == _rValue )
+ {
+ OSL_ENSURE(i < m_aValues.getLength() ,"StringRepresentation::convertSimpleToString: Index is not in range of m_aValues");
+ sReturn = m_aValues[i];
+ break;
+ }
+ }
+ }
+ }
+
+ if ( sReturn.isEmpty() )
+ m_xTypeConverter->convertToSimpleType( _rValue, uno::TypeClass_STRING ) >>= sReturn;
+ }
+ catch( const script::CannotConvertException& ) { }
+ catch( const lang::IllegalArgumentException& ) { }
+ }
+ return sReturn;
+}
+
+
+namespace
+{
+ struct ConvertIntegerFromAndToString
+ {
+ OUString operator()( sal_Int32 _rIntValue ) const
+ {
+ return OUString::number( _rIntValue );
+ }
+ sal_Int32 operator()( std::u16string_view _rStringValue ) const
+ {
+ return o3tl::toInt32(_rStringValue);
+ }
+ };
+
+ struct StringIdentity
+ {
+ OUString operator()( const OUString& _rValue ) const
+ {
+ return _rValue;
+ }
+ };
+
+ template < class ElementType, class Transformer >
+ OUString composeSequenceElements( const Sequence< ElementType >& _rElements, const Transformer& _rTransformer )
+ {
+ OUStringBuffer sCompose;
+
+ // loop through the elements and concatenate the string representations of the integers
+ // (separated by a line break)
+ for (const auto& rElement : _rElements)
+ {
+ sCompose.append(OUString(_rTransformer(rElement)));
+ sCompose.append("\n");
+ }
+ sCompose.stripEnd('\n');
+
+ return sCompose.makeStringAndClear();
+ }
+
+ template < class ElementType, class Transformer >
+ void splitComposedStringToSequence( std::u16string_view _rComposed, Sequence< ElementType >& _out_SplitUp, const Transformer& _rTransformer )
+ {
+ _out_SplitUp.realloc( 0 );
+ if ( _rComposed.empty() )
+ return;
+ sal_Int32 tokenPos = 0;
+ do
+ {
+ _out_SplitUp.realloc( _out_SplitUp.getLength() + 1 );
+ _out_SplitUp.getArray()[ _out_SplitUp.getLength() - 1 ] = static_cast<ElementType>(_rTransformer( OUString(o3tl::getToken(_rComposed, 0, '\n', tokenPos )) ));
+ }
+ while ( tokenPos != -1 );
+ }
+}
+
+
+bool StringRepresentation::convertGenericValueToString( const uno::Any& _rValue, OUString& _rStringRep )
+{
+ bool bCanConvert = true;
+
+ switch ( _rValue.getValueTypeClass() )
+ {
+ case uno::TypeClass_STRING:
+ _rValue >>= _rStringRep;
+ break;
+
+ case uno::TypeClass_BOOLEAN:
+ {
+ bool bValue = false;
+ _rValue >>= bValue;
+ _rStringRep = bValue ? PcrRes(RID_RSC_ENUM_YESNO[1])
+ : PcrRes(RID_RSC_ENUM_YESNO[0]);
+ }
+ break;
+
+ // some sequence types
+ case uno::TypeClass_SEQUENCE:
+ {
+ Sequence< OUString > aStringValues;
+ Sequence< sal_Int8 > aInt8Values;
+ Sequence< sal_uInt16 > aUInt16Values;
+ Sequence< sal_Int16 > aInt16Values;
+ Sequence< sal_uInt32 > aUInt32Values;
+ Sequence< sal_Int32 > aInt32Values;
+
+ // string sequences
+ if ( _rValue >>= aStringValues )
+ {
+ _rStringRep = composeSequenceElements( aStringValues, StringIdentity() );
+ }
+ // byte sequences
+ else if ( _rValue >>= aInt8Values )
+ {
+ _rStringRep = composeSequenceElements( aInt8Values, ConvertIntegerFromAndToString() );
+ }
+ // uInt16 sequences
+ else if ( _rValue >>= aUInt16Values )
+ {
+ _rStringRep = composeSequenceElements( aUInt16Values, ConvertIntegerFromAndToString() );
+ }
+ // Int16 sequences
+ else if ( _rValue >>= aInt16Values )
+ {
+ _rStringRep = composeSequenceElements( aInt16Values, ConvertIntegerFromAndToString() );
+ }
+ // uInt32 sequences
+ else if ( _rValue >>= aUInt32Values )
+ {
+ _rStringRep = composeSequenceElements( aUInt32Values, ConvertIntegerFromAndToString() );
+ }
+ // Int32 sequences
+ else if ( _rValue >>= aInt32Values )
+ {
+ _rStringRep = composeSequenceElements( aInt32Values, ConvertIntegerFromAndToString() );
+ }
+ else
+ bCanConvert = false;
+ }
+ break;
+ case uno::TypeClass_CONSTANT:
+ break;
+
+ // some structs
+ case uno::TypeClass_STRUCT:
+ OSL_FAIL( "StringRepresentation::convertGenericValueToString(STRUCT): this is dead code - isn't it?" );
+ if ( _rValue.getValueType().equals( cppu::UnoType< util::Date >::get() ))
+ {
+ // weird enough, the string representation of dates, as used
+ // by the control displaying dates, and thus as passed through the layers,
+ // is YYYYMMDD.
+ util::Date aUnoDate;
+ _rValue >>= aUnoDate;
+ _rStringRep = ::dbtools::DBTypeConversion::toDateString(aUnoDate);
+ }
+ else if ( _rValue.getValueType().equals( cppu::UnoType< util::Time >::get() ))
+ {
+ // similar for time (HHMMSSHH)
+ util::Time aUnoTime;
+ _rValue >>= aUnoTime;
+ _rStringRep = ::dbtools::DBTypeConversion::toTimeString(aUnoTime);
+ }
+ else if ( _rValue.getValueType().equals( cppu::UnoType< util::DateTime >::get() ))
+ {
+ util::DateTime aUnoDateTime;
+ _rValue >>= aUnoDateTime;
+ _rStringRep = ::dbtools::DBTypeConversion::toDateTimeString(aUnoDateTime);
+ }
+ else
+ bCanConvert = false;
+ break;
+
+ default:
+ bCanConvert = false;
+ break;
+ }
+
+ return bCanConvert;
+}
+
+uno::Any StringRepresentation::convertStringToSimple( const OUString& _rValue,const uno::TypeClass& _ePropertyType )
+{
+ uno::Any aReturn;
+ if ( m_xTypeConverter.is() && !_rValue.isEmpty() )
+ {
+ try
+ {
+ if ( m_aConstants.hasElements() && m_aValues.hasElements() )
+ {
+ const OUString* pIter = m_aValues.getConstArray();
+ const OUString* pEnd = pIter + m_aValues.getLength();
+ for(sal_Int32 i = 0;pIter != pEnd;++pIter,++i)
+ {
+ if ( *pIter == _rValue )
+ {
+ OSL_ENSURE(i < m_aConstants.getLength() ,"StringRepresentation::convertSimpleToString: Index is not in range of m_aValues");
+ aReturn = m_aConstants[i]->getConstantValue();
+ break;
+ }
+ }
+ }
+
+ if ( !aReturn.hasValue() )
+ aReturn = m_xTypeConverter->convertToSimpleType( Any( _rValue ), _ePropertyType );
+ }
+ catch( const script::CannotConvertException& ) { }
+ catch( const lang::IllegalArgumentException& ) { }
+ }
+ return aReturn;
+}
+
+bool StringRepresentation::convertStringToGenericValue( const OUString& _rStringRep, uno::Any& _rValue, const uno::Type& _rTargetType )
+{
+ bool bCanConvert = true;
+
+ switch ( _rTargetType.getTypeClass() )
+ {
+ case uno::TypeClass_STRING:
+ _rValue <<= _rStringRep;
+ break;
+
+ case uno::TypeClass_BOOLEAN:
+ {
+ _rValue <<= PcrRes(RID_RSC_ENUM_YESNO[0]) != _rStringRep;
+ }
+ break;
+
+ case uno::TypeClass_SEQUENCE:
+ {
+ uno::Type aElementType = ::comphelper::getSequenceElementType( _rTargetType );
+
+ switch ( aElementType.getTypeClass() )
+ {
+ case uno::TypeClass_STRING:
+ {
+ Sequence< OUString > aElements;
+ splitComposedStringToSequence( _rStringRep, aElements, StringIdentity() );
+ _rValue <<= aElements;
+ }
+ break;
+ case uno::TypeClass_SHORT:
+ {
+ Sequence< sal_Int16 > aElements;
+ splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
+ _rValue <<= aElements;
+ }
+ break;
+ case uno::TypeClass_UNSIGNED_SHORT:
+ {
+ Sequence< sal_uInt16 > aElements;
+ splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
+ _rValue <<= aElements;
+ }
+ break;
+ case uno::TypeClass_LONG:
+ {
+ Sequence< sal_Int32 > aElements;
+ splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
+ _rValue <<= aElements;
+ }
+ break;
+ case uno::TypeClass_UNSIGNED_LONG:
+ {
+ Sequence< sal_uInt32 > aElements;
+ splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
+ _rValue <<= aElements;
+ }
+ break;
+ case uno::TypeClass_BYTE:
+ {
+ Sequence< sal_Int8 > aElements;
+ splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
+ _rValue <<= aElements;
+ }
+ break;
+ default:
+ bCanConvert = false;
+ break;
+ }
+ }
+ break;
+
+ case uno::TypeClass_STRUCT:
+ OSL_FAIL( "StringRepresentation::convertStringToGenericValue(STRUCT): this is dead code - isn't it?" );
+ if ( _rTargetType.equals( cppu::UnoType< util::Date >::get() ))
+ {
+ // weird enough, the string representation of dates, as used
+ // by the control displaying dates, and thus as passed through the layers,
+ // is YYYYMMDD.
+
+ _rValue <<= ::dbtools::DBTypeConversion::toDate(_rStringRep);
+ }
+ else if ( _rTargetType.equals( cppu::UnoType< util::Time >::get() ))
+ {
+ // similar for time (HHMMSSHH)
+ _rValue <<= ::dbtools::DBTypeConversion::toTime(_rStringRep);
+ }
+ else if ( _rTargetType.equals( cppu::UnoType< util::DateTime >::get() ))
+ {
+ _rValue <<= ::dbtools::DBTypeConversion::toDateTime(_rStringRep);
+ }
+ else
+ bCanConvert = false;
+ break;
+
+ default:
+ bCanConvert = false;
+ break;
+ }
+
+ return bCanConvert;
+}
+
+
+} // pcr
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_StringRepresentation_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::StringRepresentation(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/submissionhandler.cxx b/extensions/source/propctrlr/submissionhandler.cxx
new file mode 100644
index 000000000..87609e905
--- /dev/null
+++ b/extensions/source/propctrlr/submissionhandler.cxx
@@ -0,0 +1,431 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "submissionhandler.hxx"
+#include "formmetadata.hxx"
+#include "formstrings.hxx"
+#include "handlerhelper.hxx"
+
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/form/submission/XSubmissionSupplier.hpp>
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+
+namespace pcr
+{
+
+
+ using namespace ::comphelper;
+ 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::script;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::xforms;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::inspection;
+
+
+ //= SubmissionHelper
+
+
+ SubmissionHelper::SubmissionHelper( ::osl::Mutex& _rMutex, const Reference< XPropertySet >& _rxIntrospectee, const Reference< frame::XModel >& _rxContextDocument )
+ :EFormsHelper( _rMutex, _rxIntrospectee, _rxContextDocument )
+ {
+ OSL_ENSURE( canTriggerSubmissions( _rxIntrospectee, _rxContextDocument ),
+ "SubmissionHelper::SubmissionHelper: you should not have instantiated me!" );
+ }
+
+
+ bool SubmissionHelper::canTriggerSubmissions( const Reference< XPropertySet >& _rxControlModel,
+ const Reference< frame::XModel >& _rxContextDocument )
+ {
+ if ( !EFormsHelper::isEForm( _rxContextDocument ) )
+ return false;
+
+ try
+ {
+ Reference< submission::XSubmissionSupplier > xSubmissionSupp( _rxControlModel, UNO_QUERY );
+ if ( xSubmissionSupp.is() )
+ return true;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "SubmissionHelper::canTriggerSubmissions" );
+ }
+ return false;
+ }
+
+
+ //= SubmissionPropertyHandler
+
+
+ SubmissionPropertyHandler::SubmissionPropertyHandler( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandlerComponent( _rxContext )
+ ,OPropertyChangeListener( m_aMutex )
+ {
+ }
+
+
+ SubmissionPropertyHandler::~SubmissionPropertyHandler( )
+ {
+ disposeAdapter();
+ }
+
+
+ OUString SubmissionPropertyHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.SubmissionPropertyHandler";
+ }
+
+
+ Sequence< OUString > SubmissionPropertyHandler::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.inspection.SubmissionPropertyHandler" };
+ }
+
+
+ Any SAL_CALL SubmissionPropertyHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ OSL_ENSURE(m_pHelper, "SubmissionPropertyHandler::getPropertyValue: inconsistency!");
+ // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties
+
+ Any aReturn;
+ try
+ {
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_SUBMISSION_ID:
+ {
+ Reference< submission::XSubmissionSupplier > xSubmissionSupp( m_xComponent, UNO_QUERY );
+ OSL_ENSURE( xSubmissionSupp.is(), "SubmissionPropertyHandler::getPropertyValue: this should never happen ..." );
+ // this handler is not intended for components which are no XSubmissionSupplier
+ Reference< submission::XSubmission > xSubmission;
+ if ( xSubmissionSupp.is() )
+ xSubmission = xSubmissionSupp->getSubmission( );
+ aReturn <<= xSubmission;
+ }
+ break;
+
+ case PROPERTY_ID_XFORMS_BUTTONTYPE:
+ {
+ FormButtonType eType = FormButtonType_PUSH;
+ OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_BUTTONTYPE ) >>= eType );
+ if ( ( eType != FormButtonType_PUSH ) && ( eType != FormButtonType_SUBMIT ) )
+ eType = FormButtonType_PUSH;
+ aReturn <<= eType;
+ }
+ break;
+
+ default:
+ OSL_FAIL( "SubmissionPropertyHandler::getPropertyValue: cannot handle this property!" );
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "SubmissionPropertyHandler::getPropertyValue" );
+ }
+
+ return aReturn;
+ }
+
+
+ void SAL_CALL SubmissionPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ OSL_ENSURE(m_pHelper, "SubmissionPropertyHandler::setPropertyValue: inconsistency!");
+ // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties
+
+ try
+ {
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_SUBMISSION_ID:
+ {
+ Reference< submission::XSubmission > xSubmission;
+ OSL_VERIFY( _rValue >>= xSubmission );
+
+ Reference< submission::XSubmissionSupplier > xSubmissionSupp( m_xComponent, UNO_QUERY );
+ OSL_ENSURE( xSubmissionSupp.is(), "SubmissionPropertyHandler::setPropertyValue: this should never happen ..." );
+ // this handler is not intended for components which are no XSubmissionSupplier
+ if ( xSubmissionSupp.is() )
+ {
+ xSubmissionSupp->setSubmission( xSubmission );
+ impl_setContextDocumentModified_nothrow();
+ }
+ }
+ break;
+
+ case PROPERTY_ID_XFORMS_BUTTONTYPE:
+ m_xComponent->setPropertyValue( PROPERTY_BUTTONTYPE, _rValue );
+ break;
+
+ default:
+ OSL_FAIL( "SubmissionPropertyHandler::setPropertyValue: cannot handle this id!" );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "SubmissionPropertyHandler::setPropertyValue" );
+ }
+ }
+
+
+ Sequence< OUString > SAL_CALL SubmissionPropertyHandler::getActuatingProperties( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pHelper)
+ return Sequence< OUString >();
+
+ Sequence<OUString> aReturn { PROPERTY_XFORMS_BUTTONTYPE };
+ return aReturn;
+ }
+
+
+ Sequence< OUString > SAL_CALL SubmissionPropertyHandler::getSupersededProperties( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pHelper)
+ return Sequence< OUString >();
+
+ Sequence< OUString > aReturn{ PROPERTY_TARGET_URL,
+ PROPERTY_TARGET_FRAME,
+ PROPERTY_BUTTONTYPE };
+ return aReturn;
+ }
+
+
+ void SubmissionPropertyHandler::onNewComponent()
+ {
+ if ( m_xPropChangeMultiplexer.is() )
+ {
+ m_xPropChangeMultiplexer->dispose();
+ m_xPropChangeMultiplexer.clear();
+ }
+
+ PropertyHandlerComponent::onNewComponent();
+
+ Reference< frame::XModel > xDocument( impl_getContextDocument_nothrow() );
+ DBG_ASSERT( xDocument.is(), "SubmissionPropertyHandler::onNewComponent: no document!" );
+
+ m_pHelper.reset();
+
+ if ( SubmissionHelper::canTriggerSubmissions( m_xComponent, xDocument ) )
+ {
+ m_pHelper.reset( new SubmissionHelper( m_aMutex, m_xComponent, xDocument ) );
+
+ m_xPropChangeMultiplexer = new OPropertyChangeMultiplexer( this, m_xComponent );
+ m_xPropChangeMultiplexer->addProperty( PROPERTY_BUTTONTYPE );
+ }
+ }
+
+
+ Sequence< Property > SubmissionPropertyHandler::doDescribeSupportedProperties() const
+ {
+ std::vector< Property > aProperties;
+ if (m_pHelper)
+ {
+ implAddPropertyDescription( aProperties, PROPERTY_SUBMISSION_ID, cppu::UnoType<submission::XSubmission>::get() );
+ implAddPropertyDescription( aProperties, PROPERTY_XFORMS_BUTTONTYPE, ::cppu::UnoType<FormButtonType>::get() );
+ }
+ if ( aProperties.empty() )
+ return Sequence< Property >();
+ return comphelper::containerToSequence(aProperties);
+ }
+
+
+ LineDescriptor SAL_CALL SubmissionPropertyHandler::describePropertyLine( const OUString& _rPropertyName,
+ const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !_rxControlFactory.is() )
+ throw NullPointerException();
+ if (!m_pHelper)
+ throw RuntimeException();
+
+ std::vector< OUString > aListEntries;
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_SUBMISSION_ID:
+ m_pHelper->getAllElementUINames(EFormsHelper::Submission, aListEntries, false);
+ break;
+
+ case PROPERTY_ID_XFORMS_BUTTONTYPE:
+ {
+ // available options are nearly the same as for the "normal" button type, but only the
+ // first two options
+ aListEntries = m_pInfoService->getPropertyEnumRepresentations( PROPERTY_ID_BUTTONTYPE );
+ aListEntries.resize( 2 );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "SubmissionPropertyHandler::describePropertyLine: cannot handle this id!" );
+ return LineDescriptor();
+ }
+
+ LineDescriptor aDescriptor;
+ aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory, std::move(aListEntries), false, true );
+ aDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( nPropId );
+ aDescriptor.Category = "General";
+ aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) );
+ return aDescriptor;
+ }
+
+
+ void SAL_CALL SubmissionPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) );
+ OSL_PRECOND(m_pHelper,
+ "SubmissionPropertyHandler::actuatingPropertyChanged: inconsistency!");
+ // if we survived impl_getPropertyId_throwRuntime, we should have a helper, since no helper implies no properties
+
+ switch ( nActuatingPropId )
+ {
+ case PROPERTY_ID_XFORMS_BUTTONTYPE:
+ {
+ FormButtonType eType = FormButtonType_PUSH;
+ OSL_VERIFY( _rNewValue >>= eType );
+ _rxInspectorUI->enablePropertyUI( PROPERTY_SUBMISSION_ID, eType == FormButtonType_SUBMIT );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "SubmissionPropertyHandler::actuatingPropertyChanged: cannot handle this id!" );
+ }
+ }
+
+
+ Any SAL_CALL SubmissionPropertyHandler::convertToPropertyValue( const OUString& _rPropertyName, const Any& _rControlValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Any aPropertyValue;
+
+ OSL_ENSURE(
+ m_pHelper,
+ "SubmissionPropertyHandler::convertToPropertyValue: we have no SupportedProperties!");
+ if (!m_pHelper)
+ return aPropertyValue;
+
+ OUString sControlValue;
+ OSL_VERIFY( _rControlValue >>= sControlValue );
+
+ PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) );
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_SUBMISSION_ID:
+ {
+ Reference< XSubmission > xSubmission( m_pHelper->getModelElementFromUIName( EFormsHelper::Submission, sControlValue ), UNO_QUERY );
+ aPropertyValue <<= xSubmission;
+ }
+ break;
+
+ case PROPERTY_ID_XFORMS_BUTTONTYPE:
+ {
+ ::rtl::Reference< IPropertyEnumRepresentation > aEnumConversion(
+ new DefaultEnumRepresentation( *m_pInfoService, ::cppu::UnoType<FormButtonType>::get(), PROPERTY_ID_BUTTONTYPE ) );
+ // TODO/UNOize: make aEnumConversion a member?
+ aEnumConversion->getValueFromDescription( sControlValue, aPropertyValue );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "SubmissionPropertyHandler::convertToPropertyValue: cannot handle this id!" );
+ }
+
+ return aPropertyValue;
+ }
+
+
+ Any SAL_CALL SubmissionPropertyHandler::convertToControlValue( const OUString& _rPropertyName, const Any& _rPropertyValue, const Type& _rControlValueType )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Any aControlValue;
+
+ OSL_ENSURE(
+ m_pHelper,
+ "SubmissionPropertyHandler::convertToControlValue: we have no SupportedProperties!");
+ if (!m_pHelper)
+ return aControlValue;
+
+ OSL_ENSURE( _rControlValueType.getTypeClass() == TypeClass_STRING,
+ "SubmissionPropertyHandler::convertToControlValue: all our controls should use strings for value exchange!" );
+
+ PropertyId nPropId( m_pInfoService->getPropertyId( _rPropertyName ) );
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_SUBMISSION_ID:
+ {
+ Reference< XPropertySet > xSubmission( _rPropertyValue, UNO_QUERY );
+ if ( xSubmission.is() )
+ aControlValue <<= EFormsHelper::getModelElementUIName( EFormsHelper::Submission, xSubmission );
+ }
+ break;
+
+ case PROPERTY_ID_XFORMS_BUTTONTYPE:
+ {
+ ::rtl::Reference< IPropertyEnumRepresentation > aEnumConversion(
+ new DefaultEnumRepresentation( *m_pInfoService, _rPropertyValue.getValueType(), PROPERTY_ID_BUTTONTYPE ) );
+ // TODO/UNOize: make aEnumConversion a member?
+ aControlValue <<= aEnumConversion->getDescriptionForValue( _rPropertyValue );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "SubmissionPropertyHandler::convertToControlValue: cannot handle this id!" );
+ }
+
+ return aControlValue;
+ }
+
+
+ void SubmissionPropertyHandler::_propertyChanged( const PropertyChangeEvent& _rEvent )
+ {
+ if ( _rEvent.PropertyName == PROPERTY_BUTTONTYPE )
+ firePropertyChange( PROPERTY_XFORMS_BUTTONTYPE, PROPERTY_ID_XFORMS_BUTTONTYPE, _rEvent.OldValue, _rEvent.NewValue );
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_SubmissionPropertyHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::SubmissionPropertyHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/submissionhandler.hxx b/extensions/source/propctrlr/submissionhandler.hxx
new file mode 100644
index 000000000..f263041b2
--- /dev/null
+++ b/extensions/source/propctrlr/submissionhandler.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 <memory>
+#include "propertyhandler.hxx"
+#include "eformshelper.hxx"
+
+#include <comphelper/propmultiplex.hxx>
+#include <rtl/ref.hxx>
+
+namespace comphelper
+{
+ class OPropertyChangeMultiplexer;
+}
+
+
+namespace pcr
+{
+
+
+ //= SubmissionHelper
+
+ class SubmissionHelper : public EFormsHelper
+ {
+ public:
+ SubmissionHelper(
+ osl::Mutex& _rMutex,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxIntrospectee,
+ const css::uno::Reference< css::frame::XModel >& _rxContextDocument
+ );
+
+ /** determines whether the given control model is able to trigger submissions
+
+ Instances of the <type>SubmissionHelper</type> class should not be instantiated
+ for components where this method returned <FALSE/>
+ */
+ static bool canTriggerSubmissions(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel,
+ const css::uno::Reference< css::frame::XModel >& _rxContextDocument
+ );
+ };
+
+
+ //= SubmissionPropertyHandler
+
+ /** a property handler for any virtual string properties
+ */
+ class SubmissionPropertyHandler : public PropertyHandlerComponent, public ::comphelper::OPropertyChangeListener
+ {
+ private:
+ std::unique_ptr< SubmissionHelper > m_pHelper;
+ rtl::Reference<::comphelper::OPropertyChangeMultiplexer> m_xPropChangeMultiplexer;
+
+ public:
+ explicit SubmissionPropertyHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ virtual ~SubmissionPropertyHandler() override;
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
+
+ // XPropertyHandler overriables
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Sequence< OUString >
+ SAL_CALL getActuatingProperties( ) override;
+ virtual css::uno::Sequence< OUString >
+ SAL_CALL getSupersededProperties( ) override;
+ virtual css::inspection::LineDescriptor
+ SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) override;
+ virtual css::uno::Any SAL_CALL convertToPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rControlValue ) override;
+ virtual css::uno::Any SAL_CALL convertToControlValue( const OUString& _rPropertyName, const css::uno::Any& _rPropertyValue, const css::uno::Type& _rControlValueType ) override;
+
+ // PropertyHandler overridables
+ virtual css::uno::Sequence< css::beans::Property >
+ doDescribeSupportedProperties() const override;
+ virtual void onNewComponent() override;
+
+ private:
+ // OPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& _rEvent) override;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/taborder.cxx b/extensions/source/propctrlr/taborder.cxx
new file mode 100644
index 000000000..6ada66b5a
--- /dev/null
+++ b/extensions/source/propctrlr/taborder.cxx
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "taborder.hxx"
+
+#include <bitmaps.hlst>
+#include "formstrings.hxx"
+#include <comphelper/types.hxx>
+#include <comphelper/property.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/runtime/FormController.hpp>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace pcr
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::datatransfer;
+
+ namespace {
+
+ OUString GetImage( const Reference< XPropertySet >& _rxSet )
+ {
+ OUString sImageId = RID_EXTBMP_CONTROL;
+ // TODO: classify controls also in Basic propbrw
+ if ( _rxSet.is() && ::comphelper::hasProperty( PROPERTY_CLASSID, _rxSet ) )
+ {
+ switch( ::comphelper::getINT16( _rxSet->getPropertyValue( PROPERTY_CLASSID ) ) )
+ {
+ case FormComponentType::COMMANDBUTTON: sImageId = RID_EXTBMP_BUTTON; break;
+ case FormComponentType::FIXEDTEXT: sImageId = RID_EXTBMP_FIXEDTEXT; break;
+ case FormComponentType::TEXTFIELD: sImageId = RID_EXTBMP_EDITBOX; break;
+ case FormComponentType::RADIOBUTTON: sImageId = RID_EXTBMP_RADIOBUTTON; break;
+ case FormComponentType::CHECKBOX: sImageId = RID_EXTBMP_CHECKBOX; break;
+ case FormComponentType::LISTBOX: sImageId = RID_EXTBMP_LISTBOX; break;
+ case FormComponentType::COMBOBOX: sImageId = RID_EXTBMP_COMBOBOX; break;
+ case FormComponentType::GROUPBOX: sImageId = RID_EXTBMP_GROUPBOX; break;
+ case FormComponentType::IMAGEBUTTON: sImageId = RID_EXTBMP_IMAGEBUTTON; break;
+ case FormComponentType::FILECONTROL: sImageId = RID_EXTBMP_FILECONTROL; break;
+ case FormComponentType::HIDDENCONTROL: sImageId = RID_EXTBMP_HIDDEN; break;
+ case FormComponentType::DATEFIELD: sImageId = RID_EXTBMP_DATEFIELD; break;
+ case FormComponentType::TIMEFIELD: sImageId = RID_EXTBMP_TIMEFIELD; break;
+ case FormComponentType::NUMERICFIELD: sImageId = RID_EXTBMP_NUMERICFIELD; break;
+ case FormComponentType::CURRENCYFIELD: sImageId = RID_EXTBMP_CURRENCYFIELD; break;
+ case FormComponentType::PATTERNFIELD: sImageId = RID_EXTBMP_PATTERNFIELD; break;
+ case FormComponentType::IMAGECONTROL: sImageId = RID_EXTBMP_IMAGECONTROL; break;
+ case FormComponentType::GRIDCONTROL: sImageId = RID_EXTBMP_GRID; break;
+ case FormComponentType::SCROLLBAR: sImageId = RID_EXTBMP_SCROLLBAR; break;
+ case FormComponentType::SPINBUTTON: sImageId = RID_EXTBMP_SPINBUTTON; break;
+ case FormComponentType::NAVIGATIONBAR: sImageId = RID_EXTBMP_NAVIGATIONBAR; break;
+ default:
+ OSL_FAIL( "TabOrderDialog::GetImage: unknown control type" );
+ }
+ }
+
+ return sImageId;
+ }
+
+ //= OSimpleTabModel
+
+ class OSimpleTabModel : public ::cppu::WeakImplHelper< XTabControllerModel>
+ {
+ Sequence< Reference< XControlModel > > m_aModels;
+
+ public:
+ explicit OSimpleTabModel( const Sequence< Reference< XControlModel > >& _rModels )
+ :m_aModels( _rModels )
+ {
+ }
+
+ // XTabControllerModel
+ virtual void SAL_CALL setControlModels(const Sequence< Reference< XControlModel > >& rModels) override {m_aModels = rModels;}
+ virtual Sequence< Reference< XControlModel > > SAL_CALL getControlModels() override {return m_aModels;}
+ virtual void SAL_CALL setGroup(const Sequence< Reference< XControlModel > >& /*Group*/, const OUString& /*GroupName*/) override {}
+ virtual sal_Int32 SAL_CALL getGroupCount() override {return 0;}
+ virtual void SAL_CALL getGroup(sal_Int32 /*nGroup*/, Sequence< Reference< XControlModel > >& /*Group*/, OUString& /*Name*/) override {}
+ virtual void SAL_CALL getGroupByName(const OUString& /*Name*/, Sequence< Reference< XControlModel > >& /*Group*/) override {}
+ virtual sal_Bool SAL_CALL getGroupControl() override {return false;} ;
+ virtual void SAL_CALL setGroupControl(sal_Bool /*GroupControl*/) override {};
+ };
+
+ }
+
+ //= TabOrderDialog
+ TabOrderDialog::TabOrderDialog(weld::Window* _pParent, const Reference< XTabControllerModel >& _rxTabModel,
+ const Reference< XControlContainer >& _rxControlCont, const Reference< XComponentContext >& _rxORB)
+ : GenericDialogController( _pParent, "modules/spropctrlr/ui/taborder.ui", "TabOrderDialog")
+ , m_xModel( _rxTabModel )
+ , m_xControlContainer( _rxControlCont )
+ , m_xORB( _rxORB )
+ , m_xLB_Controls(m_xBuilder->weld_tree_view("CTRLtree"))
+ , m_xPB_OK(m_xBuilder->weld_button("ok"))
+ , m_xPB_MoveUp(m_xBuilder->weld_button("upB"))
+ , m_xPB_MoveDown(m_xBuilder->weld_button("downB"))
+ , m_xPB_AutoOrder(m_xBuilder->weld_button("autoB"))
+ {
+ m_xLB_Controls->set_size_request(m_xLB_Controls->get_approximate_digit_width() * 60,
+ m_xLB_Controls->get_height_rows(10));
+ m_xLB_Controls->set_selection_mode(SelectionMode::Multiple);
+
+ m_xLB_Controls->connect_model_changed(LINK(this, TabOrderDialog, ModelHasMoved));
+ m_xPB_MoveUp->connect_clicked( LINK( this, TabOrderDialog, MoveUpClickHdl ) );
+ m_xPB_MoveDown->connect_clicked( LINK( this, TabOrderDialog, MoveDownClickHdl ) );
+ m_xPB_AutoOrder->connect_clicked( LINK( this, TabOrderDialog, AutoOrderClickHdl ) );
+ m_xPB_OK->connect_clicked( LINK( this, TabOrderDialog, OKClickHdl ) );
+ m_xPB_OK->set_sensitive(false);
+
+ if ( m_xModel.is() )
+ m_xTempModel = new OSimpleTabModel( m_xModel->getControlModels() );
+
+ if ( m_xTempModel.is() && m_xControlContainer.is() )
+ FillList();
+
+ if (m_xLB_Controls->n_children() < 2)
+ {
+ m_xPB_MoveUp->set_sensitive(false);
+ m_xPB_MoveDown->set_sensitive(false);
+ m_xPB_AutoOrder->set_sensitive(false);
+ }
+
+ }
+
+ void TabOrderDialog::SetModified()
+ {
+ m_xPB_OK->set_sensitive(true);
+ }
+
+ TabOrderDialog::~TabOrderDialog()
+ {
+ }
+
+ void TabOrderDialog::FillList()
+ {
+ DBG_ASSERT( m_xTempModel.is() && m_xControlContainer.is(), "TabOrderDialog::FillList: invalid call!" );
+ if ( !m_xTempModel.is() || !m_xControlContainer.is() )
+ return;
+
+ m_xLB_Controls->clear();
+
+ try
+ {
+ OUString aName;
+ OUString aImage;
+
+ const Sequence<Reference<css::awt::XControlModel>> aControlModels = m_xTempModel->getControlModels();
+ for ( auto const& rControlModel : aControlModels )
+ {
+ Reference< XPropertySet > xControl( rControlModel, UNO_QUERY );
+ Reference< XPropertySetInfo > xPI;
+ if ( xControl.is() )
+ xPI = xControl->getPropertySetInfo();
+
+ if ( xPI.is() )
+ {
+ if ( xPI->hasPropertyByName( PROPERTY_TABSTOP ) )
+ {
+ aName = ::comphelper::getString( xControl->getPropertyValue( PROPERTY_NAME ) );
+ // TODO: do Basic controls have a name?
+ aImage = GetImage( xControl );
+ OUString sId(weld::toId(xControl.get()));
+ m_xLB_Controls->append(sId, aName, aImage);
+ }
+ }
+ else
+ {
+ // no property set -> no tab order
+ OSL_FAIL( "TabOrderDialog::FillList: invalid control encountered!" );
+ m_xLB_Controls->clear();
+ break;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "TabOrderDialog::FillList" );
+ }
+
+ // select first entry
+ if (m_xLB_Controls->n_children())
+ m_xLB_Controls->select(0);
+ }
+
+ IMPL_LINK_NOARG( TabOrderDialog, MoveUpClickHdl, weld::Button&, void )
+ {
+ MoveSelection(-1);
+ }
+
+ IMPL_LINK_NOARG( TabOrderDialog, MoveDownClickHdl, weld::Button&, void )
+ {
+ MoveSelection(1);
+ }
+
+ IMPL_LINK_NOARG( TabOrderDialog, AutoOrderClickHdl, weld::Button&, void )
+ {
+ try
+ {
+ Reference< css::form::runtime::XFormController > xTabController = css::form::runtime::FormController::create( m_xORB );
+
+ xTabController->setModel( m_xTempModel );
+ xTabController->setContainer( m_xControlContainer );
+ xTabController->autoTabOrder();
+
+ SetModified();
+ FillList();
+
+ ::comphelper::disposeComponent( xTabController );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "TabOrderDialog::AutoOrderClickHdl" );
+ }
+ }
+
+ IMPL_LINK_NOARG( TabOrderDialog, OKClickHdl, weld::Button&, void )
+ {
+ int nEntryCount = m_xLB_Controls->n_children();
+ Sequence< Reference< XControlModel > > aSortedControlModelSeq( nEntryCount );
+ const Sequence< Reference< XControlModel > > aControlModels( m_xTempModel->getControlModels());
+ Reference< XControlModel > * pSortedControlModels = aSortedControlModelSeq.getArray();
+
+ for (int i = 0; i < nEntryCount; ++i)
+ {
+ XPropertySet* pEntry = weld::fromId<XPropertySet*>(m_xLB_Controls->get_id(i));
+ for( auto const& rControlModel : aControlModels )
+ {
+ Reference< XPropertySet > xSet(rControlModel, UNO_QUERY);
+ if (xSet.get() == pEntry)
+ {
+ pSortedControlModels[i] = rControlModel;
+ break;
+ }
+ }
+ }
+
+ // TODO: UNO action (to bracket all the single actions which are being created)
+ m_xModel->setControlModels( aSortedControlModelSeq );
+
+ m_xDialog->response(RET_OK);
+ }
+
+ IMPL_LINK_NOARG(TabOrderDialog, ModelHasMoved, weld::TreeView&, void)
+ {
+ SetModified();
+ }
+
+ void TabOrderDialog::MoveSelection(int nRelPos)
+ {
+ std::vector<int> aRows(m_xLB_Controls->get_selected_rows());
+ if (aRows.empty())
+ return;
+
+ m_xLB_Controls->unselect_all();
+ for (int i = 0; i < abs(nRelPos); ++i)
+ {
+ SetModified();
+
+ // move entries
+ if (nRelPos < 0)
+ {
+ std::sort(aRows.begin(), aRows.end());
+
+ int nFirstSelPos = aRows[0];
+ if (nFirstSelPos == 0) return;
+
+ for (auto row : aRows)
+ {
+ int nInsertPos = row - 1;
+ m_xLB_Controls->swap(nInsertPos, row);
+ }
+
+ for (auto row : aRows)
+ m_xLB_Controls->select(row - 1);
+ }
+ else if (nRelPos > 0)
+ {
+ std::sort(aRows.rbegin(), aRows.rend());
+
+ int nLastSelPos = aRows[0];
+ if( (nLastSelPos + nRelPos - i) > (m_xLB_Controls->n_children()-1) ) return;
+
+ for (auto row : aRows)
+ {
+ int nInsertPos = row + 1;
+ m_xLB_Controls->swap(nInsertPos, row);
+ }
+
+ for (auto row : aRows)
+ m_xLB_Controls->select(row + 1);
+ }
+ }
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/taborder.hxx b/extensions/source/propctrlr/taborder.hxx
new file mode 100644
index 000000000..e43f010e8
--- /dev/null
+++ b/extensions/source/propctrlr/taborder.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 <com/sun/star/awt/XTabControllerModel.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <vcl/weld.hxx>
+
+namespace pcr
+{
+ //= TabOrderDialog
+ class TabOrderDialog : public weld::GenericDialogController
+ {
+ css::uno::Reference< css::awt::XTabControllerModel >
+ m_xTempModel;
+ css::uno::Reference< css::awt::XTabControllerModel >
+ m_xModel;
+ css::uno::Reference< css::awt::XControlContainer >
+ m_xControlContainer;
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xORB;
+
+ std::unique_ptr<weld::TreeView> m_xLB_Controls;
+ std::unique_ptr<weld::Button> m_xPB_OK;
+ std::unique_ptr<weld::Button> m_xPB_MoveUp;
+ std::unique_ptr<weld::Button> m_xPB_MoveDown;
+ std::unique_ptr<weld::Button> m_xPB_AutoOrder;
+
+ DECL_LINK( ModelHasMoved, weld::TreeView&, void );
+ DECL_LINK( MoveUpClickHdl, weld::Button&, void );
+ DECL_LINK( MoveDownClickHdl, weld::Button&, void );
+ DECL_LINK( AutoOrderClickHdl, weld::Button&, void );
+ DECL_LINK( OKClickHdl, weld::Button&, void );
+
+ void FillList();
+ void MoveSelection(int nRelPos);
+
+ public:
+ TabOrderDialog(
+ weld::Window* pParent,
+ const css::uno::Reference< css::awt::XTabControllerModel >& _rxTabModel,
+ const css::uno::Reference< css::awt::XControlContainer >& _rxControlCont,
+ const css::uno::Reference< css::uno::XComponentContext >& _rxORB
+ );
+
+ virtual ~TabOrderDialog() override;
+
+ void SetModified();
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/unourl.cxx b/extensions/source/propctrlr/unourl.cxx
new file mode 100644
index 000000000..88c963012
--- /dev/null
+++ b/extensions/source/propctrlr/unourl.cxx
@@ -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 .
+ */
+
+#include "unourl.hxx"
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+
+
+ //= UnoURL
+
+ UnoURL::UnoURL( const OUString& _rCompleteURL, const Reference< XMultiServiceFactory >& _rxORB )
+ {
+ m_aURL.Complete = _rCompleteURL;
+
+ OSL_ENSURE( _rxORB.is(), "UnoURL::UnoURL: invalid ORB!" );
+ Reference< XURLTransformer > xTransform;
+ try
+ {
+ if ( _rxORB.is() )
+ {
+ xTransform.set( URLTransformer::create(comphelper::getComponentContext(_rxORB)) );
+ OSL_ENSURE( xTransform.is(), "UnoURL::UnoURL: could not create a URL transformer!" );
+ if ( xTransform.is() )
+ xTransform->parseStrict( m_aURL );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "UnoURL::UnoURL" );
+ }
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/unourl.hxx b/extensions/source/propctrlr/unourl.hxx
new file mode 100644
index 000000000..824c865ea
--- /dev/null
+++ b/extensions/source/propctrlr/unourl.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/util/URL.hpp>
+
+
+namespace pcr
+{
+
+
+ //= UnoURL
+
+ class UnoURL
+ {
+ private:
+ css::util::URL m_aURL;
+
+ public:
+ UnoURL(
+ const OUString& _rCompleteURL,
+ const css::uno::Reference< css::lang::XMultiServiceFactory >& _rxORB
+ );
+
+ operator const css::util::URL& () const { return m_aURL; }
+ };
+
+
+} // namespacepcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/usercontrol.cxx b/extensions/source/propctrlr/usercontrol.cxx
new file mode 100644
index 000000000..8cccb48e8
--- /dev/null
+++ b/extensions/source/propctrlr/usercontrol.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 "usercontrol.hxx"
+
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <svl/numuno.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <vcl/event.hxx>
+#include <tools/debug.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+#include <connectivity/dbconversion.hxx>
+#include "modulepcr.hxx"
+#include <strings.hrc>
+
+
+namespace pcr
+{
+
+
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Type;
+
+ namespace PropertyControlType = ::com::sun::star::inspection::PropertyControlType;
+
+ IMPL_LINK(OFormatSampleControl, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+ {
+ // want to handle two keys myself : Del/Backspace should empty the window (setting my prop to "standard" this way)
+ sal_uInt16 nKey = rKeyEvent.GetKeyCode().GetCode();
+ if ((KEY_DELETE == nKey) || (KEY_BACKSPACE == nKey))
+ {
+ m_xSpinButton->set_text("");
+ m_xEntry->set_text("");
+ setModified();
+ }
+
+ return true;
+ }
+
+ void OFormatSampleControl::SetFormatSupplier( const SvNumberFormatsSupplierObj* pSupplier )
+ {
+ Formatter& rFieldFormatter = m_xSpinButton->GetFormatter();
+ if (pSupplier)
+ {
+ rFieldFormatter.TreatAsNumber(true);
+
+ SvNumberFormatter* pFormatter = pSupplier->GetNumberFormatter();
+ rFieldFormatter.SetFormatter(pFormatter);
+ rFieldFormatter.SetValue( 1234.56789 );
+ }
+ else
+ {
+ rFieldFormatter.TreatAsNumber(false);
+ rFieldFormatter.SetFormatter(nullptr);
+ m_xSpinButton->set_text( "" );
+ }
+
+ m_xEntry->set_text(m_xSpinButton->get_text());
+ }
+
+ OFormatSampleControl::OFormatSampleControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : OFormatSampleControl_Base(PropertyControlType::Unknown, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ , m_xSpinButton(m_xBuilder->weld_formatted_spin_button("sample"))
+ , m_xEntry(m_xBuilder->weld_entry("entry"))
+ {
+ Formatter& rFieldFormatter = m_xSpinButton->GetFormatter();
+ rFieldFormatter.TreatAsNumber(true);
+ rFieldFormatter.ClearMinValue();
+ rFieldFormatter.ClearMaxValue();
+ m_xEntry->connect_key_press(LINK(this, OFormatSampleControl, KeyInputHdl));
+ }
+
+ void SAL_CALL OFormatSampleControl::setValue( const Any& _rValue )
+ {
+ sal_Int32 nFormatKey = 0;
+ if ( _rValue >>= nFormatKey )
+ {
+ // else set the new format key, the text will be reformatted
+ Formatter& rFieldFormatter = m_xSpinButton->GetFormatter();
+ rFieldFormatter.SetFormatKey(nFormatKey);
+
+ SvNumberFormatter* pNF = rFieldFormatter.GetFormatter();
+ const SvNumberformat* pEntry = pNF->GetEntry( nFormatKey );
+ OSL_ENSURE( pEntry, "OFormatSampleControl::setValue: invalid format entry!" );
+
+ const bool bIsTextFormat = ( pEntry && pEntry->IsTextFormat() );
+ if ( bIsTextFormat )
+ m_xSpinButton->set_text( PcrRes( RID_STR_TEXT_FORMAT ) );
+ else
+ rFieldFormatter.SetValue( pEntry ? getPreviewValue( *pEntry ) : 1234.56789 );
+ }
+ else
+ m_xSpinButton->set_text( "" );
+
+ m_xEntry->set_text(m_xSpinButton->get_text());
+ }
+
+ double OFormatSampleControl::getPreviewValue( const SvNumberformat& i_rEntry )
+ {
+ double nValue = 1234.56789;
+ switch ( i_rEntry.GetType() & ~SvNumFormatType::DEFINED )
+ {
+ case SvNumFormatType::DATE:
+ {
+ Date aCurrentDate( Date::SYSTEM );
+ static css::util::Date STANDARD_DB_DATE(30,12,1899);
+ nValue = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(aCurrentDate.GetDate()),STANDARD_DB_DATE);
+ }
+ break;
+ case SvNumFormatType::TIME:
+ case SvNumFormatType::DATETIME:
+ {
+ tools::Time aCurrentTime( tools::Time::SYSTEM );
+ nValue = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(aCurrentTime.GetTime()));
+ }
+ break;
+ default:
+ break;
+ }
+ return nValue;
+ }
+
+
+ double OFormatSampleControl::getPreviewValue(SvNumberFormatter const * _pNF, sal_Int32 _nFormatKey)
+ {
+ const SvNumberformat* pEntry = _pNF->GetEntry(_nFormatKey);
+ DBG_ASSERT( pEntry, "OFormattedNumericControl::SetFormatDescription: invalid format key!" );
+ double nValue = 1234.56789;
+ if ( pEntry )
+ nValue = getPreviewValue( *pEntry );
+ return nValue;
+ }
+
+ Any SAL_CALL OFormatSampleControl::getValue()
+ {
+ Any aPropValue;
+ if ( !m_xSpinButton->get_text().isEmpty() )
+ {
+ Formatter& rFieldFormatter = m_xSpinButton->GetFormatter();
+ aPropValue <<= rFieldFormatter.GetValue();
+ }
+ return aPropValue;
+ }
+
+ Type SAL_CALL OFormatSampleControl::getValueType()
+ {
+ return ::cppu::UnoType<sal_Int32>::get();
+ }
+
+ OFormattedNumericControl::OFormattedNumericControl(std::unique_ptr<weld::FormattedSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : OFormattedNumericControl_Base(PropertyControlType::Unknown, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ {
+ Formatter& rFormatter = getTypedControlWindow()->GetFormatter();
+ rFormatter.TreatAsNumber(true);
+ rFormatter.ClearMinValue();
+ rFormatter.ClearMaxValue();
+ }
+
+ OFormattedNumericControl::~OFormattedNumericControl()
+ {
+ }
+
+ void SAL_CALL OFormattedNumericControl::setValue( const Any& _rValue )
+ {
+ double nValue( 0 );
+ if ( _rValue >>= nValue )
+ getTypedControlWindow()->GetFormatter().SetValue(nValue);
+ else
+ getTypedControlWindow()->set_text("");
+ }
+
+ Any SAL_CALL OFormattedNumericControl::getValue()
+ {
+ Any aPropValue;
+ if ( !getTypedControlWindow()->get_text().isEmpty() )
+ aPropValue <<= getTypedControlWindow()->GetFormatter().GetValue();
+ return aPropValue;
+ }
+
+ Type SAL_CALL OFormattedNumericControl::getValueType()
+ {
+ return ::cppu::UnoType<double>::get();
+ }
+
+ void OFormattedNumericControl::SetFormatDescription(const FormatDescription& rDesc)
+ {
+ bool bFallback = true;
+
+ Formatter& rFieldFormatter = getTypedControlWindow()->GetFormatter();
+ if (rDesc.pSupplier)
+ {
+ rFieldFormatter.TreatAsNumber(true);
+
+ SvNumberFormatter* pFormatter = rDesc.pSupplier->GetNumberFormatter();
+ if (pFormatter != rFieldFormatter.GetFormatter())
+ rFieldFormatter.SetFormatter(pFormatter);
+ rFieldFormatter.SetFormatKey(rDesc.nKey);
+
+ const SvNumberformat* pEntry = rFieldFormatter.GetFormatter()->GetEntry(rFieldFormatter.GetFormatKey());
+ DBG_ASSERT( pEntry, "OFormattedNumericControl::SetFormatDescription: invalid format key!" );
+ if ( pEntry )
+ {
+ bFallback = false;
+ }
+
+ }
+
+ if ( bFallback )
+ {
+ rFieldFormatter.TreatAsNumber(false);
+ rFieldFormatter.SetFormatter(nullptr);
+ getTypedControlWindow()->set_text("");
+ }
+ }
+
+ //= OFileUrlControl
+ OFileUrlControl::OFileUrlControl(std::unique_ptr<SvtURLBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly)
+ : OFileUrlControl_Base(PropertyControlType::Unknown, std::move(xBuilder), std::move(xWidget), bReadOnly)
+ {
+ getTypedControlWindow()->DisableHistory();
+ getTypedControlWindow()->SetPlaceHolder( PcrRes( RID_EMBED_IMAGE_PLACEHOLDER ) ) ;
+ }
+
+ OFileUrlControl::~OFileUrlControl()
+ {
+ }
+
+ void SAL_CALL OFileUrlControl::setValue(const Any& rValue)
+ {
+ OUString sURL;
+ SvtURLBox* pControlWindow = getTypedControlWindow();
+ bool bSuccess = rValue >>= sURL;
+ if (bSuccess && GraphicObject::isGraphicObjectUniqueIdURL(sURL))
+ sURL = pControlWindow->GetPlaceHolder();
+ pControlWindow->set_entry_text(sURL);
+ }
+
+ Any SAL_CALL OFileUrlControl::getValue()
+ {
+ Any aPropValue;
+ if (!getTypedControlWindow()->get_active_text().isEmpty())
+ aPropValue <<= getTypedControlWindow()->GetURL();
+ return aPropValue;
+ }
+
+ Type SAL_CALL OFileUrlControl::getValueType()
+ {
+ return ::cppu::UnoType<OUString>::get();
+ }
+
+ IMPL_LINK_NOARG(OFileUrlControl, URLModifiedHdl, weld::ComboBox&, void)
+ {
+ editChanged();
+ }
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/usercontrol.hxx b/extensions/source/propctrlr/usercontrol.hxx
new file mode 100644
index 000000000..86b53e7f1
--- /dev/null
+++ b/extensions/source/propctrlr/usercontrol.hxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "commoncontrol.hxx"
+#include <svtools/inettbc.hxx>
+#include <svl/zforlist.hxx>
+
+class SvNumberFormatsSupplierObj;
+
+namespace pcr
+{
+ //= OFormatSampleControl
+ typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::Container> OFormatSampleControl_Base;
+ class OFormatSampleControl : public OFormatSampleControl_Base
+ {
+ private:
+ std::unique_ptr<weld::FormattedSpinButton> m_xSpinButton;
+ std::unique_ptr<weld::Entry> m_xEntry;
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+
+ public:
+ OFormatSampleControl(std::unique_ptr<weld::Container> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ virtual void SAL_CALL disposing() override
+ {
+ m_xEntry.reset();
+ m_xSpinButton.reset();
+ OFormatSampleControl_Base::disposing();
+ }
+
+ virtual void SetModifyHandler() override
+ {
+ m_xEntry->connect_focus_in( LINK( this, CommonBehaviourControlHelper, GetFocusHdl ) );
+ m_xEntry->connect_focus_out( LINK( this, CommonBehaviourControlHelper, LoseFocusHdl ) );
+ m_xSpinButton->connect_value_changed(LINK(this, CommonBehaviourControlHelper, FormattedModifiedHdl));
+ m_xSpinButton->connect_changed(LINK(this, CommonBehaviourControlHelper, EditModifiedHdl));
+ }
+
+ void SetFormatSupplier(const SvNumberFormatsSupplierObj* pSupplier);
+
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+
+ /** returns the default preview value for the given format key
+ *
+ * \param _pNF the number formatter
+ * \param _nFormatKey the format key
+ * \return current date or time or the value 1234.56789
+ */
+ static double getPreviewValue(SvNumberFormatter const * pNF, sal_Int32 nFormatKey);
+
+ private:
+ static double getPreviewValue( const SvNumberformat& i_rEntry );
+ };
+
+ //= FormatDescription
+ struct FormatDescription
+ {
+ SvNumberFormatsSupplierObj* pSupplier;
+ sal_Int32 nKey;
+ };
+
+ //= OFormattedNumericControl
+ typedef CommonBehaviourControl<css::inspection::XPropertyControl, weld::FormattedSpinButton> OFormattedNumericControl_Base;
+ class OFormattedNumericControl : public OFormattedNumericControl_Base
+ {
+ public:
+ OFormattedNumericControl(std::unique_ptr<weld::FormattedSpinButton> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ void SetFormatDescription( const FormatDescription& rDesc );
+
+ // make some FormattedField methods available
+ void SetDecimalDigits(sal_uInt16 nPrecision) { getTypedControlWindow()->GetFormatter().SetDecimalDigits(nPrecision); }
+ void SetDefaultValue(double dDef) { getTypedControlWindow()->GetFormatter().SetDefaultValue(dDef); }
+
+ virtual void SetModifyHandler() override
+ {
+ OFormattedNumericControl_Base::SetModifyHandler();
+ getTypedControlWindow()->connect_value_changed(LINK(this, CommonBehaviourControlHelper, FormattedModifiedHdl));
+ getTypedControlWindow()->connect_changed(LINK(this, CommonBehaviourControlHelper, EditModifiedHdl));
+ }
+
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow(); }
+
+ protected:
+ virtual ~OFormattedNumericControl() override;
+ };
+
+ //= OFileUrlControl
+ typedef CommonBehaviourControl<css::inspection::XPropertyControl, SvtURLBox> OFileUrlControl_Base;
+ class OFileUrlControl : public OFileUrlControl_Base
+ {
+ private:
+ DECL_LINK(URLModifiedHdl, weld::ComboBox&, void);
+ public:
+ OFileUrlControl(std::unique_ptr<SvtURLBox> xWidget, std::unique_ptr<weld::Builder> xBuilder, bool bReadOnly);
+
+ // XPropertyControl
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& _value ) override;
+ virtual css::uno::Type SAL_CALL getValueType() override;
+
+ virtual void SetModifyHandler() override
+ {
+ OFileUrlControl_Base::SetModifyHandler();
+ SvtURLBox* pControlWindow = getTypedControlWindow();
+ // tdf#140239 and tdf#141084 don't notify that the control has changed content until focus-out
+ pControlWindow->connect_focus_out(LINK(this, CommonBehaviourControlHelper, LoseFocusHdl));
+ pControlWindow->connect_changed(LINK(this, OFileUrlControl, URLModifiedHdl));
+ }
+
+ virtual weld::Widget* getWidget() override { return getTypedControlWindow()->getWidget(); }
+
+ protected:
+ virtual ~OFileUrlControl() override;
+ };
+
+} // namespace pcr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/xsddatatypes.cxx b/extensions/source/propctrlr/xsddatatypes.cxx
new file mode 100644
index 000000000..fdaea8e21
--- /dev/null
+++ b/extensions/source/propctrlr/xsddatatypes.cxx
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xsddatatypes.hxx"
+
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+#include <com/sun/star/xsd/XDataType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <tools/debug.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::xsd;
+ using namespace ::com::sun::star::beans;
+
+ template< typename INTERFACE, typename ARGUMENT >
+ static ARGUMENT getSave( INTERFACE* pObject, ARGUMENT ( SAL_CALL INTERFACE::*pGetter )( ) )
+ {
+ ARGUMENT aReturn = ARGUMENT();
+ try
+ {
+ aReturn = (pObject->*pGetter)( );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType: getSave" );
+ }
+ return aReturn;
+ }
+
+ XSDDataType::XSDDataType( const Reference< XDataType >& _rxDataType )
+ :m_xDataType( _rxDataType )
+ {
+ DBG_ASSERT( m_xDataType.is(), "XSDDataType::XSDDataType: invalid UNO object!" );
+ if ( m_xDataType.is() )
+ m_xFacetInfo = m_xDataType->getPropertySetInfo();
+ }
+
+
+ XSDDataType::~XSDDataType()
+ {
+ }
+
+
+ sal_Int16 XSDDataType::classify() const
+ {
+ sal_Int16 nTypeClass = DataTypeClass::STRING;
+ try
+ {
+ if ( m_xDataType.is() )
+ nTypeClass = m_xDataType->getTypeClass();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::classify" );
+ }
+ return nTypeClass;
+ }
+
+
+ bool XSDDataType::isBasicType() const
+ {
+ return getSave( m_xDataType.get(), &XDataType::getIsBasic );
+ }
+
+
+ OUString XSDDataType::getName() const
+ {
+ return getSave( m_xDataType.get(), &XDataType::getName );
+ }
+
+
+ void XSDDataType::setFacet( const OUString& _rFacetName, const Any& _rValue )
+ {
+ try
+ {
+ m_xDataType->setPropertyValue( _rFacetName, _rValue );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::setFacet: caught an exception - sure this is the right data type class for this property?" );
+ }
+ }
+
+
+ bool XSDDataType::hasFacet( const OUString& _rFacetName ) const
+ {
+ bool bReturn = false;
+ try
+ {
+ bReturn = m_xFacetInfo.is() && m_xFacetInfo->hasPropertyByName( _rFacetName );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::hasFacet" );
+ }
+ return bReturn;
+ }
+
+ Any XSDDataType::getFacet( const OUString& _rFacetName )
+ {
+ Any aReturn;
+ try
+ {
+ aReturn = m_xDataType->getPropertyValue( _rFacetName );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::getFacet: caught an exception - sure this is the right data type class for this property?" );
+ }
+ return aReturn;
+ }
+
+
+ namespace
+ {
+ void lcl_copyProperties( const Reference< XPropertySet >& _rxSource, const Reference< XPropertySet >& _rxDest )
+ {
+ Reference< XPropertySetInfo > xSourceInfo;
+ if ( _rxSource.is() )
+ xSourceInfo = _rxSource->getPropertySetInfo();
+ Reference< XPropertySetInfo > xDestInfo;
+ if ( _rxDest.is() )
+ xDestInfo = _rxDest->getPropertySetInfo();
+ OSL_ENSURE( xSourceInfo.is() && xDestInfo.is(), "lcl_copyProperties: invalid property set( info)s!" );
+ if ( !xSourceInfo.is() || !xDestInfo.is() )
+ return;
+
+ Sequence< Property > aProperties( xSourceInfo->getProperties() );
+ const Property* pProperties = aProperties.getConstArray();
+ const Property* pPropertiesEnd = pProperties + aProperties.getLength();
+ for ( ; pProperties != pPropertiesEnd; ++pProperties )
+ {
+ if ( xDestInfo->hasPropertyByName( pProperties->Name ) )
+ _rxDest->setPropertyValue( pProperties->Name, _rxSource->getPropertyValue( pProperties->Name ) );
+ }
+ }
+ }
+
+
+ void XSDDataType::copyFacetsFrom( const ::rtl::Reference< XSDDataType >& _pSourceType )
+ {
+ OSL_ENSURE( _pSourceType.is(), "XSDDataType::copyFacetsFrom: invalid source type!" );
+ if ( !_pSourceType.is() )
+ return;
+
+ try
+ {
+ Reference< XPropertySet > xSource = _pSourceType->getUnoDataType();
+ Reference< XPropertySet > xDest = getUnoDataType();
+ lcl_copyProperties( xSource, xDest );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDDataType::copyFacetsFrom" );
+ }
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/xsddatatypes.hxx b/extensions/source/propctrlr/xsddatatypes.hxx
new file mode 100644
index 000000000..0413d0250
--- /dev/null
+++ b/extensions/source/propctrlr/xsddatatypes.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 <com/sun/star/uno/Reference.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+namespace com::sun::star {
+ namespace xsd {
+ class XDataType;
+ }
+ namespace beans {
+ class XPropertySetInfo;
+ }
+}
+
+
+namespace pcr
+{
+
+
+ //= XSDDataType
+
+ class XSDDataType : public salhelper::SimpleReferenceObject
+ {
+ private:
+ css::uno::Reference< css::xsd::XDataType >
+ m_xDataType;
+ css::uno::Reference< css::beans::XPropertySetInfo >
+ m_xFacetInfo;
+
+ public:
+ explicit XSDDataType(
+ const css::uno::Reference< css::xsd::XDataType >& _rxDataType
+ );
+
+ /// retrieves the underlying UNO component
+ const css::uno::Reference< css::xsd::XDataType >&
+ getUnoDataType() const { return m_xDataType; }
+
+ /// classifies the data typ
+ sal_Int16 classify() const;
+
+ // attribute access
+ OUString getName() const;
+ bool isBasicType() const;
+
+ /// determines whether a given facet exists at the type
+ bool hasFacet( const OUString& _rFacetName ) const;
+ /// retrieves a facet value
+ css::uno::Any getFacet( const OUString& _rFacetName );
+ /// sets a facet value
+ void setFacet( const OUString& _rFacetName, const css::uno::Any& _rFacetValue );
+
+ /** copies as much facets (values, respectively) from a give data type instance
+ */
+ void copyFacetsFrom( const ::rtl::Reference< XSDDataType >& _pSourceType );
+
+ protected:
+ virtual ~XSDDataType() override;
+
+ private:
+ XSDDataType( const XSDDataType& ) = delete;
+ XSDDataType& operator=( const XSDDataType& ) = delete;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/xsdvalidationhelper.cxx b/extensions/source/propctrlr/xsdvalidationhelper.cxx
new file mode 100644
index 000000000..b5b96cc28
--- /dev/null
+++ b/extensions/source/propctrlr/xsdvalidationhelper.cxx
@@ -0,0 +1,406 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xsdvalidationhelper.hxx"
+#include "xsddatatypes.hxx"
+#include "formstrings.hxx"
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/xforms/XDataTypeRepository.hpp>
+#include <unotools/syslocale.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <tools/diagnose_ex.h>
+
+
+namespace pcr
+{
+
+
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::xsd;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::xforms;
+
+ namespace NumberFormat = ::com::sun::star::util::NumberFormat;
+
+
+ //= XSDValidationHelper
+
+
+ XSDValidationHelper::XSDValidationHelper( ::osl::Mutex& _rMutex, const Reference< XPropertySet >& _rxIntrospectee, const Reference< frame::XModel >& _rxContextDocument )
+ :EFormsHelper( _rMutex, _rxIntrospectee, _rxContextDocument )
+ ,m_bInspectingFormattedField( false )
+ {
+ try
+ {
+ Reference< XPropertySetInfo > xPSI;
+ Reference< XServiceInfo > xSI( _rxIntrospectee, UNO_QUERY );
+ if ( m_xControlModel.is() )
+ xPSI = m_xControlModel->getPropertySetInfo();
+ if ( xPSI.is()
+ && xPSI->hasPropertyByName( PROPERTY_FORMATKEY )
+ && xPSI->hasPropertyByName( PROPERTY_FORMATSSUPPLIER )
+ && xSI.is()
+ && xSI->supportsService( SERVICE_COMPONENT_FORMATTEDFIELD )
+ )
+ m_bInspectingFormattedField = true;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("extensions.propctrlr",
+ "caught an exception while examining the introspectee!");
+ }
+ }
+
+
+ void XSDValidationHelper::getAvailableDataTypeNames( std::vector< OUString >& /* [out] */ _rNames ) const
+ {
+ _rNames.resize( 0 );
+
+ try
+ {
+ Reference< XDataTypeRepository > xRepository = getDataTypeRepository();
+ if ( xRepository.is() )
+ {
+ const Sequence<OUString> aElements = xRepository->getElementNames();
+
+ _rNames.resize( aElements.getLength() );
+ std::copy( aElements.begin(), aElements.end(), _rNames.begin() );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::getAvailableDataTypeNames" );
+ }
+ }
+
+
+ Reference< XDataTypeRepository > XSDValidationHelper::getDataTypeRepository() const
+ {
+ Reference< XDataTypeRepository > xRepository;
+
+ Reference< xforms::XModel > xModel( getCurrentFormModel( ) );
+ if ( xModel.is() )
+ xRepository = xModel->getDataTypeRepository();
+
+ return xRepository;
+ }
+
+
+ Reference< XDataTypeRepository > XSDValidationHelper::getDataTypeRepository( const OUString& _rModelName ) const
+ {
+ Reference< XDataTypeRepository > xRepository;
+
+ Reference< xforms::XModel > xModel( getFormModelByName( _rModelName ) );
+ if ( xModel.is() )
+ xRepository = xModel->getDataTypeRepository();
+
+ return xRepository;
+ }
+
+
+ Reference< XDataType > XSDValidationHelper::getDataType( const OUString& _rName ) const
+ {
+ Reference< XDataType > xDataType;
+
+ if ( !_rName.isEmpty() )
+ {
+ Reference< XDataTypeRepository > xRepository = getDataTypeRepository();
+ if ( xRepository.is() )
+ xDataType = xRepository->getDataType( _rName );
+ }
+ return xDataType;
+ }
+
+
+ OUString XSDValidationHelper::getValidatingDataTypeName( ) const
+ {
+ OUString sDataTypeName;
+ try
+ {
+ Reference< XPropertySet > xBinding( getCurrentBinding() );
+ // it's allowed here to not (yet) have a binding
+ if ( xBinding.is() )
+ {
+ OSL_VERIFY( xBinding->getPropertyValue( PROPERTY_XSD_DATA_TYPE ) >>= sDataTypeName );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::getValidatingDataTypeName" );
+ }
+ return sDataTypeName;
+ }
+
+
+ ::rtl::Reference< XSDDataType > XSDValidationHelper::getDataTypeByName( const OUString& _rName ) const
+ {
+ ::rtl::Reference< XSDDataType > pReturn;
+
+ try
+ {
+ Reference< XDataType > xValidatedAgainst;
+
+ if ( !_rName.isEmpty() )
+ xValidatedAgainst = getDataType( _rName );
+
+ if ( xValidatedAgainst.is() )
+ pReturn = new XSDDataType( xValidatedAgainst );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::getDataTypeByName" );
+ }
+
+ return pReturn;
+ }
+
+
+ ::rtl::Reference< XSDDataType > XSDValidationHelper::getValidatingDataType( ) const
+ {
+ return getDataTypeByName( getValidatingDataTypeName() );
+ }
+
+
+ bool XSDValidationHelper::cloneDataType( const ::rtl::Reference< XSDDataType >& _pDataType, const OUString& _rNewName ) const
+ {
+ OSL_ENSURE( _pDataType.is(), "XSDValidationHelper::removeDataTypeFromRepository: invalid data type!" );
+ if ( !_pDataType.is() )
+ return false;
+
+ try
+ {
+ Reference< XDataTypeRepository > xRepository( getDataTypeRepository() );
+ OSL_ENSURE( xRepository.is(), "XSDValidationHelper::removeDataTypeFromRepository: invalid data type repository!" );
+ if ( !xRepository.is() )
+ return false;
+
+ Reference< XDataType > xDataType( _pDataType->getUnoDataType() );
+ OSL_ENSURE( xDataType.is(), "XSDValidationHelper::removeDataTypeFromRepository: invalid data type (II)!" );
+ if ( !xDataType.is() )
+ return false;
+
+ xRepository->cloneDataType( xDataType->getName(), _rNewName );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::cloneDataType" );
+ }
+ return true;
+ }
+
+
+ bool XSDValidationHelper::removeDataTypeFromRepository( const OUString& _rName ) const
+ {
+ try
+ {
+ Reference< XDataTypeRepository > xRepository( getDataTypeRepository() );
+ OSL_ENSURE( xRepository.is(), "XSDValidationHelper::removeDataTypeFromRepository: invalid data type repository!" );
+ if ( !xRepository.is() )
+ return false;
+
+ if ( !xRepository->hasByName( _rName ) )
+ {
+ OSL_FAIL( "XSDValidationHelper::removeDataTypeFromRepository: invalid repository and/or data type!" );
+ return false;
+ }
+
+ xRepository->revokeDataType( _rName );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::removeDataTypeFromRepository" );
+ return false;
+ }
+ return true;
+ }
+
+
+ void XSDValidationHelper::setValidatingDataTypeByName( const OUString& _rName ) const
+ {
+ try
+ {
+ Reference< XPropertySet > xBinding( getCurrentBinding() );
+ OSL_ENSURE( xBinding.is(), "XSDValidationHelper::setValidatingDataTypeByName: no active binding - how this?" );
+
+ if ( xBinding.is() )
+ {
+ // get the old data type - this is necessary for notifying property changes
+ OUString sOldDataTypeName;
+ OSL_VERIFY( xBinding->getPropertyValue( PROPERTY_XSD_DATA_TYPE ) >>= sOldDataTypeName );
+ Reference< XPropertySet > xOldType;
+ try {
+ xOldType = getDataType( sOldDataTypeName );
+ } catch( const Exception& ) { }
+
+ // set the new data type name
+ xBinding->setPropertyValue( PROPERTY_XSD_DATA_TYPE, Any( _rName ) );
+
+ // retrieve the new data type object
+ Reference< XPropertySet > xNewType = getDataType( _rName );
+
+ // fire any changes in the properties which result from this new type
+ std::set< OUString > aFilter; aFilter.insert( PROPERTY_NAME );
+ firePropertyChanges( xOldType, xNewType, aFilter );
+
+ // fire the change in the Data Type property
+ OUString sNewDataTypeName;
+ OSL_VERIFY( xBinding->getPropertyValue( PROPERTY_XSD_DATA_TYPE ) >>= sNewDataTypeName );
+ firePropertyChange( PROPERTY_XSD_DATA_TYPE, Any( sOldDataTypeName ), Any( sNewDataTypeName ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
+ }
+ }
+
+
+ void XSDValidationHelper::copyDataType( const OUString& _rFromModel, const OUString& _rToModel,
+ const OUString& _rDataTypeName ) const
+ {
+ if ( _rFromModel == _rToModel )
+ // nothing to do (me thinks)
+ return;
+
+ try
+ {
+ Reference< XDataTypeRepository > xFromRepository, xToRepository;
+ if ( !_rFromModel.isEmpty() )
+ xFromRepository = getDataTypeRepository( _rFromModel );
+ if ( !_rToModel.isEmpty() )
+ xToRepository = getDataTypeRepository( _rToModel );
+
+ if ( !xFromRepository.is() || !xToRepository.is() )
+ return;
+
+ if ( !xFromRepository->hasByName( _rDataTypeName ) || xToRepository->hasByName( _rDataTypeName ) )
+ // not existent in the source, or already existent (by name) in the destination
+ return;
+
+ // determine the built-in type belonging to the source type
+ ::rtl::Reference< XSDDataType > pSourceType = new XSDDataType( xFromRepository->getDataType( _rDataTypeName ) );
+ OUString sTargetBaseType = getBasicTypeNameForClass( pSourceType->classify(), xToRepository );
+
+ // create the target type
+ Reference< XDataType > xTargetType = xToRepository->cloneDataType( sTargetBaseType, _rDataTypeName );
+ ::rtl::Reference< XSDDataType > pTargetType = new XSDDataType( xTargetType );
+
+ // copy the facets
+ pTargetType->copyFacetsFrom( pSourceType );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::copyDataType" );
+ }
+ }
+
+
+ void XSDValidationHelper::findDefaultFormatForIntrospectee()
+ {
+ try
+ {
+ ::rtl::Reference< XSDDataType > xDataType = getValidatingDataType();
+ if ( xDataType.is() )
+ {
+ // find a NumberFormat type corresponding to the DataTypeClass
+ sal_Int16 nNumberFormatType = NumberFormat::NUMBER;
+ switch ( xDataType->classify() )
+ {
+ case DataTypeClass::DATETIME:
+ nNumberFormatType = NumberFormat::DATETIME;
+ break;
+ case DataTypeClass::DATE:
+ nNumberFormatType = NumberFormat::DATE;
+ break;
+ case DataTypeClass::TIME:
+ nNumberFormatType = NumberFormat::TIME;
+ break;
+ case DataTypeClass::STRING:
+ case DataTypeClass::anyURI:
+ case DataTypeClass::QName:
+ case DataTypeClass::NOTATION:
+ nNumberFormatType = NumberFormat::TEXT;
+ break;
+ }
+
+ // get the number formatter from the introspectee
+ Reference< XNumberFormatsSupplier > xSupplier;
+ Reference< XNumberFormatTypes > xFormatTypes;
+ OSL_VERIFY( m_xControlModel->getPropertyValue( PROPERTY_FORMATSSUPPLIER ) >>= xSupplier );
+ if ( xSupplier.is() )
+ xFormatTypes.set(xSupplier->getNumberFormats(), css::uno::UNO_QUERY);
+ OSL_ENSURE( xFormatTypes.is(), "XSDValidationHelper::findDefaultFormatForIntrospectee: no number formats for the introspectee!" );
+ if ( !xFormatTypes.is() )
+ return;
+
+ // and the standard format for the given NumberFormat type
+ sal_Int32 nDesiredFormat = xFormatTypes->getStandardFormat( nNumberFormatType, SvtSysLocale().GetLanguageTag().getLocale() );
+
+ // set this at the introspectee
+ m_xControlModel->setPropertyValue( PROPERTY_FORMATKEY, Any( nDesiredFormat ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::findDefaultFormatForIntrospectee" );
+ }
+ }
+
+
+ OUString XSDValidationHelper::getBasicTypeNameForClass( sal_Int16 _nClass ) const
+ {
+ return getBasicTypeNameForClass( _nClass, getDataTypeRepository() );
+ }
+
+
+ OUString XSDValidationHelper::getBasicTypeNameForClass( sal_Int16 _nClass, const Reference< XDataTypeRepository >& _rxRepository )
+ {
+ OUString sReturn;
+ OSL_ENSURE( _rxRepository.is(), "XSDValidationHelper::getBasicTypeNameForClass: invalid repository!" );
+ if ( !_rxRepository.is() )
+ return sReturn;
+
+ try
+ {
+ Reference< XDataType > xDataType = _rxRepository->getBasicDataType( _nClass );
+ OSL_ENSURE( xDataType.is(), "XSDValidationHelper::getBasicTypeNameForClass: invalid data type returned!" );
+ if ( xDataType.is() )
+ sReturn = xDataType->getName();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "XSDValidationHelper::getBasicTypeNameForClass" );
+ }
+
+ return sReturn;
+ }
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/xsdvalidationhelper.hxx b/extensions/source/propctrlr/xsdvalidationhelper.hxx
new file mode 100644
index 000000000..2a77ff584
--- /dev/null
+++ b/extensions/source/propctrlr/xsdvalidationhelper.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "eformshelper.hxx"
+#include "xsddatatypes.hxx"
+
+#include <com/sun/star/xsd/XDataType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <rtl/ref.hxx>
+
+
+namespace pcr
+{
+
+
+ class XSDDataType;
+
+ //= XSDValidationHelper
+
+ class XSDValidationHelper : public EFormsHelper
+ {
+ private:
+ bool m_bInspectingFormattedField;
+ public:
+ bool isInspectingFormattedField() const { return m_bInspectingFormattedField; }
+
+ public:
+ XSDValidationHelper(
+ ::osl::Mutex& _rMutex,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxIntrospectee,
+ const css::uno::Reference< css::frame::XModel >& _rxContextDocument
+ );
+
+ /** retrieves the names of all XForms models in the document the control lives in
+ */
+ void getAvailableDataTypeNames( std::vector< OUString >& /* [out] */ _rNames ) const;
+
+ /** retrieves a particular data type given by name
+ */
+ ::rtl::Reference< XSDDataType >
+ getDataTypeByName( const OUString& _rName ) const;
+
+ /** retrieves the DataType instance which the control model is currently validated against
+
+ If there is a binding set at our control model, which at the same time acts as validator,
+ and if this validator is bound to an XDataType, then this data type is retrieved here.
+ */
+ ::rtl::Reference< XSDDataType >
+ getValidatingDataType( ) const;
+
+ /** retrieves the name of the data type which the control model is currently validated against
+
+ @seealso getValidatingDataType
+ */
+ OUString
+ getValidatingDataTypeName( ) const;
+
+ /** binds the validator to a new data type
+
+ To be called with an active binding only.
+ */
+ void setValidatingDataTypeByName( const OUString& _rName ) const;
+
+ /** removes the data type given by name from the data type repository
+ */
+ bool removeDataTypeFromRepository( const OUString& _rName ) const;
+
+ /** creates a new data type, which is a clone of an existing data type
+ */
+ bool cloneDataType( const ::rtl::Reference< XSDDataType >& _pDataType, const OUString& _rNewName ) const;
+
+ /** retrieves the name of the basic data type which has the given class
+ */
+ OUString
+ getBasicTypeNameForClass( sal_Int16 _eClass ) const;
+
+ /** copy a data type from one model to another
+
+ If a data type with the given name already exists in the target model, then nothing
+ happens. In particular, the facets of the data type are not copied.
+ */
+ void copyDataType( const OUString& _rFromModel, const OUString& _rToModel,
+ const OUString& _rDataTypeName ) const;
+
+ /** finds (and sets) a default format for the formatted field we're inspecting,
+ according to the current data type the control value is evaluated against
+ */
+ void findDefaultFormatForIntrospectee();
+
+ private:
+ /** retrieves the data type repository associated with the current model
+ */
+ css::uno::Reference< css::xforms::XDataTypeRepository >
+ getDataTypeRepository() const;
+
+ /** retrieves the data type repository associated with any model
+ */
+ css::uno::Reference< css::xforms::XDataTypeRepository >
+ getDataTypeRepository( const OUString& _rModelName ) const;
+
+ /** retrieves the data type object for the given name
+ */
+ css::uno::Reference< css::xsd::XDataType >
+ getDataType( const OUString& _rName ) const;
+
+ /** retrieves the name of the basic data type which has the given class, in the given repository
+ */
+ static OUString
+ getBasicTypeNameForClass(
+ sal_Int16 _nClass,
+ const css::uno::Reference< css::xforms::XDataTypeRepository >& _rxRepository
+ );
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/xsdvalidationpropertyhandler.cxx b/extensions/source/propctrlr/xsdvalidationpropertyhandler.cxx
new file mode 100644
index 000000000..1f8351cdb
--- /dev/null
+++ b/extensions/source/propctrlr/xsdvalidationpropertyhandler.cxx
@@ -0,0 +1,673 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "xsdvalidationpropertyhandler.hxx"
+#include "formstrings.hxx"
+#include "formmetadata.hxx"
+#include "xsddatatypes.hxx"
+#include "modulepcr.hxx"
+#include <strings.hrc>
+#include <propctrlr.h>
+#include "newdatatype.hxx"
+#include "xsdvalidationhelper.hxx"
+#include "pcrcommon.hxx"
+#include "handlerhelper.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/xsd/WhiteSpaceTreatment.hpp>
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+#include <com/sun/star/inspection/PropertyControlType.hpp>
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/inspection/PropertyLineElement.hpp>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <tools/debug.hxx>
+#include <sal/macros.h>
+
+#include <algorithm>
+#include <limits>
+
+
+namespace pcr
+{
+
+
+ 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::xforms;
+ using namespace ::com::sun::star::xsd;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::inspection;
+
+ using ::com::sun::star::beans::PropertyAttribute::MAYBEVOID;
+
+
+ //= XSDValidationPropertyHandler
+
+ XSDValidationPropertyHandler::XSDValidationPropertyHandler( const Reference< XComponentContext >& _rxContext )
+ :PropertyHandlerComponent( _rxContext )
+ {
+ }
+
+
+ XSDValidationPropertyHandler::~XSDValidationPropertyHandler()
+ {
+ }
+
+
+ OUString XSDValidationPropertyHandler::getImplementationName( )
+ {
+ return "com.sun.star.comp.extensions.XSDValidationPropertyHandler";
+ }
+
+
+ Sequence< OUString > XSDValidationPropertyHandler::getSupportedServiceNames( )
+ {
+ return{ "com.sun.star.form.inspection.XSDValidationPropertyHandler" };
+ }
+
+
+ Any SAL_CALL XSDValidationPropertyHandler::getPropertyValue( const OUString& _rPropertyName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ OSL_ENSURE(m_pHelper, "XSDValidationPropertyHandler::getPropertyValue: inconsistency!");
+ // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties
+
+ Any aReturn;
+ ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType();
+ switch ( nPropId )
+ {
+ // common facets
+ case PROPERTY_ID_XSD_DATA_TYPE: aReturn = pType.is() ? pType->getFacet( PROPERTY_NAME ) : Any( OUString() ); break;
+ case PROPERTY_ID_XSD_WHITESPACES:aReturn = pType.is() ? pType->getFacet( PROPERTY_XSD_WHITESPACES ) : Any( WhiteSpaceTreatment::Preserve ); break;
+ case PROPERTY_ID_XSD_PATTERN: aReturn = pType.is() ? pType->getFacet( PROPERTY_XSD_PATTERN ) : Any( OUString() ); break;
+
+ // all other properties are simply forwarded, if they exist at the given type
+ default:
+ {
+ if ( pType.is() && pType->hasFacet( _rPropertyName ) )
+ aReturn = pType->getFacet( _rPropertyName );
+ }
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ void SAL_CALL XSDValidationPropertyHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ OSL_ENSURE(m_pHelper, "XSDValidationPropertyHandler::getPropertyValue: inconsistency!");
+ // if we survived impl_getPropertyId_throwUnknownProperty, we should have a helper, since no helper implies no properties
+
+ if ( PROPERTY_ID_XSD_DATA_TYPE == nPropId )
+ {
+ OUString sTypeName;
+ OSL_VERIFY( _rValue >>= sTypeName );
+ m_pHelper->setValidatingDataTypeByName( sTypeName );
+ impl_setContextDocumentModified_nothrow();
+ return;
+ }
+
+ ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType();
+ if ( !pType.is() )
+ {
+ OSL_FAIL( "XSDValidationPropertyHandler::setPropertyValue: you're trying to set a type facet, without a current type!" );
+ return;
+ }
+
+ pType->setFacet( _rPropertyName, _rValue );
+ impl_setContextDocumentModified_nothrow();
+ }
+
+
+ void XSDValidationPropertyHandler::onNewComponent()
+ {
+ PropertyHandlerComponent::onNewComponent();
+
+ Reference< frame::XModel > xDocument( impl_getContextDocument_nothrow() );
+ DBG_ASSERT( xDocument.is(), "XSDValidationPropertyHandler::onNewComponent: no document!" );
+ if ( EFormsHelper::isEForm( xDocument ) )
+ m_pHelper.reset( new XSDValidationHelper( m_aMutex, m_xComponent, xDocument ) );
+ else
+ m_pHelper.reset();
+ }
+
+
+ Sequence< Property > XSDValidationPropertyHandler::doDescribeSupportedProperties() const
+ {
+ std::vector< Property > aProperties;
+
+ if (m_pHelper)
+ {
+ bool bAllowBinding = m_pHelper->canBindToAnyDataType();
+
+ if ( bAllowBinding )
+ {
+ aProperties.reserve( 12 );
+
+ addStringPropertyDescription( aProperties, PROPERTY_XSD_DATA_TYPE );
+ addInt16PropertyDescription ( aProperties, PROPERTY_XSD_WHITESPACES );
+ addStringPropertyDescription( aProperties, PROPERTY_XSD_PATTERN );
+
+ // string facets
+ addInt32PropertyDescription( aProperties, PROPERTY_XSD_LENGTH, MAYBEVOID );
+ addInt32PropertyDescription( aProperties, PROPERTY_XSD_MIN_LENGTH, MAYBEVOID );
+ addInt32PropertyDescription( aProperties, PROPERTY_XSD_MAX_LENGTH, MAYBEVOID );
+
+ // decimal facets
+ addInt32PropertyDescription( aProperties, PROPERTY_XSD_TOTAL_DIGITS, MAYBEVOID );
+ addInt32PropertyDescription( aProperties, PROPERTY_XSD_FRACTION_DIGITS, MAYBEVOID );
+
+ // facets for different types
+ addInt16PropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_INT, MAYBEVOID );
+ addInt16PropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_INT, MAYBEVOID );
+ addInt16PropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_INT, MAYBEVOID );
+ addInt16PropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_INT, MAYBEVOID );
+ addDoublePropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_DOUBLE, MAYBEVOID );
+ addDoublePropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_DOUBLE, MAYBEVOID );
+ addDoublePropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_DOUBLE, MAYBEVOID );
+ addDoublePropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_DOUBLE, MAYBEVOID );
+ addDatePropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_DATE, MAYBEVOID );
+ addDatePropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_DATE, MAYBEVOID );
+ addDatePropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_DATE, MAYBEVOID );
+ addDatePropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_DATE, MAYBEVOID );
+ addTimePropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_TIME, MAYBEVOID );
+ addTimePropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_TIME, MAYBEVOID );
+ addTimePropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_TIME, MAYBEVOID );
+ addTimePropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_TIME, MAYBEVOID );
+ addDateTimePropertyDescription( aProperties, PROPERTY_XSD_MAX_INCLUSIVE_DATE_TIME, MAYBEVOID );
+ addDateTimePropertyDescription( aProperties, PROPERTY_XSD_MAX_EXCLUSIVE_DATE_TIME, MAYBEVOID );
+ addDateTimePropertyDescription( aProperties, PROPERTY_XSD_MIN_INCLUSIVE_DATE_TIME, MAYBEVOID );
+ addDateTimePropertyDescription( aProperties, PROPERTY_XSD_MIN_EXCLUSIVE_DATE_TIME, MAYBEVOID );
+ }
+ }
+
+ return comphelper::containerToSequence( aProperties );
+ }
+
+
+ Sequence< OUString > SAL_CALL XSDValidationPropertyHandler::getSupersededProperties( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ std::vector< OUString > aSuperfluous;
+ if (m_pHelper)
+ {
+ aSuperfluous.push_back( OUString(PROPERTY_CONTROLSOURCE) );
+ aSuperfluous.push_back( OUString(PROPERTY_EMPTY_IS_NULL) );
+ aSuperfluous.push_back( OUString(PROPERTY_FILTERPROPOSAL) );
+ aSuperfluous.push_back( OUString(PROPERTY_LISTSOURCETYPE) );
+ aSuperfluous.push_back( OUString(PROPERTY_LISTSOURCE) );
+ aSuperfluous.push_back( OUString(PROPERTY_BOUNDCOLUMN) );
+
+ bool bAllowBinding = m_pHelper->canBindToAnyDataType();
+
+ if ( bAllowBinding )
+ {
+ aSuperfluous.push_back( OUString(PROPERTY_MAXTEXTLEN) );
+ aSuperfluous.push_back( OUString(PROPERTY_VALUEMIN) );
+ aSuperfluous.push_back( OUString(PROPERTY_VALUEMAX) );
+ aSuperfluous.push_back( OUString(PROPERTY_DECIMAL_ACCURACY) );
+ aSuperfluous.push_back( OUString(PROPERTY_TIMEMIN) );
+ aSuperfluous.push_back( OUString(PROPERTY_TIMEMAX) );
+ aSuperfluous.push_back( OUString(PROPERTY_DATEMIN) );
+ aSuperfluous.push_back( OUString(PROPERTY_DATEMAX) );
+ aSuperfluous.push_back( OUString(PROPERTY_EFFECTIVE_MIN) );
+ aSuperfluous.push_back( OUString(PROPERTY_EFFECTIVE_MAX) );
+ }
+ }
+
+ return comphelper::containerToSequence( aSuperfluous );
+ }
+
+
+ Sequence< OUString > SAL_CALL XSDValidationPropertyHandler::getActuatingProperties( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ std::vector< OUString > aInterestedInActuations;
+ if (m_pHelper)
+ {
+ aInterestedInActuations.push_back( OUString(PROPERTY_XSD_DATA_TYPE) );
+ aInterestedInActuations.push_back( OUString(PROPERTY_XML_DATA_MODEL) );
+ }
+ return comphelper::containerToSequence( aInterestedInActuations );
+ }
+
+
+ namespace
+ {
+ void showPropertyUI( const Reference< XObjectInspectorUI >& _rxInspectorUI, const OUString& _rPropertyName, bool _bShow )
+ {
+ if ( _bShow )
+ _rxInspectorUI->showPropertyUI( _rPropertyName );
+ else
+ _rxInspectorUI->hidePropertyUI( _rPropertyName );
+ }
+ }
+
+
+ LineDescriptor SAL_CALL XSDValidationPropertyHandler::describePropertyLine( const OUString& _rPropertyName,
+ const Reference< XPropertyControlFactory >& _rxControlFactory )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !_rxControlFactory.is() )
+ throw NullPointerException();
+ if (!m_pHelper)
+ throw RuntimeException();
+
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ LineDescriptor aDescriptor;
+ if ( nPropId != PROPERTY_ID_XSD_DATA_TYPE )
+ aDescriptor.IndentLevel = 1;
+
+ // collect some information about the to-be-created control
+ sal_Int16 nControlType = PropertyControlType::TextField;
+ std::vector< OUString > aListEntries;
+ Optional< double > aMinValue( false, 0 );
+ Optional< double > aMaxValue( false, 0 );
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_XSD_DATA_TYPE:
+ nControlType = PropertyControlType::ListBox;
+
+ implGetAvailableDataTypeNames( aListEntries );
+
+ aDescriptor.PrimaryButtonId = UID_PROP_ADD_DATA_TYPE;
+ aDescriptor.SecondaryButtonId = UID_PROP_REMOVE_DATA_TYPE;
+ aDescriptor.HasPrimaryButton = aDescriptor.HasSecondaryButton = true;
+ aDescriptor.PrimaryButtonImageURL = "private:graphicrepository/extensions/res/buttonplus.png";
+ aDescriptor.SecondaryButtonImageURL = "private:graphicrepository/extensions/res/buttonminus.png";
+ break;
+
+ case PROPERTY_ID_XSD_WHITESPACES:
+ {
+ nControlType = PropertyControlType::ListBox;
+ aListEntries = m_pInfoService->getPropertyEnumRepresentations( PROPERTY_ID_XSD_WHITESPACES );
+ }
+ break;
+
+ case PROPERTY_ID_XSD_PATTERN:
+ nControlType = PropertyControlType::TextField;
+ break;
+
+ case PROPERTY_ID_XSD_LENGTH:
+ case PROPERTY_ID_XSD_MIN_LENGTH:
+ case PROPERTY_ID_XSD_MAX_LENGTH:
+ nControlType = PropertyControlType::NumericField;
+ break;
+
+ case PROPERTY_ID_XSD_TOTAL_DIGITS:
+ case PROPERTY_ID_XSD_FRACTION_DIGITS:
+ nControlType = PropertyControlType::NumericField;
+ break;
+
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_INT:
+ {
+ nControlType = PropertyControlType::NumericField;
+
+ // handle limits for various 'INT' types according to
+ // their actual semantics (year, month, day)
+
+ ::rtl::Reference< XSDDataType > xDataType( m_pHelper->getValidatingDataType() );
+ sal_Int16 nTypeClass = xDataType.is() ? xDataType->classify() : DataTypeClass::STRING;
+
+ aMinValue.IsPresent = aMaxValue.IsPresent = true;
+ aMinValue.Value = DataTypeClass::gYear == nTypeClass ? 0 : 1;
+ aMaxValue.Value = std::numeric_limits< sal_Int32 >::max();
+ if ( DataTypeClass::gMonth == nTypeClass )
+ aMaxValue.Value = 12;
+ else if ( DataTypeClass::gDay == nTypeClass )
+ aMaxValue.Value = 31;
+ }
+ break;
+
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DOUBLE:
+ nControlType = PropertyControlType::NumericField;
+ // TODO/eForms: do we have "auto-digits"?
+ break;
+
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE:
+ nControlType = PropertyControlType::DateField;
+ break;
+
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_TIME:
+ nControlType = PropertyControlType::TimeField;
+ break;
+
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE_TIME:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE_TIME:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE_TIME:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE_TIME:
+ nControlType = PropertyControlType::DateTimeField;
+ break;
+
+ default:
+ OSL_FAIL( "XSDValidationPropertyHandler::describePropertyLine: cannot handle this property!" );
+ break;
+ }
+
+ switch ( nControlType )
+ {
+ case PropertyControlType::ListBox:
+ aDescriptor.Control = PropertyHandlerHelper::createListBoxControl( _rxControlFactory, std::move(aListEntries), false, false );
+ break;
+ case PropertyControlType::NumericField:
+ aDescriptor.Control = PropertyHandlerHelper::createNumericControl( _rxControlFactory, 0, aMinValue, aMaxValue );
+ break;
+ default:
+ aDescriptor.Control = _rxControlFactory->createPropertyControl( nControlType, false );
+ break;
+ }
+
+ aDescriptor.Category = "Data";
+ aDescriptor.DisplayName = m_pInfoService->getPropertyTranslation( nPropId );
+ aDescriptor.HelpURL = HelpIdUrl::getHelpURL( m_pInfoService->getPropertyHelpId( nPropId ) );
+
+ return aDescriptor;
+ }
+
+
+ InteractiveSelectionResult SAL_CALL XSDValidationPropertyHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, Any& /*_rData*/, const Reference< XObjectInspectorUI >& _rxInspectorUI )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_ENSURE(m_pHelper, "XSDValidationPropertyHandler::onInteractivePropertySelection: we "
+ "don't have any SupportedProperties!");
+ if (!m_pHelper)
+ return InteractiveSelectionResult_Cancelled;
+
+ PropertyId nPropId( impl_getPropertyId_throwUnknownProperty( _rPropertyName ) );
+
+ switch ( nPropId )
+ {
+ case PROPERTY_ID_XSD_DATA_TYPE:
+ {
+ if ( _bPrimary )
+ {
+ OUString sNewDataTypeName;
+ if ( implPrepareCloneDataCurrentType( sNewDataTypeName ) )
+ {
+ implDoCloneCurrentDataType( sNewDataTypeName );
+ return InteractiveSelectionResult_Success;
+ }
+ }
+ else
+ return implPrepareRemoveCurrentDataType() && implDoRemoveCurrentDataType() ? InteractiveSelectionResult_Success : InteractiveSelectionResult_Cancelled;
+ }
+ break;
+
+ default:
+ OSL_FAIL( "XSDValidationPropertyHandler::onInteractivePropertySelection: unexpected property to build a dedicated UI!" );
+ break;
+ }
+ return InteractiveSelectionResult_Cancelled;
+ }
+
+
+ void SAL_CALL XSDValidationPropertyHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyHandlerComponent::addPropertyChangeListener( _rxListener );
+ if (m_pHelper)
+ m_pHelper->registerBindingListener( _rxListener );
+ }
+
+
+ void SAL_CALL XSDValidationPropertyHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (m_pHelper)
+ m_pHelper->revokeBindingListener( _rxListener );
+ PropertyHandlerComponent::removePropertyChangeListener( _rxListener );
+ }
+
+
+ bool XSDValidationPropertyHandler::implPrepareCloneDataCurrentType( OUString& _rNewName )
+ {
+ OSL_PRECOND(
+ m_pHelper,
+ "XSDValidationPropertyHandler::implPrepareCloneDataCurrentType: this will crash!");
+
+ ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType();
+ if ( !pType.is() )
+ {
+ OSL_FAIL( "XSDValidationPropertyHandler::implPrepareCloneDataCurrentType: invalid current data type!" );
+ return false;
+ }
+
+ std::vector< OUString > aExistentNames;
+ m_pHelper->getAvailableDataTypeNames( aExistentNames );
+
+ NewDataTypeDialog aDialog( nullptr, pType->getName(), aExistentNames ); // TODO/eForms: proper parent
+ if (aDialog.run() != RET_OK)
+ return false;
+
+ _rNewName = aDialog.GetName();
+ return true;
+ }
+
+
+ void XSDValidationPropertyHandler::implDoCloneCurrentDataType( const OUString& _rNewName )
+ {
+ OSL_PRECOND(m_pHelper,
+ "XSDValidationPropertyHandler::implDoCloneCurrentDataType: this will crash!");
+
+ ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType();
+ if ( !pType.is() )
+ return;
+
+ if ( !m_pHelper->cloneDataType( pType, _rNewName ) )
+ return;
+
+ m_pHelper->setValidatingDataTypeByName( _rNewName );
+ }
+
+ bool XSDValidationPropertyHandler::implPrepareRemoveCurrentDataType()
+ {
+ OSL_PRECOND(
+ m_pHelper,
+ "XSDValidationPropertyHandler::implPrepareRemoveCurrentDataType: this will crash!");
+
+ ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType();
+ if ( !pType.is() )
+ {
+ OSL_FAIL( "XSDValidationPropertyHandler::implPrepareRemoveCurrentDataType: invalid current data type!" );
+ return false;
+ }
+
+ // confirmation message
+ OUString sConfirmation( PcrRes( RID_STR_CONFIRM_DELETE_DATA_TYPE ) );
+ sConfirmation = sConfirmation.replaceFirst( "#type#", pType->getName() );
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr, // TODO/eForms: proper parent
+ VclMessageType::Question, VclButtonsType::YesNo,
+ sConfirmation));
+ return xQueryBox->run() == RET_YES;
+ }
+
+ bool XSDValidationPropertyHandler::implDoRemoveCurrentDataType()
+ {
+ OSL_PRECOND(m_pHelper,
+ "XSDValidationPropertyHandler::implDoRemoveCurrentDataType: this will crash!");
+
+ ::rtl::Reference< XSDDataType > pType = m_pHelper->getValidatingDataType();
+ if ( !pType.is() )
+ return false;
+
+ // set a new data type at the binding, which is the "basic" type for the one
+ // we are going to delete
+ // (do this before the actual deletion, so the old type is still valid for property change
+ // notifications)
+ m_pHelper->setValidatingDataTypeByName( m_pHelper->getBasicTypeNameForClass( pType->classify() ) );
+ // now remove the type
+ m_pHelper->removeDataTypeFromRepository( pType->getName() );
+
+ return true;
+ }
+
+
+ void SAL_CALL XSDValidationPropertyHandler::actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const Any& _rNewValue, const Any& _rOldValue, const Reference< XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit )
+ {
+ if ( !_rxInspectorUI.is() )
+ throw NullPointerException();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ PropertyId nActuatingPropId( impl_getPropertyId_throwRuntime( _rActuatingPropertyName ) );
+ if (!m_pHelper)
+ throw RuntimeException();
+ // if we survived impl_getPropertyId_throwRuntime, we should have a helper, since no helper implies no properties
+
+ switch ( nActuatingPropId )
+ {
+ case PROPERTY_ID_XSD_DATA_TYPE:
+ {
+ ::rtl::Reference< XSDDataType > xDataType( m_pHelper->getValidatingDataType() );
+
+ // is removal of this type possible?
+ bool bIsBasicType = xDataType.is() && xDataType->isBasicType();
+ _rxInspectorUI->enablePropertyUIElements( PROPERTY_XSD_DATA_TYPE, PropertyLineElement::PrimaryButton, xDataType.is() );
+ _rxInspectorUI->enablePropertyUIElements( PROPERTY_XSD_DATA_TYPE, PropertyLineElement::SecondaryButton, xDataType.is() && !bIsBasicType );
+
+
+ // show the facets which are available at the data type
+ OUString aFacets[] = {
+ OUString(PROPERTY_XSD_WHITESPACES), OUString(PROPERTY_XSD_PATTERN),
+ OUString(PROPERTY_XSD_LENGTH), OUString(PROPERTY_XSD_MIN_LENGTH), OUString(PROPERTY_XSD_MAX_LENGTH), OUString(PROPERTY_XSD_TOTAL_DIGITS),
+ OUString(PROPERTY_XSD_FRACTION_DIGITS),
+ OUString(PROPERTY_XSD_MAX_INCLUSIVE_INT),
+ OUString(PROPERTY_XSD_MAX_EXCLUSIVE_INT),
+ OUString(PROPERTY_XSD_MIN_INCLUSIVE_INT),
+ OUString(PROPERTY_XSD_MIN_EXCLUSIVE_INT),
+ OUString(PROPERTY_XSD_MAX_INCLUSIVE_DOUBLE),
+ OUString(PROPERTY_XSD_MAX_EXCLUSIVE_DOUBLE),
+ OUString(PROPERTY_XSD_MIN_INCLUSIVE_DOUBLE),
+ OUString(PROPERTY_XSD_MIN_EXCLUSIVE_DOUBLE),
+ OUString(PROPERTY_XSD_MAX_INCLUSIVE_DATE),
+ OUString(PROPERTY_XSD_MAX_EXCLUSIVE_DATE),
+ OUString(PROPERTY_XSD_MIN_INCLUSIVE_DATE),
+ OUString(PROPERTY_XSD_MIN_EXCLUSIVE_DATE),
+ OUString(PROPERTY_XSD_MAX_INCLUSIVE_TIME),
+ OUString(PROPERTY_XSD_MAX_EXCLUSIVE_TIME),
+ OUString(PROPERTY_XSD_MIN_INCLUSIVE_TIME),
+ OUString(PROPERTY_XSD_MIN_EXCLUSIVE_TIME),
+ OUString(PROPERTY_XSD_MAX_INCLUSIVE_DATE_TIME),
+ OUString(PROPERTY_XSD_MAX_EXCLUSIVE_DATE_TIME),
+ OUString(PROPERTY_XSD_MIN_INCLUSIVE_DATE_TIME),
+ OUString(PROPERTY_XSD_MIN_EXCLUSIVE_DATE_TIME)
+ };
+
+ size_t i=0;
+ const OUString* pLoop = nullptr;
+ for ( i = 0, pLoop = aFacets;
+ i < SAL_N_ELEMENTS( aFacets );
+ ++i, ++pLoop
+ )
+ {
+ showPropertyUI( _rxInspectorUI, *pLoop, xDataType.is() && xDataType->hasFacet( *pLoop ) );
+ _rxInspectorUI->enablePropertyUI( *pLoop, !bIsBasicType );
+ }
+ }
+ break;
+
+ case PROPERTY_ID_XML_DATA_MODEL:
+ {
+ // The data type which the current binding works with may not be present in the
+ // new model. Thus, transfer it.
+ OUString sOldModelName; _rOldValue >>= sOldModelName;
+ OUString sNewModelName; _rNewValue >>= sNewModelName;
+ OUString sDataType = m_pHelper->getValidatingDataTypeName();
+ m_pHelper->copyDataType( sOldModelName, sNewModelName, sDataType );
+
+ // the list of available data types depends on the chosen model, so update this
+ if ( !_bFirstTimeInit )
+ _rxInspectorUI->rebuildPropertyUI( PROPERTY_XSD_DATA_TYPE );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "XSDValidationPropertyHandler::actuatingPropertyChanged: cannot handle this property!" );
+ return;
+ }
+
+ // in both cases, we need to care for the current value of the XSD_DATA_TYPE property,
+ // and update the FormatKey of the formatted field we're inspecting (if any)
+ if ( !_bFirstTimeInit && m_pHelper->isInspectingFormattedField() )
+ m_pHelper->findDefaultFormatForIntrospectee();
+ }
+
+
+ void XSDValidationPropertyHandler::implGetAvailableDataTypeNames( std::vector< OUString >& /* [out] */ _rNames ) const
+ {
+ OSL_PRECOND(
+ m_pHelper,
+ "XSDValidationPropertyHandler::implGetAvailableDataTypeNames: this will crash!");
+ // start with *all* types which are available at the model
+ std::vector< OUString > aAllTypes;
+ m_pHelper->getAvailableDataTypeNames( aAllTypes );
+ _rNames.clear();
+ _rNames.reserve( aAllTypes.size() );
+
+ // then allow only those which are "compatible" with our control
+ for (auto const& dataType : aAllTypes)
+ {
+ ::rtl::Reference< XSDDataType > pType = m_pHelper->getDataTypeByName(dataType);
+ if ( pType.is() && m_pHelper->canBindToDataType( pType->classify() ) )
+ _rNames.push_back(dataType);
+ }
+ }
+
+
+} // namespace pcr
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_propctrlr_XSDValidationPropertyHandler_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new pcr::XSDValidationPropertyHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/propctrlr/xsdvalidationpropertyhandler.hxx b/extensions/source/propctrlr/xsdvalidationpropertyhandler.hxx
new file mode 100644
index 000000000..32803b215
--- /dev/null
+++ b/extensions/source/propctrlr/xsdvalidationpropertyhandler.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 "propertyhandler.hxx"
+
+#include <memory>
+
+
+namespace pcr
+{
+
+
+ class XSDValidationHelper;
+
+ //= XSDValidationPropertyHandler
+
+ class XSDValidationPropertyHandler : public PropertyHandlerComponent
+ {
+ private:
+ std::unique_ptr< XSDValidationHelper > m_pHelper;
+
+ public:
+ explicit XSDValidationPropertyHandler(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext
+ );
+
+ protected:
+ virtual ~XSDValidationPropertyHandler() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
+
+ // XPropertyHandler overriables
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& _rPropertyName ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ virtual css::uno::Sequence< OUString >
+ SAL_CALL getSupersededProperties( ) override;
+ virtual css::uno::Sequence< OUString >
+ SAL_CALL getActuatingProperties( ) override;
+ virtual css::inspection::LineDescriptor
+ SAL_CALL describePropertyLine( const OUString& _rPropertyName, const css::uno::Reference< css::inspection::XPropertyControlFactory >& _rxControlFactory ) override;
+ virtual css::inspection::InteractiveSelectionResult
+ SAL_CALL onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool _bPrimary, css::uno::Any& _rData, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI ) override;
+ virtual void SAL_CALL actuatingPropertyChanged( const OUString& _rActuatingPropertyName, const css::uno::Any& _rNewValue, const css::uno::Any& _rOldValue, const css::uno::Reference< css::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const css::uno::Reference< css::beans::XPropertyChangeListener >& _rxListener ) override;
+
+ // PropertyHandler overridables
+ virtual css::uno::Sequence< css::beans::Property >
+ doDescribeSupportedProperties() const override;
+ virtual void onNewComponent() override;
+
+ private:
+ bool implPrepareRemoveCurrentDataType();
+ bool implDoRemoveCurrentDataType();
+
+ bool implPrepareCloneDataCurrentType( OUString& _rNewName );
+ void implDoCloneCurrentDataType( const OUString& _rNewName );
+
+ /** retrieves the names of the data types which our introspectee can be validated against
+ */
+ void implGetAvailableDataTypeNames( std::vector< OUString >& /* [out] */ _rNames ) const;
+ };
+
+
+} // namespace pcr
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/grid.cxx b/extensions/source/scanner/grid.cxx
new file mode 100644
index 000000000..110779c55
--- /dev/null
+++ b/extensions/source/scanner/grid.cxx
@@ -0,0 +1,700 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <osl/thread.h>
+#include <rtl/math.hxx>
+#include <cstdio>
+
+#include <bitmaps.hlst>
+#include <cmath>
+
+#include "grid.hxx"
+#include <vcl/bitmapex.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+
+class GridWindow : public weld::CustomWidgetController
+{
+ // helper class for handles
+ struct impHandle
+ {
+ Point maPos;
+ sal_uInt16 mnOffX;
+ sal_uInt16 mnOffY;
+
+ impHandle(const Point& rPos, sal_uInt16 nX, sal_uInt16 nY)
+ : maPos(rPos), mnOffX(nX), mnOffY(nY)
+ {
+ }
+
+ bool operator<(const impHandle& rComp) const
+ {
+ return (maPos.X() < rComp.maPos.X());
+ }
+
+ void draw(vcl::RenderContext& rRenderContext, const BitmapEx& rBitmapEx)
+ {
+ const Point aOffset(rRenderContext.PixelToLogic(Point(mnOffX, mnOffY)));
+ rRenderContext.DrawBitmapEx(maPos - aOffset, rBitmapEx);
+ }
+
+ bool isHit(OutputDevice const & rWin, const Point& rPos)
+ {
+ const Point aOffset(rWin.PixelToLogic(Point(mnOffX, mnOffY)));
+ const tools::Rectangle aTarget(maPos - aOffset, maPos + aOffset);
+ return aTarget.Contains(rPos);
+ }
+ };
+
+ tools::Rectangle m_aGridArea;
+
+ double m_fMinX;
+ double m_fMinY;
+ double m_fMaxX;
+ double m_fMaxY;
+
+ double m_fChunkX;
+ double m_fMinChunkX;
+ double m_fChunkY;
+ double m_fMinChunkY;
+
+ double* m_pXValues;
+ double* m_pOrigYValues;
+ int m_nValues;
+ std::unique_ptr<double[]> m_pNewYValues;
+
+ sal_uInt16 m_BmOffX;
+ sal_uInt16 m_BmOffY;
+
+ bool m_bCutValues;
+
+ // stuff for handles
+ using Handles = std::vector<impHandle>;
+ static constexpr auto npos = std::numeric_limits<Handles::size_type>::max();
+ Handles m_aHandles;
+ Handles::size_type m_nDragIndex;
+
+ BitmapEx m_aMarkerBitmap;
+
+ Point transform( double x, double y );
+ void transform( const Point& rOriginal, double& x, double& y );
+
+ double findMinX();
+ double findMinY();
+ double findMaxX();
+ double findMaxY();
+
+ void drawGrid(vcl::RenderContext& rRenderContext);
+ void drawOriginal(vcl::RenderContext& rRenderContext);
+ void drawNew(vcl::RenderContext& rRenderContext);
+ void drawHandles(vcl::RenderContext& rRenderContext);
+
+ void computeExtremes();
+ static void computeChunk( double fMin, double fMax, double& fChunkOut, double& fMinChunkOut );
+ void computeNew();
+ static double interpolate( double x, double const * pNodeX, double const * pNodeY, int nNodes );
+
+ virtual bool MouseMove( const MouseEvent& ) override;
+ virtual bool MouseButtonDown( const MouseEvent& ) override;
+ virtual bool MouseButtonUp( const MouseEvent& ) override;
+ void onResize();
+ virtual void Resize() override;
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+ void drawLine(vcl::RenderContext& rRenderContext, double x1, double y1, double x2, double y2);
+public:
+ GridWindow();
+ void Init(double* pXValues, double* pYValues, int nValues, bool bCutValues, const BitmapEx &rMarkerBitmap);
+ virtual ~GridWindow() override;
+
+ void setBoundings( double fMinX, double fMinY, double fMaxX, double fMaxY );
+
+ double* getNewYValues() { return m_pNewYValues.get(); }
+
+ void ChangeMode(ResetType nType);
+
+ virtual void Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect ) override;
+};
+
+GridWindow::GridWindow()
+ : m_aGridArea(50, 15, 100, 100)
+ , m_fMinX(0.0)
+ , m_fMinY(0.0)
+ , m_fMaxX(0.0)
+ , m_fMaxY(0.0)
+ , m_fChunkX(0.0)
+ , m_fMinChunkX(0.0)
+ , m_fChunkY(0.0)
+ , m_fMinChunkY(0.0)
+ , m_pXValues(nullptr)
+ , m_pOrigYValues(nullptr)
+ , m_nValues(0)
+ , m_BmOffX(0)
+ , m_BmOffY(0)
+ , m_bCutValues(false)
+ , m_nDragIndex(npos)
+{
+}
+
+void GridWindow::Init(double* pXValues, double* pYValues, int nValues, bool bCutValues, const BitmapEx &rMarkerBitmap)
+{
+ m_aMarkerBitmap = rMarkerBitmap;
+ m_pXValues = pXValues;
+ m_pOrigYValues = pYValues;
+ m_nValues = nValues;
+ m_bCutValues = bCutValues;
+
+ onResize();
+
+ if (m_pOrigYValues && m_nValues)
+ {
+ m_pNewYValues.reset(new double[ m_nValues ]);
+ memcpy( m_pNewYValues.get(), m_pOrigYValues, sizeof( double ) * m_nValues );
+ }
+
+ setBoundings( 0, 0, 1023, 1023 );
+ computeExtremes();
+
+ // create left and right marker as first and last entry
+ m_BmOffX = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Width() >> 1);
+ m_BmOffY = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Height() >> 1);
+ m_aHandles.push_back(impHandle(transform(findMinX(), findMinY()), m_BmOffX, m_BmOffY));
+ m_aHandles.push_back(impHandle(transform(findMaxX(), findMaxY()), m_BmOffX, m_BmOffY));
+}
+
+void GridWindow::Resize()
+{
+ onResize();
+}
+
+void GridWindow::onResize()
+{
+ Size aSize = GetOutputSizePixel();
+ m_aGridArea.setWidth( aSize.Width() - 80 );
+ m_aGridArea.setHeight( aSize.Height() - 40 );
+}
+
+void GridWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(240, 200), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ SetOutputSizePixel(aSize);
+}
+
+GridDialog::GridDialog(weld::Window* pParent, double* pXValues, double* pYValues, int nValues)
+ : GenericDialogController(pParent, "modules/scanner/ui/griddialog.ui", "GridDialog")
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+ , m_xResetTypeBox(m_xBuilder->weld_combo_box("resetTypeCombobox"))
+ , m_xResetButton(m_xBuilder->weld_button("resetButton"))
+ , m_xGridWindow(new GridWindow)
+ , m_xGridWindowWND(new weld::CustomWeld(*m_xBuilder, "gridwindow", *m_xGridWindow))
+{
+ m_xGridWindow->Init(pXValues, pYValues, nValues, true/*bCutValues*/, BitmapEx(RID_SCANNER_HANDLE));
+ m_xResetTypeBox->set_active(0);
+ m_xResetButton->connect_clicked( LINK( this, GridDialog, ClickButtonHdl ) );
+}
+
+GridDialog::~GridDialog()
+{
+}
+
+GridWindow::~GridWindow()
+{
+ m_pNewYValues.reset();
+}
+
+double GridWindow::findMinX()
+{
+ if( ! m_pXValues )
+ return 0.0;
+ double fMin = m_pXValues[0];
+ for( int i = 1; i < m_nValues; i++ )
+ if( m_pXValues[ i ] < fMin )
+ fMin = m_pXValues[ i ];
+ return fMin;
+}
+
+double GridWindow::findMinY()
+{
+ if( ! m_pNewYValues )
+ return 0.0;
+ double fMin = m_pNewYValues[0];
+ for( int i = 1; i < m_nValues; i++ )
+ if( m_pNewYValues[ i ] < fMin )
+ fMin = m_pNewYValues[ i ];
+ return fMin;
+}
+
+
+double GridWindow::findMaxX()
+{
+ if( ! m_pXValues )
+ return 0.0;
+ double fMax = m_pXValues[0];
+ for( int i = 1; i < m_nValues; i++ )
+ if( m_pXValues[ i ] > fMax )
+ fMax = m_pXValues[ i ];
+ return fMax;
+}
+
+
+double GridWindow::findMaxY()
+{
+ if( ! m_pNewYValues )
+ return 0.0;
+ double fMax = m_pNewYValues[0];
+ for( int i = 1; i < m_nValues; i++ )
+ if( m_pNewYValues[ i ] > fMax )
+ fMax = m_pNewYValues[ i ];
+ return fMax;
+}
+
+
+void GridWindow::computeExtremes()
+{
+ if( !(m_nValues && m_pXValues && m_pOrigYValues) )
+ return;
+
+ m_fMaxX = m_fMinX = m_pXValues[0];
+ m_fMaxY = m_fMinY = m_pOrigYValues[0];
+ for( int i = 1; i < m_nValues; i++ )
+ {
+ if( m_pXValues[ i ] > m_fMaxX )
+ m_fMaxX = m_pXValues[ i ];
+ else if( m_pXValues[ i ] < m_fMinX )
+ m_fMinX = m_pXValues[ i ];
+ if( m_pOrigYValues[ i ] > m_fMaxY )
+ m_fMaxY = m_pOrigYValues[ i ];
+ else if( m_pOrigYValues[ i ] < m_fMinY )
+ m_fMinY = m_pOrigYValues[ i ];
+ }
+ setBoundings( m_fMinX, m_fMinY, m_fMaxX, m_fMaxY );
+}
+
+
+Point GridWindow::transform( double x, double y )
+{
+ Point aRet;
+
+ aRet.setX( static_cast<tools::Long>( ( x - m_fMinX ) *
+ static_cast<double>(m_aGridArea.GetWidth()) / ( m_fMaxX - m_fMinX )
+ + m_aGridArea.Left() ) );
+ aRet.setY( static_cast<tools::Long>(
+ m_aGridArea.Bottom() -
+ ( y - m_fMinY ) *
+ static_cast<double>(m_aGridArea.GetHeight()) / ( m_fMaxY - m_fMinY ) ) );
+ return aRet;
+}
+
+void GridWindow::transform( const Point& rOriginal, double& x, double& y )
+{
+ const tools::Long nWidth = m_aGridArea.GetWidth();
+ const tools::Long nHeight = m_aGridArea.GetHeight();
+ if (!nWidth || !nHeight)
+ return;
+ x = ( rOriginal.X() - m_aGridArea.Left() ) * (m_fMaxX - m_fMinX) / static_cast<double>(nWidth) + m_fMinX;
+ y = ( m_aGridArea.Bottom() - rOriginal.Y() ) * (m_fMaxY - m_fMinY) / static_cast<double>(nHeight) + m_fMinY;
+}
+
+void GridWindow::drawLine(vcl::RenderContext& rRenderContext, double x1, double y1, double x2, double y2 )
+{
+ rRenderContext.DrawLine(transform(x1, y1), transform(x2, y2));
+}
+
+void GridWindow::computeChunk( double fMin, double fMax, double& fChunkOut, double& fMinChunkOut )
+{
+ // get a nice chunk size like 10, 100, 25 or such
+ fChunkOut = ( fMax - fMin ) / 6.0;
+ int logchunk = static_cast<int>(std::log10( fChunkOut ));
+ int nChunk = static_cast<int>( fChunkOut / std::exp( static_cast<double>(logchunk-1) * M_LN10 ) );
+ if( nChunk >= 75 )
+ nChunk = 100;
+ else if( nChunk >= 35 )
+ nChunk = 50;
+ else if ( nChunk > 20 )
+ nChunk = 25;
+ else if ( nChunk >= 13 )
+ nChunk = 20;
+ else if( nChunk > 5 )
+ nChunk = 10;
+ else
+ nChunk = 5;
+ fChunkOut = static_cast<double>(nChunk) * exp( static_cast<double>(logchunk-1) * M_LN10 );
+ // compute whole chunks fitting into fMin
+ nChunk = static_cast<int>( fMin / fChunkOut );
+ fMinChunkOut = static_cast<double>(nChunk) * fChunkOut;
+ while( fMinChunkOut < fMin )
+ fMinChunkOut += fChunkOut;
+}
+
+
+void GridWindow::computeNew()
+{
+ if(2 == m_aHandles.size())
+ {
+ // special case: only left and right markers
+ double xleft, yleft;
+ double xright, yright;
+ transform(m_aHandles[0].maPos, xleft, yleft);
+ transform(m_aHandles[1].maPos, xright, yright );
+ double factor = (yright-yleft)/(xright-xleft);
+ for( int i = 0; i < m_nValues; i++ )
+ {
+ m_pNewYValues[ i ] = yleft + ( m_pXValues[ i ] - xleft )*factor;
+ }
+ }
+ else
+ {
+ // sort markers
+ std::sort(m_aHandles.begin(), m_aHandles.end());
+ const int nSorted = m_aHandles.size();
+ int i;
+
+ // get node arrays
+ std::unique_ptr<double[]> nodex(new double[ nSorted ]);
+ std::unique_ptr<double[]> nodey(new double[ nSorted ]);
+
+ for( i = 0; i < nSorted; i++ )
+ transform( m_aHandles[i].maPos, nodex[ i ], nodey[ i ] );
+
+ for( i = 0; i < m_nValues; i++ )
+ {
+ double x = m_pXValues[ i ];
+ m_pNewYValues[ i ] = interpolate( x, nodex.get(), nodey.get(), nSorted );
+ if( m_bCutValues )
+ {
+ if( m_pNewYValues[ i ] > m_fMaxY )
+ m_pNewYValues[ i ] = m_fMaxY;
+ else if( m_pNewYValues[ i ] < m_fMinY )
+ m_pNewYValues[ i ] = m_fMinY;
+ }
+ }
+ }
+}
+
+
+double GridWindow::interpolate(
+ double x,
+ double const * pNodeX,
+ double const * pNodeY,
+ int nNodes )
+{
+ // compute Lagrange interpolation
+ double ret = 0;
+ for( int i = 0; i < nNodes; i++ )
+ {
+ double sum = pNodeY[ i ];
+ for( int n = 0; n < nNodes; n++ )
+ {
+ if( n != i )
+ {
+ sum *= x - pNodeX[ n ];
+ sum /= pNodeX[ i ] - pNodeX[ n ];
+ }
+ }
+ ret += sum;
+ }
+ return ret;
+}
+
+void GridDialog::setBoundings(double fMinX, double fMinY, double fMaxX, double fMaxY)
+{
+ m_xGridWindow->setBoundings(fMinX, fMinY, fMaxX, fMaxY);
+}
+
+void GridWindow::setBoundings(double fMinX, double fMinY, double fMaxX, double fMaxY)
+{
+ m_fMinX = fMinX;
+ m_fMinY = fMinY;
+ m_fMaxX = fMaxX;
+ m_fMaxY = fMaxY;
+
+ computeChunk( m_fMinX, m_fMaxX, m_fChunkX, m_fMinChunkX );
+ computeChunk( m_fMinY, m_fMaxY, m_fChunkY, m_fMinChunkY );
+}
+
+void GridWindow::drawGrid(vcl::RenderContext& rRenderContext)
+{
+ char pBuf[256];
+ rRenderContext.SetLineColor(COL_BLACK);
+ // draw vertical lines
+ for (double fX = m_fMinChunkX; fX < m_fMaxX; fX += m_fChunkX)
+ {
+ drawLine(rRenderContext, fX, m_fMinY, fX, m_fMaxY);
+ // draw tickmarks
+ Point aPt = transform(fX, m_fMinY);
+ std::sprintf(pBuf, "%g", fX);
+ OUString aMark(pBuf, strlen(pBuf), osl_getThreadTextEncoding());
+ Size aTextSize(rRenderContext.GetTextWidth(aMark), rRenderContext.GetTextHeight());
+ aPt.AdjustX( -(aTextSize.Width() / 2) );
+ aPt.AdjustY(aTextSize.Height() / 2 );
+ rRenderContext.DrawText(aPt, aMark);
+ }
+ // draw horizontal lines
+ for (double fY = m_fMinChunkY; fY < m_fMaxY; fY += m_fChunkY)
+ {
+ drawLine(rRenderContext, m_fMinX, fY, m_fMaxX, fY);
+ // draw tickmarks
+ Point aPt = transform(m_fMinX, fY);
+ std::sprintf(pBuf, "%g", fY);
+ OUString aMark(pBuf, strlen(pBuf), osl_getThreadTextEncoding());
+ Size aTextSize(rRenderContext.GetTextWidth(aMark), rRenderContext.GetTextHeight());
+ aPt.AdjustX( -(aTextSize.Width() + 2) );
+ aPt.AdjustY( -(aTextSize.Height() / 2) );
+ rRenderContext.DrawText(aPt, aMark);
+ }
+
+ // draw boundings
+ drawLine(rRenderContext, m_fMinX, m_fMinY, m_fMaxX, m_fMinY);
+ drawLine(rRenderContext, m_fMinX, m_fMaxY, m_fMaxX, m_fMaxY);
+ drawLine(rRenderContext, m_fMinX, m_fMinY, m_fMinX, m_fMaxY);
+ drawLine(rRenderContext, m_fMaxX, m_fMinY, m_fMaxX, m_fMaxY);
+}
+
+void GridWindow::drawOriginal(vcl::RenderContext& rRenderContext)
+{
+ if (m_nValues && m_pXValues && m_pOrigYValues)
+ {
+ rRenderContext.SetLineColor(COL_RED);
+ for (int i = 0; i < m_nValues - 1; i++)
+ {
+ drawLine(rRenderContext,
+ m_pXValues[i], m_pOrigYValues[i],
+ m_pXValues[i + 1], m_pOrigYValues[i + 1]);
+ }
+ }
+}
+
+void GridWindow::drawNew(vcl::RenderContext& rRenderContext)
+{
+ if (m_nValues && m_pXValues && m_pNewYValues)
+ {
+ rRenderContext.SetClipRegion(vcl::Region(m_aGridArea));
+ rRenderContext.SetLineColor(COL_YELLOW);
+ for (int i = 0; i < m_nValues - 1; i++)
+ {
+ drawLine(rRenderContext,
+ m_pXValues[i], m_pNewYValues[i],
+ m_pXValues[i + 1], m_pNewYValues[i + 1]);
+ }
+ rRenderContext.SetClipRegion();
+ }
+}
+
+void GridWindow::drawHandles(vcl::RenderContext& rRenderContext)
+{
+ for(impHandle & rHandle : m_aHandles)
+ {
+ rHandle.draw(rRenderContext, m_aMarkerBitmap);
+ }
+}
+
+void GridWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetDialogColor()));
+ drawGrid(rRenderContext);
+ drawOriginal(rRenderContext);
+ drawNew(rRenderContext);
+ drawHandles(rRenderContext);
+}
+
+bool GridWindow::MouseMove( const MouseEvent& rEvt )
+{
+ if( rEvt.GetButtons() != MOUSE_LEFT || m_nDragIndex == npos )
+ return false;
+
+ Point aPoint( rEvt.GetPosPixel() );
+
+ if( m_nDragIndex == 0 || m_nDragIndex == m_aHandles.size() - 1)
+ {
+ aPoint.setX( m_aHandles[m_nDragIndex].maPos.X() );
+ }
+ else
+ {
+ if(aPoint.X() < m_aGridArea.Left())
+ aPoint.setX( m_aGridArea.Left() );
+ else if(aPoint.X() > m_aGridArea.Right())
+ aPoint.setX( m_aGridArea.Right() );
+ }
+
+ if( aPoint.Y() < m_aGridArea.Top() )
+ aPoint.setY( m_aGridArea.Top() );
+ else if( aPoint.Y() > m_aGridArea.Bottom() )
+ aPoint.setY( m_aGridArea.Bottom() );
+
+ if( aPoint != m_aHandles[m_nDragIndex].maPos )
+ {
+ m_aHandles[m_nDragIndex].maPos = aPoint;
+ Invalidate( m_aGridArea );
+ }
+
+ return false;
+}
+
+bool GridWindow::MouseButtonUp( const MouseEvent& rEvt )
+{
+ if( rEvt.GetButtons() == MOUSE_LEFT )
+ {
+ if( m_nDragIndex != npos )
+ {
+ m_nDragIndex = npos;
+ computeNew();
+ Invalidate(m_aGridArea);
+ }
+ }
+
+ return false;
+}
+
+bool GridWindow::MouseButtonDown( const MouseEvent& rEvt )
+{
+ Point aPoint( rEvt.GetPosPixel() );
+ Handles::size_type nMarkerIndex = npos;
+
+ for(Handles::size_type a(0); nMarkerIndex == npos && a < m_aHandles.size(); a++)
+ {
+ if(m_aHandles[a].isHit(GetDrawingArea()->get_ref_device(), aPoint))
+ {
+ nMarkerIndex = a;
+ }
+ }
+
+ if( rEvt.GetButtons() == MOUSE_LEFT )
+ {
+ // user wants to drag a button
+ if( nMarkerIndex != npos )
+ {
+ m_nDragIndex = nMarkerIndex;
+ }
+ }
+ else if( rEvt.GetButtons() == MOUSE_RIGHT )
+ {
+ // user wants to add/delete a button
+ if( nMarkerIndex != npos )
+ {
+ if( nMarkerIndex != 0 && nMarkerIndex != m_aHandles.size() - 1)
+ {
+ // delete marker under mouse
+ if( m_nDragIndex == nMarkerIndex )
+ m_nDragIndex = npos;
+
+ m_aHandles.erase(m_aHandles.begin() + nMarkerIndex);
+ }
+ }
+ else
+ {
+ m_BmOffX = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Width() >> 1);
+ m_BmOffY = sal_uInt16(m_aMarkerBitmap.GetSizePixel().Height() >> 1);
+ m_aHandles.push_back(impHandle(aPoint, m_BmOffX, m_BmOffY));
+ }
+
+ computeNew();
+ Invalidate(m_aGridArea);
+ }
+
+ return false;
+}
+
+void GridWindow::ChangeMode(ResetType nType)
+{
+ switch( nType )
+ {
+ case ResetType::LINEAR_ASCENDING:
+ {
+ for( int i = 0; i < m_nValues; i++ )
+ {
+ m_pNewYValues[ i ] = m_fMinY + (m_fMaxY-m_fMinY)/(m_fMaxX-m_fMinX)*(m_pXValues[i]-m_fMinX);
+ }
+ }
+ break;
+ case ResetType::LINEAR_DESCENDING:
+ {
+ for( int i = 0; i < m_nValues; i++ )
+ {
+ m_pNewYValues[ i ] = m_fMaxY - (m_fMaxY-m_fMinY)/(m_fMaxX-m_fMinX)*(m_pXValues[i]-m_fMinX);
+ }
+ }
+ break;
+ case ResetType::RESET:
+ {
+ if( m_pOrigYValues && m_pNewYValues && m_nValues )
+ memcpy( m_pNewYValues.get(), m_pOrigYValues, m_nValues*sizeof(double) );
+ }
+ break;
+ case ResetType::EXPONENTIAL:
+ {
+ for( int i = 0; i < m_nValues; i++ )
+ {
+ m_pNewYValues[ i ] = m_fMinY + (m_fMaxY-m_fMinY)*(rtl::math::expm1((m_pXValues[i]-m_fMinX)/(m_fMaxX-m_fMinX)))/(M_E-1.0);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (m_pNewYValues)
+ {
+ for(size_t i(0); i < m_aHandles.size(); i++)
+ {
+ // find nearest xvalue
+ double x, y;
+ transform( m_aHandles[i].maPos, x, y );
+ int nIndex = 0;
+ double delta = std::fabs( x-m_pXValues[0] );
+ for( int n = 1; n < m_nValues; n++ )
+ {
+ if( delta > std::fabs( x - m_pXValues[ n ] ) )
+ {
+ delta = std::fabs( x - m_pXValues[ n ] );
+ nIndex = n;
+ }
+ }
+ if( 0 == i )
+ m_aHandles[i].maPos = transform( m_fMinX, m_pNewYValues[ nIndex ] );
+ else if( m_aHandles.size() - 1 == i )
+ m_aHandles[i].maPos = transform( m_fMaxX, m_pNewYValues[ nIndex ] );
+ else
+ m_aHandles[i].maPos = transform( m_pXValues[ nIndex ], m_pNewYValues[ nIndex ] );
+ }
+ }
+
+ Invalidate();
+}
+
+IMPL_LINK_NOARG(GridDialog, ClickButtonHdl, weld::Button&, void)
+{
+ int nType = m_xResetTypeBox->get_active();
+ m_xGridWindow->ChangeMode(static_cast<ResetType>(nType));
+}
+
+double* GridDialog::getNewYValues()
+{
+ return m_xGridWindow->getNewYValues();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/grid.hxx b/extensions/source/scanner/grid.hxx
new file mode 100644
index 000000000..86f1a521c
--- /dev/null
+++ b/extensions/source/scanner/grid.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/customweld.hxx>
+#include <vcl/weld.hxx>
+
+class GridWindow;
+
+enum class ResetType
+{
+ LINEAR_ASCENDING = 0,
+ LINEAR_DESCENDING = 1,
+ RESET = 2,
+ EXPONENTIAL = 3
+};
+
+class GridDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::Button> m_xOKButton;
+ std::unique_ptr<weld::ComboBox> m_xResetTypeBox;
+ std::unique_ptr<weld::Button> m_xResetButton;
+ std::unique_ptr<GridWindow> m_xGridWindow;
+ std::unique_ptr<weld::CustomWeld> m_xGridWindowWND;
+
+ DECL_LINK(ClickButtonHdl, weld::Button&, void);
+
+public:
+ GridDialog(weld::Window* pParent, double* pXValues, double* pYValues, int nValues);
+ virtual ~GridDialog() override;
+ void setBoundings(double fMinX, double fMinY, double fMaxX, double fMaxY);
+ double* getNewYValues();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/sane.cxx b/extensions/source/scanner/sane.cxx
new file mode 100644
index 000000000..de03e1158
--- /dev/null
+++ b/extensions/source/scanner/sane.cxx
@@ -0,0 +1,996 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <type_traits>
+#include <math.h>
+
+#include <o3tl/safeint.hxx>
+#include <osl/file.h>
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+#include <unotools/tempfile.hxx>
+#include "sane.hxx"
+#include <dlfcn.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sal/config.h>
+#include <sal/macros.h>
+#include <memory>
+
+#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
+#include <stdarg.h>
+#define dump_state( a, b, c, d ) fprintf( stderr, a, b, c, d );
+#else
+#define dump_state( a, b, c, d ) ;
+#endif
+static void dbg_msg( const char* pString, ... )
+{
+#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
+ va_list ap;
+ va_start( ap, pString );
+ vfprintf( stderr, pString, ap );
+ va_end( ap );
+#else
+ (void)pString;
+#endif
+}
+
+#define FAIL_SHUTDOWN_STATE( x, y, z ) \
+ if( x != SANE_STATUS_GOOD ) \
+ { \
+ dump_state( "%s returned error %d (%s)\n", \
+ y, x, p_strstatus( x ) ); \
+ DeInit(); \
+ return z; \
+ }
+
+#define FAIL_STATE( x, y, z ) \
+ if( x != SANE_STATUS_GOOD ) \
+ { \
+ dump_state( "%s returned error %d (%s)\n", \
+ y, x, p_strstatus( x ) ); \
+ return z; \
+ }
+
+#define DUMP_STATE( x, y ) \
+ if( x != SANE_STATUS_GOOD ) \
+ { \
+ dump_state( "%s returned error %d (%s)\n", \
+ y, x, p_strstatus( x ) ); \
+ }
+
+int Sane::nRefCount = 0;
+oslModule Sane::pSaneLib = nullptr;
+SANE_Int Sane::nVersion = 0;
+SANE_Device** Sane::ppDevices = nullptr;
+int Sane::nDevices = 0;
+
+SANE_Status (*Sane::p_init)( SANE_Int*,
+ SANE_Auth_Callback ) = nullptr;
+void (*Sane::p_exit)() = nullptr;
+SANE_Status (*Sane::p_get_devices)( const SANE_Device***,
+ SANE_Bool ) = nullptr;
+SANE_Status (*Sane::p_open)( SANE_String_Const, SANE_Handle ) = nullptr;
+void (*Sane::p_close)( SANE_Handle ) = nullptr;
+const SANE_Option_Descriptor* (*Sane::p_get_option_descriptor)(
+ SANE_Handle, SANE_Int ) = nullptr;
+SANE_Status (*Sane::p_control_option)( SANE_Handle, SANE_Int,
+ SANE_Action, void*,
+ SANE_Int* ) = nullptr;
+SANE_Status (*Sane::p_get_parameters)( SANE_Handle,
+ SANE_Parameters* ) = nullptr;
+SANE_Status (*Sane::p_start)( SANE_Handle ) = nullptr;
+SANE_Status (*Sane::p_read)( SANE_Handle, SANE_Byte*, SANE_Int,
+ SANE_Int* ) = nullptr;
+void (*Sane::p_cancel)( SANE_Handle ) = nullptr;
+SANE_Status (*Sane::p_set_io_mode)( SANE_Handle, SANE_Bool ) = nullptr;
+SANE_Status (*Sane::p_get_select_fd)( SANE_Handle, SANE_Int* ) = nullptr;
+SANE_String_Const (*Sane::p_strstatus)( SANE_Status ) = nullptr;
+
+static bool bSaneSymbolLoadFailed = false;
+
+inline oslGenericFunction Sane::LoadSymbol( const char* pSymbolname )
+{
+ oslGenericFunction pFunction = osl_getAsciiFunctionSymbol( pSaneLib, pSymbolname );
+ if( ! pFunction )
+ {
+ fprintf( stderr, "Could not load symbol %s\n",
+ pSymbolname );
+ bSaneSymbolLoadFailed = true;
+ }
+ return pFunction;
+}
+
+SANE_Status Sane::ControlOption( int nOption, SANE_Action nAction,
+ void* pData )
+{
+ SANE_Int nInfo = 0;
+
+ SANE_Status nStatus = p_control_option( maHandle, static_cast<SANE_Int>(nOption),
+ nAction, pData, &nInfo );
+ DUMP_STATE( nStatus, "sane_control_option" );
+#if OSL_DEBUG_LEVEL > 0
+ if( nStatus != SANE_STATUS_GOOD )
+ {
+ const char* pAction = "Unknown";
+ switch( nAction )
+ {
+ case SANE_ACTION_GET_VALUE:
+ pAction = "SANE_ACTION_GET_VALUE";break;
+ case SANE_ACTION_SET_VALUE:
+ pAction = "SANE_ACTION_SET_VALUE";break;
+ case SANE_ACTION_SET_AUTO:
+ pAction = "SANE_ACTION_SET_AUTO";break;
+ }
+ dbg_msg( "Option: \"%s\" action: %s\n",
+ OUStringToOString(GetOptionName(nOption), osl_getThreadTextEncoding()).getStr(),
+ pAction );
+ }
+#endif
+ if( nInfo & SANE_INFO_RELOAD_OPTIONS )
+ ReloadOptions();
+ return nStatus;
+}
+
+Sane::Sane() :
+ mnOptions( 0 ),
+ mnDevice( -1 ),
+ maHandle( nullptr )
+{
+ if( ! nRefCount || ! pSaneLib )
+ Init();
+ nRefCount++;
+};
+
+Sane::~Sane()
+{
+ if( IsOpen() )
+ Close();
+ nRefCount--;
+ if( ! nRefCount && pSaneLib )
+ DeInit();
+}
+
+void Sane::Init()
+{
+#ifndef DISABLE_DYNLOADING
+ OUString sSaneLibName( "libsane" SAL_DLLEXTENSION );
+ pSaneLib = osl_loadModule( sSaneLibName.pData, SAL_LOADMODULE_LAZY );
+ if( ! pSaneLib )
+ {
+ sSaneLibName = "libsane" SAL_DLLEXTENSION ".1";
+ pSaneLib = osl_loadModule( sSaneLibName.pData, SAL_LOADMODULE_LAZY );
+ }
+ // try reasonable places that might not be in the library search path
+ if( ! pSaneLib )
+ {
+ OUString sSaneLibSystemPath( "/usr/local/lib/libsane" SAL_DLLEXTENSION );
+ osl_getFileURLFromSystemPath( sSaneLibSystemPath.pData, &sSaneLibName.pData );
+ pSaneLib = osl_loadModule( sSaneLibName.pData, SAL_LOADMODULE_LAZY );
+ }
+#endif
+ if( pSaneLib )
+ {
+ bSaneSymbolLoadFailed = false;
+ p_init = reinterpret_cast<SANE_Status(*)(SANE_Int*, SANE_Auth_Callback )>(
+ LoadSymbol( "sane_init" ));
+ p_exit = reinterpret_cast<void(*)()>(
+ LoadSymbol( "sane_exit" ));
+ p_get_devices = reinterpret_cast<SANE_Status(*)(const SANE_Device***,
+ SANE_Bool )>(
+ LoadSymbol( "sane_get_devices" ));
+ p_open = reinterpret_cast<SANE_Status(*)(SANE_String_Const, SANE_Handle )>(
+ LoadSymbol( "sane_open" ));
+ p_close = reinterpret_cast<void(*)(SANE_Handle)>(
+ LoadSymbol( "sane_close" ));
+ p_get_option_descriptor = reinterpret_cast<const SANE_Option_Descriptor*(*)(SANE_Handle,
+ SANE_Int)>(
+ LoadSymbol( "sane_get_option_descriptor" ));
+ p_control_option = reinterpret_cast<SANE_Status(*)(SANE_Handle, SANE_Int,
+ SANE_Action, void*, SANE_Int*)>(
+ LoadSymbol( "sane_control_option" ));
+ p_get_parameters = reinterpret_cast<SANE_Status(*)(SANE_Handle,SANE_Parameters*)>(
+ LoadSymbol( "sane_get_parameters" ));
+ p_start = reinterpret_cast<SANE_Status(*)(SANE_Handle)>(
+ LoadSymbol( "sane_start" ));
+ p_read = reinterpret_cast<SANE_Status(*)(SANE_Handle, SANE_Byte*,
+ SANE_Int, SANE_Int* )>(
+ LoadSymbol( "sane_read" ));
+ p_cancel = reinterpret_cast<void(*)(SANE_Handle)>(
+ LoadSymbol( "sane_cancel" ));
+ p_set_io_mode = reinterpret_cast<SANE_Status(*)(SANE_Handle, SANE_Bool)>(
+ LoadSymbol( "sane_set_io_mode" ));
+ p_get_select_fd = reinterpret_cast<SANE_Status(*)(SANE_Handle, SANE_Int*)>(
+ LoadSymbol( "sane_get_select_fd" ));
+ p_strstatus = reinterpret_cast<SANE_String_Const(*)(SANE_Status)>(
+ LoadSymbol( "sane_strstatus" ));
+ if( bSaneSymbolLoadFailed )
+ DeInit();
+ else
+ {
+ SANE_Status nStatus = p_init( &nVersion, nullptr );
+ FAIL_SHUTDOWN_STATE( nStatus, "sane_init", );
+ nStatus = p_get_devices( const_cast<const SANE_Device***>(&ppDevices),
+ SANE_FALSE );
+ FAIL_SHUTDOWN_STATE( nStatus, "sane_get_devices", );
+ for( nDevices = 0 ; ppDevices[ nDevices ]; nDevices++ ) ;
+ }
+ }
+#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
+ else
+ fprintf( stderr, "libsane%s could not be opened: %s\n", SAL_DLLEXTENSION,
+ dlerror() );
+#endif
+}
+
+void Sane::DeInit()
+{
+ if( pSaneLib )
+ {
+ p_exit();
+#ifndef DISABLE_DYNLOADING
+ osl_unloadModule( pSaneLib );
+#endif
+ pSaneLib = nullptr;
+ }
+}
+
+void Sane::ReloadDevices()
+{
+ if( IsOpen() )
+ Close();
+ DeInit();
+ Init();
+}
+
+void Sane::ReloadOptions()
+{
+ if( ! IsOpen() )
+ return;
+
+ const SANE_Option_Descriptor* pZero = p_get_option_descriptor( maHandle, 0 );
+ SANE_Word pOptions[2];
+ SANE_Status nStatus = p_control_option( maHandle, 0, SANE_ACTION_GET_VALUE,
+ static_cast<void*>(pOptions), nullptr );
+ if( nStatus != SANE_STATUS_GOOD )
+ fprintf( stderr, "Error: sane driver returned %s while reading number of options !\n", p_strstatus( nStatus ) );
+
+ mnOptions = pOptions[ 0 ];
+ if( o3tl::make_unsigned(pZero->size) > sizeof( SANE_Word ) )
+ fprintf( stderr, "driver returned number of options with larger size than SANE_Word!!!\n" );
+ mppOptions.reset(new const SANE_Option_Descriptor*[ mnOptions ]);
+ mppOptions[ 0 ] = pZero;
+ for( int i = 1; i < mnOptions; i++ )
+ mppOptions[ i ] = p_get_option_descriptor( maHandle, i );
+
+ CheckConsistency( nullptr, true );
+
+ maReloadOptionsLink.Call( *this );
+}
+
+bool Sane::Open( const char* name )
+{
+ SANE_Status nStatus = p_open( reinterpret_cast<SANE_String_Const>(name), &maHandle );
+ FAIL_STATE( nStatus, "sane_open", false );
+
+ ReloadOptions();
+
+ if( mnDevice == -1 )
+ {
+ OString aDevice( name );
+ for( int i = 0; i < nDevices; i++ )
+ {
+ if( aDevice == ppDevices[i]->name )
+ {
+ mnDevice = i;
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool Sane::Open( int n )
+{
+ if( n >= 0 && n < nDevices )
+ {
+ mnDevice = n;
+ return Open( ppDevices[n]->name );
+ }
+ return false;
+}
+
+void Sane::Close()
+{
+ if( maHandle )
+ {
+ p_close( maHandle );
+ mppOptions.reset();
+ maHandle = nullptr;
+ mnDevice = -1;
+ }
+}
+
+int Sane::GetOptionByName( const char* rName )
+{
+ int i;
+ OString aOption( rName );
+ for( i = 0; i < mnOptions; i++ )
+ {
+ if( mppOptions[i]->name && aOption == mppOptions[i]->name )
+ return i;
+ }
+ return -1;
+}
+
+bool Sane::GetOptionValue( int n, bool& rRet )
+{
+ if( ! maHandle || mppOptions[n]->type != SANE_TYPE_BOOL )
+ return false;
+ SANE_Word nRet;
+ SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, &nRet );
+ if( nStatus != SANE_STATUS_GOOD )
+ return false;
+
+ rRet = nRet;
+ return true;
+}
+
+bool Sane::GetOptionValue( int n, OString& rRet )
+{
+ bool bSuccess = false;
+ if( ! maHandle || mppOptions[n]->type != SANE_TYPE_STRING )
+ return false;
+ std::unique_ptr<char[]> pRet(new char[mppOptions[n]->size+1]);
+ SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pRet.get() );
+ if( nStatus == SANE_STATUS_GOOD )
+ {
+ bSuccess = true;
+ rRet = pRet.get();
+ }
+ return bSuccess;
+}
+
+bool Sane::GetOptionValue( int n, double& rRet, int nElement )
+{
+ bool bSuccess = false;
+
+ if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_INT &&
+ mppOptions[n]->type != SANE_TYPE_FIXED ) )
+ return false;
+
+ std::unique_ptr<SANE_Word[]> pRet(new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]);
+ SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pRet.get() );
+ if( nStatus == SANE_STATUS_GOOD )
+ {
+ bSuccess = true;
+ if( mppOptions[n]->type == SANE_TYPE_INT )
+ rRet = static_cast<double>(pRet[ nElement ]);
+ else
+ rRet = SANE_UNFIX( pRet[nElement] );
+ }
+ return bSuccess;
+}
+
+bool Sane::GetOptionValue( int n, double* pSet )
+{
+ if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_FIXED &&
+ mppOptions[n]->type != SANE_TYPE_INT ) )
+ return false;
+
+ std::unique_ptr<SANE_Word[]> pFixedSet(new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]);
+ SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pFixedSet.get() );
+ if( nStatus != SANE_STATUS_GOOD )
+ return false;
+ for( size_t i = 0; i <mppOptions[n]->size/sizeof(SANE_Word); i++ )
+ {
+ if( mppOptions[n]->type == SANE_TYPE_FIXED )
+ pSet[i] = SANE_UNFIX( pFixedSet[i] );
+ else
+ pSet[i] = static_cast<double>(pFixedSet[i]);
+ }
+ return true;
+}
+
+void Sane::SetOptionValue( int n, bool bSet )
+{
+ if( ! maHandle || mppOptions[n]->type != SANE_TYPE_BOOL )
+ return;
+ SANE_Word nRet = bSet ? SANE_TRUE : SANE_FALSE;
+ ControlOption( n, SANE_ACTION_SET_VALUE, &nRet );
+}
+
+void Sane::SetOptionValue( int n, std::u16string_view rSet )
+{
+ if( ! maHandle || mppOptions[n]->type != SANE_TYPE_STRING )
+ return;
+ OString aSet(OUStringToOString(rSet, osl_getThreadTextEncoding()));
+ ControlOption( n, SANE_ACTION_SET_VALUE, const_cast<char *>(aSet.getStr()) );
+}
+
+void Sane::SetOptionValue( int n, double fSet, int nElement )
+{
+ if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_INT &&
+ mppOptions[n]->type != SANE_TYPE_FIXED ) )
+ return;
+
+ if( mppOptions[n]->size/sizeof(SANE_Word) > 1 )
+ {
+ std::unique_ptr<SANE_Word[]> pSet(new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]);
+ SANE_Status nStatus = ControlOption( n, SANE_ACTION_GET_VALUE, pSet.get() );
+ if( nStatus == SANE_STATUS_GOOD )
+ {
+ pSet[nElement] = mppOptions[n]->type == SANE_TYPE_INT ?
+ static_cast<SANE_Word>(fSet) : SANE_FIX( fSet );
+ ControlOption( n, SANE_ACTION_SET_VALUE, pSet.get() );
+ }
+ }
+ else
+ {
+ SANE_Word nSetTo =
+ mppOptions[n]->type == SANE_TYPE_INT ?
+ static_cast<SANE_Word>(fSet) : SANE_FIX( fSet );
+
+ ControlOption( n, SANE_ACTION_SET_VALUE, &nSetTo );
+ }
+}
+
+void Sane::SetOptionValue( int n, double const * pSet )
+{
+ if( ! maHandle || ( mppOptions[n]->type != SANE_TYPE_INT &&
+ mppOptions[n]->type != SANE_TYPE_FIXED ) )
+ return;
+ std::unique_ptr<SANE_Word[]> pFixedSet(new SANE_Word[mppOptions[n]->size/sizeof(SANE_Word)]);
+ for( size_t i = 0; i < mppOptions[n]->size/sizeof(SANE_Word); i++ )
+ {
+ if( mppOptions[n]->type == SANE_TYPE_FIXED )
+ pFixedSet[i] = SANE_FIX( pSet[i] );
+ else
+ pFixedSet[i] = static_cast<SANE_Word>(pSet[i]);
+ }
+ ControlOption( n, SANE_ACTION_SET_VALUE, pFixedSet.get() );
+}
+
+namespace {
+
+enum FrameStyleType {
+ FrameStyle_BW, FrameStyle_Gray, FrameStyle_RGB, FrameStyle_Separated
+};
+
+}
+
+#define BYTE_BUFFER_SIZE 32768
+
+static sal_uInt8 ReadValue( FILE* fp, int depth )
+{
+ if( depth == 16 )
+ {
+ sal_uInt16 nWord;
+ // data always come in native byte order !
+ // 16 bits is not really supported by backends as of now
+ // e.g. UMAX Astra 1200S delivers 16 bit but in BIGENDIAN
+ // against SANE documentation (xscanimage gets the same result
+ // as we do
+ size_t items_read = fread( &nWord, 1, 2, fp );
+
+ if (items_read != 2)
+ {
+ SAL_WARN( "extensions.scanner", "short read, abandoning" );
+ return 0;
+ }
+
+ return static_cast<sal_uInt8>( nWord / 256 );
+ }
+ sal_uInt8 nByte;
+ size_t items_read = fread( &nByte, 1, 1, fp );
+ if (items_read != 1)
+ {
+ SAL_WARN( "extensions.scanner", "short read, abandoning" );
+ return 0;
+ }
+ return nByte;
+}
+
+bool Sane::CheckConsistency( const char* pMes, bool bInit )
+{
+ static const SANE_Option_Descriptor** pDescArray = nullptr;
+ static const SANE_Option_Descriptor* pZero = nullptr;
+
+ if( bInit )
+ {
+ pDescArray = mppOptions.get();
+ if( mppOptions )
+ pZero = mppOptions[0];
+ return true;
+ }
+
+ bool bConsistent = true;
+
+ if( pDescArray != mppOptions.get() )
+ bConsistent = false;
+ if( pZero != mppOptions[0] )
+ bConsistent = false;
+
+ if( ! bConsistent )
+ dbg_msg( "Sane is not consistent. (%s)\n", pMes );
+
+ return bConsistent;
+}
+
+bool Sane::Start( BitmapTransporter& rBitmap )
+{
+ int nStream = 0, nLine = 0, i = 0;
+ SANE_Parameters aParams;
+ FrameStyleType eType = FrameStyle_Gray;
+ bool bSuccess = true;
+ bool bWidthSet = false;
+
+ if( ! maHandle )
+ return false;
+
+ int nWidthMM = 0;
+ int nHeightMM = 0;
+ double fTLx, fTLy, fResl = 0.0;
+ int nOption;
+ nOption = GetOptionByName( "tl-x" );
+ if( nOption != -1 &&
+ GetOptionValue( nOption, fTLx ) &&
+ GetOptionUnit( nOption ) == SANE_UNIT_MM )
+ {
+ double fBRx;
+ nOption = GetOptionByName( "br-x" );
+ if( nOption != -1 &&
+ GetOptionValue( nOption, fBRx ) &&
+ GetOptionUnit( nOption ) == SANE_UNIT_MM )
+ {
+ nWidthMM = static_cast<int>(fabs(fBRx - fTLx));
+ }
+ }
+ nOption = GetOptionByName( "tl-y" );
+ if( nOption != -1 &&
+ GetOptionValue( nOption, fTLy ) &&
+ GetOptionUnit( nOption ) == SANE_UNIT_MM )
+ {
+ double fBRy;
+ nOption = GetOptionByName( "br-y" );
+ if( nOption != -1 &&
+ GetOptionValue( nOption, fBRy ) &&
+ GetOptionUnit( nOption ) == SANE_UNIT_MM )
+ {
+ nHeightMM = static_cast<int>(fabs(fBRy - fTLy));
+ }
+ }
+ if( ( nOption = GetOptionByName( "resolution" ) ) != -1 )
+ (void)GetOptionValue( nOption, fResl );
+
+ std::unique_ptr<sal_uInt8[]> pBuffer;
+
+ SANE_Status nStatus = SANE_STATUS_GOOD;
+
+ rBitmap.lock();
+ SvMemoryStream& aConverter = rBitmap.getStream();
+ aConverter.Seek( 0 );
+ aConverter.SetEndian( SvStreamEndian::LITTLE );
+
+ // write bitmap stream header
+ aConverter.WriteChar( 'B' ).WriteChar( 'M' );
+ aConverter.WriteUInt32( 0 );
+ aConverter.WriteUInt32( 0 );
+ aConverter.WriteUInt32( 60 );
+
+ // write BITMAPINFOHEADER
+ aConverter.WriteUInt32( 40 );
+ aConverter.WriteUInt32( 0 ); // fill in width later
+ aConverter.WriteUInt32( 0 ); // fill in height later
+ aConverter.WriteUInt16( 1 );
+ // create header for 24 bits
+ // correct later if necessary
+ aConverter.WriteUInt16( 24 );
+ aConverter.WriteUInt32( 0 );
+ aConverter.WriteUInt32( 0 );
+ aConverter.WriteUInt32( 0 );
+ aConverter.WriteUInt32( 0 );
+ aConverter.WriteUInt32( 0 );
+ aConverter.WriteUInt32( 0 );
+
+ for( nStream=0; nStream < 3 && bSuccess ; nStream++ )
+ {
+ nStatus = p_start( maHandle );
+ DUMP_STATE( nStatus, "sane_start" );
+ CheckConsistency( "sane_start" );
+ if( nStatus == SANE_STATUS_GOOD )
+ {
+ nStatus = p_get_parameters( maHandle, &aParams );
+ DUMP_STATE( nStatus, "sane_get_parameters" );
+ CheckConsistency( "sane_get_parameters" );
+ if (nStatus != SANE_STATUS_GOOD || aParams.bytes_per_line == 0)
+ {
+ bSuccess = false;
+ break;
+ }
+#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
+ const char* const ppFormats[] = { "SANE_FRAME_GRAY", "SANE_FRAME_RGB",
+ "SANE_FRAME_RED", "SANE_FRAME_GREEN",
+ "SANE_FRAME_BLUE", "Unknown !!!" };
+ fprintf( stderr, "Parameters for frame %d:\n", nStream );
+ if( static_cast<
+ typename std::make_unsigned<
+ typename std::underlying_type<SANE_Frame>::type>::type>(
+ aParams.format)
+ > 4 )
+ {
+ aParams.format = SANE_Frame(5);
+ }
+ fprintf( stderr, "format: %s\n", ppFormats[ static_cast<int>(aParams.format) ] );
+ fprintf( stderr, "last_frame: %s\n", aParams.last_frame ? "TRUE" : "FALSE" );
+ fprintf( stderr, "depth: %d\n", static_cast<int>(aParams.depth) );
+ fprintf( stderr, "pixels_per_line: %d\n", static_cast<int>(aParams.pixels_per_line) );
+ fprintf( stderr, "bytes_per_line: %d\n", static_cast<int>(aParams.bytes_per_line) );
+#endif
+ if( ! pBuffer )
+ {
+ pBuffer.reset(new sal_uInt8[ BYTE_BUFFER_SIZE < 4*aParams.bytes_per_line ? 4*aParams.bytes_per_line : BYTE_BUFFER_SIZE ]);
+ }
+
+ if( aParams.last_frame )
+ nStream=3;
+
+ switch( aParams.format )
+ {
+ case SANE_FRAME_GRAY:
+ eType = FrameStyle_Gray;
+ if( aParams.depth == 1 )
+ eType = FrameStyle_BW;
+ break;
+ case SANE_FRAME_RGB:
+ eType = FrameStyle_RGB;
+ break;
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ eType = FrameStyle_Separated;
+ break;
+ default:
+ fprintf( stderr, "Warning: unknown frame style !!!\n" );
+ }
+
+ bool bSynchronousRead = true;
+
+ // should be fail safe, but ... ??
+ nStatus = p_set_io_mode( maHandle, SANE_FALSE );
+ CheckConsistency( "sane_set_io_mode" );
+ if( nStatus != SANE_STATUS_GOOD )
+ {
+ bSynchronousRead = false;
+ nStatus = p_set_io_mode(maHandle, SANE_TRUE);
+ CheckConsistency( "sane_set_io_mode" );
+ if (nStatus != SANE_STATUS_GOOD)
+ {
+ SAL_WARN("extensions.scanner", "SANE driver status is: " << nStatus);
+ }
+ }
+
+ SANE_Int nLen=0;
+ SANE_Int fd = 0;
+
+ if( ! bSynchronousRead )
+ {
+ nStatus = p_get_select_fd( maHandle, &fd );
+ DUMP_STATE( nStatus, "sane_get_select_fd" );
+ CheckConsistency( "sane_get_select_fd" );
+ if( nStatus != SANE_STATUS_GOOD )
+ bSynchronousRead = true;
+ }
+ utl::TempFile aFrame;
+ aFrame.EnableKillingFile();
+ FILE* pFrame = fopen(OUStringToOString(aFrame.GetFileName(), osl_getThreadTextEncoding()).getStr(), "w+b");
+ if( ! pFrame )
+ {
+ bSuccess = false;
+ break;
+ }
+ do {
+ if( ! bSynchronousRead )
+ {
+ fd_set fdset;
+ struct timeval tv;
+
+ FD_ZERO( &fdset );
+ FD_SET( static_cast<int>(fd), &fdset );
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ if( select( fd+1, &fdset, nullptr, nullptr, &tv ) == 0 )
+ fprintf( stderr, "Timeout on sane_read descriptor\n" );
+ }
+ nLen = 0;
+ nStatus = p_read( maHandle, pBuffer.get(), BYTE_BUFFER_SIZE, &nLen );
+ CheckConsistency( "sane_read" );
+ if( nLen && ( nStatus == SANE_STATUS_GOOD ||
+ nStatus == SANE_STATUS_EOF ) )
+ {
+ bSuccess = (static_cast<size_t>(nLen) == fwrite( pBuffer.get(), 1, nLen, pFrame ));
+ if (!bSuccess)
+ break;
+ }
+ else
+ DUMP_STATE( nStatus, "sane_read" );
+ } while( nStatus == SANE_STATUS_GOOD );
+ if (nStatus != SANE_STATUS_EOF || !bSuccess)
+ {
+ fclose( pFrame );
+ bSuccess = false;
+ break;
+ }
+
+ int nFrameLength = ftell( pFrame );
+ fseek( pFrame, 0, SEEK_SET );
+ sal_uInt32 nWidth = static_cast<sal_uInt32>(aParams.pixels_per_line);
+ sal_uInt32 nHeight = static_cast<sal_uInt32>(nFrameLength / aParams.bytes_per_line);
+ if( ! bWidthSet )
+ {
+ if( ! fResl )
+ fResl = 300; // if all else fails that's a good guess
+ if( ! nWidthMM )
+ nWidthMM = static_cast<int>((static_cast<double>(nWidth) / fResl) * 25.4);
+ if( ! nHeightMM )
+ nHeightMM = static_cast<int>((static_cast<double>(nHeight) / fResl) * 25.4);
+ SAL_INFO("extensions.scanner", "set dimensions to(" << nWidth << ", " << nHeight << ") Pixel, (" << nWidthMM << ", " << nHeightMM <<
+ ") mm, resolution is " << fResl);
+
+ aConverter.Seek( 18 );
+ aConverter.WriteUInt32( nWidth );
+ aConverter.WriteUInt32( nHeight );
+ aConverter.Seek( 38 );
+ aConverter.WriteUInt32( 1000*nWidth/nWidthMM );
+ aConverter.WriteUInt32( 1000*nHeight/nHeightMM );
+ bWidthSet = true;
+ }
+ aConverter.Seek(60);
+
+ if( eType == FrameStyle_BW )
+ {
+ aConverter.Seek( 10 );
+ aConverter.WriteUInt32( 64 );
+ aConverter.Seek( 28 );
+ aConverter.WriteUInt16( 1 );
+ aConverter.Seek( 54 );
+ // write color table
+ aConverter.WriteUInt16( 0xffff );
+ aConverter.WriteUChar( 0xff );
+ aConverter.WriteUChar( 0 );
+ aConverter.WriteUInt32( 0 );
+ aConverter.Seek( 64 );
+ }
+ else if( eType == FrameStyle_Gray )
+ {
+ aConverter.Seek( 10 );
+ aConverter.WriteUInt32( 1084 );
+ aConverter.Seek( 28 );
+ aConverter.WriteUInt16( 8 );
+ aConverter.Seek( 54 );
+ // write color table
+ for( nLine = 0; nLine < 256; nLine++ )
+ {
+ aConverter.WriteUChar( nLine );
+ aConverter.WriteUChar( nLine );
+ aConverter.WriteUChar( nLine );
+ aConverter.WriteUChar( 0 );
+ }
+ aConverter.Seek( 1084 );
+ }
+
+ for (nLine = nHeight-1; nLine >= 0; --nLine)
+ {
+ if (fseek(pFrame, nLine * aParams.bytes_per_line, SEEK_SET) == -1)
+ {
+ bSuccess = false;
+ break;
+ }
+ if( eType == FrameStyle_BW ||
+ ( eType == FrameStyle_Gray && aParams.depth == 8 )
+ )
+ {
+ SANE_Int items_read = fread( pBuffer.get(), 1, aParams.bytes_per_line, pFrame );
+ if (items_read != aParams.bytes_per_line)
+ {
+ SAL_WARN( "extensions.scanner", "short read, padding with zeros" );
+ memset(pBuffer.get() + items_read, 0, aParams.bytes_per_line - items_read);
+ }
+ aConverter.WriteBytes(pBuffer.get(), aParams.bytes_per_line);
+ }
+ else if( eType == FrameStyle_Gray )
+ {
+ for( i = 0; i < (aParams.pixels_per_line); i++ )
+ {
+ sal_uInt8 nGray = ReadValue( pFrame, aParams.depth );
+ aConverter.WriteUChar( nGray );
+ }
+ }
+ else if( eType == FrameStyle_RGB )
+ {
+ for( i = 0; i < (aParams.pixels_per_line); i++ )
+ {
+ sal_uInt8 nRed, nGreen, nBlue;
+ nRed = ReadValue( pFrame, aParams.depth );
+ nGreen = ReadValue( pFrame, aParams.depth );
+ nBlue = ReadValue( pFrame, aParams.depth );
+ aConverter.WriteUChar( nBlue );
+ aConverter.WriteUChar( nGreen );
+ aConverter.WriteUChar( nRed );
+ }
+ }
+ else if( eType == FrameStyle_Separated )
+ {
+ for( i = 0; i < (aParams.pixels_per_line); i++ )
+ {
+ sal_uInt8 nValue = ReadValue( pFrame, aParams.depth );
+ switch( aParams.format )
+ {
+ case SANE_FRAME_RED:
+ aConverter.SeekRel( 2 );
+ aConverter.WriteUChar( nValue );
+ break;
+ case SANE_FRAME_GREEN:
+ aConverter.SeekRel( 1 );
+ aConverter.WriteUChar( nValue );
+ aConverter.SeekRel( 1 );
+ break;
+ case SANE_FRAME_BLUE:
+ aConverter.WriteUChar( nValue );
+ aConverter.SeekRel( 2 );
+ break;
+ case SANE_FRAME_GRAY:
+ case SANE_FRAME_RGB:
+ break;
+ }
+ }
+ }
+ int nGap = aConverter.Tell() & 3;
+ if (nGap)
+ aConverter.SeekRel( 4-nGap );
+ }
+ fclose( pFrame ); // deletes tmpfile
+ if( eType != FrameStyle_Separated )
+ break;
+ }
+ else
+ bSuccess = false;
+ }
+ // get stream length
+ int nPos = aConverter.TellEnd();
+
+ aConverter.Seek( 2 );
+ aConverter.WriteUInt32( nPos+1 );
+ aConverter.Seek( 0 );
+
+ rBitmap.unlock();
+
+ if( bSuccess )
+ {
+ // only cancel a successful operation
+ // sane disrupts memory else
+ p_cancel( maHandle );
+ CheckConsistency( "sane_cancel" );
+ }
+ pBuffer.reset();
+
+ ReloadOptions();
+
+
+ dbg_msg( "Sane::Start returns with %s\n", bSuccess ? "TRUE" : "FALSE" );
+
+ return bSuccess;
+}
+
+int Sane::GetRange( int n, std::unique_ptr<double[]>& rpDouble )
+{
+ if( mppOptions[n]->constraint_type != SANE_CONSTRAINT_RANGE &&
+ mppOptions[n]->constraint_type != SANE_CONSTRAINT_WORD_LIST )
+ {
+ return -1;
+ }
+
+ rpDouble = nullptr;
+ int nItems, i;
+ bool bIsFixed = mppOptions[n]->type == SANE_TYPE_FIXED;
+
+ dbg_msg( "Sane::GetRange of option %s ", mppOptions[n]->name );
+ if(mppOptions[n]->constraint_type == SANE_CONSTRAINT_RANGE )
+ {
+ double fMin, fMax, fQuant;
+ if( bIsFixed )
+ {
+ fMin = SANE_UNFIX( mppOptions[n]->constraint.range->min );
+ fMax = SANE_UNFIX( mppOptions[n]->constraint.range->max );
+ fQuant = SANE_UNFIX( mppOptions[n]->constraint.range->quant );
+ }
+ else
+ {
+ fMin = static_cast<double>(mppOptions[n]->constraint.range->min);
+ fMax = static_cast<double>(mppOptions[n]->constraint.range->max);
+ fQuant = static_cast<double>(mppOptions[n]->constraint.range->quant);
+ }
+ if( fQuant != 0.0 )
+ {
+ dbg_msg( "quantum range [ %lg ; %lg ; %lg ]\n",
+ fMin, fQuant, fMax );
+ nItems = static_cast<int>((fMax - fMin)/fQuant)+1;
+ rpDouble.reset(new double[ nItems ]);
+ double fValue = fMin;
+ for( i = 0; i < nItems; i++, fValue += fQuant )
+ rpDouble[i] = fValue;
+ rpDouble[ nItems-1 ] = fMax;
+ return nItems;
+ }
+ else
+ {
+ dbg_msg( "normal range [ %lg %lg ]\n",
+ fMin, fMax );
+ rpDouble.reset(new double[2]);
+ rpDouble[0] = fMin;
+ rpDouble[1] = fMax;
+ return 0;
+ }
+ }
+ else
+ {
+ nItems = mppOptions[n]->constraint.word_list[0];
+ rpDouble.reset(new double[nItems]);
+ for( i=0; i<nItems; i++ )
+ {
+ rpDouble[i] = bIsFixed ?
+ SANE_UNFIX( mppOptions[n]->constraint.word_list[i+1] ) :
+ static_cast<double>(mppOptions[n]->constraint.word_list[i+1]);
+ }
+ dbg_msg( "wordlist [ %lg ... %lg ]\n",
+ rpDouble[ 0 ], rpDouble[ nItems-1 ] );
+ return nItems;
+ }
+}
+
+static const char *ppUnits[] = {
+ "",
+ "[Pixel]",
+ "[Bit]",
+ "[mm]",
+ "[DPI]",
+ "[%]",
+ "[usec]"
+};
+
+OUString Sane::GetOptionUnitName( int n )
+{
+ OUString aText;
+ SANE_Unit nUnit = mppOptions[n]->unit;
+ size_t nUnitAsSize = static_cast<size_t>(nUnit);
+ if (nUnitAsSize >= SAL_N_ELEMENTS( ppUnits ))
+ aText = "[unknown units]";
+ else
+ aText = OUString( ppUnits[ nUnit ], strlen(ppUnits[ nUnit ]), osl_getThreadTextEncoding() );
+ return aText;
+}
+
+bool Sane::ActivateButtonOption( int n )
+{
+ SANE_Status nStatus = ControlOption( n, SANE_ACTION_SET_VALUE, nullptr );
+ return nStatus == SANE_STATUS_GOOD;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/sane.hxx b/extensions/source/scanner/sane.hxx
new file mode 100644
index 000000000..a0e631942
--- /dev/null
+++ b/extensions/source/scanner/sane.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 <sal/config.h>
+
+#include <string_view>
+
+#include <cppuhelper/implbase.hxx>
+#include <osl/thread.h>
+#include <osl/module.h>
+#include <tools/stream.hxx>
+#include <tools/link.hxx>
+#include <sane/sane.h>
+
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+using namespace com::sun::star::uno;
+
+
+class BitmapTransporter: public cppu::WeakImplHelper<css::awt::XBitmap>
+{
+ SvMemoryStream m_aStream;
+ osl::Mutex m_aProtector;
+
+public:
+
+ BitmapTransporter();
+ virtual ~BitmapTransporter() override;
+
+ virtual css::awt::Size SAL_CALL getSize() override;
+ virtual Sequence< sal_Int8 > SAL_CALL getDIB() override;
+ virtual Sequence< sal_Int8 > SAL_CALL getMaskDIB() override { return Sequence< sal_Int8 >(); }
+
+ // Misc
+ void lock() { m_aProtector.acquire(); }
+ void unlock() { m_aProtector.release(); }
+ SvMemoryStream& getStream() { return m_aStream; }
+};
+
+
+class Sane
+{
+private:
+ static int nRefCount;
+ static oslModule pSaneLib;
+
+ static SANE_Status (*p_init)( SANE_Int*,
+ SANE_Auth_Callback );
+ static void (*p_exit)();
+ static SANE_Status (*p_get_devices)( const SANE_Device***,
+ SANE_Bool );
+ static SANE_Status (*p_open)( SANE_String_Const, SANE_Handle );
+ static void (*p_close)( SANE_Handle );
+ static const SANE_Option_Descriptor* (*p_get_option_descriptor)(
+ SANE_Handle, SANE_Int );
+ static SANE_Status (*p_control_option)( SANE_Handle, SANE_Int,
+ SANE_Action, void*,
+ SANE_Int* );
+ static SANE_Status (*p_get_parameters)( SANE_Handle,
+ SANE_Parameters* );
+ static SANE_Status (*p_start)( SANE_Handle );
+ static SANE_Status (*p_read)( SANE_Handle, SANE_Byte*, SANE_Int,
+ SANE_Int* );
+ static void (*p_cancel)( SANE_Handle );
+ static SANE_Status (*p_set_io_mode)( SANE_Handle, SANE_Bool );
+ static SANE_Status (*p_get_select_fd)( SANE_Handle, SANE_Int* );
+ static SANE_String_Const (*p_strstatus)( SANE_Status );
+
+ static SANE_Int nVersion;
+ static SANE_Device** ppDevices;
+ static int nDevices;
+
+ std::unique_ptr<const SANE_Option_Descriptor*[]> mppOptions;
+ int mnOptions;
+ int mnDevice;
+ SANE_Handle maHandle;
+
+ Link<Sane&,void> maReloadOptionsLink;
+
+ static inline oslGenericFunction
+ LoadSymbol( const char* );
+ static void Init();
+ static void DeInit();
+
+ SANE_Status ControlOption( int, SANE_Action, void* );
+
+ bool CheckConsistency( const char*, bool bInit = false );
+
+public:
+ Sane();
+ ~Sane();
+
+ static bool IsSane()
+ { return pSaneLib != nullptr; }
+ bool IsOpen() const
+ { return maHandle != nullptr; }
+ static int CountDevices()
+ { return nDevices; }
+ static OUString GetName( int n )
+ { return ppDevices[n]->name ? OUString( ppDevices[n]->name, strlen(ppDevices[n]->name), osl_getThreadTextEncoding() ) : OUString(); }
+ static OUString GetVendor( int n )
+ { return ppDevices[n]->vendor ? OUString( ppDevices[n]->vendor, strlen(ppDevices[n]->vendor), osl_getThreadTextEncoding() ) : OUString(); }
+ static OUString GetModel( int n )
+ { return ppDevices[n]->model ? OUString( ppDevices[n]->model, strlen(ppDevices[n]->model), osl_getThreadTextEncoding() ) : OUString(); }
+ static OUString GetType( int n )
+ { return ppDevices[n]->type ? OUString( ppDevices[n]->type, strlen(ppDevices[n]->type), osl_getThreadTextEncoding() ) : OUString(); }
+
+ OUString GetOptionName( int n )
+ { return mppOptions[n]->name ? OUString( mppOptions[n]->name, strlen(mppOptions[n]->name), osl_getThreadTextEncoding() ) : OUString(); }
+ OUString GetOptionTitle( int n )
+ { return mppOptions[n]->title ? OUString( mppOptions[n]->title, strlen(mppOptions[n]->title), osl_getThreadTextEncoding() ) : OUString(); }
+ SANE_Value_Type GetOptionType( int n )
+ { return mppOptions[n]->type; }
+ SANE_Unit GetOptionUnit( int n )
+ { return mppOptions[n]->unit; }
+ OUString GetOptionUnitName( int n );
+ SANE_Int GetOptionCap( int n )
+ { return mppOptions[n]->cap; }
+ SANE_Constraint_Type GetOptionConstraintType( int n )
+ { return mppOptions[n]->constraint_type; }
+ const char** GetStringConstraint( int n )
+ { return const_cast<const char**>(mppOptions[n]->constraint.string_list); }
+ int GetRange( int, std::unique_ptr<double[]>& );
+
+ inline int GetOptionElements( int n );
+ int GetOptionByName( const char* );
+ bool GetOptionValue( int, bool& );
+ bool GetOptionValue( int, OString& );
+ bool GetOptionValue( int, double&, int nElement = 0 );
+ bool GetOptionValue( int, double* );
+
+ void SetOptionValue( int, bool );
+ void SetOptionValue( int, std::u16string_view );
+ void SetOptionValue( int, double, int nElement = 0 );
+ void SetOptionValue( int, double const * );
+
+ bool ActivateButtonOption( int );
+
+ int CountOptions() { return mnOptions; }
+ int GetDeviceNumber() const { return mnDevice; }
+
+ bool Open( const char* );
+ bool Open( int );
+ void Close();
+ void ReloadDevices();
+ void ReloadOptions();
+
+ bool Start( BitmapTransporter& );
+
+ inline Link<Sane&,void> SetReloadOptionsHdl( const Link<Sane&,void>& rLink );
+};
+
+
+inline int Sane::GetOptionElements( int n )
+{
+ if( mppOptions[n]->type == SANE_TYPE_FIXED ||
+ mppOptions[n]->type == SANE_TYPE_INT )
+ {
+ return mppOptions[n]->size/sizeof( SANE_Word );
+ }
+ return 1;
+}
+
+
+inline Link<Sane&,void> Sane::SetReloadOptionsHdl( const Link<Sane&,void>& rLink )
+{
+ Link<Sane&,void> aRet = maReloadOptionsLink;
+ maReloadOptionsLink = rLink;
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/sanedlg.cxx b/extensions/source/scanner/sanedlg.cxx
new file mode 100644
index 000000000..8486195c9
--- /dev/null
+++ b/extensions/source/scanner/sanedlg.cxx
@@ -0,0 +1,1467 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <tools/config.hxx>
+#include <unotools/resmgr.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/event.hxx>
+#include "sanedlg.hxx"
+#include "grid.hxx"
+#include <math.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <rtl/strbuf.hxx>
+#include <memory>
+#include <strings.hrc>
+
+#define PREVIEW_WIDTH 113
+#define PREVIEW_HEIGHT 160
+
+#define RECT_SIZE_PIX 7
+
+namespace {
+
+void DrawRectangles(vcl::RenderContext& rRenderContext, Point const & rUL, Point const & rBR)
+{
+ int nMiddleX, nMiddleY;
+ Point aBL, aUR;
+
+ aUR = Point(rBR.X(), rUL.Y());
+ aBL = Point(rUL.X(), rBR.Y());
+ nMiddleX = (rBR.X() - rUL.X()) / 2 + rUL.X();
+ nMiddleY = (rBR.Y() - rUL.Y()) / 2 + rUL.Y();
+
+ rRenderContext.DrawLine(rUL, aBL);
+ rRenderContext.DrawLine(aBL, rBR);
+ rRenderContext.DrawLine(rBR, aUR);
+ rRenderContext.DrawLine(aUR, rUL);
+ rRenderContext.DrawRect(tools::Rectangle(rUL, Size(RECT_SIZE_PIX,RECT_SIZE_PIX)));
+ rRenderContext.DrawRect(tools::Rectangle(aBL, Size(RECT_SIZE_PIX, -RECT_SIZE_PIX)));
+ rRenderContext.DrawRect(tools::Rectangle(rBR, Size(-RECT_SIZE_PIX, -RECT_SIZE_PIX)));
+ rRenderContext.DrawRect(tools::Rectangle(aUR, Size(-RECT_SIZE_PIX, RECT_SIZE_PIX )));
+ rRenderContext.DrawRect(tools::Rectangle(Point(nMiddleX - RECT_SIZE_PIX / 2, rUL.Y()), Size(RECT_SIZE_PIX, RECT_SIZE_PIX)));
+ rRenderContext.DrawRect(tools::Rectangle(Point(nMiddleX - RECT_SIZE_PIX / 2, rBR.Y()), Size(RECT_SIZE_PIX, -RECT_SIZE_PIX)));
+ rRenderContext.DrawRect(tools::Rectangle(Point(rUL.X(), nMiddleY - RECT_SIZE_PIX / 2), Size(RECT_SIZE_PIX, RECT_SIZE_PIX)));
+ rRenderContext.DrawRect(tools::Rectangle(Point(rBR.X(), nMiddleY - RECT_SIZE_PIX / 2), Size(-RECT_SIZE_PIX, RECT_SIZE_PIX)));
+}
+
+}
+
+class ScanPreview : public weld::CustomWidgetController
+{
+private:
+ enum DragDirection { TopLeft, Top, TopRight, Right, BottomRight, Bottom,
+ BottomLeft, Left };
+
+ BitmapEx maPreviewBitmapEx;
+ tools::Rectangle maPreviewRect;
+ Point maLastUL, maLastBR;
+ Point maTopLeft, maBottomRight;
+ Point maMinTopLeft, maMaxBottomRight;
+ SaneDlg* mpParentDialog;
+ DragDirection meDragDirection;
+ bool mbDragEnable;
+ bool mbDragDrawn;
+ bool mbIsDragging;
+
+public:
+ ScanPreview()
+ : maMaxBottomRight(PREVIEW_WIDTH, PREVIEW_HEIGHT)
+ , mpParentDialog(nullptr)
+ , meDragDirection(TopLeft)
+ , mbDragEnable(false)
+ , mbDragDrawn(false)
+ , mbIsDragging(false)
+ {
+ }
+
+ void Init(SaneDlg *pParent)
+ {
+ mpParentDialog = pParent;
+ }
+
+ void ResetForNewScanner()
+ {
+ maTopLeft = Point();
+ maBottomRight = Point();
+ maMinTopLeft = Point();
+ maMaxBottomRight = Point(PREVIEW_WIDTH, PREVIEW_HEIGHT);
+ }
+
+ void EnableDrag()
+ {
+ mbDragEnable = true;
+ }
+
+ void DisableDrag()
+ {
+ mbDragEnable = false;
+ }
+
+ bool IsDragEnabled() const
+ {
+ return mbDragEnable;
+ }
+
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+ virtual bool MouseMove(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ Point GetPixelPos(const Point& rIn) const;
+ Point GetLogicPos(const Point& rIn) const;
+
+ void GetPreviewLogicRect(Point& rTopLeft, Point &rBottomRight) const
+ {
+ rTopLeft = GetLogicPos(maTopLeft);
+ rBottomRight = GetLogicPos(maBottomRight);
+ }
+ void GetMaxLogicRect(Point& rTopLeft, Point &rBottomRight) const
+ {
+ rTopLeft = maMinTopLeft;
+ rBottomRight = maMaxBottomRight;
+
+ }
+ void ChangePreviewLogicTopLeftY(tools::Long Y)
+ {
+ Point aPoint(0, Y);
+ aPoint = GetPixelPos(aPoint);
+ maTopLeft.setY( aPoint.Y() );
+ }
+ void ChangePreviewLogicTopLeftX(tools::Long X)
+ {
+ Point aPoint(X, 0);
+ aPoint = GetPixelPos(aPoint);
+ maTopLeft.setX( aPoint.X() );
+ }
+ void ChangePreviewLogicBottomRightY(tools::Long Y)
+ {
+ Point aPoint(0, Y);
+ aPoint = GetPixelPos(aPoint);
+ maBottomRight.setY( aPoint.Y() );
+ }
+ void ChangePreviewLogicBottomRightX(tools::Long X)
+ {
+ Point aPoint(X, 0);
+ aPoint = GetPixelPos(aPoint);
+ maBottomRight.setX( aPoint.X() );
+ }
+ void SetPreviewLogicRect(const Point& rTopLeft, const Point &rBottomRight)
+ {
+ maTopLeft = GetPixelPos(rTopLeft);
+ maBottomRight = GetPixelPos(rBottomRight);
+ maPreviewRect = tools::Rectangle(maTopLeft,
+ Size(maBottomRight.X() - maTopLeft.X(),
+ maBottomRight.Y() - maTopLeft.Y()));
+ }
+ void SetPreviewMaxRect(const Point& rTopLeft, const Point &rBottomRight)
+ {
+ maMinTopLeft = rTopLeft;
+ maMaxBottomRight = rBottomRight;
+ }
+ void DrawDrag(vcl::RenderContext& rRenderContext);
+ void UpdatePreviewBounds();
+ void SetBitmap(SvStream &rStream)
+ {
+ ReadDIBBitmapEx(maPreviewBitmapEx, rStream, true);
+ }
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override
+ {
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(PREVIEW_WIDTH, PREVIEW_HEIGHT), MapMode(MapUnit::MapAppFont)));
+ aSize.setWidth(aSize.getWidth()+1);
+ aSize.setHeight(aSize.getHeight()+1);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ SetOutputSizePixel(aSize);
+ }
+};
+
+SaneDlg::SaneDlg(weld::Window* pParent, Sane& rSane, bool bScanEnabled)
+ : GenericDialogController(pParent, "modules/scanner/ui/sanedialog.ui", "SaneDialog")
+ , mpParent(pParent)
+ , mrSane(rSane)
+ , mbScanEnabled(bScanEnabled)
+ , mnCurrentOption(0)
+ , mnCurrentElement(0)
+ , mfMin(0.0)
+ , mfMax(0.0)
+ , doScan(false)
+ , mxCancelButton(m_xBuilder->weld_button("cancel"))
+ , mxDeviceInfoButton(m_xBuilder->weld_button("deviceInfoButton"))
+ , mxPreviewButton(m_xBuilder->weld_button("previewButton"))
+ , mxScanButton(m_xBuilder->weld_button("ok"))
+ , mxButtonOption(m_xBuilder->weld_button("optionsButton"))
+ , mxOptionTitle(m_xBuilder->weld_label("optionTitleLabel"))
+ , mxOptionDescTxt(m_xBuilder->weld_label("optionsDescLabel"))
+ , mxVectorTxt(m_xBuilder->weld_label("vectorLabel"))
+ , mxLeftField(m_xBuilder->weld_metric_spin_button("leftSpinbutton", FieldUnit::PIXEL))
+ , mxTopField(m_xBuilder->weld_metric_spin_button("topSpinbutton", FieldUnit::PIXEL))
+ , mxRightField(m_xBuilder->weld_metric_spin_button("rightSpinbutton", FieldUnit::PIXEL))
+ , mxBottomField(m_xBuilder->weld_metric_spin_button("bottomSpinbutton", FieldUnit::PIXEL))
+ , mxDeviceBox(m_xBuilder->weld_combo_box("deviceCombobox"))
+ , mxReslBox(m_xBuilder->weld_combo_box("reslCombobox"))
+ , mxAdvancedBox(m_xBuilder->weld_check_button("advancedCheckbutton"))
+ , mxVectorBox(m_xBuilder->weld_spin_button("vectorSpinbutton"))
+ , mxQuantumRangeBox(m_xBuilder->weld_combo_box("quantumRangeCombobox"))
+ , mxStringRangeBox(m_xBuilder->weld_combo_box("stringRangeCombobox"))
+ , mxBoolCheckBox(m_xBuilder->weld_check_button("boolCheckbutton"))
+ , mxStringEdit(m_xBuilder->weld_entry("stringEntry"))
+ , mxNumericEdit(m_xBuilder->weld_entry("numericEntry"))
+ , mxOptionBox(m_xBuilder->weld_tree_view("optionSvTreeListBox"))
+ , mxPreview(new ScanPreview)
+ , mxPreviewWnd(new weld::CustomWeld(*m_xBuilder, "preview", *mxPreview))
+{
+ Size aSize(mxOptionBox->get_approximate_digit_width() * 32, mxOptionBox->get_height_rows(8));
+ mxOptionTitle->set_size_request(aSize.Width(), aSize.Height() / 2);
+ mxOptionBox->set_size_request(aSize.Width(), aSize.Height());
+ mxPreview->Init(this);
+ if( Sane::IsSane() )
+ {
+ InitDevices(); // opens first sane device
+ DisableOption();
+ InitFields();
+ }
+
+ mxDeviceInfoButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
+ mxPreviewButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
+ mxScanButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
+ mxButtonOption->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
+ mxDeviceBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
+ mxOptionBox->connect_changed( LINK( this, SaneDlg, OptionsBoxSelectHdl ) );
+ mxCancelButton->connect_clicked( LINK( this, SaneDlg, ClickBtnHdl ) );
+ mxBoolCheckBox->connect_toggled( LINK( this, SaneDlg, ToggleBtnHdl ) );
+ mxStringEdit->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
+ mxNumericEdit->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
+ mxVectorBox->connect_changed( LINK( this, SaneDlg, ModifyHdl ) );
+ mxReslBox->connect_changed( LINK( this, SaneDlg, ValueModifyHdl ) );
+ mxStringRangeBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
+ mxQuantumRangeBox->connect_changed( LINK( this, SaneDlg, SelectHdl ) );
+ mxLeftField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl ) );
+ mxRightField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
+ mxTopField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
+ mxBottomField->connect_value_changed( LINK( this, SaneDlg, MetricValueModifyHdl) );
+ mxAdvancedBox->connect_toggled( LINK( this, SaneDlg, ToggleBtnHdl ) );
+
+ maOldLink = mrSane.SetReloadOptionsHdl( LINK( this, SaneDlg, ReloadSaneOptionsHdl ) );
+}
+
+SaneDlg::~SaneDlg()
+{
+ mrSane.SetReloadOptionsHdl(maOldLink);
+}
+
+namespace {
+
+OUString SaneResId(TranslateId aID)
+{
+ return Translate::get(aID, Translate::Create("pcr"));
+}
+
+}
+
+short SaneDlg::run()
+{
+ if (!Sane::IsSane())
+ {
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(mpParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SaneResId(STR_COULD_NOT_BE_INIT)));
+ xErrorBox->run();
+ return RET_CANCEL;
+ }
+ LoadState();
+ return GenericDialogController::run();
+}
+
+void SaneDlg::InitDevices()
+{
+ if( ! Sane::IsSane() )
+ return;
+
+ if( mrSane.IsOpen() )
+ mrSane.Close();
+ mrSane.ReloadDevices();
+ mxDeviceBox->clear();
+ for (int i = 0; i < Sane::CountDevices(); ++i)
+ mxDeviceBox->append_text(Sane::GetName(i));
+ if( Sane::CountDevices() )
+ {
+ mrSane.Open(0);
+ mxDeviceBox->set_active(0);
+ }
+}
+
+void SaneDlg::InitFields()
+{
+ if( ! Sane::IsSane() )
+ return;
+
+ int nOption, i, nValue;
+ double fValue;
+ const char *ppSpecialOptions[] = {
+ "resolution",
+ "tl-x",
+ "tl-y",
+ "br-x",
+ "br-y",
+ "preview"
+ };
+
+ mxPreview->EnableDrag();
+ mxReslBox->clear();
+ Point aTopLeft, aBottomRight;
+ mxPreview->GetPreviewLogicRect(aTopLeft, aBottomRight);
+ Point aMinTopLeft, aMaxBottomRight;
+ mxPreview->GetMaxLogicRect(aMinTopLeft, aMaxBottomRight);
+ mxScanButton->set_visible( mbScanEnabled );
+
+ if( ! mrSane.IsOpen() )
+ return;
+
+ // set Resolution
+ nOption = mrSane.GetOptionByName( "resolution" );
+ if( nOption != -1 )
+ {
+ double fRes;
+
+ if( mrSane.GetOptionValue( nOption, fRes ) )
+ {
+ mxReslBox->set_sensitive(true);
+
+ mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fRes)));
+ std::unique_ptr<double[]> pDouble;
+ nValue = mrSane.GetRange( nOption, pDouble );
+ if( nValue > -1 )
+ {
+ assert(pDouble);
+ if( nValue )
+ {
+ for( i=0; i<nValue; i++ )
+ {
+ if( i == 0 || i == nValue-1 || ! ( static_cast<int>(pDouble[i]) % 20) )
+ mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[i])));
+ }
+ }
+ else
+ {
+ mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[0])));
+ // Can only select 75 and 2400 dpi in Scanner dialogue
+ // scanner allows random setting of dpi resolution, a slider might be useful
+ // support that
+ // workaround: offer at least some more standard dpi resolution between
+ // min and max value
+ int bGot300 = 0;
+ for (sal_uInt32 nRes = static_cast<sal_uInt32>(pDouble[0]) * 2; nRes < static_cast<sal_uInt32>(pDouble[1]); nRes = nRes * 2)
+ {
+ if ( !bGot300 && nRes > 300 ) {
+ nRes = 300; bGot300 = 1;
+ }
+ mxReslBox->append_text(OUString::number(nRes));
+ }
+ mxReslBox->append_text(OUString::number(static_cast<sal_uInt32>(pDouble[1])));
+ }
+ }
+ else
+ mxReslBox->set_sensitive( false );
+ }
+ }
+ else
+ mxReslBox->set_sensitive( false );
+
+ // set scan area
+ for( i = 0; i < 4; i++ )
+ {
+ char const *pOptionName = nullptr;
+ weld::MetricSpinButton* pField = nullptr;
+ switch( i )
+ {
+ case 0:
+ pOptionName = "tl-x";
+ pField = mxLeftField.get();
+ break;
+ case 1:
+ pOptionName = "tl-y";
+ pField = mxTopField.get();
+ break;
+ case 2:
+ pOptionName = "br-x";
+ pField = mxRightField.get();
+ break;
+ case 3:
+ pOptionName = "br-y";
+ pField = mxBottomField.get();
+ }
+ nOption = pOptionName ? mrSane.GetOptionByName( pOptionName ) : -1;
+ if( nOption != -1 )
+ {
+ if( mrSane.GetOptionValue( nOption, fValue ) )
+ {
+ if( mrSane.GetOptionUnit( nOption ) == SANE_UNIT_MM )
+ {
+ pField->set_unit( FieldUnit::MM );
+ pField->set_value( static_cast<int>(fValue), FieldUnit::MM );
+ }
+ else // SANE_UNIT_PIXEL
+ {
+ pField->set_unit( FieldUnit::PIXEL );
+ pField->set_value( static_cast<int>(fValue), FieldUnit::PIXEL );
+ }
+ switch( i ) {
+ case 0: aTopLeft.setX( static_cast<int>(fValue) );break;
+ case 1: aTopLeft.setY( static_cast<int>(fValue) );break;
+ case 2: aBottomRight.setX( static_cast<int>(fValue) );break;
+ case 3: aBottomRight.setY( static_cast<int>(fValue) );break;
+ }
+ }
+ std::unique_ptr<double[]> pDouble;
+ nValue = mrSane.GetRange( nOption, pDouble );
+ if( nValue > -1 )
+ {
+ if( pDouble )
+ {
+ pField->set_min( static_cast<tools::Long>(pDouble[0]), FieldUnit::NONE );
+ if( nValue )
+ pField->set_max( static_cast<tools::Long>(pDouble[ nValue-1 ]), FieldUnit::NONE );
+ else
+ pField->set_max( static_cast<tools::Long>(pDouble[ 1 ]), FieldUnit::NONE );
+ }
+ switch( i ) {
+ case 0: aMinTopLeft.setX( pField->get_min(FieldUnit::NONE) );break;
+ case 1: aMinTopLeft.setY( pField->get_min(FieldUnit::NONE) );break;
+ case 2: aMaxBottomRight.setX( pField->get_max(FieldUnit::NONE) );break;
+ case 3: aMaxBottomRight.setY( pField->get_max(FieldUnit::NONE) );break;
+ }
+ }
+ else
+ {
+ switch( i ) {
+ case 0: aMinTopLeft.setX( static_cast<int>(fValue) );break;
+ case 1: aMinTopLeft.setY( static_cast<int>(fValue) );break;
+ case 2: aMaxBottomRight.setX( static_cast<int>(fValue) );break;
+ case 3: aMaxBottomRight.setY( static_cast<int>(fValue) );break;
+ }
+ }
+ pField->set_sensitive(true);
+ }
+ else
+ {
+ mxPreview->DisableDrag();
+ pField->set_min( 0, FieldUnit::NONE );
+ switch( i ) {
+ case 0:
+ aMinTopLeft.setX( 0 );
+ aTopLeft.setX( 0 );
+ pField->set_max( PREVIEW_WIDTH, FieldUnit::NONE );
+ pField->set_value( 0, FieldUnit::NONE );
+ break;
+ case 1:
+ aMinTopLeft.setY( 0 );
+ aTopLeft.setY( 0 );
+ pField->set_max( PREVIEW_HEIGHT, FieldUnit::NONE );
+ pField->set_value( 0, FieldUnit::NONE );
+ break;
+ case 2:
+ aMaxBottomRight.setX( PREVIEW_WIDTH );
+ aBottomRight.setX( PREVIEW_WIDTH );
+ pField->set_max( PREVIEW_WIDTH, FieldUnit::NONE );
+ pField->set_value( PREVIEW_WIDTH, FieldUnit::NONE );
+ break;
+ case 3:
+ aMaxBottomRight.setY( PREVIEW_HEIGHT );
+ aBottomRight.setY( PREVIEW_HEIGHT );
+ pField->set_max( PREVIEW_HEIGHT, FieldUnit::NONE );
+ pField->set_value( PREVIEW_HEIGHT, FieldUnit::NONE );
+ break;
+ }
+ pField->set_sensitive(false);
+ }
+ }
+
+ mxPreview->SetPreviewMaxRect(aMinTopLeft, aMaxBottomRight);
+ mxPreview->SetPreviewLogicRect(aTopLeft, aBottomRight);
+ mxPreview->Invalidate();
+
+ // fill OptionBox
+ mxOptionBox->clear();
+ std::unique_ptr<weld::TreeIter> xParentEntry(mxOptionBox->make_iterator());
+ bool bGroupRejected = false;
+ for( i = 1; i < mrSane.CountOptions(); i++ )
+ {
+ OUString aOption=mrSane.GetOptionName( i );
+ bool bInsertAdvanced =
+ (mrSane.GetOptionCap( i ) & SANE_CAP_ADVANCED) == 0 ||
+ mxAdvancedBox->get_active();
+ if( mrSane.GetOptionType( i ) == SANE_TYPE_GROUP )
+ {
+ if( bInsertAdvanced )
+ {
+ aOption = mrSane.GetOptionTitle( i );
+ mxOptionBox->append(xParentEntry.get());
+ mxOptionBox->set_text(*xParentEntry, aOption, 0);
+ bGroupRejected = false;
+ }
+ else
+ bGroupRejected = true;
+ }
+ else if( !aOption.isEmpty() &&
+ ! ( mrSane.GetOptionCap( i ) &
+ (
+ SANE_CAP_HARD_SELECT |
+ SANE_CAP_INACTIVE
+ ) ) &&
+ bInsertAdvanced && ! bGroupRejected )
+ {
+ bool bIsSpecial = false;
+ for( size_t n = 0; !bIsSpecial &&
+ n < SAL_N_ELEMENTS(ppSpecialOptions); n++ )
+ {
+ if( aOption == OUString::createFromAscii(ppSpecialOptions[n]) )
+ bIsSpecial=true;
+ }
+ if( ! bIsSpecial )
+ {
+ if (xParentEntry)
+ mxOptionBox->append(xParentEntry.get(), aOption);
+ else
+ mxOptionBox->append_text(aOption);
+ }
+ }
+ }
+}
+
+IMPL_LINK( SaneDlg, ClickBtnHdl, weld::Button&, rButton, void )
+{
+ if( mrSane.IsOpen() )
+ {
+ if( &rButton == mxDeviceInfoButton.get() )
+ {
+ OUString aString(SaneResId(STR_DEVICE_DESC));
+ aString = aString.replaceFirst( "%s", Sane::GetName( mrSane.GetDeviceNumber() ) );
+ aString = aString.replaceFirst( "%s", Sane::GetVendor( mrSane.GetDeviceNumber() ) );
+ aString = aString.replaceFirst( "%s", Sane::GetModel( mrSane.GetDeviceNumber() ) );
+ aString = aString.replaceFirst( "%s", Sane::GetType( mrSane.GetDeviceNumber() ) );
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ aString));
+ xInfoBox->run();
+ }
+ else if( &rButton == mxPreviewButton.get() )
+ AcquirePreview();
+ else if( &rButton == mxButtonOption.get() )
+ {
+
+ SANE_Value_Type nType = mrSane.GetOptionType( mnCurrentOption );
+ switch( nType )
+ {
+ case SANE_TYPE_BUTTON:
+ mrSane.ActivateButtonOption( mnCurrentOption );
+ break;
+ case SANE_TYPE_FIXED:
+ case SANE_TYPE_INT:
+ {
+ int nElements = mrSane.GetOptionElements( mnCurrentOption );
+ std::unique_ptr<double[]> x(new double[ nElements ]);
+ std::unique_ptr<double[]> y(new double[ nElements ]);
+ for( int i = 0; i < nElements; i++ )
+ x[ i ] = static_cast<double>(i);
+ mrSane.GetOptionValue( mnCurrentOption, y.get() );
+
+ GridDialog aGrid(m_xDialog.get(), x.get(), y.get(), nElements);
+ aGrid.set_title( mrSane.GetOptionName( mnCurrentOption ) );
+ aGrid.setBoundings( 0, mfMin, nElements, mfMax );
+ if (aGrid.run() && aGrid.getNewYValues())
+ mrSane.SetOptionValue( mnCurrentOption, aGrid.getNewYValues() );
+ }
+ break;
+ case SANE_TYPE_BOOL:
+ case SANE_TYPE_STRING:
+ case SANE_TYPE_GROUP:
+ break;
+ }
+ }
+ }
+ if (&rButton == mxScanButton.get())
+ {
+ double fRes = static_cast<double>(mxReslBox->get_active_text().toUInt32());
+ SetAdjustedNumericalValue( "resolution", fRes );
+ UpdateScanArea(true);
+ SaveState();
+ m_xDialog->response(mrSane.IsOpen() ? RET_OK : RET_CANCEL);
+ doScan = mrSane.IsOpen();
+ }
+ else if( &rButton == mxCancelButton.get() )
+ {
+ mrSane.Close();
+ m_xDialog->response(RET_CANCEL);
+ }
+}
+
+IMPL_LINK( SaneDlg, ToggleBtnHdl, weld::Toggleable&, rButton, void )
+{
+ if( mrSane.IsOpen() )
+ {
+ if( &rButton == mxBoolCheckBox.get() )
+ {
+ mrSane.SetOptionValue( mnCurrentOption,
+ mxBoolCheckBox->get_active() );
+ }
+ else if( &rButton == mxAdvancedBox.get() )
+ {
+ ReloadSaneOptionsHdl( mrSane );
+ }
+ }
+}
+
+IMPL_LINK( SaneDlg, SelectHdl, weld::ComboBox&, rListBox, void )
+{
+ if( &rListBox == mxDeviceBox.get() && Sane::IsSane() && Sane::CountDevices() )
+ {
+ int nNewNumber = mxDeviceBox->get_active();
+ int nOldNumber = mrSane.GetDeviceNumber();
+ if (nNewNumber != nOldNumber)
+ {
+ mrSane.Close();
+ mrSane.Open(nNewNumber);
+ mxPreview->ResetForNewScanner();
+ InitFields();
+ }
+ }
+ if( mrSane.IsOpen() )
+ {
+ if( &rListBox == mxQuantumRangeBox.get() )
+ {
+ double fValue = mxQuantumRangeBox->get_active_text().toDouble();
+ mrSane.SetOptionValue(mnCurrentOption, fValue, mnCurrentElement);
+ }
+ else if( &rListBox == mxStringRangeBox.get() )
+ {
+ mrSane.SetOptionValue(mnCurrentOption, mxStringRangeBox->get_active_text());
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SaneDlg, OptionsBoxSelectHdl, weld::TreeView&, void)
+{
+ if (!Sane::IsSane())
+ return;
+
+ OUString aOption = mxOptionBox->get_selected_text();
+ int nOption = mrSane.GetOptionByName(OUStringToOString(aOption,
+ osl_getThreadTextEncoding()).getStr());
+ if( nOption == -1 || nOption == mnCurrentOption )
+ return;
+
+ DisableOption();
+ mnCurrentOption = nOption;
+ mxOptionTitle->set_label(mrSane.GetOptionTitle(mnCurrentOption));
+ SANE_Value_Type nType = mrSane.GetOptionType( mnCurrentOption );
+ SANE_Constraint_Type nConstraint;
+ switch( nType )
+ {
+ case SANE_TYPE_BOOL: EstablishBoolOption();break;
+ case SANE_TYPE_STRING:
+ nConstraint = mrSane.GetOptionConstraintType( mnCurrentOption );
+ if( nConstraint == SANE_CONSTRAINT_STRING_LIST )
+ EstablishStringRange();
+ else
+ EstablishStringOption();
+ break;
+ case SANE_TYPE_FIXED:
+ case SANE_TYPE_INT:
+ {
+ nConstraint = mrSane.GetOptionConstraintType( mnCurrentOption );
+ int nElements = mrSane.GetOptionElements( mnCurrentOption );
+ mnCurrentElement = 0;
+ if( nConstraint == SANE_CONSTRAINT_RANGE ||
+ nConstraint == SANE_CONSTRAINT_WORD_LIST )
+ EstablishQuantumRange();
+ else
+ {
+ mfMin = mfMax = 0.0;
+ EstablishNumericOption();
+ }
+ if( nElements > 1 )
+ {
+ if( nElements <= 10 )
+ {
+ mxVectorBox->set_range(1, mrSane.GetOptionElements(mnCurrentOption));
+ mxVectorBox->set_value(1);
+ mxVectorBox->show();
+ mxVectorTxt->show();
+ }
+ else
+ {
+ DisableOption();
+ // bring up dialog only on button click
+ EstablishButtonOption();
+ }
+ }
+ }
+ break;
+ case SANE_TYPE_BUTTON:
+ EstablishButtonOption();
+ break;
+ default: break;
+ }
+}
+
+IMPL_LINK(SaneDlg, ModifyHdl, weld::Entry&, rEdit, void)
+{
+ if( !mrSane.IsOpen() )
+ return;
+
+ if (&rEdit == mxStringEdit.get())
+ {
+ mrSane.SetOptionValue( mnCurrentOption, mxStringEdit->get_text() );
+ }
+ else if (&rEdit == mxNumericEdit.get())
+ {
+ double fValue = mxNumericEdit->get_text().toDouble();
+ if( mfMin != mfMax && ( fValue < mfMin || fValue > mfMax ) )
+ {
+ char pBuf[256];
+ if( fValue < mfMin )
+ fValue = mfMin;
+ else if( fValue > mfMax )
+ fValue = mfMax;
+ sprintf( pBuf, "%g", fValue );
+ mxNumericEdit->set_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
+ }
+ mrSane.SetOptionValue( mnCurrentOption, fValue, mnCurrentElement );
+ }
+ else if (&rEdit == mxVectorBox.get())
+ {
+ mnCurrentElement = mxVectorBox->get_value() - 1;
+ double fValue;
+ if( mrSane.GetOptionValue( mnCurrentOption, fValue, mnCurrentElement ))
+ {
+ char pBuf[256];
+ sprintf( pBuf, "%g", fValue );
+ OUString aValue( pBuf, strlen(pBuf), osl_getThreadTextEncoding() );
+ mxNumericEdit->set_text( aValue );
+ mxQuantumRangeBox->set_active_text( aValue );
+ }
+ }
+}
+
+IMPL_LINK(SaneDlg, ValueModifyHdl, weld::ComboBox&, rEdit, void)
+{
+ if( !mrSane.IsOpen() )
+ return;
+
+ if (&rEdit != mxReslBox.get())
+ return;
+
+ double fRes = static_cast<double>(mxReslBox->get_active_text().toUInt32());
+ int nOption = mrSane.GetOptionByName( "resolution" );
+ if( nOption == -1 )
+ return;
+
+ std::unique_ptr<double[]> pDouble;
+ int nValues = mrSane.GetRange( nOption, pDouble );
+ if( nValues > 0 )
+ {
+ int i;
+ for( i = 0; i < nValues; i++ )
+ {
+ if( fRes == pDouble[i] )
+ break;
+ }
+ if( i >= nValues )
+ fRes = pDouble[0];
+ }
+ else if( nValues == 0 )
+ {
+ if( fRes < pDouble[ 0 ] )
+ fRes = pDouble[ 0 ];
+ if( fRes > pDouble[ 1 ] )
+ fRes = pDouble[ 1 ];
+ }
+ mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fRes)));
+}
+
+IMPL_LINK(SaneDlg, MetricValueModifyHdl, weld::MetricSpinButton&, rEdit, void)
+{
+ if( !mrSane.IsOpen() )
+ return;
+
+ if (&rEdit == mxTopField.get())
+ {
+ mxPreview->ChangePreviewLogicTopLeftY(mxTopField->get_value(FieldUnit::NONE));
+ mxPreview->Invalidate();
+ }
+ else if (&rEdit == mxLeftField.get())
+ {
+ mxPreview->ChangePreviewLogicTopLeftX(mxLeftField->get_value(FieldUnit::NONE));
+ mxPreview->Invalidate();
+ }
+ else if (&rEdit == mxBottomField.get())
+ {
+ mxPreview->ChangePreviewLogicBottomRightY(mxBottomField->get_value(FieldUnit::NONE));
+ mxPreview->Invalidate();
+ }
+ else if (&rEdit == mxRightField.get())
+ {
+ mxPreview->ChangePreviewLogicBottomRightX(mxRightField->get_value(FieldUnit::NONE));
+ mxPreview->Invalidate();
+ }
+}
+
+IMPL_LINK_NOARG( SaneDlg, ReloadSaneOptionsHdl, Sane&, void )
+{
+ mnCurrentOption = -1;
+ mnCurrentElement = 0;
+ DisableOption();
+ InitFields();
+ mxPreview->Invalidate();
+}
+
+void SaneDlg::AcquirePreview()
+{
+ if( ! mrSane.IsOpen() )
+ return;
+
+ UpdateScanArea( true );
+ // set small resolution for preview
+ double fResl = static_cast<double>(mxReslBox->get_active_text().toUInt32());
+ SetAdjustedNumericalValue( "resolution", 30.0 );
+
+ int nOption = mrSane.GetOptionByName( "preview" );
+ if( nOption == -1 )
+ {
+ OUString aString(SaneResId(STR_SLOW_PREVIEW));
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::OkCancel,
+ aString));
+ if (xBox->run() == RET_CANCEL)
+ return;
+ }
+ else
+ mrSane.SetOptionValue( nOption, true );
+
+ rtl::Reference<BitmapTransporter> xTransporter(new BitmapTransporter);
+ if (!mrSane.Start(*xTransporter))
+ {
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SaneResId(STR_ERROR_SCAN)));
+ xErrorBox->run();
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("extensions.scanner", "Previewbitmapstream contains " << xTransporter->getStream().TellEnd() << "bytes");
+#endif
+ xTransporter->getStream().Seek( STREAM_SEEK_TO_BEGIN );
+ mxPreview->SetBitmap(xTransporter->getStream());
+ }
+
+ SetAdjustedNumericalValue( "resolution", fResl );
+ mxReslBox->set_entry_text(OUString::number(static_cast<sal_uInt32>(fResl)));
+
+ mxPreview->UpdatePreviewBounds();
+ mxPreview->Invalidate();
+}
+
+void ScanPreview::UpdatePreviewBounds()
+{
+ if( mbDragEnable )
+ {
+ maPreviewRect = tools::Rectangle( maTopLeft,
+ Size( maBottomRight.X() - maTopLeft.X(),
+ maBottomRight.Y() - maTopLeft.Y() )
+ );
+ }
+ else
+ {
+ Size aBMSize( maPreviewBitmapEx.GetSizePixel() );
+ if( aBMSize.Width() > aBMSize.Height() && aBMSize.Width() )
+ {
+ int nVHeight = (maBottomRight.X() - maTopLeft.X()) * aBMSize.Height() / aBMSize.Width();
+ maPreviewRect = tools::Rectangle( Point( maTopLeft.X(), ( maTopLeft.Y() + maBottomRight.Y() )/2 - nVHeight/2 ),
+ Size( maBottomRight.X() - maTopLeft.X(),
+ nVHeight ) );
+ }
+ else if (aBMSize.Height())
+ {
+ int nVWidth = (maBottomRight.Y() - maTopLeft.Y()) * aBMSize.Width() / aBMSize.Height();
+ maPreviewRect = tools::Rectangle( Point( ( maTopLeft.X() + maBottomRight.X() )/2 - nVWidth/2, maTopLeft.Y() ),
+ Size( nVWidth,
+ maBottomRight.Y() - maTopLeft.Y() ) );
+ }
+ }
+}
+
+void ScanPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapAppFont));
+ rRenderContext.SetFillColor(COL_WHITE);
+ rRenderContext.SetLineColor(COL_WHITE);
+ rRenderContext.DrawRect(tools::Rectangle(Point(0, 0),
+ Size(PREVIEW_WIDTH, PREVIEW_HEIGHT)));
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
+ // check for sane values
+ rRenderContext.DrawBitmapEx(maPreviewRect.TopLeft(), maPreviewRect.GetSize(), maPreviewBitmapEx);
+
+ mbDragDrawn = false;
+ DrawDrag(rRenderContext);
+}
+
+void SaneDlg::DisableOption()
+{
+ mxBoolCheckBox->hide();
+ mxStringEdit->hide();
+ mxNumericEdit->hide();
+ mxQuantumRangeBox->hide();
+ mxStringRangeBox->hide();
+ mxButtonOption->hide();
+ mxVectorBox->hide();
+ mxVectorTxt->hide();
+ mxOptionDescTxt->hide();
+}
+
+void SaneDlg::EstablishBoolOption()
+{
+ bool bSuccess, bValue;
+
+ bSuccess = mrSane.GetOptionValue( mnCurrentOption, bValue );
+ if( bSuccess )
+ {
+ mxBoolCheckBox->set_label( mrSane.GetOptionName( mnCurrentOption ) );
+ mxBoolCheckBox->set_active( bValue );
+ mxBoolCheckBox->show();
+ }
+}
+
+void SaneDlg::EstablishStringOption()
+{
+ bool bSuccess;
+ OString aValue;
+
+ bSuccess = mrSane.GetOptionValue( mnCurrentOption, aValue );
+ if( bSuccess )
+ {
+ mxOptionDescTxt->set_label( mrSane.GetOptionName( mnCurrentOption ) );
+ mxOptionDescTxt->show();
+ mxStringEdit->set_text(OStringToOUString(aValue, osl_getThreadTextEncoding()));
+ mxStringEdit->show();
+ }
+}
+
+void SaneDlg::EstablishStringRange()
+{
+ const char** ppStrings = mrSane.GetStringConstraint( mnCurrentOption );
+ mxStringRangeBox->clear();
+ for( int i = 0; ppStrings[i] != nullptr; i++ )
+ mxStringRangeBox->append_text( OUString( ppStrings[i], strlen(ppStrings[i]), osl_getThreadTextEncoding() ) );
+ OString aValue;
+ mrSane.GetOptionValue( mnCurrentOption, aValue );
+ mxStringRangeBox->set_active_text(OStringToOUString(aValue, osl_getThreadTextEncoding()));
+ mxStringRangeBox->show();
+ mxOptionDescTxt->set_label( mrSane.GetOptionName( mnCurrentOption ) );
+ mxOptionDescTxt->show();
+}
+
+void SaneDlg::EstablishQuantumRange()
+{
+ mpRange.reset();
+ int nValues = mrSane.GetRange( mnCurrentOption, mpRange );
+ if( nValues == 0 )
+ {
+ mfMin = mpRange[ 0 ];
+ mfMax = mpRange[ 1 ];
+ mpRange.reset();
+ EstablishNumericOption();
+ }
+ else if( nValues > 0 )
+ {
+ char pBuf[ 256 ];
+ mxQuantumRangeBox->clear();
+ mfMin = mpRange[ 0 ];
+ mfMax = mpRange[ nValues-1 ];
+ for( int i = 0; i < nValues; i++ )
+ {
+ sprintf( pBuf, "%g", mpRange[ i ] );
+ mxQuantumRangeBox->append_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
+ }
+ double fValue;
+ if( mrSane.GetOptionValue( mnCurrentOption, fValue, mnCurrentElement ) )
+ {
+ sprintf( pBuf, "%g", fValue );
+ mxQuantumRangeBox->set_active_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
+ }
+ mxQuantumRangeBox->show();
+ OUString aText( mrSane.GetOptionName( mnCurrentOption ) + " " );
+ aText += mrSane.GetOptionUnitName( mnCurrentOption );
+ mxOptionDescTxt->set_label(aText);
+ mxOptionDescTxt->show();
+ }
+}
+
+void SaneDlg::EstablishNumericOption()
+{
+ bool bSuccess;
+ double fValue;
+
+ bSuccess = mrSane.GetOptionValue( mnCurrentOption, fValue );
+ if( ! bSuccess )
+ return;
+
+ char pBuf[256];
+ OUString aText( mrSane.GetOptionName( mnCurrentOption ) + " " );
+ aText += mrSane.GetOptionUnitName( mnCurrentOption );
+ if( mfMin != mfMax )
+ {
+ sprintf( pBuf, " < %g ; %g >", mfMin, mfMax );
+ aText += OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() );
+ }
+ mxOptionDescTxt->set_label( aText );
+ mxOptionDescTxt->show();
+ sprintf( pBuf, "%g", fValue );
+ mxNumericEdit->set_text( OUString( pBuf, strlen(pBuf), osl_getThreadTextEncoding() ) );
+ mxNumericEdit->show();
+}
+
+void SaneDlg::EstablishButtonOption()
+{
+ mxOptionDescTxt->set_label(mrSane.GetOptionName(mnCurrentOption));
+ mxOptionDescTxt->show();
+ mxButtonOption->show();
+}
+
+bool ScanPreview::MouseMove(const MouseEvent& rMEvt)
+{
+ if( !mbIsDragging )
+ return false;
+
+ Point aMousePos = rMEvt.GetPosPixel();
+ // move into valid area
+ Point aLogicPos = GetLogicPos( aMousePos );
+ aMousePos = GetPixelPos( aLogicPos );
+ switch( meDragDirection )
+ {
+ case TopLeft: maTopLeft = aMousePos; break;
+ case Top: maTopLeft.setY( aMousePos.Y() ); break;
+ case TopRight:
+ maTopLeft.setY( aMousePos.Y() );
+ maBottomRight.setX( aMousePos.X() );
+ break;
+ case Right: maBottomRight.setX( aMousePos.X() ); break;
+ case BottomRight: maBottomRight = aMousePos; break;
+ case Bottom: maBottomRight.setY( aMousePos.Y() ); break;
+ case BottomLeft:
+ maTopLeft.setX( aMousePos.X() );
+ maBottomRight.setY( aMousePos.Y() );
+ break;
+ case Left: maTopLeft.setX( aMousePos.X() ); break;
+ default: break;
+ }
+ int nSwap;
+ if( maTopLeft.X() > maBottomRight.X() )
+ {
+ nSwap = maTopLeft.X();
+ maTopLeft.setX( maBottomRight.X() );
+ maBottomRight.setX( nSwap );
+ }
+ if( maTopLeft.Y() > maBottomRight.Y() )
+ {
+ nSwap = maTopLeft.Y();
+ maTopLeft.setY( maBottomRight.Y() );
+ maBottomRight.setY( nSwap );
+ }
+ Invalidate();
+ mpParentDialog->UpdateScanArea(false);
+ return false;
+}
+
+bool ScanPreview::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (!mbIsDragging && mbDragEnable)
+ {
+ Point aMousePixel = rMEvt.GetPosPixel();
+
+ int nMiddleX = ( maBottomRight.X() - maTopLeft.X() ) / 2 - RECT_SIZE_PIX/2 + maTopLeft.X();
+ int nMiddleY = ( maBottomRight.Y() - maTopLeft.Y() ) / 2 - RECT_SIZE_PIX/2 + maTopLeft.Y();
+ if( aMousePixel.Y() >= maTopLeft.Y() &&
+ aMousePixel.Y() < maTopLeft.Y() + RECT_SIZE_PIX )
+ {
+ if( aMousePixel.X() >= maTopLeft.X() &&
+ aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
+ {
+ meDragDirection = TopLeft;
+ mbIsDragging = true;
+ }
+ else if( aMousePixel.X() >= nMiddleX &&
+ aMousePixel.X() < nMiddleX + RECT_SIZE_PIX )
+ {
+ meDragDirection = Top;
+ mbIsDragging = true;
+ }
+ else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
+ aMousePixel.X() <= maBottomRight.X() )
+ {
+ meDragDirection = TopRight;
+ mbIsDragging = true;
+ }
+ }
+ else if( aMousePixel.Y() >= nMiddleY &&
+ aMousePixel.Y() < nMiddleY + RECT_SIZE_PIX )
+ {
+ if( aMousePixel.X() >= maTopLeft.X() &&
+ aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
+ {
+ meDragDirection = Left;
+ mbIsDragging = true;
+ }
+ else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
+ aMousePixel.X() <= maBottomRight.X() )
+ {
+ meDragDirection = Right;
+ mbIsDragging = true;
+ }
+ }
+ else if( aMousePixel.Y() <= maBottomRight.Y() &&
+ aMousePixel.Y() > maBottomRight.Y() - RECT_SIZE_PIX )
+ {
+ if( aMousePixel.X() >= maTopLeft.X() &&
+ aMousePixel.X() < maTopLeft.X() + RECT_SIZE_PIX )
+ {
+ meDragDirection = BottomLeft;
+ mbIsDragging = true;
+ }
+ else if( aMousePixel.X() >= nMiddleX &&
+ aMousePixel.X() < nMiddleX + RECT_SIZE_PIX )
+ {
+ meDragDirection = Bottom;
+ mbIsDragging = true;
+ }
+ else if( aMousePixel.X() > maBottomRight.X() - RECT_SIZE_PIX &&
+ aMousePixel.X() <= maBottomRight.X() )
+ {
+ meDragDirection = BottomRight;
+ mbIsDragging = true;
+ }
+ }
+ }
+
+ if( mbIsDragging )
+ Invalidate();
+
+ return false;
+}
+
+bool ScanPreview::MouseButtonUp(const MouseEvent&)
+{
+ if( mbIsDragging )
+ mpParentDialog->UpdateScanArea(true);
+ mbIsDragging = false;
+
+ return false;
+}
+
+void ScanPreview::DrawDrag(vcl::RenderContext& rRenderContext)
+{
+ if (!mbDragEnable)
+ return;
+
+ RasterOp eROP = rRenderContext.GetRasterOp();
+ rRenderContext.SetRasterOp(RasterOp::Invert);
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
+
+ if (mbDragDrawn)
+ DrawRectangles(rRenderContext, maLastUL, maLastBR);
+
+ maLastUL = maTopLeft;
+ maLastBR = maBottomRight;
+ DrawRectangles(rRenderContext, maTopLeft, maBottomRight);
+
+ mbDragDrawn = true;
+ rRenderContext.SetRasterOp(eROP);
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapAppFont));
+}
+
+Point ScanPreview::GetPixelPos( const Point& rIn) const
+{
+ Point aConvert(
+ ( ( rIn.X() * PREVIEW_WIDTH ) /
+ ( maMaxBottomRight.X() - maMinTopLeft.X() ) )
+ ,
+ ( ( rIn.Y() * PREVIEW_HEIGHT )
+ / ( maMaxBottomRight.Y() - maMinTopLeft.Y() ) )
+ );
+
+ return GetDrawingArea()->get_ref_device().LogicToPixel(aConvert, MapMode(MapUnit::MapAppFont));
+}
+
+Point ScanPreview::GetLogicPos(const Point& rIn) const
+{
+ Point aConvert = GetDrawingArea()->get_ref_device().PixelToLogic(rIn, MapMode(MapUnit::MapAppFont));
+ if( aConvert.X() < 0 )
+ aConvert.setX( 0 );
+ if( aConvert.X() >= PREVIEW_WIDTH )
+ aConvert.setX( PREVIEW_WIDTH-1 );
+ if( aConvert.Y() < 0 )
+ aConvert.setY( 0 );
+ if( aConvert.Y() >= PREVIEW_HEIGHT )
+ aConvert.setY( PREVIEW_HEIGHT-1 );
+
+ aConvert.setX( aConvert.X() * ( maMaxBottomRight.X() - maMinTopLeft.X() ) );
+ aConvert.setX( aConvert.X() / ( PREVIEW_WIDTH) );
+ aConvert.setY( aConvert.Y() * ( maMaxBottomRight.Y() - maMinTopLeft.Y() ) );
+ aConvert.setY( aConvert.Y() / ( PREVIEW_HEIGHT) );
+ return aConvert;
+}
+
+void SaneDlg::UpdateScanArea(bool bSend)
+{
+ if (!mxPreview->IsDragEnabled())
+ return;
+
+ Point aUL, aBR;
+ mxPreview->GetPreviewLogicRect(aUL, aBR);
+
+ mxLeftField->set_value(aUL.X(), FieldUnit::NONE);
+ mxTopField->set_value(aUL.Y(), FieldUnit::NONE);
+ mxRightField->set_value(aBR.X(), FieldUnit::NONE);
+ mxBottomField->set_value(aBR.Y(), FieldUnit::NONE);
+
+ if (!bSend)
+ return;
+
+ if( mrSane.IsOpen() )
+ {
+ SetAdjustedNumericalValue( "tl-x", static_cast<double>(aUL.X()) );
+ SetAdjustedNumericalValue( "tl-y", static_cast<double>(aUL.Y()) );
+ SetAdjustedNumericalValue( "br-x", static_cast<double>(aBR.X()) );
+ SetAdjustedNumericalValue( "br-y", static_cast<double>(aBR.Y()) );
+ }
+}
+
+bool SaneDlg::LoadState()
+{
+ int i;
+
+ if( ! Sane::IsSane() )
+ return false;
+
+ const char* pEnv = getenv("HOME");
+ OUString aFileName = (pEnv ? OUString(pEnv, strlen(pEnv), osl_getThreadTextEncoding() ) : OUString()) + "/.so_sane_state";
+ Config aConfig( aFileName );
+ if( ! aConfig.HasGroup( "SANE" ) )
+ return false;
+
+ aConfig.SetGroup( "SANE" );
+ OString aString = aConfig.ReadKey( "SO_LastSaneDevice" );
+ for( i = 0; i < Sane::CountDevices() && aString != OUStringToOString(Sane::GetName(i), osl_getThreadTextEncoding()); i++ ) ;
+ if( i == Sane::CountDevices() )
+ return false;
+
+ mrSane.Close();
+ mrSane.Open( aString.getStr() );
+
+ DisableOption();
+ InitFields();
+
+ if( mrSane.IsOpen() )
+ {
+ int iMax = aConfig.GetKeyCount();
+ for (i = 0; i < iMax; ++i)
+ {
+ aString = aConfig.GetKeyName( i );
+ OString aValue = aConfig.ReadKey( i );
+ int nOption = mrSane.GetOptionByName( aString.getStr() );
+ if( nOption == -1 )
+ continue;
+
+ if (aValue.startsWith("BOOL="))
+ {
+ aValue = aValue.copy(RTL_CONSTASCII_LENGTH("BOOL="));
+ bool aBOOL = aValue.toInt32() != 0;
+ mrSane.SetOptionValue( nOption, aBOOL );
+ }
+ else if (aValue.startsWith("STRING="))
+ {
+ aValue = aValue.copy(RTL_CONSTASCII_LENGTH("STRING="));
+ mrSane.SetOptionValue(nOption,OStringToOUString(aValue, osl_getThreadTextEncoding()) );
+ }
+ else if (aValue.startsWith("NUMERIC="))
+ {
+ aValue = aValue.copy(RTL_CONSTASCII_LENGTH("NUMERIC="));
+
+ sal_Int32 nIndex = 0;
+ int n = 0;
+ do
+ {
+ OString aSub = aValue.getToken(0, ':', nIndex);
+ double fValue=0.0;
+ sscanf(aSub.getStr(), "%lg", &fValue);
+ SetAdjustedNumericalValue(aString.getStr(), fValue, n++);
+ }
+ while ( nIndex >= 0 );
+ }
+ }
+ }
+
+ DisableOption();
+ InitFields();
+
+ return true;
+}
+
+void SaneDlg::SaveState()
+{
+ if( ! Sane::IsSane() )
+ return;
+
+ const char* pEnv = getenv( "HOME" );
+ OUString aFileName;
+
+ if( pEnv )
+ aFileName = OUString::createFromAscii(pEnv) + "/.so_sane_state";
+ else
+ aFileName = OStringToOUString("", osl_getThreadTextEncoding()) + "/.so_sane_state";
+
+ Config aConfig( aFileName );
+ aConfig.DeleteGroup( "SANE" );
+ aConfig.SetGroup( "SANE" );
+ aConfig.WriteKey( "SO_LastSANEDevice",
+ OUStringToOString(mxDeviceBox->get_active_text(), RTL_TEXTENCODING_UTF8) );
+
+ static char const* pSaveOptions[] = {
+ "resolution",
+ "tl-x",
+ "tl-y",
+ "br-x",
+ "br-y"
+ };
+ for(const char * pSaveOption : pSaveOptions)
+ {
+ OString aOption = pSaveOption;
+ int nOption = mrSane.GetOptionByName( pSaveOption );
+ if( nOption > -1 )
+ {
+ SANE_Value_Type nType = mrSane.GetOptionType( nOption );
+ switch( nType )
+ {
+ case SANE_TYPE_BOOL:
+ {
+ bool bValue;
+ if( mrSane.GetOptionValue( nOption, bValue ) )
+ {
+ OString aString = "BOOL=" + OString::number(static_cast<sal_Int32>(bValue));
+ aConfig.WriteKey(aOption, aString);
+ }
+ }
+ break;
+ case SANE_TYPE_STRING:
+ {
+ OString aValue;
+ if( mrSane.GetOptionValue( nOption, aValue ) )
+ {
+ OString aString = "STRING=" + aValue;
+ aConfig.WriteKey( aOption, aString );
+ }
+ }
+ break;
+ case SANE_TYPE_FIXED:
+ case SANE_TYPE_INT:
+ {
+ OStringBuffer aString("NUMERIC=");
+ double fValue;
+ char buf[256];
+ int n;
+
+ for( n = 0; n < mrSane.GetOptionElements( nOption ); n++ )
+ {
+ if( ! mrSane.GetOptionValue( nOption, fValue, n ) )
+ break;
+ if( n > 0 )
+ aString.append(':');
+ sprintf( buf, "%lg", fValue );
+ aString.append(buf);
+ }
+ if( n >= mrSane.GetOptionElements( nOption ) )
+ aConfig.WriteKey( aOption, aString.makeStringAndClear() );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+bool SaneDlg::SetAdjustedNumericalValue(
+ const char* pOption,
+ double fValue,
+ int nElement )
+{
+ if (! Sane::IsSane() || ! mrSane.IsOpen())
+ return false;
+ int const nOption(mrSane.GetOptionByName(pOption));
+ if (nOption == -1)
+ return false;
+
+ if( nElement < 0 || nElement >= mrSane.GetOptionElements( nOption ) )
+ return false;
+
+ std::unique_ptr<double[]> pValues;
+ int nValues;
+ if( ( nValues = mrSane.GetRange( nOption, pValues ) ) < 0 )
+ {
+ return false;
+ }
+
+ SAL_INFO("extensions.scanner", "SaneDlg::SetAdjustedNumericalValue(\"" << pOption << "\", " << fValue << ") ");
+
+ if( nValues )
+ {
+ int nNearest = 0;
+ double fNearest = 1e6;
+ for( int i = 0; i < nValues; i++ )
+ {
+ if( fabs( fValue - pValues[ i ] ) < fNearest )
+ {
+ fNearest = fabs( fValue - pValues[ i ] );
+ nNearest = i;
+ }
+ }
+ fValue = pValues[ nNearest ];
+ }
+ else
+ {
+ if( fValue < pValues[0] )
+ fValue = pValues[0];
+ if( fValue > pValues[1] )
+ fValue = pValues[1];
+ }
+ mrSane.SetOptionValue( nOption, fValue, nElement );
+ SAL_INFO("extensions.scanner", "yields " << fValue);
+
+
+ return true;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/sanedlg.hxx b/extensions/source/scanner/sanedlg.hxx
new file mode 100644
index 000000000..e9ba1540d
--- /dev/null
+++ b/extensions/source/scanner/sanedlg.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/customweld.hxx>
+#include <vcl/weld.hxx>
+
+#include "sane.hxx"
+
+class ScanPreview;
+
+class SaneDlg : public weld::GenericDialogController
+{
+private:
+ weld::Window* mpParent;
+ Sane& mrSane;
+ bool mbScanEnabled;
+
+ Link<Sane&,void> maOldLink;
+
+ int mnCurrentOption;
+ int mnCurrentElement;
+ std::unique_ptr<double[]> mpRange;
+ double mfMin, mfMax;
+
+ bool doScan;
+
+ std::unique_ptr<weld::Button> mxCancelButton;
+ std::unique_ptr<weld::Button> mxDeviceInfoButton;
+ std::unique_ptr<weld::Button> mxPreviewButton;
+ std::unique_ptr<weld::Button> mxScanButton;
+ std::unique_ptr<weld::Button> mxButtonOption;
+
+ std::unique_ptr<weld::Label> mxOptionTitle;
+ std::unique_ptr<weld::Label> mxOptionDescTxt;
+ std::unique_ptr<weld::Label> mxVectorTxt;
+
+ std::unique_ptr<weld::MetricSpinButton> mxLeftField;
+ std::unique_ptr<weld::MetricSpinButton> mxTopField;
+ std::unique_ptr<weld::MetricSpinButton> mxRightField;
+ std::unique_ptr<weld::MetricSpinButton> mxBottomField;
+
+ std::unique_ptr<weld::ComboBox> mxDeviceBox;
+ std::unique_ptr<weld::ComboBox> mxReslBox;
+ std::unique_ptr<weld::CheckButton> mxAdvancedBox;
+
+ std::unique_ptr<weld::SpinButton> mxVectorBox;
+ std::unique_ptr<weld::ComboBox> mxQuantumRangeBox;
+ std::unique_ptr<weld::ComboBox> mxStringRangeBox;
+
+ std::unique_ptr<weld::CheckButton> mxBoolCheckBox;
+
+ std::unique_ptr<weld::Entry> mxStringEdit;
+ std::unique_ptr<weld::Entry> mxNumericEdit;
+
+ std::unique_ptr<weld::TreeView> mxOptionBox;
+
+ std::unique_ptr<ScanPreview> mxPreview;
+ std::unique_ptr<weld::CustomWeld> mxPreviewWnd;
+
+ DECL_LINK( ClickBtnHdl, weld::Button&, void );
+ DECL_LINK( ToggleBtnHdl, weld::Toggleable&, void );
+ DECL_LINK( SelectHdl, weld::ComboBox&, void );
+ DECL_LINK( ModifyHdl, weld::Entry&, void );
+ DECL_LINK( MetricValueModifyHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( ValueModifyHdl, weld::ComboBox&, void );
+ DECL_LINK( ReloadSaneOptionsHdl, Sane&, void );
+ DECL_LINK( OptionsBoxSelectHdl, weld::TreeView&, void );
+
+ void SaveState();
+ bool LoadState();
+
+ void InitDevices();
+ void InitFields();
+ void AcquirePreview();
+ void DisableOption();
+ void EstablishBoolOption();
+ void EstablishStringOption();
+ void EstablishStringRange();
+ void EstablishQuantumRange();
+ void EstablishNumericOption();
+ void EstablishButtonOption();
+
+ // helper
+ bool SetAdjustedNumericalValue( const char* pOption, double fValue, int nElement = 0 );
+public:
+ SaneDlg(weld::Window*, Sane&, bool);
+ virtual ~SaneDlg() override;
+
+ virtual short run() override;
+ void UpdateScanArea( bool );
+ bool getDoScan() const { return doScan;}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/scanner.cxx b/extensions/source/scanner/scanner.cxx
new file mode 100644
index 000000000..b661a4f7e
--- /dev/null
+++ b/extensions/source/scanner/scanner.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 "scanner.hxx"
+
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+Reference< XInterface > ScannerManager_CreateInstance( const Reference< css::lang::XMultiServiceFactory >& /*rxFactory*/ )
+{
+ return *( new ScannerManager() );
+}
+
+
+ScannerManager::ScannerManager() :
+ mpData( nullptr )
+{
+ AcquireData();
+}
+
+
+ScannerManager::~ScannerManager()
+{
+ ReleaseData();
+}
+
+
+Sequence< sal_Int8 > SAL_CALL ScannerManager::getMaskDIB()
+{
+ return Sequence< sal_Int8 >();
+}
+
+
+OUString ScannerManager::getImplementationName()
+{
+ return "com.sun.star.scanner.ScannerManager";
+}
+
+
+sal_Bool ScannerManager::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+
+css::uno::Sequence<OUString> ScannerManager::getSupportedServiceNames()
+{
+ return { "com.sun.star.scanner.ScannerManager" };
+}
+
+
+sal_Bool SAL_CALL ScannerManager::configureScanner( ScannerContext& rContext )
+{
+ return configureScannerAndScan( rContext, nullptr );
+}
+
+void SAL_CALL ScannerManager::initialize(const css::uno::Sequence<css::uno::Any>& rArguments)
+{
+ ::comphelper::NamedValueCollection aProperties(rArguments);
+ if (aProperties.has("ParentWindow"))
+ aProperties.get("ParentWindow") >>= mxDialogParent;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_ScannerManager_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ScannerManager());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/scanner.hxx b/extensions/source/scanner/scanner.hxx
new file mode 100644
index 000000000..42e03a453
--- /dev/null
+++ b/extensions/source/scanner/scanner.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/scanner/XScannerManager2.hpp>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::scanner;
+
+class ScannerManager final :
+ public cppu::WeakImplHelper<
+ XScannerManager2, css::awt::XBitmap, css::lang::XServiceInfo, css::lang::XInitialization>
+{
+ osl::Mutex maProtector;
+ css::uno::Reference<css::awt::XWindow> mxDialogParent;
+ void* mpData;
+
+ static void AcquireData();
+ void ReleaseData();
+
+public:
+
+ ScannerManager();
+ virtual ~ScannerManager() override;
+
+ // XScannerManager
+ virtual Sequence< ScannerContext > SAL_CALL getAvailableScanners() override;
+ virtual sal_Bool SAL_CALL configureScanner( ScannerContext& scanner_context ) override;
+ virtual sal_Bool SAL_CALL configureScannerAndScan( ScannerContext& scanner_context, const Reference< css::lang::XEventListener >& rxListener ) override;
+ virtual void SAL_CALL startScan( const ScannerContext& scanner_context, const Reference< css::lang::XEventListener >& rxListener ) override;
+ virtual ScanError SAL_CALL getError( const ScannerContext& scanner_context ) override;
+ virtual Reference< css::awt::XBitmap > SAL_CALL getBitmap( const ScannerContext& scanner_context ) override;
+
+ // XBitmap
+ virtual css::awt::Size SAL_CALL getSize() override;
+ virtual Sequence< sal_Int8 > SAL_CALL getDIB() override;
+ virtual Sequence< sal_Int8 > SAL_CALL getMaskDIB() override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override;
+
+#ifdef _WIN32
+ void* GetData() const { return mpData; }
+#endif
+ void SetData( void* pData ) { ReleaseData(); mpData = pData; }
+ };
+
+/// @throws Exception
+Reference< XInterface > ScannerManager_CreateInstance( const Reference< css::lang::XMultiServiceFactory >& rxFactory );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/scanunx.cxx b/extensions/source/scanner/scanunx.cxx
new file mode 100644
index 000000000..b06e30a27
--- /dev/null
+++ b/extensions/source/scanner/scanunx.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 "scanner.hxx"
+#include "sanedlg.hxx"
+#include <o3tl/safeint.hxx>
+#include <osl/thread.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <memory>
+
+#include <com/sun/star/scanner/ScannerException.hpp>
+
+BitmapTransporter::BitmapTransporter()
+{
+ SAL_INFO("extensions.scanner", "BitmapTransporter");
+}
+
+
+BitmapTransporter::~BitmapTransporter()
+{
+ SAL_INFO("extensions.scanner", "~BitmapTransporter");
+}
+
+
+css::awt::Size BitmapTransporter::getSize()
+{
+ osl::MutexGuard aGuard( m_aProtector );
+ css::awt::Size aRet;
+
+ // ensure that there is at least a header
+ int nLen = m_aStream.TellEnd();
+ if( nLen > 15 )
+ {
+ int nPreviousPos = m_aStream.Tell();
+ m_aStream.Seek( 4 );
+ m_aStream.ReadInt32( aRet.Width ).ReadInt32( aRet.Height );
+ m_aStream.Seek( nPreviousPos );
+ }
+ else
+ aRet.Width = aRet.Height = 0;
+
+
+ return aRet;
+}
+
+
+Sequence< sal_Int8 > BitmapTransporter::getDIB()
+{
+ osl::MutexGuard aGuard( m_aProtector );
+ int nPreviousPos = m_aStream.Tell();
+
+ // create return value
+ int nBytes = m_aStream.TellEnd();
+ m_aStream.Seek( 0 );
+
+ Sequence< sal_Int8 > aValue( nBytes );
+ m_aStream.ReadBytes( aValue.getArray(), nBytes );
+ m_aStream.Seek( nPreviousPos );
+
+ return aValue;
+}
+
+namespace {
+
+struct SaneHolder
+{
+ Sane m_aSane;
+ Reference< css::awt::XBitmap > m_xBitmap;
+ osl::Mutex m_aProtector;
+ ScanError m_nError;
+ bool m_bBusy;
+
+ SaneHolder() : m_nError(ScanError_ScanErrorNone), m_bBusy(false) {}
+};
+
+ typedef std::vector< std::shared_ptr<SaneHolder> > sanevec;
+ class allSanes
+ {
+ private:
+ int mnRefCount;
+ public:
+ sanevec m_aSanes;
+ allSanes() : mnRefCount(0) {}
+ void acquire();
+ void release();
+ };
+
+ void allSanes::acquire()
+ {
+ ++mnRefCount;
+ }
+
+ void allSanes::release()
+ {
+ // was unused, now because of i99835: "Scanning interface not SANE API
+ // compliant" destroy all SaneHolder to get Sane Dtor called
+ --mnRefCount;
+ if (!mnRefCount)
+ m_aSanes.clear();
+ }
+
+ struct theSaneProtector : public rtl::Static<osl::Mutex, theSaneProtector> {};
+ struct theSanes : public rtl::Static<allSanes, theSanes> {};
+
+class ScannerThread : public osl::Thread
+{
+ std::shared_ptr<SaneHolder> m_pHolder;
+ Reference< css::lang::XEventListener > m_xListener;
+ ScannerManager* m_pManager; // just for the disposing call
+
+public:
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override { delete this; }
+public:
+ ScannerThread( const std::shared_ptr<SaneHolder>& pHolder,
+ const Reference< css::lang::XEventListener >& listener,
+ ScannerManager* pManager );
+ virtual ~ScannerThread() override;
+};
+
+}
+
+ScannerThread::ScannerThread(const std::shared_ptr<SaneHolder>& pHolder,
+ const Reference< css::lang::XEventListener >& listener,
+ ScannerManager* pManager)
+ : m_pHolder( pHolder ), m_xListener( listener ), m_pManager( pManager )
+{
+ SAL_INFO("extensions.scanner", "ScannerThread");
+}
+
+
+ScannerThread::~ScannerThread()
+{
+ SAL_INFO("extensions.scanner", "~ScannerThread");
+}
+
+
+void ScannerThread::run()
+{
+ osl_setThreadName("ScannerThread");
+
+ osl::MutexGuard aGuard( m_pHolder->m_aProtector );
+ rtl::Reference<BitmapTransporter> pTransporter = new BitmapTransporter;
+
+ m_pHolder->m_xBitmap = pTransporter;
+
+ m_pHolder->m_bBusy = true;
+ if( m_pHolder->m_aSane.IsOpen() )
+ {
+ int nOption = m_pHolder->m_aSane.GetOptionByName( "preview" );
+ if( nOption != -1 )
+ m_pHolder->m_aSane.SetOptionValue( nOption, false );
+
+ m_pHolder->m_nError =
+ m_pHolder->m_aSane.Start( *pTransporter ) ?
+ ScanError_ScanErrorNone : ScanError_ScanCanceled;
+ }
+ else
+ m_pHolder->m_nError = ScanError_ScannerNotAvailable;
+
+
+ Reference< XInterface > xXInterface( static_cast< OWeakObject* >( m_pManager ) );
+ m_xListener->disposing( css::lang::EventObject(xXInterface) );
+ m_pHolder->m_bBusy = false;
+}
+
+
+void ScannerManager::AcquireData()
+{
+ osl::MutexGuard aGuard( theSaneProtector::get() );
+ theSanes::get().acquire();
+}
+
+
+void ScannerManager::ReleaseData()
+{
+ osl::MutexGuard aGuard( theSaneProtector::get() );
+ theSanes::get().release();
+}
+
+
+css::awt::Size ScannerManager::getSize()
+{
+ css::awt::Size aRet;
+ aRet.Width = aRet.Height = 0;
+ return aRet;
+}
+
+
+Sequence< sal_Int8 > ScannerManager::getDIB()
+{
+ return Sequence< sal_Int8 >();
+}
+
+
+Sequence< ScannerContext > ScannerManager::getAvailableScanners()
+{
+ osl::MutexGuard aGuard( theSaneProtector::get() );
+ sanevec &rSanes = theSanes::get().m_aSanes;
+
+ if( rSanes.empty() )
+ {
+ auto pSaneHolder = std::make_shared<SaneHolder>();
+ if( Sane::IsSane() )
+ rSanes.push_back( pSaneHolder );
+ }
+
+ if( Sane::IsSane() )
+ {
+ Sequence< ScannerContext > aRet{ { /* ScannerName */ "SANE", /* InternalData */ 0 } };
+ return aRet;
+ }
+
+ return Sequence< ScannerContext >();
+}
+
+
+sal_Bool ScannerManager::configureScannerAndScan( ScannerContext& scanner_context,
+ const Reference< css::lang::XEventListener >& listener )
+{
+ bool bRet;
+ bool bScan;
+ {
+ osl::MutexGuard aGuard( theSaneProtector::get() );
+ sanevec &rSanes = theSanes::get().m_aSanes;
+
+ SAL_INFO("extensions.scanner", "ScannerManager::configureScanner");
+
+ if( scanner_context.InternalData < 0 || o3tl::make_unsigned(scanner_context.InternalData) >= rSanes.size() )
+ throw ScannerException(
+ "Scanner does not exist",
+ Reference< XScannerManager >( this ),
+ ScanError_InvalidContext
+ );
+
+ std::shared_ptr<SaneHolder> pHolder = rSanes[scanner_context.InternalData];
+ if( pHolder->m_bBusy )
+ throw ScannerException(
+ "Scanner is busy",
+ Reference< XScannerManager >( this ),
+ ScanError_ScanInProgress
+ );
+
+ pHolder->m_bBusy = true;
+ SaneDlg aDlg(Application::GetFrameWeld(mxDialogParent), pHolder->m_aSane, listener.is());
+ bRet = aDlg.run();
+ bScan = aDlg.getDoScan();
+ pHolder->m_bBusy = false;
+ }
+ if ( bScan )
+ startScan( scanner_context, listener );
+
+ return bRet;
+}
+
+
+void ScannerManager::startScan( const ScannerContext& scanner_context,
+ const Reference< css::lang::XEventListener >& listener )
+{
+ osl::MutexGuard aGuard( theSaneProtector::get() );
+ sanevec &rSanes = theSanes::get().m_aSanes;
+
+ SAL_INFO("extensions.scanner", "ScannerManager::startScan");
+
+ if( scanner_context.InternalData < 0 || o3tl::make_unsigned(scanner_context.InternalData) >= rSanes.size() )
+ throw ScannerException(
+ "Scanner does not exist",
+ Reference< XScannerManager >( this ),
+ ScanError_InvalidContext
+ );
+ std::shared_ptr<SaneHolder> pHolder = rSanes[scanner_context.InternalData];
+ if( pHolder->m_bBusy )
+ throw ScannerException(
+ "Scanner is busy",
+ Reference< XScannerManager >( this ),
+ ScanError_ScanInProgress
+ );
+ pHolder->m_bBusy = true;
+
+ ScannerThread* pThread = new ScannerThread( pHolder, listener, this );
+ pThread->create();
+}
+
+
+ScanError ScannerManager::getError( const ScannerContext& scanner_context )
+{
+ osl::MutexGuard aGuard( theSaneProtector::get() );
+ sanevec &rSanes = theSanes::get().m_aSanes;
+
+ if( scanner_context.InternalData < 0 || o3tl::make_unsigned(scanner_context.InternalData) >= rSanes.size() )
+ throw ScannerException(
+ "Scanner does not exist",
+ Reference< XScannerManager >( this ),
+ ScanError_InvalidContext
+ );
+
+ std::shared_ptr<SaneHolder> pHolder = rSanes[scanner_context.InternalData];
+
+ return pHolder->m_nError;
+}
+
+
+Reference< css::awt::XBitmap > ScannerManager::getBitmap( const ScannerContext& scanner_context )
+{
+ osl::MutexGuard aGuard( theSaneProtector::get() );
+ sanevec &rSanes = theSanes::get().m_aSanes;
+
+ if( scanner_context.InternalData < 0 || o3tl::make_unsigned(scanner_context.InternalData) >= rSanes.size() )
+ throw ScannerException(
+ "Scanner does not exist",
+ Reference< XScannerManager >( this ),
+ ScanError_InvalidContext
+ );
+ std::shared_ptr<SaneHolder> pHolder = rSanes[scanner_context.InternalData];
+
+ osl::MutexGuard aProtGuard( pHolder->m_aProtector );
+
+ Reference< css::awt::XBitmap > xRet( pHolder->m_xBitmap );
+ pHolder->m_xBitmap.clear();
+
+ return xRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/scanwin.cxx b/extensions/source/scanner/scanwin.cxx
new file mode 100644
index 000000000..109f2944a
--- /dev/null
+++ b/extensions/source/scanner/scanwin.cxx
@@ -0,0 +1,646 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/scanner/ScannerException.hpp>
+
+#include "twain32shim.hxx"
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <config_folders.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <osl/conditn.hxx>
+#include <osl/file.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/bootstrap.hxx>
+#include <salhelper/thread.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/stream.hxx>
+#include <tools/helpers.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include "scanner.hxx"
+
+namespace
+{
+enum TwainState
+{
+ TWAIN_STATE_NONE = 0,
+ TWAIN_STATE_SCANNING = 1,
+ TWAIN_STATE_DONE = 2,
+ TWAIN_STATE_CANCELED = 3
+};
+
+struct HANDLEDeleter
+{
+ using pointer = HANDLE;
+ void operator()(HANDLE h) { CloseHandle(h); }
+};
+
+using ScopedHANDLE = std::unique_ptr<HANDLE, HANDLEDeleter>;
+
+class Twain
+{
+public:
+ Twain();
+ ~Twain();
+
+ bool SelectSource(ScannerManager& rMgr, const VclPtr<vcl::Window>& xTopWindow);
+ bool PerformTransfer(ScannerManager& rMgr,
+ const css::uno::Reference<css::lang::XEventListener>& rxListener,
+ const VclPtr<vcl::Window>& xTopWindow);
+ void WaitReadyForNextTask();
+
+ TwainState GetState() const { return meState; }
+
+private:
+ friend class ShimListenerThread;
+ class ShimListenerThread : public salhelper::Thread
+ {
+ public:
+ ShimListenerThread(const VclPtr<vcl::Window>& xTopWindow);
+ ~ShimListenerThread() override;
+ void execute() override;
+ const OUString& getError() { return msErrorReported; }
+
+ // These methods are executed outside of own thread
+ bool WaitInitialization();
+ bool WaitRequestResult();
+ void DontNotify() { mbDontNotify = true; }
+ void RequestDestroy();
+ bool RequestSelectSource();
+ bool RequestInitXfer();
+
+ private:
+ VclPtr<vcl::Window> mxTopWindow; // the window that we made modal
+ bool mbDontNotify = false;
+ HWND mhWndShim = nullptr; // shim main window handle
+ OUString msErrorReported;
+ osl::Condition mcInitCompleted; // initially not set
+ bool mbInitSucceeded = false;
+ osl::Condition mcGotRequestResult;
+ bool mbRequestResult = false;
+
+ void SendShimRequest(WPARAM nRequest);
+ bool SendShimRequestWithResult(WPARAM nRequest);
+ void NotificationHdl(WPARAM nEvent, LPARAM lParam);
+ void NotifyOwner(WPARAM nEvent);
+ void NotifyXFerOwner(LPARAM nHandle);
+ };
+ css::uno::Reference<css::lang::XEventListener> mxListener;
+ css::uno::Reference<css::scanner::XScannerManager> mxMgr;
+ ScannerManager* mpCurMgr = nullptr;
+ TwainState meState = TWAIN_STATE_NONE;
+ rtl::Reference<ShimListenerThread> mpThread;
+ osl::Mutex maMutex;
+
+ DECL_LINK(ImpNotifyHdl, void*, void);
+ DECL_LINK(ImpNotifyXferHdl, void*, void);
+ void Notify(WPARAM nEvent); // called by shim communication thread to notify me
+ void NotifyXFer(LPARAM nHandle); // called by shim communication thread to notify me
+
+ bool InitializeNewShim(ScannerManager& rMgr, const VclPtr<vcl::Window>& xTopWindow);
+
+ void Reset(); // cleanup thread and manager
+};
+
+Twain aTwain;
+
+Twain::ShimListenerThread::ShimListenerThread(const VclPtr<vcl::Window>& xTopWindow)
+ : salhelper::Thread("TWAINShimListenerThread")
+ , mxTopWindow(xTopWindow)
+{
+ if (mxTopWindow)
+ {
+ mxTopWindow->IncModalCount(); // the operation is modal to the frame
+ }
+}
+
+Twain::ShimListenerThread::~ShimListenerThread()
+{
+ if (mxTopWindow)
+ {
+ mxTopWindow->DecModalCount(); // unblock the frame
+ }
+}
+
+bool Twain::ShimListenerThread::WaitInitialization()
+{
+ mcInitCompleted.wait();
+ return mbInitSucceeded;
+}
+
+bool Twain::ShimListenerThread::WaitRequestResult()
+{
+ mcGotRequestResult.wait();
+ return mbRequestResult;
+}
+
+void Twain::ShimListenerThread::SendShimRequest(WPARAM nRequest)
+{
+ if (mhWndShim)
+ PostMessageW(mhWndShim, WM_TWAIN_REQUEST, nRequest, 0);
+}
+
+bool Twain::ShimListenerThread::SendShimRequestWithResult(WPARAM nRequest)
+{
+ mcGotRequestResult.reset();
+ mbRequestResult = false;
+ SendShimRequest(nRequest);
+ return WaitRequestResult();
+}
+
+void Twain::ShimListenerThread::RequestDestroy() { SendShimRequest(TWAIN_REQUEST_QUIT); }
+
+bool Twain::ShimListenerThread::RequestSelectSource()
+{
+ assert(mbInitSucceeded);
+ return SendShimRequestWithResult(TWAIN_REQUEST_SELECTSOURCE);
+}
+
+bool Twain::ShimListenerThread::RequestInitXfer()
+{
+ assert(mbInitSucceeded);
+ return SendShimRequestWithResult(TWAIN_REQUEST_INITXFER);
+}
+
+void Twain::ShimListenerThread::NotifyOwner(WPARAM nEvent)
+{
+ if (!mbDontNotify)
+ aTwain.Notify(nEvent);
+}
+
+void Twain::ShimListenerThread::NotifyXFerOwner(LPARAM nHandle)
+{
+ if (!mbDontNotify)
+ aTwain.NotifyXFer(nHandle);
+}
+
+// May only be called from the own thread, so no threading issues modifying self
+void Twain::ShimListenerThread::NotificationHdl(WPARAM nEvent, LPARAM lParam)
+{
+ switch (nEvent)
+ {
+ case TWAIN_EVENT_NOTIFYHWND: // shim reported its main HWND for communications
+ if (!mcInitCompleted.check()) // only if not yet initialized!
+ {
+ // Owner is still waiting mcInitCompleted in its Twain::InitializeNewShim,
+ // holding its access mutex
+ mhWndShim = reinterpret_cast<HWND>(lParam);
+
+ mbInitSucceeded = lParam != 0;
+ mcInitCompleted.set();
+ }
+ break;
+ case TWAIN_EVENT_SCANNING:
+ NotifyOwner(nEvent);
+ break;
+ case TWAIN_EVENT_XFER:
+ NotifyXFerOwner(lParam);
+ break;
+ case TWAIN_EVENT_REQUESTRESULT:
+ mbRequestResult = lParam;
+ mcGotRequestResult.set();
+ break;
+ // We don't handle TWAIN_EVENT_QUIT notification from shim, because we send it ourselves
+ // in the end of execute()
+ }
+}
+
+// Spawn a separate 32-bit process to use TWAIN on Windows, and listen for its notifications
+void Twain::ShimListenerThread::execute()
+{
+ MSG msg;
+ // Initialize thread message queue before launching shim process
+ PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
+
+ try
+ {
+ ScopedHANDLE hShimProcess;
+ {
+ // Determine twain32shim executable URL:
+ OUString shimURL("$BRAND_BASE_DIR/" LIBO_BIN_FOLDER "/twain32shim.exe");
+ rtl::Bootstrap::expandMacros(shimURL);
+
+ OUString sCmdLine;
+ if (osl::FileBase::getSystemPathFromFileURL(shimURL, sCmdLine) != osl::FileBase::E_None)
+ throw std::exception("getSystemPathFromFileURL failed!");
+
+ HANDLE hDup;
+ if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
+ &hDup, SYNCHRONIZE | THREAD_QUERY_LIMITED_INFORMATION, TRUE, 0))
+ ThrowLastError("DuplicateHandle");
+ // we will not need our copy as soon as shim has its own inherited one
+ ScopedHANDLE hScopedDup(hDup);
+ DWORD nDup = static_cast<DWORD>(reinterpret_cast<sal_uIntPtr>(hDup));
+ if (reinterpret_cast<HANDLE>(nDup) != hDup)
+ throw std::exception("HANDLE does not fit to 32 bit - cannot pass to shim!");
+
+ // Send this thread handle as the first parameter
+ sCmdLine = "\"" + sCmdLine + "\" " + OUString::number(nDup);
+
+ // We need a WinAPI HANDLE of the process to be able to wait on it and detect the process
+ // termination; so use WinAPI to start the process, not osl_executeProcess.
+
+ STARTUPINFOW si{};
+ si.cb = sizeof(si);
+ PROCESS_INFORMATION pi;
+
+ if (!CreateProcessW(nullptr, const_cast<LPWSTR>(o3tl::toW(sCmdLine.getStr())), nullptr,
+ nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi))
+ ThrowLastError("CreateProcessW");
+
+ CloseHandle(pi.hThread);
+ hShimProcess.reset(pi.hProcess);
+ }
+ HANDLE h = hShimProcess.get();
+ while (true)
+ {
+ DWORD nWaitResult = MsgWaitForMultipleObjects(1, &h, FALSE, INFINITE, QS_POSTMESSAGE);
+ // Process any messages in queue before checking if we need to break, to not loose
+ // possible pending notifications
+ while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
+ {
+ // process it here
+ if (msg.message == WM_TWAIN_EVENT)
+ {
+ NotificationHdl(msg.wParam, msg.lParam);
+ }
+ }
+ if (nWaitResult == WAIT_OBJECT_0)
+ {
+ // shim process exited - return
+ break;
+ }
+ if (nWaitResult == WAIT_FAILED)
+ {
+ // Some Win32 error - report and return
+ ThrowLastError("MsgWaitForMultipleObjects");
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
+ msErrorReported = OUString(e.what(), strlen(e.what()), RTL_TEXTENCODING_UTF8);
+ // allow owner to resume (in case the condition isn't set yet)
+ mcInitCompleted.set(); // let mbInitSucceeded keep its (maybe false) value!
+ }
+ // allow owner to resume (in case the conditions isn't set yet)
+ mcGotRequestResult.set();
+ NotifyOwner(TWAIN_EVENT_QUIT);
+}
+
+Twain::Twain() {}
+
+Twain::~Twain()
+{
+ osl::MutexGuard aGuard(maMutex);
+ if (mpThread)
+ {
+ mpThread->DontNotify();
+ mpThread->RequestDestroy();
+ mpThread->join();
+ mpThread.clear();
+ }
+}
+
+void Twain::Reset()
+{
+ mpThread->join();
+ if (!mpThread->getError().isEmpty())
+ SAL_WARN("extensions.scanner", mpThread->getError());
+ mpThread.clear();
+ mpCurMgr = nullptr;
+ mxMgr.clear();
+}
+
+bool Twain::InitializeNewShim(ScannerManager& rMgr, const VclPtr<vcl::Window>& xTopWindow)
+{
+ osl::MutexGuard aGuard(maMutex);
+ if (mpThread)
+ return false; // Have a shim for another task already!
+
+ // hold reference to ScannerManager, to prevent premature death
+ mxMgr = mpCurMgr = &rMgr;
+
+ mpThread.set(new ShimListenerThread(xTopWindow));
+ mpThread->launch();
+ const bool bSuccess = mpThread->WaitInitialization();
+ if (!bSuccess)
+ Reset();
+
+ return bSuccess;
+}
+
+void Twain::Notify(WPARAM nEvent)
+{
+ Application::PostUserEvent(LINK(this, Twain, ImpNotifyHdl), reinterpret_cast<void*>(nEvent));
+}
+
+void Twain::NotifyXFer(LPARAM nHandle)
+{
+ Application::PostUserEvent(LINK(this, Twain, ImpNotifyXferHdl),
+ reinterpret_cast<void*>(nHandle));
+}
+
+bool Twain::SelectSource(ScannerManager& rMgr, const VclPtr<vcl::Window>& xTopWindow)
+{
+ osl::MutexGuard aGuard(maMutex);
+ bool bRet = false;
+
+ if (InitializeNewShim(rMgr, xTopWindow))
+ {
+ meState = TWAIN_STATE_NONE;
+ bRet = mpThread->RequestSelectSource();
+ }
+
+ return bRet;
+}
+
+bool Twain::PerformTransfer(ScannerManager& rMgr,
+ const css::uno::Reference<css::lang::XEventListener>& rxListener,
+ const VclPtr<vcl::Window>& xTopWindow)
+{
+ osl::MutexGuard aGuard(maMutex);
+ bool bRet = false;
+
+ if (InitializeNewShim(rMgr, xTopWindow))
+ {
+ mxListener = rxListener;
+ meState = TWAIN_STATE_NONE;
+ bRet = mpThread->RequestInitXfer();
+ }
+
+ return bRet;
+}
+
+void Twain::WaitReadyForNextTask()
+{
+ while ([&]() {
+ osl::MutexGuard aGuard(maMutex);
+ return bool(mpThread);
+ }())
+ {
+ Application::Reschedule(true);
+ }
+}
+
+IMPL_LINK(Twain, ImpNotifyHdl, void*, pParam, void)
+{
+ osl::MutexGuard aGuard(maMutex);
+ WPARAM nEvent = reinterpret_cast<WPARAM>(pParam);
+ switch (nEvent)
+ {
+ case TWAIN_EVENT_SCANNING:
+ meState = TWAIN_STATE_SCANNING;
+ break;
+
+ case TWAIN_EVENT_QUIT:
+ {
+ if (meState != TWAIN_STATE_DONE)
+ meState = TWAIN_STATE_CANCELED;
+
+ css::lang::EventObject event(mxMgr); // mxMgr will be cleared below
+
+ if (mpThread)
+ Reset();
+
+ if (mxListener.is())
+ {
+ mxListener->disposing(event);
+ mxListener.clear();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+IMPL_LINK(Twain, ImpNotifyXferHdl, void*, pParam, void)
+{
+ osl::MutexGuard aGuard(maMutex);
+ if (mpThread)
+ {
+ mpCurMgr->SetData(pParam);
+ meState = pParam ? TWAIN_STATE_DONE : TWAIN_STATE_CANCELED;
+
+ css::lang::EventObject event(mxMgr); // mxMgr will be cleared below
+
+ Reset();
+
+ if (mxListener.is())
+ mxListener->disposing(css::lang::EventObject(mxMgr));
+ }
+
+ mxListener.clear();
+}
+
+VclPtr<vcl::Window> ImplGetActiveFrameWindow()
+{
+ try
+ {
+ // query desktop instance
+ css::uno::Reference<css::frame::XDesktop2> xDesktop
+ = css::frame::Desktop::create(comphelper::getProcessComponentContext());
+ if (css::uno::Reference<css::frame::XFrame> xActiveFrame = xDesktop->getActiveFrame())
+ return VCLUnoHelper::GetWindow(xActiveFrame->getComponentWindow());
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ SAL_WARN("extensions.scanner", "ImplGetActiveFrame: Could not determine active frame!");
+ return nullptr;
+}
+
+} // namespace
+
+void ScannerManager::AcquireData() {}
+
+void ScannerManager::ReleaseData()
+{
+ if (mpData)
+ {
+ CloseHandle(static_cast<HANDLE>(mpData));
+ mpData = nullptr;
+ }
+}
+
+css::awt::Size ScannerManager::getSize()
+{
+ css::awt::Size aRet;
+
+ if (mpData)
+ {
+ HANDLE hMap = static_cast<HANDLE>(mpData);
+ // map full size
+ const sal_Int8* pMap = static_cast<sal_Int8*>(MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0));
+ if (pMap)
+ {
+ const BITMAPINFOHEADER* pBIH = reinterpret_cast<const BITMAPINFOHEADER*>(pMap + 4);
+ aRet.Width = pBIH->biWidth;
+ aRet.Height = pBIH->biHeight;
+
+ UnmapViewOfFile(pMap);
+ }
+ }
+
+ return aRet;
+}
+
+css::uno::Sequence<sal_Int8> ScannerManager::getDIB()
+{
+ css::uno::Sequence<sal_Int8> aRet;
+
+ if (mpData)
+ {
+ HANDLE hMap = static_cast<HANDLE>(mpData);
+ // map full size
+ const sal_Int8* pMap = static_cast<sal_Int8*>(MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0));
+ if (pMap)
+ {
+ DWORD nDIBSize;
+ memcpy(&nDIBSize, pMap, 4); // size of the following DIB
+
+ const BITMAPINFOHEADER* pBIH = reinterpret_cast<const BITMAPINFOHEADER*>(pMap + 4);
+
+ sal_uInt32 nColEntries = 0;
+
+ switch (pBIH->biBitCount)
+ {
+ case 1:
+ case 4:
+ case 8:
+ nColEntries = pBIH->biClrUsed ? pBIH->biClrUsed : (1 << pBIH->biBitCount);
+ break;
+
+ case 24:
+ nColEntries = pBIH->biClrUsed ? pBIH->biClrUsed : 0;
+ break;
+
+ case 16:
+ case 32:
+ nColEntries = pBIH->biClrUsed;
+ if (pBIH->biCompression == BI_BITFIELDS)
+ nColEntries += 3;
+ break;
+ }
+
+ aRet = css::uno::Sequence<sal_Int8>(sizeof(BITMAPFILEHEADER) + nDIBSize);
+
+ sal_Int8* pBuf = aRet.getArray();
+ SvMemoryStream* pMemStm
+ = new SvMemoryStream(pBuf, sizeof(BITMAPFILEHEADER), StreamMode::WRITE);
+
+ pMemStm->WriteChar('B').WriteChar('M').WriteUInt32(0).WriteUInt32(0);
+ pMemStm->WriteUInt32(sizeof(BITMAPFILEHEADER) + pBIH->biSize
+ + (nColEntries * sizeof(RGBQUAD)));
+
+ delete pMemStm;
+ memcpy(pBuf + sizeof(BITMAPFILEHEADER), pBIH, nDIBSize);
+
+ UnmapViewOfFile(pMap);
+ }
+
+ ReleaseData();
+ }
+
+ return aRet;
+}
+
+css::uno::Sequence<ScannerContext> SAL_CALL ScannerManager::getAvailableScanners()
+{
+ osl::MutexGuard aGuard(maProtector);
+ css::uno::Sequence<ScannerContext> aRet(1);
+
+ aRet.getArray()[0].ScannerName = "TWAIN";
+ aRet.getArray()[0].InternalData = 0;
+
+ return aRet;
+}
+
+sal_Bool SAL_CALL ScannerManager::configureScannerAndScan(
+ ScannerContext& rContext, const css::uno::Reference<css::lang::XEventListener>& rxListener)
+{
+ osl::MutexGuard aGuard(maProtector);
+ css::uno::Reference<XScannerManager> xThis(this);
+
+ if (rContext.InternalData != 0 || rContext.ScannerName != "TWAIN")
+ throw ScannerException("Scanner does not exist", xThis, ScanError_InvalidContext);
+
+ ReleaseData();
+
+ VclPtr<vcl::Window> xTopWindow = ImplGetActiveFrameWindow();
+ if (xTopWindow)
+ xTopWindow
+ ->IncModalCount(); // to avoid changes between the two operations that each block the window
+ comphelper::ScopeGuard aModalGuard([xTopWindow]() {
+ if (xTopWindow)
+ xTopWindow->DecModalCount();
+ });
+
+ const bool bSelected = aTwain.SelectSource(*this, xTopWindow);
+ if (bSelected)
+ {
+ aTwain.WaitReadyForNextTask();
+ aTwain.PerformTransfer(*this, rxListener, xTopWindow);
+ }
+ return bSelected;
+}
+
+void SAL_CALL
+ScannerManager::startScan(const ScannerContext& rContext,
+ const css::uno::Reference<css::lang::XEventListener>& rxListener)
+{
+ osl::MutexGuard aGuard(maProtector);
+ css::uno::Reference<XScannerManager> xThis(this);
+
+ if (rContext.InternalData != 0 || rContext.ScannerName != "TWAIN")
+ throw ScannerException("Scanner does not exist", xThis, ScanError_InvalidContext);
+
+ ReleaseData();
+ aTwain.PerformTransfer(*this, rxListener, ImplGetActiveFrameWindow());
+}
+
+ScanError SAL_CALL ScannerManager::getError(const ScannerContext& rContext)
+{
+ osl::MutexGuard aGuard(maProtector);
+ css::uno::Reference<XScannerManager> xThis(this);
+
+ if (rContext.InternalData != 0 || rContext.ScannerName != "TWAIN")
+ throw ScannerException("Scanner does not exist", xThis, ScanError_InvalidContext);
+
+ return ((aTwain.GetState() == TWAIN_STATE_CANCELED) ? ScanError_ScanCanceled
+ : ScanError_ScanErrorNone);
+}
+
+css::uno::Reference<css::awt::XBitmap>
+ SAL_CALL ScannerManager::getBitmap(const ScannerContext& /*rContext*/)
+{
+ osl::MutexGuard aGuard(maProtector);
+ return css::uno::Reference<css::awt::XBitmap>(this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/scn.component b/extensions/source/scanner/scn.component
new file mode 100644
index 000000000..19215a7c3
--- /dev/null
+++ b/extensions/source/scanner/scn.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.scanner.ScannerManager"
+ constructor="extensions_ScannerManager_get_implementation">
+ <service name="com.sun.star.scanner.ScannerManager"/>
+ </implementation>
+</component>
diff --git a/extensions/source/scanner/twain32shim.cxx b/extensions/source/scanner/twain32shim.cxx
new file mode 100644
index 000000000..beca35f4f
--- /dev/null
+++ b/extensions/source/scanner/twain32shim.cxx
@@ -0,0 +1,604 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/*
+ * twain32shim.exe is a separate 32-bit executable that serves as a shim
+ * between LibreOffice and Windows' 32-bit TWAIN component. Without it,
+ * it's impossible for 64-bit program to use TWAIN on Windows.
+ * Using 64-bit TWAIN DSM library from twain.org to avoid using the shim
+ * is not an option, because scanner manufacturers only provide 32-bit
+ * drivers, and 64-bit drivers are only offered as 3rd-party commercial
+ * products. The shim is also used in 32-bit LibreOffice for uniformity.
+*/
+
+#include "twain32shim.hxx"
+#include <tools/helpers.hxx>
+#include <twain/twain.h>
+#include <o3tl/unit_conversion.hxx>
+
+#define WM_TWAIN_FALLBACK (WM_SHIM_INTERNAL + 0)
+
+namespace
+{
+double FixToDouble(const TW_FIX32& rFix) { return rFix.Whole + rFix.Frac / 65536.; }
+
+const wchar_t sTwainWndClass[] = L"TwainClass";
+
+class ImpTwain
+{
+public:
+ ImpTwain(HANDLE hParentThread);
+ ~ImpTwain();
+
+private:
+ enum class TWAINState
+ {
+ DSMunloaded = 1,
+ DSMloaded = 2,
+ DSMopened = 3,
+ DSopened = 4,
+ DSenabled = 5,
+ DSreadyToXfer = 6,
+ Xferring = 7,
+ };
+
+ TW_IDENTITY m_aAppId;
+ TW_IDENTITY m_aSrcId;
+ DWORD m_nParentThreadId;
+ HANDLE m_hProc;
+ DSMENTRYPROC m_pDSM = nullptr;
+ HMODULE m_hMod = nullptr;
+ TWAINState m_nCurState = TWAINState::DSMunloaded;
+ HWND m_hTwainWnd = nullptr;
+ HHOOK m_hTwainHook = nullptr;
+ HANDLE m_hMap = nullptr; // the *duplicated* handle
+
+ static bool IsTwainClassWnd(HWND hWnd);
+ static ImpTwain* GetImpFromWnd(HWND hWnd);
+ static void ImplCreateWnd(HWND hWnd, LPARAM lParam);
+ static LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam);
+ static LRESULT CALLBACK MsgHook(int nCode, WPARAM wParam, LPARAM lParam);
+
+ void Destroy() { ImplFallback(TWAIN_EVENT_QUIT); }
+ bool SelectSource();
+ bool InitXfer();
+
+ void NotifyParent(WPARAM nEvent, LPARAM lParam);
+ bool ImplHandleMsg(MSG* pMsg);
+ void ImplOpenSourceManager();
+ void ImplOpenSource();
+ bool ImplEnableSource();
+ void ImplXfer();
+ void ImplFallback(WPARAM nEvent);
+
+ void ImplFallbackHdl(WPARAM nEvent);
+ void ImplRequestHdl(WPARAM nRequest);
+};
+
+//static
+bool ImpTwain::IsTwainClassWnd(HWND hWnd)
+{
+ const int nBufSize = SAL_N_ELEMENTS(sTwainWndClass);
+ wchar_t sClassName[nBufSize];
+ return (GetClassNameW(hWnd, sClassName, nBufSize) && wcscmp(sClassName, sTwainWndClass) == 0);
+}
+
+//static
+ImpTwain* ImpTwain::GetImpFromWnd(HWND hWnd)
+{
+ if (!IsTwainClassWnd(hWnd))
+ return nullptr;
+ return reinterpret_cast<ImpTwain*>(GetWindowLongPtrW(hWnd, GWLP_USERDATA));
+}
+
+//static
+void ImpTwain::ImplCreateWnd(HWND hWnd, LPARAM lParam)
+{
+ CREATESTRUCT* pCS = reinterpret_cast<CREATESTRUCT*>(lParam);
+ if (pCS && IsTwainClassWnd(hWnd))
+ SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCS->lpCreateParams));
+}
+
+// static
+LRESULT CALLBACK ImpTwain::WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
+{
+ ImpTwain* pImpTwain = GetImpFromWnd(hWnd);
+ switch (nMsg)
+ {
+ case WM_CREATE:
+ ImplCreateWnd(hWnd, lParam);
+ break;
+ case WM_TWAIN_FALLBACK:
+ if (pImpTwain)
+ pImpTwain->ImplFallbackHdl(wParam);
+ break;
+ case WM_TWAIN_REQUEST:
+ if (pImpTwain)
+ pImpTwain->ImplRequestHdl(wParam);
+ break;
+ }
+ return DefWindowProcW(hWnd, nMsg, wParam, lParam);
+}
+
+// static
+LRESULT CALLBACK ImpTwain::MsgHook(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ MSG* pMsg = reinterpret_cast<MSG*>(lParam);
+ if (nCode >= 0 && pMsg)
+ {
+ ImpTwain* pImpTwain = GetImpFromWnd(pMsg->hwnd);
+ if (pImpTwain && pImpTwain->ImplHandleMsg(pMsg))
+ {
+ pMsg->message = WM_USER;
+ pMsg->lParam = 0;
+
+ return 0;
+ }
+ }
+
+ return CallNextHookEx(nullptr, nCode, wParam, lParam);
+}
+
+HANDLE GetProcOfThread(HANDLE hThread)
+{
+ DWORD nProcId = GetProcessIdOfThread(hThread);
+ if (!nProcId)
+ ThrowLastError("GetProcessIdOfThread");
+ HANDLE hRet = OpenProcess(PROCESS_DUP_HANDLE, FALSE, nProcId);
+ if (!hRet)
+ ThrowLastError("OpenProcess");
+ return hRet;
+}
+
+ImpTwain::ImpTwain(HANDLE hParentThread)
+ : m_aAppId{ /* Id */ 0,
+ { /* Version.MajorNum */ 1,
+ /* Version.MinorNum */ 0,
+ /* Version.Language */ TWLG_USA,
+ /* Version.Country */ TWCY_USA,
+ /* Version.Info */ "8.0" },
+ /* ProtocolMajor */ TWON_PROTOCOLMAJOR,
+ /* ProtocolMinor */ TWON_PROTOCOLMINOR,
+ /* SupportedGroups */ DG_IMAGE | DG_CONTROL,
+ /* Manufacturer */ "Sun Microsystems",
+ /* ProductFamily */ "Office",
+ /* ProductName */ "Office" }
+ , m_nParentThreadId(GetThreadId(hParentThread))
+ , m_hProc(GetProcOfThread(hParentThread))
+{
+ WNDCLASSW aWc = { 0, &WndProc, 0, sizeof(WNDCLASSW), GetModuleHandleW(nullptr),
+ nullptr, nullptr, nullptr, nullptr, sTwainWndClass };
+ if (!RegisterClassW(&aWc))
+ ThrowLastError("RegisterClassW");
+ m_hTwainWnd = CreateWindowExW(WS_EX_TOPMOST, aWc.lpszClassName, L"TWAIN", 0, 0, 0, 0, 0,
+ HWND_DESKTOP, nullptr, aWc.hInstance, this);
+ if (!m_hTwainWnd)
+ ThrowLastError("CreateWindowExW");
+ m_hTwainHook = SetWindowsHookExW(WH_GETMESSAGE, &MsgHook, nullptr, GetCurrentThreadId());
+ if (!m_hTwainHook)
+ ThrowLastError("SetWindowsHookExW");
+
+ NotifyParent(TWAIN_EVENT_NOTIFYHWND, reinterpret_cast<LPARAM>(m_hTwainWnd));
+}
+
+ImpTwain::~ImpTwain()
+{
+ DestroyWindow(m_hTwainWnd);
+ UnhookWindowsHookEx(m_hTwainHook);
+}
+
+bool ImpTwain::SelectSource()
+{
+ TW_UINT16 nRet = TWRC_FAILURE;
+
+ ImplOpenSourceManager();
+
+ if (TWAINState::DSMopened == m_nCurState)
+ {
+ TW_IDENTITY aIdent;
+
+ aIdent.Id = 0;
+ aIdent.ProductName[0] = '\0';
+ NotifyParent(TWAIN_EVENT_SCANNING, 0);
+ nRet = m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, &aIdent);
+ }
+
+ Destroy();
+ return (TWRC_SUCCESS == nRet);
+}
+
+bool ImpTwain::InitXfer()
+{
+ bool bRet = false;
+
+ ImplOpenSourceManager();
+
+ if (TWAINState::DSMopened == m_nCurState)
+ {
+ ImplOpenSource();
+
+ if (TWAINState::DSopened == m_nCurState)
+ bRet = ImplEnableSource();
+ }
+
+ if (!bRet)
+ Destroy();
+
+ return bRet;
+}
+
+void ImpTwain::ImplOpenSourceManager()
+{
+ if (TWAINState::DSMunloaded == m_nCurState)
+ {
+ m_hMod = LoadLibraryW(L"TWAIN_32.DLL");
+ if (!m_hMod)
+ {
+ // Windows directory might not be in DLL search path sometimes, so try the full path
+ PWSTR sPath;
+ if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Windows, 0, nullptr, &sPath)))
+ {
+ std::wstring sPathAndFile = sPath;
+ CoTaskMemFree(sPath);
+ sPathAndFile += L"\\TWAIN_32.DLL";
+ m_hMod = LoadLibraryW(sPathAndFile.c_str());
+ }
+ }
+ if (m_hMod)
+ {
+ m_nCurState = TWAINState::DSMloaded;
+
+ m_pDSM = reinterpret_cast<DSMENTRYPROC>(GetProcAddress(m_hMod, "DSM_Entry"));
+ if (m_pDSM
+ && (m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, &m_hTwainWnd)
+ == TWRC_SUCCESS))
+ {
+ m_nCurState = TWAINState::DSMopened;
+ }
+ }
+ }
+}
+
+void ImpTwain::ImplOpenSource()
+{
+ if (TWAINState::DSMopened == m_nCurState)
+ {
+ if ((m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, &m_aSrcId)
+ == TWRC_SUCCESS)
+ && (m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, &m_aSrcId)
+ == TWRC_SUCCESS))
+ {
+ TW_CAPABILITY aCap
+ = { CAP_XFERCOUNT, TWON_ONEVALUE, GlobalAlloc(GHND, sizeof(TW_ONEVALUE)) };
+ TW_ONEVALUE* pVal = static_cast<TW_ONEVALUE*>(GlobalLock(aCap.hContainer));
+
+ pVal->ItemType = TWTY_INT16;
+ pVal->Item = 1;
+ GlobalUnlock(aCap.hContainer);
+ m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &aCap);
+ GlobalFree(aCap.hContainer);
+ m_nCurState = TWAINState::DSopened;
+ }
+ }
+}
+
+bool ImpTwain::ImplEnableSource()
+{
+ bool bRet = false;
+
+ if (TWAINState::DSopened == m_nCurState)
+ {
+ TW_USERINTERFACE aUI = { true, true, m_hTwainWnd };
+
+ NotifyParent(TWAIN_EVENT_SCANNING, 0);
+ m_nCurState = TWAINState::DSenabled;
+
+ if (m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS, &aUI)
+ == TWRC_SUCCESS)
+ {
+ bRet = true;
+ }
+ else
+ {
+ // dialog failed
+ m_nCurState = TWAINState::DSopened;
+ }
+ }
+
+ return bRet;
+}
+
+void ImpTwain::NotifyParent(WPARAM nEvent, LPARAM lParam)
+{
+ PostThreadMessageW(m_nParentThreadId, WM_TWAIN_EVENT, nEvent, lParam);
+}
+
+bool ImpTwain::ImplHandleMsg(MSG* pMsg)
+{
+ if (!m_pDSM)
+ return false;
+
+ TW_EVENT aEvt = { pMsg, MSG_NULL };
+ TW_UINT16 nRet = m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, &aEvt);
+
+ switch (aEvt.TWMessage)
+ {
+ case MSG_XFERREADY:
+ {
+ WPARAM nEvent = TWAIN_EVENT_QUIT;
+
+ if (TWAINState::DSenabled == m_nCurState)
+ {
+ m_nCurState = TWAINState::DSreadyToXfer;
+ ImplXfer();
+
+ if (m_hMap)
+ nEvent = TWAIN_EVENT_XFER;
+ }
+ else if (TWAINState::Xferring == m_nCurState && m_hMap)
+ {
+ // Already sent TWAIN_EVENT_XFER; not processed yet;
+ // duplicate event
+ nEvent = TWAIN_EVENT_NONE;
+ }
+
+ ImplFallback(nEvent);
+ }
+ break;
+
+ case MSG_CLOSEDSREQ:
+ Destroy();
+ break;
+
+ case MSG_NULL:
+ nRet = TWRC_NOTDSEVENT;
+ break;
+ }
+
+ return (TWRC_DSEVENT == nRet);
+}
+
+void ImpTwain::ImplXfer()
+{
+ if (m_nCurState == TWAINState::DSreadyToXfer)
+ {
+ TW_IMAGEINFO aInfo;
+ HANDLE hDIB = nullptr;
+ double nXRes, nYRes;
+
+ if (m_pDSM(&m_aAppId, &m_aSrcId, DG_IMAGE, DAT_IMAGEINFO, MSG_GET, &aInfo) == TWRC_SUCCESS)
+ {
+ nXRes = FixToDouble(aInfo.XResolution);
+ nYRes = FixToDouble(aInfo.YResolution);
+ }
+ else
+ nXRes = nYRes = -1;
+
+ switch (m_pDSM(&m_aAppId, &m_aSrcId, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, &hDIB))
+ {
+ case TWRC_CANCEL:
+ m_nCurState = TWAINState::Xferring;
+ break;
+
+ case TWRC_XFERDONE:
+ {
+ if (hDIB)
+ {
+ m_hMap = nullptr;
+ const HGLOBAL hGlob = static_cast<HGLOBAL>(hDIB);
+ const SIZE_T nDIBSize = GlobalSize(hGlob);
+ const DWORD nMapSize = nDIBSize + 4; // leading 4 bytes for size
+ if (nMapSize > nDIBSize) // check for wrap
+ {
+ if (LPVOID pBmpMem = GlobalLock(hGlob))
+ {
+ if ((nXRes > 0) && (nYRes > 0))
+ {
+ // set resolution of bitmap
+ BITMAPINFOHEADER* pBIH = static_cast<BITMAPINFOHEADER*>(pBmpMem);
+
+ const auto[m, d]
+ = getConversionMulDiv(o3tl::Length::in, o3tl::Length::m);
+ pBIH->biXPelsPerMeter = std::round(o3tl::convert(nXRes, d, m));
+ pBIH->biYPelsPerMeter = std::round(o3tl::convert(nYRes, d, m));
+ }
+
+ HANDLE hMap = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr,
+ PAGE_READWRITE, 0, nMapSize, nullptr);
+ if (hMap)
+ {
+ LPVOID pMap = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, nMapSize);
+ if (pMap)
+ {
+ memcpy(pMap, &nMapSize, 4); // size of the following DIB
+ memcpy(static_cast<char*>(pMap) + 4, pBmpMem, nDIBSize);
+ FlushViewOfFile(pMap, nDIBSize);
+ UnmapViewOfFile(pMap);
+
+ DuplicateHandle(GetCurrentProcess(), hMap, m_hProc, &m_hMap, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ }
+
+ CloseHandle(hMap);
+ }
+
+ GlobalUnlock(hGlob);
+ }
+ }
+ }
+
+ GlobalFree(static_cast<HGLOBAL>(hDIB));
+
+ m_nCurState = TWAINState::Xferring;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void ImpTwain::ImplFallback(WPARAM nEvent)
+{
+ PostMessageW(m_hTwainWnd, WM_TWAIN_FALLBACK, nEvent, 0);
+}
+
+void ImpTwain::ImplFallbackHdl(WPARAM nEvent)
+{
+ bool bFallback = true;
+
+ switch (m_nCurState)
+ {
+ case TWAINState::Xferring:
+ case TWAINState::DSreadyToXfer:
+ {
+ TW_PENDINGXFERS aXfers;
+
+ if (m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER, &aXfers)
+ == TWRC_SUCCESS)
+ {
+ if (aXfers.Count != 0)
+ m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET, &aXfers);
+ }
+
+ m_nCurState = TWAINState::DSenabled;
+ }
+ break;
+
+ case TWAINState::DSenabled:
+ {
+ TW_USERINTERFACE aUI = { true, true, m_hTwainWnd };
+
+ m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS, &aUI);
+ m_nCurState = TWAINState::DSopened;
+ }
+ break;
+
+ case TWAINState::DSopened:
+ {
+ m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, &m_aSrcId);
+ m_nCurState = TWAINState::DSMopened;
+ }
+ break;
+
+ case TWAINState::DSMopened:
+ {
+ m_pDSM(&m_aAppId, nullptr, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, &m_hTwainWnd);
+ m_nCurState = TWAINState::DSMloaded;
+ }
+ break;
+
+ case TWAINState::DSMloaded:
+ {
+ m_pDSM = nullptr;
+ FreeLibrary(m_hMod);
+ m_hMod = nullptr;
+ m_nCurState = TWAINState::DSMunloaded;
+ }
+ break;
+
+ case TWAINState::DSMunloaded:
+ {
+ if (nEvent > TWAIN_EVENT_NONE)
+ NotifyParent(nEvent, reinterpret_cast<LPARAM>(m_hMap));
+ PostQuitMessage(0);
+
+ bFallback = false;
+ }
+ break;
+ }
+
+ if (bFallback)
+ ImplFallback(nEvent);
+}
+
+void ImpTwain::ImplRequestHdl(WPARAM nRequest)
+{
+ switch (nRequest)
+ {
+ case TWAIN_REQUEST_QUIT:
+ Destroy();
+ break;
+ case TWAIN_REQUEST_SELECTSOURCE:
+ NotifyParent(TWAIN_EVENT_REQUESTRESULT, LPARAM(SelectSource()));
+ break;
+ case TWAIN_REQUEST_INITXFER:
+ NotifyParent(TWAIN_EVENT_REQUESTRESULT, LPARAM(InitXfer()));
+ break;
+ }
+}
+} // namespace
+
+int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
+{
+ int argc = 0;
+ LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ if (argc != 2)
+ return 1; // Wrong argument count
+ // 1st argument is parent thread handle; must be inherited.
+ // HANDLE is 32-bit in 32-bit applications, so wcstoul is OK.
+ HANDLE hParentThread = reinterpret_cast<HANDLE>(wcstoul(argv[1], nullptr, 10));
+ LocalFree(argv);
+ if (!hParentThread)
+ return 2; // Invalid parent thread handle argument value
+
+ int nRet = 0;
+ try
+ {
+ ImpTwain aImpTwain(hParentThread); // creates main window
+
+ MSG msg;
+ while (true)
+ {
+ DWORD nWaitResult
+ = MsgWaitForMultipleObjects(1, &hParentThread, FALSE, INFINITE, QS_ALLINPUT);
+ if (nWaitResult == WAIT_OBJECT_0)
+ return 5; // Parent process' thread died before we exited
+ if (nWaitResult == WAIT_FAILED)
+ return 6; // Some Win32 error
+ // nWaitResult == WAIT_OBJECT_0 + nCount => an event is in queue
+ bool bQuit = false;
+ while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
+ {
+ // process it here
+ if (msg.message == WM_QUIT)
+ {
+ bQuit = true;
+ nRet = msg.wParam;
+ }
+ else
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+ if (bQuit)
+ break;
+ }
+ }
+ catch (const std::exception& e)
+ {
+ printf("Exception thrown: %s", e.what());
+ nRet = 7;
+ }
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/scanner/twain32shim.hxx b/extensions/source/scanner/twain32shim.hxx
new file mode 100644
index 000000000..c9e87ee8b
--- /dev/null
+++ b/extensions/source/scanner/twain32shim.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/.
+ *
+ */
+
+#pragma once
+
+#include <prewin.h>
+#include <Shlobj.h>
+#include <postwin.h>
+#include <exception>
+#include <string>
+#include <sstream>
+#include <iomanip>
+
+// Don't use WM_USER
+
+// Notifications from shim to parent; wParam = event id
+#define WM_TWAIN_EVENT (WM_USER + 1)
+
+// lParam is HWND
+#define TWAIN_EVENT_NOTIFYHWND 0
+
+// lParam is result (bool indicating success)
+#define TWAIN_EVENT_REQUESTRESULT 1
+
+// lParam is ignored
+#define TWAIN_EVENT_NONE 10
+#define TWAIN_EVENT_QUIT 11
+#define TWAIN_EVENT_SCANNING 12
+
+// lParam is HANDLE to shared file mapping valid in context of parent process
+#define TWAIN_EVENT_XFER 13
+
+// Requests from parent to shim; wParam = request id
+#define WM_TWAIN_REQUEST (WM_USER + 2)
+
+#define TWAIN_REQUEST_QUIT 0 // Destroy()
+#define TWAIN_REQUEST_SELECTSOURCE 1
+#define TWAIN_REQUEST_INITXFER 2
+
+// messages starting from this are not to be used for interprocess communications
+#define WM_SHIM_INTERNAL (WM_USER + 200)
+
+template <typename IntType> std::string Num2Hex(IntType n)
+{
+ std::stringstream sMsg;
+ sMsg << "0x" << std::uppercase << std::setfill('0') << std::setw(sizeof(n) * 2) << std::hex
+ << n;
+ return sMsg.str();
+}
+
+void ThrowWin32Error(const char* sFunc, DWORD nWin32Error)
+{
+ std::stringstream sMsg;
+ sMsg << sFunc << " failed with Win32 error code " << Num2Hex(nWin32Error) << "!";
+
+ throw std::exception(sMsg.str().c_str());
+}
+
+void ThrowLastError(const char* sFunc) { ThrowWin32Error(sFunc, GetLastError()); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/actionlistener.hxx b/extensions/source/update/check/actionlistener.hxx
new file mode 100644
index 000000000..8fdfd1565
--- /dev/null
+++ b/extensions/source/update/check/actionlistener.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 <salhelper/simplereferenceobject.hxx>
+
+class IActionListener : public virtual salhelper::SimpleReferenceObject
+{
+ public:
+
+ virtual void cancel() = 0;
+ virtual void download() = 0;
+ virtual void install() = 0;
+ virtual void pause() = 0;
+ virtual void resume() = 0;
+ virtual void closeAfterFailure() = 0;
+
+protected:
+ virtual ~IActionListener() override {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/download.cxx b/extensions/source/update/check/download.cxx
new file mode 100644
index 000000000..ba371bdee
--- /dev/null
+++ b/extensions/source/update/check/download.cxx
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <curl/curl.h>
+
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <osl/file.h>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include "download.hxx"
+
+namespace beans = com::sun::star::beans ;
+namespace container = com::sun::star::container ;
+namespace lang = com::sun::star::lang ;
+namespace uno = com::sun::star::uno ;
+
+namespace {
+
+struct OutData
+{
+ rtl::Reference< DownloadInteractionHandler >Handler;
+ OUString File;
+ OUString DestinationDir;
+ oslFileHandle FileHandle;
+ sal_uInt64 Offset;
+ osl::Condition& StopCondition;
+ CURL *curl;
+
+ explicit OutData(osl::Condition& rCondition) : FileHandle(nullptr), Offset(0), StopCondition(rCondition), curl(nullptr) {};
+};
+
+}
+
+static void openFile( OutData& out )
+{
+ char * effective_url;
+ curl_easy_getinfo(out.curl, CURLINFO_EFFECTIVE_URL, &effective_url);
+
+ curl_off_t nDownloadSize;
+ curl_easy_getinfo(out.curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &nDownloadSize);
+
+ OString aURL(effective_url);
+
+ // ensure no trailing '/'
+ sal_Int32 nLen = aURL.getLength();
+ while( (nLen > 0) && ('/' == aURL[nLen-1]) )
+ aURL = aURL.copy(0, --nLen);
+
+ // extract file name last '/'
+ sal_Int32 nIndex = aURL.lastIndexOf('/');
+ if( nIndex > 0 )
+ {
+ out.File = out.DestinationDir
+ + OStringToOUString(aURL.subView(nIndex), RTL_TEXTENCODING_UTF8);
+
+ oslFileError rc;
+
+ // Give the user an overwrite warning if the target file exists
+ const sal_Int32 openFlags = osl_File_OpenFlag_Write | osl_File_OpenFlag_Create;
+ do
+ {
+ rc = osl_openFile(out.File.pData, &out.FileHandle, openFlags);
+
+ if( osl_File_E_EXIST == rc && ! out.Handler->downloadTargetExists(out.File) )
+ {
+ out.StopCondition.set();
+ break;
+ }
+
+ } while( osl_File_E_EXIST == rc );
+
+ if( osl_File_E_None == rc )
+ out.Handler->downloadStarted(out.File, static_cast<sal_Int64>(nDownloadSize));
+ }
+}
+
+
+static OString
+getStringValue(const uno::Reference< container::XNameAccess >& xNameAccess, const OUString& aName)
+{
+ OSL_ASSERT(xNameAccess->hasByName(aName));
+ uno::Any aValue = xNameAccess->getByName(aName);
+
+ return OUStringToOString(aValue.get<OUString>(), RTL_TEXTENCODING_UTF8);
+}
+
+
+static sal_Int32
+getInt32Value(const uno::Reference< container::XNameAccess >& xNameAccess,
+ const OUString& aName)
+{
+ OSL_ASSERT(xNameAccess->hasByName(aName));
+ uno::Any aValue = xNameAccess->getByName(aName);
+
+ sal_Int32 n = -1;
+ aValue >>= n;
+ return n;
+}
+
+
+static size_t
+write_function( void *ptr, size_t size, size_t nmemb, void *stream )
+{
+ OutData *out = static_cast < OutData * > (stream);
+
+ if( nullptr == out->FileHandle )
+ openFile(*out);
+
+ sal_uInt64 nBytesWritten = 0;
+
+ if( nullptr != out->FileHandle )
+ osl_writeFile(out->FileHandle, ptr, size * nmemb, &nBytesWritten);
+
+ return static_cast<size_t>(nBytesWritten);
+}
+
+
+static int
+progress_callback( void *clientp, curl_off_t dltotal, curl_off_t dlnow, SAL_UNUSED_PARAMETER curl_off_t, SAL_UNUSED_PARAMETER curl_off_t)
+{
+ OutData *out = static_cast < OutData * > (clientp);
+
+ assert(out);
+
+ if (out && !out->StopCondition.check())
+ {
+ float fPercent = 0;
+ if ( dltotal + out->Offset )
+ fPercent = (dlnow + out->Offset) * 100 / (dltotal + out->Offset);
+ if( fPercent < 0 )
+ fPercent = 0;
+
+ // Do not report progress for redirection replies
+ long nCode;
+ curl_easy_getinfo(out->curl, CURLINFO_RESPONSE_CODE, &nCode);
+ if( (nCode != 302) && (nCode != 303) && (dltotal > 0) )
+ out->Handler->downloadProgressAt(static_cast<sal_Int8>(fPercent));
+
+ return 0;
+ }
+
+ // If stop condition is set, return non 0 value to abort
+ return -1;
+}
+
+
+void
+Download::getProxyForURL(std::u16string_view rURL, OString& rHost, sal_Int32& rPort) const
+{
+ uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
+ css::configuration::theDefaultProvider::get( m_xContext ) );
+
+ beans::PropertyValue aProperty;
+ aProperty.Name = "nodepath";
+ aProperty.Value <<= OUString("org.openoffice.Inet/Settings");
+
+ uno::Sequence< uno::Any > aArgumentList{ uno::Any(aProperty) };
+
+ uno::Reference< container::XNameAccess > xNameAccess(
+ xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", aArgumentList ),
+ uno::UNO_QUERY_THROW );
+
+ OSL_ASSERT(xNameAccess->hasByName("ooInetProxyType"));
+ uno::Any aValue = xNameAccess->getByName("ooInetProxyType");
+
+ sal_Int32 nProxyType = aValue.get< sal_Int32 >();
+ if( 0 != nProxyType ) // type 0 means "direct connection to the internet
+ {
+ if( o3tl::starts_with(rURL, u"http:") )
+ {
+ rHost = getStringValue(xNameAccess, "ooInetHTTPProxyName");
+ rPort = getInt32Value(xNameAccess, "ooInetHTTPProxyPort");
+ }
+ else if( o3tl::starts_with(rURL, u"https:") )
+ {
+ rHost = getStringValue(xNameAccess, "ooInetHTTPSProxyName");
+ rPort = getInt32Value(xNameAccess, "ooInetHTTPSProxyPort");
+ }
+ else if( o3tl::starts_with(rURL, u"ftp:") )
+ {
+ rHost = getStringValue(xNameAccess, "ooInetFTPProxyName");
+ rPort = getInt32Value(xNameAccess, "ooInetFTPProxyPort");
+ }
+ }
+}
+
+
+static bool curl_run(std::u16string_view rURL, OutData& out, const OString& aProxyHost, sal_Int32 nProxyPort)
+{
+ /* Need to investigate further whether it is necessary to call
+ * curl_global_init or not - leave it for now (as the ftp UCB content
+ * provider does as well).
+ */
+
+ CURL * pCURL = curl_easy_init();
+ bool ret = false;
+
+ if( nullptr != pCURL )
+ {
+ out.curl = pCURL;
+
+ OString aURL(OUStringToOString(rURL, RTL_TEXTENCODING_UTF8));
+ (void)curl_easy_setopt(pCURL, CURLOPT_URL, aURL.getStr());
+
+ // abort on http errors
+ (void)curl_easy_setopt(pCURL, CURLOPT_FAILONERROR, 1);
+
+ // enable redirection
+ (void)curl_easy_setopt(pCURL, CURLOPT_FOLLOWLOCATION, 1);
+ // only allow redirect to https://
+#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 85)
+ curl_easy_setopt(pCURL, CURLOPT_REDIR_PROTOCOLS_STR, "https");
+#else
+ curl_easy_setopt(pCURL, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
+#endif
+
+ // write function
+ (void)curl_easy_setopt(pCURL, CURLOPT_WRITEDATA, &out);
+ (void)curl_easy_setopt(pCURL, CURLOPT_WRITEFUNCTION, &write_function);
+
+ // progress handler - Condition::check unfortunately is not defined const
+ (void)curl_easy_setopt(pCURL, CURLOPT_NOPROGRESS, 0);
+ (void)curl_easy_setopt(pCURL, CURLOPT_XFERINFOFUNCTION, &progress_callback);
+ (void)curl_easy_setopt(pCURL, CURLOPT_PROGRESSDATA, &out);
+
+ // proxy
+ (void)curl_easy_setopt(pCURL, CURLOPT_PROXY, aProxyHost.getStr());
+ (void)curl_easy_setopt(pCURL, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+ if( -1 != nProxyPort )
+ (void)curl_easy_setopt(pCURL, CURLOPT_PROXYPORT, nProxyPort);
+
+ if( out.Offset > 0 )
+ {
+ // curl_off_t offset = nOffset; libcurl seems to be compiled with large
+ // file support (and we not) ..
+ sal_Int64 offset = static_cast<sal_Int64>(out.Offset);
+ (void)curl_easy_setopt(pCURL, CURLOPT_RESUME_FROM_LARGE, offset);
+ }
+
+ CURLcode cc = curl_easy_perform(pCURL);
+
+ // treat zero byte downloads as errors
+ if( nullptr == out.FileHandle )
+ openFile(out);
+
+ if( CURLE_OK == cc )
+ {
+ out.Handler->downloadFinished(out.File);
+ ret = true;
+ }
+
+ if ( CURLE_PARTIAL_FILE == cc )
+ {
+ // this sometimes happens, when a user throws away his user data, but has already
+ // completed the download of an update.
+ curl_off_t nDownloadSize;
+ curl_easy_getinfo( pCURL, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &nDownloadSize );
+ if ( -1 == nDownloadSize )
+ {
+ out.Handler->downloadFinished(out.File);
+ ret = true;
+ }
+ }
+
+ // Avoid target file being removed
+ else if( (CURLE_ABORTED_BY_CALLBACK == cc) || out.StopCondition.check() )
+ ret = true;
+
+ // Only report errors when not stopped
+ else
+ {
+ OString aMessage("Unknown error");
+
+ const char * error_message = curl_easy_strerror(cc);
+ if( nullptr != error_message )
+ aMessage = error_message;
+
+ if ( CURLE_HTTP_RETURNED_ERROR == cc )
+ {
+ long nError;
+ curl_easy_getinfo( pCURL, CURLINFO_RESPONSE_CODE, &nError );
+
+ if ( 403 == nError )
+ aMessage += " 403: Access denied!";
+ else if ( 404 == nError )
+ aMessage += " 404: File not found!";
+ else if ( 416 == nError )
+ {
+ // we got this error probably, because we already downloaded the file
+ out.Handler->downloadFinished(out.File);
+ ret = true;
+ }
+ else
+ {
+ aMessage += ":error code = " + OString::number( nError ) + " !";
+ }
+ }
+ if ( !ret )
+ out.Handler->downloadStalled( OStringToOUString(aMessage, RTL_TEXTENCODING_UTF8) );
+ }
+
+ curl_easy_cleanup(pCURL);
+ }
+
+ return ret;
+}
+
+
+bool
+Download::start(const OUString& rURL, const OUString& rFile, const OUString& rDestinationDir)
+{
+ OSL_ASSERT( m_aHandler.is() );
+
+ OutData out(m_aCondition);
+ OUString aFile( rFile );
+
+ // when rFile is empty, there is no remembered file name. If there is already a file with the
+ // same name ask the user if she wants to resume a download or restart the download
+ if ( aFile.isEmpty() )
+ {
+ // GetFileName()
+ OUString aURL( rURL );
+ // ensure no trailing '/'
+ sal_Int32 nLen = aURL.getLength();
+ while( (nLen > 0) && ('/' == aURL[ nLen-1 ]) )
+ aURL = aURL.copy( 0, --nLen );
+
+ // extract file name last '/'
+ sal_Int32 nIndex = aURL.lastIndexOf('/');
+ aFile = rDestinationDir + aURL.subView( nIndex );
+
+ // check for existing file
+ oslFileError rc = osl_openFile( aFile.pData, &out.FileHandle, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
+ osl_closeFile(out.FileHandle);
+ out.FileHandle = nullptr;
+
+ if( osl_File_E_EXIST == rc )
+ {
+ if ( m_aHandler->checkDownloadDestination( aURL.copy( nIndex+1 ) ) )
+ {
+ osl_removeFile( aFile.pData );
+ aFile.clear();
+ }
+ else
+ m_aHandler->downloadStarted( aFile, 0 );
+ }
+ else
+ {
+ osl_removeFile( aFile.pData );
+ aFile.clear();
+ }
+ }
+
+ out.File = aFile;
+ out.DestinationDir = rDestinationDir;
+ out.Handler = m_aHandler;
+
+ if( !aFile.isEmpty() )
+ {
+ oslFileError rc = osl_openFile(aFile.pData, &out.FileHandle, osl_File_OpenFlag_Write);
+
+ if( osl_File_E_None == rc )
+ {
+ // Set file pointer to the end of the file on resume
+ if( osl_File_E_None == osl_setFilePos(out.FileHandle, osl_Pos_End, 0) )
+ {
+ osl_getFilePos(out.FileHandle, &out.Offset);
+ }
+ }
+ else if( osl_File_E_NOENT == rc ) // file has been deleted meanwhile ..
+ out.File.clear();
+ }
+
+ OString aProxyHost;
+ sal_Int32 nProxyPort = -1;
+ getProxyForURL(rURL, aProxyHost, nProxyPort);
+
+ bool ret = curl_run(rURL, out, aProxyHost, nProxyPort);
+
+ if( nullptr != out.FileHandle )
+ {
+ osl_syncFile(out.FileHandle);
+ osl_closeFile(out.FileHandle);
+
+// #i90930# Don't remove already downloaded bits, when curl_run reports an error
+// because later calls might be successful
+// if( ! ret )
+// osl_removeFile(out.File.pData);
+ }
+
+ m_aCondition.reset();
+ return ret;
+}
+
+
+void
+Download::stop()
+{
+ m_aCondition.set();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/download.hxx b/extensions/source/update/check/download.hxx
new file mode 100644
index 000000000..12a11ddde
--- /dev/null
+++ b/extensions/source/update/check/download.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 <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <osl/conditn.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+struct DownloadInteractionHandler : public virtual salhelper::SimpleReferenceObject
+{
+ virtual bool checkDownloadDestination(const OUString& rFileName) = 0;
+
+ // called if the destination file already exists, but resume is false
+ virtual bool downloadTargetExists(const OUString& rFileName) = 0;
+
+ // called when curl reports an error
+ virtual void downloadStalled(const OUString& rErrorMessage) = 0;
+
+ // progress handler
+ virtual void downloadProgressAt(sal_Int8 nPercent) = 0;
+
+ // called on first progress notification
+ virtual void downloadStarted(const OUString& rFileName, sal_Int64 nFileSize) = 0;
+
+ // called when download has been finished
+ virtual void downloadFinished(const OUString& rFileName) = 0;
+
+protected:
+ virtual ~DownloadInteractionHandler() override {}
+};
+
+class Download
+{
+public:
+ Download(const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ const rtl::Reference<DownloadInteractionHandler>& rHandler)
+ : m_xContext(xContext)
+ , m_aHandler(rHandler){};
+
+ // returns true when the content of rURL was successfully written to rLocalFile
+ bool start(const OUString& rURL, const OUString& rFile, const OUString& rDestinationDir);
+
+ // stops the download after the next write operation
+ void stop();
+
+protected:
+ // Determines the appropriate proxy settings for the given URL. Returns true if a proxy should be used
+ void getProxyForURL(std::u16string_view rURL, OString& rHost, sal_Int32& rPort) const;
+
+private:
+ osl::Condition m_aCondition;
+ const css::uno::Reference<css::uno::XComponentContext>& m_xContext;
+ const rtl::Reference<DownloadInteractionHandler> m_aHandler;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/onlinecheck.cxx b/extensions/source/update/check/onlinecheck.cxx
new file mode 100644
index 000000000..af6ade879
--- /dev/null
+++ b/extensions/source/update/check/onlinecheck.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 <sal/types.h>
+#include <sal/macros.h>
+
+#include "onlinecheck.hxx"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <wininet.h>
+
+// #i71984
+extern "C" bool WNT_hasInternetConnection()
+{
+ DWORD dwFlags;
+ WCHAR szConnectionName[1024];
+
+ __try {
+ bool fIsConnected = InternetGetConnectedStateExW(
+ &dwFlags,
+ szConnectionName,
+ SAL_N_ELEMENTS(szConnectionName),
+ 0 );
+
+ return fIsConnected;
+
+ } __except( EXCEPTION_EXECUTE_HANDLER ) {
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/onlinecheck.hxx b/extensions/source/update/check/onlinecheck.hxx
new file mode 100644
index 000000000..fcfedcf48
--- /dev/null
+++ b/extensions/source/update/check/onlinecheck.hxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#if defined(_WIN32)
+extern "C" bool WNT_hasInternetConnection();
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/org/openoffice/Office/Addons.xcu b/extensions/source/update/check/org/openoffice/Office/Addons.xcu
new file mode 100644
index 000000000..29aa55e47
--- /dev/null
+++ b/extensions/source/update/check/org/openoffice/Office/Addons.xcu
@@ -0,0 +1,42 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<oor:component-data oor:name="Addons" oor:package="org.openoffice.Office" xmlns:install="http://openoffice.org/2004/installation" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <node oor:name="AddonUI" install:module="onlineupdate">
+ <node oor:name="OfficeHelp">
+ <node oor:name="UpdateCheckJob" oor:op="replace">
+ <prop oor:name="URL" oor:type="xs:string">
+ <value>vnd.sun.star.job:alias=UpdateCheck</value>
+ </prop>
+ <prop oor:name="ImageIdentifier" oor:type="xs:string">
+ <value/>
+ </prop>
+ <prop oor:name="Title" oor:type="xs:string">
+ <value xml:lang="en-US">~Check for Updates...</value>
+ </prop>
+ <prop oor:name="Target" oor:type="xs:string">
+ <value>_self</value>
+ </prop>
+ <prop oor:name="Context" oor:type="xs:string">
+ <value/>
+ </prop>
+ </node>
+ </node>
+ </node>
+</oor:component-data>
+
diff --git a/extensions/source/update/check/org/openoffice/Office/Jobs.xcu b/extensions/source/update/check/org/openoffice/Office/Jobs.xcu
new file mode 100644
index 000000000..58db40480
--- /dev/null
+++ b/extensions/source/update/check/org/openoffice/Office/Jobs.xcu
@@ -0,0 +1,66 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<oor:component-data oor:name="Jobs" oor:package="org.openoffice.Office" xmlns:install="http://openoffice.org/2004/installation" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <node oor:name="Jobs" install:module="onlineupdate">
+ <node oor:name="UpdateCheck" oor:op="replace">
+ <prop oor:name="Service">
+ <value>com.sun.star.setup.UpdateCheck</value>
+ </prop>
+ <node oor:name="Arguments">
+ <prop oor:name="AutoCheckEnabled" oor:type="xs:boolean" oor:op="replace">
+ <value>true</value>
+ </prop>
+ <prop oor:name="LastCheck" oor:type="xs:long" oor:op="replace">
+ <value>0</value>
+ </prop>
+ <prop oor:name="CheckInterval" oor:type="xs:long" oor:op="replace">
+ <!--
+ Every Day = 86400
+ Every Week = 604800
+ Every Month = 2592000
+ -->
+ <value>604800</value>
+ </prop>
+ <prop oor:name="DownloadDestination" oor:type="xs:string" oor:op="replace">
+ <value></value>
+ </prop>
+ <prop oor:name="AutoDownloadEnabled" oor:type="xs:boolean" oor:op="replace">
+ <value>false</value>
+ </prop>
+ <prop oor:name="DownloadSupported" oor:type="xs:boolean" oor:op="replace">
+ <value>true</value>
+ </prop>
+ <prop oor:name="DownloadPaused" oor:type="xs:boolean" oor:op="replace">
+ <value>false</value>
+ </prop>
+ <prop oor:name="ExtendedUserAgent" oor:type="xs:boolean" oor:op="replace">
+ <value>false</value>
+ </prop>
+ </node>
+ </node>
+ </node>
+ <node oor:name="Events" install:module="onlineupdate">
+ <node oor:name="onFirstVisibleTask" oor:op="fuse">
+ <node oor:name="JobList">
+ <node oor:name="UpdateCheck" oor:op="replace"/>
+ </node>
+ </node>
+ </node>
+</oor:component-data>
+
diff --git a/extensions/source/update/check/updatecheck.cxx b/extensions/source/update/check/updatecheck.cxx
new file mode 100644
index 000000000..8669f890e
--- /dev/null
+++ b/extensions/source/update/check/updatecheck.cxx
@@ -0,0 +1,1617 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <comphelper/scopeguard.hxx>
+#include <config_folders.h>
+
+#include "updatecheck.hxx"
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/deployment/UpdateInformationProvider.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/office/Quickstart.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteException.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/task/XJobExecutor.hpp>
+
+#include <rtl/bootstrap.hxx>
+#include <osl/process.h>
+#include <osl/file.hxx>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+
+#ifdef _WIN32
+#include <o3tl/safeCoInitUninit.hxx>
+#include <objbase.h>
+#endif
+
+#include "onlinecheck.hxx"
+#include "updateprotocol.hxx"
+#include "updatecheckconfig.hxx"
+
+namespace beans = com::sun::star::beans ;
+namespace deployment = com::sun::star::deployment ;
+namespace frame = com::sun::star::frame ;
+namespace lang = com::sun::star::lang ;
+namespace c3s = com::sun::star::system ;
+namespace task = com::sun::star::task ;
+namespace uno = com::sun::star::uno ;
+
+constexpr OUStringLiteral PROPERTY_TITLE = u"BubbleHeading";
+constexpr OUStringLiteral PROPERTY_TEXT = u"BubbleText";
+constexpr OUStringLiteral PROPERTY_SHOW_BUBBLE = u"BubbleVisible";
+constexpr OUStringLiteral PROPERTY_CLICK_HDL = u"MenuClickHDL";
+constexpr OUStringLiteral PROPERTY_SHOW_MENUICON = u"MenuIconVisible";
+
+// Returns the URL of the release note for the given position
+OUString getReleaseNote(const UpdateInfo& rInfo, sal_uInt8 pos, bool autoDownloadEnabled)
+{
+ for (auto const& elem : rInfo.ReleaseNotes)
+ {
+ if( pos == elem.Pos )
+ {
+ if( (pos > 2) || !autoDownloadEnabled || elem.URL2.isEmpty() )
+ return elem.URL;
+ }
+ else if( (pos == elem.Pos2) && ((1 == elem.Pos) || (2 == elem.Pos)) && autoDownloadEnabled )
+ return elem.URL2;
+ }
+
+ return OUString();
+}
+
+
+namespace
+{
+
+OUString getBuildId()
+{
+ OUString aPathVal("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
+ rtl::Bootstrap::expandMacros(aPathVal);
+ return aPathVal;
+}
+
+
+#if (defined LINUX || defined __sun)
+OUString getBaseInstallation()
+{
+ OUString aPathVal("$BRAND_BASE_DIR");
+ rtl::Bootstrap::expandMacros(aPathVal);
+ return aPathVal;
+}
+#endif
+
+
+bool isObsoleteUpdateInfo(std::u16string_view rBuildId)
+{
+ return rBuildId != getBuildId() && !rBuildId.empty();
+}
+
+
+OUString getImageFromFileName(const OUString& aFile)
+{
+#ifndef _WIN32
+ OUString aUnpackPath;
+ if( osl_getExecutableFile(&aUnpackPath.pData) == osl_Process_E_None )
+ {
+ sal_uInt32 lastIndex = aUnpackPath.lastIndexOf('/');
+ if ( lastIndex > 0 )
+ {
+ aUnpackPath = OUString::Concat(aUnpackPath.subView( 0, lastIndex+1 )) +
+ "unpack_update";
+ }
+
+ oslFileHandle hOut = nullptr;
+ oslProcess hProcess = nullptr;
+
+ OUString aSystemPath;
+ osl::File::getSystemPathFromFileURL(aFile, aSystemPath);
+
+ oslProcessError rc = osl_executeProcess_WithRedirectedIO(
+ aUnpackPath.pData, // [in] Image name
+ &aSystemPath.pData, 1, // [in] Arguments
+ osl_Process_WAIT | osl_Process_NORMAL, // [in] Options
+ nullptr, // [in] Security
+ nullptr, // [in] Working directory
+ nullptr, 0, // [in] Environment variables
+ &hProcess, // [out] Process handle
+ nullptr, &hOut, nullptr // [out] File handles for redirected I/O
+ );
+
+ if( osl_Process_E_None == rc )
+ {
+ // Create a guard to ensure correct cleanup in its dtor in any case
+ comphelper::ScopeGuard g([hOut, hProcess] () {
+ osl_closeFile(hOut);
+ osl_freeProcessHandle(hProcess);
+ });
+
+ oslProcessInfo aInfo;
+ aInfo.Size = sizeof(oslProcessInfo);
+
+ if( osl_Process_E_None == osl_getProcessInfo(hProcess, osl_Process_EXITCODE, &aInfo) )
+ {
+ if( 0 == aInfo.Code )
+ {
+ char szBuffer[4096];
+ sal_uInt64 nBytesRead = 0;
+ const sal_uInt64 nBytesToRead = sizeof(szBuffer) - 1;
+
+ OUString aImageName;
+ while( osl_File_E_None == osl_readFile(hOut, szBuffer, nBytesToRead, &nBytesRead) )
+ {
+ char *pc = szBuffer + nBytesRead;
+ do
+ {
+ *pc = '\0'; --pc;
+ }
+ while( ('\n' == *pc) || ('\r' == *pc) );
+
+ aImageName += OUString(szBuffer, pc - szBuffer + 1, osl_getThreadTextEncoding());
+
+ if( nBytesRead < nBytesToRead )
+ break;
+ }
+
+ if( osl::FileBase::E_None == osl::FileBase::getFileURLFromSystemPath(aImageName, aImageName) )
+ return aImageName;
+ }
+ }
+ }
+ }
+#endif
+
+ return aFile;
+}
+
+
+uno::Reference< beans::XPropertySet > createMenuBarUI(
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Reference< task::XJob >& xJob)
+{
+ if( !xContext.is() )
+ throw uno::RuntimeException(
+ "UpdateCheckJob: empty component context", uno::Reference< uno::XInterface > () );
+
+ uno::Reference< lang::XMultiComponentFactory > xServiceManager(xContext->getServiceManager());
+ if( !xServiceManager.is() )
+ throw uno::RuntimeException(
+ "UpdateCheckJob: unable to obtain service manager from component context", uno::Reference< uno::XInterface > () );
+
+ uno::Reference< beans::XPropertySet > xMenuBarUI(
+ xServiceManager->createInstanceWithContext( "com.sun.star.setup.UpdateCheckUI", xContext ),
+ uno::UNO_QUERY_THROW);
+
+ xMenuBarUI->setPropertyValue( PROPERTY_CLICK_HDL, uno::Any( xJob ) );
+
+ return xMenuBarUI;
+}
+
+
+typedef sal_Bool (* OnlineCheckFunc) ();
+
+class UpdateCheckThread : public WorkerThread
+{
+
+public:
+ UpdateCheckThread( osl::Condition& rCondition,
+ const uno::Reference<uno::XComponentContext>& xContext,
+ rtl::Reference<UpdateCheck> const & controller );
+
+ virtual void SAL_CALL join() override;
+ virtual void SAL_CALL terminate() override;
+ virtual void cancel() override;
+
+ void cancelAsSoonAsPossible();
+
+protected:
+ virtual ~UpdateCheckThread() override;
+
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override;
+
+ /* Wrapper around checkForUpdates */
+ bool runCheck( bool & rbExtensionsChecked );
+
+private:
+
+ /* Used to avoid dialup login windows (on platforms we know how to double this) */
+ static bool hasInternetConnection()
+ {
+#ifdef _WIN32
+ return WNT_hasInternetConnection();
+#else
+ return true;
+#endif
+ }
+
+ /* Creates a new instance of UpdateInformationProvider and returns this instance */
+ uno::Reference<deployment::XUpdateInformationProvider> createProvider()
+ {
+ osl::MutexGuard aGuard(m_aMutex);
+ m_xProvider = deployment::UpdateInformationProvider::create(m_xContext);
+ return m_xProvider;
+ };
+
+ /* Returns the remembered instance of UpdateInformationProvider if any */
+ uno::Reference<deployment::XUpdateInformationProvider> getProvider()
+ { osl::MutexGuard aGuard(m_aMutex); return m_xProvider; };
+
+ /* Releases the remembered instance of UpdateInformationProvider if any */
+ void clearProvider()
+ { osl::MutexGuard aGuard(m_aMutex); m_xProvider.clear(); };
+
+ osl::Mutex m_aMutex;
+
+protected:
+ osl::Condition& m_aCondition;
+
+private:
+ const uno::Reference<uno::XComponentContext> m_xContext;
+ uno::Reference<deployment::XUpdateInformationProvider> m_xProvider;
+ rtl::Reference<UpdateCheck> m_controller;
+ bool m_cancelAsSoonAsPossible;
+};
+
+
+class ManualUpdateCheckThread : public UpdateCheckThread
+{
+public:
+ ManualUpdateCheckThread( osl::Condition& rCondition, const uno::Reference<uno::XComponentContext>& xContext ) :
+ UpdateCheckThread(rCondition, xContext, {}) {};
+
+ virtual void SAL_CALL run() override;
+};
+
+
+class MenuBarButtonJob : public ::cppu::WeakImplHelper< task::XJob >
+{
+public:
+ explicit MenuBarButtonJob(const rtl::Reference< UpdateCheck >& rUpdateCheck);
+
+ // XJob
+ virtual uno::Any SAL_CALL execute(const uno::Sequence<beans::NamedValue>&) override;
+
+private:
+ rtl::Reference< UpdateCheck > m_aUpdateCheck;
+};
+
+class DownloadThread : public WorkerThread
+{
+public:
+ DownloadThread(
+ osl::Condition& rCondition,
+ const uno::Reference<uno::XComponentContext>& xContext,
+ const rtl::Reference< DownloadInteractionHandler >& rHandler,
+ const OUString& rURL );
+
+ virtual void SAL_CALL run() override;
+ virtual void cancel() override;
+ virtual void SAL_CALL suspend() override;
+ virtual void SAL_CALL onTerminated() override;
+
+protected:
+ virtual ~DownloadThread() override;
+
+private:
+ osl::Condition& m_aCondition;
+ const uno::Reference<uno::XComponentContext> m_xContext;
+ const OUString m_aURL;
+ Download m_aDownload;
+};
+
+
+class ShutdownThread : public osl::Thread
+{
+public:
+ explicit ShutdownThread(const uno::Reference<uno::XComponentContext>& xContext);
+
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override;
+
+protected:
+ virtual ~ShutdownThread() override;
+
+private:
+ osl::Condition m_aCondition;
+ const uno::Reference<uno::XComponentContext> m_xContext;
+};
+
+
+UpdateCheckThread::UpdateCheckThread( osl::Condition& rCondition,
+ const uno::Reference<uno::XComponentContext>& xContext,
+ rtl::Reference<UpdateCheck> const & controller ) :
+ m_aCondition(rCondition),
+ m_xContext(xContext),
+ m_controller(controller),
+ m_cancelAsSoonAsPossible(false)
+{
+ createSuspended();
+
+ // actually run the thread
+ resume();
+}
+
+
+UpdateCheckThread::~UpdateCheckThread()
+{
+}
+
+
+void SAL_CALL
+UpdateCheckThread::terminate()
+{
+ // Cancel potentially hanging http request ..
+ cancel();
+ // .. before terminating
+ osl::Thread::terminate();
+}
+
+
+void SAL_CALL
+UpdateCheckThread::join()
+{
+ uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
+
+ // do not join during an update check until #i73893# is fixed
+ if( ! xProvider.is() )
+ {
+ osl::Thread::join();
+ }
+}
+
+
+void
+UpdateCheckThread::cancel()
+{
+ uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
+
+ if( xProvider.is() )
+ xProvider->cancel();
+}
+
+void UpdateCheckThread::cancelAsSoonAsPossible() {
+ {
+ osl::MutexGuard g(m_aMutex);
+ m_cancelAsSoonAsPossible = true;
+ }
+ m_aCondition.set();
+}
+
+bool
+UpdateCheckThread::runCheck( bool & rbExtensionsChecked )
+{
+ bool ret = false;
+ UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
+
+ UpdateInfo aInfo;
+ rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
+
+ if( checkForUpdates(aInfo, m_xContext, aController->getInteractionHandler(), createProvider()) )
+ {
+ aController->setUpdateInfo(aInfo);
+ eUIState = UpdateCheck::getUIState(aInfo);
+ ret = true;
+ }
+ else
+ aController->setCheckFailedState();
+
+ // We will only look for extension updates, when there is no 'check for office updates' dialog open
+ // and when there was no office update found
+ if ( ( eUIState != UPDATESTATE_UPDATE_AVAIL ) &&
+ ( eUIState != UPDATESTATE_UPDATE_NO_DOWNLOAD ) &&
+ !aController->isDialogShowing() &&
+ !rbExtensionsChecked )
+ {
+ bool bHasExtensionUpdates = checkForExtensionUpdates( m_xContext );
+ aController->setHasExtensionUpdates( bHasExtensionUpdates );
+ if ( bHasExtensionUpdates )
+ aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL );
+ rbExtensionsChecked = true;
+ }
+
+ // joining with this thread is safe again
+ clearProvider();
+ return ret;
+}
+
+
+void SAL_CALL
+UpdateCheckThread::onTerminated()
+{
+ delete this;
+}
+
+
+void SAL_CALL
+UpdateCheckThread::run()
+{
+ osl_setThreadName("UpdateCheckThread");
+
+ TimeValue systime;
+ TimeValue nExtCheckTime;
+ osl_getSystemTime( &nExtCheckTime );
+
+ osl::Condition::Result aResult = osl::Condition::result_timeout;
+ TimeValue tv = { 10, 0 };
+
+ // Initial wait to avoid doing further time consuming tasks during start-up
+ aResult = m_aCondition.wait(&tv);
+ {
+ osl::MutexGuard g(m_aMutex);
+ if (m_cancelAsSoonAsPossible) {
+ goto done;
+ }
+ }
+
+ try {
+ bool bExtensionsChecked = false;
+
+ while( schedule() )
+ {
+ /* Use cases:
+ * a) manual check requested from auto check thread - "last check" should not be checked (one time)
+ * a1) manual check was requested in the middle of a running auto check,
+ * condition is set
+ * a2) manual check was requested while waiting for a retry,
+ * condition is set
+ * a3) manual check was requested while waiting for time to next
+ * scheduled check elapsing, condition is set
+ * a4) manual check was requested during initial wait, condition is set
+ * b) check interval got changed, condition may be set - same sub-cases as a),
+ * but "last check" should be honored
+ * c) normal auto check mode, condition not set - "last check" should be honored
+ */
+
+ // Accessing const members without synchronization
+ rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *aController);
+
+ // FIXME: remember last & offset ?
+ sal_Int64 last = rModel->getLastChecked();
+ sal_Int64 offset = rModel->getCheckInterval();
+
+ rModel.clear();
+
+ // last == 0 means check immediately
+ bool checkNow = last <= 0;
+
+ // Reset the condition to avoid busy loops
+ if( osl::Condition::result_ok == aResult )
+ {
+ m_aCondition.reset();
+ aResult = osl::Condition::result_timeout;
+ checkNow = aController->isDialogShowing();
+ }
+
+ if( ! checkNow )
+ {
+ osl_getSystemTime(&systime);
+
+ // Go back to sleep until time has elapsed
+ sal_Int64 next = last + offset;
+ if( last + offset > systime.Seconds )
+ {
+ // This can not be > 32 Bit for now ..
+ tv.Seconds = static_cast< sal_Int32 > (next - systime.Seconds);
+ aResult = m_aCondition.wait(&tv);
+ {
+ osl::MutexGuard g(m_aMutex);
+ if (m_cancelAsSoonAsPossible) {
+ goto done;
+ }
+ }
+ continue;
+ }
+ }
+
+ static sal_uInt8 n = 0;
+
+ if( ! hasInternetConnection() || ! runCheck( bExtensionsChecked ) )
+ {
+ // the extension update check should be independent from the office update check
+
+ osl_getSystemTime( &systime );
+ if ( nExtCheckTime.Seconds + offset < systime.Seconds )
+ bExtensionsChecked = false;
+
+ // Increase next by 15, 60, .. minutes
+ static const sal_Int32 nRetryInterval[] = { 900, 3600, 14400, 86400 };
+
+ if( n < SAL_N_ELEMENTS(nRetryInterval) )
+ ++n;
+
+ tv.Seconds = nRetryInterval[n-1];
+ aResult = m_aCondition.wait(&tv);
+ {
+ osl::MutexGuard g(m_aMutex);
+ if (m_cancelAsSoonAsPossible) {
+ goto done;
+ }
+ }
+ }
+ else // reset retry counter
+ {
+ n = 0;
+ bExtensionsChecked = false;
+ }
+ }
+ }
+
+ catch(const uno::Exception&) {
+ // Silently catch all errors
+ TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated" );
+ }
+
+done:
+ if (m_controller.is()) {
+ m_controller->notifyUpdateCheckFinished();
+ }
+}
+
+
+void SAL_CALL
+ManualUpdateCheckThread::run()
+{
+ try {
+ bool bExtensionsChecked = false;
+ runCheck( bExtensionsChecked );
+ m_aCondition.reset();
+ }
+ catch(const uno::Exception&) {
+ // Silently catch all errors
+ TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated" );
+ }
+}
+
+
+MenuBarButtonJob::MenuBarButtonJob(const rtl::Reference< UpdateCheck >& rUpdateCheck) :
+ m_aUpdateCheck(rUpdateCheck)
+{
+};
+
+
+uno::Any SAL_CALL
+MenuBarButtonJob::execute(const uno::Sequence<beans::NamedValue>& )
+{
+ if ( m_aUpdateCheck->shouldShowExtUpdDlg() )
+ m_aUpdateCheck->showExtensionDialog();
+ else
+ m_aUpdateCheck->showDialog();
+
+ return uno::Any();
+}
+
+
+DownloadThread::DownloadThread(osl::Condition& rCondition,
+ const uno::Reference<uno::XComponentContext>& xContext,
+ const rtl::Reference< DownloadInteractionHandler >& rHandler,
+ const OUString& rURL) :
+ m_aCondition(rCondition),
+ m_xContext(xContext),
+ m_aURL(rURL),
+ m_aDownload(xContext, rHandler)
+{
+ createSuspended();
+}
+
+
+DownloadThread::~DownloadThread()
+{
+}
+
+
+void SAL_CALL
+DownloadThread::run()
+{
+ osl_setThreadName("DownloadThread");
+
+#ifdef _WIN32
+ int nNbCallCoInitializeExForReinit = 0;
+ // for SystemShellExecute
+ o3tl::safeCoInitializeEx(COINIT_APARTMENTTHREADED, nNbCallCoInitializeExForReinit);
+#endif
+
+ while( schedule() )
+ {
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
+
+ OUString aLocalFile = rModel->getLocalFileName();
+ OUString aDownloadDest = rModel->getDownloadDestination();
+
+ // release config class for now
+ rModel.clear();
+
+ static sal_uInt8 n = 0;
+ if( ! m_aDownload.start(m_aURL, aLocalFile, aDownloadDest ) )
+ {
+ // retry every 15s unless the dialog is not visible
+ TimeValue tv;
+ tv.Seconds = 15;
+
+ if( ! UpdateCheck::get()->isDialogShowing() )
+ {
+ // Increase next by 1, 5, 15, 60, .. minutes
+ static const sal_Int16 nRetryInterval[] = { 60, 300, 900, 3600 };
+
+ if( n < SAL_N_ELEMENTS(nRetryInterval) )
+ ++n;
+
+ tv.Seconds = nRetryInterval[n-1];
+ }
+ m_aCondition.wait(&tv);
+ }
+ else
+ {
+ // reset wait period after successful download
+ n=0;
+ }
+ }
+#ifdef _WIN32
+ o3tl::safeCoUninitializeReinit(COINIT_MULTITHREADED, nNbCallCoInitializeExForReinit);
+#endif
+}
+
+
+void DownloadThread::cancel()
+{
+ m_aDownload.stop();
+ resume();
+
+ rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
+ aController->cancelDownload();
+}
+
+
+void SAL_CALL DownloadThread::suspend()
+{
+ osl::Thread::suspend();
+ m_aDownload.stop();
+}
+
+
+void SAL_CALL DownloadThread::onTerminated()
+{
+ delete this;
+}
+
+
+ShutdownThread::ShutdownThread( const uno::Reference<uno::XComponentContext>& xContext) :
+ m_xContext( xContext )
+{
+ create();
+}
+
+
+ShutdownThread::~ShutdownThread()
+{
+}
+
+
+void SAL_CALL
+ShutdownThread::run()
+{
+ osl_setThreadName("ShutdownThread");
+
+ TimeValue tv = { 0, 250 };
+
+ m_aCondition.wait(&tv);
+
+ // Tell QuickStarter not to veto ..
+ uno::Reference< css::beans::XFastPropertySet > xQuickStarter = css::office::Quickstart::createDefault(m_xContext);
+
+ xQuickStarter->setFastPropertyValue(0, uno::Any(false));
+
+ // Shutdown the office
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(m_xContext);
+
+ xDesktop->terminate();
+}
+
+
+void SAL_CALL ShutdownThread::onTerminated()
+{
+ delete this;
+}
+
+
+} // anonymous namespace
+
+UpdateCheck::UpdateCheck()
+ : m_eState(NOT_INITIALIZED)
+ , m_eUpdateState(UPDATESTATES_COUNT)
+ , m_pThread(nullptr)
+ , m_bHasExtensionUpdate(false)
+ , m_bShowExtUpdDlg(false)
+ , m_updateCheckRunning(false)
+{
+}
+
+UpdateCheck::~UpdateCheck() {}
+
+void
+UpdateCheck::initialize(const uno::Sequence< beans::NamedValue >& rValues,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ if( NOT_INITIALIZED == m_eState )
+ {
+ NamedValueByNameAccess aNameAccess(rValues);
+ UpdateCheckROModel aModel( aNameAccess );
+ m_xContext = xContext;
+
+ OUString aUpdateEntryVersion = aModel.getUpdateEntryVersion();
+
+ aModel.getUpdateEntry(m_aUpdateInfo);
+
+ bool obsoleteUpdateInfo = isObsoleteUpdateInfo(aUpdateEntryVersion);
+ bool bContinueDownload = false;
+ bool bDownloadAvailable = false;
+
+ m_bHasExtensionUpdate = checkForPendingUpdates( xContext );
+ m_bShowExtUpdDlg = false;
+
+ OUString aLocalFileName = aModel.getLocalFileName();
+
+ if( !aLocalFileName.isEmpty() )
+ {
+ bContinueDownload = true;
+
+ // Try to get the number of bytes already on disk
+ osl::DirectoryItem aDirectoryItem;
+ if( osl::DirectoryItem::E_None == osl::DirectoryItem::get(aLocalFileName, aDirectoryItem) )
+ {
+ osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileSize);
+ if( osl::DirectoryItem::E_None == aDirectoryItem.getFileStatus(aFileStatus) )
+ {
+ sal_Int64 nDownloadSize = aModel.getDownloadSize();
+ sal_Int64 nFileSize = aFileStatus.getFileSize();
+
+ if( nDownloadSize > 0 )
+ {
+ if ( nDownloadSize <= nFileSize ) // we have already downloaded everything
+ {
+ bContinueDownload = false;
+ bDownloadAvailable = true;
+ m_aImageName = getImageFromFileName( aLocalFileName );
+ }
+ else // Calculate initial percent value.
+ {
+ sal_Int32 nPercent = static_cast<sal_Int32>(100 * nFileSize / nDownloadSize);
+ getUpdateHandler()->setProgress( nPercent );
+ }
+ }
+ }
+ }
+
+ if ( bContinueDownload )
+ {
+ bool downloadPaused = aModel.isDownloadPaused();
+
+ enableDownload(true, downloadPaused);
+ // coverity[lock_order : FALSE] - incorrect report of lock order error with std::recursive_mutex
+ setUIState(downloadPaused ? UPDATESTATE_DOWNLOAD_PAUSED : UPDATESTATE_DOWNLOADING);
+ }
+
+ }
+ if ( !bContinueDownload )
+ {
+ // We do this intentionally only if no download is in progress ..
+ if( obsoleteUpdateInfo )
+ {
+ // Bring-up release note for position 5 ..
+ const OUString aURL(getReleaseNote(m_aUpdateInfo, 5));
+ if( !aURL.isEmpty() )
+ showReleaseNote(aURL);
+
+ // Data is outdated, probably due to installed update
+ rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( xContext, *this );
+ aConfig->clearUpdateFound();
+ aConfig->clearLocalFileName();
+
+
+ m_aUpdateInfo = UpdateInfo();
+ // Remove outdated release notes
+ storeReleaseNote( 1, OUString() );
+ storeReleaseNote( 2, OUString() );
+ }
+ else
+ {
+ enableAutoCheck(aModel.isAutoCheckEnabled());
+ if ( bDownloadAvailable )
+ setUIState( UPDATESTATE_DOWNLOAD_AVAIL );
+ else
+ {
+ // coverity[lock_order : FALSE] - incorrect report of lock order error with std::recursive_mutex
+ setUIState(getUIState(m_aUpdateInfo));
+ }
+ }
+ }
+ }
+}
+
+
+void
+UpdateCheck::cancel()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ WorkerThread *pThread = m_pThread;
+ UpdateState eUIState = getUIState(m_aUpdateInfo);
+
+ aGuard.unlock();
+
+ if( nullptr != pThread )
+ pThread->cancel();
+
+ setUIState(eUIState);
+}
+
+
+void
+UpdateCheck::download()
+{
+ std::unique_lock aGuard(m_aMutex);
+ UpdateInfo aInfo(m_aUpdateInfo);
+ State eState = m_eState;
+ aGuard.unlock();
+
+ if (aInfo.Sources.empty())
+ {
+ SAL_WARN("extensions.update", "download called without source");
+ return;
+ }
+
+ if( aInfo.Sources[0].IsDirect )
+ {
+ // Ignore second click of a double click
+ if( DOWNLOADING != eState )
+ {
+ shutdownThread(true);
+
+ {
+ std::scoped_lock aGuard2(m_aMutex);
+ enableDownload(true);
+ }
+ setUIState(UPDATESTATE_DOWNLOADING);
+ }
+ }
+ else
+ {
+ showReleaseNote(aInfo.Sources[0].URL); // Display in browser
+ }
+}
+
+
+void
+UpdateCheck::install()
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ const uno::Reference< c3s::XSystemShellExecute > xShellExecute = c3s::SystemShellExecute::create( m_xContext );
+
+ try {
+ // Construct install command ??
+
+ // Store release note for position 3 and 4
+ OUString aURL(getReleaseNote(m_aUpdateInfo, 3));
+ storeReleaseNote(1, aURL);
+
+ aURL = getReleaseNote(m_aUpdateInfo, 4);
+ storeReleaseNote(2, aURL);
+
+ OUString aInstallImage(m_aImageName);
+ osl::FileBase::getSystemPathFromFileURL(aInstallImage, aInstallImage);
+
+ sal_Int32 nFlags;
+#if (defined LINUX || defined __sun)
+ nFlags = 42;
+ OUString aParameter = getBaseInstallation();
+ if( !aParameter.isEmpty() )
+ osl::FileBase::getSystemPathFromFileURL(aParameter, aParameter);
+
+ aParameter += " &";
+#else
+ nFlags = c3s::SystemShellExecuteFlags::DEFAULTS;
+ OUString const aParameter;
+#endif
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get( m_xContext );
+ rModel->clearLocalFileName();
+
+ xShellExecute->execute(aInstallImage, aParameter, nFlags);
+ new ShutdownThread( m_xContext );
+ } catch(const uno::Exception&) {
+ m_aUpdateHandler->setErrorMessage( m_aUpdateHandler->getDefaultInstErrMsg() );
+ }
+}
+
+
+void
+UpdateCheck::pause()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if( nullptr != m_pThread )
+ m_pThread->suspend();
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
+ aGuard.unlock();
+
+ rModel->storeDownloadPaused(true);
+ setUIState(UPDATESTATE_DOWNLOAD_PAUSED);
+}
+
+
+void
+UpdateCheck::resume()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if( nullptr != m_pThread )
+ m_pThread->resume();
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
+ aGuard.unlock();
+
+ rModel->storeDownloadPaused(false);
+ setUIState(UPDATESTATE_DOWNLOADING);
+}
+
+
+void
+UpdateCheck::closeAfterFailure()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if ( ( m_eState == DISABLED ) || ( m_eState == CHECK_SCHEDULED ) )
+ {
+ const UpdateState eUIState = getUIState( m_aUpdateInfo );
+ aGuard.unlock();
+ setUIState( eUIState, true );
+ }
+}
+
+void UpdateCheck::notifyUpdateCheckFinished() {
+ std::scoped_lock l(m_aMutex);
+ m_updateCheckRunning = false;
+ m_updateCheckFinished.notify_all();
+}
+
+void UpdateCheck::waitForUpdateCheckFinished() {
+ UpdateCheckThread * thread;
+ {
+ std::scoped_lock l(m_aMutex);
+ thread = dynamic_cast<UpdateCheckThread *>(m_pThread);
+ }
+ if (thread != nullptr) {
+ thread->cancelAsSoonAsPossible();
+ }
+ for (;;) {
+ std::unique_lock lock(m_aMutex);
+ if (!m_updateCheckRunning) {
+ return;
+ }
+ m_updateCheckFinished.wait(lock);
+ }
+}
+
+void
+UpdateCheck::shutdownThread(bool join)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ // copy thread object pointer to stack
+ osl::Thread *pThread = m_pThread;
+ m_pThread = nullptr;
+ aGuard.unlock();
+
+ if( nullptr != pThread )
+ {
+ pThread->terminate();
+ if( join )
+ {
+ m_aCondition.set();
+ pThread->join();
+ m_aCondition.reset();
+ }
+ }
+}
+
+
+void
+UpdateCheck::enableAutoCheck(bool enable)
+{
+ if( enable )
+ {
+ m_updateCheckRunning = true;
+ m_pThread = new UpdateCheckThread(m_aCondition, m_xContext, this);
+ }
+
+ m_eState = enable ? CHECK_SCHEDULED : DISABLED;
+}
+
+
+void
+UpdateCheck::enableDownload(bool enable, bool paused)
+{
+ OSL_ASSERT(nullptr == m_pThread);
+
+ if( enable )
+ {
+ m_pThread = new DownloadThread(m_aCondition, m_xContext, this, m_aUpdateInfo.Sources[0].URL );
+ State eState = DISABLED;
+ if( !paused )
+ {
+ eState = DOWNLOADING;
+ m_pThread->resume();
+ }
+ else
+ eState = DOWNLOAD_PAUSED;
+
+ m_eState = eState;
+ }
+ else {
+ enableAutoCheck(UpdateCheckConfig::get(m_xContext)->isAutoCheckEnabled());
+ }
+
+}
+
+
+bool
+UpdateCheck::downloadTargetExists(const OUString& rFileName)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
+ UpdateState eUIState = UPDATESTATE_DOWNLOADING;
+
+ bool cont = false;
+
+ if( aUpdateHandler->isVisible() )
+ {
+ cont = aUpdateHandler->showOverwriteWarning();
+ if( cont )
+ {
+ if( osl_File_E_None != osl_removeFile(rFileName.pData) )
+ {
+ // FIXME: error message
+ cont = false;
+ }
+ }
+ else
+ eUIState = getUIState(m_aUpdateInfo);
+ }
+ else
+ {
+ m_aImageName = getImageFromFileName(rFileName);
+ eUIState = UPDATESTATE_DOWNLOAD_AVAIL;
+ }
+
+ if( !cont )
+ {
+ shutdownThread(false);
+ enableDownload(false);
+
+ aGuard.unlock();
+ setUIState(eUIState);
+ }
+
+ return cont;
+}
+
+
+bool UpdateCheck::checkDownloadDestination( const OUString& rFileName )
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ rtl::Reference< UpdateHandler > aUpdateHandler( getUpdateHandler() );
+
+ bool bReload = false;
+
+ if( aUpdateHandler->isVisible() )
+ {
+ bReload = aUpdateHandler->showOverwriteWarning( rFileName );
+ }
+
+ return bReload;
+}
+
+
+void
+UpdateCheck::downloadStalled(const OUString& rErrorMessage)
+{
+ std::unique_lock aGuard(m_aMutex);
+ rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
+ aGuard.unlock();
+
+ aUpdateHandler->setErrorMessage(rErrorMessage);
+ setUIState(UPDATESTATE_ERROR_DOWNLOADING);
+}
+
+
+void
+UpdateCheck::downloadProgressAt(sal_Int8 nPercent)
+{
+ std::unique_lock aGuard(m_aMutex);
+ rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
+ aGuard.unlock();
+
+ aUpdateHandler->setProgress(nPercent);
+ setUIState(UPDATESTATE_DOWNLOADING);
+}
+
+
+void
+UpdateCheck::downloadStarted(const OUString& rLocalFileName, sal_Int64 nFileSize)
+{
+ if ( nFileSize > 0 )
+ {
+ std::scoped_lock aGuard(m_aMutex);
+
+ rtl::Reference< UpdateCheckConfig > aModel(UpdateCheckConfig::get(m_xContext));
+ aModel->storeLocalFileName(rLocalFileName, nFileSize);
+
+ // Bring-up release note for position 1 ..
+ const OUString aURL(getReleaseNote(m_aUpdateInfo, 1, aModel->isAutoDownloadEnabled()));
+ if( !aURL.isEmpty() )
+ showReleaseNote(aURL);
+ }
+}
+
+
+void
+UpdateCheck::downloadFinished(const OUString& rLocalFileName)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ // no more retries
+ m_pThread->terminate();
+
+ m_aImageName = getImageFromFileName(rLocalFileName);
+ UpdateInfo aUpdateInfo(m_aUpdateInfo);
+
+ aGuard.unlock();
+ setUIState(UPDATESTATE_DOWNLOAD_AVAIL);
+
+ // Bring-up release note for position 2 ..
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get( m_xContext );
+ const OUString aURL(getReleaseNote(aUpdateInfo, 2, rModel->isAutoDownloadEnabled()));
+ if( !aURL.isEmpty() )
+ showReleaseNote(aURL);
+}
+
+
+void
+UpdateCheck::cancelDownload()
+{
+ shutdownThread(true);
+
+ std::scoped_lock aGuard(m_aMutex);
+ enableDownload(false);
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
+
+ OUString aLocalFile(rModel->getLocalFileName());
+ rModel->clearLocalFileName();
+ rModel->storeDownloadPaused(false);
+
+ if( isObsoleteUpdateInfo(rModel->getUpdateEntryVersion()) )
+ {
+ rModel->clearUpdateFound(); // This wasn't done during init yet ..
+ m_aUpdateInfo = UpdateInfo();
+ }
+
+ /*oslFileError rc =*/ osl_removeFile(aLocalFile.pData);
+ // FIXME: error handling ..
+
+}
+
+
+void
+UpdateCheck::showDialog(bool forceCheck)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ bool update_found = !m_aUpdateInfo.BuildId.isEmpty();
+ bool bSetUIState = ! m_aUpdateHandler.is();
+
+ UpdateState eDialogState = UPDATESTATES_COUNT;
+
+ switch( m_eState )
+ {
+ case DISABLED:
+ case CHECK_SCHEDULED:
+ if( forceCheck || ! update_found ) // Run check when forced or if we did not find an update yet
+ {
+ eDialogState = UPDATESTATE_CHECKING;
+ bSetUIState = true;
+ }
+ else if(m_aUpdateInfo.Sources[0].IsDirect)
+ eDialogState = UPDATESTATE_UPDATE_AVAIL;
+ else
+ eDialogState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
+ break;
+
+ case DOWNLOADING:
+ eDialogState = UPDATESTATE_DOWNLOADING;
+ break;
+
+ case DOWNLOAD_PAUSED:
+ eDialogState = UPDATESTATE_DOWNLOAD_PAUSED;
+ break;
+
+ case NOT_INITIALIZED:
+ OSL_ASSERT( false );
+ break;
+ }
+
+ if( bSetUIState )
+ {
+ aGuard.unlock();
+ setUIState(eDialogState, true); // suppress bubble as Dialog will be visible soon
+ aGuard.lock();
+ }
+
+ getUpdateHandler()->setVisible();
+
+ // Run check in separate thread ..
+ if( UPDATESTATE_CHECKING == eDialogState )
+ {
+ if( DISABLED == m_eState )
+ {
+ // destructs itself when done, not cancellable for now ..
+ new ManualUpdateCheckThread(m_aCondition, m_xContext);
+ }
+
+ m_aCondition.set();
+ }
+}
+
+
+void
+UpdateCheck::setUpdateInfo(const UpdateInfo& aInfo)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ bool bSuppressBubble = aInfo.BuildId == m_aUpdateInfo.BuildId;
+ m_aUpdateInfo = aInfo;
+
+ OSL_ASSERT(DISABLED == m_eState || CHECK_SCHEDULED == m_eState);
+
+ // Ignore leading non direct download if we get direct ones
+ std::vector< DownloadSource >::iterator iter = std::find_if(m_aUpdateInfo.Sources.begin(), m_aUpdateInfo.Sources.end(),
+ [](const DownloadSource& rSource) { return rSource.IsDirect; });
+
+ if( (iter != m_aUpdateInfo.Sources.begin()) &&
+ (iter != m_aUpdateInfo.Sources.end()) &&
+ iter->IsDirect )
+ {
+ m_aUpdateInfo.Sources.erase(m_aUpdateInfo.Sources.begin(), --iter);
+ }
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *this);
+ OSL_ASSERT( rModel.is() );
+
+ // Decide whether to use alternate release note pos ..
+ bool autoDownloadEnabled = rModel->isAutoDownloadEnabled();
+
+ for (auto & elem : m_aUpdateInfo.ReleaseNotes)
+ {
+ if( ((1 == elem.Pos) || (2 == elem.Pos)) && autoDownloadEnabled && !elem.URL2.isEmpty())
+ {
+ elem.URL = elem.URL2;
+ elem.URL2.clear();
+ elem.Pos = elem.Pos2;
+ elem.Pos2 = 0;
+ }
+ }
+
+ // do not move below store/clear ..
+ rModel->updateLastChecked();
+
+ UpdateState eUIState;
+ if( !m_aUpdateInfo.Sources.empty() )
+ {
+ rModel->storeUpdateFound(aInfo, getBuildId());
+
+ if( m_aUpdateInfo.Sources[0].IsDirect )
+ {
+ eUIState = UPDATESTATE_UPDATE_AVAIL;
+
+ if( rModel->isAutoDownloadEnabled() )
+ {
+ shutdownThread(false);
+ eUIState = UPDATESTATE_DOWNLOADING;
+ enableDownload(true);
+ }
+ }
+ else
+ eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
+ }
+ else
+ {
+ eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
+ rModel->clearUpdateFound();
+ }
+
+ aGuard.unlock();
+ setUIState(eUIState, bSuppressBubble);
+}
+
+
+void
+UpdateCheck::setCheckFailedState()
+{
+ setUIState(UPDATESTATE_ERROR_CHECKING);
+}
+
+
+void UpdateCheck::handleMenuBarUI( const rtl::Reference< UpdateHandler >& rUpdateHandler,
+ UpdateState& eState,
+ bool suppressBubble )
+{
+ uno::Reference<beans::XPropertySet> xMenuBarUI( m_xMenuBarUI );
+
+ if ( ( UPDATESTATE_NO_UPDATE_AVAIL == eState ) && m_bHasExtensionUpdate )
+ eState = UPDATESTATE_EXT_UPD_AVAIL;
+
+ if ( UPDATESTATE_EXT_UPD_AVAIL == eState )
+ m_bShowExtUpdDlg = true;
+ else
+ m_bShowExtUpdDlg = false;
+
+ if( xMenuBarUI.is() )
+ {
+ if( UPDATESTATE_NO_UPDATE_AVAIL == eState )
+ {
+ xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::Any(false) );
+ }
+ else
+ {
+ xMenuBarUI->setPropertyValue( PROPERTY_TITLE, uno::Any(rUpdateHandler->getBubbleTitle(eState)) );
+ xMenuBarUI->setPropertyValue( PROPERTY_TEXT, uno::Any(rUpdateHandler->getBubbleText(eState)) );
+
+ if( ! suppressBubble && ( ! rUpdateHandler->isVisible() || rUpdateHandler->isMinimized() ) )
+ xMenuBarUI->setPropertyValue( PROPERTY_SHOW_BUBBLE, uno::Any( true ) );
+
+ if( UPDATESTATE_CHECKING != eState )
+ xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::Any(true) );
+ }
+ }
+}
+
+
+void UpdateCheck::setUIState(UpdateState eState, bool suppressBubble)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if( ! m_xMenuBarUI.is() &&
+ (DISABLED != m_eState) &&
+ ( m_bHasExtensionUpdate || (UPDATESTATE_NO_UPDATE_AVAIL != eState)) &&
+ (UPDATESTATE_CHECKING != eState) &&
+ (UPDATESTATE_ERROR_CHECKING != eState)
+ )
+ {
+ m_xMenuBarUI = createMenuBarUI(m_xContext, new MenuBarButtonJob(this));
+ }
+
+ // Show bubble only when the status has changed
+ if ( eState == m_eUpdateState )
+ suppressBubble = true;
+ else
+ m_eUpdateState = eState;
+
+ rtl::Reference<UpdateHandler> aUpdateHandler(getUpdateHandler());
+ OSL_ASSERT( aUpdateHandler.is() );
+
+ UpdateInfo aUpdateInfo(m_aUpdateInfo);
+ OUString aImageName(m_aImageName);
+
+ aGuard.unlock();
+
+ handleMenuBarUI( aUpdateHandler, eState, suppressBubble );
+
+ if( (UPDATESTATE_UPDATE_AVAIL == eState)
+ || (UPDATESTATE_DOWNLOAD_PAUSED == eState)
+ || (UPDATESTATE_DOWNLOADING == eState) )
+ {
+ uno::Reference< uno::XComponentContext > xContext(m_xContext);
+
+ OUString aDownloadDestination =
+ UpdateCheckConfig::get(xContext, this)->getDownloadDestination();
+
+ osl_getSystemPathFromFileURL(aDownloadDestination.pData, &aDownloadDestination.pData);
+
+ aUpdateHandler->setDownloadPath(aDownloadDestination);
+ }
+ else if( UPDATESTATE_DOWNLOAD_AVAIL == eState )
+ {
+ aUpdateHandler->setDownloadFile(aImageName);
+ }
+
+ aUpdateHandler->setDescription(aUpdateInfo.Description);
+ aUpdateHandler->setNextVersion(aUpdateInfo.Version);
+ aUpdateHandler->setState(eState);
+}
+
+
+UpdateState
+UpdateCheck::getUIState(const UpdateInfo& rInfo)
+{
+ UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
+
+ if( !rInfo.BuildId.isEmpty() )
+ {
+ if( rInfo.Sources[0].IsDirect )
+ eUIState = UPDATESTATE_UPDATE_AVAIL;
+ else
+ eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
+ }
+
+ return eUIState;
+}
+
+
+void
+UpdateCheck::showReleaseNote(const OUString& rURL) const
+{
+ const uno::Reference< c3s::XSystemShellExecute > xShellExecute(
+ c3s::SystemShellExecute::create( m_xContext ) );
+
+ try {
+ xShellExecute->execute(rURL, OUString(), c3s::SystemShellExecuteFlags::URIS_ONLY);
+ } catch(const c3s::SystemShellExecuteException&) {
+ }
+}
+
+
+bool
+UpdateCheck::storeReleaseNote(sal_Int8 nNum, const OUString &rURL)
+{
+ osl::FileBase::RC rc;
+ OUString aTargetDir( UpdateCheckConfig::getAllUsersDirectory() + "/sun" );
+
+ osl::Directory::createPath( aTargetDir );
+
+ OUString aFileName = "releasenote" +
+ OUString::number( nNum ) +
+ ".url";
+
+ OUString aFilePath;
+ rc = osl::FileBase::getAbsoluteFileURL( aTargetDir, aFileName, aFilePath );
+ if ( rc != osl::FileBase::E_None ) return false;
+
+ osl::File::remove( aFilePath );
+
+ // don't store empty release notes, but delete old ones
+ if ( rURL.isEmpty() )
+ return true;
+
+ osl::File aFile( aFilePath );
+ rc = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
+ if ( rc != osl::FileBase::E_None ) return false;
+
+ OString aLineBuf("[InternetShortcut]\r\n");
+ sal_uInt64 nWritten = 0;
+
+ OUString aURL( rURL );
+#ifdef _WIN32
+ rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
+ if ( rc != osl::FileBase::E_None ) return false;
+ aURL = "URL=" + rURL;
+#endif
+ aLineBuf = OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 );
+ rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
+ if ( rc != osl::FileBase::E_None ) return false;
+
+ aFile.close();
+ return true;
+}
+
+
+void UpdateCheck::showExtensionDialog()
+{
+ uno::Reference< uno::XInterface > xService;
+
+ if( ! m_xContext.is() )
+ throw uno::RuntimeException(
+ "UpdateCheck::showExtensionDialog(): empty component context", uno::Reference< uno::XInterface > () );
+
+ uno::Reference< lang::XMultiComponentFactory > xServiceManager( m_xContext->getServiceManager() );
+ if( !xServiceManager.is() )
+ throw uno::RuntimeException(
+ "UpdateCheck::showExtensionDialog(): unable to obtain service manager from component context", uno::Reference< uno::XInterface > () );
+
+ xService = xServiceManager->createInstanceWithContext( "com.sun.star.deployment.ui.PackageManagerDialog", m_xContext );
+ uno::Reference< task::XJobExecutor > xExecutable( xService, uno::UNO_QUERY );
+ if ( xExecutable.is() )
+ xExecutable->trigger( "SHOW_UPDATE_DIALOG" );
+}
+
+
+rtl::Reference<UpdateHandler>
+UpdateCheck::getUpdateHandler()
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ if( ! m_aUpdateHandler.is() )
+ m_aUpdateHandler = new UpdateHandler(m_xContext, this);
+
+ return m_aUpdateHandler;
+}
+
+
+uno::Reference< task::XInteractionHandler >
+UpdateCheck::getInteractionHandler() const
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ uno::Reference< task::XInteractionHandler > xHandler;
+
+ if( m_aUpdateHandler.is() && m_aUpdateHandler->isVisible() )
+ xHandler = m_aUpdateHandler.get();
+
+ return xHandler;
+}
+
+
+bool
+UpdateCheck::isDialogShowing() const
+{
+ std::scoped_lock aGuard(m_aMutex);
+ return m_aUpdateHandler.is() && m_aUpdateHandler->isVisible();
+};
+
+
+void
+UpdateCheck::autoCheckStatusChanged(bool enabled)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if( (CHECK_SCHEDULED == m_eState) && !enabled )
+ shutdownThread(false);
+
+ if( (DISABLED == m_eState) || (CHECK_SCHEDULED == m_eState) )
+ {
+ enableAutoCheck(enabled);
+ UpdateState eState = getUIState(m_aUpdateInfo);
+ aGuard.unlock();
+ setUIState(eState);
+ }
+};
+
+
+void
+UpdateCheck::autoCheckIntervalChanged()
+{
+ // just wake-up
+ m_aCondition.set();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updatecheck.hxx b/extensions/source/update/check/updatecheck.hxx
new file mode 100644
index 000000000..546616f57
--- /dev/null
+++ b/extensions/source/update/check/updatecheck.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 <sal/config.h>
+
+#include <condition_variable>
+#include <mutex>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <osl/conditn.hxx>
+#include <osl/thread.hxx>
+#include <rtl/instance.hxx>
+
+#include "updateinfo.hxx"
+#include "updatecheckconfiglistener.hxx"
+#include "actionlistener.hxx"
+#include "updatehdl.hxx"
+#include "download.hxx"
+
+
+class UpdateCheck;
+
+class UpdateCheckInitData {
+
+public:
+ inline rtl::Reference< UpdateCheck > operator() () const;
+};
+
+class WorkerThread : public osl::Thread
+{
+public:
+ virtual void cancel() = 0;
+};
+
+class UpdateCheck :
+ public UpdateCheckConfigListener,
+ public IActionListener,
+ public DownloadInteractionHandler,
+ public rtl::StaticWithInit< rtl::Reference< UpdateCheck >, UpdateCheckInitData >
+{
+ UpdateCheck();
+
+ virtual ~UpdateCheck() override;
+
+public:
+ operator rtl::Reference< UpdateCheckConfigListener > ()
+ { return static_cast< UpdateCheckConfigListener * > (this); }
+
+ void initialize(const css::uno::Sequence<css::beans::NamedValue>& rValues,
+ const css::uno::Reference<css::uno::XComponentContext>& xContext);
+
+ // Update internal update info member
+ void setUpdateInfo(const UpdateInfo& aInfo);
+
+ /* This method turns on the menubar icon, triggers the bubble window or
+ * updates the dialog text when appropriate
+ */
+ void setUIState(UpdateState eState, bool suppressBubble = false);
+
+ // Returns the UI state that matches rInfo best
+ static UpdateState getUIState(const UpdateInfo& rInfo);
+
+ // Check for updates failed
+ void setCheckFailedState();
+
+ // Executes the update check dialog for manual checks and downloads interaction
+ void showDialog(bool forceCheck = false);
+
+ // Returns true if the update dialog is currently showing
+ bool isDialogShowing() const;
+ bool shouldShowExtUpdDlg() const { return ( m_bShowExtUpdDlg && m_bHasExtensionUpdate ); }
+ void showExtensionDialog();
+ void setHasExtensionUpdates( bool bHasUpdates ) { m_bHasExtensionUpdate = bHasUpdates; }
+ bool hasOfficeUpdate() const { return (m_aUpdateInfo.BuildId.getLength() > 0); }
+
+ // DownloadInteractionHandler
+ virtual bool downloadTargetExists(const OUString& rFileName) override;
+ virtual void downloadStalled(const OUString& rErrorMessage) override;
+ virtual void downloadProgressAt(sal_Int8 nProcent) override;
+ virtual void downloadStarted(const OUString& rLocalFileName, sal_Int64 nFileSize) override;
+ virtual void downloadFinished(const OUString& rLocalFileName) override;
+ // checks if the download target already exists and asks user what to do next
+ virtual bool checkDownloadDestination( const OUString& rFile ) override;
+
+ // Cancels the download action (and resumes checking if enabled)
+ void cancelDownload();
+
+ // Returns the XInteractionHandler of the UpdateHandler instance if present (and visible)
+ css::uno::Reference< css::task::XInteractionHandler > getInteractionHandler() const;
+
+ // UpdateCheckConfigListener
+ virtual void autoCheckStatusChanged(bool enabled) override;
+ virtual void autoCheckIntervalChanged() override;
+
+ // IActionListener
+ void cancel() override;
+ void download() override;
+ void install() override;
+ void pause() override;
+ void resume() override;
+ void closeAfterFailure() override;
+
+ void notifyUpdateCheckFinished();
+
+ void waitForUpdateCheckFinished();
+
+private:
+
+ // Schedules or cancels next automatic check for updates
+ void enableAutoCheck(bool enable);
+
+ // Starts/resumes or stops a download
+ void enableDownload(bool enable, bool paused=false);
+
+ // Shuts down the currently running thread
+ void shutdownThread(bool join);
+
+ // Returns the update handler instance
+ rtl::Reference<UpdateHandler> getUpdateHandler();
+
+ // Open the given URL in a browser
+ void showReleaseNote(const OUString& rURL) const;
+
+ // stores the release note url on disk to be used by setup app
+ static bool storeReleaseNote(sal_Int8 nNum, const OUString &rURL);
+
+ /* This method turns on the menubar icon and triggers the bubble window
+ */
+ void handleMenuBarUI( const rtl::Reference< UpdateHandler >& rUpdateHandler,
+ UpdateState& eState, bool suppressBubble );
+ enum State {
+ NOT_INITIALIZED,
+ DISABLED,
+ CHECK_SCHEDULED,
+ DOWNLOADING,
+ DOWNLOAD_PAUSED
+ };
+
+ State m_eState;
+ UpdateState m_eUpdateState;
+
+ mutable std::recursive_mutex m_aMutex;
+ WorkerThread *m_pThread;
+ osl::Condition m_aCondition;
+
+ UpdateInfo m_aUpdateInfo;
+ OUString m_aImageName;
+ bool m_bHasExtensionUpdate;
+ bool m_bShowExtUpdDlg;
+
+ rtl::Reference<UpdateHandler> m_aUpdateHandler;
+ css::uno::Reference<css::beans::XPropertySet> m_xMenuBarUI;
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+
+ bool m_updateCheckRunning = false;
+ std::condition_variable_any m_updateCheckFinished;
+
+ friend class UpdateCheckInitData;
+};
+
+inline rtl::Reference< UpdateCheck >
+UpdateCheckInitData::operator() () const
+{
+ return rtl::Reference< UpdateCheck > (new UpdateCheck());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updatecheckconfig.cxx b/extensions/source/update/check/updatecheckconfig.cxx
new file mode 100644
index 000000000..e728d91e7
--- /dev/null
+++ b/extensions/source/update/check/updatecheckconfig.cxx
@@ -0,0 +1,660 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "updatecheckconfig.hxx"
+#include "updatecheck.hxx"
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/security.hxx>
+#include <osl/time.h>
+#include <osl/file.hxx>
+#include <sal/macros.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#ifdef _WIN32
+#include <objbase.h>
+#include <shlobj.h>
+#endif
+
+namespace container = com::sun::star::container ;
+namespace beans = com::sun::star::beans ;
+namespace lang = com::sun::star::lang ;
+namespace util = com::sun::star::util ;
+namespace uno = com::sun::star::uno ;
+
+#define LAST_CHECK "LastCheck"
+#define UPDATE_VERSION "UpdateVersion"
+#define UPDATE_BUILDID "UpdateBuildId"
+#define UPDATE_DESCRIPTION "UpdateDescription"
+#define DOWNLOAD_URL "DownloadURL"
+#define IS_DIRECT_DOWNLOAD "IsDirectDownload"
+#define OLD_VERSION "UpdateFoundFor"
+#define AUTOCHECK_ENABLED "AutoCheckEnabled"
+#define AUTODOWNLOAD_ENABLED "AutoDownloadEnabled"
+#define CHECK_INTERVAL "CheckInterval"
+#define LOCAL_FILE "LocalFile"
+#define DOWNLOAD_SIZE "DownloadSize"
+#define DOWNLOAD_PAUSED "DownloadPaused"
+#define DOWNLOAD_DESTINATION "DownloadDestination"
+#define RELEASE_NOTE "ReleaseNote"
+
+#define PROPERTY_VERSION "Version"
+
+const char * const aUpdateEntryProperties[] = {
+ UPDATE_VERSION,
+ UPDATE_BUILDID,
+ UPDATE_DESCRIPTION,
+ DOWNLOAD_URL,
+ IS_DIRECT_DOWNLOAD,
+ RELEASE_NOTE"1",
+ RELEASE_NOTE"2",
+ RELEASE_NOTE"3",
+ RELEASE_NOTE"4",
+ RELEASE_NOTE"5",
+ OLD_VERSION
+};
+
+const sal_uInt32 nUpdateEntryProperties = SAL_N_ELEMENTS(aUpdateEntryProperties);
+
+css::uno::Any NamedValueByNameAccess::getValue(const char * pName)
+{
+ const sal_Int32 nLen = m_rValues.getLength();
+ for( sal_Int32 n=0; n < nLen; ++n )
+ {
+ if( m_rValues[n].Name.equalsAscii( pName ) )
+ return m_rValues[n].Value;
+ }
+ return css::uno::Any();
+}
+
+bool
+UpdateCheckROModel::isAutoCheckEnabled() const
+{
+ return m_aNameAccess.getValue(AUTOCHECK_ENABLED).get<bool>();
+}
+
+bool
+UpdateCheckROModel::isDownloadPaused() const
+{
+ return m_aNameAccess.getValue(DOWNLOAD_PAUSED).get<bool>();
+}
+
+OUString
+UpdateCheckROModel::getStringValue(const char * pStr) const
+{
+ uno::Any aAny( m_aNameAccess.getValue(pStr) );
+ OUString aRet;
+
+ aAny >>= aRet;
+
+ return aRet;
+}
+
+OUString UpdateCheckROModel::getLocalFileName() const
+{
+ return getStringValue(LOCAL_FILE);
+};
+
+sal_Int64 UpdateCheckROModel::getDownloadSize() const
+{
+ uno::Any aAny( m_aNameAccess.getValue(DOWNLOAD_SIZE) );
+ sal_Int64 nRet = -1;
+
+ aAny >>= nRet;
+ return nRet;
+};
+
+OUString
+UpdateCheckROModel::getUpdateEntryVersion() const
+{
+ return getStringValue(OLD_VERSION);
+}
+
+void
+UpdateCheckROModel::getUpdateEntry(UpdateInfo& rInfo) const
+{
+ rInfo.BuildId = getStringValue(UPDATE_BUILDID);
+ rInfo.Version = getStringValue(UPDATE_VERSION);
+ rInfo.Description = getStringValue(UPDATE_DESCRIPTION);
+
+ bool isDirectDownload = false;
+ m_aNameAccess.getValue(IS_DIRECT_DOWNLOAD) >>= isDirectDownload;
+
+ rInfo.Sources.push_back( DownloadSource( isDirectDownload, getStringValue(DOWNLOAD_URL) ) );
+
+ for(sal_Int32 n=1; n < 6; ++n )
+ {
+ OUString aUStr = getStringValue(
+ OString(OString::Concat(RELEASE_NOTE) + OString::number(n)).getStr());
+ if( !aUStr.isEmpty() )
+ rInfo.ReleaseNotes.push_back(ReleaseNote(static_cast<sal_Int8>(n), aUStr));
+ }
+}
+
+OUString UpdateCheckConfig::getDownloadsDirectory()
+{
+ OUString aRet;
+
+#ifdef _WIN32
+ PWSTR szPath;
+
+ if (SHGetKnownFolderPath(FOLDERID_Downloads, 0, nullptr, &szPath) == S_OK)
+ {
+ aRet = o3tl::toU(szPath);
+ CoTaskMemFree(szPath);
+ osl::FileBase::getFileURLFromSystemPath( aRet, aRet );
+ }
+#else
+ // This should become a desktop specific setting in some system backend ..
+ OUString aHomeDir;
+ osl::Security().getHomeDir( aHomeDir );
+ aRet = aHomeDir + "/Desktop";
+
+ // Set path to home directory when there is no /Desktop directory
+ osl::Directory aDocumentsDir( aRet );
+ if( osl::FileBase::E_None != aDocumentsDir.open() )
+ aRet = aHomeDir;
+#endif
+
+ return aRet;
+}
+
+OUString UpdateCheckConfig::getAllUsersDirectory()
+{
+ OUString aRet;
+
+#ifdef _WIN32
+ WCHAR szPath[MAX_PATH];
+
+ if (TRUE == SHGetSpecialFolderPathW(nullptr, szPath, CSIDL_COMMON_DOCUMENTS, true))
+ {
+ aRet = o3tl::toU(szPath);
+ osl::FileBase::getFileURLFromSystemPath( aRet, aRet );
+ }
+#else
+ osl::FileBase::getTempDirURL(aRet);
+#endif
+
+ return aRet;
+}
+
+UpdateCheckConfig::UpdateCheckConfig( const uno::Reference<container::XNameContainer>& xContainer,
+ const uno::Reference<container::XNameContainer>& xAvailableUpdates,
+ const uno::Reference<container::XNameContainer>& xIgnoredUpdates,
+ const ::rtl::Reference< UpdateCheckConfigListener >& rListener ) :
+ m_xContainer( xContainer ),
+ m_xAvailableUpdates( xAvailableUpdates ),
+ m_xIgnoredUpdates( xIgnoredUpdates ),
+ m_rListener( rListener )
+{}
+
+UpdateCheckConfig::~UpdateCheckConfig()
+{}
+
+::rtl::Reference< UpdateCheckConfig >
+UpdateCheckConfig::get(
+ const uno::Reference<uno::XComponentContext>& xContext,
+ const ::rtl::Reference< UpdateCheckConfigListener >& rListener)
+{
+ uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
+ css::configuration::theDefaultProvider::get( xContext ) );
+
+ beans::PropertyValue aProperty;
+ aProperty.Name = "nodepath";
+ aProperty.Value <<= OUString("org.openoffice.Office.Jobs/Jobs/UpdateCheck/Arguments");
+
+ uno::Sequence< uno::Any > aArgumentList{ uno::Any(aProperty) };
+
+ uno::Reference< container::XNameContainer > xContainer(
+ xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess", aArgumentList ),
+ uno::UNO_QUERY_THROW );
+
+ aProperty.Value <<= OUString("/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/IgnoredUpdates");
+ aArgumentList = { uno::Any(aProperty) };
+ uno::Reference< container::XNameContainer > xIgnoredExt( xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationUpdateAccess", aArgumentList ), uno::UNO_QUERY_THROW );
+
+ aProperty.Value <<= OUString("/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/AvailableUpdates");
+ aArgumentList = { uno::Any(aProperty) };
+ uno::Reference< container::XNameContainer > xUpdateAvail( xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationUpdateAccess", aArgumentList ), uno::UNO_QUERY_THROW );
+
+ return new UpdateCheckConfig( xContainer, xUpdateAvail, xIgnoredExt, rListener );
+}
+
+bool
+UpdateCheckConfig::isAutoCheckEnabled() const
+{
+ bool nValue = false;
+ const_cast < UpdateCheckConfig *> (this)->getByName( AUTOCHECK_ENABLED ) >>= nValue;
+ return nValue;
+}
+
+bool
+UpdateCheckConfig::isAutoDownloadEnabled() const
+{
+ bool nValue = false;
+ const_cast < UpdateCheckConfig *> (this)->getByName( AUTODOWNLOAD_ENABLED ) >>= nValue;
+ return nValue;
+}
+
+OUString
+UpdateCheckConfig::getUpdateEntryVersion() const
+{
+ OUString aValue;
+
+ // getByName is defined as non const in XNameAccess
+ const_cast < UpdateCheckConfig *> (this)->getByName( OLD_VERSION ) >>= aValue;
+
+ return aValue;
+}
+
+sal_Int64
+UpdateCheckConfig::getLastChecked() const
+{
+ sal_Int64 nValue = 0;
+
+ // getByName is defined as non const in XNameAccess
+ const_cast < UpdateCheckConfig *> (this)->getByName( LAST_CHECK ) >>= nValue;
+
+ return nValue;
+}
+
+sal_Int64
+UpdateCheckConfig::getCheckInterval() const
+{
+ sal_Int64 nValue = 0;
+
+ // getByName is defined as non const in XNameAccess
+ const_cast < UpdateCheckConfig *> (this)->getByName( CHECK_INTERVAL ) >>= nValue;
+
+ return nValue;
+}
+
+OUString
+UpdateCheckConfig::getLocalFileName() const
+{
+ OUString aName = LOCAL_FILE;
+ OUString aRet;
+
+ if( m_xContainer->hasByName(aName) )
+ m_xContainer->getByName(aName) >>= aRet;
+
+ return aRet;
+}
+
+OUString
+UpdateCheckConfig::getDownloadDestination() const
+{
+ OUString aRet;
+
+ const_cast <UpdateCheckConfig *> (this)->getByName(DOWNLOAD_DESTINATION) >>= aRet;
+
+ return aRet;
+}
+
+void
+UpdateCheckConfig::storeLocalFileName(const OUString& rLocalFileName, sal_Int64 nFileSize)
+{
+ const sal_uInt8 nItems = 2;
+ const OUString aNameList[nItems] = { OUString(LOCAL_FILE), OUString(DOWNLOAD_SIZE) };
+ const uno::Any aValueList[nItems] = { uno::Any(rLocalFileName), uno::Any(nFileSize) };
+
+ for( sal_uInt8 i=0; i < nItems; ++i )
+ {
+ if( m_xContainer->hasByName(aNameList[i]) )
+ m_xContainer->replaceByName(aNameList[i], aValueList[i]);
+ else
+ m_xContainer->insertByName(aNameList[i], aValueList[i]);
+ }
+
+ commitChanges();
+}
+
+void
+UpdateCheckConfig::clearLocalFileName()
+{
+ const sal_uInt8 nItems = 2;
+ const OUString aNameList[nItems] = { OUString(LOCAL_FILE), OUString(DOWNLOAD_SIZE) };
+
+ for(const auto & i : aNameList)
+ {
+ if( m_xContainer->hasByName(i) )
+ m_xContainer->removeByName(i);
+ }
+
+ commitChanges();
+}
+
+void
+UpdateCheckConfig::storeDownloadPaused(bool paused)
+{
+ replaceByName(DOWNLOAD_PAUSED , uno::Any(paused));
+ commitChanges();
+}
+
+void
+UpdateCheckConfig::updateLastChecked()
+{
+ TimeValue systime;
+ osl_getSystemTime(&systime);
+
+ sal_Int64 lastCheck = systime.Seconds;
+
+ replaceByName(LAST_CHECK, uno::Any(lastCheck));
+}
+
+void
+UpdateCheckConfig::storeUpdateFound( const UpdateInfo& rInfo, const OUString& aCurrentBuild)
+
+{
+ bool autoDownloadEnabled = isAutoDownloadEnabled();
+
+ uno::Any aValues[nUpdateEntryProperties] =
+ {
+ uno::Any(rInfo.Version),
+ uno::Any(rInfo.BuildId),
+ uno::Any(rInfo.Description),
+ uno::Any(rInfo.Sources[0].URL),
+ uno::Any(rInfo.Sources[0].IsDirect),
+ uno::Any(getReleaseNote(rInfo, 1, autoDownloadEnabled) ),
+ uno::Any(getReleaseNote(rInfo, 2, autoDownloadEnabled) ),
+ uno::Any(getReleaseNote(rInfo, 3, autoDownloadEnabled) ),
+ uno::Any(getReleaseNote(rInfo, 4, autoDownloadEnabled) ),
+ uno::Any(getReleaseNote(rInfo, 5, autoDownloadEnabled) ),
+ uno::Any(aCurrentBuild)
+ };
+
+ OUString aName;
+ for( sal_uInt32 n=0; n < nUpdateEntryProperties; ++n )
+ {
+ aName = OUString::createFromAscii(aUpdateEntryProperties[n]);
+
+ if( m_xContainer->hasByName(aName) )
+ m_xContainer->replaceByName(aName, aValues[n]);
+ else
+ m_xContainer->insertByName(aName,aValues[n]);
+ }
+
+ commitChanges();
+}
+
+void
+UpdateCheckConfig::clearUpdateFound()
+{
+ OUString aName;
+
+ for(const char* aUpdateEntryProperty : aUpdateEntryProperties)
+ {
+ aName = OUString::createFromAscii(aUpdateEntryProperty);
+
+ try {
+ if( m_xContainer->hasByName(aName) )
+ m_xContainer->removeByName(aName);
+ } catch(const lang::WrappedTargetException& ) {
+ // Can not remove value, probably in share layer
+ OSL_ASSERT(false);
+ m_xContainer->replaceByName(aName, uno::Any(OUString()));
+ }
+ }
+
+ /* As we have removed UpdateVersionFound from the shared configuration
+ * existing entries in the user layer do not have a oor operation and
+ * thus are completely ignored (which also means they can not be removed).
+ */
+
+ commitChanges();
+}
+
+uno::Type SAL_CALL
+UpdateCheckConfig::getElementType()
+{
+ return m_xContainer->getElementType();
+}
+
+sal_Bool SAL_CALL
+UpdateCheckConfig::hasElements()
+{
+ return m_xContainer->hasElements();
+}
+
+uno::Any SAL_CALL
+UpdateCheckConfig::getByName( const OUString& aName )
+{
+ uno::Any aValue = m_xContainer->getByName( aName );
+
+ // Provide dynamic default value
+ if( aName == DOWNLOAD_DESTINATION )
+ {
+ OUString aStr;
+ aValue >>= aStr;
+
+ if( aStr.isEmpty() )
+ aValue <<= getDownloadsDirectory();
+ }
+ return aValue;
+}
+
+uno::Sequence< OUString > SAL_CALL
+UpdateCheckConfig::getElementNames()
+{
+ return m_xContainer->getElementNames();
+}
+
+sal_Bool SAL_CALL
+UpdateCheckConfig::hasByName( const OUString& aName )
+{
+ return m_xContainer->hasByName( aName );
+}
+
+void SAL_CALL
+UpdateCheckConfig::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ return m_xContainer->replaceByName( aName, aElement );
+}
+
+// XChangesBatch
+
+void SAL_CALL
+UpdateCheckConfig::commitChanges()
+{
+ uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY);
+ if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() )
+ {
+ util::ChangesSet aChangesSet = xChangesBatch->getPendingChanges();
+ xChangesBatch->commitChanges();
+
+ if( m_rListener.is() )
+ {
+ const sal_Int32 nChanges = aChangesSet.getLength();
+ OUString aString;
+
+ for( sal_Int32 i=0; i<nChanges; ++i )
+ {
+ aChangesSet[i].Accessor >>= aString;
+ if( aString.endsWith(AUTOCHECK_ENABLED "']") )
+ {
+ bool bEnabled = false;
+ aChangesSet[i].Element >>= bEnabled;
+ m_rListener->autoCheckStatusChanged(bEnabled);
+ }
+ else if( aString.endsWith(CHECK_INTERVAL "']") )
+ {
+ m_rListener->autoCheckIntervalChanged();
+ }
+ }
+ }
+ }
+
+ xChangesBatch.set( m_xAvailableUpdates, uno::UNO_QUERY );
+ if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() )
+ {
+ xChangesBatch->commitChanges();
+ }
+ xChangesBatch.set( m_xIgnoredUpdates, uno::UNO_QUERY );
+ if( xChangesBatch.is() && xChangesBatch->hasPendingChanges() )
+ {
+ xChangesBatch->commitChanges();
+ }
+}
+
+sal_Bool SAL_CALL
+UpdateCheckConfig::hasPendingChanges( )
+{
+ uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY);
+ if( xChangesBatch.is() )
+ return xChangesBatch->hasPendingChanges();
+
+ return false;
+}
+
+uno::Sequence< util::ElementChange > SAL_CALL
+UpdateCheckConfig::getPendingChanges( )
+{
+ uno::Reference< util::XChangesBatch > xChangesBatch(m_xContainer, uno::UNO_QUERY);
+ if( xChangesBatch.is() )
+ return xChangesBatch->getPendingChanges();
+
+ return uno::Sequence< util::ElementChange >();
+}
+
+bool UpdateCheckConfig::storeExtensionVersion( const OUString& rExtensionName,
+ const OUString& rVersion )
+{
+ bool bNotify = true;
+
+ if ( m_xAvailableUpdates->hasByName( rExtensionName ) )
+ uno::Reference< beans::XPropertySet >( m_xAvailableUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->setPropertyValue( PROPERTY_VERSION, uno::Any( rVersion ) );
+ else
+ {
+ uno::Reference< beans::XPropertySet > elem( uno::Reference< lang::XSingleServiceFactory >( m_xAvailableUpdates, uno::UNO_QUERY_THROW )->createInstance(), uno::UNO_QUERY_THROW );
+ elem->setPropertyValue( PROPERTY_VERSION, uno::Any( rVersion ) );
+ m_xAvailableUpdates->insertByName( rExtensionName, uno::Any( elem ) );
+ }
+
+ if ( m_xIgnoredUpdates->hasByName( rExtensionName ) )
+ {
+ OUString aIgnoredVersion;
+ uno::Any aValue( uno::Reference< beans::XPropertySet >( m_xIgnoredUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
+ aValue >>= aIgnoredVersion;
+ if ( aIgnoredVersion.isEmpty() ) // no version means ignore all updates
+ bNotify = false;
+ else if ( aIgnoredVersion == rVersion ) // the user wanted to ignore this update
+ bNotify = false;
+ }
+
+ commitChanges();
+
+ return bNotify;
+}
+
+bool UpdateCheckConfig::checkExtensionVersion( const OUString& rExtensionName,
+ const OUString& rVersion )
+{
+ if ( m_xAvailableUpdates->hasByName( rExtensionName ) )
+ {
+ OUString aStoredVersion;
+ uno::Any aValue( uno::Reference< beans::XPropertySet >( m_xAvailableUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
+ aValue >>= aStoredVersion;
+
+ if ( m_xIgnoredUpdates->hasByName( rExtensionName ) )
+ {
+ OUString aIgnoredVersion;
+ uno::Any aValue2( uno::Reference< beans::XPropertySet >( m_xIgnoredUpdates->getByName( rExtensionName ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
+ aValue2 >>= aIgnoredVersion;
+ if ( aIgnoredVersion.isEmpty() ) // no version means ignore all updates
+ return false;
+ else if ( aIgnoredVersion == aStoredVersion ) // the user wanted to ignore this update
+ return false;
+ // TODO: else delete ignored entry?
+ }
+ if ( isVersionGreater( rVersion, aStoredVersion ) )
+ return true;
+ else
+ {
+ m_xAvailableUpdates->removeByName( rExtensionName );
+ commitChanges();
+ }
+ }
+
+ return false;
+}
+
+OUString UpdateCheckConfig::getSubVersion( const OUString& rVersion,
+ sal_Int32 *nIndex )
+{
+ while ( *nIndex < rVersion.getLength() && rVersion[*nIndex] == '0')
+ {
+ ++*nIndex;
+ }
+
+ return rVersion.getToken( 0, '.', *nIndex );
+}
+
+/// checks if the second version string is greater than the first one
+bool UpdateCheckConfig::isVersionGreater( const OUString& rVersion1,
+ const OUString& rVersion2 )
+{
+ for ( sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0; )
+ {
+ OUString sSub1( getSubVersion( rVersion1, &i1 ) );
+ OUString sSub2( getSubVersion( rVersion2, &i2 ) );
+
+ if ( sSub1.getLength() < sSub2.getLength() ) {
+ return true;
+ } else if ( sSub1.getLength() > sSub2.getLength() ) {
+ return false;
+ } else if ( sSub1 < sSub2 ) {
+ return true;
+ } else if ( sSub1 > sSub2 ) {
+ return false;
+ }
+ }
+ return false;
+}
+
+OUString SAL_CALL
+UpdateCheckConfig::getImplementationName()
+{
+ return "vnd.sun.UpdateCheckConfig";
+}
+
+sal_Bool SAL_CALL
+UpdateCheckConfig::supportsService(OUString const & serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+UpdateCheckConfig::getSupportedServiceNames()
+{
+ return { "com.sun.star.setup.UpdateCheckConfig" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_update_UpdateCheckConfig_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(UpdateCheckConfig::get(context, *UpdateCheck::get()).get());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updatecheckconfig.hxx b/extensions/source/update/check/updatecheckconfig.hxx
new file mode 100644
index 000000000..a9836c624
--- /dev/null
+++ b/extensions/source/update/check/updatecheckconfig.hxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <rtl/ref.hxx>
+
+#include "updatecheckconfiglistener.hxx"
+#include "updateinfo.hxx"
+
+/* This helper class provides by name access to a sequence of named values */
+class NamedValueByNameAccess
+{
+ const css::uno::Sequence< css::beans::NamedValue >& m_rValues;
+
+public:
+ explicit NamedValueByNameAccess(
+ const css::uno::Sequence< css::beans::NamedValue >& rValues) :
+ m_rValues(rValues) {} ;
+
+ css::uno::Any getValue(const char * pName);
+};
+
+
+/* This class encapsulates the configuration item actually used for storing the state
+ * the update check is actually in.
+ */
+class UpdateCheckROModel
+{
+public:
+ explicit UpdateCheckROModel(NamedValueByNameAccess& aNameAccess) : m_aNameAccess(aNameAccess) {};
+
+ bool isAutoCheckEnabled() const;
+ bool isDownloadPaused() const;
+ OUString getLocalFileName() const;
+ sal_Int64 getDownloadSize() const;
+
+ OUString getUpdateEntryVersion() const;
+ void getUpdateEntry(UpdateInfo& rInfo) const;
+
+private:
+
+ OUString getStringValue(const char *) const;
+
+ NamedValueByNameAccess& m_aNameAccess;
+};
+
+
+/* This class implements the non published UNO service com.sun.star.setup.UpdateCheckConfig,
+ * which primary use is to be able to track changes done in the Tools -> Options page of this
+ * component, as this is not supported by the OOo configuration for extendable groups.
+ */
+
+class UpdateCheckConfig : public ::cppu::WeakImplHelper<
+ css::container::XNameReplace,
+ css::util::XChangesBatch,
+ css::lang::XServiceInfo >
+{
+ UpdateCheckConfig( const css::uno::Reference< css::container::XNameContainer >& xContainer,
+ const css::uno::Reference< css::container::XNameContainer >& xAvailableUpdates,
+ const css::uno::Reference< css::container::XNameContainer >& xIgnoredUpdates,
+ const ::rtl::Reference< UpdateCheckConfigListener >& rListener );
+
+ virtual ~UpdateCheckConfig() override;
+
+public:
+
+ static ::rtl::Reference< UpdateCheckConfig > get(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const ::rtl::Reference< UpdateCheckConfigListener >& rListener = ::rtl::Reference< UpdateCheckConfigListener >());
+
+ // Should really implement ROModel...
+ bool isAutoCheckEnabled() const;
+ bool isAutoDownloadEnabled() const;
+ OUString getUpdateEntryVersion() const;
+
+ /* Updates the timestamp of last check, but does not commit the change
+ * as either clearUpdateFound() or setUpdateFound() are expected to get
+ * called next.
+ */
+ void updateLastChecked();
+
+ /* Returns the date of the last successful check in seconds since 1970 */
+ sal_Int64 getLastChecked() const;
+
+ /* Returns configured check interval in seconds */
+ sal_Int64 getCheckInterval() const;
+
+ /* Reset values of previously remembered update
+ */
+ void clearUpdateFound();
+
+ /* Stores the specified data of an available update
+ */
+ void storeUpdateFound(const UpdateInfo& rInfo, const OUString& aCurrentBuild);
+
+ // Returns the local file name of a started download
+ OUString getLocalFileName() const;
+
+ // Returns the local file name of a started download
+ OUString getDownloadDestination() const;
+
+ // stores the local file name of a just started download
+ void storeLocalFileName(const OUString& rFileName, sal_Int64 nFileSize);
+
+ // Removes the local file name of a download
+ void clearLocalFileName();
+
+ // Stores the bool value for manually paused downloads
+ void storeDownloadPaused(bool paused);
+
+ // Returns the directory for downloaded files
+ static OUString getDownloadsDirectory();
+
+ // Returns a directory accessible for all users
+ static OUString getAllUsersDirectory();
+
+ // store and retrieve information about extensions
+ bool storeExtensionVersion( const OUString& rExtensionName,
+ const OUString& rVersion );
+ bool checkExtensionVersion( const OUString& rExtensionName,
+ const OUString& rVersion );
+
+ // 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;
+
+ // XChangesBatch
+ virtual void SAL_CALL commitChanges( ) override;
+ virtual sal_Bool SAL_CALL hasPendingChanges( ) override;
+ virtual css::uno::Sequence< css::util::ElementChange > SAL_CALL getPendingChanges( ) override;
+
+ // 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;
+
+private:
+
+ static OUString getSubVersion( const OUString& rVersion, sal_Int32 *nIndex );
+ static bool isVersionGreater( const OUString& rVersion1, const OUString& rVersion2 );
+
+ const css::uno::Reference< css::container::XNameContainer > m_xContainer;
+ const css::uno::Reference< css::container::XNameContainer > m_xAvailableUpdates;
+ const css::uno::Reference< css::container::XNameContainer > m_xIgnoredUpdates;
+ const ::rtl::Reference< UpdateCheckConfigListener > m_rListener;
+};
+
+/// @throws css::uno::RuntimeException
+template <typename T>
+T getValue( const css::uno::Sequence< css::beans::NamedValue >& rNamedValues, const char * pszName )
+{
+ for( css::beans::NamedValue const & nv : rNamedValues )
+ {
+ // Unfortunately gcc-3.3 does not like Any.get<T>();
+ if( nv.Name.equalsAscii( pszName ) )
+ {
+ T value = T();
+ if( ! (nv.Value >>= value) )
+ throw css::uno::RuntimeException(
+ OUString(
+ cppu_Any_extraction_failure_msg(
+ &nv.Value,
+ ::cppu::getTypeFavourUnsigned(&value).getTypeLibType() ),
+ SAL_NO_ACQUIRE ),
+ css::uno::Reference< css::uno::XInterface >() );
+
+ return value;
+ }
+ }
+
+ return T();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updatecheckconfiglistener.hxx b/extensions/source/update/check/updatecheckconfiglistener.hxx
new file mode 100644
index 000000000..903200f68
--- /dev/null
+++ b/extensions/source/update/check/updatecheckconfiglistener.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <salhelper/simplereferenceobject.hxx>
+
+/* This interface should be implemented by classes acting
+ * as controller (as in the MVC pattern).
+ */
+
+struct UpdateCheckConfigListener : public virtual salhelper::SimpleReferenceObject
+{
+ virtual void autoCheckStatusChanged(bool enabled) = 0;
+ virtual void autoCheckIntervalChanged() = 0;
+
+protected:
+ virtual ~UpdateCheckConfigListener() override {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updatecheckjob.cxx b/extensions/source/update/check/updatecheckjob.cxx
new file mode 100644
index 000000000..82d2f7439
--- /dev/null
+++ b/extensions/source/update/check/updatecheckjob.cxx
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "updatecheck.hxx"
+#include "updatecheckconfig.hxx"
+#include "updatehdl.hxx"
+#include "updateprotocol.hxx"
+
+#include <memory>
+#include <mutex>
+#include <utility>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/task/XJob.hpp>
+
+namespace beans = com::sun::star::beans ;
+namespace frame = com::sun::star::frame ;
+namespace lang = com::sun::star::lang ;
+namespace task = com::sun::star::task ;
+namespace uno = com::sun::star::uno ;
+
+namespace
+{
+
+class InitUpdateCheckJobThread : public osl::Thread
+{
+public:
+ InitUpdateCheckJobThread( const uno::Reference< uno::XComponentContext > &xContext,
+ const uno::Sequence< beans::NamedValue > &xParameters,
+ bool bShowDialog );
+
+ virtual void SAL_CALL run() override;
+
+ void setTerminating();
+
+private:
+ osl::Condition m_aCondition;
+ uno::Reference<uno::XComponentContext> m_xContext;
+ uno::Sequence<beans::NamedValue> m_xParameters;
+ bool m_bShowDialog;
+ bool m_bTerminating;
+
+ std::mutex m_mutex;
+ rtl::Reference<UpdateCheck> m_controller;
+};
+
+class UpdateCheckJob :
+ public ::cppu::WeakImplHelper< task::XJob, lang::XServiceInfo, frame::XTerminateListener >
+{
+ virtual ~UpdateCheckJob() override;
+
+public:
+
+ UpdateCheckJob(
+ css::uno::Reference<css::uno::XComponentContext> const & context,
+ css::uno::Reference<css::frame::XDesktop2> const & desktop):
+ m_xContext(context), m_xDesktop(desktop)
+ {}
+
+ // XJob
+ virtual uno::Any SAL_CALL execute(const uno::Sequence<beans::NamedValue>&) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination( lang::EventObject const & evt ) override;
+ virtual void SAL_CALL notifyTermination( lang::EventObject const & evt ) override;
+
+private:
+ uno::Reference<uno::XComponentContext> m_xContext;
+ uno::Reference< frame::XDesktop2 > m_xDesktop;
+ std::unique_ptr< InitUpdateCheckJobThread > m_pInitThread;
+
+ void handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp );
+ void terminateAndJoinThread();
+};
+
+InitUpdateCheckJobThread::InitUpdateCheckJobThread(
+ const uno::Reference< uno::XComponentContext > &xContext,
+ const uno::Sequence< beans::NamedValue > &xParameters,
+ bool bShowDialog ) :
+ m_xContext( xContext ),
+ m_xParameters( xParameters ),
+ m_bShowDialog( bShowDialog ),
+ m_bTerminating( false )
+{
+ create();
+}
+
+
+void SAL_CALL InitUpdateCheckJobThread::run()
+{
+ osl_setThreadName("InitUpdateCheckJobThread");
+
+ if (!m_bShowDialog) {
+ TimeValue tv = { 25, 0 };
+ m_aCondition.wait( &tv );
+ if ( m_bTerminating )
+ return;
+ }
+
+ try {
+ rtl::Reference< UpdateCheck > aController( UpdateCheck::get() );
+ // At least for the automatic ("onFirstVisibleTask", i.e., !m_bShowDialog) check, wait for
+ // m_controller during setTerminating, to prevent m_controller from still having threads
+ // running during exit (ideally, we would make sure that all threads are joined before exit,
+ // but the UpdateCheck logic is rather convoluted, so play it safe for now and only address
+ // the automatic update check that is known to cause issues during `make check`, not the
+ // manually triggered update check scenario):
+ if (!m_bShowDialog) {
+ std::scoped_lock l(m_mutex);
+ m_controller = aController;
+ }
+ aController->initialize( m_xParameters, m_xContext );
+
+ if ( m_bShowDialog )
+ aController->showDialog( true );
+ } catch (const uno::Exception &) {
+ // fdo#64962 - don't bring the app down on some unexpected exception.
+ TOOLS_WARN_EXCEPTION("extensions.update", "Caught init update exception, thread terminated" );
+ {
+ std::scoped_lock l(m_mutex);
+ m_controller.clear();
+ }
+ }
+}
+
+void InitUpdateCheckJobThread::setTerminating() {
+ m_bTerminating = true;
+ m_aCondition.set();
+ rtl::Reference<UpdateCheck> controller;
+ {
+ std::scoped_lock l(m_mutex);
+ std::swap(controller, m_controller);
+ }
+ if (controller.is()) {
+ controller->waitForUpdateCheckFinished();
+ }
+}
+
+UpdateCheckJob::~UpdateCheckJob()
+{
+}
+
+uno::Any
+UpdateCheckJob::execute(const uno::Sequence<beans::NamedValue>& namedValues)
+{
+ for ( sal_Int32 n=namedValues.getLength(); n-- > 0; )
+ {
+ if ( namedValues[ n ].Name == "DynamicData" )
+ {
+ uno::Sequence<beans::NamedValue> aListProp;
+ if ( namedValues[n].Value >>= aListProp )
+ {
+ for ( sal_Int32 i=aListProp.getLength(); i-- > 0; )
+ {
+ if ( aListProp[ i ].Name == "updateList" )
+ {
+ handleExtensionUpdates( aListProp );
+ return uno::Any();
+ }
+ }
+ }
+ }
+ }
+
+ uno::Sequence<beans::NamedValue> aConfig =
+ getValue< uno::Sequence<beans::NamedValue> > (namedValues, "JobConfig");
+
+ /* Determine the way we got invoked here -
+ * see Developers Guide Chapter "4.7.2 Jobs" to understand the magic
+ */
+
+ uno::Sequence<beans::NamedValue> aEnvironment =
+ getValue< uno::Sequence<beans::NamedValue> > (namedValues, "Environment");
+
+ OUString aEventName = getValue< OUString > (aEnvironment, "EventName");
+
+ m_pInitThread.reset(
+ new InitUpdateCheckJobThread(
+ m_xContext, aConfig,
+ aEventName != "onFirstVisibleTask"));
+
+ return uno::Any();
+}
+
+
+void UpdateCheckJob::handleExtensionUpdates( const uno::Sequence< beans::NamedValue > &rListProp )
+{
+ try {
+ uno::Sequence< uno::Sequence< OUString > > aList =
+ getValue< uno::Sequence< uno::Sequence< OUString > > > ( rListProp, "updateList" );
+ bool bPrepareOnly = getValue< bool > ( rListProp, "prepareOnly" );
+
+ // we will first store any new found updates and then check, if there are any
+ // pending updates.
+ storeExtensionUpdateInfos( m_xContext, aList );
+
+ if ( bPrepareOnly )
+ return;
+
+ bool bHasUpdates = checkForPendingUpdates( m_xContext );
+
+ rtl::Reference<UpdateCheck> aController( UpdateCheck::get() );
+ if ( ! aController.is() )
+ return;
+
+ aController->setHasExtensionUpdates( bHasUpdates );
+
+ if ( ! aController->hasOfficeUpdate() )
+ {
+ if ( bHasUpdates )
+ aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL, true );
+ else
+ aController->setUIState( UPDATESTATE_NO_UPDATE_AVAIL, true );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated");
+ }
+}
+
+
+OUString SAL_CALL
+UpdateCheckJob::getImplementationName()
+{
+ return "vnd.sun.UpdateCheck";
+}
+
+
+uno::Sequence< OUString > SAL_CALL
+UpdateCheckJob::getSupportedServiceNames()
+{
+ return { "com.sun.star.setup.UpdateCheck" };
+}
+
+sal_Bool SAL_CALL
+UpdateCheckJob::supportsService( OUString const & serviceName )
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+
+// XEventListener
+void SAL_CALL UpdateCheckJob::disposing( lang::EventObject const & rEvt )
+{
+ bool shutDown = ( rEvt.Source == m_xDesktop );
+
+ if ( shutDown && m_xDesktop.is() )
+ {
+ terminateAndJoinThread();
+ m_xDesktop->removeTerminateListener( this );
+ m_xDesktop.clear();
+ }
+}
+
+
+// XTerminateListener
+void SAL_CALL UpdateCheckJob::queryTermination( lang::EventObject const & )
+{
+}
+
+void UpdateCheckJob::terminateAndJoinThread()
+{
+ if (m_pInitThread != nullptr)
+ {
+ m_pInitThread->setTerminating();
+ m_pInitThread->join();
+ m_pInitThread.reset();
+ }
+}
+
+void SAL_CALL UpdateCheckJob::notifyTermination( lang::EventObject const & )
+{
+ terminateAndJoinThread();
+}
+
+} // anonymous namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_update_UpdateCheckJob_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ css::uno::Reference<css::frame::XDesktop2> desktop(
+ css::frame::Desktop::create(context));
+ rtl::Reference<UpdateCheckJob> job(new UpdateCheckJob(context, desktop));
+ desktop->addTerminateListener(job);
+ return cppu::acquire(job.get());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updatehdl.cxx b/extensions/source/update/check/updatehdl.cxx
new file mode 100644
index 000000000..24fb96bda
--- /dev/null
+++ b/extensions/source/update/check/updatehdl.cxx
@@ -0,0 +1,1281 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstddef>
+
+#include "updatehdl.hxx"
+#include <helpids.h>
+
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <rtl/ustring.hxx>
+
+#include <com/sun/star/uno/Sequence.h>
+
+#include <com/sun/star/awt/ActionEvent.hpp>
+#include <com/sun/star/awt/PushButtonType.hpp>
+#include <com/sun/star/awt/UnoControlDialog.hpp>
+#include <com/sun/star/awt/VclWindowPeerAttribute.hpp>
+#include <com/sun/star/awt/WindowAttribute.hpp>
+#include <com/sun/star/awt/XButton.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/awt/XMessageBox.hpp>
+#include <com/sun/star/awt/XAnimation.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/awt/XVclContainer.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XWindow2.hpp>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/InteractionRequestStringResolver.hpp>
+
+#include <strings.hrc>
+#include <unotools/resmgr.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+
+constexpr OUStringLiteral COMMAND_CLOSE = u"close";
+
+constexpr OUStringLiteral CTRL_THROBBER = u"throbber";
+constexpr OUStringLiteral CTRL_PROGRESS = u"progress";
+
+constexpr OUStringLiteral TEXT_STATUS = u"text_status";
+constexpr OUStringLiteral TEXT_PERCENT = u"text_percent";
+constexpr OUStringLiteral TEXT_DESCRIPTION = u"text_description";
+
+constexpr OUStringLiteral FIXED_LINE_MODEL = u"com.sun.star.awt.UnoControlFixedLineModel";
+constexpr OUStringLiteral FIXED_TEXT_MODEL = u"com.sun.star.awt.UnoControlFixedTextModel";
+constexpr OUStringLiteral EDIT_FIELD_MODEL = u"com.sun.star.awt.UnoControlEditModel";
+constexpr OUStringLiteral BUTTON_MODEL = u"com.sun.star.awt.UnoControlButtonModel";
+constexpr OUStringLiteral GROUP_BOX_MODEL = u"com.sun.star.awt.UnoControlGroupBoxModel";
+
+using namespace com::sun::star;
+
+
+UpdateHandler::UpdateHandler( const uno::Reference< uno::XComponentContext > & rxContext,
+ const rtl::Reference< IActionListener > & rxActionListener ) :
+ mxContext( rxContext ),
+ mxActionListener( rxActionListener ),
+ meCurState( UPDATESTATES_COUNT ),
+ meLastState( UPDATESTATES_COUNT ),
+ mnPercent( 0 ),
+ mnLastCtrlState( -1 ),
+ mbDownloadBtnHasDots( false ),
+ mbVisible( false ),
+ mbStringsLoaded( false ),
+ mbMinimized( false ),
+ mbListenerAdded(false),
+ mbShowsMessageBox(false)
+{
+}
+
+
+UpdateHandler::~UpdateHandler()
+{
+ mxContext = nullptr;
+ mxUpdDlg = nullptr;
+ mxInteractionHdl = nullptr;
+ mxActionListener = nullptr;
+}
+
+
+void UpdateHandler::enableControls( short nCtrlState )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ if ( nCtrlState == mnLastCtrlState )
+ return;
+
+ // the help button should always be the last button in the
+ // enum list and must never be disabled
+ for ( int i=0; i<HELP_BUTTON; i++ )
+ {
+ short nCurStateVal = static_cast<short>(nCtrlState >> i);
+ short nOldStateVal = static_cast<short>(mnLastCtrlState >> i);
+ if ( ( nCurStateVal & 0x01 ) != ( nOldStateVal & 0x01 ) )
+ {
+ bool bEnableControl = ( ( nCurStateVal & 0x01 ) == 0x01 );
+ setControlProperty( msButtonIDs[i], "Enabled", uno::Any( bEnableControl ) );
+ }
+ }
+
+ mnLastCtrlState = nCtrlState;
+}
+
+
+void UpdateHandler::setDownloadBtnLabel( bool bAppendDots )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ if ( mbDownloadBtnHasDots != bAppendDots )
+ {
+ OUString aLabel( msDownload );
+
+ if ( bAppendDots )
+ aLabel += "...";
+
+ setControlProperty( msButtonIDs[DOWNLOAD_BUTTON], "Label", uno::Any( aLabel ) );
+ setControlProperty( msButtonIDs[DOWNLOAD_BUTTON], "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_DOWNLOAD2 )) );
+
+ mbDownloadBtnHasDots = bAppendDots;
+ }
+}
+
+
+void UpdateHandler::setState( UpdateState eState )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ meCurState = eState;
+
+ if ( mxUpdDlg.is() && mbVisible )
+ updateState( meCurState );
+}
+
+
+bool UpdateHandler::isVisible() const
+{
+ if ( !mxUpdDlg.is() ) return false;
+
+ uno::Reference< awt::XWindow2 > xWindow( mxUpdDlg, uno::UNO_QUERY );
+
+ if ( xWindow.is() )
+ return xWindow->isVisible();
+ else
+ return false;
+}
+
+
+void UpdateHandler::setVisible( bool bVisible )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ mbVisible = bVisible;
+
+ if ( bVisible )
+ {
+ if ( !mxUpdDlg.is() )
+ createDialog();
+
+ // this should never happen, but if it happens we better return here
+ if ( !mxUpdDlg.is() )
+ return;
+
+ updateState( meCurState );
+
+ uno::Reference< awt::XWindow > xWindow( mxUpdDlg, uno::UNO_QUERY );
+
+ if ( xWindow.is() )
+ xWindow->setVisible( bVisible );
+
+ uno::Reference< awt::XTopWindow > xTopWindow( mxUpdDlg, uno::UNO_QUERY );
+ if ( xTopWindow.is() )
+ {
+ xTopWindow->toFront();
+ if ( !mbListenerAdded )
+ {
+ xTopWindow->addTopWindowListener( this );
+ mbListenerAdded = true;
+ }
+ }
+ }
+ else if ( mxUpdDlg.is() )
+ {
+ uno::Reference< awt::XWindow > xWindow( mxUpdDlg, uno::UNO_QUERY );
+
+ if ( xWindow.is() )
+ xWindow->setVisible( bVisible );
+ }
+}
+
+
+void UpdateHandler::setProgress( sal_Int32 nPercent )
+{
+ if ( nPercent > 100 )
+ nPercent = 100;
+ else if ( nPercent < 0 )
+ nPercent = 0;
+
+ if ( nPercent != mnPercent )
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ mnPercent = nPercent;
+ setControlProperty( CTRL_PROGRESS, "ProgressValue", uno::Any( nPercent ) );
+ setControlProperty( TEXT_PERCENT, "Text", uno::Any( substVariables(msPercent) ) );
+ }
+}
+
+
+void UpdateHandler::setErrorMessage( const OUString& rErrorMsg )
+{
+ setControlProperty( TEXT_DESCRIPTION, "Text", uno::Any( rErrorMsg ) );
+}
+
+
+void UpdateHandler::setDownloadFile( std::u16string_view rFilePath )
+{
+ std::size_t nLast = rFilePath.rfind( '/' );
+ if ( nLast != std::u16string_view::npos )
+ {
+ msDownloadFile = rFilePath.substr( nLast+1 );
+ const OUString aDownloadURL(rFilePath.substr( 0, nLast ));
+ osl::FileBase::getSystemPathFromFileURL( aDownloadURL, msDownloadPath );
+ }
+}
+
+
+OUString UpdateHandler::getBubbleText( UpdateState eState )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ OUString sText;
+ sal_Int32 nIndex = static_cast<sal_Int32>(eState);
+
+ loadStrings();
+
+ if ( ( UPDATESTATE_UPDATE_AVAIL <= nIndex ) && ( nIndex < UPDATESTATES_COUNT ) )
+ sText = substVariables( msBubbleTexts[ nIndex - UPDATESTATE_UPDATE_AVAIL ] );
+
+ return sText;
+}
+
+
+OUString UpdateHandler::getBubbleTitle( UpdateState eState )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ OUString sText;
+ sal_Int32 nIndex = static_cast<sal_Int32>(eState);
+
+ loadStrings();
+
+ if ( ( UPDATESTATE_UPDATE_AVAIL <= nIndex ) && ( nIndex < UPDATESTATES_COUNT ) )
+ sText = substVariables( msBubbleTitles[ nIndex - UPDATESTATE_UPDATE_AVAIL] );
+
+ return sText;
+}
+
+
+OUString UpdateHandler::getDefaultInstErrMsg()
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ loadStrings();
+
+ return substVariables( msInstallError );
+}
+
+// XActionListener
+
+void SAL_CALL UpdateHandler::disposing( const lang::EventObject& rEvt )
+{
+ if ( rEvt.Source == mxUpdDlg )
+ mxUpdDlg.clear();
+}
+
+
+void SAL_CALL UpdateHandler::actionPerformed( awt::ActionEvent const & rEvent )
+{
+ DialogControls eButton = BUTTON_COUNT;
+ for ( int i = 0; i < BUTTON_COUNT; i++ )
+ {
+ if ( rEvent.ActionCommand == msButtonIDs[i] )
+ {
+ eButton = static_cast<DialogControls>(i);
+ break;
+ }
+ }
+
+ if ( rEvent.ActionCommand == COMMAND_CLOSE )
+ {
+ if ( ( mnLastCtrlState & ( 1 << CLOSE_BUTTON ) ) == ( 1 << CLOSE_BUTTON ) )
+ eButton = CLOSE_BUTTON;
+ else
+ eButton = CANCEL_BUTTON;
+ }
+
+ switch ( eButton ) {
+ case CANCEL_BUTTON:
+ {
+ bool bCancel = true;
+
+ if ( ( meCurState == UPDATESTATE_DOWNLOADING ) ||
+ ( meCurState == UPDATESTATE_DOWNLOAD_PAUSED ) ||
+ ( meCurState == UPDATESTATE_ERROR_DOWNLOADING ) )
+ bCancel = showWarning( msCancelMessage );
+
+ if ( bCancel )
+ {
+ mxActionListener->cancel();
+ setVisible( false );
+ }
+ break;
+ }
+ case CLOSE_BUTTON:
+ setVisible( false );
+ if ( meCurState == UPDATESTATE_ERROR_CHECKING )
+ mxActionListener->closeAfterFailure();
+ break;
+ case DOWNLOAD_BUTTON:
+ mxActionListener->download();
+ break;
+ case INSTALL_BUTTON:
+ if ( showWarning( msInstallMessage ) )
+ mxActionListener->install();
+ break;
+ case PAUSE_BUTTON:
+ mxActionListener->pause();
+ break;
+ case RESUME_BUTTON:
+ mxActionListener->resume();
+ break;
+ case HELP_BUTTON:
+ break;
+ default:
+ OSL_FAIL( "UpdateHandler::actionPerformed: unknown command!" );
+ }
+}
+
+// XTopWindowListener
+
+void SAL_CALL UpdateHandler::windowOpened( const lang::EventObject& )
+{
+}
+
+
+void SAL_CALL UpdateHandler::windowClosing( const lang::EventObject& e )
+{
+ awt::ActionEvent aActionEvt;
+ aActionEvt.ActionCommand = COMMAND_CLOSE;
+ aActionEvt.Source = e.Source;
+
+ actionPerformed( aActionEvt );
+}
+
+
+void SAL_CALL UpdateHandler::windowClosed( const lang::EventObject& )
+{
+}
+
+
+void SAL_CALL UpdateHandler::windowMinimized( const lang::EventObject& )
+{
+ mbMinimized = true;
+}
+
+
+void SAL_CALL UpdateHandler::windowNormalized( const lang::EventObject& )
+{
+ mbMinimized = false;
+}
+
+
+void SAL_CALL UpdateHandler::windowActivated( const lang::EventObject& )
+{
+}
+
+
+void SAL_CALL UpdateHandler::windowDeactivated( const lang::EventObject& )
+{
+}
+
+// XInteractionHandler
+
+void SAL_CALL UpdateHandler::handle( uno::Reference< task::XInteractionRequest > const & rRequest)
+{
+ if ( !mxInteractionHdl.is() )
+ {
+ if( !mxContext.is() )
+ throw uno::RuntimeException( "UpdateHandler:: empty component context", *this );
+
+ uno::Reference< lang::XMultiComponentFactory > xServiceManager(mxContext->getServiceManager());
+
+ if( !xServiceManager.is() )
+ throw uno::RuntimeException( "UpdateHandler: unable to obtain service manager from component context", *this );
+
+ mxInteractionHdl.set(
+ task::InteractionHandler::createWithParent(mxContext, nullptr),
+ uno::UNO_QUERY_THROW);
+ }
+ uno::Reference< task::XInteractionRequestStringResolver > xStrResolver =
+ task::InteractionRequestStringResolver::create( mxContext );
+ beans::Optional< OUString > aErrorText = xStrResolver->getStringFromInformationalRequest( rRequest );
+ if ( aErrorText.IsPresent )
+ {
+ setControlProperty( TEXT_DESCRIPTION, "Text", uno::Any( aErrorText.Value ) );
+
+ uno::Sequence< uno::Reference< task::XInteractionContinuation > > xContinuations = rRequest->getContinuations();
+ if ( xContinuations.getLength() == 1 )
+ {
+ if ( meCurState == UPDATESTATE_CHECKING )
+ setState( UPDATESTATE_ERROR_CHECKING );
+ else if ( meCurState == UPDATESTATE_DOWNLOADING )
+ setState( UPDATESTATE_ERROR_DOWNLOADING );
+
+ xContinuations[0]->select();
+ }
+ else
+ mxInteractionHdl->handle( rRequest );
+ }
+ else
+ mxInteractionHdl->handle( rRequest );
+}
+
+
+// XTerminateListener
+
+void SAL_CALL UpdateHandler::queryTermination( const lang::EventObject& )
+{
+ if ( mbShowsMessageBox )
+ {
+ uno::Reference< awt::XTopWindow > xTopWindow( mxUpdDlg, uno::UNO_QUERY );
+ if ( xTopWindow.is() )
+ xTopWindow->toFront();
+
+ throw frame::TerminationVetoException(
+ "The office cannot be closed while displaying a warning!",
+ static_cast<frame::XTerminateListener*>(this));
+ }
+ else
+ setVisible( false );
+}
+
+
+void SAL_CALL UpdateHandler::notifyTermination( const lang::EventObject& )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ if ( mxUpdDlg.is() )
+ {
+ uno::Reference< awt::XTopWindow > xTopWindow( mxUpdDlg, uno::UNO_QUERY );
+ if ( xTopWindow.is() )
+ xTopWindow->removeTopWindowListener( this );
+
+ uno::Reference< lang::XComponent > xComponent( mxUpdDlg, uno::UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+
+ mxUpdDlg.clear();
+ }
+}
+
+
+void UpdateHandler::updateState( UpdateState eState )
+{
+ if ( meLastState == eState )
+ return;
+
+ OUString sText;
+
+ switch ( eState )
+ {
+ case UPDATESTATE_CHECKING:
+ showControls( (1<<CANCEL_BUTTON) + (1<<THROBBER_CTRL) );
+ enableControls( 1<<CANCEL_BUTTON );
+ setControlProperty( TEXT_STATUS, "Text", uno::Any( substVariables(msChecking) ) );
+ setControlProperty( TEXT_DESCRIPTION, "Text", uno::Any( OUString() ) );
+ focusControl( CANCEL_BUTTON );
+ break;
+ case UPDATESTATE_ERROR_CHECKING:
+ showControls( 0 );
+ enableControls( 1 << CLOSE_BUTTON );
+ setControlProperty( TEXT_STATUS, "Text", uno::Any( substVariables(msCheckingError) ) );
+ focusControl( CLOSE_BUTTON );
+ break;
+ case UPDATESTATE_UPDATE_AVAIL:
+ showControls( 0 );
+ enableControls( ( 1 << CLOSE_BUTTON ) + ( 1 << DOWNLOAD_BUTTON ) );
+ setControlProperty( TEXT_STATUS, "Text", uno::Any( substVariables(msUpdFound) ) );
+
+ sText = substVariables(msDownloadWarning);
+ if ( !msDescriptionMsg.isEmpty() )
+ sText += "\n\n" + msDescriptionMsg;
+ setControlProperty( TEXT_DESCRIPTION, "Text", uno::Any( sText ) );
+
+ setDownloadBtnLabel( false );
+ focusControl( DOWNLOAD_BUTTON );
+ break;
+ case UPDATESTATE_UPDATE_NO_DOWNLOAD:
+ showControls( 0 );
+ enableControls( ( 1 << CLOSE_BUTTON ) + ( 1 << DOWNLOAD_BUTTON ) );
+ setControlProperty( TEXT_STATUS, "Text", uno::Any( substVariables(msUpdFound) ) );
+
+ sText = substVariables(msDownloadNotAvail);
+ if ( !msDescriptionMsg.isEmpty() )
+ sText += "\n\n" + msDescriptionMsg;
+ setControlProperty( TEXT_DESCRIPTION, "Text", uno::Any( sText ) );
+
+ setDownloadBtnLabel( true );
+ focusControl( DOWNLOAD_BUTTON );
+ break;
+ case UPDATESTATE_NO_UPDATE_AVAIL:
+ case UPDATESTATE_EXT_UPD_AVAIL: // will only be set, when there are no office updates avail
+ showControls( 0 );
+ enableControls( 1 << CLOSE_BUTTON );
+ setControlProperty( TEXT_STATUS, "Text", uno::Any( substVariables(msNoUpdFound) ) );
+ setControlProperty( TEXT_DESCRIPTION, "Text", uno::Any( OUString() ) );
+ focusControl( CLOSE_BUTTON );
+ break;
+ case UPDATESTATE_DOWNLOADING:
+ showControls( (1<<PROGRESS_CTRL) + (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) + (1<<RESUME_BUTTON) );
+ enableControls( (1<<CLOSE_BUTTON) + (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) );
+ setControlProperty( TEXT_STATUS, "Text", uno::Any( substVariables(msDownloading) ) );
+ setControlProperty( TEXT_PERCENT, "Text", uno::Any( substVariables(msPercent) ) );
+ setControlProperty( TEXT_DESCRIPTION, "Text", uno::Any( substVariables(msDownloadWarning) ) );
+ setControlProperty( CTRL_PROGRESS, "ProgressValue", uno::Any( mnPercent ) );
+ focusControl( CLOSE_BUTTON );
+ break;
+ case UPDATESTATE_DOWNLOAD_PAUSED:
+ showControls( (1<<PROGRESS_CTRL) + (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) + (1<<RESUME_BUTTON) );
+ enableControls( (1<<CLOSE_BUTTON) + (1<<CANCEL_BUTTON) + (1<<RESUME_BUTTON) );
+ setControlProperty( TEXT_STATUS, "Text", uno::Any( substVariables(msDownloadPause) ) );
+ setControlProperty( TEXT_PERCENT, "Text", uno::Any( substVariables(msPercent) ) );
+ setControlProperty( TEXT_DESCRIPTION, "Text", uno::Any( substVariables(msDownloadWarning) ) );
+ setControlProperty( CTRL_PROGRESS, "ProgressValue", uno::Any( mnPercent ) );
+ focusControl( CLOSE_BUTTON );
+ break;
+ case UPDATESTATE_ERROR_DOWNLOADING:
+ showControls( (1<<PROGRESS_CTRL) + (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) + (1<<RESUME_BUTTON) );
+ enableControls( (1<<CLOSE_BUTTON) + (1<<CANCEL_BUTTON) );
+ setControlProperty( TEXT_STATUS, "Text", uno::Any( substVariables(msDownloadError) ) );
+ focusControl( CLOSE_BUTTON );
+ break;
+ case UPDATESTATE_DOWNLOAD_AVAIL:
+ showControls( 0 );
+ enableControls( (1<<CLOSE_BUTTON) + (1<<INSTALL_BUTTON) );
+ setControlProperty( TEXT_STATUS, "Text", uno::Any( substVariables(msReady2Install) ) );
+ setControlProperty( TEXT_DESCRIPTION, "Text", uno::Any( substVariables(msDownloadDescr) ) );
+ focusControl( INSTALL_BUTTON );
+ break;
+ case UPDATESTATE_AUTO_START:
+ case UPDATESTATES_COUNT:
+ //do nothing, only count!
+ break;
+ }
+
+ meLastState = eState;
+}
+
+OUString UpdateHandler::loadString(const std::locale& rLocale,
+ TranslateId pResourceId)
+{
+ return Translate::get(pResourceId, rLocale);
+}
+
+OUString UpdateHandler::substVariables( const OUString &rSource ) const
+{
+ return rSource
+ .replaceAll( "%NEXTVERSION", msNextVersion )
+ .replaceAll( "%DOWNLOAD_PATH", msDownloadPath )
+ .replaceAll( "%FILE_NAME", msDownloadFile )
+ .replaceAll( "%PERCENT", OUString::number( mnPercent ) );
+}
+
+void UpdateHandler::loadStrings()
+{
+ if ( mbStringsLoaded )
+ return;
+ else
+ mbStringsLoaded = true;
+
+ std::locale loc = Translate::Create("pcr");
+
+ msChecking = loadString( loc, RID_UPDATE_STR_CHECKING );
+ msCheckingError = loadString( loc, RID_UPDATE_STR_CHECKING_ERR );
+ msNoUpdFound = loadString( loc, RID_UPDATE_STR_NO_UPD_FOUND );
+
+ msUpdFound = loadString( loc, RID_UPDATE_STR_UPD_FOUND );
+ setFullVersion( msUpdFound );
+
+ msDlgTitle = loadString( loc, RID_UPDATE_STR_DLG_TITLE );
+ msDownloadPause = loadString( loc, RID_UPDATE_STR_DOWNLOAD_PAUSE );
+ msDownloadError = loadString( loc, RID_UPDATE_STR_DOWNLOAD_ERR );
+ msDownloadWarning = loadString( loc, RID_UPDATE_STR_DOWNLOAD_WARN );
+ msDownloadDescr = loadString( loc, RID_UPDATE_STR_DOWNLOAD_DESCR );
+ msDownloadNotAvail = loadString( loc, RID_UPDATE_STR_DOWNLOAD_UNAVAIL );
+ msDownloading = loadString( loc, RID_UPDATE_STR_DOWNLOADING );
+ msReady2Install = loadString( loc, RID_UPDATE_STR_READY_INSTALL );
+ msCancelMessage = loadString( loc, RID_UPDATE_STR_CANCEL_DOWNLOAD );
+ msInstallMessage = loadString( loc, RID_UPDATE_STR_BEGIN_INSTALL );
+ msInstallError = loadString( loc, RID_UPDATE_STR_INSTALL_ERROR );
+ msOverwriteWarning = loadString( loc, RID_UPDATE_STR_OVERWRITE_WARNING );
+ msPercent = loadString( loc, RID_UPDATE_STR_PERCENT );
+ msReloadWarning = loadString( loc, RID_UPDATE_STR_RELOAD_WARNING );
+ msReloadReload = loadString( loc, RID_UPDATE_STR_RELOAD_RELOAD );
+ msReloadContinue = loadString( loc, RID_UPDATE_STR_RELOAD_CONTINUE );
+
+ msStatusFL = loadString( loc, RID_UPDATE_FT_STATUS );
+ msDescription = loadString( loc, RID_UPDATE_FT_DESCRIPTION );
+
+ msClose = loadString( loc, RID_UPDATE_BTN_CLOSE );
+ msDownload = loadString( loc, RID_UPDATE_BTN_DOWNLOAD );
+ msInstall = loadString( loc, RID_UPDATE_BTN_INSTALL );
+ msPauseBtn = loadString( loc, RID_UPDATE_BTN_PAUSE );
+ msResumeBtn = loadString( loc, RID_UPDATE_BTN_RESUME );
+ msCancelBtn = loadString( loc, RID_UPDATE_BTN_CANCEL );
+
+ std::pair<TranslateId, TranslateId> RID_UPDATE_BUBBLE[] =
+ {
+ { RID_UPDATE_BUBBLE_UPDATE_AVAIL, RID_UPDATE_BUBBLE_T_UPDATE_AVAIL },
+ { RID_UPDATE_BUBBLE_UPDATE_NO_DOWN, RID_UPDATE_BUBBLE_T_UPDATE_NO_DOWN },
+ { RID_UPDATE_BUBBLE_AUTO_START, RID_UPDATE_BUBBLE_T_AUTO_START },
+ { RID_UPDATE_BUBBLE_DOWNLOADING, RID_UPDATE_BUBBLE_T_DOWNLOADING },
+ { RID_UPDATE_BUBBLE_DOWNLOAD_PAUSED, RID_UPDATE_BUBBLE_T_DOWNLOAD_PAUSED },
+ { RID_UPDATE_BUBBLE_ERROR_DOWNLOADING, RID_UPDATE_BUBBLE_T_ERROR_DOWNLOADING },
+ { RID_UPDATE_BUBBLE_DOWNLOAD_AVAIL, RID_UPDATE_BUBBLE_T_DOWNLOAD_AVAIL },
+ { RID_UPDATE_BUBBLE_EXT_UPD_AVAIL, RID_UPDATE_BUBBLE_T_EXT_UPD_AVAIL }
+ };
+
+ static_assert(SAL_N_ELEMENTS(RID_UPDATE_BUBBLE) == UPDATESTATES_COUNT - UPDATESTATE_UPDATE_AVAIL, "mismatch");
+
+ // all update states before UPDATESTATE_UPDATE_AVAIL don't have a bubble
+ // so we can ignore them
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UPDATE_BUBBLE); ++i)
+ {
+ msBubbleTexts[i] = loadString(loc, RID_UPDATE_BUBBLE[i].first);
+ msBubbleTitles[i] = loadString(loc, RID_UPDATE_BUBBLE[i].second);
+ }
+
+ for ( int i=0; i < BUTTON_COUNT; i++ )
+ {
+ msButtonIDs[ i ] = "BUTTON_" + OUString::number( i );
+ }
+}
+
+
+void UpdateHandler::startThrobber( bool bStart )
+{
+ uno::Reference< awt::XControlContainer > xContainer( mxUpdDlg, uno::UNO_QUERY );
+ uno::Reference< awt::XAnimation > xThrobber( xContainer->getControl( CTRL_THROBBER ), uno::UNO_QUERY );
+
+ if ( xThrobber.is() )
+ {
+ if ( bStart )
+ xThrobber->startAnimation();
+ else
+ xThrobber->stopAnimation();
+ }
+
+ uno::Reference< awt::XWindow > xWindow( xContainer->getControl( CTRL_THROBBER ), uno::UNO_QUERY );
+ if (xWindow.is() )
+ xWindow->setVisible( bStart );
+}
+
+
+void UpdateHandler::setControlProperty( const OUString &rCtrlName,
+ const OUString &rPropName,
+ const uno::Any &rPropValue )
+{
+ if ( !mxUpdDlg.is() ) return;
+
+ uno::Reference< awt::XControlContainer > xContainer( mxUpdDlg, uno::UNO_QUERY );
+ uno::Reference< awt::XControl > xControl( xContainer->getControl( rCtrlName ), uno::UNO_SET_THROW );
+ uno::Reference< awt::XControlModel > xControlModel( xControl->getModel(), uno::UNO_SET_THROW );
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY_THROW );
+
+ try {
+ xPropSet->setPropertyValue( rPropName, rPropValue );
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ TOOLS_WARN_EXCEPTION( "extensions.update", "UpdateHandler::setControlProperty" );
+ }
+}
+
+
+void UpdateHandler::showControl( const OUString &rCtrlName, bool bShow )
+{
+ uno::Reference< awt::XControlContainer > xContainer( mxUpdDlg, uno::UNO_QUERY );
+
+ if ( !xContainer.is() )
+ {
+ OSL_FAIL( "UpdateHandler::showControl: could not get control container!" );
+ return;
+ }
+
+ uno::Reference< awt::XWindow > xWindow( xContainer->getControl( rCtrlName ), uno::UNO_QUERY );
+ if ( xWindow.is() )
+ xWindow->setVisible( bShow );
+}
+
+
+void UpdateHandler::focusControl( DialogControls eID )
+{
+ uno::Reference< awt::XControlContainer > xContainer( mxUpdDlg, uno::UNO_QUERY );
+
+ if ( !xContainer.is() )
+ {
+ OSL_FAIL( "UpdateHandler::focusControl: could not get control container!" );
+ return;
+ }
+
+ OSL_ENSURE( (eID < BUTTON_COUNT), "UpdateHandler::focusControl: id too big!" );
+
+ uno::Reference< awt::XWindow > xWindow( xContainer->getControl( msButtonIDs[static_cast<short>(eID)] ), uno::UNO_QUERY );
+ if ( xWindow.is() )
+ xWindow->setFocus();
+}
+
+
+void UpdateHandler::insertControlModel( uno::Reference< awt::XControlModel > const & rxDialogModel,
+ OUString const & rServiceName,
+ OUString const & rControlName,
+ awt::Rectangle const & rPosSize,
+ uno::Sequence< beans::NamedValue > const & rProps )
+{
+ uno::Reference< lang::XMultiServiceFactory > xFactory (rxDialogModel, uno::UNO_QUERY_THROW);
+ uno::Reference< awt::XControlModel > xModel (xFactory->createInstance (rServiceName), uno::UNO_QUERY_THROW);
+ uno::Reference< beans::XPropertySet > xPropSet (xModel, uno::UNO_QUERY_THROW);
+
+ for (beans::NamedValue const & prop : rProps)
+ {
+ xPropSet->setPropertyValue (prop.Name, prop.Value);
+ }
+
+ // @see awt/UnoControlDialogElement.idl
+ xPropSet->setPropertyValue( "Name", uno::Any (rControlName) );
+ xPropSet->setPropertyValue( "PositionX", uno::Any (rPosSize.X) );
+ xPropSet->setPropertyValue( "PositionY", uno::Any (rPosSize.Y) );
+ xPropSet->setPropertyValue( "Height", uno::Any (rPosSize.Height) );
+ xPropSet->setPropertyValue( "Width", uno::Any (rPosSize.Width) );
+
+ // insert by Name into DialogModel container
+ uno::Reference< container::XNameContainer > xContainer (rxDialogModel, uno::UNO_QUERY_THROW);
+ xContainer->insertByName( rControlName, uno::Any (uno::Reference< uno::XInterface >(xModel, uno::UNO_QUERY)));
+}
+
+
+void UpdateHandler::setFullVersion( OUString& rString )
+{
+ uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider(
+ css::configuration::theDefaultProvider::get( mxContext ) );
+
+ beans::PropertyValue aProperty;
+ aProperty.Name = "nodepath";
+ aProperty.Value <<= OUString("org.openoffice.Setup/Product");
+
+ uno::Sequence< uno::Any > aArgumentList{ uno::Any(aProperty) };
+
+ uno::Reference< uno::XInterface > xConfigAccess = xConfigurationProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
+ aArgumentList );
+
+ uno::Reference< container::XNameAccess > xNameAccess( xConfigAccess, uno::UNO_QUERY_THROW );
+
+ OUString aProductVersion;
+ xNameAccess->getByName("ooSetupVersion") >>= aProductVersion;
+ OUString aProductFullVersion;
+ xNameAccess->getByName("ooSetupVersionAboutBox") >>= aProductFullVersion;
+ rString = rString.replaceFirst( aProductVersion, aProductFullVersion );
+}
+
+
+bool UpdateHandler::showWarning( const OUString &rWarningText ) const
+{
+ bool bRet = false;
+
+ uno::Reference< awt::XControl > xControl( mxUpdDlg, uno::UNO_QUERY );
+ if ( !xControl.is() ) return bRet;
+
+ uno::Reference< awt::XWindowPeer > xPeer = xControl->getPeer();
+ if ( !xPeer.is() ) return bRet;
+
+ uno::Reference< awt::XToolkit > xToolkit = xPeer->getToolkit();
+ if ( !xToolkit.is() ) return bRet;
+
+ awt::WindowDescriptor aDescriptor;
+
+ sal_Int32 nWindowAttributes = awt::WindowAttribute::BORDER | awt::WindowAttribute::MOVEABLE | awt::WindowAttribute::CLOSEABLE;
+ nWindowAttributes |= awt::VclWindowPeerAttribute::YES_NO;
+ nWindowAttributes |= awt::VclWindowPeerAttribute::DEF_NO;
+
+ aDescriptor.Type = awt::WindowClass_MODALTOP;
+ aDescriptor.WindowServiceName = "warningbox";
+ aDescriptor.ParentIndex = -1;
+ aDescriptor.Parent = xPeer;
+ aDescriptor.Bounds = awt::Rectangle( 10, 10, 250, 150 );
+ aDescriptor.WindowAttributes = nWindowAttributes;
+
+ uno::Reference< awt::XMessageBox > xMsgBox( xToolkit->createWindow( aDescriptor ), uno::UNO_QUERY );
+ if ( xMsgBox.is() )
+ {
+ mbShowsMessageBox = true;
+ sal_Int16 nRet;
+ // xMsgBox->setCaptionText( msCancelTitle );
+ xMsgBox->setMessageText( rWarningText );
+ nRet = xMsgBox->execute();
+ if ( nRet == 2 ) // RET_YES == 2
+ bRet = true;
+ mbShowsMessageBox = false;
+ }
+
+ uno::Reference< lang::XComponent > xComponent( xMsgBox, uno::UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+
+ return bRet;
+}
+
+
+bool UpdateHandler::showWarning( const OUString &rWarningText,
+ const OUString &rBtnText_1,
+ const OUString &rBtnText_2 ) const
+{
+ bool bRet = false;
+
+ uno::Reference< awt::XControl > xControl( mxUpdDlg, uno::UNO_QUERY );
+ if ( !xControl.is() ) return bRet;
+
+ uno::Reference< awt::XWindowPeer > xPeer = xControl->getPeer();
+ if ( !xPeer.is() ) return bRet;
+
+ uno::Reference< awt::XToolkit > xToolkit = xPeer->getToolkit();
+ if ( !xToolkit.is() ) return bRet;
+
+ awt::WindowDescriptor aDescriptor;
+
+ sal_Int32 nWindowAttributes = awt::WindowAttribute::BORDER | awt::WindowAttribute::MOVEABLE | awt::WindowAttribute::CLOSEABLE;
+ nWindowAttributes |= awt::VclWindowPeerAttribute::YES_NO;
+ nWindowAttributes |= awt::VclWindowPeerAttribute::DEF_NO;
+
+ aDescriptor.Type = awt::WindowClass_MODALTOP;
+ aDescriptor.WindowServiceName = "warningbox";
+ aDescriptor.ParentIndex = -1;
+ aDescriptor.Parent = xPeer;
+ aDescriptor.Bounds = awt::Rectangle( 10, 10, 250, 150 );
+ aDescriptor.WindowAttributes = nWindowAttributes;
+
+ uno::Reference< awt::XMessageBox > xMsgBox( xToolkit->createWindow( aDescriptor ), uno::UNO_QUERY );
+ if ( xMsgBox.is() )
+ {
+ uno::Reference< awt::XVclContainer > xMsgBoxCtrls( xMsgBox, uno::UNO_QUERY );
+ if ( xMsgBoxCtrls.is() )
+ {
+ uno::Sequence< uno::Reference< awt::XWindow > > xChildren = xMsgBoxCtrls->getWindows();
+
+ for ( uno::Reference< awt::XWindow > const & child : std::as_const(xChildren) )
+ {
+ uno::Reference< awt::XVclWindowPeer > xMsgBoxCtrl( child, uno::UNO_QUERY );
+ if ( xMsgBoxCtrl.is() )
+ {
+ bool bIsDefault = true;
+ uno::Any aValue = xMsgBoxCtrl->getProperty( "DefaultButton" );
+ aValue >>= bIsDefault;
+ if ( bIsDefault )
+ xMsgBoxCtrl->setProperty( "Text", uno::Any( rBtnText_1 ) );
+ else
+ xMsgBoxCtrl->setProperty( "Text", uno::Any( rBtnText_2 ) );
+ }
+ }
+ }
+
+ sal_Int16 nRet;
+ // xMsgBox->setCaptionText( msCancelTitle );
+ mbShowsMessageBox = true;
+ xMsgBox->setMessageText( rWarningText );
+ nRet = xMsgBox->execute();
+ if ( nRet == 2 ) // RET_YES == 2
+ bRet = true;
+
+ mbShowsMessageBox = false;
+ }
+
+ uno::Reference< lang::XComponent > xComponent( xMsgBox, uno::UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+
+ return bRet;
+}
+
+
+bool UpdateHandler::showOverwriteWarning( std::u16string_view rFileName ) const
+{
+ return showWarning(
+ (msReloadWarning
+ .replaceAll( "%FILENAME", rFileName )
+ .replaceAll( "%DOWNLOAD_PATH", msDownloadPath )),
+ msReloadContinue, msReloadReload );
+}
+
+
+bool UpdateHandler::showOverwriteWarning() const
+{
+ return showWarning( msOverwriteWarning );
+}
+
+
+#define BUTTON_HEIGHT 14
+#define BUTTON_WIDTH 50
+#define BUTTON_X_OFFSET 7
+#define BUTTON_Y_OFFSET 3
+#define LABEL_HEIGHT 10
+
+#define DIALOG_WIDTH 300
+#define DIALOG_BORDER 5
+#define INNER_BORDER 3
+#define TEXT_OFFSET 1
+#define BOX_HEIGHT1 ( LABEL_HEIGHT + 3*BUTTON_HEIGHT + 2*BUTTON_Y_OFFSET + 2*INNER_BORDER )
+#define BOX_HEIGHT2 50
+#define EDIT_WIDTH ( DIALOG_WIDTH - 2 * DIALOG_BORDER )
+#define BOX1_BTN_X ( DIALOG_BORDER + EDIT_WIDTH - BUTTON_WIDTH - INNER_BORDER )
+#define BOX1_BTN_Y ( DIALOG_BORDER + LABEL_HEIGHT + INNER_BORDER)
+#define THROBBER_WIDTH 16
+#define THROBBER_HEIGHT 16
+#define THROBBER_X_POS ( DIALOG_BORDER + 8 )
+#define THROBBER_Y_POS ( DIALOG_BORDER + 23 )
+#define BUTTON_BAR_HEIGHT 24
+#define LABEL_OFFSET ( LABEL_HEIGHT + 4 )
+#define DIALOG_HEIGHT ( BOX_HEIGHT1 + BOX_HEIGHT2 + LABEL_OFFSET + BUTTON_BAR_HEIGHT + 3 * DIALOG_BORDER )
+#define LABEL_Y_POS ( 2 * DIALOG_BORDER + BOX_HEIGHT1 )
+#define EDIT2_Y_POS ( LABEL_Y_POS + LABEL_HEIGHT )
+#define BUTTON_BAR_Y_POS ( EDIT2_Y_POS + DIALOG_BORDER + BOX_HEIGHT2 )
+#define BUTTON_Y_POS ( BUTTON_BAR_Y_POS + 8 )
+#define CLOSE_BTN_X ( DIALOG_WIDTH - DIALOG_BORDER - BUTTON_WIDTH )
+#define INSTALL_BTN_X ( CLOSE_BTN_X - 2 * BUTTON_X_OFFSET - BUTTON_WIDTH )
+#define DOWNLOAD_BTN_X ( INSTALL_BTN_X - BUTTON_X_OFFSET - BUTTON_WIDTH )
+#define PROGRESS_WIDTH 80
+#define PROGRESS_HEIGHT 10
+#define PROGRESS_X_POS ( DIALOG_BORDER + 8 )
+#define PROGRESS_Y_POS ( DIALOG_BORDER + 2*LABEL_OFFSET )
+
+
+void UpdateHandler::showControls( short nControls )
+{
+ // The buttons from CANCEL_BUTTON to RESUME_BUTTON will be shown or
+ // hidden on demand
+ short nShiftMe;
+ for ( int i = 0; i <= int(RESUME_BUTTON); i++ )
+ {
+ nShiftMe = static_cast<short>(nControls >> i);
+ showControl( msButtonIDs[i], static_cast<bool>(nShiftMe & 0x01) );
+ }
+
+ nShiftMe = static_cast<short>(nControls >> THROBBER_CTRL);
+ startThrobber( static_cast<bool>(nShiftMe & 0x01) );
+
+ nShiftMe = static_cast<short>(nControls >> PROGRESS_CTRL);
+ showControl( CTRL_PROGRESS, static_cast<bool>(nShiftMe & 0x01) );
+ showControl( TEXT_PERCENT, static_cast<bool>(nShiftMe & 0x01) );
+
+ // Status text needs to be smaller, when there are buttons at the right side of the dialog
+ if ( ( nControls & ( (1<<CANCEL_BUTTON) + (1<<PAUSE_BUTTON) + (1<<RESUME_BUTTON) ) ) != 0 )
+ setControlProperty( TEXT_STATUS, "Width", uno::Any( sal_Int32(EDIT_WIDTH - BUTTON_WIDTH - 2*INNER_BORDER - TEXT_OFFSET ) ) );
+ else
+ setControlProperty( TEXT_STATUS, "Width", uno::Any( sal_Int32(EDIT_WIDTH - 2*TEXT_OFFSET ) ) );
+
+ // Status text needs to be taller, when we show the progress bar
+ if ( ( nControls & ( 1<<PROGRESS_CTRL ) ) != 0 )
+ setControlProperty( TEXT_STATUS, "Height", uno::Any( sal_Int32(LABEL_HEIGHT) ) );
+ else
+ setControlProperty( TEXT_STATUS, "Height", uno::Any( sal_Int32(BOX_HEIGHT1 - 4*TEXT_OFFSET - LABEL_HEIGHT ) ) );
+}
+
+
+void UpdateHandler::createDialog()
+{
+ if ( !mxContext.is() )
+ {
+ OSL_ASSERT( false );
+ return;
+ }
+
+ if( mxContext.is() )
+ {
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( mxContext );
+ xDesktop->addTerminateListener( this );
+ }
+
+ loadStrings();
+
+ uno::Reference< lang::XMultiComponentFactory > xFactory( mxContext->getServiceManager(), uno::UNO_SET_THROW );
+ uno::Reference< awt::XControlModel > xControlModel( xFactory->createInstanceWithContext(
+ "com.sun.star.awt.UnoControlDialogModel",
+ mxContext), uno::UNO_QUERY_THROW );
+ {
+ // @see awt/UnoControlDialogModel.idl
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY_THROW );
+
+ xPropSet->setPropertyValue( "Title", uno::Any( msDlgTitle ) );
+ xPropSet->setPropertyValue( "Closeable", uno::Any( true ) );
+ xPropSet->setPropertyValue( "Enabled", uno::Any( true ) );
+ xPropSet->setPropertyValue( "Moveable", uno::Any( true ) );
+ xPropSet->setPropertyValue( "Sizeable", uno::Any( true ) );
+ xPropSet->setPropertyValue( "DesktopAsParent", uno::Any( true ) );
+ xPropSet->setPropertyValue( "PositionX", uno::Any(sal_Int32( 100 )) );
+ xPropSet->setPropertyValue( "PositionY", uno::Any(sal_Int32( 100 )) );
+ xPropSet->setPropertyValue( "Width", uno::Any(sal_Int32( DIALOG_WIDTH )) );
+ xPropSet->setPropertyValue( "Height", uno::Any(sal_Int32( DIALOG_HEIGHT )) );
+ xPropSet->setPropertyValue( "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_DLG )) );
+ }
+ { // Label (fixed text) <status>
+ uno::Sequence< beans::NamedValue > aProps { { "Label", uno::Any( msStatusFL ) } };
+
+ insertControlModel( xControlModel, FIXED_TEXT_MODEL, "fixedLineStatus",
+ awt::Rectangle( DIALOG_BORDER+1, DIALOG_BORDER, EDIT_WIDTH-2, LABEL_HEIGHT ),
+ aProps );
+ }
+ { // box around <status> text
+ uno::Sequence< beans::NamedValue > aProps;
+
+ insertControlModel( xControlModel, GROUP_BOX_MODEL, "StatusBox",
+ awt::Rectangle( DIALOG_BORDER, DIALOG_BORDER + LABEL_HEIGHT, EDIT_WIDTH, BOX_HEIGHT1 - LABEL_HEIGHT ),
+ aProps );
+ }
+ { // Text (multiline edit) <status>
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "Text", uno::Any( substVariables(msChecking) ) },
+ { "Border", uno::Any( sal_Int16( 0 ) ) },
+ { "PaintTransparent", uno::Any( true ) },
+ { "MultiLine", uno::Any( true ) },
+ { "ReadOnly", uno::Any( true ) },
+ { "AutoVScroll", uno::Any( true ) },
+ { "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_STATUS )) }
+ };
+
+ insertControlModel( xControlModel, EDIT_FIELD_MODEL, TEXT_STATUS,
+ awt::Rectangle( DIALOG_BORDER + TEXT_OFFSET,
+ DIALOG_BORDER + LABEL_HEIGHT + TEXT_OFFSET,
+ EDIT_WIDTH - 2*TEXT_OFFSET,
+ BOX_HEIGHT1 - 4*TEXT_OFFSET - LABEL_HEIGHT ),
+ aProps );
+ }
+ { // Text (edit) <percent>
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "Text", uno::Any( substVariables(msPercent) ) },
+ { "Border", uno::Any( sal_Int16( 0 ) ) },
+ { "PaintTransparent", uno::Any( true ) },
+ { "ReadOnly", uno::Any( true ) },
+ };
+
+ insertControlModel( xControlModel, EDIT_FIELD_MODEL, TEXT_PERCENT,
+ awt::Rectangle( PROGRESS_X_POS + PROGRESS_WIDTH + DIALOG_BORDER,
+ PROGRESS_Y_POS,
+ EDIT_WIDTH - PROGRESS_WIDTH - BUTTON_WIDTH - 2*DIALOG_BORDER,
+ LABEL_HEIGHT ),
+ aProps );
+ }
+ { // pause button
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "DefaultButton", uno::Any( false ) },
+ { "Enabled", uno::Any( true ) },
+ { "PushButtonType", uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) },
+ { "Label", uno::Any( msPauseBtn ) },
+ { "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_PAUSE )) }
+ };
+
+ insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[PAUSE_BUTTON],
+ awt::Rectangle( BOX1_BTN_X, BOX1_BTN_Y, BUTTON_WIDTH, BUTTON_HEIGHT ),
+ aProps );
+ }
+ { // resume button
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "DefaultButton", uno::Any( false ) },
+ { "Enabled", uno::Any( true ) },
+ { "PushButtonType", uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) },
+ { "Label", uno::Any( msResumeBtn ) },
+ { "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_RESUME )) }
+ };
+
+ insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[RESUME_BUTTON],
+ awt::Rectangle( BOX1_BTN_X,
+ BOX1_BTN_Y + BUTTON_Y_OFFSET + BUTTON_HEIGHT,
+ BUTTON_WIDTH,
+ BUTTON_HEIGHT ),
+ aProps );
+ }
+ { // abort button
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "DefaultButton", uno::Any( false ) },
+ { "Enabled", uno::Any( true ) },
+ { "PushButtonType", uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) },
+ { "Label", uno::Any( msCancelBtn ) },
+ { "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_CANCEL )) }
+ };
+
+ insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[CANCEL_BUTTON],
+ awt::Rectangle( BOX1_BTN_X,
+ BOX1_BTN_Y + (2*(BUTTON_HEIGHT+BUTTON_Y_OFFSET)),
+ BUTTON_WIDTH,
+ BUTTON_HEIGHT ),
+ aProps );
+ }
+ { // Label (FixedText) <description>
+ uno::Sequence< beans::NamedValue > aProps { { "Label", uno::Any( msDescription ) } };
+
+ insertControlModel( xControlModel, FIXED_TEXT_MODEL, "fixedTextDescription",
+ awt::Rectangle( DIALOG_BORDER+1, LABEL_Y_POS, EDIT_WIDTH-2, LABEL_HEIGHT ),
+ aProps );
+ }
+ { // box around <description> text
+ uno::Sequence< beans::NamedValue > aProps;
+
+ insertControlModel( xControlModel, GROUP_BOX_MODEL, "DescriptionBox",
+ awt::Rectangle( DIALOG_BORDER, EDIT2_Y_POS, EDIT_WIDTH, BOX_HEIGHT2 ),
+ aProps );
+ }
+ { // Text (MultiLineEdit) <description>
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "Text", uno::Any( OUString() ) },
+ { "Border", uno::Any( sal_Int16( 0 ) ) },
+ { "PaintTransparent", uno::Any( true ) },
+ { "MultiLine", uno::Any( true ) },
+ { "ReadOnly", uno::Any( true ) },
+ { "AutoVScroll", uno::Any( true ) },
+ { "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_DESCRIPTION )) }
+ };
+
+ insertControlModel( xControlModel, EDIT_FIELD_MODEL, TEXT_DESCRIPTION,
+ awt::Rectangle( DIALOG_BORDER + TEXT_OFFSET,
+ EDIT2_Y_POS + 2*TEXT_OFFSET,
+ EDIT_WIDTH - 3*TEXT_OFFSET,
+ BOX_HEIGHT2 - 3*TEXT_OFFSET ),
+ aProps );
+ }
+ { // @see awt/UnoControlFixedLineModel.idl
+ uno::Sequence< beans::NamedValue > aProps { { "Orientation", uno::Any( sal_Int32( 0 ) ) } };
+
+ insertControlModel( xControlModel, FIXED_LINE_MODEL, "fixedLine",
+ awt::Rectangle( 0, BUTTON_BAR_Y_POS, DIALOG_WIDTH, 5 ),
+ aProps );
+ }
+ { // close button // @see awt/UnoControlButtonModel.idl
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "DefaultButton", uno::Any( false ) },
+ { "Enabled", uno::Any( true ) },
+ // [property] short PushButtonType
+ // with own "ButtonActionListener"
+ { "PushButtonType", uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) },
+ // with default ActionListener => endDialog().
+ // setProperty( aProps, 2, "PushButtonType", uno::Any( sal_Int16(awt::PushButtonType_CANCEL) ) );
+ // [property] string Label // only if PushButtonType_STANDARD
+ { "Label", uno::Any( msClose ) },
+ { "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_CLOSE )) }
+ };
+
+ insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[ CLOSE_BUTTON ],
+ awt::Rectangle( CLOSE_BTN_X, BUTTON_Y_POS, BUTTON_WIDTH, BUTTON_HEIGHT ),
+ aProps );
+ }
+ { // install button
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "DefaultButton", uno::Any( false ) },
+ { "Enabled", uno::Any( true ) },
+ { "PushButtonType", uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) },
+ { "Label", uno::Any( msInstall ) },
+ { "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_INSTALL )) }
+ };
+
+ insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[INSTALL_BUTTON],
+ awt::Rectangle( INSTALL_BTN_X, BUTTON_Y_POS, BUTTON_WIDTH, BUTTON_HEIGHT ),
+ aProps );
+ }
+ { // download button
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "DefaultButton", uno::Any( false ) },
+ { "Enabled", uno::Any( true ) },
+ { "PushButtonType", uno::Any( sal_Int16(awt::PushButtonType_STANDARD) ) },
+ { "Label", uno::Any( msDownload ) },
+ { "HelpURL", uno::Any(OUString( INET_HID_SCHEME + HID_CHECK_FOR_UPD_DOWNLOAD )) }
+ };
+
+ insertControlModel ( xControlModel, BUTTON_MODEL, msButtonIDs[DOWNLOAD_BUTTON],
+ awt::Rectangle( DOWNLOAD_BTN_X, BUTTON_Y_POS, BUTTON_WIDTH, BUTTON_HEIGHT ),
+ aProps );
+ }
+ { // help button
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "DefaultButton", uno::Any( false ) },
+ { "Enabled", uno::Any( true ) },
+ { "PushButtonType", uno::Any( sal_Int16(awt::PushButtonType_HELP) ) }
+ };
+
+ insertControlModel( xControlModel, BUTTON_MODEL, msButtonIDs[HELP_BUTTON],
+ awt::Rectangle( DIALOG_BORDER, BUTTON_Y_POS, BUTTON_WIDTH, BUTTON_HEIGHT ),
+ aProps );
+ }
+ { // @see awt/UnoControlThrobberModel.idl
+ uno::Sequence< beans::NamedValue > aProps;
+
+ insertControlModel( xControlModel, "com.sun.star.awt.SpinningProgressControlModel", CTRL_THROBBER,
+ awt::Rectangle( THROBBER_X_POS, THROBBER_Y_POS, THROBBER_WIDTH, THROBBER_HEIGHT),
+ aProps );
+ }
+ { // @see awt/UnoControlProgressBarModel.idl
+ uno::Sequence< beans::NamedValue > aProps
+ {
+ { "Enabled", uno::Any( true ) },
+ { "ProgressValue", uno::Any( sal_Int32( 0 ) ) },
+ { "ProgressValueMax", uno::Any( sal_Int32( 100 ) ) },
+ { "ProgressValueMin", uno::Any( sal_Int32( 0 ) ) },
+ };
+ insertControlModel( xControlModel, "com.sun.star.awt.UnoControlProgressBarModel", CTRL_PROGRESS,
+ awt::Rectangle( PROGRESS_X_POS, PROGRESS_Y_POS, PROGRESS_WIDTH, PROGRESS_HEIGHT ),
+ aProps);
+ }
+
+ uno::Reference< awt::XUnoControlDialog > xControl = awt::UnoControlDialog::create( mxContext );
+ xControl->setModel( xControlModel );
+
+ if ( !mbVisible )
+ {
+ xControl->setVisible( false );
+ }
+
+ xControl->createPeer( nullptr, nullptr );
+ {
+ for ( int i = 0; i < HELP_BUTTON; i++ )
+ {
+ uno::Reference< awt::XButton > xButton ( xControl->getControl( msButtonIDs[i] ), uno::UNO_QUERY);
+ if (xButton.is())
+ {
+ xButton->setActionCommand( msButtonIDs[i] );
+ xButton->addActionListener( this );
+ }
+ }
+ }
+
+ mxUpdDlg.set( xControl, uno::UNO_QUERY_THROW );
+ mnLastCtrlState = -1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updatehdl.hxx b/extensions/source/update/check/updatehdl.hxx
new file mode 100644
index 000000000..297cf730c
--- /dev/null
+++ b/extensions/source/update/check/updatehdl.hxx
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <osl/mutex.hxx>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/awt/XActionListener.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/awt/XDialog.hpp>
+#include <com/sun/star/awt/XTopWindowListener.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <unotools/resmgr.hxx>
+#include <rtl/ref.hxx>
+
+#include "actionlistener.hxx"
+
+enum DialogControls
+{
+ CANCEL_BUTTON = 0,
+ PAUSE_BUTTON,
+ RESUME_BUTTON,
+ INSTALL_BUTTON,
+ DOWNLOAD_BUTTON,
+ CLOSE_BUTTON,
+ HELP_BUTTON,
+ BUTTON_COUNT,
+ THROBBER_CTRL,
+ PROGRESS_CTRL
+};
+
+enum UpdateState {
+ UPDATESTATE_CHECKING = 0,
+ UPDATESTATE_ERROR_CHECKING,
+ UPDATESTATE_NO_UPDATE_AVAIL,
+ UPDATESTATE_UPDATE_AVAIL,
+ UPDATESTATE_UPDATE_NO_DOWNLOAD,
+ UPDATESTATE_AUTO_START,
+ UPDATESTATE_DOWNLOADING,
+ UPDATESTATE_DOWNLOAD_PAUSED,
+ UPDATESTATE_ERROR_DOWNLOADING,
+ UPDATESTATE_DOWNLOAD_AVAIL,
+ UPDATESTATE_EXT_UPD_AVAIL,
+ UPDATESTATES_COUNT
+};
+
+class UpdateHandler : public cppu::WeakImplHelper< css::awt::XActionListener,
+ css::awt::XTopWindowListener,
+ css::task::XInteractionHandler,
+ css::frame::XTerminateListener >
+{
+private:
+ css::uno::Reference< css::uno::XComponentContext > mxContext;
+ css::uno::Reference< css::awt::XDialog > mxUpdDlg;
+ css::uno::Reference< css::task::XInteractionHandler > mxInteractionHdl;
+ rtl::Reference< IActionListener > mxActionListener;
+
+ UpdateState meCurState;
+ UpdateState meLastState;
+ sal_Int32 mnPercent;
+ short mnLastCtrlState;
+ bool mbDownloadBtnHasDots;
+ bool mbVisible;
+ bool mbStringsLoaded;
+ bool mbMinimized;
+ bool mbListenerAdded;
+ mutable bool mbShowsMessageBox;
+
+ osl::Mutex maMutex;
+
+ OUString msNextVersion;
+ OUString msDownloadPath;
+ OUString msDownloadFile;
+ OUString msDescriptionMsg;
+ OUString msChecking; // RID_UPDATE_STR_CHECKING
+ OUString msCheckingError; // RID_UPDATE_STR_CHECKING_ERR
+ OUString msNoUpdFound; // RID_UPDATE_STR_NO_UPD_FOUND
+ OUString msUpdFound; // RID_UPDATE_STR_UPD_FOUND
+ OUString msDlgTitle; // RID_UPDATE_STR_DLG_TITLE
+ OUString msDownloadPause; // RID_UPDATE_STR_DOWNLOAD_PAUSE
+ OUString msDownloadError; // RID_UPDATE_STR_DOWNLOAD_ERR
+ OUString msDownloadWarning; // RID_UPDATE_STR_DOWNLOAD_WARN
+ OUString msDownloadDescr; // RID_UPDATE_STR_DOWNLOAD_WARN
+ OUString msDownloadNotAvail; // RID_UPDATE_STR_DOWNLOAD_UNAVAIL
+ OUString msDownloading; // RID_UPDATE_STR_DOWNLOADING
+ OUString msReady2Install; // RID_UPDATE_STR_READY_INSTALL
+ OUString msCancelMessage; // RID_UPDATE_STR_CANCEL_DOWNLOAD
+ OUString msInstallMessage; // RID_UPDATE_STR_BEGIN_INSTALL
+ OUString msInstallError; // RID_UPDATE_STR_INSTALL_ERROR
+ OUString msOverwriteWarning; // RID_UPDATE_STR_OVERWRITE_WARNING
+ OUString msPercent; // RID_UPDATE_STR_PERCENT
+ OUString msReloadWarning; // RID_UPDATE_STR_OVERWRITE_WARNING
+ OUString msReloadReload; // RID_UPDATE_STR_OVERWRITE_WARNING
+ OUString msReloadContinue; // RID_UPDATE_STR_OVERWRITE_WARNING
+ OUString msStatusFL; // RID_UPDATE_FT_STATUS
+ OUString msDescription; // RID_UPDATE_FT_DESCRIPTION
+ OUString msClose; // RID_UPDATE_BTN_CLOSE
+ OUString msDownload; // RID_UPDATE_BTN_DOWNLOAD
+ OUString msInstall; // RID_UPDATE_BTN_INSTALL
+ OUString msPauseBtn; // RID_UPDATE_BTN_PAUSE
+ OUString msResumeBtn; // RID_UPDATE_BTN_RESUME
+ OUString msCancelBtn; // RID_UPDATE_BTN_CANCEL
+ OUString msButtonIDs[ BUTTON_COUNT ];
+ OUString msBubbleTexts[ UPDATESTATES_COUNT ];
+ OUString msBubbleTitles[ UPDATESTATES_COUNT ];
+
+ void createDialog();
+ void updateState( UpdateState eNewState );
+ void startThrobber( bool bStart = true );
+ void setControlProperty( const OUString &rCtrlName,
+ const OUString &rPropName,
+ const css::uno::Any &rPropValue );
+ void showControl( const OUString &rCtrlName, bool bShow = true );
+ void showControls( short nControls );
+ void focusControl( DialogControls eID );
+ void enableControls( short nCtrlState );
+ void setDownloadBtnLabel( bool bAppendDots );
+ void loadStrings();
+ static OUString loadString(const std::locale& rLocale,
+ TranslateId pResourceId);
+ OUString substVariables( const OUString &rSource ) const;
+ static void insertControlModel( css::uno::Reference< css::awt::XControlModel > const & rxDialogModel,
+ OUString const & rServiceName,
+ OUString const & rControlName,
+ css::awt::Rectangle const & rPosSize,
+ css::uno::Sequence< css::beans::NamedValue > const & rProps );
+
+ void setFullVersion( OUString& rString );
+
+public:
+ UpdateHandler( const css::uno::Reference< css::uno::XComponentContext > & rxContext,
+ const rtl::Reference< IActionListener > & rxActionListener );
+ virtual ~UpdateHandler() override;
+ UpdateHandler(const UpdateHandler&) = delete;
+ UpdateHandler& operator=(const UpdateHandler&) = delete;
+
+ bool isVisible() const;
+ bool isMinimized() const { return mbMinimized; }
+ void setVisible( bool bVisible = true );
+ void setProgress( sal_Int32 nPercent );
+ void setNextVersion( const OUString &rNextVersion ) { msNextVersion = rNextVersion; }
+ void setDownloadPath( const OUString &rPath ) { msDownloadPath = rPath; }
+ void setDownloadFile( std::u16string_view rPath );
+ void setErrorMessage( const OUString &rErrorMsg );
+ void setDescription( const OUString &rDescription ){ msDescriptionMsg = rDescription; }
+
+ void setState( UpdateState eState );
+ OUString getBubbleText( UpdateState eState );
+ OUString getBubbleTitle( UpdateState eState );
+ OUString getDefaultInstErrMsg();
+ bool showWarning( const OUString &rWarning ) const;
+ bool showWarning( const OUString &rWarning, const OUString& rBtnText_1, const OUString& rBtnText_2 ) const;
+ bool showOverwriteWarning( std::u16string_view rFileName ) const;
+ bool showOverwriteWarning() const;
+
+ // Allows runtime exceptions to be thrown by const methods
+ operator css::uno::Reference< css::uno::XInterface > () const
+ { return const_cast< cppu::OWeakObject * > (static_cast< cppu::OWeakObject const * > (this)); };
+
+ // XActionListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject &rObj ) override;
+ virtual void SAL_CALL actionPerformed( css::awt::ActionEvent const & rEvent) override;
+
+ // XTopWindowListener
+ virtual void SAL_CALL windowOpened( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL windowClosing( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL windowClosed( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL windowMinimized( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL windowNormalized( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL windowActivated( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL windowDeactivated( const css::lang::EventObject& e ) override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle( const css::uno::Reference< css::task::XInteractionRequest >& Request ) override;
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL notifyTermination( const css::lang::EventObject& e ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updateinfo.hxx b/extensions/source/update/check/updateinfo.hxx
new file mode 100644
index 000000000..79387b358
--- /dev/null
+++ b/extensions/source/update/check/updateinfo.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <vector>
+
+struct DownloadSource
+{
+ bool IsDirect;
+ OUString URL;
+
+ DownloadSource(bool bIsDirect, const OUString& aURL) : IsDirect(bIsDirect), URL(aURL) {};
+ DownloadSource(const DownloadSource& ds) : IsDirect(ds.IsDirect), URL(ds.URL) {};
+
+ DownloadSource & operator=( const DownloadSource & ds ) { IsDirect = ds.IsDirect; URL = ds.URL; return *this; };
+};
+
+struct ReleaseNote
+{
+ sal_uInt8 Pos;
+ OUString URL;
+ sal_uInt8 Pos2;
+ OUString URL2;
+
+ ReleaseNote(sal_uInt8 pos, const OUString& aURL) : Pos(pos), URL(aURL), Pos2(0), URL2() {};
+
+ ReleaseNote(const ReleaseNote& rn) :Pos(rn.Pos), URL(rn.URL), Pos2(rn.Pos2), URL2(rn.URL2) {};
+ ReleaseNote & operator=( const ReleaseNote& rn) { Pos=rn.Pos; URL=rn.URL; Pos2=rn.Pos2; URL2=rn.URL2; return *this; };
+};
+
+struct UpdateInfo
+{
+ OUString BuildId;
+ OUString Version;
+ OUString Description;
+ std::vector< DownloadSource > Sources;
+ std::vector< ReleaseNote > ReleaseNotes;
+};
+
+// Returns the URL of the release note for the given position
+OUString getReleaseNote(const UpdateInfo& rInfo, sal_uInt8 pos, bool autoDownloadEnabled=false);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updateprotocol.cxx b/extensions/source/update/check/updateprotocol.cxx
new file mode 100644
index 000000000..db8319c79
--- /dev/null
+++ b/extensions/source/update/check/updateprotocol.cxx
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/xpath/XPathException.hpp>
+
+#include "updateprotocol.hxx"
+#include "updatecheckconfig.hxx"
+
+#include <com/sun/star/deployment/UpdateInformationEntry.hpp>
+#include <com/sun/star/deployment/XPackageInformationProvider.hpp>
+
+
+#include <rtl/ref.hxx>
+#include <rtl/bootstrap.hxx>
+#include <osl/diagnose.h>
+
+namespace container = css::container ;
+namespace deployment = css::deployment ;
+namespace uno = css::uno ;
+namespace task = css::task ;
+namespace xml = css::xml ;
+
+
+static bool
+getBootstrapData(
+ uno::Sequence< OUString > & rRepositoryList,
+ OUString & rGitID,
+ OUString & rInstallSetID)
+{
+ rGitID = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}";
+ rtl::Bootstrap::expandMacros( rGitID );
+ if ( rGitID.isEmpty() )
+ return false;
+
+ rInstallSetID = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":UpdateID}";
+ rtl::Bootstrap::expandMacros( rInstallSetID );
+ if ( rInstallSetID.isEmpty() )
+ return false;
+
+ OUString aValue( "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":UpdateURL}" );
+ rtl::Bootstrap::expandMacros( aValue );
+
+ if( !aValue.isEmpty() )
+ {
+ rRepositoryList = { aValue };
+ }
+
+ return true;
+}
+
+
+// Returns 'true' if successfully connected to the update server
+bool
+checkForUpdates(
+ UpdateInfo& o_rUpdateInfo,
+ uno::Reference< uno::XComponentContext > const & rxContext,
+ uno::Reference< task::XInteractionHandler > const & rxInteractionHandler,
+ const uno::Reference< deployment::XUpdateInformationProvider >& rUpdateInfoProvider)
+{
+ OUString myArch;
+ OUString myOS;
+
+ rtl::Bootstrap::get("_OS", myOS);
+ rtl::Bootstrap::get("_ARCH", myArch);
+
+ uno::Sequence< OUString > aRepositoryList;
+ OUString aGitID;
+ OUString aInstallSetID;
+
+ if( ! ( getBootstrapData(aRepositoryList, aGitID, aInstallSetID) && (aRepositoryList.getLength() > 0) ) )
+ return false;
+
+ return checkForUpdates( o_rUpdateInfo, rxContext, rxInteractionHandler, rUpdateInfoProvider,
+ myOS, myArch,
+ aRepositoryList, aGitID, aInstallSetID );
+}
+
+bool
+checkForUpdates(
+ UpdateInfo& o_rUpdateInfo,
+ const uno::Reference< uno::XComponentContext > & rxContext,
+ const uno::Reference< task::XInteractionHandler > & rxInteractionHandler,
+ const uno::Reference< deployment::XUpdateInformationProvider >& rUpdateInfoProvider,
+ std::u16string_view rOS,
+ std::u16string_view rArch,
+ const uno::Sequence< OUString > &rRepositoryList,
+ std::u16string_view rGitID,
+ const OUString &rInstallSetID )
+{
+ if( !rxContext.is() )
+ throw uno::RuntimeException( "checkForUpdates: empty component context" );
+
+ OSL_ASSERT( rxContext->getServiceManager().is() );
+
+ // XPath implementation
+ uno::Reference< xml::xpath::XXPathAPI > xXPath = xml::xpath::XPathAPI::create(rxContext);
+
+ xXPath->registerNS( "inst", "http://update.libreoffice.org/description" );
+
+ if( rxInteractionHandler.is() )
+ rUpdateInfoProvider->setInteractionHandler(rxInteractionHandler);
+
+ try
+ {
+ uno::Reference< container::XEnumeration > aUpdateInfoEnumeration =
+ rUpdateInfoProvider->getUpdateInformationEnumeration( rRepositoryList, rInstallSetID );
+
+ if ( !aUpdateInfoEnumeration.is() )
+ return false; // something went wrong ..
+
+ OUString aXPathExpression =
+ OUString::Concat("/child::inst:description[inst:os=\'")+
+ rOS +
+ "\' and inst:arch=\'"+
+ rArch +
+ "\' and inst:gitid!=\'"+
+ rGitID +
+ "\']";
+
+
+ while( aUpdateInfoEnumeration->hasMoreElements() )
+ {
+ deployment::UpdateInformationEntry aEntry;
+
+ if( aUpdateInfoEnumeration->nextElement() >>= aEntry )
+ {
+ uno::Reference< xml::dom::XNode > xNode( aEntry.UpdateDocument );
+ uno::Reference< xml::dom::XNodeList > xNodeList;
+ try {
+ xNodeList = xXPath->selectNodeList(xNode, aXPathExpression
+ + "/inst:update/attribute::src");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+
+ sal_Int32 i, imax = xNodeList->getLength();
+ for( i = 0; i < imax; ++i )
+ {
+ uno::Reference< xml::dom::XNode > xNode2( xNodeList->item(i) );
+
+ if( xNode2.is() )
+ {
+ uno::Reference< xml::dom::XElement > xParent(xNode2->getParentNode(), uno::UNO_QUERY_THROW);
+ OUString aType = xParent->getAttribute("type");
+ bool bIsDirect = !aType.equalsIgnoreAsciiCase("text/html");
+
+ o_rUpdateInfo.Sources.push_back( DownloadSource(bIsDirect, xNode2->getNodeValue()) );
+ }
+ }
+
+ uno::Reference< xml::dom::XNode > xNode2;
+ try {
+ xNode2 = xXPath->selectSingleNode(xNode, aXPathExpression
+ + "/inst:version/text()");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+
+ if( xNode2.is() )
+ o_rUpdateInfo.Version = xNode2->getNodeValue();
+
+ try {
+ xNode2 = xXPath->selectSingleNode(xNode, aXPathExpression
+ + "/inst:buildid/text()");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+
+ if( xNode2.is() )
+ o_rUpdateInfo.BuildId = xNode2->getNodeValue();
+
+ o_rUpdateInfo.Description = aEntry.Description;
+
+ // Release Notes
+ try {
+ xNodeList = xXPath->selectNodeList(xNode, aXPathExpression
+ + "/inst:relnote");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ imax = xNodeList->getLength();
+ for( i = 0; i < imax; ++i )
+ {
+ uno::Reference< xml::dom::XElement > xRelNote(xNodeList->item(i), uno::UNO_QUERY);
+ if( xRelNote.is() )
+ {
+ sal_Int32 pos = xRelNote->getAttribute("pos").toInt32();
+
+ ReleaseNote aRelNote(static_cast<sal_uInt8>(pos), xRelNote->getAttribute("src"));
+
+ if( xRelNote->hasAttribute("src2") )
+ {
+ pos = xRelNote->getAttribute("pos2").toInt32();
+ aRelNote.Pos2 = static_cast<sal_Int8>(pos);
+ aRelNote.URL2 = xRelNote->getAttribute("src2");
+ }
+
+ o_rUpdateInfo.ReleaseNotes.push_back(aRelNote);
+ }
+ }
+
+ if( !o_rUpdateInfo.Sources.empty() )
+ return true;
+ }
+ }
+ }
+ catch( ... )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+bool storeExtensionUpdateInfos( const uno::Reference< uno::XComponentContext > & rxContext,
+ const uno::Sequence< uno::Sequence< OUString > > &rUpdateInfos )
+{
+ bool bNotify = false;
+
+ if ( rUpdateInfos.hasElements() )
+ {
+ rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( rxContext );
+
+ for ( sal_Int32 i = rUpdateInfos.getLength() - 1; i >= 0; i-- )
+ {
+ bNotify |= aConfig->storeExtensionVersion( rUpdateInfos[i][0], rUpdateInfos[i][1] );
+ }
+ }
+ return bNotify;
+}
+
+
+// Returns 'true' if there are updates for any extension
+
+bool checkForExtensionUpdates( const uno::Reference< uno::XComponentContext > & rxContext )
+{
+ uno::Sequence< uno::Sequence< OUString > > aUpdateList;
+
+ uno::Reference< deployment::XPackageInformationProvider > xInfoProvider;
+ try
+ {
+ uno::Any aValue( rxContext->getValueByName(
+ "/singletons/com.sun.star.deployment.PackageInformationProvider" ) );
+ OSL_VERIFY( aValue >>= xInfoProvider );
+ }
+ catch( const uno::Exception& )
+ {
+ OSL_FAIL( "checkForExtensionUpdates: could not create the PackageInformationProvider!" );
+ }
+
+ if ( !xInfoProvider.is() ) return false;
+
+ aUpdateList = xInfoProvider->isUpdateAvailable( OUString() );
+ bool bNotify = storeExtensionUpdateInfos( rxContext, aUpdateList );
+
+ return bNotify;
+}
+
+
+// Returns 'true' if there are any pending updates for any extension (offline check)
+
+bool checkForPendingUpdates( const uno::Reference< uno::XComponentContext > & rxContext )
+{
+ uno::Sequence< uno::Sequence< OUString > > aExtensionList;
+ uno::Reference< deployment::XPackageInformationProvider > xInfoProvider;
+ try
+ {
+ uno::Any aValue( rxContext->getValueByName(
+ "/singletons/com.sun.star.deployment.PackageInformationProvider" ) );
+ OSL_VERIFY( aValue >>= xInfoProvider );
+ }
+ catch( const uno::Exception& )
+ {
+ OSL_FAIL( "checkForExtensionUpdates: could not create the PackageInformationProvider!" );
+ }
+
+ if ( !xInfoProvider.is() ) return false;
+
+ bool bPendingUpdateFound = false;
+
+ aExtensionList = xInfoProvider->getExtensionList();
+ if ( aExtensionList.hasElements() )
+ {
+ rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( rxContext );
+
+ for ( sal_Int32 i = aExtensionList.getLength() - 1; i >= 0; i-- )
+ {
+ bPendingUpdateFound = aConfig->checkExtensionVersion( aExtensionList[i][0], aExtensionList[i][1] );
+ if ( bPendingUpdateFound )
+ break;
+ }
+ }
+
+ return bPendingUpdateFound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updateprotocol.hxx b/extensions/source/update/check/updateprotocol.hxx
new file mode 100644
index 000000000..4dedeb0d6
--- /dev/null
+++ b/extensions/source/update/check/updateprotocol.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 <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+
+#include "updateinfo.hxx"
+
+// Returns 'true' if successfully connected to the update server
+bool checkForUpdates(
+ UpdateInfo& o_rUpdateInfo,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::task::XInteractionHandler >& rxInteractionHandler,
+ const css::uno::Reference< css::deployment::XUpdateInformationProvider >& rxProvider
+);
+
+// The same as above, that does not read the info from bootstrap
+SAL_DLLPUBLIC_EXPORT bool
+checkForUpdates(
+ UpdateInfo& o_rUpdateInfo,
+ const css::uno::Reference< css::uno::XComponentContext > & rxContext,
+ const css::uno::Reference< css::task::XInteractionHandler > & rxInteractionHandler,
+ const css::uno::Reference< css::deployment::XUpdateInformationProvider >& rUpdateInfoProvider,
+ std::u16string_view rOS,
+ std::u16string_view rArch,
+ const css::uno::Sequence< OUString > &rRepositoryList,
+ std::u16string_view rGitID,
+ const OUString &rInstallID
+);
+
+// Returns 'true' if there are updates for any extension
+bool checkForExtensionUpdates(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext
+);
+
+bool checkForPendingUpdates(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext
+);
+
+bool storeExtensionUpdateInfos(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::uno::Sequence< OUString > > &rUpdateInfos
+);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updateprotocoltest.cxx b/extensions/source/update/check/updateprotocoltest.cxx
new file mode 100644
index 000000000..070f930af
--- /dev/null
+++ b/extensions/source/update/check/updateprotocoltest.cxx
@@ -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 .
+ */
+
+
+#include <cppuhelper/bootstrap.hxx>
+
+#include "updateprotocol.hxx"
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+
+#include <sal/main.h>
+#include <osl/process.h>
+#include <stdio.h>
+#include "sal/log.hxx"
+
+namespace task = ::com::sun::star::task;
+namespace uno = ::com::sun::star::uno;
+
+
+SAL_IMPLEMENT_MAIN()
+{
+ (void) argv;
+ (void) argc;
+
+ if( osl_getCommandArgCount() != 0 )
+ {
+ fprintf(stderr, "Usage: updateprotocoltest\n");
+ return -1;
+ }
+
+ // create the initial component context
+ uno::Reference< uno::XComponentContext > rComponentContext = cppu::defaultBootstrap_InitialComponentContext();
+
+ // initialize UCB (for backwards compatibility, in case some code still uses
+ // plain createInstance w/o args directly to obtain an instance):
+ css::ucb::UniversalContentBroker::create(rComponentContext);
+
+
+ OUString aURL;
+ OUString aVersion;
+
+ try
+ {
+ if( checkForUpdates(rComponentContext, uno::Reference< task::XInteractionHandler > (), aURL, aVersion) )
+ {
+ SAL_INFO("extensions.update", "Update found: " << aVersion << " on " << aURL);
+ }
+ else
+ {
+ SAL_INFO("extensions.update", "no updates found" );
+ }
+ }
+ catch( ... )
+ {
+ SAL_INFO("extensions.update", "unhandled exception caught" );
+ }
+
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/check/updchk.uno.component b/extensions/source/update/check/updchk.uno.component
new file mode 100644
index 000000000..f147e3065
--- /dev/null
+++ b/extensions/source/update/check/updchk.uno.component
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="vnd.sun.UpdateCheck"
+ constructor="extensions_update_UpdateCheckJob_get_implementation">
+ <service name="com.sun.star.setup.UpdateCheck"/>
+ </implementation>
+ <implementation name="vnd.sun.UpdateCheckConfig"
+ constructor="extensions_update_UpdateCheckConfig_get_implementation">
+ <service name="com.sun.star.setup.UpdateCheckConfig"/>
+ </implementation>
+</component>
diff --git a/extensions/source/update/feed/test/updatefeedtest.cxx b/extensions/source/update/feed/test/updatefeedtest.cxx
new file mode 100644
index 000000000..119aab24a
--- /dev/null
+++ b/extensions/source/update/feed/test/updatefeedtest.cxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cppuhelper/servicefactory.hxx>
+#include <cppuhelper/bootstrap.hxx>
+
+#include <com/sun/star/lang/XInitialization.hpp>
+
+
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <com/sun/star/deployment/UpdateInformationProvider.hpp>
+
+#include <sal/main.h>
+#include <osl/process.h>
+#include <sal/log.hxx>
+#include <stdio.h>
+
+namespace deployment = ::com::sun::star::deployment;
+namespace lang = ::com::sun::star::lang;
+namespace uno = ::com::sun::star::uno;
+namespace xml = ::com::sun::star::xml;
+
+
+SAL_IMPLEMENT_MAIN()
+{
+ (void) argv;
+ (void) argc;
+
+ if( osl_getCommandArgCount() != 1 )
+ {
+ fprintf(stderr, "Usage: updatefeedtest <url>\n");
+ return -1;
+ }
+
+ // create the initial component context
+ uno::Reference< uno::XComponentContext > rComponentContext = cppu::defaultBootstrap_InitialComponentContext();
+
+ // initialize UCB (for backwards compatibility, in case some code still uses
+ // plain createInstance w/o args directly to obtain an instance):
+ ucb::UniversalContentBroker::create(rComponentContext);
+
+ // retrieve the update information provider
+ uno::Reference< deployment::XUpdateInformationProvider > rUpdateInformationProvider =
+ deployment::UpdateInformationProvider::create( rComponentContext );
+
+ uno::Sequence< OUString > theURLs(1);
+ osl_getCommandArg( 0, &theURLs[0].pData );
+ // theURLs[0] = "http://localhost/~olli/atomfeed.xml";
+
+ OUString aExtension = "MyExtension";
+
+ try
+ {
+ uno::Sequence< uno::Reference< xml::dom::XElement > > theUpdateInfo =
+ rUpdateInformationProvider->getUpdateInformation( theURLs, aExtension );
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("extensions.update", "");
+ }
+ catch( ... )
+ {
+ SAL_WARN("extensions.update", "exception of undetermined type caught" );
+ }
+
+
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/feed/updatefeed.component b/extensions/source/update/feed/updatefeed.component
new file mode 100644
index 000000000..83e30a8c2
--- /dev/null
+++ b/extensions/source/update/feed/updatefeed.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="vnd.sun.UpdateInformationProvider"
+ constructor="extensions_update_UpdateInformationProvider_get_implementation">
+ <service name="com.sun.star.deployment.UpdateInformationProvider"/>
+ </implementation>
+</component>
diff --git a/extensions/source/update/feed/updatefeed.cxx b/extensions/source/update/feed/updatefeed.cxx
new file mode 100644
index 000000000..ee83c3f9b
--- /dev/null
+++ b/extensions/source/update/feed/updatefeed.cxx
@@ -0,0 +1,755 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <config_folders.h>
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/UpdateInformationEntry.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <com/sun/star/ucb/XWebDAVCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XCommandProcessor2.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument3.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/task/PasswordContainerInteractionHandler.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/xpath/XPathException.hpp>
+#include <rtl/ref.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <osl/conditn.hxx>
+#include <vcl/svapp.hxx>
+
+namespace beans = com::sun::star::beans ;
+namespace container = com::sun::star::container ;
+namespace deployment = com::sun::star::deployment ;
+namespace io = com::sun::star::io ;
+namespace lang = com::sun::star::lang ;
+namespace task = com::sun::star::task ;
+namespace ucb = com::sun::star::ucb ;
+namespace uno = com::sun::star::uno ;
+namespace xml = com::sun::star::xml ;
+
+
+namespace
+{
+
+#ifdef DEBUG
+
+class InputStreamWrapper : public ::cppu::WeakImplHelper< io::XInputStream >
+{
+ uno::Reference< io::XInputStream > m_xStream;
+
+public:
+ explicit InputStreamWrapper(const uno::Reference< io::XInputStream >& rxStream) :
+ m_xStream(rxStream) {};
+
+ virtual sal_Int32 SAL_CALL readBytes(uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead)
+ {
+ sal_Int32 n = m_xStream->readBytes(aData, nBytesToRead);
+ return n;
+ };
+ virtual sal_Int32 SAL_CALL readSomeBytes(uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead)
+ {
+ sal_Int32 n = m_xStream->readSomeBytes(aData, nMaxBytesToRead);
+ return n;
+ };
+ virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip )
+ { m_xStream->skipBytes(nBytesToSkip); };
+ virtual sal_Int32 SAL_CALL available()
+ { return m_xStream->available(); };
+ virtual void SAL_CALL closeInput( )
+ {};
+};
+
+#define INPUT_STREAM(i) new InputStreamWrapper(i)
+#else
+#define INPUT_STREAM(i) i
+#endif
+
+
+class ActiveDataSink : public ::cppu::WeakImplHelper< io::XActiveDataSink >
+{
+ uno::Reference< io::XInputStream > m_xStream;
+
+public:
+ ActiveDataSink() {};
+
+ virtual uno::Reference< io::XInputStream > SAL_CALL getInputStream() override { return m_xStream; };
+ virtual void SAL_CALL setInputStream( uno::Reference< io::XInputStream > const & rStream ) override { m_xStream = rStream; };
+};
+
+
+class UpdateInformationProvider :
+ public ::cppu::WeakImplHelper< deployment::XUpdateInformationProvider,
+ ucb::XWebDAVCommandEnvironment,
+ lang::XServiceInfo >
+{
+ OUString getUserAgent(bool bExtended);
+ bool isUserAgentExtended() const;
+public:
+ uno::Reference< xml::dom::XElement > getDocumentRoot(const uno::Reference< xml::dom::XNode >& rxNode);
+ uno::Reference< xml::dom::XNode > getChildNode(const uno::Reference< xml::dom::XNode >& rxNode, std::u16string_view rName);
+
+
+ // XUpdateInformationService
+ virtual uno::Sequence< uno::Reference< xml::dom::XElement > > SAL_CALL
+ getUpdateInformation(
+ uno::Sequence< OUString > const & repositories,
+ OUString const & extensionId
+ ) override;
+
+ virtual void SAL_CALL cancel() override;
+
+ virtual void SAL_CALL setInteractionHandler(
+ uno::Reference< task::XInteractionHandler > const & handler ) override;
+
+ virtual uno::Reference< container::XEnumeration > SAL_CALL
+ getUpdateInformationEnumeration(
+ uno::Sequence< OUString > const & repositories,
+ OUString const & extensionId
+ ) override;
+
+ // XCommandEnvironment
+ virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() override;
+
+ virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler() override { return uno::Reference< ucb::XProgressHandler >(); };
+
+ // XWebDAVCommandEnvironment
+ virtual uno::Sequence< beans::StringPair > SAL_CALL getUserRequestHeaders(
+ const OUString&, ucb::WebDAVHTTPMethod ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ UpdateInformationProvider(const uno::Reference<uno::XComponentContext>& xContext,
+ const uno::Reference< ucb::XUniversalContentBroker >& xUniversalContentBroker,
+ const uno::Reference< xml::dom::XDocumentBuilder >& xDocumentBuilder,
+ const uno::Reference< xml::xpath::XXPathAPI >& xXPathAPI);
+
+protected:
+
+ virtual ~UpdateInformationProvider() override;
+ static OUString getConfigurationItem(uno::Reference<lang::XMultiServiceFactory> const & configurationProvider, OUString const & node, OUString const & item);
+ static uno::Any getConfigurationItemAny(uno::Reference<lang::XMultiServiceFactory> const & configurationProvider, OUString const & node, OUString const & item);
+
+private:
+ uno::Reference< io::XInputStream > load(const OUString& rURL);
+
+ void storeCommandInfo( sal_Int32 nCommandId,
+ uno::Reference< ucb::XCommandProcessor > const & rxCommandProcessor);
+
+ const uno::Reference< uno::XComponentContext> m_xContext;
+
+ const uno::Reference< ucb::XUniversalContentBroker > m_xUniversalContentBroker;
+ const uno::Reference< xml::dom::XDocumentBuilder > m_xDocumentBuilder;
+ const uno::Reference< xml::xpath::XXPathAPI > m_xXPathAPI;
+
+ uno::Sequence< beans::StringPair > m_aRequestHeaderList;
+
+ uno::Reference< ucb::XCommandProcessor > m_xCommandProcessor;
+ uno::Reference< task::XInteractionHandler > m_xInteractionHandler;
+ uno::Reference< task::XInteractionHandler > m_xPwContainerInteractionHandler;
+
+ osl::Mutex m_aMutex;
+ osl::Condition m_bCancelled;
+
+ sal_Int32 m_nCommandId;
+};
+
+
+class UpdateInformationEnumeration : public ::cppu::WeakImplHelper< container::XEnumeration >
+{
+public:
+ UpdateInformationEnumeration(const uno::Reference< xml::dom::XNodeList >& xNodeList,
+ const rtl::Reference< UpdateInformationProvider >& xUpdateInformationProvider) :
+ m_xUpdateInformationProvider(xUpdateInformationProvider),
+ m_xNodeList(xNodeList),
+ m_nNodes(xNodeList.is() ? xNodeList->getLength() : 0),
+ m_nCount(0)
+ {
+ };
+
+ // XEnumeration
+ sal_Bool SAL_CALL hasMoreElements() override { return m_nCount < m_nNodes; };
+ uno::Any SAL_CALL nextElement() override
+ {
+ OSL_ASSERT( m_xNodeList.is() );
+ OSL_ASSERT( m_xUpdateInformationProvider.is() );
+
+ if( m_nCount >= m_nNodes )
+ throw container::NoSuchElementException(OUString::number(m_nCount), *this);
+
+ try
+ {
+ deployment::UpdateInformationEntry aEntry;
+
+ uno::Reference< xml::dom::XNode > xAtomEntryNode( m_xNodeList->item(m_nCount++) );
+
+ uno::Reference< xml::dom::XNode > xSummaryNode(
+ m_xUpdateInformationProvider->getChildNode( xAtomEntryNode, u"summary/text()" )
+ );
+
+ if( xSummaryNode.is() )
+ aEntry.Description = xSummaryNode->getNodeValue();
+
+ uno::Reference< xml::dom::XNode > xContentNode(
+ m_xUpdateInformationProvider->getChildNode( xAtomEntryNode, u"content" ) );
+
+ if( xContentNode.is() )
+ aEntry.UpdateDocument = m_xUpdateInformationProvider->getDocumentRoot(xContentNode);
+
+ return uno::Any(aEntry);
+ }
+ catch( ucb::CommandAbortedException const &)
+ {
+ // action has been aborted
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetException( "Command aborted", *this, anyEx );
+ }
+ catch( uno::RuntimeException const & )
+ {
+ // let runtime exception pass
+ throw;
+ }
+ catch( uno::Exception const &)
+ {
+ // document not accessible
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetException( "Document not accessible", *this, anyEx );
+ }
+ }
+
+private:
+ const rtl::Reference< UpdateInformationProvider > m_xUpdateInformationProvider;
+ const uno::Reference< xml::dom::XNodeList > m_xNodeList;
+ const sal_Int32 m_nNodes;
+ sal_Int32 m_nCount;
+};
+
+
+class SingleUpdateInformationEnumeration : public ::cppu::WeakImplHelper< container::XEnumeration >
+{
+public:
+ explicit SingleUpdateInformationEnumeration(const uno::Reference< xml::dom::XElement >& xElement)
+ : m_nCount(0) { m_aEntry.UpdateDocument = xElement; };
+
+ // XEnumeration
+ sal_Bool SAL_CALL hasMoreElements() override { return 0 == m_nCount; };
+ uno::Any SAL_CALL nextElement() override
+ {
+ if( m_nCount > 0 )
+ throw container::NoSuchElementException(OUString::number(m_nCount), *this);
+
+ ++m_nCount;
+ return uno::Any(m_aEntry);
+ };
+
+private:
+ sal_Int32 m_nCount;
+ deployment::UpdateInformationEntry m_aEntry;
+};
+
+UpdateInformationProvider::UpdateInformationProvider(
+ const uno::Reference<uno::XComponentContext>& xContext,
+ const uno::Reference< ucb::XUniversalContentBroker >& xUniversalContentBroker,
+ const uno::Reference< xml::dom::XDocumentBuilder >& xDocumentBuilder,
+ const uno::Reference< xml::xpath::XXPathAPI >& xXPathAPI)
+ : m_xContext(xContext)
+ , m_xUniversalContentBroker(xUniversalContentBroker)
+ , m_xDocumentBuilder(xDocumentBuilder)
+ , m_xXPathAPI(xXPathAPI)
+ , m_aRequestHeaderList(2)
+ , m_nCommandId(0)
+{
+ uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider(
+ css::configuration::theDefaultProvider::get(m_xContext));
+
+ auto pRequestHeaderList = m_aRequestHeaderList.getArray();
+ pRequestHeaderList[0].First = "Accept-Language";
+ pRequestHeaderList[0].Second = getConfigurationItem( xConfigurationProvider, "org.openoffice.Setup/L10N", "ooLocale" );
+}
+
+bool
+UpdateInformationProvider::isUserAgentExtended() const
+{
+ bool bExtendedUserAgent = false;
+ try {
+ uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider(
+ css::configuration::theDefaultProvider::get(m_xContext));
+
+ uno::Any aExtended = getConfigurationItemAny(
+ xConfigurationProvider,
+ "org.openoffice.Office.Jobs/Jobs/UpdateCheck/Arguments",
+ "ExtendedUserAgent");
+ aExtended >>= bExtendedUserAgent;
+ } catch (const uno::RuntimeException &) {
+ SAL_WARN("extensions.update", "Online update disabled");
+ }
+ return bExtendedUserAgent;
+}
+
+OUString UpdateInformationProvider::getUserAgent(bool bExtended)
+{
+ uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider(
+ css::configuration::theDefaultProvider::get(m_xContext));
+
+ OUStringBuffer buf;
+ buf.append(
+ getConfigurationItem(
+ xConfigurationProvider,
+ "org.openoffice.Setup/Product",
+ "ooName"));
+ buf.append(' ');
+ buf.append(
+ getConfigurationItem(
+ xConfigurationProvider,
+ "org.openoffice.Setup/Product",
+ "ooSetupVersion"));
+
+ OUString extension(
+ getConfigurationItem(
+ xConfigurationProvider,
+ "org.openoffice.Setup/Product",
+ "ooSetupExtension"));
+ if (!extension.isEmpty())
+ buf.append(extension);
+
+ OUString product(buf.makeStringAndClear());
+
+ OUString aUserAgent( "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":UpdateUserAgent}" );
+ OUString aExtended;
+ if( bExtended )
+ {
+ aExtended = Application::GetHWOSConfInfo();
+ }
+ rtl::Bootstrap::expandMacros( aUserAgent );
+ aUserAgent = aUserAgent.replaceAll("<PRODUCT>", product);
+ aUserAgent = aUserAgent.replaceAll("<OPTIONAL_OS_HW_DATA>", aExtended);
+ SAL_INFO("extensions.update", "UpdateUserAgent: " << aUserAgent);
+
+ return aUserAgent;
+}
+
+uno::Sequence< beans::StringPair > SAL_CALL UpdateInformationProvider::getUserRequestHeaders(
+ const OUString &aURL, ucb::WebDAVHTTPMethod )
+{
+ bool bExtendedUserAgent;
+ uno::Sequence< beans::StringPair > aPair = m_aRequestHeaderList;
+
+ // Internal use from cui/ some magic URLs
+ if( aURL.startsWith( "useragent:" ) )
+ bExtendedUserAgent = (aURL == "useragent:extended");
+ else
+ bExtendedUserAgent = isUserAgentExtended();
+
+ OUString aUserAgent = getUserAgent(bExtendedUserAgent);
+
+ if( aUserAgent.isEmpty() )
+ aPair.realloc(1);
+ else
+ {
+ auto pPair = aPair.getArray();
+ pPair[1].First = "User-Agent";
+ pPair[1].Second = aUserAgent;
+ }
+
+ return aPair;
+};
+
+UpdateInformationProvider::~UpdateInformationProvider()
+{
+}
+
+uno::Any
+UpdateInformationProvider::getConfigurationItemAny(uno::Reference<lang::XMultiServiceFactory> const & configurationProvider, OUString const & node, OUString const & item)
+{
+ beans::PropertyValue aProperty;
+ aProperty.Name = "nodepath";
+ aProperty.Value <<= node;
+
+ uno::Sequence< uno::Any > aArgumentList{ uno::Any(aProperty) };
+ uno::Reference< container::XNameAccess > xNameAccess(
+ configurationProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ aArgumentList ),
+ uno::UNO_QUERY_THROW);
+
+ return xNameAccess->getByName(item);
+}
+
+OUString
+UpdateInformationProvider::getConfigurationItem(uno::Reference<lang::XMultiServiceFactory> const & configurationProvider, OUString const & node, OUString const & item)
+{
+ OUString sRet;
+ getConfigurationItemAny(configurationProvider, node, item) >>= sRet;
+ return sRet;
+}
+
+void
+UpdateInformationProvider::storeCommandInfo(
+ sal_Int32 nCommandId,
+ uno::Reference< ucb::XCommandProcessor > const & rxCommandProcessor)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ m_nCommandId = nCommandId;
+ m_xCommandProcessor = rxCommandProcessor;
+}
+
+uno::Reference< io::XInputStream >
+UpdateInformationProvider::load(const OUString& rURL)
+{
+ uno::Reference< ucb::XContentIdentifier > xId = m_xUniversalContentBroker->createContentIdentifier(rURL);
+
+ if( !xId.is() )
+ throw uno::RuntimeException(
+ "unable to obtain universal content id", *this);
+
+ uno::Reference< ucb::XCommandProcessor > xCommandProcessor(m_xUniversalContentBroker->queryContent(xId), uno::UNO_QUERY_THROW);
+ rtl::Reference< ActiveDataSink > aSink(new ActiveDataSink());
+
+ // Disable KeepAlive in webdav - don't want millions of office
+ // instances phone home & clog up servers
+ uno::Sequence< beans::NamedValue > aProps { { "KeepAlive", uno::Any(false) } };
+
+ ucb::OpenCommandArgument3 aOpenArgument;
+ aOpenArgument.Mode = ucb::OpenMode::DOCUMENT;
+ aOpenArgument.Priority = 32768;
+ aOpenArgument.Sink = *aSink;
+ aOpenArgument.OpeningFlags = aProps;
+
+ ucb::Command aCommand;
+ aCommand.Name = "open";
+ aCommand.Argument <<= aOpenArgument;
+
+ sal_Int32 nCommandId = xCommandProcessor->createCommandIdentifier();
+
+ storeCommandInfo(nCommandId, xCommandProcessor);
+ try
+ {
+ xCommandProcessor->execute(aCommand, nCommandId,
+ static_cast < XCommandEnvironment *> (this));
+ }
+ catch( const uno::Exception & /* e */ )
+ {
+ storeCommandInfo(0, uno::Reference< ucb::XCommandProcessor > ());
+
+ uno::Reference< ucb::XCommandProcessor2 > xCommandProcessor2(xCommandProcessor, uno::UNO_QUERY);
+ if( xCommandProcessor2.is() )
+ xCommandProcessor2->releaseCommandIdentifier(nCommandId);
+
+ throw;
+ }
+ storeCommandInfo(0, uno::Reference< ucb::XCommandProcessor > ());
+
+ uno::Reference< ucb::XCommandProcessor2 > xCommandProcessor2(xCommandProcessor, uno::UNO_QUERY);
+ if( xCommandProcessor2.is() )
+ xCommandProcessor2->releaseCommandIdentifier(nCommandId);
+
+ return INPUT_STREAM(aSink->getInputStream());
+}
+
+
+// TODO: docu content node
+
+uno::Reference< xml::dom::XElement >
+UpdateInformationProvider::getDocumentRoot(const uno::Reference< xml::dom::XNode >& rxNode)
+{
+ OSL_ASSERT(m_xDocumentBuilder.is());
+
+ uno::Reference< xml::dom::XElement > xElement(rxNode, uno::UNO_QUERY_THROW);
+
+ // load the document referenced in 'src' attribute ..
+ if( xElement->hasAttribute( "src" ) )
+ {
+ uno::Reference< xml::dom::XDocument > xUpdateXML =
+ m_xDocumentBuilder->parse(load(xElement->getAttribute( "src" )));
+
+ OSL_ASSERT( xUpdateXML.is() );
+
+ if( xUpdateXML.is() )
+ return xUpdateXML->getDocumentElement();
+ }
+ // .. or return the (single) child element
+ else
+ {
+ uno::Reference< xml::dom::XNodeList> xChildNodes = rxNode->getChildNodes();
+
+ // ignore possible #text nodes
+ sal_Int32 nmax = xChildNodes->getLength();
+ for(sal_Int32 n=0; n < nmax; n++)
+ {
+ uno::Reference< xml::dom::XElement > xChildElement(xChildNodes->item(n), uno::UNO_QUERY);
+ if( xChildElement.is() )
+ {
+ /* Copy the content to a dedicated document since XXPathAPI->selectNodeList
+ * seems to evaluate expression always relative to the root node.
+ */
+ uno::Reference< xml::dom::XDocument > xUpdateXML = m_xDocumentBuilder->newDocument();
+ xUpdateXML->appendChild( xUpdateXML->importNode(xChildElement, true ) );
+ return xUpdateXML->getDocumentElement();
+ }
+ }
+ }
+
+ return uno::Reference< xml::dom::XElement > ();
+}
+
+
+uno::Reference< xml::dom::XNode >
+UpdateInformationProvider::getChildNode(const uno::Reference< xml::dom::XNode >& rxNode,
+ std::u16string_view rName)
+{
+ OSL_ASSERT(m_xXPathAPI.is());
+ try {
+ return m_xXPathAPI->selectSingleNode(rxNode, OUString::Concat("./atom:") + rName);
+ } catch (const xml::xpath::XPathException &) {
+ // ignore
+ return nullptr;
+ }
+}
+
+
+uno::Reference< container::XEnumeration > SAL_CALL
+UpdateInformationProvider::getUpdateInformationEnumeration(
+ uno::Sequence< OUString > const & repositories,
+ OUString const & extensionId
+)
+{
+ OSL_ASSERT(m_xDocumentBuilder.is());
+
+ // reset cancelled flag
+ m_bCancelled.reset();
+
+ for(sal_Int32 n=0; n<repositories.getLength(); n++)
+ {
+ try
+ {
+ uno::Reference< xml::dom::XDocument > xDocument = m_xDocumentBuilder->parse(load(repositories[n]));
+ uno::Reference< xml::dom::XElement > xElement;
+
+ if( xDocument.is() )
+ xElement = xDocument->getDocumentElement();
+
+ if( xElement.is() )
+ {
+ if( xElement->getNodeName() == "feed" )
+ {
+ OUString aXPathExpression;
+
+ if( !extensionId.isEmpty() )
+ aXPathExpression = "//atom:entry/atom:category[@term=\'" + extensionId + "\']/..";
+ else
+ aXPathExpression = "//atom:entry";
+
+ uno::Reference< xml::dom::XNodeList > xNodeList;
+ try {
+ xNodeList = m_xXPathAPI->selectNodeList(xDocument,
+ aXPathExpression);
+ } catch (const xml::xpath::XPathException &) {
+ // ignore
+ }
+
+ return new UpdateInformationEnumeration(xNodeList, this);
+ }
+ else
+ {
+ return new SingleUpdateInformationEnumeration(xElement);
+ }
+ }
+
+ if( m_bCancelled.check() )
+ break;
+ }
+ catch( uno::RuntimeException const& /*e*/)
+ {
+ // #i118675# ignore runtime exceptions for now
+ // especially the "unsatisfied query for interface of
+ // type com.sun.star.ucb.XCommandProcessor!" exception
+ }
+
+ // rethrow only if last url in the list
+ catch( uno::Exception const & )
+ {
+ if( n+1 >= repositories.getLength() )
+ throw;
+ }
+ }
+
+ return uno::Reference< container::XEnumeration >();
+}
+
+
+uno::Sequence< uno::Reference< xml::dom::XElement > > SAL_CALL
+UpdateInformationProvider::getUpdateInformation(
+ uno::Sequence< OUString > const & repositories,
+ OUString const & extensionId
+)
+{
+ uno::Reference< container::XEnumeration > xEnumeration(
+ getUpdateInformationEnumeration(repositories, extensionId)
+ );
+
+ std::vector< uno::Reference< xml::dom::XElement > > aRet;
+
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() )
+ {
+ try
+ {
+ deployment::UpdateInformationEntry aEntry;
+ if( (xEnumeration->nextElement() >>= aEntry ) && aEntry.UpdateDocument.is() )
+ {
+ aRet.push_back(aEntry.UpdateDocument);
+ }
+ }
+
+ catch( const lang::WrappedTargetException& e )
+ {
+ // command aborted, return what we have got so far
+ if( e.TargetException.isExtractableTo( ::cppu::UnoType< css::ucb::CommandAbortedException >::get() ) )
+ {
+ break;
+ }
+
+ // ignore files that can't be loaded
+ }
+ }
+ }
+
+ return comphelper::containerToSequence(aRet);
+}
+
+
+void SAL_CALL
+UpdateInformationProvider::cancel()
+{
+ m_bCancelled.set();
+
+ osl::MutexGuard aGuard(m_aMutex);
+ if( m_xCommandProcessor.is() )
+ m_xCommandProcessor->abort(m_nCommandId);
+}
+
+
+void SAL_CALL
+UpdateInformationProvider::setInteractionHandler(
+ uno::Reference< task::XInteractionHandler > const & handler )
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ m_xInteractionHandler = handler;
+}
+
+
+uno::Reference< task::XInteractionHandler > SAL_CALL
+UpdateInformationProvider::getInteractionHandler()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_xInteractionHandler.is() )
+ return m_xInteractionHandler;
+ else
+ {
+ try
+ {
+ // Supply an interaction handler that uses the password container
+ // service to obtain credentials without displaying a password gui.
+
+ if ( !m_xPwContainerInteractionHandler.is() )
+ m_xPwContainerInteractionHandler
+ = task::PasswordContainerInteractionHandler::create(
+ m_xContext );
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ throw;
+ }
+ catch ( uno::Exception const & )
+ {
+ }
+ return m_xPwContainerInteractionHandler;
+ }
+}
+
+
+
+OUString SAL_CALL
+UpdateInformationProvider::getImplementationName()
+{
+ return "vnd.sun.UpdateInformationProvider";
+}
+
+
+uno::Sequence< OUString > SAL_CALL
+UpdateInformationProvider::getSupportedServiceNames()
+{
+ return { "com.sun.star.deployment.UpdateInformationProvider" };
+}
+
+sal_Bool SAL_CALL
+UpdateInformationProvider::supportsService( OUString const & serviceName )
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+} // anonymous namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_update_UpdateInformationProvider_get_implementation(
+ css::uno::XComponentContext* xContext , css::uno::Sequence<css::uno::Any> const&)
+{
+ uno::Reference< ucb::XUniversalContentBroker > xUniversalContentBroker =
+ ucb::UniversalContentBroker::create(xContext);
+
+ uno::Reference< xml::dom::XDocumentBuilder > xDocumentBuilder(
+ xml::dom::DocumentBuilder::create(xContext));
+
+ uno::Reference< xml::xpath::XXPathAPI > xXPath = xml::xpath::XPathAPI::create( xContext );
+
+ xXPath->registerNS( "atom", "http://www.w3.org/2005/Atom" );
+
+ return cppu::acquire(
+ new UpdateInformationProvider(xContext, xUniversalContentBroker, xDocumentBuilder, xXPath));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/ui/updatecheckui.cxx b/extensions/source/update/ui/updatecheckui.cxx
new file mode 100644
index 000000000..6a4462669
--- /dev/null
+++ b/extensions/source/update/ui/updatecheckui.cxx
@@ -0,0 +1,306 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/document/XDocumentEventListener.hpp>
+#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/graphic/XGraphicProvider.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <unotools/resmgr.hxx>
+#include <vcl/image.hxx>
+#include <vcl/menubarupdateicon.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/strings.hrc>
+
+#include <bitmaps.hlst>
+
+constexpr OUStringLiteral PROPERTY_TITLE = u"BubbleHeading";
+constexpr OUStringLiteral PROPERTY_TEXT = u"BubbleText";
+constexpr OUStringLiteral PROPERTY_IMAGE = u"BubbleImageURL";
+constexpr OUStringLiteral PROPERTY_SHOW_BUBBLE = u"BubbleVisible";
+constexpr OUStringLiteral PROPERTY_CLICK_HDL = u"MenuClickHDL";
+constexpr OUStringLiteral PROPERTY_SHOW_MENUICON = u"MenuIconVisible";
+
+using namespace ::com::sun::star;
+
+
+namespace
+{
+
+class UpdateCheckUI : public ::cppu::WeakImplHelper
+ < lang::XServiceInfo, document::XDocumentEventListener, beans::XPropertySet >
+{
+ uno::Reference< uno::XComponentContext > m_xContext;
+ uno::Reference< task::XJob > mrJob;
+ OUString maBubbleImageURL;
+ MenuBarUpdateIconManager maBubbleManager;
+ std::locale maSfxLocale;
+
+private:
+ DECL_LINK(ClickHdl, LinkParamNone*, void);
+
+ Image GetBubbleImage( OUString const &rURL );
+
+public:
+ explicit UpdateCheckUI(const uno::Reference<uno::XComponentContext>&);
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XDocumentEventListener
+ virtual void SAL_CALL documentEventOccured(const document::DocumentEvent& Event) override;
+ virtual void SAL_CALL disposing(const lang::EventObject& Event) override;
+
+ //XPropertySet
+ virtual uno::Reference< beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(const OUString& PropertyName, const uno::Any& aValue) override;
+ virtual uno::Any SAL_CALL getPropertyValue(const OUString& PropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(const OUString& PropertyName,
+ const uno::Reference< beans::XPropertyChangeListener > & aListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(const OUString& PropertyName,
+ const uno::Reference< beans::XPropertyChangeListener > & aListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& PropertyName,
+ const uno::Reference< beans::XVetoableChangeListener > & aListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName,
+ const uno::Reference< beans::XVetoableChangeListener > & aListener) override;
+};
+
+UpdateCheckUI::UpdateCheckUI(const uno::Reference<uno::XComponentContext>& xContext)
+ : m_xContext(xContext)
+{
+ maSfxLocale = Translate::Create("sfx");
+
+ uno::Reference< document::XDocumentEventBroadcaster > xBroadcaster( frame::theGlobalEventBroadcaster::get(m_xContext) );
+ xBroadcaster->addDocumentEventListener( this );
+
+ SolarMutexGuard aGuard;
+
+ maBubbleManager.SetBubbleImage(GetBubbleImage(maBubbleImageURL));
+ maBubbleManager.SetClickHdl(LINK(this, UpdateCheckUI, ClickHdl));
+}
+
+OUString SAL_CALL
+UpdateCheckUI::getImplementationName()
+{
+ return "vnd.sun.UpdateCheckUI";
+}
+
+uno::Sequence< OUString > SAL_CALL
+UpdateCheckUI::getSupportedServiceNames()
+{
+ return { "com.sun.star.setup.UpdateCheckUI" };
+}
+
+sal_Bool SAL_CALL
+UpdateCheckUI::supportsService( OUString const & serviceName )
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+Image UpdateCheckUI::GetBubbleImage( OUString const &rURL )
+{
+ Image aImage;
+
+ if ( !maBubbleImageURL.isEmpty() )
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ if( !xContext.is() )
+ throw uno::RuntimeException(
+ "UpdateCheckUI: unable to obtain service manager from component context" );
+
+ try
+ {
+ uno::Reference< graphic::XGraphicProvider > xGraphProvider(graphic::GraphicProvider::create(xContext));
+ uno::Sequence< beans::PropertyValue > aMediaProps{ comphelper::makePropertyValue("URL",
+ rURL) };
+ uno::Reference< graphic::XGraphic > xGraphic = xGraphProvider->queryGraphic( aMediaProps );
+ if ( xGraphic.is() )
+ {
+ aImage = Image( xGraphic );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+
+ if ( aImage.GetSizePixel().Width() == 0 )
+ aImage = Image(StockImage::Yes, SV_RESID_BITMAP_INFOBOX);
+
+ return aImage;
+}
+
+void SAL_CALL UpdateCheckUI::documentEventOccured(const document::DocumentEvent& rEvent)
+{
+ SolarMutexGuard aGuard;
+
+ if( rEvent.EventName == "OnPrepareViewClosing" )
+ {
+ maBubbleManager.RemoveBubbleWindow(true);
+ }
+}
+
+void SAL_CALL UpdateCheckUI::disposing(const lang::EventObject&)
+{
+}
+
+uno::Reference< beans::XPropertySetInfo > UpdateCheckUI::getPropertySetInfo()
+{
+ return nullptr;
+}
+
+void UpdateCheckUI::setPropertyValue(const OUString& rPropertyName,
+ const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ OUString aString;
+
+ if( rPropertyName == PROPERTY_TITLE ) {
+ rValue >>= aString;
+ maBubbleManager.SetBubbleTitle(aString);
+ }
+ else if( rPropertyName == PROPERTY_TEXT ) {
+ rValue >>= aString;
+ maBubbleManager.SetBubbleText(aString);
+ }
+ else if( rPropertyName == PROPERTY_IMAGE ) {
+ rValue >>= aString;
+ if ( aString != maBubbleImageURL ) {
+ maBubbleImageURL = aString;
+ maBubbleManager.SetBubbleImage(GetBubbleImage(maBubbleImageURL));
+ }
+ }
+ else if( rPropertyName == PROPERTY_SHOW_BUBBLE ) {
+ bool bShowBubble= false;
+ rValue >>= bShowBubble;
+ maBubbleManager.SetShowBubble(bShowBubble);
+ }
+ else if( rPropertyName == PROPERTY_CLICK_HDL ) {
+ uno::Reference< task::XJob > aJob;
+ rValue >>= aJob;
+ if ( !aJob.is() )
+ throw lang::IllegalArgumentException();
+ mrJob = aJob;
+ }
+ else if (rPropertyName == PROPERTY_SHOW_MENUICON ) {
+ bool bShowMenuIcon = false;
+ rValue >>= bShowMenuIcon;
+ maBubbleManager.SetShowMenuIcon(bShowMenuIcon);
+ }
+ else
+ throw beans::UnknownPropertyException(rPropertyName);
+}
+
+uno::Any UpdateCheckUI::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet;
+
+ if( rPropertyName == PROPERTY_TITLE )
+ aRet <<= maBubbleManager.GetBubbleTitle();
+ else if( rPropertyName == PROPERTY_TEXT )
+ aRet <<= maBubbleManager.GetBubbleText();
+ else if( rPropertyName == PROPERTY_SHOW_BUBBLE )
+ aRet <<= maBubbleManager.GetShowBubble();
+ else if( rPropertyName == PROPERTY_IMAGE )
+ aRet <<= maBubbleImageURL;
+ else if( rPropertyName == PROPERTY_CLICK_HDL )
+ aRet <<= mrJob;
+ else if( rPropertyName == PROPERTY_SHOW_MENUICON )
+ aRet <<= maBubbleManager.GetShowMenuIcon();
+ else
+ throw beans::UnknownPropertyException(rPropertyName);
+
+ return aRet;
+}
+
+
+void UpdateCheckUI::addPropertyChangeListener( const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+{
+ //no bound properties
+}
+
+
+void UpdateCheckUI::removePropertyChangeListener( const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+{
+ //no bound properties
+}
+
+void UpdateCheckUI::addVetoableChangeListener( const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ //no vetoable properties
+}
+
+void UpdateCheckUI::removeVetoableChangeListener( const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ //no vetoable properties
+}
+
+IMPL_LINK_NOARG(UpdateCheckUI, ClickHdl, LinkParamNone*, void)
+{
+ SolarMutexGuard aGuard;
+
+ if ( mrJob.is() )
+ {
+ try {
+ uno::Sequence<beans::NamedValue> aEmpty;
+ mrJob->execute( aEmpty );
+ }
+ catch(const uno::Exception&) {
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ Translate::get(STR_NO_WEBBROWSER_FOUND, maSfxLocale)));
+ xErrorBox->run();
+ }
+ }
+}
+
+} // anonymous namespace
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+extensions_update_UpdateCheckUI_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ SolarMutexGuard aGuard;
+ return cppu::acquire(new UpdateCheckUI(context));
+}
+
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/extensions/source/update/ui/updchk.component b/extensions/source/update/ui/updchk.component
new file mode 100644
index 000000000..19c7dd86e
--- /dev/null
+++ b/extensions/source/update/ui/updchk.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="vnd.sun.UpdateCheckUI"
+ constructor="extensions_update_UpdateCheckUI_get_implementation">
+ <service name="com.sun.star.setup.UpdateCheckUI"/>
+ </implementation>
+</component>