From 267c6f2ac71f92999e969232431ba04678e7437e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 07:54:39 +0200 Subject: Adding upstream version 4:24.2.0. Signed-off-by: Daniel Baumann --- forms/AllLangMoTarget_frm.mk | 13 + forms/IwyuFilter_forms.yaml | 78 + forms/JunitTest_forms_complex.mk | 22 + forms/JunitTest_forms_unoapi_1.mk | 14 + forms/JunitTest_forms_unoapi_2.mk | 14 + forms/JunitTest_forms_unoapi_3.mk | 14 + forms/JunitTest_forms_unoapi_4.mk | 14 + forms/Library_frm.mk | 155 + forms/Makefile | 7 + forms/Module_forms.mk | 28 + forms/README.md | 3 + forms/inc/pch/precompiled_frm.cxx | 12 + forms/inc/pch/precompiled_frm.hxx | 275 ++ forms/inc/strings.hrc | 87 + forms/qa/complex/forms/CheckOGroupBoxModel.java | 168 + forms/qa/forms_all.sce | 25 + forms/qa/integration/forms/BooleanValidator.java | 64 + forms/qa/integration/forms/CellBinding.java | 543 +++ forms/qa/integration/forms/ControlValidation.java | 176 + forms/qa/integration/forms/ControlValidator.java | 45 + forms/qa/integration/forms/DateValidator.java | 80 + forms/qa/integration/forms/DocumentHelper.java | 375 ++ forms/qa/integration/forms/DocumentType.java | 39 + forms/qa/integration/forms/DocumentViewHelper.java | 221 ++ forms/qa/integration/forms/FormComponent.java | 114 + forms/qa/integration/forms/FormControlTest.java | 951 +++++ forms/qa/integration/forms/FormLayer.java | 318 ++ forms/qa/integration/forms/FormPropertyBags.java | 201 + forms/qa/integration/forms/ImageComparison.java | 79 + forms/qa/integration/forms/ListBox.java | 285 ++ forms/qa/integration/forms/ListSelection.java | 280 ++ forms/qa/integration/forms/ListSelection.props | 1 + .../integration/forms/ListSelectionValidator.java | 51 + forms/qa/integration/forms/MasterDetailForms.java | 400 ++ forms/qa/integration/forms/NumericValidator.java | 68 + forms/qa/integration/forms/RadioButtons.java | 426 +++ .../integration/forms/SingleControlValidation.java | 170 + .../qa/integration/forms/SpreadsheetDocument.java | 68 + forms/qa/integration/forms/SpreadsheetView.java | 59 + .../qa/integration/forms/TableCellTextBinding.java | 192 + forms/qa/integration/forms/TestCase.java | 150 + forms/qa/integration/forms/TextValidator.java | 65 + forms/qa/integration/forms/TimeValidator.java | 73 + forms/qa/integration/forms/ValueBinding.java | 112 + forms/qa/integration/forms/WaitForInput.java | 58 + forms/qa/integration/forms/XMLFormSettings.java | 213 ++ forms/qa/integration/forms/dbfTools.java | 73 + .../openoffice/complex/forms/tools/ResultSet.java | 273 ++ forms/qa/org/openoffice/xforms/Instance.java | 145 + forms/qa/org/openoffice/xforms/Model.java | 99 + forms/qa/org/openoffice/xforms/XMLDocument.java | 97 + forms/qa/unoapi/forms_1.sce | 27 + forms/qa/unoapi/forms_2.sce | 27 + forms/qa/unoapi/forms_3.sce | 27 + forms/qa/unoapi/forms_4.sce | 26 + forms/qa/unoapi/knownissues.xcl | 151 + forms/qa/unoapi/testdocuments/TestDB/testDB.dbf | Bin 0 -> 11101 bytes forms/qa/unoapi/testdocuments/TestDB/testDB.dbt | Bin 0 -> 18435 bytes forms/source/component/BaseListBox.hxx | 30 + forms/source/component/Button.cxx | 782 ++++ forms/source/component/Button.hxx | 201 + forms/source/component/CheckBox.cxx | 293 ++ forms/source/component/CheckBox.hxx | 83 + forms/source/component/Columns.cxx | 901 +++++ forms/source/component/Columns.hxx | 322 ++ forms/source/component/ComboBox.cxx | 888 +++++ forms/source/component/ComboBox.hxx | 145 + forms/source/component/Currency.cxx | 253 ++ forms/source/component/Currency.hxx | 85 + forms/source/component/DatabaseForm.cxx | 4042 ++++++++++++++++++++ forms/source/component/DatabaseForm.hxx | 528 +++ forms/source/component/Date.cxx | 326 ++ forms/source/component/Date.hxx | 116 + forms/source/component/Edit.cxx | 718 ++++ forms/source/component/Edit.hxx | 178 + forms/source/component/EditBase.cxx | 377 ++ forms/source/component/EditBase.hxx | 96 + forms/source/component/EventThread.cxx | 198 + forms/source/component/EventThread.hxx | 106 + forms/source/component/File.cxx | 275 ++ forms/source/component/File.hxx | 96 + forms/source/component/Filter.cxx | 882 +++++ forms/source/component/Filter.hxx | 138 + forms/source/component/FixedText.cxx | 125 + forms/source/component/FixedText.hxx | 67 + forms/source/component/FormComponent.cxx | 2816 ++++++++++++++ forms/source/component/FormattedField.cxx | 1022 +++++ forms/source/component/FormattedField.hxx | 182 + forms/source/component/FormattedFieldWrapper.cxx | 366 ++ forms/source/component/FormattedFieldWrapper.hxx | 89 + forms/source/component/FormsCollection.cxx | 141 + forms/source/component/FormsCollection.hxx | 149 + forms/source/component/Grid.cxx | 992 +++++ forms/source/component/Grid.hxx | 200 + forms/source/component/GroupBox.cxx | 162 + forms/source/component/GroupBox.hxx | 82 + forms/source/component/GroupManager.cxx | 422 ++ forms/source/component/GroupManager.hxx | 194 + forms/source/component/Hidden.cxx | 177 + forms/source/component/Hidden.hxx | 76 + forms/source/component/ImageButton.cxx | 249 ++ forms/source/component/ImageButton.hxx | 101 + forms/source/component/ImageControl.cxx | 992 +++++ forms/source/component/ImageControl.hxx | 196 + forms/source/component/ListBox.cxx | 2202 +++++++++++ forms/source/component/ListBox.hxx | 332 ++ forms/source/component/Numeric.cxx | 202 + forms/source/component/Numeric.hxx | 88 + forms/source/component/Pattern.cxx | 237 ++ forms/source/component/Pattern.hxx | 94 + forms/source/component/RadioButton.cxx | 402 ++ forms/source/component/RadioButton.hxx | 93 + forms/source/component/Time.cxx | 334 ++ forms/source/component/Time.hxx | 115 + forms/source/component/cachedrowset.cxx | 175 + forms/source/component/cachedrowset.hxx | 78 + forms/source/component/clickableimage.cxx | 838 ++++ forms/source/component/clickableimage.hxx | 282 ++ forms/source/component/cloneable.cxx | 59 + forms/source/component/entrylisthelper.cxx | 325 ++ forms/source/component/entrylisthelper.hxx | 196 + forms/source/component/errorbroadcaster.cxx | 94 + forms/source/component/errorbroadcaster.hxx | 60 + forms/source/component/findpos.cxx | 47 + forms/source/component/findpos.hxx | 31 + forms/source/component/formcontrolfont.cxx | 578 +++ forms/source/component/imgprod.cxx | 505 +++ forms/source/component/imgprod.hxx | 96 + forms/source/component/navigationbar.cxx | 487 +++ forms/source/component/navigationbar.hxx | 116 + forms/source/component/propertybaghelper.cxx | 338 ++ forms/source/component/refvaluecomponent.cxx | 294 ++ forms/source/component/refvaluecomponent.hxx | 87 + forms/source/component/scrollbar.cxx | 317 ++ forms/source/component/scrollbar.hxx | 93 + forms/source/component/spinbutton.cxx | 271 ++ forms/source/component/spinbutton.hxx | 89 + forms/source/helper/commandimageprovider.cxx | 131 + forms/source/helper/controlfeatureinterception.cxx | 147 + forms/source/helper/formnavigation.cxx | 447 +++ forms/source/helper/resettable.cxx | 79 + forms/source/helper/urltransformer.cxx | 83 + forms/source/helper/windowstateguard.cxx | 213 ++ forms/source/inc/FormComponent.hxx | 1207 ++++++ forms/source/inc/InterfaceContainer.hxx | 301 ++ forms/source/inc/cloneable.hxx | 41 + forms/source/inc/commandimageprovider.hxx | 53 + forms/source/inc/componenttools.hxx | 84 + forms/source/inc/controlfeatureinterception.hxx | 87 + forms/source/inc/featuredispatcher.hxx | 101 + forms/source/inc/formcontrolfont.hxx | 96 + forms/source/inc/formnavigation.hxx | 217 ++ forms/source/inc/frm_resource.hxx | 38 + forms/source/inc/frm_strings.hxx | 287 ++ forms/source/inc/limitedformats.hxx | 93 + forms/source/inc/property.hxx | 340 ++ forms/source/inc/propertybaghelper.hxx | 147 + forms/source/inc/resettable.hxx | 62 + forms/source/inc/services.hxx | 195 + forms/source/inc/togglestate.hxx | 33 + forms/source/inc/urltransformer.hxx | 70 + forms/source/inc/windowstateguard.hxx | 70 + forms/source/misc/InterfaceContainer.cxx | 1306 +++++++ forms/source/misc/componenttools.cxx | 105 + forms/source/misc/limitedformats.cxx | 379 ++ forms/source/misc/property.cxx | 220 ++ forms/source/resource/frm_resource.cxx | 30 + forms/source/richtext/attributedispatcher.cxx | 115 + forms/source/richtext/attributedispatcher.hxx | 79 + forms/source/richtext/clipboarddispatcher.cxx | 191 + forms/source/richtext/clipboarddispatcher.hxx | 91 + forms/source/richtext/featuredispatcher.cxx | 140 + forms/source/richtext/featuredispatcher.hxx | 89 + .../richtext/parametrizedattributedispatcher.cxx | 129 + .../richtext/parametrizedattributedispatcher.hxx | 61 + forms/source/richtext/richtextcontrol.cxx | 652 ++++ forms/source/richtext/richtextcontrol.hxx | 128 + forms/source/richtext/richtextengine.cxx | 138 + forms/source/richtext/richtextengine.hxx | 71 + forms/source/richtext/richtextimplcontrol.cxx | 639 ++++ forms/source/richtext/richtextimplcontrol.hxx | 183 + forms/source/richtext/richtextmodel.cxx | 641 ++++ forms/source/richtext/richtextmodel.hxx | 186 + forms/source/richtext/richtextunowrapper.cxx | 116 + forms/source/richtext/richtextunowrapper.hxx | 81 + forms/source/richtext/richtextvclcontrol.cxx | 351 ++ forms/source/richtext/richtextvclcontrol.hxx | 122 + forms/source/richtext/richtextviewport.cxx | 102 + forms/source/richtext/richtextviewport.hxx | 64 + forms/source/richtext/rtattributehandler.cxx | 441 +++ forms/source/richtext/rtattributehandler.hxx | 163 + forms/source/richtext/rtattributes.hxx | 132 + forms/source/richtext/specialdispatchers.cxx | 171 + forms/source/richtext/specialdispatchers.hxx | 91 + forms/source/richtext/textattributelistener.hxx | 53 + forms/source/runtime/formoperations.cxx | 1766 +++++++++ forms/source/runtime/formoperations.hxx | 382 ++ forms/source/solar/component/navbarcontrol.cxx | 500 +++ forms/source/solar/component/navbarcontrol.hxx | 128 + forms/source/solar/control/navtoolbar.cxx | 641 ++++ forms/source/solar/inc/navtoolbar.hxx | 164 + forms/source/xforms/NameContainer.hxx | 159 + forms/source/xforms/binding.cxx | 1275 ++++++ forms/source/xforms/binding.hxx | 406 ++ forms/source/xforms/boolexpression.cxx | 42 + forms/source/xforms/boolexpression.hxx | 44 + forms/source/xforms/collection.hxx | 280 ++ forms/source/xforms/computedexpression.cxx | 182 + forms/source/xforms/computedexpression.hxx | 121 + forms/source/xforms/convert.cxx | 336 ++ forms/source/xforms/convert.hxx | 78 + forms/source/xforms/datatyperepository.cxx | 295 ++ forms/source/xforms/datatyperepository.hxx | 83 + forms/source/xforms/datatypes.cxx | 1199 ++++++ forms/source/xforms/datatypes.hxx | 422 ++ forms/source/xforms/enumeration.cxx | 59 + forms/source/xforms/enumeration.hxx | 50 + forms/source/xforms/evaluationcontext.hxx | 58 + forms/source/xforms/mip.cxx | 98 + forms/source/xforms/mip.hxx | 102 + forms/source/xforms/model.cxx | 606 +++ forms/source/xforms/model.hxx | 359 ++ forms/source/xforms/model_helper.hxx | 146 + forms/source/xforms/model_ui.cxx | 1004 +++++ forms/source/xforms/namedcollection.hxx | 111 + forms/source/xforms/pathexpression.cxx | 113 + forms/source/xforms/pathexpression.hxx | 71 + forms/source/xforms/propertysetbase.cxx | 158 + forms/source/xforms/propertysetbase.hxx | 340 ++ forms/source/xforms/resourcehelper.cxx | 65 + forms/source/xforms/resourcehelper.hxx | 41 + forms/source/xforms/submission.cxx | 616 +++ forms/source/xforms/submission.hxx | 219 ++ forms/source/xforms/submission/replace.cxx | 131 + forms/source/xforms/submission/serialization.hxx | 60 + .../xforms/submission/serialization_app_xml.cxx | 126 + .../xforms/submission/serialization_app_xml.hxx | 40 + .../xforms/submission/serialization_urlencoded.cxx | 181 + .../xforms/submission/serialization_urlencoded.hxx | 47 + forms/source/xforms/submission/submission.hxx | 132 + forms/source/xforms/submission/submission_get.cxx | 98 + forms/source/xforms/submission/submission_get.hxx | 33 + forms/source/xforms/submission/submission_post.cxx | 78 + forms/source/xforms/submission/submission_post.hxx | 32 + forms/source/xforms/submission/submission_put.cxx | 67 + forms/source/xforms/submission/submission_put.hxx | 33 + forms/source/xforms/unohelper.cxx | 73 + forms/source/xforms/unohelper.hxx | 41 + forms/source/xforms/xforms_services.cxx | 59 + forms/source/xforms/xformsevent.cxx | 83 + forms/source/xforms/xformsevent.hxx | 68 + forms/source/xforms/xmlhelper.cxx | 133 + forms/source/xforms/xmlhelper.hxx | 40 + forms/source/xforms/xpathlib/extension.cxx | 74 + forms/source/xforms/xpathlib/extension.hxx | 52 + forms/source/xforms/xpathlib/xpathlib.cxx | 542 +++ forms/source/xforms/xpathlib/xpathlib.hxx | 74 + forms/util/frm.component | 295 ++ forms/workben/xforms/sickness.odt | Bin 0 -> 9233 bytes forms/workben/xforms/simple_validation.odt | Bin 0 -> 7184 bytes forms/workben/xforms/vacationrequest.odt | Bin 0 -> 8089 bytes forms/workben/xforms/xforms-ubl.odt | Bin 0 -> 22265 bytes 262 files changed, 65558 insertions(+) create mode 100644 forms/AllLangMoTarget_frm.mk create mode 100644 forms/IwyuFilter_forms.yaml create mode 100644 forms/JunitTest_forms_complex.mk create mode 100644 forms/JunitTest_forms_unoapi_1.mk create mode 100644 forms/JunitTest_forms_unoapi_2.mk create mode 100644 forms/JunitTest_forms_unoapi_3.mk create mode 100644 forms/JunitTest_forms_unoapi_4.mk create mode 100644 forms/Library_frm.mk create mode 100644 forms/Makefile create mode 100644 forms/Module_forms.mk create mode 100644 forms/README.md create mode 100644 forms/inc/pch/precompiled_frm.cxx create mode 100644 forms/inc/pch/precompiled_frm.hxx create mode 100644 forms/inc/strings.hrc create mode 100644 forms/qa/complex/forms/CheckOGroupBoxModel.java create mode 100644 forms/qa/forms_all.sce create mode 100644 forms/qa/integration/forms/BooleanValidator.java create mode 100644 forms/qa/integration/forms/CellBinding.java create mode 100644 forms/qa/integration/forms/ControlValidation.java create mode 100644 forms/qa/integration/forms/ControlValidator.java create mode 100644 forms/qa/integration/forms/DateValidator.java create mode 100644 forms/qa/integration/forms/DocumentHelper.java create mode 100644 forms/qa/integration/forms/DocumentType.java create mode 100644 forms/qa/integration/forms/DocumentViewHelper.java create mode 100644 forms/qa/integration/forms/FormComponent.java create mode 100644 forms/qa/integration/forms/FormControlTest.java create mode 100644 forms/qa/integration/forms/FormLayer.java create mode 100644 forms/qa/integration/forms/FormPropertyBags.java create mode 100644 forms/qa/integration/forms/ImageComparison.java create mode 100644 forms/qa/integration/forms/ListBox.java create mode 100644 forms/qa/integration/forms/ListSelection.java create mode 100644 forms/qa/integration/forms/ListSelection.props create mode 100644 forms/qa/integration/forms/ListSelectionValidator.java create mode 100644 forms/qa/integration/forms/MasterDetailForms.java create mode 100644 forms/qa/integration/forms/NumericValidator.java create mode 100644 forms/qa/integration/forms/RadioButtons.java create mode 100644 forms/qa/integration/forms/SingleControlValidation.java create mode 100644 forms/qa/integration/forms/SpreadsheetDocument.java create mode 100644 forms/qa/integration/forms/SpreadsheetView.java create mode 100644 forms/qa/integration/forms/TableCellTextBinding.java create mode 100644 forms/qa/integration/forms/TestCase.java create mode 100644 forms/qa/integration/forms/TextValidator.java create mode 100644 forms/qa/integration/forms/TimeValidator.java create mode 100644 forms/qa/integration/forms/ValueBinding.java create mode 100644 forms/qa/integration/forms/WaitForInput.java create mode 100644 forms/qa/integration/forms/XMLFormSettings.java create mode 100644 forms/qa/integration/forms/dbfTools.java create mode 100644 forms/qa/org/openoffice/complex/forms/tools/ResultSet.java create mode 100644 forms/qa/org/openoffice/xforms/Instance.java create mode 100644 forms/qa/org/openoffice/xforms/Model.java create mode 100644 forms/qa/org/openoffice/xforms/XMLDocument.java create mode 100644 forms/qa/unoapi/forms_1.sce create mode 100644 forms/qa/unoapi/forms_2.sce create mode 100644 forms/qa/unoapi/forms_3.sce create mode 100644 forms/qa/unoapi/forms_4.sce create mode 100644 forms/qa/unoapi/knownissues.xcl create mode 100644 forms/qa/unoapi/testdocuments/TestDB/testDB.dbf create mode 100644 forms/qa/unoapi/testdocuments/TestDB/testDB.dbt create mode 100644 forms/source/component/BaseListBox.hxx create mode 100644 forms/source/component/Button.cxx create mode 100644 forms/source/component/Button.hxx create mode 100644 forms/source/component/CheckBox.cxx create mode 100644 forms/source/component/CheckBox.hxx create mode 100644 forms/source/component/Columns.cxx create mode 100644 forms/source/component/Columns.hxx create mode 100644 forms/source/component/ComboBox.cxx create mode 100644 forms/source/component/ComboBox.hxx create mode 100644 forms/source/component/Currency.cxx create mode 100644 forms/source/component/Currency.hxx create mode 100644 forms/source/component/DatabaseForm.cxx create mode 100644 forms/source/component/DatabaseForm.hxx create mode 100644 forms/source/component/Date.cxx create mode 100644 forms/source/component/Date.hxx create mode 100644 forms/source/component/Edit.cxx create mode 100644 forms/source/component/Edit.hxx create mode 100644 forms/source/component/EditBase.cxx create mode 100644 forms/source/component/EditBase.hxx create mode 100644 forms/source/component/EventThread.cxx create mode 100644 forms/source/component/EventThread.hxx create mode 100644 forms/source/component/File.cxx create mode 100644 forms/source/component/File.hxx create mode 100644 forms/source/component/Filter.cxx create mode 100644 forms/source/component/Filter.hxx create mode 100644 forms/source/component/FixedText.cxx create mode 100644 forms/source/component/FixedText.hxx create mode 100644 forms/source/component/FormComponent.cxx create mode 100644 forms/source/component/FormattedField.cxx create mode 100644 forms/source/component/FormattedField.hxx create mode 100644 forms/source/component/FormattedFieldWrapper.cxx create mode 100644 forms/source/component/FormattedFieldWrapper.hxx create mode 100644 forms/source/component/FormsCollection.cxx create mode 100644 forms/source/component/FormsCollection.hxx create mode 100644 forms/source/component/Grid.cxx create mode 100644 forms/source/component/Grid.hxx create mode 100644 forms/source/component/GroupBox.cxx create mode 100644 forms/source/component/GroupBox.hxx create mode 100644 forms/source/component/GroupManager.cxx create mode 100644 forms/source/component/GroupManager.hxx create mode 100644 forms/source/component/Hidden.cxx create mode 100644 forms/source/component/Hidden.hxx create mode 100644 forms/source/component/ImageButton.cxx create mode 100644 forms/source/component/ImageButton.hxx create mode 100644 forms/source/component/ImageControl.cxx create mode 100644 forms/source/component/ImageControl.hxx create mode 100644 forms/source/component/ListBox.cxx create mode 100644 forms/source/component/ListBox.hxx create mode 100644 forms/source/component/Numeric.cxx create mode 100644 forms/source/component/Numeric.hxx create mode 100644 forms/source/component/Pattern.cxx create mode 100644 forms/source/component/Pattern.hxx create mode 100644 forms/source/component/RadioButton.cxx create mode 100644 forms/source/component/RadioButton.hxx create mode 100644 forms/source/component/Time.cxx create mode 100644 forms/source/component/Time.hxx create mode 100644 forms/source/component/cachedrowset.cxx create mode 100644 forms/source/component/cachedrowset.hxx create mode 100644 forms/source/component/clickableimage.cxx create mode 100644 forms/source/component/clickableimage.hxx create mode 100644 forms/source/component/cloneable.cxx create mode 100644 forms/source/component/entrylisthelper.cxx create mode 100644 forms/source/component/entrylisthelper.hxx create mode 100644 forms/source/component/errorbroadcaster.cxx create mode 100644 forms/source/component/errorbroadcaster.hxx create mode 100644 forms/source/component/findpos.cxx create mode 100644 forms/source/component/findpos.hxx create mode 100644 forms/source/component/formcontrolfont.cxx create mode 100644 forms/source/component/imgprod.cxx create mode 100644 forms/source/component/imgprod.hxx create mode 100644 forms/source/component/navigationbar.cxx create mode 100644 forms/source/component/navigationbar.hxx create mode 100644 forms/source/component/propertybaghelper.cxx create mode 100644 forms/source/component/refvaluecomponent.cxx create mode 100644 forms/source/component/refvaluecomponent.hxx create mode 100644 forms/source/component/scrollbar.cxx create mode 100644 forms/source/component/scrollbar.hxx create mode 100644 forms/source/component/spinbutton.cxx create mode 100644 forms/source/component/spinbutton.hxx create mode 100644 forms/source/helper/commandimageprovider.cxx create mode 100644 forms/source/helper/controlfeatureinterception.cxx create mode 100644 forms/source/helper/formnavigation.cxx create mode 100644 forms/source/helper/resettable.cxx create mode 100644 forms/source/helper/urltransformer.cxx create mode 100644 forms/source/helper/windowstateguard.cxx create mode 100644 forms/source/inc/FormComponent.hxx create mode 100644 forms/source/inc/InterfaceContainer.hxx create mode 100644 forms/source/inc/cloneable.hxx create mode 100644 forms/source/inc/commandimageprovider.hxx create mode 100644 forms/source/inc/componenttools.hxx create mode 100644 forms/source/inc/controlfeatureinterception.hxx create mode 100644 forms/source/inc/featuredispatcher.hxx create mode 100644 forms/source/inc/formcontrolfont.hxx create mode 100644 forms/source/inc/formnavigation.hxx create mode 100644 forms/source/inc/frm_resource.hxx create mode 100644 forms/source/inc/frm_strings.hxx create mode 100644 forms/source/inc/limitedformats.hxx create mode 100644 forms/source/inc/property.hxx create mode 100644 forms/source/inc/propertybaghelper.hxx create mode 100644 forms/source/inc/resettable.hxx create mode 100644 forms/source/inc/services.hxx create mode 100644 forms/source/inc/togglestate.hxx create mode 100644 forms/source/inc/urltransformer.hxx create mode 100644 forms/source/inc/windowstateguard.hxx create mode 100644 forms/source/misc/InterfaceContainer.cxx create mode 100644 forms/source/misc/componenttools.cxx create mode 100644 forms/source/misc/limitedformats.cxx create mode 100644 forms/source/misc/property.cxx create mode 100644 forms/source/resource/frm_resource.cxx create mode 100644 forms/source/richtext/attributedispatcher.cxx create mode 100644 forms/source/richtext/attributedispatcher.hxx create mode 100644 forms/source/richtext/clipboarddispatcher.cxx create mode 100644 forms/source/richtext/clipboarddispatcher.hxx create mode 100644 forms/source/richtext/featuredispatcher.cxx create mode 100644 forms/source/richtext/featuredispatcher.hxx create mode 100644 forms/source/richtext/parametrizedattributedispatcher.cxx create mode 100644 forms/source/richtext/parametrizedattributedispatcher.hxx create mode 100644 forms/source/richtext/richtextcontrol.cxx create mode 100644 forms/source/richtext/richtextcontrol.hxx create mode 100644 forms/source/richtext/richtextengine.cxx create mode 100644 forms/source/richtext/richtextengine.hxx create mode 100644 forms/source/richtext/richtextimplcontrol.cxx create mode 100644 forms/source/richtext/richtextimplcontrol.hxx create mode 100644 forms/source/richtext/richtextmodel.cxx create mode 100644 forms/source/richtext/richtextmodel.hxx create mode 100644 forms/source/richtext/richtextunowrapper.cxx create mode 100644 forms/source/richtext/richtextunowrapper.hxx create mode 100644 forms/source/richtext/richtextvclcontrol.cxx create mode 100644 forms/source/richtext/richtextvclcontrol.hxx create mode 100644 forms/source/richtext/richtextviewport.cxx create mode 100644 forms/source/richtext/richtextviewport.hxx create mode 100644 forms/source/richtext/rtattributehandler.cxx create mode 100644 forms/source/richtext/rtattributehandler.hxx create mode 100644 forms/source/richtext/rtattributes.hxx create mode 100644 forms/source/richtext/specialdispatchers.cxx create mode 100644 forms/source/richtext/specialdispatchers.hxx create mode 100644 forms/source/richtext/textattributelistener.hxx create mode 100644 forms/source/runtime/formoperations.cxx create mode 100644 forms/source/runtime/formoperations.hxx create mode 100644 forms/source/solar/component/navbarcontrol.cxx create mode 100644 forms/source/solar/component/navbarcontrol.hxx create mode 100644 forms/source/solar/control/navtoolbar.cxx create mode 100644 forms/source/solar/inc/navtoolbar.hxx create mode 100644 forms/source/xforms/NameContainer.hxx create mode 100644 forms/source/xforms/binding.cxx create mode 100644 forms/source/xforms/binding.hxx create mode 100644 forms/source/xforms/boolexpression.cxx create mode 100644 forms/source/xforms/boolexpression.hxx create mode 100644 forms/source/xforms/collection.hxx create mode 100644 forms/source/xforms/computedexpression.cxx create mode 100644 forms/source/xforms/computedexpression.hxx create mode 100644 forms/source/xforms/convert.cxx create mode 100644 forms/source/xforms/convert.hxx create mode 100644 forms/source/xforms/datatyperepository.cxx create mode 100644 forms/source/xforms/datatyperepository.hxx create mode 100644 forms/source/xforms/datatypes.cxx create mode 100644 forms/source/xforms/datatypes.hxx create mode 100644 forms/source/xforms/enumeration.cxx create mode 100644 forms/source/xforms/enumeration.hxx create mode 100644 forms/source/xforms/evaluationcontext.hxx create mode 100644 forms/source/xforms/mip.cxx create mode 100644 forms/source/xforms/mip.hxx create mode 100644 forms/source/xforms/model.cxx create mode 100644 forms/source/xforms/model.hxx create mode 100644 forms/source/xforms/model_helper.hxx create mode 100644 forms/source/xforms/model_ui.cxx create mode 100644 forms/source/xforms/namedcollection.hxx create mode 100644 forms/source/xforms/pathexpression.cxx create mode 100644 forms/source/xforms/pathexpression.hxx create mode 100644 forms/source/xforms/propertysetbase.cxx create mode 100644 forms/source/xforms/propertysetbase.hxx create mode 100644 forms/source/xforms/resourcehelper.cxx create mode 100644 forms/source/xforms/resourcehelper.hxx create mode 100644 forms/source/xforms/submission.cxx create mode 100644 forms/source/xforms/submission.hxx create mode 100644 forms/source/xforms/submission/replace.cxx create mode 100644 forms/source/xforms/submission/serialization.hxx create mode 100644 forms/source/xforms/submission/serialization_app_xml.cxx create mode 100644 forms/source/xforms/submission/serialization_app_xml.hxx create mode 100644 forms/source/xforms/submission/serialization_urlencoded.cxx create mode 100644 forms/source/xforms/submission/serialization_urlencoded.hxx create mode 100644 forms/source/xforms/submission/submission.hxx create mode 100644 forms/source/xforms/submission/submission_get.cxx create mode 100644 forms/source/xforms/submission/submission_get.hxx create mode 100644 forms/source/xforms/submission/submission_post.cxx create mode 100644 forms/source/xforms/submission/submission_post.hxx create mode 100644 forms/source/xforms/submission/submission_put.cxx create mode 100644 forms/source/xforms/submission/submission_put.hxx create mode 100644 forms/source/xforms/unohelper.cxx create mode 100644 forms/source/xforms/unohelper.hxx create mode 100644 forms/source/xforms/xforms_services.cxx create mode 100644 forms/source/xforms/xformsevent.cxx create mode 100644 forms/source/xforms/xformsevent.hxx create mode 100644 forms/source/xforms/xmlhelper.cxx create mode 100644 forms/source/xforms/xmlhelper.hxx create mode 100644 forms/source/xforms/xpathlib/extension.cxx create mode 100644 forms/source/xforms/xpathlib/extension.hxx create mode 100644 forms/source/xforms/xpathlib/xpathlib.cxx create mode 100644 forms/source/xforms/xpathlib/xpathlib.hxx create mode 100644 forms/util/frm.component create mode 100644 forms/workben/xforms/sickness.odt create mode 100644 forms/workben/xforms/simple_validation.odt create mode 100644 forms/workben/xforms/vacationrequest.odt create mode 100644 forms/workben/xforms/xforms-ubl.odt (limited to 'forms') diff --git a/forms/AllLangMoTarget_frm.mk b/forms/AllLangMoTarget_frm.mk new file mode 100644 index 0000000000..daca0e06d1 --- /dev/null +++ b/forms/AllLangMoTarget_frm.mk @@ -0,0 +1,13 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$(eval $(call gb_AllLangMoTarget_AllLangMoTarget,frm)) + +$(eval $(call gb_AllLangMoTarget_set_polocation,frm,forms)) + +# vim: set noet sw=4 ts=4: diff --git a/forms/IwyuFilter_forms.yaml b/forms/IwyuFilter_forms.yaml new file mode 100644 index 0000000000..cdc4d2cf06 --- /dev/null +++ b/forms/IwyuFilter_forms.yaml @@ -0,0 +1,78 @@ +--- +assumeFilename: forms/source/component/DatabaseForm.cxx +excludelist: + forms/source/inc/property.hxx: + # Needed for macro defines + - frm_strings.hxx + forms/inc/strings.hrc: + # Needed for TranslateId macro + - unotools/resmgr.hxx + forms/source/component/errorbroadcaster.hxx: + # Don't propose hxx -> h change in URE libs + - cppuhelper/interfacecontainer.hxx + forms/source/xforms/unohelper.hxx: + # Needed for css shortcut + - sal/types.h + forms/source/xforms/datatypes.hxx: + # Needed for macro defines + - com/sun/star/beans/PropertyAttribute.hpp + forms/source/xforms/propertysetbase.hxx: + # Needed for macro defines + - com/sun/star/beans/PropertyAttribute.hpp + forms/source/component/DatabaseForm.cxx: + # Actually used + - com/sun/star/io/XObjectInputStream.hpp + - com/sun/star/io/XObjectOutputStream.hpp + - tools/datetime.hxx + forms/source/component/ImageControl.cxx: + # Needed for template + - com/sun/star/ui/dialogs/XFilePicker3.hpp + forms/source/component/imgprod.cxx: + # Actually used + - com/sun/star/io/XInputStream.hpp + forms/source/component/scrollbar.cxx: + # Needed for rtl::math::round + - rtl/math.hxx + forms/source/richtext/parametrizedattributedispatcher.cxx: + # Actually used + - com/sun/star/beans/PropertyValue.hpp + forms/source/richtext/richtextunowrapper.cxx: + # Needed for UnoType template instantiation + - com/sun/star/lang/Locale.hpp + # Needed for macro defines to work + - editeng/unoprnms.hxx + forms/source/runtime/formoperations.cxx: + # Needed for template + - com/sun/star/sdbc/XConnection.hpp + forms/source/solar/component/navbarcontrol.cxx: + # Actually used + - com/sun/star/awt/XControlModel.hpp + forms/source/xforms/binding.cxx: + # Actually used + - com/sun/star/xml/dom/XNodeList.hpp + - com/sun/star/xml/dom/events/XEventListener.hpp + - com/sun/star/lang/XUnoTunnel.hpp + - com/sun/star/container/XNameContainer.hpp + forms/source/xforms/enumeration.cxx: + # Actually used + - com/sun/star/container/XIndexAccess.hpp + - com/sun/star/uno/Any.hxx + forms/source/xforms/pathexpression.cxx: + # Actually used + - com/sun/star/xml/dom/XNode.hpp + - com/sun/star/xml/dom/XNodeList.hpp + forms/source/xforms/propertysetbase.cxx: + # Actually used + - com/sun/star/uno/Reference.hxx + forms/source/xforms/submission.cxx: + # Actually used + - com/sun/star/xforms/XModel.hpp + - com/sun/star/xml/dom/XDocumentFragment.hpp + - com/sun/star/task/XInteractionHandler.hpp + forms/source/xforms/unohelper.cxx: + # Actually used + - com/sun/star/uno/Reference.hxx + - com/sun/star/beans/XPropertySet.hpp + forms/source/xforms/submission/serialization_urlencoded.cxx: + # Actually used + - com/sun/star/xml/dom/XNode.hpp diff --git a/forms/JunitTest_forms_complex.mk b/forms/JunitTest_forms_complex.mk new file mode 100644 index 0000000000..6f95409a8f --- /dev/null +++ b/forms/JunitTest_forms_complex.mk @@ -0,0 +1,22 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_JunitTest_JunitTest,forms_complex)) + +$(eval $(call gb_JunitTest_use_unoapi_jars,forms_complex)) + +$(eval $(call gb_JunitTest_add_sourcefiles,forms_complex,\ + forms/qa/complex/forms/CheckOGroupBoxModel \ +)) + +$(eval $(call gb_JunitTest_add_classes,forms_complex,\ + complex.forms.CheckOGroupBoxModel \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/forms/JunitTest_forms_unoapi_1.mk b/forms/JunitTest_forms_unoapi_1.mk new file mode 100644 index 0000000000..bf900dc78f --- /dev/null +++ b/forms/JunitTest_forms_unoapi_1.mk @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_JunitTest_JunitTest,forms_unoapi_1)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,forms_unoapi_1)) + +# vim: set noet sw=4 ts=4: diff --git a/forms/JunitTest_forms_unoapi_2.mk b/forms/JunitTest_forms_unoapi_2.mk new file mode 100644 index 0000000000..21121bb542 --- /dev/null +++ b/forms/JunitTest_forms_unoapi_2.mk @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_JunitTest_JunitTest,forms_unoapi_2)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,forms_unoapi_2)) + +# vim: set noet sw=4 ts=4: diff --git a/forms/JunitTest_forms_unoapi_3.mk b/forms/JunitTest_forms_unoapi_3.mk new file mode 100644 index 0000000000..7ed5010ce4 --- /dev/null +++ b/forms/JunitTest_forms_unoapi_3.mk @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_JunitTest_JunitTest,forms_unoapi_3)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,forms_unoapi_3)) + +# vim: set noet sw=4 ts=4: diff --git a/forms/JunitTest_forms_unoapi_4.mk b/forms/JunitTest_forms_unoapi_4.mk new file mode 100644 index 0000000000..a07ca857f6 --- /dev/null +++ b/forms/JunitTest_forms_unoapi_4.mk @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_JunitTest_JunitTest,forms_unoapi_4)) + +$(eval $(call gb_JunitTest_set_unoapi_test_defaults,forms_unoapi_4)) + +# vim: set noet sw=4 ts=4: diff --git a/forms/Library_frm.mk b/forms/Library_frm.mk new file mode 100644 index 0000000000..8fc09fc700 --- /dev/null +++ b/forms/Library_frm.mk @@ -0,0 +1,155 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Library_Library,frm)) + +$(eval $(call gb_Library_set_include,frm,\ + $$(INCLUDE) \ + -I$(SRCDIR)/forms/inc \ + -I$(SRCDIR)/forms/source/inc \ + -I$(SRCDIR)/forms/source/solar/inc \ + -I$(WORKDIR)/YaccTarget/connectivity/source/parse \ +)) + +$(eval $(call gb_Library_set_precompiled_header,frm,forms/inc/pch/precompiled_frm)) + +$(eval $(call gb_Library_use_api,frm,\ + offapi \ + oovbaapi \ + udkapi \ +)) + +$(eval $(call gb_Library_use_libraries,frm,\ + comphelper \ + cppu \ + cppuhelper \ + $(call gb_Helper_optional,DBCONNECTIVITY,dbtools) \ + editeng \ + i18nlangtag \ + sal \ + salhelper \ + sfx \ + svl \ + svt \ + svxcore \ + tk \ + tl \ + ucbhelper \ + utl \ + vcl \ +)) + +$(eval $(call gb_Library_use_externals,frm,\ + boost_headers \ + icui18n \ + icuuc \ + icu_headers \ + libxml2 \ +)) + +$(eval $(call gb_Library_set_componentfile,frm,forms/util/frm,services)) + +$(eval $(call gb_Library_add_exception_objects,frm,\ + forms/source/component/Button \ + forms/source/component/cachedrowset \ + forms/source/component/CheckBox \ + forms/source/component/clickableimage \ + forms/source/component/cloneable \ + forms/source/component/errorbroadcaster \ + forms/source/component/Columns \ + forms/source/component/ComboBox \ + forms/source/component/Currency \ + forms/source/component/Date \ + forms/source/component/DatabaseForm \ + forms/source/component/EditBase \ + forms/source/component/Edit \ + forms/source/component/entrylisthelper \ + forms/source/component/EventThread \ + forms/source/component/File \ + forms/source/component/Filter \ + forms/source/component/findpos \ + forms/source/component/FixedText \ + forms/source/component/FormattedField \ + forms/source/component/FormattedFieldWrapper \ + forms/source/component/FormComponent \ + forms/source/component/formcontrolfont \ + forms/source/component/FormsCollection \ + forms/source/component/Grid \ + forms/source/component/GroupBox \ + forms/source/component/GroupManager \ + forms/source/component/Hidden \ + forms/source/component/ImageButton \ + forms/source/component/ImageControl \ + forms/source/component/imgprod \ + forms/source/component/ListBox \ + forms/source/component/navigationbar \ + forms/source/component/Numeric \ + forms/source/component/Pattern \ + forms/source/component/propertybaghelper \ + forms/source/component/RadioButton \ + forms/source/component/refvaluecomponent \ + forms/source/component/scrollbar \ + forms/source/component/spinbutton \ + forms/source/component/Time \ + forms/source/helper/commandimageprovider \ + forms/source/helper/controlfeatureinterception \ + forms/source/helper/formnavigation \ + forms/source/helper/resettable \ + forms/source/helper/urltransformer \ + forms/source/helper/windowstateguard \ + forms/source/misc/componenttools \ + forms/source/misc/InterfaceContainer \ + forms/source/misc/limitedformats \ + forms/source/misc/property \ + forms/source/resource/frm_resource \ + forms/source/richtext/attributedispatcher \ + forms/source/richtext/clipboarddispatcher \ + forms/source/richtext/featuredispatcher \ + forms/source/richtext/parametrizedattributedispatcher \ + forms/source/richtext/richtextcontrol \ + forms/source/richtext/richtextengine \ + forms/source/richtext/richtextimplcontrol \ + forms/source/richtext/richtextmodel \ + forms/source/richtext/richtextunowrapper \ + forms/source/richtext/richtextvclcontrol \ + forms/source/richtext/richtextviewport \ + forms/source/richtext/rtattributehandler \ + forms/source/richtext/specialdispatchers \ + forms/source/runtime/formoperations \ + forms/source/solar/component/navbarcontrol \ + forms/source/solar/control/navtoolbar \ + forms/source/xforms/binding \ + forms/source/xforms/boolexpression \ + forms/source/xforms/computedexpression \ + forms/source/xforms/convert \ + forms/source/xforms/datatyperepository \ + forms/source/xforms/datatypes \ + forms/source/xforms/enumeration \ + forms/source/xforms/mip \ + forms/source/xforms/model \ + forms/source/xforms/model_ui \ + forms/source/xforms/pathexpression \ + forms/source/xforms/propertysetbase \ + forms/source/xforms/resourcehelper \ + forms/source/xforms/submission \ + forms/source/xforms/submission/replace \ + forms/source/xforms/submission/serialization_app_xml \ + forms/source/xforms/submission/serialization_urlencoded \ + forms/source/xforms/submission/submission_get \ + forms/source/xforms/submission/submission_post \ + forms/source/xforms/submission/submission_put \ + forms/source/xforms/unohelper \ + forms/source/xforms/xformsevent \ + forms/source/xforms/xforms_services \ + forms/source/xforms/xmlhelper \ + forms/source/xforms/xpathlib/extension \ + forms/source/xforms/xpathlib/xpathlib \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/forms/Makefile b/forms/Makefile new file mode 100644 index 0000000000..ccb1c85a04 --- /dev/null +++ b/forms/Makefile @@ -0,0 +1,7 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/forms/Module_forms.mk b/forms/Module_forms.mk new file mode 100644 index 0000000000..7ba484b81d --- /dev/null +++ b/forms/Module_forms.mk @@ -0,0 +1,28 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Module_Module,forms)) + +$(eval $(call gb_Module_add_targets,forms,\ + Library_frm \ +)) + +$(eval $(call gb_Module_add_l10n_targets,forms,\ + AllLangMoTarget_frm \ +)) + +$(eval $(call gb_Module_add_subsequentcheck_targets,forms,\ + JunitTest_forms_unoapi_1 \ + JunitTest_forms_unoapi_2 \ + JunitTest_forms_unoapi_3 \ + JunitTest_forms_unoapi_4 \ + JunitTest_forms_complex \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/forms/README.md b/forms/README.md new file mode 100644 index 0000000000..53a20e14b8 --- /dev/null +++ b/forms/README.md @@ -0,0 +1,3 @@ +# Embedded Forms Control and Pieces + +design forms in documents, fields and database driven. diff --git a/forms/inc/pch/precompiled_frm.cxx b/forms/inc/pch/precompiled_frm.cxx new file mode 100644 index 0000000000..ea011d914e --- /dev/null +++ b/forms/inc/pch/precompiled_frm.cxx @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "precompiled_frm.hxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/inc/pch/precompiled_frm.hxx b/forms/inc/pch/precompiled_frm.hxx new file mode 100644 index 0000000000..cbf7aa3cf8 --- /dev/null +++ b/forms/inc/pch/precompiled_frm.hxx @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + This file has been autogenerated by update_pch.sh. It is possible to edit it + manually (such as when an include file has been moved/renamed/removed). All such + manual changes will be rewritten by the next run of update_pch.sh (which presumably + also fixes all possible problems, so it's usually better to use it). + + Generated on 2021-04-08 13:50:44 using: + ./bin/update_pch forms frm --cutoff=2 --exclude:system --exclude:module --exclude:local + + If after updating build fails, use the following command to locate conflicting headers: + ./bin/update_pch_bisect ./forms/inc/pch/precompiled_frm.hxx "make forms.build" --find-conflicts +*/ + +#include +#if PCH_LEVEL >= 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // PCH_LEVEL >= 1 +#if PCH_LEVEL >= 2 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // PCH_LEVEL >= 2 +#if PCH_LEVEL >= 3 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // PCH_LEVEL >= 3 +#if PCH_LEVEL >= 4 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // PCH_LEVEL >= 4 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/inc/strings.hrc b/forms/inc/strings.hrc new file mode 100644 index 0000000000..37a3d83883 --- /dev/null +++ b/forms/inc/strings.hrc @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#define NC_(Context, String) TranslateId(Context, u8##String) + +#define RID_BASELISTBOX_ERROR_FILLLIST NC_("RID_BASELISTBOX_ERROR_FILLLIST", "The contents of a combo box or list field could not be determined.") +#define RID_STR_IMPORT_GRAPHIC NC_("RID_STR_IMPORT_GRAPHIC", "Insert Image" ) +#define RID_STR_CONTROL_SUBSTITUTED_NAME NC_("RID_STR_CONTROL_SUBSTITUTED_NAME", "substituted") +#define RID_STR_CONTROL_SUBSTITUTED_EPXPLAIN NC_("RID_STR_CONTROL_SUBSTITUTED_EPXPLAIN", "An error occurred while this control was being loaded. It was therefore replaced with a placeholder.") +#define RID_STR_READERROR NC_("RID_STR_READERROR", "Error reading data from database" ) +#define RID_STR_CONNECTERROR NC_("RID_STR_CONNECTERROR", "Connection failed" ) +#define RID_ERR_LOADING_FORM NC_("RID_ERR_LOADING_FORM", "The data content could not be loaded.") +#define RID_ERR_REFRESHING_FORM NC_("RID_ERR_REFRESHING_FORM", "The data content could not be updated") +#define RID_STR_ERR_INSERTRECORD NC_("RID_STR_ERR_INSERTRECORD", "Error inserting the new record") +#define RID_STR_ERR_UPDATERECORD NC_("RID_STR_ERR_UPDATERECORD", "Error updating the current record") +#define RID_STR_ERR_DELETERECORD NC_("RID_STR_ERR_DELETERECORD", "Error deleting the current record") +#define RID_STR_ERR_DELETERECORDS NC_("RID_STR_ERR_DELETERECORDS", "Error deleting the specified records") +#define RID_STR_NEED_NON_NULL_OBJECT NC_("RID_STR_NEED_NON_NULL_OBJECT", "The object cannot be NULL.") +#define RID_STR_OPEN_GRAPHICS NC_("RID_STR_OPEN_GRAPHICS", "Insert Image from...") +#define RID_STR_CLEAR_GRAPHICS NC_("RID_STR_CLEAR_GRAPHICS", "Remove Image") +#define RID_STR_INVALIDSTREAM NC_("RID_STR_INVALIDSTREAM", "The given stream is invalid.") +#define RID_STR_SYNTAXERROR NC_("RID_STR_SYNTAXERROR", "Syntax error in query expression" ) +#define RID_STR_INCOMPATIBLE_TYPES NC_("RID_STR_INCOMPATIBLE_TYPES", "The value types supported by the binding cannot be used for exchanging data with this control.") +#define RID_STR_LABEL_RECORD NC_("RID_STR_LABEL_RECORD", "Record") +#define RID_STR_INVALID_VALIDATOR NC_("RID_STR_INVALID_VALIDATOR", "The control is connected to an external value binding, which at the same time acts as validator. You need to revoke the value binding, before you can set a new validator.") +#define RID_STR_LABEL_OF NC_("RID_STR_LABEL_OF", "of") +#define RID_STR_QUERY_SAVE_MODIFIED_ROW NC_("RID_STR_QUERY_SAVE_MODIFIED_ROW", "The content of the current form has been modified.\nDo you want to save your changes?") +#define RID_STR_COULD_NOT_SET_ORDER NC_("RID_STR_COULD_NOT_SET_ORDER", "Error setting the sort criteria") +#define RID_STR_COULD_NOT_SET_FILTER NC_("RID_STR_COULD_NOT_SET_FILTER", "Error setting the filter criteria") +#define RID_STR_FEATURE_REQUIRES_PARAMETERS NC_("RID_STR_FEATURE_REQUIRES_PARAMETERS", "To execute this function, parameters are needed.") +#define RID_STR_FEATURE_NOT_EXECUTABLE NC_("RID_STR_FEATURE_NOT_EXECUTABLE", "This function cannot be executed, but is only for status queries.") +#define RID_STR_FEATURE_UNKNOWN NC_("RID_STR_FEATURE_UNKNOWN", "Unknown function.") + +#define RID_STR_XFORMS_NO_BINDING_EXPRESSION NC_("RID_STR_XFORMS_NO_BINDING_EXPRESSION", "Please enter a binding expression.") +#define RID_STR_XFORMS_INVALID_BINDING_EXPRESSION NC_("RID_STR_XFORMS_INVALID_BINDING_EXPRESSION", "This is an invalid binding expression.") +#define RID_STR_XFORMS_INVALID_VALUE NC_("RID_STR_XFORMS_INVALID_VALUE", "Value is invalid.") +#define RID_STR_XFORMS_REQUIRED NC_("RID_STR_XFORMS_REQUIRED", "A value is required.") +#define RID_STR_XFORMS_INVALID_CONSTRAINT NC_("RID_STR_XFORMS_INVALID_CONSTRAINT", "The constraint '$1' not validated.") +#define RID_STR_XFORMS_VALUE_IS_NOT_A NC_("RID_STR_XFORMS_VALUE_IS_NOT_A", "The value is not of the type '$2'.") +#define RID_STR_XFORMS_VALUE_MAX_INCL NC_("RID_STR_XFORMS_VALUE_MAX_INCL", "The value must be smaller than or equal to $2.") +#define RID_STR_XFORMS_VALUE_MAX_EXCL NC_("RID_STR_XFORMS_VALUE_MAX_EXCL", "The value must be smaller than $2.") +#define RID_STR_XFORMS_VALUE_MIN_INCL NC_("RID_STR_XFORMS_VALUE_MIN_INCL", "The value must be greater than or equal to $2.") +#define RID_STR_XFORMS_VALUE_MIN_EXCL NC_("RID_STR_XFORMS_VALUE_MIN_EXCL", "The value must be greater than $2.") +#define RID_STR_XFORMS_VALUE_TOTAL_DIGITS NC_("RID_STR_XFORMS_VALUE_TOTAL_DIGITS", "$2 digits allowed at most.") +#define RID_STR_XFORMS_VALUE_FRACTION_DIGITS NC_("RID_STR_XFORMS_VALUE_FRACTION_DIGITS", "$2 fraction digits allowed at most.") +#define RID_STR_XFORMS_VALUE_LENGTH NC_("RID_STR_XFORMS_VALUE_LENGTH", "The string must be $2 characters long.") +#define RID_STR_XFORMS_VALUE_MIN_LENGTH NC_("RID_STR_XFORMS_VALUE_MIN_LENGTH", "The string must be at least $2 characters long.") +#define RID_STR_XFORMS_VALUE_MAX_LENGTH NC_("RID_STR_XFORMS_VALUE_MAX_LENGTH", "The string can only be $2 characters long at most.") +#define RID_STR_DATATYPE_STRING NC_("RID_STR_DATATYPE_STRING", "String") +#define RID_STR_DATATYPE_URL NC_("RID_STR_DATATYPE_URL", "Hyperlink") +#define RID_STR_DATATYPE_BOOLEAN NC_("RID_STR_DATATYPE_BOOLEAN", "True/False (Boolean)") +#define RID_STR_DATATYPE_DECIMAL NC_("RID_STR_DATATYPE_DECIMAL", "Decimal") +#define RID_STR_DATATYPE_FLOAT NC_("RID_STR_DATATYPE_FLOAT", "Floating point") +#define RID_STR_DATATYPE_DOUBLE NC_("RID_STR_DATATYPE_DOUBLE", "Double precision") +#define RID_STR_DATATYPE_DATE NC_("RID_STR_DATATYPE_DATE", "Date") +#define RID_STR_DATATYPE_TIME NC_("RID_STR_DATATYPE_TIME", "Time") +#define RID_STR_DATATYPE_DATETIME NC_("RID_STR_DATATYPE_DATETIME", "Date and Time") +#define RID_STR_DATATYPE_YEAR NC_("RID_STR_DATATYPE_YEAR", "Year") +#define RID_STR_DATATYPE_MONTH NC_("RID_STR_DATATYPE_MONTH", "Month") +#define RID_STR_DATATYPE_DAY NC_("RID_STR_DATATYPE_DAY", "Day") +#define RID_STR_XFORMS_CANT_EVALUATE NC_("RID_STR_XFORMS_CANT_EVALUATE", "Error during evaluation") +#define RID_STR_XFORMS_PATTERN_DOESNT_MATCH NC_("RID_STR_XFORMS_PATTERN_DOESNT_MATCH", "The string '$1' does not match the required regular expression '$2'.") +#define RID_STR_XFORMS_BINDING_UI_NAME NC_("RID_STR_XFORMS_BINDING_UI_NAME", "Binding" ) +#define RID_STR_XFORMS_CANT_REMOVE_TYPE NC_("RID_STR_XFORMS_CANT_REMOVE_TYPE", "This is a built-in type and cannot be removed." ) +#define RID_STR_XFORMS_WARN_TARGET_IS_FILE NC_("RID_STR_XFORMS_WARN_TARGET_IS_FILE", "Are you sure you want to write to local file \"$\"?" ) + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/qa/complex/forms/CheckOGroupBoxModel.java b/forms/qa/complex/forms/CheckOGroupBoxModel.java new file mode 100644 index 0000000000..807d14c4ff --- /dev/null +++ b/forms/qa/complex/forms/CheckOGroupBoxModel.java @@ -0,0 +1,168 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package complex.forms; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openoffice.test.OfficeConnection; + +import util.FormTools; +import util.SOfficeFactory; +import util.ValueChanger; + +import com.sun.star.beans.Property; +import com.sun.star.beans.PropertyAttribute; +import com.sun.star.beans.PropertyChangeEvent; +import com.sun.star.beans.XMultiPropertySet; +import com.sun.star.beans.XPropertiesChangeListener; +import com.sun.star.drawing.XControlShape; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.XCloseable; + +/** + */ +public class CheckOGroupBoxModel +{ + + private XMultiPropertySet m_xPropSet; + private XComponent m_xDrawDoc; + + @Before public void before() throws Exception + { + SOfficeFactory SOF = SOfficeFactory.getFactory(getMSF()); + + System.out.println("creating a draw document"); + m_xDrawDoc = SOF.createDrawDoc(null); + + String objName = "GroupBox"; + XControlShape shape = FormTools.insertControlShape(m_xDrawDoc, 5000, 7000, 2000, 2000, objName); + m_xPropSet = UnoRuntime.queryInterface(XMultiPropertySet.class, shape.getControl()); + } + + @After public void after() throws Exception + { + XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, m_xDrawDoc); + if (xClose != null) + { + xClose.close(true); + } + } + + @Test public void setPropertyValues() throws Exception + { + String[] boundPropsToTest = getBoundPropsToTest(); + + MyChangeListener ml = new MyChangeListener(); + m_xPropSet.addPropertiesChangeListener(boundPropsToTest, ml); + + Object[] gValues = m_xPropSet.getPropertyValues(boundPropsToTest); + Object[] newValue = new Object[gValues.length]; + System.out.println("Trying to change all properties."); + for (int i = 0; i < boundPropsToTest.length; i++) + { + newValue[i] = ValueChanger.changePValue(gValues[i]); + } + m_xPropSet.setPropertyValues(boundPropsToTest, newValue); + + assertTrue("Listener was not called.", ml.wasListenerCalled()); + m_xPropSet.removePropertiesChangeListener(ml); + } + + private String[] getBoundPropsToTest() + { + Property[] properties = m_xPropSet.getPropertySetInfo().getProperties(); + ArrayList tNames = new ArrayList(); + + for (Property property : properties) + { + boolean isWritable = ((property.Attributes + & PropertyAttribute.READONLY) == 0); + boolean isNotNull = ((property.Attributes + & PropertyAttribute.MAYBEVOID) == 0); + boolean isBound = ((property.Attributes + & PropertyAttribute.BOUND) != 0); + + if (isWritable && isNotNull && isBound) + { + tNames.add(property.Name); + } + } // endfor + + //get an array of bound properties + String[] testPropsNames = tNames.toArray(new String[tNames.size()]); + return testPropsNames; + } + + /** + * Listener implementation which sets a flag when + * listener was called. + */ + private static class MyChangeListener implements XPropertiesChangeListener + { + + private boolean propertiesChanged = false; + + public void propertiesChange(PropertyChangeEvent[] e) + { + propertiesChanged = true; + } + + public void disposing(EventObject obj) + { + } + + private boolean wasListenerCalled() + { + return propertiesChanged; + } + + + } + + private XMultiServiceFactory getMSF() + { + return UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager()); + } + + // setup and close connections + @BeforeClass + public static void setUpConnection() throws Exception + { + System.out.println("setUpConnection()"); + connection.setUp(); + } + + @AfterClass + public static void tearDownConnection() + throws InterruptedException, com.sun.star.uno.Exception + { + System.out.println("tearDownConnection()"); + connection.tearDown(); + } + private static final OfficeConnection connection = new OfficeConnection(); +} diff --git a/forms/qa/forms_all.sce b/forms/qa/forms_all.sce new file mode 100644 index 0000000000..23fa91baac --- /dev/null +++ b/forms/qa/forms_all.sce @@ -0,0 +1,25 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +-o integration.forms.RadioButtons +-o integration.forms.FormControlTest +-o integration.forms.FormPropertyBags +-o integration.forms.CellBinding +-o integration.forms.ListSelection +-o integration.forms.MasterDetailForms +-o integration.forms.XMLFormSettings +-o integration.forms.ListBox diff --git a/forms/qa/integration/forms/BooleanValidator.java b/forms/qa/integration/forms/BooleanValidator.java new file mode 100644 index 0000000000..9d85a86161 --- /dev/null +++ b/forms/qa/integration/forms/BooleanValidator.java @@ -0,0 +1,64 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +import com.sun.star.uno.AnyConverter; + +public class BooleanValidator extends integration.forms.ControlValidator +{ + private final boolean m_preventChecked; + + /** Creates a new instance of BooleanValidator */ + public BooleanValidator( boolean preventChecked ) + { + m_preventChecked = preventChecked; + } + + public String explainInvalid( Object Value ) + { + try + { + if ( AnyConverter.isVoid( Value ) ) + return "'indetermined' is not an allowed state"; + boolean value = ((Boolean)Value).booleanValue(); + if ( m_preventChecked && ( value ) ) + return "no no no. Don't check it."; + } + catch( java.lang.Exception e ) + { + return "ooops. Unknown error"; + } + return ""; + } + + public boolean isValid( Object Value ) + { + try + { + if ( AnyConverter.isVoid( Value ) ) + return false; + + boolean value = ((Boolean)Value).booleanValue(); + return !(m_preventChecked && ( value )); + } + catch( java.lang.Exception e ) + { + } + return false; + } +} diff --git a/forms/qa/integration/forms/CellBinding.java b/forms/qa/integration/forms/CellBinding.java new file mode 100644 index 0000000000..56d811a25d --- /dev/null +++ b/forms/qa/integration/forms/CellBinding.java @@ -0,0 +1,543 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +import com.sun.star.uno.*; +import com.sun.star.util.*; +import com.sun.star.lang.*; +import com.sun.star.beans.*; +import com.sun.star.form.binding.*; +import com.sun.star.accessibility.*; +import com.sun.star.awt.XListBox; +import com.sun.star.table.CellAddress; +import com.sun.star.table.XCell; +import com.sun.star.sheet.XCellRangeData; +import com.sun.star.sheet.XCellRangeFormula; +import com.sun.star.text.XTextRange; + +public class CellBinding extends complexlib.ComplexTestCase +{ + /** the test document our form layer lives in */ + private SpreadsheetDocument m_document; + /** our form layer */ + private FormLayer m_formLayer; + + @Override + public String[] getTestMethodNames() + { + return new String[] { + "checkTextFieldBinding", + "checkBooleanRadioBinding", + "checkStringRadioBinding", + "checkBooleanCheckBoxBinding", + "checkStringCheckBoxBinding", + "checkListBoxBinding", + "checkListBoxIndexBinding" + }; + } + + @Override + public String getTestObjectName() + { + return "Form Control Spreadsheet Cell Binding Test"; + } + + /* ------------------------------------------------------------------ */ + /** closes our document, if we have an open one + */ + private void closeDocument() + { + try + { + // close our document + if ( m_document != null ) + { + XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class, + m_document.getDocument() ); + closeDoc.close( true ); + } + } + catch ( com.sun.star.uno.Exception e ) + { + } + } + + /* ------------------------------------------------------------------ */ + public void before() throws com.sun.star.uno.Exception, java.lang.Exception + { + /* our service factory */ + XMultiServiceFactory orb = param.getMSF(); + m_document = new SpreadsheetDocument( orb ); + m_formLayer = new FormLayer( m_document ); + } + + /* ------------------------------------------------------------------ */ + public void after() throws java.lang.Exception + { + closeDocument(); + } + + /* ------------------------------------------------------------------ */ + public void checkTextFieldBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + final short col = 0; + final short row = 2; + final String text = "content"; + final String otherText = "something else"; + final String yetAnotherText = "yet another text"; + + // create a normal text control + XPropertySet controlModel = m_formLayer.createControlAndShape( "DatabaseTextField", 30, 9, 30, 6 ); + + // bind it to cell A1 + bindToCell( controlModel, col, row ); + + // switch to alive mode + m_document.getCurrentView().toggleFormDesignMode(); + + // test the data transfer control -> cell + simulateUserTextInput( controlModel, text ); + verifyStringCellContent( col, row, text, "A text field does not forward its user input to the cell." ); + + // the same, but this time changing the control value programmatically + controlModel.setPropertyValue( "Text", otherText ); + verifyStringCellContent( col, row, otherText, "A text field does not forward programmatic changes to the cell." ); + + // the other way round: cell->control + setCellText( col, row, yetAnotherText ); + String controlText = (String)controlModel.getPropertyValue( "Text" ); + if ( !controlText.equals( yetAnotherText ) ) + failed( "Changes in the cell are not forwarded to the text field." ); + } + /* ------------------------------------------------------------------ */ + public void checkBooleanRadioBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + // two radio buttons + XPropertySet primaryRadio = createRadio( 28, "radio button no. 1", "radio group", "primary" ); + XPropertySet secondaryRadio = createRadio( 33, "radio button no. 2", "radio group", "secodary" ); + + // bind them + short col = (short)0; + short row1 = (short)6; + short row2 = (short)7; + bindToCell( primaryRadio, col, row1 ); + bindToCell( secondaryRadio, col, row2 ); + + // check the first button + simulateUserRadioCheck( primaryRadio ); + // check the cell content + verifyNumericCellContent( col, row1, 1, "Radio buttons do not forward their (boolean) values to cells (1)." ); + verifyNumericCellContent( col, row2, 0, "Radio buttons do not forward their (boolean) values to cells (2)." ); + // check the second button + simulateUserRadioCheck( secondaryRadio ); + // check the cell content + verifyNumericCellContent( col, row1, 0, "Radio buttons do not forward their (boolean) values to cells (3)." ); + verifyNumericCellContent( col, row2, 1, "Radio buttons do not forward their (boolean) values to cells (4)." ); + + // the other way round: writing values into the cell + setCellValue( col, row1, 1.0 ); + // setting this should have checked the primary radio, which should have unchecked the secondary radio, + // which should have been propagated to the second cell + verifyNumericCellContent( col, row2, 0, "Changing primary cell is not propagated to the secondary cell (via the radio buttons)." ); + + // setting an empty cell should result in the radio being unchecked + setCellEmpty( col, row1 ); + if ( ((Short)primaryRadio.getPropertyValue( "State" )).shortValue() != 0 ) + failed( "Setting a cell to 'empty' does not reset the bound radio button." ); + } + + /* ------------------------------------------------------------------ */ + public void checkStringRadioBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + // two radio buttons + XPropertySet primaryRadio = createRadio( 46, "radio button A", "radio ref group", "primary" ); + XPropertySet secondaryRadio = createRadio( 51, "radio button B", "radio ref group", "secodary" ); + + // give the ref values + String refValueA = "ref value A"; + String refValueB = "ref value B"; + primaryRadio.setPropertyValue( "RefValue", refValueA ); + secondaryRadio.setPropertyValue( "RefValue", refValueB ); + + // bind them to the same cell + short col = (short)0; + short row = (short)10; + bindToCell( primaryRadio, col, row ); + bindToCell( secondaryRadio, col, row ); + + // checking a radio should set the respective ref value at the cell + simulateUserRadioCheck( primaryRadio ); + verifyStringCellContent( col, row, refValueA, "A bound radio button with a reference value does not pass this value to the cell upon checking (1)." ); + simulateUserRadioCheck( secondaryRadio ); + verifyStringCellContent( col, row, refValueB, "A bound radio button with a reference value does not pass this value to the cell upon checking (2)." ); + + // changing the cell should check the buttons if the cell text equals the ref value + setCellText( col, row, "no ref value" ); + verifyRadioStates( primaryRadio, secondaryRadio, (short)0, (short)0, "Radio button not unchecked, though the bound cell value does not equal ref value." ); + + setCellText( col, row, refValueA ); + verifyRadioStates( primaryRadio, secondaryRadio, (short)1, (short)0, "Radio button not properly un/checked according to the cell and ref value (1)." ); + + setCellText( col, row, refValueB ); + verifyRadioStates( primaryRadio, secondaryRadio, (short)0, (short)1, "Radio button not properly un/checked according to the cell and ref value (2)." ); + } + + /* ------------------------------------------------------------------ */ + public void checkBooleanCheckBoxBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XPropertySet checkBox = m_formLayer.createControlAndShape( "DatabaseCheckBox", 30, 59, 40, 4 ); + checkBox.setPropertyValue( "Label", "check box" ); + checkBox.setPropertyValue( "TriState", Boolean.TRUE ); + + short col = (short)0; + short row = (short)13; + bindToCell( checkBox, col, row ); + + // initialize with "not checked" + checkBox.setPropertyValue( "State", Short.valueOf( (short)0 ) ); + verifyNumericCellContent( col, row, 0, "programmatically unchecking the check box is not propagated to the cell." ); + + // first click: "not checked" -> "checked" + simulateUserCheckBoxCheck( checkBox, (short)1 ); + verifyNumericCellContent( col, row, 1, "moving the check box state to 'checked' is not propagated to the cell." ); + + // second click: "checked" -> "indetermined" + simulateUserCheckBoxCheck( checkBox, (short)2 ); + verifyVoidCell( col, row, "propagating the 'indetermined' state to the cell does not work." ); + + // third click: "indetermined" -> "not checked" + simulateUserCheckBoxCheck( checkBox, (short)0 ); + verifyNumericCellContent( col, row, 0, "unchecking a check box via UI is not propagated to the cell." ); + } + + /* ------------------------------------------------------------------ */ + public void checkStringCheckBoxBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + String refValue = "checked "; + + XPropertySet checkBox = m_formLayer.createControlAndShape( "DatabaseCheckBox", 30, 68, 40, 4 ); + checkBox.setPropertyValue( "Label", "check box with ref value" ); + checkBox.setPropertyValue( "TriState", Boolean.TRUE ); + checkBox.setPropertyValue( "RefValue", refValue ); + + short col = (short)0; + short row = (short)15; + bindToCell( checkBox, col, row ); + + // initialize with "not checked" + checkBox.setPropertyValue( "State", Short.valueOf( (short)0 ) ); + verifyNumericCellContent( col, row, 0, "programmatically unchecking the check box is not propagated to the cell." ); + + // first click: "not checked" -> "checked" + simulateUserCheckBoxCheck( checkBox, (short)1 ); + verifyStringCellContent( col, row, refValue, "moving the check box state to 'checked' does not propagated the ref value to the cell." ); + + // second click: "checked" -> "indetermined" + simulateUserCheckBoxCheck( checkBox, (short)2 ); + verifyVoidCell( col, row, "propagating the 'indetermined' state to the cell does not work, when exchanging ref values." ); + + // third click: "indetermined" -> "not checked" + simulateUserCheckBoxCheck( checkBox, (short)0 ); + verifyStringCellContent( col, row, "", "unchecking a check box via UI does not propagated the ref value to the cell." ); + } + + /* ------------------------------------------------------------------ */ + /** verifies that a list box, which is bound via an ordinary value binding, + * works as expected + */ + public void checkListBoxBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XPropertySet listBox = m_formLayer.createControlAndShape( "DatabaseListBox", 30, 80, 40, 6 ); + listBox.setPropertyValue( "Dropdown", Boolean.TRUE ); + listBox.setPropertyValue( "StringItemList", new String[] { "Apples", "Oranges", "Peaches" } ); + + short col = (short)0; + short row = (short)18; + + + // add a list entry source which fills the list boxes list from cells in the + // spreadsheet + short sourceCol = (short)4; + setCellText( sourceCol, (short)( row - 1 ), "Apples" ); + setCellText( sourceCol, (short)( row + 0 ), "Oranges" ); + setCellText( sourceCol, (short)( row + 1 ), "Peaches" ); + + // TODO: this is currently prone to deadlocks + + + // bind to a cell + bindToCell( listBox, col, row ); + + + // do the tests + listBox.setPropertyValue( "SelectedItems", new short[] { (short)0 } ); + verifyStringCellContent( col, row, "Apples", "programmatically selecting a list entry is not propagated to the cell." ); + + simulateUserListBoxSelection( listBox, "Oranges" ); + verifyStringCellContent( col, row, "Oranges", "UI-selecting a list entry is not propagated to the cell." ); + + setCellText( col, row, "Peaches" ); + short[] selectedItems = (short[])listBox.getPropertyValue( "SelectedItems" ); + assureEquals( "changes in the cell bound to a list box are not propagated to the list box selection", + 2, selectedItems[0] ); + } + + /* ------------------------------------------------------------------ */ + /** verifies that a list box, which is bound via a value binding exchanging the index + * of the selected entry, works as expected + */ + public void checkListBoxIndexBinding() throws com.sun.star.uno.Exception, java.lang.Exception + { + XPropertySet listBox = m_formLayer.createControlAndShape( "DatabaseListBox", 30, 94, 40, 6 ); + listBox.setPropertyValue( "Dropdown", Boolean.TRUE ); + listBox.setPropertyValue( "StringItemList", new String[] { "Pears", "Bananas", "Strawberries" } ); + + short col = (short)0; + short row = (short)21; + + + // add a list entry source which fills the list boxes list from cells in the + // spreadsheet + short sourceCol = (short)4; + setCellText( sourceCol, (short)( row - 1 ), "Pears" ); + setCellText( sourceCol, (short)( row + 0 ), "Bananas" ); + setCellText( sourceCol, (short)( row + 1 ), "Strawberries" ); + + // TODO: this is currently prone to deadlocks + + + // bind to a cell + bindToCell( listBox, col, row, "com.sun.star.table.ListPositionCellBinding" ); + + + // do the tests + listBox.setPropertyValue( "SelectedItems", new short[] { (short)0 } ); + verifyNumericCellContent( col, row, 1, "programmatically selecting a list entry is not propagated (as index) to the cell." ); + + simulateUserListBoxSelection( listBox, "Bananas" ); + verifyNumericCellContent( col, row, 2, "UI-selecting a list entry is not propagated (as index) to the cell." ); + + setCellValue( col, row, 3 ); + short[] selectedItems = (short[])listBox.getPropertyValue( "SelectedItems" ); + assureEquals( "changes in the cell bound to a list box via list index are not propagated to the list box selection", + 2, selectedItems[0] ); + } + + /* ------------------------------------------------------------------ */ + /** verifies that the content of a given cell equals a given string + */ + private XPropertySet createRadio( int yPos, String label, String name, String tag ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XPropertySet radio = m_formLayer.createControlAndShape( "DatabaseRadioButton", 30, yPos, 40, 4 ); + radio.setPropertyValue( "Label", label ); + radio.setPropertyValue( "Name", name ); + radio.setPropertyValue( "Tag", tag ); + return radio; + } + + /* ------------------------------------------------------------------ */ + /** verifies the states of two radio button + */ + private boolean verifyRadioStates( XPropertySet radio1, XPropertySet radio2, short value1, short value2, + String errorMessage ) throws com.sun.star.uno.Exception, java.lang.Exception + { + if ( ( ((Short)radio1.getPropertyValue( "State" )).shortValue() != value1 ) + || ( ((Short)radio2.getPropertyValue( "State" )).shortValue() != value2 ) + ) + { + failed( errorMessage ); + return false; + } + return true; + } + + /* ------------------------------------------------------------------ */ + /** verifies that the content of a given cell equals a given string + */ + private boolean verifyVoidCell( short col, short row, String failErrorMessage ) throws com.sun.star.uno.Exception + { + XCellRangeData cell = UnoRuntime.queryInterface( XCellRangeData.class, + m_document.getSheet( 0 ).getCellByPosition( col, row ) + ); + Object cellContent = cell.getDataArray()[0][0]; + if ( ((com.sun.star.uno.Any)cellContent).getType().getTypeClass() != com.sun.star.uno.TypeClass.VOID ) + { + failed( failErrorMessage ); + return false; + } + return true; + } + + /* ------------------------------------------------------------------ */ + /** verifies that the content of a given cell equals a given string + */ + private boolean verifyNumericCellContent( short col, short row, double value, String failErrorMessage ) throws com.sun.star.uno.Exception + { + XCell cell = UnoRuntime.queryInterface( XCell.class, + m_document.getSheet( 0 ).getCellByPosition( col, row ) + ); + if ( cell.getValue() != value ) + { + failed( failErrorMessage ); + return false; + } + return true; + } + + /* ------------------------------------------------------------------ */ + /** verifies that the content of a given cell equals a given string + */ + private boolean verifyStringCellContent( short col, short row, String text, String failErrorMessage ) throws com.sun.star.uno.Exception + { + XTextRange cell = UnoRuntime.queryInterface( XTextRange.class, + m_document.getSheet( 0 ).getCellByPosition( col, row ) + ); + if ( !cell.getString().equals( text ) ) + { + failed( failErrorMessage ); + return false; + } + return true; + } + + /* ------------------------------------------------------------------ */ + /** sets the text of a given cell to a given string + */ + private void setCellText( short col, short row, String text ) throws com.sun.star.uno.Exception + { + XTextRange cell = UnoRuntime.queryInterface( XTextRange.class, + m_document.getSheet( 0 ).getCellByPosition( col, row ) + ); + cell.setString( text ); + } + + /* ------------------------------------------------------------------ */ + /** sets a numeric value in a given cell + */ + private void setCellValue( short col, short row, double value ) throws com.sun.star.uno.Exception + { + XCell cell = UnoRuntime.queryInterface( XCell.class, + m_document.getSheet( 0 ).getCellByPosition( col, row ) + ); + cell.setValue( value ); + } + + /* ------------------------------------------------------------------ */ + /** sets a numeric value in a given cell + */ + private void setCellEmpty( short col, short row ) throws com.sun.star.uno.Exception + { + // as long as #i29130# is not fixed, we do not set the cell to "empty", but to + // an invalid form, which serves well for our purpose + XCellRangeFormula cell = UnoRuntime.queryInterface( XCellRangeFormula.class, + m_document.getSheet( 0 ).getCellByPosition( col, row ) + ); + String[][] args = new String[][] { new String[] { "=INVALID_FUNCTION()" } }; + cell.setFormulaArray( args ); + } + + /* ------------------------------------------------------------------ */ + /** binds the given control model to the given cell in the first sheet, + * using the given service name for the binding + */ + private void bindToCell( XPropertySet controlModel, short column, short row, String _bindingServiceName ) throws com.sun.star.uno.Exception + { + XBindableValue bindableModel = UnoRuntime.queryInterface( XBindableValue.class, + controlModel + ); + + CellAddress address = new CellAddress(); + address.Column = column; + address.Row = row; + address.Sheet = 0; + + NamedValue[] parameters = new NamedValue[] { new NamedValue() }; + parameters[0].Name = "BoundCell"; + parameters[0].Value = address; + + XValueBinding cellBinding = UnoRuntime.queryInterface( XValueBinding.class, + m_document.createInstanceWithArguments( _bindingServiceName, parameters ) + ); + + bindableModel.setValueBinding( cellBinding ); + } + + /* ------------------------------------------------------------------ */ + /** binds the given control model to the given cell in the first sheet + */ + private void bindToCell( XPropertySet _controlModel, short _column, short _row ) throws com.sun.star.uno.Exception + { + bindToCell( _controlModel, _column, _row, "com.sun.star.table.CellValueBinding" ); + } + + /* ------------------------------------------------------------------ */ + /** simulates a user action to check a radio button + */ + private void simulateUserRadioCheck( XPropertySet radioModel ) throws com.sun.star.uno.Exception + { + XAccessible accessible = UnoRuntime.queryInterface( + XAccessible.class, m_document.getCurrentView().getControl( radioModel ) ); + + XAccessibleValue xValue = UnoRuntime.queryInterface( + XAccessibleValue.class, accessible.getAccessibleContext() ); + + Integer newValue = Integer.valueOf( 1 ); + xValue.setCurrentValue( newValue ); + } + + /* ------------------------------------------------------------------ */ + /** simulates a user action to check a radio button + */ + private void simulateUserCheckBoxCheck( XPropertySet checkBox, short state ) throws com.sun.star.uno.Exception + { + XAccessible accessible = UnoRuntime.queryInterface( + XAccessible.class, m_document.getCurrentView().getControl( checkBox ) ); + + XAccessibleValue xValue = UnoRuntime.queryInterface( + XAccessibleValue.class, accessible.getAccessibleContext() ); + + xValue.setCurrentValue( Short.valueOf( state ) ); + } + + /* ------------------------------------------------------------------ */ + /** simulates a user selecting an entry in a list box + */ + private void simulateUserListBoxSelection( XPropertySet _listBox, String _selectEntry ) throws com.sun.star.uno.Exception + { + XListBox listBoxControl = UnoRuntime.queryInterface( + XListBox.class, m_document.getCurrentView().getControl( _listBox ) ); + listBoxControl.selectItem( _selectEntry, true ); + } + + /* ------------------------------------------------------------------ */ + /** simulates text input into the control belonging to the given model + */ + private void simulateUserTextInput( XPropertySet controlModel, String text ) throws com.sun.star.uno.Exception + { + XAccessible accessible = UnoRuntime.queryInterface( + XAccessible.class, m_document.getCurrentView().getControl( controlModel ) ); + + UnoRuntime.queryInterface( XServiceInfo.class, + accessible.getAccessibleContext() ); + + XAccessibleEditableText textAccess = UnoRuntime.queryInterface( + XAccessibleEditableText.class, accessible.getAccessibleContext() ); + + textAccess.setText( text ); + } +} diff --git a/forms/qa/integration/forms/ControlValidation.java b/forms/qa/integration/forms/ControlValidation.java new file mode 100644 index 0000000000..dce4a23f2d --- /dev/null +++ b/forms/qa/integration/forms/ControlValidation.java @@ -0,0 +1,176 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package integration.forms; + +import com.sun.star.uno.*; +import com.sun.star.util.*; +import com.sun.star.lang.*; +import com.sun.star.beans.*; + +public class ControlValidation extends complexlib.ComplexTestCase implements com.sun.star.lang.XEventListener +{ + private DocumentHelper m_document; /// our current test document + private XMultiServiceFactory m_orb; /// our service factory + + @Override + public String[] getTestMethodNames() + { + return new String[] { + "interactiveValidation" + }; + } + + @Override + public String getTestObjectName() + { + return "Form Control Validation Test"; + } + + public static boolean isInteractiveTest() + { + return true; + } + + /* ------------------------------------------------------------------ */ + /* test framework */ + /* ------------------------------------------------------------------ */ + public void before() throws java.lang.Exception + { + m_orb = param.getMSF(); + } + + /* ------------------------------------------------------------------ */ + private void prepareTestStep( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + m_document = DocumentHelper.blankTextDocument( m_orb ); + m_document.getDocument( ).addEventListener( this ); + } + + /* ------------------------------------------------------------------ */ + public void after() + { + closeDocument(); + } + + /* ------------------------------------------------------------------ */ + /** closes our document, if we have an open one + */ + private void closeDocument() + { + try + { + // close our document + if ( m_document != null ) + { + XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class, + m_document.getDocument() ); + closeDoc.close( true ); + } + } + catch ( com.sun.star.uno.Exception e ) + { + e.printStackTrace( System.err ); + } + } + + /* ------------------------------------------------------------------ */ + /* public test methods */ + /* ------------------------------------------------------------------ */ + public void interactiveValidation() throws com.sun.star.uno.Exception, java.lang.Exception + { + prepareTestStep(); + + SingleControlValidation validation; + XPropertySet focusField; + + validation = new SingleControlValidation( m_document, 5, 5, "DatabaseFormattedField", new NumericValidator() ); + focusField = validation.getInputField(); + validation.setExplanatoryText( "Please enter a number between 0 and 100, with at most 1 decimal digit" ); + + validation = new SingleControlValidation( m_document, 90, 5, "DatabaseTextField", new TextValidator() ); + validation.setExplanatoryText( "Please enter a text whose length is a multiple of 3, and which does not contain the letter 'Z'" ); + + validation = new SingleControlValidation( m_document, 5, 55, "DatabaseDateField", new DateValidator() ); + validation.setExplanatoryText( "Please enter a date in the current month" ); + validation.getInputField().setPropertyValue( "Dropdown", Boolean.TRUE ); + + validation = new SingleControlValidation( m_document, 90, 55, "DatabaseTimeField", new TimeValidator() ); + validation.setExplanatoryText( "Please enter a time. Valid values are all full hours." ); + + validation = new SingleControlValidation( m_document, 5, 110, "DatabaseCheckBox", new BooleanValidator( false ) ); + validation.setExplanatoryText( "Please check (well, or uncheck) the box. Don't leave it in indetermined state." ); + validation.getInputField().setPropertyValue( "TriState", Boolean.TRUE ); + + validation = new SingleControlValidation( m_document, 90, 110, "DatabaseRadioButton", new BooleanValidator( true ), 3, 0 ); + validation.setExplanatoryText( "Please check any but the first button" ); + + validation = new SingleControlValidation( m_document, 5, 165, "DatabaseListBox", new ListSelectionValidator( ), 1, 24 ); + validation.setExplanatoryText( "Please select not more than two entries." ); + validation.getInputField().setPropertyValue( "MultiSelection", Boolean.TRUE ); + validation.getInputField().setPropertyValue( "StringItemList", new String[] { "first", "second", "third", "forth", "fivth" } ); + + // switch to alive mode + m_document.getCurrentView( ).toggleFormDesignMode( ); + m_document.getCurrentView( ).grabControlFocus( focusField ); + + // wait for the user telling us to exit + waitForUserInput(); + } + + /* ------------------------------------------------------------------ */ + /* internal methods */ + /* ------------------------------------------------------------------ */ + /** waits for the user to press a key (on the console where she started the java program) + or the document to be closed by the user. + @return + if the user pressed a key on the console, if she closed the document + */ + protected boolean waitForUserInput() throws java.lang.Exception + { + synchronized (this) + { + integration.forms.WaitForInput aWait = new integration.forms.WaitForInput( this ); + aWait.start(); + wait(); + + // if the waiter thread is done, the user pressed enter + boolean bKeyPressed = aWait.isDone(); + if ( !bKeyPressed ) + aWait.interrupt(); + + return bKeyPressed; + } + } + + /* ------------------------------------------------------------------ */ + /* XEventListener overridables */ + /* ------------------------------------------------------------------ */ + public void disposing( com.sun.star.lang.EventObject eventObject ) + { + if ( m_document.getDocument().equals( eventObject.Source ) ) + { + // notify ourself that we can stop waiting for user input + synchronized (this) + { + notify(); + } + } + } + +} diff --git a/forms/qa/integration/forms/ControlValidator.java b/forms/qa/integration/forms/ControlValidator.java new file mode 100644 index 0000000000..fca013a191 --- /dev/null +++ b/forms/qa/integration/forms/ControlValidator.java @@ -0,0 +1,45 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +/** base class for components validating the content of form controls + */ +public abstract class ControlValidator implements com.sun.star.form.validation.XValidator +{ + + public void addValidityConstraintListener(com.sun.star.form.validation.XValidityConstraintListener xValidityConstraintListener) + { + } + + public void removeValidityConstraintListener(com.sun.star.form.validation.XValidityConstraintListener xValidityConstraintListener) + { + } + + protected boolean isVoid( Object Value ) + { + try + { + return ( com.sun.star.uno.AnyConverter.getType(Value).getTypeClass() + == com.sun.star.uno.TypeClass.VOID ); + } + catch( java.lang.ClassCastException e ) + { + } + return false; + } +} diff --git a/forms/qa/integration/forms/DateValidator.java b/forms/qa/integration/forms/DateValidator.java new file mode 100644 index 0000000000..255ec6f6f6 --- /dev/null +++ b/forms/qa/integration/forms/DateValidator.java @@ -0,0 +1,80 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +public class DateValidator extends integration.forms.ControlValidator +{ + + public String explainInvalid( Object Value ) + { + try + { + if ( isVoid( Value ) ) + return "empty input"; + + com.sun.star.util.Date dateValue = (com.sun.star.util.Date)Value; + if ( isDedicatedInvalidDate( dateValue ) ) + return "this is no valid date"; + + if ( !isNextMonthsDate( dateValue ) ) + return "date must denote a day in the current month"; + } + catch( java.lang.Exception e ) + { + return "oops. What did you enter for this to happen?"; + } + return ""; + } + + public boolean isValid( Object Value ) + { + try + { + if ( isVoid( Value ) ) + return false; + + com.sun.star.util.Date dateValue = (com.sun.star.util.Date) + com.sun.star.uno.AnyConverter.toObject( + com.sun.star.util.Date.class, Value); + if ( isDedicatedInvalidDate( dateValue ) ) + return false; + + return isNextMonthsDate( dateValue ); + } + catch( java.lang.Exception e ) + { + e.printStackTrace( System.err ); + } + return false; + } + + private boolean isDedicatedInvalidDate( com.sun.star.util.Date dateValue ) + { + return ( dateValue.Day == 0 ) && ( dateValue.Month == 0 ) && ( dateValue.Year == 0 ); + } + + private boolean isNextMonthsDate( com.sun.star.util.Date dateValue ) + { + java.util.Calendar today = java.util.Calendar.getInstance(); + java.util.Calendar date = (java.util.Calendar) today.clone(); + today.set(java.util.Calendar.DATE, 1); + date.set(dateValue.Year, dateValue.Month -1, 1); // Month value is 0-based. e.g., 0 for January. + date.add(java.util.Calendar.MONTH, -1); + return date.compareTo(today) == 0; + } +} diff --git a/forms/qa/integration/forms/DocumentHelper.java b/forms/qa/integration/forms/DocumentHelper.java new file mode 100644 index 0000000000..64caf0a152 --- /dev/null +++ b/forms/qa/integration/forms/DocumentHelper.java @@ -0,0 +1,375 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +import com.sun.star.beans.PropertyState; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XIndexContainer; +import com.sun.star.container.XNameContainer; +import com.sun.star.document.MacroExecMode; +import com.sun.star.drawing.XDrawPage; +import com.sun.star.drawing.XDrawPageSupplier; +import com.sun.star.drawing.XDrawPages; +import com.sun.star.drawing.XDrawPagesSupplier; +import com.sun.star.form.XFormsSupplier; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XController; +import com.sun.star.frame.XFrame; +import com.sun.star.frame.XModel; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.util.XModifiable; + +/**************************************************************************/ + +/**************************************************************************/ +/** provides a small wrapper around a document +*/ +public class DocumentHelper +{ + private final XMultiServiceFactory m_orb; + private XComponent m_documentComponent; + + /* ================================================================== */ + /* ------------------------------------------------------------------ */ + public DocumentHelper( XMultiServiceFactory orb, XComponent document ) + { + m_orb = orb; + m_documentComponent = document; + } + + /* ------------------------------------------------------------------ */ + protected static XComponent implLoadAsComponent( XMultiServiceFactory orb, String documentOrFactoryURL ) throws com.sun.star.uno.Exception + { + return implLoadAsComponent( orb, documentOrFactoryURL, new PropertyValue[0] ); + } + + /* ------------------------------------------------------------------ */ + private static XComponent implLoadAsComponent( XMultiServiceFactory orb, String documentOrFactoryURL, final PropertyValue[] i_args ) throws com.sun.star.uno.Exception + { + XComponentLoader aLoader = UnoRuntime.queryInterface( + XComponentLoader.class, + orb.createInstance( "com.sun.star.frame.Desktop" ) + ); + + XComponent document = dbfTools.queryComponent( + aLoader.loadComponentFromURL( documentOrFactoryURL, "_blank", 0, i_args ) + ); + return document; + } + + /* ------------------------------------------------------------------ */ + private static DocumentHelper implLoadDocument( XMultiServiceFactory orb, String documentOrFactoryURL ) throws com.sun.star.uno.Exception + { + return implLoadDocument( orb, documentOrFactoryURL, new PropertyValue[0] ); + } + + /* ------------------------------------------------------------------ */ + private static DocumentHelper implLoadDocument( XMultiServiceFactory orb, String documentOrFactoryURL, final PropertyValue[] i_args ) throws com.sun.star.uno.Exception + { + XComponent document = implLoadAsComponent( orb, documentOrFactoryURL, i_args ); + + XServiceInfo xSI = UnoRuntime.queryInterface( XServiceInfo.class, + document ); + if ( xSI.supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) ) + return new SpreadsheetDocument( orb, document ); + return new DocumentHelper( orb, document ); + } + + /* ------------------------------------------------------------------ */ + public static DocumentHelper loadDocument( XMultiServiceFactory orb, String documentURL ) throws com.sun.star.uno.Exception + { + return implLoadDocument( orb, documentURL ); + } + + /* ------------------------------------------------------------------ */ + public static DocumentHelper blankTextDocument( XMultiServiceFactory orb ) throws com.sun.star.uno.Exception + { + return blankDocument( orb, DocumentType.WRITER ); + } + + + + /* ------------------------------------------------------------------ */ + public static DocumentHelper blankDocument( XMultiServiceFactory orb, DocumentType eType ) throws com.sun.star.uno.Exception + { + final PropertyValue[] args = new PropertyValue[] { + new PropertyValue( "MacroExecutionMode", -1, MacroExecMode.ALWAYS_EXECUTE, PropertyState.DIRECT_VALUE ) + }; + return implLoadDocument( orb, getDocumentFactoryURL( eType ), args ); + } + + /* ================================================================== */ + /* ------------------------------------------------------------------ */ + public XComponent getDocument( ) + { + return m_documentComponent; + } + + /* ------------------------------------------------------------------ */ + public boolean isModified() + { + XModifiable modify = query( XModifiable.class ); + return modify.isModified(); + } + + /* ------------------------------------------------------------------ */ + public T query( Class aInterfaceClass ) + { + return UnoRuntime.queryInterface( aInterfaceClass, m_documentComponent ); + } + + /* ------------------------------------------------------------------ */ + public XMultiServiceFactory getOrb( ) + { + return m_orb; + } + + /* ------------------------------------------------------------------ */ + /** retrieves the current view of the document + @return + the view component, queried for the interface described by aInterfaceClass + */ + public DocumentViewHelper getCurrentView( ) + { + // get the model interface for the document + XModel xDocModel = UnoRuntime.queryInterface(XModel.class, m_documentComponent ); + // get the current controller for the document - as a controller is tied to a view, + // this gives us the currently active view for the document. + XController xController = xDocModel.getCurrentController(); + + if ( classify() == DocumentType.CALC ) + return new SpreadsheetView( m_orb, this, xController ); + + return new DocumentViewHelper( m_orb, this, xController ); + } + + /* ------------------------------------------------------------------ */ + /** reloads the document + * + * The reload is done by dispatching the respective URL at a frame of the document. + * As a consequence, if you have references to a view of the document, or any interface + * of the document, they will become invalid. + * The Model instance itself, at which you called reload, will still be valid, it will + * automatically update its internal state after the reload. + * + * Another consequence is that if the document does not have a view at all, it cannot + * be reloaded. + */ + public void reload() throws Exception + { + DocumentViewHelper view = getCurrentView(); + XFrame frame = view.getController().getFrame(); + XModel oldModel = frame.getController().getModel(); + + getCurrentView().dispatch( ".uno:Reload" ); + + m_documentComponent = UnoRuntime.queryInterface( XComponent.class, + frame.getController().getModel() ); + + XModel newModel = getCurrentView().getController().getModel(); + if ( UnoRuntime.areSame( oldModel, newModel ) ) + throw new java.lang.IllegalStateException( "reload failed" ); + } + + /* ------------------------------------------------------------------ */ + /** creates a new form which is a child of the given form components container + + @param xParentContainer + The parent container for the new form + @param sInitialName + The initial name of the form. May be null, in this case the default (which + is an implementation detail) applies. + */ + private XIndexContainer createSubForm( XIndexContainer xParentContainer, String sInitialName ) + throws com.sun.star.uno.Exception + { + // create a new form + Object xNewForm = m_orb.createInstance( "com.sun.star.form.component.DataForm" ); + + // insert + xParentContainer.insertByIndex( xParentContainer.getCount(), xNewForm ); + + // set the name if necessary + if ( null != sInitialName ) + { + XPropertySet xFormProps = dbfTools.queryPropertySet( xNewForm ); + xFormProps.setPropertyValue( "Name", sInitialName ); + } + + // outta here + return UnoRuntime.queryInterface( XIndexContainer.class, xNewForm ); + } + + /* ------------------------------------------------------------------ */ + /** creates a new form which is a child of the given form components container + + @param aParentContainer + The parent container for the new form + @param sInitialName + The initial name of the form. May be null, in this case the default (which + is an implementation detail) applies. + */ + public XIndexContainer createSubForm( Object aParentContainer, String sInitialName ) + throws com.sun.star.uno.Exception + { + XIndexContainer xParentContainer = UnoRuntime.queryInterface( + XIndexContainer.class, aParentContainer ); + return createSubForm( xParentContainer, sInitialName ); + } + + /* ------------------------------------------------------------------ */ + /** creates a form which is a sibling of the given form + @param aForm + A sinbling of the to be created form. + + @param sInitialName + The initial name of the form. May be null, in this case the default (which + is an implementation detail) applies. + */ + public XIndexContainer createSiblingForm( Object aForm, String sInitialName ) throws com.sun.star.uno.Exception + { + // get the parent + XIndexContainer xContainer = (XIndexContainer)dbfTools.getParent( + aForm, XIndexContainer.class ); + // append a new form to this parent container + return createSubForm( xContainer, sInitialName ); + } + + + + /* ------------------------------------------------------------------ */ + /** returns a URL which can be used to create a document of a certain type + */ + public static String getDocumentFactoryURL( DocumentType eType ) + { + if ( eType == DocumentType.WRITER ) + return "private:factory/swriter"; + if ( eType == DocumentType.CALC ) + return "private:factory/scalc"; + if ( eType == DocumentType.DRAWING ) + return "private:factory/sdraw"; + if ( eType == DocumentType.XMLFORM ) + return "private:factory/swriter?slot=21053"; + return "private:factory/swriter"; + } + + /* ------------------------------------------------------------------ */ + /** classifies a document + */ + private DocumentType classify( ) + { + XServiceInfo xSI = UnoRuntime.queryInterface( + XServiceInfo.class, m_documentComponent ); + + if ( xSI.supportsService( "com.sun.star.text.TextDocument" ) ) + return DocumentType.WRITER; + else if ( xSI.supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) ) + return DocumentType.CALC; + else if ( xSI.supportsService( "com.sun.star.drawing.DrawingDocument" ) ) + return DocumentType.DRAWING; + + return DocumentType.UNKNOWN; + } + + /* ------------------------------------------------------------------ */ + /** retrieves a com.sun.star.drawing.DrawPage of the document, denoted by index + * @param index + * the index of the draw page + * @throws + * com.sun.star.lang.IndexOutOfBoundsException + * com.sun.star.lang.WrappedTargetException + */ + protected XDrawPage getDrawPage( int index ) throws com.sun.star.lang.IndexOutOfBoundsException, com.sun.star.lang.WrappedTargetException + { + XDrawPagesSupplier xSuppPages = UnoRuntime.queryInterface( + XDrawPagesSupplier.class, getDocument() ); + XDrawPages xPages = xSuppPages.getDrawPages(); + + return UnoRuntime.queryInterface( XDrawPage.class, xPages.getByIndex( index ) ); + } + + /* ------------------------------------------------------------------ */ + /** retrieves the DrawPage of the document + */ + protected XDrawPage getMainDrawPage( ) throws com.sun.star.uno.Exception + { + XDrawPage xReturn; + + // in case of a Writer document, this is rather easy: simply ask the XDrawPageSupplier + XDrawPageSupplier xSuppPage = UnoRuntime.queryInterface( + XDrawPageSupplier.class, getDocument() ); + if ( null != xSuppPage ) + xReturn = xSuppPage.getDrawPage(); + else + { // the model itself is no draw page supplier - okay, it may be a Writer or Calc document + // (or any other multi-page document) + XDrawPagesSupplier xSuppPages = UnoRuntime.queryInterface( + XDrawPagesSupplier.class, getDocument() ); + XDrawPages xPages = xSuppPages.getDrawPages(); + + xReturn = UnoRuntime.queryInterface( XDrawPage.class, xPages.getByIndex( 0 ) ); + + // Note that this is no really error-proof code: If the document model does not support the + // XDrawPagesSupplier interface, or if the pages collection returned is empty, this will break. + } + + return xReturn; + } + + /* ------------------------------------------------------------------ */ + /** retrieves the root of the hierarchy of form components + */ + protected XNameContainer getFormComponentTreeRoot( ) throws com.sun.star.uno.Exception + { + XFormsSupplier xSuppForms = UnoRuntime.queryInterface( + XFormsSupplier.class, getMainDrawPage( ) ); + + XNameContainer xFormsCollection = null; + if ( null != xSuppForms ) + { + xFormsCollection = xSuppForms.getForms(); + } + return xFormsCollection; + } + + /* ------------------------------------------------------------------ */ + /** creates a component at the service factory provided by the document + */ + public XInterface createInstance( String serviceSpecifier ) throws com.sun.star.uno.Exception + { + XMultiServiceFactory xORB = UnoRuntime.queryInterface( XMultiServiceFactory.class, + m_documentComponent ); + return (XInterface)xORB.createInstance( serviceSpecifier ); + } + + /* ------------------------------------------------------------------ */ + /** creates a component at the service factory provided by the document + */ + public XInterface createInstanceWithArguments( String serviceSpecifier, Object[] arguments ) throws com.sun.star.uno.Exception + { + XMultiServiceFactory xORB = UnoRuntime.queryInterface( XMultiServiceFactory.class, + m_documentComponent ); + return (XInterface) xORB.createInstanceWithArguments( serviceSpecifier, arguments ); + } +} + diff --git a/forms/qa/integration/forms/DocumentType.java b/forms/qa/integration/forms/DocumentType.java new file mode 100644 index 0000000000..105c31fc26 --- /dev/null +++ b/forms/qa/integration/forms/DocumentType.java @@ -0,0 +1,39 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +/** a helper "enumeration class" for classifying a document type +*/ +public class DocumentType extends com.sun.star.uno.Enum +{ + private DocumentType( int value ) + { + super( value ); + } + + + + public static final DocumentType WRITER = new DocumentType(0); + public static final DocumentType CALC = new DocumentType(1); + public static final DocumentType DRAWING = new DocumentType(2); + public static final DocumentType XMLFORM = new DocumentType(3); + public static final DocumentType UNKNOWN = new DocumentType(-1); + + +} + diff --git a/forms/qa/integration/forms/DocumentViewHelper.java b/forms/qa/integration/forms/DocumentViewHelper.java new file mode 100644 index 0000000000..d936ea267f --- /dev/null +++ b/forms/qa/integration/forms/DocumentViewHelper.java @@ -0,0 +1,221 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +/**************************************************************************/ + +import com.sun.star.awt.XControl; +import com.sun.star.awt.XControlModel; +import com.sun.star.awt.XWindow; +import com.sun.star.awt.XToolkitExperimental; +import com.sun.star.beans.PropertyValue; +import com.sun.star.form.XForm; +import com.sun.star.form.runtime.XFormController; +import com.sun.star.frame.XController; +import com.sun.star.frame.XDispatch; +import com.sun.star.frame.XDispatchProvider; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.URL; +import com.sun.star.util.XURLTransformer; +import com.sun.star.view.XControlAccess; +import com.sun.star.view.XFormLayerAccess; +import org.openoffice.xforms.XMLDocument; + +/**************************************************************************/ +/** provides a small wrapper around a document view +*/ +public class DocumentViewHelper +{ + private final XMultiServiceFactory m_orb; + private final XController m_controller; + private final DocumentHelper m_document; + + /* ------------------------------------------------------------------ */ + final protected XController getController() + { + return m_controller; + } + + /* ------------------------------------------------------------------ */ + final protected DocumentHelper getDocument() + { + return m_document; + } + + /* ------------------------------------------------------------------ */ + public DocumentViewHelper( XMultiServiceFactory orb, DocumentHelper document, XController controller ) + { + m_orb = orb; + m_document = document; + m_controller = controller; + } + + /* ------------------------------------------------------------------ */ + /** Quick access to a given interface of the view + @param aInterfaceClass + the class of the interface which shall be returned + */ + public T query( Class aInterfaceClass ) + { + return UnoRuntime.queryInterface( aInterfaceClass, m_controller ); + } + + /* ------------------------------------------------------------------ */ + /** retrieves a dispatcher for the given URL, obtained at the current view of the document + @param aURL + a one-element array. The first element must contain a valid + URL::Complete value. Upon return, the URL is correctly + parsed. + @return + the dispatcher for the URL in question + */ + private XDispatch getDispatcher( URL[] aURL ) throws java.lang.Exception + { + XDispatch xReturn = null; + + // go get the current view + XController xController = query( XController.class ); + // go get the dispatch provider of its frame + XDispatchProvider xProvider = UnoRuntime.queryInterface( + XDispatchProvider.class, xController.getFrame() ); + if ( null != xProvider ) + { + // need a URLTransformer + XURLTransformer xTransformer = UnoRuntime.queryInterface( + XURLTransformer.class, m_orb.createInstance( "com.sun.star.util.URLTransformer" ) ); + xTransformer.parseStrict( aURL ); + + xReturn = xProvider.queryDispatch( aURL[0], "", 0 ); + } + return xReturn; + } + + /* ------------------------------------------------------------------ */ + /** retrieves a dispatcher for the given URL, obtained at the current view of the document + */ + public XDispatch getDispatcher( String url ) throws java.lang.Exception + { + URL[] aURL = new URL[] { new URL() }; + aURL[0].Complete = url; + return getDispatcher( aURL ); + } + + /* ------------------------------------------------------------------ */ + /** dispatches the given URL into the view, if there's a dispatcher for it + + @return + if the URL was successfully dispatched + */ + public boolean dispatch( String url ) throws java.lang.Exception + { + URL[] completeURL = new URL[] { new URL() }; + completeURL[0].Complete = url; + XDispatch dispatcher = getDispatcher( completeURL ); + if ( dispatcher == null ) + return false; + + PropertyValue[] aDummyArgs = new PropertyValue[] { }; + dispatcher.dispatch( completeURL[0], aDummyArgs ); + return true; + } + + /* ------------------------------------------------------------------ */ + /** retrieves a control within the current view of a document + @param xModel + specifies the control model whose control should be located + @return + the control tied to the model + */ + public XControl getControl( XControlModel xModel ) throws com.sun.star.uno.Exception + { + // the current view of the document + XControlAccess xCtrlAcc = query( XControlAccess.class ); + // delegate the task of looking for the control + return xCtrlAcc.getControl( xModel ); + } + + /* ------------------------------------------------------------------ */ + public XControl getControl( Object aModel ) throws com.sun.star.uno.Exception + { + XControlModel xModel = UnoRuntime.queryInterface( XControlModel.class, aModel ); + return getControl( xModel ); + } + + /* ------------------------------------------------------------------ */ + public T getControl( Object aModel, Class aInterfaceClass ) throws com.sun.star.uno.Exception + { + XControlModel xModel = UnoRuntime.queryInterface( XControlModel.class, aModel ); + return UnoRuntime.queryInterface( aInterfaceClass, getControl( xModel ) ); + } + + /* ------------------------------------------------------------------ */ + /** retrieves the form controller for a given logical form + */ + private XFormController getFormController( XForm _form ) + { + XFormLayerAccess formLayerAccess = query( XFormLayerAccess.class ); + return formLayerAccess.getFormController( _form ); + } + + /* ------------------------------------------------------------------ */ + /** retrieves the form controller for a given logical form + */ + public XFormController getFormController( Object _form ) + { + return getFormController( UnoRuntime.queryInterface( XForm.class, _form )); + } + + /* ------------------------------------------------------------------ */ + /** toggles the design mode of the form layer of active view of our sample document + */ + protected void toggleFormDesignMode( ) throws java.lang.Exception + { + if ( m_document instanceof XMLDocument ) + dispatch( ".uno:SwitchXFormsDesignMode" ); + else + dispatch( ".uno:SwitchControlDesignMode" ); + // at least SwitchControlDesignMode is async, so wait for it to be done + XToolkitExperimental xToolkit = UnoRuntime.queryInterface( + XToolkitExperimental.class, + m_orb.createInstance("com.sun.star.awt.Toolkit")); + xToolkit.processEventsToIdle(); + } + + /* ------------------------------------------------------------------ */ + /** sets the focus to a specific control + @param xModel + a control model. The focus is set to that control which is part of our view + and associated with the given model. + */ + public void grabControlFocus( Object xModel ) throws com.sun.star.uno.Exception + { + // look for the control from the current view which belongs to the model + XControl xControl = getControl( xModel ); + + // the focus can be set to an XWindow only + XWindow xControlWindow = UnoRuntime.queryInterface( XWindow.class, + xControl ); + + // grab the focus + xControlWindow.setFocus(); + } + + +} + diff --git a/forms/qa/integration/forms/FormComponent.java b/forms/qa/integration/forms/FormComponent.java new file mode 100644 index 0000000000..1861c084e0 --- /dev/null +++ b/forms/qa/integration/forms/FormComponent.java @@ -0,0 +1,114 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.form.XFormsSupplier; +import com.sun.star.container.XNameAccess; +import com.sun.star.container.XIndexAccess; +import com.sun.star.container.XChild; +import com.sun.star.container.XNamed; +import com.sun.star.drawing.XDrawPage; + +public class FormComponent +{ + private final Object m_component; + private final XNameAccess m_nameAccess; + private final XIndexAccess m_indexAccess; + + /* ------------------------------------------------------------------ */ + private FormComponent() + { + m_component = null; + m_nameAccess = null; + m_indexAccess = null; + } + + /* ------------------------------------------------------------------ */ + public FormComponent( XDrawPage drawPage ) + { + XFormsSupplier supp = UnoRuntime.queryInterface( + XFormsSupplier.class, drawPage ); + m_component = supp.getForms(); + + m_nameAccess = (XNameAccess)m_component; + m_indexAccess = UnoRuntime.queryInterface( + XIndexAccess.class, m_component ); + UnoRuntime.queryInterface( + XChild.class, m_component ); + UnoRuntime.queryInterface( + XNamed.class, m_component ); + } + + /* ------------------------------------------------------------------ */ + private FormComponent( Object element ) + { + m_component = element; + m_nameAccess = UnoRuntime.queryInterface( + XNameAccess.class, m_component ); + m_indexAccess = UnoRuntime.queryInterface( + XIndexAccess.class, m_component ); + UnoRuntime.queryInterface( + XChild.class, m_component ); + UnoRuntime.queryInterface( + XNamed.class, m_component ); + } + + /* ------------------------------------------------------------------ */ + /** Quick access to a given interface of the view + @param aInterfaceClass + the class of the interface which shall be returned + */ + public T query( Class aInterfaceClass ) + { + return UnoRuntime.queryInterface( aInterfaceClass, m_component ); + } + + /* ------------------------------------------------------------------ */ + public FormComponent getByName( String name ) + { + try + { + if ( m_nameAccess != null ) + return new FormComponent( m_nameAccess.getByName( name ) ); + } + catch( com.sun.star.uno.Exception e ) + { + System.err.println( e ); + e.printStackTrace( System.err ); + } + return new FormComponent(); + } + + /* ------------------------------------------------------------------ */ + public FormComponent getByIndex( int index ) + { + try + { + if ( m_indexAccess != null ) + return new FormComponent( m_indexAccess.getByIndex( index ) ); + } + catch( com.sun.star.uno.Exception e ) + { + System.err.println( e ); + e.printStackTrace( System.err ); + } + return new FormComponent(); + } + +} diff --git a/forms/qa/integration/forms/FormControlTest.java b/forms/qa/integration/forms/FormControlTest.java new file mode 100644 index 0000000000..773a0197af --- /dev/null +++ b/forms/qa/integration/forms/FormControlTest.java @@ -0,0 +1,951 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +import com.sun.star.awt.XImageProducer; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XNameAccess; +import com.sun.star.form.runtime.XFormController; +import com.sun.star.form.XImageProducerSupplier; +import com.sun.star.frame.XDispatch; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.sdb.CommandType; +import com.sun.star.sdb.SQLErrorEvent; +import com.sun.star.sdb.XSQLErrorBroadcaster; +import com.sun.star.sdb.XSQLErrorListener; +import com.sun.star.sdbc.XDataSource; +import com.sun.star.sdbc.XResultSet; +import com.sun.star.sdbc.XResultSetUpdate; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XNamingService; +import com.sun.star.util.URL; +import com.sun.star.util.XCloseable; +import com.sun.star.util.XURLTransformer; +import connectivity.tools.HsqlDatabase; +import connectivity.tools.sdb.Connection; +import java.io.FileOutputStream; + + +public class FormControlTest extends complexlib.ComplexTestCase implements XSQLErrorListener +{ + private static String s_tableName = "CTC_form_controls"; + + private HsqlDatabase m_databaseDocument; + private XDataSource m_dataSource; + private XPropertySet m_dataSourceProps; + private XMultiServiceFactory m_orb; + private DocumentHelper m_document; + private FormLayer m_formLayer; + private XPropertySet m_masterForm; + private String m_sImageURL; + private SQLErrorEvent m_mostRecentErrorEvent; + + private static final String m_dataSourceName = "integration.forms.FormControlTest"; + + /* ------------------------------------------------------------------ */ + @Override + public String[] getTestMethodNames() + { + return new String[] { + "checkFirstRow", + "checkInsertRow", + "checkImageControl", + "checkCrossUpdates_checkBox", + "checkCrossUpdates_radioButton", + "checkRowUpdates", + "checkEmptyIsNull" + }; + } + + /* ------------------------------------------------------------------ */ + @Override + public String getTestObjectName() + { + return "Database Form Controls Test"; + } + + /* ------------------------------------------------------------------ */ + /// pre-test initialization + public void before() throws com.sun.star.uno.Exception, java.lang.Exception + { + // ensure that we have a data source to work with, and the required tables + if ( !ensureDataSource() || !ensureTables() ) + { + failed( "could not access the required data source or table therein." ); + return; + } + + // create the document which we work on + createSampleDocument(); + + createImageFile(); + } + + /* ------------------------------------------------------------------ */ + public void checkFirstRow() throws com.sun.star.uno.Exception, java.lang.Exception + { + moveToFirst(); + + // and check the content of the various controls + if ( !checkRadios( (short)1, (short)0, (short)0 ) + || !checkDoubleValue( 1, "ID", "Value" ) + || !checkDoubleValue( 42, "f_integer", "EffectiveValue" ) + || !checkStringValue( "the answer", "f_text", "Text" ) + || !checkDoubleValue( 0.12, "f_decimal", "Value" ) + || !checkIntValue ( 20030922, "f_date", "Date" ) + || !checkIntValue ( 15000000, "f_time", "Time" ) + || !checkIntValue ( 20030923, "f_timestamp_date", "Date" ) + || !checkIntValue ( 17152300, "f_timestamp_time", "Time" ) + || !checkShortValue ( (short)1, "f_tinyint", "State" ) + ) + { + failed( "checking the content of one or more controls on the first row failed (see the log for details)" ); + return; + } + } + + /* ------------------------------------------------------------------ */ + public void checkInsertRow() throws com.sun.star.uno.Exception, java.lang.Exception + { + // move the cursor to the insert row + moveToInsertRow(); + + // and check the content of the various controls + if ( !verifyCleanInsertRow() ) + { + failed( "checking the content of one or more controls on the insert row failed (see the log for details)" ); + return; + } + } + + /* ------------------------------------------------------------------ */ + /// some tests with the image control + public void checkImageControl() throws com.sun.star.uno.Exception, java.lang.Exception + { + // since we did not yet insert any image, the control should not display one ... + moveToFirst(); + if ( !verifyReferenceImage( new byte[0] ) ) + { + failed( "image control failed to display empty image" ); + return; + } + + // check if the image control is able to insert our sample image into the database + // insert an + XPropertySet xImageModel = getControlModel( "f_blob" ); + xImageModel.setPropertyValue( "ImageURL", m_sImageURL ); + + if ( !verifyReferenceImage( getSamplePictureBytes() ) ) + { + failed( "image control does not display the sample image as required" ); + return; + } + + // save the record + saveRecordByUI(); + + // still needs to be the sample image + if ( !verifyReferenceImage( getSamplePictureBytes() ) ) + { + failed( "image control does not, after saving the record, display the sample image as required" ); + return; + } + + // on the next record, the image should be empty + moveToNext(); + if ( !verifyReferenceImage( new byte[0] ) ) + { + failed( "image control failed to display empty image, after coming from a non-empty image" ); + return; + } + + // back to the record where we just inserted the image, it should be our sample image + moveToFirst(); + if ( !verifyReferenceImage( getSamplePictureBytes() ) ) + { + failed( "image control does not, after coming back to the record, display the sample image as required" ); + return; + } + + // okay, now remove the image + xImageModel.setPropertyValue( "ImageURL", "" ); + if ( !verifyReferenceImage( new byte[0] ) ) + { + failed( "image control failed to remove the image" ); + return; + } + nextRecordByUI(); + previousRecordByUI(); + if ( !verifyReferenceImage( new byte[0] ) ) + { + failed( "image still there after coming back, though we just removed it" ); + return; + } + } + + /* ------------------------------------------------------------------ */ + /** This is both a test for controls which are bound to the same column (they must reflect + * each others updates), and for the immediate updates which need to happen for both check + * boxes and radio buttons: They must commit their content to the underlying column as soon + * as the change is made, *not* only upon explicit commit + */ + public void checkCrossUpdates_checkBox() throws com.sun.star.uno.Exception, java.lang.Exception + { + // move to the first record + moveToFirst(); + if ( !checkShortValue ( (short)1, "f_tinyint", "State" ) + || !checkDoubleValue( 1, "f_tinyint_format", "EffectiveValue" ) + ) + { + failed( "huh? inconsistence in the test!" ); + // we created the sample data in a way that the f_tinyint field should contain a "1" at the first + // record. We already asserted the proper function of the check box in checkFirstRow, so if this + // fails here, the script became inconsistent + return; + } + + XPropertySet checkModel = getControlModel( "f_tinyint" ); + checkModel.setPropertyValue( "State", Short.valueOf( (short)0 ) ); + + // setting the state of the check box needs to be reflected in the formatted field immediately + if ( !checkDoubleValue( 0, "f_tinyint_format", "EffectiveValue" ) ) + { + failed( "cross-update failed: updating the check box should result in updating the same-bound formatted field (1)!" ); + return; + } + + // same for the "indetermined" state of the check box + checkModel.setPropertyValue( "State", Short.valueOf( (short)2 ) ); + if ( !checkNullValue( "f_tinyint_format", "EffectiveValue" ) ) + { + failed( "cross-update failed: updating the check box should result in updating the same-bound formatted field (2)!" ); + return; + } + + // undo the changes done so far + undoRecordByUI(); + // and see if this is properly reflected in the controls + if ( !checkShortValue ( (short)1, "f_tinyint", "State" ) + || !checkDoubleValue( 1, "f_tinyint_format", "EffectiveValue" ) + ) + { + failed( "either the check box or the formatted field failed to recognize the UNDO!" ); + return; + } + + // the other way round - when changing the formatted field - the change should *not* + // be reflected to the check box, since the formatted field needs an explicit commit + XPropertySet tinyFormattedModel = getControlModel( "f_tinyint_format" ); + m_document.getCurrentView().grabControlFocus( tinyFormattedModel ); + m_formLayer.userTextInput( tinyFormattedModel, "0" ); + if ( !checkShortValue ( (short)1, "f_tinyint", "State" ) + ) + { + failed( "the check box should not be updated here! (did the formatted model commit immediately?)" ); + return; + } + + // set the focus to *any* other control (since we just have it at hand, we use the check box control) + // this should result in the formatted control being committed, and thus in the check box updating + m_document.getCurrentView().grabControlFocus( checkModel ); + if ( !checkShortValue ( (short)0, "f_tinyint", "State" ) + ) + { + failed( "formatted field did not commit (or check box did not update)" ); + return; + } + + // undo the changes done so far, so we leave the document in a clean state for the next test + undoRecordByUI(); + } + + /* ------------------------------------------------------------------ */ + /** very similar to checkCrossUpdates_checkBox - does nearly the same for the radio buttons. See there for more + * explanations. + */ + public void checkCrossUpdates_radioButton() throws com.sun.star.uno.Exception, java.lang.Exception + { + // move to the first record + moveToFirst(); + if ( !checkRadios( (short)1, (short)0, (short)0 ) + || !checkStringValue( "none", "f_text_enum_text", "Text" ) + ) + { + failed( "huh? inconsistence in the test!" ); + return; + } + + XPropertySet radioModel = getRadioModel( "radio_group", "normal" ); + radioModel.setPropertyValue( "State", Short.valueOf( (short)1 ) ); + + // setting the state of the radio button needs to be reflected in the formatted field immediately + if ( !checkStringValue( "normal", "f_text_enum_text", "Text" ) ) + { + failed( "cross-update failed: updating the radio button should result in updating the same-bound text field (1)!" ); + return; + } + + // same for the "indetermined" state of the check box + getRadioModel( "radio_group", "important" ).setPropertyValue( "State", Short.valueOf( (short)1 ) ); + if ( !checkStringValue( "important", "f_text_enum_text", "Text" ) ) + { + failed( "cross-update failed: updating the radio button should result in updating the same-bound text field (2)!" ); + return; + } + + // undo the changes done so far + undoRecordByUI(); + // and see if this is properly reflected in the controls + if ( !checkRadios( (short)1, (short)0, (short)0 ) + || !checkStringValue( "none", "f_text_enum_text", "Text" ) + ) + { + failed( "either the radio button or the text field failed to recognize the UNDO!" ); + return; + } + + // the other way round - when changing the formatted field - the change should *not* + // be reflected to the check box, since the formatted field needs an explicit commit + XPropertySet textModel = getControlModel( "f_text_enum_text" ); + m_document.getCurrentView().grabControlFocus( textModel ); + m_formLayer.userTextInput( textModel, "normal" ); + if ( !checkRadios( (short)1, (short)0, (short)0 ) + ) + { + failed( "the radio buttons should not be updated here! (did the formatted model commit immediately?)" ); + return; + } + + // set the focus to *any* other control (since we just have it at hand, we use the check box control) + // this should result in the formatted control being committed, and thus in the check box updating + m_document.getCurrentView().grabControlFocus( radioModel ); + if ( !checkRadios( (short)0, (short)1, (short)0 ) + ) + { + failed( "text field did not commit (or radio button did not update)" ); + return; + } + + // undo the changes done so far, so we leave the document in a clean state for the next test + undoRecordByUI(); + } + + /* ------------------------------------------------------------------ */ + /** some tests with updating the table via our controls + */ + public void checkRowUpdates() throws com.sun.star.uno.Exception, java.lang.Exception + { + // start with inserting a new record + moveToInsertRow(); + assure( "insert row not in expected clean state", verifyCleanInsertRow() ); + + userTextInput( "ID", "3", true ); + userTextInput( "f_integer", "729", true ); + userTextInput( "f_text", "test", true ); + userTextInput( "f_decimal", "152343", true ); + userTextInput( "f_date", "31.12.1999", true ); + userTextInput( "f_time", "23:59:59", true ); + + // move to the next row, this should automatically commit the changes we made + nextRecordByUI(); + // and back to the row we just inserted + previousRecordByUI(); + + if ( !checkDoubleValue( 3, "ID", "Value" ) + || !checkDoubleValue( 729, "f_integer", "EffectiveValue" ) + || !checkStringValue( "test", "f_text", "Text" ) + || !checkDoubleValue( 152343, "f_decimal", "Value" ) + || !checkIntValue ( 19991231, "f_date", "Date" ) + || !checkIntValue ( 23595900, "f_time", "Time" ) + ) + { + failed( "the changes we made on the insert row have not been committed" ); + return; + } + + // now change the data, to see if regular updates work, too + userTextInput( "ID", "4", true ); + userTextInput( "f_integer", "618", true ); + userTextInput( "f_text", "yet another stupid, meaningless text", true ); + userTextInput( "f_required_text", "this must not be NULL", true ); + userTextInput( "f_decimal", "4562", true ); + userTextInput( "f_date", "26.03.2004", true ); + userTextInput( "f_time", "17:05:00", true ); + + // move to the next row, this should automatically commit the changes we made + nextRecordByUI(); + // and back to the row we just inserted + previousRecordByUI(); + + if ( !checkDoubleValue( 4, "ID", "Value" ) + || !checkDoubleValue( 618, "f_integer", "EffectiveValue" ) + || !checkStringValue( "yet another stupid, meaningless text", "f_text", "Text" ) + || !checkDoubleValue( 4562, "f_decimal", "Value" ) + || !checkIntValue ( 20040326, "f_date", "Date" ) + || !checkIntValue ( 17050000, "f_time", "Time" ) + ) + { + failed( "the changes we made on the insert row have not been committed" ); + return; + } + + m_document.getCurrentView().grabControlFocus( getControlModel( "ID" ) ); + } + + /* ------------------------------------------------------------------ */ + /** checks the "ConvertEmptyToNull" property behavior of an edit control + * + */ + public void checkEmptyIsNull() throws com.sun.star.uno.Exception, java.lang.Exception + { + // start with inserting a new record + moveToInsertRow(); + assure( "insert row not in expected clean state", verifyCleanInsertRow() ); + + // make an input in any field, but leave the edit control which is bound to a required field + // empty + userTextInput( "ID", "5", true ); + userTextInput( "f_text", "more text", true ); + + // this should *not* fail. Even if we did not input anything into the control bound to the + // f_required_text column, this control's reset (done when moving to the insertion row) is + // expected to write an empty string into its bound column, since its EmptyIsNULL property + // is set to FALSE + // (#i92471#) + m_mostRecentErrorEvent = null; + nextRecordByUI(); + assure( "updating an incomplete record did not work as expected", m_mostRecentErrorEvent == null ); + } + + /* ------------------------------------------------------------------ */ + private boolean verifyCleanInsertRow( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + // and check the content of the various controls + return ( checkRadios( (short)0, (short)0, (short)0 ) + && checkShortValue( (short)2, "f_tinyint", "State" ) + && checkStringValue( "", "f_text", "Text" ) + && checkNullValue( "ID", "Value" ) + && checkNullValue( "f_integer", "EffectiveValue" ) + && checkNullValue( "f_decimal", "Value" ) + ); + } + + /* ------------------------------------------------------------------ */ + /// post-test cleanup + public void after() throws com.sun.star.uno.Exception, java.lang.Exception + { + // close our document + if ( m_document != null ) + { + XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class, + m_document.getDocument() ); + closeDoc.close( true ); + } + } + + + /* ------------------------------------------------------------------ */ + private boolean ensureDataSource() throws Exception + { + m_orb = param.getMSF(); + + XNameAccess databaseContext = UnoRuntime.queryInterface( XNameAccess.class, + m_orb.createInstance( "com.sun.star.sdb.DatabaseContext" ) ); + XNamingService namingService = UnoRuntime.queryInterface( XNamingService.class, + databaseContext ); + + // revoke the data source, if it previously existed + if ( databaseContext.hasByName( m_dataSourceName ) ) + namingService.revokeObject( m_dataSourceName ); + + // create a new ODB file, and register it with its URL + m_databaseDocument = new HsqlDatabase( m_orb ); + String documentURL = m_databaseDocument.getDocumentURL(); + namingService.registerObject( m_dataSourceName, databaseContext.getByName( documentURL ) ); + + m_dataSource = UnoRuntime.queryInterface( XDataSource.class, + databaseContext.getByName( m_dataSourceName ) ); + m_dataSourceProps = dbfTools.queryPropertySet( m_dataSource ); + + XPropertySet dataSourceSettings = UnoRuntime.queryInterface( XPropertySet.class, + m_dataSourceProps.getPropertyValue( "Settings" ) ); + dataSourceSettings.setPropertyValue( "FormsCheckRequiredFields", Boolean.FALSE ); + + return m_dataSource != null; + } + + /* ------------------------------------------------------------------ */ + /** retrieves the control model with the given name + */ + private XPropertySet getControlModel( String name ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XNameAccess nameAccess = UnoRuntime.queryInterface( XNameAccess.class, + m_masterForm ); + return UnoRuntime.queryInterface( XPropertySet.class, + nameAccess.getByName( name ) ); + } + + /* ------------------------------------------------------------------ */ + private void createSampleDocument() throws com.sun.star.uno.Exception, java.lang.Exception + { + + m_document = DocumentHelper.blankTextDocument( m_orb ); + m_formLayer = new FormLayer( m_document ); + + // insert some controls + XPropertySet xIDField = m_formLayer.insertControlLine( "DatabaseNumericField", "ID", "", 3 ); + m_formLayer.insertControlLine( "DatabaseFormattedField","f_integer", "", 11 ); + m_formLayer.insertControlLine( "DatabaseTextField", "f_text", "", 19 ); + XPropertySet xReqField = m_formLayer.insertControlLine( "DatabaseTextField", "f_required_text", "", 27 ); + m_formLayer.insertControlLine( "DatabaseNumericField", "f_decimal", "", 35 ); + m_formLayer.insertControlLine( "DatabaseDateField", "f_date", "", 43 ); + XPropertySet xTimeField = m_formLayer.insertControlLine( "DatabaseTimeField", "f_time", "", 51 ); + m_formLayer.insertControlLine( "DatabaseDateField", "f_timestamp", "_date", 59 ); + m_formLayer.insertControlLine( "DatabaseTimeField", "f_timestamp", "_time", 67 ); + XPropertySet xImageField = m_formLayer.insertControlLine( "DatabaseImageControl", "f_blob", "", 2, 75, 40 ); + m_formLayer.insertControlLine( "DatabaseTextField", "f_text_enum", "_text", 80, 25, 6 ); + XPropertySet xCheckBox = m_formLayer.insertControlLine( "DatabaseCheckBox", "f_tinyint", "", 80, 33, 6 ); + m_formLayer.insertControlLine( "DatabaseFormattedField","f_tinyint", "_format",80, 41, 6 ); + m_formLayer.insertControlLine( "DatabaseTextField", "dummy", "", 150 ); + + xIDField.setPropertyValue( "DecimalAccuracy", Short.valueOf( (short)0 ) ); + xImageField.setPropertyValue( "ScaleImage", Boolean.TRUE ); + xImageField.setPropertyValue( "Tabstop", Boolean.TRUE ); + xCheckBox.setPropertyValue( "TriState", Boolean.TRUE ); + xCheckBox.setPropertyValue( "DefaultState", Short.valueOf( (short)2 ) ); + xTimeField.setPropertyValue( "TimeFormat", Short.valueOf( (short)1 ) ); + xTimeField.setPropertyValue( "TimeMax", Integer.valueOf( 23595999 ) ); + xReqField.setPropertyValue( "ConvertEmptyToNull", Boolean.FALSE ); + + // the logical form + m_masterForm = (XPropertySet)dbfTools.getParent( xIDField, XPropertySet.class ); + m_masterForm.setPropertyValue( "DataSourceName", m_dataSourceProps.getPropertyValue( "Name" ) ); + m_masterForm.setPropertyValue( "CommandType", Integer.valueOf( CommandType.TABLE ) ); + m_masterForm.setPropertyValue( "Command", s_tableName ); + + insertRadio( 3, "none", "none" ); + insertRadio( 10, "normal", "normal" ); + insertRadio( 17, "important", "important" ); + + // switch the forms into data entry mode + m_document.getCurrentView( ).toggleFormDesignMode( ); + + XFormController masterFormController = m_document.getCurrentView().getFormController( m_masterForm ); + XSQLErrorBroadcaster errorBroadcaster = UnoRuntime.queryInterface( XSQLErrorBroadcaster.class, + masterFormController ); + errorBroadcaster.addSQLErrorListener( this ); + + // set the focus to the ID control + m_document.getCurrentView().grabControlFocus( xIDField ); + } + + /* ------------------------------------------------------------------ */ + private void insertRadio( int nYPos, String label, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XPropertySet xRadio = m_formLayer.createControlAndShape( "DatabaseRadioButton", 106, nYPos, 25, 6 ); + xRadio.setPropertyValue( "Label", label ); + xRadio.setPropertyValue( "RefValue", refValue ); + xRadio.setPropertyValue( "Name", "radio_group" ); + xRadio.setPropertyValue( "DataField", "f_text_enum"); + } + + /* ------------------------------------------------------------------ */ + private String getCreateTableStatement( ) + { + String sCreateTableStatement = "CREATE TABLE \"" + s_tableName + "\" ("; + sCreateTableStatement += "\"ID\" INTEGER NOT NULL PRIMARY KEY,"; + sCreateTableStatement += "\"f_integer\" INTEGER default NULL,"; + sCreateTableStatement += "\"f_text\" VARCHAR(50) default NULL,"; + sCreateTableStatement += "\"f_required_text\" VARCHAR(50) NOT NULL,"; + sCreateTableStatement += "\"f_decimal\" DECIMAL(10,2) default NULL,"; + sCreateTableStatement += "\"f_date\" DATE default NULL,"; + sCreateTableStatement += "\"f_time\" TIME default NULL,"; + sCreateTableStatement += "\"f_timestamp\" DATETIME default NULL,"; + sCreateTableStatement += "\"f_blob\" VARBINARY,"; + sCreateTableStatement += "\"f_text_enum\" VARCHAR(50) default NULL,"; + sCreateTableStatement += "\"f_tinyint\" TINYINT default NULL"; + sCreateTableStatement += ");"; + return sCreateTableStatement; + } + + /* ------------------------------------------------------------------ */ + private String[] getSampleDataValueString( ) throws java.lang.Exception + { + String[] aValues = new String[] { + "1,42,'the answer','foo',0.12,'2003-09-22','15:00:00','2003-09-23 17:15:23',NULL,'none',1", + "2,13,'the question','bar',12.43,'2003-09-24','16:18:00','2003-09-24 08:45:12',NULL,'none',0" + }; + return aValues; + } + + /* ------------------------------------------------------------------ */ + private boolean ensureTables() throws com.sun.star.uno.Exception, java.lang.Exception + { + Connection connection = new Connection( m_dataSource.getConnection( "", "" ) ); + + // drop the table, if it already exists + if ( !implExecuteStatement( "DROP TABLE \"" + s_tableName + "\" IF EXISTS" ) + || !implExecuteStatement( getCreateTableStatement() ) + ) + { + failed( "could not create the required sample table!" ); + return false; + } + + String sInsertionPrefix = "INSERT INTO \"" + s_tableName + "\" VALUES ("; + String[] aValues = getSampleDataValueString(); + for ( int i=0; i" ); + log.println( " found : " + value.toString() ); + } + else + return true; + + return false; + } + + /* ------------------------------------------------------------------ */ + private boolean checkIntValue( int requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception + { + try + { + if ( "f_time".equals(fieldName) ) + // http://bugs.mysql.com/bug.php?id=5681 + return true; + if (fieldName == null) { + return false; + } + int currentValue = ((Integer)getControlModel( fieldName ).getPropertyValue( propertyName )).intValue(); + if ( currentValue != requiredValue ) + { + log.println( "wrong value of the " + fieldName + " field!" ); + log.println( " expected: " + requiredValue ); + log.println( " found : " + currentValue ); + } + else + return true; + } + catch( com.sun.star.uno.Exception e ) + { + log.println( "caught an exception while retrieving property value '" + propertyName + "' of control model '" + fieldName + "'" ); + throw e; + } + + return false; + } + + /* ------------------------------------------------------------------ */ + private boolean checkShortValue( short requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception + { + try + { + short currentValue = ((Short)getControlModel( fieldName ).getPropertyValue( propertyName )).shortValue(); + if ( currentValue != requiredValue ) + { + log.println( "wrong value of the " + fieldName + " field!" ); + log.println( " expected: " + requiredValue ); + log.println( " found : " + currentValue ); + } + else + return true; + } + catch( com.sun.star.uno.Exception e ) + { + log.println( "caught an exception while retrieving property value '" + propertyName + "' of control model '" + fieldName + "'" ); + throw e; + } + + return false; + } + + /* ------------------------------------------------------------------ */ + private boolean checkDoubleValue( double requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception + { + double currentValue = ((Double)getControlModel( fieldName ).getPropertyValue( propertyName )).doubleValue(); + if ( currentValue != requiredValue ) + { + log.println( "wrong value of the " + fieldName + " field!" ); + log.println( " expected: " + requiredValue ); + log.println( " found : " + currentValue ); + } + else + return true; + + return false; + } + + /* ------------------------------------------------------------------ */ + private boolean checkStringValue( String requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception + { + String currentValue = (String)getControlModel( fieldName ).getPropertyValue( propertyName ); + if ( !currentValue.equals( requiredValue ) ) + { + log.println( "wrong value of the " + fieldName + " field!" ); + log.println( " expected: " + requiredValue ); + log.println( " found : " + currentValue ); + } + else + return true; + + return false; + } + + /* ------------------------------------------------------------------ */ + private XPropertySet getRadioModel( String name, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception + { + return m_formLayer.getRadioModelByRefValue( m_masterForm, name, refValue ); + } + + /* ------------------------------------------------------------------ */ + /** executes the given statement on the given connection + */ + protected boolean implExecuteStatement( String sStatement ) throws java.lang.Exception + { + try + { + m_databaseDocument.executeSQL( sStatement ); + } + catch(com.sun.star.sdbc.SQLException e) + { + System.err.println( e ); + return false; + } + + return true; + } + + /* ------------------------------------------------------------------ */ + /** simulates a user's text input into a control given by model name + */ + private void userTextInput( String modelName, String text, boolean withCommit ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XPropertySet controlModel = getControlModel( modelName ); + // the form runtime environment (namely the form controller) rely on focus events for recognizing + // control content changes ... + if ( withCommit ) + m_document.getCurrentView().grabControlFocus( controlModel ); + + m_formLayer.userTextInput( controlModel, text ); + + // focus back to a dummy control model so the content of the model we just changed will + // be committed to the underlying database column + if ( withCommit ) + m_document.getCurrentView().grabControlFocus( getControlModel( "dummy" ) ); + } + + /* ------------------------------------------------------------------ */ + private void moveToInsertRow() throws com.sun.star.uno.Exception, java.lang.Exception + { + XResultSetUpdate xResultSet = UnoRuntime.queryInterface( XResultSetUpdate.class, m_masterForm ); + xResultSet.moveToInsertRow( ); + } + + /* ------------------------------------------------------------------ */ + private void moveToFirst() throws com.sun.star.uno.Exception, java.lang.Exception + { + XResultSet xResultSet = UnoRuntime.queryInterface( XResultSet.class, m_masterForm ); + xResultSet.first( ); + } + + /* ------------------------------------------------------------------ */ + private void moveToNext() throws com.sun.star.uno.Exception, java.lang.Exception + { + XResultSet xResultSet = UnoRuntime.queryInterface( XResultSet.class, m_masterForm ); + xResultSet.next( ); + } + + /* ------------------------------------------------------------------ */ + /** simulates pressing a toolbox button with the given URL + */ + private void executeSlot( String slotURL ) throws java.lang.Exception + { + XDispatch xDispatch = m_document.getCurrentView().getDispatcher( slotURL ); + + URL[] url = new URL[] { new URL() }; + url[0].Complete = slotURL; + XURLTransformer xTransformer = UnoRuntime.queryInterface( + XURLTransformer.class, m_orb.createInstance( "com.sun.star.util.URLTransformer" ) ); + xTransformer.parseStrict( url ); + + PropertyValue[] aArgs = new PropertyValue[0]; + xDispatch.dispatch( url[0], aArgs ); + } + + /* ------------------------------------------------------------------ */ + /** undos the changes on the current record, by simulating pressing of the respective toolbox button + */ + private void undoRecordByUI() throws java.lang.Exception + { + executeSlot( ".uno:RecUndo" ); + } + + /* ------------------------------------------------------------------ */ + /** saves the current record, by simulating pressing of the respective toolbox button + */ + private void saveRecordByUI() throws java.lang.Exception + { + executeSlot( ".uno:RecSave" ); + } + + /* ------------------------------------------------------------------ */ + /** moves to the next record, by simulating pressing of the respective toolbox button + */ + private void nextRecordByUI() throws java.lang.Exception + { + executeSlot( ".uno:NextRecord" ); + } + /* ------------------------------------------------------------------ */ + /** moves to the previous record, by simulating pressing of the respective toolbox button + */ + private void previousRecordByUI() throws java.lang.Exception + { + executeSlot( ".uno:PrevRecord" ); + } + + /* ------------------------------------------------------------------ */ + private void createImageFile() throws java.io.IOException + { + m_sImageURL = util.utils.getOfficeTempDir( m_orb ) + "image.gif"; + + FileOutputStream aFile = null; + try + { + aFile = new FileOutputStream( m_sImageURL ); + aFile.write( getSamplePicture() ); + } + finally + { + if ( aFile != null ) + aFile.close(); + } + log.println( "created temporary image file: " + m_sImageURL ); + + // for later setting the url at the image control, we need a real URL, no system path + m_sImageURL = util.utils.getOfficeTemp( m_orb ) + "image.gif"; + } + + /* ------------------------------------------------------------------ */ + private byte[] getSamplePicture() + { + byte[] aBytes = new byte[] { + (byte)0x47, (byte)0x49, (byte)0x46, (byte)0x38, (byte)0x39, (byte)0x61, (byte)0x0A, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0xB3, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, + (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, + (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, + (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x2C, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x20, (byte)0x10, (byte)0xC8, (byte)0x49, (byte)0x41, (byte)0xB9, (byte)0xF8, (byte)0xCA, + (byte)0x12, (byte)0xBA, (byte)0x2F, (byte)0x5B, (byte)0x30, (byte)0x8C, (byte)0x43, (byte)0x00, (byte)0x5A, (byte)0x22, (byte)0x41, (byte)0x94, (byte)0x27, (byte)0x37, (byte)0xA8, (byte)0x6C, + (byte)0x48, (byte)0xC6, (byte)0xA8, (byte)0xD7, (byte)0xB5, (byte)0x19, (byte)0x56, (byte)0xED, (byte)0x11, (byte)0x00, (byte)0x3B + }; + + return aBytes; + } + + /* ------------------------------------------------------------------ */ + private byte[] getSamplePictureBytes() + { + byte[] aBytes = new byte[] { + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, + (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, + (byte)0x01, (byte)0x03, (byte)0x03, (byte)0x03, (byte)0x03, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x03, (byte)0x04, (byte)0x04, (byte)0x03, (byte)0x01, + (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x03, (byte)0x04, (byte)0x04, (byte)0x03, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x03, + (byte)0x03, (byte)0x03, (byte)0x03, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, + (byte)0x00, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 + }; + return aBytes; + } + + /* ------------------------------------------------------------------ */ + private boolean verifyReferenceImage( byte[] referenceBytes ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XPropertySet xImageModel = getControlModel( "f_blob" ); + + // check if the image control properly says that there currently is no image on the first record + XImageProducerSupplier xSuppProducer = UnoRuntime.queryInterface( XImageProducerSupplier.class, + xImageModel ); + XImageProducer xProducer = xSuppProducer.getImageProducer(); + + ImageComparison compareImages = new ImageComparison( referenceBytes, this ); + synchronized( this ) + { + xProducer.addConsumer( compareImages ); + xProducer.startProduction(); + } + xProducer.removeConsumer( compareImages ); + + return compareImages.imagesEqual( ); + } + + /* ------------------------------------------------------------------ */ + public void errorOccured( SQLErrorEvent _event ) + { + // just remember for the moment + m_mostRecentErrorEvent = _event; + } + + /* ------------------------------------------------------------------ */ + public void disposing( EventObject _event ) + { + // not interested in + } +} diff --git a/forms/qa/integration/forms/FormLayer.java b/forms/qa/integration/forms/FormLayer.java new file mode 100644 index 0000000000..3d28ff054b --- /dev/null +++ b/forms/qa/integration/forms/FormLayer.java @@ -0,0 +1,318 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleEditableText; +import com.sun.star.uno.UnoRuntime; + +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XIndexContainer; +import com.sun.star.container.XIndexAccess; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.drawing.XControlShape; +import com.sun.star.drawing.XShapes; +import com.sun.star.awt.Size; +import com.sun.star.awt.Point; +import com.sun.star.awt.VisualEffect; +import com.sun.star.awt.XControlModel; +import com.sun.star.text.TextContentAnchorType; +import com.sun.star.drawing.XDrawPage; + +public class FormLayer +{ + private final DocumentHelper m_document; + private XDrawPage m_page; + + /* ------------------------------------------------------------------ */ + /** Creates a new instance of FormLayer */ + public FormLayer( DocumentHelper _document ) + { + m_document = _document; + } + + /* ------------------------------------------------------------------ */ + /** sets the page which is to be used for subsequent insertions of controls/shapes + */ + void setInsertPage( int page ) throws com.sun.star.lang.IndexOutOfBoundsException, com.sun.star.lang.WrappedTargetException + { + m_page = m_document.getDrawPage( page ); + } + + /* ------------------------------------------------------------------ */ + /** creates a control in the document + +

Note that control here is an incorrect terminology. What the method really does is + it creates a control shape, together with a control model, and inserts them into the document model. + This will result in every view to this document creating a control described by the model-shape pair. +

+ + @param sFormComponentService + the service name of the form component to create, e.g. "TextField" + @param nXPos + the abscissa of the position of the newly inserted shape + @param nWidth + the width of the newly inserted shape + @param nHeight + the height of the newly inserted shape + @param _parentForm + the form to use as parent for the newly create form component. May be null, in this case + a default parent is chosen by the implementation + @return + the property access to the control's model + */ + public XPropertySet createControlAndShape( String sFormComponentService, int nXPos, + int nYPos, int nWidth, int nHeight, Object _parentForm ) throws java.lang.Exception + { + // let the document create a shape + XMultiServiceFactory xDocAsFactory = UnoRuntime.queryInterface( + XMultiServiceFactory.class, m_document.getDocument() ); + XControlShape xShape = UnoRuntime.queryInterface( XControlShape.class, + xDocAsFactory.createInstance( "com.sun.star.drawing.ControlShape" ) ); + + // position and size of the shape + xShape.setSize( new Size( nWidth * 100, nHeight * 100 ) ); + xShape.setPosition( new Point( nXPos * 100, nYPos * 100 ) ); + + // adjust the anchor so that the control is tied to the page + XPropertySet xShapeProps = dbfTools.queryPropertySet( xShape ); + TextContentAnchorType eAnchorType = TextContentAnchorType.AT_PARAGRAPH; + xShapeProps.setPropertyValue( "AnchorType", eAnchorType ); + + // create the form component (the model of a form control) + String sQualifiedComponentName = "com.sun.star.form.component." + sFormComponentService; + XControlModel xModel = UnoRuntime.queryInterface( XControlModel.class, + m_document.getOrb().createInstance( sQualifiedComponentName ) ); + + // insert the model into the form component hierarchy, if the caller gave us a location + if ( null != _parentForm ) + { + XIndexContainer parentForm; + if ( _parentForm instanceof XIndexContainer ) + parentForm = (XIndexContainer)_parentForm; + else + parentForm = UnoRuntime.queryInterface( XIndexContainer.class, _parentForm ); + parentForm.insertByIndex( parentForm.getCount(), xModel ); + } + + // knitt them + xShape.setControl( xModel ); + + // add the shape to the shapes collection of the document + XDrawPage pageWhereToInsert = ( m_page != null ) ? m_page : m_document.getMainDrawPage(); + + XShapes xDocShapes = UnoRuntime.queryInterface( XShapes.class, pageWhereToInsert ); + xDocShapes.add( xShape ); + + // and outta here with the XPropertySet interface of the model + XPropertySet xModelProps = dbfTools.queryPropertySet( xModel ); + return xModelProps; + } + + /* ------------------------------------------------------------------ */ + /** creates a control in the document + +

Note that control here is an incorrect terminology. What the method really does is + it creates a control shape, together with a control model, and inserts them into the document model. + This will result in every view to this document creating a control described by the model-shape pair. +

+ + @param sFormComponentService + the service name of the form component to create, e.g. "TextField" + @param nXPos + the abscissa of the position of the newly inserted shape + @param nWidth + the width of the newly inserted shape + @param nHeight + the height of the newly inserted shape + @return + the property access to the control's model + */ + public XPropertySet createControlAndShape( String sFormComponentService, int nXPos, + int nYPos, int nWidth, int nHeight ) throws java.lang.Exception + { + return createControlAndShape( sFormComponentService, nXPos, nYPos, nWidth, nHeight, null ); + } + + /** creates a pair of controls, namely a label control, and another control labeled by it + * + * @param _formComponentServiceName + * the service name for the control which is not the label control + * @param _label + * the label to be shown in the label control + * @param _xPos + * the horizontal position of the control pair + * @param _yPos + * the vertical position of the control pair + * @param _height + * the height of the control which is not the label control + * @return + * the model of the control which is not the label control + */ + public XPropertySet createLabeledControl( String _formComponentServiceName, String _label, int _xPos, + int _yPos, int _height ) + throws java.lang.Exception + { + // insert the label control + XPropertySet label = createControlAndShape( "FixedText", _xPos, _yPos, 25, 6 ); + label.setPropertyValue( "Label", _label ); + + // insert the text field control + XPropertySet field = createControlAndShape( _formComponentServiceName, + _xPos + 25, _yPos, 40, _height ); + // knit it to its label component + field.setPropertyValue( "LabelControl", label ); + + // names + label.setPropertyValue( "Name", _label + "_Label" ); + field.setPropertyValue( "Name", _label ); + + return field; + } + + /* ------------------------------------------------------------------ */ + /** creates a line of controls, consisting of a label and a field for data input. + +

In opposite to the second form of this method, here the height of the field, + as well as the abscissa of the label, are under the control of the caller.

+ + @param sControlType + specifies the type of the data input control + @param sFieldName + specifies the field name the text field should be bound to + @param _controlNamePostfix + specifies a postfix to append to the logical control names + @param nYPos + specifies the Y position of the line to start at + @param nHeight + the height of the field + @return + the control model of the created data input field + */ + public XPropertySet insertControlLine( String sControlType, String sFieldName, String _controlNamePostfix, + int nXPos, int nYPos, int nHeight ) + throws java.lang.Exception + { + // insert the label control + XPropertySet xLabelModel = createControlAndShape( "FixedText", nXPos, nYPos, 25, 6 ); + xLabelModel.setPropertyValue( "Label", sFieldName ); + + // insert the text field control + XPropertySet xFieldModel = createControlAndShape( sControlType, nXPos + 26, nYPos, 40, nHeight ); + xFieldModel.setPropertyValue( "DataField", sFieldName ); + if ( xFieldModel.getPropertySetInfo().hasPropertyByName( "Border" ) ) + { + xFieldModel.setPropertyValue( "Border", Short.valueOf( VisualEffect.FLAT ) ); + if ( xFieldModel.getPropertySetInfo().hasPropertyByName( "BorderColor" ) ) + xFieldModel.setPropertyValue( "BorderColor", Integer.valueOf( 0x00C0C0C0 ) ); + } + // knit it to its label component + xFieldModel.setPropertyValue( "LabelControl", xLabelModel ); + + // some names, so later on we can find them + if ( _controlNamePostfix == null ) + _controlNamePostfix = ""; + xLabelModel.setPropertyValue( "Name", sFieldName + _controlNamePostfix + "_Label" ); + xFieldModel.setPropertyValue( "Name", sFieldName + _controlNamePostfix ); + + return xFieldModel; + } + + /* ------------------------------------------------------------------ */ + /** creates a line of controls, consisting of a label and a field for data input. + + @param sControlType + specifies the type of the data input control + @param sFieldName + specifies the field name the text field should be bound to + @param nYPos + specifies the Y position of the line to start at + @return + the control model of the created data input field + */ + public XPropertySet insertControlLine( String sControlType, String sFieldName, String sControlNamePostfix, int nYPos ) + throws java.lang.Exception + { + return insertControlLine( sControlType, sFieldName, sControlNamePostfix, 10, nYPos, 6 ); + } + + /* ------------------------------------------------------------------ */ + /** retrieves the radio button model with the given name and the given ref value + * @param form + * the parent form of the radio button model to find + * @param name + * the name of the radio button + * @param refValue + * the reference value of the radio button + */ + public XPropertySet getRadioModelByRefValue( XPropertySet form, String name, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XIndexAccess indexAccess = UnoRuntime.queryInterface( XIndexAccess.class, form ); + + for ( int i=0; i 0 ) + failedFieldTypes.append( ", " ); + failedFieldTypes.append( fieldTypes[i] ); + } + } + /*assure( "The following field types do not work when used as bound list box fields: " + failedFieldTypes.toString() + + " (row " + row + ")", failedFieldTypes.length() == 0 );*/ + + formOperations.execute( FormFeature.MoveToNext ); + } + + } + finally + { + closeDocument(); + } + } + + /* ------------------------------------------------------------------ */ + @Override + public void before() throws Exception, java.lang.Exception + { + super.before(); + impl_createDatabase(); + } + + /* ------------------------------------------------------------------ */ + @Override + protected void prepareDocument() throws com.sun.star.uno.Exception, java.lang.Exception + { + super.prepareDocument(); + impl_createForm(); + } + + /* ------------------------------------------------------------------ */ + private String[][] impl_getFieldTypeDefinitions() + { + return new String[][] { + new String[] { + "bigint", "boolean", "date", "decimal", "double", "float", "numeric", "time", "timestamp", "tinyint", "varchar" + }, + new String[] { + null, null, null, "(10,2)", null, null, "(10,2)", null, null, null, "(50)" + } + }; + } + + /* ------------------------------------------------------------------ */ + private String[] impl_getTypedValue( final String _asType ) + { + Map< String, String[] > valueMap = new HashMap< String, String[] >(); + valueMap.put( "bigint", new String[] { "1111111111", "222222222" } ); + valueMap.put( "boolean", new String[] { "false", "true" } ); + valueMap.put( "date", new String[] { "2001-01-01", "2002-02-02" } ); + valueMap.put( "decimal", new String[] { "1.11", "2.22" } ); + valueMap.put( "double", new String[] { "1.11", "2.22" } ); + valueMap.put( "float", new String[] { "1.11", "2.22" } ); + valueMap.put( "numeric", new String[] { "1.11", "2.22" } ); + valueMap.put( "time", new String[] { "01:01:01", "02:02:02" } ); + valueMap.put( "timestamp", new String[] { "2001-01-01 01:01:01", "2002-02-02 02:02:02" } ); + valueMap.put( "tinyint", new String[] { "1", "2" } ); + valueMap.put( "varchar", new String[] { "first", "second" } ); + + return valueMap.get( _asType ); + } + + /* ------------------------------------------------------------------ */ + private String[] impl_getDisplayValues() + { + return new String[] { "one", "two" }; + } + + /* ------------------------------------------------------------------ */ + private void impl_createDatabase() throws java.lang.Exception + { + try + { + m_database = new HsqlDatabase( m_orb ); + Connection connection = m_database.defaultConnection(); + System.out.println( m_database.getDocumentURL() ); + + final String[][] fieldDefinitions = impl_getFieldTypeDefinitions(); + final String[] keyTypes = fieldDefinitions[0]; + final String[] keyCreationArgs = fieldDefinitions[1]; + + ArrayList< HsqlColumnDescriptor > foreignKeyColumns = new ArrayList< HsqlColumnDescriptor >(); + foreignKeyColumns.add( new HsqlColumnDescriptor( "ID", "integer", HsqlColumnDescriptor.PRIMARY ) ); + + ArrayList< String[] > foreignKeyValues = new ArrayList< String[] >(); + + StringBuilder foreignKeyInsertSQL = new StringBuilder(); + foreignKeyInsertSQL.append( "INSERT INTO \"" + m_foreignKeyTableName + "\" VALUES (?" ); + + final String[] displayValues = impl_getDisplayValues(); + + for ( int i=0; i 65535 ) Then\n" + + " oControlModel = oEvent.Source.Model\n" + + " sSheet = oControlModel.StringItemList( oEvent.Selected )\n" + + "\n" + + " oSheets = thisComponent.Sheets\n" + + " oSheet = oSheets.getByName(sSheet)\n" + + "\n" + + " oView = thisComponent.CurrentController\n" + + " oView.setActiveSheet( oSheet )\n" + + " End If\n" + + "End Sub\n" + + "\n" + + "Sub onButtonClicked\n" + + " MsgBox \"clicked\"\n" + + "End Sub\n"; + + basicLib.insertByName( "callbacks", sListSelectionScript ); + } + catch( com.sun.star.uno.Exception e ) + { + e.printStackTrace( System.err ); + failed( "caught an exception: " + e.toString() ); + } + } + + /* ------------------------------------------------------------------ */ + final private void impl_assignStarBasicScript( XPropertySet controlModel, String interfaceName, String interfaceMethod, String scriptCode ) + { + try + { + XIndexContainer parentForm = (XIndexContainer)dbfTools.getParent( controlModel, XIndexContainer.class ); + + XEventAttacherManager manager = UnoRuntime.queryInterface( + XEventAttacherManager.class, parentForm ); + + int containerPosition = -1; + for ( int i = 0; i < parentForm.getCount(); ++i ) + { + XPropertySet child = dbfTools.queryPropertySet( parentForm.getByIndex( i ) ); + if ( child.equals( controlModel ) ) + { + containerPosition = i; + break; + } + } + manager.registerScriptEvent( containerPosition, new ScriptEventDescriptor( + interfaceName, + interfaceMethod, + "", + "StarBasic", + scriptCode + ) ); + } + catch( com.sun.star.uno.Exception e ) + { + e.printStackTrace( System.err ); + failed( "caught an exception: " + e.toString() ); + } + } + + /* ------------------------------------------------------------------ */ + @Override + protected void prepareDocument() throws com.sun.star.uno.Exception, java.lang.Exception + { + super.prepareDocument(); + impl_setupListenerScript(); + + SpreadsheetDocument document = (SpreadsheetDocument)m_document; + XSpreadsheets sheets = document.getSheets(); + + // delete all sheets except one + String[] sheetNames = sheets.getElementNames(); + for ( short i = 1; i < sheetNames.length; ++i ) + sheets.removeByName( sheetNames[ i ] ); + + // need 5 sheets + String[] newSheetNames = new String[] { "first", "second", "third", "forth", "fifth" }; + + // give the first one the right name + XNamed sheet = UnoRuntime.queryInterface( XNamed.class, + sheets.getByName( sheetNames[ 0 ] ) + ); + sheet.setName( newSheetNames[ 0 ] ); + + // add some dummy buttons + for ( int i = 0; i < 4; ++i ) + { + XPropertySet buttonModel = m_formLayer.createControlAndShape( "CommandButton", 10, 10 + i * 10, 30, 8 ); + impl_assignStarBasicScript( buttonModel, "XActionListener", "actionPerformed", "document:default.callbacks.onButtonClicked" ); + } + + // and a list box + XPropertySet listBox = m_formLayer.createControlAndShape( "ListBox", 50, 10, 40, 6 ); + listBox.setPropertyValue( "Dropdown", Boolean.TRUE ); + listBox.setPropertyValue( "StringItemList", newSheetNames ); + listBox.setPropertyValue( "Name", "ListBox" ); + + impl_assignStarBasicScript( listBox, "XItemListener", "itemStateChanged", "document:default.callbacks.onListBoxSelected" ); + + // clone this sheet + for ( short i = 1; i < newSheetNames.length; ++i ) + sheets.copyByName( newSheetNames[0], newSheetNames[i], i ); + + // switch the thing to alive mode + m_document.getCurrentView().toggleFormDesignMode(); + + try + { + XStorable storable = m_document.query( XStorable.class ); + java.io.File testFile = java.io.File.createTempFile( getTestObjectName(),".ods"); + storable.storeAsURL( testFile.getAbsoluteFile().toURI().toURL().toString(), new com.sun.star.beans.PropertyValue[]{} ); + testFile.deleteOnExit(); + } + catch( java.lang.Throwable e ) + { + e.printStackTrace(); + failed( "caught an exception: " + e.toString() ); + } + } + + /* ------------------------------------------------------------------ */ + protected XControlModel getListBoxModel( XSpreadsheet sheet ) + { + XDrawPageSupplier suppPage = UnoRuntime.queryInterface( + XDrawPageSupplier.class, sheet ); + FormComponent formsRoot = new FormComponent( suppPage.getDrawPage() ); + XControlModel listBoxModel = formsRoot.getByIndex( 0 ). + getByName( "ListBox" ).query( XControlModel.class ); + return listBoxModel; + } + + /* ------------------------------------------------------------------ */ + protected XListBox getListBoxControl( XSpreadsheet sheet ) throws com.sun.star.uno.Exception + { + return UnoRuntime.queryInterface( + XListBox.class, m_document.getCurrentView().getControl( getListBoxModel( sheet ) ) ); + } + } diff --git a/forms/qa/integration/forms/ListSelection.props b/forms/qa/integration/forms/ListSelection.props new file mode 100644 index 0000000000..92b56bef99 --- /dev/null +++ b/forms/qa/integration/forms/ListSelection.props @@ -0,0 +1 @@ +ThreadTimeOut=600000 diff --git a/forms/qa/integration/forms/ListSelectionValidator.java b/forms/qa/integration/forms/ListSelectionValidator.java new file mode 100644 index 0000000000..164110d30f --- /dev/null +++ b/forms/qa/integration/forms/ListSelectionValidator.java @@ -0,0 +1,51 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package integration.forms; + +public class ListSelectionValidator extends integration.forms.ControlValidator +{ + public String explainInvalid( Object Value ) + { + try + { + short[] selectionIndexes = (short[])Value; + if ( selectionIndexes.length > 2 ) + return "please 2 entries, at most"; + } + catch( java.lang.Exception e ) + { + return "oops. What's this?"; + } + return ""; + } + + public boolean isValid( Object Value ) + { + try + { + short[] selectionIndexes = (short[])Value; + return selectionIndexes.length <= 2; + } + catch( java.lang.Exception e ) + { + } + return false; + } + +} diff --git a/forms/qa/integration/forms/MasterDetailForms.java b/forms/qa/integration/forms/MasterDetailForms.java new file mode 100644 index 0000000000..4b448fe04b --- /dev/null +++ b/forms/qa/integration/forms/MasterDetailForms.java @@ -0,0 +1,400 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package integration.forms; + +import com.sun.star.beans.NamedValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XIndexContainer; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.container.XNameContainer; +import com.sun.star.embed.XComponentSupplier; +import com.sun.star.form.XGridColumnFactory; +import com.sun.star.form.XGridFieldDataSupplier; +import com.sun.star.form.XLoadable; +import com.sun.star.lang.XComponent; +import com.sun.star.sdb.CommandType; +import com.sun.star.sdb.XFormDocumentsSupplier; +import com.sun.star.sdbc.SQLException; +import com.sun.star.sdbc.XColumnLocate; +import com.sun.star.ucb.Command; +import com.sun.star.ucb.OpenMode; +import com.sun.star.ucb.XCommandProcessor; +import com.sun.star.uno.Type; +import com.sun.star.util.XModifiable; +import connectivity.tools.CRMDatabase; +import connectivity.tools.HsqlColumnDescriptor; +import connectivity.tools.HsqlDatabase; +import connectivity.tools.HsqlTableDescriptor; +import org.openoffice.complex.forms.tools.ResultSet; + + +public class MasterDetailForms extends complexlib.ComplexTestCase implements com.sun.star.form.XLoadListener +{ + private XMultiServiceFactory m_orb; + + private XPropertySet m_masterForm; + private XPropertySet m_detailForm; + private ResultSet m_masterResult; + private ResultSet m_detailResult; + + final private Object m_waitForLoad = new Object(); + private boolean m_loaded = false; + + /* ------------------------------------------------------------------ */ + @Override + public String[] getTestMethodNames() + { + return new String[] { + "checkMultipleKeys", + "checkDetailFormDefaults" + }; + } + + /* ------------------------------------------------------------------ */ + public void before() + { + m_orb = param.getMSF(); + } + + /* ------------------------------------------------------------------ */ + @Override + public String getTestObjectName() + { + return "Form Control Spreadsheet Cell Binding Test"; + } + + /* ------------------------------------------------------------------ */ + /** creates the table structure needed for the test + */ + private void impl_createTableStructure( final HsqlDatabase _databaseDocument ) throws SQLException + { + HsqlColumnDescriptor[] masterColumns = { + new HsqlColumnDescriptor( "ID1", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "ID2", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "value", "VARCHAR(50)" ), + }; + HsqlColumnDescriptor[] detailColumns = { + new HsqlColumnDescriptor( "ID", "INTEGER", HsqlColumnDescriptor.PRIMARY ), + new HsqlColumnDescriptor( "FK_ID1", "INTEGER", HsqlColumnDescriptor.REQUIRED, "master", "ID1" ), + new HsqlColumnDescriptor( "FK_ID2", "INTEGER", HsqlColumnDescriptor.REQUIRED, "master", "ID2" ), + new HsqlColumnDescriptor( "name", "VARCHAR(50)" ), + }; + _databaseDocument.createTable( new HsqlTableDescriptor( "master", masterColumns ) ); + _databaseDocument.createTable( new HsqlTableDescriptor( "detail", detailColumns ) ); + + _databaseDocument.executeSQL( "INSERT INTO \"master\" VALUES ( 1, 1, 'First Record' )" ); + _databaseDocument.executeSQL( "INSERT INTO \"master\" VALUES ( 1, 2, 'Second Record' )" ); + _databaseDocument.executeSQL( "INSERT INTO \"detail\" VALUES ( 1, 1, 1, 'record 1.1 (1)')"); + _databaseDocument.executeSQL( "INSERT INTO \"detail\" VALUES ( 2, 1, 1, 'record 1.1 (2)')"); + _databaseDocument.executeSQL( "INSERT INTO \"detail\" VALUES ( 3, 1, 2, 'record 1.2 (1)')"); + + _databaseDocument.defaultConnection().refreshTables(); + } + + /* ------------------------------------------------------------------ */ + private void impl_createForms( final HsqlDatabase _databaseDocument ) throws com.sun.star.uno.Exception + { + m_masterForm = dbfTools.queryPropertySet( m_orb.createInstance( "com.sun.star.form.component.DataForm" ) ); + m_masterForm.setPropertyValue( "ActiveConnection", _databaseDocument.defaultConnection().getXConnection() ); + m_masterForm.setPropertyValue( "CommandType", Integer.valueOf( com.sun.star.sdb.CommandType.TABLE ) ); + m_masterForm.setPropertyValue( "Command", "master" ); + + m_masterResult = new ResultSet( m_masterForm ); + + m_detailForm = dbfTools.queryPropertySet( m_orb.createInstance( "com.sun.star.form.component.DataForm" ) ); + m_detailForm.setPropertyValue( "ActiveConnection", _databaseDocument.defaultConnection().getXConnection() ); + m_detailForm.setPropertyValue( "CommandType", Integer.valueOf( com.sun.star.sdb.CommandType.TABLE ) ); + m_detailForm.setPropertyValue( "Command", "detail" ); + + m_detailResult = new ResultSet( m_detailForm ); + + XNameContainer masterContainer = UnoRuntime.queryInterface( XNameContainer.class, m_masterForm ); + masterContainer.insertByName( "slave", m_detailForm ); + } + + /* ------------------------------------------------------------------ */ + /** checks if master-detail relationships including multiple keys work + */ + public void checkMultipleKeys() throws com.sun.star.uno.Exception, java.lang.Exception + { + HsqlDatabase databaseDocument = null; + try + { + databaseDocument = new HsqlDatabase( m_orb ); + impl_createTableStructure( databaseDocument ); + impl_createForms( databaseDocument ); + + m_detailForm.setPropertyValue( "MasterFields", new String[] { "ID1", "ID2" } ); + m_detailForm.setPropertyValue( "DetailFields", new String[] { "FK_ID1", "FK_ID2" } ); + + XLoadable loadMaster = UnoRuntime.queryInterface( XLoadable.class, m_masterForm ); + XLoadable loadDetail = UnoRuntime.queryInterface( XLoadable.class, m_detailForm ); + loadDetail.addLoadListener( this ); + + // wait until the detail form is loaded + loadMaster.load(); + impl_waitForLoadedEvent(); + + // okay, now the master form should be on the first record + assure( "wrong form state after loading (ID1)", m_masterResult.getInt(1) == 1 ); + assure( "wrong form state after loading (ID2)", m_masterResult.getInt(2) == 1 ); + assure( "wrong form state after loading (value)", m_masterResult.getString(3).equals( "First Record" ) ); + + // the values in the linked fields should be identical + int expectedDetailRowCounts[] = { 2, 1 }; + do + { + verifyColumnValueIdentity( "ID1", "FK_ID1" ); + verifyColumnValueIdentity( "ID2", "FK_ID2" ); + + m_detailResult.last(); + int masterPos = m_masterResult.getRow(); + assure( "wrong number of records in detail form, for master form at pos " + masterPos, + ((Integer)m_detailForm.getPropertyValue( "RowCount" )).intValue() == expectedDetailRowCounts[ masterPos - 1 ] ); + + if (!m_masterResult.next()) + return; + impl_waitForLoadedEvent(); + } + while ( !m_masterResult.isAfterLast() ); + assure( "wrong number of records in master form", 2 == ((Integer)m_masterForm.getPropertyValue( "RowCount" )).intValue() ); + } + finally + { + if ( databaseDocument != null ) + databaseDocument.closeAndDelete(); + impl_cleanUpStep(); + } + } + + /* ------------------------------------------------------------------ */ + private final void impl_cleanUpStep() + { + if ( m_masterForm != null ) + dbfTools.disposeComponent( m_masterForm ); + if ( m_detailForm != null ) + dbfTools.disposeComponent( m_detailForm ); + m_masterForm = m_detailForm = null; + } + + /* ------------------------------------------------------------------ */ + /** checks whether default values in detail forms work as expected. + * + * Effectively, this test case verifies the issues #i106574# and #i105235# did not creep back in. + */ + public void checkDetailFormDefaults() throws Exception + { + CRMDatabase database = null; + XCommandProcessor subComponentCommands = null; + try + { + // create our standard CRM database document + database = new CRMDatabase( m_orb, true ); + + // create a form document therein + XFormDocumentsSupplier formDocSupp = UnoRuntime.queryInterface( XFormDocumentsSupplier.class, database.getDatabase().getModel() ); + XMultiServiceFactory formFactory = UnoRuntime.queryInterface( XMultiServiceFactory.class, formDocSupp.getFormDocuments() ); + NamedValue[] loadArgs = new NamedValue[] { + new NamedValue( "ActiveConnection", database.getConnection().getXConnection() ), + new NamedValue( "MediaType", "application/vnd.oasis.opendocument.text" ) + }; + + subComponentCommands = UnoRuntime.queryInterface( + XCommandProcessor.class, + formFactory.createInstanceWithArguments( "com.sun.star.sdb.DocumentDefinition", loadArgs ) ); + Command command = new Command(); + command.Name = "openDesign"; + command.Argument = Short.valueOf( OpenMode.DOCUMENT ); + + DocumentHelper subDocument = new DocumentHelper( m_orb, + UnoRuntime.queryInterface( XComponent.class, + subComponentCommands.execute( command, subComponentCommands.createCommandIdentifier(), null ) + ) + ); + FormLayer formLayer = new FormLayer( subDocument ); + XPropertySet controlModel = formLayer.insertControlLine( "DatabaseNumericField", "ID", "", 10 ); + formLayer.insertControlLine( "DatabaseTextField", "Name", "", 20 ); + formLayer.insertControlLine( "DatabaseTextField", "Description", "", 30 ); + + m_masterForm = (XPropertySet)dbfTools.getParent( controlModel, XPropertySet.class ); + m_masterForm.setPropertyValue( "Command", "categories" ); + m_masterForm.setPropertyValue( "CommandType", Integer.valueOf( CommandType.TABLE ) ); + + // create a detail form + m_detailForm = UnoRuntime.queryInterface( XPropertySet.class, subDocument.createSubForm( m_masterForm, "products" ) ); + m_detailForm.setPropertyValue( "Command", "SELECT \"ID\", \"Name\", \"CategoryID\" FROM \"products\"" ); + m_detailForm.setPropertyValue( "CommandType", Integer.valueOf( CommandType.COMMAND ) ); + m_detailForm.setPropertyValue( "MasterFields", new String[] { "ID" } ); + m_detailForm.setPropertyValue( "DetailFields", new String[] { "CategoryID" } ); + + // create a grid control in the detail form, with some columns + XPropertySet gridControlModel = formLayer.createControlAndShape( "GridControl", 20, 40, 130, 50, m_detailForm ); + gridControlModel.setPropertyValue( "Name", "product list" ); + XIndexContainer gridColumns = UnoRuntime.queryInterface( XIndexContainer.class, gridControlModel ); + impl_createGridColumn( gridColumns, "TextField", "ID" ); + XPropertySet nameColumn = impl_createGridColumn( gridColumns, "TextField", "Name" ); + nameColumn.setPropertyValue( "Width", Integer.valueOf( 600 ) ); // 6 cm + nameColumn.setPropertyValue( "DefaultText", "default text" ); + + // go live + m_masterResult = new ResultSet( m_masterForm ); + m_detailResult = new ResultSet( m_detailForm ); + + XLoadable loadDetail = UnoRuntime.queryInterface( XLoadable.class, m_detailForm ); + loadDetail.addLoadListener( this ); + + subDocument.getCurrentView().toggleFormDesignMode(); + impl_waitForLoadedEvent(); + + // now that we set up this, do the actual tests + // First, https://bz.apache.org/ooo/show_bug.cgi?id=105235 described the problem + // that default values in the sub form didn't work when the master form was navigated to a row + // for which no detail records were present, and the default of the column/control is the same + // as the last known value. + + // so, take the current value of the "Name" column, and set it as default value ... + String defaultValue = m_detailResult.getString( 2 ); + nameColumn.setPropertyValue( "DefaultText", defaultValue ); + // ... then move to the second main form row ... + m_masterResult.absolute( 2 ); + impl_waitForLoadedEvent(); + // ... which should result in an empty sub form ... + assure( "test precondition not met: The second master form record is expected to have no detail records, " + + "else the test becomes meaningless", impl_isNewRecord( m_detailForm ) ); + // ... and in the "Name" column having the proper text + String actualValue = (String)nameColumn.getPropertyValue( "Text" ); + assureEquals( "#i105235#: default value in sub form not working (not propagated to column model)", defaultValue, actualValue ); + // However, checking the column model's value alone is not enough - we need to ensure it is properly + // propagated to the control. + XGridFieldDataSupplier gridData = subDocument.getCurrentView().getControl( + gridControlModel, XGridFieldDataSupplier.class ); + actualValue = (String)(gridData.queryFieldData( 0, Type.STRING )[1]); + assureEquals( "#i105235#: default value in sub form not working (not propagated to column)", defaultValue, actualValue ); + } + finally + { + if ( subComponentCommands != null ) + { + XComponentSupplier componentSupplier = UnoRuntime.queryInterface( XComponentSupplier.class, subComponentCommands ); + XModifiable modifySubComponent = UnoRuntime.queryInterface( XModifiable.class, componentSupplier.getComponent() ); + modifySubComponent.setModified( false ); + Command command = new Command(); + command.Name = "close"; + subComponentCommands.execute( command, subComponentCommands.createCommandIdentifier(), null ); + } + + if ( database != null ) + database.saveAndClose(); + + impl_cleanUpStep(); + } + } + + /* ------------------------------------------------------------------ */ + private boolean impl_isNewRecord( final XPropertySet _rowSet ) + { + boolean isNew = false; + try + { + isNew = ((Boolean)_rowSet.getPropertyValue( "IsNew" )).booleanValue(); + } + catch ( Exception ex ) + { + failed( "obtaining the IsNew property failed" ); + } + return isNew; + } + + /* ------------------------------------------------------------------ */ + private XPropertySet impl_createGridColumn( final XIndexContainer _gridModel, final String _columnType, final String _boundField ) throws Exception + { + final XGridColumnFactory columnFactory = UnoRuntime.queryInterface( XGridColumnFactory.class, _gridModel ); + XPropertySet column = columnFactory.createColumn( _columnType ); + column.setPropertyValue( "DataField", _boundField ); + column.setPropertyValue( "Name", _boundField ); + column.setPropertyValue( "Label", _boundField ); + _gridModel.insertByIndex( _gridModel.getCount(), column ); + return column; + } + + private void impl_waitForLoadedEvent() + { + synchronized( m_waitForLoad ) + { + while ( !m_loaded ) + { + try { m_waitForLoad.wait(); } + catch( InterruptedException e ) { } + } + // reset the flag for the next time + m_loaded = false; + } + } + + /** assures that the (integer) values in the given columns of our master and detail forms are identical + */ + private void verifyColumnValueIdentity( final String masterColName, final String detailColName ) throws SQLException + { + XColumnLocate locateMasterCols = UnoRuntime.queryInterface( XColumnLocate.class, m_masterForm ); + XColumnLocate locateDetailCols = UnoRuntime.queryInterface( XColumnLocate.class, m_detailForm ); + + int masterValue = m_masterResult.getInt( locateMasterCols.findColumn( masterColName ) ); + int detailValue = m_detailResult.getInt( locateDetailCols.findColumn( detailColName ) ); + + assure( "values in linked column pair " + detailColName + "->" + masterColName + " (" + + detailValue + "->" + masterValue + ") do not match (master position: " + m_masterResult.getRow() + ")!", + masterValue == detailValue ); + } + + public void disposing(com.sun.star.lang.EventObject eventObject) + { + } + + public void loaded(com.sun.star.lang.EventObject eventObject) + { + synchronized( m_waitForLoad ) + { + m_loaded = true; + m_waitForLoad.notify(); + } + } + + public void reloaded(com.sun.star.lang.EventObject eventObject) + { + synchronized( m_waitForLoad ) + { + m_loaded = true; + m_waitForLoad.notify(); + } + } + + public void reloading(com.sun.star.lang.EventObject eventObject) + { + } + + public void unloaded(com.sun.star.lang.EventObject eventObject) + { + } + + public void unloading(com.sun.star.lang.EventObject eventObject) + { + } +} diff --git a/forms/qa/integration/forms/NumericValidator.java b/forms/qa/integration/forms/NumericValidator.java new file mode 100644 index 0000000000..06370cfb30 --- /dev/null +++ b/forms/qa/integration/forms/NumericValidator.java @@ -0,0 +1,68 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +public class NumericValidator extends integration.forms.ControlValidator +{ + + public String explainInvalid( Object Value ) + { + try + { + double value = ((Double)Value).doubleValue(); + if ( Double.compare( Double.NaN, value ) == 0 ) + return "This is NotANumber"; + if ( !isProperRange( value ) ) + return "The value must be between 0 and 100"; + if ( !isProperDigitCount( value ) ) + return "The value must have at most one decimal digit"; + } + catch( java.lang.Exception e ) + { + return "This is no valid number"; + } + return ""; + } + + public boolean isValid( Object Value ) + { + try + { + double value = ((Double)Value).doubleValue(); + if ( Double.compare( Double.NaN, value ) == 0 ) + return false; + if ( !isProperRange( value ) ) + return false; + return isProperDigitCount( value ); + } + catch( java.lang.Exception e ) + { + } + return false; + } + + private boolean isProperRange( double value) + { + return ( value >= 0 ) && ( value <= 100 ); + } + + private boolean isProperDigitCount( double value) + { + return ( Math.floor( value * 10 ) == value * 10 ); + } +} diff --git a/forms/qa/integration/forms/RadioButtons.java b/forms/qa/integration/forms/RadioButtons.java new file mode 100644 index 0000000000..4b6e5de6f3 --- /dev/null +++ b/forms/qa/integration/forms/RadioButtons.java @@ -0,0 +1,426 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +import com.sun.star.uno.*; +import com.sun.star.util.*; +import com.sun.star.lang.*; +import com.sun.star.container.*; +import com.sun.star.beans.*; +import com.sun.star.awt.XRadioButton; +import java.util.Arrays; + +public class RadioButtons extends complexlib.ComplexTestCase +{ + private DocumentHelper m_document; /// our current test document + private FormLayer m_formLayer; /// quick access to the form layer + private XMultiServiceFactory m_orb; /// our service factory + private XPropertySet m_primaryForm; /// the primary form, to be used in text documents and in the first page of spreadsheets + private XPropertySet m_secondaryForm; /// the secondary form, to be used in the second page of spreadsheets + + /* ------------------------------------------------------------------ */ + @Override + public String[] getTestMethodNames() + { + return new String[] { + "checkSingleButtons", + "checkThreeGroups", + "checkMultipleForms", + "checkCalcPageSwitch" + }; + } + + /* ------------------------------------------------------------------ */ + @Override + public String getTestObjectName() + { + return "Form Radio Buttons Test"; + } + + /* ------------------------------------------------------------------ */ + public void before() throws java.lang.Exception + { + m_orb = param.getMSF(); + } + + /* ------------------------------------------------------------------ */ + private XPropertySet insertRadio( int nXPos, int nYPos, String label, String name, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception + { + return insertRadio( nXPos, nYPos, label, name, refValue, null ); + } + + /* ------------------------------------------------------------------ */ + private XPropertySet insertRadio( int nXPos, int nYPos, String label, String name, String refValue, XPropertySet parentForm ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XIndexContainer parentContainer = dbfTools.queryIndexContainer( parentForm ); + XPropertySet xRadio = m_formLayer.createControlAndShape( "DatabaseRadioButton", nXPos, nYPos, 25, 6, parentContainer ); + xRadio.setPropertyValue( "Label", label ); + xRadio.setPropertyValue( "RefValue", refValue ); + xRadio.setPropertyValue( "Name", name ); + + if ( null == m_primaryForm ) + m_primaryForm = (XPropertySet)dbfTools.getParent( xRadio, XPropertySet.class ); + + return xRadio; + } + + /* ------------------------------------------------------------------ */ + /** this checks whether n groups of radio buttons, consisting of only one button each, + * behave properly + */ + public void checkSingleButtons() throws com.sun.star.uno.Exception, java.lang.Exception + { + prepareTestStep( false ); + + insertRadio( 20, 30, "group 1", "group 1", "" ); + insertRadio( 20, 38, "group 2", "group 2", "" ); + insertRadio( 20, 46, "group 3", "group 3", "" ); + insertRadio( 20, 54, "group 4", "group 4", "" ); + + // switch to alive mode + m_document.getCurrentView( ).toggleFormDesignMode( ); + + checkRadio( "group 1", "" ); + verifySingleRadios( 1, 0, 0, 0 ); + + checkRadio( "group 4", "" ); + verifySingleRadios( 1, 0, 0, 1 ); + + checkRadio( "group 2", "" ); + verifySingleRadios( 1, 1, 0, 1 ); + + checkRadio( "group 3", "" ); + verifySingleRadios( 1, 1, 1, 1 ); + + cleanupTestStep(); + } + + /* ------------------------------------------------------------------ */ + /** creates three groups of radio buttons in a sample document, and checks whether they're working + */ + public void checkThreeGroups( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + prepareTestStep( false ); + + insertRadio( 20, 30, "group 1 (a)", "group 1", "a" ); + insertRadio( 20, 38, "group 1 (b)", "group 1", "b" ); + + insertRadio( 20, 50, "group 2 (a)", "group 2", "a" ); + insertRadio( 20, 58, "group 2 (b)", "group 2", "b" ); + + insertRadio( 20, 70, "group 3 (a)", "group 3", "a" ); + insertRadio( 20, 78, "group 3 (b)", "group 3", "b" ); + + // switch to alive mode + m_document.getCurrentView( ).toggleFormDesignMode( ); + + // initially, after switching to alive mode, all buttons should be unchecked + verifySixPack( 0, 0, 0, 0, 0, 0 ); + + // check one button in every group + checkRadio( "group 1", "a" ); + checkRadio( "group 2", "b" ); + checkRadio( "group 3", "a" ); + // and verify that this worked + verifySixPack( 1, 0, 0, 1, 1, 0 ); + + // check all buttons which are currently unchecked + checkRadio( "group 1", "b" ); + checkRadio( "group 2", "a" ); + checkRadio( "group 3", "b" ); + // this should have reset the previous checks in the respective groups + verifySixPack( 0, 1, 1, 0, 0, 1 ); + + // and back to the previous check state + checkRadio( "group 1", "a" ); + checkRadio( "group 2", "b" ); + checkRadio( "group 3", "a" ); + verifySixPack( 1, 0, 0, 1, 1, 0 ); + + cleanupTestStep(); + } + + /* ------------------------------------------------------------------ */ + /** tests whether radio buttons which belong to different forms behave properly + */ + public void checkMultipleForms( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + prepareTestStep( false ); + + insertRadio( 20, 30, "group 1 (a)", "group 1", "a" ); + insertRadio( 20, 38, "group 1 (b)", "group 1", "b" ); + insertRadio( 20, 46, "group 1 (c)", "group 1", "c" ); + + m_secondaryForm = dbfTools.queryPropertySet( m_document.createSiblingForm( m_primaryForm, "secondary" ) ); + + insertRadio( 70, 30, "group 2 (a)", "group 2", "a", m_secondaryForm ); + insertRadio( 70, 38, "group 2 (b)", "group 2", "b", m_secondaryForm ); + insertRadio( 70, 46, "group 2 (c)", "group 2", "c", m_secondaryForm ); + + // switch to alive mode + m_document.getCurrentView( ).toggleFormDesignMode( ); + + // play around with different check states + checkRadio( "group 1", "b", m_primaryForm ); + checkRadio( "group 2", "c", m_secondaryForm ); + verifyTwoFormRadios( 0, 1, 0, 0, 0, 1 ); + + checkRadio( "group 1", "c", m_primaryForm ); + verifyTwoFormRadios( 0, 0, 1, 0, 0, 1 ); + + checkRadio( "group 2", "a", m_secondaryForm ); + verifyTwoFormRadios( 0, 0, 1, 1, 0, 0 ); + + checkRadio( "group 1", "a", m_primaryForm ); + verifyTwoFormRadios( 1, 0, 0, 1, 0, 0 ); + + checkRadio( "group 2", "b", m_secondaryForm ); + verifyTwoFormRadios( 1, 0, 0, 0, 1, 0 ); + + cleanupTestStep(); + } + + /* ------------------------------------------------------------------ */ + /** tests for a special bug which we once had, where radio buttons lost their state after + * switching spreadsheet pages + */ + public void checkCalcPageSwitch( ) throws com.sun.star.uno.Exception, java.lang.Exception + { + prepareTestStep( true ); + + m_formLayer.setInsertPage( 0 ); + insertRadio( 15, 20, "group 1 (a)", "group 1", "a" ); + insertRadio( 15, 26, "group 1 (b)", "group 1", "b" ); + + m_formLayer.setInsertPage( 1 ); + XPropertySet xRadio = insertRadio( 15, 20, "group 2 (a)", "group 2", "a" ); + insertRadio( 15, 26, "group 2 (b)", "group 2", "b" ); + m_secondaryForm = (XPropertySet)dbfTools.getParent( xRadio, XPropertySet.class ); + + // switch to alive mode + SpreadsheetView view = (SpreadsheetView)m_document.getCurrentView( ); + view.toggleFormDesignMode( ); + // and do initial checking + checkRadio( "group 1", "a", m_primaryForm ); + view.activateSheet( 1 ); + checkRadio( "group 2", "b", m_secondaryForm ); + + // see whether the check states on the first page survived the page switch + verifySheetRadios( 1, 0, 0, 1 ); + // switch back to the first sheet, and see whether the check states survived + view.activateSheet( 0 ); + verifySheetRadios( 1, 0, 0, 1 ); + // and for completely, check again after switching to third sheet and back to the first + view.activateSheet( 2 ); + view.activateSheet( 1 ); + verifySheetRadios( 1, 0, 0, 1 ); + + cleanupTestStep(); + } + + /* ------------------------------------------------------------------ */ + public void after() + { + closeDocument(); + } + + /* ------------------------------------------------------------------ */ + /** closes our document, if we have an open one + */ + private void closeDocument() + { + try + { + // close our document + if ( m_document != null ) + { + XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class, + m_document.getDocument() ); + closeDoc.close( true ); + } + } + catch ( com.sun.star.uno.Exception e ) + { + } + } + + /* ------------------------------------------------------------------ */ + private void prepareTestStep( boolean useSpreadsheetDocument ) throws com.sun.star.uno.Exception, java.lang.Exception + { + m_primaryForm = null; + + m_document = useSpreadsheetDocument ? new SpreadsheetDocument( m_orb ) : DocumentHelper.blankTextDocument( m_orb ); + m_formLayer = new FormLayer( m_document ); + } + + /* ------------------------------------------------------------------ */ + private void cleanupTestStep( ) + { + closeDocument(); + } + + /* ------------------------------------------------------------------ */ + /** checks or unchecks the radio button (in our primary form) with the given name and the given ref value + */ + private void checkRadio( String groupName, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception + { + checkRadio( groupName, refValue, m_primaryForm ); + } + + /* ------------------------------------------------------------------ */ + /** checks or unchecks the radio button with the given name and the given ref value + */ + private void checkRadio( String groupName, String refValue, XPropertySet form ) throws com.sun.star.uno.Exception, java.lang.Exception + { + XPropertySet xRadio = getRadioModel( groupName, refValue, form ); + + XRadioButton radioButton = UnoRuntime.queryInterface( + XRadioButton.class, m_document.getCurrentView().getControl( xRadio ) ); + radioButton.setState( true ); + } + + /* ------------------------------------------------------------------ */ + private XPropertySet getRadioModel( String name, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception + { + return getRadioModel( name, refValue, m_primaryForm ); + } + + /* ------------------------------------------------------------------ */ + private XPropertySet getRadioModel( String name, String refValue, XPropertySet form ) throws com.sun.star.uno.Exception, java.lang.Exception + { + return m_formLayer.getRadioModelByRefValue( form, name, refValue ); + } + + /* ------------------------------------------------------------------ */ + /** verifies a number of radio buttons for their states + */ + private boolean verifyRadios( XPropertySet[] radios, short[] expectedStates, String errorMessage ) throws com.sun.star.uno.Exception + { + short[] actualStates = new short[radios.length]; + + // collect all current states. This is just to be able to emit them, in case of a failure + for ( int i = 0; i m_listeners; + + /** Creates a new instance of TableCellTextBinding */ + public TableCellTextBinding( XCell cell ) + { + m_cellText = UnoRuntime.queryInterface( XTextRange.class, cell ); + + m_newCellText = ""; + m_listeners = new java.util.LinkedList(); + + start(); + } + + /** retrieves the list of data types which this binding can exchange + */ + public com.sun.star.uno.Type[] getSupportedValueTypes() + { + try + { + // well, only strings here ... + return new Type[] { + getStringType() + }; + } + catch( java.lang.Exception e ) + { + } + return new Type[] { }; + } + + /** retrieves the current value + */ + public Object getValue(com.sun.star.uno.Type type) throws com.sun.star.form.binding.IncompatibleTypesException + { + if ( !type.equals( getStringType() ) ) + throw new com.sun.star.form.binding.IncompatibleTypesException(); + + return m_cellText.getString(); + } + + /** sets a new value + */ + public void setValue(Object obj) throws com.sun.star.form.binding.IncompatibleTypesException + { + String text; + try + { + text = (String)obj; + } + catch( java.lang.ClassCastException e ) + { + throw new com.sun.star.form.binding.IncompatibleTypesException(); + } + // remember the new text + // and wake up the thread which is waiting for it + synchronized( m_writeSignal ) + { + m_newCellText = text; + m_haveNewCellText = true; + m_writeSignal.notify(); + } + } + + /** determines whether a given value type is supported + */ + public boolean supportsType(com.sun.star.uno.Type type) + { + return type.equals( getStringType() ); + } + + /** retrieves the UNO type for the string class + */ + private static final Type getStringType() + { + return new com.sun.star.uno.Type( String.class ); + } + + /** runs the thread + */ + @Override + public void run() + { + try + { + m_writeSignal = new Object(); + while ( true ) + { + // go sleep a while + synchronized( m_writeSignal ) + { + m_writeSignal.wait( 200 ); + // if there's new text in the control, propagate it to the cell + if ( m_haveNewCellText ) + { + m_cellText.setString( m_newCellText ); + m_lastKnownCellText = m_newCellText; + } + m_haveNewCellText = false; + } + + // if there's new text in the cell, propagate it to the control + String currentCellText = m_cellText.getString(); + if ( !currentCellText.equals( m_lastKnownCellText ) ) + { + m_lastKnownCellText = currentCellText; + // notify the modification + synchronized( m_listeners ) + { + com.sun.star.lang.EventObject eventSource = new com.sun.star.lang.EventObject( this ); + + java.util.Iterator loop = m_listeners.iterator(); + while ( loop.hasNext() ) + { + ((XModifyListener)loop.next()).modified( eventSource ); + } + } + } + } + } + catch( java.lang.Exception e ) + { + e.printStackTrace(System.err); + } + } + + public void addModifyListener(com.sun.star.util.XModifyListener xModifyListener) + { + synchronized( m_listeners ) + { + m_listeners.add( xModifyListener ); + } + } + + public void removeModifyListener(com.sun.star.util.XModifyListener xModifyListener) + { + synchronized( m_listeners ) + { + m_listeners.remove( xModifyListener ); + } + } + +} diff --git a/forms/qa/integration/forms/TestCase.java b/forms/qa/integration/forms/TestCase.java new file mode 100644 index 0000000000..cc1e6ec623 --- /dev/null +++ b/forms/qa/integration/forms/TestCase.java @@ -0,0 +1,150 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package integration.forms; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.util.XCloseable; +import com.sun.star.util.XModifiable; + +public abstract class TestCase extends complexlib.ComplexTestCase implements com.sun.star.lang.XEventListener +{ + protected XMultiServiceFactory m_orb; /// our service factory + private DocumentType m_documentType; /// the type of our document + protected DocumentHelper m_document; /// our current test document + protected FormLayer m_formLayer; + + /** Creates a new instance of TestCase */ + public TestCase( DocumentType docType ) + { + m_documentType = docType; + } + + /* ------------------------------------------------------------------ */ + @Override + public String getTestObjectName() + { + return this.getClass().getName(); + } + + /* ------------------------------------------------------------------ */ + public void before() throws java.lang.Exception + { + m_orb = param.getMSF(); + } + + /* ------------------------------------------------------------------ */ + public void after() throws java.lang.Exception + { + } + + /* ------------------------------------------------------------------ */ + /** closes our document, if we have an open one, via (simulated) user input + */ + protected void closeDocumentByUI() + { + try + { + if ( m_document != null ) + { + // first, set the document to "unmodified" + XModifiable docModify = m_document.query( XModifiable.class ); + docModify.setModified( false ); + + m_document.getCurrentView().dispatch( ".uno:CloseDoc" ); + + // CloseDoc is asynchronous, so wait until it's done - or 1 second, at most + synchronized ( this ) { wait( 1000 ); } + } + } + catch ( java.lang.Exception e ) + { + e.printStackTrace( System.err ); + } + } + + /* ------------------------------------------------------------------ */ + /** closes our document, if we have an open one + */ + protected void closeDocument() + { + try + { + // close our document + if ( m_document != null ) + { + XCloseable closeDoc = m_document.query( XCloseable.class ); + closeDoc.close( true ); + } + } + catch ( com.sun.star.uno.Exception e ) + { + e.printStackTrace( System.err ); + } + } + + /* ------------------------------------------------------------------ */ + /** prepares a new document to work with + */ + protected void prepareDocument() throws com.sun.star.uno.Exception, java.lang.Exception + { + m_document = DocumentHelper.blankDocument( m_orb, m_documentType ); + m_document.getDocument( ).addEventListener( this ); + m_formLayer = new FormLayer( m_document ); + } + + /* ------------------------------------------------------------------ */ + /* internal methods */ + /* ------------------------------------------------------------------ */ + /** waits for the user to press a key (on the console where she started the java program) + or the document to be closed by the user. + @return + if the user pressed a key on the console, if she closed the document + */ + protected boolean waitForUserInput() throws java.lang.Exception + { + synchronized (this) + { + integration.forms.WaitForInput aWait = new integration.forms.WaitForInput( this ); + aWait.start(); + wait(); + + // if the waiter thread is done, the user pressed enter + boolean bKeyPressed = aWait.isDone(); + if ( !bKeyPressed ) + aWait.interrupt(); + + return bKeyPressed; + } + } + + /* ------------------------------------------------------------------ */ + /* XEventListener overridables */ + /* ------------------------------------------------------------------ */ + public void disposing( com.sun.star.lang.EventObject eventObject ) + { + if ( m_document.getDocument().equals( eventObject.Source ) ) + { + // notify ourself that we can stop waiting for user input + synchronized (this) + { + notify(); + } + } + } +} diff --git a/forms/qa/integration/forms/TextValidator.java b/forms/qa/integration/forms/TextValidator.java new file mode 100644 index 0000000000..0ed59dd41c --- /dev/null +++ b/forms/qa/integration/forms/TextValidator.java @@ -0,0 +1,65 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +public class TextValidator extends integration.forms.ControlValidator +{ + + public String explainInvalid( Object Value ) + { + try + { + String value = (String)Value; + if ( containsZs( value ) ) + return "No Z's allowed here"; + if ( !isProperChunks( value ) ) + return "Need 3 * n characters"; + } + catch( java.lang.Exception e ) + { + return "ooops. Unknown error"; + } + return ""; + } + + public boolean isValid( Object Value ) + { + try + { + String value = (String)Value; + if ( containsZs( value ) ) + return false; + return isProperChunks( value ); + } + catch( java.lang.Exception e ) + { + } + return false; + } + + private boolean isProperChunks( String value ) + { + return ( value.length() % 3 ) == 0; + } + + private boolean containsZs( String value ) + { + return ( value.indexOf( 'Z' ) != -1 ) + || ( value.indexOf( 'z' ) != -1 ); + } +} diff --git a/forms/qa/integration/forms/TimeValidator.java b/forms/qa/integration/forms/TimeValidator.java new file mode 100644 index 0000000000..8ff19464ab --- /dev/null +++ b/forms/qa/integration/forms/TimeValidator.java @@ -0,0 +1,73 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +public class TimeValidator extends integration.forms.ControlValidator +{ + + public String explainInvalid( Object Value ) + { + try + { + if ( isVoid( Value ) ) + return "empty input"; + + com.sun.star.util.Time timeValue = (com.sun.star.util.Time)Value; + if ( isInvalidTime( timeValue ) ) + return "this is no valid time"; + if ( !isFullHour( timeValue ) ) + return "time must denote a full hour"; + } + catch( java.lang.Exception e ) + { + return "this is no valid time"; + } + return ""; + } + + public boolean isValid( Object Value ) + { + try + { + if ( isVoid( Value ) ) + return false; + + com.sun.star.util.Time timeValue = (com.sun.star.util.Time) + com.sun.star.uno.AnyConverter.toObject( + com.sun.star.util.Time.class, Value); + if ( isInvalidTime( timeValue ) ) + return false; + return isFullHour( timeValue ); + } + catch( java.lang.Exception e ) + { + e.printStackTrace( System.err ); + } + return false; + } + + private boolean isInvalidTime( com.sun.star.util.Time timeValue ) + { + return ( timeValue.Hours == -1 ) && ( timeValue.Minutes == -1 ) && ( timeValue.Seconds == -1 ) && ( timeValue.NanoSeconds == -1 ); + } + + private boolean isFullHour( com.sun.star.util.Time timeValue ) + { + return ( timeValue.Minutes == 0 ) && ( timeValue.Seconds == 0 ) && ( timeValue.NanoSeconds == 0 ); + } +} diff --git a/forms/qa/integration/forms/ValueBinding.java b/forms/qa/integration/forms/ValueBinding.java new file mode 100644 index 0000000000..3b6e66bdcc --- /dev/null +++ b/forms/qa/integration/forms/ValueBinding.java @@ -0,0 +1,112 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +import com.sun.star.uno.UnoRuntime; + +import com.sun.star.beans.XPropertySet; +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XText; +import com.sun.star.text.XTextTable; +import com.sun.star.text.XTextCursor; +import com.sun.star.form.binding.XValueBinding; +import com.sun.star.form.binding.XBindableValue; + +public class ValueBinding extends integration.forms.TestCase +{ + /** Creates a new instance of ValueBinding */ + public ValueBinding() + { + super( DocumentType.WRITER ); + } + + public static boolean isInteractiveTest() + { + return true; + } + + /* ------------------------------------------------------------------ */ + @Override + public String[] getTestMethodNames() + { + return new String[] { + "checkBindingProperties" + }; + } + + /* ------------------------------------------------------------------ */ + @Override + public String getTestObjectName() + { + return "Form Control Value Binding Test"; + } + + /* ------------------------------------------------------------------ */ + @Override + public void before() throws com.sun.star.uno.Exception, java.lang.Exception + { + super.before(); + prepareDocument(); + } + + /* ------------------------------------------------------------------ */ + @Override + public void after() throws com.sun.star.uno.Exception, java.lang.Exception + { + super.waitForUserInput(); + super.closeDocument(); + } + + /* ------------------------------------------------------------------ */ + public void checkBindingProperties() throws java.lang.Exception + { + } + + /* ------------------------------------------------------------------ */ + @Override + protected void prepareDocument() throws com.sun.star.uno.Exception, java.lang.Exception + { + super.prepareDocument(); + + // insert a table with exactly one cell. The content of this table will be synced with + // the content of a form control + XTextDocument textDoc = UnoRuntime.queryInterface( XTextDocument.class, m_document.getDocument() ); + XText documentText = textDoc.getText(); + XTextCursor textCursor = documentText.createTextCursor(); + + XTextTable table = UnoRuntime.queryInterface( XTextTable.class, + m_document.createInstance( "com.sun.star.text.TextTable" ) + ); + table.initialize( 1, 1 ); + documentText.insertTextContent( textCursor, table, false ); + + // insert our sample control + XPropertySet textControl = m_formLayer.insertControlLine( "DatabaseTextField", "Test", "", 10 ); + + // create a value binding for the first cell of the table + XValueBinding cellBinding = new TableCellTextBinding( table.getCellByName( "A1" ) ); + // and bind it to the control + XBindableValue bindable = UnoRuntime.queryInterface( + XBindableValue.class, textControl + ); + bindable.setValueBinding( cellBinding ); + + // toggle the view to alive mode + m_document.getCurrentView( ).toggleFormDesignMode( ); + } + } diff --git a/forms/qa/integration/forms/WaitForInput.java b/forms/qa/integration/forms/WaitForInput.java new file mode 100644 index 0000000000..c04c4dbe39 --- /dev/null +++ b/forms/qa/integration/forms/WaitForInput.java @@ -0,0 +1,58 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package integration.forms; + +class WaitForInput extends java.lang.Thread +{ + private final Object m_aToNotify; + private boolean m_bDone; + + public WaitForInput( Object aToNotify ) + { + m_aToNotify = aToNotify; + m_bDone = false; + } + + public boolean isDone() + { + return m_bDone; + } + + @Override + public void run() + { + try + { + System.out.println( "\npress enter to exit" ); + System.in.read(); + + m_bDone = true; + // notify that the user pressed the key + synchronized (m_aToNotify) + { + m_aToNotify.notify(); + } + } + catch( java.lang.Exception e ) + { + // not really interested in + System.err.println( e ); + } + } +} + diff --git a/forms/qa/integration/forms/XMLFormSettings.java b/forms/qa/integration/forms/XMLFormSettings.java new file mode 100644 index 0000000000..8b393d0943 --- /dev/null +++ b/forms/qa/integration/forms/XMLFormSettings.java @@ -0,0 +1,213 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package integration.forms; + +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.form.binding.IncompatibleTypesException; +import com.sun.star.form.binding.XBindableValue; +import com.sun.star.form.binding.XValueBinding; +import com.sun.star.frame.XStorable; +import com.sun.star.io.IOException; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.CloseVetoException; +import com.sun.star.util.XCloseable; +import com.sun.star.xml.dom.XNode; +import com.sun.star.xsd.DataTypeClass; +import java.io.File; +import org.openoffice.xforms.Instance; +import org.openoffice.xforms.Model; +import org.openoffice.xforms.XMLDocument; + +public class XMLFormSettings extends complexlib.ComplexTestCase +{ + private XMLDocument m_document; + private Model m_defaultModel; + private FormLayer m_formLayer; + private XPropertySet m_stringBinding; + private XPropertySet m_booleanBinding; + private XPropertySet m_dateBinding; + + /* ------------------------------------------------------------------ */ + @Override + public String[] getTestMethodNames() + { + return new String[] { + "checkExternalData" + }; + } + + /* ------------------------------------------------------------------ */ + @Override + public String getTestObjectName() + { + return "Form Control Spreadsheet Cell Binding Test"; + } + + /* ------------------------------------------------------------------ */ + public void before() throws java.lang.Exception + { + // create the document and assign related members + XMultiServiceFactory orb = param.getMSF(); + m_document = new XMLDocument( orb ); + m_formLayer = new FormLayer( m_document ); + + // create a simple structure in the DOM tree: an element with two attributes + String[] modelNames = m_document.getXFormModelNames(); + m_defaultModel = m_document.getXFormModel( modelNames[0] ); + final Instance defaultInstance = m_defaultModel.getDefaultInstance(); + // remove the default root node + defaultInstance.removeNode( "instanceData" ); + // create test structures + XNode stringElement = defaultInstance.createElement( "stringElement" ); + XNode booleanAttrib = defaultInstance.createAttribute( stringElement, "booleanAttribute", "true" ); + XNode dateAttrib = defaultInstance.createAttribute( stringElement, "dateAttribute" ); + + assure( "booleanAttrib's parent is wrong", + UnoRuntime.areSame( stringElement, booleanAttrib.getParentNode() ) ); + assure( "dateAttrib's parent is wrong", + UnoRuntime.areSame( stringElement, dateAttrib.getParentNode() ) ); + + // also create bindings for the element and its attributes, of the proper type + m_stringBinding = m_defaultModel.createBindingForNode( stringElement, DataTypeClass.STRING ); + m_booleanBinding = m_defaultModel.createBindingForNode( booleanAttrib, DataTypeClass.BOOLEAN ); + m_dateBinding = m_defaultModel.createBindingForNode( dateAttrib, DataTypeClass.DATE ); + + // TODO: set up the bindings so that the date bindings is relevant if and only if + // the boolean value is true + + // store the document + File tempFile = File.createTempFile( "xmlforms", ".odt" ); + tempFile.deleteOnExit(); + String fileURL = tempFile.toURI().toURL().toExternalForm(); + XStorable store = UnoRuntime.queryInterface( XStorable.class, + m_document.getDocument() ); + store.storeAsURL( fileURL, new PropertyValue[] {} ); + assure( "document still modified after saving it", !m_document.isModified() ); + } + + /* ------------------------------------------------------------------ */ + public void after() throws com.sun.star.uno.Exception, java.lang.Exception + { + impl_closeDocument(); + } + + /* ------------------------------------------------------------------ */ + private void impl_closeDocument() throws CloseVetoException + { + if ( m_document != null ) + { + XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class, + m_document.getDocument() ); + closeDoc.close( true ); + } + } + + /* ------------------------------------------------------------------ */ + private static void impl_bind( XPropertySet _control, XPropertySet _binding ) throws IncompatibleTypesException + { + XBindableValue bindableControl = UnoRuntime.queryInterface( + XBindableValue.class, _control ); + XValueBinding binding = UnoRuntime.queryInterface( + XValueBinding.class, _binding ); + bindableControl.setValueBinding( binding ); + } + + /* ------------------------------------------------------------------ */ + /** checks if master-detail relationships including multiple keys work + */ + public void checkExternalData() throws com.sun.star.uno.Exception, java.lang.Exception + { + // some controls + XPropertySet stringControl = m_formLayer.createLabeledControl( + "DatabaseTextField", "Task", 10, 10, 6 ); + impl_bind( stringControl, m_stringBinding ); + + XPropertySet booleanControl = m_formLayer.createControlAndShape( + "DatabaseCheckBox", 35, 18, 25, 6 ); + booleanControl.setPropertyValue( "Label", "has due date" ); + impl_bind( booleanControl, m_booleanBinding ); + + XPropertySet dateControl = m_formLayer.createControlAndShape( + "DatabaseDateField", 40, 26, 25, 6 ); + dateControl.setPropertyValue( "Dropdown", Boolean.TRUE ); + impl_bind( dateControl, m_dateBinding ); + + m_document.getCurrentView( ).toggleFormDesignMode( ); + + // ensure the model is set up as containing "document-internal" data + m_defaultModel.setIsDocumentInternalData( true ); + assure( "setting up the document to contain 'internal data' failed", + m_defaultModel.getIsDocumentInternalData() ); + impl_storeDocument(); + + // okay, here we go ... + // what this particular test is about is to check whether we can set up the model + // so that any changes to any controls bound to any data in this model actually marks + // the containing document as modified + m_formLayer.userTextInput( stringControl, "don't break this test" ); + assure( "model data changed, but document is not modified", + m_document.isModified() ); + + // TODO: do this with the other control/binding types, too + + // no the other way round: set up the model to contain "document-external" data + m_defaultModel.setIsDocumentInternalData( false ); + assure( "setting up the document to contain 'internal data' failed", + !m_defaultModel.getIsDocumentInternalData() ); + impl_storeDocument(); + + // and check that now, changes in the controls / model data are not reflected in + // document's modified state + m_formLayer.userTextInput( stringControl, "(or any other test, that is)" ); + assure( "model data changed, but document is modified", + !m_document.isModified() ); + + + // finally, check whether the flag survives loading and saving + Model internalDataModel = m_document.addXFormModel( "internalData" ); + internalDataModel.setIsDocumentInternalData( true ); + Model externalDataModel = m_document.addXFormModel( "externalData" ); + externalDataModel.setIsDocumentInternalData( false ); + + impl_storeDocument(); + m_document.reload(); + + internalDataModel = m_document.getXFormModel( "internalData" ); + externalDataModel = m_document.getXFormModel( "externalData" ); + + assure( "setting up a model to contain 'internal data' did not survive reloading", + internalDataModel.getIsDocumentInternalData() ); + assure( "setting up a model to contain 'external data' did not survive reloading", + !externalDataModel.getIsDocumentInternalData() ); + } + + /* ------------------------------------------------------------------ */ + /** stores our document + * @throws com.sun.star.io.IOException + */ + private void impl_storeDocument() throws IOException + { + XStorable store = UnoRuntime.queryInterface( XStorable.class, + m_document.getDocument() ); + store.store(); + assure( "document still modified after saving it", !m_document.isModified() ); + } +} diff --git a/forms/qa/integration/forms/dbfTools.java b/forms/qa/integration/forms/dbfTools.java new file mode 100644 index 0000000000..fe61c6ef48 --- /dev/null +++ b/forms/qa/integration/forms/dbfTools.java @@ -0,0 +1,73 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package integration.forms; + +import com.sun.star.uno.*; +import com.sun.star.lang.*; +import com.sun.star.beans.*; +import com.sun.star.container.*; + +/** provides global helpers +*/ +class dbfTools +{ + /* ------------------------------------------------------------------ */ + /** disposes the component given + */ + public static void disposeComponent( Object xComp ) throws java.lang.RuntimeException + { + XComponent xComponent = queryComponent( xComp ); + if ( null != xComponent ) + xComponent.dispose(); + } + + /* ------------------------------------------------------------------ */ + /** queries an object for the XPropertySet interface + */ + public static XPropertySet queryPropertySet( Object aComp ) + { + return UnoRuntime.queryInterface( XPropertySet.class, aComp ); + } + + /* ------------------------------------------------------------------ */ + /** queries an object for the XIndexContainer interface + */ + public static XIndexContainer queryIndexContainer( Object aComp ) + { + return UnoRuntime.queryInterface( XIndexContainer.class, aComp ); + } + + /* ------------------------------------------------------------------ */ + /** queries an object for the XComponent interface + */ + public static XComponent queryComponent( Object aComp ) + { + return UnoRuntime.queryInterface( XComponent.class, aComp ); + } + + /* ------------------------------------------------------------------ */ + /** retrieves the parent of the given object + */ + @SuppressWarnings("unchecked") + static Object getParent( Object aComponent, Class aInterfaceClass ) + { + XChild xAsChild = UnoRuntime.queryInterface( XChild.class, aComponent ); + return UnoRuntime.queryInterface( aInterfaceClass, xAsChild.getParent() ); + } +} diff --git a/forms/qa/org/openoffice/complex/forms/tools/ResultSet.java b/forms/qa/org/openoffice/complex/forms/tools/ResultSet.java new file mode 100644 index 0000000000..7f87db7453 --- /dev/null +++ b/forms/qa/org/openoffice/complex/forms/tools/ResultSet.java @@ -0,0 +1,273 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package org.openoffice.complex.forms.tools; + +import com.sun.star.container.XNameAccess; +import com.sun.star.io.XInputStream; +import com.sun.star.sdbc.SQLException; +import com.sun.star.sdbc.XArray; +import com.sun.star.sdbc.XBlob; +import com.sun.star.sdbc.XClob; +import com.sun.star.sdbc.XRef; +import com.sun.star.sdbc.XResultSet; +import com.sun.star.sdbc.XRow; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.Date; +import com.sun.star.util.DateTime; +import com.sun.star.util.Time; + +public class ResultSet implements XResultSet, XRow +{ + private final XResultSet m_resultSet; + private final XRow m_row; + + public ResultSet( final Object _resultSet ) + { + m_resultSet = UnoRuntime.queryInterface( XResultSet.class, _resultSet ); + m_row = UnoRuntime.queryInterface( XRow.class, _resultSet ); + } + + public + boolean next() throws SQLException + { + return m_resultSet.next(); + } + + public + boolean isBeforeFirst() throws SQLException + { + return m_resultSet.isBeforeFirst(); + } + + public + boolean isAfterLast() throws SQLException + { + return m_resultSet.isAfterLast(); + } + + public + boolean isFirst() throws SQLException + { + return m_resultSet.isFirst(); + } + + public + boolean isLast() throws SQLException + { + return m_resultSet.isLast(); + } + + public + void beforeFirst() throws SQLException + { + m_resultSet.beforeFirst(); + } + + public + void afterLast() throws SQLException + { + m_resultSet.afterLast(); + } + + public + boolean first() throws SQLException + { + return m_resultSet.first(); + } + + public + boolean last() throws SQLException + { + return m_resultSet.last(); + } + + public + int getRow() throws SQLException + { + return m_resultSet.getRow(); + } + + public + boolean absolute( int _row ) throws SQLException + { + return m_resultSet.absolute( _row ); + } + + public + boolean relative( int _offset ) throws SQLException + { + return m_resultSet.relative( _offset ); + } + + public + boolean previous() throws SQLException + { + return m_resultSet.previous(); + } + + public + void refreshRow() throws SQLException + { + m_resultSet.refreshRow(); + } + + public + boolean rowUpdated() throws SQLException + { + return m_resultSet.rowUpdated(); + } + + public + boolean rowInserted() throws SQLException + { + return m_resultSet.rowInserted(); + } + + public + boolean rowDeleted() throws SQLException + { + return m_resultSet.rowDeleted(); + } + + public + Object getStatement() throws SQLException + { + return m_resultSet.getStatement(); + } + + public + boolean wasNull() throws SQLException + { + return m_row.wasNull(); + } + + public + String getString( int _colIndex ) throws SQLException + { + return m_row.getString( _colIndex ); + } + + public + boolean getBoolean( int _colIndex ) throws SQLException + { + return m_row.getBoolean( _colIndex ); + } + + public + byte getByte( int _colIndex ) throws SQLException + { + return m_row.getByte( _colIndex ); + } + + public + short getShort( int _colIndex ) throws SQLException + { + return m_row.getShort( _colIndex ); + } + + public + int getInt( int _colIndex ) throws SQLException + { + return m_row.getInt( _colIndex ); + } + + public + long getLong( int _colIndex ) throws SQLException + { + return m_row.getLong( _colIndex ); + } + + public + float getFloat( int _colIndex ) throws SQLException + { + return m_row.getFloat( _colIndex ); + } + + public + double getDouble( int _colIndex ) throws SQLException + { + return m_row.getDouble( _colIndex ); + } + + public + byte[] getBytes( int _colIndex ) throws SQLException + { + return m_row.getBytes( _colIndex ); + } + + public + Date getDate( int _colIndex ) throws SQLException + { + return m_row.getDate( _colIndex ); + } + + public + Time getTime( int _colIndex ) throws SQLException + { + return m_row.getTime( _colIndex ); + } + + public + DateTime getTimestamp( int _colIndex ) throws SQLException + { + return m_row.getTimestamp( _colIndex ); + } + + public + XInputStream getBinaryStream( int _colIndex ) throws SQLException + { + return m_row.getBinaryStream( _colIndex ); + } + + public + XInputStream getCharacterStream( int _colIndex ) throws SQLException + { + return m_row.getCharacterStream( _colIndex ); + } + + public + Object getObject( int _colIndex, XNameAccess _typeMap ) throws SQLException + { + return m_row.getObject( _colIndex, _typeMap ); + } + + public + XRef getRef( int _colIndex ) throws SQLException + { + return m_row.getRef( _colIndex ); + } + + public + XBlob getBlob( int _colIndex ) throws SQLException + { + return m_row.getBlob( _colIndex ); + } + + public + XClob getClob( int _colIndex ) throws SQLException + { + return m_row.getClob( _colIndex ); + } + + public + XArray getArray( int _colIndex ) throws SQLException + { + return m_row.getArray( _colIndex ); + } +} diff --git a/forms/qa/org/openoffice/xforms/Instance.java b/forms/qa/org/openoffice/xforms/Instance.java new file mode 100644 index 0000000000..d98c647a95 --- /dev/null +++ b/forms/qa/org/openoffice/xforms/Instance.java @@ -0,0 +1,145 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package org.openoffice.xforms; + +import com.sun.star.xml.dom.DOMException; +import com.sun.star.xml.dom.XDocument; +import com.sun.star.xml.dom.XNode; +import com.sun.star.xml.dom.XNodeList; +import java.util.NoSuchElementException; + +public class Instance +{ + private final Model m_model; + private final XDocument m_domInstance; + + protected Instance( Model _model, XDocument _domInstance ) + { + m_model = _model; + m_domInstance = _domInstance; + } + + /** creates a new element in the instance + * + * The element will be inserted immediately below the root node of the instance. + * + * @param _elementName the name of the to-be-created element + * @return the node of the newly created element + */ + public XNode createElement( String _elementName ) throws DOMException + { + return createElement( m_domInstance, _elementName, null ); + } + + + + /** creates a new element in the instance + * + * The element will be inserted immediately below a given XNode. + * + * @param _parentElement + * the node whose child shall be created + * @param _elementName + * the name of the to-be-created element + * @param _initialNodeValue + * the initial value to set at the node. Might be null, in this case no value is set. + * @return + * the node of the newly created element + */ + private XNode createElement( XNode _parentElement, String _elementName, String _initialNodeValue ) throws DOMException + { + XNode node = _parentElement.appendChild( + m_model.getUIHelper().createElement( _parentElement, _elementName ) + ); + if ( _initialNodeValue != null ) + node.setNodeValue( _initialNodeValue ); + return node; + } + + /** removes a child of the root-level node from the instance + * + * @param _elementName + * the name of the to-be-removed child + */ + public XNode removeNode( String _elementName ) throws DOMException + { + return removeNode( m_domInstance, _elementName ); + } + + /** removes a node from the instance + * + * @param _parentElement + * the node whose child is to be removed + * @param _elementName + * the name of the to-be-removed child + */ + private XNode removeNode( XNode _parentElement, String _elementName ) throws DOMException + { + XNodeList nodes = _parentElement.getChildNodes(); + for ( int i=0; i + +namespace frm +{ +const sal_uInt16 ENTRY_NOT_FOUND = 0xFFFF; +const sal_uInt16 BOUNDCOLUMN = 0x0001; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/Button.cxx b/forms/source/component/Button.cxx new file mode 100644 index 0000000000..28cac777ea --- /dev/null +++ b/forms/source/component/Button.cxx @@ -0,0 +1,782 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "Button.hxx" +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace frm +{ + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using ::com::sun::star::frame::XDispatchProviderInterceptor; + + +//= OButtonModel + + +OButtonModel::OButtonModel(const Reference& _rxFactory) + :OClickableImageBaseModel( _rxFactory, VCL_CONTROLMODEL_COMMANDBUTTON, FRM_SUN_CONTROL_COMMANDBUTTON ) + // use the old control name for compatibility reasons + ,m_aResetHelper( *this, m_aMutex ) + ,m_eDefaultState( TRISTATE_FALSE ) +{ + m_nClassId = FormComponentType::COMMANDBUTTON; +} + + +Any SAL_CALL OButtonModel::queryAggregation( const Type& _type ) +{ + Any aReturn = OClickableImageBaseModel::queryAggregation( _type ); + if ( !aReturn.hasValue() ) + aReturn = OButtonModel_Base::queryInterface( _type ); + return aReturn; +} + + +Sequence< Type > OButtonModel::_getTypes() +{ + return ::comphelper::concatSequences( + OClickableImageBaseModel::_getTypes(), + OButtonModel_Base::getTypes() + ); +} + + +OButtonModel::OButtonModel( const OButtonModel* _pOriginal, const Reference& _rxFactory ) + :OClickableImageBaseModel( _pOriginal, _rxFactory ) + ,m_aResetHelper( *this, m_aMutex ) + ,m_eDefaultState( _pOriginal->m_eDefaultState ) +{ + m_nClassId = FormComponentType::COMMANDBUTTON; + + implInitializeImageURL(); +} + + +OButtonModel::~OButtonModel() +{ +} + + +void OButtonModel::describeFixedProperties( Sequence< Property >& _rProps ) const +{ + OClickableImageBaseModel::describeFixedProperties( _rProps ); + sal_Int32 nOldCount = _rProps.getLength(); + _rProps.realloc( nOldCount + 6); + css::beans::Property* pProperties = _rProps.getArray() + nOldCount; + *pProperties++ = css::beans::Property(PROPERTY_BUTTONTYPE, PROPERTY_ID_BUTTONTYPE, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_STATE, PROPERTY_ID_DEFAULT_STATE, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_DISPATCHURLINTERNAL, PROPERTY_ID_DISPATCHURLINTERNAL, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_TARGET_URL, PROPERTY_ID_TARGET_URL, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_TARGET_FRAME, PROPERTY_ID_TARGET_FRAME, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?"); +} + + +css::uno::Reference< css::util::XCloneable > SAL_CALL OButtonModel::createClone() +{ + rtl::Reference pClone = new OButtonModel(this, getContext()); + pClone->clonedFrom(this); + return pClone; +} + + +// XServiceInfo + +css::uno::Sequence OButtonModel::getSupportedServiceNames() +{ + css::uno::Sequence aSupported = OClickableImageBaseModel::getSupportedServiceNames(); + aSupported.realloc( aSupported.getLength() + 2 ); + + OUString* pArray = aSupported.getArray(); + pArray[ aSupported.getLength() - 2 ] = FRM_SUN_COMPONENT_COMMANDBUTTON; + pArray[ aSupported.getLength() - 1 ] = FRM_COMPONENT_COMMANDBUTTON; + + return aSupported; +} + + +OUString OButtonModel::getServiceName() +{ + return FRM_COMPONENT_COMMANDBUTTON; // old (non-sun) name for compatibility ! +} + + +void OButtonModel::write(const Reference& _rxOutStream) +{ + OClickableImageBaseModel::write(_rxOutStream); + + _rxOutStream->writeShort(0x0003); // Version + + { + OStreamSection aSection( _rxOutStream ); + // this will allow readers to skip unknown bytes in their dtor + + _rxOutStream->writeShort( static_cast(m_eButtonType) ); + + OUString sTmp = INetURLObject::decode( m_sTargetURL, INetURLObject::DecodeMechanism::Unambiguous); + _rxOutStream << sTmp; + _rxOutStream << m_sTargetFrame; + writeHelpTextCompatibly(_rxOutStream); + _rxOutStream << isDispatchUrlInternal(); + } +} + + +void OButtonModel::read(const Reference& _rxInStream) +{ + OClickableImageBaseModel::read(_rxInStream); + + sal_uInt16 nVersion = _rxInStream->readShort(); // Version + switch (nVersion) + { + case 0x0001: + { + m_eButtonType = static_cast(_rxInStream->readShort()); + + _rxInStream >> m_sTargetURL; + _rxInStream >> m_sTargetFrame; + } + break; + + case 0x0002: + { + m_eButtonType = static_cast(_rxInStream->readShort()); + + _rxInStream >> m_sTargetURL; + _rxInStream >> m_sTargetFrame; + readHelpTextCompatibly(_rxInStream); + } + break; + + case 0x0003: + { + OStreamSection aSection( _rxInStream ); + // this will skip any unknown bytes in its dtor + + // button type + m_eButtonType = static_cast(_rxInStream->readShort()); + + // URL + _rxInStream >> m_sTargetURL; + + // target frame + _rxInStream >> m_sTargetFrame; + + // help text + readHelpTextCompatibly(_rxInStream); + + // DispatchInternal + bool bDispatch; + _rxInStream >> bDispatch; + setDispatchUrlInternal(bDispatch); + } + break; + + default: + OSL_FAIL("OButtonModel::read : unknown version !"); + m_eButtonType = FormButtonType_PUSH; + m_sTargetURL.clear(); + m_sTargetFrame.clear(); + break; + } +} + + +void SAL_CALL OButtonModel::disposing() +{ + m_aResetHelper.disposing(); + OClickableImageBaseModel::disposing(); +} + + +void SAL_CALL OButtonModel::reset() +{ + if ( !m_aResetHelper.approveReset() ) + return; + + impl_resetNoBroadcast_nothrow(); + + m_aResetHelper.notifyResetted(); +} + + +void SAL_CALL OButtonModel::addResetListener( const Reference< XResetListener >& _listener ) +{ + m_aResetHelper.addResetListener( _listener ); +} + + +void SAL_CALL OButtonModel::removeResetListener( const Reference< XResetListener >& _listener ) +{ + m_aResetHelper.removeResetListener( _listener ); +} + + +void SAL_CALL OButtonModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const +{ + switch ( _nHandle ) + { + case PROPERTY_ID_DEFAULT_STATE: + _rValue <<= static_cast(m_eDefaultState); + break; + + default: + OClickableImageBaseModel::getFastPropertyValue( _rValue, _nHandle ); + break; + } +} + + +void SAL_CALL OButtonModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue ) +{ + switch ( _nHandle ) + { + case PROPERTY_ID_DEFAULT_STATE: + { + sal_Int16 nDefaultState = sal_Int16(TRISTATE_FALSE); + OSL_VERIFY( _rValue >>= nDefaultState ); + m_eDefaultState = static_cast(nDefaultState); + impl_resetNoBroadcast_nothrow(); + } + break; + + default: + OClickableImageBaseModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue ); + break; + } +} + + +sal_Bool SAL_CALL OButtonModel::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue ) +{ + bool bModified = false; + switch ( _nHandle ) + { + case PROPERTY_ID_DEFAULT_STATE: + bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, static_cast(m_eDefaultState) ); + break; + + default: + bModified = OClickableImageBaseModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue ); + break; + } + return bModified; +} + + +Any OButtonModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const +{ + Any aDefault; + switch ( _nHandle ) + { + case PROPERTY_ID_DEFAULT_STATE: + aDefault <<= sal_Int16(TRISTATE_FALSE); + break; + + default: + aDefault = OClickableImageBaseModel::getPropertyDefaultByHandle( _nHandle ); + break; + } + return aDefault; +} + + +void OButtonModel::impl_resetNoBroadcast_nothrow() +{ + try + { + setPropertyValue( PROPERTY_STATE, getPropertyValue( PROPERTY_DEFAULT_STATE ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("forms.component"); + } +} + + +// OButtonControl + + +Sequence OButtonControl::_getTypes() +{ + return ::comphelper::concatSequences( + OButtonControl_BASE::getTypes(), + OClickableImageBaseControl::_getTypes(), + OFormNavigationHelper::getTypes() + ); +} + + +css::uno::Sequence OButtonControl::getSupportedServiceNames() +{ + css::uno::Sequence aSupported = OClickableImageBaseControl::getSupportedServiceNames(); + aSupported.realloc(aSupported.getLength() + 2); + + OUString*pArray = aSupported.getArray(); + pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_COMMANDBUTTON; + pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_COMMANDBUTTON; + return aSupported; +} + + +OButtonControl::OButtonControl(const Reference& _rxFactory) + :OClickableImageBaseControl(_rxFactory, VCL_CONTROL_COMMANDBUTTON) + ,OFormNavigationHelper( _rxFactory ) + ,m_nClickEvent( nullptr ) + ,m_nTargetUrlFeatureId( -1 ) + ,m_bEnabledByPropertyValue( false ) +{ + osl_atomic_increment(&m_refCount); + { + // Register as ActionListener + Reference xButton; + query_aggregation( m_xAggregate, xButton); + if (xButton.is()) + xButton->addActionListener(this); + } + // For Listener: refcount at one + osl_atomic_decrement(&m_refCount); +} + + +OButtonControl::~OButtonControl() +{ + if (m_nClickEvent) + Application::RemoveUserEvent(m_nClickEvent); +} + +// UNO binding + +Any SAL_CALL OButtonControl::queryAggregation(const Type& _rType) +{ + // if asked for the XTypeProvider, don't let OButtonControl_BASE do this + Any aReturn; + if ( !_rType.equals( cppu::UnoType::get() ) ) + aReturn = OButtonControl_BASE::queryInterface( _rType ); + + if ( !aReturn.hasValue() ) + aReturn = OClickableImageBaseControl::queryAggregation( _rType ); + + if ( !aReturn.hasValue() ) + aReturn = OFormNavigationHelper::queryInterface( _rType ); + + return aReturn; +} + + +void SAL_CALL OButtonControl::disposing() +{ + startOrStopModelPropertyListening( false ); + + OClickableImageBaseControl::disposing(); + OFormNavigationHelper::dispose(); +} + + +void SAL_CALL OButtonControl::disposing( const EventObject& _rSource ) +{ + OControl::disposing( _rSource ); + OFormNavigationHelper::disposing( _rSource ); +} + +// ActionListener + +void OButtonControl::actionPerformed(const ActionEvent& /*rEvent*/) +{ + // Asynchronous for css::util::URL-Button + ImplSVEvent * n = Application::PostUserEvent( LINK(this, OButtonControl, OnClick) ); + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_nClickEvent = n; + } +} + + +IMPL_LINK_NOARG(OButtonControl, OnClick, void*, void) +{ + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + m_nClickEvent = nullptr; + + if (m_aApproveActionListeners.getLength()) + { + // if there are listeners, start the action in an own thread, to not allow + // them to block us here (we're in the application's main thread) + getImageProducerThread()->addEvent(); + } + else + { + // Else, don't. We then must not notify the Listeners in any case, + // not even if added later on. + aGuard.clear(); + + // recognize the button type + Reference xSet(getModel(), UNO_QUERY); + if (!xSet.is()) + return; + + if (FormButtonType_PUSH == *o3tl::doAccess(xSet->getPropertyValue(PROPERTY_BUTTONTYPE))) + { + // notify the action listeners for a push button + ::comphelper::OInterfaceIteratorHelper3 aIter(m_aActionListeners); + ActionEvent aEvt(static_cast(this), m_aActionCommand); + while(aIter.hasMoreElements() ) + { + // catch exceptions + // and catch them on a per-listener basis - if one listener fails, the others still need + // to get notified + try + { + aIter.next()->actionPerformed(aEvt); + } +#ifdef DBG_UTIL + catch( const RuntimeException& ) + { + // silence this + } +#endif + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "forms.component", "OButtonControl::OnClick: caught an exception other than RuntimeException!" ); + } + } + } + else + actionPerformed_Impl( false, css::awt::MouseEvent() ); + } +} + + +void OButtonControl::actionPerformed_Impl( bool _bNotifyListener, const css::awt::MouseEvent& _rEvt ) +{ + { + sal_Int16 nFeatureId = -1; + { + ::osl::MutexGuard aGuard( m_aMutex ); + nFeatureId = m_nTargetUrlFeatureId; + } + + if ( nFeatureId != -1 ) + { + if ( !approveAction() ) + return; + + SolarMutexGuard aGuard; + dispatch( nFeatureId ); + return; + } + } + + OClickableImageBaseControl::actionPerformed_Impl( _bNotifyListener, _rEvt ); +} + +// XButton + +void OButtonControl::setLabel(const OUString& Label) +{ + Reference xButton; + query_aggregation( m_xAggregate, xButton ); + if (xButton.is()) + xButton->setLabel(Label); +} + + +void SAL_CALL OButtonControl::setActionCommand(const OUString& _rCommand) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_aActionCommand = _rCommand; + } + + Reference xButton; + query_aggregation( m_xAggregate, xButton); + if (xButton.is()) + xButton->setActionCommand(_rCommand); +} + + +void SAL_CALL OButtonControl::addActionListener(const Reference& _rxListener) +{ + m_aActionListeners.addInterface(_rxListener); +} + + +void SAL_CALL OButtonControl::removeActionListener(const Reference& _rxListener) +{ + m_aActionListeners.removeInterface(_rxListener); +} + +namespace { + +class DoPropertyListening +{ +private: + Reference< XPropertySet > m_xProps; + Reference< XPropertyChangeListener > m_xListener; + bool m_bStartListening; + +public: + DoPropertyListening( + const Reference< XInterface >& _rxComponent, + const Reference< XPropertyChangeListener >& _rxListener, + bool _bStart + ); + + void handleListening( const OUString& _rPropertyName ); +}; + +} + +DoPropertyListening::DoPropertyListening( + const Reference< XInterface >& _rxComponent, const Reference< XPropertyChangeListener >& _rxListener, + bool _bStart ) + :m_xProps( _rxComponent, UNO_QUERY ) + ,m_xListener( _rxListener ) + ,m_bStartListening( _bStart ) +{ + DBG_ASSERT( m_xProps.is() || !_rxComponent.is(), "DoPropertyListening::DoPropertyListening: valid component, but no property set!" ); + DBG_ASSERT( m_xListener.is(), "DoPropertyListening::DoPropertyListening: invalid listener!" ); +} + + +void DoPropertyListening::handleListening( const OUString& _rPropertyName ) +{ + if ( m_xProps.is() ) + { + if ( m_bStartListening ) + m_xProps->addPropertyChangeListener( _rPropertyName, m_xListener ); + else + m_xProps->removePropertyChangeListener( _rPropertyName, m_xListener ); + } +} + + +void OButtonControl::startOrStopModelPropertyListening( bool _bStart ) +{ + DoPropertyListening aListeningHandler( getModel(), this, _bStart ); + aListeningHandler.handleListening( PROPERTY_TARGET_URL ); + aListeningHandler.handleListening( PROPERTY_BUTTONTYPE ); + aListeningHandler.handleListening( PROPERTY_ENABLED ); +} + + +sal_Bool SAL_CALL OButtonControl::setModel( const Reference< XControlModel >& _rxModel ) +{ + startOrStopModelPropertyListening( false ); + bool bResult = OClickableImageBaseControl::setModel( _rxModel ); + startOrStopModelPropertyListening( true ); + + m_bEnabledByPropertyValue = true; + Reference< XPropertySet > xModelProps( _rxModel, UNO_QUERY ); + if ( xModelProps.is() ) + xModelProps->getPropertyValue( PROPERTY_ENABLED ) >>= m_bEnabledByPropertyValue; + + modelFeatureUrlPotentiallyChanged( ); + + return bResult; +} + + +void OButtonControl::modelFeatureUrlPotentiallyChanged( ) +{ + sal_Int16 nOldUrlFeatureId = m_nTargetUrlFeatureId; + + // Do we have another TargetURL now? If so, we need to update our dispatches + m_nTargetUrlFeatureId = getModelUrlFeatureId( ); + if ( nOldUrlFeatureId != m_nTargetUrlFeatureId ) + { + invalidateSupportedFeaturesSet(); + if ( !isDesignMode() ) + updateDispatches( ); + } +} + + +void SAL_CALL OButtonControl::propertyChange( const PropertyChangeEvent& _rEvent ) +{ + if ( _rEvent.PropertyName == PROPERTY_TARGET_URL + || _rEvent.PropertyName == PROPERTY_BUTTONTYPE + ) + { + modelFeatureUrlPotentiallyChanged( ); + } + else if ( _rEvent.PropertyName == PROPERTY_ENABLED ) + { + _rEvent.NewValue >>= m_bEnabledByPropertyValue; + } +} + + +namespace +{ + bool isFormControllerURL( std::u16string_view _rURL ) + { + static constexpr std::u16string_view PREFIX = u".uno:FormController/"; + return ( _rURL.size() > PREFIX.size() ) + && ( o3tl::starts_with(_rURL, PREFIX ) ); + } +} + + +sal_Int16 OButtonControl::getModelUrlFeatureId( ) const +{ + sal_Int16 nFeatureId = -1; + + // some URL related properties of the model + OUString sUrl; + FormButtonType eButtonType = FormButtonType_PUSH; + + Reference< XPropertySet > xModelProps( const_cast< OButtonControl* >( this )->getModel(), UNO_QUERY ); + if ( xModelProps.is() ) + { + xModelProps->getPropertyValue( PROPERTY_TARGET_URL ) >>= sUrl; + xModelProps->getPropertyValue( PROPERTY_BUTTONTYPE ) >>= eButtonType; + } + + // are we a URL button? + if ( eButtonType == FormButtonType_URL ) + { + // is it a feature URL? + if ( isFormControllerURL( sUrl ) ) + { + nFeatureId = OFormNavigationMapper::getFeatureId( sUrl ); + } + } + + return nFeatureId; +} + + +void SAL_CALL OButtonControl::setDesignMode( sal_Bool _bOn ) +{ + OClickableImageBaseControl::setDesignMode( _bOn ); + + if ( _bOn ) + disconnectDispatchers(); + else + connectDispatchers(); + // this will connect if not already connected and just update else +} + + +void OButtonControl::getSupportedFeatures( ::std::vector< sal_Int16 >& /* [out] */ _rFeatureIds ) +{ + if ( -1 != m_nTargetUrlFeatureId ) + _rFeatureIds.push_back( m_nTargetUrlFeatureId ); +} + + +void OButtonControl::featureStateChanged( sal_Int16 _nFeatureId, bool _bEnabled ) +{ + if ( _nFeatureId == m_nTargetUrlFeatureId ) + { + // enable or disable our peer, according to the new state + Reference< XVclWindowPeer > xPeer( getPeer(), UNO_QUERY ); + if ( xPeer.is() ) + xPeer->setProperty( PROPERTY_ENABLED, Any( m_bEnabledByPropertyValue && _bEnabled ) ); + // if we're disabled according to our model's property, then + // we don't care for the feature state, but *are* disabled. + // If the model's property states that we're enabled, then we *do* + // care for the feature state + } + + // base class + OFormNavigationHelper::featureStateChanged( _nFeatureId, _bEnabled ); +} + + +void OButtonControl::allFeatureStatesChanged( ) +{ + if ( -1 != m_nTargetUrlFeatureId ) + // we have only one supported feature, so simulate it has changed ... + featureStateChanged( m_nTargetUrlFeatureId, isEnabled( m_nTargetUrlFeatureId ) ); + + // base class + OFormNavigationHelper::allFeatureStatesChanged( ); +} + + +bool OButtonControl::isEnabled( sal_Int16 _nFeatureId ) const +{ + if ( const_cast< OButtonControl* >( this )->isDesignMode() ) + // TODO: the model property? + return true; + + return OFormNavigationHelper::isEnabled( _nFeatureId ); +} + + +void SAL_CALL OButtonControl::registerDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor ) +{ + OClickableImageBaseControl::registerDispatchProviderInterceptor( _rxInterceptor ); + OFormNavigationHelper::registerDispatchProviderInterceptor( _rxInterceptor ); +} + + +void SAL_CALL OButtonControl::releaseDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor ) +{ + OClickableImageBaseControl::releaseDispatchProviderInterceptor( _rxInterceptor ); + OFormNavigationHelper::releaseDispatchProviderInterceptor( _rxInterceptor ); +} + +} // namespace frm + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_form_OButtonModel_get_implementation(css::uno::XComponentContext* component, + css::uno::Sequence const &) +{ + return cppu::acquire(new frm::OButtonModel(component)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_form_OButtonControl_get_implementation(css::uno::XComponentContext* component, + css::uno::Sequence const &) +{ + return cppu::acquire(new frm::OButtonControl(component)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/Button.hxx b/forms/source/component/Button.hxx new file mode 100644 index 0000000000..7ccaf7d8ad --- /dev/null +++ b/forms/source/component/Button.hxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "clickableimage.hxx" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct ImplSVEvent; + +namespace frm +{ + +typedef ::cppu::ImplHelper1 < css::form::XReset + > OButtonModel_Base; +class OButtonModel :public OClickableImageBaseModel + ,public OButtonModel_Base +{ +public: + OButtonModel( + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + OButtonModel( + const OButtonModel* _pOriginal, + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + virtual ~OButtonModel() override; + + // UNO + DECLARE_UNO3_AGG_DEFAULTS( OButtonModel, OClickableImageBaseModel ) + virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override; + + css::uno::Sequence< css::uno::Type> _getTypes() override; + +// css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.form.OButtonModel"; } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + +// css::io::XPersistObject + virtual OUString SAL_CALL getServiceName() override; + virtual void SAL_CALL write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override; + virtual void SAL_CALL read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override; + + // XReset + virtual void SAL_CALL reset( ) override; + virtual void SAL_CALL addResetListener( const css::uno::Reference< css::form::XResetListener >& aListener ) override; + virtual void SAL_CALL removeResetListener( const css::uno::Reference< css::form::XResetListener >& aListener ) override; + + // OControlModel's property handling + virtual void describeFixedProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps + ) const override; + + // XPropertySet and friends + virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle) const override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) 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 css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + +protected: + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + +private: + void impl_resetNoBroadcast_nothrow(); + + using ::cppu::OPropertySetHelper::getFastPropertyValue; + +private: + ResetHelper m_aResetHelper; + + // + ToggleState m_eDefaultState; // the default check state + // +protected: + using OClickableImageBaseModel::disposing; +}; + + +// OButtonControl + +typedef ::cppu::ImplHelper3 < css::awt::XButton + , css::awt::XActionListener + , css::beans::XPropertyChangeListener + > OButtonControl_BASE; + +class OButtonControl :public OButtonControl_BASE + ,public OClickableImageBaseControl + ,public OFormNavigationHelper +{ +private: + ImplSVEvent * m_nClickEvent; + sal_Int16 m_nTargetUrlFeatureId; + /// caches the value of the "Enabled" property of our model + bool m_bEnabledByPropertyValue; + +protected: + + // UNO binding + virtual css::uno::Sequence< css::uno::Type> _getTypes() override; + +public: + explicit OButtonControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory); + virtual ~OButtonControl() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.form.OButtonControl"; } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + + // UNO binding + DECLARE_UNO3_AGG_DEFAULTS(OButtonControl, OClickableImageBaseControl) + virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override; + + // XActionListener + virtual void SAL_CALL actionPerformed(const css::awt::ActionEvent& rEvent) override; + + // XButton + virtual void SAL_CALL addActionListener(const css::uno::Reference< css::awt::XActionListener>& _rxListener) override; + virtual void SAL_CALL removeActionListener(const css::uno::Reference< css::awt::XActionListener>& _rxListener) override; + virtual void SAL_CALL setLabel(const OUString& Label) override; + virtual void SAL_CALL setActionCommand(const OUString& _rCommand) override; + + // OComponentHelper + 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& _rSource) override; + + // XControl + virtual sal_Bool SAL_CALL setModel( const css::uno::Reference< css::awt::XControlModel >& _rxModel ) override; + void SAL_CALL setDesignMode(sal_Bool bOn) override; + +protected: + // OFormNavigationHelper overriables + virtual void getSupportedFeatures( ::std::vector< sal_Int16 >& /* [out] */ _rFeatureIds ) override; + virtual void featureStateChanged( sal_Int16 _nFeatureId, bool _bEnabled ) override; + virtual void allFeatureStatesChanged( ) override; + virtual bool isEnabled( sal_Int16 _nFeatureId ) const override; + + // XDispatchProviderInterception disambiguation + virtual void SAL_CALL registerDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& Interceptor ) override; + virtual void SAL_CALL releaseDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& Interceptor ) override; + + // OImageControl overridables + virtual void actionPerformed_Impl( bool bNotifyListener, const css::awt::MouseEvent& _rEvt ) override; + +private: + DECL_LINK( OnClick, void*, void ); + + /// to be called whenever the feature URL represented by our model has potentially changed + void modelFeatureUrlPotentiallyChanged( ); + + /// retrieves the feature id (see OFormNavigationHelper) of the TargetURL of the model. + sal_Int16 getModelUrlFeatureId( ) const; + + /// starts or stops listening for changes in model properties we're interested in + void startOrStopModelPropertyListening( bool _bStart ); +}; + + +} // namespace frm + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/CheckBox.cxx b/forms/source/component/CheckBox.cxx new file mode 100644 index 0000000000..fcfdabc53a --- /dev/null +++ b/forms/source/component/CheckBox.cxx @@ -0,0 +1,293 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "CheckBox.hxx" +#include +#include +#include +#include +#include +#include + +namespace frm +{ +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::form::binding; + +OCheckBoxControl::OCheckBoxControl(const Reference& _rxFactory) + :OBoundControl(_rxFactory, VCL_CONTROL_CHECKBOX) +{ +} + + +css::uno::Sequence SAL_CALL OCheckBoxControl::getSupportedServiceNames() +{ + css::uno::Sequence aSupported = OBoundControl::getSupportedServiceNames(); + aSupported.realloc(aSupported.getLength() + 2); + + OUString* pArray = aSupported.getArray(); + pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_CHECKBOX; + pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_CHECKBOX; + return aSupported; +} + + +//= OCheckBoxModel + +OCheckBoxModel::OCheckBoxModel(const Reference& _rxFactory) + :OReferenceValueComponent( _rxFactory, VCL_CONTROLMODEL_CHECKBOX, FRM_SUN_CONTROL_CHECKBOX ) + // use the old control name for compytibility reasons +{ + + m_nClassId = FormComponentType::CHECKBOX; + initValueProperty( PROPERTY_STATE, PROPERTY_ID_STATE ); +} + + +OCheckBoxModel::OCheckBoxModel( const OCheckBoxModel* _pOriginal, const Reference& _rxFactory ) + :OReferenceValueComponent( _pOriginal, _rxFactory ) +{ +} + + +OCheckBoxModel::~OCheckBoxModel() +{ +} + + +css::uno::Reference< css::util::XCloneable > SAL_CALL OCheckBoxModel::createClone() +{ + rtl::Reference pClone = new OCheckBoxModel(this, getContext()); + pClone->clonedFrom(this); + return pClone; +} + + +// XServiceInfo + +css::uno::Sequence SAL_CALL OCheckBoxModel::getSupportedServiceNames() +{ + css::uno::Sequence aSupported = OReferenceValueComponent::getSupportedServiceNames(); + + sal_Int32 nOldLen = aSupported.getLength(); + aSupported.realloc( nOldLen + 9 ); + OUString* pStoreTo = aSupported.getArray() + nOldLen; + + *pStoreTo++ = BINDABLE_CONTROL_MODEL; + *pStoreTo++ = DATA_AWARE_CONTROL_MODEL; + *pStoreTo++ = VALIDATABLE_CONTROL_MODEL; + + *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL; + *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL; + + *pStoreTo++ = FRM_SUN_COMPONENT_CHECKBOX; + *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_CHECKBOX; + *pStoreTo++ = BINDABLE_DATABASE_CHECK_BOX; + + *pStoreTo++ = FRM_COMPONENT_CHECKBOX; + + return aSupported; +} + + +void OCheckBoxModel::describeFixedProperties( Sequence< Property >& _rProps ) const +{ + OReferenceValueComponent::describeFixedProperties( _rProps ); + sal_Int32 nOldCount = _rProps.getLength(); + _rProps.realloc( nOldCount + 1); + css::beans::Property* pProperties = _rProps.getArray() + nOldCount; + *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?"); +} + + +OUString SAL_CALL OCheckBoxModel::getServiceName() +{ + return FRM_COMPONENT_CHECKBOX; // old (non-sun) name for compatibility ! +} + + +void SAL_CALL OCheckBoxModel::write(const Reference& _rxOutStream) +{ + OReferenceValueComponent::write(_rxOutStream); + + // Version + _rxOutStream->writeShort(0x0003); + // Properties + _rxOutStream << getReferenceValue(); + _rxOutStream << static_cast(getDefaultChecked()); + writeHelpTextCompatibly(_rxOutStream); + // from version 0x0003 : common properties + writeCommonProperties(_rxOutStream); +} + + +void SAL_CALL OCheckBoxModel::read(const Reference& _rxInStream) +{ + OReferenceValueComponent::read(_rxInStream); + osl::MutexGuard aGuard(m_aMutex); + + // Version + sal_uInt16 nVersion = _rxInStream->readShort(); + + OUString sReferenceValue; + sal_Int16 nDefaultChecked( 0 ); + switch ( nVersion ) + { + case 0x0001: + _rxInStream >> sReferenceValue; + nDefaultChecked = _rxInStream->readShort(); + break; + case 0x0002: + _rxInStream >> sReferenceValue; + _rxInStream >> nDefaultChecked; + readHelpTextCompatibly( _rxInStream ); + break; + case 0x0003: + _rxInStream >> sReferenceValue; + _rxInStream >> nDefaultChecked; + readHelpTextCompatibly(_rxInStream); + readCommonProperties(_rxInStream); + break; + default: + OSL_FAIL("OCheckBoxModel::read : unknown version !"); + defaultCommonProperties(); + break; + } + setReferenceValue( sReferenceValue ); + setDefaultChecked( static_cast< ToggleState >( nDefaultChecked ) ); + + // After reading in, display the default values + if ( !getControlSource().isEmpty() ) + // (not if we don't have a control source - the "State" property acts like it is persistent, then + resetNoBroadcast(); +} + +bool OCheckBoxModel::DbUseBool() +{ + return getReferenceValue().isEmpty() && getNoCheckReferenceValue().isEmpty(); +} + + +Any OCheckBoxModel::translateDbColumnToControlValue() +{ + Any aValue; + + + // Set value in ControlModel + bool bValue = bool(); // avoid warning + if(DbUseBool()) + { + bValue = m_xColumn->getBoolean(); + } + else + { + const OUString sVal(m_xColumn->getString()); + if (sVal == getReferenceValue()) + bValue = true; + else if (sVal == getNoCheckReferenceValue()) + bValue = false; + else + aValue <<= static_cast(getDefaultChecked()); + } + if ( m_xColumn->wasNull() ) + { + bool bTriState = true; + if ( m_xAggregateSet.is() ) + m_xAggregateSet->getPropertyValue( PROPERTY_TRISTATE ) >>= bTriState; + aValue <<= static_cast( bTriState ? TRISTATE_INDET : getDefaultChecked() ); + } + else if ( !aValue.hasValue() ) + { + // Since above either bValue is initialised, either aValue.hasValue(), + // bValue cannot be used uninitialised here. + // But GCC does not see/understand that, which breaks -Werror builds, + // so we explicitly default-initialise it. + aValue <<= static_cast( bValue ? TRISTATE_TRUE : TRISTATE_FALSE ); + } + + return aValue; +} + + +bool OCheckBoxModel::commitControlValueToDbColumn( bool /*_bPostReset*/ ) +{ + OSL_PRECOND( m_xColumnUpdate.is(), "OCheckBoxModel::commitControlValueToDbColumn: not bound!" ); + if ( !m_xColumnUpdate ) + return true; + + Any aControlValue( m_xAggregateSet->getPropertyValue( PROPERTY_STATE ) ); + try + { + sal_Int16 nValue = TRISTATE_INDET; + aControlValue >>= nValue; + switch (nValue) + { + case TRISTATE_INDET: + m_xColumnUpdate->updateNull(); + break; + case TRISTATE_TRUE: + if (DbUseBool()) + m_xColumnUpdate->updateBoolean( true ); + else + m_xColumnUpdate->updateString( getReferenceValue() ); + break; + case TRISTATE_FALSE: + if (DbUseBool()) + m_xColumnUpdate->updateBoolean( false ); + else + m_xColumnUpdate->updateString( getNoCheckReferenceValue() ); + break; + default: + OSL_FAIL("OCheckBoxModel::commitControlValueToDbColumn: invalid value !"); + } + } + catch(const Exception&) + { + OSL_FAIL("OCheckBoxModel::commitControlValueToDbColumn: could not commit !"); + } + return true; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_form_OCheckBoxModel_get_implementation(css::uno::XComponentContext* component, + css::uno::Sequence const &) +{ + return cppu::acquire(new frm::OCheckBoxModel(component)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_form_OCheckBoxControl_get_implementation(css::uno::XComponentContext* component, + css::uno::Sequence const &) +{ + return cppu::acquire(new frm::OCheckBoxControl(component)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/CheckBox.hxx b/forms/source/component/CheckBox.hxx new file mode 100644 index 0000000000..8023e7446c --- /dev/null +++ b/forms/source/component/CheckBox.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 "refvaluecomponent.hxx" + + +namespace frm +{ + +class OCheckBoxModel final : public OReferenceValueComponent +{ + bool DbUseBool(); + +public: + OCheckBoxModel( + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + OCheckBoxModel( + const OCheckBoxModel* _pOriginal, + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + virtual ~OCheckBoxModel() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.form.OCheckBoxModel"; } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + + // XPersistObject + virtual OUString SAL_CALL getServiceName() override; + virtual void SAL_CALL + write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override; + virtual void SAL_CALL + read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override; + + // OControlModel's property handling + virtual void describeFixedProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps + ) const override; + +private: + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + // OBoundControlModel overridables + virtual css::uno::Any translateDbColumnToControlValue( ) override; + virtual bool commitControlValueToDbColumn( bool _bPostReset ) override; +}; + +class OCheckBoxControl : public OBoundControl +{ +public: + explicit OCheckBoxControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.form.OCheckBoxControl"; } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; +}; + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/Columns.cxx b/forms/source/component/Columns.cxx new file mode 100644 index 0000000000..56be293fc5 --- /dev/null +++ b/forms/source/component/Columns.cxx @@ -0,0 +1,901 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "Columns.hxx" +#include +#include +#include "findpos.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace frm +{ + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::form::binding; + +const sal_uInt16 WIDTH = 0x0001; +const sal_uInt16 ALIGN = 0x0002; +const sal_uInt16 OLD_HIDDEN = 0x0004; +const sal_uInt16 COMPATIBLE_HIDDEN = 0x0008; + + +const css::uno::Sequence& getColumnTypes() +{ + static css::uno::Sequence aColumnTypes = []() + { + css::uno::Sequence tmp(10); + OUString* pNames = tmp.getArray(); + pNames[TYPE_CHECKBOX] = "CheckBox"; + pNames[TYPE_COMBOBOX] = "ComboBox"; + pNames[TYPE_CURRENCYFIELD] = "CurrencyField"; + pNames[TYPE_DATEFIELD] = "DateField"; + pNames[TYPE_FORMATTEDFIELD] = "FormattedField"; + pNames[TYPE_LISTBOX] = "ListBox"; + pNames[TYPE_NUMERICFIELD] = "NumericField"; + pNames[TYPE_PATTERNFIELD] = "PatternField"; + pNames[TYPE_TEXTFIELD] = "TextField"; + pNames[TYPE_TIMEFIELD] = "TimeField"; + return tmp; + }(); + return aColumnTypes; +} + + +sal_Int32 getColumnTypeByModelName(const OUString& aModelName) +{ + static constexpr OUString aModelPrefix (u"com.sun.star.form.component."_ustr); + static constexpr OUString aCompatibleModelPrefix (u"stardiv.one.form.component."_ustr); + + sal_Int32 nTypeId = -1; + if (aModelName == FRM_COMPONENT_EDIT) + nTypeId = TYPE_TEXTFIELD; + else + { + sal_Int32 nPrefixPos = aModelName.indexOf(aModelPrefix); +#ifdef DBG_UTIL + sal_Int32 nCompatiblePrefixPos = aModelName.indexOf(aCompatibleModelPrefix); + DBG_ASSERT( (nPrefixPos != -1) || (nCompatiblePrefixPos != -1), + "::getColumnTypeByModelName() : wrong service!"); +#endif + + OUString aColumnType = (nPrefixPos != -1) + ? aModelName.copy(aModelPrefix.getLength()) + : aModelName.copy(aCompatibleModelPrefix.getLength()); + + const css::uno::Sequence& rColumnTypes = getColumnTypes(); + nTypeId = ::detail::findPos(aColumnType, rColumnTypes); + } + return nTypeId; +} + +const Sequence& OGridColumn::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theOGridColumnImplementationId; + return theOGridColumnImplementationId.getSeq(); +} + + +sal_Int64 SAL_CALL OGridColumn::getSomething( const Sequence& _rIdentifier) +{ + sal_Int64 nReturn(0); + + if ( comphelper::isUnoTunnelId(_rIdentifier) ) + { + nReturn = comphelper::getSomething_cast(this); + } + else + { + Reference< XUnoTunnel > xAggTunnel; + if ( query_aggregation( m_xAggregate, xAggTunnel ) ) + return xAggTunnel->getSomething( _rIdentifier ); + } + return nReturn; +} + + +Sequence SAL_CALL OGridColumn::getImplementationId() +{ + return css::uno::Sequence(); +} + + +Sequence SAL_CALL OGridColumn::getTypes() +{ + TypeBag aTypes( OGridColumn_BASE::getTypes() ); + // erase the types which we do not support + aTypes.removeType( cppu::UnoType::get() ); + aTypes.removeType( cppu::UnoType::get() ); + aTypes.removeType( cppu::UnoType::get() ); + aTypes.removeType( cppu::UnoType::get() ); + + // but re-add their base class(es) + aTypes.addType( cppu::UnoType::get() ); + + Reference< XTypeProvider > xProv; + if ( query_aggregation( m_xAggregate, xProv )) + aTypes.addTypes( xProv->getTypes() ); + + aTypes.removeType( cppu::UnoType::get() ); + aTypes.removeType( cppu::UnoType::get() ); + aTypes.removeType( cppu::UnoType::get() ); + + return aTypes.getTypes(); +} + + +Any SAL_CALL OGridColumn::queryAggregation( const Type& _rType ) +{ + Any aReturn; + // some functionality at our aggregate cannot be reasonably fulfilled here. + if ( _rType.equals(cppu::UnoType::get()) + || _rType.equals(cppu::UnoType::get()) + || _rType.equals(cppu::UnoType::get()) + || _rType.equals(cppu::UnoType::get()) + || comphelper::isAssignableFrom(cppu::UnoType::get(),_rType) + ) + return aReturn; + + aReturn = OGridColumn_BASE::queryAggregation(_rType); + if (!aReturn.hasValue()) + { + aReturn = OPropertySetAggregationHelper::queryInterface(_rType); + if (!aReturn.hasValue() && m_xAggregate.is()) + aReturn = m_xAggregate->queryAggregation(_rType); + } + + return aReturn; +} + + +OGridColumn::OGridColumn( const Reference& _rContext, OUString _sModelName ) + :OGridColumn_BASE(m_aMutex) + ,OPropertySetAggregationHelper(OGridColumn_BASE::rBHelper) + ,m_aHidden( Any( false ) ) + ,m_aModelName(std::move(_sModelName)) +{ + + // Create the UnoControlModel + if ( m_aModelName.isEmpty() ) // is there a to-be-aggregated model? + return; + + osl_atomic_increment( &m_refCount ); + + { + m_xAggregate.set( _rContext->getServiceManager()->createInstanceWithContext( m_aModelName, _rContext ), UNO_QUERY ); + setAggregation( m_xAggregate ); + } + + if ( m_xAggregate.is() ) + { // don't omit those brackets - they ensure that the following temporary is properly deleted + m_xAggregate->setDelegator( static_cast< ::cppu::OWeakObject* >( this ) ); + } + + // Set refcount back to zero + osl_atomic_decrement( &m_refCount ); +} + + +OGridColumn::OGridColumn( const OGridColumn* _pOriginal ) + :OGridColumn_BASE( m_aMutex ) + ,OPropertySetAggregationHelper( OGridColumn_BASE::rBHelper ) +{ + + m_aWidth = _pOriginal->m_aWidth; + m_aAlign = _pOriginal->m_aAlign; + m_aHidden = _pOriginal->m_aHidden; + m_aModelName = _pOriginal->m_aModelName; + m_aLabel = _pOriginal->m_aLabel; + + osl_atomic_increment( &m_refCount ); + { + { + m_xAggregate = createAggregateClone( _pOriginal ); + setAggregation( m_xAggregate ); + } + + if ( m_xAggregate.is() ) + { // don't omit this brackets - they ensure that the following temporary is properly deleted + m_xAggregate->setDelegator( static_cast< ::cppu::OWeakObject* >( this ) ); + } + } + osl_atomic_decrement( &m_refCount ); +} + + +OGridColumn::~OGridColumn() +{ + if (!OGridColumn_BASE::rBHelper.bDisposed) + { + acquire(); + dispose(); + } + + // Free the aggregate + if (m_xAggregate.is()) + { + css::uno::Reference xIface; + m_xAggregate->setDelegator(xIface); + } + +} + +// XEventListener + +void SAL_CALL OGridColumn::disposing(const EventObject& _rSource) +{ + OPropertySetAggregationHelper::disposing(_rSource); + + Reference xEvtLstner; + if (query_aggregation(m_xAggregate, xEvtLstner)) + xEvtLstner->disposing(_rSource); +} + +// OGridColumn_BASE + +void OGridColumn::disposing() +{ + OGridColumn_BASE::disposing(); + OPropertySetAggregationHelper::disposing(); + + Reference xComp; + if (query_aggregation(m_xAggregate, xComp)) + xComp->dispose(); +} + + +void OGridColumn::clearAggregateProperties( Sequence< Property >& _rProps, bool bAllowDropDown ) +{ + // some properties are not to be exposed to the outer world + static const o3tl::sorted_vector< OUString > aForbiddenProperties { + PROPERTY_ALIGN, + PROPERTY_AUTOCOMPLETE, + PROPERTY_BACKGROUNDCOLOR, + PROPERTY_BORDER, + PROPERTY_BORDERCOLOR, + PROPERTY_ECHO_CHAR, + PROPERTY_FILLCOLOR, + PROPERTY_FONT, + PROPERTY_FONT_NAME, + PROPERTY_FONT_STYLENAME, + PROPERTY_FONT_FAMILY, + PROPERTY_FONT_CHARSET, + PROPERTY_FONT_HEIGHT, + PROPERTY_FONT_WEIGHT, + PROPERTY_FONT_SLANT, + PROPERTY_FONT_UNDERLINE, + PROPERTY_FONT_STRIKEOUT, + PROPERTY_FONT_WORDLINEMODE, + PROPERTY_TEXTLINECOLOR, + PROPERTY_FONTEMPHASISMARK, + PROPERTY_FONTRELIEF, + PROPERTY_HARDLINEBREAKS, + PROPERTY_HSCROLL, + PROPERTY_LABEL, + PROPERTY_LINECOLOR, + PROPERTY_MULTISELECTION, + PROPERTY_PRINTABLE, + PROPERTY_TABINDEX, + PROPERTY_TABSTOP, + PROPERTY_TEXTCOLOR, + PROPERTY_VSCROLL, + PROPERTY_CONTROLLABEL, + PROPERTY_RICH_TEXT, + PROPERTY_VERTICAL_ALIGN, + PROPERTY_IMAGE_URL, + PROPERTY_IMAGE_POSITION, + PROPERTY_ENABLEVISIBLE + }; + + Sequence< Property > aNewProps( _rProps.getLength() ); + Property* pNewProps = aNewProps.getArray(); + + const Property* pProps = _rProps.getConstArray(); + const Property* pPropsEnd = pProps + _rProps.getLength(); + for ( ; pProps != pPropsEnd; ++pProps ) + { + if ( aForbiddenProperties.find( pProps->Name ) == aForbiddenProperties.end() + && (bAllowDropDown || pProps->Name != PROPERTY_DROPDOWN)) + *pNewProps++ = *pProps; + } + + aNewProps.realloc( pNewProps - aNewProps.getArray() ); + _rProps = aNewProps; +} + + +void OGridColumn::setOwnProperties(Sequence& aDescriptor) +{ + aDescriptor.realloc(5); + Property* pProperties = aDescriptor.getArray(); + *pProperties++ = css::beans::Property(PROPERTY_LABEL, PROPERTY_ID_LABEL, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_WIDTH, PROPERTY_ID_WIDTH, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT); + *pProperties++ = css::beans::Property(PROPERTY_ALIGN, PROPERTY_ID_ALIGN, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT); + *pProperties++ = css::beans::Property(PROPERTY_HIDDEN, PROPERTY_ID_HIDDEN, cppu::UnoType::get(), + css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT); + *pProperties++ = css::beans::Property(PROPERTY_COLUMNSERVICENAME, PROPERTY_ID_COLUMNSERVICENAME, cppu::UnoType::get(), css::beans::PropertyAttribute::READONLY); +} + +// Reference + +void OGridColumn::getFastPropertyValue(Any& rValue, sal_Int32 nHandle ) const +{ + switch (nHandle) + { + case PROPERTY_ID_COLUMNSERVICENAME: + rValue <<= m_aModelName; + break; + case PROPERTY_ID_LABEL: + rValue <<= m_aLabel; + break; + case PROPERTY_ID_WIDTH: + rValue = m_aWidth; + break; + case PROPERTY_ID_ALIGN: + rValue = m_aAlign; + break; + case PROPERTY_ID_HIDDEN: + rValue = m_aHidden; + break; + default: + OPropertySetAggregationHelper::getFastPropertyValue(rValue, nHandle); + } +} + + +sal_Bool OGridColumn::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue, + sal_Int32 nHandle, const Any& rValue ) +{ + bool bModified(false); + switch (nHandle) + { + case PROPERTY_ID_LABEL: + bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aLabel); + break; + case PROPERTY_ID_WIDTH: + bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aWidth, cppu::UnoType::get()); + break; + case PROPERTY_ID_ALIGN: + bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aAlign, cppu::UnoType::get()); + // strange enough, css.awt.TextAlign is a 32-bit integer, while the Align property (both here for grid controls + // and for ordinary toolkit controls) is a 16-bit integer. So, allow for 32 bit, but normalize it to 16 bit + if ( bModified ) + { + sal_Int32 nAlign( 0 ); + if ( rConvertedValue >>= nAlign ) + rConvertedValue <<= static_cast(nAlign); + } + break; + case PROPERTY_ID_HIDDEN: + bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, getBOOL(m_aHidden)); + break; + } + return bModified; +} + + +void OGridColumn::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) +{ + switch (nHandle) + { + case PROPERTY_ID_LABEL: + DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "invalid type" ); + rValue >>= m_aLabel; + break; + case PROPERTY_ID_WIDTH: + m_aWidth = rValue; + break; + case PROPERTY_ID_ALIGN: + m_aAlign = rValue; + break; + case PROPERTY_ID_HIDDEN: + m_aHidden = rValue; + break; + } +} + + +// XPropertyState + +Any OGridColumn::getPropertyDefaultByHandle( sal_Int32 nHandle ) const +{ + switch (nHandle) + { + case PROPERTY_ID_WIDTH: + case PROPERTY_ID_ALIGN: + return Any(); + case PROPERTY_ID_HIDDEN: + return Any(false); + default: + return OPropertySetAggregationHelper::getPropertyDefaultByHandle(nHandle); + } +} + +// XCloneable + +Reference< XCloneable > SAL_CALL OGridColumn::createClone( ) +{ + return createCloneColumn(); +} + +// XPersistObject + +void OGridColumn::write(const Reference& _rxOutStream) +{ + // 1. Write the UnoControl + Reference xMark(_rxOutStream, UNO_QUERY); + sal_Int32 nMark = xMark->createMark(); + + sal_Int32 nLen = 0; + _rxOutStream->writeLong(nLen); + + Reference xPersist; + if (query_aggregation(m_xAggregate, xPersist)) + xPersist->write(_rxOutStream); + + // Calculate the length + nLen = xMark->offsetToMark(nMark) - 4; + xMark->jumpToMark(nMark); + _rxOutStream->writeLong(nLen); + xMark->jumpToFurthest(); + xMark->deleteMark(nMark); + + // 2. Write a version number + _rxOutStream->writeShort(0x0002); + + sal_uInt16 nAnyMask = 0; + if (m_aWidth.getValueType().getTypeClass() == TypeClass_LONG) + nAnyMask |= WIDTH; + + if (m_aAlign.getValueTypeClass() == TypeClass_SHORT) + nAnyMask |= ALIGN; + + nAnyMask |= COMPATIBLE_HIDDEN; + + _rxOutStream->writeShort(nAnyMask); + + if (nAnyMask & WIDTH) + _rxOutStream->writeLong(getINT32(m_aWidth)); + if (nAnyMask & ALIGN) + _rxOutStream->writeShort(getINT16(m_aAlign)); + + // Name + _rxOutStream << m_aLabel; + + // the new place for the hidden flag : after m_aLabel, so older office version read the correct label, too + if (nAnyMask & COMPATIBLE_HIDDEN) + _rxOutStream->writeBoolean(getBOOL(m_aHidden)); +} + + +void OGridColumn::read(const Reference& _rxInStream) +{ + // 1. Read the UnoControl + sal_Int32 nLen = _rxInStream->readLong(); + if (nLen) + { + Reference xMark(_rxInStream, UNO_QUERY); + sal_Int32 nMark = xMark->createMark(); + Reference xPersist; + if (query_aggregation(m_xAggregate, xPersist)) + xPersist->read(_rxInStream); + + xMark->jumpToMark(nMark); + _rxInStream->skipBytes(nLen); + xMark->deleteMark(nMark); + } + + // 2. Write a version number + _rxInStream->readShort(); // version; + sal_uInt16 nAnyMask = _rxInStream->readShort(); + + if (nAnyMask & WIDTH) + { + sal_Int32 nValue = _rxInStream->readLong(); + m_aWidth <<= nValue; + } + + if (nAnyMask & ALIGN) + { + sal_Int16 nValue = _rxInStream->readShort(); + m_aAlign <<= nValue; + } + if (nAnyMask & OLD_HIDDEN) + { + bool bValue = _rxInStream->readBoolean(); + m_aHidden <<= bValue; + } + + // Name + _rxInStream >> m_aLabel; + + if (nAnyMask & COMPATIBLE_HIDDEN) + { + bool bValue = _rxInStream->readBoolean(); + m_aHidden <<= bValue; + } +} + +TextFieldColumn::TextFieldColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_TEXTFIELD) +{ +} +TextFieldColumn::TextFieldColumn(const TextFieldColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> TextFieldColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& TextFieldColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void TextFieldColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, false); + setOwnProperties(_rProps); + } +} +rtl::Reference TextFieldColumn::createCloneColumn() const +{ + return new TextFieldColumn(this); +} + +PatternFieldColumn::PatternFieldColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_PATTERNFIELD) +{ +} +PatternFieldColumn::PatternFieldColumn(const PatternFieldColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> PatternFieldColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& PatternFieldColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void PatternFieldColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, false); + setOwnProperties(_rProps); + } +} +rtl::Reference PatternFieldColumn::createCloneColumn() const +{ + return new PatternFieldColumn(this); +} + +DateFieldColumn::DateFieldColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_DATEFIELD) +{ +} +DateFieldColumn::DateFieldColumn(const DateFieldColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> DateFieldColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& DateFieldColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void DateFieldColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, true); + setOwnProperties(_rProps); + } +} +rtl::Reference DateFieldColumn::createCloneColumn() const +{ + return new DateFieldColumn(this); +} + +TimeFieldColumn::TimeFieldColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_TIMEFIELD) +{ +} +TimeFieldColumn::TimeFieldColumn(const TimeFieldColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> TimeFieldColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& TimeFieldColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void TimeFieldColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, false); + setOwnProperties(_rProps); + } +} +rtl::Reference TimeFieldColumn::createCloneColumn() const +{ + return new TimeFieldColumn(this); +} + +NumericFieldColumn::NumericFieldColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_NUMERICFIELD) +{ +} +NumericFieldColumn::NumericFieldColumn(const NumericFieldColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> NumericFieldColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& NumericFieldColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void NumericFieldColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, false); + setOwnProperties(_rProps); + } +} +rtl::Reference NumericFieldColumn::createCloneColumn() const +{ + return new NumericFieldColumn(this); +} + +CurrencyFieldColumn::CurrencyFieldColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_CURRENCYFIELD) +{ +} +CurrencyFieldColumn::CurrencyFieldColumn(const CurrencyFieldColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> CurrencyFieldColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& CurrencyFieldColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void CurrencyFieldColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, false); + setOwnProperties(_rProps); + } +} +rtl::Reference CurrencyFieldColumn::createCloneColumn() const +{ + return new CurrencyFieldColumn(this); +} + +CheckBoxColumn::CheckBoxColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_CHECKBOX) +{ +} +CheckBoxColumn::CheckBoxColumn(const CheckBoxColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> CheckBoxColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& CheckBoxColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void CheckBoxColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, false); + setOwnProperties(_rProps); + } +} +rtl::Reference CheckBoxColumn::createCloneColumn() const +{ + return new CheckBoxColumn(this); +} + +ComboBoxColumn::ComboBoxColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_COMBOBOX) +{ +} +ComboBoxColumn::ComboBoxColumn(const ComboBoxColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> ComboBoxColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& ComboBoxColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void ComboBoxColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, false); + setOwnProperties(_rProps); + } +} +rtl::Reference ComboBoxColumn::createCloneColumn() const +{ + return new ComboBoxColumn(this); +} + +ListBoxColumn::ListBoxColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_LISTBOX) +{ +} +ListBoxColumn::ListBoxColumn(const ListBoxColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> ListBoxColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& ListBoxColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void ListBoxColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, false); + setOwnProperties(_rProps); + } +} +rtl::Reference ListBoxColumn::createCloneColumn() const +{ + return new ListBoxColumn(this); +} + +FormattedFieldColumn::FormattedFieldColumn(const css::uno::Reference& _rContext) + :OGridColumn(_rContext, FRM_SUN_COMPONENT_FORMATTEDFIELD) +{ +} +FormattedFieldColumn::FormattedFieldColumn(const FormattedFieldColumn* _pCloneFrom) + :OGridColumn( _pCloneFrom ) +{ +} +css::uno::Reference< css::beans::XPropertySetInfo> FormattedFieldColumn::getPropertySetInfo() +{ + css::uno::Reference< css::beans::XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& FormattedFieldColumn::getInfoHelper() +{ + return *getArrayHelper(); +} +void FormattedFieldColumn::fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const +{ + if (m_xAggregateSet.is()) + { + _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties(); + clearAggregateProperties(_rAggregateProps, false); + setOwnProperties(_rProps); + } +} +rtl::Reference FormattedFieldColumn::createCloneColumn() const +{ + return new FormattedFieldColumn(this); +} + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/Columns.hxx b/forms/source/component/Columns.hxx new file mode 100644 index 0000000000..6c12197b90 --- /dev/null +++ b/forms/source/component/Columns.hxx @@ -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 . + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace comphelper; + + +namespace frm +{ + +typedef ::cppu::WeakAggComponentImplHelper2 < css::lang::XUnoTunnel + , css::util::XCloneable > OGridColumn_BASE; +class OGridColumn :public ::cppu::BaseMutex + ,public OGridColumn_BASE + ,public OPropertySetAggregationHelper + ,public OCloneableAggregation +{ +// [properties] + css::uno::Any m_aWidth; // column width + css::uno::Any m_aAlign; // column alignment + css::uno::Any m_aHidden; // column hidden? +// [properties] + + OUString m_aModelName; + +// [properties] + OUString m_aLabel; // Column name +// [properties] + +public: + OGridColumn(const css::uno::Reference& _rContext, OUString _sModelName); + explicit OGridColumn(const OGridColumn* _pOriginal ); + virtual ~OGridColumn() override; + + // UNO binding + DECLARE_UNO3_AGG_DEFAULTS(OGridControlModel, OGridColumn_BASE) + virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override; + + static const css::uno::Sequence& getUnoTunnelId(); + // XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence& _rIdentifier) override; + + // XTypeProvider + virtual css::uno::Sequence SAL_CALL getImplementationId() override; + virtual css::uno::Sequence< css::uno::Type> SAL_CALL getTypes() override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override; + + // XPersistObject + void write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream); + void read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream); + + // XPropertySet + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override = 0; + virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) 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; + + using OPropertySetAggregationHelper::getFastPropertyValue; + + // css::beans::XPropertyState + virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override; + + // XCloneable + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + const OUString& getModelName() const { return m_aModelName; } + +protected: + static void clearAggregateProperties(css::uno::Sequence< css::beans::Property>& seqProps, bool bAllowDropDown); + static void setOwnProperties(css::uno::Sequence< css::beans::Property>& seqProps); + + virtual rtl::Reference createCloneColumn() const = 0; +}; + +// column type ids +#define TYPE_CHECKBOX 0 +#define TYPE_COMBOBOX 1 +#define TYPE_CURRENCYFIELD 2 +#define TYPE_DATEFIELD 3 +#define TYPE_FORMATTEDFIELD 4 +#define TYPE_LISTBOX 5 +#define TYPE_NUMERICFIELD 6 +#define TYPE_PATTERNFIELD 7 +#define TYPE_TEXTFIELD 8 +#define TYPE_TIMEFIELD 9 + +// List of all known columns +const css::uno::Sequence& getColumnTypes(); +sal_Int32 getColumnTypeByModelName(const OUString& aModelName); + +// Columns +class TextFieldColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< TextFieldColumn > +{ +public: + explicit TextFieldColumn(const css::uno::Reference& _rContext ); + explicit TextFieldColumn(const TextFieldColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +class PatternFieldColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< PatternFieldColumn > +{ +public: + explicit PatternFieldColumn(const css::uno::Reference& _rContext ); + explicit PatternFieldColumn(const PatternFieldColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +class DateFieldColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< DateFieldColumn > +{ +public: + explicit DateFieldColumn(const css::uno::Reference& _rContext ); + explicit DateFieldColumn(const DateFieldColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +class TimeFieldColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< TimeFieldColumn > +{ +public: + explicit TimeFieldColumn(const css::uno::Reference& _rContext ); + explicit TimeFieldColumn(const TimeFieldColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +class NumericFieldColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< NumericFieldColumn > +{ +public: + explicit NumericFieldColumn(const css::uno::Reference& _rContext ); + explicit NumericFieldColumn(const NumericFieldColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +class CurrencyFieldColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< CurrencyFieldColumn > +{ +public: + explicit CurrencyFieldColumn(const css::uno::Reference& _rContext ); + explicit CurrencyFieldColumn(const CurrencyFieldColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +class CheckBoxColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< CheckBoxColumn > +{ +public: + explicit CheckBoxColumn(const css::uno::Reference& _rContext ); + explicit CheckBoxColumn(const CheckBoxColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +class ComboBoxColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< ComboBoxColumn > +{ +public: + explicit ComboBoxColumn(const css::uno::Reference& _rContext ); + explicit ComboBoxColumn(const ComboBoxColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +class ListBoxColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< ListBoxColumn > +{ +public: + explicit ListBoxColumn(const css::uno::Reference& _rContext ); + explicit ListBoxColumn(const ListBoxColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +class FormattedFieldColumn + :public OGridColumn + ,public OAggregationArrayUsageHelper< FormattedFieldColumn > +{ +public: + explicit FormattedFieldColumn(const css::uno::Reference& _rContext ); + explicit FormattedFieldColumn(const FormattedFieldColumn* _pCloneFrom); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual void fillProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps, + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + virtual rtl::Reference createCloneColumn() const override; +}; + +} // namespace frm + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/ComboBox.cxx b/forms/source/component/ComboBox.cxx new file mode 100644 index 0000000000..eaeaf9f84a --- /dev/null +++ b/forms/source/component/ComboBox.cxx @@ -0,0 +1,888 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "ComboBox.hxx" +#include +#include + +#include +#include +#include "BaseListBox.hxx" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace dbtools; + + +namespace frm +{ +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::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::form::binding; + + +Sequence OComboBoxModel::_getTypes() +{ + return ::comphelper::concatSequences( + OBoundControlModel::_getTypes(), + OEntryListHelper::getTypes(), + OErrorBroadcaster::getTypes() + ); +} + +// XServiceInfo + +css::uno::Sequence SAL_CALL OComboBoxModel::getSupportedServiceNames() +{ + css::uno::Sequence aSupported = OBoundControlModel::getSupportedServiceNames(); + + sal_Int32 nOldLen = aSupported.getLength(); + aSupported.realloc( nOldLen + 9 ); + OUString* pStoreTo = aSupported.getArray() + nOldLen; + + *pStoreTo++ = BINDABLE_CONTROL_MODEL; + *pStoreTo++ = DATA_AWARE_CONTROL_MODEL; + *pStoreTo++ = VALIDATABLE_CONTROL_MODEL; + + *pStoreTo++ = BINDABLE_DATA_AWARE_CONTROL_MODEL; + *pStoreTo++ = VALIDATABLE_BINDABLE_CONTROL_MODEL; + + *pStoreTo++ = FRM_SUN_COMPONENT_COMBOBOX; + *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_COMBOBOX; + *pStoreTo++ = BINDABLE_DATABASE_COMBO_BOX; + + *pStoreTo++ = FRM_COMPONENT_COMBOBOX; + + return aSupported; +} + + +Any SAL_CALL OComboBoxModel::queryAggregation(const Type& _rType) +{ + Any aReturn = OBoundControlModel::queryAggregation( _rType ); + if ( !aReturn.hasValue() ) + aReturn = OEntryListHelper::queryInterface( _rType ); + if ( !aReturn.hasValue() ) + aReturn = OErrorBroadcaster::queryInterface( _rType ); + return aReturn; +} + + +OComboBoxModel::OComboBoxModel(const Reference& _rxFactory) + :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_COMBOBOX, FRM_SUN_CONTROL_COMBOBOX, true, true, true ) + // use the old control name for compatibility reasons + ,OEntryListHelper( static_cast(*this) ) + ,OErrorBroadcaster( OComponentHelper::rBHelper ) + ,m_eListSourceType(ListSourceType_TABLE) + ,m_bEmptyIsNull(true) +{ + m_nClassId = FormComponentType::COMBOBOX; + initValueProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT ); +} + + +OComboBoxModel::OComboBoxModel( const OComboBoxModel* _pOriginal, const Reference& _rxFactory ) + :OBoundControlModel( _pOriginal, _rxFactory ) + ,OEntryListHelper( *_pOriginal, static_cast(*this) ) + ,OErrorBroadcaster( OComponentHelper::rBHelper ) + ,m_aListSource( _pOriginal->m_aListSource ) + ,m_aDefaultText( _pOriginal->m_aDefaultText ) + ,m_eListSourceType( _pOriginal->m_eListSourceType ) + ,m_bEmptyIsNull( _pOriginal->m_bEmptyIsNull ) +{ +} + + +OComboBoxModel::~OComboBoxModel() +{ + if (!OComponentHelper::rBHelper.bDisposed) + { + acquire(); + dispose(); + } + +} + +// XCloneable + +css::uno::Reference< css::util::XCloneable > SAL_CALL OComboBoxModel::createClone() +{ + rtl::Reference pClone = new OComboBoxModel(this, getContext()); + pClone->clonedFrom(this); + return pClone; +} + + +void OComboBoxModel::disposing() +{ + OBoundControlModel::disposing(); + OEntryListHelper::disposing(); + OErrorBroadcaster::disposing(); +} + + +void OComboBoxModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const +{ + switch (_nHandle) + { + case PROPERTY_ID_LISTSOURCETYPE: + _rValue <<= m_eListSourceType; + break; + + case PROPERTY_ID_LISTSOURCE: + _rValue <<= m_aListSource; + break; + + case PROPERTY_ID_EMPTY_IS_NULL: + _rValue <<= m_bEmptyIsNull; + break; + + case PROPERTY_ID_DEFAULT_TEXT: + _rValue <<= m_aDefaultText; + break; + + case PROPERTY_ID_STRINGITEMLIST: + _rValue <<= comphelper::containerToSequence(getStringItemList()); + break; + + case PROPERTY_ID_TYPEDITEMLIST: + _rValue <<= getTypedItemList(); + break; + + default: + OBoundControlModel::getFastPropertyValue(_rValue, _nHandle); + } +} + + +void OComboBoxModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue) +{ + switch (_nHandle) + { + case PROPERTY_ID_LISTSOURCETYPE : + DBG_ASSERT(_rValue.getValueType().equals(::cppu::UnoType::get()), + "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" ); + _rValue >>= m_eListSourceType; + break; + + case PROPERTY_ID_LISTSOURCE : + DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_STRING, + "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" ); + _rValue >>= m_aListSource; + // The ListSource has changed -> reload + if (ListSourceType_VALUELIST != m_eListSourceType) + { + if ( m_xCursor.is() && !hasField() && !hasExternalListSource() ) + // combo box is already connected to a database, and no external list source + // data source changed -> refresh + loadData( false ); + } + break; + + case PROPERTY_ID_EMPTY_IS_NULL : + DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, + "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" ); + _rValue >>= m_bEmptyIsNull; + break; + + case PROPERTY_ID_DEFAULT_TEXT : + DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_STRING, + "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" ); + _rValue >>= m_aDefaultText; + resetNoBroadcast(); + break; + + case PROPERTY_ID_STRINGITEMLIST: + { + ControlModelLock aLock( *this ); + setNewStringItemList( _rValue, aLock ); + // FIXME: this is bogus. setNewStringItemList expects a guard which has the *only* + // lock to the mutex, but setFastPropertyValue_NoBroadcast is already called with + // a lock - so we effectively has two locks here, of which setNewStringItemList can + // only control one. + } + break; + + case PROPERTY_ID_TYPEDITEMLIST: + { + ControlModelLock aLock( *this ); + setNewTypedItemList( _rValue, aLock ); + // Same FIXME as above. + } + break; + + default: + OBoundControlModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue); + } +} + +sal_Bool OComboBoxModel::convertFastPropertyValue( + Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue) +{ + bool bModified(false); + switch (_nHandle) + { + case PROPERTY_ID_LISTSOURCETYPE : + bModified = tryPropertyValueEnum(_rConvertedValue, _rOldValue, _rValue, m_eListSourceType); + break; + + case PROPERTY_ID_LISTSOURCE : + bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aListSource); + break; + + case PROPERTY_ID_EMPTY_IS_NULL : + bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bEmptyIsNull); + break; + + case PROPERTY_ID_DEFAULT_TEXT : + bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aDefaultText); + break; + + case PROPERTY_ID_STRINGITEMLIST: + bModified = convertNewListSourceProperty( _rConvertedValue, _rOldValue, _rValue ); + break; + + case PROPERTY_ID_TYPEDITEMLIST : + if (hasExternalListSource()) + throw IllegalArgumentException(); + bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, getTypedItemList()); + break; + + default: + bModified = OBoundControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue); + break; + } + return bModified; +} + +void OComboBoxModel::describeFixedProperties( Sequence< Property >& _rProps ) const +{ + OBoundControlModel::describeFixedProperties( _rProps ); + sal_Int32 nOldCount = _rProps.getLength(); + _rProps.realloc( nOldCount + 7); + css::beans::Property* pProperties = _rProps.getArray() + nOldCount; + *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCETYPE, PROPERTY_ID_LISTSOURCETYPE, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCE, PROPERTY_ID_LISTSOURCE, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType::get(), + css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_STRINGITEMLIST, PROPERTY_ID_STRINGITEMLIST, cppu::UnoType>::get(), css::beans::PropertyAttribute::BOUND); + *pProperties++ = css::beans::Property(PROPERTY_TYPEDITEMLIST, PROPERTY_ID_TYPEDITEMLIST, cppu::UnoType>::get(), css::beans::PropertyAttribute::OPTIONAL); + DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?"); +} + + +void OComboBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const +{ + OBoundControlModel::describeAggregateProperties( _rAggregateProps ); + + // superseded properties: + RemoveProperty( _rAggregateProps, PROPERTY_STRINGITEMLIST ); + RemoveProperty( _rAggregateProps, PROPERTY_TYPEDITEMLIST ); +} + + +OUString SAL_CALL OComboBoxModel::getServiceName() +{ + return FRM_COMPONENT_COMBOBOX; // old (non-sun) name for compatibility ! +} + + +void SAL_CALL OComboBoxModel::write(const Reference& _rxOutStream) +{ + OBoundControlModel::write(_rxOutStream); + + // Version + // Version 0x0002: EmptyIsNull + // Version 0x0003: ListSource->Seq + // Version 0x0004: DefaultText + // Version 0x0005: HelpText + _rxOutStream->writeShort(0x0006); + + // Mask for Any + sal_uInt16 nAnyMask = 0; + if (m_aBoundColumn.getValueType().getTypeClass() == TypeClass_SHORT) + nAnyMask |= BOUNDCOLUMN; + _rxOutStream << nAnyMask; + + css::uno::Sequence aListSourceSeq(&m_aListSource, 1); + _rxOutStream << aListSourceSeq; + _rxOutStream << static_cast(m_eListSourceType); + + if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN) + { + sal_Int16 nBoundColumn = 0; + m_aBoundColumn >>= nBoundColumn; + _rxOutStream << nBoundColumn; + } + + _rxOutStream << m_bEmptyIsNull; + _rxOutStream << m_aDefaultText; + writeHelpTextCompatibly(_rxOutStream); + + // from version 0x0006 : common properties + writeCommonProperties(_rxOutStream); +} + + +void SAL_CALL OComboBoxModel::read(const Reference& _rxInStream) +{ + OBoundControlModel::read(_rxInStream); + ControlModelLock aLock( *this ); + + // since we are "overwriting" the StringItemList of our aggregate (means we have + // an own place to store the value, instead of relying on our aggregate storing it), + // we need to respect what the aggregate just read for the StringItemList property. + try + { + if ( m_xAggregateSet.is() ) + setNewStringItemList( m_xAggregateSet->getPropertyValue( PROPERTY_STRINGITEMLIST ), aLock ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "forms.component", "OComboBoxModel::read: caught an exception while examining the aggregate's string item list!" ); + } + + // Version + sal_uInt16 nVersion = _rxInStream->readShort(); + DBG_ASSERT(nVersion > 0, "OComboBoxModel::read : version 0 ? this should never have been written !"); + + if (nVersion > 0x0006) + { + OSL_FAIL("OComboBoxModel::read : invalid (means unknown) version !"); + m_aListSource.clear(); + m_aBoundColumn <<= sal_Int16(0); + m_aDefaultText.clear(); + m_eListSourceType = ListSourceType_TABLE; + m_bEmptyIsNull = true; + defaultCommonProperties(); + return; + } + + // Mask for Any + sal_uInt16 nAnyMask; + _rxInStream >> nAnyMask; + + // ListSource + if (nVersion < 0x0003) + { + _rxInStream >> m_aListSource; + } + else // nVersion == 4 + { + m_aListSource.clear(); + css::uno::Sequence aListSource; + _rxInStream >> aListSource; + for (const OUString& rToken : std::as_const(aListSource)) + m_aListSource += rToken; + } + + sal_Int16 nListSourceType; + _rxInStream >> nListSourceType; + m_eListSourceType = static_cast(nListSourceType); + + if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN) + { + sal_Int16 nValue; + _rxInStream >> nValue; + m_aBoundColumn <<= nValue; + } + + if (nVersion > 0x0001) + { + bool bNull; + _rxInStream >> bNull; + m_bEmptyIsNull = bNull; + } + + if (nVersion > 0x0003) // nVersion == 4 + _rxInStream >> m_aDefaultText; + + // StringList must be emptied if a ListSource is set. + // This can be the case if we save in alive mode. + if ( !m_aListSource.isEmpty() + && !hasExternalListSource() + ) + { + setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence() ) ); + setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence() ) ); + } + + if (nVersion > 0x0004) + readHelpTextCompatibly(_rxInStream); + + if (nVersion > 0x0005) + readCommonProperties(_rxInStream); + + // After reading in, display the default values + if ( !getControlSource().isEmpty() ) + { + // (not if we don't have a control source - the "State" property acts like it is persistent, then + resetNoBroadcast(); + } +} + + +void OComboBoxModel::loadData( bool _bForce ) +{ + DBG_ASSERT(m_eListSourceType != ListSourceType_VALUELIST, "OComboBoxModel::loadData : do not call for a value list !"); + DBG_ASSERT( !hasExternalListSource(), "OComboBoxModel::loadData: cannot load from DB when I have an external list source!" ); + + if ( hasExternalListSource() ) + return; + + // Get Connection + if (!m_xCursor.is()) + return; + Reference xConnection = getConnection(m_xCursor); + if (!xConnection.is()) + return; + + Reference xServiceInfo(xConnection, UNO_QUERY); + if (!xServiceInfo.is() || !xServiceInfo->supportsService(SRV_SDB_CONNECTION)) + { + OSL_FAIL("OComboBoxModel::loadData : invalid connection !"); + return; + } + + if (m_aListSource.isEmpty() || m_eListSourceType == ListSourceType_VALUELIST) + return; + + ::utl::SharedUNOComponent< XResultSet > xListCursor; + try + { + m_aListRowSet.setConnection( xConnection ); + + bool bExecuteRowSet( false ); + switch (m_eListSourceType) + { + case ListSourceType_TABLEFIELDS: + // don't work with a statement here, the fields will be collected below + break; + case ListSourceType_TABLE: + { + // does the bound field belong to the table ? + // if we use an alias for the bound field, we won't find it + // in that case we use the first field of the table + + Reference xFieldsByName = getTableFields(xConnection, m_aListSource); + + OUString aFieldName; + if ( xFieldsByName.is() && xFieldsByName->hasByName( getControlSource() ) ) + { + aFieldName = getControlSource(); + } + else + { + // otherwise look for the alias + Reference xFormProp(m_xCursor,UNO_QUERY); + Reference< XColumnsSupplier > xSupplyFields; + xFormProp->getPropertyValue("SingleSelectQueryComposer") >>= xSupplyFields; + + // search the field + DBG_ASSERT(xSupplyFields.is(), "OComboBoxModel::loadData : invalid query composer !"); + + Reference< XNameAccess > xFieldNames = xSupplyFields->getColumns(); + if ( xFieldNames->hasByName( getControlSource() ) ) + { + Reference< XPropertySet > xComposerFieldAsSet; + xFieldNames->getByName( getControlSource() ) >>= xComposerFieldAsSet; + if (hasProperty(PROPERTY_FIELDSOURCE, xComposerFieldAsSet)) + xComposerFieldAsSet->getPropertyValue(PROPERTY_FIELDSOURCE) >>= aFieldName; + } + } + + if (aFieldName.isEmpty()) + break; + + Reference xMeta = xConnection->getMetaData(); + OSL_ENSURE(xMeta.is(),"No database meta data!"); + if ( xMeta.is() ) + { + OUString aQuote = xMeta->getIdentifierQuoteString(); + + OUString sCatalog, sSchema, sTable; + qualifiedNameComponents( xMeta, m_aListSource, sCatalog, sSchema, sTable, EComposeRule::InDataManipulation ); + + OUString aStatement = + "SELECT DISTINCT " + + quoteName( aQuote, aFieldName ) + + " FROM " + + composeTableNameForSelect( xConnection, sCatalog, sSchema, sTable ); + + m_aListRowSet.setEscapeProcessing( false ); + m_aListRowSet.setCommand( aStatement ); + bExecuteRowSet = true; + } + } break; + case ListSourceType_QUERY: + { + m_aListRowSet.setCommandFromQuery( m_aListSource ); + bExecuteRowSet = true; + } + break; + + default: + { + m_aListRowSet.setEscapeProcessing( ListSourceType_SQLPASSTHROUGH != m_eListSourceType ); + m_aListRowSet.setCommand( m_aListSource ); + bExecuteRowSet = true; + } + } + + if ( bExecuteRowSet ) + { + if ( !_bForce && !m_aListRowSet.isDirty() ) + { + // if none of the settings of the row set changed, compared to the last + // invocation of loadData, then don't re-fill the list. Instead, assume + // the list entries are the same. + return; + } + xListCursor.reset( m_aListRowSet.execute() ); + } + } + catch(const SQLException& eSQL) + { + onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST)); + return; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("forms.component"); + return; + } + + ::std::vector< OUString > aStringList; + aStringList.reserve(16); + try + { + OSL_ENSURE( xListCursor.is() || ( ListSourceType_TABLEFIELDS == m_eListSourceType ), + "OComboBoxModel::loadData: logic error!" ); + if ( !xListCursor.is() && ( ListSourceType_TABLEFIELDS != m_eListSourceType ) ) + return; + + switch (m_eListSourceType) + { + case ListSourceType_SQL: + case ListSourceType_SQLPASSTHROUGH: + case ListSourceType_TABLE: + case ListSourceType_QUERY: + { + // The XDatabaseVariant of the first column + Reference xSupplyCols(xListCursor, UNO_QUERY); + DBG_ASSERT(xSupplyCols.is(), "OComboBoxModel::loadData : cursor supports the row set service but is no column supplier?!"); + Reference xColumns; + if (xSupplyCols.is()) + { + xColumns.set(xSupplyCols->getColumns(), UNO_QUERY); + DBG_ASSERT(xColumns.is(), "OComboBoxModel::loadData : no columns supplied by the row set !"); + } + Reference< XPropertySet > xDataField; + if ( xColumns.is() ) + xColumns->getByIndex(0) >>= xDataField; + if ( !xDataField.is() ) + return; + + ::dbtools::FormattedColumnValue aValueFormatter( getContext(), m_xCursor, xDataField ); + + // Fill Lists + sal_Int16 i = 0; + // At the moment by definition the list cursor is positioned _before_ the first row + while (xListCursor->next() && (i++ xFieldNames = getTableFields(xConnection, m_aListSource); + if (xFieldNames.is()) + { + const Sequence aFieldNames = xFieldNames->getElementNames(); + aStringList.insert(aStringList.end(), aFieldNames.begin(), aFieldNames.end()); + } + } + break; + default: + OSL_FAIL( "OComboBoxModel::loadData: unreachable!" ); + break; + } + } + catch(const SQLException& eSQL) + { + onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST)); + return; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("forms.component"); + return; + } + + // Set String-Sequence at ListBox + setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( comphelper::containerToSequence(aStringList) ) ); + // Reset TypedItemList, no matching data. + setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence() ) ); +} + + +void OComboBoxModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm ) +{ + Reference xField = getField(); + if ( xField.is() ) + m_pValueFormatter.reset( new ::dbtools::FormattedColumnValue( getContext(), Reference< XRowSet >( _rxForm, UNO_QUERY ), xField ) ); + getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= m_aDesignModeStringItems; + + // Only load data if a ListSource was supplied + if ( !m_aListSource.isEmpty() && m_xCursor.is() && !hasExternalListSource() ) + loadData( false ); +} + + +void OComboBoxModel::onDisconnectedDbColumn() +{ + m_pValueFormatter.reset(); + + // reset the string item list + if ( !hasExternalListSource() ) + setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( m_aDesignModeStringItems ) ); + + m_aListRowSet.dispose(); +} + + +void SAL_CALL OComboBoxModel::reloaded( const EventObject& aEvent ) +{ + OBoundControlModel::reloaded(aEvent); + + // reload data if we have a list source + if ( !m_aListSource.isEmpty() && m_xCursor.is() && !hasExternalListSource() ) + loadData( false ); +} + + +void OComboBoxModel::resetNoBroadcast() +{ + OBoundControlModel::resetNoBroadcast(); + m_aLastKnownValue.clear(); +} + + +bool OComboBoxModel::commitControlValueToDbColumn( bool _bPostReset ) +{ + Any aNewValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) ); + + OUString sNewValue; + aNewValue >>= sNewValue; + + bool bModified = ( aNewValue != m_aLastKnownValue ); + if ( bModified ) + { + if ( !aNewValue.hasValue() + || ( sNewValue.isEmpty() // an empty string + && m_bEmptyIsNull // which should be interpreted as NULL + ) + ) + { + m_xColumnUpdate->updateNull(); + } + else + { + try + { + OSL_PRECOND(m_pValueFormatter, + "OComboBoxModel::commitControlValueToDbColumn: no value formatter!"); + if (m_pValueFormatter) + { + if ( !m_pValueFormatter->setFormattedValue( sNewValue ) ) + return false; + } + else + m_xColumnUpdate->updateString( sNewValue ); + } + catch ( const Exception& ) + { + return false; + } + } + + m_aLastKnownValue = aNewValue; + } + + // add the new value to the list + bool bAddToList = bModified && !_bPostReset; + // (only if this is not the "commit" triggered by a "reset") + + if ( !bAddToList ) + return true; + + css::uno::Sequence aStringItemList; + if ( !(getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aStringItemList) ) + return true; + + bool bFound = false; + for (const OUString& rStringItem : std::as_const(aStringItemList)) + { + if ( (bFound = rStringItem == sNewValue) ) + break; + } + + // not found -> add + if (!bFound) + { + sal_Int32 nOldLen = aStringItemList.getLength(); + aStringItemList.realloc( nOldLen + 1 ); + aStringItemList.getArray()[ nOldLen ] = sNewValue; + + setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( aStringItemList ) ); + setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence() ) ); + } + + return true; +} + +// XPropertiesChangeListener + +Any OComboBoxModel::translateDbColumnToControlValue() +{ + OSL_PRECOND(m_pValueFormatter, + "OComboBoxModel::translateDbColumnToControlValue: no value formatter!"); + if (m_pValueFormatter) + { + OUString sValue( m_pValueFormatter->getFormattedValue() ); + if ( sValue.isEmpty() + && m_pValueFormatter->getColumn().is() + && m_pValueFormatter->getColumn()->wasNull() + ) + { + m_aLastKnownValue.clear(); + } + else + { + + m_aLastKnownValue <<= sValue; + } + } + else + m_aLastKnownValue.clear(); + + return m_aLastKnownValue.hasValue() ? m_aLastKnownValue : Any( OUString() ); + // (m_aLastKnownValue is allowed to be VOID, the control value isn't) +} + + +Any OComboBoxModel::getDefaultForReset() const +{ + return Any( m_aDefaultText ); +} + + +void OComboBoxModel::stringItemListChanged( ControlModelLock& /*_rInstanceLock*/ ) +{ + if ( m_xAggregateSet.is() ) + { + m_xAggregateSet->setPropertyValue( PROPERTY_STRINGITEMLIST, Any( comphelper::containerToSequence(getStringItemList()) ) ); + m_xAggregateSet->setPropertyValue( PROPERTY_TYPEDITEMLIST, Any( getTypedItemList()) ) ; + } +} + + +void OComboBoxModel::refreshInternalEntryList() +{ + DBG_ASSERT( !hasExternalListSource(), "OComboBoxModel::refreshInternalEntryList: invalid call!" ); + + if ( !hasExternalListSource( ) + && ( m_eListSourceType != ListSourceType_VALUELIST ) + && ( m_xCursor.is() ) + ) + { + loadData( true ); + } +} + + +void SAL_CALL OComboBoxModel::disposing( const EventObject& _rSource ) +{ + if ( !OEntryListHelper::handleDisposing( _rSource ) ) + OBoundControlModel::disposing( _rSource ); +} + + +//= OComboBoxControl + +OComboBoxControl::OComboBoxControl(const Reference& _rxContext) + :OBoundControl(_rxContext, VCL_CONTROL_COMBOBOX) +{ +} + + +css::uno::Sequence SAL_CALL OComboBoxControl::getSupportedServiceNames() +{ + css::uno::Sequence aSupported = OBoundControl::getSupportedServiceNames(); + aSupported.realloc(aSupported.getLength() + 2); + + OUString* pArray = aSupported.getArray(); + pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_COMBOBOX; + pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_COMBOBOX; + return aSupported; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_form_OComboBoxModel_get_implementation(css::uno::XComponentContext* component, + css::uno::Sequence const &) +{ + return cppu::acquire(new frm::OComboBoxModel(component)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_form_OComboBoxControl_get_implementation(css::uno::XComponentContext* component, + css::uno::Sequence const &) +{ + return cppu::acquire(new frm::OComboBoxControl(component)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/ComboBox.hxx b/forms/source/component/ComboBox.hxx new file mode 100644 index 0000000000..ed23601442 --- /dev/null +++ b/forms/source/component/ComboBox.hxx @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include "errorbroadcaster.hxx" +#include "entrylisthelper.hxx" +#include "cachedrowset.hxx" + +#include + +#include + + +namespace frm +{ + +class OComboBoxModel final + :public OBoundControlModel + ,public OEntryListHelper + ,public OErrorBroadcaster +{ + CachedRowSet m_aListRowSet; // the row set to fill the list + css::uno::Any m_aBoundColumn; // obsolete + OUString m_aListSource; + OUString m_aDefaultText; // DefaultText + css::uno::Any m_aLastKnownValue; + + css::uno::Sequence m_aDesignModeStringItems; + + css::form::ListSourceType m_eListSourceType; // ListSource's type + bool m_bEmptyIsNull; // Empty string is interpreted as NULL + + ::std::unique_ptr< ::dbtools::FormattedColumnValue > m_pValueFormatter; + + virtual css::uno::Sequence< css::uno::Type> _getTypes() override; + +public: + OComboBoxModel( + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + OComboBoxModel( + const OComboBoxModel* _pOriginal, + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + virtual ~OComboBoxModel() override; + + virtual void SAL_CALL disposing() override; + + // OPropertySetHelper + virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle) const override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override; + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue ) override; + + // XLoadListener + virtual void SAL_CALL reloaded( const css::lang::EventObject& aEvent ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.form.OComboBoxModel"; } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + + // UNO + DECLARE_UNO3_AGG_DEFAULTS(OComboBoxModel, OBoundControlModel) + virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override; + + // XPersistObject + virtual OUString SAL_CALL getServiceName() override; + virtual void SAL_CALL + write(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream) override; + virtual void SAL_CALL + read(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream) override; + + // OControlModel's property handling + virtual void describeFixedProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps + ) const override; + virtual void describeAggregateProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override; + + // prevent method hiding + using OBoundControlModel::getFastPropertyValue; + +private: + // OBoundControlModel overridables + virtual css::uno::Any translateDbColumnToControlValue( ) override; + virtual bool commitControlValueToDbColumn( bool _bPostReset ) override; + + virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override; + virtual void onDisconnectedDbColumn() override; + + virtual css::uno::Any getDefaultForReset() const override; + + virtual void resetNoBroadcast() override; + + // OEntryListHelper overridables + virtual void stringItemListChanged( ControlModelLock& _rInstanceLock ) override; + virtual void refreshInternalEntryList() override; + + void loadData( bool _bForce ); + + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; +}; + +class OComboBoxControl : public OBoundControl +{ +public: + explicit OComboBoxControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.form.OComboBoxControl"; } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; +}; + + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/Currency.cxx b/forms/source/component/Currency.cxx new file mode 100644 index 0000000000..0b25df4f85 --- /dev/null +++ b/forms/source/component/Currency.cxx @@ -0,0 +1,253 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "Currency.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace frm +{ + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; + +OCurrencyControl::OCurrencyControl(const Reference& _rxFactory) + :OBoundControl(_rxFactory, VCL_CONTROL_CURRENCYFIELD) +{ +} + +css::uno::Sequence SAL_CALL OCurrencyControl::getSupportedServiceNames() +{ + css::uno::Sequence aSupported = OBoundControl::getSupportedServiceNames(); + aSupported.realloc(aSupported.getLength() + 2); + + OUString*pArray = aSupported.getArray(); + pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_CURRENCYFIELD; + pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_CURRENCYFIELD; + return aSupported; +} + + +// OCurrencyModel + +void OCurrencyModel::implConstruct() +{ + if (!m_xAggregateSet.is()) + return; + + try + { + // get the system international information + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& aLocaleInfo = aSysLocale.GetLocaleData(); + + OUString sCurrencySymbol; + bool bPrependCurrencySymbol = false; + switch ( aLocaleInfo.getCurrPositiveFormat() ) + { + case 0: // $1 + sCurrencySymbol = aLocaleInfo.getCurrSymbol(); + bPrependCurrencySymbol = true; + break; + case 1: // 1$ + sCurrencySymbol = aLocaleInfo.getCurrSymbol(); + bPrependCurrencySymbol = false; + break; + case 2: // $ 1 + sCurrencySymbol = aLocaleInfo.getCurrSymbol() + " "; + bPrependCurrencySymbol = true; + break; + case 3: // 1 $ + sCurrencySymbol = " " + aLocaleInfo.getCurrSymbol(); + bPrependCurrencySymbol = false; + break; + } + if (!sCurrencySymbol.isEmpty()) + { + m_xAggregateSet->setPropertyValue(PROPERTY_CURRENCYSYMBOL, Any(sCurrencySymbol)); + m_xAggregateSet->setPropertyValue(PROPERTY_CURRSYM_POSITION, Any(bPrependCurrencySymbol)); + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION( "forms.component", "OCurrencyModel::implConstruct: caught an exception while initializing the aggregate!" ); + } +} + + +OCurrencyModel::OCurrencyModel(const Reference& _rxFactory) + :OEditBaseModel( _rxFactory, VCL_CONTROLMODEL_CURRENCYFIELD, FRM_SUN_CONTROL_CURRENCYFIELD, false, true ) + // use the old control name for compatibility reasons +{ + + m_nClassId = FormComponentType::CURRENCYFIELD; + initValueProperty( PROPERTY_VALUE, PROPERTY_ID_VALUE ); + + implConstruct(); +} + + +OCurrencyModel::OCurrencyModel( const OCurrencyModel* _pOriginal, const Reference& _rxFactory ) + :OEditBaseModel( _pOriginal, _rxFactory ) +{ + implConstruct(); +} + + +OCurrencyModel::~OCurrencyModel() +{ +} + +// XCloneable + +css::uno::Reference< css::util::XCloneable > SAL_CALL OCurrencyModel::createClone() +{ + rtl::Reference pClone = new OCurrencyModel(this, getContext()); + pClone->clonedFrom(this); + return pClone; +} + + +// XServiceInfo + +css::uno::Sequence SAL_CALL OCurrencyModel::getSupportedServiceNames() +{ + css::uno::Sequence aSupported = OBoundControlModel::getSupportedServiceNames(); + + sal_Int32 nOldLen = aSupported.getLength(); + aSupported.realloc( nOldLen + 5 ); + OUString* pStoreTo = aSupported.getArray() + nOldLen; + + *pStoreTo++ = DATA_AWARE_CONTROL_MODEL; + *pStoreTo++ = VALIDATABLE_CONTROL_MODEL; + + *pStoreTo++ = FRM_SUN_COMPONENT_CURRENCYFIELD; + *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_CURRENCYFIELD; + + *pStoreTo++ = FRM_COMPONENT_CURRENCYFIELD; + + return aSupported; +} + + +void OCurrencyModel::describeFixedProperties( Sequence< Property >& _rProps ) const +{ + OEditBaseModel::describeFixedProperties( _rProps ); + sal_Int32 nOldCount = _rProps.getLength(); + _rProps.realloc( nOldCount + 2); + css::beans::Property* pProperties = _rProps.getArray() + nOldCount; + // Set Value to transient + // ModifyPropertyAttributes(_rAggregateProps, PROPERTY_VALUE, PropertyAttribute::TRANSIENT, 0); + + *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_VALUE, PROPERTY_ID_DEFAULT_VALUE, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID); + *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND); + DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?"); +} + + +OUString SAL_CALL OCurrencyModel::getServiceName() +{ + return FRM_COMPONENT_CURRENCYFIELD; // old (non-sun) name for compatibility ! +} + + +bool OCurrencyModel::commitControlValueToDbColumn( bool /*_bPostReset*/ ) +{ + Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) ); + if ( aControlValue != m_aSaveValue ) + { + if ( aControlValue.getValueType().getTypeClass() == TypeClass_VOID ) + m_xColumnUpdate->updateNull(); + else + { + try + { + m_xColumnUpdate->updateDouble( getDouble( aControlValue ) ); + } + catch(const Exception&) + { + return false; + } + } + m_aSaveValue = aControlValue; + } + return true; +} + + +Any OCurrencyModel::translateDbColumnToControlValue() +{ + m_aSaveValue <<= m_xColumn->getDouble(); + if ( m_xColumn->wasNull() ) + m_aSaveValue.clear(); + return m_aSaveValue; +} + +// XReset + +Any OCurrencyModel::getDefaultForReset() const +{ + Any aValue; + if ( m_aDefault.getValueType().getTypeClass() == TypeClass_DOUBLE ) + aValue = m_aDefault; + + return aValue; +} + + +void OCurrencyModel::resetNoBroadcast() +{ + OEditBaseModel::resetNoBroadcast(); + m_aSaveValue.clear(); +} + + +} // namespace frm + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_form_OCurrencyModel_get_implementation(css::uno::XComponentContext* component, + css::uno::Sequence const &) +{ + return cppu::acquire(new frm::OCurrencyModel(component)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_form_OCurrencyControl_get_implementation(css::uno::XComponentContext* component, + css::uno::Sequence const &) +{ + return cppu::acquire(new frm::OCurrencyControl(component)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/Currency.hxx b/forms/source/component/Currency.hxx new file mode 100644 index 0000000000..f77f711f09 --- /dev/null +++ b/forms/source/component/Currency.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 "EditBase.hxx" + + +namespace frm +{ + +class OCurrencyModel final + :public OEditBaseModel +{ + css::uno::Any m_aSaveValue; + +public: + OCurrencyModel( + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + OCurrencyModel( + const OCurrencyModel* _pOriginal, + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + virtual ~OCurrencyModel() override; + + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.form.OCurrencyModel"; } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + + // css::io::XPersistObject + virtual OUString SAL_CALL getServiceName() override; + + // OControlModel's property handling + virtual void describeFixedProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps + ) const override; + +private: + // OBoundControlModel overridables + virtual css::uno::Any translateDbColumnToControlValue( ) override; + virtual bool commitControlValueToDbColumn( bool _bPostReset ) override; + + virtual css::uno::Any getDefaultForReset() const override; + + virtual void resetNoBroadcast() override; + + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + void implConstruct(); +}; + +class OCurrencyControl: public OBoundControl +{ +public: + explicit OCurrencyControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext); + // css::lang::XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.form.OCurrencyControl"; } + + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; +}; + + +} // namespace frm + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/component/DatabaseForm.cxx b/forms/source/component/DatabaseForm.cxx new file mode 100644 index 0000000000..0d6aafaaa5 --- /dev/null +++ b/forms/source/component/DatabaseForm.cxx @@ -0,0 +1,4042 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include "DatabaseForm.hxx" +#include "EventThread.hxx" +#include +#include +#include "GroupManager.hxx" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::dbtools; +using namespace ::comphelper; +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::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; + + +namespace frm +{ + +namespace { + +class DocumentModifyGuard +{ +public: + explicit DocumentModifyGuard( const Reference< XInterface >& _rxFormComponent ) + :m_xDocumentModify( getXModel( _rxFormComponent ), UNO_QUERY ) + { + impl_changeModifiableFlag_nothrow( false ); + } + ~DocumentModifyGuard() + { + impl_changeModifiableFlag_nothrow( true ); + } + +private: + void impl_changeModifiableFlag_nothrow( const bool _enable ) + { + try + { + if ( m_xDocumentModify.is() ) + _enable ? m_xDocumentModify->enableSetModified() : m_xDocumentModify->disableSetModified(); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("forms.component"); + } + } + +private: + Reference< XModifiable2 > m_xDocumentModify; +}; + +} + +// submitting and resetting html-forms asynchronously +class OFormSubmitResetThread: public OComponentEventThread +{ +protected: + + // process an event. while processing the mutex isn't locked, and pCompImpl + // is made sure to remain valid + virtual void processEvent( ::cppu::OComponentHelper* _pCompImpl, + const EventObject* _pEvt, + const Reference& _rControl, + bool _bSubmit) override; + +public: + + explicit OFormSubmitResetThread(ODatabaseForm* pControl) : OComponentEventThread(pControl) { } +}; + + +void OFormSubmitResetThread::processEvent( + ::cppu::OComponentHelper* pCompImpl, + const EventObject *_pEvt, + const Reference& _rControl, + bool _bSubmit) +{ + if (_bSubmit) + static_cast(pCompImpl)->submit_impl(_rControl, *static_cast(_pEvt)); + else + static_cast(pCompImpl)->reset_impl(true); +} + + +//= ODatabaseForm + +Sequence SAL_CALL ODatabaseForm::getImplementationId() +{ + return css::uno::Sequence(); +} + + +Sequence SAL_CALL ODatabaseForm::getTypes() +{ + // ask the aggregate + Sequence aAggregateTypes; + Reference xAggregateTypes; + if (query_aggregation(m_xAggregate, xAggregateTypes)) + aAggregateTypes = xAggregateTypes->getTypes(); + + Sequence< Type > aRet = concatSequences( + aAggregateTypes, ODatabaseForm_BASE1::getTypes(), OFormComponents::getTypes() + ); + aRet = concatSequences( aRet, ODatabaseForm_BASE2::getTypes(), ODatabaseForm_BASE3::getTypes() ); + return concatSequences( aRet, OPropertySetAggregationHelper::getTypes() ); +} + + +Any SAL_CALL ODatabaseForm::queryAggregation(const Type& _rType) +{ + Any aReturn = ODatabaseForm_BASE1::queryInterface(_rType); + // our own interfaces + if (!aReturn.hasValue()) + { + aReturn = ODatabaseForm_BASE2::queryInterface(_rType); + // property set related interfaces + if (!aReturn.hasValue()) + { + aReturn = OPropertySetAggregationHelper::queryInterface(_rType); + + // form component collection related interfaces + if (!aReturn.hasValue()) + { + aReturn = OFormComponents::queryAggregation(_rType); + + // interfaces already present in the aggregate which we want to reroute + // only available if we could create the aggregate + if (!aReturn.hasValue() && m_xAggregateAsRowSet.is()) + aReturn = ODatabaseForm_BASE3::queryInterface(_rType); + + // aggregate interfaces + // (ask the aggregated object _after_ the OComponentHelper (base of OFormComponents), + // so calls to the XComponent interface reach us and not the aggregation) + if (!aReturn.hasValue() && m_xAggregate.is()) + aReturn = m_xAggregate->queryAggregation(_rType); + } + } + } + + return aReturn; +} + + +ODatabaseForm::ODatabaseForm(const Reference& _rxContext) + :OFormComponents(_rxContext) + ,OPropertySetAggregationHelper(OComponentHelper::rBHelper) + ,OPropertyChangeListener(m_aMutex) + ,m_aLoadListeners(m_aMutex) + ,m_aRowSetApproveListeners(m_aMutex) + ,m_aSubmitListeners(m_aMutex) + ,m_aErrorListeners(m_aMutex) + ,m_aResetListeners(m_aMutex) + ,m_aPropertyBagHelper( *this ) + ,m_aParameterManager( m_aMutex, _rxContext ) + ,m_aFilterManager() + ,m_nResetsPending(0) + ,m_nPrivileges(0) + ,m_bInsertOnly( false ) + ,m_eSubmitMethod(FormSubmitMethod_GET) + ,m_eSubmitEncoding(FormSubmitEncoding_URL) + ,m_eNavigation(NavigationBarMode_CURRENT) + ,m_bAllowInsert(true) + ,m_bAllowUpdate(true) + ,m_bAllowDelete(true) + ,m_bLoaded(false) + ,m_bSubForm(false) + ,m_bForwardingConnection(false) + ,m_bSharingConnection( false ) +{ + impl_construct(); +} + + +ODatabaseForm::ODatabaseForm( const ODatabaseForm& _cloneSource ) + :OFormComponents( _cloneSource ) + ,OPropertySetAggregationHelper( OComponentHelper::rBHelper ) + ,OPropertyChangeListener( m_aMutex ) + ,ODatabaseForm_BASE1() + ,ODatabaseForm_BASE2() + ,ODatabaseForm_BASE3() + ,IPropertyBagHelperContext() + ,m_aLoadListeners( m_aMutex ) + ,m_aRowSetApproveListeners( m_aMutex ) + ,m_aSubmitListeners( m_aMutex ) + ,m_aErrorListeners( m_aMutex ) + ,m_aResetListeners( m_aMutex ) + ,m_aPropertyBagHelper( *this ) + ,m_aParameterManager( m_aMutex, _cloneSource.m_xContext ) + ,m_aFilterManager() + ,m_nResetsPending( 0 ) + ,m_nPrivileges( 0 ) + ,m_bInsertOnly( _cloneSource.m_bInsertOnly ) + ,m_aControlBorderColorFocus( _cloneSource.m_aControlBorderColorFocus ) + ,m_aControlBorderColorMouse( _cloneSource.m_aControlBorderColorMouse ) + ,m_aControlBorderColorInvalid( _cloneSource.m_aControlBorderColorInvalid ) + ,m_aDynamicControlBorder( _cloneSource.m_aDynamicControlBorder ) + ,m_sName( _cloneSource.m_sName ) + ,m_aTargetURL( _cloneSource.m_aTargetURL ) + ,m_aTargetFrame( _cloneSource.m_aTargetFrame ) + ,m_eSubmitMethod( _cloneSource.m_eSubmitMethod ) + ,m_eSubmitEncoding( _cloneSource.m_eSubmitEncoding ) + ,m_eNavigation( _cloneSource.m_eNavigation ) + ,m_bAllowInsert( _cloneSource.m_bAllowInsert ) + ,m_bAllowUpdate( _cloneSource.m_bAllowUpdate ) + ,m_bAllowDelete( _cloneSource.m_bAllowDelete ) + ,m_bLoaded( false ) + ,m_bSubForm( false ) + ,m_bForwardingConnection( false ) + ,m_bSharingConnection( false ) +{ + + impl_construct(); + + osl_atomic_increment( &m_refCount ); + { + // our aggregated rowset itself is not cloneable, so simply copy the properties + ::comphelper::copyProperties( _cloneSource.m_xAggregateSet, m_xAggregateSet ); + + // also care for the dynamic properties: If the clone source has properties which we do not have, + // then add them + try + { + Reference< XPropertySet > xSourceProps( const_cast< ODatabaseForm& >( _cloneSource ).queryAggregation( + cppu::UnoType::get() ), UNO_QUERY_THROW ); + Reference< XPropertySetInfo > xSourcePSI( xSourceProps->getPropertySetInfo(), UNO_SET_THROW ); + Reference< XPropertyState > xSourcePropState( xSourceProps, UNO_QUERY ); + + Reference< XPropertySetInfo > xDestPSI( getPropertySetInfo(), UNO_SET_THROW ); + + const Sequence< Property > aSourceProperties( xSourcePSI->getProperties() ); + for ( auto const & sourceProperty : aSourceProperties ) + { + if ( xDestPSI->hasPropertyByName( sourceProperty.Name ) ) + continue; + + // the initial value passed to XPropertyContainer is also used as default, usually. So, try + // to retrieve the default of the source property + Any aInitialValue; + if ( xSourcePropState.is() ) + { + aInitialValue = xSourcePropState->getPropertyDefault( sourceProperty.Name ); + } + else + { + aInitialValue = xSourceProps->getPropertyValue( sourceProperty.Name ); + } + addProperty( sourceProperty.Name, sourceProperty.Attributes, aInitialValue ); + setPropertyValue( sourceProperty.Name, xSourceProps->getPropertyValue( sourceProperty.Name ) ); + } + } + catch(const RuntimeException&) + { + throw; + } + catch(const Exception&) + { + css::uno::Any a(cppu::getCaughtException()); + throw WrappedTargetRuntimeException( + "Could not clone the given database form.", + *const_cast< ODatabaseForm* >( &_cloneSource ), + a + ); + } + } + osl_atomic_decrement( &m_refCount ); +} + +void ODatabaseForm::impl_construct() +{ + // aggregate a row set + osl_atomic_increment(&m_refCount); + { + m_xAggregate.set( m_xContext->getServiceManager()->createInstanceWithContext(SRV_SDB_ROWSET, m_xContext), UNO_QUERY_THROW ); + m_xAggregateAsRowSet.set( m_xAggregate, UNO_QUERY_THROW ); + setAggregation( m_xAggregate ); + } + + // listen for the properties, important for Parameters + if ( m_xAggregateSet.is() ) + { + m_xAggregatePropertyMultiplexer = new OPropertyChangeMultiplexer(this, m_xAggregateSet, false); + m_xAggregatePropertyMultiplexer->addProperty(PROPERTY_COMMAND); + m_xAggregatePropertyMultiplexer->addProperty(PROPERTY_ACTIVE_CONNECTION); + } + + { + Reference< XWarningsSupplier > xRowSetWarnings( m_xAggregate, UNO_QUERY ); + m_aWarnings.setExternalWarnings( xRowSetWarnings ); + } + + if ( m_xAggregate.is() ) + { + m_xAggregate->setDelegator( static_cast< XWeak* >( this ) ); + } + + { + m_aFilterManager.initialize( m_xAggregateSet ); + m_aParameterManager.initialize( this, m_xAggregate ); + + declareForwardedProperty( PROPERTY_ID_ACTIVE_CONNECTION ); + } + osl_atomic_decrement( &m_refCount ); + + m_pGroupManager = new OGroupManager( this ); +} + + +ODatabaseForm::~ODatabaseForm() +{ + m_pGroupManager.clear(); + + if (m_xAggregate.is()) + m_xAggregate->setDelegator( nullptr ); + + m_aWarnings.setExternalWarnings( nullptr ); + + if (m_xAggregatePropertyMultiplexer.is()) + { + m_xAggregatePropertyMultiplexer->dispose(); + m_xAggregatePropertyMultiplexer.clear(); + } +} + + +// html tools + +OUString ODatabaseForm::GetDataEncoded(bool _bURLEncoded,const Reference& SubmitButton, const css::awt::MouseEvent& MouseEvt) +{ + // Fill List of successful Controls + HtmlSuccessfulObjList aSuccObjList; + FillSuccessfulList( aSuccObjList, SubmitButton, MouseEvt ); + + + // Aggregate the list to OUString + OUStringBuffer aResult; + OUString aName; + OUString aValue; + + for ( HtmlSuccessfulObjList::iterator pSuccObj = aSuccObjList.begin(); + pSuccObj < aSuccObjList.end(); + ++pSuccObj + ) + { + aName = pSuccObj->aName; + aValue = pSuccObj->aValue; + if( pSuccObj->nRepresentation == SUCCESSFUL_REPRESENT_FILE && !aValue.isEmpty() ) + { + // For File URLs we transfer the file name and not a URL, because Netscape does it like that + INetURLObject aURL; + aURL.SetSmartProtocol(INetProtocol::File); + aURL.SetSmartURL(aValue); + if( INetProtocol::File == aURL.GetProtocol() ) + aValue = INetURLObject::decode(aURL.PathToFileName(), INetURLObject::DecodeMechanism::Unambiguous); + } + Encode( aName ); + Encode( aValue ); + + aResult.append(aName + "=" + aValue); + + if (pSuccObj < aSuccObjList.end() - 1) + { + if ( _bURLEncoded ) + aResult.append('&'); + else + aResult.append("\r\n"); + } + } + + + aSuccObjList.clear(); + + return aResult.makeStringAndClear(); +} + + +// html tools + +Sequence ODatabaseForm::GetDataMultiPartEncoded(const Reference& SubmitButton, const css::awt::MouseEvent& MouseEvt, OUString& rContentType) +{ + + // Create Parent + INetMIMEMessage aParent; + aParent.EnableAttachMultipartFormDataChild(); + + + // Fill List of successful Controls + HtmlSuccessfulObjList aSuccObjList; + FillSuccessfulList( aSuccObjList, SubmitButton, MouseEvt ); + + + // Aggregate List to OUString + for (auto const& succObj : aSuccObjList) + { + if( succObj.nRepresentation == SUCCESSFUL_REPRESENT_TEXT ) + InsertTextPart( aParent, succObj.aName, succObj.aValue ); + else if( succObj.nRepresentation == SUCCESSFUL_REPRESENT_FILE ) + InsertFilePart( aParent, succObj.aName, succObj.aValue ); + } + + + // Delete List + aSuccObjList.clear(); + + // Create MessageStream for parent + INetMIMEMessageStream aMessStream(&aParent, true); + + // Copy MessageStream to SvStream + SvMemoryStream aMemStream; + std::unique_ptr pBuf(new char[1025]); + int nRead; + while( (nRead = aMessStream.Read(pBuf.get(), 1024)) > 0 ) + { + aMemStream.WriteBytes(pBuf.get(), nRead); + } + pBuf.reset(); + + aMemStream.FlushBuffer(); + aMemStream.Seek( 0 ); + void const * pData = aMemStream.GetData(); + sal_Int32 nLen = aMemStream.TellEnd(); + + rContentType = aParent.GetContentType(); + return Sequence(static_cast(pData), nLen); +} + + +namespace +{ + void appendDigits( sal_Int32 _nNumber, sal_Int8 nDigits, OUStringBuffer& _rOut ) + { + sal_Int32 nCurLen = _rOut.getLength(); + _rOut.append( _nNumber ); + while ( _rOut.getLength() - nCurLen < nDigits ) + _rOut.insert( nCurLen, '0' ); + } +} + + +void ODatabaseForm::AppendComponent(HtmlSuccessfulObjList& rList, const Reference& xComponentSet, std::u16string_view rNamePrefix, + const Reference& rxSubmitButton, const css::awt::MouseEvent& MouseEvt) +{ + if (!xComponentSet.is()) + return; + + // TODO: Catch nested Forms; or would we need to submit them? + if (!hasProperty(PROPERTY_CLASSID, xComponentSet)) + return; + + // Get names + if (!hasProperty(PROPERTY_NAME, xComponentSet)) + return; + + sal_Int16 nClassId = 0; + xComponentSet->getPropertyValue(PROPERTY_CLASSID) >>= nClassId; + OUString aName; + xComponentSet->getPropertyValue( PROPERTY_NAME ) >>= aName; + if( aName.isEmpty() && nClassId != FormComponentType::IMAGEBUTTON) + return; + else // Extend name with the prefix + aName = rNamePrefix + aName; + + switch( nClassId ) + { + // Buttons + case FormComponentType::COMMANDBUTTON: + { + // We only evaluate the pressed Submit button + // If one is passed at all + if( rxSubmitButton.is() ) + { + Reference xSubmitButtonComponent(rxSubmitButton->getModel(), UNO_QUERY); + if (xSubmitButtonComponent == xComponentSet && hasProperty(PROPERTY_LABEL, xComponentSet)) + { + // =