summaryrefslogtreecommitdiffstats
path: root/forms
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /forms
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--forms/AllLangMoTarget_frm.mk13
-rw-r--r--forms/IwyuFilter_forms.yaml78
-rw-r--r--forms/JunitTest_forms_complex.mk22
-rw-r--r--forms/JunitTest_forms_unoapi_1.mk14
-rw-r--r--forms/JunitTest_forms_unoapi_2.mk14
-rw-r--r--forms/JunitTest_forms_unoapi_3.mk14
-rw-r--r--forms/JunitTest_forms_unoapi_4.mk14
-rw-r--r--forms/Library_frm.mk155
-rw-r--r--forms/Makefile7
-rw-r--r--forms/Module_forms.mk28
-rw-r--r--forms/README.md3
-rw-r--r--forms/inc/pch/precompiled_frm.cxx12
-rw-r--r--forms/inc/pch/precompiled_frm.hxx275
-rw-r--r--forms/inc/strings.hrc87
-rw-r--r--forms/qa/complex/forms/CheckOGroupBoxModel.java168
-rw-r--r--forms/qa/forms_all.sce25
-rw-r--r--forms/qa/integration/forms/BooleanValidator.java64
-rw-r--r--forms/qa/integration/forms/CellBinding.java543
-rw-r--r--forms/qa/integration/forms/ControlValidation.java176
-rw-r--r--forms/qa/integration/forms/ControlValidator.java45
-rw-r--r--forms/qa/integration/forms/DateValidator.java80
-rw-r--r--forms/qa/integration/forms/DocumentHelper.java375
-rw-r--r--forms/qa/integration/forms/DocumentType.java39
-rw-r--r--forms/qa/integration/forms/DocumentViewHelper.java221
-rw-r--r--forms/qa/integration/forms/FormComponent.java114
-rw-r--r--forms/qa/integration/forms/FormControlTest.java951
-rw-r--r--forms/qa/integration/forms/FormLayer.java318
-rw-r--r--forms/qa/integration/forms/FormPropertyBags.java201
-rw-r--r--forms/qa/integration/forms/ImageComparison.java79
-rw-r--r--forms/qa/integration/forms/ListBox.java285
-rw-r--r--forms/qa/integration/forms/ListSelection.java280
-rw-r--r--forms/qa/integration/forms/ListSelection.props1
-rw-r--r--forms/qa/integration/forms/ListSelectionValidator.java51
-rw-r--r--forms/qa/integration/forms/MasterDetailForms.java400
-rw-r--r--forms/qa/integration/forms/NumericValidator.java68
-rw-r--r--forms/qa/integration/forms/RadioButtons.java426
-rw-r--r--forms/qa/integration/forms/SingleControlValidation.java170
-rw-r--r--forms/qa/integration/forms/SpreadsheetDocument.java68
-rw-r--r--forms/qa/integration/forms/SpreadsheetView.java59
-rw-r--r--forms/qa/integration/forms/TableCellTextBinding.java192
-rw-r--r--forms/qa/integration/forms/TestCase.java150
-rw-r--r--forms/qa/integration/forms/TextValidator.java65
-rw-r--r--forms/qa/integration/forms/TimeValidator.java73
-rw-r--r--forms/qa/integration/forms/ValueBinding.java112
-rw-r--r--forms/qa/integration/forms/WaitForInput.java58
-rw-r--r--forms/qa/integration/forms/XMLFormSettings.java213
-rw-r--r--forms/qa/integration/forms/dbfTools.java73
-rw-r--r--forms/qa/org/openoffice/complex/forms/tools/ResultSet.java273
-rw-r--r--forms/qa/org/openoffice/xforms/Instance.java145
-rw-r--r--forms/qa/org/openoffice/xforms/Model.java99
-rw-r--r--forms/qa/org/openoffice/xforms/XMLDocument.java97
-rw-r--r--forms/qa/unoapi/forms_1.sce27
-rw-r--r--forms/qa/unoapi/forms_2.sce27
-rw-r--r--forms/qa/unoapi/forms_3.sce27
-rw-r--r--forms/qa/unoapi/forms_4.sce26
-rw-r--r--forms/qa/unoapi/knownissues.xcl151
-rw-r--r--forms/qa/unoapi/testdocuments/TestDB/testDB.dbfbin0 -> 11101 bytes
-rw-r--r--forms/qa/unoapi/testdocuments/TestDB/testDB.dbtbin0 -> 18435 bytes
-rw-r--r--forms/source/component/BaseListBox.hxx30
-rw-r--r--forms/source/component/Button.cxx782
-rw-r--r--forms/source/component/Button.hxx201
-rw-r--r--forms/source/component/CheckBox.cxx293
-rw-r--r--forms/source/component/CheckBox.hxx83
-rw-r--r--forms/source/component/Columns.cxx901
-rw-r--r--forms/source/component/Columns.hxx322
-rw-r--r--forms/source/component/ComboBox.cxx888
-rw-r--r--forms/source/component/ComboBox.hxx145
-rw-r--r--forms/source/component/Currency.cxx253
-rw-r--r--forms/source/component/Currency.hxx85
-rw-r--r--forms/source/component/DatabaseForm.cxx4042
-rw-r--r--forms/source/component/DatabaseForm.hxx528
-rw-r--r--forms/source/component/Date.cxx326
-rw-r--r--forms/source/component/Date.hxx116
-rw-r--r--forms/source/component/Edit.cxx718
-rw-r--r--forms/source/component/Edit.hxx178
-rw-r--r--forms/source/component/EditBase.cxx377
-rw-r--r--forms/source/component/EditBase.hxx96
-rw-r--r--forms/source/component/EventThread.cxx198
-rw-r--r--forms/source/component/EventThread.hxx106
-rw-r--r--forms/source/component/File.cxx275
-rw-r--r--forms/source/component/File.hxx96
-rw-r--r--forms/source/component/Filter.cxx882
-rw-r--r--forms/source/component/Filter.hxx138
-rw-r--r--forms/source/component/FixedText.cxx125
-rw-r--r--forms/source/component/FixedText.hxx67
-rw-r--r--forms/source/component/FormComponent.cxx2816
-rw-r--r--forms/source/component/FormattedField.cxx1022
-rw-r--r--forms/source/component/FormattedField.hxx182
-rw-r--r--forms/source/component/FormattedFieldWrapper.cxx366
-rw-r--r--forms/source/component/FormattedFieldWrapper.hxx89
-rw-r--r--forms/source/component/FormsCollection.cxx141
-rw-r--r--forms/source/component/FormsCollection.hxx149
-rw-r--r--forms/source/component/Grid.cxx992
-rw-r--r--forms/source/component/Grid.hxx200
-rw-r--r--forms/source/component/GroupBox.cxx162
-rw-r--r--forms/source/component/GroupBox.hxx82
-rw-r--r--forms/source/component/GroupManager.cxx422
-rw-r--r--forms/source/component/GroupManager.hxx194
-rw-r--r--forms/source/component/Hidden.cxx177
-rw-r--r--forms/source/component/Hidden.hxx76
-rw-r--r--forms/source/component/ImageButton.cxx249
-rw-r--r--forms/source/component/ImageButton.hxx101
-rw-r--r--forms/source/component/ImageControl.cxx992
-rw-r--r--forms/source/component/ImageControl.hxx196
-rw-r--r--forms/source/component/ListBox.cxx2202
-rw-r--r--forms/source/component/ListBox.hxx332
-rw-r--r--forms/source/component/Numeric.cxx202
-rw-r--r--forms/source/component/Numeric.hxx88
-rw-r--r--forms/source/component/Pattern.cxx237
-rw-r--r--forms/source/component/Pattern.hxx94
-rw-r--r--forms/source/component/RadioButton.cxx402
-rw-r--r--forms/source/component/RadioButton.hxx93
-rw-r--r--forms/source/component/Time.cxx334
-rw-r--r--forms/source/component/Time.hxx115
-rw-r--r--forms/source/component/cachedrowset.cxx175
-rw-r--r--forms/source/component/cachedrowset.hxx78
-rw-r--r--forms/source/component/clickableimage.cxx838
-rw-r--r--forms/source/component/clickableimage.hxx282
-rw-r--r--forms/source/component/cloneable.cxx59
-rw-r--r--forms/source/component/entrylisthelper.cxx325
-rw-r--r--forms/source/component/entrylisthelper.hxx196
-rw-r--r--forms/source/component/errorbroadcaster.cxx94
-rw-r--r--forms/source/component/errorbroadcaster.hxx60
-rw-r--r--forms/source/component/findpos.cxx47
-rw-r--r--forms/source/component/findpos.hxx31
-rw-r--r--forms/source/component/formcontrolfont.cxx578
-rw-r--r--forms/source/component/imgprod.cxx505
-rw-r--r--forms/source/component/imgprod.hxx96
-rw-r--r--forms/source/component/navigationbar.cxx487
-rw-r--r--forms/source/component/navigationbar.hxx116
-rw-r--r--forms/source/component/propertybaghelper.cxx338
-rw-r--r--forms/source/component/refvaluecomponent.cxx294
-rw-r--r--forms/source/component/refvaluecomponent.hxx87
-rw-r--r--forms/source/component/scrollbar.cxx317
-rw-r--r--forms/source/component/scrollbar.hxx93
-rw-r--r--forms/source/component/spinbutton.cxx271
-rw-r--r--forms/source/component/spinbutton.hxx89
-rw-r--r--forms/source/helper/commandimageprovider.cxx131
-rw-r--r--forms/source/helper/controlfeatureinterception.cxx147
-rw-r--r--forms/source/helper/formnavigation.cxx447
-rw-r--r--forms/source/helper/resettable.cxx79
-rw-r--r--forms/source/helper/urltransformer.cxx83
-rw-r--r--forms/source/helper/windowstateguard.cxx213
-rw-r--r--forms/source/inc/FormComponent.hxx1207
-rw-r--r--forms/source/inc/InterfaceContainer.hxx301
-rw-r--r--forms/source/inc/cloneable.hxx41
-rw-r--r--forms/source/inc/commandimageprovider.hxx53
-rw-r--r--forms/source/inc/componenttools.hxx84
-rw-r--r--forms/source/inc/controlfeatureinterception.hxx87
-rw-r--r--forms/source/inc/featuredispatcher.hxx101
-rw-r--r--forms/source/inc/formcontrolfont.hxx96
-rw-r--r--forms/source/inc/formnavigation.hxx217
-rw-r--r--forms/source/inc/frm_resource.hxx38
-rw-r--r--forms/source/inc/frm_strings.hxx287
-rw-r--r--forms/source/inc/limitedformats.hxx93
-rw-r--r--forms/source/inc/property.hxx340
-rw-r--r--forms/source/inc/propertybaghelper.hxx147
-rw-r--r--forms/source/inc/resettable.hxx62
-rw-r--r--forms/source/inc/services.hxx195
-rw-r--r--forms/source/inc/togglestate.hxx33
-rw-r--r--forms/source/inc/urltransformer.hxx70
-rw-r--r--forms/source/inc/windowstateguard.hxx70
-rw-r--r--forms/source/misc/InterfaceContainer.cxx1306
-rw-r--r--forms/source/misc/componenttools.cxx105
-rw-r--r--forms/source/misc/limitedformats.cxx379
-rw-r--r--forms/source/misc/property.cxx220
-rw-r--r--forms/source/resource/frm_resource.cxx30
-rw-r--r--forms/source/richtext/attributedispatcher.cxx115
-rw-r--r--forms/source/richtext/attributedispatcher.hxx79
-rw-r--r--forms/source/richtext/clipboarddispatcher.cxx191
-rw-r--r--forms/source/richtext/clipboarddispatcher.hxx91
-rw-r--r--forms/source/richtext/featuredispatcher.cxx140
-rw-r--r--forms/source/richtext/featuredispatcher.hxx89
-rw-r--r--forms/source/richtext/parametrizedattributedispatcher.cxx129
-rw-r--r--forms/source/richtext/parametrizedattributedispatcher.hxx61
-rw-r--r--forms/source/richtext/richtextcontrol.cxx652
-rw-r--r--forms/source/richtext/richtextcontrol.hxx128
-rw-r--r--forms/source/richtext/richtextengine.cxx138
-rw-r--r--forms/source/richtext/richtextengine.hxx71
-rw-r--r--forms/source/richtext/richtextimplcontrol.cxx639
-rw-r--r--forms/source/richtext/richtextimplcontrol.hxx183
-rw-r--r--forms/source/richtext/richtextmodel.cxx641
-rw-r--r--forms/source/richtext/richtextmodel.hxx186
-rw-r--r--forms/source/richtext/richtextunowrapper.cxx116
-rw-r--r--forms/source/richtext/richtextunowrapper.hxx81
-rw-r--r--forms/source/richtext/richtextvclcontrol.cxx351
-rw-r--r--forms/source/richtext/richtextvclcontrol.hxx122
-rw-r--r--forms/source/richtext/richtextviewport.cxx102
-rw-r--r--forms/source/richtext/richtextviewport.hxx64
-rw-r--r--forms/source/richtext/rtattributehandler.cxx441
-rw-r--r--forms/source/richtext/rtattributehandler.hxx163
-rw-r--r--forms/source/richtext/rtattributes.hxx132
-rw-r--r--forms/source/richtext/specialdispatchers.cxx171
-rw-r--r--forms/source/richtext/specialdispatchers.hxx91
-rw-r--r--forms/source/richtext/textattributelistener.hxx53
-rw-r--r--forms/source/runtime/formoperations.cxx1766
-rw-r--r--forms/source/runtime/formoperations.hxx382
-rw-r--r--forms/source/solar/component/navbarcontrol.cxx500
-rw-r--r--forms/source/solar/component/navbarcontrol.hxx128
-rw-r--r--forms/source/solar/control/navtoolbar.cxx641
-rw-r--r--forms/source/solar/inc/navtoolbar.hxx164
-rw-r--r--forms/source/xforms/NameContainer.hxx159
-rw-r--r--forms/source/xforms/binding.cxx1275
-rw-r--r--forms/source/xforms/binding.hxx406
-rw-r--r--forms/source/xforms/boolexpression.cxx42
-rw-r--r--forms/source/xforms/boolexpression.hxx44
-rw-r--r--forms/source/xforms/collection.hxx280
-rw-r--r--forms/source/xforms/computedexpression.cxx182
-rw-r--r--forms/source/xforms/computedexpression.hxx121
-rw-r--r--forms/source/xforms/convert.cxx336
-rw-r--r--forms/source/xforms/convert.hxx78
-rw-r--r--forms/source/xforms/datatyperepository.cxx295
-rw-r--r--forms/source/xforms/datatyperepository.hxx83
-rw-r--r--forms/source/xforms/datatypes.cxx1199
-rw-r--r--forms/source/xforms/datatypes.hxx422
-rw-r--r--forms/source/xforms/enumeration.cxx59
-rw-r--r--forms/source/xforms/enumeration.hxx50
-rw-r--r--forms/source/xforms/evaluationcontext.hxx58
-rw-r--r--forms/source/xforms/mip.cxx98
-rw-r--r--forms/source/xforms/mip.hxx102
-rw-r--r--forms/source/xforms/model.cxx606
-rw-r--r--forms/source/xforms/model.hxx359
-rw-r--r--forms/source/xforms/model_helper.hxx146
-rw-r--r--forms/source/xforms/model_ui.cxx1004
-rw-r--r--forms/source/xforms/namedcollection.hxx111
-rw-r--r--forms/source/xforms/pathexpression.cxx113
-rw-r--r--forms/source/xforms/pathexpression.hxx71
-rw-r--r--forms/source/xforms/propertysetbase.cxx158
-rw-r--r--forms/source/xforms/propertysetbase.hxx340
-rw-r--r--forms/source/xforms/resourcehelper.cxx65
-rw-r--r--forms/source/xforms/resourcehelper.hxx41
-rw-r--r--forms/source/xforms/submission.cxx616
-rw-r--r--forms/source/xforms/submission.hxx219
-rw-r--r--forms/source/xforms/submission/replace.cxx131
-rw-r--r--forms/source/xforms/submission/serialization.hxx60
-rw-r--r--forms/source/xforms/submission/serialization_app_xml.cxx126
-rw-r--r--forms/source/xforms/submission/serialization_app_xml.hxx40
-rw-r--r--forms/source/xforms/submission/serialization_urlencoded.cxx181
-rw-r--r--forms/source/xforms/submission/serialization_urlencoded.hxx47
-rw-r--r--forms/source/xforms/submission/submission.hxx132
-rw-r--r--forms/source/xforms/submission/submission_get.cxx98
-rw-r--r--forms/source/xforms/submission/submission_get.hxx33
-rw-r--r--forms/source/xforms/submission/submission_post.cxx78
-rw-r--r--forms/source/xforms/submission/submission_post.hxx32
-rw-r--r--forms/source/xforms/submission/submission_put.cxx67
-rw-r--r--forms/source/xforms/submission/submission_put.hxx33
-rw-r--r--forms/source/xforms/unohelper.cxx73
-rw-r--r--forms/source/xforms/unohelper.hxx41
-rw-r--r--forms/source/xforms/xforms_services.cxx59
-rw-r--r--forms/source/xforms/xformsevent.cxx83
-rw-r--r--forms/source/xforms/xformsevent.hxx68
-rw-r--r--forms/source/xforms/xmlhelper.cxx133
-rw-r--r--forms/source/xforms/xmlhelper.hxx40
-rw-r--r--forms/source/xforms/xpathlib/extension.cxx74
-rw-r--r--forms/source/xforms/xpathlib/extension.hxx52
-rw-r--r--forms/source/xforms/xpathlib/xpathlib.cxx542
-rw-r--r--forms/source/xforms/xpathlib/xpathlib.hxx74
-rw-r--r--forms/util/frm.component295
-rw-r--r--forms/workben/xforms/sickness.odtbin0 -> 9233 bytes
-rw-r--r--forms/workben/xforms/simple_validation.odtbin0 -> 7184 bytes
-rw-r--r--forms/workben/xforms/vacationrequest.odtbin0 -> 8089 bytes
-rw-r--r--forms/workben/xforms/xforms-ubl.odtbin0 -> 22265 bytes
262 files changed, 65558 insertions, 0 deletions
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 <sal/config.h>
+#if PCH_LEVEL >= 1
+#include <algorithm>
+#include <cassert>
+#include <climits>
+#include <cstddef>
+#include <cstdlib>
+#include <functional>
+#include <iterator>
+#include <limits.h>
+#include <limits>
+#include <map>
+#include <memory>
+#include <new>
+#include <optional>
+#include <ostream>
+#include <set>
+#include <string.h>
+#include <string_view>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+#include <boost/lexical_cast.hpp>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/diagnose.h>
+#include <osl/endian.h>
+#include <osl/interlck.h>
+#include <osl/mutex.hxx>
+#include <rtl/alloc.h>
+#include <rtl/character.hxx>
+#include <rtl/instance.hxx>
+#include <rtl/math.h>
+#include <rtl/math.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/stringconcat.hxx>
+#include <rtl/stringutils.hxx>
+#include <rtl/tencinfo.h>
+#include <rtl/textenc.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <sal/types.h>
+#include <vcl/dllapi.h>
+#include <comphelper/errcode.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/image.hxx>
+#include <vcl/keycod.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/vclenum.hxx>
+#include <vcl/vclptr.hxx>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <basegfx/color/bcolor.hxx>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/XResetListener.hpp>
+#include <com/sun/star/form/XSubmit.hpp>
+#include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
+#include <com/sun/star/form/runtime/FormFeature.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/graphic/GraphicObject.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/i18n/ForbiddenCharacters.hpp>
+#include <com/sun/star/i18n/LanguageCountryInfo.hpp>
+#include <com/sun/star/i18n/LocaleDataItem2.hpp>
+#include <com/sun/star/i18n/LocaleItem.hpp>
+#include <com/sun/star/i18n/reservedWords.hpp>
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Type.h>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/XAggregation.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/uno/XWeak.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/xpath/XPathObjectType.hpp>
+#include <com/sun/star/xml/xpath/XXPathObject.hpp>
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+#include <comphelper/basicio.hxx>
+#include <comphelper/comphelperdllapi.h>
+#include <comphelper/enumhelper.hxx>
+#include <comphelper/guarding.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/streamsection.hxx>
+#include <comphelper/types.hxx>
+#include <comphelper/uno3.hxx>
+#include <connectivity/dbconversion.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbtoolsdllapi.hxx>
+#include <connectivity/formattedcolumnvalue.hxx>
+#include <cppuhelper/cppuhelperdllapi.h>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <cppuhelper/implbase_ex.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editengdllapi.h>
+#include <editeng/editstat.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <editeng/svxenum.hxx>
+#include <i18nlangtag/lang.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <sfx2/dllapi.h>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/groupid.hxx>
+#include <sfx2/shell.hxx>
+#include <sfx2/signaturestate.hxx>
+#include <sot/formats.hxx>
+#include <svl/cenumitm.hxx>
+#include <svl/eitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/svldllapi.h>
+#include <svl/typedwhich.hxx>
+#include <svtools/imageresourceaccess.hxx>
+#include <svx/svxdllapi.h>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/color.hxx>
+#include <tools/date.hxx>
+#include <tools/datetime.hxx>
+#include <tools/debug.hxx>
+#include <tools/degree.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/fontenum.hxx>
+#include <tools/gen.hxx>
+#include <tools/inetmime.hxx>
+#include <tools/lineend.hxx>
+#include <tools/link.hxx>
+#include <tools/long.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/ref.hxx>
+#include <tools/solar.h>
+#include <tools/stream.hxx>
+#include <tools/time.hxx>
+#include <tools/toolsdllapi.h>
+#include <tools/urlobj.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/sharedunocomponent.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/unotoolsdllapi.h>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#include <FormComponent.hxx>
+#include <commandimageprovider.hxx>
+#include <componenttools.hxx>
+#include <controlfeatureinterception.hxx>
+#include <featuredispatcher.hxx>
+#include <frm_resource.hxx>
+#include <frm_strings.hxx>
+#include <navtoolbar.hxx>
+#include <property.hxx>
+#include <propertybaghelper.hxx>
+#include <services.hxx>
+#include <urltransformer.hxx>
+#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 <unotools/resmgr.hxx>
+
+#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<String> tNames = new ArrayList<String>();
+
+ 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 <b>index</b>
+ * 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
+ <TRUE/> if the user pressed a key on the console, <FALSE/> 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> T query( Class<T> 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 <type scope="com.sun.star.drawing">DrawPage</type> 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> T query( Class<T> 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
+ <member scope="com.sun.star.util">URL::Complete</member> 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
+ <TRUE/> 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> T getControl( Object aModel, Class<T> 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> T query( Class<T> 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<aValues.length; ++i )
+ if ( !implExecuteStatement( sInsertionPrefix + aValues[ i ] + ")" ) )
+ {
+ failed( "could not create the required sample data" );
+ return false;
+ }
+
+ connection.refreshTables();
+
+ // do not need the connection anymore
+ connection.close();
+
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /// checks the 3 radio buttons for the given states
+ private boolean checkRadios( short stateNone, short stateNormal, short stateImportant ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ if ( ((Short)getRadioModel( "radio_group", "none" ).getPropertyValue( "State" )).shortValue() != stateNone )
+ {
+ failed( "wrong value of the 'none' radio button!" );
+ }
+ else if ( ((Short)getRadioModel( "radio_group", "normal" ).getPropertyValue( "State" )).shortValue() != stateNormal )
+ {
+ failed( "wrong value of the 'normal' radio button!" );
+ }
+ else if ( ((Short)getRadioModel( "radio_group", "important" ).getPropertyValue( "State" )).shortValue() != stateImportant )
+ {
+ failed( "wrong value of the 'important' radio button!" );
+ }
+ else
+ return true;
+
+ return false;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean checkNullValue( String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ Object value = getControlModel( fieldName ).getPropertyValue( propertyName );
+ if ( !util.utils.isVoid( value ) )
+ {
+ log.println( "wrong value of the " + fieldName + " field!" );
+ log.println( " expected: <null/>" );
+ 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
+
+ <p>Note that <em>control<em> 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.
+ </p>
+
+ @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
+
+ <p>Note that <em>control<em> 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.
+ </p>
+
+ @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.
+
+ <p>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.</p>
+
+ @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<indexAccess.getCount(); ++i )
+ {
+ XPropertySet control = dbfTools.queryPropertySet( indexAccess.getByIndex( i ) );
+
+ if ( ((String)control.getPropertyValue( "Name" )).equals( name ) )
+ if ( ((String)control.getPropertyValue( "RefValue" )).equals( refValue ) )
+ return control;
+ }
+ return null;
+ }
+
+
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves a control model with a given (integer) access path
+ */
+ public XPropertySet getControlModel( int[] _accessPath ) throws com.sun.star.uno.Exception
+ {
+ XIndexAccess indexAcc = UnoRuntime.queryInterface( XIndexAccess.class,
+ m_document.getFormComponentTreeRoot() );
+ XPropertySet controlModel = null;
+ int i=0;
+ while ( ( indexAcc != null ) && ( i < _accessPath.length ) )
+ {
+ controlModel = UnoRuntime.queryInterface( XPropertySet.class,
+ indexAcc.getByIndex( _accessPath[i] ) );
+ indexAcc = UnoRuntime.queryInterface( XIndexAccess.class,
+ controlModel );
+ ++i;
+ }
+ return controlModel;
+ }
+
+
+
+ /* ------------------------------------------------------------------ */
+ /** simulates a user's text input into a control given by control model
+ */
+ public void userTextInput( XPropertySet controlModel, String text ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // we will *not* simply set the value property at the model. This is not the same as
+ // doing a user input, as the latter will trigger a lot of notifications, which the forms runtime environment
+ // (namely the FormController) relies on to notice that the control changed.
+ // Instead, we use the Accessibility interfaces of the control to simulate text input
+ XAccessible formattedAccessible = UnoRuntime.queryInterface( XAccessible.class,
+ m_document.getCurrentView().getControl( controlModel )
+ );
+ XAccessibleEditableText textAccess = UnoRuntime.queryInterface( XAccessibleEditableText.class,
+ formattedAccessible.getAccessibleContext() );
+ textAccess.setText( text );
+ }
+}
diff --git a/forms/qa/integration/forms/FormPropertyBags.java b/forms/qa/integration/forms/FormPropertyBags.java
new file mode 100644
index 0000000000..61aa984c17
--- /dev/null
+++ b/forms/qa/integration/forms/FormPropertyBags.java
@@ -0,0 +1,201 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.beans.PropertyAttribute;
+import com.sun.star.beans.PropertyChangeEvent;
+import com.sun.star.beans.PropertyExistException;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.beans.PropertyVetoException;
+import com.sun.star.beans.UnknownPropertyException;
+import com.sun.star.beans.XPropertyChangeListener;
+import com.sun.star.beans.XPropertyContainer;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.beans.XPropertySetInfo;
+import com.sun.star.frame.XStorable;
+import com.sun.star.lang.EventObject;
+import com.sun.star.uno.UnoRuntime;
+
+import com.sun.star.lang.XMultiServiceFactory;
+
+import com.sun.star.util.XCloseable;
+
+public class FormPropertyBags extends complexlib.ComplexTestCase implements XPropertyChangeListener
+{
+ private DocumentHelper m_document;
+ private FormLayer m_formLayer;
+ private XMultiServiceFactory m_orb;
+
+ private PropertyChangeEvent m_propertyChangeEvent;
+
+ /** Creates a new instance of FormPropertyBags */
+ public FormPropertyBags()
+ {
+ m_propertyChangeEvent = new PropertyChangeEvent();
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkSomething"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Component Property Bag Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void before() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ m_orb = param.getMSF();
+ m_document = DocumentHelper.blankTextDocument( m_orb );
+ m_formLayer = new FormLayer( m_document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_closeDoc() throws com.sun.star.uno.Exception
+ {
+ if ( m_document != null )
+ {
+ XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class, m_document.getDocument() );
+ closeDoc.close( true );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void after() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ impl_closeDoc();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkSomething() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet textFieldModel = m_formLayer.createControlAndShape( "DatabaseTextField", 10, 10, 25, 6 );
+
+ // check whether adding new properties is successful
+ XPropertyContainer propContainer = UnoRuntime.queryInterface(
+ XPropertyContainer.class, textFieldModel );
+ assure("XPropertyContainer not supported!", propContainer != null );
+
+ propContainer.addProperty( "SomeBoundText", PropertyAttribute.BOUND, "InitialBoundText" );
+ propContainer.addProperty( "SomeTransientText", PropertyAttribute.TRANSIENT, "InitialTransientProperty" );
+ propContainer.addProperty( "SomeReadonlyText", PropertyAttribute.READONLY, "InitialReadonlyText" );
+ propContainer.addProperty( "SomeNumericValue", PropertyAttribute.BOUND, Integer.valueOf( 42 ) );
+
+ XPropertySetInfo propertyInfo = textFieldModel.getPropertySetInfo();
+ assure( "Per service definition, dynamic properties are expected to be forced to be removable",
+ ( propertyInfo.getPropertyByName("SomeBoundText").Attributes & PropertyAttribute.REMOVABLE ) != 0 );
+
+ // a second addition of a property with an existent name should be rejected
+ boolean caughtExpected = false;
+ try { propContainer.addProperty( "SomeBoundText", PropertyAttribute.BOUND, "InitialBoundText" ); }
+ catch( PropertyExistException e ) { caughtExpected = true; }
+ catch( Exception e ) { }
+ assure( "repeated additions of a property with the same name should be rejected",
+ caughtExpected );
+
+ // check whether the properties are bound as expected
+ impl_checkPropertyValueNotification( textFieldModel );
+
+ // check property value persistence
+ impl_checkPropertyPersistence();
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_checkPropertyValueNotification( XPropertySet _controlModel ) throws com.sun.star.uno.Exception
+ {
+ _controlModel.addPropertyChangeListener( "", this );
+
+ _controlModel.setPropertyValue( "SomeBoundText", "ChangedBoundText" );
+ assure( "changes in the bound property are not properly notified",
+ m_propertyChangeEvent.PropertyName.equals( "SomeBoundText" )
+ && m_propertyChangeEvent.OldValue.equals( "InitialBoundText" )
+ && m_propertyChangeEvent.NewValue.equals( "ChangedBoundText" ) );
+
+ m_propertyChangeEvent = null;
+ _controlModel.setPropertyValue( "SomeTransientText", "ChangedTransientText" );
+ assure( "changes in non-bound properties should not be notified",
+ m_propertyChangeEvent == null );
+
+ boolean caughtExpected = false;
+ try { _controlModel.setPropertyValue( "SomeReadonlyText", "ChangedReadonlyText" ); }
+ catch( PropertyVetoException e ) { caughtExpected = true; }
+ catch( Exception e ) { }
+ assure( "trying to write a read-only property did not give the expected result",
+ caughtExpected );
+
+ _controlModel.removePropertyChangeListener( "", this );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_checkPropertyPersistence() throws com.sun.star.uno.Exception
+ {
+ // store the document
+ XStorable store = UnoRuntime.queryInterface( XStorable.class, m_document.getDocument() );
+ String documentURL = util.utils.getOfficeTemp( m_orb ) + "document.odt";
+ PropertyValue[] storeArguments = new PropertyValue[] { new PropertyValue() };
+ storeArguments[0].Name = "FilterName";
+ storeArguments[0].Value = "writer8";
+ store.storeAsURL( documentURL, storeArguments );
+
+ // close and re-load it
+ impl_closeDoc();
+
+ m_document = DocumentHelper.loadDocument( m_orb, documentURL );
+ m_formLayer = new FormLayer( m_document );
+
+ XPropertySet textFieldModel = m_formLayer.getControlModel( new int[] { 0, 0 } );
+
+ // all persistent properties should have the expected values
+ assure( "persistent properties did not survive reload (1)!", ((String)textFieldModel.getPropertyValue( "SomeBoundText" )).equals( "ChangedBoundText" ) );
+ assure( "persistent properties did not survive reload (2)!", ((String)textFieldModel.getPropertyValue( "SomeReadonlyText" )).equals( "InitialReadonlyText" ) );
+// assure( "persistent properties did not survive reload (3)!", ((Integer)textFieldModel.getPropertyValue( "SomeNumericValue" )).equals( Integer.valueOf( 42 ) ) );
+ // cannot check this until the types really survive - at the moment, integers are converted to doubles...
+
+ // the transient property should not have survived
+ boolean caughtExpected = false;
+ try { textFieldModel.getPropertyValue( "SomeTransientText" ); }
+ catch( UnknownPropertyException e ) { caughtExpected = true; }
+ assure( "transient property did survive reload!", caughtExpected );
+
+ // There would be more things to check.
+ // For instance, it would be desirable of the property attributes would have survived
+ // the reload, and the property defaults (XPropertyState).
+ // However, the file format currently doesn't allow for this, so those information
+ // is lost when saving the document.
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void propertyChange(PropertyChangeEvent _propertyChangeEvent)
+ {
+ m_propertyChangeEvent = _propertyChangeEvent;
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void disposing(EventObject eventObject)
+ {
+ // not interested in
+ }
+}
diff --git a/forms/qa/integration/forms/ImageComparison.java b/forms/qa/integration/forms/ImageComparison.java
new file mode 100644
index 0000000000..e28771fc72
--- /dev/null
+++ b/forms/qa/integration/forms/ImageComparison.java
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 final class ImageComparison implements com.sun.star.awt.XImageConsumer
+{
+
+ private final byte[] m_referenceBytes;
+ private int m_referencePosition;
+ private final Object m_notifyDone;
+
+ public boolean imagesEqual( )
+ {
+ return m_referencePosition == m_referenceBytes.length;
+ }
+
+ /** Creates a new instance of ImageComparison */
+ public ImageComparison( byte[] referenceBytes, Object toNotify )
+ {
+ m_referenceBytes = referenceBytes;
+ m_referencePosition = 0;
+ m_notifyDone = toNotify;
+ }
+
+ public void complete(int param, com.sun.star.awt.XImageProducer xImageProducer)
+ {
+ synchronized( m_notifyDone )
+ {
+ m_notifyDone.notify();
+ }
+ }
+
+ public void init(int param, int param1)
+ {
+ }
+
+ public void setColorModel(short param, int[] values, int param2, int param3, int param4, int param5)
+ {
+ }
+
+ public void setPixelsByBytes(int param, int param1, int param2, int param3, byte[] values, int param5, int param6)
+ {
+ if ( m_referencePosition == -1 )
+ // already failed
+ return;
+
+ int i = 0;
+ while ( ( m_referencePosition < m_referenceBytes.length ) && ( i < values.length ) )
+ {
+ if ( m_referenceBytes[ m_referencePosition ] != values[ i ] )
+ {
+ m_referencePosition = -1;
+ break;
+ }
+ ++i;
+ ++m_referencePosition;
+ }
+ }
+
+ public void setPixelsByLongs(int param, int param1, int param2, int param3, int[] values, int param5, int param6)
+ {
+ }
+
+}
diff --git a/forms/qa/integration/forms/ListBox.java b/forms/qa/integration/forms/ListBox.java
new file mode 100644
index 0000000000..fc60b48376
--- /dev/null
+++ b/forms/qa/integration/forms/ListBox.java
@@ -0,0 +1,285 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.sun.star.awt.XListBox;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.container.XChild;
+import com.sun.star.container.XIndexAccess;
+import com.sun.star.container.XNameAccess;
+import com.sun.star.form.ListSourceType;
+import com.sun.star.form.runtime.FormFeature;
+import com.sun.star.form.runtime.XFormController;
+import com.sun.star.form.runtime.XFormOperations;
+import com.sun.star.sdb.CommandType;
+import com.sun.star.sdbc.XParameters;
+import com.sun.star.sdbc.XPreparedStatement;
+import com.sun.star.uno.Exception;
+import com.sun.star.uno.UnoRuntime;
+
+import connectivity.tools.HsqlColumnDescriptor;
+import connectivity.tools.HsqlDatabase;
+import connectivity.tools.HsqlTableDescriptor;
+import connectivity.tools.sdb.Connection;
+
+public class ListBox extends TestCase
+{
+ HsqlDatabase m_database = null;
+ private static final String m_foreignKeyTableName = "foreign_keys";
+
+ public ListBox()
+ {
+ super( DocumentType.WRITER );
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkForeignKeys"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkForeignKeys() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ try
+ {
+ // create the form document
+ prepareDocument();
+
+ final XIndexAccess formsCollection = UnoRuntime.queryInterface( XIndexAccess.class,
+ m_document.getFormComponentTreeRoot() );
+ final XNameAccess form = UnoRuntime.queryInterface( XNameAccess.class, formsCollection.getByIndex(0) );
+
+ final DocumentViewHelper view = m_document.getCurrentView();
+ final XFormController formController = view.getFormController( form );
+ final XFormOperations formOperations = formController.getFormOperations();
+
+ // move through all records, and check that the display values in the list boxes are as expected
+ final String[][] fieldTypesDefinitions = impl_getFieldTypeDefinitions();
+ final String[] fieldTypes = fieldTypesDefinitions[0];
+
+ final String[] displayValues = impl_getDisplayValues();
+
+ formOperations.execute( FormFeature.MoveToFirst );
+ for ( int row=0; row<2; ++row )
+ {
+ StringBuilder failedFieldTypes = new StringBuilder();
+ for ( int i=0; i<fieldTypes.length; ++i )
+ {
+ final String columnFKName = fieldTypes[i] + "_fk";
+ Object listBoxModel = form.getByName( columnFKName );
+ XListBox listBoxControl = UnoRuntime.queryInterface( XListBox.class,
+ view.getControl( listBoxModel ) );
+ if ( !listBoxControl.getSelectedItem().equals( displayValues[row] ) )
+ {
+ if ( failedFieldTypes.length() > 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<keyTypes.length; ++i )
+ {
+ final String tableName = keyTypes[i] + "_pk";
+ final String columnPKName = keyTypes[i] + "_pk";
+ final String columnFKName = keyTypes[i] + "_fk";
+ final String columnType = keyTypes[i] + ( keyCreationArgs[i] != null ? keyCreationArgs[i] : "" );
+ m_database.createTable( new HsqlTableDescriptor( tableName,
+ new HsqlColumnDescriptor[] {
+ new HsqlColumnDescriptor( columnPKName, columnType, HsqlColumnDescriptor.PRIMARY ),
+ new HsqlColumnDescriptor( "content", "varchar(50)" )
+ }
+ ) );
+
+ // insert a few rows
+ StringBuilder sql = new StringBuilder();
+ sql.append( "INSERT INTO \"" );
+ sql.append( tableName );
+ sql.append( "\" VALUES (?, ?)");
+ XPreparedStatement statement = connection.prepareStatement( sql.toString() );
+ XParameters statementParameters = UnoRuntime.queryInterface( XParameters.class, statement );
+
+ final String[] keyValues = impl_getTypedValue( keyTypes[i] );
+
+ for ( int row=0; row<displayValues.length; ++row )
+ {
+ statementParameters.setString( 1, keyValues[row] );
+ statementParameters.setString( 2, displayValues[row] );
+ statement.execute();
+ }
+
+ // remember a column descriptor for later creation of the table with the foreign keys
+ foreignKeyColumns.add( new HsqlColumnDescriptor( columnFKName, columnType, HsqlColumnDescriptor.REQUIRED,
+ tableName, columnPKName ) );
+
+ // remember the data to fill into this table
+ foreignKeyValues.add( keyValues );
+ foreignKeyInsertSQL.append( ", ?" );
+ }
+
+ // create the table taking all those foreign keys
+ m_database.createTable( new HsqlTableDescriptor( m_foreignKeyTableName, foreignKeyColumns.toArray( new HsqlColumnDescriptor[foreignKeyColumns.size()] ) ) );
+ // fill in some data
+ foreignKeyInsertSQL.append( ")" );
+ XPreparedStatement statement = connection.prepareStatement( foreignKeyInsertSQL.toString() );
+ XParameters statementParameters = UnoRuntime.queryInterface( XParameters.class, statement );
+ for ( int row=0; row<2; ++row )
+ {
+ statementParameters.setInt( 1, row );
+ for ( int i=0; i<keyTypes.length; ++i )
+ {
+ statementParameters.setString( i+2, foreignKeyValues.get(i)[row] );
+ }
+ statement.execute();
+ }
+
+ m_database.defaultConnection().refreshTables();
+ }
+ finally
+ {
+ if ( m_database != null )
+ m_database.store();
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_createForm() throws java.lang.Exception
+ {
+ // a single control for the ID field
+ XPropertySet controlModel = m_formLayer.insertControlLine( "DatabaseNumericField", "ID", null, 10, 10, 6 );
+ // bind the form to the foreign_keys table
+ XPropertySet form = dbfTools.queryPropertySet( dbfTools.getParent( controlModel, XChild.class ) );
+ form.setPropertyValue( "Command", m_foreignKeyTableName );
+ form.setPropertyValue( "CommandType", CommandType.TABLE );
+ form.setPropertyValue( "DataSourceName", m_database.getDocumentURL() );
+
+ // create list boxes for the different foreign keys
+ final String[][] fieldDefinitions = impl_getFieldTypeDefinitions();
+ final String[] fieldTypes = fieldDefinitions[0];
+ for ( int i=0; i<fieldTypes.length; ++i )
+ {
+ final String tableName = fieldTypes[i] + "_pk";
+ final String columnFKName = fieldTypes[i] + "_fk";
+ final String columnPKName = fieldTypes[i] + "_pk";
+ XPropertySet listBoxModel = m_formLayer.insertControlLine( "DatabaseListBox", columnFKName, null, 10, 20 + 10*i, 6 );
+ listBoxModel.setPropertyValue( "Dropdown", Boolean.TRUE );
+ listBoxModel.setPropertyValue( "ListSourceType", ListSourceType.SQL );
+ listBoxModel.setPropertyValue( "ListSource", new String[] { "SELECT \"content\", \"" + columnPKName +
+ "\" FROM \"" + tableName + "\"" } );
+ listBoxModel.setPropertyValue( "BoundColumn", Short.valueOf( (short)1 ) );
+ }
+
+ m_document.getCurrentView().toggleFormDesignMode();
+ }
+ }
diff --git a/forms/qa/integration/forms/ListSelection.java b/forms/qa/integration/forms/ListSelection.java
new file mode 100644
index 0000000000..5e9fd8a55e
--- /dev/null
+++ b/forms/qa/integration/forms/ListSelection.java
@@ -0,0 +1,280 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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.sheet.XSpreadsheet;
+import com.sun.star.sheet.XSpreadsheets;
+import com.sun.star.sheet.XSpreadsheetView;
+import com.sun.star.container.XNamed;
+import com.sun.star.container.XNameContainer;
+import com.sun.star.container.XIndexContainer;
+import com.sun.star.drawing.XDrawPageSupplier;
+import com.sun.star.awt.XControlModel;
+import com.sun.star.awt.XListBox;
+import com.sun.star.script.XLibraryContainer;
+import com.sun.star.script.XEventAttacherManager;
+import com.sun.star.script.ScriptEventDescriptor;
+import com.sun.star.accessibility.XAccessible;
+import com.sun.star.accessibility.XAccessibleContext;
+import com.sun.star.accessibility.XAccessibleSelection;
+import com.sun.star.frame.XStorable;
+
+public class ListSelection extends integration.forms.TestCase
+{
+ /** Creates a new instance of ListSelection */
+ public ListSelection()
+ {
+ super( DocumentType.CALC );
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkUserListSelection"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Control List Selection Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkUserListSelection() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ int runs = 5;
+ for ( int i = 0; i < runs; ++i )
+ {
+ log.println( "Round " + ( i + 1 ) + " of " + runs );
+ prepareDocument();
+ impl_clickListBox();
+ synchronized( this ) { this.wait( 1000 ); }
+ closeDocument();
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ final private void impl_clickListBox()
+ {
+ try
+ {
+ final int runs = 10;
+ java.util.Random generator = new java.util.Random();
+ for ( int i = 0; i < runs; ++i )
+ {
+ // obtain the active sheet
+ XSpreadsheetView view = m_document.getCurrentView().query( XSpreadsheetView.class );
+ XSpreadsheet activeSheet = view.getActiveSheet();
+
+ // Accessibility access to the list box control in this sheet
+ XAccessible accessibleListBox = UnoRuntime.queryInterface(
+ XAccessible.class, getListBoxControl( activeSheet ) );
+ XAccessibleContext context = accessibleListBox.getAccessibleContext();
+
+ // the first "accessible child" of a list box is its list
+ XAccessibleSelection accessibleList = UnoRuntime.queryInterface(
+ XAccessibleSelection.class, context.getAccessibleChild( 1 ) );
+
+ int selectPosition = generator.nextInt( 5 );
+ String selectSheetName = getListBoxControl( activeSheet ).getItem( (short)selectPosition );
+ accessibleList.selectAccessibleChild( selectPosition );
+ try
+ {
+ synchronized( this )
+ {
+ this.wait( 500 );
+ }
+ }
+ catch( InterruptedException e ) { }
+
+ XNamed sheetName = UnoRuntime.queryInterface( XNamed.class, view.getActiveSheet() );
+ assure( "sheet was not selected as expected!", sheetName.getName().equals( selectSheetName ) );
+ }
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ failed( "caught an exception: " + e.toString() );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ final private void impl_setupListenerScript()
+ {
+ try
+ {
+ XPropertySet docProps = dbfTools.queryPropertySet( m_document.getDocument() );
+ XLibraryContainer basicLibs = UnoRuntime.queryInterface(
+ XLibraryContainer.class, docProps.getPropertyValue( "BasicLibraries" ) );
+ XNameContainer basicLib = basicLibs.createLibrary( "default" );
+
+ String sListSelectionScript =
+ "Option Explicit\n" +
+ "\n" +
+ "Sub onListBoxSelected( oEvent as Object )\n" +
+ " Dim oView as Object\n" +
+ " Dim oSheet as Object\n" +
+ " Dim oSheets as Object\n" +
+ "\n" +
+ " Dim oControlModel as Object\n" +
+ " Dim sSheet as String\n" +
+ "\n" +
+ " if ( oEvent.Selected <> 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<radios.length; ++i )
+ {
+ actualStates[i] = ((Short)radios[i].getPropertyValue( "State" )).shortValue();
+ }
+
+ // now actually check the states
+ for ( int i = 0; i<radios.length; ++i )
+ {
+ if ( actualStates[i] != expectedStates[i] )
+ {
+ failed( errorMessage + " (expected: " + Arrays.toString( expectedStates ) + ", found: " + Arrays.toString( actualStates ) + ")" );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of the 4 radio buttons from the checkSingleButtons test
+ */
+ private boolean verifySingleRadios( int state1, int state2, int state3, int state4 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet[] radios = new XPropertySet[4];
+ radios[0] = getRadioModel( "group 1", "" );
+ radios[1] = getRadioModel( "group 2", "" );
+ radios[2] = getRadioModel( "group 3", "" );
+ radios[3] = getRadioModel( "group 4", "" );
+
+ short[] states = new short[4];
+ states[0] = (short)state1;
+ states[1] = (short)state2;
+ states[2] = (short)state3;
+ states[3] = (short)state4;
+
+ return verifyRadios( radios, states, "single-group radio buttons do not work!" );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of 6 radio buttons form the checkThreeGroups test
+ */
+ private boolean verifySixPack( XPropertySet[] radios, String errorMessage,
+ int state1, int state2, int state3, int state4, int state5, int state6 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ short[] states = new short[6];
+ states[0] = (short)state1;
+ states[1] = (short)state2;
+ states[2] = (short)state3;
+ states[3] = (short)state4;
+ states[4] = (short)state5;
+ states[5] = (short)state6;
+
+ return verifyRadios( radios, states, errorMessage );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of 6 radio buttons
+ */
+ private boolean verifySixPack( int state1, int state2, int state3, int state4, int state5, int state6 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet[] radios = new XPropertySet[6];
+ radios[0] = getRadioModel( "group 1", "a" );
+ radios[1] = getRadioModel( "group 1", "b" );
+ radios[2] = getRadioModel( "group 2", "a" );
+ radios[3] = getRadioModel( "group 2", "b" );
+ radios[4] = getRadioModel( "group 3", "a" );
+ radios[5] = getRadioModel( "group 3", "b" );
+
+ return verifySixPack( radios, "six radio buttons, forming three different groups, do not properly work!",
+ state1, state2, state3, state4, state5, state6 );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of the 6 radio buttons in our checkMultipleForms test
+ */
+ private boolean verifyTwoFormRadios( int state1, int state2, int state3, int state4, int state5, int state6 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet[] radios = new XPropertySet[6];
+ radios[0] = getRadioModel( "group 1", "a", m_primaryForm );
+ radios[1] = getRadioModel( "group 1", "b", m_primaryForm );
+ radios[2] = getRadioModel( "group 1", "c", m_primaryForm );
+ radios[3] = getRadioModel( "group 2", "a", m_secondaryForm );
+ radios[4] = getRadioModel( "group 2", "b", m_secondaryForm );
+ radios[5] = getRadioModel( "group 2", "c", m_secondaryForm );
+
+ return verifySixPack( radios, "radio buttons on different forms do not work properly!",
+ state1, state2, state3, state4, state5, state6 );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of the 4 radio buttons in our spreadsheet document (checkCalcPageSwitch)
+ */
+ private boolean verifySheetRadios( int state1, int state2, int state3, int state4 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet[] radios = new XPropertySet[4];
+ radios[0] = getRadioModel( "group 1", "a", m_primaryForm );
+ radios[1] = getRadioModel( "group 1", "b", m_primaryForm );
+ radios[2] = getRadioModel( "group 2", "a", m_secondaryForm );
+ radios[3] = getRadioModel( "group 2", "b", m_secondaryForm );
+
+ short[] states = new short[4];
+ states[0] = (short)state1;
+ states[1] = (short)state2;
+ states[2] = (short)state3;
+ states[3] = (short)state4;
+
+ return verifyRadios( radios, states, "seems some of the radio button check states didn't survive the page activation(s)!" );
+ }
+}
+
diff --git a/forms/qa/integration/forms/SingleControlValidation.java b/forms/qa/integration/forms/SingleControlValidation.java
new file mode 100644
index 0000000000..0873d34ac8
--- /dev/null
+++ b/forms/qa/integration/forms/SingleControlValidation.java
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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.beans.*;
+import com.sun.star.form.validation.*;
+
+public class SingleControlValidation implements XFormComponentValidityListener
+{
+ private final DocumentHelper m_document; /// our current test document
+ private final FormLayer m_formLayer; /// quick access to the form layer
+
+ private XPropertySet m_inputField;
+ private XPropertySet m_inputLabel;
+ private XPropertySet m_statusField;
+ private XPropertySet m_explanationField;
+ private final XValidator m_validator;
+
+ /* ------------------------------------------------------------------ */
+ public SingleControlValidation( DocumentHelper document, int columnPos, int rowPos, String formComponentService, XValidator validator )
+ {
+ m_document = document;
+ m_validator = validator;
+ m_formLayer = new FormLayer( m_document );
+ createControls( columnPos, rowPos, formComponentService, 1, 0 );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public SingleControlValidation( DocumentHelper document, int columnPos, int rowPos, String formComponentService, XValidator validator, int controlCount, int controlHeight )
+ {
+ m_document = document;
+ m_validator = validator;
+ m_formLayer = new FormLayer( m_document );
+ createControls( columnPos, rowPos, formComponentService, controlCount, controlHeight );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public XPropertySet getInputField()
+ {
+ return m_inputField;
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void setExplanatoryText( String text )
+ {
+ try
+ {
+ m_inputLabel.setPropertyValue( "Label", text );
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void createControls( int columnPos, int rowPos, String formComponentService, int controlCount, int controlHeight )
+ {
+ try
+ {
+ m_inputLabel = m_formLayer.createControlAndShape( "FixedText", columnPos, rowPos, 70, 12, null );
+ m_inputLabel.setPropertyValue( "MultiLine", Boolean.TRUE );
+
+ com.sun.star.awt.FontDescriptor font = (com.sun.star.awt.FontDescriptor)m_inputLabel.getPropertyValue( "FontDescriptor" );
+ font.Weight = com.sun.star.awt.FontWeight.BOLD;
+ m_inputLabel.setPropertyValue( "FontDescriptor", font );
+
+ if ( controlHeight == 0 )
+ controlHeight = 6;
+
+ int controlPos = rowPos + 12;
+ XPropertySet[] controls = new XPropertySet[ controlCount ];
+ for ( int i = 0; i < controlCount; ++i, controlPos += controlHeight )
+ {
+ controls[ i ] = m_formLayer.createControlAndShape( formComponentService, columnPos, controlPos, 25, controlHeight, null );
+ controls[ i ].setPropertyValue( "Name", formComponentService );
+ controls[ i ].setPropertyValue( "Tag", String.valueOf( i ) );
+
+ if ( controls[ i ].getPropertySetInfo().hasPropertyByName( "Border" ) )
+ controls[ i ].setPropertyValue( "Border", Short.valueOf( (short)2 ) );
+
+ XValidatableFormComponent xComp = UnoRuntime.queryInterface( XValidatableFormComponent.class,
+ controls[ i ] );
+ xComp.addFormComponentValidityListener( this );
+ }
+ m_inputField = controls[ 0 ];
+
+
+ controlPos += 4;
+ XPropertySet xLabel = m_formLayer.createControlAndShape( "FixedText", columnPos, controlPos, 70, 4, null );
+ xLabel.setPropertyValue( "Label", "Status:" );
+ controlPos += 4;
+ m_statusField = m_formLayer.createControlAndShape( "FixedText", columnPos, controlPos, 70, 4, null );
+ m_statusField.setPropertyValue( "Label", "" );
+
+
+ controlPos += 6;
+ xLabel = m_formLayer.createControlAndShape( "FixedText", columnPos, controlPos, 70, 4, null );
+ xLabel.setPropertyValue( "Label", "Explanation for invalidity:" );
+ controlPos += 4;
+ m_explanationField = m_formLayer.createControlAndShape( "FixedText", columnPos, controlPos, 70, 4, null );
+ m_explanationField.setPropertyValue( "Label", "" );
+
+ XValidatable xValidatable = UnoRuntime.queryInterface( XValidatable.class, m_inputField );
+ xValidatable.setValidator( m_validator );
+ }
+ catch( java.lang.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* XEventListener overridables */
+ /* ------------------------------------------------------------------ */
+ public void disposing( com.sun.star.lang.EventObject eventObject )
+ {
+ // not interested in
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* XFormComponentValidityListener overridables */
+ /* ------------------------------------------------------------------ */
+ public void componentValidityChanged( com.sun.star.lang.EventObject eventObject )
+ {
+ try
+ {
+ if ( m_inputField.equals( eventObject.Source ) )
+ {
+ XValidatableFormComponent xComp = UnoRuntime.queryInterface( XValidatableFormComponent.class,
+ eventObject.Source );
+ // the current value
+ Object value = xComp.getCurrentValue();
+
+ // the current validity flag
+ boolean isValid = xComp.isValid();
+
+ m_statusField.setPropertyValue("Label", isValid ? "valid" : "invalid" );
+ m_statusField.setPropertyValue( "TextColor", Integer.valueOf( isValid ? 0x008000 : 0x800000 ) );
+
+ String validityMessage = "";
+ if ( !isValid )
+ validityMessage = m_validator.explainInvalid( value );
+ m_explanationField.setPropertyValue( "Label", validityMessage );
+ }
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ }
+
+}
diff --git a/forms/qa/integration/forms/SpreadsheetDocument.java b/forms/qa/integration/forms/SpreadsheetDocument.java
new file mode 100644
index 0000000000..2db38ab923
--- /dev/null
+++ b/forms/qa/integration/forms/SpreadsheetDocument.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;
+
+import com.sun.star.uno.*;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XComponent;
+import com.sun.star.table.XCellRange;
+import com.sun.star.container.XIndexAccess;
+import com.sun.star.sheet.XSpreadsheetDocument;
+import com.sun.star.sheet.XSpreadsheets;
+
+public class SpreadsheetDocument extends DocumentHelper
+{
+ /** Creates a new blank spreadsheet document */
+ /* ------------------------------------------------------------------ */
+ public SpreadsheetDocument( XMultiServiceFactory orb ) throws com.sun.star.uno.Exception
+ {
+ super( orb, implLoadAsComponent( orb, "private:factory/scalc" ) );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public SpreadsheetDocument( XMultiServiceFactory orb, XComponent document )
+ {
+ super( orb, document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** returns the sheets collection
+ */
+ public XSpreadsheets getSheets()
+ {
+ XSpreadsheetDocument spreadsheetDoc = UnoRuntime.queryInterface( XSpreadsheetDocument.class,
+ getDocument()
+ );
+ return spreadsheetDoc.getSheets();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** returns the sheet with the given index
+ */
+ public XCellRange getSheet( int index ) throws com.sun.star.uno.Exception
+ {
+ XIndexAccess sheets = UnoRuntime.queryInterface( XIndexAccess.class,
+ getSheets()
+ );
+ return UnoRuntime.queryInterface( XCellRange.class,
+ sheets.getByIndex( index )
+ );
+ }
+
+}
diff --git a/forms/qa/integration/forms/SpreadsheetView.java b/forms/qa/integration/forms/SpreadsheetView.java
new file mode 100644
index 0000000000..22ff1c06b9
--- /dev/null
+++ b/forms/qa/integration/forms/SpreadsheetView.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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.frame.*;
+import com.sun.star.sheet.*;
+import com.sun.star.container.*;
+
+public class SpreadsheetView extends integration.forms.DocumentViewHelper
+{
+
+ /** Creates a new instance of SpreadsheetView */
+ public SpreadsheetView( XMultiServiceFactory orb, DocumentHelper document, XController controller )
+ {
+ super( orb, document, controller );
+ }
+
+ /** activates the sheet with the given index
+ */
+ void activateSheet( int sheetIndex )
+ {
+ try
+ {
+ // get the sheet to activate
+ XSpreadsheetDocument doc = UnoRuntime.queryInterface(
+ XSpreadsheetDocument.class, getDocument().getDocument() );
+ XIndexAccess sheets = UnoRuntime.queryInterface(
+ XIndexAccess.class, doc.getSheets() );
+
+ XSpreadsheet sheet = UnoRuntime.queryInterface(
+ XSpreadsheet.class, sheets.getByIndex( sheetIndex ) );
+
+ // activate
+ XSpreadsheetView view = UnoRuntime.queryInterface(
+ XSpreadsheetView.class, getController() );
+ view.setActiveSheet( sheet );
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ }
+ }
+}
diff --git a/forms/qa/integration/forms/TableCellTextBinding.java b/forms/qa/integration/forms/TableCellTextBinding.java
new file mode 100644
index 0000000000..7f745a8f78
--- /dev/null
+++ b/forms/qa/integration/forms/TableCellTextBinding.java
@@ -0,0 +1,192 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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.Type;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.table.XCell;
+import com.sun.star.util.XModifyListener;
+import com.sun.star.text.XTextRange;
+
+/** a value binding to be connected to a form control
+
+ This binding synchronizes the text contained in a table cell (which you must
+ pass upon construction) to the text in an XBindableValue.
+
+ Well, in real it does not synchronize both directions. The ValueBinding
+ service has not much room for own activity: It allows notification of changes
+ in the own value, and it allows external instances to set the current value.
+
+ Note that we implement this binding as a separate thread, which is (more or
+ less permanently) polling for a new text at the cell. This is unfortunate, but
+ sadly the Writer table cells do not support actively notifying changes in their
+ content to other interested parties.
+*/
+public class TableCellTextBinding
+ extends java.lang.Thread
+ implements com.sun.star.form.binding.XValueBinding,
+ com.sun.star.util.XModifyBroadcaster
+{
+ private final XTextRange m_cellText;
+ private Object m_writeSignal;
+ private String m_newCellText;
+ private String m_lastKnownCellText;
+ private boolean m_haveNewCellText;
+ private final java.util.List<com.sun.star.util.XModifyListener> 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<com.sun.star.util.XModifyListener>();
+
+ 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
+ <TRUE/> if the user pressed a key on the console, <FALSE/> 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<nodes.getLength(); ++i )
+ {
+ XNode node = nodes.item(i);
+ if ( node.getLocalName().equals( _elementName ) )
+ {
+ _parentElement.removeChild( node );
+ return node;
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+
+
+
+
+ /** creates an attribute for the given node
+ *
+ * @param _parentElement
+ * the element at which the attribute should be created
+ * @param _attribName
+ * the name of the to-be-created attribute
+ * @return
+ * the DOM node, which has already been inserted into the DOM tree
+ */
+ public XNode createAttribute( XNode _parentElement, String _attribName ) throws DOMException
+ {
+ return createAttribute( _parentElement, _attribName, null );
+ }
+
+ /** creates an attribute for the given node
+ *
+ * @param _parentElement
+ * the element at which the attribute should be created
+ * @param _attribName
+ * the name of the to-be-created attribute
+ * @param _initialNodeValue
+ * the initial value to set at the node. Might be null, in this case no value is set.
+ * @return
+ * the DOM node, which has already been inserted into the DOM tree
+ */
+ public XNode createAttribute( XNode _parentElement, String _attribName, String _initialNodeValue ) throws DOMException
+ {
+ XNode node = _parentElement.appendChild(
+ m_model.getUIHelper().createAttribute( _parentElement, _attribName )
+ );
+ if ( _initialNodeValue != null )
+ node.setNodeValue( _initialNodeValue );
+ return node;
+ }
+}
diff --git a/forms/qa/org/openoffice/xforms/Model.java b/forms/qa/org/openoffice/xforms/Model.java
new file mode 100644
index 0000000000..37c3915b2a
--- /dev/null
+++ b/forms/qa/org/openoffice/xforms/Model.java
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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.beans.XPropertySet;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.xforms.XFormsUIHelper1;
+import com.sun.star.xforms.XModel;
+import com.sun.star.xml.dom.XNode;
+
+public class Model
+{
+ private final XModel m_model;
+ private final XPropertySet m_modelProps;
+ private final XFormsUIHelper1 m_helper;
+
+ protected Model( Object _model )
+ {
+ m_model = UnoRuntime.queryInterface( XModel.class, _model );
+ m_modelProps = UnoRuntime.queryInterface( XPropertySet.class, _model );
+ m_helper = UnoRuntime.queryInterface( XFormsUIHelper1.class,
+ m_model );
+ }
+
+
+
+ protected XFormsUIHelper1 getUIHelper()
+ {
+ return m_helper;
+ }
+
+ public Instance getDefaultInstance()
+ {
+ return new Instance( this, m_model.getDefaultInstance() );
+ }
+
+ /** creates a binding for the given DOM node
+ *
+ * @param _node the DOM node to create a binding for
+ * @param _dataTypeClass the data type to be used for the binding
+ */
+ public XPropertySet createBindingForNode( XNode _node, short _dataTypeClass )
+ {
+ XPropertySet binding = m_helper.getBindingForNode(_node, true);
+ try
+ {
+ String basicTypeName = (String)m_model.getDataTypeRepository().getBasicDataType( _dataTypeClass ).
+ getPropertyValue( "Name" );
+ binding.setPropertyValue( "Type", basicTypeName );
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ return binding;
+ }
+
+ public void setIsDocumentInternalData( boolean _internalData )
+ {
+ try
+ {
+ m_modelProps.setPropertyValue("ExternalData", Boolean.valueOf(!_internalData));
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ public boolean getIsDocumentInternalData()
+ {
+ boolean isInternalData = false;
+ try
+ {
+ isInternalData = !((Boolean)m_modelProps.getPropertyValue( "ExternalData" )).booleanValue();
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ return isInternalData;
+ }
+}
diff --git a/forms/qa/org/openoffice/xforms/XMLDocument.java b/forms/qa/org/openoffice/xforms/XMLDocument.java
new file mode 100644
index 0000000000..d4dfcbc218
--- /dev/null
+++ b/forms/qa/org/openoffice/xforms/XMLDocument.java
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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.container.NoSuchElementException;
+import com.sun.star.container.XNameContainer;
+import com.sun.star.lang.WrappedTargetException;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.Exception;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.xforms.XFormsSupplier;
+import com.sun.star.xforms.XFormsUIHelper1;
+import com.sun.star.xforms.XModel;
+import integration.forms.DocumentType;
+
+public class XMLDocument extends integration.forms.DocumentHelper
+{
+ private XNameContainer m_forms;
+
+ /* ------------------------------------------------------------------ */
+ public XMLDocument( XMultiServiceFactory _orb ) throws Exception
+ {
+ super( _orb, implLoadAsComponent( _orb, getDocumentFactoryURL( DocumentType.XMLFORM ) ) );
+ impl_initialize( getDocument() );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_initialize( XComponent _document )
+ {
+ XFormsSupplier formsSupplier = UnoRuntime.queryInterface( XFormsSupplier.class,
+ _document );
+
+ if ( formsSupplier == null )
+ throw new IllegalArgumentException();
+
+ m_forms = formsSupplier.getXForms();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public String[] getXFormModelNames()
+ {
+ return m_forms.getElementNames();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public Model getXFormModel( String _modelName ) throws NoSuchElementException
+ {
+ try
+ {
+ return new Model(m_forms.getByName(_modelName));
+ }
+ catch (WrappedTargetException ex)
+ {
+ throw new NoSuchElementException(ex);
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ public Model addXFormModel( String _modelName )
+ {
+ XModel newModel = null;
+ try
+ {
+ newModel = UnoRuntime.queryInterface( XModel.class,
+ getOrb().createInstance( "com.sun.star.xforms.Model" ) );
+ newModel.setID(_modelName);
+ XFormsUIHelper1 modelHelper = UnoRuntime.queryInterface(
+ XFormsUIHelper1.class, newModel );
+ modelHelper.newInstance( "Instance 1", "", true );
+ newModel.initialize();
+
+ m_forms.insertByName(_modelName, newModel);
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ return new Model( newModel );
+ }
+}
diff --git a/forms/qa/unoapi/forms_1.sce b/forms/qa/unoapi/forms_1.sce
new file mode 100644
index 0000000000..d3d8df7512
--- /dev/null
+++ b/forms/qa/unoapi/forms_1.sce
@@ -0,0 +1,27 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this 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 forms.OButtonControl
+#i111285 -o forms.OButtonModel
+-o forms.OCheckBoxControl
+-o forms.OCheckBoxModel
+-o forms.OComboBoxControl
+-o forms.OComboBoxModel
+-o forms.OCurrencyControl
+-o forms.OCurrencyModel
+#-o forms.ODatabaseForm
+-o forms.ODateControl
diff --git a/forms/qa/unoapi/forms_2.sce b/forms/qa/unoapi/forms_2.sce
new file mode 100644
index 0000000000..30f4b24018
--- /dev/null
+++ b/forms/qa/unoapi/forms_2.sce
@@ -0,0 +1,27 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this 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 forms.ODateModel
+-o forms.OEditControl
+-o forms.OEditModel
+#i109939 -o forms.OFileControlModel
+-o forms.OFixedTextModel
+-o forms.OFormattedControl
+#i109939 -o forms.OFormattedFieldWrapper
+# LETS A DOCUMENT OPEN -o forms.OFormsCollection
+-o forms.OGridControlModel
+-o forms.OGroupBoxControl
diff --git a/forms/qa/unoapi/forms_3.sce b/forms/qa/unoapi/forms_3.sce
new file mode 100644
index 0000000000..e8b2754255
--- /dev/null
+++ b/forms/qa/unoapi/forms_3.sce
@@ -0,0 +1,27 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this 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 forms.OHiddenModel
+-o forms.OImageButtonControl
+-o forms.OImageButtonModel
+-o forms.OImageControlControl
+-o forms.OImageControlModel
+#i114669 -o forms.OListBoxControl
+-o forms.OListBoxModel
+-o forms.ONavigationBarControl
+-o forms.ONavigationBarModel
+-o forms.ONumericControl
diff --git a/forms/qa/unoapi/forms_4.sce b/forms/qa/unoapi/forms_4.sce
new file mode 100644
index 0000000000..b6bfbc1f5b
--- /dev/null
+++ b/forms/qa/unoapi/forms_4.sce
@@ -0,0 +1,26 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this 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 forms.ONumericModel
+-o forms.OPatternControl
+-o forms.OPatternModel
+-o forms.ORadioButtonControl
+-o forms.ORadioButtonModel
+-o forms.OScrollBarModel
+-o forms.OSpinButtonModel
+-o forms.OTimeControl
+-o forms.OTimeModel
diff --git a/forms/qa/unoapi/knownissues.xcl b/forms/qa/unoapi/knownissues.xcl
new file mode 100644
index 0000000000..5d81d139d7
--- /dev/null
+++ b/forms/qa/unoapi/knownissues.xcl
@@ -0,0 +1,151 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+### i45204 ###
+forms.OCurrencyModel::com::sun::star::form::XUpdateBroadcaster
+
+### i84235 ###
+forms.ODatabaseForm::com::sun::star::sdbc::XWarningsSupplier
+
+### i45204 ###
+forms.OCurrencyModel::com::sun::star::form::XUpdateBroadcaster
+
+### i79098 ###
+forms.OScrollBarModel::com::sun::star::awt::UnoControlScrollBarModel
+
+### i79100 ###
+forms.ORadioButtonModel::com::sun::star::awt::UnoControlRadioButtonModel
+
+### i79108 ###
+forms.OButtonControl::com::sun::star::form::submission::XSubmission
+
+### i79108 ###
+forms.OImageButtonControl::com::sun::star::form::submission::XSubmission
+
+### i79109 ###
+forms.OFixedTextModel::com::sun::star::awt::UnoControlFixedTextModel
+
+### i79110 ###
+forms.OCheckBoxModel::com::sun::star::awt::UnoControlCheckBoxModel
+
+### i79111 ###
+forms.OButtonModel::com::sun::star::awt::UnoControlButtonModel
+
+### i84283 ###
+forms.ODatabaseForm::com::sun::star::form::XDatabaseParameterBroadcaster
+
+### i84298 ###
+forms.ODatabaseForm::com::sun::star::sdbc::XParameters
+
+### i73994 ###
+forms.OEditModel::com::sun::star::style::ParagraphProperties
+
+### i84311 ###
+forms.OFixedTextModel::com::sun::star::beans::XMultiPropertySet
+
+### i84311 ###
+forms.OFixedTextModel::com::sun::star::beans::XFastPropertySet
+
+### i84314 ###
+forms.OImageButtonModel::com::sun::star::awt::UnoControlImageControlModel
+
+### i84315 ###
+forms.OImageControlModel::com::sun::star::awt::UnoControlImageControlModel
+
+### i84316 ###
+forms.ONumericModel::com::sun::star::form::XUpdateBroadcaster
+
+### i46489 ###
+forms.ORadioButtonControl::com::sun::star::awt::XWindow
+forms.OCheckBoxControl::com::sun::star::awt::XWindow
+forms.OGroupBoxControl::com::sun::star::awt::XWindow
+forms.OListBoxControl::com::sun::star::awt::XWindow
+forms.OComboBoxControl::com::sun::star::awt::XWindow
+forms.OEditControl::com::sun::star::awt::XWindow
+forms.ODateControl::com::sun::star::awt::XWindow
+forms.OTimeControl::com::sun::star::awt::XWindow
+forms.ONumericControl::com::sun::star::awt::XWindow
+forms.OCurrencyControl::com::sun::star::awt::XWindow
+forms.OPatternControl::com::sun::star::awt::XWindow
+forms.OFormattedControl::com::sun::star::awt::XWindow
+forms.OButtonControl::com::sun::star::awt::XWindow
+forms.OImageButtonControl::com::sun::star::awt::XWindow
+forms.OImageControlControl::com::sun::star::awt::XWindow
+forms.ONavigationBarControl::com::sun::star::awt::XWindow
+
+### i87212 ###
+forms.ONavigationBarControl::com::sun::star::lang::XComponent
+
+### i87213 ###
+forms.OGroupBoxControl::com::sun::star::awt::XView
+forms.OListBoxControl::com::sun::star::awt::XView
+forms.OEditControl::com::sun::star::awt::XView
+forms.ORadioButtonControl::com::sun::star::awt::XView
+forms.OButtonControl::com::sun::star::awt::XView
+forms.OPatternControl::com::sun::star::awt::XView
+forms.OCheckBoxControl::com::sun::star::awt::XView
+forms.OTimeControl::com::sun::star::awt::XView
+forms.OCurrencyControl::com::sun::star::awt::XView
+forms.OFormattedControl::com::sun::star::awt::XView
+forms.ONumericControl::com::sun::star::awt::XView
+forms.OImageButtonControl::com::sun::star::awt::XView
+forms.OComboBoxControl::com::sun::star::awt::XView
+forms.ODateControl::com::sun::star::awt::XView
+forms.OImageControlControl::com::sun::star::awt::XView
+forms.ONavigationBarControl::com::sun::star::awt::XView
+
+### i87214 ###
+forms.OEditControl::com::sun::star::form::XChangeBroadcaster
+
+### i87215 ###
+forms.OListBoxControl::com::sun::star::form::XChangeBroadcaster
+
+### i87861 ###
+forms.OEditModel::com::sun::star::form::XUpdateBroadcaster
+
+### i89421 ###
+forms.OGroupBoxModel::com::sun::star::lang::XComponent
+
+### i89422 ###
+forms.OHiddenModel::com::sun::star::form::component::HiddenControl
+
+### i90352 ###
+forms.OListBoxControl::com::sun::star::lang::XComponent
+
+### i94950 ###
+forms.OTimeModel::com::sun::star::form::XUpdateBroadcaster
+forms.OPatternModel::com::sun::star::form::XUpdateBroadcaster
+forms.OListBoxModel::com::sun::star::form::XUpdateBroadcaster
+forms.OFormattedFieldWrapper::com::sun::star::form::XUpdateBroadcaster
+forms.ODateModel::com::sun::star::form::XUpdateBroadcaster
+forms.OComboBoxModel::com::sun::star::form::XUpdateBroadcaster
+
+### i111006 ###
+forms.OFileControlModel::com::sun::star::beans::XFastPropertySet
+forms.OFileControlModel::com::sun::star::form::FormControlModel
+
+### i113201 ###
+forms.OEditModel::com::sun::star::form::validation::XValidatableFormComponent
+
+### i114209 ###
+forms.OGridControlModel::com::sun::star::view::XSelectionSupplier
+
+### i111333 ###
+forms.OImageControlControl::com::sun::star::awt::XControl
+
+forms.OEditModel::com::sun::star::io::XPersistObject
diff --git a/forms/qa/unoapi/testdocuments/TestDB/testDB.dbf b/forms/qa/unoapi/testdocuments/TestDB/testDB.dbf
new file mode 100644
index 0000000000..aa3c8262a5
--- /dev/null
+++ b/forms/qa/unoapi/testdocuments/TestDB/testDB.dbf
Binary files differ
diff --git a/forms/qa/unoapi/testdocuments/TestDB/testDB.dbt b/forms/qa/unoapi/testdocuments/TestDB/testDB.dbt
new file mode 100644
index 0000000000..2183ea7ba6
--- /dev/null
+++ b/forms/qa/unoapi/testdocuments/TestDB/testDB.dbt
Binary files differ
diff --git a/forms/source/component/BaseListBox.hxx b/forms/source/component/BaseListBox.hxx
new file mode 100644
index 0000000000..fe0930ea70
--- /dev/null
+++ b/forms/source/component/BaseListBox.hxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+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 <property.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+#include <comphelper/streamsection.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/string_view.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+
+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<XComponentContext>& _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<XComponentContext>& _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<FormButtonType>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_STATE, PROPERTY_ID_DEFAULT_STATE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DISPATCHURLINTERNAL, PROPERTY_ID_DISPATCHURLINTERNAL, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_URL, PROPERTY_ID_TARGET_URL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_FRAME, PROPERTY_ID_TARGET_FRAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::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<OButtonModel> pClone = new OButtonModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> OButtonModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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<XObjectOutputStream>& _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<sal_uInt16>(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<XObjectInputStream>& _rxInStream)
+{
+ OClickableImageBaseModel::read(_rxInStream);
+
+ sal_uInt16 nVersion = _rxInStream->readShort(); // Version
+ switch (nVersion)
+ {
+ case 0x0001:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+
+ _rxInStream >> m_sTargetURL;
+ _rxInStream >> m_sTargetFrame;
+ }
+ break;
+
+ case 0x0002:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_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<FormButtonType>(_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<sal_Int16>(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<ToggleState>(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<sal_Int16>(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<Type> OButtonControl::_getTypes()
+{
+ return ::comphelper::concatSequences(
+ OButtonControl_BASE::getTypes(),
+ OClickableImageBaseControl::_getTypes(),
+ OFormNavigationHelper::getTypes()
+ );
+}
+
+
+css::uno::Sequence<OUString> OButtonControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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<XComponentContext>& _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> 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<XTypeProvider>::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<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (!xSet.is())
+ return;
+
+ if (FormButtonType_PUSH == *o3tl::doAccess<FormButtonType>(xSet->getPropertyValue(PROPERTY_BUTTONTYPE)))
+ {
+ // notify the action listeners for a push button
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aActionListeners);
+ ActionEvent aEvt(static_cast<XWeak*>(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> 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> xButton;
+ query_aggregation( m_xAggregate, xButton);
+ if (xButton.is())
+ xButton->setActionCommand(_rCommand);
+}
+
+
+void SAL_CALL OButtonControl::addActionListener(const Reference<XActionListener>& _rxListener)
+{
+ m_aActionListeners.addInterface(_rxListener);
+}
+
+
+void SAL_CALL OButtonControl::removeActionListener(const Reference<XActionListener>& _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<css::uno::Any> 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<css::uno::Any> 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 <togglestate.hxx>
+#include <formnavigation.hxx>
+#include <resettable.hxx>
+
+#include <com/sun/star/awt/MouseEvent.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/awt/ActionEvent.hpp>
+#include <com/sun/star/awt/XActionListener.hpp>
+#include <com/sun/star/awt/XButton.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+
+#include <cppuhelper/implbase1.hxx>
+
+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<OUString> 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;
+
+ // <properties>
+ ToggleState m_eDefaultState; // the default check state
+ // </properties>
+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<OUString> 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 <property.hxx>
+#include <services.hxx>
+#include <comphelper/basicio.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+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<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_CHECKBOX)
+{
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OCheckBoxControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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<XComponentContext>& _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<XComponentContext>& _rxFactory )
+ :OReferenceValueComponent( _pOriginal, _rxFactory )
+{
+}
+
+
+OCheckBoxModel::~OCheckBoxModel()
+{
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OCheckBoxModel::createClone()
+{
+ rtl::Reference<OCheckBoxModel> pClone = new OCheckBoxModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OCheckBoxModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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<sal_Int16>::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<css::io::XObjectOutputStream>& _rxOutStream)
+{
+ OReferenceValueComponent::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0003);
+ // Properties
+ _rxOutStream << getReferenceValue();
+ _rxOutStream << static_cast<sal_Int16>(getDefaultChecked());
+ writeHelpTextCompatibly(_rxOutStream);
+ // from version 0x0003 : common properties
+ writeCommonProperties(_rxOutStream);
+}
+
+
+void SAL_CALL OCheckBoxModel::read(const Reference<css::io::XObjectInputStream>& _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<sal_Int16>(getDefaultChecked());
+ }
+ if ( m_xColumn->wasNull() )
+ {
+ bool bTriState = true;
+ if ( m_xAggregateSet.is() )
+ m_xAggregateSet->getPropertyValue( PROPERTY_TRISTATE ) >>= bTriState;
+ aValue <<= static_cast<sal_Int16>( 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<sal_Int16>( 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<css::uno::Any> 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<css::uno::Any> 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<OUString> 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<OUString> 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 <property.hxx>
+#include <componenttools.hxx>
+#include "findpos.hxx"
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <comphelper/property.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/types.hxx>
+#include <services.hxx>
+#include <tools/debug.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <utility>
+
+
+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<OUString>& getColumnTypes()
+{
+ static css::uno::Sequence<OUString> aColumnTypes = []()
+ {
+ css::uno::Sequence<OUString> 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<OUString>& rColumnTypes = getColumnTypes();
+ nTypeId = ::detail::findPos(aColumnType, rColumnTypes);
+ }
+ return nTypeId;
+}
+
+const Sequence<sal_Int8>& OGridColumn::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theOGridColumnImplementationId;
+ return theOGridColumnImplementationId.getSeq();
+}
+
+
+sal_Int64 SAL_CALL OGridColumn::getSomething( const Sequence<sal_Int8>& _rIdentifier)
+{
+ sal_Int64 nReturn(0);
+
+ if ( comphelper::isUnoTunnelId<OGridColumn>(_rIdentifier) )
+ {
+ nReturn = comphelper::getSomething_cast(this);
+ }
+ else
+ {
+ Reference< XUnoTunnel > xAggTunnel;
+ if ( query_aggregation( m_xAggregate, xAggTunnel ) )
+ return xAggTunnel->getSomething( _rIdentifier );
+ }
+ return nReturn;
+}
+
+
+Sequence<sal_Int8> SAL_CALL OGridColumn::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+Sequence<Type> SAL_CALL OGridColumn::getTypes()
+{
+ TypeBag aTypes( OGridColumn_BASE::getTypes() );
+ // erase the types which we do not support
+ aTypes.removeType( cppu::UnoType<XFormComponent>::get() );
+ aTypes.removeType( cppu::UnoType<XServiceInfo>::get() );
+ aTypes.removeType( cppu::UnoType<XBindableValue>::get() );
+ aTypes.removeType( cppu::UnoType<XPropertyContainer>::get() );
+
+ // but re-add their base class(es)
+ aTypes.addType( cppu::UnoType<XChild>::get() );
+
+ Reference< XTypeProvider > xProv;
+ if ( query_aggregation( m_xAggregate, xProv ))
+ aTypes.addTypes( xProv->getTypes() );
+
+ aTypes.removeType( cppu::UnoType<XTextRange>::get() );
+ aTypes.removeType( cppu::UnoType<XSimpleText>::get() );
+ aTypes.removeType( cppu::UnoType<XText>::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<XFormComponent>::get())
+ || _rType.equals(cppu::UnoType<XServiceInfo>::get())
+ || _rType.equals(cppu::UnoType<XBindableValue>::get())
+ || _rType.equals(cppu::UnoType<XPropertyContainer>::get())
+ || comphelper::isAssignableFrom(cppu::UnoType<XTextRange>::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<XComponentContext>& _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<css::uno::XInterface> xIface;
+ m_xAggregate->setDelegator(xIface);
+ }
+
+}
+
+// XEventListener
+
+void SAL_CALL OGridColumn::disposing(const EventObject& _rSource)
+{
+ OPropertySetAggregationHelper::disposing(_rSource);
+
+ Reference<XEventListener> xEvtLstner;
+ if (query_aggregation(m_xAggregate, xEvtLstner))
+ xEvtLstner->disposing(_rSource);
+}
+
+// OGridColumn_BASE
+
+void OGridColumn::disposing()
+{
+ OGridColumn_BASE::disposing();
+ OPropertySetAggregationHelper::disposing();
+
+ Reference<XComponent> 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<Property>& aDescriptor)
+{
+ aDescriptor.realloc(5);
+ Property* pProperties = aDescriptor.getArray();
+ *pProperties++ = css::beans::Property(PROPERTY_LABEL, PROPERTY_ID_LABEL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_WIDTH, PROPERTY_ID_WIDTH, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_ALIGN, PROPERTY_ID_ALIGN, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_HIDDEN, PROPERTY_ID_HIDDEN, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_COLUMNSERVICENAME, PROPERTY_ID_COLUMNSERVICENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY);
+}
+
+// Reference<XPropertySet>
+
+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<sal_Int32>::get());
+ break;
+ case PROPERTY_ID_ALIGN:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aAlign, cppu::UnoType<sal_Int32>::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<sal_Int16>(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<XObjectOutputStream>& _rxOutStream)
+{
+ // 1. Write the UnoControl
+ Reference<XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ sal_Int32 nMark = xMark->createMark();
+
+ sal_Int32 nLen = 0;
+ _rxOutStream->writeLong(nLen);
+
+ Reference<XPersistObject> 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<XObjectInputStream>& _rxInStream)
+{
+ // 1. Read the UnoControl
+ sal_Int32 nLen = _rxInStream->readLong();
+ if (nLen)
+ {
+ Reference<XMarkableStream> xMark(_rxInStream, UNO_QUERY);
+ sal_Int32 nMark = xMark->createMark();
+ Reference<XPersistObject> 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<css::uno::XComponentContext>& _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<OGridColumn> TextFieldColumn::createCloneColumn() const
+{
+ return new TextFieldColumn(this);
+}
+
+PatternFieldColumn::PatternFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> PatternFieldColumn::createCloneColumn() const
+{
+ return new PatternFieldColumn(this);
+}
+
+DateFieldColumn::DateFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> DateFieldColumn::createCloneColumn() const
+{
+ return new DateFieldColumn(this);
+}
+
+TimeFieldColumn::TimeFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> TimeFieldColumn::createCloneColumn() const
+{
+ return new TimeFieldColumn(this);
+}
+
+NumericFieldColumn::NumericFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> NumericFieldColumn::createCloneColumn() const
+{
+ return new NumericFieldColumn(this);
+}
+
+CurrencyFieldColumn::CurrencyFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> CurrencyFieldColumn::createCloneColumn() const
+{
+ return new CurrencyFieldColumn(this);
+}
+
+CheckBoxColumn::CheckBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> CheckBoxColumn::createCloneColumn() const
+{
+ return new CheckBoxColumn(this);
+}
+
+ComboBoxColumn::ComboBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> ComboBoxColumn::createCloneColumn() const
+{
+ return new ComboBoxColumn(this);
+}
+
+ListBoxColumn::ListBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> ListBoxColumn::createCloneColumn() const
+{
+ return new ListBoxColumn(this);
+}
+
+FormattedFieldColumn::FormattedFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> 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 <cloneable.hxx>
+
+#include <com/sun/star/io/XObjectInputStream.hpp>
+#include <com/sun/star/io/XObjectOutputStream.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+
+#include <comphelper/propagg.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase2.hxx>
+#include <rtl/ref.hxx>
+
+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<css::uno::XComponentContext>& _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<sal_Int8>& getUnoTunnelId();
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence<sal_Int8>& _rIdentifier) override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> 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<OGridColumn> 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<OUString>& getColumnTypes();
+sal_Int32 getColumnTypeByModelName(const OUString& aModelName);
+
+// Columns
+class TextFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< TextFieldColumn >
+{
+public:
+ explicit TextFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> createCloneColumn() const override;
+};
+
+class PatternFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< PatternFieldColumn >
+{
+public:
+ explicit PatternFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> createCloneColumn() const override;
+};
+
+class DateFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< DateFieldColumn >
+{
+public:
+ explicit DateFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> createCloneColumn() const override;
+};
+
+class TimeFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< TimeFieldColumn >
+{
+public:
+ explicit TimeFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> createCloneColumn() const override;
+};
+
+class NumericFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< NumericFieldColumn >
+{
+public:
+ explicit NumericFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> createCloneColumn() const override;
+};
+
+class CurrencyFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< CurrencyFieldColumn >
+{
+public:
+ explicit CurrencyFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> createCloneColumn() const override;
+};
+
+class CheckBoxColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< CheckBoxColumn >
+{
+public:
+ explicit CheckBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> createCloneColumn() const override;
+};
+
+class ComboBoxColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< ComboBoxColumn >
+{
+public:
+ explicit ComboBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> createCloneColumn() const override;
+};
+
+class ListBoxColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< ListBoxColumn >
+{
+public:
+ explicit ListBoxColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> createCloneColumn() const override;
+};
+
+class FormattedFieldColumn
+ :public OGridColumn
+ ,public OAggregationArrayUsageHelper< FormattedFieldColumn >
+{
+public:
+ explicit FormattedFieldColumn(const css::uno::Reference<css::uno::XComponentContext>& _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<OGridColumn> 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 <property.hxx>
+#include <services.hxx>
+
+#include <frm_resource.hxx>
+#include <strings.hrc>
+#include "BaseListBox.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <connectivity/dbtools.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/sharedunocomponent.hxx>
+
+#include <limits.h>
+
+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<Type> OComboBoxModel::_getTypes()
+{
+ return ::comphelper::concatSequences(
+ OBoundControlModel::_getTypes(),
+ OEntryListHelper::getTypes(),
+ OErrorBroadcaster::getTypes()
+ );
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OComboBoxModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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<XComponentContext>& _rxFactory)
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_COMBOBOX, FRM_SUN_CONTROL_COMBOBOX, true, true, true )
+ // use the old control name for compatibility reasons
+ ,OEntryListHelper( static_cast<OControlModel&>(*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<XComponentContext>& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ ,OEntryListHelper( *_pOriginal, static_cast<OControlModel&>(*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<OComboBoxModel> 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<ListSourceType>::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<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCETYPE, PROPERTY_ID_LISTSOURCETYPE, cppu::UnoType<ListSourceType>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCE, PROPERTY_ID_LISTSOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_STRINGITEMLIST, PROPERTY_ID_STRINGITEMLIST, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TYPEDITEMLIST, PROPERTY_ID_TYPEDITEMLIST, cppu::UnoType<Sequence< Any >>::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<css::io::XObjectOutputStream>& _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<OUString> aListSourceSeq(&m_aListSource, 1);
+ _rxOutStream << aListSourceSeq;
+ _rxOutStream << static_cast<sal_Int16>(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<css::io::XObjectInputStream>& _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<OUString> aListSource;
+ _rxInStream >> aListSource;
+ for (const OUString& rToken : std::as_const(aListSource))
+ m_aListSource += rToken;
+ }
+
+ sal_Int16 nListSourceType;
+ _rxInStream >> nListSourceType;
+ m_eListSourceType = static_cast<ListSourceType>(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<OUString>() ) );
+ setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
+ }
+
+ 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> xConnection = getConnection(m_xCursor);
+ if (!xConnection.is())
+ return;
+
+ Reference<XServiceInfo> 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<XNameAccess> xFieldsByName = getTableFields(xConnection, m_aListSource);
+
+ OUString aFieldName;
+ if ( xFieldsByName.is() && xFieldsByName->hasByName( getControlSource() ) )
+ {
+ aFieldName = getControlSource();
+ }
+ else
+ {
+ // otherwise look for the alias
+ Reference<XPropertySet> 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<XDatabaseMetaData> 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<XColumnsSupplier> xSupplyCols(xListCursor, UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "OComboBoxModel::loadData : cursor supports the row set service but is no column supplier?!");
+ Reference<XIndexAccess> 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++<SHRT_MAX)) // Set max. count
+ {
+ aStringList.push_back( aValueFormatter.getFormattedValue() );
+ }
+ }
+ break;
+ case ListSourceType_TABLEFIELDS:
+ {
+ Reference<XNameAccess> xFieldNames = getTableFields(xConnection, m_aListSource);
+ if (xFieldNames.is())
+ {
+ const Sequence<OUString> 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<css::uno::Any>() ) );
+}
+
+
+void OComboBoxModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ Reference<XPropertySet> 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<OUString> 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<css::uno::Any>() ) );
+ }
+
+ 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<XComponentContext>& _rxContext)
+ :OBoundControl(_rxContext, VCL_CONTROL_COMBOBOX)
+{
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OComboBoxControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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<css::uno::Any> 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<css::uno::Any> 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 <memory>
+#include <FormComponent.hxx>
+#include "errorbroadcaster.hxx"
+#include "entrylisthelper.hxx"
+#include "cachedrowset.hxx"
+
+#include <com/sun/star/form/ListSourceType.hpp>
+
+#include <connectivity/formattedcolumnvalue.hxx>
+
+
+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<OUString> 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<OUString> 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<OUString> 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 <property.hxx>
+#include <services.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <comphelper/types.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+
+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<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_CURRENCYFIELD)
+{
+}
+
+css::uno::Sequence<OUString> SAL_CALL OCurrencyControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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<XComponentContext>& _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<XComponentContext>& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+{
+ implConstruct();
+}
+
+
+OCurrencyModel::~OCurrencyModel()
+{
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OCurrencyModel::createClone()
+{
+ rtl::Reference<OCurrencyModel> pClone = new OCurrencyModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OCurrencyModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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<double>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::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<css::uno::Any> 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<css::uno::Any> 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<OUString> 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<OUString> 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 <sal/config.h>
+
+#include <string_view>
+
+#include <componenttools.hxx>
+#include "DatabaseForm.hxx"
+#include "EventThread.hxx"
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include "GroupManager.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/DataSelectionType.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/TabulatorCycle.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/io/XObjectInputStream.hpp>
+#include <com/sun/star/io/XObjectOutputStream.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdb/RowSetVetoException.hpp>
+#include <com/sun/star/sdb/XColumnUpdate.hpp>
+#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/Privilege.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/XModifiable2.hpp>
+
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/seqstream.hxx>
+#include <comphelper/sequence.hxx>
+#include <connectivity/dbtools.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/types.hxx>
+#include <rtl/math.hxx>
+#include <rtl/tencinfo.h>
+#include <svl/inettype.hxx>
+#include <tools/datetime.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/inetmsg.hxx>
+#include <tools/inetstrm.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <osl/mutex.hxx>
+
+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<XControl>& _rControl,
+ bool _bSubmit) override;
+
+public:
+
+ explicit OFormSubmitResetThread(ODatabaseForm* pControl) : OComponentEventThread(pControl) { }
+};
+
+
+void OFormSubmitResetThread::processEvent(
+ ::cppu::OComponentHelper* pCompImpl,
+ const EventObject *_pEvt,
+ const Reference<XControl>& _rControl,
+ bool _bSubmit)
+{
+ if (_bSubmit)
+ static_cast<ODatabaseForm *>(pCompImpl)->submit_impl(_rControl, *static_cast<const css::awt::MouseEvent*>(_pEvt));
+ else
+ static_cast<ODatabaseForm *>(pCompImpl)->reset_impl(true);
+}
+
+
+//= ODatabaseForm
+
+Sequence<sal_Int8> SAL_CALL ODatabaseForm::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+Sequence<Type> SAL_CALL ODatabaseForm::getTypes()
+{
+ // ask the aggregate
+ Sequence<Type> aAggregateTypes;
+ Reference<XTypeProvider> 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<XComponentContext>& _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<XPropertySet>::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<XControl>& 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<sal_Int8> ODatabaseForm::GetDataMultiPartEncoded(const Reference<XControl>& 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<char[]> 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<sal_Int8>(static_cast<sal_Int8 const *>(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<XPropertySet>& xComponentSet, std::u16string_view rNamePrefix,
+ const Reference<XControl>& 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<XPropertySet> xSubmitButtonComponent(rxSubmitButton->getModel(), UNO_QUERY);
+ if (xSubmitButtonComponent == xComponentSet && hasProperty(PROPERTY_LABEL, xComponentSet))
+ {
+ // <name>=<label>
+ OUString aLabel;
+ xComponentSet->getPropertyValue( PROPERTY_LABEL ) >>= aLabel;
+ rList.emplace_back(aName, aLabel );
+ }
+ }
+ } break;
+
+ // ImageButtons
+ case FormComponentType::IMAGEBUTTON:
+ {
+ // We only evaluate the pressed Submit button
+ // If one is passed at all
+ if( rxSubmitButton.is() )
+ {
+ Reference<XPropertySet> xSubmitButtonComponent(rxSubmitButton->getModel(), UNO_QUERY);
+ if (xSubmitButtonComponent == xComponentSet)
+ {
+ // <name>.x=<pos.X>&<name>.y=<pos.Y>
+ OUString aRhs = OUString::number( MouseEvt.X );
+
+ // Only if a name is available we have a name.x
+ OUStringBuffer aLhs(aName);
+ if (!aName.isEmpty())
+ aLhs.append(".x");
+ else
+ aLhs.append("x");
+ rList.emplace_back(aLhs.makeStringAndClear(), aRhs );
+
+ aLhs.append(aName);
+ aRhs = OUString::number( MouseEvt.Y );
+ if (!aName.isEmpty())
+ aLhs.append(".y");
+ else
+ aLhs.append("y");
+ rList.emplace_back(aLhs.makeStringAndClear(), aRhs );
+ }
+ }
+ } break;
+
+ // CheckBoxes/RadioButtons
+ case FormComponentType::CHECKBOX:
+ case FormComponentType::RADIOBUTTON:
+ {
+ // <name>=<refValue>
+ if( !hasProperty(PROPERTY_STATE, xComponentSet) )
+ break;
+ sal_Int16 nChecked = 0;
+ xComponentSet->getPropertyValue( PROPERTY_STATE ) >>= nChecked;
+ if( nChecked != 1 )
+ break;
+
+ OUString aStrValue;
+ if( hasProperty(PROPERTY_REFVALUE, xComponentSet) )
+ xComponentSet->getPropertyValue( PROPERTY_REFVALUE ) >>= aStrValue;
+
+ rList.emplace_back(aName, aStrValue );
+ } break;
+
+ // Edit
+ case FormComponentType::TEXTFIELD:
+ {
+ // <name>=<text>
+ if( !hasProperty(PROPERTY_TEXT, xComponentSet) )
+ break;
+
+ // Special treatment for multiline edit only if we have a control for it
+ Any aTmp = xComponentSet->getPropertyValue( PROPERTY_MULTILINE );
+ bool bMulti = rxSubmitButton.is()
+ && (aTmp.getValueType().getTypeClass() == TypeClass_BOOLEAN)
+ && getBOOL(aTmp);
+ OUString sText;
+ if ( bMulti ) // For multiline edit, get the text at the control
+ {
+
+ Reference<XControlContainer> xControlContainer(rxSubmitButton->getContext(), UNO_QUERY);
+ if( !xControlContainer.is() ) break;
+
+ // Find the right control
+ bool bFound = false;
+ const Sequence<Reference<XControl>> aControls = xControlContainer->getControls();
+ for( auto const& xControl : aControls )
+ {
+ Reference<XPropertySet> xModel(xControl->getModel(), UNO_QUERY);
+ if ((bFound = xModel == xComponentSet))
+ {
+ Reference<XTextComponent> xTextComponent(xControl, UNO_QUERY);
+ if( xTextComponent.is() )
+ sText = xTextComponent->getText();
+ break;
+ }
+ }
+ // Couldn't find control or it does not exist (edit in the grid)
+ if (!bFound)
+ xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= sText;
+ }
+ else
+ xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= sText;
+
+ rList.emplace_back(aName, sText );
+ } break;
+
+ // ComboBox, Patternfield
+ case FormComponentType::COMBOBOX:
+ case FormComponentType::PATTERNFIELD:
+ {
+ // <name>=<text>
+ if( hasProperty(PROPERTY_TEXT, xComponentSet) )
+ {
+ OUString aText;
+ xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= aText;
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+ case FormComponentType::CURRENCYFIELD:
+ case FormComponentType::NUMERICFIELD:
+ {
+ // <name>=<value> // Value is a double with dot as decimal delimiter
+ // no value (NULL) means empty value
+ if( hasProperty(PROPERTY_VALUE, xComponentSet) )
+ {
+ OUString aText;
+ Any aVal = xComponentSet->getPropertyValue( PROPERTY_VALUE );
+
+ double aDoubleVal = 0;
+ if (aVal >>= aDoubleVal)
+ {
+ sal_Int16 nScale = 0;
+ xComponentSet->getPropertyValue( PROPERTY_DECIMAL_ACCURACY ) >>= nScale;
+ aText = ::rtl::math::doubleToUString(aDoubleVal, rtl_math_StringFormat_F, nScale, '.', true);
+ }
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+ case FormComponentType::DATEFIELD:
+ {
+ // <name>=<value> // Value is a Date with the format MM-DD-YYYY
+ // no value (NULL) means empty value
+ if( hasProperty(PROPERTY_DATE, xComponentSet) )
+ {
+ OUString aText;
+ Any aVal = xComponentSet->getPropertyValue( PROPERTY_DATE );
+ sal_Int32 nInt32Val = 0;
+ if (aVal >>= nInt32Val)
+ {
+ ::Date aDate( nInt32Val );
+ OUStringBuffer aBuffer;
+ appendDigits( aDate.GetMonth(), 2, aBuffer );
+ aBuffer.append( '-' );
+ appendDigits( aDate.GetDay(), 2, aBuffer );
+ aBuffer.append( '-' );
+ appendDigits( aDate.GetYear(), 4, aBuffer );
+ aText = aBuffer.makeStringAndClear();
+ }
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+ case FormComponentType::TIMEFIELD:
+ {
+ // <name>=<value> // Value is a Time with the format HH:MM:SS
+ // no value (NULL) means empty value
+ if( hasProperty(PROPERTY_TIME, xComponentSet) )
+ {
+ OUString aText;
+ Any aVal = xComponentSet->getPropertyValue( PROPERTY_TIME );
+ sal_Int32 nInt32Val = 0;
+ if (aVal >>= nInt32Val)
+ {
+ ::tools::Time aTime(nInt32Val);
+ OUStringBuffer aBuffer;
+ appendDigits( aTime.GetHour(), 2, aBuffer );
+ aBuffer.append( '-' );
+ appendDigits( aTime.GetMin(), 2, aBuffer );
+ aBuffer.append( '-' );
+ appendDigits( aTime.GetSec(), 2, aBuffer );
+ aText = aBuffer.makeStringAndClear();
+ }
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+
+ // starform
+ case FormComponentType::HIDDENCONTROL:
+ {
+
+ // <name>=<value>
+ if( hasProperty(PROPERTY_HIDDEN_VALUE, xComponentSet) )
+ {
+ OUString aText;
+ xComponentSet->getPropertyValue( PROPERTY_HIDDEN_VALUE ) >>= aText;
+ rList.emplace_back(aName, aText );
+ }
+ } break;
+
+ // starform
+ case FormComponentType::FILECONTROL:
+ {
+ // <name>=<text>
+ if( hasProperty(PROPERTY_TEXT, xComponentSet) )
+ {
+
+ OUString aText;
+ xComponentSet->getPropertyValue( PROPERTY_TEXT ) >>= aText;
+ rList.emplace_back(aName, aText, SUCCESSFUL_REPRESENT_FILE );
+ }
+ } break;
+
+ // starform
+ case FormComponentType::LISTBOX:
+ {
+
+ // <name>=<Token0>&<name>=<Token1>&...&<name>=<TokenN> (multiple selection)
+ if (!hasProperty(PROPERTY_SELECT_SEQ, xComponentSet) ||
+ !hasProperty(PROPERTY_STRINGITEMLIST, xComponentSet))
+ break;
+
+ // Displayed values
+ Sequence< OUString > aVisibleList;
+ xComponentSet->getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aVisibleList;
+ sal_Int32 nStringCnt = aVisibleList.getLength();
+ const OUString* pStrings = aVisibleList.getConstArray();
+
+ // Value list
+ Sequence< OUString > aValueList;
+ xComponentSet->getPropertyValue( PROPERTY_VALUE_SEQ ) >>= aValueList;
+ sal_Int32 nValCnt = aValueList.getLength();
+ const OUString* pVals = aValueList.getConstArray();
+
+ // Selection
+ Sequence<sal_Int16> aSelectList;
+ xComponentSet->getPropertyValue( PROPERTY_SELECT_SEQ ) >>= aSelectList;
+ sal_Int32 nSelCount = aSelectList.getLength();
+ const sal_Int16* pSels = aSelectList.getConstArray();
+
+ // Simple or multiple selection
+ // For simple selections MT only accounts for the list's first entry.
+ if (nSelCount > 1 && !getBOOL(xComponentSet->getPropertyValue(PROPERTY_MULTISELECTION)))
+ nSelCount = 1;
+
+ // The indices in the selection list can also be invalid, so we first have to
+ // find the valid ones to determine the length of the new list.
+ sal_Int32 nCurCnt = 0;
+ sal_Int32 i;
+ for( i=0; i<nSelCount; ++i )
+ {
+ if( pSels[i] < nStringCnt )
+ ++nCurCnt;
+ }
+
+ OUString aSubValue;
+ for(i=0; i<nCurCnt; ++i )
+ {
+ sal_Int16 nSelPos = pSels[i];
+ if (nSelPos < nValCnt && !pVals[nSelPos].isEmpty())
+ {
+ aSubValue = pVals[nSelPos];
+ }
+ else
+ {
+ aSubValue = pStrings[nSelPos];
+ }
+ rList.emplace_back(aName, aSubValue );
+ }
+ } break;
+ case FormComponentType::GRIDCONTROL:
+ {
+ // Each of the column values is sent;
+ // the name is extended by the grid's prefix.
+ Reference<XIndexAccess> xContainer(xComponentSet, UNO_QUERY);
+ if (!xContainer.is())
+ break;
+
+ aName += ".";
+
+ Reference<XPropertySet> xSet;
+ sal_Int32 nCount = xContainer->getCount();
+ // we know already how many objects should be appended,
+ // so why not allocate the space for them
+ rList.reserve( nCount + rList.capacity() ); // not size()
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ xContainer->getByIndex(i) >>= xSet;
+ if (xSet.is())
+ AppendComponent(rList, xSet, aName, rxSubmitButton, MouseEvt);
+ }
+ }
+ }
+}
+
+
+void ODatabaseForm::FillSuccessfulList( HtmlSuccessfulObjList& rList,
+ const Reference<XControl>& rxSubmitButton, const css::awt::MouseEvent& MouseEvt )
+{
+ // Delete list
+ rList.clear();
+ // Iterate over Components
+ Reference<XPropertySet> xComponentSet;
+
+ // we know already how many objects should be appended,
+ // so why not allocate the space for them
+ rList.reserve( getCount() );
+ for( sal_Int32 nIndex=0; nIndex < getCount(); nIndex++ )
+ {
+ getByIndex( nIndex ) >>= xComponentSet;
+ AppendComponent(rList, xComponentSet, std::u16string_view(), rxSubmitButton, MouseEvt);
+ }
+}
+
+
+void ODatabaseForm::Encode( OUString& rString )
+{
+ OUStringBuffer aResult;
+
+ // Line endings are represented as CR
+ rString = convertLineEnd(rString, LINEEND_CR);
+
+ // Check each character
+ sal_Int32 nStrLen = rString.getLength();
+ sal_Unicode nCharCode;
+ for( sal_Int32 nCurPos=0; nCurPos < nStrLen; ++nCurPos )
+ {
+ nCharCode = rString[nCurPos];
+
+ // Handle chars, which are not an alphanumeric character and character codes > 127
+ if( (!rtl::isAsciiAlphanumeric(nCharCode) && nCharCode != ' ')
+ || nCharCode > 127 )
+ {
+ switch( nCharCode )
+ {
+ case 13: // CR
+ aResult.append("%0D%0A"); // CR LF in hex
+ break;
+
+
+ // Special treatment for Netscape
+ case 42: // '*'
+ case 45: // '-'
+ case 46: // '.'
+ case 64: // '@'
+ case 95: // '_'
+ aResult.append(nCharCode);
+ break;
+
+ default:
+ {
+ // Convert to hex
+ short nHi = static_cast<sal_Int16>(nCharCode) / 16;
+ short nLo = static_cast<sal_Int16>(nCharCode) - (nHi*16);
+ if( nHi > 9 ) nHi += int('A')-10; else nHi += int('0');
+ if( nLo > 9 ) nLo += int('A')-10; else nLo += int('0');
+ aResult.append("%"
+ + OUStringChar(static_cast<sal_Unicode>(nHi))
+ + OUStringChar(static_cast<sal_Unicode>(nLo)) );
+ }
+ }
+ }
+ else
+ aResult.append(nCharCode);
+ }
+
+ // Replace spaces with '+'
+ rString = aResult.makeStringAndClear().replace(' ', '+');
+}
+
+
+void ODatabaseForm::InsertTextPart( INetMIMEMessage& rParent, std::u16string_view rName,
+ std::u16string_view rData )
+{
+ // Create part as MessageChild
+ std::unique_ptr<INetMIMEMessage> pChild(new INetMIMEMessage);
+
+ // Header
+ //TODO: Encode rName into a properly formatted Content-Disposition header
+ // field as per RFC 2231:
+ OUString aContentDisp = OUString::Concat("form-data; name=\"") + rName + "\"";
+ pChild->SetContentDisposition(aContentDisp);
+
+ rtl_TextEncoding eSystemEncoding = osl_getThreadTextEncoding();
+ const char* pBestMatchingEncoding = rtl_getBestMimeCharsetFromTextEncoding( eSystemEncoding );
+ OUString aBestMatchingEncoding = OUString::createFromAscii(pBestMatchingEncoding);
+ pChild->SetContentType(
+ "text/plain; charset=\"" + aBestMatchingEncoding + "\"");
+ pChild->SetContentTransferEncoding("8bit");
+
+ // Body
+ SvMemoryStream* pStream = new SvMemoryStream;
+ pStream->WriteLine( OUStringToOString(rData, rtl_getTextEncodingFromMimeCharset(pBestMatchingEncoding)) );
+ pStream->FlushBuffer();
+ pStream->Seek( 0 );
+ pChild->SetDocumentLB( new SvLockBytes(pStream, true) );
+ rParent.AttachChild( std::move(pChild) );
+}
+
+
+void ODatabaseForm::InsertFilePart( INetMIMEMessage& rParent, std::u16string_view rName,
+ const OUString& rFileName )
+{
+ OUString aFileName(rFileName);
+ OUString aContentType(CONTENT_TYPE_STR_TEXT_PLAIN);
+ std::unique_ptr<SvStream> pStream;
+
+ if (!aFileName.isEmpty())
+ {
+ // We can only process File URLs yet
+ INetURLObject aURL;
+ aURL.SetSmartProtocol(INetProtocol::File);
+ aURL.SetSmartURL(rFileName);
+ if( INetProtocol::File == aURL.GetProtocol() )
+ {
+ aFileName = INetURLObject::decode(aURL.PathToFileName(), INetURLObject::DecodeMechanism::Unambiguous);
+ pStream = ::utl::UcbStreamHelper::CreateStream(aFileName, StreamMode::READ);
+ if (!pStream || (pStream->GetError() != ERRCODE_NONE))
+ {
+ pStream.reset();
+ }
+ sal_Int32 nSepInd = aFileName.lastIndexOf('.');
+ OUString aExtension = aFileName.copy( nSepInd + 1 );
+ INetContentType eContentType = INetContentTypes::GetContentType4Extension( aExtension );
+ if (eContentType != CONTENT_TYPE_UNKNOWN)
+ aContentType = INetContentTypes::GetContentType(eContentType);
+ }
+ }
+
+ // If something didn't work, we create an empty MemoryStream
+ if( !pStream )
+ pStream.reset( new SvMemoryStream );
+
+
+ // Create part as MessageChild
+ std::unique_ptr<INetMIMEMessage> pChild(new INetMIMEMessage);
+
+
+ // Header
+ //TODO: Encode rName and aFileName into a properly formatted
+ // Content-Disposition header field as per RFC 2231:
+ OUString aContentDisp =
+ OUString::Concat("form-data; name=\"") +
+ rName +
+ "\""
+ "; filename=\"" +
+ aFileName +
+ "\"";
+ pChild->SetContentDisposition(aContentDisp);
+ pChild->SetContentType( aContentType );
+ pChild->SetContentTransferEncoding("8bit");
+
+
+ // Body
+ pChild->SetDocumentLB( new SvLockBytes(pStream.release(), true) );
+ rParent.AttachChild( std::move(pChild) );
+}
+
+
+// internals
+
+void ODatabaseForm::onError( const SQLErrorEvent& _rEvent )
+{
+ m_aErrorListeners.notifyEach( &XSQLErrorListener::errorOccured, _rEvent );
+}
+
+
+void ODatabaseForm::onError( const SQLException& _rException, const OUString& _rContextDescription )
+{
+ if ( !m_aErrorListeners.getLength() )
+ return;
+
+ SQLErrorEvent aEvent( *this, Any( prependErrorInfo( _rException, *this, _rContextDescription ) ) );
+ onError( aEvent );
+}
+
+
+void ODatabaseForm::updateParameterInfo()
+{
+ m_aParameterManager.updateParameterInfo( m_aFilterManager );
+}
+
+
+bool ODatabaseForm::hasValidParent() const
+{
+ // do we have to fill the parameters again?
+ if (!m_bSubForm)
+ return true;
+ Reference<XResultSet> xResultSet(m_xParent, UNO_QUERY);
+ if (!xResultSet.is())
+ {
+ OSL_FAIL("ODatabaseForm::hasValidParent() : no parent resultset !");
+ return false;
+ }
+ try
+ {
+ Reference< XPropertySet > xSet( m_xParent, UNO_QUERY );
+ Reference< XLoadable > xLoad( m_xParent, UNO_QUERY );
+ if ( xLoad->isLoaded()
+ && ( xResultSet->isBeforeFirst()
+ || xResultSet->isAfterLast()
+ || getBOOL( xSet->getPropertyValue( PROPERTY_ISNEW ) )
+ )
+ )
+ // the parent form is loaded and on a "virtual" row -> not valid
+ return false;
+ }
+ catch(const Exception&)
+ {
+ // parent could be forwardonly?
+ return false;
+ }
+ return true;
+}
+
+
+bool ODatabaseForm::fillParameters( ::osl::ResettableMutexGuard& _rClearForNotifies, const Reference< XInteractionHandler >& _rxCompletionHandler )
+{
+ // do we have to fill the parameters again?
+ if ( !m_aParameterManager.isUpToDate() )
+ updateParameterInfo();
+
+ // is there a valid parent?
+ if ( m_bSubForm && !hasValidParent() )
+ return true;
+
+ // ensure we're connected
+ if ( !implEnsureConnection() )
+ return false;
+
+ if ( m_aParameterManager.isUpToDate() )
+ return m_aParameterManager.fillParameterValues( _rxCompletionHandler, _rClearForNotifies );
+
+ return true;
+}
+
+
+void ODatabaseForm::saveInsertOnlyState( )
+{
+ OSL_ENSURE( !m_aIgnoreResult.hasValue(), "ODatabaseForm::saveInsertOnlyState: overriding old value!" );
+ m_aIgnoreResult = m_xAggregateSet->getPropertyValue( PROPERTY_INSERTONLY );
+}
+
+
+void ODatabaseForm::restoreInsertOnlyState( )
+{
+ if ( m_aIgnoreResult.hasValue() )
+ {
+ m_xAggregateSet->setPropertyValue( PROPERTY_INSERTONLY, m_aIgnoreResult );
+ m_aIgnoreResult = Any();
+ }
+}
+
+
+bool ODatabaseForm::executeRowSet(::osl::ResettableMutexGuard& _rClearForNotifies, bool bMoveToFirst, const Reference< XInteractionHandler >& _rxCompletionHandler)
+{
+ if (!m_xAggregateAsRowSet.is())
+ return false;
+
+ if (!fillParameters(_rClearForNotifies, _rxCompletionHandler))
+ return false;
+
+ restoreInsertOnlyState( );
+
+ // ensure the aggregated row set has the correct properties
+ sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
+
+ // if we have a parent, who is not positioned on a valid row
+ // we can't be updatable!
+ if (m_bSubForm && !hasValidParent())
+ {
+ nConcurrency = ResultSetConcurrency::READ_ONLY;
+
+ // don't use any parameters if we don't have a valid parent
+ m_aParameterManager.setAllParametersNull();
+
+ // switch to "insert only" mode
+ saveInsertOnlyState( );
+ m_xAggregateSet->setPropertyValue( PROPERTY_INSERTONLY, Any( true ) );
+ }
+ else if (m_bAllowInsert || m_bAllowUpdate || m_bAllowDelete)
+ nConcurrency = ResultSetConcurrency::UPDATABLE;
+ else
+ nConcurrency = ResultSetConcurrency::READ_ONLY;
+
+ m_xAggregateSet->setPropertyValue( PROPERTY_RESULTSET_CONCURRENCY, Any( nConcurrency ) );
+ m_xAggregateSet->setPropertyValue( PROPERTY_RESULTSET_TYPE, Any( sal_Int32(ResultSetType::SCROLL_SENSITIVE) ) );
+
+ bool bSuccess = false;
+ try
+ {
+ m_xAggregateAsRowSet->execute();
+ bSuccess = true;
+ }
+ catch(const RowSetVetoException&)
+ {
+ }
+ catch(const SQLException& eDb)
+ {
+ _rClearForNotifies.clear();
+ if (!m_sCurrentErrorContext.isEmpty())
+ onError(eDb, m_sCurrentErrorContext);
+ else
+ onError(eDb, ResourceManager::loadString(RID_STR_READERROR));
+ _rClearForNotifies.reset();
+
+ restoreInsertOnlyState( );
+ }
+
+ if (bSuccess)
+ {
+ // adjust the privilege property
+ // m_nPrivileges;
+ m_xAggregateSet->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges;
+ if (!m_bAllowInsert)
+ m_nPrivileges &= ~Privilege::INSERT;
+ if (!m_bAllowUpdate)
+ m_nPrivileges &= ~Privilege::UPDATE;
+ if (!m_bAllowDelete)
+ m_nPrivileges &= ~Privilege::DELETE;
+
+ if (bMoveToFirst)
+ {
+ // the row set is positioned _before_ the first row (per definitionem), so move the set ...
+ try
+ {
+ // if we have an insert only rowset we move to the insert row
+ next();
+ if (((m_nPrivileges & Privilege::INSERT) == Privilege::INSERT)
+ && isAfterLast())
+ {
+ // move on the insert row of set
+ // resetting must be done later, after the load events have been posted
+ // see: moveToInsertRow and load , reload
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->moveToInsertRow();
+ }
+ }
+ catch(const SQLException& eDB)
+ {
+ _rClearForNotifies.clear();
+ if (!m_sCurrentErrorContext.isEmpty())
+ onError(eDB, m_sCurrentErrorContext);
+ else
+ onError(eDB, ResourceManager::loadString(RID_STR_READERROR));
+ _rClearForNotifies.reset();
+ bSuccess = false;
+ }
+ }
+ }
+ return bSuccess;
+}
+
+
+void ODatabaseForm::disposing()
+{
+ if (m_xAggregatePropertyMultiplexer.is())
+ m_xAggregatePropertyMultiplexer->dispose();
+
+ if (m_bLoaded)
+ unload();
+
+ // cancel the submit/reset-thread
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_pThread.clear();
+ }
+
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aLoadListeners.disposeAndClear(aEvt);
+ m_aRowSetApproveListeners.disposeAndClear(aEvt);
+ m_aResetListeners.disposeAndClear(aEvt);
+ m_aSubmitListeners.disposeAndClear(aEvt);
+ m_aErrorListeners.disposeAndClear(aEvt);
+
+ m_aParameterManager.dispose(); // To free any references it may have to be me
+ m_aFilterManager.dispose(); // (ditto)
+
+ OFormComponents::disposing();
+ OPropertySetAggregationHelper::disposing();
+
+ // stop listening on the aggregate
+ if (m_xAggregateAsRowSet.is())
+ m_xAggregateAsRowSet->removeRowSetListener(this);
+
+ // dispose the active connection
+ Reference<XComponent> xAggregationComponent;
+ if (query_aggregation(m_xAggregate, xAggregationComponent))
+ xAggregationComponent->dispose();
+
+ m_aPropertyBagHelper.dispose();
+}
+
+
+Reference< XConnection > ODatabaseForm::getConnection()
+{
+ Reference< XConnection > xConn;
+ m_xAggregateSet->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConn;
+ return xConn;
+}
+
+
+::osl::Mutex& ODatabaseForm::getMutex()
+{
+ return m_aMutex;
+}
+
+
+// property handling
+
+void ODatabaseForm::describeFixedAndAggregateProperties(
+ Sequence< Property >& _rProps,
+ Sequence< Property >& _rAggregateProps ) const
+{
+ _rProps.realloc( 23 );
+ css::beans::Property* pProperties = _rProps.getArray();
+
+ if (m_xAggregateSet.is())
+ _rAggregateProps = m_xAggregateSet->getPropertySetInfo()->getProperties();
+
+
+ // we want to "override" the privileges, since we have additional "AllowInsert" etc. properties
+ RemoveProperty( _rAggregateProps, PROPERTY_PRIVILEGES );
+
+ // InsertOnly is also to be overridden, since we sometimes change it ourself
+ RemoveProperty( _rAggregateProps, PROPERTY_INSERTONLY );
+
+ // we remove and re-declare the DataSourceName property, 'cause we want it to be constrained, and the
+ // original property of our aggregate isn't
+ RemoveProperty( _rAggregateProps, PROPERTY_DATASOURCE );
+
+ // for connection sharing, we need to override the ActiveConnection property, too
+ RemoveProperty( _rAggregateProps, PROPERTY_ACTIVE_CONNECTION );
+
+ // the Filter property is also overwritten, since we have some implicit filters
+ // (e.g. the ones which result from linking master fields to detail fields
+ // via column names instead of parameters)
+ RemoveProperty( _rAggregateProps, PROPERTY_FILTER );
+ RemoveProperty( _rAggregateProps, PROPERTY_HAVINGCLAUSE );
+ RemoveProperty( _rAggregateProps, PROPERTY_APPLYFILTER );
+
+ *pProperties++ = css::beans::Property(PROPERTY_ACTIVE_CONNECTION, PROPERTY_ID_ACTIVE_CONNECTION, cppu::UnoType<XConnection>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT |
+ css::beans::PropertyAttribute::MAYBEVOID | PropertyAttribute::CONSTRAINED);
+ *pProperties++ = css::beans::Property(PROPERTY_APPLYFILTER, PROPERTY_ID_APPLYFILTER, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_MASTERFIELDS, PROPERTY_ID_MASTERFIELDS, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DETAILFIELDS, PROPERTY_ID_DETAILFIELDS, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DATASOURCE, PROPERTY_ID_DATASOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::CONSTRAINED);
+ *pProperties++ = css::beans::Property(PROPERTY_CYCLE, PROPERTY_ID_CYCLE, cppu::UnoType<TabulatorCycle>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FILTER, PROPERTY_ID_FILTER, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_HAVINGCLAUSE, PROPERTY_ID_HAVINGCLAUSE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_INSERTONLY, PROPERTY_ID_INSERTONLY, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_NAVIGATION, PROPERTY_ID_NAVIGATION, cppu::UnoType<NavigationBarMode>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_ALLOWADDITIONS, PROPERTY_ID_ALLOWADDITIONS, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_ALLOWEDITS, PROPERTY_ID_ALLOWEDITS, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_ALLOWDELETIONS, PROPERTY_ID_ALLOWDELETIONS, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_PRIVILEGES, PROPERTY_ID_PRIVILEGES, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_URL, PROPERTY_ID_TARGET_URL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_FRAME, PROPERTY_ID_TARGET_FRAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_SUBMIT_METHOD, PROPERTY_ID_SUBMIT_METHOD, cppu::UnoType<FormSubmitMethod>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_SUBMIT_ENCODING, PROPERTY_ID_SUBMIT_ENCODING, cppu::UnoType<FormSubmitEncoding>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DYNAMIC_CONTROL_BORDER, PROPERTY_ID_DYNAMIC_CONTROL_BORDER, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT );
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROL_BORDER_COLOR_FOCUS, PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROL_BORDER_COLOR_MOUSE, PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROL_BORDER_COLOR_INVALID, PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+Reference< XMultiPropertySet > ODatabaseForm::getPropertiesInterface()
+{
+ return this;
+}
+
+
+::cppu::IPropertyArrayHelper& ODatabaseForm::getInfoHelper()
+{
+ return m_aPropertyBagHelper.getInfoHelper();
+}
+
+
+Reference< XPropertySetInfo > ODatabaseForm::getPropertySetInfo()
+{
+ return createPropertySetInfo( getInfoHelper() );
+}
+
+
+void SAL_CALL ODatabaseForm::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
+{
+ m_aPropertyBagHelper.addProperty( _rName, _nAttributes, _rInitialValue );
+}
+
+
+void SAL_CALL ODatabaseForm::removeProperty( const OUString& _rName )
+{
+ m_aPropertyBagHelper.removeProperty( _rName );
+}
+
+
+Sequence< PropertyValue > SAL_CALL ODatabaseForm::getPropertyValues()
+{
+ return m_aPropertyBagHelper.getPropertyValues();
+}
+
+
+void SAL_CALL ODatabaseForm::setPropertyValues( const Sequence< PropertyValue >& _rProps )
+{
+ m_aPropertyBagHelper.setPropertyValues( _rProps );
+}
+
+
+Any SAL_CALL ODatabaseForm::getWarnings( )
+{
+ return m_aWarnings.getWarnings();
+}
+
+
+void SAL_CALL ODatabaseForm::clearWarnings( )
+{
+ m_aWarnings.clearWarnings();
+}
+
+
+Reference< XCloneable > SAL_CALL ODatabaseForm::createClone( )
+{
+ rtl::Reference<ODatabaseForm> pClone = new ODatabaseForm( *this );
+ pClone->clonedFrom( *this );
+ return pClone;
+}
+
+
+void ODatabaseForm::fire( sal_Int32* pnHandles, const Any* pNewValues, const Any* pOldValues, sal_Int32 nCount )
+{
+ // same as in getFastPropertyValue(sal_Int32) : if we're resetting currently don't fire any changes of the
+ // IsModified property from sal_False to sal_True, as this is only temporary 'til the reset is done
+ if (m_nResetsPending > 0)
+ {
+ // look for the PROPERTY_ID_ISMODIFIED
+ sal_Int32 nPos = 0;
+ for (nPos=0; nPos<nCount; ++nPos)
+ if (pnHandles[nPos] == PROPERTY_ID_ISMODIFIED)
+ break;
+
+ if ((nPos < nCount) && (pNewValues[nPos].getValueType().getTypeClass() == TypeClass_BOOLEAN) && getBOOL(pNewValues[nPos]))
+ { // yeah, we found it, and it changed to TRUE
+ if (nPos == 0)
+ { // just cut the first element
+ ++pnHandles;
+ ++pNewValues;
+ ++pOldValues;
+ --nCount;
+ }
+ else if (nPos == nCount - 1)
+ // just cut the last element
+ --nCount;
+ else
+ { // split into two base class calls
+ OPropertySetAggregationHelper::fire(pnHandles, pNewValues, pOldValues, nPos, false/*bVetoable*/);
+ ++nPos;
+ OPropertySetAggregationHelper::fire(pnHandles + nPos, pNewValues + nPos, pOldValues + nPos, nCount - nPos, false/*bVetoable*/);
+ return;
+ }
+ }
+ }
+
+ OPropertySetAggregationHelper::fire(pnHandles, pNewValues, pOldValues, nCount, false/*bVetoable*/);
+}
+
+
+Any SAL_CALL ODatabaseForm::getFastPropertyValue( sal_Int32 nHandle )
+{
+ if ((nHandle == PROPERTY_ID_ISMODIFIED) && (m_nResetsPending > 0))
+ return css::uno::Any(false);
+ // don't allow the aggregate which is currently being reset to return a (temporary) "yes"
+ else
+ return OPropertySetAggregationHelper::getFastPropertyValue(nHandle);
+}
+
+
+void ODatabaseForm::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ rValue <<= m_bInsertOnly;
+ break;
+
+ case PROPERTY_ID_FILTER:
+ rValue <<= m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicFilter );
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ rValue <<= m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicHaving );
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ rValue <<= m_aFilterManager.isApplyPublicFilter();
+ break;
+
+ case PROPERTY_ID_DATASOURCE:
+ rValue = m_xAggregateSet->getPropertyValue( PROPERTY_DATASOURCE );
+ break;
+
+ case PROPERTY_ID_TARGET_URL:
+ rValue <<= m_aTargetURL;
+ break;
+ case PROPERTY_ID_TARGET_FRAME:
+ rValue <<= m_aTargetFrame;
+ break;
+ case PROPERTY_ID_SUBMIT_METHOD:
+ rValue <<= m_eSubmitMethod;
+ break;
+ case PROPERTY_ID_SUBMIT_ENCODING:
+ rValue <<= m_eSubmitEncoding;
+ break;
+ case PROPERTY_ID_NAME:
+ rValue <<= m_sName;
+ break;
+ case PROPERTY_ID_MASTERFIELDS:
+ rValue <<= m_aMasterFields;
+ break;
+ case PROPERTY_ID_DETAILFIELDS:
+ rValue <<= m_aDetailFields;
+ break;
+ case PROPERTY_ID_CYCLE:
+ rValue = m_aCycle;
+ break;
+ case PROPERTY_ID_NAVIGATION:
+ rValue <<= m_eNavigation;
+ break;
+ case PROPERTY_ID_ALLOWADDITIONS:
+ rValue <<= m_bAllowInsert;
+ break;
+ case PROPERTY_ID_ALLOWEDITS:
+ rValue <<= m_bAllowUpdate;
+ break;
+ case PROPERTY_ID_ALLOWDELETIONS:
+ rValue <<= m_bAllowDelete;
+ break;
+ case PROPERTY_ID_PRIVILEGES:
+ rValue <<= m_nPrivileges;
+ break;
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ rValue = m_aDynamicControlBorder;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ rValue = m_aControlBorderColorFocus;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ rValue = m_aControlBorderColorMouse;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ rValue = m_aControlBorderColorInvalid;
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( nHandle ) )
+ m_aPropertyBagHelper.getDynamicFastPropertyValue( nHandle, rValue );
+ else
+ OPropertySetAggregationHelper::getFastPropertyValue( rValue, nHandle );
+ break;
+ }
+}
+
+sal_Bool ODatabaseForm::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue,
+ sal_Int32 nHandle, const Any& rValue )
+{
+ bool bModified(false);
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_bInsertOnly );
+ break;
+
+ case PROPERTY_ID_FILTER:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicFilter ) );
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicHaving ) );
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aFilterManager.isApplyPublicFilter() );
+ break;
+
+ case PROPERTY_ID_DATASOURCE:
+ {
+ Any aAggregateProperty;
+ getFastPropertyValue(aAggregateProperty, PROPERTY_ID_DATASOURCE);
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, aAggregateProperty, cppu::UnoType<OUString>::get());
+ }
+ break;
+ case PROPERTY_ID_TARGET_URL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aTargetURL);
+ break;
+ case PROPERTY_ID_TARGET_FRAME:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aTargetFrame);
+ break;
+ case PROPERTY_ID_SUBMIT_METHOD:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_eSubmitMethod);
+ break;
+ case PROPERTY_ID_SUBMIT_ENCODING:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_eSubmitEncoding);
+ break;
+ case PROPERTY_ID_NAME:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sName);
+ break;
+ case PROPERTY_ID_MASTERFIELDS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aMasterFields);
+ break;
+ case PROPERTY_ID_DETAILFIELDS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDetailFields);
+ break;
+ case PROPERTY_ID_CYCLE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aCycle, cppu::UnoType<TabulatorCycle>::get());
+ break;
+ case PROPERTY_ID_NAVIGATION:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_eNavigation);
+ break;
+ case PROPERTY_ID_ALLOWADDITIONS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAllowInsert);
+ break;
+ case PROPERTY_ID_ALLOWEDITS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAllowUpdate);
+ break;
+ case PROPERTY_ID_ALLOWDELETIONS:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAllowDelete);
+ break;
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aDynamicControlBorder, cppu::UnoType<bool>::get() );
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aControlBorderColorFocus, cppu::UnoType<sal_Int32>::get() );
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aControlBorderColorMouse, cppu::UnoType<sal_Int32>::get() );
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_aControlBorderColorInvalid, cppu::UnoType<sal_Int32>::get() );
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle ( nHandle ) )
+ bModified = m_aPropertyBagHelper.convertDynamicFastPropertyValue( nHandle, rValue, rConvertedValue, rOldValue );
+ else
+ bModified = OPropertySetAggregationHelper::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue );
+ break;
+ }
+ return bModified;
+}
+
+void ODatabaseForm::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ rValue >>= m_bInsertOnly;
+ if ( m_aIgnoreResult.hasValue() )
+ m_aIgnoreResult <<= m_bInsertOnly;
+ else
+ m_xAggregateSet->setPropertyValue( PROPERTY_INSERTONLY, Any( m_bInsertOnly ) );
+ break;
+
+ case PROPERTY_ID_FILTER:
+ {
+ OUString sNewFilter;
+ rValue >>= sNewFilter;
+ m_aFilterManager.setFilterComponent( FilterManager::FilterComponent::PublicFilter, sNewFilter );
+ }
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ {
+ OUString sNewFilter;
+ rValue >>= sNewFilter;
+ m_aFilterManager.setFilterComponent( FilterManager::FilterComponent::PublicHaving, sNewFilter );
+ }
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ {
+ bool bApply = true;
+ rValue >>= bApply;
+ m_aFilterManager.setApplyPublicFilter( bApply );
+ }
+ break;
+
+ case PROPERTY_ID_DATASOURCE:
+ {
+ Reference< XConnection > xSomeConnection;
+ if ( ::dbtools::isEmbeddedInDatabase( getParent(), xSomeConnection ) )
+ throw PropertyVetoException();
+
+ try
+ {
+ m_xAggregateSet->setPropertyValue(PROPERTY_DATASOURCE, rValue);
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ break;
+ case PROPERTY_ID_TARGET_URL:
+ rValue >>= m_aTargetURL;
+ break;
+ case PROPERTY_ID_TARGET_FRAME:
+ rValue >>= m_aTargetFrame;
+ break;
+ case PROPERTY_ID_SUBMIT_METHOD:
+ rValue >>= m_eSubmitMethod;
+ break;
+ case PROPERTY_ID_SUBMIT_ENCODING:
+ rValue >>= m_eSubmitEncoding;
+ break;
+ case PROPERTY_ID_NAME:
+ rValue >>= m_sName;
+ break;
+ case PROPERTY_ID_MASTERFIELDS:
+ rValue >>= m_aMasterFields;
+ invalidateParameters();
+ break;
+ case PROPERTY_ID_DETAILFIELDS:
+ rValue >>= m_aDetailFields;
+ invalidateParameters();
+ break;
+ case PROPERTY_ID_CYCLE:
+ m_aCycle = rValue;
+ break;
+ case PROPERTY_ID_NAVIGATION:
+ rValue >>= m_eNavigation;
+ break;
+ case PROPERTY_ID_ALLOWADDITIONS:
+ m_bAllowInsert = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ALLOWEDITS:
+ m_bAllowUpdate = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ALLOWDELETIONS:
+ m_bAllowDelete = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ m_aDynamicControlBorder = rValue;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ m_aControlBorderColorFocus = rValue;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ m_aControlBorderColorMouse = rValue;
+ break;
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ m_aControlBorderColorInvalid = rValue;
+ break;
+
+ case PROPERTY_ID_ACTIVE_CONNECTION:
+ {
+ Reference< XConnection > xOuterConnection;
+ if ( ::dbtools::isEmbeddedInDatabase( getParent(), xOuterConnection ) )
+ {
+ if ( xOuterConnection != Reference< XConnection >( rValue, UNO_QUERY ) )
+ // somebody's trying to set a connection which is not equal the connection
+ // implied by the database we're embedded in
+ throw PropertyVetoException();
+ }
+ OPropertySetAggregationHelper::setFastPropertyValue_NoBroadcast( nHandle, rValue );
+ break;
+ }
+
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( nHandle ) )
+ m_aPropertyBagHelper.setDynamicFastPropertyValue( nHandle, rValue );
+ else
+ OPropertySetAggregationHelper::setFastPropertyValue_NoBroadcast( nHandle, rValue );
+ break;
+ }
+}
+
+
+void ODatabaseForm::forwardingPropertyValue( sal_Int32 _nHandle )
+{
+ OSL_ENSURE( _nHandle == PROPERTY_ID_ACTIVE_CONNECTION, "ODatabaseForm::forwardingPropertyValue: unexpected property!" );
+ if ( _nHandle == PROPERTY_ID_ACTIVE_CONNECTION )
+ {
+ if ( m_bSharingConnection )
+ stopSharingConnection( );
+ m_bForwardingConnection = true;
+ }
+}
+
+
+void ODatabaseForm::forwardedPropertyValue( sal_Int32 _nHandle )
+{
+ OSL_ENSURE( _nHandle == PROPERTY_ID_ACTIVE_CONNECTION, "ODatabaseForm::forwardedPropertyValue: unexpected property!" );
+ if ( _nHandle == PROPERTY_ID_ACTIVE_CONNECTION )
+ {
+ m_bForwardingConnection = false;
+ }
+}
+
+
+// css::beans::XPropertyState
+
+PropertyState ODatabaseForm::getPropertyStateByHandle(sal_Int32 nHandle)
+{
+ PropertyState eState;
+ switch (nHandle)
+ {
+ case PROPERTY_ID_NAVIGATION:
+ return (NavigationBarMode_CURRENT == m_eNavigation) ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
+
+ case PROPERTY_ID_CYCLE:
+ eState = m_aCycle.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_INSERTONLY:
+ eState = m_bInsertOnly ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_FILTER:
+ if ( m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicFilter ).isEmpty() )
+ eState = PropertyState_DEFAULT_VALUE;
+ else
+ eState = PropertyState_DIRECT_VALUE;
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ if ( m_aFilterManager.getFilterComponent( FilterManager::FilterComponent::PublicHaving ).isEmpty() )
+ eState = PropertyState_DEFAULT_VALUE;
+ else
+ eState = PropertyState_DIRECT_VALUE;
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ eState = m_aFilterManager.isApplyPublicFilter() ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
+ break;
+
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ eState = m_aDynamicControlBorder.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ eState = m_aControlBorderColorFocus.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ eState = m_aControlBorderColorMouse.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ eState = m_aControlBorderColorInvalid.hasValue() ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ break;
+
+ default:
+ eState = OPropertySetAggregationHelper::getPropertyStateByHandle(nHandle);
+ }
+ return eState;
+}
+
+
+void ODatabaseForm::setPropertyToDefaultByHandle(sal_Int32 nHandle)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ case PROPERTY_ID_FILTER:
+ case PROPERTY_ID_HAVINGCLAUSE:
+ case PROPERTY_ID_APPLYFILTER:
+ case PROPERTY_ID_NAVIGATION:
+ case PROPERTY_ID_CYCLE:
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ setFastPropertyValue( nHandle, getPropertyDefaultByHandle( nHandle ) );
+ break;
+
+ default:
+ OPropertySetAggregationHelper::setPropertyToDefaultByHandle(nHandle);
+ }
+}
+
+
+Any ODatabaseForm::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+{
+ Any aReturn;
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INSERTONLY:
+ case PROPERTY_ID_DYNAMIC_CONTROL_BORDER:
+ aReturn <<= false;
+ break;
+
+ case PROPERTY_ID_FILTER:
+ aReturn <<= OUString();
+ break;
+
+ case PROPERTY_ID_HAVINGCLAUSE:
+ aReturn <<= OUString();
+ break;
+
+ case PROPERTY_ID_APPLYFILTER:
+ aReturn <<= true;
+ break;
+
+ case PROPERTY_ID_NAVIGATION:
+ aReturn <<= NavigationBarMode_CURRENT;
+ break;
+
+ case PROPERTY_ID_CYCLE:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE:
+ case PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID:
+ break;
+
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( nHandle ) )
+ m_aPropertyBagHelper.getDynamicPropertyDefaultByHandle( nHandle, aReturn );
+ else
+ aReturn = OPropertySetAggregationHelper::getPropertyDefaultByHandle( nHandle );
+ break;
+ }
+ return aReturn;
+}
+
+
+// css::form::XReset
+
+void SAL_CALL ODatabaseForm::reset()
+{
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+
+ if (isLoaded())
+ {
+ ::osl::MutexGuard aResetGuard(m_aResetSafety);
+ ++m_nResetsPending;
+ reset_impl(true);
+ return;
+ }
+
+ if ( m_aResetListeners.getLength() )
+ {
+ ::osl::MutexGuard aResetGuard(m_aResetSafety);
+ ++m_nResetsPending;
+ // create an own thread if we have (approve-)reset-listeners (so the listeners can't do that much damage
+ // to this thread which is probably the main one)
+ if (!m_pThread.is())
+ {
+ m_pThread = new OFormSubmitResetThread(this);
+ m_pThread->create();
+ }
+ m_pThread->addEvent(std::make_unique<EventObject>());
+ }
+ else
+ {
+ // direct call without any approving by the listeners
+ aGuard.clear();
+
+ ::osl::MutexGuard aResetGuard(m_aResetSafety);
+ ++m_nResetsPending;
+ reset_impl(false);
+ }
+}
+
+
+void ODatabaseForm::reset_impl(bool _bApproveByListeners)
+{
+ if ( _bApproveByListeners )
+ {
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aResetListeners);
+ EventObject aEvent(*this);
+ while (aIter.hasMoreElements())
+ if (!aIter.next()->approveReset(aEvent))
+ return;
+ }
+
+ ::osl::ResettableMutexGuard aResetGuard(m_aResetSafety);
+ // do we have a database connected form and stay on the insert row
+ bool bInsertRow = false;
+ if (m_xAggregateSet.is())
+ bInsertRow = getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ISNEW));
+ if (bInsertRow)
+ {
+ try
+ {
+ // Iterate through all columns and set the default value
+ Reference< XColumnsSupplier > xColsSuppl( m_xAggregateSet, UNO_QUERY );
+ Reference< XIndexAccess > xIndexCols( xColsSuppl->getColumns(), UNO_QUERY );
+ for (sal_Int32 i = 0; i < xIndexCols->getCount(); ++i)
+ {
+ Reference< XPropertySet > xColProps;
+ xIndexCols->getByIndex(i) >>= xColProps;
+
+ Reference< XColumnUpdate > xColUpdate( xColProps, UNO_QUERY );
+ if ( !xColUpdate.is() )
+ continue;
+
+ Reference< XPropertySetInfo > xPSI;
+ if ( xColProps.is() )
+ xPSI = xColProps->getPropertySetInfo( );
+
+ static constexpr OUString PROPERTY_CONTROLDEFAULT = u"ControlDefault"_ustr;
+ if ( xPSI.is() && xPSI->hasPropertyByName( PROPERTY_CONTROLDEFAULT ) )
+ {
+ Any aDefault = xColProps->getPropertyValue( PROPERTY_CONTROLDEFAULT );
+
+ bool bReadOnly = false;
+ if ( xPSI->hasPropertyByName( PROPERTY_ISREADONLY ) )
+ xColProps->getPropertyValue( PROPERTY_ISREADONLY ) >>= bReadOnly;
+
+ if ( !bReadOnly )
+ {
+ try
+ {
+ if ( aDefault.hasValue() )
+ xColUpdate->updateObject( aDefault );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ }
+
+ if (m_bSubForm)
+ {
+ Reference< XColumnsSupplier > xParentColSupp( m_xParent, UNO_QUERY );
+ Reference< XNameAccess > xParentCols;
+ if ( xParentColSupp.is() )
+ xParentCols = xParentColSupp->getColumns();
+
+ if ( xParentCols.is() && xParentCols->hasElements() && m_aMasterFields.hasElements() )
+ {
+ try
+ {
+ // analyze our parameters
+ if ( !m_aParameterManager.isUpToDate() )
+ updateParameterInfo();
+
+ m_aParameterManager.resetParameterValues( );
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("ODatabaseForm::reset_impl: could not initialize the master-detail-driven parameters!");
+ }
+ }
+ }
+ }
+
+ aResetGuard.clear();
+ // iterate through all components. don't use an XIndexAccess as this will cause massive
+ // problems with the count.
+ Reference<XEnumeration> xIter = createEnumeration();
+ while (xIter->hasMoreElements())
+ {
+ Reference<XReset> xReset;
+ xIter->nextElement() >>= xReset;
+ if (xReset.is())
+ {
+ // TODO: all reset-methods have to be thread-safe
+ xReset->reset();
+ }
+ }
+
+ aResetGuard.reset();
+ // ensure that the row isn't modified
+ // (do this _before_ the listeners are notified ! their reaction (maybe asynchronous) may depend
+ // on the modified state of the row)
+ if (bInsertRow)
+ m_xAggregateSet->setPropertyValue(PROPERTY_ISMODIFIED, css::uno::Any(false));
+
+ aResetGuard.clear();
+ {
+ css::lang::EventObject aEvent( *this );
+ m_aResetListeners.notifyEach(&css::form::XResetListener::resetted, aEvent);
+ }
+
+ aResetGuard.reset();
+ // and again : ensure the row isn't modified
+ // we already did this after we (and maybe our dependents) reset the values, but the listeners may have changed the row, too
+ if (bInsertRow)
+ m_xAggregateSet->setPropertyValue(PROPERTY_ISMODIFIED, css::uno::Any(false));
+
+ --m_nResetsPending;
+}
+
+
+void SAL_CALL ODatabaseForm::addResetListener(const Reference<XResetListener>& _rListener)
+{
+ m_aResetListeners.addInterface( _rListener );
+}
+
+
+void SAL_CALL ODatabaseForm::removeResetListener(const Reference<XResetListener>& _rListener)
+{
+ m_aResetListeners.removeInterface( _rListener );
+}
+
+
+// css::form::XSubmit
+
+void SAL_CALL ODatabaseForm::submit( const Reference<XControl>& Control,
+ const css::awt::MouseEvent& MouseEvt )
+{
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ // Do we have controls and a Submit URL?
+ if( !getCount() || m_aTargetURL.isEmpty() )
+ return;
+ }
+
+ ::osl::ClearableMutexGuard aGuard(m_aMutex);
+ if (m_aSubmitListeners.getLength())
+ {
+ // create an own thread if we have (approve-)submit-listeners (so the listeners can't do that much damage
+ // to this thread which is probably the main one)
+ if (!m_pThread.is())
+ {
+ m_pThread = new OFormSubmitResetThread(this);
+ m_pThread->create();
+ }
+ m_pThread->addEvent(std::make_unique<css::awt::MouseEvent>(MouseEvt), Control, true);
+ }
+ else
+ {
+ // direct call without any approving by the listeners
+ aGuard.clear();
+ submit_impl( Control, MouseEvt );
+ }
+}
+
+static void lcl_dispatch(const Reference< XFrame >& xFrame,const Reference<XURLTransformer>& xTransformer,const OUString& aURLStr,const OUString& aReferer,const OUString& aTargetName
+ ,std::u16string_view aData,rtl_TextEncoding _eEncoding)
+{
+ URL aURL;
+ aURL.Complete = aURLStr;
+ xTransformer->parseStrict(aURL);
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch(aURL, aTargetName,
+ FrameSearchFlag::SELF | FrameSearchFlag::PARENT | FrameSearchFlag::CHILDREN |
+ FrameSearchFlag::SIBLINGS | FrameSearchFlag::CREATE | FrameSearchFlag::TASKS);
+
+ if (!xDisp.is())
+ return;
+
+
+ // build a sequence from the to-be-submitted string
+ OString a8BitData(OUStringToOString(aData, _eEncoding));
+ // always ANSI #58641
+ Sequence< sal_Int8 > aPostData(reinterpret_cast<const sal_Int8*>(a8BitData.getStr()), a8BitData.getLength());
+ Reference< XInputStream > xPostData = new SequenceInputStream(aPostData);
+
+ Sequence<PropertyValue> aArgs
+ {
+ comphelper::makePropertyValue("Referer", aReferer),
+ comphelper::makePropertyValue("PostData", xPostData)
+ };
+
+ xDisp->dispatch(aURL, aArgs);
+}
+
+void ODatabaseForm::submit_impl(const Reference<XControl>& Control, const css::awt::MouseEvent& MouseEvt)
+{
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aSubmitListeners);
+ EventObject aEvt(static_cast<XWeak*>(this));
+ bool bCanceled = false;
+ while (aIter.hasMoreElements() && !bCanceled)
+ {
+ if (!aIter.next()->approveSubmit(aEvt))
+ bCanceled = true;
+ }
+
+ if (bCanceled)
+ return;
+
+ FormSubmitEncoding eSubmitEncoding;
+ FormSubmitMethod eSubmitMethod;
+ OUString aURLStr;
+ OUString aReferer;
+ OUString aTargetName;
+ Reference< XModel > xModel;
+ {
+ SolarMutexGuard aGuard;
+ // starform->Forms
+
+ Reference<XChild> xParent(m_xParent, UNO_QUERY);
+
+ if (xParent.is())
+ xModel = getXModel(xParent->getParent());
+
+ if (xModel.is())
+ aReferer = xModel->getURL();
+
+ // TargetItem
+ aTargetName = m_aTargetFrame;
+
+ eSubmitEncoding = m_eSubmitEncoding;
+ eSubmitMethod = m_eSubmitMethod;
+ aURLStr = m_aTargetURL;
+ }
+
+ if (!xModel.is())
+ return;
+ Reference< XFrame > xFrame = xModel->getCurrentController()->getFrame();
+ if (!xFrame.is())
+ return;
+
+ Reference<XURLTransformer> xTransformer(URLTransformer::create(m_xContext));
+
+ // URL encoding
+ if( eSubmitEncoding == FormSubmitEncoding_URL )
+ {
+ OUString aData;
+ {
+ SolarMutexGuard aGuard;
+ aData = GetDataEncoded(true, Control, MouseEvt);
+ }
+
+ URL aURL;
+ // FormMethod GET
+ if( eSubmitMethod == FormSubmitMethod_GET )
+ {
+ INetURLObject aUrlObj( aURLStr, INetURLObject::EncodeMechanism::WasEncoded );
+ aUrlObj.SetParam( aData, INetURLObject::EncodeMechanism::All );
+ aURL.Complete = aUrlObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+ if (xTransformer.is())
+ xTransformer->parseStrict(aURL);
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch(aURL, aTargetName,
+ FrameSearchFlag::SELF | FrameSearchFlag::PARENT | FrameSearchFlag::CHILDREN |
+ FrameSearchFlag::SIBLINGS | FrameSearchFlag::CREATE | FrameSearchFlag::TASKS);
+
+ if (xDisp.is())
+ {
+ Sequence<PropertyValue> aArgs { comphelper::makePropertyValue("Referer", aReferer) };
+ xDisp->dispatch(aURL, aArgs);
+ }
+ }
+ // FormMethod POST
+ else if( eSubmitMethod == FormSubmitMethod_POST )
+ {
+ lcl_dispatch(xFrame,xTransformer,aURLStr,aReferer,aTargetName,aData,RTL_TEXTENCODING_MS_1252);
+ }
+ }
+ else if( eSubmitEncoding == FormSubmitEncoding_MULTIPART )
+ {
+ URL aURL;
+ aURL.Complete = aURLStr;
+ xTransformer->parseStrict(aURL);
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch(aURL, aTargetName,
+ FrameSearchFlag::SELF | FrameSearchFlag::PARENT | FrameSearchFlag::CHILDREN |
+ FrameSearchFlag::SIBLINGS | FrameSearchFlag::CREATE | FrameSearchFlag::TASKS);
+
+ if (xDisp.is())
+ {
+ OUString aContentType;
+ Sequence<sal_Int8> aData;
+ {
+ SolarMutexGuard aGuard;
+ aData = GetDataMultiPartEncoded(Control, MouseEvt, aContentType);
+ }
+ if (!aData.hasElements())
+ return;
+
+ // build a sequence from the to-be-submitted string
+ Reference< XInputStream > xPostData = new SequenceInputStream(aData);
+
+ Sequence<PropertyValue> aArgs
+ {
+ comphelper::makePropertyValue("Referer", aReferer),
+ comphelper::makePropertyValue("ContentType", aContentType),
+ comphelper::makePropertyValue("PostData", xPostData)
+ };
+
+ xDisp->dispatch(aURL, aArgs);
+ }
+ }
+ else if( eSubmitEncoding == FormSubmitEncoding_TEXT )
+ {
+ OUString aData;
+ {
+ SolarMutexGuard aGuard;
+ aData = GetDataEncoded(false, Reference<XControl> (), MouseEvt);
+ }
+
+ lcl_dispatch(xFrame,xTransformer,aURLStr,aReferer,aTargetName,aData,osl_getThreadTextEncoding());
+ }
+ else {
+ OSL_FAIL("ODatabaseForm::submit_Impl : wrong encoding !");
+ }
+
+}
+
+// XSubmit
+
+void SAL_CALL ODatabaseForm::addSubmitListener(const Reference<XSubmitListener>& _rListener)
+{
+ m_aSubmitListeners.addInterface(_rListener);
+}
+
+
+void SAL_CALL ODatabaseForm::removeSubmitListener(const Reference<XSubmitListener>& _rListener)
+{
+ m_aSubmitListeners.removeInterface(_rListener);
+}
+
+
+// css::sdbc::XSQLErrorBroadcaster
+
+void SAL_CALL ODatabaseForm::addSQLErrorListener(const Reference<XSQLErrorListener>& _rListener)
+{
+ m_aErrorListeners.addInterface(_rListener);
+}
+
+
+void SAL_CALL ODatabaseForm::removeSQLErrorListener(const Reference<XSQLErrorListener>& _rListener)
+{
+ m_aErrorListeners.removeInterface(_rListener);
+}
+
+
+void ODatabaseForm::invalidateParameters()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ m_aParameterManager.clearAllParameterInformation();
+}
+
+
+// OChangeListener
+
+void ODatabaseForm::_propertyChanged(const PropertyChangeEvent& evt)
+{
+ if (evt.PropertyName == PROPERTY_ACTIVE_CONNECTION && !m_bForwardingConnection)
+ {
+ // the rowset changed its active connection itself (without interaction from our side), so
+ // we need to fire this event, too
+ sal_Int32 nHandle = PROPERTY_ID_ACTIVE_CONNECTION;
+ fire(&nHandle, &evt.NewValue, &evt.OldValue, 1);
+ }
+ else // it was one of the statement relevant props
+ {
+ // if the statement has changed we have to delete the parameter info
+ invalidateParameters();
+ }
+}
+
+
+// smartXChild
+
+void SAL_CALL ODatabaseForm::setParent(const css::uno::Reference<css::uno::XInterface>& Parent)
+{
+ // SYNCHRONIZED ----->
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+
+ Reference<XForm> xParentForm(getParent(), UNO_QUERY);
+ if (xParentForm.is())
+ {
+ try
+ {
+ Reference< XRowSetApproveBroadcaster > xParentApprBroadcast( xParentForm, UNO_QUERY_THROW );
+ xParentApprBroadcast->removeRowSetApproveListener( this );
+
+ Reference< XLoadable > xParentLoadable( xParentForm, UNO_QUERY_THROW );
+ xParentLoadable->removeLoadListener( this );
+
+ Reference< XPropertySet > xParentProperties( xParentForm, UNO_QUERY_THROW );
+ xParentProperties->removePropertyChangeListener( PROPERTY_ISNEW, this );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+
+ OFormComponents::setParent(Parent);
+
+ xParentForm.set(getParent(), UNO_QUERY);
+ if ( xParentForm.is() )
+ {
+ try
+ {
+ Reference< XRowSetApproveBroadcaster > xParentApprBroadcast( xParentForm, UNO_QUERY_THROW );
+ xParentApprBroadcast->addRowSetApproveListener( this );
+
+ Reference< XLoadable > xParentLoadable( xParentForm, UNO_QUERY_THROW );
+ xParentLoadable->addLoadListener( this );
+
+ Reference< XPropertySet > xParentProperties( xParentForm, UNO_QUERY_THROW );
+ xParentProperties->addPropertyChangeListener( PROPERTY_ISNEW, this );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+
+ Reference< XPropertySet > xAggregateProperties( m_xAggregateSet );
+ aGuard.clear();
+ // <----- SYNCHRONIZED
+
+ Reference< XConnection > xOuterConnection;
+ bool bIsEmbedded = ::dbtools::isEmbeddedInDatabase( Parent, xOuterConnection );
+
+ if ( bIsEmbedded )
+ xAggregateProperties->setPropertyValue( PROPERTY_DATASOURCE, Any( OUString() ) );
+}
+
+
+// smartXTabControllerModel
+
+sal_Bool SAL_CALL ODatabaseForm::getGroupControl()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ // Should controls be combined into a TabOrder group?
+ if (m_aCycle.hasValue())
+ {
+ sal_Int32 nCycle = 0;
+ ::cppu::enum2int(nCycle, m_aCycle);
+ return static_cast<TabulatorCycle>(nCycle) != TabulatorCycle_PAGE;
+ }
+
+ if (isLoaded() && getConnection().is())
+ return true;
+
+ return false;
+}
+
+
+void SAL_CALL ODatabaseForm::setControlModels(const Sequence<Reference<XControlModel> >& rControls)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ // Set TabIndex in the order of the sequence
+ sal_Int32 nCount = getCount();
+
+ // HiddenControls and forms are not listed
+ if (rControls.getLength() > nCount)
+ return;
+
+ sal_Int16 nTabIndex = 1;
+ for (auto const& rControl : rControls)
+ {
+ Reference<XFormComponent> xComp(rControl, UNO_QUERY);
+ if (xComp.is())
+ {
+ // Find component in the list
+ for (sal_Int32 j = 0; j < nCount; ++j)
+ {
+ Reference<XFormComponent> xElement(
+ getByIndex(j), css::uno::UNO_QUERY);
+ if (xComp == xElement)
+ {
+ Reference<XPropertySet> xSet(xComp, UNO_QUERY);
+ if (xSet.is() && hasProperty(PROPERTY_TABINDEX, xSet))
+ xSet->setPropertyValue( PROPERTY_TABINDEX, Any(nTabIndex++) );
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+Sequence<Reference<XControlModel> > SAL_CALL ODatabaseForm::getControlModels()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ return m_pGroupManager->getControlModels();
+}
+
+
+void SAL_CALL ODatabaseForm::setGroup( const Sequence<Reference<XControlModel> >& _rGroup, const OUString& Name )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // The controls are grouped by adjusting their names to the name of the
+ // first control of the sequence
+ Reference< XPropertySet > xSet;
+ OUString sGroupName( Name );
+
+ for( auto const& rControl : _rGroup )
+ {
+ xSet.set(rControl, css::uno::UNO_QUERY);
+ if ( !xSet.is() )
+ {
+ // can't throw an exception other than a RuntimeException (which would not be appropriate),
+ // so we ignore (and only assert) this
+ OSL_FAIL( "ODatabaseForm::setGroup: invalid arguments!" );
+ continue;
+ }
+
+ if (sGroupName.isEmpty())
+ xSet->getPropertyValue(PROPERTY_NAME) >>= sGroupName;
+ else
+ xSet->setPropertyValue(PROPERTY_NAME, Any(sGroupName));
+ }
+}
+
+
+sal_Int32 SAL_CALL ODatabaseForm::getGroupCount()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ return m_pGroupManager->getGroupCount();
+}
+
+
+void SAL_CALL ODatabaseForm::getGroup( sal_Int32 nGroup, Sequence<Reference<XControlModel> >& _rGroup, OUString& _rName )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ _rGroup.realloc(0);
+ _rName.clear();
+
+ if ((nGroup < 0) || (nGroup >= m_pGroupManager->getGroupCount()))
+ return;
+ m_pGroupManager->getGroup( nGroup, _rGroup, _rName );
+}
+
+
+void SAL_CALL ODatabaseForm::getGroupByName(const OUString& Name, Sequence< Reference<XControlModel> >& _rGroup)
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ _rGroup.realloc(0);
+ m_pGroupManager->getGroupByName( Name, _rGroup );
+}
+
+
+// css::lang::XEventListener
+
+void SAL_CALL ODatabaseForm::disposing(const EventObject& Source)
+{
+ // does the call come from the connection which we are sharing with our parent?
+ if ( isSharingConnection() )
+ {
+ Reference< XConnection > xConnSource( Source.Source, UNO_QUERY );
+ if ( xConnSource.is() )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ Reference< XConnection > xActiveConn;
+ m_xAggregateSet->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xActiveConn;
+ OSL_ENSURE( xActiveConn.get() == xConnSource.get(), "ODatabaseForm::disposing: where did this come from?" );
+ // there should be exactly one XConnection object we're listening at - our aggregate connection
+#endif
+ disposingSharedConnection( xConnSource );
+ }
+ }
+
+ OInterfaceContainer::disposing(Source);
+
+ // does the disposing come from the aggregate ?
+ if (m_xAggregate.is())
+ { // no -> forward it
+ css::uno::Reference<css::lang::XEventListener> xListener;
+ if (query_aggregation(m_xAggregate, xListener))
+ xListener->disposing(Source);
+ }
+}
+
+
+void ODatabaseForm::impl_createLoadTimer()
+{
+ OSL_PRECOND( m_pLoadTimer == nullptr, "ODatabaseForm::impl_createLoadTimer: timer already exists!" );
+ m_pLoadTimer.reset(new Timer("DatabaseFormLoadTimer"));
+ m_pLoadTimer->SetTimeout(100);
+ m_pLoadTimer->SetInvokeHandler(LINK(this,ODatabaseForm,OnTimeout));
+}
+
+
+// css::form::XLoadListener
+
+void SAL_CALL ODatabaseForm::loaded(const EventObject& /*aEvent*/)
+{
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference< XRowSet > xParentRowSet( m_xParent, UNO_QUERY_THROW );
+ xParentRowSet->addRowSetListener( this );
+
+ impl_createLoadTimer();
+ }
+
+ load_impl( true );
+}
+
+
+void SAL_CALL ODatabaseForm::unloading(const EventObject& /*aEvent*/)
+{
+ {
+ // now stop the rowset listening if we are a subform
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pLoadTimer && m_pLoadTimer->IsActive() )
+ m_pLoadTimer->Stop();
+ m_pLoadTimer.reset();
+
+ Reference< XRowSet > xParentRowSet( m_xParent, UNO_QUERY_THROW );
+ xParentRowSet->removeRowSetListener( this );
+ }
+
+ unload();
+}
+
+
+void SAL_CALL ODatabaseForm::unloaded(const EventObject& /*aEvent*/)
+{
+ // nothing to do
+}
+
+
+void SAL_CALL ODatabaseForm::reloading(const EventObject& /*aEvent*/)
+{
+ // now stop the rowset listening if we are a subform
+ ::osl::MutexGuard aGuard(m_aMutex);
+ Reference<XRowSet> xParentRowSet(m_xParent, UNO_QUERY);
+ if (xParentRowSet.is())
+ xParentRowSet->removeRowSetListener(this);
+
+ if (m_pLoadTimer && m_pLoadTimer->IsActive())
+ m_pLoadTimer->Stop();
+}
+
+
+void SAL_CALL ODatabaseForm::reloaded(const EventObject& /*aEvent*/)
+{
+ reload_impl(true);
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ Reference<XRowSet> xParentRowSet(m_xParent, UNO_QUERY);
+ if (xParentRowSet.is())
+ xParentRowSet->addRowSetListener(this);
+ }
+}
+
+
+IMPL_LINK_NOARG(ODatabaseForm, OnTimeout, Timer *, void)
+{
+ reload_impl(true);
+}
+
+
+// css::form::XLoadable
+
+void SAL_CALL ODatabaseForm::load()
+{
+ load_impl(false);
+}
+
+
+bool ODatabaseForm::canShareConnection( const Reference< XPropertySet >& _rxParentProps )
+{
+ // our own data source
+ OUString sOwnDatasource;
+ m_xAggregateSet->getPropertyValue( PROPERTY_DATASOURCE ) >>= sOwnDatasource;
+
+ // our parents data source
+ OUString sParentDataSource;
+ OSL_ENSURE( _rxParentProps.is() && _rxParentProps->getPropertySetInfo().is() && _rxParentProps->getPropertySetInfo()->hasPropertyByName( PROPERTY_DATASOURCE ),
+ "ODatabaseForm::doShareConnection: invalid parent form!" );
+ if ( _rxParentProps.is() )
+ _rxParentProps->getPropertyValue( PROPERTY_DATASOURCE ) >>= sParentDataSource;
+
+ bool bCanShareConnection = false;
+
+ // both rowsets share are connected to the same data source
+ if ( sParentDataSource == sOwnDatasource )
+ {
+ if ( !sParentDataSource.isEmpty() )
+ // and it's really a data source name (not empty)
+ bCanShareConnection = true;
+ else
+ { // the data source name is empty
+ // -> ok for the URL
+ OUString sParentURL;
+ OUString sMyURL;
+ _rxParentProps->getPropertyValue( PROPERTY_URL ) >>= sParentURL;
+ m_xAggregateSet->getPropertyValue( PROPERTY_URL ) >>= sMyURL;
+
+ bCanShareConnection = (sParentURL == sMyURL);
+ }
+ }
+
+ if ( bCanShareConnection )
+ {
+ // check for the user/password
+
+ // take the user property on the rowset (if any) into account
+ OUString sParentUser, sParentPwd;
+ _rxParentProps->getPropertyValue( PROPERTY_USER ) >>= sParentUser;
+ _rxParentProps->getPropertyValue( PROPERTY_PASSWORD ) >>= sParentPwd;
+
+ OUString sMyUser, sMyPwd;
+ m_xAggregateSet->getPropertyValue( PROPERTY_USER ) >>= sMyUser;
+ m_xAggregateSet->getPropertyValue( PROPERTY_PASSWORD ) >>= sMyPwd;
+
+ bCanShareConnection =
+ ( sParentUser == sMyUser )
+ && ( sParentPwd == sMyPwd );
+ }
+
+ return bCanShareConnection;
+}
+
+
+void ODatabaseForm::doShareConnection( const Reference< XPropertySet >& _rxParentProps )
+{
+ // get the connection of the parent
+ Reference< XConnection > xParentConn;
+ _rxParentProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xParentConn;
+ OSL_ENSURE( xParentConn.is(), "ODatabaseForm::doShareConnection: we're a valid sub-form, but the parent has no connection?!" );
+
+ if ( xParentConn.is() )
+ {
+ // add as dispose listener to the connection
+ Reference< XComponent > xParentConnComp( xParentConn, UNO_QUERY );
+ OSL_ENSURE( xParentConnComp.is(), "ODatabaseForm::doShareConnection: invalid connection!" );
+ xParentConnComp->addEventListener( static_cast< XLoadListener* >( this ) );
+
+ // forward the connection to our own aggregate
+ m_bForwardingConnection = true;
+ m_xAggregateSet->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( xParentConn ) );
+ m_bForwardingConnection = false;
+
+ m_bSharingConnection = true;
+ }
+ else
+ m_bSharingConnection = false;
+}
+
+
+void ODatabaseForm::disposingSharedConnection( const Reference< XConnection >& /*_rxConn*/ )
+{
+ stopSharingConnection();
+
+ // TODO: we could think about whether or not to re-connect.
+ unload( );
+}
+
+
+void ODatabaseForm::stopSharingConnection( )
+{
+ OSL_ENSURE( m_bSharingConnection, "ODatabaseForm::stopSharingConnection: invalid call!" );
+
+ if ( !m_bSharingConnection )
+ return;
+
+ // get the connection
+ Reference< XConnection > xSharedConn;
+ m_xAggregateSet->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xSharedConn;
+ OSL_ENSURE( xSharedConn.is(), "ODatabaseForm::stopSharingConnection: there's no conn!" );
+
+ // remove ourself as event listener
+ Reference< XComponent > xSharedConnComp( xSharedConn, UNO_QUERY );
+ if ( xSharedConnComp.is() )
+ xSharedConnComp->removeEventListener( static_cast< XLoadListener* >( this ) );
+
+ // no need to dispose the conn: we're not the owner, this is our parent
+ // (in addition, this method may be called if the connection is being disposed while we use it)
+
+ // reset the property
+ xSharedConn.clear();
+ m_bForwardingConnection = true;
+ m_xAggregateSet->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( xSharedConn ) );
+ m_bForwardingConnection = false;
+
+ // reset the flag
+ m_bSharingConnection = false;
+}
+
+namespace
+{
+ Reference<css::awt::XWindow> GetDialogParentWindow(const Reference<XModel>& rModel)
+ {
+ if (!rModel.is())
+ return nullptr;
+ Reference<XController> xController(rModel->getCurrentController());
+ if (!xController.is())
+ return nullptr;
+ Reference<XFrame> xFrame(xController->getFrame());
+ if (!xFrame.is())
+ return nullptr;
+ return xFrame->getContainerWindow();
+ }
+}
+
+bool ODatabaseForm::implEnsureConnection()
+{
+ try
+ {
+ if ( getConnection( ).is() )
+ // if our aggregate already has a connection, nothing needs to be done about it
+ return true;
+
+ // see whether we're an embedded form
+ Reference< XConnection > xOuterConnection;
+ if ( ::dbtools::isEmbeddedInDatabase( getParent(), xOuterConnection ) )
+ {
+ m_xAggregateSet->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( xOuterConnection ) );
+ return xOuterConnection.is();
+ }
+
+ m_bSharingConnection = false;
+
+ // if we're a sub form, we try to re-use the connection of our parent
+ if (m_bSubForm)
+ {
+ OSL_ENSURE( Reference< XForm >( getParent(), UNO_QUERY ).is(),
+ "ODatabaseForm::implEnsureConnection: m_bSubForm is TRUE, but the parent is no form?" );
+
+ Reference< XPropertySet > xParentProps( getParent(), UNO_QUERY );
+
+ // can we re-use (aka share) the connection of the parent?
+ if ( canShareConnection( xParentProps ) )
+ {
+ // yep -> do it
+ doShareConnection( xParentProps );
+ // success?
+ if ( m_bSharingConnection )
+ // yes -> outta here
+ return true;
+ }
+ }
+
+ if (m_xAggregateSet.is())
+ {
+ //Dig out a suitable parent for any warning dialogs
+ Reference<css::awt::XWindow> m_xDialogParent;
+ Reference<XChild> xParent(m_xParent, UNO_QUERY);
+ if (xParent.is())
+ m_xDialogParent = GetDialogParentWindow(getXModel(xParent->getParent()));
+
+ Reference< XConnection > xConnection = connectRowset(
+ Reference<XRowSet> (m_xAggregate, UNO_QUERY),
+ m_xContext, m_xDialogParent
+ );
+ return xConnection.is();
+ }
+ }
+ catch(const SQLException& eDB)
+ {
+ onError(eDB, ResourceManager::loadString(RID_STR_CONNECTERROR));
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return false;
+}
+
+
+void ODatabaseForm::load_impl(bool bCausedByParentForm, bool bMoveToFirst, const Reference< XInteractionHandler >& _rxCompletionHandler )
+{
+ ::osl::ResettableMutexGuard aGuard(m_aMutex);
+
+ // are we already loaded?
+ if (isLoaded())
+ return;
+
+ m_bSubForm = bCausedByParentForm;
+
+ // if we don't have a connection, we are not intended to be a database form or the aggregate was not able
+ // to establish a connection
+ bool bConnected = implEnsureConnection();
+
+ // we don't have to execute if we do not have a command to execute
+ bool bExecute = bConnected && m_xAggregateSet.is() && !getString(m_xAggregateSet->getPropertyValue(PROPERTY_COMMAND)).isEmpty();
+
+ // a database form always uses caching
+ // we use starting fetchsize with at least 10 rows
+ if (bConnected)
+ m_xAggregateSet->setPropertyValue(PROPERTY_FETCHSIZE, Any(sal_Int32(40)));
+
+ // if we're loaded as sub form we got a "rowSetChanged" from the parent rowset _before_ we got the "loaded"
+ // so we don't need to execute the statement again, this was already done
+ // (and there were no relevant changes between these two listener calls, the "load" of a form is quite an
+ // atomic operation.)
+
+ bool bSuccess = false;
+ if (bExecute)
+ {
+ m_sCurrentErrorContext = ResourceManager::loadString(RID_ERR_LOADING_FORM);
+ bSuccess = executeRowSet(aGuard, bMoveToFirst, _rxCompletionHandler);
+ }
+
+ if (bSuccess)
+ {
+ m_bLoaded = true;
+ aGuard.clear();
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aLoadListeners.notifyEach( &XLoadListener::loaded, aEvt );
+
+ // if we are on the insert row, we have to reset all controls
+ // to set the default values
+ if (bExecute && getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ISNEW)))
+ reset();
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::unload()
+{
+ ::osl::ResettableMutexGuard aGuard(m_aMutex);
+ if (!isLoaded())
+ return;
+
+ m_pLoadTimer.reset();
+
+ aGuard.clear();
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aLoadListeners.notifyEach( &XLoadListener::unloading, aEvt );
+
+ if (m_xAggregateAsRowSet.is())
+ {
+ // we may have reset the InsertOnly property on the aggregate - restore it
+ restoreInsertOnlyState( );
+
+ // clear the parameters if there are any
+ invalidateParameters();
+
+ try
+ {
+ // close the aggregate
+ Reference<XCloseable> xCloseable;
+ query_aggregation( m_xAggregate, xCloseable);
+ if (xCloseable.is())
+ xCloseable->close();
+ }
+ catch(const SQLException&)
+ {
+ }
+ }
+
+ aGuard.reset();
+ m_bLoaded = false;
+
+ // if the connection we used while we were loaded is only shared with our parent, we
+ // reset it
+ if ( isSharingConnection() )
+ stopSharingConnection();
+
+ aGuard.clear();
+ m_aLoadListeners.notifyEach( &XLoadListener::unloaded, aEvt );
+}
+
+
+void SAL_CALL ODatabaseForm::reload()
+{
+ reload_impl(true);
+}
+
+
+void ODatabaseForm::reload_impl(bool bMoveToFirst, const Reference< XInteractionHandler >& _rxCompletionHandler )
+{
+ ::osl::ResettableMutexGuard aGuard(m_aMutex);
+ if (!isLoaded())
+ return;
+
+ DocumentModifyGuard aModifyGuard( *this );
+ // ensures the document is not marked as "modified" just because we change some control's content during
+ // reloading...
+
+ EventObject aEvent(static_cast<XWeak*>(this));
+ {
+ // only if there is no approve listener we can post the event at this time
+ // otherwise see approveRowsetChange
+ // the approval is done by the aggregate
+ if (!m_aRowSetApproveListeners.getLength())
+ {
+ aGuard.clear();
+ m_aLoadListeners.notifyEach( &XLoadListener::reloading, aEvent);
+ aGuard.reset();
+ }
+ }
+
+ bool bSuccess = true;
+ try
+ {
+ m_sCurrentErrorContext = ResourceManager::loadString(RID_ERR_REFRESHING_FORM);
+ bSuccess = executeRowSet(aGuard, bMoveToFirst, _rxCompletionHandler);
+ }
+ catch(const SQLException&)
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "ODatabaseForm::reload_impl : shouldn't executeRowSet catch this exception?");
+ }
+
+ if (bSuccess)
+ {
+ aGuard.clear();
+ m_aLoadListeners.notifyEach( &XLoadListener::reloaded, aEvent);
+
+ // if we are on the insert row, we have to reset all controls
+ // to set the default values
+ if (getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ISNEW)))
+ reset();
+ }
+ else
+ m_bLoaded = false;
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isLoaded()
+{
+ return m_bLoaded;
+}
+
+
+void SAL_CALL ODatabaseForm::addLoadListener(const Reference<XLoadListener>& aListener)
+{
+ m_aLoadListeners.addInterface(aListener);
+}
+
+
+void SAL_CALL ODatabaseForm::removeLoadListener(const Reference<XLoadListener>& aListener)
+{
+ m_aLoadListeners.removeInterface(aListener);
+}
+
+
+// css::sdbc::XCloseable
+void SAL_CALL ODatabaseForm::close()
+{
+ // unload will close the aggregate
+ unload();
+}
+
+
+// css::sdbc::XRowSetListener
+
+void SAL_CALL ODatabaseForm::cursorMoved(const EventObject& /*event*/)
+{
+ // reload the subform with the new parameters of the parent
+ // do this handling delayed to provide of execute too many SQL Statements
+ osl::MutexGuard aGuard(m_aMutex);
+
+ DBG_ASSERT( m_pLoadTimer, "ODatabaseForm::cursorMoved: how can this happen?!" );
+ if ( !m_pLoadTimer )
+ impl_createLoadTimer();
+
+ if ( m_pLoadTimer->IsActive() )
+ m_pLoadTimer->Stop();
+
+ // and start the timer again
+ m_pLoadTimer->Start();
+}
+
+
+void SAL_CALL ODatabaseForm::rowChanged(const EventObject& /*event*/)
+{
+ // ignore it
+}
+
+
+void SAL_CALL ODatabaseForm::rowSetChanged(const EventObject& /*event*/)
+{
+ // not interested in :
+ // if our parent is an ODatabaseForm, too, then after this rowSetChanged we'll get a "reloaded"
+ // or a "loaded" event.
+ // If somebody gave us another parent which is an XRowSet but doesn't handle an execute as
+ // "load" respectively "reload"... can't do anything...
+}
+
+
+bool ODatabaseForm::impl_approveRowChange_throw( const EventObject& _rEvent, const bool _bAllowSQLException,
+ ::osl::ClearableMutexGuard& _rGuard )
+{
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aRowSetApproveListeners );
+ _rGuard.clear();
+ while ( aIter.hasMoreElements() )
+ {
+ Reference< XRowSetApproveListener > xListener( aIter.next() );
+ try
+ {
+ if ( !xListener->approveRowSetChange( _rEvent ) )
+ return false;
+ }
+ catch (const DisposedException& e)
+ {
+ if ( e.Context == xListener )
+ aIter.remove();
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const SQLException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ if ( _bAllowSQLException )
+ throw;
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ return true;
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::approveCursorMove(const EventObject& event)
+{
+ // is our aggregate calling?
+ if (event.Source == css::uno::Reference<css::uno::XInterface>(static_cast<XWeak*>(this)))
+ {
+ // Our aggregate doesn't have any ApproveRowSetListeners (expect ourself), as we re-routed the queryInterface
+ // for XRowSetApproveBroadcaster-interface.
+ // So we have to multiplex this approve request.
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aRowSetApproveListeners );
+ while ( aIter.hasMoreElements() )
+ {
+ Reference< XRowSetApproveListener > xListener( aIter.next() );
+ try
+ {
+ if ( !xListener->approveCursorMove( event ) )
+ return false;
+ }
+ catch (const DisposedException& e)
+ {
+ if ( e.Context == xListener )
+ aIter.remove();
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ return true;
+ }
+ else
+ {
+ // this is a call from our parent ...
+ // a parent's cursor move will result in a re-execute of our own row-set, so we have to
+ // ask our own RowSetChangesListeners, too
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ if ( !impl_approveRowChange_throw( event, false, aGuard ) )
+ return false;
+ }
+ return true;
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::approveRowChange(const RowChangeEvent& event)
+{
+ // is our aggregate calling?
+ if (event.Source != css::uno::Reference<css::uno::XInterface>(static_cast<XWeak*>(this)))
+ return true;
+
+ // Our aggregate doesn't have any ApproveRowSetListeners (expect ourself), as we re-routed the queryInterface
+ // for XRowSetApproveBroadcaster-interface.
+ // So we have to multiplex this approve request.
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aRowSetApproveListeners );
+ while ( aIter.hasMoreElements() )
+ {
+ Reference< XRowSetApproveListener > xListener( aIter.next() );
+ try
+ {
+ if ( !xListener->approveRowChange( event ) )
+ return false;
+ }
+ catch (const DisposedException& e)
+ {
+ if ( e.Context == xListener )
+ aIter.remove();
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ return true;
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::approveRowSetChange(const EventObject& event)
+{
+ if (event.Source == css::uno::Reference<css::uno::XInterface>(static_cast<XWeak*>(this))) // ignore our aggregate as we handle this approve ourself
+ {
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ bool bWasLoaded = isLoaded();
+ if ( !impl_approveRowChange_throw( event, false, aGuard ) )
+ return false;
+
+ if ( bWasLoaded )
+ {
+ m_aLoadListeners.notifyEach( &XLoadListener::reloading, event );
+ }
+ }
+ else
+ {
+ // this is a call from our parent ...
+ // a parent's cursor move will result in a re-execute of our own row-set, so we have to
+ // ask our own RowSetChangesListeners, too
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ if ( !impl_approveRowChange_throw( event, false, aGuard ) )
+ return false;
+ }
+ return true;
+}
+
+
+// css::sdb::XRowSetApproveBroadcaster
+
+void SAL_CALL ODatabaseForm::addRowSetApproveListener(const Reference<XRowSetApproveListener>& _rListener)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ m_aRowSetApproveListeners.addInterface(_rListener);
+
+ // do we have to multiplex ?
+ if (m_aRowSetApproveListeners.getLength() == 1)
+ {
+ Reference<XRowSetApproveBroadcaster> xBroadcaster;
+ if (query_aggregation( m_xAggregate, xBroadcaster))
+ {
+ Reference<XRowSetApproveListener> xListener(static_cast<XRowSetApproveListener*>(this));
+ xBroadcaster->addRowSetApproveListener(xListener);
+ }
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::removeRowSetApproveListener(const Reference<XRowSetApproveListener>& _rListener)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ // do we have to remove the multiplex ?
+ m_aRowSetApproveListeners.removeInterface(_rListener);
+ if ( m_aRowSetApproveListeners.getLength() == 0 )
+ {
+ Reference<XRowSetApproveBroadcaster> xBroadcaster;
+ if (query_aggregation( m_xAggregate, xBroadcaster))
+ {
+ Reference<XRowSetApproveListener> xListener(static_cast<XRowSetApproveListener*>(this));
+ xBroadcaster->removeRowSetApproveListener(xListener);
+ }
+ }
+}
+
+
+// com::sun:star::form::XDatabaseParameterBroadcaster
+
+void SAL_CALL ODatabaseForm::addDatabaseParameterListener(const Reference<XDatabaseParameterListener>& _rListener)
+{
+ m_aParameterManager.addParameterListener( _rListener );
+}
+
+void SAL_CALL ODatabaseForm::removeDatabaseParameterListener(const Reference<XDatabaseParameterListener>& _rListener)
+{
+ m_aParameterManager.removeParameterListener( _rListener );
+}
+
+
+void SAL_CALL ODatabaseForm::addParameterListener(const Reference<XDatabaseParameterListener>& _rListener)
+{
+ ODatabaseForm::addDatabaseParameterListener( _rListener );
+}
+
+
+void SAL_CALL ODatabaseForm::removeParameterListener(const Reference<XDatabaseParameterListener>& _rListener)
+{
+ ODatabaseForm::removeDatabaseParameterListener( _rListener );
+}
+
+
+// css::sdb::XCompletedExecution
+
+void SAL_CALL ODatabaseForm::executeWithCompletion( const Reference< XInteractionHandler >& _rxHandler )
+{
+ ::osl::ClearableMutexGuard aGuard(m_aMutex);
+ // the difference between execute and load is, that we position on the first row in case of load
+ // after execute we remain before the first row
+ if (!isLoaded())
+ {
+ aGuard.clear();
+ load_impl(false, false, _rxHandler);
+ }
+ else
+ {
+ EventObject event(static_cast< XWeak* >(this));
+ if ( !impl_approveRowChange_throw( event, true, aGuard ) )
+ return;
+
+ // we're loaded and somebody wants to execute ourself -> this means a reload
+ reload_impl(false, _rxHandler);
+ }
+}
+
+
+// css::sdbc::XRowSet
+
+void SAL_CALL ODatabaseForm::execute()
+{
+ osl::ClearableMutexGuard aGuard(m_aMutex);
+ // if somebody calls an execute and we're not loaded we reroute this call to our load method.
+
+ // the difference between execute and load is, that we position on the first row in case of load
+ // after execute we remain before the first row
+ if (!isLoaded())
+ {
+ aGuard.clear();
+ load_impl(false, false);
+ }
+ else
+ {
+ EventObject event(static_cast< XWeak* >(this));
+ if ( !impl_approveRowChange_throw( event, true, aGuard ) )
+ return;
+
+ // we're loaded and somebody wants to execute ourself -> this means a reload
+ reload_impl(false);
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::addRowSetListener(const Reference<XRowSetListener>& _rListener)
+{
+ if (m_xAggregateAsRowSet.is())
+ m_xAggregateAsRowSet->addRowSetListener(_rListener);
+}
+
+
+void SAL_CALL ODatabaseForm::removeRowSetListener(const Reference<XRowSetListener>& _rListener)
+{
+ if (m_xAggregateAsRowSet.is())
+ m_xAggregateAsRowSet->removeRowSetListener(_rListener);
+}
+
+
+// css::sdbc::XResultSet
+
+sal_Bool SAL_CALL ODatabaseForm::next()
+{
+ return m_xAggregateAsRowSet->next();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isBeforeFirst()
+{
+ return m_xAggregateAsRowSet->isBeforeFirst();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isAfterLast()
+{
+ return m_xAggregateAsRowSet->isAfterLast();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isFirst()
+{
+ return m_xAggregateAsRowSet->isFirst();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::isLast()
+{
+ return m_xAggregateAsRowSet->isLast();
+}
+
+
+void SAL_CALL ODatabaseForm::beforeFirst()
+{
+ m_xAggregateAsRowSet->beforeFirst();
+}
+
+
+void SAL_CALL ODatabaseForm::afterLast()
+{
+ m_xAggregateAsRowSet->afterLast();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::first()
+{
+ return m_xAggregateAsRowSet->first();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::last()
+{
+ return m_xAggregateAsRowSet->last();
+}
+
+
+sal_Int32 SAL_CALL ODatabaseForm::getRow()
+{
+ return m_xAggregateAsRowSet->getRow();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::absolute(sal_Int32 row)
+{
+ return m_xAggregateAsRowSet->absolute(row);
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::relative(sal_Int32 rows)
+{
+ return m_xAggregateAsRowSet->relative(rows);
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::previous()
+{
+ return m_xAggregateAsRowSet->previous();
+}
+
+
+void SAL_CALL ODatabaseForm::refreshRow()
+{
+ m_xAggregateAsRowSet->refreshRow();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::rowUpdated()
+{
+ return m_xAggregateAsRowSet->rowUpdated();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::rowInserted()
+{
+ return m_xAggregateAsRowSet->rowInserted();
+}
+
+
+sal_Bool SAL_CALL ODatabaseForm::rowDeleted()
+{
+ return m_xAggregateAsRowSet->rowDeleted();
+}
+
+
+css::uno::Reference<css::uno::XInterface> SAL_CALL ODatabaseForm::getStatement()
+{
+ return m_xAggregateAsRowSet->getStatement();
+}
+
+// css::sdbc::XResultSetUpdate
+// exceptions during insert update and delete will be forwarded to the errorlistener
+
+void SAL_CALL ODatabaseForm::insertRow()
+{
+ try
+ {
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->insertRow();
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_INSERTRECORD));
+ throw;
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::updateRow()
+{
+ try
+ {
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->updateRow();
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_UPDATERECORD));
+ throw;
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::deleteRow()
+{
+ try
+ {
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->deleteRow();
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_DELETERECORD));
+ throw;
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::cancelRowUpdates()
+{
+ try
+ {
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->cancelRowUpdates();
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_INSERTRECORD));
+ throw;
+ }
+}
+
+
+void SAL_CALL ODatabaseForm::moveToInsertRow()
+{
+ Reference<XResultSetUpdate> xUpdate;
+ if (!query_aggregation( m_xAggregate, xUpdate))
+ return;
+
+ // _always_ move to the insert row
+ //
+ // Formerly, the following line was conditioned with a "not is new", means we did not move the aggregate
+ // to the insert row if it was already positioned there.
+ //
+ // This prevented the RowSet implementation from resetting its column values. We, ourself, formerly
+ // did this reset of columns in reset_impl, where we set every column to the ControlDefault, or, if this
+ // was not present, to NULL. However, the problem with setting to NULL was #88888#, the problem with
+ // _not_ setting to NULL (which was the original fix for #88888#) was #97955#.
+ //
+ // So now we
+ // * move our aggregate to the insert row
+ // * in reset_impl
+ // - set the control defaults into the columns if not void
+ // - do _not_ set the columns to NULL if no control default is set
+ //
+ // Still, there is #72756#. During fixing this bug, DG introduced not calling the aggregate here. So
+ // in theory, we re-introduced #72756#. But the bug described therein does not happen anymore, as the
+ // preliminaries for it changed (no display of guessed values for new records with autoinc fields)
+ //
+ // BTW: the public Issuezilla bug is #i2815#
+ //
+ xUpdate->moveToInsertRow();
+
+ // then set the default values and the parameters given from the parent
+ reset();
+}
+
+
+void SAL_CALL ODatabaseForm::moveToCurrentRow()
+{
+ Reference<XResultSetUpdate> xUpdate;
+ if (query_aggregation( m_xAggregate, xUpdate))
+ xUpdate->moveToCurrentRow();
+}
+
+// css::sdbcx::XDeleteRows
+
+Sequence<sal_Int32> SAL_CALL ODatabaseForm::deleteRows(const Sequence<Any>& rows)
+{
+ try
+ {
+ Reference<XDeleteRows> xDelete;
+ if (query_aggregation( m_xAggregate, xDelete))
+ return xDelete->deleteRows(rows);
+ }
+ catch(const RowSetVetoException&)
+ {
+ throw;
+ }
+ catch(const SQLException& eDb)
+ {
+ onError(eDb, ResourceManager::loadString(RID_STR_ERR_DELETERECORDS));
+ throw;
+ }
+
+ return Sequence< sal_Int32 >();
+}
+
+// css::sdbc::XParameters
+
+void SAL_CALL ODatabaseForm::setNull(sal_Int32 parameterIndex, sal_Int32 sqlType)
+{
+ m_aParameterManager.setNull(parameterIndex, sqlType);
+}
+
+
+void SAL_CALL ODatabaseForm::setObjectNull(sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName)
+{
+ m_aParameterManager.setObjectNull(parameterIndex, sqlType, typeName);
+}
+
+
+void SAL_CALL ODatabaseForm::setBoolean(sal_Int32 parameterIndex, sal_Bool x)
+{
+ m_aParameterManager.setBoolean(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setByte(sal_Int32 parameterIndex, sal_Int8 x)
+{
+ m_aParameterManager.setByte(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setShort(sal_Int32 parameterIndex, sal_Int16 x)
+{
+ m_aParameterManager.setShort(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setInt(sal_Int32 parameterIndex, sal_Int32 x)
+{
+ m_aParameterManager.setInt(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setLong(sal_Int32 parameterIndex, sal_Int64 x)
+{
+ m_aParameterManager.setLong(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setFloat(sal_Int32 parameterIndex, float x)
+{
+ m_aParameterManager.setFloat(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setDouble(sal_Int32 parameterIndex, double x)
+{
+ m_aParameterManager.setDouble(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setString(sal_Int32 parameterIndex, const OUString& x)
+{
+ m_aParameterManager.setString(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setBytes(sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x)
+{
+ m_aParameterManager.setBytes(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setDate(sal_Int32 parameterIndex, const css::util::Date& x)
+{
+ m_aParameterManager.setDate(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setTime(sal_Int32 parameterIndex, const css::util::Time& x)
+{
+ m_aParameterManager.setTime(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setTimestamp(sal_Int32 parameterIndex, const css::util::DateTime& x)
+{
+ m_aParameterManager.setTimestamp(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setBinaryStream(sal_Int32 parameterIndex, const Reference<XInputStream>& x, sal_Int32 length)
+{
+ m_aParameterManager.setBinaryStream(parameterIndex, x, length);
+}
+
+
+void SAL_CALL ODatabaseForm::setCharacterStream(sal_Int32 parameterIndex, const Reference<XInputStream>& x, sal_Int32 length)
+{
+ m_aParameterManager.setCharacterStream(parameterIndex, x, length);
+}
+
+
+void SAL_CALL ODatabaseForm::setObjectWithInfo(sal_Int32 parameterIndex, const Any& x, sal_Int32 targetSqlType, sal_Int32 scale)
+{
+ m_aParameterManager.setObjectWithInfo(parameterIndex, x, targetSqlType, scale);
+}
+
+
+void SAL_CALL ODatabaseForm::setObject(sal_Int32 parameterIndex, const Any& x)
+{
+ m_aParameterManager.setObject(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setRef(sal_Int32 parameterIndex, const Reference<XRef>& x)
+{
+ m_aParameterManager.setRef(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setBlob(sal_Int32 parameterIndex, const Reference<XBlob>& x)
+{
+ m_aParameterManager.setBlob(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setClob(sal_Int32 parameterIndex, const Reference<XClob>& x)
+{
+ m_aParameterManager.setClob(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::setArray(sal_Int32 parameterIndex, const Reference<XArray>& x)
+{
+ m_aParameterManager.setArray(parameterIndex, x);
+}
+
+
+void SAL_CALL ODatabaseForm::clearParameters()
+{
+ m_aParameterManager.clearParameters();
+}
+
+
+void SAL_CALL ODatabaseForm::propertyChange( const PropertyChangeEvent& evt )
+{
+ if ( evt.Source == m_xParent )
+ {
+ if ( evt.PropertyName == PROPERTY_ISNEW )
+ {
+ bool bCurrentIsNew( false );
+ OSL_VERIFY( evt.NewValue >>= bCurrentIsNew );
+ if ( !bCurrentIsNew )
+ reload_impl( true );
+ }
+ return;
+ }
+ OFormComponents::propertyChange( evt );
+}
+
+// css::lang::XServiceInfo
+
+OUString SAL_CALL ODatabaseForm::getImplementationName()
+{
+ return "com.sun.star.comp.forms.ODatabaseForm";
+}
+
+
+Sequence< OUString > SAL_CALL ODatabaseForm::getSupportedServiceNames()
+{
+ // the services of our aggregate
+ Sequence< OUString > aServices;
+ Reference< XServiceInfo > xInfo;
+ if (query_aggregation(m_xAggregate, xInfo))
+ aServices = xInfo->getSupportedServiceNames();
+
+ // concat without own services
+ return ::comphelper::concatSequences(
+ css::uno::Sequence<OUString> {
+ FRM_SUN_FORMCOMPONENT, "com.sun.star.form.FormComponents",
+ FRM_SUN_COMPONENT_FORM, FRM_SUN_COMPONENT_HTMLFORM,
+ FRM_SUN_COMPONENT_DATAFORM, FRM_COMPONENT_FORM },
+ aServices
+ );
+}
+
+sal_Bool SAL_CALL ODatabaseForm::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+// css::io::XPersistObject
+const sal_uInt16 CYCLE = 0x0001;
+const sal_uInt16 DONTAPPLYFILTER = 0x0002;
+
+OUString ODatabaseForm::getServiceName()
+{
+ return FRM_COMPONENT_FORM; // old (non-sun) name for compatibility !
+}
+
+void SAL_CALL ODatabaseForm::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ DBG_ASSERT(m_xAggregateSet.is(), "ODatabaseForm::write : only to be called if the aggregate exists !");
+
+ // all children
+ OFormComponents::write(_rxOutStream);
+
+ // version
+ _rxOutStream->writeShort(0x0005);
+
+ // Name
+ _rxOutStream << m_sName;
+
+ OUString sDataSource;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->getPropertyValue(PROPERTY_DATASOURCE) >>= sDataSource;
+ _rxOutStream << sDataSource;
+
+ // former CursorSource
+ OUString sCommand;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->getPropertyValue(PROPERTY_COMMAND) >>= sCommand;
+ _rxOutStream << sCommand;
+
+ // former MasterFields
+ _rxOutStream << m_aMasterFields;
+ // former DetailFields
+ _rxOutStream << m_aDetailFields;
+
+ // former DataSelectionType
+ DataSelectionType eTranslated = DataSelectionType_TABLE;
+ if (m_xAggregateSet.is())
+ {
+ sal_Int32 nCommandType = 0;
+ m_xAggregateSet->getPropertyValue(PROPERTY_COMMANDTYPE) >>= nCommandType;
+ switch (nCommandType)
+ {
+ case CommandType::TABLE : eTranslated = DataSelectionType_TABLE; break;
+ case CommandType::QUERY : eTranslated = DataSelectionType_QUERY; break;
+ case CommandType::COMMAND:
+ {
+ bool bEscapeProcessing = getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_ESCAPE_PROCESSING));
+ eTranslated = bEscapeProcessing ? DataSelectionType_SQL : DataSelectionType_SQLPASSTHROUGH;
+ }
+ break;
+ default : OSL_FAIL("ODatabaseForm::write : wrong CommandType !");
+ }
+ }
+ _rxOutStream->writeShort(static_cast<sal_Int16>(eTranslated)); // former DataSelectionType
+
+ // very old versions expect a CursorType here
+ _rxOutStream->writeShort(2); // DatabaseCursorType_KEYSET
+
+ _rxOutStream->writeBoolean(m_eNavigation != NavigationBarMode_NONE);
+
+ // former DataEntry
+ if (m_xAggregateSet.is())
+ _rxOutStream->writeBoolean(getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_INSERTONLY)));
+ else
+ _rxOutStream->writeBoolean(false);
+
+ _rxOutStream->writeBoolean(m_bAllowInsert);
+ _rxOutStream->writeBoolean(m_bAllowUpdate);
+ _rxOutStream->writeBoolean(m_bAllowDelete);
+
+ // html form stuff
+ OUString sTmp = INetURLObject::decode( m_aTargetURL, INetURLObject::DecodeMechanism::Unambiguous);
+ _rxOutStream << sTmp;
+ _rxOutStream->writeShort( static_cast<sal_Int16>(m_eSubmitMethod) );
+ _rxOutStream->writeShort( static_cast<sal_Int16>(m_eSubmitEncoding) );
+ _rxOutStream << m_aTargetFrame;
+
+ // version 2 didn't know some options and the "default" state
+ sal_Int32 nCycle = sal_Int32(TabulatorCycle_RECORDS);
+ if (m_aCycle.hasValue())
+ {
+ ::cppu::enum2int(nCycle, m_aCycle);
+ if (m_aCycle == TabulatorCycle_PAGE)
+ // unknown in earlier versions
+ nCycle = sal_Int32(TabulatorCycle_RECORDS);
+ }
+ _rxOutStream->writeShort(static_cast<sal_Int16>(nCycle));
+
+ _rxOutStream->writeShort(static_cast<sal_Int16>(m_eNavigation));
+
+ OUString sFilter;
+ OUString sSort;
+ if (m_xAggregateSet.is())
+ {
+ m_xAggregateSet->getPropertyValue(PROPERTY_FILTER) >>= sFilter;
+ // version 4
+ m_xAggregateSet->getPropertyValue(PROPERTY_SORT) >>= sSort;
+ }
+ _rxOutStream << sFilter;
+ _rxOutStream << sSort;
+
+ // version 3
+ sal_uInt16 nAnyMask = 0;
+ if (m_aCycle.hasValue())
+ nAnyMask |= CYCLE;
+
+ if (m_xAggregateSet.is() && !getBOOL(m_xAggregateSet->getPropertyValue(PROPERTY_APPLYFILTER)))
+ nAnyMask |= DONTAPPLYFILTER;
+
+ _rxOutStream->writeShort(nAnyMask);
+
+ if (nAnyMask & CYCLE)
+ {
+ sal_Int32 nRealCycle = 0;
+ ::cppu::enum2int(nRealCycle, m_aCycle);
+ _rxOutStream->writeShort(static_cast<sal_Int16>(nRealCycle));
+ }
+
+ // version 5
+ OUString sHaving;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->getPropertyValue(PROPERTY_HAVINGCLAUSE) >>= sHaving;
+ _rxOutStream << sHaving;
+}
+
+
+void SAL_CALL ODatabaseForm::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ DBG_ASSERT(m_xAggregateSet.is(), "ODatabaseForm::read : only to be called if the aggregate exists !");
+
+ OFormComponents::read(_rxInStream);
+
+ // version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+
+ _rxInStream >> m_sName;
+
+ OUString sAggregateProp;
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_DATASOURCE, Any(sAggregateProp));
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_COMMAND, Any(sAggregateProp));
+
+ _rxInStream >> m_aMasterFields;
+ _rxInStream >> m_aDetailFields;
+
+ sal_Int16 nCursorSourceType = _rxInStream->readShort();
+ sal_Int32 nCommandType = 0;
+ switch (static_cast<DataSelectionType>(nCursorSourceType))
+ {
+ case DataSelectionType_TABLE : nCommandType = CommandType::TABLE; break;
+ case DataSelectionType_QUERY : nCommandType = CommandType::QUERY; break;
+ case DataSelectionType_SQL:
+ case DataSelectionType_SQLPASSTHROUGH:
+ {
+ nCommandType = CommandType::COMMAND;
+ bool bEscapeProcessing = static_cast<DataSelectionType>(nCursorSourceType) != DataSelectionType_SQLPASSTHROUGH;
+ m_xAggregateSet->setPropertyValue(PROPERTY_ESCAPE_PROCESSING, Any(bEscapeProcessing));
+ }
+ break;
+ default : OSL_FAIL("ODatabaseForm::read : wrong CommandType !");
+ }
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_COMMANDTYPE, Any(nCommandType));
+
+ // obsolete
+ _rxInStream->readShort();
+
+ // navigation mode was a boolean in version 1
+ // was a sal_Bool in version 1
+ bool bNavigation = _rxInStream->readBoolean();
+ if (nVersion == 1)
+ m_eNavigation = bNavigation ? NavigationBarMode_CURRENT : NavigationBarMode_NONE;
+
+ bool bInsertOnly = _rxInStream->readBoolean();
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_INSERTONLY, Any(bInsertOnly));
+
+ m_bAllowInsert = _rxInStream->readBoolean();
+ m_bAllowUpdate = _rxInStream->readBoolean();
+ m_bAllowDelete = _rxInStream->readBoolean();
+
+ // html stuff
+ OUString sTmp;
+ _rxInStream >> sTmp;
+ m_aTargetURL = INetURLObject::decode( sTmp, INetURLObject::DecodeMechanism::Unambiguous);
+ m_eSubmitMethod = static_cast<FormSubmitMethod>(_rxInStream->readShort());
+ m_eSubmitEncoding = static_cast<FormSubmitEncoding>(_rxInStream->readShort());
+ _rxInStream >> m_aTargetFrame;
+
+ if (nVersion > 1)
+ {
+ sal_Int32 nCycle = _rxInStream->readShort();
+ m_aCycle <<= TabulatorCycle(nCycle);
+ m_eNavigation = static_cast<NavigationBarMode>(_rxInStream->readShort());
+
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_FILTER, Any(sAggregateProp));
+ if(nVersion > 3)
+ {
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_SORT, Any(sAggregateProp));
+ }
+ }
+
+ sal_uInt16 nAnyMask = 0;
+ if (nVersion > 2)
+ {
+ nAnyMask = _rxInStream->readShort();
+ if (nAnyMask & CYCLE)
+ {
+ sal_Int32 nCycle = _rxInStream->readShort();
+ m_aCycle <<= TabulatorCycle(nCycle);
+ }
+ else
+ m_aCycle.clear();
+ }
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_APPLYFILTER, Any((nAnyMask & DONTAPPLYFILTER) == 0));
+
+ if(nVersion > 4)
+ {
+ _rxInStream >> sAggregateProp;
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_HAVINGCLAUSE, Any(sAggregateProp));
+ }
+}
+
+
+void ODatabaseForm::implInserted( const ElementDescription* _pElement )
+{
+ OFormComponents::implInserted( _pElement );
+
+ Reference< XSQLErrorBroadcaster > xBroadcaster( _pElement->xInterface, UNO_QUERY );
+ Reference< XForm > xForm ( _pElement->xInterface, UNO_QUERY );
+
+ if ( xBroadcaster.is() && !xForm.is() )
+ { // the object is an error broadcaster, but no form itself -> add ourself as listener
+ xBroadcaster->addSQLErrorListener( this );
+ }
+}
+
+
+void ODatabaseForm::implRemoved(const css::uno::Reference<css::uno::XInterface>& _rxObject)
+{
+ OFormComponents::implRemoved( _rxObject );
+
+ Reference<XSQLErrorBroadcaster> xBroadcaster(_rxObject, UNO_QUERY);
+ Reference<XForm> xForm(_rxObject, UNO_QUERY);
+ if (xBroadcaster.is() && !xForm.is())
+ { // the object is an error broadcaster, but no form itself -> remove ourself as listener
+ xBroadcaster->removeSQLErrorListener(this);
+ }
+}
+
+void SAL_CALL ODatabaseForm::errorOccured(const SQLErrorEvent& _rEvent)
+{
+ // give it to my own error listener
+ onError(_rEvent);
+ // TODO: think about extending the chain with an SQLContext object saying
+ // "this was an error of one of my children"
+}
+
+// css::container::XNamed
+OUString SAL_CALL ODatabaseForm::getName()
+{
+ OUString sReturn;
+ try
+ {
+ OPropertySetHelper::getFastPropertyValue(PROPERTY_ID_NAME) >>= sReturn;
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ throw WrappedTargetRuntimeException(
+ "ODatabaseForm::getName",
+ *this,
+ ::cppu::getCaughtException()
+ );
+ }
+ return sReturn;
+}
+
+void SAL_CALL ODatabaseForm::setName(const OUString& aName)
+{
+ setFastPropertyValue(PROPERTY_ID_NAME, Any(aName));
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_ODatabaseForm_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ODatabaseForm(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/DatabaseForm.hxx b/forms/source/component/DatabaseForm.hxx
new file mode 100644
index 0000000000..73831a9c67
--- /dev/null
+++ b/forms/source/component/DatabaseForm.hxx
@@ -0,0 +1,528 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <propertybaghelper.hxx>
+#include <com/sun/star/sdb/XSQLErrorListener.hpp>
+#include <com/sun/star/sdb/XSQLErrorBroadcaster.hpp>
+#include <com/sun/star/form/FormSubmitMethod.hpp>
+#include <com/sun/star/form/FormSubmitEncoding.hpp>
+#include <com/sun/star/form/XSubmit.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/form/XDatabaseParameterBroadcaster2.hpp>
+#include <com/sun/star/sdb/XCompletedExecution.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/XDeleteRows.hpp>
+#include <com/sun/star/sdbc/XResultSetUpdate.hpp>
+#include <com/sun/star/sdb/XRowSetApproveListener.hpp>
+#include <com/sun/star/sdb/XRowSetApproveBroadcaster.hpp>
+#include <com/sun/star/form/NavigationBarMode.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/XLoadListener.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/awt/XTabControllerModel.hpp>
+#include <com/sun/star/sdbc/XRowSetListener.hpp>
+#include <com/sun/star/sdbc/XCloseable.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/sdbc/XParameters.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/sdbc/XWarningsSupplier.hpp>
+
+
+#include <tools/link.hxx>
+#include <InterfaceContainer.hxx>
+
+#include <connectivity/parameters.hxx>
+#include <connectivity/filtermanager.hxx>
+#include <connectivity/warningscontainer.hxx>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propmultiplex.hxx>
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/implbase12.hxx>
+#include <cppuhelper/implbase4.hxx>
+#include <cppuhelper/implbase7.hxx>
+#include <rtl/ref.hxx>
+
+namespace com::sun::star::sdbc { class SQLException; }
+
+class Timer;
+class INetMIMEMessage;
+
+
+namespace frm
+{
+
+
+//= html tools
+
+
+const sal_uInt16 SUCCESSFUL_REPRESENT_TEXT = 0x0001;
+const sal_uInt16 SUCCESSFUL_REPRESENT_FILE = 0x0002;
+
+
+class HtmlSuccessfulObj
+{
+public:
+ OUString aName;
+ OUString aValue;
+ sal_uInt16 nRepresentation;
+
+ HtmlSuccessfulObj( OUString _aName, OUString _aValue,
+ sal_uInt16 _nRepresent = SUCCESSFUL_REPRESENT_TEXT )
+ :aName(std::move( _aName ))
+ ,aValue(std::move( _aValue ))
+ ,nRepresentation( _nRepresent )
+ {
+ }
+};
+
+typedef std::vector<HtmlSuccessfulObj> HtmlSuccessfulObjList;
+
+
+class OGroupManager;
+class OFormSubmitResetThread;
+typedef ::cppu::ImplHelper12 < css::form::XForm
+ , css::awt::XTabControllerModel
+ , css::form::XLoadListener
+ , css::sdbc::XRowSetListener
+ , css::sdb::XRowSetApproveListener
+ , css::form::XDatabaseParameterBroadcaster2
+ , css::sdb::XSQLErrorListener
+ , css::sdb::XSQLErrorBroadcaster
+ , css::form::XReset
+ , css::form::XSubmit
+ , css::form::XLoadable
+ , css::container::XNamed
+ > ODatabaseForm_BASE1;
+
+
+typedef ::cppu::ImplHelper4 < css::lang::XServiceInfo
+ , css::beans::XPropertyContainer
+ , css::beans::XPropertyAccess
+ , css::sdbc::XWarningsSupplier
+ > ODatabaseForm_BASE2;
+
+typedef ::cppu::ImplHelper7< css::sdbc::XCloseable,
+ css::sdbc::XRowSet,
+ css::sdb::XCompletedExecution,
+ css::sdb::XRowSetApproveBroadcaster,
+ css::sdbc::XResultSetUpdate,
+ css::sdbcx::XDeleteRows,
+ css::sdbc::XParameters > ODatabaseForm_BASE3;
+
+
+class ODatabaseForm :public OFormComponents
+ ,public OPropertySetAggregationHelper
+ ,public OPropertyChangeListener
+ ,public ODatabaseForm_BASE1
+ ,public ODatabaseForm_BASE2
+ ,public ODatabaseForm_BASE3
+ ,public IPropertyBagHelperContext
+{
+ friend class OFormSubmitResetThread;
+
+ // listener administration
+ ::comphelper::OInterfaceContainerHelper3<css::form::XLoadListener> m_aLoadListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XRowSetApproveListener> m_aRowSetApproveListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XSubmitListener> m_aSubmitListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XSQLErrorListener> m_aErrorListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XResetListener> m_aResetListeners;
+ ::osl::Mutex m_aResetSafety;
+ css::uno::Any m_aCycle;
+ css::uno::Any m_aIgnoreResult; // set when we are a subform and our master form positioned on a new row
+ css::uno::Sequence< OUString > m_aMasterFields;
+ css::uno::Sequence< OUString > m_aDetailFields;
+
+ // the object doin' most of the work - an SDB-rowset
+ css::uno::Reference< css::uno::XAggregation> m_xAggregate;
+ // same object, interface as member because of performance reasons
+ css::uno::Reference< css::sdbc::XRowSet> m_xAggregateAsRowSet;
+
+ PropertyBagHelper m_aPropertyBagHelper;
+ ::dbtools::WarningsContainer m_aWarnings;
+ rtl::Reference<OPropertyChangeMultiplexer> m_xAggregatePropertyMultiplexer;
+ // Management of the Control Groups
+ rtl::Reference<OGroupManager> m_pGroupManager;
+ ::dbtools::ParameterManager m_aParameterManager;
+ ::dbtools::FilterManager m_aFilterManager;
+ std::unique_ptr<Timer> m_pLoadTimer;
+
+ rtl::Reference<OFormSubmitResetThread> m_pThread;
+ OUString m_sCurrentErrorContext;
+ // will be used as additional context information
+ // when an exception is caught and forwarded to the listeners
+
+ sal_Int32 m_nResetsPending;
+// <overwritten_properties>
+ sal_Int32 m_nPrivileges;
+ bool m_bInsertOnly;
+// </overwritten_properties>
+
+// <properties>
+ css::uno::Any m_aControlBorderColorFocus;
+ css::uno::Any m_aControlBorderColorMouse;
+ css::uno::Any m_aControlBorderColorInvalid;
+ css::uno::Any m_aDynamicControlBorder;
+ OUString m_sName;
+ OUString m_aTargetURL;
+ OUString m_aTargetFrame;
+ css::form::FormSubmitMethod m_eSubmitMethod;
+ css::form::FormSubmitEncoding m_eSubmitEncoding;
+ css::form::NavigationBarMode m_eNavigation;
+ bool m_bAllowInsert : 1;
+ bool m_bAllowUpdate : 1;
+ bool m_bAllowDelete : 1;
+// </properties>
+ bool m_bLoaded : 1;
+ bool m_bSubForm : 1;
+ bool m_bForwardingConnection : 1; // true if we're setting the ActiveConnection on the aggregate
+ bool m_bSharingConnection : 1; // true if the connection we're using is shared with our parent
+
+public:
+ explicit ODatabaseForm(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ ODatabaseForm( const ODatabaseForm& _cloneSource );
+ virtual ~ODatabaseForm() override;
+
+ // UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(ODatabaseForm, OFormComponents)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type> SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override;
+
+ // css::lang::XComponent
+ virtual void SAL_CALL disposing() override;
+
+ // property handling
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ 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;
+
+ css::uno::Any SAL_CALL getFastPropertyValue( sal_Int32 nHandle ) override;
+ void fire( sal_Int32 * pnHandles, const css::uno::Any * pNewValues, const css::uno::Any * pOldValues, sal_Int32 nCount );
+
+ // IPropertyBagHelperContext
+ virtual ::osl::Mutex& getMutex() override;
+ virtual void describeFixedAndAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& _out_rFixedProperties,
+ css::uno::Sequence< css::beans::Property >& _out_rAggregateProperties
+ ) const override;
+ virtual css::uno::Reference< css::beans::XMultiPropertySet >
+ getPropertiesInterface() override;
+
+ // css::beans::XPropertyState
+ virtual css::beans::PropertyState getPropertyStateByHandle(sal_Int32 nHandle) override;
+ virtual void setPropertyToDefaultByHandle(sal_Int32 nHandle) override;
+ virtual css::uno::Any getPropertyDefaultByHandle(sal_Int32 nHandle) const override;
+
+ // css::sdbc::XSQLErrorBroadcaster
+ virtual void SAL_CALL addSQLErrorListener(const css::uno::Reference< css::sdb::XSQLErrorListener>& _rxListener) override;
+ virtual void SAL_CALL removeSQLErrorListener(const css::uno::Reference< css::sdb::XSQLErrorListener>& _rxListener) override;
+
+ // css::form::XForm
+ // nothing to implement
+
+ // css::form::XReset
+ virtual void SAL_CALL reset() override;
+ virtual void SAL_CALL addResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+ virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+
+ // css::form::XSubmit
+ virtual void SAL_CALL submit(const css::uno::Reference< css::awt::XControl>& aControl, const css::awt::MouseEvent& aMouseEvt) override;
+ virtual void SAL_CALL addSubmitListener(const css::uno::Reference< css::form::XSubmitListener>& _rxListener) override;
+ virtual void SAL_CALL removeSubmitListener(const css::uno::Reference< css::form::XSubmitListener>& _rxListener) override;
+
+ // css::container::XChild
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getParent() override { return OFormComponents::getParent(); }
+ virtual void SAL_CALL setParent(const css::uno::Reference<css::uno::XInterface>& Parent) override;
+
+ // css::container::XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName(const OUString& aName) override;
+
+ // css::awt::XTabControllerModel
+ virtual sal_Bool SAL_CALL getGroupControl() override;
+ virtual void SAL_CALL setGroupControl(sal_Bool /*_bGroupControl*/) override { }
+ virtual void SAL_CALL setControlModels(const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& _rControls) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > > SAL_CALL getControlModels() override;
+ virtual void SAL_CALL setGroup(const css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& _rGroup, const OUString& _rGroupName) override;
+ virtual sal_Int32 SAL_CALL getGroupCount() override;
+ virtual void SAL_CALL getGroup(sal_Int32 _nGroup, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& _rxGroup, OUString& _rName) override;
+ virtual void SAL_CALL getGroupByName(const OUString& _rName, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel > >& _rxGroup) override;
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+ // css::form::XLoadListener
+ virtual void SAL_CALL loaded(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL unloading(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL unloaded(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL reloading(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL reloaded(const css::lang::EventObject& aEvent) override;
+
+ // css::form::XLoadable
+ virtual void SAL_CALL load() override;
+ virtual void SAL_CALL unload() override;
+ virtual void SAL_CALL reload() override;
+ virtual sal_Bool SAL_CALL isLoaded() override;
+ virtual void SAL_CALL addLoadListener(const css::uno::Reference< css::form::XLoadListener>& _rxListener) override;
+ virtual void SAL_CALL removeLoadListener(const css::uno::Reference< css::form::XLoadListener>& _rxListener) override;
+
+ // css::sdbc::XCloseable
+ virtual void SAL_CALL close() override;
+
+ // css::sdbc::XRowSetListener
+ virtual void SAL_CALL cursorMoved(const css::lang::EventObject& event) override;
+ virtual void SAL_CALL rowChanged(const css::lang::EventObject& event) override;
+ virtual void SAL_CALL rowSetChanged(const css::lang::EventObject& event) override;
+
+ // css::sdb::XRowSetApproveListener
+ virtual sal_Bool SAL_CALL approveCursorMove(const css::lang::EventObject& event) override;
+ virtual sal_Bool SAL_CALL approveRowChange(const css::sdb::RowChangeEvent& event) override;
+ virtual sal_Bool SAL_CALL approveRowSetChange(const css::lang::EventObject& event) override;
+
+ // css::sdb::XRowSetApproveBroadcaster
+ virtual void SAL_CALL addRowSetApproveListener(const css::uno::Reference< css::sdb::XRowSetApproveListener>& _rxListener) override;
+ virtual void SAL_CALL removeRowSetApproveListener(const css::uno::Reference< css::sdb::XRowSetApproveListener>& _rxListener) override;
+
+ // com::sun:star::form::XDatabaseParameterBroadcaster2
+ virtual void SAL_CALL addDatabaseParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& _rxListener) override;
+ virtual void SAL_CALL removeDatabaseParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& _rxListener) override;
+
+ // com::sun:star::form::XDatabaseParameterBroadcaster
+ virtual void SAL_CALL addParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& _rxListener) override;
+ virtual void SAL_CALL removeParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& _rxListener) override;
+
+ // css::sdbc::XRowSet
+ virtual void SAL_CALL execute() override;
+ virtual void SAL_CALL addRowSetListener(const css::uno::Reference< css::sdbc::XRowSetListener>& _rxListener) override;
+ virtual void SAL_CALL removeRowSetListener(const css::uno::Reference< css::sdbc::XRowSetListener>& _rxListener) override;
+
+ // css::sdb::XCompletedExecution
+ virtual void SAL_CALL executeWithCompletion( const css::uno::Reference< css::task::XInteractionHandler >& handler ) override;
+
+ // css::sdbc::XResultSet
+ virtual sal_Bool SAL_CALL next() override;
+ virtual sal_Bool SAL_CALL isBeforeFirst() override;
+ virtual sal_Bool SAL_CALL isAfterLast() override;
+ virtual sal_Bool SAL_CALL isFirst() override;
+ virtual sal_Bool SAL_CALL isLast() override;
+ virtual void SAL_CALL beforeFirst() override;
+ virtual void SAL_CALL afterLast() override;
+ virtual sal_Bool SAL_CALL first() override;
+ virtual sal_Bool SAL_CALL last() override;
+ virtual sal_Int32 SAL_CALL getRow() override;
+ virtual sal_Bool SAL_CALL absolute(sal_Int32 row) override;
+ virtual sal_Bool SAL_CALL relative(sal_Int32 rows) override;
+ virtual sal_Bool SAL_CALL previous() override;
+ virtual void SAL_CALL refreshRow() override;
+ virtual sal_Bool SAL_CALL rowUpdated() override;
+ virtual sal_Bool SAL_CALL rowInserted() override;
+ virtual sal_Bool SAL_CALL rowDeleted() override;
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getStatement() override;
+
+ // css::sdbc::XResultSetUpdate
+ virtual void SAL_CALL insertRow() override;
+ virtual void SAL_CALL updateRow() override;
+ virtual void SAL_CALL deleteRow() override;
+ virtual void SAL_CALL cancelRowUpdates() override;
+ virtual void SAL_CALL moveToInsertRow() override;
+ virtual void SAL_CALL moveToCurrentRow() override;
+
+ // css::sdbcx::XDeleteRows
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL deleteRows(const css::uno::Sequence< css::uno::Any>& rows) override;
+
+ // css::lang::XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > 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;
+
+ // css::sdbc::XSQLErrorListener
+ virtual void SAL_CALL errorOccured(const css::sdb::SQLErrorEvent& aEvent) override;
+
+ // css::sdbc::XParameters
+ virtual void SAL_CALL setNull(sal_Int32 parameterIndex, sal_Int32 sqlType) override;
+ virtual void SAL_CALL setObjectNull(sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName) override;
+ virtual void SAL_CALL setBoolean(sal_Int32 parameterIndex, sal_Bool x) override;
+ virtual void SAL_CALL setByte(sal_Int32 parameterIndex, sal_Int8 x) override;
+ virtual void SAL_CALL setShort(sal_Int32 parameterIndex, sal_Int16 x) override;
+ virtual void SAL_CALL setInt(sal_Int32 parameterIndex, sal_Int32 x) override;
+ virtual void SAL_CALL setLong(sal_Int32 parameterIndex, sal_Int64 x) override;
+ virtual void SAL_CALL setFloat(sal_Int32 parameterIndex, float x) override;
+ virtual void SAL_CALL setDouble(sal_Int32 parameterIndex, double x) override;
+ virtual void SAL_CALL setString(sal_Int32 parameterIndex, const OUString& x) override;
+ virtual void SAL_CALL setBytes(sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x) override;
+ virtual void SAL_CALL setDate(sal_Int32 parameterIndex, const css::util::Date& x) override;
+ virtual void SAL_CALL setTime(sal_Int32 parameterIndex, const css::util::Time& x) override;
+ virtual void SAL_CALL setTimestamp(sal_Int32 parameterIndex, const css::util::DateTime& x) override;
+ virtual void SAL_CALL setBinaryStream(sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream>& x, sal_Int32 length) override;
+ virtual void SAL_CALL setCharacterStream(sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream>& x, sal_Int32 length) override;
+ virtual void SAL_CALL setObject(sal_Int32 parameterIndex, const css::uno::Any& x) override;
+ virtual void SAL_CALL setObjectWithInfo(sal_Int32 parameterIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale) override;
+ virtual void SAL_CALL setRef(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XRef>& x) override;
+ virtual void SAL_CALL setBlob(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XBlob>& x) override;
+ virtual void SAL_CALL setClob(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XClob>& x) override;
+ virtual void SAL_CALL setArray(sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XArray>& x) override;
+ virtual void SAL_CALL clearParameters() override;
+
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override;
+
+ // XPropertyContainer
+ virtual void SAL_CALL addProperty( const OUString& Name, ::sal_Int16 Attributes, const css::uno::Any& DefaultValue ) override;
+ virtual void SAL_CALL removeProperty( const OUString& Name ) override;
+
+ // XPropertyAccess
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getPropertyValues( ) override;
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& aProps ) override;
+ using OPropertySetAggregationHelper::setPropertyValues;
+
+ // XWarningsSupplier
+ virtual css::uno::Any SAL_CALL getWarnings( ) override;
+ virtual void SAL_CALL clearWarnings( ) override;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+protected:
+ // OPropertySetAggregationHelper overridables
+ virtual void forwardingPropertyValue( sal_Int32 _nHandle ) override;
+ virtual void forwardedPropertyValue( sal_Int32 _nHandle ) override;
+
+ // OInterfaceContainer overridables
+ virtual void implInserted( const ElementDescription* _pElement ) override;
+ virtual void implRemoved(const css::uno::Reference<css::uno::XInterface>& _rxObject) override;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged( const css::beans::PropertyChangeEvent& ) override;
+
+private:
+ bool executeRowSet(::osl::ResettableMutexGuard& _rClearForNotifies, bool bMoveToFirst,
+ const css::uno::Reference< css::task::XInteractionHandler >& _rxCompletionHandler);
+ bool fillParameters(::osl::ResettableMutexGuard& _rClearForNotifies,
+ const css::uno::Reference< css::task::XInteractionHandler >& _rxCompletionHandler);
+ void updateParameterInfo();
+ bool hasValidParent() const;
+
+ // impl methods
+ /// @throws css::uno::RuntimeException
+ void load_impl(bool bCausedByParentForm, bool bMoveToFirst = true,
+ const css::uno::Reference< css::task::XInteractionHandler >& _rxCompletionHandler = css::uno::Reference< css::task::XInteractionHandler >());
+ /// @throws css::uno::RuntimeException
+ void reload_impl(bool bMoveToFirst,
+ const css::uno::Reference< css::task::XInteractionHandler >& _rxCompletionHandler = css::uno::Reference< css::task::XInteractionHandler >());
+ void submit_impl(const css::uno::Reference< css::awt::XControl>& Control, const css::awt::MouseEvent& MouseEvt);
+ void reset_impl(bool _bApproveByListeners);
+
+ bool implEnsureConnection();
+
+ // connection sharing
+
+ /// checks if we can re-use (aka share) the connection of the given parent
+ bool canShareConnection( const css::uno::Reference< css::beans::XPropertySet >& _rxParentProps );
+
+ /// starts sharing the connection with the parent
+ void doShareConnection( const css::uno::Reference< css::beans::XPropertySet >& _rxParentProps );
+
+ /// stops sharing the connection with the parent
+ void stopSharingConnection( );
+
+ /// called when the connection which we share with our parent is being disposed
+ void disposingSharedConnection( const css::uno::Reference< css::sdbc::XConnection >& _rxConn );
+
+ /// checks if we currently share our connection with our parent
+ bool isSharingConnection( ) const { return m_bSharingConnection; }
+
+ /** calls our row set approval listeners
+
+ @param _rEvent
+ the event to notify
+ @param _bAllowSQLException
+ <TRUE/> if SQLExceptions are allowed to leave the method
+ @param _rGuard
+ the guard to be cleared before actually calling into the listeners, but after making
+ a copy of the listeners array to operate on.
+ @return
+ <TRUE/> if and only if the execution has been approved
+ */
+ bool impl_approveRowChange_throw(
+ const css::lang::EventObject& _rEvent,
+ const bool _bAllowSQLException,
+ ::osl::ClearableMutexGuard& _rGuard
+ );
+
+ /// invalidate all our parameter-related stuff
+ void invalidateParameters();
+
+ void saveInsertOnlyState( );
+ void restoreInsertOnlyState( );
+
+ // error handling
+ void onError(const css::sdb::SQLErrorEvent& _rEvent);
+ void onError(const css::sdbc::SQLException&, const OUString& _rContextDescription);
+
+ // html tools
+ OUString GetDataEncoded(bool _bURLEncoded,const css::uno::Reference< css::awt::XControl>& SubmitButton, const css::awt::MouseEvent& MouseEvt);
+ css::uno::Sequence<sal_Int8> GetDataMultiPartEncoded(const css::uno::Reference< css::awt::XControl>& SubmitButton, const css::awt::MouseEvent& MouseEvt,
+ OUString& rContentType);
+
+ void AppendComponent(HtmlSuccessfulObjList& rList, const css::uno::Reference< css::beans::XPropertySet>& xComponentSet, std::u16string_view rNamePrefix,
+ const css::uno::Reference< css::awt::XControl>& rxSubmitButton, const css::awt::MouseEvent& MouseEvt);
+
+ void FillSuccessfulList(HtmlSuccessfulObjList& rList, const css::uno::Reference< css::awt::XControl>& rxSubmitButton, const css::awt::MouseEvent& MouseEvt);
+
+ static void InsertTextPart(INetMIMEMessage& rParent, std::u16string_view rName, std::u16string_view rData);
+ static void InsertFilePart(INetMIMEMessage& rParent, std::u16string_view rName, const OUString& rFileName);
+ static void Encode(OUString& rString);
+
+ css::uno::Reference< css::sdbc::XConnection > getConnection();
+
+ void impl_createLoadTimer();
+
+ void impl_construct();
+
+ DECL_LINK( OnTimeout, Timer*, void );
+protected:
+ using OPropertySetHelper::getPropertyValues;
+};
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Date.cxx b/forms/source/component/Date.cxx
new file mode 100644
index 0000000000..3305e7695d
--- /dev/null
+++ b/forms/source/component/Date.cxx
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Date.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <connectivity/dbconversion.hxx>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+using namespace dbtools;
+
+namespace frm
+{
+
+
+using namespace ::com::sun::star;
+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::util;
+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;
+
+
+ODateControl::ODateControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_DATEFIELD)
+{
+}
+
+
+Sequence<Type> ODateControl::_getTypes()
+{
+ return OBoundControl::_getTypes();
+}
+
+css::uno::Sequence<OUString> SAL_CALL ODateControl::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { FRM_SUN_CONTROL_DATEFIELD, STARDIV_ONE_FORM_CONTROL_DATEFIELD };
+ return comphelper::concatSequences(OBoundControl::getSupportedServiceNames(), vals);
+}
+
+
+Sequence<Type> ODateModel::_getTypes()
+{
+ return OEditBaseModel::_getTypes();
+}
+
+
+ODateModel::ODateModel(const Reference<XComponentContext>& _rxFactory)
+ : OEditBaseModel(_rxFactory, VCL_CONTROLMODEL_DATEFIELD,
+ FRM_SUN_CONTROL_DATEFIELD, true, true)
+ // use the old control name for compytibility reasons
+ , OLimitedFormats(_rxFactory, FormComponentType::DATEFIELD)
+ , m_bDateTimeField(false)
+{
+ m_nClassId = FormComponentType::DATEFIELD;
+ initValueProperty( PROPERTY_DATE, PROPERTY_ID_DATE );
+
+ setAggregateSet(m_xAggregateFastSet, getOriginalHandle(PROPERTY_ID_DATEFORMAT));
+
+ osl_atomic_increment( &m_refCount );
+ try
+ {
+ if ( m_xAggregateSet.is() )
+ m_xAggregateSet->setPropertyValue( PROPERTY_DATEMIN, Any(util::Date(1, 1, 1800)) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "ODateModel::ODateModel" );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+ODateModel::ODateModel( const ODateModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ : OEditBaseModel(_pOriginal, _rxFactory)
+ , OLimitedFormats(_rxFactory, FormComponentType::DATEFIELD)
+ , m_bDateTimeField(false)
+{
+ setAggregateSet( m_xAggregateFastSet, getOriginalHandle( PROPERTY_ID_DATEFORMAT ) );
+}
+
+
+ODateModel::~ODateModel( )
+{
+ setAggregateSet(Reference< XFastPropertySet >(), -1);
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL ODateModel::createClone()
+{
+ rtl::Reference<ODateModel> pClone = new ODateModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL ODateModel::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals {
+ BINDABLE_CONTROL_MODEL,
+ DATA_AWARE_CONTROL_MODEL,
+ VALIDATABLE_CONTROL_MODEL,
+ BINDABLE_DATA_AWARE_CONTROL_MODEL,
+ VALIDATABLE_BINDABLE_CONTROL_MODEL,
+ FRM_SUN_COMPONENT_DATEFIELD,
+ FRM_SUN_COMPONENT_DATABASE_DATEFIELD,
+ BINDABLE_DATABASE_DATE_FIELD,
+ FRM_COMPONENT_DATEFIELD
+ };
+
+ return comphelper::concatSequences(OBoundControlModel::getSupportedServiceNames(), vals);
+}
+
+
+OUString SAL_CALL ODateModel::getServiceName()
+{
+ return FRM_COMPONENT_DATEFIELD; // old (non-sun) name for compatibility !
+}
+
+// XPropertySet
+
+void ODateModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 4);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_DATE, PROPERTY_ID_DEFAULT_DATE, cppu::UnoType<util::Date>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FORMATKEY, PROPERTY_ID_FORMATKEY, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_FORMATSSUPPLIER, PROPERTY_ID_FORMATSSUPPLIER, cppu::UnoType<XNumberFormatsSupplier>::get(),
+ css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+void SAL_CALL ODateModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle ) const
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_FORMATKEY:
+ getFormatKeyPropertyValue(_rValue);
+ break;
+ case PROPERTY_ID_FORMATSSUPPLIER:
+ _rValue <<= getFormatsSupplier();
+ break;
+ default:
+ OEditBaseModel::getFastPropertyValue(_rValue, _nHandle);
+ break;
+ }
+}
+
+
+sal_Bool SAL_CALL ODateModel::convertFastPropertyValue(Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle, const Any& _rValue )
+{
+ if (PROPERTY_ID_FORMATKEY == _nHandle)
+ return convertFormatKeyPropertyValue(_rConvertedValue, _rOldValue, _rValue);
+ else
+ return OEditBaseModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue );
+}
+
+
+void SAL_CALL ODateModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ if (PROPERTY_ID_FORMATKEY == _nHandle)
+ setFormatKeyPropertyValue(_rValue);
+ else
+ OEditBaseModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
+}
+
+// XLoadListener
+
+void ODateModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ OBoundControlModel::onConnectedDbColumn( _rxForm );
+ Reference<XPropertySet> xField = getField();
+ if (!xField.is())
+ return;
+
+ m_bDateTimeField = false;
+ try
+ {
+ sal_Int32 nFieldType = 0;
+ xField->getPropertyValue(PROPERTY_FIELDTYPE) >>= nFieldType;
+ m_bDateTimeField = (nFieldType == DataType::TIMESTAMP);
+ }
+ catch(const Exception&)
+ {
+ }
+}
+
+
+bool ODateModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+ if ( aControlValue == m_aSaveValue )
+ return true;
+
+ if ( !aControlValue.hasValue() )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ util::Date aDate;
+ if ( !( aControlValue >>= aDate ) )
+ {
+ sal_Int32 nAsInt(0);
+ aControlValue >>= nAsInt;
+ aDate = DBTypeConversion::toDate(nAsInt);
+ }
+
+ if ( !m_bDateTimeField )
+ m_xColumnUpdate->updateDate( aDate );
+ else
+ {
+ util::DateTime aDateTime = m_xColumn->getTimestamp();
+ aDateTime.Day = aDate.Day;
+ aDateTime.Month = aDate.Month;
+ aDateTime.Year = aDate.Year;
+ m_xColumnUpdate->updateTimestamp( aDateTime );
+ }
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aControlValue;
+ return true;
+}
+
+
+Any ODateModel::translateControlValueToExternalValue( ) const
+{
+ return getControlValue();
+}
+
+
+Any ODateModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ return _rExternalValue;
+}
+
+
+Any ODateModel::translateControlValueToValidatableValue( ) const
+{
+ return getControlValue();
+}
+
+
+Any ODateModel::translateDbColumnToControlValue()
+{
+ util::Date aDate = m_xColumn->getDate();
+ if (m_xColumn->wasNull())
+ m_aSaveValue.clear();
+ else
+ m_aSaveValue <<= aDate;
+
+ return m_aSaveValue;
+}
+
+
+Any ODateModel::getDefaultForReset() const
+{
+ return m_aDefault;
+}
+
+
+void ODateModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aSaveValue.clear();
+}
+
+
+Sequence< Type > ODateModel::getSupportedBindingTypes()
+{
+ return Sequence< Type >( & cppu::UnoType<util::Date>::get(), 1 );
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ODateModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ODateModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ODateControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ODateControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Date.hxx b/forms/source/component/Date.hxx
new file mode 100644
index 0000000000..77d4d8a3e0
--- /dev/null
+++ b/forms/source/component/Date.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+#include <limitedformats.hxx>
+
+
+namespace frm
+{
+
+class ODateModel
+ :public OEditBaseModel
+ ,public OLimitedFormats
+{
+ css::uno::Any m_aSaveValue;
+ bool m_bDateTimeField;
+
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ ODateModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ ODateModel(
+ const ODateModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~ODateModel() override;
+
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // css::beans::XPropertySet
+ 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;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ODateModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPropertySet
+ using OEditBaseModel::getFastPropertyValue;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+protected:
+ // OBoundControlModel overridables
+ virtual css::uno::Any
+ translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Any
+ translateControlValueToExternalValue( ) const override;
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+
+ virtual css::uno::Any translateControlValueToValidatableValue( ) const override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual void resetNoBroadcast() override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+class ODateControl: public OBoundControl
+{
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit ODateControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ DECLARE_UNO3_AGG_DEFAULTS(ODateControl, OBoundControl)
+
+// css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ODateControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Edit.cxx b/forms/source/component/Edit.cxx
new file mode 100644
index 0000000000..902f077d3b
--- /dev/null
+++ b/forms/source/component/Edit.cxx
@@ -0,0 +1,718 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "Edit.hxx"
+#include <property.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/form/XSubmit.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+
+#include <vcl/svapp.hxx>
+#include <vcl/keycodes.hxx>
+
+#include <connectivity/formattedcolumnvalue.hxx>
+
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+
+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::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<Type> OEditControl::_getTypes()
+{
+ // my two base classes
+ static Sequence<Type> const aTypes = concatSequences(OBoundControl::_getTypes(), OEditControl_BASE::getTypes());
+ return aTypes;
+}
+
+
+Any SAL_CALL OEditControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OBoundControl::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OEditControl_BASE::queryInterface(_rType);
+
+ return aReturn;
+}
+
+
+OEditControl::OEditControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl( _rxFactory, FRM_SUN_CONTROL_RICHTEXTCONTROL )
+ ,m_aChangeListeners(m_aMutex)
+ ,m_nKeyEvent( nullptr )
+{
+
+ osl_atomic_increment(&m_refCount);
+ {
+ Reference<XWindow> xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ {
+ xComp->addFocusListener(this);
+ xComp->addKeyListener(this);
+ }
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+OEditControl::~OEditControl()
+{
+ if( m_nKeyEvent )
+ Application::RemoveUserEvent( m_nKeyEvent );
+
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+// XChangeBroadcaster
+
+void OEditControl::addChangeListener(const Reference<XChangeListener>& l)
+{
+ m_aChangeListeners.addInterface( l );
+}
+
+
+void OEditControl::removeChangeListener(const Reference<XChangeListener>& l)
+{
+ m_aChangeListeners.removeInterface( l );
+}
+
+// OComponentHelper
+
+void OEditControl::disposing()
+{
+ OBoundControl::disposing();
+
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aChangeListeners.disposeAndClear(aEvt);
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> OEditControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 3);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-3] = FRM_SUN_CONTROL_TEXTFIELD;
+ pArray[aSupported.getLength()-2] = STARDIV_ONE_FORM_CONTROL_EDIT;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_TEXTFIELD;
+ return aSupported;
+}
+
+// XEventListener
+
+void OEditControl::disposing(const EventObject& Source)
+{
+ OBoundControl::disposing(Source);
+}
+
+// XFocusListener
+
+void OEditControl::focusGained( const FocusEvent& /*e*/ )
+{
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (xSet.is())
+ xSet->getPropertyValue( PROPERTY_TEXT ) >>= m_aHtmlChangeValue;
+}
+
+
+void OEditControl::focusLost( const FocusEvent& /*e*/ )
+{
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (xSet.is())
+ {
+ OUString sNewHtmlChangeValue;
+ xSet->getPropertyValue( PROPERTY_TEXT ) >>= sNewHtmlChangeValue;
+ if( sNewHtmlChangeValue != m_aHtmlChangeValue )
+ {
+ EventObject aEvt( *this );
+ m_aChangeListeners.notifyEach( &XChangeListener::changed, aEvt );
+ }
+ }
+}
+
+// XKeyListener
+
+void OEditControl::keyPressed(const css::awt::KeyEvent& e)
+{
+ if( e.KeyCode != KEY_RETURN || e.Modifiers != 0 )
+ return;
+
+ // Is the Control in a form with a submit URL?
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if( !xSet.is() )
+ return;
+
+ // Not for multiline edits
+ Any aTmp( xSet->getPropertyValue(PROPERTY_MULTILINE));
+ if ((aTmp.getValueType().equals(cppu::UnoType<bool>::get())) && getBOOL(aTmp))
+ return;
+
+ Reference<XFormComponent> xFComp(xSet, UNO_QUERY);
+ css::uno::Reference<css::uno::XInterface> xParent = xFComp->getParent();
+ if( !xParent.is() )
+ return;
+
+ Reference<XPropertySet> xFormSet(xParent, UNO_QUERY);
+ if( !xFormSet.is() )
+ return;
+
+ aTmp = xFormSet->getPropertyValue( PROPERTY_TARGET_URL );
+ if (!aTmp.getValueType().equals(cppu::UnoType<OUString>::get()) ||
+ getString(aTmp).isEmpty() )
+ return;
+
+ Reference<XIndexAccess> xElements(xParent, UNO_QUERY);
+ sal_Int32 nCount = xElements->getCount();
+ if( nCount > 1 )
+ {
+ Reference<XPropertySet> xFCSet;
+ for( sal_Int32 nIndex=0; nIndex < nCount; nIndex++ )
+ {
+ // Any aElement(xElements->getByIndex(nIndex));
+ xElements->getByIndex(nIndex) >>= xFCSet;
+ OSL_ENSURE(xFCSet.is(),"OEditControl::keyPressed: No XPropertySet!");
+
+ if (hasProperty(PROPERTY_CLASSID, xFCSet) &&
+ getINT16(xFCSet->getPropertyValue(PROPERTY_CLASSID)) == FormComponentType::TEXTFIELD)
+ {
+ // Found another Edit -> then do not submit!
+ if (xFCSet != xSet)
+ return;
+ }
+ }
+ }
+
+ // Because we're still in the header, trigger submit asynchronously
+ if( m_nKeyEvent )
+ Application::RemoveUserEvent( m_nKeyEvent );
+ m_nKeyEvent = Application::PostUserEvent( LINK(this, OEditControl, OnKeyPressed) );
+}
+
+
+void OEditControl::keyReleased(const css::awt::KeyEvent& /*e*/)
+{
+}
+
+
+IMPL_LINK_NOARG(OEditControl, OnKeyPressed, void*, void)
+{
+ m_nKeyEvent = nullptr;
+
+ Reference<XFormComponent> xFComp(getModel(), UNO_QUERY);
+ css::uno::Reference<css::uno::XInterface> xParent = xFComp->getParent();
+ Reference<XSubmit> xSubmit(xParent, UNO_QUERY);
+ if (xSubmit.is())
+ xSubmit->submit( Reference<XControl>(), css::awt::MouseEvent() );
+}
+
+
+
+OEditModel::OEditModel(const Reference<XComponentContext>& _rxFactory)
+ :OEditBaseModel( _rxFactory, FRM_SUN_COMPONENT_RICHTEXTCONTROL, FRM_SUN_CONTROL_TEXTFIELD, true, true )
+ ,m_bMaxTextLenModified(false)
+ ,m_bWritingFormattedFake(false)
+{
+
+ m_nClassId = FormComponentType::TEXTFIELD;
+ initValueProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT );
+}
+
+
+OEditModel::OEditModel( const OEditModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+ ,m_bMaxTextLenModified(false)
+ ,m_bWritingFormattedFake(false)
+{
+
+ // Note that most of the properties are not clone from the original object:
+ // Things as the format key, it's type, and such, depend on the field being part of a loaded form
+ // (they're initialized in onConnectedDbColumn). Even if the original object _is_ part of such a form, we ourself
+ // certainly aren't, so these members are defaulted. If we're inserted into a form which is already loaded,
+ // they will be set to new values, anyway...
+}
+
+
+OEditModel::~OEditModel()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OEditModel::createClone()
+{
+ rtl::Reference<OEditModel> pClone = new OEditModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+void OEditModel::disposing()
+{
+ OEditBaseModel::disposing();
+ m_pValueFormatter.reset();
+}
+
+// XPersistObject
+
+OUString SAL_CALL OEditModel::getServiceName()
+{
+ return FRM_COMPONENT_EDIT; // old (non-sun) name for compatibility !
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OEditModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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_TEXTFIELD;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_TEXTFIELD;
+ *pStoreTo++ = BINDABLE_DATABASE_TEXT_FIELD;
+
+ *pStoreTo++ = FRM_COMPONENT_TEXTFIELD;
+
+ return aSupported;
+}
+
+// XPropertySet
+void SAL_CALL OEditModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle ) const
+{
+ if ( PROPERTY_ID_PERSISTENCE_MAXTEXTLENGTH == nHandle )
+ {
+ if ( m_bMaxTextLenModified )
+ rValue <<= sal_Int16(0);
+ else if ( m_xAggregateSet.is() )
+ rValue = m_xAggregateSet->getPropertyValue(PROPERTY_MAXTEXTLEN);
+ }
+ else
+ {
+ OEditBaseModel::getFastPropertyValue(rValue, nHandle );
+ }
+}
+
+
+void OEditModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 5);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_PERSISTENCE_MAXTEXTLENGTH, PROPERTY_ID_PERSISTENCE_MAXTEXTLENGTH, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FILTERPROPOSAL, PROPERTY_ID_FILTERPROPOSAL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+void OEditModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+{
+ OEditBaseModel::describeAggregateProperties( _rAggregateProps );
+
+ // our aggregate is a rich text model, which also derives from OControlModel, as
+ // do we, so we need to remove some duplicate properties
+ RemoveProperty( _rAggregateProps, PROPERTY_TABINDEX );
+ RemoveProperty( _rAggregateProps, PROPERTY_CLASSID );
+ RemoveProperty( _rAggregateProps, PROPERTY_NAME );
+ RemoveProperty( _rAggregateProps, PROPERTY_TAG );
+ RemoveProperty( _rAggregateProps, PROPERTY_NATIVE_LOOK );
+ RemoveProperty( _rAggregateProps, PROPERTY_STANDARD_THEME );
+
+}
+
+
+bool OEditModel::implActsAsRichText( ) const
+{
+ bool bActAsRichText = false;
+ if ( m_xAggregateSet.is() )
+ {
+ OSL_VERIFY( m_xAggregateSet->getPropertyValue( PROPERTY_RICH_TEXT ) >>= bActAsRichText );
+ }
+ return bActAsRichText;
+}
+
+
+void SAL_CALL OEditModel::reset( )
+{
+ // no reset if we currently act as rich text control
+ if ( implActsAsRichText() )
+ return;
+
+ OEditBaseModel::reset();
+}
+
+
+namespace
+{
+ void lcl_transferProperties( const Reference< XPropertySet >& _rxSource, const Reference< XPropertySet >& _rxDest )
+ {
+ try
+ {
+ Reference< XPropertySetInfo > xSourceInfo;
+ if ( _rxSource.is() )
+ xSourceInfo = _rxSource->getPropertySetInfo();
+
+ Reference< XPropertySetInfo > xDestInfo;
+ if ( _rxDest.is() )
+ xDestInfo = _rxDest->getPropertySetInfo();
+
+ if ( !xSourceInfo.is() || !xDestInfo.is() )
+ {
+ OSL_FAIL( "lcl_transferProperties: invalid property set(s)!" );
+ return;
+ }
+
+ const Sequence< Property > aSourceProps( xSourceInfo->getProperties() );
+ for ( auto const & sourceprop : aSourceProps )
+ {
+ if ( !xDestInfo->hasPropertyByName( sourceprop.Name ) )
+ {
+ continue;
+ }
+
+ Property aDestProp( xDestInfo->getPropertyByName( sourceprop.Name ) );
+ if ( 0 != ( aDestProp.Attributes & PropertyAttribute::READONLY ) )
+ {
+ continue;
+ }
+
+ try
+ {
+ _rxDest->setPropertyValue( sourceprop.Name, _rxSource->getPropertyValue( sourceprop.Name ) );
+ }
+ catch(const IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "could not transfer the property named '"
+ << sourceprop.Name
+ << "'" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+}
+
+
+void OEditModel::writeAggregate( const Reference< XObjectOutputStream >& _rxOutStream ) const
+{
+ // we need to fake the writing of our aggregate. Since #i24387#, we have another aggregate,
+ // but for compatibility, we need to use an "old" aggregate for writing and reading
+
+ Reference< XPropertySet > xFakedAggregate(
+ getContext()->getServiceManager()->createInstanceWithContext( VCL_CONTROLMODEL_EDIT, getContext() ),
+ UNO_QUERY
+ );
+ OSL_ENSURE( xFakedAggregate.is(), "OEditModel::writeAggregate: could not create an old EditControlModel!" );
+ if ( !xFakedAggregate.is() )
+ return;
+
+ lcl_transferProperties( m_xAggregateSet, xFakedAggregate );
+
+ Reference< XPersistObject > xFakedPersist( xFakedAggregate, UNO_QUERY );
+ OSL_ENSURE( xFakedPersist.is(), "OEditModel::writeAggregate: no XPersistObject!" );
+ if ( xFakedPersist.is() )
+ xFakedPersist->write( _rxOutStream );
+}
+
+
+void OEditModel::readAggregate( const Reference< XObjectInputStream >& _rxInStream )
+{
+ // we need to fake the reading of our aggregate. Since #i24387#, we have another aggregate,
+ // but for compatibility, we need to use an "old" aggregate for writing and reading
+
+ Reference< XPropertySet > xFakedAggregate(
+ getContext()->getServiceManager()->createInstanceWithContext( VCL_CONTROLMODEL_EDIT, getContext() ),
+ UNO_QUERY
+ );
+ Reference< XPersistObject > xFakedPersist( xFakedAggregate, UNO_QUERY );
+ OSL_ENSURE( xFakedPersist.is(), "OEditModel::readAggregate: no XPersistObject, or no faked aggregate at all!" );
+ if ( xFakedPersist.is() )
+ {
+ xFakedPersist->read( _rxInStream );
+ lcl_transferProperties( xFakedAggregate, m_xAggregateSet );
+ }
+}
+
+
+void OEditModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ Any aCurrentText;
+ sal_Int16 nOldTextLen = 0;
+ // Am I loaded at the moment and did I switch MaxTextLen temporarily?
+ if ( m_bMaxTextLenModified )
+ { // -> for the duration of saving, make my aggregated model believe the old TextLen
+
+ // before doing this we have to save the current text value of the aggregate, as this may be affected by resetting the text len
+ aCurrentText = m_xAggregateSet->getPropertyValue(PROPERTY_TEXT);
+
+ m_xAggregateSet->getPropertyValue(PROPERTY_MAXTEXTLEN) >>= nOldTextLen;
+ m_xAggregateSet->setPropertyValue(PROPERTY_MAXTEXTLEN, Any(sal_Int16(0)));
+ }
+
+ OEditBaseModel::write(_rxOutStream);
+
+ if ( m_bMaxTextLenModified )
+ { // Reset again
+ m_xAggregateSet->setPropertyValue(PROPERTY_MAXTEXTLEN, Any(nOldTextLen));
+ // and reset the text
+ // First we set it to an empty string : Without this the second setPropertyValue would not do anything as it thinks
+ // we aren't changing the prop (it didn't notify the - implicit - change of the text prop while setting the max text len)
+ // This seems to be a bug with in toolkit's EditControl-implementation.
+ m_xAggregateSet->setPropertyValue(PROPERTY_TEXT, Any(OUString()));
+ m_xAggregateSet->setPropertyValue(PROPERTY_TEXT, aCurrentText);
+ }
+}
+
+
+void OEditModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OEditBaseModel::read(_rxInStream);
+
+ // Some versions (5.1 'til about 552) wrote a wrong DefaultControl-property value which is unknown
+ // to older versions (5.0).
+ // correct this ...
+ if (m_xAggregateSet.is())
+ {
+ Any aDefaultControl = m_xAggregateSet->getPropertyValue(PROPERTY_DEFAULTCONTROL);
+ if ( (aDefaultControl.getValueType().getTypeClass() == TypeClass_STRING)
+ && (getString(aDefaultControl) == STARDIV_ONE_FORM_CONTROL_TEXTFIELD )
+ )
+ {
+ m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, Any( STARDIV_ONE_FORM_CONTROL_EDIT ) );
+ // Older as well as current versions should understand this : the former knew only the STARDIV_ONE_FORM_CONTROL_EDIT,
+ // the latter are registered for both STARDIV_ONE_FORM_CONTROL_EDIT and STARDIV_ONE_FORM_CONTROL_TEXTFIELD.
+ }
+ }
+}
+
+
+sal_uInt16 OEditModel::getPersistenceFlags() const
+{
+ sal_uInt16 nFlags = OEditBaseModel::getPersistenceFlags();
+
+ if (m_bWritingFormattedFake)
+ nFlags |= PF_FAKE_FORMATTED_FIELD;
+
+ return nFlags;
+}
+
+
+void OEditModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ Reference< XPropertySet > xField = getField();
+ if ( !xField.is() )
+ return;
+
+ m_pValueFormatter.reset( new ::dbtools::FormattedColumnValue( getContext(), Reference< XRowSet >( _rxForm, UNO_QUERY ), xField ) );
+
+ if ( m_pValueFormatter->getKeyType() == NumberFormat::SCIENTIFIC )
+ return;
+
+ m_bMaxTextLenModified = getINT16(m_xAggregateSet->getPropertyValue(PROPERTY_MAXTEXTLEN)) != 0;
+ if ( !m_bMaxTextLenModified )
+ {
+ sal_Int32 nFieldLen = 0;
+ xField->getPropertyValue("Precision") >>= nFieldLen;
+
+ if (nFieldLen > 0 && nFieldLen <= SAL_MAX_INT16)
+ {
+ Any aVal;
+ aVal <<= static_cast<sal_Int16>(nFieldLen);
+ m_xAggregateSet->setPropertyValue(PROPERTY_MAXTEXTLEN, aVal);
+
+ m_bMaxTextLenModified = true;
+ }
+ }
+ else
+ m_bMaxTextLenModified = false; // to get sure that the text len won't be set in unloaded
+}
+
+
+void OEditModel::onDisconnectedDbColumn()
+{
+ OEditBaseModel::onDisconnectedDbColumn();
+
+ m_pValueFormatter.reset();
+
+ if ( hasField() && m_bMaxTextLenModified )
+ {
+ Any aVal;
+ aVal <<= sal_Int16(0); // Only if it was 0, I switched it in onConnectedDbColumn
+ m_xAggregateSet->setPropertyValue(PROPERTY_MAXTEXTLEN, aVal);
+ m_bMaxTextLenModified = false;
+ }
+}
+
+
+bool OEditModel::approveDbColumnType( sal_Int32 _nColumnType )
+{
+ // if we act as rich text currently, we do not allow binding to a database column
+ if ( implActsAsRichText() )
+ return false;
+
+ return OEditBaseModel::approveDbColumnType( _nColumnType );
+}
+
+
+bool OEditModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aNewValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+
+ OUString sNewValue;
+ aNewValue >>= sNewValue;
+
+ if ( !aNewValue.hasValue()
+ || ( sNewValue.isEmpty() // an empty string
+ && m_bEmptyIsNull // which should be interpreted as NULL
+ )
+ )
+ {
+ m_xColumnUpdate->updateNull();
+ }
+ else
+ {
+ OSL_PRECOND(m_pValueFormatter,
+ "OEditModel::commitControlValueToDbColumn: no value formatter!");
+ try
+ {
+ if (m_pValueFormatter)
+ {
+ if ( !m_pValueFormatter->setFormattedValue( sNewValue ) )
+ return false;
+ }
+ else
+ m_xColumnUpdate->updateString( sNewValue );
+ }
+ catch ( const Exception& )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+Any OEditModel::translateDbColumnToControlValue()
+{
+ OSL_PRECOND(m_pValueFormatter,
+ "OEditModel::translateDbColumnToControlValue: no value formatter!");
+ Any aRet;
+ if (m_pValueFormatter)
+ {
+ OUString sValue( m_pValueFormatter->getFormattedValue() );
+ if ( sValue.isEmpty()
+ && m_pValueFormatter->getColumn().is()
+ && m_pValueFormatter->getColumn()->wasNull()
+ )
+ {
+ }
+ else
+ {
+ // #i2817# OJ
+ sal_uInt16 nMaxTextLen = getINT16( m_xAggregateSet->getPropertyValue( PROPERTY_MAXTEXTLEN ) );
+ if ( nMaxTextLen && sValue.getLength() > nMaxTextLen )
+ {
+ sal_Int32 nDiff = sValue.getLength() - nMaxTextLen;
+ sValue = sValue.replaceAt( nMaxTextLen, nDiff, u"" );
+ }
+
+ aRet <<= sValue;
+ }
+ }
+
+ return aRet.hasValue() ? aRet : Any( OUString() );
+}
+
+
+Any OEditModel::getDefaultForReset() const
+{
+ return Any( m_aDefaultText );
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OEditModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OEditModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OEditControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OEditControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Edit.hxx b/forms/source/component/Edit.hxx
new file mode 100644
index 0000000000..219b41d21d
--- /dev/null
+++ b/forms/source/component/Edit.hxx
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "EditBase.hxx"
+
+#include <tools/link.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/implbase3.hxx>
+
+#include <com/sun/star/form/XChangeBroadcaster.hpp>
+
+namespace dbtools { class FormattedColumnValue; }
+struct ImplSVEvent;
+
+namespace frm
+{
+
+class OEditModel final : public OEditBaseModel
+{
+ ::std::unique_ptr< ::dbtools::FormattedColumnValue >
+ m_pValueFormatter;
+ bool m_bMaxTextLenModified : 1; // set to <TRUE/> when we change the MaxTextLen of the aggregate
+
+ bool m_bWritingFormattedFake : 1; // are we writing something which should be interpreted as formatted upon reading?
+
+public:
+ OEditModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OEditModel(
+ const OEditModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OEditModel() override;
+
+private:
+ void enableFormattedWriteFake() { m_bWritingFormattedFake = true; }
+ void disableFormattedWriteFake() { m_bWritingFormattedFake = false; }
+ bool lastReadWasFormattedFake() const { return (getLastReadVersion() & PF_FAKE_FORMATTED_FIELD) != 0; }
+
+ friend class OFormattedFieldWrapper;
+ friend class OFormattedModel; // temporary
+
+public:
+ virtual void SAL_CALL disposing() override;
+
+ // XPropertySet
+ virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+
+ // XPersistObject
+ 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;
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // XPropertySet
+ using OBoundControlModel::getFastPropertyValue;
+
+ // XReset
+ virtual void SAL_CALL reset( ) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OEditModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() 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
+ using OBoundControlModel::disposing;
+
+private:
+ // OControlModel overridables
+ virtual void writeAggregate( const css::uno::Reference< css::io::XObjectOutputStream >& _rxOutStream ) const override;
+ virtual void readAggregate( const css::uno::Reference< css::io::XObjectInputStream >& _rxInStream ) override;
+
+ // OBoundControlModel overridables
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+ virtual void onDisconnectedDbColumn() override;
+
+ virtual bool approveDbColumnType( sal_Int32 _nColumnType ) override;
+
+ virtual sal_uInt16 getPersistenceFlags() const override;
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ bool implActsAsRichText( ) const;
+};
+
+
+//= OEditControl
+
+typedef ::cppu::ImplHelper3< css::awt::XFocusListener,
+ css::awt::XKeyListener,
+ css::form::XChangeBroadcaster > OEditControl_BASE;
+
+class OEditControl : public OBoundControl
+ ,public OEditControl_BASE
+{
+ ::comphelper::OInterfaceContainerHelper3<css::form::XChangeListener>
+ m_aChangeListeners;
+
+ OUString m_aHtmlChangeValue;
+ ImplSVEvent * m_nKeyEvent;
+
+public:
+ explicit OEditControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext);
+ virtual ~OEditControl() override;
+
+ DECLARE_UNO3_AGG_DEFAULTS(OEditControl, OBoundControl)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+// css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OEditControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+// css::form::XChangeBroadcaster
+ virtual void SAL_CALL addChangeListener(const css::uno::Reference< css::form::XChangeListener>& _rxListener) override;
+ virtual void SAL_CALL removeChangeListener(const css::uno::Reference< css::form::XChangeListener>& _rxListener) override;
+
+// css::awt::XFocusListener
+ virtual void SAL_CALL focusGained( const css::awt::FocusEvent& e ) override;
+ virtual void SAL_CALL focusLost( const css::awt::FocusEvent& e ) override;
+
+// css::awt::XKeyListener
+ virtual void SAL_CALL keyPressed(const css::awt::KeyEvent& e) override;
+ virtual void SAL_CALL keyReleased(const css::awt::KeyEvent& e) override;
+
+ // XControl
+ using OBoundControl::createPeer;
+
+private:
+ DECL_LINK( OnKeyPressed, void*, void );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/EditBase.cxx b/forms/source/component/EditBase.cxx
new file mode 100644
index 0000000000..b584eaa828
--- /dev/null
+++ b/forms/source/component/EditBase.cxx
@@ -0,0 +1,377 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "EditBase.hxx"
+#include <property.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/time.hxx>
+#include <tools/date.hxx>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/Date.hpp>
+
+
+namespace frm
+{
+using namespace ::com::sun::star;
+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;
+
+namespace
+{
+ const sal_uInt16 DEFAULT_LONG = 0x0001;
+ const sal_uInt16 DEFAULT_DOUBLE = 0x0002;
+ const sal_uInt16 FILTERPROPOSAL = 0x0004;
+ const sal_uInt16 DEFAULT_TIME = 0x0008;
+ const sal_uInt16 DEFAULT_DATE = 0x0010;
+}
+
+
+OEditBaseModel::OEditBaseModel( const Reference< XComponentContext >& _rxFactory, const OUString& rUnoControlModelName,
+ const OUString& rDefault, const bool _bSupportExternalBinding, const bool _bSupportsValidation )
+ :OBoundControlModel( _rxFactory, rUnoControlModelName, rDefault, true, _bSupportExternalBinding, _bSupportsValidation )
+ ,m_nLastReadVersion(0)
+ ,m_bEmptyIsNull(true)
+ ,m_bFilterProposal(false)
+{
+}
+
+
+OEditBaseModel::OEditBaseModel( const OEditBaseModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ ,m_nLastReadVersion(0)
+{
+
+ m_bFilterProposal = _pOriginal->m_bFilterProposal;
+ m_bEmptyIsNull = _pOriginal->m_bEmptyIsNull;
+ m_aDefault = _pOriginal->m_aDefault;
+ m_aDefaultText = _pOriginal->m_aDefaultText;
+}
+
+
+OEditBaseModel::~OEditBaseModel( )
+{
+}
+
+// XPersist
+
+void OEditBaseModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OBoundControlModel::write(_rxOutStream);
+
+ // Version
+ sal_uInt16 nVersionId = 0x0006;
+ DBG_ASSERT((getPersistenceFlags() & ~PF_SPECIAL_FLAGS) == 0,
+ "OEditBaseModel::write : invalid special version flags !");
+ // please don't use other flags, older versions can't interpret them !
+
+ nVersionId |= getPersistenceFlags();
+ _rxOutStream->writeShort(nVersionId);
+
+ // Name
+ _rxOutStream->writeShort(0); // obsolete
+ _rxOutStream << m_aDefaultText;
+
+ // Masking for any
+ sal_uInt16 nAnyMask = 0;
+ if (m_aDefault.getValueType().getTypeClass() == TypeClass_LONG)
+ nAnyMask |= DEFAULT_LONG;
+ else if (m_aDefault.getValueType().getTypeClass() == TypeClass_DOUBLE)
+ nAnyMask |= DEFAULT_DOUBLE;
+ else if (m_aDefault.getValueType() == cppu::UnoType<util::Time>::get())
+ nAnyMask |= DEFAULT_TIME;
+ else if (m_aDefault.getValueType() == cppu::UnoType<util::Date>::get())
+ nAnyMask |= DEFAULT_DATE;
+
+ if (m_bFilterProposal) // Don't save a value, because it's boolean
+ nAnyMask |= FILTERPROPOSAL;
+
+ _rxOutStream->writeBoolean(m_bEmptyIsNull);
+ _rxOutStream->writeShort(nAnyMask);
+
+ if ((nAnyMask & DEFAULT_LONG) == DEFAULT_LONG)
+ _rxOutStream->writeLong(getINT32(m_aDefault));
+ else if ((nAnyMask & DEFAULT_DOUBLE) == DEFAULT_DOUBLE)
+ _rxOutStream->writeDouble(getDouble(m_aDefault));
+ else if ((nAnyMask & DEFAULT_TIME) == DEFAULT_TIME)
+ {
+ util::Time aTime;
+ OSL_VERIFY(m_aDefault >>= aTime);
+ _rxOutStream->writeHyper(::tools::Time(aTime).GetTime());
+ }
+ else if ((nAnyMask & DEFAULT_DATE) == DEFAULT_DATE)
+ {
+ util::Date aDate;
+ OSL_VERIFY(m_aDefault >>= aDate);
+ _rxOutStream->writeLong(::Date(aDate).GetDate());
+ }
+
+ // since version 5 we write the help text
+ writeHelpTextCompatibly(_rxOutStream);
+ // (that's potentially bad : at the time I added the above line we had two derived classes : OEditModel and
+ // OFormattedModel. The first one does not have an own version handling, so it can't write the help text itself,
+ // the second one does its own writing (reading) after calling our method, so normally we shouldn't write any
+ // additional members as this is not compatible to older office versions.
+ // We decided to place the writing of the help text here as it seems the less worse alternative. There is no delivered
+ // office version including formatted controls (and thus the OFormattedModel), and the OFormattedModel::read seems
+ // robust against this change (as it will read a wrong and unknown file version and thus set its members to defaults).
+
+ if ((nVersionId & PF_HANDLE_COMMON_PROPS) != 0)
+ writeCommonEditProperties(_rxOutStream);
+
+ // !!! properties common to all OEditBaseModel derived classes should be written in writeCommonEditProperties !!!
+}
+
+
+sal_uInt16 OEditBaseModel::getPersistenceFlags() const
+{
+ return PF_HANDLE_COMMON_PROPS;
+}
+
+
+void OEditBaseModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OBoundControlModel::read(_rxInStream);
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // Version's own version number
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ m_nLastReadVersion = nVersion;
+
+ bool bHandleCommonProps = (nVersion & PF_HANDLE_COMMON_PROPS) != 0;
+ nVersion = nVersion & ~PF_SPECIAL_FLAGS;
+
+ // obsolete
+ _rxInStream->readShort();
+
+ _rxInStream >> m_aDefaultText;
+
+ if (nVersion >= 0x0003)
+ {
+ m_bEmptyIsNull = _rxInStream->readBoolean();
+
+ sal_uInt16 nAnyMask = _rxInStream->readShort();
+ if ((nAnyMask & DEFAULT_LONG) == DEFAULT_LONG)
+ {
+ sal_Int32 nValue = _rxInStream->readLong();
+ m_aDefault <<= nValue;
+ }
+ else if ((nAnyMask & DEFAULT_DOUBLE) == DEFAULT_DOUBLE)
+ {
+ double fValue = _rxInStream->readDouble();
+ m_aDefault <<= fValue;
+ }
+ else if ((nAnyMask & DEFAULT_TIME) == DEFAULT_TIME)
+ {
+ m_aDefault <<= ::tools::Time(_rxInStream->readHyper()).GetUNOTime();
+ }
+ else if ((nAnyMask & DEFAULT_DATE) == DEFAULT_DATE)
+ {
+ m_aDefault <<= ::Date(_rxInStream->readLong()).GetUNODate();
+ }
+
+ if ((nAnyMask & FILTERPROPOSAL) == FILTERPROPOSAL)
+ m_bFilterProposal = true;
+ }
+
+ if (nVersion > 4)
+ readHelpTextCompatibly(_rxInStream);
+
+ if (bHandleCommonProps)
+ readCommonEditProperties(_rxInStream);
+
+ // After reading, display 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 OEditBaseModel::defaultCommonEditProperties()
+{
+ OBoundControlModel::defaultCommonProperties();
+ // no own common properties at the moment
+}
+
+
+void OEditBaseModel::readCommonEditProperties(const Reference<XObjectInputStream>& _rxInStream)
+{
+ sal_Int32 nLen = _rxInStream->readLong();
+
+ Reference<XMarkableStream> xMark(_rxInStream, UNO_QUERY);
+ DBG_ASSERT(xMark.is(), "OBoundControlModel::readCommonProperties : can only work with markable streams !");
+ sal_Int32 nMark = xMark->createMark();
+
+ // read properties common to all OBoundControlModels
+ OBoundControlModel::readCommonProperties(_rxInStream);
+
+ // read properties common to all OEditBaseModels
+
+ // skip the remaining bytes
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nLen);
+ xMark->deleteMark(nMark);
+}
+
+
+void OEditBaseModel::writeCommonEditProperties(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ Reference<XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ DBG_ASSERT(xMark.is(), "OEditBaseModel::writeCommonProperties : can only work with markable streams !");
+ sal_Int32 nMark = xMark->createMark();
+
+ // a placeholder where we will write the overall length (later in this method)
+ sal_Int32 nLen = 0;
+ _rxOutStream->writeLong(nLen);
+
+ // write properties common to all OBoundControlModels
+ OBoundControlModel::writeCommonProperties(_rxOutStream);
+
+ // write properties common to all OEditBaseModels
+
+ // close the block - write the correct length at the beginning
+ nLen = xMark->offsetToMark(nMark) - sizeof(nLen);
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+}
+
+
+void OEditBaseModel::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_EMPTY_IS_NULL:
+ rValue <<= m_bEmptyIsNull;
+ break;
+ case PROPERTY_ID_FILTERPROPOSAL:
+ rValue <<= m_bFilterProposal;
+ break;
+ case PROPERTY_ID_DEFAULT_TEXT:
+ rValue <<= m_aDefaultText;
+ break;
+ case PROPERTY_ID_DEFAULT_VALUE:
+ case PROPERTY_ID_DEFAULT_DATE:
+ case PROPERTY_ID_DEFAULT_TIME:
+ rValue = m_aDefault;
+ break;
+ default:
+ OBoundControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+}
+
+sal_Bool OEditBaseModel::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue,
+ sal_Int32 nHandle, const Any& rValue )
+{
+ bool bModified(false);
+ switch (nHandle)
+ {
+ case PROPERTY_ID_EMPTY_IS_NULL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bEmptyIsNull);
+ break;
+ case PROPERTY_ID_FILTERPROPOSAL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bFilterProposal);
+ break;
+ case PROPERTY_ID_DEFAULT_TEXT:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefaultText);
+ break;
+ case PROPERTY_ID_DEFAULT_VALUE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefault, cppu::UnoType<double>::get());
+ break;
+ case PROPERTY_ID_DEFAULT_DATE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefault, cppu::UnoType<util::Date>::get());
+ break;
+ case PROPERTY_ID_DEFAULT_TIME:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefault, cppu::UnoType<util::Time>::get());
+ break;
+ default:
+ bModified = OBoundControlModel::convertFastPropertyValue(
+ rConvertedValue,
+ rOldValue,
+ nHandle,
+ rValue);
+ }
+ return bModified;
+}
+
+void OEditBaseModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_EMPTY_IS_NULL:
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, "invalid type" );
+ m_bEmptyIsNull = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_FILTERPROPOSAL:
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, "invalid type" );
+ m_bFilterProposal = getBOOL(rValue);
+ break;
+ // Changing the default values causes a reset
+ case PROPERTY_ID_DEFAULT_TEXT:
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "invalid type" );
+ rValue >>= m_aDefaultText;
+ resetNoBroadcast();
+ break;
+ case PROPERTY_ID_DEFAULT_VALUE:
+ case PROPERTY_ID_DEFAULT_DATE:
+ case PROPERTY_ID_DEFAULT_TIME:
+ m_aDefault = rValue;
+ resetNoBroadcast();
+ break;
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue );
+ }
+}
+
+// XPropertyState
+
+Any OEditBaseModel::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_DEFAULT_TEXT:
+ return Any(OUString());
+ case PROPERTY_ID_FILTERPROPOSAL:
+ return Any(false);
+ case PROPERTY_ID_DEFAULT_VALUE:
+ case PROPERTY_ID_DEFAULT_DATE:
+ case PROPERTY_ID_DEFAULT_TIME:
+ return Any();
+ default:
+ return OBoundControlModel::getPropertyDefaultByHandle(nHandle);
+ }
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/EditBase.hxx b/forms/source/component/EditBase.hxx
new file mode 100644
index 0000000000..c74476bc03
--- /dev/null
+++ b/forms/source/component/EditBase.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+
+// persistence flags for use with the version id
+#define PF_HANDLE_COMMON_PROPS 0x8000
+ // Derived classes which use their own persistence methods (read/write) and have an own
+ // version handling therein may want to clear this flag in getPersistenceFlags.
+ // If done so, this class will write a version without a call to writeCommonEditProperties.
+#define PF_FAKE_FORMATTED_FIELD 0x4000
+ // ... hmmm... a fake, as the name suggests. see OFormattedFieldWrapper
+
+#define PF_SPECIAL_FLAGS 0xFF00
+
+
+namespace frm
+{
+
+class OEditBaseModel : public OBoundControlModel
+{
+ sal_Int16 m_nLastReadVersion;
+
+protected:
+// [properties] for all EditingFields
+ css::uno::Any m_aDefault;
+ OUString m_aDefaultText; // default value
+ bool m_bEmptyIsNull : 1; // empty string will be interpreted as NULL when committing
+ bool m_bFilterProposal : 1; // use a list of possible value in filtermode
+// [properties]
+
+ sal_Int16 getLastReadVersion() const { return m_nLastReadVersion; }
+
+public:
+ OEditBaseModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ const OUString& _rUnoControlModelTypeName,
+ const OUString& _rDefault,
+ const bool _bSupportExternalBinding,
+ const bool _bSupportsValidation
+ );
+
+ OEditBaseModel(
+ const OEditBaseModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+
+ virtual ~OEditBaseModel() override;
+
+ // XPersistObject
+ 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;
+
+ // XPropertySet
+ 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 ::cppu::OPropertySetHelper::getFastPropertyValue;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+protected:
+ // new properties common to all edit models should be handled with the following two methods
+ void readCommonEditProperties(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream);
+ void writeCommonEditProperties(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream);
+ void defaultCommonEditProperties();
+
+ virtual sal_uInt16 getPersistenceFlags() const;
+ // derived classes may use this if they want this base class to write additional version flags
+ // (one of the PF_... constants). After ::read they may ask for that flags with getLastReadVersion
+};
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/EventThread.cxx b/forms/source/component/EventThread.cxx
new file mode 100644
index 0000000000..2d5336fe38
--- /dev/null
+++ b/forms/source/component/EventThread.cxx
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "EventThread.hxx"
+#include <comphelper/guarding.hxx>
+#include <tools/debug.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+#include <memory>
+
+namespace frm
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+
+OComponentEventThread::OComponentEventThread( ::cppu::OComponentHelper* pCompImpl ) :
+ m_xComp( pCompImpl )
+{
+
+ osl_atomic_increment(&m_refCount);
+
+ // and add us at the Control
+ {
+ Reference<XEventListener> xEvtLstnr = static_cast<XEventListener*>(this);
+ m_xComp->addEventListener( xEvtLstnr );
+ }
+
+ osl_atomic_decrement(&m_refCount);
+}
+
+OComponentEventThread::~OComponentEventThread()
+{
+
+ DBG_ASSERT( m_aEvents.empty(),
+ "OComponentEventThread::~OComponentEventThread: Didn't call dispose?" );
+
+ impl_clearEventQueue();
+}
+
+Any SAL_CALL OComponentEventThread::queryInterface(const Type& _rType)
+{
+ Any aReturn = OWeakObject::queryInterface(_rType);
+
+ if (!aReturn.hasValue())
+ aReturn = ::cppu::queryInterface(_rType,
+ static_cast<XEventListener*>(this)
+ );
+
+ return aReturn;
+}
+
+void OComponentEventThread::impl_clearEventQueue()
+{
+ m_aEvents.clear();
+ m_aControls.clear();
+ m_aFlags.clear();
+}
+
+void OComponentEventThread::disposing( const EventObject& evt )
+{
+ if( evt.Source != static_cast<XWeak*>(m_xComp.get()) )
+ return;
+
+ std::unique_lock aGuard( m_aMutex );
+
+ // Remove EventListener
+ Reference<XEventListener> xEvtLstnr = static_cast<XEventListener*>(this);
+ m_xComp->removeEventListener( xEvtLstnr );
+
+ // Clear EventQueue
+ impl_clearEventQueue();
+
+ // Free the Control and set pCompImpl to 0,
+ // so that the thread knows, that it should terminate.
+ m_xComp.clear();
+
+ // Wake up the thread and terminate
+ m_aCond.set();
+ terminate();
+}
+
+void OComponentEventThread::addEvent( std::unique_ptr<EventObject> _pEvt )
+{
+ Reference<XControl> xTmp;
+ addEvent( std::move(_pEvt), xTmp );
+}
+
+void OComponentEventThread::addEvent( std::unique_ptr<EventObject> _pEvt,
+ const Reference<XControl>& rControl,
+ bool bFlag )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ // Put data into the queue
+ m_aEvents.push_back( std::move( _pEvt ) );
+
+ Reference<XWeak> xWeakControl(rControl, UNO_QUERY);
+ Reference<XAdapter> xControlAdapter = xWeakControl.is() ? xWeakControl->queryAdapter() : Reference<XAdapter>();
+ m_aControls.push_back( xControlAdapter );
+
+ m_aFlags.push_back( bFlag );
+
+ // Wake up thread
+ m_aCond.set();
+}
+
+void SAL_CALL OComponentEventThread::onTerminated()
+{
+ OComponentEventThread_TBASE::onTerminated();
+
+ release( );
+}
+
+void OComponentEventThread::run()
+{
+ osl_setThreadName("frm::OComponentEventThread");
+
+ acquire( );
+
+ // Hold on to ourselves, so that we're not deleted if a dispose is called at some point in time
+ css::uno::Reference<css::uno::XInterface> xThis(static_cast<XWeak*>(this));
+
+ do
+ {
+ std::unique_lock aGuard(m_aMutex);
+
+ while( !m_aEvents.empty() )
+ {
+ // Get the Control and hold on to it so that it cannot be deleted during actionPerformed
+ rtl::Reference<::cppu::OComponentHelper> xComp = m_xComp;
+
+ ThreadEvents::iterator firstEvent( m_aEvents.begin() );
+ std::unique_ptr<EventObject> pEvt = std::move(*firstEvent);
+ m_aEvents.erase( firstEvent );
+
+ ThreadObjects::iterator firstControl( m_aControls.begin() );
+ Reference<XAdapter> xControlAdapter = *firstControl;
+ m_aControls.erase( firstControl );
+
+ auto firstFlag( m_aFlags.begin() );
+ bool bFlag = *firstFlag;
+ m_aFlags.erase( firstFlag );
+
+ {
+ aGuard.unlock();
+ // Because a queryHardRef can throw an Exception, it should not be called when
+ // the mutex is locked.
+ Reference<XControl> xControl;
+ if ( xControlAdapter.is() )
+ xControl.set(
+ xControlAdapter->queryAdapted(), css::uno::UNO_QUERY);
+
+ if( xComp.is() )
+ processEvent( xComp.get(), pEvt.get(), xControl, bFlag );
+ aGuard.lock();
+ }
+ }
+
+ // After a Dispose, we do not know the Control anymore.
+ // Thus, we must not wait either.
+ if( !m_xComp.is() )
+ return;
+
+ // Reset waiting condition
+ m_aCond.reset();
+ {
+ aGuard.unlock();
+ // And wait ... if, in the meantime, an Event came in after all
+ m_aCond.wait();
+ aGuard.lock();
+ }
+ }
+ while( true );
+}
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/EventThread.hxx b/forms/source/component/EventThread.hxx
new file mode 100644
index 0000000000..df7b33ee13
--- /dev/null
+++ b/forms/source/component/EventThread.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <osl/thread.hxx>
+#include <osl/conditn.hxx>
+#include <cppuhelper/component.hxx>
+#include <comphelper/uno3.hxx>
+#include <rtl/ref.hxx>
+
+
+using namespace comphelper;
+
+
+namespace frm
+{
+
+
+typedef ::osl::Thread OComponentEventThread_TBASE;
+class OComponentEventThread
+ :public OComponentEventThread_TBASE
+ ,public css::lang::XEventListener
+ ,public ::cppu::OWeakObject
+{
+ typedef std::vector<std::unique_ptr<css::lang::EventObject>> ThreadEvents;
+ typedef std::vector< css::uno::Reference< css::uno::XAdapter> > ThreadObjects;
+
+ std::mutex m_aMutex;
+ ::osl::Condition m_aCond; // Queue filled?
+ ThreadEvents m_aEvents; // EventQueue
+ ThreadObjects m_aControls; // Control for Submit
+ std::vector<bool> m_aFlags; // Flags for Submit/Reset
+
+ rtl::Reference<::cppu::OComponentHelper> m_xComp; // Implementation of the Control
+
+protected:
+
+ // XThread
+ virtual void SAL_CALL run() override;
+
+ virtual void SAL_CALL onTerminated() override;
+
+ // Edit an Event:
+ // The mutex is not locked, but pCompImpl stays valid in any case.
+ // pEvt can be a derived type, namely the one that cloneEvent returns.
+ // rControl is only set, if a Control has been passed in addEvent.
+ // Because the Control is only held as a WeakRef, it can disappear in the meantime.
+ virtual void processEvent( ::cppu::OComponentHelper* _pCompImpl,
+ const css::lang::EventObject* _pEvt,
+ const css::uno::Reference< css::awt::XControl>& _rControl,
+ bool _bFlag) = 0;
+
+public:
+
+ // UNO binding
+ DECLARE_UNO3_DEFAULTS(OComponentEventThread, OWeakObject)
+ virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type& _rType) override;
+
+ explicit OComponentEventThread(::cppu::OComponentHelper* pCompImpl);
+ virtual ~OComponentEventThread() override;
+
+ void addEvent( std::unique_ptr<css::lang::EventObject> _pEvt );
+ void addEvent( std::unique_ptr<css::lang::EventObject> _pEvt, const css::uno::Reference< css::awt::XControl>& rControl,
+ bool bFlag = false );
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource ) override;
+
+ // Resolve ambiguity: both OWeakObject and osl::Thread have these memory operators
+ using osl::Thread::operator new;
+ using osl::Thread::operator delete;
+
+private:
+ void impl_clearEventQueue();
+};
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/File.cxx b/forms/source/component/File.cxx
new file mode 100644
index 0000000000..f363d39d7c
--- /dev/null
+++ b/forms/source/component/File.cxx
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "File.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+#include <property.hxx>
+#include <services.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+
+using namespace comphelper;
+
+
+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;
+
+
+Sequence<Type> OFileControlModel::_getTypes()
+{
+ static Sequence<Type> const aTypes =
+ concatSequences(OControlModel::_getTypes(), Sequence<Type>{ cppu::UnoType<XReset>::get() });
+ return aTypes;
+}
+
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> OFileControlModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_FILECONTROL;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_FILECONTROL;
+ return aSupported;
+}
+
+
+OFileControlModel::OFileControlModel(const Reference<XComponentContext>& _rxFactory)
+ :OControlModel(_rxFactory, VCL_CONTROLMODEL_FILECONTROL)
+ ,m_aResetListeners(m_aMutex)
+{
+ m_nClassId = FormComponentType::FILECONTROL;
+}
+
+
+OFileControlModel::OFileControlModel( const OFileControlModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+ ,m_aResetListeners( m_aMutex )
+{
+
+ m_sDefaultValue = _pOriginal->m_sDefaultValue;
+}
+
+
+OFileControlModel::~OFileControlModel()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OFileControlModel::createClone()
+{
+ rtl::Reference<OFileControlModel> pClone = new OFileControlModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+Any SAL_CALL OFileControlModel::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OControlModel::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = ::cppu::queryInterface(_rType
+ ,static_cast<XReset*>(this)
+ );
+
+ return aReturn;
+}
+
+// OComponentHelper
+
+void OFileControlModel::disposing()
+{
+ OControlModel::disposing();
+
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aResetListeners.disposeAndClear(aEvt);
+}
+
+
+Any OFileControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+{
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_TEXT:
+ return Any( OUString() );
+ }
+ return OControlModel::getPropertyDefaultByHandle( _nHandle );
+}
+
+
+void OFileControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_DEFAULT_TEXT : rValue <<= m_sDefaultValue; break;
+ default:
+ OControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+}
+
+
+void OFileControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_DEFAULT_TEXT :
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "OFileControlModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_sDefaultValue;
+ break;
+ default:
+ OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue);
+ }
+}
+
+
+sal_Bool OFileControlModel::convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_DEFAULT_TEXT :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sDefaultValue);
+ default:
+ return OControlModel::convertFastPropertyValue(rConvertedValue, rOldValue, nHandle, rValue);
+ }
+}
+
+
+void OFileControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 2);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL OFileControlModel::getServiceName()
+{
+ return FRM_COMPONENT_FILECONTROL; // old (non-sun) name for compatibility !
+}
+
+
+void OFileControlModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // Version
+ _rxOutStream->writeShort(0x0002);
+ // Default value
+ _rxOutStream << m_sDefaultValue;
+ writeHelpTextCompatibly(_rxOutStream);
+}
+
+
+void OFileControlModel::read(const Reference<css::io::XObjectInputStream>& _rxInStream)
+{
+ OControlModel::read(_rxInStream);
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ // Default value
+ switch (nVersion)
+ {
+ case 1:
+ _rxInStream >> m_sDefaultValue; break;
+ case 2:
+ _rxInStream >> m_sDefaultValue;
+ readHelpTextCompatibly(_rxInStream);
+ break;
+ default:
+ OSL_FAIL("OFileControlModel::read : unknown version !");
+ m_sDefaultValue.clear();
+ }
+
+ // Display default values after read
+// _reset();
+}
+
+
+void SAL_CALL OFileControlModel::reset()
+{
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aResetListeners);
+ EventObject aEvt(static_cast<XWeak*>(this));
+ bool bContinue = true;
+ while (aIter.hasMoreElements() && bContinue)
+ bContinue = aIter.next()->approveReset(aEvt);
+
+ if (bContinue)
+ {
+ // don't lock our mutex as setting aggregate properties
+ // may cause any uno controls belonging to us to lock the solar mutex, which is potentially dangerous with
+ // our own mutex locked
+ m_xAggregateSet->setPropertyValue(PROPERTY_TEXT, Any(m_sDefaultValue));
+ m_aResetListeners.notifyEach( &XResetListener::resetted, aEvt );
+ }
+}
+
+
+void OFileControlModel::addResetListener(const Reference<XResetListener>& _rxListener)
+{
+ m_aResetListeners.addInterface(_rxListener);
+}
+
+
+void OFileControlModel::removeResetListener(const Reference<XResetListener>& _rxListener)
+{
+ m_aResetListeners.removeInterface(_rxListener);
+}
+
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFileControlModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFileControlModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/File.hxx b/forms/source/component/File.hxx
new file mode 100644
index 0000000000..6ef7532aa8
--- /dev/null
+++ b/forms/source/component/File.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+
+
+namespace frm
+{
+
+class OFileControlModel
+ :public OControlModel
+ ,public css::form::XReset
+{
+ ::comphelper::OInterfaceContainerHelper3<css::form::XResetListener> m_aResetListeners;
+ OUString m_sDefaultValue;
+
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ OFileControlModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OFileControlModel(
+ const OFileControlModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OFileControlModel() override;
+
+ DECLARE_UNO3_AGG_DEFAULTS(OFileControlModel, OControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OFileControlModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() 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;
+
+ // 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>& _rxListener) override;
+ virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OControlModel::disposing;
+ using OControlModel::getFastPropertyValue;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Filter.cxx b/forms/source/component/Filter.cxx
new file mode 100644
index 0000000000..295d4af747
--- /dev/null
+++ b/forms/source/component/Filter.cxx
@@ -0,0 +1,882 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include "Filter.hxx"
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include <frm_strings.hxx>
+
+#include <com/sun/star/awt/VclWindowPeerAttribute.hpp>
+#include <com/sun/star/awt/XCheckBox.hpp>
+#include <com/sun/star/awt/XComboBox.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/awt/XRadioButton.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/sdb/ErrorMessageDialog.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/awt/XItemList.hpp>
+
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/formattedcolumnvalue.hxx>
+#include <connectivity/predicateinput.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/gen.hxx>
+
+
+namespace frm
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::ui::dialogs;
+
+ using namespace ::connectivity;
+
+ OFilterControl::OFilterControl( const Reference< XComponentContext >& _rxORB )
+ :m_aTextListeners( *this )
+ ,m_xContext( _rxORB )
+ ,m_nControlClass( FormComponentType::TEXTFIELD )
+ ,m_bFilterList( false )
+ ,m_bMultiLine( false )
+ ,m_bFilterListFilled( false )
+ {
+ }
+
+
+ bool OFilterControl::ensureInitialized( )
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if ( !m_xField.is() )
+ {
+ OSL_FAIL( "OFilterControl::ensureInitialized: improperly initialized: no field!" );
+ return false;
+ }
+
+ if ( !m_xConnection.is() )
+ {
+ OSL_FAIL( "OFilterControl::ensureInitialized: improperly initialized: no connection!" );
+ return false;
+ }
+
+ if ( !m_xFormatter.is() )
+ {
+ // we can create one from the connection, if it's an SDB connection
+
+ Reference< XNumberFormatsSupplier > xFormatSupplier = ::dbtools::getNumberFormats( m_xConnection, true, m_xContext );
+
+ if ( xFormatSupplier.is() )
+ {
+ m_xFormatter.set(NumberFormatter::create(m_xContext), UNO_QUERY_THROW );
+ m_xFormatter->attachNumberFormatsSupplier( xFormatSupplier );
+ }
+ }
+ if ( !m_xFormatter.is() )
+ {
+ OSL_FAIL( "OFilterControl::ensureInitialized: no number formatter!" );
+ // no fallback anymore
+ return false;
+ }
+#endif
+ return true;
+ }
+
+
+ Any SAL_CALL OFilterControl::queryAggregation( const Type & rType )
+ {
+ Any aRet = UnoControl::queryAggregation( rType);
+ if(!aRet.hasValue())
+ aRet = OFilterControl_BASE::queryInterface(rType);
+
+ return aRet;
+ }
+
+
+ OUString OFilterControl::GetComponentServiceName() const
+ {
+ OUString aServiceName;
+ switch (m_nControlClass)
+ {
+ case FormComponentType::RADIOBUTTON:
+ aServiceName = "radiobutton";
+ break;
+ case FormComponentType::CHECKBOX:
+ aServiceName = "checkbox";
+ break;
+ case FormComponentType::COMBOBOX:
+ aServiceName = "combobox";
+ break;
+ case FormComponentType::LISTBOX:
+ aServiceName = "listbox";
+ break;
+ default:
+ if (m_bMultiLine)
+ aServiceName = "MultiLineEdit";
+ else
+ aServiceName = "Edit";
+ }
+ return aServiceName;
+ }
+
+ // XComponent
+
+ void OFilterControl::dispose()
+ {
+ EventObject aEvt(*this);
+ m_aTextListeners.disposeAndClear( aEvt );
+ UnoControl::dispose();
+ }
+
+
+ void OFilterControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer )
+ {
+ UnoControl::createPeer( rxToolkit, rParentPeer );
+
+ try
+ {
+ Reference< XVclWindowPeer > xVclWindow( getPeer(), UNO_QUERY_THROW );
+ switch ( m_nControlClass )
+ {
+ case FormComponentType::CHECKBOX:
+ {
+ // checkboxes always have a tristate-mode
+ xVclWindow->setProperty( PROPERTY_TRISTATE, Any( true ) );
+ xVclWindow->setProperty( PROPERTY_STATE, Any( sal_Int32( TRISTATE_INDET ) ) );
+
+ Reference< XCheckBox > xBox( getPeer(), UNO_QUERY_THROW );
+ xBox->addItemListener( this );
+
+ }
+ break;
+
+ case FormComponentType::RADIOBUTTON:
+ {
+ xVclWindow->setProperty( PROPERTY_STATE, Any( sal_Int32( TRISTATE_FALSE ) ) );
+
+ Reference< XRadioButton > xRadio( getPeer(), UNO_QUERY_THROW );
+ xRadio->addItemListener( this );
+ }
+ break;
+
+ case FormComponentType::LISTBOX:
+ {
+ Reference< XListBox > xListBox( getPeer(), UNO_QUERY_THROW );
+ xListBox->addItemListener( this );
+ [[fallthrough]];
+ }
+
+ case FormComponentType::COMBOBOX:
+ {
+ xVclWindow->setProperty(PROPERTY_AUTOCOMPLETE, Any( true ) );
+ [[fallthrough]];
+ }
+
+ default:
+ {
+ Reference< XWindow > xWindow( getPeer(), UNO_QUERY );
+ xWindow->addFocusListener( this );
+
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ xText->setMaxTextLen(0);
+ }
+ break;
+ }
+
+ // filter controls are _never_ readonly
+ Reference< XPropertySet > xModel( getModel(), UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
+ if ( xModelPSI->hasPropertyByName( PROPERTY_READONLY ) )
+ xVclWindow->setProperty( PROPERTY_READONLY, Any( false ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ if (m_bFilterList)
+ m_bFilterListFilled = false;
+ }
+
+
+ void OFilterControl::PrepareWindowDescriptor( WindowDescriptor& rDescr )
+ {
+ if (m_bFilterList)
+ rDescr.WindowAttributes |= VclWindowPeerAttribute::DROPDOWN;
+ }
+
+
+ void OFilterControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal )
+ {
+ // these properties are ignored
+ if (rPropName == PROPERTY_TEXT ||
+ rPropName == PROPERTY_STATE)
+ return;
+
+ UnoControl::ImplSetPeerProperty( rPropName, rVal );
+ }
+
+ // XEventListener
+
+ void SAL_CALL OFilterControl::disposing(const EventObject& Source)
+ {
+ UnoControl::disposing(Source);
+ }
+
+ // XItemListener
+
+ void SAL_CALL OFilterControl::itemStateChanged( const ItemEvent& rEvent )
+ {
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rEvent;
+#else
+ OUStringBuffer aText;
+ switch (m_nControlClass)
+ {
+ case FormComponentType::CHECKBOX:
+ {
+ if ( ( rEvent.Selected == TRISTATE_TRUE ) || ( rEvent.Selected == TRISTATE_FALSE ) )
+ {
+ sal_Int32 nBooleanComparisonMode = ::dbtools::DatabaseMetaData( m_xConnection ).getBooleanComparisonMode();
+
+ bool bSelected = ( rEvent.Selected == TRISTATE_TRUE );
+
+ OUString sExpressionMarker( "$expression$" );
+ ::dbtools::getBooleanComparisonPredicate(
+ sExpressionMarker,
+ bSelected,
+ nBooleanComparisonMode,
+ aText
+ );
+
+ OUString sText( aText.makeStringAndClear() );
+ sal_Int32 nMarkerPos( sText.indexOf( sExpressionMarker ) );
+ OSL_ENSURE( nMarkerPos == 0, "OFilterControl::itemStateChanged: unsupported boolean comparison mode!" );
+ // If this assertion fails, then getBooleanComparisonPredicate created a predicate which
+ // does not start with the expression we gave it. The only known case is when
+ // the comparison mode is ACCESS_COMPAT, and the value is TRUE. In this case,
+ // the expression is rather complex.
+ // Well, so this is a known issue - the filter controls (and thus the form based filter)
+ // do not work with boolean MS Access fields.
+ // To fix this, we would probably have to revert here to always return "1" or "0" as normalized
+ // filter, and change our client code to properly translate this (which could be some effort).
+ if ( nMarkerPos == 0 )
+ aText.append( sText.subView(sExpressionMarker.getLength()) );
+ else
+ {
+ // fallback
+ aText.appendAscii( bSelected ? "1" : "0" );
+ }
+ }
+ }
+ break;
+
+ case FormComponentType::LISTBOX:
+ {
+ try
+ {
+ const Reference< XItemList > xItemList( getModel(), UNO_QUERY_THROW );
+ OUString sItemText( xItemList->getItemText( rEvent.Selected ) );
+
+ const MapString2String::const_iterator itemPos = m_aDisplayItemToValueItem.find( sItemText );
+ if ( itemPos != m_aDisplayItemToValueItem.end() )
+ {
+ sItemText = itemPos->second;
+ if ( !sItemText.isEmpty() )
+ {
+ ::dbtools::OPredicateInputController aPredicateInput( m_xContext, m_xConnection, getParseContext() );
+ OUString sErrorMessage;
+ OSL_VERIFY( aPredicateInput.normalizePredicateString( sItemText, m_xField, &sErrorMessage ) );
+ }
+ }
+ aText.append( sItemText );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ break;
+
+ case FormComponentType::RADIOBUTTON:
+ {
+ if ( rEvent.Selected == TRISTATE_TRUE )
+ aText.append( ::comphelper::getString( Reference< XPropertySet >( getModel(), UNO_QUERY_THROW )->getPropertyValue( PROPERTY_REFVALUE ) ) );
+ }
+ break;
+ }
+
+ OUString sText( aText.makeStringAndClear() );
+ if ( m_aText != sText )
+ {
+ m_aText = sText;
+ TextEvent aEvt;
+ aEvt.Source = *this;
+ m_aTextListeners.notifyEach(&css::awt::XTextListener::textChanged, aEvt);
+ }
+#endif
+ }
+
+
+ void OFilterControl::implInitFilterList()
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if ( !ensureInitialized( ) )
+ // already asserted in ensureInitialized
+ return;
+
+ // ensure the cursor and the statement are disposed as soon as we leave
+ ::utl::SharedUNOComponent< XResultSet > xListCursor;
+ ::utl::SharedUNOComponent< XStatement > xStatement;
+
+ try
+ {
+ m_bFilterListFilled = true;
+
+ if ( !m_xField.is() )
+ return;
+
+ OUString sFieldName;
+ m_xField->getPropertyValue( PROPERTY_NAME ) >>= sFieldName;
+
+ // here we need a table to which the field belongs to
+ const Reference< XChild > xModelAsChild( getModel(), UNO_QUERY_THROW );
+ const Reference< XRowSet > xForm( xModelAsChild->getParent(), UNO_QUERY_THROW );
+ const Reference< XPropertySet > xFormProps( xForm, UNO_QUERY_THROW );
+
+ // create a query composer
+ Reference< XColumnsSupplier > xSuppColumns;
+ xFormProps->getPropertyValue("SingleSelectQueryComposer") >>= xSuppColumns;
+
+ const Reference< XConnection > xConnection( ::dbtools::getConnection( xForm ), UNO_SET_THROW );
+ const Reference< XNameAccess > xFieldNames( xSuppColumns->getColumns(), UNO_SET_THROW );
+ if ( !xFieldNames->hasByName( sFieldName ) )
+ return;
+ OUString sRealFieldName, sTableName;
+ const Reference< XPropertySet > xComposerFieldProps( xFieldNames->getByName( sFieldName ), UNO_QUERY_THROW );
+ xComposerFieldProps->getPropertyValue( PROPERTY_REALNAME ) >>= sRealFieldName;
+ xComposerFieldProps->getPropertyValue( PROPERTY_TABLENAME ) >>= sTableName;
+
+ // obtain the table of the field
+ const Reference< XTablesSupplier > xSuppTables( xSuppColumns, UNO_QUERY_THROW );
+ const Reference< XNameAccess > xTablesNames( xSuppTables->getTables(), UNO_SET_THROW );
+ const Reference< XNamed > xNamedTable( xTablesNames->getByName( sTableName ), UNO_QUERY_THROW );
+ sTableName = xNamedTable->getName();
+
+ // create a statement selecting all values for the given field
+ OUStringBuffer aStatement;
+
+ const Reference< XDatabaseMetaData > xMeta( xConnection->getMetaData(), UNO_SET_THROW );
+ const OUString sQuoteChar = xMeta->getIdentifierQuoteString();
+
+ aStatement.append(
+ "SELECT DISTINCT "
+ + sQuoteChar
+ + sRealFieldName
+ + sQuoteChar );
+
+ // if the field had an alias in our form's statement, give it this alias in the new statement, too
+ if ( !sFieldName.isEmpty() && ( sFieldName != sRealFieldName ) )
+ {
+ aStatement.append(" AS "+ sQuoteChar + sFieldName + sQuoteChar );
+ }
+
+ aStatement.append( " FROM " );
+
+ OUString sCatalog, sSchema, sTable;
+ ::dbtools::qualifiedNameComponents( xMeta, sTableName, sCatalog, sSchema, sTable, ::dbtools::EComposeRule::InDataManipulation );
+ aStatement.append( ::dbtools::composeTableNameForSelect( xConnection, sCatalog, sSchema, sTable ) );
+
+ // execute the statement
+ xStatement.reset( xConnection->createStatement() );
+ const OUString sSelectStatement( aStatement.makeStringAndClear( ) );
+ xListCursor.reset( xStatement->executeQuery( sSelectStatement ) );
+
+ // retrieve the one column which we take the values from
+ const Reference< XColumnsSupplier > xSupplyCols( xListCursor, UNO_QUERY_THROW );
+ const Reference< XIndexAccess > xFields( xSupplyCols->getColumns(), UNO_QUERY_THROW );
+ const Reference< XPropertySet > xDataField( xFields->getByIndex(0), UNO_QUERY_THROW );
+
+ // ensure the values will be formatted according to the field format
+ const ::dbtools::FormattedColumnValue aFormatter( m_xFormatter, xDataField );
+
+ ::std::vector< OUString > aProposals;
+ aProposals.reserve(16);
+
+ while ( xListCursor->next() && ( aProposals.size() < o3tl::make_unsigned( SHRT_MAX ) ) )
+ {
+ const OUString sCurrentValue = aFormatter.getFormattedValue();
+ aProposals.push_back( sCurrentValue );
+ }
+
+ // fill the list items into our peer
+ Sequence< OUString> aStringSeq( comphelper::containerToSequence(aProposals) );
+
+ const Reference< XComboBox > xComboBox( getPeer(), UNO_QUERY_THROW );
+ xComboBox->addItems( aStringSeq, 0 );
+
+ // set the drop down line count to something reasonable
+ const sal_Int16 nLineCount = ::std::min( sal_Int16( 16 ), sal_Int16( aStringSeq.getLength() ) );
+ xComboBox->setDropDownLineCount( nLineCount );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+#endif
+ }
+
+ // XFocusListener
+
+ void SAL_CALL OFilterControl::focusGained(const FocusEvent& /*e*/)
+ {
+ // should we fill the combobox?
+ if (m_bFilterList && !m_bFilterListFilled)
+ implInitFilterList();
+ }
+
+
+ void SAL_CALL OFilterControl::focusLost(const FocusEvent& /*e*/)
+ {
+ }
+
+
+ sal_Bool SAL_CALL OFilterControl::commit()
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if ( !ensureInitialized( ) )
+ // already asserted in ensureInitialized
+ return true;
+
+ OUString aText;
+ switch (m_nControlClass)
+ {
+ case FormComponentType::TEXTFIELD:
+ case FormComponentType::COMBOBOX:
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ aText = xText->getText();
+ } break;
+ default:
+ return true;
+ }
+ if ( m_aText == aText )
+ return true;
+ // check the text with the SQL-Parser
+ OUString aNewText = aText.trim();
+ if ( !aNewText.isEmpty() )
+ {
+ ::dbtools::OPredicateInputController aPredicateInput( m_xContext, m_xConnection, getParseContext() );
+ OUString sErrorMessage;
+ if ( !aPredicateInput.normalizePredicateString( aNewText, m_xField, &sErrorMessage ) )
+ {
+ // display the error and outta here
+ SQLContext aError(ResourceManager::loadString(RID_STR_SYNTAXERROR), {}, {}, 0, {}, sErrorMessage);
+ displayException( aError );
+ return false;
+ }
+ }
+
+ setText(aNewText);
+ TextEvent aEvt;
+ aEvt.Source = *this;
+ m_aTextListeners.notifyEach(&css::awt::XTextListener::textChanged, aEvt);
+#endif
+ return true;
+ }
+
+ // XTextComponent
+
+ void SAL_CALL OFilterControl::addTextListener(const Reference< XTextListener > & l)
+ {
+ m_aTextListeners.addInterface( l );
+ }
+
+
+ void SAL_CALL OFilterControl::removeTextListener(const Reference< XTextListener > & l)
+ {
+ m_aTextListeners.removeInterface( l );
+ }
+
+
+ void SAL_CALL OFilterControl::setText( const OUString& aText )
+ {
+ if ( !ensureInitialized( ) )
+ // already asserted in ensureInitialized
+ return;
+
+ switch (m_nControlClass)
+ {
+ case FormComponentType::CHECKBOX:
+ {
+ Reference< XVclWindowPeer > xVclWindow( getPeer(), UNO_QUERY );
+ if (xVclWindow.is())
+ {
+ Any aValue;
+ if ( aText == "1"
+ || aText.equalsIgnoreAsciiCase("TRUE")
+ || aText.equalsIgnoreAsciiCase("IS TRUE")
+ )
+ {
+ aValue <<= sal_Int32(TRISTATE_TRUE);
+ }
+ else if ( aText == "0" || aText.equalsIgnoreAsciiCase("FALSE") )
+ {
+ aValue <<= sal_Int32(TRISTATE_FALSE);
+ }
+ else
+ aValue <<= sal_Int32(TRISTATE_INDET);
+
+ m_aText = aText;
+ xVclWindow->setProperty( PROPERTY_STATE, aValue );
+ }
+ } break;
+ case FormComponentType::RADIOBUTTON:
+ {
+ Reference< XVclWindowPeer > xVclWindow( getPeer(), UNO_QUERY );
+ if (xVclWindow.is())
+ {
+ OUString aRefText = ::comphelper::getString(css::uno::Reference< XPropertySet > (getModel(), UNO_QUERY_THROW)->getPropertyValue(PROPERTY_REFVALUE));
+ Any aValue;
+ if (aText == aRefText)
+ aValue <<= sal_Int32(TRISTATE_TRUE);
+ else
+ aValue <<= sal_Int32(TRISTATE_FALSE);
+ m_aText = aText;
+ xVclWindow->setProperty(PROPERTY_STATE, aValue);
+ }
+ } break;
+ case FormComponentType::LISTBOX:
+ {
+ Reference< XListBox > xListBox( getPeer(), UNO_QUERY );
+ if (xListBox.is())
+ {
+ m_aText = aText;
+ MapString2String::const_iterator itemPos = m_aDisplayItemToValueItem.find( m_aText );
+ if ( itemPos == m_aDisplayItemToValueItem.end() )
+ {
+ const bool isQuoted = ( m_aText.getLength() > 1 )
+ && ( m_aText[0] == '\'' )
+ && ( m_aText[ m_aText.getLength() - 1 ] == '\'' );
+ if ( isQuoted )
+ {
+ m_aText = m_aText.copy( 1, m_aText.getLength() - 2 );
+ itemPos = m_aDisplayItemToValueItem.find( m_aText );
+ }
+ }
+
+ OSL_ENSURE( ( itemPos != m_aDisplayItemToValueItem.end() ) || m_aText.isEmpty(),
+ "OFilterControl::setText: this text is not in my display list!" );
+ if ( itemPos == m_aDisplayItemToValueItem.end() )
+ m_aText.clear();
+
+ if ( m_aText.isEmpty() )
+ {
+ while ( xListBox->getSelectedItemPos() >= 0 )
+ {
+ xListBox->selectItemPos( xListBox->getSelectedItemPos(), false );
+ }
+ }
+ else
+ {
+ xListBox->selectItem( m_aText, true );
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ {
+ m_aText = aText;
+ xText->setText(aText);
+ }
+ }
+ }
+ }
+
+
+ void SAL_CALL OFilterControl::insertText( const css::awt::Selection& rSel, const OUString& aText )
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ {
+ xText->insertText(rSel, aText);
+ m_aText = xText->getText();
+ }
+ }
+
+
+ OUString SAL_CALL OFilterControl::getText()
+ {
+ return m_aText;
+ }
+
+
+ OUString SAL_CALL OFilterControl::getSelectedText()
+ {
+ OUString aSelected;
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ aSelected = xText->getSelectedText();
+
+ return aSelected;
+ }
+
+
+ void SAL_CALL OFilterControl::setSelection( const css::awt::Selection& aSelection )
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ xText->setSelection( aSelection );
+ }
+
+
+ css::awt::Selection SAL_CALL OFilterControl::getSelection()
+ {
+ css::awt::Selection aSel;
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ aSel = xText->getSelection();
+ return aSel;
+ }
+
+
+ sal_Bool SAL_CALL OFilterControl::isEditable()
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ return xText.is() && xText->isEditable();
+ }
+
+
+ void SAL_CALL OFilterControl::setEditable( sal_Bool bEditable )
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ xText->setEditable(bEditable);
+ }
+
+
+ sal_Int16 SAL_CALL OFilterControl::getMaxTextLen()
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ return xText.is() ? xText->getMaxTextLen() : 0;
+ }
+
+
+ void SAL_CALL OFilterControl::setMaxTextLen( sal_Int16 nLength )
+ {
+ Reference< XTextComponent > xText( getPeer(), UNO_QUERY );
+ if (xText.is())
+ xText->setMaxTextLen(nLength);
+ }
+
+
+ void OFilterControl::displayException( const css::sdb::SQLContext& _rExcept )
+ {
+ try
+ {
+ Reference< XExecutableDialog > xErrorDialog = ErrorMessageDialog::create( m_xContext, "", m_xMessageParent, Any(_rExcept));
+ xErrorDialog->execute();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+
+
+ void SAL_CALL OFilterControl::initialize( const Sequence< Any >& aArguments )
+ {
+ const Any* pArguments = aArguments.getConstArray();
+ const Any* pArgumentsEnd = pArguments + aArguments.getLength();
+
+ PropertyValue aProp;
+ NamedValue aValue;
+ const OUString* pName = nullptr;
+ const Any* pValue = nullptr;
+ Reference< XPropertySet > xControlModel;
+
+ if (aArguments.getLength() == 3
+ && (aArguments[0] >>= m_xMessageParent)
+ && (aArguments[1] >>= m_xFormatter)
+ && (aArguments[2] >>= xControlModel))
+ {
+ initControlModel(xControlModel);
+ }
+ else for ( ; pArguments != pArgumentsEnd; ++pArguments )
+ {
+ // we recognize PropertyValues and NamedValues
+ if ( *pArguments >>= aProp )
+ {
+ pName = &aProp.Name;
+ pValue = &aProp.Value;
+ }
+ else if ( *pArguments >>= aValue )
+ {
+ pName = &aValue.Name;
+ pValue = &aValue.Value;
+ }
+ else
+ {
+ OSL_FAIL( "OFilterControl::initialize: unrecognized argument!" );
+ continue;
+ }
+
+ if ( *pName == "MessageParent" )
+ {
+ // the message parent
+ *pValue >>= m_xMessageParent;
+ OSL_ENSURE( m_xMessageParent.is(), "OFilterControl::initialize: invalid MessageParent!" );
+ }
+ else if ( *pName == "NumberFormatter" )
+ {
+ // the number format. This argument is optional.
+ *pValue >>= m_xFormatter;
+ OSL_ENSURE( m_xFormatter.is(), "OFilterControl::initialize: invalid NumberFormatter!" );
+ }
+ else if ( *pName == "ControlModel" )
+ {
+ // the control model for which we act as filter control
+ if ( !(*pValue >>= xControlModel ) )
+ {
+ OSL_FAIL( "OFilterControl::initialize: invalid control model argument!" );
+ continue;
+ }
+ initControlModel(xControlModel);
+ }
+ }
+ }
+
+ void OFilterControl::initControlModel(Reference< XPropertySet > const & xControlModel)
+ {
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) xControlModel;
+#else
+ if ( !xControlModel.is() )
+ {
+ OSL_FAIL( "OFilterControl::initialize: invalid control model argument!" );
+ return;
+ }
+ // some properties which are "derived" from the control model we're working for
+
+ // the field
+ m_xField.clear();
+ OSL_ENSURE( ::comphelper::hasProperty( PROPERTY_BOUNDFIELD, xControlModel ), "OFilterControl::initialize: control model needs a bound field property!" );
+ xControlModel->getPropertyValue( PROPERTY_BOUNDFIELD ) >>= m_xField;
+
+
+ // filter list and control class
+ m_bFilterList = ::comphelper::hasProperty( PROPERTY_FILTERPROPOSAL, xControlModel ) && ::comphelper::getBOOL( xControlModel->getPropertyValue( PROPERTY_FILTERPROPOSAL ) );
+ if ( m_bFilterList )
+ m_nControlClass = FormComponentType::COMBOBOX;
+ else
+ {
+ sal_Int16 nClassId = ::comphelper::getINT16( xControlModel->getPropertyValue( PROPERTY_CLASSID ) );
+ switch (nClassId)
+ {
+ case FormComponentType::CHECKBOX:
+ case FormComponentType::RADIOBUTTON:
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ m_nControlClass = nClassId;
+ if ( FormComponentType::LISTBOX == nClassId )
+ {
+ Sequence< OUString > aDisplayItems;
+ OSL_VERIFY( xControlModel->getPropertyValue( PROPERTY_STRINGITEMLIST ) >>= aDisplayItems );
+ Sequence< OUString > aValueItems;
+ OSL_VERIFY( xControlModel->getPropertyValue( PROPERTY_VALUE_SEQ ) >>= aValueItems );
+ OSL_ENSURE( aDisplayItems.getLength() == aValueItems.getLength(), "OFilterControl::initialize: inconsistent item lists!" );
+ for ( sal_Int32 i=0; i < ::std::min( aDisplayItems.getLength(), aValueItems.getLength() ); ++i )
+ m_aDisplayItemToValueItem[ aDisplayItems[i] ] = aValueItems[i];
+ }
+ break;
+ default:
+ m_bMultiLine = ::comphelper::hasProperty( PROPERTY_MULTILINE, xControlModel ) && ::comphelper::getBOOL( xControlModel->getPropertyValue( PROPERTY_MULTILINE ) );
+ m_nControlClass = FormComponentType::TEXTFIELD;
+ break;
+ }
+ }
+
+
+ // the connection meta data for the form which we're working for
+ Reference< XChild > xModel( xControlModel, UNO_QUERY );
+ Reference< XRowSet > xForm;
+ if ( xModel.is() )
+ xForm.set(xModel->getParent(), css::uno::UNO_QUERY);
+ m_xConnection = ::dbtools::getConnection( xForm );
+ OSL_ENSURE( m_xConnection.is(), "OFilterControl::initialize: unable to determine the form's connection!" );
+#endif
+ }
+
+ OUString SAL_CALL OFilterControl::getImplementationName( )
+ {
+ return "com.sun.star.comp.forms.OFilterControl";
+ }
+
+ sal_Bool SAL_CALL OFilterControl::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL OFilterControl::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.control.FilterControl",
+ "com.sun.star.awt.UnoControl" };
+ }
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_OFilterControl_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFilterControl(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Filter.hxx b/forms/source/component/Filter.hxx
new file mode 100644
index 0000000000..8815f5f11a
--- /dev/null
+++ b/forms/source/component/Filter.hxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/form/XBoundComponent.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XNumberFormatter.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <toolkit/controls/unocontrol.hxx>
+
+#include <toolkit/helper/listenermultiplexer.hxx>
+#include <cppuhelper/implbase5.hxx>
+#include <comphelper/uno3.hxx>
+#include <svx/ParseContext.hxx>
+
+#include <unordered_map>
+
+
+namespace frm
+{
+
+
+ // OFilterControl
+
+ typedef ::cppu::ImplHelper5 < css::awt::XTextComponent
+ , css::awt::XFocusListener
+ , css::awt::XItemListener
+ , css::form::XBoundComponent
+ , css::lang::XInitialization
+ > OFilterControl_BASE;
+
+ class OFilterControl final :public UnoControl
+ ,public OFilterControl_BASE
+ ,public ::svxform::OParseContextClient
+ {
+ TextListenerMultiplexer m_aTextListeners;
+
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::beans::XPropertySet > m_xField;
+ css::uno::Reference< css::util::XNumberFormatter > m_xFormatter;
+ css::uno::Reference< css::sdbc::XConnection > m_xConnection;
+ css::uno::Reference< css::awt::XWindow > m_xMessageParent;
+
+ typedef std::unordered_map< OUString, OUString > MapString2String;
+ MapString2String m_aDisplayItemToValueItem;
+
+ OUString m_aText;
+ sal_Int16 m_nControlClass; // which kind of control do we use?
+ bool m_bFilterList : 1;
+ bool m_bMultiLine : 1;
+ bool m_bFilterListFilled : 1;
+
+ void implInitFilterList();
+ void initControlModel(css::uno::Reference< css::beans::XPropertySet > const & xControlModel);
+
+ public:
+ explicit OFilterControl( const css::uno::Reference< css::uno::XComponentContext >& _rxORB );
+
+ DECLARE_UNO3_AGG_DEFAULTS(OFilterControl,OWeakAggObject)
+ css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override;
+
+ virtual OUString GetComponentServiceName() const override;
+ virtual void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit > & rxToolkit, const css::uno::Reference< css::awt::XWindowPeer > & rParentPeer ) override;
+
+ // css::lang::XComponent
+ virtual void SAL_CALL dispose() override;
+
+ // css::awt::XTextComponent
+ virtual void SAL_CALL addTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) override;
+ virtual void SAL_CALL removeTextListener( const css::uno::Reference< css::awt::XTextListener > & l ) override;
+ virtual void SAL_CALL setText( const OUString& aText ) override;
+ virtual void SAL_CALL insertText( const css::awt::Selection& rSel, const OUString& aText ) override;
+ virtual OUString SAL_CALL getText() override;
+ virtual OUString SAL_CALL getSelectedText() override;
+ virtual void SAL_CALL setSelection( const css::awt::Selection& aSelection ) override;
+ virtual css::awt::Selection SAL_CALL getSelection() override;
+ virtual sal_Bool SAL_CALL isEditable() override;
+ virtual void SAL_CALL setEditable( sal_Bool bEditable ) override;
+ virtual void SAL_CALL setMaxTextLen( sal_Int16 nLength ) override;
+ virtual sal_Int16 SAL_CALL getMaxTextLen() override;
+
+ // css::form::XBoundComponent
+ virtual void SAL_CALL addUpdateListener(const css::uno::Reference< css::form::XUpdateListener > & /*l*/) override {}
+ virtual void SAL_CALL removeUpdateListener(const css::uno::Reference< css::form::XUpdateListener > & /*l*/) override {}
+ virtual sal_Bool SAL_CALL commit() override;
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+ // css::awt::XFocusListener
+ virtual void SAL_CALL focusGained(const css::awt::FocusEvent& e) override;
+ virtual void SAL_CALL focusLost(const css::awt::FocusEvent& e) override;
+
+ // css::awt::XItemListener
+ virtual void SAL_CALL itemStateChanged(const css::awt::ItemEvent& rEvent) override;
+
+ // css::util::XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ private:
+ virtual void PrepareWindowDescriptor( css::awt::WindowDescriptor& rDesc ) override;
+ virtual void ImplSetPeerProperty( const OUString& rPropName, const css::uno::Any& rVal ) override;
+
+ bool ensureInitialized( );
+
+ void displayException( const css::sdb::SQLContext& _rExcept );
+ };
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FixedText.cxx b/forms/source/component/FixedText.cxx
new file mode 100644
index 0000000000..e68b09fed0
--- /dev/null
+++ b/forms/source/component/FixedText.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "FixedText.hxx"
+#include <frm_strings.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/form/FormComponentType.hpp>
+
+#include <comphelper/property.hxx>
+
+
+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 comphelper;
+
+OFixedTextModel::OFixedTextModel( const Reference<XComponentContext>& _rxFactory )
+ :OControlModel(_rxFactory, VCL_CONTROLMODEL_FIXEDTEXT)
+
+{
+ m_nClassId = FormComponentType::FIXEDTEXT;
+}
+
+
+OFixedTextModel::OFixedTextModel( const OFixedTextModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+
+{
+}
+
+
+OFixedTextModel::~OFixedTextModel( )
+{
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OFixedTextModel::createClone()
+{
+ rtl::Reference<OFixedTextModel> pClone = new OFixedTextModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OFixedTextModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_FIXEDTEXT;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_FIXEDTEXT;
+ return aSupported;
+}
+
+
+void OFixedTextModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+{
+ OControlModel::describeAggregateProperties( _rAggregateProps );
+ RemoveProperty( _rAggregateProps, PROPERTY_TABSTOP );
+}
+
+
+OUString SAL_CALL OFixedTextModel::getServiceName()
+{
+ return FRM_COMPONENT_FIXEDTEXT; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL OFixedTextModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0002);
+ writeHelpTextCompatibly(_rxOutStream);
+}
+
+
+void SAL_CALL OFixedTextModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OControlModel::read(_rxInStream);
+
+ // Version
+ sal_Int16 nVersion = _rxInStream->readShort();
+ if (nVersion > 1)
+ readHelpTextCompatibly(_rxInStream);
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFixedTextModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFixedTextModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FixedText.hxx b/forms/source/component/FixedText.hxx
new file mode 100644
index 0000000000..a4bafab60d
--- /dev/null
+++ b/forms/source/component/FixedText.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+
+
+namespace frm
+{
+
+class OFixedTextModel
+ :public OControlModel
+{
+public:
+ OFixedTextModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OFixedTextModel(
+ const OFixedTextModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OFixedTextModel() override;
+
+// XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OFixedTextModel"; }
+
+ virtual css::uno::Sequence<OUString> 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 describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormComponent.cxx b/forms/source/component/FormComponent.cxx
new file mode 100644
index 0000000000..e18ca14628
--- /dev/null
+++ b/forms/source/component/FormComponent.cxx
@@ -0,0 +1,2816 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <componenttools.hxx>
+#include <FormComponent.hxx>
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include <property.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/sdb/XRowSetChangeBroadcaster.hpp>
+#include <com/sun/star/sdb/XRowSetSupplier.hpp>
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+
+#include <comphelper/basicio.hxx>
+#include <comphelper/guarding.hxx>
+#include <comphelper/property.hxx>
+#include <connectivity/dbtools.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+
+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;
+using namespace ::com::sun::star::form::validation;
+using namespace ::dbtools;
+using namespace ::comphelper;
+
+// FieldChangeNotifier
+void ControlModelLock::impl_notifyAll_nothrow()
+{
+ m_rModel.firePropertyChanges( m_aHandles, m_aOldValues, m_aNewValues, OControlModel::LockAccess() );
+}
+
+void ControlModelLock::addPropertyNotification( const sal_Int32 _nHandle, const Any& _rOldValue, const Any& _rNewValue )
+{
+ assert( m_aHandles.size() == m_aOldValues.size() && m_aOldValues.size() == m_aNewValues.size() );
+
+ m_aHandles.push_back( _nHandle );
+ m_aOldValues.push_back( _rOldValue );
+ m_aNewValues.push_back( _rNewValue );
+}
+
+namespace {
+
+class FieldChangeNotifier
+{
+public:
+ explicit FieldChangeNotifier(ControlModelLock& _rLock)
+ : m_rLock( _rLock )
+ , m_rModel( dynamic_cast< OBoundControlModel& >( _rLock.getModel() ) )
+ {
+ m_xOldField = m_rModel.getField();
+ }
+
+ ~FieldChangeNotifier()
+ {
+ Reference< XPropertySet > xNewField( m_rModel.getField() );
+ if ( m_xOldField != xNewField )
+ m_rLock.addPropertyNotification( PROPERTY_ID_BOUNDFIELD, Any( m_xOldField ), Any( xNewField ) );
+ }
+
+private:
+ ControlModelLock& m_rLock;
+ OBoundControlModel& m_rModel;
+ Reference< XPropertySet > m_xOldField;
+};
+
+}
+
+// base class for form layer controls
+OControl::OControl( const Reference< XComponentContext >& _rxContext, const OUString& _rAggregateService, const bool _bSetDelegator )
+ :OComponentHelper(m_aMutex)
+ ,m_xContext( _rxContext )
+{
+ // Aggregate VCL Control
+ // Increment the RefCount for aggregates, because the aggregate by itself increments the RefCount in the setDelegator
+ osl_atomic_increment( &m_refCount );
+ {
+ m_xAggregate.set(_rxContext->getServiceManager()->createInstanceWithContext(_rAggregateService, _rxContext), css::uno::UNO_QUERY);
+ m_xControl.set(m_xAggregate, css::uno::UNO_QUERY);
+ }
+ osl_atomic_decrement( &m_refCount );
+
+ if ( _bSetDelegator )
+ doSetDelegator();
+}
+
+OControl::~OControl()
+{
+ doResetDelegator();
+}
+
+void OControl::doResetDelegator()
+{
+ if ( m_xAggregate.is() )
+ m_xAggregate->setDelegator( nullptr );
+}
+
+void OControl::doSetDelegator()
+{
+ osl_atomic_increment( &m_refCount );
+ if ( m_xAggregate.is() )
+ { // those brackets are important for some compilers, don't remove!
+ // (they ensure that the temporary object created in the line below
+ // is destroyed *before* the refcount-decrement)
+ m_xAggregate->setDelegator( static_cast< XWeak* >( this ) );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+// UNO Binding
+Any SAL_CALL OControl::queryAggregation( const Type& _rType )
+{
+ // ask the base class
+ Any aReturn( OComponentHelper::queryAggregation(_rType) );
+ // ask our own interfaces
+ if (!aReturn.hasValue())
+ {
+ aReturn = OControl_BASE::queryInterface(_rType);
+ // ask our aggregate
+ if (!aReturn.hasValue() && m_xAggregate.is())
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+
+ return aReturn;
+}
+
+Sequence<sal_Int8> SAL_CALL OControl::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+Sequence<Type> SAL_CALL OControl::getTypes()
+{
+ TypeBag aTypes( _getTypes() );
+
+ Reference< XTypeProvider > xProv;
+ if ( query_aggregation( m_xAggregate, xProv ) )
+ aTypes.addTypes( xProv->getTypes() );
+
+ return aTypes.getTypes();
+}
+
+Sequence<Type> OControl::_getTypes()
+{
+ return TypeBag( OComponentHelper::getTypes(), OControl_BASE::getTypes() ).getTypes();
+}
+
+// OComponentHelper
+void OControl::disposing()
+{
+ OComponentHelper::disposing();
+
+ m_aWindowStateGuard.attach( nullptr, nullptr );
+
+ Reference< XComponent > xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ xComp->dispose();
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL OControl::supportsService(const OUString& _rsServiceName)
+{
+ return cppu::supportsService(this, _rsServiceName);
+}
+
+Sequence< OUString > OControl::getAggregateServiceNames() const
+{
+ Sequence< OUString > aAggServices;
+ Reference< XServiceInfo > xInfo;
+ if ( query_aggregation( m_xAggregate, xInfo ) )
+ aAggServices = xInfo->getSupportedServiceNames();
+
+ return aAggServices;
+}
+
+Sequence<OUString> SAL_CALL OControl::getSupportedServiceNames()
+{
+ // no own supported service names
+ return getAggregateServiceNames();
+}
+
+// XEventListener
+void SAL_CALL OControl::disposing(const css::lang::EventObject& _rEvent)
+{
+ Reference< XInterface > xAggAsIface;
+ query_aggregation(m_xAggregate, xAggAsIface);
+
+ // does the disposing come from the aggregate?
+ if (xAggAsIface != Reference< XInterface >(_rEvent.Source, UNO_QUERY))
+ { // no -> forward it
+ Reference<css::lang::XEventListener> xListener;
+ if (query_aggregation(m_xAggregate, xListener))
+ xListener->disposing(_rEvent);
+ }
+}
+
+// XControl
+void SAL_CALL OControl::setContext(const Reference< XInterface >& Context)
+{
+ if (m_xControl.is())
+ m_xControl->setContext(Context);
+}
+
+Reference< XInterface > SAL_CALL OControl::getContext()
+{
+ return m_xControl.is() ? m_xControl->getContext() : Reference< XInterface >();
+}
+
+void OControl::impl_resetStateGuard_nothrow()
+{
+ Reference< XWindow2 > xWindow;
+ Reference< XControlModel > xModel;
+ try
+ {
+ xWindow.set( getPeer(), UNO_QUERY );
+ xModel = getModel();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ m_aWindowStateGuard.attach( xWindow, xModel );
+}
+
+void SAL_CALL OControl::createPeer(const Reference<XToolkit>& _rxToolkit, const Reference<XWindowPeer>& _rxParent)
+{
+ if ( m_xControl.is() )
+ {
+ m_xControl->createPeer( _rxToolkit, _rxParent );
+ impl_resetStateGuard_nothrow();
+ }
+}
+
+Reference<XWindowPeer> SAL_CALL OControl::getPeer()
+{
+ return m_xControl.is() ? m_xControl->getPeer() : Reference<XWindowPeer>();
+}
+
+sal_Bool SAL_CALL OControl::setModel(const Reference<XControlModel>& Model)
+{
+ if ( !m_xControl.is() )
+ return false;
+
+ bool bSuccess = m_xControl->setModel( Model );
+ impl_resetStateGuard_nothrow();
+ return bSuccess;
+}
+
+Reference<XControlModel> SAL_CALL OControl::getModel()
+{
+ return m_xControl.is() ? m_xControl->getModel() : Reference<XControlModel>();
+}
+
+Reference<XView> SAL_CALL OControl::getView()
+{
+ return m_xControl.is() ? m_xControl->getView() : Reference<XView>();
+}
+
+void SAL_CALL OControl::setDesignMode(sal_Bool bOn)
+{
+ if (m_xControl.is())
+ m_xControl->setDesignMode(bOn);
+}
+
+sal_Bool SAL_CALL OControl::isDesignMode()
+{
+ return !m_xControl.is() || m_xControl->isDesignMode();
+}
+
+sal_Bool SAL_CALL OControl::isTransparent()
+{
+ return !m_xControl.is() || m_xControl->isTransparent();
+}
+
+OBoundControl::OBoundControl( const Reference< XComponentContext >& _rxContext,
+ const OUString& _rAggregateService, const bool _bSetDelegator )
+ :OControl( _rxContext, _rAggregateService, _bSetDelegator )
+ ,m_bLocked(false)
+{
+}
+
+OBoundControl::~OBoundControl()
+{
+}
+
+Sequence< Type> OBoundControl::_getTypes()
+{
+ return TypeBag( OControl::_getTypes(), OBoundControl_BASE::getTypes() ).getTypes();
+}
+
+Any SAL_CALL OBoundControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn;
+
+ // XTypeProvider first - don't ask the OBoundControl_BASE, it would deliver incomplete types
+ if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() ) )
+ aReturn = OControl::queryAggregation( _rType );
+
+ // ask our own interfaces
+ // (do this first (except XTypeProvider ) - we want to "overwrite" XPropertiesChangeListener)
+ if ( !aReturn.hasValue() )
+ aReturn = OBoundControl_BASE::queryInterface( _rType );
+
+ // ask the base class
+ if ( !aReturn.hasValue() )
+ aReturn = OControl::queryAggregation( _rType );
+
+ return aReturn;
+}
+
+sal_Bool SAL_CALL OBoundControl::getLock()
+{
+ return m_bLocked;
+}
+
+void SAL_CALL OBoundControl::setLock(sal_Bool _bLock)
+{
+ if (m_bLocked == bool(_bLock))
+ return;
+
+ osl::MutexGuard aGuard(m_aMutex);
+ _setLock(_bLock);
+ m_bLocked = _bLock;
+}
+
+void OBoundControl::_setLock(bool _bLock)
+{
+ // try to set the text component to readonly
+ Reference< XWindowPeer > xPeer = getPeer();
+ Reference< XTextComponent > xText( xPeer, UNO_QUERY );
+
+ if ( xText.is() )
+ xText->setEditable( !_bLock );
+ else
+ {
+ // disable the window
+ Reference< XWindow > xComp( xPeer, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->setEnable( !_bLock );
+ }
+}
+
+sal_Bool SAL_CALL OBoundControl::setModel( const Reference< XControlModel >& _rxModel )
+{
+ return OControl::setModel( _rxModel );
+}
+
+void SAL_CALL OBoundControl::disposing(const EventObject& Source)
+{
+ // just disambiguate
+ OControl::disposing(Source);
+}
+
+void OBoundControl::disposing()
+{
+ OControl::disposing();
+}
+
+// OControlModel
+Sequence<sal_Int8> SAL_CALL OControlModel::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+Sequence<Type> SAL_CALL OControlModel::getTypes()
+{
+ TypeBag aTypes( _getTypes() );
+
+ Reference< XTypeProvider > xProv;
+
+ if ( query_aggregation( m_xAggregate, xProv ) )
+ aTypes.addTypes( xProv->getTypes() );
+
+ return aTypes.getTypes();
+}
+
+Sequence<Type> OControlModel::_getTypes()
+{
+ return TypeBag( OComponentHelper::getTypes(),
+ OPropertySetAggregationHelper::getTypes(),
+ OControlModel_BASE::getTypes()
+ ).getTypes();
+}
+
+Any SAL_CALL OControlModel::queryAggregation(const Type& _rType)
+{
+ // base class 1
+ Any aReturn(OComponentHelper::queryAggregation(_rType));
+
+ // base class 2
+ if (!aReturn.hasValue())
+ {
+ aReturn = OControlModel_BASE::queryInterface(_rType);
+
+ // our own interfaces
+ if (!aReturn.hasValue())
+ {
+ aReturn = OPropertySetAggregationHelper::queryInterface(_rType);
+ // our aggregate
+ if (!aReturn.hasValue() && m_xAggregate.is() && !_rType.equals(cppu::UnoType<XCloneable>::get()))
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+ }
+ return aReturn;
+}
+
+void OControlModel::readHelpTextCompatibly(const css::uno::Reference< css::io::XObjectInputStream >& _rxInStream)
+{
+ OUString sHelpText;
+ ::comphelper::operator>>( _rxInStream, sHelpText);
+ try
+ {
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_HELPTEXT, Any(sHelpText));
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ SAL_WARN("forms.component", "OControlModel::readHelpTextCompatibly: could not forward the property value to the aggregate!");
+ }
+}
+
+void OControlModel::writeHelpTextCompatibly(const css::uno::Reference< css::io::XObjectOutputStream >& _rxOutStream)
+{
+ OUString sHelpText;
+ try
+ {
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->getPropertyValue(PROPERTY_HELPTEXT) >>= sHelpText;
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ SAL_WARN("forms.component", "OControlModel::writeHelpTextCompatibly: could not retrieve the property value from the aggregate!");
+ }
+ ::comphelper::operator<<( _rxOutStream, sHelpText);
+}
+
+OControlModel::OControlModel(
+ const Reference<XComponentContext>& _rxContext,
+ const OUString& _rUnoControlModelTypeName,
+ const OUString& rDefault, const bool _bSetDelegator)
+ :OComponentHelper(m_aMutex)
+ ,OPropertySetAggregationHelper(OComponentHelper::rBHelper)
+ ,m_xContext( _rxContext )
+ ,m_lockCount( 0 )
+ ,m_aPropertyBagHelper( *this )
+ ,m_nTabIndex(FRM_DEFAULT_TABINDEX)
+ ,m_nClassId(FormComponentType::CONTROL)
+ ,m_bNativeLook( false )
+ ,m_bStandardTheme( false )
+ ,m_bGenerateVbEvents( false )
+ ,m_nControlTypeinMSO(0) // 0 : default value is create from AOO
+ ,m_nObjIDinMSO(INVALID_OBJ_ID_IN_MSO)
+ // form controls are usually embedded into documents, not dialogs, and in documents
+ // the native look is ugly...
+ // #i37342#
+{
+ if (_rUnoControlModelTypeName.isEmpty()) // the is a model we have to aggregate
+ return;
+
+ osl_atomic_increment(&m_refCount);
+ {
+ m_xAggregate.set(m_xContext->getServiceManager()->createInstanceWithContext(_rUnoControlModelTypeName, m_xContext), UNO_QUERY);
+ setAggregation(m_xAggregate);
+
+ if ( m_xAggregateSet.is() )
+ {
+ try
+ {
+ if ( !rDefault.isEmpty() )
+ m_xAggregateSet->setPropertyValue( PROPERTY_DEFAULTCONTROL, Any( rDefault ) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OControlModel::OControlModel");
+ }
+ }
+ }
+ if (_bSetDelegator)
+ doSetDelegator();
+
+ // Refcount is at NULL again
+ osl_atomic_decrement(&m_refCount);
+}
+
+OControlModel::OControlModel( const OControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory, const bool _bCloneAggregate, const bool _bSetDelegator )
+ :OComponentHelper( m_aMutex )
+ ,OPropertySetAggregationHelper( OComponentHelper::rBHelper )
+ ,m_xContext( _rxFactory )
+ ,m_lockCount( 0 )
+ ,m_aPropertyBagHelper( *this )
+ ,m_nTabIndex( FRM_DEFAULT_TABINDEX )
+ ,m_nClassId( FormComponentType::CONTROL )
+{
+ DBG_ASSERT( _pOriginal, "OControlModel::OControlModel: invalid original!" );
+
+ // copy members
+ m_aName = _pOriginal->m_aName;
+ m_aTag = _pOriginal->m_aTag;
+ m_nTabIndex = _pOriginal->m_nTabIndex;
+ m_nClassId = _pOriginal->m_nClassId;
+ m_bNativeLook = _pOriginal->m_bNativeLook;
+ m_bStandardTheme = _pOriginal->m_bStandardTheme;
+ m_bGenerateVbEvents = _pOriginal->m_bGenerateVbEvents;
+ m_nControlTypeinMSO = _pOriginal->m_nControlTypeinMSO;
+ m_nObjIDinMSO = _pOriginal->m_nObjIDinMSO;
+
+ if ( !_bCloneAggregate )
+ return;
+
+ // temporarily increment refcount because of temporary references to ourself in the following
+ osl_atomic_increment( &m_refCount );
+ {
+ // transfer the (only, at the very moment!) ref count
+ m_xAggregate = createAggregateClone( _pOriginal );
+
+ // set aggregation (retrieve other direct interfaces of the aggregate)
+ setAggregation( m_xAggregate );
+ }
+
+ // set the delegator, if allowed by our derived class
+ if ( _bSetDelegator )
+ doSetDelegator();
+
+ // decrement ref count
+ osl_atomic_decrement( &m_refCount );
+}
+
+OControlModel::~OControlModel()
+{
+ // release the aggregate
+ doResetDelegator( );
+}
+
+void OControlModel::clonedFrom( const OControlModel* /*_pOriginal*/ )
+{
+ // nothing to do in this base class
+}
+
+void OControlModel::doResetDelegator()
+{
+ if (m_xAggregate.is())
+ m_xAggregate->setDelegator(nullptr);
+}
+
+void OControlModel::doSetDelegator()
+{
+ osl_atomic_increment(&m_refCount);
+ if (m_xAggregate.is())
+ {
+ m_xAggregate->setDelegator(static_cast<XWeak*>(this));
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+// XChild
+Reference< XInterface > SAL_CALL OControlModel::getParent()
+{
+ return m_xParent;
+}
+
+void SAL_CALL OControlModel::setParent(const Reference< XInterface >& _rxParent)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ Reference<XComponent> xComp(m_xParent, UNO_QUERY);
+ if (xComp.is())
+ xComp->removeEventListener(static_cast<XPropertiesChangeListener*>(this));
+
+ m_xParent = _rxParent;
+ xComp.set(m_xParent, css::uno::UNO_QUERY);
+
+ if ( xComp.is() )
+ xComp->addEventListener(static_cast<XPropertiesChangeListener*>(this));
+}
+
+// XNamed
+OUString SAL_CALL OControlModel::getName()
+{
+ OUString aReturn;
+ try
+ {
+ OPropertySetHelper::getFastPropertyValue(PROPERTY_ID_NAME) >>= aReturn;
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw WrappedTargetRuntimeException(
+ "OControlModel::getName",
+ *this,
+ a
+ );
+ }
+ return aReturn;
+}
+
+void SAL_CALL OControlModel::setName(const OUString& _rName)
+{
+ try
+ {
+ setFastPropertyValue(PROPERTY_ID_NAME, Any(_rName));
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw WrappedTargetRuntimeException(
+ "OControlModel::setName",
+ *this,
+ a
+ );
+ }
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL OControlModel::supportsService(const OUString& _rServiceName)
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+Sequence< OUString > OControlModel::getAggregateServiceNames() const
+{
+ Sequence< OUString > aAggServices;
+ Reference< XServiceInfo > xInfo;
+ if ( query_aggregation( m_xAggregate, xInfo ) )
+ aAggServices = xInfo->getSupportedServiceNames();
+ return aAggServices;
+}
+
+Sequence<OUString> SAL_CALL OControlModel::getSupportedServiceNames()
+{
+ return ::comphelper::concatSequences(
+ getAggregateServiceNames(),
+ getSupportedServiceNames_Static()
+ );
+}
+
+Sequence< OUString > OControlModel::getSupportedServiceNames_Static()
+{
+ return { FRM_SUN_FORMCOMPONENT, "com.sun.star.form.FormControlModel" };
+}
+
+// XEventListener
+void SAL_CALL OControlModel::disposing(const css::lang::EventObject& _rSource)
+{
+ // release the parent
+ if (_rSource.Source == m_xParent)
+ {
+ osl::MutexGuard aGuard(m_aMutex);
+ m_xParent = nullptr;
+ }
+ else
+ {
+ Reference<css::lang::XEventListener> xEvtLst;
+ if (query_aggregation(m_xAggregate, xEvtLst))
+ {
+ osl::MutexGuard aGuard(m_aMutex);
+ xEvtLst->disposing(_rSource);
+ }
+ }
+}
+
+// OComponentHelper
+void OControlModel::disposing()
+{
+ OPropertySetAggregationHelper::disposing();
+
+ Reference<css::lang::XComponent> xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ xComp->dispose();
+
+ setParent(Reference<XFormComponent>());
+
+ m_aPropertyBagHelper.dispose();
+}
+
+void OControlModel::writeAggregate( const Reference< XObjectOutputStream >& _rxOutStream ) const
+{
+ Reference< XPersistObject > xPersist;
+ if ( query_aggregation( m_xAggregate, xPersist ) )
+ xPersist->write( _rxOutStream );
+}
+
+void OControlModel::readAggregate( const Reference< XObjectInputStream >& _rxInStream )
+{
+ Reference< XPersistObject > xPersist;
+ if ( query_aggregation( m_xAggregate, xPersist ) )
+ xPersist->read( _rxInStream );
+}
+
+void SAL_CALL OControlModel::write(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ // 1. writing the UnoControls
+ Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ if ( !xMark.is() )
+ {
+ throw IOException(
+ ResourceManager::loadString(RID_STR_INVALIDSTREAM),
+ static_cast< ::cppu::OWeakObject* >( this )
+ );
+ }
+
+ sal_Int32 nMark = xMark->createMark();
+ sal_Int32 nLen = 0;
+
+ _rxOutStream->writeLong(nLen);
+
+ writeAggregate( _rxOutStream );
+
+ // determining the length
+ nLen = xMark->offsetToMark(nMark) - 4;
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+
+ // 2. writing a version number
+ _rxOutStream->writeShort(0x0003);
+
+ // 3. writing the general properties
+ ::comphelper::operator<<( _rxOutStream, m_aName);
+ _rxOutStream->writeShort(m_nTabIndex);
+ ::comphelper::operator<<( _rxOutStream, m_aTag); // 3rd version
+
+ // IMPORTANT NOTE!
+ // don't write any new members here: this wouldn't be compatible with older versions, as OControlModel
+ // is a base class which is called in derived classes "read" method. So if you increment the version
+ // and write new stuff, older office versions will read this in the _derived_ classes, which may result
+ // in anything from data loss to crash.
+ // EOIN!
+}
+
+void OControlModel::read(const Reference<css::io::XObjectInputStream>& InStream)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ Reference<css::io::XMarkableStream> xMark(InStream, UNO_QUERY);
+ if ( !xMark.is() )
+ {
+ throw IOException(
+ ResourceManager::loadString(RID_STR_INVALIDSTREAM),
+ static_cast< ::cppu::OWeakObject* >( this )
+ );
+ }
+
+ // 1. reading the UnoControls
+ sal_Int32 nLen = InStream->readLong();
+ if (nLen)
+ {
+ sal_Int32 nMark = xMark->createMark();
+
+ try
+ {
+ readAggregate( InStream );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ xMark->jumpToMark(nMark);
+ InStream->skipBytes(nLen);
+ xMark->deleteMark(nMark);
+ }
+
+ // 2. reading the version number
+ sal_uInt16 nVersion = InStream->readShort();
+
+ // 3. reading the general properties
+ ::comphelper::operator>>( InStream, m_aName);
+ m_nTabIndex = InStream->readShort();
+
+ if (nVersion > 0x0002)
+ ::comphelper::operator>>( InStream, m_aTag);
+
+ // we had a version where we wrote the help text
+ if (nVersion == 0x0004)
+ readHelpTextCompatibly(InStream);
+
+ DBG_ASSERT(nVersion < 5, "OControlModel::read : suspicious version number !");
+ // 4 was the version where we wrote the help text
+ // later versions shouldn't exist (see write for a detailed comment)
+}
+
+PropertyState OControlModel::getPropertyStateByHandle( sal_Int32 _nHandle )
+{
+ // simply compare the current and the default value
+ Any aCurrentValue = getPropertyDefaultByHandle( _nHandle );
+ Any aDefaultValue; getFastPropertyValue( aDefaultValue, _nHandle );
+
+ bool bEqual = aCurrentValue == aDefaultValue;
+ return bEqual ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
+}
+
+void OControlModel::setPropertyToDefaultByHandle( sal_Int32 _nHandle)
+{
+ Any aDefault = getPropertyDefaultByHandle( _nHandle );
+
+ Any aConvertedValue, aOldValue;
+ if ( convertFastPropertyValue( aConvertedValue, aOldValue, _nHandle, aDefault ) )
+ {
+ setFastPropertyValue_NoBroadcast( _nHandle, aConvertedValue );
+ // TODO: fire the property change
+ }
+}
+
+Any OControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+{
+ Any aReturn;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_NAME:
+ case PROPERTY_ID_TAG:
+ aReturn <<= OUString();
+ break;
+ case PROPERTY_ID_CLASSID:
+ aReturn <<= sal_Int16(FormComponentType::CONTROL);
+ break;
+ case PROPERTY_ID_TABINDEX:
+ aReturn <<= sal_Int16(FRM_DEFAULT_TABINDEX);
+ break;
+ case PROPERTY_ID_NATIVE_LOOK:
+ aReturn <<= true;
+ break;
+ case PROPERTY_ID_STANDARD_THEME:
+ aReturn <<= false;
+ break;
+ case PROPERTY_ID_GENERATEVBAEVENTS:
+ aReturn <<= false;
+ break;
+ // added for exporting OCX control
+ case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
+ aReturn <<= sal_Int16(0);
+ break;
+ case PROPERTY_ID_OBJ_ID_IN_MSO:
+ aReturn <<= sal_uInt16(INVALID_OBJ_ID_IN_MSO);
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
+ m_aPropertyBagHelper.getDynamicPropertyDefaultByHandle( _nHandle, aReturn );
+ else
+ SAL_WARN("forms.component", "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
+ }
+ return aReturn;
+}
+
+void OControlModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+{
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_NAME:
+ _rValue <<= m_aName;
+ break;
+ case PROPERTY_ID_TAG:
+ _rValue <<= m_aTag;
+ break;
+ case PROPERTY_ID_CLASSID:
+ _rValue <<= m_nClassId;
+ break;
+ case PROPERTY_ID_TABINDEX:
+ _rValue <<= m_nTabIndex;
+ break;
+ case PROPERTY_ID_NATIVE_LOOK:
+ _rValue <<= m_bNativeLook;
+ break;
+ case PROPERTY_ID_STANDARD_THEME:
+ _rValue <<= m_bStandardTheme;
+ break;
+ case PROPERTY_ID_GENERATEVBAEVENTS:
+ _rValue <<= m_bGenerateVbEvents;
+ break;
+ // added for exporting OCX control
+ case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
+ _rValue <<= m_nControlTypeinMSO;
+ break;
+ case PROPERTY_ID_OBJ_ID_IN_MSO:
+ _rValue <<= m_nObjIDinMSO;
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
+ m_aPropertyBagHelper.getDynamicFastPropertyValue( _nHandle, _rValue );
+ else
+ OPropertySetAggregationHelper::getFastPropertyValue( _rValue, _nHandle );
+ break;
+ }
+}
+
+sal_Bool OControlModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
+{
+ bool bModified(false);
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_NAME:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aName);
+ break;
+ case PROPERTY_ID_TAG:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aTag);
+ break;
+ case PROPERTY_ID_TABINDEX:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nTabIndex);
+ break;
+ case PROPERTY_ID_NATIVE_LOOK:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bNativeLook);
+ break;
+ case PROPERTY_ID_STANDARD_THEME:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bStandardTheme);
+ break;
+ case PROPERTY_ID_GENERATEVBAEVENTS:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_bGenerateVbEvents);
+ break;
+ // added for exporting OCX control
+ case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nControlTypeinMSO);
+ break;
+ case PROPERTY_ID_OBJ_ID_IN_MSO:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_nObjIDinMSO);
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
+ bModified = m_aPropertyBagHelper.convertDynamicFastPropertyValue( _nHandle, _rValue, _rConvertedValue, _rOldValue );
+ else
+ SAL_WARN("forms.component", "OControlModel::convertFastPropertyValue: unknown handle " << _nHandle);
+ break;
+ }
+ return bModified;
+}
+
+void OControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_NAME:
+ DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
+ "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
+ _rValue >>= m_aName;
+ break;
+ case PROPERTY_ID_TAG:
+ DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<OUString>::get(),
+ "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
+ _rValue >>= m_aTag;
+ break;
+ case PROPERTY_ID_TABINDEX:
+ DBG_ASSERT(_rValue.getValueType() == cppu::UnoType<sal_Int16>::get(),
+ "OControlModel::setFastPropertyValue_NoBroadcast : invalid type" );
+ _rValue >>= m_nTabIndex;
+ break;
+ case PROPERTY_ID_NATIVE_LOOK:
+ OSL_VERIFY( _rValue >>= m_bNativeLook );
+ break;
+ case PROPERTY_ID_STANDARD_THEME:
+ OSL_VERIFY( _rValue >>= m_bStandardTheme );
+ break;
+ case PROPERTY_ID_GENERATEVBAEVENTS:
+ OSL_VERIFY( _rValue >>= m_bGenerateVbEvents );
+ break;
+ // added for exporting OCX control
+ case PROPERTY_ID_CONTROL_TYPE_IN_MSO:
+ OSL_VERIFY( _rValue >>= m_nControlTypeinMSO );
+ break;
+ case PROPERTY_ID_OBJ_ID_IN_MSO:
+ OSL_VERIFY( _rValue >>= m_nObjIDinMSO );
+ break;
+ default:
+ if ( m_aPropertyBagHelper.hasDynamicPropertyByHandle( _nHandle ) )
+ m_aPropertyBagHelper.setDynamicFastPropertyValue( _nHandle, _rValue );
+ else
+ SAL_WARN("forms.component", "OControlModel::setFastPropertyValue_NoBroadcast: unknown handle " << _nHandle );
+ break;
+ }
+}
+
+void OControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ _rProps.realloc(8);
+ css::beans::Property* pProperties = _rProps.getArray();
+ *pProperties++ = css::beans::Property(PROPERTY_CLASSID, PROPERTY_ID_CLASSID, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_NATIVE_LOOK, PROPERTY_ID_NATIVE_LOOK, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_STANDARD_THEME, PROPERTY_ID_STANDARD_THEME, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_TAG, PROPERTY_ID_TAG, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_GENERATEVBAEVENTS, PROPERTY_ID_GENERATEVBAEVENTS, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROL_TYPE_IN_MSO, PROPERTY_ID_CONTROL_TYPE_IN_MSO, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_OBJ_ID_IN_MSO, PROPERTY_ID_OBJ_ID_IN_MSO, cppu::UnoType<cppu::UnoUnsignedShortType>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+void OControlModel::describeAggregateProperties( Sequence< Property >& /* [out] */ _rAggregateProps ) const
+{
+ if ( m_xAggregateSet.is() )
+ {
+ Reference< XPropertySetInfo > xPSI( m_xAggregateSet->getPropertySetInfo() );
+ if ( xPSI.is() )
+ _rAggregateProps = xPSI->getProperties();
+ }
+}
+
+::osl::Mutex& OControlModel::getMutex()
+{
+ return m_aMutex;
+}
+
+void OControlModel::describeFixedAndAggregateProperties( Sequence< Property >& _out_rFixedProperties, Sequence< Property >& _out_rAggregateProperties ) const
+{
+ describeFixedProperties( _out_rFixedProperties );
+ describeAggregateProperties( _out_rAggregateProperties );
+}
+
+Reference< XMultiPropertySet > OControlModel::getPropertiesInterface()
+{
+ return this;
+}
+
+Reference< XPropertySetInfo> SAL_CALL OControlModel::getPropertySetInfo()
+{
+ return createPropertySetInfo( getInfoHelper() );
+}
+
+::cppu::IPropertyArrayHelper& OControlModel::getInfoHelper()
+{
+ return m_aPropertyBagHelper.getInfoHelper();
+}
+
+void SAL_CALL OControlModel::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
+{
+ m_aPropertyBagHelper.addProperty( _rName, _nAttributes, _rInitialValue );
+}
+
+void SAL_CALL OControlModel::removeProperty( const OUString& _rName )
+{
+ m_aPropertyBagHelper.removeProperty( _rName );
+}
+
+Sequence< PropertyValue > SAL_CALL OControlModel::getPropertyValues()
+{
+ return m_aPropertyBagHelper.getPropertyValues();
+}
+
+void SAL_CALL OControlModel::setPropertyValues( const Sequence< PropertyValue >& _rProps )
+{
+ m_aPropertyBagHelper.setPropertyValues( _rProps );
+}
+
+void OControlModel::lockInstance( LockAccess )
+{
+ m_aMutex.acquire();
+ osl_atomic_increment( &m_lockCount );
+}
+
+oslInterlockedCount OControlModel::unlockInstance( LockAccess )
+{
+ OSL_ENSURE( m_lockCount > 0, "OControlModel::unlockInstance: not locked!" );
+ oslInterlockedCount lockCount = osl_atomic_decrement( &m_lockCount );
+ m_aMutex.release();
+ return lockCount;
+}
+
+void OControlModel::firePropertyChanges( const std::vector< sal_Int32 >& _rHandles, const std::vector< Any >& _rOldValues,
+ const std::vector< Any >& _rNewValues, LockAccess )
+{
+ OPropertySetHelper::fire(
+ const_cast< std::vector< sal_Int32 >& >( _rHandles ).data(),
+ _rNewValues.data(),
+ _rOldValues.data(),
+ _rHandles.size(),
+ false
+ );
+}
+
+// OBoundControlModel
+Any SAL_CALL OBoundControlModel::queryAggregation( const Type& _rType )
+{
+ Any aReturn( OControlModel::queryAggregation(_rType) );
+ if (!aReturn.hasValue())
+ {
+ aReturn = OBoundControlModel_BASE1::queryInterface(_rType);
+
+ if ( !aReturn.hasValue() && m_bCommitable )
+ aReturn = OBoundControlModel_COMMITTING::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() && m_bSupportsExternalBinding )
+ aReturn = OBoundControlModel_BINDING::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() && m_bSupportsValidation )
+ aReturn = OBoundControlModel_VALIDATION::queryInterface( _rType );
+ }
+ return aReturn;
+}
+
+OBoundControlModel::OBoundControlModel(
+ const Reference< XComponentContext>& _rxFactory,
+ const OUString& _rUnoControlModelTypeName, const OUString& _rDefault,
+ const bool _bCommitable, const bool _bSupportExternalBinding, const bool _bSupportsValidation )
+ :OControlModel( _rxFactory, _rUnoControlModelTypeName, _rDefault, false )
+ ,OPropertyChangeListener( m_aMutex )
+ ,m_nValuePropertyAggregateHandle( -1 )
+ ,m_nFieldType( DataType::OTHER )
+ ,m_bValuePropertyMayBeVoid( false )
+ ,m_aResetHelper( *this, m_aMutex )
+ ,m_aUpdateListeners(m_aMutex)
+ ,m_aFormComponentListeners( m_aMutex )
+ ,m_bInputRequired( false )
+ ,m_bFormListening( false )
+ ,m_bLoaded(false)
+ ,m_bRequired(false)
+ ,m_bCommitable(_bCommitable)
+ ,m_bSupportsExternalBinding( _bSupportExternalBinding )
+ ,m_bSupportsValidation( _bSupportsValidation )
+ ,m_bForwardValueChanges(true)
+ ,m_bTransferringValue( false )
+ ,m_bIsCurrentValueValid( true )
+ ,m_bBindingControlsRO( false )
+ ,m_bBindingControlsEnable( false )
+ ,m_eControlValueChangeInstigator( eOther )
+ ,m_aLabelServiceName(FRM_SUN_COMPONENT_FIXEDTEXT)
+{
+ // start property listening at the aggregate
+ implInitAggMultiplexer( );
+}
+
+OBoundControlModel::OBoundControlModel(
+ const OBoundControlModel* _pOriginal, const Reference< XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory, true, false )
+ ,OPropertyChangeListener( m_aMutex )
+ ,m_nValuePropertyAggregateHandle( _pOriginal->m_nValuePropertyAggregateHandle )
+ ,m_nFieldType( DataType::OTHER )
+ ,m_bValuePropertyMayBeVoid( _pOriginal->m_bValuePropertyMayBeVoid )
+ ,m_aResetHelper( *this, m_aMutex )
+ ,m_aUpdateListeners( m_aMutex )
+ ,m_aFormComponentListeners( m_aMutex )
+ ,m_xValidator( _pOriginal->m_xValidator )
+ ,m_bInputRequired( false )
+ ,m_bFormListening( false )
+ ,m_bLoaded( false )
+ ,m_bRequired( false )
+ ,m_bCommitable( _pOriginal->m_bCommitable )
+ ,m_bSupportsExternalBinding( _pOriginal->m_bSupportsExternalBinding )
+ ,m_bSupportsValidation( _pOriginal->m_bSupportsValidation )
+ ,m_bForwardValueChanges( true )
+ ,m_bTransferringValue( false )
+ ,m_bIsCurrentValueValid( _pOriginal->m_bIsCurrentValueValid )
+ ,m_bBindingControlsRO( false )
+ ,m_bBindingControlsEnable( false )
+ ,m_eControlValueChangeInstigator( eOther )
+{
+ // start property listening at the aggregate
+ implInitAggMultiplexer( );
+ m_aLabelServiceName = _pOriginal->m_aLabelServiceName;
+ m_sValuePropertyName = _pOriginal->m_sValuePropertyName;
+ m_nValuePropertyAggregateHandle = _pOriginal->m_nValuePropertyAggregateHandle;
+ m_bValuePropertyMayBeVoid = _pOriginal->m_bValuePropertyMayBeVoid;
+ m_aValuePropertyType = _pOriginal->m_aValuePropertyType;
+ m_aControlSource = _pOriginal->m_aControlSource;
+ m_bInputRequired = _pOriginal->m_bInputRequired;
+ // m_xLabelControl, though being a property, is not to be cloned, not even the reference will be transferred.
+ // (the former should be clear - a clone of the object we're only referencing does not make sense)
+ // (the second would violate the restriction for label controls that they're part of the
+ // same form component hierarchy - we ourself are no part, yet, so we can't have a label control)
+ // start listening for changes at the value property
+ implInitValuePropertyListening( );
+}
+
+OBoundControlModel::~OBoundControlModel()
+{
+ if ( !OComponentHelper::rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+
+ doResetDelegator( );
+ OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::~OBoundControlModel: what about my property multiplexer?" );
+ if ( m_pAggPropMultiplexer )
+ {
+ m_pAggPropMultiplexer->dispose();
+ m_pAggPropMultiplexer = nullptr;
+ }
+}
+
+void OBoundControlModel::clonedFrom( const OControlModel* _pOriginal )
+{
+ const OBoundControlModel* pBoundOriginal = static_cast< const OBoundControlModel* >( _pOriginal );
+ // the value binding can be handled as if somebody called setValueBinding here
+ // By definition, bindings can be share between bindables
+ if ( !(pBoundOriginal && pBoundOriginal->m_xExternalBinding.is()) )
+ return;
+
+ try
+ {
+ setValueBinding( pBoundOriginal->m_xExternalBinding );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+}
+
+void OBoundControlModel::implInitAggMultiplexer( )
+{
+ osl_atomic_increment( &m_refCount );
+ if ( m_xAggregateSet.is() )
+ {
+ m_pAggPropMultiplexer = new OPropertyChangeMultiplexer( this, m_xAggregateSet, false );
+ }
+
+ osl_atomic_decrement( &m_refCount );
+ doSetDelegator();
+}
+
+void OBoundControlModel::implInitValuePropertyListening( ) const
+{
+ // start listening for changes at the value property
+ // There are three pre-requisites for this to be done:
+ // 1. We support external value bindings. In this case, the changes in the control value need to
+ // be propagated to the external binding immediately when they happen
+ // 2. We support external validation. In this case, we need to listen for changes in the value
+ // property, since we need to revalidate then.
+ // 3. We are not committable. In this case, changes in the control value need to be propagated
+ // to the database column immediately when they happen.
+ if ( m_bSupportsExternalBinding || m_bSupportsValidation || !m_bCommitable )
+ {
+ OSL_ENSURE( m_pAggPropMultiplexer, "OBoundControlModel::implInitValuePropertyListening: no multiplexer!" );
+ if ( m_pAggPropMultiplexer && !m_sValuePropertyName.isEmpty() )
+ m_pAggPropMultiplexer->addProperty( m_sValuePropertyName );
+ }
+}
+
+void OBoundControlModel::initOwnValueProperty( const OUString& i_rValuePropertyName )
+{
+ OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
+ "OBoundControlModel::initOwnValueProperty: value property is already initialized!" );
+ OSL_ENSURE( !i_rValuePropertyName.isEmpty(), "OBoundControlModel::initOwnValueProperty: invalid property name!" );
+ m_sValuePropertyName = i_rValuePropertyName;
+}
+
+void OBoundControlModel::initValueProperty( const OUString& _rValuePropertyName, sal_Int32 _nValuePropertyExternalHandle )
+{
+ OSL_PRECOND( m_sValuePropertyName.isEmpty() && -1 == m_nValuePropertyAggregateHandle,
+ "OBoundControlModel::initValueProperty: value property is already initialized!" );
+ OSL_ENSURE( !_rValuePropertyName.isEmpty(), "OBoundControlModel::initValueProperty: invalid property name!" );
+ OSL_ENSURE( _nValuePropertyExternalHandle != -1, "OBoundControlModel::initValueProperty: invalid property handle!" );
+
+ m_sValuePropertyName = _rValuePropertyName;
+ m_nValuePropertyAggregateHandle = getOriginalHandle( _nValuePropertyExternalHandle );
+ OSL_ENSURE( m_nValuePropertyAggregateHandle != -1, "OBoundControlModel::initValueProperty: unable to find the original handle!" );
+
+ if ( m_nValuePropertyAggregateHandle != -1 )
+ {
+ Reference< XPropertySetInfo > xPropInfo( m_xAggregateSet->getPropertySetInfo(), UNO_SET_THROW );
+ Property aValuePropDesc = xPropInfo->getPropertyByName( m_sValuePropertyName );
+ m_aValuePropertyType = aValuePropDesc.Type;
+ m_bValuePropertyMayBeVoid = ( aValuePropDesc.Attributes & PropertyAttribute::MAYBEVOID ) != 0;
+ }
+
+ // start listening for changes at the value property
+ implInitValuePropertyListening( );
+}
+
+void OBoundControlModel::suspendValueListening( )
+{
+ OSL_PRECOND( !m_sValuePropertyName.isEmpty(), "OBoundControlModel::suspendValueListening: don't have a value property!" );
+ OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::suspendValueListening: I *am* not listening!" );
+
+ if ( m_pAggPropMultiplexer )
+ m_pAggPropMultiplexer->lock();
+}
+
+void OBoundControlModel::resumeValueListening( )
+{
+ OSL_PRECOND( !m_sValuePropertyName.isEmpty(), "OBoundControlModel::resumeValueListening: don't have a value property!" );
+ OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::resumeValueListening: I *am* not listening at all!" );
+ OSL_PRECOND( !m_pAggPropMultiplexer || m_pAggPropMultiplexer->locked(), "OBoundControlModel::resumeValueListening: listening not suspended currently!" );
+ if ( m_pAggPropMultiplexer )
+ m_pAggPropMultiplexer->unlock();
+}
+
+Sequence< Type > OBoundControlModel::_getTypes()
+{
+ TypeBag aTypes(
+ OControlModel::_getTypes(),
+ OBoundControlModel_BASE1::getTypes()
+ );
+
+ if ( m_bCommitable )
+ aTypes.addTypes( OBoundControlModel_COMMITTING::getTypes() );
+
+ if ( m_bSupportsExternalBinding )
+ aTypes.addTypes( OBoundControlModel_BINDING::getTypes() );
+
+ if ( m_bSupportsValidation )
+ aTypes.addTypes( OBoundControlModel_VALIDATION::getTypes() );
+ return aTypes.getTypes();
+}
+
+// OComponentHelper
+void OBoundControlModel::disposing()
+{
+ OControlModel::disposing();
+
+ osl::MutexGuard aGuard(m_aMutex);
+
+ if ( m_pAggPropMultiplexer )
+ m_pAggPropMultiplexer->dispose();
+
+ // notify all our listeners
+ css::lang::EventObject aEvt( static_cast< XWeak* >( this ) );
+ m_aUpdateListeners.disposeAndClear( aEvt );
+ m_aResetHelper.disposing();
+
+ // disconnect from our database column
+ // TODO: could we replace the following 5 lines with a call to impl_disconnectDatabaseColumn_noNotify?
+ // The only more thing which it does is calling onDisconnectedDbColumn - could this
+ // cause trouble? At least when we continue to call OControlModel::disposing before, it *may*.
+ if ( hasField() )
+ {
+ getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
+ resetField();
+ }
+
+ m_xCursor = nullptr;
+ Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->removeEventListener(static_cast< XEventListener* >( static_cast< XPropertyChangeListener* >( this ) ) );
+ // disconnect from our external value binding
+ if ( hasExternalValueBinding() )
+ disconnectExternalValueBinding();
+ // ditto for the validator
+ if ( hasValidator() )
+ disconnectValidator( );
+}
+
+void OBoundControlModel::onValuePropertyChange( ControlModelLock& i_rControLock )
+{
+ if ( hasExternalValueBinding() )
+ {
+ // the control value changed, while we have an external value binding
+ // -> forward the value to it
+ if ( m_eControlValueChangeInstigator != eExternalBinding )
+ transferControlValueToExternal( i_rControLock );
+ }
+
+ else if ( !m_bCommitable && m_xColumnUpdate.is() )
+ {
+ // the control value changed, while we are bound to a database column,
+ // but not committable (which means changes in the control have to be reflected to
+ // the underlying database column immediately)
+ // -> forward the value to the database column
+ if ( m_eControlValueChangeInstigator != eDbColumnBinding )
+ commitControlValueToDbColumn( false );
+ }
+
+ // validate the new value
+ if ( m_bSupportsValidation )
+ recheckValidity( true );
+}
+
+void OBoundControlModel::_propertyChanged( const PropertyChangeEvent& _rEvt )
+{
+ ControlModelLock aLock( *this );
+ OSL_ENSURE( _rEvt.PropertyName == m_sValuePropertyName,
+ "OBoundControlModel::_propertyChanged: where did this come from (1)?" );
+ OSL_ENSURE( m_pAggPropMultiplexer && !m_pAggPropMultiplexer->locked(),
+ "OBoundControlModel::_propertyChanged: where did this come from (2)?" );
+ if ( _rEvt.PropertyName == m_sValuePropertyName )
+ {
+ onValuePropertyChange( aLock );
+ }
+}
+
+void OBoundControlModel::startAggregatePropertyListening( const OUString& _rPropertyName )
+{
+ OSL_PRECOND( m_pAggPropMultiplexer, "OBoundControlModel::startAggregatePropertyListening: no multiplexer!" );
+ OSL_ENSURE( !_rPropertyName.isEmpty(), "OBoundControlModel::startAggregatePropertyListening: invalid property name!" );
+ if ( m_pAggPropMultiplexer && !_rPropertyName.isEmpty() )
+ {
+ m_pAggPropMultiplexer->addProperty( _rPropertyName );
+ }
+}
+
+void OBoundControlModel::doFormListening( const bool _bStart )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::doFormListening: external value binding should overrule the database binding!" );
+ if ( isFormListening() == _bStart )
+ return;
+ if ( m_xAmbientForm.is() )
+ _bStart ? m_xAmbientForm->addLoadListener( this ) : m_xAmbientForm->removeLoadListener( this );
+ Reference< XLoadable > xParentLoadable( getParent(), UNO_QUERY );
+ if ( getParent().is() && !xParentLoadable.is() )
+ {
+ // if our parent does not directly support the XLoadable interface, then it might support the
+ // XRowSetSupplier/XRowSetChangeBroadcaster interfaces. In this case we have to listen for changes
+ // broadcasted by the latter.
+ Reference< XRowSetChangeBroadcaster > xRowSetBroadcaster( getParent(), UNO_QUERY );
+ if ( xRowSetBroadcaster.is() )
+ _bStart ? xRowSetBroadcaster->addRowSetChangeListener( this ) : xRowSetBroadcaster->removeRowSetChangeListener( this );
+ }
+
+ m_bFormListening = _bStart && m_xAmbientForm.is();
+}
+
+// XChild
+void SAL_CALL OBoundControlModel::setParent(const Reference<XInterface>& _rxParent)
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ if ( getParent() == _rxParent )
+ return;
+ // disconnect from database column (which is controlled by parent, directly or indirectly)
+ if ( hasField() )
+ impl_disconnectDatabaseColumn_noNotify();
+ // log off old listeners
+ if ( isFormListening() )
+ doFormListening( false );
+ // actually set the new parent
+ OControlModel::setParent( _rxParent );
+ // a new parent means a new ambient form
+ impl_determineAmbientForm_nothrow();
+ if ( !hasExternalValueBinding() )
+ {
+ // log on new listeners
+ doFormListening( true );
+ // re-connect to database column of the new parent
+ if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
+ impl_connectDatabaseColumn_noNotify( false );
+ }
+}
+
+// XEventListener
+void SAL_CALL OBoundControlModel::disposing(const css::lang::EventObject& _rEvent)
+{
+ ControlModelLock aLock( *this );
+ if ( _rEvent.Source == getField() )
+ {
+ resetField();
+ }
+
+ else if ( _rEvent.Source == m_xLabelControl )
+ {
+ Reference<XPropertySet> xOldValue = m_xLabelControl;
+ m_xLabelControl = nullptr;
+ // fire a propertyChanged (when we leave aLock's scope)
+ aLock.addPropertyNotification( PROPERTY_ID_CONTROLLABEL, Any( xOldValue ), Any( m_xLabelControl ) );
+ }
+
+ else if ( _rEvent.Source == m_xExternalBinding )
+ { // *first* check for the external binding
+ disconnectExternalValueBinding( );
+ }
+
+ else if ( _rEvent.Source == m_xValidator )
+ { // *then* check for the validator. Reason is that bindings may also act as validator at the same
+ // time, in this case, the validator is automatically revoked when the binding is revoked
+ disconnectValidator( );
+ }
+
+ else
+ OControlModel::disposing(_rEvent);
+}
+
+// XServiceInfo
+css::uno::Sequence<OUString> SAL_CALL OBoundControlModel::getSupportedServiceNames()
+{
+ return ::comphelper::combineSequences(
+ getAggregateServiceNames(),
+ getSupportedServiceNames_Static()
+ );
+}
+
+Sequence< OUString > OBoundControlModel::getSupportedServiceNames_Static()
+{
+ Sequence<OUString> aOwnServiceNames { "com.sun.star.form.DataAwareControlModel" };
+ return ::comphelper::concatSequences(
+ OControlModel::getSupportedServiceNames_Static(),
+ aOwnServiceNames
+ );
+}
+
+// XPersist
+void SAL_CALL OBoundControlModel::write( const Reference<css::io::XObjectOutputStream>& _rxOutStream )
+{
+ OControlModel::write(_rxOutStream);
+ osl::MutexGuard aGuard(m_aMutex);
+ // Version
+ _rxOutStream->writeShort(0x0002);
+ // Controlsource
+ ::comphelper::operator<<( _rxOutStream, m_aControlSource);
+ // !!! IMPORTANT NOTE !!!
+ // don't write any new members here: this wouldn't be compatible with older versions, as OBoundControlModel
+ // is a base class which is called in derived classes "read" method. So if you increment the version
+ // and write new stuff, older office versions will read this in the _derived_ classes, which may result
+ // in anything from data loss to crash.
+ // (use writeCommonProperties instead, this is called in derived classes write-method)
+ // !!! EOIN !!!
+}
+
+void OBoundControlModel::defaultCommonProperties()
+{
+ Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
+ if (xComp.is())
+ xComp->removeEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+ m_xLabelControl = nullptr;
+}
+
+void OBoundControlModel::readCommonProperties(const Reference<css::io::XObjectInputStream>& _rxInStream)
+{
+ sal_Int32 nLen = _rxInStream->readLong();
+ Reference<css::io::XMarkableStream> xMark(_rxInStream, UNO_QUERY);
+ DBG_ASSERT(xMark.is(), "OBoundControlModel::readCommonProperties : can only work with markable streams !");
+ sal_Int32 nMark = xMark->createMark();
+ // read the reference to the label control
+ Reference<css::io::XPersistObject> xPersist;
+ sal_Int32 nUsedFlag;
+ nUsedFlag = _rxInStream->readLong();
+ if (nUsedFlag)
+ xPersist = _rxInStream->readObject();
+ m_xLabelControl.set(xPersist, css::uno::UNO_QUERY);
+ Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
+ if (xComp.is())
+ xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+ // read any other new common properties here
+ // skip the remaining bytes
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nLen);
+ xMark->deleteMark(nMark);
+}
+
+void OBoundControlModel::writeCommonProperties(const Reference<css::io::XObjectOutputStream>& _rxOutStream)
+{
+ Reference<css::io::XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ DBG_ASSERT(xMark.is(), "OBoundControlModel::writeCommonProperties : can only work with markable streams !");
+ sal_Int32 nMark = xMark->createMark();
+ // a placeholder where we will write the overall length (later in this method)
+ sal_Int32 nLen = 0;
+ _rxOutStream->writeLong(nLen);
+ // write the reference to the label control
+ Reference<css::io::XPersistObject> xPersist(m_xLabelControl, UNO_QUERY);
+ sal_Int32 nUsedFlag = 0;
+ if (xPersist.is())
+ nUsedFlag = 1;
+ _rxOutStream->writeLong(nUsedFlag);
+ if (xPersist.is())
+ _rxOutStream->writeObject(xPersist);
+ // write any other new common properties here
+ // write the correct length at the beginning of the block
+ nLen = xMark->offsetToMark(nMark) - sizeof(nLen);
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+}
+
+void SAL_CALL OBoundControlModel::read( const Reference< css::io::XObjectInputStream >& _rxInStream )
+{
+ OControlModel::read(_rxInStream);
+ osl::MutexGuard aGuard(m_aMutex);
+ _rxInStream->readShort(); // version;
+ ::comphelper::operator>>( _rxInStream, m_aControlSource);
+}
+
+void OBoundControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INPUT_REQUIRED:
+ rValue <<= m_bInputRequired;
+ break;
+ case PROPERTY_ID_CONTROLSOURCEPROPERTY:
+ rValue <<= m_sValuePropertyName;
+ break;
+ case PROPERTY_ID_CONTROLSOURCE:
+ rValue <<= m_aControlSource;
+ break;
+ case PROPERTY_ID_BOUNDFIELD:
+ rValue <<= getField();
+ break;
+ case PROPERTY_ID_CONTROLLABEL:
+ if (!m_xLabelControl.is())
+ rValue.clear();
+ else
+ rValue <<= m_xLabelControl;
+ break;
+ default:
+ OControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+}
+
+sal_Bool OBoundControlModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle,
+ const Any& _rValue)
+{
+ bool bModified(false);
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_INPUT_REQUIRED:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_bInputRequired );
+ break;
+ case PROPERTY_ID_CONTROLSOURCE:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aControlSource);
+ break;
+ case PROPERTY_ID_BOUNDFIELD:
+ SAL_WARN("forms.component", "OBoundControlModel::convertFastPropertyValue: BoundField should be a read-only property !" );
+ throw css::lang::IllegalArgumentException();
+ case PROPERTY_ID_CONTROLLABEL:
+ if (!_rValue.hasValue())
+ { // property set to void
+ _rConvertedValue = Any();
+ getFastPropertyValue(_rOldValue, _nHandle);
+ bModified = m_xLabelControl.is();
+ }
+
+ else
+ {
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_xLabelControl);
+ if (!m_xLabelControl.is())
+ // an empty interface is interpreted as VOID
+ _rOldValue.clear();
+ }
+
+ break;
+ default:
+ bModified = OControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
+ }
+ return bModified;
+}
+
+Any OBoundControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+{
+ Any aDefault;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_INPUT_REQUIRED:
+ aDefault <<= false;
+ break;
+ case PROPERTY_ID_CONTROLSOURCE:
+ aDefault <<= OUString();
+ break;
+ case PROPERTY_ID_CONTROLLABEL:
+ aDefault <<= Reference< XPropertySet >();
+ break;
+ }
+ return aDefault;
+}
+
+void OBoundControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_INPUT_REQUIRED:
+ OSL_VERIFY( rValue >>= m_bInputRequired );
+ break;
+ case PROPERTY_ID_CONTROLSOURCE:
+ OSL_VERIFY( rValue >>= m_aControlSource );
+ break;
+ case PROPERTY_ID_BOUNDFIELD:
+ SAL_WARN("forms.component", "OBoundControlModel::setFastPropertyValue_NoBroadcast : BoundField should be a read-only property !");
+ throw css::lang::IllegalArgumentException();
+ case PROPERTY_ID_CONTROLLABEL:
+ {
+ if ( rValue.hasValue() && ( rValue.getValueTypeClass() != TypeClass_INTERFACE ) )
+ throw css::lang::IllegalArgumentException();
+ Reference< XInterface > xNewValue( rValue, UNO_QUERY );
+ if ( !xNewValue.is() )
+ { // set property to "void"
+ Reference< XComponent > xComp( m_xLabelControl, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
+ m_xLabelControl = nullptr;
+ break;
+ }
+
+ Reference< XControlModel > xAsModel ( xNewValue, UNO_QUERY );
+ Reference< XServiceInfo > xAsServiceInfo ( xAsModel, UNO_QUERY );
+ Reference< XPropertySet > xAsPropSet ( xAsServiceInfo, UNO_QUERY );
+ Reference< XChild > xAsChild ( xAsPropSet, UNO_QUERY );
+ if ( !xAsChild.is() || !xAsServiceInfo->supportsService( m_aLabelServiceName ) )
+ {
+ throw css::lang::IllegalArgumentException();
+ }
+
+ // Check if we and the given model have a common ancestor (up to the forms collection)
+ Reference<XChild> xCont(this);
+ Reference< XInterface > xMyTopLevel = xCont->getParent();
+ while (xMyTopLevel.is())
+ {
+ Reference<XForm> xAsForm(xMyTopLevel, UNO_QUERY);
+ if (!xAsForm.is())
+ // found my root
+ break;
+ Reference<XChild> xLoopAsChild(xMyTopLevel, UNO_QUERY);
+ xMyTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
+ }
+
+ Reference< XInterface > xNewTopLevel = xAsChild->getParent();
+ while (xNewTopLevel.is())
+ {
+ Reference<XForm> xAsForm(xNewTopLevel, UNO_QUERY);
+ if (!xAsForm.is())
+ break;
+ Reference<XChild> xLoopAsChild(xNewTopLevel, UNO_QUERY);
+ xNewTopLevel = xLoopAsChild.is() ? xLoopAsChild->getParent() : Reference< XInterface >();
+ }
+
+ if (xNewTopLevel != xMyTopLevel)
+ {
+ // the both objects don't belong to the same forms collection -> not acceptable
+ throw css::lang::IllegalArgumentException();
+ }
+
+ m_xLabelControl = xAsPropSet;
+ Reference<css::lang::XComponent> xComp(m_xLabelControl, UNO_QUERY);
+ if (xComp.is())
+ xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+ }
+
+ break;
+ default:
+ OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue );
+ }
+}
+
+// XPropertyChangeListener
+void SAL_CALL OBoundControlModel::propertyChange( const PropertyChangeEvent& evt )
+{
+ // if the DBColumn value changed, transfer it to the control
+ if ( evt.PropertyName == PROPERTY_VALUE )
+ {
+ OSL_ENSURE( evt.Source == getField(), "OBoundControlModel::propertyChange: value changes from components other than our database column?" );
+ osl::MutexGuard aGuard(m_aMutex);
+ if ( m_bForwardValueChanges && m_xColumn.is() )
+ transferDbValueToControl();
+ }
+
+ else
+ {
+ OSL_ENSURE( evt.Source == m_xExternalBinding, "OBoundControlModel::propertyChange: where did this come from?" );
+ // our binding has properties which can control properties of ourself
+ OUString sBindingControlledProperty;
+ bool bForwardToLabelControl = false;
+ if ( evt.PropertyName == PROPERTY_READONLY )
+ {
+ sBindingControlledProperty = PROPERTY_READONLY;
+ }
+
+ else if ( evt.PropertyName == PROPERTY_RELEVANT )
+ {
+ sBindingControlledProperty = PROPERTY_ENABLED;
+ bForwardToLabelControl = true;
+ }
+
+ else
+ return;
+ try
+ {
+ setPropertyValue( sBindingControlledProperty, evt.NewValue );
+ if ( bForwardToLabelControl && m_xLabelControl.is() )
+ m_xLabelControl->setPropertyValue( sBindingControlledProperty, evt.NewValue );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ SAL_WARN("forms.component", "OBoundControlModel::propertyChange: could not adjust my binding-controlled property!");
+ }
+
+ }
+}
+
+void SAL_CALL OBoundControlModel::onRowSetChanged( const EventObject& /*i_Event*/ )
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ // disconnect from database column (which is controlled by parent, directly or indirectly)
+ if ( hasField() )
+ impl_disconnectDatabaseColumn_noNotify();
+ // log off old listeners
+ if ( isFormListening() )
+ doFormListening( false );
+ // determine the new ambient form
+ impl_determineAmbientForm_nothrow();
+ // log on new listeners
+ doFormListening( true );
+ // re-connect to database column if needed and possible
+ if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
+ impl_connectDatabaseColumn_noNotify( false );
+}
+
+// XBoundComponent
+void SAL_CALL OBoundControlModel::addUpdateListener(const Reference<XUpdateListener>& _rxListener)
+{
+ m_aUpdateListeners.addInterface(_rxListener);
+}
+
+void SAL_CALL OBoundControlModel::removeUpdateListener(const Reference< XUpdateListener>& _rxListener)
+{
+ m_aUpdateListeners.removeInterface(_rxListener);
+}
+
+sal_Bool SAL_CALL OBoundControlModel::commit()
+{
+ ControlModelLock aLock( *this );
+ OSL_PRECOND( m_bCommitable, "OBoundControlModel::commit: invalid call (I'm not committable!) " );
+ if ( hasExternalValueBinding() )
+ {
+ // in most cases, no action is required: For most derivees, we know the value property of
+ // our control (see initValueProperty), and when an external binding is active, we
+ // instantly forward all changes in this property to the external binding.
+ if ( m_sValuePropertyName.isEmpty() )
+ // but for those derivees which did not use this feature, we need an
+ // explicit transfer
+ transferControlValueToExternal( aLock );
+ return true;
+ }
+
+ OSL_ENSURE( !hasExternalValueBinding(), "OBoundControlModel::commit: control flow broken!" );
+ // we reach this only if we're not working with an external binding
+ if ( !hasField() )
+ return true;
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aUpdateListeners );
+ EventObject aEvent;
+ aEvent.Source = static_cast< XWeak* >( this );
+ bool bSuccess = true;
+ aLock.release();
+ // UNSAFE >
+ while (aIter.hasMoreElements() && bSuccess)
+ bSuccess = aIter.next()->approveUpdate( aEvent );
+ // < UNSAFE
+ aLock.acquire();
+ if ( bSuccess )
+ {
+ try
+ {
+ if ( m_xColumnUpdate.is() )
+ bSuccess = commitControlValueToDbColumn( false );
+ }
+
+ catch(const Exception&)
+ {
+ bSuccess = false;
+ }
+
+ }
+
+ if ( bSuccess )
+ {
+ aLock.release();
+ m_aUpdateListeners.notifyEach( &XUpdateListener::updated, aEvent );
+ }
+ return bSuccess;
+}
+
+void OBoundControlModel::resetField()
+{
+ m_xColumnUpdate.clear();
+ m_xColumn.clear();
+ m_xField.clear();
+ m_nFieldType = DataType::OTHER;
+}
+
+void OBoundControlModel::connectToField(const Reference<XRowSet>& rForm)
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::connectToField: invalid call (have an external binding)!" );
+ // if there's a connection to the database
+ if (!(rForm.is() && getConnection(rForm).is()))
+ return;
+
+ // determine field and PropertyChangeListener
+ m_xCursor = rForm;
+ Reference<XPropertySet> xFieldCandidate;
+ if (m_xCursor.is())
+ {
+ Reference<XColumnsSupplier> xColumnsSupplier(m_xCursor, UNO_QUERY);
+ DBG_ASSERT(xColumnsSupplier.is(), "OBoundControlModel::connectToField : the row set should support the css::sdb::ResultSet service !");
+ if (xColumnsSupplier.is())
+ {
+ Reference<XNameAccess> xColumns = xColumnsSupplier->getColumns();
+ if (xColumns.is() && xColumns->hasByName(m_aControlSource))
+ {
+ OSL_VERIFY( xColumns->getByName(m_aControlSource) >>= xFieldCandidate );
+ }
+
+ }
+
+ }
+
+ try
+ {
+ sal_Int32 nFieldType = DataType::OTHER;
+ if ( xFieldCandidate.is() )
+ {
+ xFieldCandidate->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
+ if ( approveDbColumnType( nFieldType ) )
+ impl_setField_noNotify( xFieldCandidate );
+ }
+
+ else
+ impl_setField_noNotify( nullptr );
+ if ( m_xField.is() )
+ {
+ if ( m_xField->getPropertySetInfo()->hasPropertyByName( PROPERTY_VALUE ) )
+ {
+ m_nFieldType = nFieldType;
+ // listen to changing values
+ m_xField->addPropertyChangeListener( PROPERTY_VALUE, this );
+ m_xColumnUpdate.set( m_xField, UNO_QUERY );
+ m_xColumn.set( m_xField, UNO_QUERY );
+ sal_Int32 nNullableFlag = ColumnValue::NO_NULLS;
+ m_xField->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullableFlag;
+ // tdf#122319 - don't allow nullable form components if input is required
+ m_bRequired = (ColumnValue::NO_NULLS == nNullableFlag || m_bInputRequired);
+ // we're optimistic: in case of ColumnValue_NULLABLE_UNKNOWN we assume nullability...
+ }
+ else
+ {
+ SAL_WARN("forms.component", "OBoundControlModel::connectToField: property " << PROPERTY_VALUE << " not supported!");
+ impl_setField_noNotify( nullptr );
+ }
+
+ }
+
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ resetField();
+ }
+}
+
+void OBoundControlModel::initFromField( const Reference< XRowSet >& _rxRowSet )
+{
+ // but only if the rowset is positioned on a valid record
+ if ( !(hasField() && _rxRowSet.is()) )
+ return;
+
+ bool shouldTransfer(!_rxRowSet->isBeforeFirst() && !_rxRowSet->isAfterLast());
+ if (!shouldTransfer)
+ {
+ const Reference< XPropertySet > xPS(_rxRowSet, UNO_QUERY);
+ if (xPS.is())
+ {
+ assert(!shouldTransfer);
+ xPS->getPropertyValue("IsNew") >>= shouldTransfer;
+ }
+ }
+ if ( shouldTransfer )
+ transferDbValueToControl();
+ else
+ // reset the field if the row set is empty
+ // #i30661#
+ resetNoBroadcast();
+}
+
+bool OBoundControlModel::approveDbColumnType(sal_Int32 _nColumnType)
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::approveDbColumnType: invalid call (have an external binding)!" );
+ if ((_nColumnType == DataType::BINARY) || (_nColumnType == DataType::VARBINARY)
+ || (_nColumnType == DataType::LONGVARBINARY) || (_nColumnType == DataType::OTHER)
+ || (_nColumnType == DataType::OBJECT) || (_nColumnType == DataType::DISTINCT)
+ || (_nColumnType == DataType::STRUCT) || (_nColumnType == DataType::ARRAY)
+ || (_nColumnType == DataType::BLOB) /*|| (_nColumnType == DataType::CLOB)*/
+ || (_nColumnType == DataType::REF) || (_nColumnType == DataType::SQLNULL))
+ return false;
+ return true;
+}
+
+void OBoundControlModel::impl_determineAmbientForm_nothrow()
+{
+ Reference< XInterface > xParent( getParent() );
+ m_xAmbientForm.set( xParent, UNO_QUERY );
+ if ( !m_xAmbientForm.is() )
+ {
+ Reference< XRowSetSupplier > xSupRowSet( xParent, UNO_QUERY );
+ if ( xSupRowSet.is() )
+ m_xAmbientForm.set( xSupRowSet->getRowSet(), UNO_QUERY );
+ }
+}
+
+void OBoundControlModel::impl_connectDatabaseColumn_noNotify( bool _bFromReload )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: not to be called with an external value binding!" );
+ // consistency checks
+ DBG_ASSERT( !( hasField() && !_bFromReload ),
+ "OBoundControlModel::impl_connectDatabaseColumn_noNotify: the form is just *loaded*, but we already have a field!" );
+
+ Reference< XRowSet > xRowSet( m_xAmbientForm, UNO_QUERY );
+ OSL_ENSURE( xRowSet.is(), "OBoundControlModel::impl_connectDatabaseColumn_noNotify: no row set!" );
+ if ( !xRowSet.is() )
+ return;
+ if ( !hasField() || _bFromReload )
+ {
+ // connect to the column
+ connectToField( xRowSet );
+ }
+
+ // now that we're connected (more or less, even if we did not find a column),
+ // we definitely want to forward any potentially occurring value changes
+ m_bForwardValueChanges = true;
+ // let derived classes react on this new connection
+ m_bLoaded = true;
+ onConnectedDbColumn( xRowSet );
+ // initially transfer the db column value to the control, if we successfully connected to a database column
+ if ( hasField() )
+ initFromField( xRowSet );
+}
+
+void OBoundControlModel::impl_disconnectDatabaseColumn_noNotify()
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::impl_disconnectDatabaseColumn_noNotify: not to be called with an external value binding!" );
+ // let derived classes react on this
+ onDisconnectedDbColumn();
+ if ( hasField() )
+ {
+ getField()->removePropertyChangeListener( PROPERTY_VALUE, this );
+ resetField();
+ }
+
+ m_xCursor = nullptr;
+ m_bLoaded = false;
+}
+
+// XLoadListener
+void SAL_CALL OBoundControlModel::loaded( const EventObject& _rEvent )
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::loaded: where does this come from?" );
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::loaded: we should never reach this with an external value binding!" );
+ if ( hasExternalValueBinding() )
+ return;
+ impl_connectDatabaseColumn_noNotify( false );
+}
+
+void SAL_CALL OBoundControlModel::unloaded( const css::lang::EventObject& /*aEvent*/ )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloaded: we should never reach this with an external value binding!" );
+}
+
+void SAL_CALL OBoundControlModel::reloading( const css::lang::EventObject& /*aEvent*/ )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloading: we should never reach this with an external value binding!" );
+ if ( hasExternalValueBinding() )
+ return;
+ osl::MutexGuard aGuard(m_aMutex);
+ m_bForwardValueChanges = false;
+}
+
+void SAL_CALL OBoundControlModel::unloading(const css::lang::EventObject& /*aEvent*/)
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::unloading: we should never reach this with an external value binding!" );
+ if ( hasExternalValueBinding() )
+ return;
+ impl_disconnectDatabaseColumn_noNotify();
+}
+
+void SAL_CALL OBoundControlModel::reloaded( const EventObject& _rEvent )
+{
+ ControlModelLock aLock( *this );
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ OSL_ENSURE( _rEvent.Source == m_xAmbientForm, "OBoundControlModel::reloaded: where does this come from?" );
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::reloaded: we should never reach this with an external value binding!" );
+ if ( hasExternalValueBinding() )
+ return;
+ impl_connectDatabaseColumn_noNotify( true );
+}
+
+void OBoundControlModel::setControlValue( const Any& _rValue, ValueChangeInstigator _eInstigator )
+{
+ m_eControlValueChangeInstigator = _eInstigator;
+ doSetControlValue( _rValue );
+ m_eControlValueChangeInstigator = eOther;
+}
+
+void OBoundControlModel::doSetControlValue( const Any& _rValue )
+{
+ OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
+ "OBoundControlModel::doSetControlValue: invalid aggregate !" );
+ OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( m_nValuePropertyAggregateHandle != -1 ),
+ "OBoundControlModel::doSetControlValue: please override if you have own value property handling!" );
+ try
+ {
+ // release our mutex once (it's acquired in one of the calling methods), as setting aggregate properties
+ // may cause any uno controls belonging to us to lock the solar mutex, which is potentially dangerous with
+ // our own mutex locked
+ MutexRelease aRelease( m_aMutex );
+ if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() )
+ {
+ m_xAggregateFastSet->setFastPropertyValue( m_nValuePropertyAggregateHandle, _rValue );
+ }
+
+ else if ( !m_sValuePropertyName.isEmpty() && m_xAggregateSet.is() )
+ {
+ m_xAggregateSet->setPropertyValue( m_sValuePropertyName, _rValue );
+ }
+
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::doSetControlValue");
+ }
+}
+
+void OBoundControlModel::onConnectedValidator( )
+{
+ try
+ {
+ // if we have an external validator, we do not want the control to force invalid
+ // inputs to the default value. Instead, invalid inputs should be translated
+ // to NaN (not a number)
+ Reference< XPropertySetInfo > xAggregatePropertyInfo;
+ if ( m_xAggregateSet.is() )
+ xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo();
+ if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) )
+ m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, Any( false ) );
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::onConnectedValidator");
+ }
+
+ recheckValidity( false );
+}
+
+void OBoundControlModel::onDisconnectedValidator( )
+{
+ try
+ {
+ Reference< XPropertySetInfo > xAggregatePropertyInfo;
+ if ( m_xAggregateSet.is() )
+ xAggregatePropertyInfo = m_xAggregateSet->getPropertySetInfo();
+ if ( xAggregatePropertyInfo.is() && xAggregatePropertyInfo->hasPropertyByName( PROPERTY_ENFORCE_FORMAT ) )
+ m_xAggregateSet->setPropertyValue( PROPERTY_ENFORCE_FORMAT, Any( true ) );
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::onDisconnectedValidator");
+ }
+
+ recheckValidity( false );
+}
+
+void OBoundControlModel::onConnectedExternalValue( )
+{
+ calculateExternalValueType();
+}
+
+void OBoundControlModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ )
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onConnectedDbColumn: how this? There's an external value binding!" );
+}
+
+void OBoundControlModel::onDisconnectedDbColumn()
+{
+ OSL_PRECOND( !hasExternalValueBinding(), "OBoundControlModel::onDisconnectedDbColumn: how this? There's an external value binding!" );
+}
+
+// XReset
+Any OBoundControlModel::getDefaultForReset() const
+{
+ return Any();
+}
+
+void OBoundControlModel::resetNoBroadcast()
+{
+ setControlValue( getDefaultForReset(), eOther );
+}
+
+void OBoundControlModel::addResetListener(const Reference<XResetListener>& l)
+{
+ m_aResetHelper.addResetListener( l );
+}
+
+void OBoundControlModel::removeResetListener(const Reference<XResetListener>& l)
+{
+ m_aResetHelper.removeResetListener( l );
+}
+
+void OBoundControlModel::reset()
+{
+ if ( !m_aResetHelper.approveReset() )
+ return;
+ ControlModelLock aLock( *this );
+ // on a new record?
+ bool bIsNewRecord = false;
+ Reference<XPropertySet> xSet( m_xCursor, UNO_QUERY );
+ if ( xSet.is() )
+ {
+ try
+ {
+ xSet->getPropertyValue( PROPERTY_ISNEW ) >>= bIsNewRecord;
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ }
+
+ // cursor on an invalid row?
+ bool bInvalidCursorPosition = true;
+ try
+ {
+ bInvalidCursorPosition = m_xCursor.is()
+ && ( m_xCursor->isAfterLast()
+ || m_xCursor->isBeforeFirst()
+ )
+ && !bIsNewRecord;
+ }
+
+ catch( const SQLException& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::reset: caught an SQL exception!" );
+ }
+
+ // #i24495# - don't count the insert row as "invalid"
+ bool bSimpleReset =
+ ( !m_xColumn.is() // no connection to a database column
+ || ( m_xCursor.is() // OR we have an improperly positioned cursor
+ && bInvalidCursorPosition
+ )
+ || hasExternalValueBinding() // OR we have an external value binding
+ );
+ if ( !bSimpleReset )
+ {
+ // The default values will be set if and only if the current value of the field which we're bound
+ // to is NULL.
+ // Else, the current field value should be refreshed
+ // This behaviour is not completely ... "matured": What should happen if the field as well as the
+ // control have a default value?
+ bool bIsNull = true;
+ // we have to access the field content at least once to get a reliable result by XColumn::wasNull
+ try
+ {
+ // normally, we'd do a getString here. However, this is extremely expensive in the case
+ // of binary fields. Unfortunately, getString is the only method which is guaranteed
+ // to *always* succeed, all other getXXX methods may fail if the column is asked for a
+ // non-convertible type
+ sal_Int32 nFieldType = DataType::OBJECT;
+ getField()->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType;
+ if ( ( nFieldType == DataType::BINARY )
+ || ( nFieldType == DataType::VARBINARY )
+ || ( nFieldType == DataType::LONGVARBINARY )
+ || ( nFieldType == DataType::OBJECT )
+ /*|| ( nFieldType == DataType::CLOB )*/
+ )
+ m_xColumn->getBinaryStream();
+ else if ( nFieldType == DataType::BLOB )
+ m_xColumn->getBlob();
+ else
+ m_xColumn->getString();
+ bIsNull = m_xColumn->wasNull();
+ }
+
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ SAL_WARN("forms.component", "OBoundControlModel::reset: this should have succeeded in all cases!");
+ }
+
+ bool bNeedValueTransfer = true;
+ if ( bIsNull )
+ {
+ if ( bIsNewRecord )
+ {
+ // reset the control to its default
+ resetNoBroadcast();
+ // and immediately commit the changes to the DB column, to keep consistency
+ commitControlValueToDbColumn( true );
+ bNeedValueTransfer = false;
+ }
+
+ }
+
+ if ( bNeedValueTransfer )
+ transferDbValueToControl();
+ }
+
+ else
+ {
+ resetNoBroadcast();
+ // transfer to the external binding, if necessary
+ if ( hasExternalValueBinding() )
+ transferControlValueToExternal( aLock );
+ }
+
+ // revalidate, if necessary
+ if ( hasValidator() )
+ recheckValidity( true );
+ aLock.release();
+ m_aResetHelper.notifyResetted();
+}
+
+void OBoundControlModel::impl_setField_noNotify( const Reference< XPropertySet>& _rxField )
+{
+ DBG_ASSERT( !hasExternalValueBinding(), "OBoundControlModel::impl_setField_noNotify: We have an external value binding!" );
+ m_xField = _rxField;
+}
+
+bool OBoundControlModel::impl_approveValueBinding_nolock( const Reference< XValueBinding >& _rxBinding )
+{
+ if ( !_rxBinding.is() )
+ return false;
+ Sequence< Type > aTypeCandidates;
+ {
+ // SYNCHRONIZED >
+ ::osl::MutexGuard aGuard( m_aMutex );
+ aTypeCandidates = getSupportedBindingTypes();
+ // < SYNCHRONIZED
+ }
+
+ for ( auto const & type : std::as_const(aTypeCandidates) )
+ {
+ if ( _rxBinding->supportsType( type ) )
+ return true;
+ }
+ return false;
+}
+
+void OBoundControlModel::connectExternalValueBinding(
+ const Reference< XValueBinding >& _rxBinding, ControlModelLock& _rInstanceLock )
+{
+ OSL_PRECOND( _rxBinding.is(), "OBoundControlModel::connectExternalValueBinding: invalid binding instance!" );
+ OSL_PRECOND( !hasExternalValueBinding( ), "OBoundControlModel::connectExternalValueBinding: precond not met (currently have a binding)!" );
+ // if we're connected to a database column, suspend this
+ if ( hasField() )
+ impl_disconnectDatabaseColumn_noNotify();
+ // suspend listening for load-related events at out ambient form.
+ // This is because an external value binding overrules a possible database binding.
+ if ( isFormListening() )
+ doFormListening( false );
+ // remember this new binding
+ m_xExternalBinding = _rxBinding;
+ // tell the derivee
+ onConnectedExternalValue();
+ try
+ {
+ // add as value listener so we get notified when the value changes
+ Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY );
+ if ( xModifiable.is() )
+ xModifiable->addModifyListener( this );
+ // add as property change listener for some (possibly present) properties we're
+ // interested in
+ Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY );
+ Reference< XPropertySetInfo > xBindingPropsInfo( xBindingProps.is() ? xBindingProps->getPropertySetInfo() : Reference< XPropertySetInfo >() );
+ if ( xBindingPropsInfo.is() )
+ {
+ if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_READONLY ) )
+ {
+ xBindingProps->addPropertyChangeListener( PROPERTY_READONLY, this );
+ m_bBindingControlsRO = true;
+ }
+
+ if ( xBindingPropsInfo->hasPropertyByName( PROPERTY_RELEVANT ) )
+ {
+ xBindingProps->addPropertyChangeListener( PROPERTY_RELEVANT, this );
+ m_bBindingControlsEnable = true;
+ }
+
+ }
+
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ // propagate our new value
+ transferExternalValueToControl( _rInstanceLock );
+ // if the binding is also a validator, use it, too. This is a constraint of the
+ // com.sun.star.form.binding.ValidatableBindableFormComponent service
+ if ( !m_bSupportsValidation )
+ return;
+
+ try
+ {
+ Reference< XValidator > xAsValidator( _rxBinding, UNO_QUERY );
+ if ( xAsValidator.is() )
+ setValidator( xAsValidator );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+}
+
+void OBoundControlModel::disconnectExternalValueBinding( )
+{
+ try
+ {
+ // not listening at the binding anymore
+ Reference< XModifyBroadcaster > xModifiable( m_xExternalBinding, UNO_QUERY );
+ if ( xModifiable.is() )
+ xModifiable->removeModifyListener( this );
+ // remove as property change listener
+ Reference< XPropertySet > xBindingProps( m_xExternalBinding, UNO_QUERY );
+ if ( m_bBindingControlsRO )
+ xBindingProps->removePropertyChangeListener( PROPERTY_READONLY, this );
+ if ( m_bBindingControlsEnable )
+ xBindingProps->removePropertyChangeListener( PROPERTY_RELEVANT, this );
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::disconnectExternalValueBinding");
+ }
+
+ // if the binding also acts as our validator, disconnect the validator, too
+ if ( ( m_xExternalBinding == m_xValidator ) && m_xValidator.is() )
+ disconnectValidator( );
+ // no binding anymore
+ m_xExternalBinding.clear();
+ // be a load listener at our form, again. This was suspended while we had
+ // an external value binding in place.
+ doFormListening( true );
+ // re-connect to database column of the new parent
+ if ( m_xAmbientForm.is() && m_xAmbientForm->isLoaded() )
+ impl_connectDatabaseColumn_noNotify( false );
+}
+
+void SAL_CALL OBoundControlModel::setValueBinding( const Reference< XValueBinding >& _rxBinding )
+{
+ OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::setValueBinding: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support binding to external data
+ // allow reset
+ if ( _rxBinding.is() && !impl_approveValueBinding_nolock( _rxBinding ) )
+ {
+ throw IncompatibleTypesException(
+ ResourceManager::loadString(RID_STR_INCOMPATIBLE_TYPES),
+ *this
+ );
+ }
+
+ ControlModelLock aLock( *this );
+ // since a ValueBinding overrules any potentially active database binding, the change in a ValueBinding
+ // might trigger a change in our BoundField.
+ FieldChangeNotifier aBoundFieldNotifier( aLock );
+ // disconnect from the old binding
+ if ( hasExternalValueBinding() )
+ disconnectExternalValueBinding( );
+ // connect to the new binding
+ if ( _rxBinding.is() )
+ connectExternalValueBinding( _rxBinding, aLock );
+}
+
+Reference< XValueBinding > SAL_CALL OBoundControlModel::getValueBinding( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_bSupportsExternalBinding, "OBoundControlModel::getValueBinding: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support binding to external data
+ return m_xExternalBinding;
+}
+
+void SAL_CALL OBoundControlModel::modified( const EventObject& _rEvent )
+{
+ ControlModelLock aLock( *this );
+ OSL_PRECOND( hasExternalValueBinding(), "OBoundControlModel::modified: Where did this come from?" );
+ if ( !m_bTransferringValue && ( m_xExternalBinding == _rEvent.Source ) && m_xExternalBinding.is() )
+ {
+ transferExternalValueToControl( aLock );
+ }
+}
+
+void OBoundControlModel::transferDbValueToControl( )
+{
+ try
+ {
+ setControlValue( translateDbColumnToControlValue(), eDbColumnBinding );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+}
+
+void OBoundControlModel::transferExternalValueToControl( ControlModelLock& _rInstanceLock )
+{
+ Reference< XValueBinding > xExternalBinding( m_xExternalBinding );
+ Type aValueExchangeType( getExternalValueType() );
+ _rInstanceLock.release();
+ // UNSAFE >
+ Any aExternalValue;
+ try
+ {
+ aExternalValue = xExternalBinding->getValue( aValueExchangeType );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ // < UNSAFE
+ _rInstanceLock.acquire();
+ setControlValue( translateExternalValueToControlValue( aExternalValue ), eExternalBinding );
+}
+
+void OBoundControlModel::transferControlValueToExternal( ControlModelLock& _rInstanceLock )
+{
+ OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
+ "OBoundControlModel::transferControlValueToExternal: precondition not met!" );
+ if ( !m_xExternalBinding.is() )
+ return;
+
+ Any aExternalValue( translateControlValueToExternalValue() );
+ m_bTransferringValue = true;
+ _rInstanceLock.release();
+ // UNSAFE >
+ try
+ {
+ m_xExternalBinding->setValue( aExternalValue );
+ }
+
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ // < UNSAFE
+ _rInstanceLock.acquire();
+ m_bTransferringValue = false;
+}
+
+Sequence< Type > OBoundControlModel::getSupportedBindingTypes()
+{
+ return Sequence< Type >( &m_aValuePropertyType, 1 );
+}
+
+void OBoundControlModel::calculateExternalValueType()
+{
+ m_aExternalValueType = Type();
+ if ( !m_xExternalBinding.is() )
+ return;
+ const Sequence< Type > aTypeCandidates( getSupportedBindingTypes() );
+ for ( auto const & typeCandidate : aTypeCandidates )
+ {
+ if ( m_xExternalBinding->supportsType( typeCandidate ) )
+ {
+ m_aExternalValueType = typeCandidate;
+ break;
+ }
+ }
+}
+
+Any OBoundControlModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ OSL_PRECOND( m_bSupportsExternalBinding && hasExternalValueBinding(),
+ "OBoundControlModel::translateExternalValueToControlValue: precondition not met!" );
+ Any aControlValue( _rExternalValue );
+ // if the external value is VOID, and our value property is not allowed to be VOID,
+ // then default-construct a value
+ if ( !aControlValue.hasValue() && !m_bValuePropertyMayBeVoid )
+ aControlValue.setValue( nullptr, m_aValuePropertyType );
+ // out of here
+ return aControlValue;
+}
+
+Any OBoundControlModel::translateControlValueToExternalValue( ) const
+{
+ return getControlValue( );
+}
+
+Any OBoundControlModel::translateControlValueToValidatableValue( ) const
+{
+ OSL_PRECOND( m_xValidator.is(), "OBoundControlModel::translateControlValueToValidatableValue: no validator, so why should I?" );
+ if ( ( m_xValidator == m_xExternalBinding ) && m_xValidator.is() )
+ return translateControlValueToExternalValue();
+ return getControlValue();
+}
+
+Any OBoundControlModel::getControlValue( ) const
+{
+ OSL_PRECOND( m_xAggregateFastSet.is() && m_xAggregateSet.is(),
+ "OBoundControlModel::getControlValue: invalid aggregate !" );
+ OSL_PRECOND( !m_sValuePropertyName.isEmpty() || ( m_nValuePropertyAggregateHandle != -1 ),
+ "OBoundControlModel::getControlValue: please override if you have own value property handling!" );
+ // determine the current control value
+ Any aControlValue;
+ if ( ( m_nValuePropertyAggregateHandle != -1 ) && m_xAggregateFastSet.is() )
+ {
+ aControlValue = m_xAggregateFastSet->getFastPropertyValue( m_nValuePropertyAggregateHandle );
+ }
+
+ else if ( !m_sValuePropertyName.isEmpty() && m_xAggregateSet.is() )
+ {
+ aControlValue = m_xAggregateSet->getPropertyValue( m_sValuePropertyName );
+ }
+ return aControlValue;
+}
+
+void OBoundControlModel::connectValidator( const Reference< XValidator >& _rxValidator )
+{
+ OSL_PRECOND( _rxValidator.is(), "OBoundControlModel::connectValidator: invalid validator instance!" );
+ OSL_PRECOND( !hasValidator( ), "OBoundControlModel::connectValidator: precond not met (have a validator currently)!" );
+ m_xValidator = _rxValidator;
+
+ // add as value listener so we get notified when the value changes
+ if ( m_xValidator.is() )
+ {
+ try
+ {
+ m_xValidator->addValidityConstraintListener( this );
+ }
+
+ catch( const RuntimeException& )
+ {
+ }
+ }
+ onConnectedValidator( );
+}
+
+void OBoundControlModel::disconnectValidator( )
+{
+ OSL_PRECOND( hasValidator( ), "OBoundControlModel::connectValidator: precond not met (don't have a validator currently)!" );
+
+ // add as value listener so we get notified when the value changes
+ if ( m_xValidator.is() )
+ {
+ try
+ {
+ m_xValidator->removeValidityConstraintListener( this );
+ }
+
+ catch( const RuntimeException& )
+ {
+ }
+ }
+
+ m_xValidator.clear();
+
+ onDisconnectedValidator( );
+}
+
+void SAL_CALL OBoundControlModel::setValidator( const Reference< XValidator >& _rxValidator )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::setValidator: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support validation
+
+ // early out if the validator does not change
+ if ( _rxValidator == m_xValidator )
+ return;
+
+ if ( m_xValidator.is() && ( m_xValidator == m_xExternalBinding ) )
+ throw VetoException(
+ ResourceManager::loadString(RID_STR_INVALID_VALIDATOR),
+ *this
+ );
+
+ // disconnect from the old validator
+ if ( hasValidator() )
+ disconnectValidator( );
+
+ // connect to the new validator
+ if ( _rxValidator.is() )
+ connectValidator( _rxValidator );
+}
+
+Reference< XValidator > SAL_CALL OBoundControlModel::getValidator( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::getValidator: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support validation
+
+ return m_xValidator;
+}
+
+void SAL_CALL OBoundControlModel::validityConstraintChanged( const EventObject& /*Source*/ )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ OSL_PRECOND( m_bSupportsValidation, "OBoundControlModel::validityConstraintChanged: How did you reach this method?" );
+ // the interface for this method should not have been exposed if we do not
+ // support validation
+
+ recheckValidity( false );
+}
+
+sal_Bool SAL_CALL OBoundControlModel::isValid( )
+{
+ return m_bIsCurrentValueValid;
+}
+
+css::uno::Any OBoundControlModel::getCurrentFormComponentValue() const
+{
+ if ( hasValidator() )
+ return translateControlValueToValidatableValue();
+ return getControlValue();
+}
+
+Any SAL_CALL OBoundControlModel::getCurrentValue( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return getCurrentFormComponentValue();
+}
+
+void SAL_CALL OBoundControlModel::addFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
+{
+ if ( Listener.is() )
+ m_aFormComponentListeners.addInterface( Listener );
+}
+
+void SAL_CALL OBoundControlModel::removeFormComponentValidityListener( const Reference< validation::XFormComponentValidityListener >& Listener )
+{
+ if ( Listener.is() )
+ m_aFormComponentListeners.removeInterface( Listener );
+}
+
+void OBoundControlModel::recheckValidity( bool _bForceNotification )
+{
+ try
+ {
+ bool bIsCurrentlyValid = true;
+ if ( hasValidator() )
+ bIsCurrentlyValid = m_xValidator->isValid( translateControlValueToValidatableValue() );
+
+ if ( ( bIsCurrentlyValid != m_bIsCurrentValueValid ) || _bForceNotification )
+ {
+ m_bIsCurrentValueValid = bIsCurrentlyValid;
+
+ // release our mutex for the notifications
+ MutexRelease aRelease( m_aMutex );
+ m_aFormComponentListeners.notifyEach( &validation::XFormComponentValidityListener::componentValidityChanged, EventObject( *this ) );
+ }
+
+ }
+
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.component", "OBoundControlModel::recheckValidity");
+ }
+}
+
+void OBoundControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 5);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCE, PROPERTY_ID_CONTROLSOURCE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_BOUNDFIELD, PROPERTY_ID_BOUNDFIELD, cppu::UnoType<XPropertySet>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLLABEL, PROPERTY_ID_CONTROLLABEL, cppu::UnoType<XPropertySet>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCEPROPERTY, PROPERTY_ID_CONTROLSOURCEPROPERTY, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_INPUT_REQUIRED, PROPERTY_ID_INPUT_REQUIRED, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormattedField.cxx b/forms/source/component/FormattedField.cxx
new file mode 100644
index 0000000000..119fcc35f6
--- /dev/null
+++ b/forms/source/component/FormattedField.cxx
@@ -0,0 +1,1022 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "FormattedField.hxx"
+#include <services.hxx>
+#include <property.hxx>
+#include <propertybaghelper.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/numbers.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbconversion.hxx>
+#include <o3tl/any.hxx>
+#include <svl/numformat.hxx>
+#include <svl/numuno.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <tools/debug.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/awt/MouseEvent.hpp>
+#include <com/sun/star/form/XSubmit.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <osl/mutex.hxx>
+// needed as long as we use the SolarMutex
+#include <comphelper/streamsection.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <unotools/desktopterminationobserver.hxx>
+#include <vector>
+#include <algorithm>
+
+
+using namespace dbtools;
+using namespace css::uno;
+using namespace css::sdb;
+using namespace css::sdbc;
+using namespace css::sdbcx;
+using namespace css::beans;
+using namespace css::container;
+using namespace css::form;
+using namespace css::awt;
+using namespace css::io;
+using namespace css::lang;
+using namespace css::util;
+using namespace css::form::binding;
+
+namespace frm
+{
+namespace {
+
+class StandardFormatsSupplier : public SvNumberFormatsSupplierObj, public ::utl::ITerminationListener
+{
+protected:
+ std::unique_ptr<SvNumberFormatter> m_pMyPrivateFormatter;
+ static WeakReference< XNumberFormatsSupplier > s_xDefaultFormatsSupplier;
+public:
+ static Reference< XNumberFormatsSupplier > get( const Reference< XComponentContext >& _rxORB );
+protected:
+ StandardFormatsSupplier(const Reference< XComponentContext >& _rxFactory,LanguageType _eSysLanguage);
+ virtual ~StandardFormatsSupplier() override;
+protected:
+ virtual bool queryTermination() const override;
+ virtual void notifyTermination() override;
+};
+
+}
+
+WeakReference< XNumberFormatsSupplier > StandardFormatsSupplier::s_xDefaultFormatsSupplier;
+StandardFormatsSupplier::StandardFormatsSupplier(const Reference< XComponentContext > & _rxContext,LanguageType _eSysLanguage)
+ :m_pMyPrivateFormatter(new SvNumberFormatter(_rxContext, _eSysLanguage))
+{
+ SetNumberFormatter(m_pMyPrivateFormatter.get());
+ // #i29147#
+ ::utl::DesktopTerminationObserver::registerTerminationListener( this );
+}
+StandardFormatsSupplier::~StandardFormatsSupplier()
+{
+ ::utl::DesktopTerminationObserver::revokeTerminationListener( this );
+}
+Reference< XNumberFormatsSupplier > StandardFormatsSupplier::get( const Reference< XComponentContext >& _rxORB )
+{
+ LanguageType eSysLanguage = LANGUAGE_SYSTEM;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ Reference< XNumberFormatsSupplier > xSupplier = s_xDefaultFormatsSupplier;
+ if ( xSupplier.is() )
+ return xSupplier;
+ // get the Office's locale
+ eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
+ }
+ rtl::Reference<StandardFormatsSupplier> pSupplier = new StandardFormatsSupplier( _rxORB, eSysLanguage );
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ Reference< XNumberFormatsSupplier > xSupplier = s_xDefaultFormatsSupplier;
+ if ( xSupplier.is() )
+ // somebody used the small time frame where the mutex was not locked to create and set
+ // the supplier
+ return xSupplier;
+ s_xDefaultFormatsSupplier = css::uno::Reference<css::uno::XWeak>(pSupplier);
+ }
+ return pSupplier;
+}
+bool StandardFormatsSupplier::queryTermination() const
+{
+ return true;
+}
+void StandardFormatsSupplier::notifyTermination()
+{
+ Reference< XNumberFormatsSupplier > xKeepAlive = this;
+ // when the application is terminating, release our static reference so that we are cleared/destructed
+ // earlier than upon unloading the library
+ // #i29147#
+ s_xDefaultFormatsSupplier = WeakReference< XNumberFormatsSupplier >( );
+ SetNumberFormatter( nullptr );
+ m_pMyPrivateFormatter.reset();
+}
+Sequence<Type> OFormattedControl::_getTypes()
+{
+ return ::comphelper::concatSequences(
+ OFormattedControl_BASE::getTypes(),
+ OBoundControl::_getTypes()
+ );
+}
+Any SAL_CALL OFormattedControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OBoundControl::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OFormattedControl_BASE::queryInterface(_rType);
+ return aReturn;
+}
+OFormattedControl::OFormattedControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_FORMATTEDFIELD)
+ ,m_nKeyEvent(nullptr)
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ Reference<XWindow> xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ {
+ xComp->addKeyListener(this);
+ }
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+OFormattedControl::~OFormattedControl()
+{
+ if( m_nKeyEvent )
+ Application::RemoveUserEvent( m_nKeyEvent );
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+// XKeyListener
+void OFormattedControl::disposing(const EventObject& _rSource)
+{
+ OBoundControl::disposing(_rSource);
+}
+void OFormattedControl::keyPressed(const css::awt::KeyEvent& e)
+{
+ if( e.KeyCode != KEY_RETURN || e.Modifiers != 0 )
+ return;
+ // Is the control located in a form with a Submit URL?
+ Reference<css::beans::XPropertySet> xSet(getModel(), UNO_QUERY);
+ if( !xSet.is() )
+ return;
+ Reference<XFormComponent> xFComp(xSet, UNO_QUERY);
+ css::uno::Reference<css::uno::XInterface> xParent = xFComp->getParent();
+ if( !xParent.is() )
+ return;
+ Reference<css::beans::XPropertySet> xFormSet(xParent, UNO_QUERY);
+ if( !xFormSet.is() )
+ return;
+ Any aTmp(xFormSet->getPropertyValue( PROPERTY_TARGET_URL ));
+ if (!aTmp.has<OUString>() ||
+ getString(aTmp).isEmpty() )
+ return;
+ Reference<XIndexAccess> xElements(xParent, UNO_QUERY);
+ sal_Int32 nCount = xElements->getCount();
+ if( nCount > 1 )
+ {
+ Reference<css::beans::XPropertySet> xFCSet;
+ for( sal_Int32 nIndex=0; nIndex < nCount; nIndex++ )
+ {
+ // Any aElement(xElements->getByIndex(nIndex));
+ xElements->getByIndex(nIndex) >>= xFCSet;
+ if (hasProperty(PROPERTY_CLASSID, xFCSet) &&
+ getINT16(xFCSet->getPropertyValue(PROPERTY_CLASSID)) == FormComponentType::TEXTFIELD)
+ {
+ // Found another Edit -> Do not submit then
+ if (xFCSet != xSet)
+ return;
+ }
+ }
+ }
+ // Because we're still in the Handler, execute submit asynchronously
+ if( m_nKeyEvent )
+ Application::RemoveUserEvent( m_nKeyEvent );
+ m_nKeyEvent = Application::PostUserEvent( LINK(this, OFormattedControl,
+ OnKeyPressed) );
+}
+
+void OFormattedControl::keyReleased(const css::awt::KeyEvent& /*e*/)
+{
+}
+
+IMPL_LINK_NOARG(OFormattedControl, OnKeyPressed, void*, void)
+{
+ m_nKeyEvent = nullptr;
+ Reference<XFormComponent> xFComp(getModel(), UNO_QUERY);
+ css::uno::Reference<css::uno::XInterface> xParent = xFComp->getParent();
+ Reference<XSubmit> xSubmit(xParent, UNO_QUERY);
+ if (xSubmit.is())
+ xSubmit->submit( Reference<XControl> (), css::awt::MouseEvent() );
+}
+
+css::uno::Sequence<OUString> OFormattedControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_FORMATTEDFIELD;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_FORMATTEDFIELD;
+ return aSupported;
+}
+
+void OFormattedModel::implConstruct()
+{
+ // members
+ m_bOriginalNumeric = false;
+ m_bNumeric = false;
+ m_xOriginalFormatter = nullptr;
+ m_nKeyType = NumberFormat::UNDEFINED;
+ m_aNullDate = DBTypeConversion::getStandardDate();
+ // default our formats supplier
+ osl_atomic_increment(&m_refCount);
+ setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER);
+ osl_atomic_decrement(&m_refCount);
+ startAggregatePropertyListening( PROPERTY_FORMATKEY );
+ startAggregatePropertyListening( PROPERTY_FORMATSSUPPLIER );
+}
+OFormattedModel::OFormattedModel(const Reference<XComponentContext>& _rxFactory)
+ :OEditBaseModel(_rxFactory, VCL_CONTROLMODEL_FORMATTEDFIELD, FRM_SUN_CONTROL_FORMATTEDFIELD, true, true )
+ // use the old control name for compytibility reasons
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+{
+ implConstruct();
+ m_nClassId = FormComponentType::TEXTFIELD;
+ initValueProperty( PROPERTY_EFFECTIVE_VALUE, PROPERTY_ID_EFFECTIVE_VALUE );
+}
+OFormattedModel::OFormattedModel( const OFormattedModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+{
+ implConstruct();
+}
+
+OFormattedModel::~OFormattedModel()
+{
+}
+
+// XCloneable
+css::uno::Reference< css::util::XCloneable > SAL_CALL OFormattedModel::createClone()
+{
+ rtl::Reference<OFormattedModel> pClone = new OFormattedModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+void SAL_CALL OFormattedModel::disposing()
+{
+ OErrorBroadcaster::disposing();
+ OEditBaseModel::disposing();
+}
+
+// XServiceInfo
+css::uno::Sequence<OUString> OFormattedModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OEditBaseModel::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_FORMATTEDFIELD;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_FORMATTEDFIELD;
+ *pStoreTo++ = BINDABLE_DATABASE_FORMATTED_FIELD;
+ *pStoreTo++ = FRM_COMPONENT_FORMATTEDFIELD;
+ return aSupported;
+}
+
+// XAggregation
+Any SAL_CALL OFormattedModel::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OEditBaseModel::queryAggregation( _rType );
+ return aReturn.hasValue() ? aReturn : OErrorBroadcaster::queryInterface( _rType );
+}
+
+// XTypeProvider
+Sequence< Type > OFormattedModel::_getTypes()
+{
+ return ::comphelper::concatSequences(
+ OEditBaseModel::_getTypes(),
+ OErrorBroadcaster::getTypes()
+ );
+}
+
+// XPersistObject
+OUString SAL_CALL OFormattedModel::getServiceName()
+{
+ return FRM_COMPONENT_EDIT;
+}
+
+// XPropertySet
+void OFormattedModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 3);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FILTERPROPOSAL, PROPERTY_ID_FILTERPROPOSAL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+void OFormattedModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+{
+ OEditBaseModel::describeAggregateProperties( _rAggregateProps );
+ // TreatAsNumeric is not transient: we want to attach it to the UI
+ // This is necessary to make EffectiveDefault (which may be text or a number) meaningful
+ ModifyPropertyAttributes(_rAggregateProps, PROPERTY_TREATASNUMERIC, 0, PropertyAttribute::TRANSIENT);
+ // Same for FormatKey
+ // (though the paragraph above for the TreatAsNumeric does not hold anymore - we do not have an UI for this.
+ // But we have for the format key ...)
+ ModifyPropertyAttributes(_rAggregateProps, PROPERTY_FORMATKEY, 0, PropertyAttribute::TRANSIENT);
+ RemoveProperty(_rAggregateProps, PROPERTY_STRICTFORMAT);
+ // no strict format property for formatted fields: it does not make sense, 'cause
+ // there is no general way to decide which characters/sub strings are allowed during the input of an
+ // arbitrary formatted control
+}
+
+void OFormattedModel::setPropertyToDefaultByHandle(sal_Int32 nHandle)
+{
+ if (nHandle == PROPERTY_ID_FORMATSSUPPLIER)
+ {
+ Reference<XNumberFormatsSupplier> xSupplier = calcDefaultFormatsSupplier();
+ DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::setPropertyToDefaultByHandle(FORMATSSUPPLIER) : have no aggregate !");
+ if (m_xAggregateSet.is())
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(xSupplier));
+ }
+ else
+ OEditBaseModel::setPropertyToDefaultByHandle(nHandle);
+}
+
+void OFormattedModel::setPropertyToDefault(const OUString& aPropertyName)
+{
+ OPropertyArrayAggregationHelper& rPH = m_aPropertyBagHelper.getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName( aPropertyName );
+ if (nHandle == PROPERTY_ID_FORMATSSUPPLIER)
+ setPropertyToDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER);
+ else
+ OEditBaseModel::setPropertyToDefault(aPropertyName);
+}
+
+Any OFormattedModel::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+{
+ if (nHandle == PROPERTY_ID_FORMATSSUPPLIER)
+ {
+ Reference<XNumberFormatsSupplier> xSupplier = calcDefaultFormatsSupplier();
+ return Any(xSupplier);
+ }
+ else
+ return OEditBaseModel::getPropertyDefaultByHandle(nHandle);
+}
+
+Any SAL_CALL OFormattedModel::getPropertyDefault( const OUString& aPropertyName )
+{
+ OPropertyArrayAggregationHelper& rPH = m_aPropertyBagHelper.getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName( aPropertyName );
+ if (nHandle == PROPERTY_ID_FORMATSSUPPLIER)
+ return getPropertyDefaultByHandle(PROPERTY_ID_FORMATSSUPPLIER);
+ else
+ return OEditBaseModel::getPropertyDefault(aPropertyName);
+}
+
+void OFormattedModel::_propertyChanged( const css::beans::PropertyChangeEvent& evt )
+{
+ // TODO: check how this works with external bindings
+ OSL_ENSURE( evt.Source == m_xAggregateSet, "OFormattedModel::_propertyChanged: where did this come from?" );
+ if ( evt.Source != m_xAggregateSet )
+ return;
+
+ if ( evt.PropertyName == PROPERTY_FORMATKEY )
+ {
+ if ( evt.NewValue.getValueType().getTypeClass() == TypeClass_LONG )
+ {
+ try
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference<XNumberFormatsSupplier> xSupplier( calcFormatsSupplier() );
+ m_nKeyType = getNumberFormatType(xSupplier->getNumberFormats(), getINT32( evt.NewValue ) );
+ // as m_aSaveValue (which is used by commitControlValueToDbColumn) is format dependent we have
+ // to recalc it, which is done by translateDbColumnToControlValue
+ if ( m_xColumn.is() && m_xAggregateFastSet.is() && !m_xCursor->isBeforeFirst() && !m_xCursor->isAfterLast())
+ {
+ setControlValue( translateDbColumnToControlValue(), eOther );
+ }
+ // if we're connected to an external value binding, then re-calculate the type
+ // used to exchange the value - it depends on the format, too
+ if ( hasExternalValueBinding() )
+ {
+ calculateExternalValueType();
+ }
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ return;
+ }
+ if ( evt.PropertyName == PROPERTY_FORMATSSUPPLIER )
+ {
+ updateFormatterNullDate();
+ return;
+ }
+ OBoundControlModel::_propertyChanged( evt );
+}
+
+void OFormattedModel::updateFormatterNullDate()
+{
+ // calc the current NULL date
+ Reference< XNumberFormatsSupplier > xSupplier( calcFormatsSupplier() );
+ if ( xSupplier.is() )
+ xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
+}
+
+Reference< XNumberFormatsSupplier > OFormattedModel::calcFormatsSupplier() const
+{
+ Reference<XNumberFormatsSupplier> xSupplier;
+ DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::calcFormatsSupplier : have no aggregate !");
+ // Does my aggregate model have a FormatSupplier?
+ if( m_xAggregateSet.is() )
+ m_xAggregateSet->getPropertyValue(PROPERTY_FORMATSSUPPLIER) >>= xSupplier;
+ if (!xSupplier.is())
+ // check if my parent form has a supplier
+ xSupplier = calcFormFormatsSupplier();
+ if (!xSupplier.is())
+ xSupplier = calcDefaultFormatsSupplier();
+ DBG_ASSERT(xSupplier.is(), "OFormattedModel::calcFormatsSupplier : no supplier !");
+ // We should have one by now
+ return xSupplier;
+}
+
+Reference<XNumberFormatsSupplier> OFormattedModel::calcFormFormatsSupplier() const
+{
+ Reference<XChild> xMe(const_cast<OFormattedModel*>(this));
+ // By this we make sure that we get the right object even when aggregating
+ DBG_ASSERT(xMe.is(), "OFormattedModel::calcFormFormatsSupplier : I should have a content interface !");
+ // Iterate through until we reach a StartForm (starting with an own Parent)
+ Reference<XChild> xParent(xMe->getParent(), UNO_QUERY);
+ Reference<XForm> xNextParentForm(xParent, UNO_QUERY);
+ while (!xNextParentForm.is() && xParent.is())
+ {
+ xParent.set(xParent->getParent(), css::uno::UNO_QUERY);
+ xNextParentForm.set(xParent, css::uno::UNO_QUERY);
+ }
+ if (!xNextParentForm.is())
+ {
+ OSL_FAIL("OFormattedModel::calcFormFormatsSupplier : have no ancestor which is a form !");
+ return nullptr;
+ }
+ // The FormatSupplier of my ancestor (if it has one)
+ Reference< XRowSet > xRowSet( xNextParentForm, UNO_QUERY );
+ Reference< XNumberFormatsSupplier > xSupplier;
+ if (xRowSet.is())
+ xSupplier = getNumberFormats( getConnection(xRowSet), true, getContext() );
+ return xSupplier;
+}
+
+Reference< XNumberFormatsSupplier > OFormattedModel::calcDefaultFormatsSupplier() const
+{
+ return StandardFormatsSupplier::get( getContext() );
+}
+
+// XBoundComponent
+void OFormattedModel::loaded(const EventObject& rEvent)
+{
+ // HACK: our onConnectedDbColumn accesses our NumberFormatter which locks the solar mutex (as it doesn't have
+ // an own one). To prevent deadlocks with other threads which may request a property from us in an
+ // UI-triggered action (e.g. a tooltip) we lock the solar mutex _here_ before our base class locks
+ // its own mutex (which is used for property requests)
+ // alternative a): we use two mutexes, one which is passed to the OPropertysetHelper and used for
+ // property requests and one for our own code. This would need a lot of code rewriting
+ // alternative b): The NumberFormatter has to be really threadsafe (with an own mutex), which is
+ // the only "clean" solution for me.
+ SolarMutexGuard aGuard;
+ OEditBaseModel::loaded(rEvent);
+}
+
+void OFormattedModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ m_xOriginalFormatter = nullptr;
+ // get some properties of the field
+ Reference<XPropertySet> xField = getField();
+ sal_Int32 nFormatKey = 0;
+ DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::onConnectedDbColumn : have no aggregate !");
+ if (m_xAggregateSet.is())
+ { // all the following doesn't make any sense if we have no aggregate ...
+ Any aSupplier = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATSSUPPLIER);
+ DBG_ASSERT( aSupplier.hasValue(), "OFormattedModel::onConnectedDbColumn : invalid property value !" );
+ // This should've been set to the correct value in the ctor or in the read
+ Any aFmtKey = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATKEY);
+ if ( !(aFmtKey >>= nFormatKey ) )
+ { // nobody gave us a format to use. So we examine the field we're bound to for a
+ // format key, and use it ourself, too
+ sal_Int32 nType = DataType::VARCHAR;
+ if (xField.is())
+ {
+ aFmtKey = xField->getPropertyValue(PROPERTY_FORMATKEY);
+ xField->getPropertyValue(PROPERTY_FIELDTYPE) >>= nType ;
+ }
+ Reference<XNumberFormatsSupplier> xSupplier = calcFormFormatsSupplier();
+ DBG_ASSERT(xSupplier.is(), "OFormattedModel::onConnectedDbColumn : bound to a field but no parent with a formatter ? how this ?");
+ if (xSupplier.is())
+ {
+ m_bOriginalNumeric = getBOOL(getPropertyValue(PROPERTY_TREATASNUMERIC));
+ if (!aFmtKey.hasValue())
+ { // we aren't bound to a field (or this field's format is invalid)
+ // -> determine the standard text (or numeric) format of the supplier
+ Reference<XNumberFormatTypes> xTypes(xSupplier->getNumberFormats(), UNO_QUERY);
+ if (xTypes.is())
+ {
+ Locale aApplicationLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+ if (m_bOriginalNumeric)
+ aFmtKey <<= xTypes->getStandardFormat(NumberFormat::NUMBER, aApplicationLocale);
+ else
+ aFmtKey <<= xTypes->getStandardFormat(NumberFormat::TEXT, aApplicationLocale);
+ }
+ }
+ aSupplier >>= m_xOriginalFormatter;
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(xSupplier));
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, aFmtKey);
+ // Adapt the NumericFalg to my bound field
+ if (xField.is())
+ {
+ m_bNumeric = false;
+ switch (nType)
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ case DataType::BIGINT:
+ case DataType::FLOAT:
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ case DataType::DATE:
+ case DataType::TIME:
+ case DataType::TIMESTAMP:
+ m_bNumeric = true;
+ break;
+ }
+ }
+ else
+ m_bNumeric = m_bOriginalNumeric;
+ setPropertyValue(PROPERTY_TREATASNUMERIC, Any(m_bNumeric));
+ OSL_VERIFY( aFmtKey >>= nFormatKey );
+ }
+ }
+ }
+ Reference<XNumberFormatsSupplier> xSupplier = calcFormatsSupplier();
+ m_bNumeric = getBOOL( getPropertyValue( PROPERTY_TREATASNUMERIC ) );
+ m_nKeyType = getNumberFormatType( xSupplier->getNumberFormats(), nFormatKey );
+ xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
+ OEditBaseModel::onConnectedDbColumn( _rxForm );
+}
+
+void OFormattedModel::onDisconnectedDbColumn()
+{
+ OEditBaseModel::onDisconnectedDbColumn();
+ if (m_xOriginalFormatter.is())
+ { // Our aggregated model does not hold any Format information
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(m_xOriginalFormatter));
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, Any());
+ setPropertyValue(PROPERTY_TREATASNUMERIC, Any(m_bOriginalNumeric));
+ m_xOriginalFormatter = nullptr;
+ }
+ m_nKeyType = NumberFormat::UNDEFINED;
+ m_aNullDate = DBTypeConversion::getStandardDate();
+}
+
+void OFormattedModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OEditBaseModel::write(_rxOutStream);
+ _rxOutStream->writeShort(0x0003);
+ DBG_ASSERT(m_xAggregateSet.is(), "OFormattedModel::write : have no aggregate !");
+ // Bring my Format (may be void) to a persistent Format.
+ // The Supplier together with the Key is already persistent, but that doesn't mean
+ // we have to save the Supplier (which would be quite some overhead)
+ Reference<XNumberFormatsSupplier> xSupplier;
+ Any aFmtKey;
+ bool bVoidKey = true;
+ if (m_xAggregateSet.is())
+ {
+ Any aSupplier = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATSSUPPLIER);
+ if (aSupplier.getValueType().getTypeClass() != TypeClass_VOID)
+ {
+ OSL_VERIFY( aSupplier >>= xSupplier );
+ }
+ aFmtKey = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATKEY);
+ bVoidKey = (!xSupplier.is() || !aFmtKey.hasValue()) || (isLoaded() && m_xOriginalFormatter.is());
+ // (no Format and/or Key) OR (loaded and faked Formatter)
+ }
+ _rxOutStream->writeBoolean(!bVoidKey);
+ if (!bVoidKey)
+ {
+ // Create persistent values from the FormatKey and the Formatter
+ Any aKey = m_xAggregateSet->getPropertyValue(PROPERTY_FORMATKEY);
+ sal_Int32 nKey = aKey.hasValue() ? getINT32(aKey) : 0;
+ Reference<XNumberFormats> xFormats = xSupplier->getNumberFormats();
+ OUString sFormatDescription;
+ LanguageType eFormatLanguage = LANGUAGE_DONTKNOW;
+ static constexpr OUString s_aLocaleProp = u"Locale"_ustr;
+ Reference<css::beans::XPropertySet> xFormat = xFormats->getByKey(nKey);
+ if (hasProperty(s_aLocaleProp, xFormat))
+ {
+ Any aLocale = xFormat->getPropertyValue(s_aLocaleProp);
+ DBG_ASSERT(aLocale.has<Locale>(), "OFormattedModel::write : invalid language property !");
+ if (auto pLocale = o3tl::tryAccess<Locale>(aLocale))
+ {
+ eFormatLanguage = LanguageTag::convertToLanguageType( *pLocale, false);
+ }
+ }
+ static constexpr OUString s_aFormatStringProp = u"FormatString"_ustr;
+ if (hasProperty(s_aFormatStringProp, xFormat))
+ xFormat->getPropertyValue(s_aFormatStringProp) >>= sFormatDescription;
+ _rxOutStream->writeUTF(sFormatDescription);
+ _rxOutStream->writeLong(static_cast<sal_uInt16>(eFormatLanguage));
+ }
+ // version 2 : write the properties common to all OEditBaseModels
+ writeCommonEditProperties(_rxOutStream);
+ // version 3 : write the effective value property of the aggregate
+ // Due to a bug within the UnoControlFormattedFieldModel implementation (our default aggregate)
+ // this props value isn't correctly read and this can't be corrected without being incompatible.
+ // so we have our own handling.
+ // and to be a little bit more compatible we make the following section skippable
+ {
+ OStreamSection aDownCompat(_rxOutStream);
+ // a sub version within the skippable block
+ _rxOutStream->writeShort(0x0000);
+ // version 0: the effective value of the aggregate
+ Any aEffectiveValue;
+ if (m_xAggregateSet.is())
+ {
+ try { aEffectiveValue = m_xAggregateSet->getPropertyValue(PROPERTY_EFFECTIVE_VALUE); } catch(const Exception&) { }
+ }
+ {
+ OStreamSection aDownCompat2(_rxOutStream);
+ switch (aEffectiveValue.getValueType().getTypeClass())
+ {
+ case TypeClass_STRING:
+ _rxOutStream->writeShort(0x0000);
+ _rxOutStream->writeUTF(::comphelper::getString(aEffectiveValue));
+ break;
+ case TypeClass_DOUBLE:
+ _rxOutStream->writeShort(0x0001);
+ _rxOutStream->writeDouble(::comphelper::getDouble(aEffectiveValue));
+ break;
+ default: // void and all unknown states
+ DBG_ASSERT(!aEffectiveValue.hasValue(), "FmXFormattedModel::write : unknown property value type !");
+ _rxOutStream->writeShort(0x0002);
+ break;
+ }
+ }
+ }
+}
+
+void OFormattedModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OEditBaseModel::read(_rxInStream);
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ Reference<XNumberFormatsSupplier> xSupplier;
+ sal_Int32 nKey = -1;
+ switch (nVersion)
+ {
+ case 0x0001 :
+ case 0x0002 :
+ case 0x0003 :
+ {
+ bool bNonVoidKey = _rxInStream->readBoolean();
+ if (bNonVoidKey)
+ {
+ // read string and language...
+ OUString sFormatDescription = _rxInStream->readUTF();
+ LanguageType eDescriptionLanguage(_rxInStream->readLong());
+ // and let a formatter roll dice based on that to create a key...
+ xSupplier = calcFormatsSupplier();
+ // calcFormatsSupplier first takes the one from the model, then one from the starform, then a new one...
+ Reference<XNumberFormats> xFormats = xSupplier->getNumberFormats();
+ if (xFormats.is())
+ {
+ Locale aDescriptionLanguage( LanguageTag::convertToLocale(eDescriptionLanguage));
+ nKey = xFormats->queryKey(sFormatDescription, aDescriptionLanguage, false);
+ if (nKey == sal_Int32(-1))
+ { // does not yet exist in my formatter...
+ nKey = xFormats->addNew(sFormatDescription, aDescriptionLanguage);
+ }
+ }
+ }
+ if ((nVersion == 0x0002) || (nVersion == 0x0003))
+ readCommonEditProperties(_rxInStream);
+ if (nVersion == 0x0003)
+ { // since version 3 there is a "skippable" block at this position
+ OStreamSection aDownCompat(_rxInStream);
+ _rxInStream->readShort(); // sub-version
+ // version 0 and higher: the "effective value" property
+ Any aEffectiveValue;
+ {
+ OStreamSection aDownCompat2(_rxInStream);
+ switch (_rxInStream->readShort())
+ {
+ case 0: // String
+ aEffectiveValue <<= _rxInStream->readUTF();
+ break;
+ case 1: // double
+ aEffectiveValue <<= _rxInStream->readDouble();
+ break;
+ case 2:
+ break;
+ case 3:
+ OSL_FAIL("FmXFormattedModel::read : unknown effective value type!");
+ }
+ }
+ // this property is only to be set if we have no control source: in all other cases the base class made a
+ // reset after it's read and this set the effective value to a default value
+ if ( m_xAggregateSet.is() && getControlSource().isEmpty() )
+ {
+ try
+ {
+ m_xAggregateSet->setPropertyValue(PROPERTY_EFFECTIVE_VALUE, aEffectiveValue);
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ }
+ }
+ break;
+ default :
+ OSL_FAIL("OFormattedModel::read : unknown version !");
+ // then the format of the aggregated set stay like it was during creation: void
+ defaultCommonEditProperties();
+ break;
+ }
+ if ((nKey != -1) && m_xAggregateSet.is())
+ {
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATSSUPPLIER, Any(xSupplier));
+ m_xAggregateSet->setPropertyValue(PROPERTY_FORMATKEY, Any(nKey));
+ }
+ else
+ {
+ setPropertyToDefault(PROPERTY_FORMATSSUPPLIER);
+ setPropertyToDefault(PROPERTY_FORMATKEY);
+ }
+}
+
+sal_uInt16 OFormattedModel::getPersistenceFlags() const
+{
+ return (OEditBaseModel::getPersistenceFlags() & ~PF_HANDLE_COMMON_PROPS);
+ // a) we do our own call to writeCommonEditProperties
+}
+
+bool OFormattedModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+ if ( aControlValue == m_aSaveValue )
+ return true;
+
+ // empty string + EmptyIsNull = void
+ if ( !aControlValue.hasValue()
+ || ( ( aControlValue.getValueType().getTypeClass() == TypeClass_STRING )
+ && getString( aControlValue ).isEmpty()
+ && m_bEmptyIsNull
+ )
+ )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ double f = 0.0;
+ if ( aControlValue.getValueType().getTypeClass() == TypeClass_DOUBLE || (aControlValue >>= f)) // #i110323
+ {
+ DBTypeConversion::setValue( m_xColumnUpdate, m_aNullDate, getDouble( aControlValue ), m_nKeyType );
+ }
+ else
+ {
+ DBG_ASSERT( aControlValue.getValueType().getTypeClass() == TypeClass_STRING, "OFormattedModel::commitControlValueToDbColumn: invalid value type!" );
+ m_xColumnUpdate->updateString( getString( aControlValue ) );
+ }
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aControlValue;
+ return true;
+}
+
+void OFormattedModel::onConnectedExternalValue( )
+{
+ OEditBaseModel::onConnectedExternalValue();
+ updateFormatterNullDate();
+}
+
+Any OFormattedModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ Any aControlValue;
+ switch( _rExternalValue.getValueTypeClass() )
+ {
+ case TypeClass_VOID:
+ break;
+ case TypeClass_STRING:
+ aControlValue = _rExternalValue;
+ break;
+ case TypeClass_BOOLEAN:
+ {
+ bool bExternalValue = false;
+ _rExternalValue >>= bExternalValue;
+ aControlValue <<= static_cast<double>( bExternalValue ? 1 : 0 );
+ }
+ break;
+ default:
+ {
+ if ( _rExternalValue.getValueType().equals( cppu::UnoType< css::util::Date >::get() ) )
+ {
+ css::util::Date aDate;
+ _rExternalValue >>= aDate;
+ aControlValue <<= DBTypeConversion::toDouble( aDate, m_aNullDate );
+ }
+ else if ( _rExternalValue.getValueType().equals( cppu::UnoType< css::util::Time >::get() ) )
+ {
+ css::util::Time aTime;
+ _rExternalValue >>= aTime;
+ aControlValue <<= DBTypeConversion::toDouble( aTime );
+ }
+ else if ( _rExternalValue.getValueType().equals( cppu::UnoType< css::util::DateTime >::get() ) )
+ {
+ css::util::DateTime aDateTime;
+ _rExternalValue >>= aDateTime;
+ aControlValue <<= DBTypeConversion::toDouble( aDateTime, m_aNullDate );
+ }
+ else
+ {
+ OSL_ENSURE( _rExternalValue.getValueTypeClass() == TypeClass_DOUBLE,
+ "OFormattedModel::translateExternalValueToControlValue: don't know how to translate this type!" );
+ double fValue = 0;
+ OSL_VERIFY( _rExternalValue >>= fValue );
+ aControlValue <<= fValue;
+ }
+ }
+ }
+ return aControlValue;
+}
+
+Any OFormattedModel::translateControlValueToExternalValue( ) const
+{
+ OSL_PRECOND( hasExternalValueBinding(),
+ "OFormattedModel::translateControlValueToExternalValue: precondition not met!" );
+ Any aControlValue( getControlValue() );
+ if ( !aControlValue.hasValue() )
+ return aControlValue;
+ Any aExternalValue;
+ // translate into the external value type
+ Type aExternalValueType( getExternalValueType() );
+ switch ( aExternalValueType.getTypeClass() )
+ {
+ case TypeClass_STRING:
+ {
+ OUString sString;
+ if ( aControlValue >>= sString )
+ {
+ aExternalValue <<= sString;
+ break;
+ }
+ [[fallthrough]];
+ }
+ case TypeClass_BOOLEAN:
+ {
+ double fValue = 0;
+ OSL_VERIFY( aControlValue >>= fValue );
+ // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
+ // and the control value is a string. This implies some weird misconfiguration
+ // of the FormattedModel, so we won't care for it for the moment.
+ aExternalValue <<= fValue != 0.0;
+ }
+ break;
+ default:
+ {
+ double fValue = 0;
+ OSL_VERIFY( aControlValue >>= fValue );
+ // if this asserts ... well, the somebody set the TreatAsNumeric property to false,
+ // and the control value is a string. This implies some weird misconfiguration
+ // of the FormattedModel, so we won't care for it for the moment.
+ if ( aExternalValueType.equals( cppu::UnoType< css::util::Date >::get() ) )
+ {
+ aExternalValue <<= DBTypeConversion::toDate( fValue, m_aNullDate );
+ }
+ else if ( aExternalValueType.equals( cppu::UnoType< css::util::Time >::get() ) )
+ {
+ aExternalValue <<= DBTypeConversion::toTime( fValue );
+ }
+ else if ( aExternalValueType.equals( cppu::UnoType< css::util::DateTime >::get() ) )
+ {
+ aExternalValue <<= DBTypeConversion::toDateTime( fValue, m_aNullDate );
+ }
+ else
+ {
+ OSL_ENSURE( aExternalValueType.equals( cppu::UnoType< double >::get() ),
+ "OFormattedModel::translateControlValueToExternalValue: don't know how to translate this type!" );
+ aExternalValue <<= fValue;
+ }
+ }
+ break;
+ }
+ return aExternalValue;
+}
+
+Any OFormattedModel::translateDbColumnToControlValue()
+{
+ if ( m_bNumeric )
+ m_aSaveValue <<= DBTypeConversion::getValue( m_xColumn, m_aNullDate ); // #100056# OJ
+ else
+ m_aSaveValue <<= m_xColumn->getString();
+ if ( m_xColumn->wasNull() )
+ m_aSaveValue.clear();
+ return m_aSaveValue;
+}
+
+Sequence< Type > OFormattedModel::getSupportedBindingTypes()
+{
+ ::std::vector< Type > aTypes;
+ switch ( m_nKeyType & ~NumberFormat::DEFINED )
+ {
+ case NumberFormat::DATE:
+ aTypes.push_back(cppu::UnoType< css::util::Date >::get() );
+ break;
+ case NumberFormat::TIME:
+ aTypes.push_back(cppu::UnoType< css::util::Time >::get() );
+ break;
+ case NumberFormat::DATETIME:
+ aTypes.push_back(cppu::UnoType< css::util::DateTime >::get() );
+ break;
+ case NumberFormat::TEXT:
+ aTypes.push_back(cppu::UnoType< OUString >::get() );
+ break;
+ case NumberFormat::LOGICAL:
+ aTypes.push_back(cppu::UnoType< sal_Bool >::get() );
+ break;
+ }
+ aTypes.push_back( cppu::UnoType< double >::get() );
+ return comphelper::containerToSequence(aTypes);
+}
+
+Any OFormattedModel::getDefaultForReset() const
+{
+ return m_xAggregateSet->getPropertyValue( PROPERTY_EFFECTIVE_DEFAULT );
+}
+
+void OFormattedModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aSaveValue.clear();
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFormattedControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFormattedControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormattedField.hxx b/forms/source/component/FormattedField.hxx
new file mode 100644
index 0000000000..c9d0f1c170
--- /dev/null
+++ b/forms/source/component/FormattedField.hxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+#include <tools/link.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include "errorbroadcaster.hxx"
+
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+
+struct ImplSVEvent;
+
+namespace frm
+{
+class OFormattedModel final
+ :public OEditBaseModel
+ ,public OErrorBroadcaster
+ {
+ // the original, in case I faked the format properties of my aggregated model,
+ // i.e. I just passed on the attributes of the field to which I am bound
+ // (only valid if loaded)
+
+ css::uno::Reference< css::util::XNumberFormatsSupplier> m_xOriginalFormatter;
+ css::util::Date m_aNullDate;
+ css::uno::Any m_aSaveValue;
+
+ sal_Int16 m_nKeyType;
+ bool m_bOriginalNumeric : 1,
+ m_bNumeric : 1; // analogous for the TreatAsNumeric-property
+
+ css::uno::Reference< css::util::XNumberFormatsSupplier> calcDefaultFormatsSupplier() const;
+ css::uno::Reference< css::util::XNumberFormatsSupplier> calcFormFormatsSupplier() const;
+ css::uno::Reference< css::util::XNumberFormatsSupplier> calcFormatsSupplier() const;
+
+ OFormattedModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OFormattedModel(
+ const OFormattedModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OFormattedModel() override;
+
+ friend class OFormattedFieldWrapper;
+
+ public:
+ // XInterface
+ DECLARE_UNO3_AGG_DEFAULTS( OFormattedModel, OEditBaseModel )
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+ // XAggregation
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OFormattedModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPersistObject
+ 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;
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // XLoadListener
+ virtual void SAL_CALL loaded(const css::lang::EventObject& rEvent) override;
+
+ // XPropertyState
+ void setPropertyToDefaultByHandle(sal_Int32 nHandle) override;
+ css::uno::Any getPropertyDefaultByHandle(sal_Int32 nHandle) const override;
+
+ void SAL_CALL setPropertyToDefault(const OUString& aPropertyName) override;
+ css::uno::Any SAL_CALL getPropertyDefault( const OUString& aPropertyName ) 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;
+
+ // XPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& evt) override;
+
+ // prevent method hiding
+ using OEditBaseModel::disposing;
+ using OEditBaseModel::getFastPropertyValue;
+
+ virtual sal_uInt16 getPersistenceFlags() const override;
+ // as we have an own version handling for persistence
+
+ // OBoundControlModel overridables
+ virtual css::uno::Any
+ translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any
+ translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any
+ translateControlValueToExternalValue( ) const override;
+ virtual void onConnectedExternalValue( ) override;
+
+ virtual css::uno::Any
+ getDefaultForReset() const override;
+ virtual void resetNoBroadcast() override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+ virtual void onDisconnectedDbColumn() override;
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ void implConstruct();
+
+ void updateFormatterNullDate();
+ };
+
+ typedef ::cppu::ImplHelper1< css::awt::XKeyListener> OFormattedControl_BASE;
+ class OFormattedControl : public OBoundControl
+ ,public OFormattedControl_BASE
+ {
+ ImplSVEvent * m_nKeyEvent;
+
+ public:
+ explicit OFormattedControl(const css::uno::Reference< css::uno::XComponentContext>& _rxContext);
+ virtual ~OFormattedControl() override;
+
+ DECLARE_UNO3_AGG_DEFAULTS(OFormattedControl, OBoundControl)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OFormattedControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+ // css::awt::XKeyListener
+ virtual void SAL_CALL keyPressed(const css::awt::KeyEvent& e) override;
+ virtual void SAL_CALL keyReleased(const css::awt::KeyEvent& e) override;
+
+ // css::awt::XControl
+ using OBoundControl::setDesignMode;
+
+ // disambiguation
+ using OBoundControl::disposing;
+
+ private:
+ DECL_LINK( OnKeyPressed, void*, void );
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormattedFieldWrapper.cxx b/forms/source/component/FormattedFieldWrapper.cxx
new file mode 100644
index 0000000000..834527ccd7
--- /dev/null
+++ b/forms/source/component/FormattedFieldWrapper.cxx
@@ -0,0 +1,366 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "FormattedFieldWrapper.hxx"
+#include "Edit.hxx"
+#include "FormattedField.hxx"
+#include <services.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <connectivity/dbtools.hxx>
+#include <tools/debug.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <com/sun/star/io/XMarkableStream.hpp>
+
+using namespace comphelper;
+using 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;
+
+OFormattedFieldWrapper::OFormattedFieldWrapper(const Reference<XComponentContext>& _rxFactory,
+ OUString const & implementationName)
+ :m_xContext(_rxFactory)
+ ,m_implementationName(implementationName)
+{
+}
+
+css::uno::Reference<css::uno::XInterface> OFormattedFieldWrapper::createFormattedFieldWrapper(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory, bool bActAsFormatted, OUString const & implementationName)
+{
+ rtl::Reference<OFormattedFieldWrapper> pRef = new OFormattedFieldWrapper(_rxFactory,
+ implementationName);
+
+ if (bActAsFormatted)
+ {
+ // instantiate a FormattedModel
+ // (instantiate it directly ..., as the OFormattedModel isn't
+ // registered for any service names anymore)
+ rtl::Reference<OFormattedModel> pModel = new OFormattedModel(pRef->m_xContext);
+
+ pRef->m_xAggregate = pModel;
+ OSL_ENSURE(pRef->m_xAggregate.is(), "the OFormattedModel didn't have an XAggregation interface !");
+
+ // _before_ setting the delegator, give it to the member references
+ pRef->m_xFormattedPart = pModel;
+ pRef->m_pEditPart.set(new OEditModel(pRef->m_xContext));
+ }
+
+ if (pRef->m_xAggregate.is())
+ { // has to be in its own block because of the temporary variable created by *this
+ pRef->m_xAggregate->setDelegator(static_cast<XWeak*>(pRef.get()));
+ }
+
+ css::uno::Reference<css::uno::XInterface> xRef(*pRef);
+
+ return xRef;
+}
+
+Reference< XCloneable > SAL_CALL OFormattedFieldWrapper::createClone()
+{
+ ensureAggregate();
+
+ rtl::Reference< OFormattedFieldWrapper > xRef(new OFormattedFieldWrapper(m_xContext,
+ m_implementationName));
+
+ Reference< XCloneable > xCloneAccess;
+ query_aggregation( m_xAggregate, xCloneAccess );
+
+ // clone the aggregate
+ if ( xCloneAccess.is() )
+ {
+ Reference< XCloneable > xClone = xCloneAccess->createClone();
+ xRef->m_xAggregate.set(xClone, UNO_QUERY);
+ OSL_ENSURE(xRef->m_xAggregate.is(), "invalid aggregate cloned !");
+
+ xRef->m_xFormattedPart.set(
+ Reference< XInterface >(xClone), css::uno::UNO_QUERY);
+
+ if ( m_pEditPart.is() )
+ {
+ xRef->m_pEditPart.set( new OEditModel(m_pEditPart.get(), m_xContext) );
+ }
+ }
+ else
+ { // the clone source does not yet have an aggregate -> we don't yet need one, too
+ }
+
+ if ( xRef->m_xAggregate.is() )
+ { // has to be in its own block because of the temporary variable created by *this
+ xRef->m_xAggregate->setDelegator(static_cast< XWeak* >(xRef.get()));
+ }
+
+ return xRef;
+}
+
+OFormattedFieldWrapper::~OFormattedFieldWrapper()
+{
+ // release the aggregated object (if any)
+ if (m_xAggregate.is())
+ m_xAggregate->setDelegator(css::uno::Reference<css::uno::XInterface> ());
+
+}
+
+Any SAL_CALL OFormattedFieldWrapper::queryAggregation(const Type& _rType)
+{
+ Any aReturn;
+
+ if (_rType.equals( cppu::UnoType<XTypeProvider>::get() ) )
+ { // a XTypeProvider interface needs a working aggregate - we don't want to give the type provider
+ // of our base class (OFormattedFieldWrapper_Base) to the caller as it supplies nearly nothing
+ ensureAggregate();
+ if (m_xAggregate.is())
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+
+ if (!aReturn.hasValue())
+ {
+ aReturn = OFormattedFieldWrapper_Base::queryAggregation(_rType);
+
+ if ((_rType.equals( cppu::UnoType<XServiceInfo>::get() ) ) && aReturn.hasValue())
+ { // somebody requested an XServiceInfo interface and our base class provided it
+ // check our aggregate if it has one, too
+ ensureAggregate();
+ }
+
+ if (!aReturn.hasValue())
+ {
+ aReturn = ::cppu::queryInterface( _rType,
+ static_cast< XPersistObject* >( this ),
+ static_cast< XCloneable* >( this )
+ );
+
+ if (!aReturn.hasValue())
+ {
+ // somebody requests an interface other than the basics (XInterface) and other than
+ // the two we can supply without an aggregate. So ensure
+ // the aggregate exists.
+ ensureAggregate();
+ if (m_xAggregate.is())
+ aReturn = m_xAggregate->queryAggregation(_rType);
+ }
+ }
+ }
+
+ return aReturn;
+}
+
+OUString SAL_CALL OFormattedFieldWrapper::getServiceName()
+{
+ // return the old compatibility name for an EditModel
+ return FRM_COMPONENT_EDIT;
+}
+
+OUString SAL_CALL OFormattedFieldWrapper::getImplementationName( )
+{
+ return m_implementationName;
+}
+
+sal_Bool SAL_CALL OFormattedFieldWrapper::supportsService( const OUString& _rServiceName )
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+Sequence< OUString > SAL_CALL OFormattedFieldWrapper::getSupportedServiceNames( )
+{
+ DBG_ASSERT(m_xAggregate.is(), "OFormattedFieldWrapper::getSupportedServiceNames: should never have made it 'til here without an aggregate!");
+ Reference< XServiceInfo > xSI;
+ m_xAggregate->queryAggregation(cppu::UnoType<XServiceInfo>::get()) >>= xSI;
+ return xSI->getSupportedServiceNames();
+}
+
+void SAL_CALL OFormattedFieldWrapper::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ // can't write myself
+ ensureAggregate();
+
+ // if we act as real edit field, we can simple forward this write request
+ if (!m_xFormattedPart.is())
+ {
+ Reference<XPersistObject> xAggregatePersistence;
+ query_aggregation(m_xAggregate, xAggregatePersistence);
+ DBG_ASSERT(xAggregatePersistence.is(), "OFormattedFieldWrapper::write : don't know how to handle this : can't write !");
+ // oops ... We gave an XPersistObject interface to the caller but now we aren't an XPersistObject ...
+ if (xAggregatePersistence.is())
+ xAggregatePersistence->write(_rxOutStream);
+ return;
+ }
+
+ // else we have to write an edit part first
+ OSL_ENSURE(m_pEditPart.is(), "OFormattedFieldWrapper::write : formatted part without edit part ?");
+ if ( !m_pEditPart.is() )
+ throw RuntimeException( OUString(), *this );
+
+ // for this we transfer the current props of the formatted part to the edit part
+ Reference<XPropertySet> xFormatProps(m_xFormattedPart, UNO_QUERY);
+ Reference<XPropertySet> xEditProps = m_pEditPart;
+
+ Locale aAppLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
+ dbtools::TransferFormComponentProperties(xFormatProps, xEditProps, aAppLanguage);
+
+ // then write the edit part, after switching to "fake mode"
+ m_pEditPart->enableFormattedWriteFake();
+ m_pEditPart->write(_rxOutStream);
+ m_pEditPart->disableFormattedWriteFake();
+
+ // and finally write the formatted part we're really interested in
+ m_xFormattedPart->write(_rxOutStream);
+}
+
+void SAL_CALL OFormattedFieldWrapper::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ SolarMutexGuard g;
+ if (m_xAggregate.is())
+ { // we already made a decision if we're an EditModel or a FormattedModel
+
+ // if we act as formatted, we have to read the edit part first
+ if (m_xFormattedPart.is())
+ {
+ // two possible cases:
+ // a) the stuff was written by a version which didn't work with an Edit header (all intermediate
+ // versions >5.1 && <=568)
+ // b) it was written by a version using edit headers
+ // as we can distinguish a) from b) only after we have read the edit part, we need to remember the
+ // position
+ Reference<XMarkableStream> xInMarkable(_rxInStream, UNO_QUERY);
+ DBG_ASSERT(xInMarkable.is(), "OFormattedFieldWrapper::read : can only work with markable streams !");
+ sal_Int32 nBeforeEditPart = xInMarkable->createMark();
+
+ m_pEditPart->read(_rxInStream);
+ // this only works because an edit model can read the stuff written by a formatted model
+ // (maybe with some assertions) , but not vice versa
+ if (!m_pEditPart->lastReadWasFormattedFake())
+ { // case a), written with a version without the edit part fake, so seek to the start position, again
+ xInMarkable->jumpToMark(nBeforeEditPart);
+ }
+ xInMarkable->deleteMark(nBeforeEditPart);
+ }
+
+ Reference<XPersistObject> xAggregatePersistence;
+ query_aggregation(m_xAggregate, xAggregatePersistence);
+ DBG_ASSERT(xAggregatePersistence.is(), "OFormattedFieldWrapper::read : don't know how to handle this : can't read !");
+ // oops ... We gave an XPersistObject interface to the caller but now we aren't an XPersistObject ...
+
+ if (xAggregatePersistence.is())
+ xAggregatePersistence->read(_rxInStream);
+ return;
+ }
+
+ // we have to decide from the data within the stream whether we should
+ // be an EditModel or a FormattedModel
+
+ {
+ // let an OEditModel do the reading
+ rtl::Reference< OEditModel > pBasicReader(new OEditModel(m_xContext));
+ pBasicReader->read(_rxInStream);
+
+ // was it really an edit model ?
+ if (!pBasicReader->lastReadWasFormattedFake())
+ {
+ // yes -> all fine
+ m_xAggregate = pBasicReader;
+ }
+ else
+ { // no -> substitute it with a formatted model
+ // let the formatted model do the reading
+ m_xFormattedPart.set(new OFormattedModel(m_xContext));
+ m_xFormattedPart->read(_rxInStream);
+ m_pEditPart = pBasicReader;
+ m_xAggregate.set( m_xFormattedPart, UNO_QUERY );
+ }
+ }
+
+ // do the aggregation
+ osl_atomic_increment(&m_refCount);
+ if (m_xAggregate.is())
+ { // has to be in its own block because of the temporary variable created by *this
+ m_xAggregate->setDelegator(static_cast<XWeak*>(this));
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+void OFormattedFieldWrapper::ensureAggregate()
+{
+ if (m_xAggregate.is())
+ return;
+
+ {
+ // instantiate an EditModel (the only place where we are allowed to decide that we're a FormattedModel
+ // is in ::read)
+ css::uno::Reference<css::uno::XInterface> xEditModel = m_xContext->getServiceManager()->createInstanceWithContext(FRM_SUN_COMPONENT_TEXTFIELD, m_xContext);
+ if (!xEditModel.is())
+ {
+ // arghhh... instantiate it directly... it's dirty, but we really need this aggregate
+ rtl::Reference<OEditModel> pModel = new OEditModel(m_xContext);
+ xEditModel.set(static_cast<XWeak*>(pModel.get()), css::uno::UNO_QUERY);
+ }
+
+ m_xAggregate.set(xEditModel, UNO_QUERY);
+ DBG_ASSERT(m_xAggregate.is(), "OFormattedFieldWrapper::ensureAggregate : the OEditModel didn't have an XAggregation interface !");
+
+ {
+ Reference< XServiceInfo > xSI(m_xAggregate, UNO_QUERY);
+ if (!xSI.is())
+ {
+ OSL_FAIL("OFormattedFieldWrapper::ensureAggregate: the aggregate has no XServiceInfo!");
+ m_xAggregate.clear();
+ }
+ }
+ }
+
+ osl_atomic_increment(&m_refCount);
+ if (m_xAggregate.is())
+ { // has to be in its own block because of the temporary variable created by *this
+ m_xAggregate->setDelegator(static_cast<XWeak*>(this));
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFormattedFieldWrapper_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ css::uno::Reference<css::uno::XInterface> inst(
+ OFormattedFieldWrapper::createFormattedFieldWrapper(
+ component, false, "com.sun.star.form.OFormattedFieldWrapper"));
+ inst->acquire();
+ return inst.get();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_OFormattedFieldWrapper_ForcedFormatted_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ css::uno::Reference<css::uno::XInterface> inst(
+ OFormattedFieldWrapper::createFormattedFieldWrapper(
+ component, true, "com.sun.star.comp.forms.OFormattedFieldWrapper_ForcedFormatted"));
+ inst->acquire();
+ return inst.get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormattedFieldWrapper.hxx b/forms/source/component/FormattedFieldWrapper.hxx
new file mode 100644
index 0000000000..9dc093dece
--- /dev/null
+++ b/forms/source/component/FormattedFieldWrapper.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/implbase3.hxx>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <rtl/ref.hxx>
+
+namespace frm
+{
+
+class OEditModel;
+
+//= OFormattedFieldWrapper
+
+typedef ::cppu::WeakAggImplHelper3 < css::io::XPersistObject
+ , css::lang::XServiceInfo
+ , css::util::XCloneable
+ > OFormattedFieldWrapper_Base;
+
+class OFormattedFieldWrapper final : public OFormattedFieldWrapper_Base
+{
+ css::uno::Reference< css::uno::XComponentContext> m_xContext;
+ OUString m_implementationName;
+
+ css::uno::Reference< css::uno::XAggregation> m_xAggregate;
+
+ rtl::Reference< OEditModel > m_pEditPart;
+ // if we act as formatted this is used to write the EditModel part
+ css::uno::Reference< css::io::XPersistObject> m_xFormattedPart;
+
+ OFormattedFieldWrapper(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ OUString const & implementationName);
+
+ virtual ~OFormattedFieldWrapper() override;
+
+public:
+ // if we act as formatted, this is the PersistObject interface of our aggregate, used
+ // to read and write the FormattedModel part
+ // if bActAsFormatted is false, the state is undetermined until somebody calls
+ // ::read or does anything which requires a living aggregate
+ static css::uno::Reference<css::uno::XInterface> createFormattedFieldWrapper(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory, bool bActAsFormatted, OUString const & implementationName);
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS(OFormattedFieldWrapper, OWeakAggObject)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // 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;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+private:
+ /// ensure we're in a defined state, which means a FormattedModel _OR_ an EditModel
+ void ensureAggregate();
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormsCollection.cxx b/forms/source/component/FormsCollection.cxx
new file mode 100644
index 0000000000..0f1b6d95c0
--- /dev/null
+++ b/forms/source/component/FormsCollection.cxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "FormsCollection.hxx"
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/form/XForm.hpp>
+#include <rtl/ref.hxx>
+
+using namespace frm;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::util;
+
+OUString SAL_CALL OFormsCollection::getServiceName()
+{
+ return "com.sun.star.form.Forms";
+}
+
+Sequence< sal_Int8 > SAL_CALL OFormsCollection::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+Sequence<Type> SAL_CALL OFormsCollection::getTypes()
+{
+ return concatSequences(OInterfaceContainer::getTypes(), ::cppu::OComponentHelper::getTypes(), OFormsCollection_BASE::getTypes());
+}
+
+OFormsCollection::OFormsCollection(const Reference<XComponentContext>& _rxFactory)
+ : ::cppu::OComponentHelper( m_aMutex )
+ ,OInterfaceContainer( _rxFactory, m_aMutex, cppu::UnoType<XForm>::get() )
+ ,OFormsCollection_BASE()
+{
+}
+
+OFormsCollection::OFormsCollection( const OFormsCollection& _cloneSource )
+ : ::cppu::OComponentHelper( m_aMutex )
+ ,OInterfaceContainer( m_aMutex, _cloneSource )
+ ,OFormsCollection_BASE()
+{
+}
+
+OFormsCollection::~OFormsCollection()
+{
+ if (!::cppu::OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+Any SAL_CALL OFormsCollection::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OFormsCollection_BASE::queryInterface(_rType);
+ if (!aReturn.hasValue())
+ {
+ aReturn = OInterfaceContainer::queryInterface(_rType);
+
+ if (!aReturn.hasValue())
+ aReturn = ::cppu::OComponentHelper::queryAggregation(_rType);
+ }
+
+ return aReturn;
+}
+
+OUString SAL_CALL OFormsCollection::getImplementationName()
+{
+ return "com.sun.star.form.OFormsCollection";
+}
+
+sal_Bool SAL_CALL OFormsCollection::supportsService( const OUString& _rServiceName )
+{
+ return cppu::supportsService(this, _rServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL OFormsCollection::getSupportedServiceNames()
+{
+ return { "com.sun.star.form.Forms", "com.sun.star.form.FormComponents" };
+}
+
+// XCloneable
+Reference< XCloneable > SAL_CALL OFormsCollection::createClone( )
+{
+ rtl::Reference<OFormsCollection> pClone = new OFormsCollection( *this );
+ pClone->clonedFrom( *this );
+ return static_cast<OInterfaceContainer*>(pClone.get());
+}
+
+// OComponentHelper
+
+void OFormsCollection::disposing()
+{
+ {
+ SAL_INFO( "forms.component", "forms::OFormsCollection::disposing" );
+ OInterfaceContainer::disposing();
+ }
+ ::cppu::OComponentHelper::disposing();
+ m_xParent = nullptr;
+}
+
+//XChild
+
+void OFormsCollection::setParent(const css::uno::Reference<css::uno::XInterface>& Parent)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_xParent = Parent;
+}
+
+css::uno::Reference<css::uno::XInterface> OFormsCollection::getParent()
+{
+ return m_xParent;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OFormsCollection_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OFormsCollection(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/FormsCollection.hxx b/forms/source/component/FormsCollection.hxx
new file mode 100644
index 0000000000..6a97148af0
--- /dev/null
+++ b/forms/source/component/FormsCollection.hxx
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <InterfaceContainer.hxx>
+#include <cppuhelper/component.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <comphelper/uno3.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/XForms.hpp>
+
+
+namespace frm
+{
+
+// OFormsCollection
+// Implements the UNO Container for Forms and contains all assigned Forms.
+// It can either represent the Context for Forms or be passed a Context.
+
+typedef ::cppu::ImplHelper2< css::form::XForms
+ ,css::lang::XServiceInfo > OFormsCollection_BASE;
+class OFormsCollection
+ :public ::cppu::OComponentHelper
+ ,public OInterfaceContainer
+ ,public OFormsCollection_BASE
+{
+ ::osl::Mutex m_aMutex;
+ css::uno::Reference<css::uno::XInterface> m_xParent; // Parent
+
+public:
+ explicit OFormsCollection(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ OFormsCollection( const OFormsCollection& _cloneSource );
+ virtual ~OFormsCollection() override;
+
+public:
+ DECLARE_UNO3_AGG_DEFAULTS(OFormsCollection, ::cppu::OComponentHelper)
+
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames( ) override;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // css::container::XChild
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getParent() override;
+ virtual void SAL_CALL setParent(const css::uno::Reference<css::uno::XInterface>& Parent) override;
+
+ // prevent method hiding
+ using OInterfaceContainer::disposing;
+
+ // inheritance ambiguity
+ virtual css::uno::Type SAL_CALL getElementType() override
+ { return OInterfaceContainer::getElementType(); }
+ virtual sal_Bool SAL_CALL hasElements() override
+ { return OInterfaceContainer::hasElements(); }
+ virtual css::uno::Any SAL_CALL getByName(const OUString& p1) override
+ { return OInterfaceContainer::getByName(p1); }
+ virtual css::uno::Sequence<OUString> SAL_CALL getElementNames() override
+ { return OInterfaceContainer::getElementNames(); }
+ virtual sal_Bool SAL_CALL hasByName(const OUString& p1) override
+ { return OInterfaceContainer::hasByName(p1); }
+ virtual void SAL_CALL replaceByName(const OUString& p1, const css::uno::Any& p2) override
+ { OInterfaceContainer::replaceByName(p1, p2); }
+ virtual void SAL_CALL insertByName(const OUString& p1, const css::uno::Any& p2) override
+ { OInterfaceContainer::insertByName(p1, p2); }
+ virtual void SAL_CALL removeByName(const OUString& p1) override
+ { OInterfaceContainer::removeByName(p1); }
+ virtual sal_Int32 SAL_CALL getCount() override
+ { return OInterfaceContainer::getCount(); }
+ virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 p1) override
+ { return OInterfaceContainer::getByIndex(p1); }
+ virtual void SAL_CALL replaceByIndex(sal_Int32 p1, const css::uno::Any& p2) override
+ { return OInterfaceContainer::replaceByIndex(p1, p2); }
+ virtual void SAL_CALL insertByIndex(sal_Int32 p1, const css::uno::Any& p2) override
+ { return OInterfaceContainer::insertByIndex(p1, p2); }
+ virtual void SAL_CALL removeByIndex(sal_Int32 p1) override
+ { return OInterfaceContainer::removeByIndex(p1); }
+ virtual css::uno::Reference<css::container::XEnumeration> SAL_CALL createEnumeration() override
+ { return OInterfaceContainer::createEnumeration(); }
+ virtual void SAL_CALL registerScriptEvent(sal_Int32 p1, const css::script::ScriptEventDescriptor& p2) override
+ { OInterfaceContainer::registerScriptEvent(p1, p2); }
+ virtual void SAL_CALL registerScriptEvents(sal_Int32 p1, const css::uno::Sequence<css::script::ScriptEventDescriptor>& p2) override
+ { OInterfaceContainer::registerScriptEvents(p1, p2); }
+ virtual void SAL_CALL revokeScriptEvent(sal_Int32 p1, const OUString& p2, const OUString& p3, const OUString& p4) override
+ { OInterfaceContainer::revokeScriptEvent(p1, p2, p3, p4); }
+ virtual void SAL_CALL revokeScriptEvents(sal_Int32 p1) override
+ { OInterfaceContainer::revokeScriptEvents(p1); }
+ virtual void SAL_CALL insertEntry(sal_Int32 p1) override
+ { OInterfaceContainer::insertEntry(p1); }
+ virtual void SAL_CALL removeEntry(sal_Int32 p1) override
+ { OInterfaceContainer::removeEntry(p1); }
+ virtual css::uno::Sequence<css::script::ScriptEventDescriptor> SAL_CALL getScriptEvents(sal_Int32 p1) override
+ { return OInterfaceContainer::getScriptEvents(p1); }
+ virtual void SAL_CALL attach(sal_Int32 p1, const css::uno::Reference<css::uno::XInterface>& p2, const css::uno::Any& p3) override
+ { OInterfaceContainer::attach(p1, p2, p3); }
+ virtual void SAL_CALL detach(sal_Int32 p1, const css::uno::Reference<css::uno::XInterface>& p2) override
+ { OInterfaceContainer::detach(p1, p2); }
+ virtual void SAL_CALL addScriptListener(const css::uno::Reference<css::script::XScriptListener>& p1) override
+ { OInterfaceContainer::addScriptListener(p1); }
+ virtual void SAL_CALL removeScriptListener(const css::uno::Reference<css::script::XScriptListener>& p1) override
+ { OInterfaceContainer::removeScriptListener(p1); }
+ virtual void SAL_CALL dispose() override
+ { ::cppu::OComponentHelper::dispose(); }
+ virtual void SAL_CALL addEventListener(const css::uno::Reference<css::lang::XEventListener>& p1) override
+ { ::cppu::OComponentHelper::addEventListener(p1); }
+ virtual void SAL_CALL removeEventListener(const css::uno::Reference<css::lang::XEventListener>& p1) override
+ { ::cppu::OComponentHelper::removeEventListener(p1); }
+ virtual void SAL_CALL addContainerListener(const css::uno::Reference<css::container::XContainerListener>& p1) override
+ { OInterfaceContainer::addContainerListener(p1); }
+ virtual void SAL_CALL removeContainerListener(const css::uno::Reference<css::container::XContainerListener>& p1) override
+ { OInterfaceContainer::removeContainerListener(p1); }
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Grid.cxx b/forms/source/component/Grid.cxx
new file mode 100644
index 0000000000..1290aa91af
--- /dev/null
+++ b/forms/source/component/Grid.cxx
@@ -0,0 +1,992 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 "findpos.hxx"
+#include "Grid.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/types.hxx>
+#include <vcl/unohelp.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star::uno;
+
+namespace frm
+{
+using namespace ::com::sun::star;
+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::view;
+namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+const sal_uInt16 ROWHEIGHT = 0x0001;
+const sal_uInt16 FONTTYPE = 0x0002;
+const sal_uInt16 FONTSIZE = 0x0004;
+const sal_uInt16 FONTATTRIBS = 0x0008;
+const sal_uInt16 TABSTOP = 0x0010;
+const sal_uInt16 TEXTCOLOR = 0x0020;
+const sal_uInt16 FONTDESCRIPTOR = 0x0040;
+const sal_uInt16 RECORDMARKER = 0x0080;
+const sal_uInt16 BACKGROUNDCOLOR = 0x0100;
+
+OGridControlModel::OGridControlModel(const Reference<XComponentContext>& _rxFactory)
+ :OControlModel(_rxFactory, OUString())
+ ,OInterfaceContainer(_rxFactory, m_aMutex, cppu::UnoType<XPropertySet>::get())
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,FontControlModel( false )
+ ,m_aSelectListeners(m_aMutex)
+ ,m_aResetListeners(m_aMutex)
+ ,m_aRowSetChangeListeners(m_aMutex)
+ ,m_aDefaultControl( FRM_SUN_CONTROL_GRIDCONTROL )
+ ,m_nBorder(1)
+ ,m_nWritingMode( WritingMode2::CONTEXT )
+ ,m_nContextWritingMode( WritingMode2::CONTEXT )
+ ,m_bEnableVisible(true)
+ ,m_bEnable(true)
+ ,m_bNavigation(true)
+ ,m_bRecordMarker(true)
+ ,m_bPrintable(true)
+ ,m_bAlwaysShowCursor(false)
+ ,m_bDisplaySynchron(true)
+{
+ m_nClassId = FormComponentType::GRIDCONTROL;
+}
+
+OGridControlModel::OGridControlModel( const OGridControlModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+ ,OInterfaceContainer( _rxFactory, m_aMutex, cppu::UnoType<XPropertySet>::get() )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,FontControlModel( _pOriginal )
+ ,m_aSelectListeners( m_aMutex )
+ ,m_aResetListeners( m_aMutex )
+ ,m_aRowSetChangeListeners( m_aMutex )
+{
+ m_aDefaultControl = _pOriginal->m_aDefaultControl;
+ m_bEnable = _pOriginal->m_bEnable;
+ m_bEnableVisible = _pOriginal->m_bEnableVisible;
+ m_bNavigation = _pOriginal->m_bNavigation;
+ m_nBorder = _pOriginal->m_nBorder;
+ m_nWritingMode = _pOriginal->m_nWritingMode;
+ m_nContextWritingMode = _pOriginal->m_nContextWritingMode;
+ m_bRecordMarker = _pOriginal->m_bRecordMarker;
+ m_bPrintable = _pOriginal->m_bPrintable;
+ m_bAlwaysShowCursor = _pOriginal->m_bAlwaysShowCursor;
+ m_bDisplaySynchron = _pOriginal->m_bDisplaySynchron;
+ // clone the columns
+ cloneColumns( _pOriginal );
+ // TODO: clone the events?
+}
+
+OGridControlModel::~OGridControlModel()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+// XCloneable
+Reference< XCloneable > SAL_CALL OGridControlModel::createClone( )
+{
+ rtl::Reference<OGridControlModel> pClone = new OGridControlModel( this, getContext() );
+ pClone->OControlModel::clonedFrom( this );
+ // do not call OInterfaceContainer::clonedFrom, it would clone the elements aka columns, which is
+ // already done in the ctor
+ //pClone->OInterfaceContainer::clonedFrom( *this );
+ return static_cast< XCloneable* >( static_cast< OControlModel* >( pClone.get() ) );
+}
+
+void OGridControlModel::cloneColumns( const OGridControlModel* _pOriginalContainer )
+{
+ try
+ {
+ Reference< XCloneable > xColCloneable;
+ sal_Int32 nIndex = 0;
+ for (auto const& column : _pOriginalContainer->m_aItems)
+ {
+ // ask the col for a factory for the clone
+ xColCloneable.set(column, css::uno::UNO_QUERY);
+ DBG_ASSERT( xColCloneable.is(), "OGridControlModel::cloneColumns: column is not cloneable!" );
+ if ( xColCloneable.is() )
+ {
+ // create a clone of the column
+ Reference< XCloneable > xColClone( xColCloneable->createClone() );
+ DBG_ASSERT( xColClone.is(), "OGridControlModel::cloneColumns: invalid column clone!" );
+ if ( xColClone.is() )
+ {
+ // insert this clone into our own container
+ insertByIndex( nIndex, xColClone->queryInterface( m_aElementType ) );
+ }
+ }
+ ++nIndex;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OGridControlModel::cloneColumns: caught an exception while cloning the columns!" );
+ }
+}
+
+// XServiceInfo
+css::uno::Sequence<OUString> OGridControlModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 4);
+ auto pSupported = aSupported.getArray();
+ pSupported[aSupported.getLength()-4] = "com.sun.star.awt.UnoControlModel";
+ pSupported[aSupported.getLength()-3] = FRM_SUN_COMPONENT_GRIDCONTROL;
+ pSupported[aSupported.getLength()-2] = FRM_COMPONENT_GRID;
+ pSupported[aSupported.getLength()-1] = FRM_COMPONENT_GRIDCONTROL;
+ return aSupported;
+}
+Any SAL_CALL OGridControlModel::queryAggregation( const Type& _rType )
+{
+ Any aReturn = OGridControlModel_BASE::queryInterface(_rType);
+ if ( !aReturn.hasValue() )
+ {
+ aReturn = OControlModel::queryAggregation( _rType );
+ if ( !aReturn.hasValue() )
+ {
+ aReturn = OInterfaceContainer::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OErrorBroadcaster::queryInterface( _rType );
+ }
+ }
+ return aReturn;
+}
+
+// XSQLErrorListener
+void SAL_CALL OGridControlModel::errorOccured( const SQLErrorEvent& _rEvent )
+{
+ // forward the errors which happened to my columns to my own listeners
+ onError( _rEvent );
+}
+
+// XRowSetSupplier
+Reference< XRowSet > SAL_CALL OGridControlModel::getRowSet( )
+{
+ return Reference< XRowSet >( getParent(), UNO_QUERY );
+}
+
+void SAL_CALL OGridControlModel::setRowSet( const Reference< XRowSet >& /*_rxDataSource*/ )
+{
+ OSL_FAIL( "OGridControlModel::setRowSet: not supported!" );
+}
+
+void SAL_CALL OGridControlModel::addRowSetChangeListener( const Reference< XRowSetChangeListener >& i_Listener )
+{
+ if ( i_Listener.is() )
+ m_aRowSetChangeListeners.addInterface( i_Listener );
+}
+
+void SAL_CALL OGridControlModel::removeRowSetChangeListener( const Reference< XRowSetChangeListener >& i_Listener )
+{
+ m_aRowSetChangeListeners.removeInterface( i_Listener );
+}
+
+// XChild
+void SAL_CALL OGridControlModel::setParent( const css::uno::Reference<css::uno::XInterface>& i_Parent )
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ if ( i_Parent == getParent() )
+ return;
+ OControlModel::setParent( i_Parent );
+ EventObject aEvent( *this );
+ aGuard.clear();
+ m_aRowSetChangeListeners.notifyEach( &XRowSetChangeListener::onRowSetChanged, aEvent );
+}
+Sequence< Type > SAL_CALL OGridControlModel::getTypes( )
+{
+ return concatSequences(
+ concatSequences(
+ OControlModel::getTypes(),
+ OInterfaceContainer::getTypes(),
+ OErrorBroadcaster::getTypes()
+ ),
+ OGridControlModel_BASE::getTypes()
+ );
+}
+
+// OComponentHelper
+void OGridControlModel::disposing()
+{
+ OControlModel::disposing();
+ OErrorBroadcaster::disposing();
+ OInterfaceContainer::disposing();
+ setParent(nullptr);
+ EventObject aEvt(static_cast<XWeak*>(this));
+ m_aSelectListeners.disposeAndClear(aEvt);
+ m_aResetListeners.disposeAndClear(aEvt);
+ m_aRowSetChangeListeners.disposeAndClear(aEvt);
+}
+
+// XEventListener
+void OGridControlModel::disposing(const EventObject& _rEvent)
+{
+ OControlModel::disposing( _rEvent );
+ OInterfaceContainer::disposing( _rEvent );
+}
+
+// XSelectionSupplier
+sal_Bool SAL_CALL OGridControlModel::select(const Any& rElement)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ Reference<XPropertySet> xSel;
+ if (rElement.hasValue())
+ {
+ xSel.set(rElement, css::uno::UNO_QUERY);
+ if (!xSel.is())
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ css::uno::Reference<css::uno::XInterface> xMe = static_cast<XWeak*>(this);
+ if (xSel.is())
+ {
+ Reference<XChild> xAsChild(xSel, UNO_QUERY);
+ if (!xAsChild.is() || (xAsChild->getParent() != xMe))
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ if ( xSel != m_xSelection )
+ {
+ m_xSelection = xSel;
+ aGuard.clear();
+ m_aSelectListeners.notifyEach( &XSelectionChangeListener::selectionChanged, EventObject( *this ) );
+ return true;
+ }
+ return false;
+}
+Any SAL_CALL OGridControlModel::getSelection()
+{
+ return Any(m_xSelection);
+}
+
+void OGridControlModel::addSelectionChangeListener(const Reference< XSelectionChangeListener >& _rxListener)
+{
+ m_aSelectListeners.addInterface(_rxListener);
+}
+
+void OGridControlModel::removeSelectionChangeListener(const Reference< XSelectionChangeListener >& _rxListener)
+{
+ m_aSelectListeners.removeInterface(_rxListener);
+}
+
+// XGridColumnFactory
+Reference<XPropertySet> SAL_CALL OGridControlModel::createColumn(const OUString& ColumnType)
+{
+ SolarMutexGuard g;
+ const Sequence< OUString >& rColumnTypes = frm::getColumnTypes();
+ return createColumnById( ::detail::findPos( ColumnType, rColumnTypes ) );
+}
+Reference<XPropertySet> OGridControlModel::createColumnById(sal_Int32 nTypeId) const
+{
+ Reference<XPropertySet> xReturn;
+ switch (nTypeId)
+ {
+ case TYPE_CHECKBOX: xReturn = new CheckBoxColumn( getContext() ); break;
+ case TYPE_COMBOBOX: xReturn = new ComboBoxColumn( getContext() ); break;
+ case TYPE_CURRENCYFIELD: xReturn = new CurrencyFieldColumn( getContext() ); break;
+ case TYPE_DATEFIELD: xReturn = new DateFieldColumn( getContext() ); break;
+ case TYPE_LISTBOX: xReturn = new ListBoxColumn( getContext() ); break;
+ case TYPE_NUMERICFIELD: xReturn = new NumericFieldColumn( getContext() ); break;
+ case TYPE_PATTERNFIELD: xReturn = new PatternFieldColumn( getContext() ); break;
+ case TYPE_TEXTFIELD: xReturn = new TextFieldColumn( getContext() ); break;
+ case TYPE_TIMEFIELD: xReturn = new TimeFieldColumn( getContext() ); break;
+ case TYPE_FORMATTEDFIELD: xReturn = new FormattedFieldColumn( getContext() ); break;
+ default:
+ OSL_FAIL("OGridControlModel::createColumn: Unknown Column");
+ break;
+ }
+ return xReturn;
+}
+css::uno::Sequence<OUString> SAL_CALL OGridControlModel::getColumnTypes()
+{
+ return frm::getColumnTypes();
+}
+
+// XReset
+void SAL_CALL OGridControlModel::reset()
+{
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aResetListeners);
+ EventObject aEvt(static_cast<XWeak*>(this));
+ bool bContinue = true;
+ while (aIter.hasMoreElements() && bContinue)
+ bContinue = aIter.next()->approveReset(aEvt);
+ if (bContinue)
+ {
+ _reset();
+ m_aResetListeners.notifyEach( &XResetListener::resetted, aEvt );
+ }
+}
+void SAL_CALL OGridControlModel::addResetListener(const Reference<XResetListener>& _rxListener)
+{
+ m_aResetListeners.addInterface(_rxListener);
+}
+void SAL_CALL OGridControlModel::removeResetListener(const Reference<XResetListener>& _rxListener)
+{
+ m_aResetListeners.removeInterface(_rxListener);
+}
+void OGridControlModel::_reset()
+{
+ Reference<XReset> xReset;
+ sal_Int32 nCount = getCount();
+ for (sal_Int32 nIndex=0; nIndex < nCount; nIndex++)
+ {
+ getByIndex( nIndex ) >>= xReset;
+ if (xReset.is())
+ xReset->reset();
+ }
+}
+
+// XPropertySet
+void OGridControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ _rProps.realloc(37);
+ css::beans::Property* pProperties = _rProps.getArray();
+ *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_CLASSID, PROPERTY_ID_CLASSID, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_TAG, PROPERTY_ID_TAG, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABSTOP, PROPERTY_ID_TABSTOP, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_HASNAVIGATION, PROPERTY_ID_HASNAVIGATION, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_ENABLED, PROPERTY_ID_ENABLED, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_ENABLEVISIBLE, PROPERTY_ID_ENABLEVISIBLE, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_BORDER, PROPERTY_ID_BORDER, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_BORDERCOLOR, PROPERTY_ID_BORDERCOLOR, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULTCONTROL, PROPERTY_ID_DEFAULTCONTROL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TEXTCOLOR, PROPERTY_ID_TEXTCOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_BACKGROUNDCOLOR, PROPERTY_ID_BACKGROUNDCOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT, PROPERTY_ID_FONT, cppu::UnoType<FontDescriptor>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_ROWHEIGHT, PROPERTY_ID_ROWHEIGHT, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_HELPTEXT, PROPERTY_ID_HELPTEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_NAME, PROPERTY_ID_FONT_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_STYLENAME, PROPERTY_ID_FONT_STYLENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_FAMILY, PROPERTY_ID_FONT_FAMILY, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_CHARSET, PROPERTY_ID_FONT_CHARSET, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_HEIGHT, PROPERTY_ID_FONT_HEIGHT, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WEIGHT, PROPERTY_ID_FONT_WEIGHT, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_SLANT, PROPERTY_ID_FONT_SLANT, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_UNDERLINE, PROPERTY_ID_FONT_UNDERLINE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WORDLINEMODE, PROPERTY_ID_FONT_WORDLINEMODE, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_TEXTLINECOLOR, PROPERTY_ID_TEXTLINECOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_FONTEMPHASISMARK, PROPERTY_ID_FONTEMPHASISMARK, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONTRELIEF, PROPERTY_ID_FONTRELIEF, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_STRIKEOUT, PROPERTY_ID_FONT_STRIKEOUT, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_RECORDMARKER, PROPERTY_ID_RECORDMARKER, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_PRINTABLE, PROPERTY_ID_PRINTABLE, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_CURSORCOLOR, PROPERTY_ID_CURSORCOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT |
+ css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_ALWAYSSHOWCURSOR, PROPERTY_ID_ALWAYSSHOWCURSOR, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_DISPLAYSYNCHRON, PROPERTY_ID_DISPLAYSYNCHRON, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_HELPURL, PROPERTY_ID_HELPURL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_WRITING_MODE, PROPERTY_ID_WRITING_MODE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTEXT_WRITING_MODE, PROPERTY_ID_CONTEXT_WRITING_MODE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+void OGridControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ rValue <<= m_nContextWritingMode;
+ break;
+ case PROPERTY_ID_WRITING_MODE:
+ rValue <<= m_nWritingMode;
+ break;
+ case PROPERTY_ID_HELPTEXT:
+ rValue <<= m_sHelpText;
+ break;
+ case PROPERTY_ID_HELPURL:
+ rValue <<= m_sHelpURL;
+ break;
+ case PROPERTY_ID_DISPLAYSYNCHRON:
+ rValue <<= m_bDisplaySynchron;
+ break;
+ case PROPERTY_ID_ALWAYSSHOWCURSOR:
+ rValue <<= m_bAlwaysShowCursor;
+ break;
+ case PROPERTY_ID_CURSORCOLOR:
+ rValue = m_aCursorColor;
+ break;
+ case PROPERTY_ID_PRINTABLE:
+ rValue <<= m_bPrintable;
+ break;
+ case PROPERTY_ID_TABSTOP:
+ rValue = m_aTabStop;
+ break;
+ case PROPERTY_ID_HASNAVIGATION:
+ rValue <<= m_bNavigation;
+ break;
+ case PROPERTY_ID_RECORDMARKER:
+ rValue <<= m_bRecordMarker;
+ break;
+ case PROPERTY_ID_ENABLED:
+ rValue <<= m_bEnable;
+ break;
+ case PROPERTY_ID_ENABLEVISIBLE:
+ rValue <<= m_bEnableVisible;
+ break;
+ case PROPERTY_ID_BORDER:
+ rValue <<= m_nBorder;
+ break;
+ case PROPERTY_ID_BORDERCOLOR:
+ rValue = m_aBorderColor;
+ break;
+ case PROPERTY_ID_DEFAULTCONTROL:
+ rValue <<= m_aDefaultControl;
+ break;
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ rValue = m_aBackgroundColor;
+ break;
+ case PROPERTY_ID_ROWHEIGHT:
+ rValue = m_aRowHeight;
+ break;
+ default:
+ if ( isFontRelatedProperty( nHandle ) )
+ FontControlModel::getFastPropertyValue( rValue, nHandle );
+ else
+ OControlModel::getFastPropertyValue( rValue, nHandle );
+ }
+}
+
+sal_Bool OGridControlModel::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue,
+ sal_Int32 nHandle, const Any& rValue )
+{
+ bool bModified(false);
+ switch (nHandle)
+ {
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_nContextWritingMode );
+ break;
+ case PROPERTY_ID_WRITING_MODE:
+ bModified = tryPropertyValue( rConvertedValue, rOldValue, rValue, m_nWritingMode );
+ break;
+ case PROPERTY_ID_HELPTEXT:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sHelpText);
+ break;
+ case PROPERTY_ID_HELPURL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sHelpURL);
+ break;
+ case PROPERTY_ID_DISPLAYSYNCHRON:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bDisplaySynchron);
+ break;
+ case PROPERTY_ID_ALWAYSSHOWCURSOR:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bAlwaysShowCursor);
+ break;
+ case PROPERTY_ID_CURSORCOLOR:
+ if (!rValue.hasValue() || !m_aCursorColor.hasValue())
+ {
+ if (rValue.hasValue() && (TypeClass_LONG != rValue.getValueType().getTypeClass()))
+ {
+ throw IllegalArgumentException();
+ }
+ rOldValue = m_aCursorColor;
+ rConvertedValue = rValue;
+ bModified = rOldValue != rConvertedValue;
+ }
+ else
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, getINT32(m_aCursorColor));
+ break;
+ case PROPERTY_ID_PRINTABLE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bPrintable);
+ break;
+ case PROPERTY_ID_TABSTOP:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aTabStop, cppu::UnoType<bool>::get());
+ break;
+ case PROPERTY_ID_HASNAVIGATION:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bNavigation);
+ break;
+ case PROPERTY_ID_RECORDMARKER:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bRecordMarker);
+ break;
+ case PROPERTY_ID_ENABLED:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bEnable);
+ break;
+ case PROPERTY_ID_ENABLEVISIBLE:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bEnableVisible);
+ break;
+ case PROPERTY_ID_BORDER:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_nBorder);
+ break;
+ case PROPERTY_ID_BORDERCOLOR:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aBorderColor, cppu::UnoType<sal_Int32>::get());
+ break;
+ case PROPERTY_ID_DEFAULTCONTROL:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aDefaultControl);
+ break;
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aBackgroundColor, cppu::UnoType<sal_Int32>::get());
+ break;
+ case PROPERTY_ID_ROWHEIGHT:
+ {
+ bModified = tryPropertyValue(rConvertedValue, rOldValue, rValue, m_aRowHeight, cppu::UnoType<sal_Int32>::get());
+ sal_Int32 nNewVal( 0 );
+ if ( ( rConvertedValue >>= nNewVal ) && ( nNewVal <= 0 ) )
+ {
+ rConvertedValue.clear();
+ bModified = m_aRowHeight.hasValue();
+ }
+ }
+ break;
+ default:
+ if ( isFontRelatedProperty( nHandle ) )
+ bModified = FontControlModel::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue );
+ else
+ bModified = OControlModel::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue);
+ }
+ return bModified;
+}
+void OGridControlModel::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ rValue >>= m_nContextWritingMode;
+ break;
+ case PROPERTY_ID_WRITING_MODE:
+ rValue >>= m_nWritingMode;
+ break;
+ case PROPERTY_ID_HELPTEXT:
+ rValue >>= m_sHelpText;
+ break;
+ case PROPERTY_ID_HELPURL:
+ rValue >>= m_sHelpURL;
+ break;
+ case PROPERTY_ID_DISPLAYSYNCHRON:
+ m_bDisplaySynchron = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ALWAYSSHOWCURSOR:
+ m_bAlwaysShowCursor = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_CURSORCOLOR:
+ m_aCursorColor = rValue;
+ break;
+ case PROPERTY_ID_PRINTABLE:
+ m_bPrintable = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_TABSTOP:
+ m_aTabStop = rValue;
+ break;
+ case PROPERTY_ID_HASNAVIGATION:
+ m_bNavigation = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ENABLED:
+ m_bEnable = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_ENABLEVISIBLE:
+ m_bEnableVisible = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_RECORDMARKER:
+ m_bRecordMarker = getBOOL(rValue);
+ break;
+ case PROPERTY_ID_BORDER:
+ rValue >>= m_nBorder;
+ break;
+ case PROPERTY_ID_BORDERCOLOR:
+ m_aBorderColor = rValue;
+ break;
+ case PROPERTY_ID_DEFAULTCONTROL:
+ rValue >>= m_aDefaultControl;
+ break;
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ m_aBackgroundColor = rValue;
+ break;
+ case PROPERTY_ID_ROWHEIGHT:
+ m_aRowHeight = rValue;
+ break;
+ default:
+ if ( isFontRelatedProperty( nHandle ) )
+ {
+ FontControlModel::setFastPropertyValue_NoBroadcast_impl(
+ *this, &OGridControlModel::setDependentFastPropertyValue,
+ nHandle, rValue);
+ }
+ else
+ OControlModel::setFastPropertyValue_NoBroadcast( nHandle, rValue );
+ }
+}
+
+//XPropertyState
+Any OGridControlModel::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+{
+ Any aReturn;
+ switch (nHandle)
+ {
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ case PROPERTY_ID_WRITING_MODE:
+ aReturn <<= WritingMode2::CONTEXT;
+ break;
+ case PROPERTY_ID_DEFAULTCONTROL:
+ aReturn <<= STARDIV_ONE_FORM_CONTROL_GRID;
+ break;
+ case PROPERTY_ID_PRINTABLE:
+ case PROPERTY_ID_HASNAVIGATION:
+ case PROPERTY_ID_RECORDMARKER:
+ case PROPERTY_ID_DISPLAYSYNCHRON:
+ case PROPERTY_ID_ENABLED:
+ case PROPERTY_ID_ENABLEVISIBLE:
+ aReturn <<= true;
+ break;
+ case PROPERTY_ID_ALWAYSSHOWCURSOR:
+ aReturn <<= false;
+ break;
+ case PROPERTY_ID_HELPURL:
+ case PROPERTY_ID_HELPTEXT:
+ aReturn <<= OUString();
+ break;
+ case PROPERTY_ID_BORDER:
+ aReturn <<= sal_Int16(1);
+ break;
+ case PROPERTY_ID_BORDERCOLOR:
+ case PROPERTY_ID_TABSTOP:
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ case PROPERTY_ID_ROWHEIGHT:
+ case PROPERTY_ID_CURSORCOLOR:
+ // void
+ break;
+ default:
+ if ( isFontRelatedProperty( nHandle ) )
+ aReturn = FontControlModel::getPropertyDefaultByHandle( nHandle );
+ else
+ aReturn = OControlModel::getPropertyDefaultByHandle(nHandle);
+ }
+ return aReturn;
+}
+
+void OGridControlModel::gotColumn( const Reference< XInterface >& _rxColumn )
+{
+ Reference< XSQLErrorBroadcaster > xBroadcaster( _rxColumn, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ xBroadcaster->addSQLErrorListener( this );
+}
+
+void OGridControlModel::lostColumn(const Reference< XInterface >& _rxColumn)
+{
+ if ( m_xSelection == _rxColumn )
+ { // the currently selected element was replaced
+ m_xSelection.clear();
+ EventObject aEvt( static_cast< XWeak* >( this ) );
+ m_aSelectListeners.notifyEach( &XSelectionChangeListener::selectionChanged, aEvt );
+ }
+ Reference< XSQLErrorBroadcaster > xBroadcaster( _rxColumn, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ xBroadcaster->removeSQLErrorListener( this );
+}
+
+void OGridControlModel::implRemoved(const css::uno::Reference<css::uno::XInterface>& _rxObject)
+{
+ OInterfaceContainer::implRemoved(_rxObject);
+ lostColumn(_rxObject);
+}
+
+void OGridControlModel::implInserted( const ElementDescription* _pElement )
+{
+ OInterfaceContainer::implInserted( _pElement );
+ gotColumn( _pElement->xInterface );
+}
+
+void OGridControlModel::impl_replacedElement( const ContainerEvent& _rEvent, ::osl::ClearableMutexGuard& _rInstanceLock )
+{
+ Reference< XInterface > xOldColumn( _rEvent.ReplacedElement, UNO_QUERY );
+ Reference< XInterface > xNewColumn( _rEvent.Element, UNO_QUERY );
+ bool bNewSelection = ( xOldColumn == m_xSelection );
+ lostColumn( xOldColumn );
+ gotColumn( xNewColumn );
+ if ( bNewSelection )
+ m_xSelection.set( xNewColumn, UNO_QUERY );
+ OInterfaceContainer::impl_replacedElement( _rEvent, _rInstanceLock );
+ // < SYNCHRONIZED
+ if ( bNewSelection )
+ {
+ m_aSelectListeners.notifyEach( &XSelectionChangeListener::selectionChanged, EventObject( *this ) );
+ }
+}
+
+ElementDescription* OGridControlModel::createElementMetaData( )
+{
+ return new ElementDescription;
+}
+
+void OGridControlModel::approveNewElement( const Reference< XPropertySet >& _rxObject, ElementDescription* _pElement )
+{
+ OGridColumn* pCol = comphelper::getFromUnoTunnel<OGridColumn>( _rxObject );
+ if ( !pCol )
+ throw IllegalArgumentException();
+ OInterfaceContainer::approveNewElement( _rxObject, _pElement );
+}
+
+// XPersistObject
+OUString SAL_CALL OGridControlModel::getServiceName()
+{
+ return FRM_COMPONENT_GRID; // old (non-sun) name for compatibility!
+}
+
+void OGridControlModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+ Reference<XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ // 1. Version
+ _rxOutStream->writeShort(0x0008);
+ // 2. Columns
+ sal_Int32 nLen = getCount();
+ _rxOutStream->writeLong(nLen);
+ for (sal_Int32 i = 0; i < nLen; i++)
+ {
+ // first the service name for the underlying model
+ OGridColumn* pCol = comphelper::getFromUnoTunnel<OGridColumn>(m_aItems[i]);
+ DBG_ASSERT(pCol != nullptr, "OGridControlModel::write : such items should never reach it into my container !");
+ _rxOutStream << pCol->getModelName();
+ // then the object itself
+ sal_Int32 nMark = xMark->createMark();
+ sal_Int32 nObjLen = 0;
+ _rxOutStream->writeLong(nObjLen);
+ // writing the column
+ pCol->write(_rxOutStream);
+ // determining the length
+ nObjLen = xMark->offsetToMark(nMark) - 4;
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nObjLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+ }
+ // 3. Events
+ writeEvents(_rxOutStream);
+ // 4. Attributes
+ // Masking for all 'any' types
+ sal_uInt16 nAnyMask = 0;
+ if (m_aRowHeight.getValueType().getTypeClass() == TypeClass_LONG)
+ nAnyMask |= ROWHEIGHT;
+ if ( getFont() != getDefaultFont() )
+ nAnyMask |= FONTATTRIBS | FONTSIZE | FONTTYPE | FONTDESCRIPTOR;
+ if (m_aTabStop.getValueType().getTypeClass() == TypeClass_BOOLEAN)
+ nAnyMask |= TABSTOP;
+ if ( hasTextColor() )
+ nAnyMask |= TEXTCOLOR;
+ if (m_aBackgroundColor.getValueType().getTypeClass() == TypeClass_LONG)
+ nAnyMask |= BACKGROUNDCOLOR;
+ if (!m_bRecordMarker)
+ nAnyMask |= RECORDMARKER;
+ _rxOutStream->writeShort(nAnyMask);
+ if (nAnyMask & ROWHEIGHT)
+ _rxOutStream->writeLong(getINT32(m_aRowHeight));
+ // old structures
+ const FontDescriptor& aFont = getFont();
+ if ( nAnyMask & FONTDESCRIPTOR )
+ {
+ // Attrib
+ _rxOutStream->writeShort( sal::static_int_cast< sal_Int16 >( vcl::unohelper::ConvertFontWeight( aFont.Weight ) ) );
+ _rxOutStream->writeShort( sal::static_int_cast< sal_Int16 >( aFont.Slant ) );
+ _rxOutStream->writeShort( aFont.Underline );
+ _rxOutStream->writeShort( aFont.Strikeout );
+ _rxOutStream->writeShort( sal_Int16(aFont.Orientation * 10) );
+ _rxOutStream->writeBoolean( aFont.Kerning );
+ _rxOutStream->writeBoolean( aFont.WordLineMode );
+ // Size
+ _rxOutStream->writeLong( aFont.Width );
+ _rxOutStream->writeLong( aFont.Height );
+ _rxOutStream->writeShort( sal::static_int_cast< sal_Int16 >( vcl::unohelper::ConvertFontWidth( aFont.CharacterWidth ) ) );
+ // Type
+ _rxOutStream->writeUTF( aFont.Name );
+ _rxOutStream->writeUTF( aFont.StyleName );
+ _rxOutStream->writeShort( aFont.Family );
+ _rxOutStream->writeShort( aFont.CharSet );
+ _rxOutStream->writeShort( aFont.Pitch );
+ }
+ _rxOutStream << m_aDefaultControl;
+ _rxOutStream->writeShort(m_nBorder);
+ _rxOutStream->writeBoolean(m_bEnable);
+ if (nAnyMask & TABSTOP)
+ _rxOutStream->writeBoolean(getBOOL(m_aTabStop));
+ _rxOutStream->writeBoolean(m_bNavigation);
+ if (nAnyMask & TEXTCOLOR)
+ _rxOutStream->writeLong( sal_Int32(getTextColor()) );
+ // new since version 6
+ _rxOutStream << m_sHelpText;
+ if (nAnyMask & FONTDESCRIPTOR)
+ _rxOutStream << getFont();
+ if (nAnyMask & RECORDMARKER)
+ _rxOutStream->writeBoolean(m_bRecordMarker);
+ // new since version 7
+ _rxOutStream->writeBoolean(m_bPrintable);
+ // new since version 8
+ if (nAnyMask & BACKGROUNDCOLOR)
+ _rxOutStream->writeLong(getINT32(m_aBackgroundColor));
+}
+
+void OGridControlModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ SolarMutexGuard g;
+ OControlModel::read(_rxInStream);
+ Reference<XMarkableStream> xMark(_rxInStream, UNO_QUERY);
+ // 1. version
+ sal_Int16 nVersion = _rxInStream->readShort();
+ // 2. reading the columns
+ sal_Int32 nLen = _rxInStream->readLong();
+ if (nLen)
+ {
+ for (sal_Int32 i = 0; i < nLen; i++)
+ {
+ // reading the model names
+ OUString sModelName;
+ _rxInStream >> sModelName;
+ Reference<XPropertySet> xCol(createColumnById(getColumnTypeByModelName(sModelName)));
+ DBG_ASSERT(xCol.is(), "OGridControlModel::read : unknown column type !");
+ sal_Int32 nObjLen = _rxInStream->readLong();
+ if (nObjLen)
+ {
+ sal_Int32 nMark = xMark->createMark();
+ if (xCol.is())
+ {
+ OGridColumn* pCol = comphelper::getFromUnoTunnel<OGridColumn>(xCol);
+ pCol->read(_rxInStream);
+ }
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nObjLen);
+ xMark->deleteMark(nMark);
+ }
+ if ( xCol.is() )
+ implInsert( i, xCol, false, nullptr, false );
+ }
+ }
+ // In the base implementation events are only read, elements in the container exist
+ // but since before TF_ONE for the GridControl events were always written, so they
+ // need to be read, too
+ sal_Int32 nObjLen = _rxInStream->readLong();
+ if (nObjLen)
+ {
+ sal_Int32 nMark = xMark->createMark();
+ Reference<XPersistObject> xObj(m_xEventAttacher, UNO_QUERY);
+ if (xObj.is())
+ xObj->read(_rxInStream);
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nObjLen);
+ xMark->deleteMark(nMark);
+ }
+ // reading the attachment
+ for (sal_Int32 i = 0; i < nLen; i++)
+ {
+ css::uno::Reference<css::uno::XInterface> xIfc(m_aItems[i], UNO_QUERY);
+ Reference<XPropertySet> xSet(xIfc, UNO_QUERY);
+ Any aHelper;
+ aHelper <<= xSet;
+ m_xEventAttacher->attach( i, xIfc, aHelper );
+ }
+ // 4. reading the attributes
+ if (nVersion == 1)
+ return;
+ // Masking for any
+ sal_uInt16 nAnyMask = _rxInStream->readShort();
+ if (nAnyMask & ROWHEIGHT)
+ {
+ sal_Int32 nValue = _rxInStream->readLong();
+ m_aRowHeight <<= nValue;
+ }
+ FontDescriptor aFont( getFont() );
+ if ( nAnyMask & FONTATTRIBS )
+ {
+ aFont.Weight = static_cast<float>(vcl::unohelper::ConvertFontWeight( _rxInStream->readShort() ));
+ aFont.Slant = static_cast<FontSlant>(_rxInStream->readShort());
+ aFont.Underline = _rxInStream->readShort();
+ aFont.Strikeout = _rxInStream->readShort();
+ aFont.Orientation = static_cast<float>(_rxInStream->readShort()) / 10;
+ aFont.Kerning = _rxInStream->readBoolean() != 0;
+ aFont.WordLineMode = _rxInStream->readBoolean() != 0;
+ }
+ if ( nAnyMask & FONTSIZE )
+ {
+ aFont.Width = static_cast<sal_Int16>(_rxInStream->readLong());
+ aFont.Height = static_cast<sal_Int16>(_rxInStream->readLong());
+ aFont.CharacterWidth = static_cast<float>(vcl::unohelper::ConvertFontWidth( _rxInStream->readShort() ));
+ }
+ if ( nAnyMask & FONTTYPE )
+ {
+ aFont.Name = _rxInStream->readUTF();
+ aFont.StyleName = _rxInStream->readUTF();
+ aFont.Family = _rxInStream->readShort();
+ aFont.CharSet = _rxInStream->readShort();
+ aFont.Pitch = _rxInStream->readShort();
+ }
+ if ( nAnyMask & ( FONTATTRIBS | FONTSIZE | FONTTYPE ) )
+ setFont( aFont );
+ // Name
+ _rxInStream >> m_aDefaultControl;
+ m_nBorder = _rxInStream->readShort();
+ m_bEnable = _rxInStream->readBoolean();
+ if (nAnyMask & TABSTOP)
+ {
+ m_aTabStop <<= (_rxInStream->readBoolean() != 0);
+ }
+ if (nVersion > 3)
+ m_bNavigation = _rxInStream->readBoolean();
+ if (nAnyMask & TEXTCOLOR)
+ {
+ sal_Int32 nValue = _rxInStream->readLong();
+ setTextColor( ::Color(ColorTransparency, nValue) );
+ }
+ // new since version 6
+ if (nVersion > 5)
+ _rxInStream >> m_sHelpText;
+ if (nAnyMask & FONTDESCRIPTOR)
+ {
+ FontDescriptor aUNOFont;
+ _rxInStream >> aUNOFont;
+ setFont( aFont );
+ }
+ if (nAnyMask & RECORDMARKER)
+ m_bRecordMarker = _rxInStream->readBoolean();
+ // new since version 7
+ if (nVersion > 6)
+ m_bPrintable = _rxInStream->readBoolean();
+ if (nAnyMask & BACKGROUNDCOLOR)
+ {
+ sal_Int32 nValue = _rxInStream->readLong();
+ m_aBackgroundColor <<= nValue;
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT XInterface*
+com_sun_star_form_OGridControlModel_get_implementation(XComponentContext* component,
+ Sequence<Any> const &)
+{
+ return cppu::acquire(new frm::OGridControlModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Grid.hxx b/forms/source/component/Grid.hxx
new file mode 100644
index 0000000000..8339bb49a5
--- /dev/null
+++ b/forms/source/component/Grid.hxx
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "errorbroadcaster.hxx"
+#include <FormComponent.hxx>
+#include <formcontrolfont.hxx>
+#include <InterfaceContainer.hxx>
+
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/sdb/XRowSetSupplier.hpp>
+#include <com/sun/star/sdb/XRowSetChangeBroadcaster.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/implbase7.hxx>
+
+
+namespace frm
+{
+
+class OGridColumn;
+
+// OGridControlModel
+
+typedef ::cppu::ImplHelper7 < css::awt::XControlModel
+ , css::form::XGridColumnFactory
+ , css::form::XReset
+ , css::view::XSelectionSupplier
+ , css::sdb::XSQLErrorListener
+ , css::sdb::XRowSetSupplier
+ , css::sdb::XRowSetChangeBroadcaster
+ > OGridControlModel_BASE;
+
+class OGridControlModel final :public OControlModel
+ ,public OInterfaceContainer
+ ,public OErrorBroadcaster
+ ,public FontControlModel
+ ,public OGridControlModel_BASE
+{
+ ::comphelper::OInterfaceContainerHelper3<css::view::XSelectionChangeListener> m_aSelectListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XResetListener> m_aResetListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XRowSetChangeListener> m_aRowSetChangeListeners;
+
+// [properties]
+ css::uno::Any m_aRowHeight; // Row height
+ css::uno::Any m_aTabStop;
+ css::uno::Any m_aBackgroundColor;
+ css::uno::Any m_aCursorColor; // transient
+ css::uno::Any m_aBorderColor;
+ OUString m_aDefaultControl;
+ OUString m_sHelpText;
+// [properties]
+
+ css::uno::Reference< css::beans::XPropertySet > m_xSelection;
+
+// [properties]
+ OUString m_sHelpURL; // URL
+ sal_Int16 m_nBorder;
+ sal_Int16 m_nWritingMode;
+ sal_Int16 m_nContextWritingMode;
+ bool m_bEnableVisible : 1;
+ bool m_bEnable : 1;
+ bool m_bNavigation : 1;
+ bool m_bRecordMarker : 1;
+ bool m_bPrintable : 1;
+ bool m_bAlwaysShowCursor : 1; // transient
+ bool m_bDisplaySynchron : 1; // transient
+// [properties]
+
+ void _reset();
+
+public:
+ OGridControlModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OGridControlModel(
+ const OGridControlModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OGridControlModel() override;
+
+ // UNO Binding
+ DECLARE_UNO3_AGG_DEFAULTS(OGridControlModel, OControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XChild
+ virtual void SAL_CALL setParent(const css::uno::Reference<css::uno::XInterface>& Parent) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OGridControlModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ 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;
+
+ // XReset
+ virtual void SAL_CALL reset() override;
+ virtual void SAL_CALL addResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+ virtual void SAL_CALL removeResetListener(const css::uno::Reference< css::form::XResetListener>& _rxListener) override;
+
+ // XSelectionSupplier
+ virtual sal_Bool SAL_CALL select(const css::uno::Any& aElement) override;
+ virtual css::uno::Any SAL_CALL getSelection() override;
+ virtual void SAL_CALL addSelectionChangeListener(const css::uno::Reference< css::view::XSelectionChangeListener >& xListener) override;
+ virtual void SAL_CALL removeSelectionChangeListener(const css::uno::Reference< css::view::XSelectionChangeListener >& xListener) override;
+
+ // XGridColumnFactory
+ virtual css::uno::Reference< css::beans::XPropertySet> SAL_CALL createColumn(const OUString& ColumnType) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getColumnTypes() 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;
+
+ // XPropertySet
+ 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;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+ // XSQLErrorListener
+ virtual void SAL_CALL errorOccured( const css::sdb::SQLErrorEvent& _rEvent ) override;
+
+ // XRowSetSupplier
+ virtual css::uno::Reference< css::sdbc::XRowSet > SAL_CALL getRowSet( ) override;
+ virtual void SAL_CALL setRowSet( const css::uno::Reference< css::sdbc::XRowSet >& xDataSource ) override;
+
+ // XRowSetChangeBroadcaster
+ virtual void SAL_CALL addRowSetChangeListener( const css::uno::Reference< css::sdb::XRowSetChangeListener >& i_Listener ) override;
+ virtual void SAL_CALL removeRowSetChangeListener( const css::uno::Reference< css::sdb::XRowSetChangeListener >& i_Listener ) override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OControlModel::disposing;
+ using OControlModel::getFastPropertyValue;
+
+private:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ virtual void approveNewElement(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxObject,
+ ElementDescription* _pElement
+ ) override;
+
+ css::uno::Reference< css::beans::XPropertySet> createColumnById(sal_Int32 nTypeId) const;
+
+ virtual ElementDescription* createElementMetaData( ) override;
+
+ virtual void implRemoved(const css::uno::Reference<css::uno::XInterface>& _rxObject) override;
+ virtual void implInserted( const ElementDescription* _pElement ) override;
+ virtual void impl_replacedElement(
+ const css::container::ContainerEvent& _rEvent,
+ ::osl::ClearableMutexGuard& _rInstanceLock
+ ) override;
+
+ void gotColumn(const css::uno::Reference< css::uno::XInterface >& _rxColumn);
+ void lostColumn(const css::uno::Reference< css::uno::XInterface >& _rxColumn);
+
+ void cloneColumns( const OGridControlModel* _pOriginalContainer );
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/GroupBox.cxx b/forms/source/component/GroupBox.cxx
new file mode 100644
index 0000000000..fab26e2e7f
--- /dev/null
+++ b/forms/source/component/GroupBox.cxx
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "GroupBox.hxx"
+#include <frm_strings.hxx>
+#include <services.hxx>
+#include <comphelper/property.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+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 comphelper;
+
+// OGroupBoxModel
+
+
+OGroupBoxModel::OGroupBoxModel(const Reference<XComponentContext>& _rxFactory)
+ :OControlModel(_rxFactory, VCL_CONTROLMODEL_GROUPBOX, VCL_CONTROL_GROUPBOX)
+{
+ m_nClassId = FormComponentType::GROUPBOX;
+}
+
+
+OGroupBoxModel::OGroupBoxModel( const OGroupBoxModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+{
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OGroupBoxModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_GROUPBOX;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_GROUPBOX;
+ return aSupported;
+}
+
+
+OGroupBoxModel::~OGroupBoxModel()
+{
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OGroupBoxModel::createClone()
+{
+ rtl::Reference<OGroupBoxModel> pClone = new OGroupBoxModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+void OGroupBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+{
+ OControlModel::describeAggregateProperties( _rAggregateProps );
+ // don't want to have the TabStop property
+ RemoveProperty(_rAggregateProps, PROPERTY_TABSTOP);
+}
+
+
+OUString SAL_CALL OGroupBoxModel::getServiceName()
+{
+ return FRM_COMPONENT_GROUPBOX; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL OGroupBoxModel::write(const Reference< XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0002);
+ writeHelpTextCompatibly(_rxOutStream);
+}
+
+
+void SAL_CALL OGroupBoxModel::read(const Reference< XObjectInputStream>& _rxInStream)
+{
+ OControlModel::read( _rxInStream );
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ DBG_ASSERT(nVersion > 0, "OGroupBoxModel::read : version 0 ? this should never have been written !");
+
+ if (nVersion == 2)
+ readHelpTextCompatibly(_rxInStream);
+
+ if (nVersion > 0x0002)
+ {
+ OSL_FAIL("OGroupBoxModel::read : unknown version !");
+ }
+};
+
+
+// OGroupBoxControl
+
+OGroupBoxControl::OGroupBoxControl(const Reference<XComponentContext>& _rxFactory)
+ :OControl(_rxFactory, VCL_CONTROL_GROUPBOX)
+{
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OGroupBoxControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_GROUPBOX;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_GROUPBOX;
+ return aSupported;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OGroupBoxModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OGroupBoxModel(component));
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OGroupBoxControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OGroupBoxControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/GroupBox.hxx b/forms/source/component/GroupBox.hxx
new file mode 100644
index 0000000000..c1c53df0b6
--- /dev/null
+++ b/forms/source/component/GroupBox.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+
+
+namespace frm
+{
+
+class OGroupBoxModel
+ :public OControlModel
+{
+public:
+ OGroupBoxModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OGroupBoxModel(
+ const OGroupBoxModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OGroupBoxModel() override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OGroupBoxModel"; }
+
+ virtual css::uno::Sequence<OUString> 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 describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+
+// OGroupBoxControl (only for compatibility for 5.0)
+
+class OGroupBoxControl : public OControl
+{
+public:
+ explicit OGroupBoxControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OGroupBoxControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/GroupManager.cxx b/forms/source/component/GroupManager.cxx
new file mode 100644
index 0000000000..edd296d6c7
--- /dev/null
+++ b/forms/source/component/GroupManager.cxx
@@ -0,0 +1,422 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "GroupManager.hxx"
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <frm_strings.hxx>
+
+#include <algorithm>
+#include <utility>
+
+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::lang;
+using namespace ::comphelper;
+
+namespace
+{
+ bool isRadioButton( const Reference< XPropertySet >& _rxComponent )
+ {
+ bool bIs = false;
+ if ( hasProperty( PROPERTY_CLASSID, _rxComponent ) )
+ {
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ _rxComponent->getPropertyValue( PROPERTY_CLASSID ) >>= nClassId;
+ if ( nClassId == FormComponentType::RADIOBUTTON )
+ bIs = true;
+ }
+ return bIs;
+ }
+}
+
+OGroupCompAcc::OGroupCompAcc(const Reference<XPropertySet>& rxElement, OGroupComp _aGroupComp )
+ :m_xComponent( rxElement )
+ ,m_aGroupComp(std::move( _aGroupComp ))
+{
+}
+
+bool OGroupCompAcc::operator==( const OGroupCompAcc& rCompAcc ) const
+{
+ return m_xComponent == rCompAcc.m_xComponent;
+}
+
+class OGroupCompAccLess
+{
+public:
+ bool operator() (const OGroupCompAcc& lhs, const OGroupCompAcc& rhs) const
+ {
+ return
+ reinterpret_cast<sal_Int64>(lhs.m_xComponent.get())
+ < reinterpret_cast<sal_Int64>(rhs.m_xComponent.get());
+ }
+};
+
+OGroupComp::OGroupComp()
+ :m_nPos( -1 )
+ ,m_nTabIndex( 0 )
+{
+}
+
+OGroupComp::OGroupComp(const Reference<XPropertySet>& rxSet, sal_Int32 nInsertPos )
+ : m_xComponent( rxSet )
+ , m_xControlModel(rxSet,UNO_QUERY)
+ , m_nPos( nInsertPos )
+ , m_nTabIndex(0)
+{
+ if (m_xComponent.is())
+ {
+ if (hasProperty( PROPERTY_TABINDEX, m_xComponent ) )
+ // Indices smaller than 0 are treated like 0
+ m_nTabIndex = std::max(getINT16(m_xComponent->getPropertyValue( PROPERTY_TABINDEX )) , sal_Int16(0));
+ }
+}
+
+bool OGroupComp::operator==( const OGroupComp& rComp ) const
+{
+ return m_nTabIndex == rComp.GetTabIndex() && m_nPos == rComp.GetPos();
+}
+
+class OGroupCompLess
+{
+public:
+ bool operator() (const OGroupComp& lhs, const OGroupComp& rhs) const
+ {
+ bool bResult;
+ // TabIndex of 0 will be added at the end
+ if (lhs.m_nTabIndex == rhs.GetTabIndex())
+ bResult = lhs.m_nPos < rhs.GetPos();
+ else if (lhs.m_nTabIndex && rhs.GetTabIndex())
+ bResult = lhs.m_nTabIndex < rhs.GetTabIndex();
+ else
+ bResult = lhs.m_nTabIndex != 0;
+ return bResult;
+ }
+};
+
+OGroup::OGroup( OUString sGroupName )
+ :m_aGroupName(std::move( sGroupName ))
+ ,m_nInsertPos(0)
+{
+}
+
+void OGroup::InsertComponent( const Reference<XPropertySet>& xSet )
+{
+ OGroupComp aNewGroupComp( xSet, m_nInsertPos );
+ sal_Int32 nPosInserted = insert_sorted(m_aCompArray, aNewGroupComp, OGroupCompLess());
+
+ OGroupCompAcc aNewGroupCompAcc( xSet, m_aCompArray[nPosInserted] );
+ insert_sorted(m_aCompAccArray, aNewGroupCompAcc, OGroupCompAccLess());
+ m_nInsertPos++;
+}
+
+void OGroup::RemoveComponent( const Reference<XPropertySet>& rxElement )
+{
+ sal_Int32 nGroupCompAccPos;
+ OGroupCompAcc aSearchCompAcc( rxElement, OGroupComp() );
+ if ( seek_entry(m_aCompAccArray, aSearchCompAcc, nGroupCompAccPos, OGroupCompAccLess()) )
+ {
+ OGroupCompAcc& aGroupCompAcc = m_aCompAccArray[nGroupCompAccPos];
+ const OGroupComp& aGroupComp = aGroupCompAcc.GetGroupComponent();
+
+ sal_Int32 nGroupCompPos;
+ if ( seek_entry(m_aCompArray, aGroupComp, nGroupCompPos, OGroupCompLess()) )
+ {
+ m_aCompAccArray.erase( m_aCompAccArray.begin() + nGroupCompAccPos );
+ m_aCompArray.erase( m_aCompArray.begin() + nGroupCompPos );
+
+ /*
+ * By removing the GroupComp the insertion position has become invalid.
+ * We do not to change it here, however, because it's passed on continuously
+ * and ascending distinctively.
+ */
+ }
+ else
+ {
+ OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "OGroup::RemoveComponent: Component not in Group" );
+ }
+}
+
+Sequence< Reference<XControlModel> > OGroup::GetControlModels() const
+{
+ Sequence<Reference<XControlModel> > aControlModelSeq( m_aCompArray.size() );
+ Reference<XControlModel>* pModels = aControlModelSeq.getArray();
+
+ for (auto const& rGroupComp : m_aCompArray)
+ {
+ *pModels++ = rGroupComp.GetControlModel();
+ }
+ return aControlModelSeq;
+}
+
+OGroupManager::OGroupManager(const Reference< XContainer >& _rxContainer)
+ :m_pCompGroup( new OGroup( "AllComponentGroup" ) )
+ ,m_xContainer(_rxContainer)
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ _rxContainer->addContainerListener(this);
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+OGroupManager::~OGroupManager()
+{
+}
+
+// XPropertyChangeListener
+void OGroupManager::disposing(const EventObject& evt)
+{
+ Reference<XContainer> xContainer(evt.Source, UNO_QUERY);
+ if (xContainer.get() == m_xContainer.get())
+ {
+ m_pCompGroup.reset();
+
+ // delete group
+ m_aGroupArr.clear();
+ m_xContainer.clear();
+ }
+}
+
+void OGroupManager::removeFromGroupMap(const OUString& _sGroupName,const Reference<XPropertySet>& _xSet)
+{
+ // remove Component from CompGroup
+ m_pCompGroup->RemoveComponent( _xSet );
+
+ OGroupArr::iterator aFind = m_aGroupArr.find(_sGroupName);
+
+ if ( aFind != m_aGroupArr.end() )
+ {
+ // group exists
+ aFind->second.RemoveComponent( _xSet );
+
+ // If the count of Group elements == 1 -> deactivate Group
+ sal_Int32 nCount = aFind->second.Count();
+ if ( nCount == 1 || nCount == 0 )
+ {
+ OActiveGroups::iterator aActiveFind = ::std::find(
+ m_aActiveGroupMap.begin(),
+ m_aActiveGroupMap.end(),
+ aFind
+ );
+ if ( aActiveFind != m_aActiveGroupMap.end() )
+ {
+ // the group is active. Deactivate it if the remaining component
+ // is *no* radio button
+ if ( nCount == 0 || !isRadioButton( aFind->second.GetObject( 0 ) ) )
+ m_aActiveGroupMap.erase( aActiveFind );
+ }
+ }
+ }
+
+
+ // Deregister as PropertyChangeListener at Component
+ _xSet->removePropertyChangeListener( PROPERTY_NAME, this );
+ if (hasProperty(PROPERTY_GROUP_NAME, _xSet))
+ _xSet->removePropertyChangeListener( PROPERTY_GROUP_NAME, this );
+ if (hasProperty(PROPERTY_TABINDEX, _xSet))
+ _xSet->removePropertyChangeListener( PROPERTY_TABINDEX, this );
+}
+
+void SAL_CALL OGroupManager::propertyChange(const PropertyChangeEvent& evt)
+{
+ Reference<XPropertySet> xSet(evt.Source, UNO_QUERY);
+
+ // remove Component from group
+ OUString sGroupName;
+ if (hasProperty( PROPERTY_GROUP_NAME, xSet ))
+ xSet->getPropertyValue( PROPERTY_GROUP_NAME ) >>= sGroupName;
+ if (evt.PropertyName == PROPERTY_NAME) {
+ if (!sGroupName.isEmpty())
+ return; // group hasn't changed; ignore this name change.
+ // no GroupName; use Name as GroupName
+ evt.OldValue >>= sGroupName;
+ }
+ else if (evt.PropertyName == PROPERTY_GROUP_NAME) {
+ evt.OldValue >>= sGroupName;
+ if (sGroupName.isEmpty()) {
+ // No prior GroupName; fallback to Name
+ xSet->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
+ }
+ }
+ else
+ sGroupName = GetGroupName( xSet );
+
+ removeFromGroupMap(sGroupName,xSet);
+
+ // Re-insert Component
+ InsertElement( xSet );
+}
+
+// XContainerListener
+void SAL_CALL OGroupManager::elementInserted(const ContainerEvent& Event)
+{
+ Reference< XPropertySet > xProps;
+ Event.Element >>= xProps;
+ if ( xProps.is() )
+ InsertElement( xProps );
+}
+
+void SAL_CALL OGroupManager::elementRemoved(const ContainerEvent& Event)
+{
+ Reference<XPropertySet> xProps;
+ Event.Element >>= xProps;
+ if ( xProps.is() )
+ RemoveElement( xProps );
+}
+
+void SAL_CALL OGroupManager::elementReplaced(const ContainerEvent& Event)
+{
+ Reference<XPropertySet> xProps;
+ Event.ReplacedElement >>= xProps;
+ if ( xProps.is() )
+ RemoveElement( xProps );
+
+ xProps.clear();
+ Event.Element >>= xProps;
+ if ( xProps.is() )
+ InsertElement( xProps );
+}
+
+// Other functions
+Sequence<Reference<XControlModel> > OGroupManager::getControlModels() const
+{
+ return m_pCompGroup->GetControlModels();
+}
+
+sal_Int32 OGroupManager::getGroupCount() const
+{
+ return m_aActiveGroupMap.size();
+}
+
+void OGroupManager::getGroup(sal_Int32 nGroup, Sequence< Reference<XControlModel> >& _rGroup, OUString& _rName)
+{
+ OSL_ENSURE(nGroup >= 0 && o3tl::make_unsigned(nGroup) < m_aActiveGroupMap.size(),"OGroupManager::getGroup: Invalid group index!");
+ OGroupArr::iterator aGroupPos = m_aActiveGroupMap[nGroup];
+ _rName = aGroupPos->second.GetGroupName();
+ _rGroup = aGroupPos->second.GetControlModels();
+}
+
+void OGroupManager::getGroupByName(const OUString& _rName, Sequence< Reference<XControlModel> >& _rGroup)
+{
+ OGroupArr::iterator aFind = m_aGroupArr.find(_rName);
+ if ( aFind != m_aGroupArr.end() )
+ _rGroup = aFind->second.GetControlModels();
+}
+
+void OGroupManager::InsertElement( const Reference<XPropertySet>& xSet )
+{
+ // Only ControlModels
+ Reference<XControlModel> xControl(xSet, UNO_QUERY);
+ if (!xControl.is() )
+ return;
+
+ // Add Component to CompGroup
+ m_pCompGroup->InsertComponent( xSet );
+
+ // Add Component to Group
+ OUString sGroupName( GetGroupName( xSet ) );
+
+ OGroupArr::iterator aFind = m_aGroupArr.find(sGroupName);
+
+ if ( aFind == m_aGroupArr.end() )
+ {
+ aFind = m_aGroupArr.emplace(sGroupName,OGroup(sGroupName)).first;
+ }
+
+ aFind->second.InsertComponent( xSet );
+
+ // if we have at least 2 elements in the group, then this is an "active group"
+ bool bActivateGroup = aFind->second.Count() == 2;
+
+ // Additionally, if the component is a radio button, then it's group becomes active,
+ // too. With this, we ensure that in a container with n radio buttons which all are
+ // in different groups the selection still works reliably (means that all radios can be
+ // clicked independently)
+ if ( aFind->second.Count() == 1 )
+ {
+ if ( isRadioButton( xSet ) )
+ bActivateGroup = true;
+ }
+
+ if ( bActivateGroup )
+ {
+ OActiveGroups::iterator aAlreadyExistent = ::std::find(
+ m_aActiveGroupMap.begin(),
+ m_aActiveGroupMap.end(),
+ aFind
+ );
+ if ( aAlreadyExistent == m_aActiveGroupMap.end() )
+ m_aActiveGroupMap.push_back( aFind );
+ }
+
+ // Register as PropertyChangeListener at Component
+ xSet->addPropertyChangeListener( PROPERTY_NAME, this );
+ if (hasProperty(PROPERTY_GROUP_NAME, xSet))
+ xSet->addPropertyChangeListener( PROPERTY_GROUP_NAME, this );
+
+ // Not everyone needs to support Tabindex
+ if (hasProperty(PROPERTY_TABINDEX, xSet))
+ xSet->addPropertyChangeListener( PROPERTY_TABINDEX, this );
+}
+
+void OGroupManager::RemoveElement( const Reference<XPropertySet>& xSet )
+{
+ // Only ControlModels
+ Reference<XControlModel> xControl(xSet, UNO_QUERY);
+ if (!xControl.is() )
+ return;
+
+ // Remove Component from Group
+ OUString sGroupName( GetGroupName( xSet ) );
+
+ removeFromGroupMap(sGroupName,xSet);
+}
+
+OUString OGroupManager::GetGroupName( const css::uno::Reference< css::beans::XPropertySet>& xComponent )
+{
+ if (!xComponent.is())
+ return OUString();
+ OUString sGroupName;
+ if (hasProperty( PROPERTY_GROUP_NAME, xComponent )) {
+ xComponent->getPropertyValue( PROPERTY_GROUP_NAME ) >>= sGroupName;
+ if (sGroupName.isEmpty())
+ xComponent->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
+ }
+ else
+ xComponent->getPropertyValue( PROPERTY_NAME ) >>= sGroupName;
+ return sGroupName;
+}
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/GroupManager.hxx b/forms/source/component/GroupManager.hxx
new file mode 100644
index 0000000000..4cefd7ac3a
--- /dev/null
+++ b/forms/source/component/GroupManager.hxx
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+/*
+ * The GroupManager listens at the StarForm for FormComponent insertion and removal as well as
+ * its properties "Name" and "TabIndex" and updates its Group using this information.
+ *
+ * The GroupManager manages a Group in which all Controls are sorted by TabIndex.
+ * It also manages an array of Groups, in which each FormComponent is assigned a
+ * Group according to its name.
+ * Each Group is activated using a Map, if they contain more than one element.
+ *
+ * The Groups manage the FormComponents internally using two arrays.
+ * In the GroupCompArray the Components are sorted by TabIndex and insertion position.
+ * Because this array is accessed via the FormComponent, we also have the GroupCompAccessArray
+ * in which the FormComponents are sorted by their storage address.
+ * Every element of the GroupCompArray has a pointer to the GroupCompArray.
+ */
+namespace frm
+{
+
+
+ template <class ELEMENT, class LESS_COMPARE>
+ sal_Int32 insert_sorted(::std::vector<ELEMENT>& _rArray, const ELEMENT& _rNewElement, const LESS_COMPARE& _rCompareOp)
+ {
+ typename ::std::vector<ELEMENT>::iterator aInsertPos = ::std::lower_bound(
+ _rArray.begin(),
+ _rArray.end(),
+ _rNewElement,
+ _rCompareOp
+ );
+ aInsertPos = _rArray.insert(aInsertPos, _rNewElement);
+ return aInsertPos - _rArray.begin();
+ }
+
+ template <class ELEMENT, class LESS_COMPARE>
+ bool seek_entry(const ::std::vector<ELEMENT>& _rArray, const ELEMENT& _rNewElement, sal_Int32& nPos, const LESS_COMPARE& _rCompareOp)
+ {
+ typename ::std::vector<ELEMENT>::const_iterator aExistentPos = ::std::lower_bound(
+ _rArray.begin(),
+ _rArray.end(),
+ _rNewElement,
+ _rCompareOp
+ );
+ if ((aExistentPos != _rArray.end()) && (*aExistentPos == _rNewElement))
+ { // we have a valid "lower or equal" element and it's really "equal"
+ nPos = aExistentPos - _rArray.begin();
+ return true;
+ }
+ nPos = -1;
+ return false;
+ }
+
+
+class OGroupComp
+{
+ css::uno::Reference< css::beans::XPropertySet> m_xComponent;
+ css::uno::Reference< css::awt::XControlModel> m_xControlModel;
+ sal_Int32 m_nPos;
+ sal_Int16 m_nTabIndex;
+
+ friend class OGroupCompLess;
+
+public:
+ OGroupComp(const css::uno::Reference< css::beans::XPropertySet>& rxElement, sal_Int32 nInsertPos );
+ OGroupComp();
+
+ bool operator==( const OGroupComp& rComp ) const;
+
+ const css::uno::Reference< css::beans::XPropertySet>& GetComponent() const { return m_xComponent; }
+ const css::uno::Reference< css::awt::XControlModel>& GetControlModel() const { return m_xControlModel; }
+
+ sal_Int32 GetPos() const { return m_nPos; }
+ sal_Int16 GetTabIndex() const { return m_nTabIndex; }
+};
+
+
+class OGroupComp;
+class OGroupCompAcc
+{
+ css::uno::Reference< css::beans::XPropertySet> m_xComponent;
+
+ OGroupComp m_aGroupComp;
+
+ friend class OGroupCompAccLess;
+
+public:
+ OGroupCompAcc(const css::uno::Reference< css::beans::XPropertySet>& rxElement, OGroupComp _aGroupComp );
+
+ bool operator==( const OGroupCompAcc& rCompAcc ) const;
+
+ const OGroupComp& GetGroupComponent() const { return m_aGroupComp; }
+};
+
+class OGroup final
+{
+ std::vector<OGroupComp> m_aCompArray;
+ std::vector<OGroupCompAcc> m_aCompAccArray;
+
+ OUString m_aGroupName;
+ sal_uInt16 m_nInsertPos; // The insertion position of the GroupComps is determined by the Group
+
+ friend class OGroupLess;
+
+public:
+ explicit OGroup(OUString sGroupName);
+
+ const OUString& GetGroupName() const { return m_aGroupName; }
+ css::uno::Sequence< css::uno::Reference< css::awt::XControlModel> > GetControlModels() const;
+
+ void InsertComponent( const css::uno::Reference< css::beans::XPropertySet>& rxElement );
+ void RemoveComponent( const css::uno::Reference< css::beans::XPropertySet>& rxElement );
+ sal_uInt16 Count() const { return sal::static_int_cast< sal_uInt16 >(m_aCompArray.size()); }
+ const css::uno::Reference< css::beans::XPropertySet>& GetObject( sal_uInt16 nP ) const
+ { return m_aCompArray[nP].GetComponent(); }
+};
+
+typedef std::map<OUString, OGroup> OGroupArr;
+typedef std::vector<OGroupArr::iterator> OActiveGroups;
+
+
+class OGroupManager : public ::cppu::WeakImplHelper< css::beans::XPropertyChangeListener, css::container::XContainerListener>
+{
+ std::unique_ptr<OGroup>
+ m_pCompGroup; // Sort all Components by TabIndices
+ OGroupArr m_aGroupArr; // Sort all Components by group
+ OActiveGroups m_aActiveGroupMap; // This map contains all indices of all groups with more than 1 element
+
+ css::uno::Reference< css::container::XContainer >
+ m_xContainer;
+
+ // Helper functions
+ void InsertElement( const css::uno::Reference< css::beans::XPropertySet>& rxElement );
+ void RemoveElement( const css::uno::Reference< css::beans::XPropertySet>& rxElement );
+ void removeFromGroupMap(const OUString& _sGroupName,const css::uno::Reference< css::beans::XPropertySet>& _xSet);
+
+public:
+ explicit OGroupManager(const css::uno::Reference< css::container::XContainer >& _rxContainer);
+ virtual ~OGroupManager() override;
+
+// css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+// css::beans::XPropertyChangeListener
+ virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override;
+
+// css::container::XContainerListener
+ virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& _rEvent) override;
+ virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& _rEvent) override;
+ virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& _rEvent) override;
+
+// Other functions
+ sal_Int32 getGroupCount() const;
+ void getGroup(sal_Int32 nGroup, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel> >& _rGroup, OUString& Name);
+ void getGroupByName(const OUString& Name, css::uno::Sequence< css::uno::Reference< css::awt::XControlModel> >& _rGroup);
+ css::uno::Sequence< css::uno::Reference< css::awt::XControlModel> > getControlModels() const;
+
+ static OUString GetGroupName( const css::uno::Reference< css::beans::XPropertySet>& xComponent );
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Hidden.cxx b/forms/source/component/Hidden.cxx
new file mode 100644
index 0000000000..2bef53ca49
--- /dev/null
+++ b/forms/source/component/Hidden.cxx
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Hidden.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+
+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;
+
+
+OHiddenModel::OHiddenModel(const Reference<XComponentContext>& _rxFactory)
+ :OControlModel(_rxFactory, OUString())
+{
+ m_nClassId = FormComponentType::HIDDENCONTROL;
+}
+
+
+OHiddenModel::OHiddenModel( const OHiddenModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+{
+ m_sHiddenValue = _pOriginal->m_sHiddenValue;
+}
+
+
+OHiddenModel::~OHiddenModel( )
+{
+}
+
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OHiddenModel::createClone()
+{
+ rtl::Reference<OHiddenModel> pClone = new OHiddenModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+void OHiddenModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_HIDDEN_VALUE : _rValue <<= m_sHiddenValue; break;
+ default:
+ OControlModel::getFastPropertyValue(_rValue, _nHandle);
+ }
+}
+
+
+void OHiddenModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_HIDDEN_VALUE :
+ DBG_ASSERT(_rValue.getValueType().getTypeClass() == TypeClass_STRING, "OHiddenModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_sHiddenValue;
+ break;
+ default:
+ OControlModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
+ }
+}
+
+
+sal_Bool OHiddenModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
+{
+ bool bModified(false);
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_HIDDEN_VALUE :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_sHiddenValue);
+ break;
+ default:
+ bModified = OControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
+ break;
+ }
+ return bModified;
+}
+
+
+void OHiddenModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ _rProps.realloc(4);
+ css::beans::Property* pProperties = _rProps.getArray();
+ *pProperties++ = css::beans::Property(PROPERTY_CLASSID, PROPERTY_ID_CLASSID, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_HIDDEN_VALUE, PROPERTY_ID_HIDDEN_VALUE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TAG, PROPERTY_ID_TAG, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OHiddenModel::getSupportedServiceNames()
+{
+ return css::uno::Sequence<OUString>{
+ FRM_SUN_COMPONENT_HIDDENCONTROL, FRM_SUN_FORMCOMPONENT,
+ FRM_COMPONENT_HIDDEN, FRM_COMPONENT_HIDDENCONTROL };
+}
+
+
+OUString SAL_CALL OHiddenModel::getServiceName()
+{
+ return FRM_COMPONENT_HIDDEN; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL OHiddenModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ // Version
+ _rxOutStream->writeShort(0x0002);
+
+ // Value
+ _rxOutStream << m_sHiddenValue;
+
+ OControlModel::write(_rxOutStream);
+}
+
+
+void SAL_CALL OHiddenModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+
+ // Name
+ DBG_ASSERT(nVersion != 1, "OHiddenModel::read : this version is obsolete !");
+ switch (nVersion)
+ {
+ case 1 : { OUString sDummy; _rxInStream >> sDummy; _rxInStream >> m_sHiddenValue; } break;
+ case 2 : _rxInStream >> m_sHiddenValue; break;
+ default : OSL_FAIL("OHiddenModel::read : unknown version !"); m_sHiddenValue.clear();
+ }
+ OControlModel::read(_rxInStream);
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OHiddenModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OHiddenModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Hidden.hxx b/forms/source/component/Hidden.hxx
new file mode 100644
index 0000000000..7bd01ccf93
--- /dev/null
+++ b/forms/source/component/Hidden.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+
+
+namespace frm
+{
+
+class OHiddenModel
+ :public OControlModel
+{
+ OUString m_sHiddenValue;
+
+public:
+ OHiddenModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OHiddenModel(
+ const OHiddenModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OHiddenModel() 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;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OHiddenModel"; }
+
+ virtual css::uno::Sequence<OUString> 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;
+
+ // prevent method hiding
+ using OControlModel::getFastPropertyValue;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ImageButton.cxx b/forms/source/component/ImageButton.cxx
new file mode 100644
index 0000000000..57bbe39696
--- /dev/null
+++ b/forms/source/component/ImageButton.cxx
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ImageButton.hxx"
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+#include <comphelper/basicio.hxx>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <property.hxx>
+#include <services.hxx>
+
+namespace frm
+{
+
+using namespace ::com::sun::star;
+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::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+// OImageButtonModel
+OImageButtonModel::OImageButtonModel(const Reference<XComponentContext>& _rxFactory)
+ :OClickableImageBaseModel( _rxFactory, VCL_CONTROLMODEL_IMAGEBUTTON, FRM_SUN_CONTROL_IMAGEBUTTON )
+ // use the old control name for compytibility reasons
+{
+ m_nClassId = FormComponentType::IMAGEBUTTON;
+}
+
+OImageButtonModel::OImageButtonModel( const OImageButtonModel* _pOriginal, const Reference<XComponentContext>& _rxFactory)
+ :OClickableImageBaseModel( _pOriginal, _rxFactory )
+{
+ implInitializeImageURL();
+}
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OImageButtonModel::createClone()
+{
+ rtl::Reference<OImageButtonModel> pClone = new OImageButtonModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+OImageButtonModel::~OImageButtonModel()
+{
+}
+
+// XServiceInfo
+css::uno::Sequence<OUString> OImageButtonModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OClickableImageBaseModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_IMAGEBUTTON;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_IMAGEBUTTON;
+ return aSupported;
+}
+
+void OImageButtonModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OClickableImageBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 5);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_BUTTONTYPE, PROPERTY_ID_BUTTONTYPE, cppu::UnoType<FormButtonType>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DISPATCHURLINTERNAL, PROPERTY_ID_DISPATCHURLINTERNAL, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_URL, PROPERTY_ID_TARGET_URL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TARGET_FRAME, PROPERTY_ID_TARGET_FRAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+OUString OImageButtonModel::getServiceName()
+{
+ return FRM_COMPONENT_IMAGEBUTTON; // old (non-sun) name for compatibility !
+}
+
+void OImageButtonModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OControlModel::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0003);
+ _rxOutStream->writeShort(static_cast<sal_uInt16>(m_eButtonType));
+
+ OUString sTmp(INetURLObject::decode( m_sTargetURL, INetURLObject::DecodeMechanism::Unambiguous));
+ _rxOutStream << sTmp;
+ _rxOutStream << m_sTargetFrame;
+ writeHelpTextCompatibly(_rxOutStream);
+}
+
+void OImageButtonModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OControlModel::read(_rxInStream);
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+
+ switch (nVersion)
+ {
+ case 0x0001:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+ }
+ break;
+ case 0x0002:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+ _rxInStream >> m_sTargetURL;
+ _rxInStream >> m_sTargetFrame;
+ }
+ break;
+ case 0x0003:
+ {
+ m_eButtonType = static_cast<FormButtonType>(_rxInStream->readShort());
+ _rxInStream >> m_sTargetURL;
+ _rxInStream >> m_sTargetFrame;
+ readHelpTextCompatibly(_rxInStream);
+ }
+ break;
+
+ default :
+ OSL_FAIL("OImageButtonModel::read : unknown version !");
+ m_eButtonType = FormButtonType_PUSH;
+ m_sTargetURL.clear();
+ m_sTargetFrame.clear();
+ break;
+ }
+}
+
+// OImageButtonControl
+Sequence<Type> OImageButtonControl::_getTypes()
+{
+ static Sequence<Type> const aTypes =
+ concatSequences(OClickableImageBaseControl::_getTypes(), OImageButtonControl_BASE::getTypes());
+ return aTypes;
+}
+
+css::uno::Sequence<OUString> OImageButtonControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OClickableImageBaseControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_IMAGEBUTTON;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_IMAGEBUTTON;
+ return aSupported;
+}
+
+OImageButtonControl::OImageButtonControl(const Reference<XComponentContext>& _rxFactory)
+ :OClickableImageBaseControl(_rxFactory, VCL_CONTROL_IMAGEBUTTON)
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ // Register as MouseListener
+ Reference< awt::XWindow > xComp;
+ query_aggregation( m_xAggregate, xComp);
+ if (xComp.is())
+ xComp->addMouseListener( static_cast< awt::XMouseListener* >( this ) );
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+// UNO Binding
+Any SAL_CALL OImageButtonControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OClickableImageBaseControl::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OImageButtonControl_BASE::queryInterface(_rType);
+
+ return aReturn;
+}
+
+void OImageButtonControl::mousePressed(const awt::MouseEvent& e)
+{
+ SolarMutexGuard aSolarGuard;
+
+ if (e.Buttons != awt::MouseButton::LEFT)
+ return;
+
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ 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()->OComponentEventThread::addEvent( std::make_unique<awt::MouseEvent>(e) );
+ }
+ else
+ {
+ // Or else don't; we must not notify the listeners in that case.
+ // Even not if it's added later on.
+ aGuard.clear();
+ actionPerformed_Impl( false, e );
+ }
+}
+
+void SAL_CALL OImageButtonControl::mouseReleased(const awt::MouseEvent& /*e*/)
+{
+}
+
+void SAL_CALL OImageButtonControl::mouseEntered(const awt::MouseEvent& /*e*/)
+{
+}
+
+void SAL_CALL OImageButtonControl::mouseExited(const awt::MouseEvent& /*e*/)
+{
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OImageButtonModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OImageButtonModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OImageButtonControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OImageButtonControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ImageButton.hxx b/forms/source/component/ImageButton.hxx
new file mode 100644
index 0000000000..0e5c402abb
--- /dev/null
+++ b/forms/source/component/ImageButton.hxx
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in 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 <com/sun/star/awt/XMouseListener.hpp>
+
+
+namespace frm
+{
+
+class OImageButtonModel
+ :public OClickableImageBaseModel
+{
+public:
+ OImageButtonModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OImageButtonModel(
+ const OImageButtonModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OImageButtonModel() override;
+
+// css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OImageButtonModel"; }
+
+ virtual css::uno::Sequence<OUString> 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;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+typedef ::cppu::ImplHelper1< css::awt::XMouseListener> OImageButtonControl_BASE;
+class OImageButtonControl : public OClickableImageBaseControl,
+ public OImageButtonControl_BASE
+{
+protected:
+ // UNO Binding
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit OImageButtonControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OImageButtonControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // UNO Binding
+ DECLARE_UNO3_AGG_DEFAULTS(OImageButtonControl, OClickableImageBaseControl)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override
+ { OControl::disposing(_rSource); }
+
+ // XMouseListener
+ virtual void SAL_CALL mousePressed(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseReleased(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseEntered(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseExited(const css::awt::MouseEvent& e) override;
+
+ // prevent method hiding
+ using OClickableImageBaseControl::disposing;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ImageControl.cxx b/forms/source/component/ImageControl.cxx
new file mode 100644
index 0000000000..c2bc0953c6
--- /dev/null
+++ b/forms/source/component/ImageControl.cxx
@@ -0,0 +1,992 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ImageControl.hxx"
+
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include <property.hxx>
+#include <services.hxx>
+#include <componenttools.hxx>
+
+#include <svtools/imageresourceaccess.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <com/sun/star/awt/PopupMenu.hpp>
+#include <com/sun/star/awt/XPopupMenu.hpp>
+#include <com/sun/star/awt/PopupMenuDirection.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/GraphicObject.hpp>
+#include <tools/urlobj.hxx>
+#include <tools/stream.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/streamhelper.hxx>
+#include <comphelper/guarding.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <svl/urihelper.hxx>
+
+#include <memory>
+
+#define ID_OPEN_GRAPHICS 1
+#define ID_CLEAR_GRAPHICS 2
+
+namespace frm
+{
+
+using namespace ::com::sun::star;
+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::ui::dialogs;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::graphic;
+using namespace ::com::sun::star::frame;
+
+
+//= OImageControlModel
+
+namespace
+{
+ enum ImageStoreType
+ {
+ ImageStoreBinary,
+ ImageStoreLink,
+
+ ImageStoreInvalid
+ };
+
+ ImageStoreType lcl_getImageStoreType( const sal_Int32 _nFieldType )
+ {
+ // binary/longvarchar types could be used to store images in binary representation
+ if ( ( _nFieldType == DataType::BINARY )
+ || ( _nFieldType == DataType::VARBINARY )
+ || ( _nFieldType == DataType::LONGVARBINARY )
+ || ( _nFieldType == DataType::OTHER )
+ || ( _nFieldType == DataType::OBJECT )
+ || ( _nFieldType == DataType::BLOB )
+ || ( _nFieldType == DataType::LONGVARCHAR )
+ || ( _nFieldType == DataType::CLOB )
+ )
+ return ImageStoreBinary;
+
+ // char types could be used to store links to images
+ if ( ( _nFieldType == DataType::CHAR )
+ || ( _nFieldType == DataType::VARCHAR )
+ )
+ return ImageStoreLink;
+
+ return ImageStoreInvalid;
+ }
+}
+
+
+// OImageControlModel
+
+Sequence<Type> OImageControlModel::_getTypes()
+{
+ return concatSequences(
+ OBoundControlModel::_getTypes(),
+ OImageControlModel_Base::getTypes()
+ );
+}
+
+
+OImageControlModel::OImageControlModel(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_IMAGECONTROL, FRM_SUN_CONTROL_IMAGECONTROL, false, false, false )
+ // use the old control name for compytibility reasons
+ ,m_bExternalGraphic( true )
+ ,m_bReadOnly( false )
+{
+ m_nClassId = FormComponentType::IMAGECONTROL;
+ initOwnValueProperty( PROPERTY_IMAGE_URL );
+
+ implConstruct();
+}
+
+
+OImageControlModel::OImageControlModel( const OImageControlModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ // use the old control name for compytibility reasons
+ ,m_bExternalGraphic( true )
+ ,m_bReadOnly( _pOriginal->m_bReadOnly )
+ ,m_sImageURL( _pOriginal->m_sImageURL )
+ ,m_xGraphicObject( _pOriginal->m_xGraphicObject )
+{
+ implConstruct();
+
+ osl_atomic_increment( &m_refCount );
+ {
+ // simulate a propertyChanged event for the ImageURL
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_handleNewImageURL_lck( eOther );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+void OImageControlModel::implConstruct()
+{
+ m_xImageProducer = new ImageProducer;
+ m_xImageProducer->SetDoneHdl( LINK( this, OImageControlModel, OnImageImportDone ) );
+}
+
+
+OImageControlModel::~OImageControlModel()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OImageControlModel::createClone()
+{
+ rtl::Reference<OImageControlModel> pClone = new OImageControlModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> OImageControlModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_IMAGECONTROL;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_IMAGECONTROL;
+ return aSupported;
+}
+
+
+Any SAL_CALL OImageControlModel::queryAggregation(const Type& _rType)
+{
+ // Order matters: we want to "override" the XImageProducer interface of the aggregate without
+ // own XImageProducer interface, thus we need to query OImageControlModel_Base first
+ Any aReturn = OImageControlModel_Base::queryInterface( _rType );
+
+ // BUT: _don't_ let it feel responsible for the XTypeProvider interface
+ // (as this is implemented by our base class in the proper way)
+ if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() )
+ || !aReturn.hasValue()
+ )
+ aReturn = OBoundControlModel::queryAggregation( _rType );
+
+ return aReturn;
+}
+
+
+bool OImageControlModel::approveDbColumnType( sal_Int32 _nColumnType )
+{
+ return ImageStoreInvalid != lcl_getImageStoreType( _nColumnType );
+}
+
+
+void OImageControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_READONLY:
+ rValue <<= m_bReadOnly;
+ break;
+ case PROPERTY_ID_IMAGE_URL:
+ rValue <<= m_sImageURL;
+ break;
+ case PROPERTY_ID_GRAPHIC:
+ rValue <<= m_xGraphicObject.is() ? m_xGraphicObject->getGraphic() : Reference< XGraphic >();
+ break;
+ default:
+ OBoundControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+}
+
+
+void OImageControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_READONLY :
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, "OImageControlModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ m_bReadOnly = getBOOL(rValue);
+ break;
+
+ case PROPERTY_ID_IMAGE_URL:
+ OSL_VERIFY( rValue >>= m_sImageURL );
+ impl_handleNewImageURL_lck( eOther );
+ {
+ ControlModelLock aLock( *this );
+ // that's a fake ... onValuePropertyChange expects to receive the only lock to our instance,
+ // but we're already called with our mutex locked ...
+ onValuePropertyChange( aLock );
+ }
+ break;
+
+ case PROPERTY_ID_GRAPHIC:
+ {
+ Reference< XGraphic > xGraphic;
+ OSL_VERIFY( rValue >>= xGraphic );
+ if ( !xGraphic.is() )
+ m_xGraphicObject.clear();
+ else
+ {
+ m_xGraphicObject = graphic::GraphicObject::create( m_xContext );
+ m_xGraphicObject->setGraphic( xGraphic );
+ }
+
+ if ( m_bExternalGraphic )
+ {
+ m_sImageURL = OUString();
+ // TODO: speaking strictly, this would need to be notified, since ImageURL is a bound property. However,
+ // this method here is called with a locked mutex, so we cannot simply call listeners ...
+ // I think the missing notification (and thus clients which potentially cannot observe the change)
+ // is less severe than the potential deadlock ...
+ }
+ }
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue);
+ break;
+ }
+}
+
+
+sal_Bool OImageControlModel::convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue)
+{
+ switch (nHandle)
+ {
+ case PROPERTY_ID_READONLY :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bReadOnly);
+
+ case PROPERTY_ID_IMAGE_URL:
+ return tryPropertyValue( rConvertedValue, rOldValue, rValue, m_sImageURL );
+
+ case PROPERTY_ID_GRAPHIC:
+ {
+ const Reference< XGraphic > xGraphic( getFastPropertyValue( PROPERTY_ID_GRAPHIC ), UNO_QUERY );
+ return tryPropertyValue( rConvertedValue, rOldValue, rValue, xGraphic );
+ }
+
+ default:
+ return OBoundControlModel::convertFastPropertyValue(rConvertedValue, rOldValue, nHandle, rValue);
+ }
+}
+
+
+void OImageControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OBoundControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 4);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_GRAPHIC, PROPERTY_ID_GRAPHIC, cppu::UnoType<XGraphic>::get(),
+ css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_IMAGE_URL, PROPERTY_ID_IMAGE_URL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_READONLY, PROPERTY_ID_READONLY, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+void OImageControlModel::describeAggregateProperties( Sequence< Property >& /* [out] */ o_rAggregateProperties ) const
+{
+ OBoundControlModel::describeAggregateProperties( o_rAggregateProperties );
+ // remove ImageURL and Graphic properties, we "override" them.
+ // This is because our aggregate synchronizes those
+ // two, but we have an own synchronization mechanism.
+ RemoveProperty( o_rAggregateProperties, PROPERTY_IMAGE_URL );
+ RemoveProperty( o_rAggregateProperties, PROPERTY_GRAPHIC );
+}
+
+
+OUString OImageControlModel::getServiceName()
+{
+ return FRM_COMPONENT_IMAGECONTROL; // old (non-sun) name for compatibility !
+}
+
+
+void OImageControlModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ // Base class
+ OBoundControlModel::write(_rxOutStream);
+ // Version
+ _rxOutStream->writeShort(0x0003);
+ // Name
+ _rxOutStream->writeBoolean(m_bReadOnly);
+ writeHelpTextCompatibly(_rxOutStream);
+ // from version 0x0003 : common properties
+ writeCommonProperties(_rxOutStream);
+}
+
+
+void OImageControlModel::read(const Reference<XObjectInputStream>& _rxInStream)
+{
+ OBoundControlModel::read(_rxInStream);
+
+ // Version
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ switch (nVersion)
+ {
+ case 0x0001:
+ m_bReadOnly = _rxInStream->readBoolean();
+ break;
+ case 0x0002:
+ m_bReadOnly = _rxInStream->readBoolean();
+ readHelpTextCompatibly(_rxInStream);
+ break;
+ case 0x0003:
+ m_bReadOnly = _rxInStream->readBoolean();
+ readHelpTextCompatibly(_rxInStream);
+ readCommonProperties(_rxInStream);
+ break;
+ default :
+ OSL_FAIL("OImageControlModel::read : unknown version !");
+ m_bReadOnly = false;
+ defaultCommonProperties();
+ break;
+ }
+ // Display default values after read
+ if ( !getControlSource().isEmpty() )
+ { // (not if we don't have a control source - the "State" property acts like it is persistent, then
+ ::osl::MutexGuard aGuard(m_aMutex); // resetNoBroadcast expects this mutex guarding
+ resetNoBroadcast();
+ }
+}
+
+
+bool OImageControlModel::impl_updateStreamForURL_lck( const OUString& _rURL, ValueChangeInstigator _eInstigator )
+{
+ // create a stream for the image specified by the URL
+ std::unique_ptr< SvStream > pImageStream;
+ Reference< XInputStream > xImageStream;
+
+ if ( ::svt::GraphicAccess::isSupportedURL( _rURL ) )
+ {
+ xImageStream = ::svt::GraphicAccess::getImageXStream( getContext(), _rURL );
+ }
+ else
+ {
+ pImageStream = ::utl::UcbStreamHelper::CreateStream( _rURL, StreamMode::READ );
+ bool bSetNull = (pImageStream == nullptr) || (ERRCODE_NONE != pImageStream->GetErrorCode());
+
+ if ( !bSetNull )
+ {
+ // get the size of the stream
+ sal_uInt64 const nSize = pImageStream->remainingSize();
+ if (pImageStream->GetBufferSize() < 8192)
+ pImageStream->SetBufferSize(8192);
+ pImageStream->Seek(STREAM_SEEK_TO_BEGIN);
+
+ xImageStream = new ::utl::OInputStreamHelper( new SvLockBytes( pImageStream.get(), false ), nSize );
+ }
+ }
+
+ if ( xImageStream.is() )
+ {
+ if ( m_xColumnUpdate.is() )
+ m_xColumnUpdate->updateBinaryStream( xImageStream, xImageStream->available() );
+ else
+ setControlValue( Any( xImageStream ), _eInstigator );
+ xImageStream->closeInput();
+ return true;
+ }
+
+ return false;
+}
+
+
+void OImageControlModel::impl_handleNewImageURL_lck( ValueChangeInstigator _eInstigator )
+{
+ switch ( lcl_getImageStoreType( getFieldType() ) )
+ {
+ case ImageStoreBinary:
+ if ( impl_updateStreamForURL_lck( m_sImageURL, _eInstigator ) )
+ return;
+ break;
+
+ case ImageStoreLink:
+ {
+ OUString sCommitURL( m_sImageURL );
+ if ( !m_sDocumentURL.isEmpty() )
+ sCommitURL = URIHelper::simpleNormalizedMakeRelative( m_sDocumentURL, sCommitURL );
+ OSL_ENSURE( m_xColumnUpdate.is(), "OImageControlModel::impl_handleNewImageURL_lck: no bound field, but ImageStoreLink?!" );
+ if ( m_xColumnUpdate.is() )
+ {
+ m_xColumnUpdate->updateString( sCommitURL );
+ return;
+ }
+ }
+ break;
+
+ case ImageStoreInvalid:
+ OSL_FAIL( "OImageControlModel::impl_handleNewImageURL_lck: image storage type type!" );
+ break;
+ }
+
+ // if we're here, then the above code was unable to update our field/control from the given URL
+ // => fall back to NULL/VOID
+ if ( m_xColumnUpdate.is() )
+ m_xColumnUpdate->updateNull();
+ else
+ setControlValue( Any(), _eInstigator );
+}
+
+
+bool OImageControlModel::commitControlValueToDbColumn( bool _bPostReset )
+{
+ if ( _bPostReset )
+ {
+ // since this is a "commit after reset", we can simply update the column
+ // with null - this is our "default" which we were just reset to
+ if ( m_xColumnUpdate.is() )
+ m_xColumnUpdate->updateNull();
+ }
+ else
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ impl_handleNewImageURL_lck( eDbColumnBinding );
+ }
+
+ return true;
+}
+
+
+namespace
+{
+ bool lcl_isValidDocumentURL( std::u16string_view _rDocURL )
+ {
+ return ( !_rDocURL.empty() && _rDocURL != u"private:object" );
+ }
+}
+
+
+void OImageControlModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ OBoundControlModel::onConnectedDbColumn( _rxForm );
+
+ try
+ {
+ Reference< XModel > xDocument( getXModel( *this ) );
+ if ( xDocument.is() )
+ {
+ m_sDocumentURL = xDocument->getURL();
+ if ( !lcl_isValidDocumentURL( m_sDocumentURL ) )
+ {
+ Reference< XChild > xAsChild( xDocument, UNO_QUERY );
+ while ( xAsChild.is() && !lcl_isValidDocumentURL( m_sDocumentURL ) )
+ {
+ xDocument.set( xAsChild->getParent(), UNO_QUERY );
+ if ( xDocument.is() )
+ m_sDocumentURL = xDocument->getURL();
+ xAsChild.set( xDocument, UNO_QUERY );
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+}
+
+
+void OImageControlModel::onDisconnectedDbColumn()
+{
+ OBoundControlModel::onDisconnectedDbColumn();
+
+ m_sDocumentURL.clear();
+}
+
+
+Any OImageControlModel::translateDbColumnToControlValue()
+{
+ switch ( lcl_getImageStoreType( getFieldType() ) )
+ {
+ case ImageStoreBinary:
+ {
+ Reference< XInputStream > xImageStream( m_xColumn->getBinaryStream() );
+ if ( m_xColumn->wasNull() )
+ xImageStream.clear();
+ return Any( xImageStream );
+ }
+ case ImageStoreLink:
+ {
+ OUString sImageLink( m_xColumn->getString() );
+ if ( !m_sDocumentURL.isEmpty() )
+ sImageLink = INetURLObject::GetAbsURL( m_sDocumentURL, sImageLink );
+ return Any( sImageLink );
+ }
+ case ImageStoreInvalid:
+ OSL_FAIL( "OImageControlModel::translateDbColumnToControlValue: invalid field type!" );
+ break;
+ }
+ return Any();
+}
+
+
+Any OImageControlModel::getControlValue( ) const
+{
+ return Any( m_sImageURL );
+}
+
+
+void OImageControlModel::doSetControlValue( const Any& _rValue )
+{
+ DBG_ASSERT( GetImageProducer() && m_xImageProducer.is(), "OImageControlModel::doSetControlValue: no image producer!" );
+ if ( !GetImageProducer() || !m_xImageProducer.is() )
+ return;
+
+ bool bStartProduction = false;
+ switch ( lcl_getImageStoreType( getFieldType() ) )
+ {
+ case ImageStoreBinary:
+ {
+ // give the image producer the stream
+ Reference< XInputStream > xInStream;
+ _rValue >>= xInStream;
+ GetImageProducer()->setImage( xInStream );
+ bStartProduction = true;
+ }
+ break;
+
+ case ImageStoreLink:
+ {
+ OUString sImageURL;
+ _rValue >>= sImageURL;
+ GetImageProducer()->SetImage( sImageURL );
+ bStartProduction = true;
+ }
+ break;
+
+ case ImageStoreInvalid:
+ OSL_FAIL( "OImageControlModel::doSetControlValue: invalid field type!" );
+ break;
+
+ } // switch ( lcl_getImageStoreType( getFieldType() ) )
+
+ if ( bStartProduction )
+ {
+ // start production
+ rtl::Reference< ImageProducer > xProducer = m_xImageProducer;
+ {
+ // release our mutex once (it's acquired in the calling method!), as starting the image production may
+ // result in the locking of the solar mutex (unfortunately the default implementation of our aggregate,
+ // VCLXImageControl, does this locking)
+ MutexRelease aRelease(m_aMutex);
+ xProducer->startProduction();
+ }
+ }
+}
+
+void OImageControlModel::resetNoBroadcast()
+{
+ if ( hasField() ) // only reset when we are connected to a column
+ OBoundControlModel::resetNoBroadcast( );
+}
+
+
+Reference< XImageProducer > SAL_CALL OImageControlModel::getImageProducer()
+{
+ return this;
+}
+
+
+void SAL_CALL OImageControlModel::addConsumer( const Reference< XImageConsumer >& _rxConsumer )
+{
+ GetImageProducer()->addConsumer( _rxConsumer );
+}
+
+
+void SAL_CALL OImageControlModel::removeConsumer( const Reference< XImageConsumer >& _rxConsumer )
+{
+ GetImageProducer()->removeConsumer( _rxConsumer );
+}
+
+
+void SAL_CALL OImageControlModel::startProduction( )
+{
+ GetImageProducer()->startProduction();
+}
+
+
+IMPL_LINK( OImageControlModel, OnImageImportDone, ::Graphic*, i_pGraphic, void )
+{
+ const Reference< XGraphic > xGraphic(i_pGraphic != nullptr ? i_pGraphic->GetXGraphic() : nullptr);
+ m_bExternalGraphic = false;
+ try
+ {
+ setPropertyValue( PROPERTY_GRAPHIC, Any( xGraphic ) );
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ m_bExternalGraphic = true;
+}
+
+
+// OImageControlControl
+
+Sequence<Type> OImageControlControl::_getTypes()
+{
+ return concatSequences(
+ OBoundControl::_getTypes(),
+ OImageControlControl_Base::getTypes()
+ );
+}
+
+
+OImageControlControl::OImageControlControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_IMAGECONTROL)
+ ,m_aModifyListeners( m_aMutex )
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ // Add as Focus- and MouseListener
+ Reference< XWindow > xComp;
+ query_aggregation( m_xAggregate, xComp );
+ if ( xComp.is() )
+ xComp->addMouseListener( this );
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+Any SAL_CALL OImageControlControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OBoundControl::queryAggregation( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = ::cppu::queryInterface(
+ _rType,
+ static_cast< XMouseListener* >( this ),
+ static_cast< XModifyBroadcaster* >( this )
+ );
+
+ return aReturn;
+}
+
+
+css::uno::Sequence<OUString> OImageControlControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_IMAGECONTROL;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_IMAGECONTROL;
+ return aSupported;
+}
+
+
+void SAL_CALL OImageControlControl::addModifyListener( const Reference< XModifyListener >& Listener )
+{
+ m_aModifyListeners.addInterface( Listener );
+}
+
+
+void SAL_CALL OImageControlControl::removeModifyListener( const Reference< XModifyListener >& Listener )
+{
+ m_aModifyListeners.removeInterface( Listener );
+}
+
+
+void SAL_CALL OImageControlControl::disposing()
+{
+ EventObject aEvent( *this );
+ m_aModifyListeners.disposeAndClear( aEvent );
+
+ OBoundControl::disposing();
+}
+
+
+void SAL_CALL OImageControlControl::disposing( const EventObject& Event )
+{
+ OBoundControl::disposing( Event );
+}
+
+
+void OImageControlControl::implClearGraphics( bool _bForce )
+{
+ Reference< XPropertySet > xSet( getModel(), UNO_QUERY );
+ if ( !xSet.is() )
+ return;
+
+ if ( _bForce )
+ {
+ OUString sOldImageURL;
+ xSet->getPropertyValue( PROPERTY_IMAGE_URL ) >>= sOldImageURL;
+
+ if ( sOldImageURL.isEmpty() )
+ // the ImageURL is already empty, so simply setting a new empty one would not suffice
+ // (since it would be ignored)
+ xSet->setPropertyValue( PROPERTY_IMAGE_URL, Any( OUString( "private:emptyImage" ) ) );
+ // (the concrete URL we're passing here doesn't matter. It's important that
+ // the model cannot resolve it to a valid resource describing an image stream
+ }
+
+ xSet->setPropertyValue( PROPERTY_IMAGE_URL, Any( OUString() ) );
+}
+
+
+bool OImageControlControl::implInsertGraphics()
+{
+ Reference< XPropertySet > xSet( getModel(), UNO_QUERY );
+ if ( !xSet.is() )
+ return false;
+
+ OUString sTitle = ResourceManager::loadString(RID_STR_IMPORT_GRAPHIC);
+ // build some arguments for the upcoming dialog
+ try
+ {
+ Reference< XWindow > xWindow( static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY );
+ ::sfx2::FileDialogHelper aDialog(TemplateDescription::FILEOPEN_LINK_PREVIEW, FileDialogFlags::Graphic,
+ Application::GetFrameWeld(xWindow));
+ aDialog.SetContext(sfx2::FileDialogHelper::FormsInsertImage);
+ aDialog.SetTitle( sTitle );
+
+ Reference< XFilePickerControlAccess > xController( aDialog.GetFilePicker(), UNO_QUERY_THROW );
+ xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, css::uno::Any(true));
+
+ Reference<XPropertySet> xBoundField;
+ if ( hasProperty( PROPERTY_BOUNDFIELD, xSet ) )
+ xSet->getPropertyValue( PROPERTY_BOUNDFIELD ) >>= xBoundField;
+ bool bHasField = xBoundField.is();
+
+ // if the control is bound to a DB field, then it's not possible to decide whether or not to link
+ xController->enableControl(ExtendedFilePickerElementIds::CHECKBOX_LINK, !bHasField );
+
+ // if the control is bound to a DB field, then linking of the image depends on the type of the field
+ bool bImageIsLinked = true;
+ if ( bHasField )
+ {
+ sal_Int32 nFieldType = DataType::OTHER;
+ OSL_VERIFY( xBoundField->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType );
+ bImageIsLinked = ( lcl_getImageStoreType( nFieldType ) == ImageStoreLink );
+ }
+ xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, Any( bImageIsLinked ) );
+
+ if ( ERRCODE_NONE == aDialog.Execute() )
+ {
+ // reset the url property in case it already has the value we're about to set - in this case
+ // our propertyChanged would not get called without this.
+ implClearGraphics( false );
+ bool bIsLink = false;
+ xController->getValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0) >>= bIsLink;
+ // Force bIsLink to be sal_True if we're bound to a field. Though we initialized the file picker with IsLink=TRUE
+ // in this case, and disabled the respective control, there might be picker implementations which do not
+ // respect this, and return IsLink=FALSE here. In this case, "normalize" the flag.
+ // #i112659#
+ bIsLink |= bHasField;
+ if ( !bIsLink )
+ {
+ Graphic aGraphic;
+ aDialog.GetGraphic( aGraphic );
+ xSet->setPropertyValue( PROPERTY_GRAPHIC, Any( aGraphic.GetXGraphic() ) );
+ }
+ else
+ xSet->setPropertyValue( PROPERTY_IMAGE_URL, Any( aDialog.GetPath() ) );
+
+ return true;
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OImageControlControl::implInsertGraphics: caught an exception while attempting to execute the FilePicker!");
+ }
+ return false;
+}
+
+
+bool OImageControlControl::impl_isEmptyGraphics_nothrow() const
+{
+ bool bIsEmpty = true;
+
+ try
+ {
+ Reference< XPropertySet > xModelProps( const_cast< OImageControlControl* >( this )->getModel(), UNO_QUERY_THROW );
+ Reference< XGraphic > xGraphic;
+ OSL_VERIFY( xModelProps->getPropertyValue("Graphic") >>= xGraphic );
+ bIsEmpty = !xGraphic.is();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return bIsEmpty;
+}
+
+// MouseListener
+
+void OImageControlControl::mousePressed(const css::awt::MouseEvent& e)
+{
+ SolarMutexGuard aGuard;
+
+ if (e.Buttons != MouseButton::LEFT)
+ return;
+
+ bool bModified = false;
+ // is this a request for a context menu?
+ if ( e.PopupTrigger )
+ {
+ Reference< XPopupMenu > xMenu( awt::PopupMenu::create( m_xContext ) );
+ DBG_ASSERT( xMenu.is(), "OImageControlControl::mousePressed: could not create a popup menu!" );
+
+ Reference< XWindowPeer > xWindowPeer = getPeer();
+ DBG_ASSERT( xWindowPeer.is(), "OImageControlControl::mousePressed: no window!" );
+
+ if ( xMenu.is() && xWindowPeer.is() )
+ {
+ xMenu->insertItem( ID_OPEN_GRAPHICS, ResourceManager::loadString(RID_STR_OPEN_GRAPHICS), 0, 0 );
+ xMenu->insertItem( ID_CLEAR_GRAPHICS, ResourceManager::loadString(RID_STR_CLEAR_GRAPHICS), 0, 1 );
+
+ // check if the ImageURL is empty
+ if ( impl_isEmptyGraphics_nothrow() )
+ xMenu->enableItem( ID_CLEAR_GRAPHICS, false );
+
+ awt::Rectangle aRect( e.X, e.Y, 0, 0 );
+ if ( ( e.X < 0 ) || ( e.Y < 0 ) )
+ { // context menu triggered by keyboard
+ // position it in the center of the control
+ Reference< XWindow > xWindow( static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY );
+ OSL_ENSURE( xWindow.is(), "OImageControlControl::mousePressed: me not a window? How this?" );
+ if ( xWindow.is() )
+ {
+ awt::Rectangle aPosSize = xWindow->getPosSize();
+ aRect.X = aPosSize.Width / 2;
+ aRect.Y = aPosSize.Height / 2;
+ }
+ }
+
+ const sal_Int16 nResult = xMenu->execute( xWindowPeer, aRect, PopupMenuDirection::EXECUTE_DEFAULT );
+
+ switch ( nResult )
+ {
+ case ID_OPEN_GRAPHICS:
+ implInsertGraphics();
+ bModified = true;
+ break;
+
+ case ID_CLEAR_GRAPHICS:
+ implClearGraphics( true );
+ bModified = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+
+ // Double click
+ if (e.ClickCount == 2)
+ {
+
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (!xSet.is())
+ return;
+
+ // If the Control is not bound, do not display a dialog (because the to-be-sent URL would be invalid anyway)
+ Reference<XPropertySet> xBoundField;
+ if (hasProperty(PROPERTY_BOUNDFIELD, xSet))
+ xBoundField.set(
+ xSet->getPropertyValue(PROPERTY_BOUNDFIELD),
+ css::uno::UNO_QUERY);
+ if (!xBoundField.is())
+ {
+ // but only if our IMAGE_URL property is handled as if it is transient, which is equivalent to
+ // an empty control source
+ if ( !hasProperty(PROPERTY_CONTROLSOURCE, xSet) || !::comphelper::getString(xSet->getPropertyValue(PROPERTY_CONTROLSOURCE)).isEmpty() )
+ return;
+ }
+
+ bool bReadOnly = false;
+ xSet->getPropertyValue(PROPERTY_READONLY) >>= bReadOnly;
+ if (bReadOnly)
+ return;
+
+ if ( implInsertGraphics() )
+ bModified = true;
+ }
+ }
+
+ if ( bModified )
+ {
+ EventObject aEvent( *this );
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent );
+ }
+}
+
+
+void SAL_CALL OImageControlControl::mouseReleased(const awt::MouseEvent& /*e*/)
+{
+}
+
+
+void SAL_CALL OImageControlControl::mouseEntered(const awt::MouseEvent& /*e*/)
+{
+}
+
+
+void SAL_CALL OImageControlControl::mouseExited(const awt::MouseEvent& /*e*/)
+{
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OImageControlModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OImageControlModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OImageControlControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OImageControlControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ImageControl.hxx b/forms/source/component/ImageControl.hxx
new file mode 100644
index 0000000000..245d13163e
--- /dev/null
+++ b/forms/source/component/ImageControl.hxx
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include "imgprod.hxx"
+#include <com/sun/star/form/XImageProducerSupplier.hpp>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/graphic/XGraphicObject.hpp>
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <rtl/ref.hxx>
+
+
+namespace frm
+{
+
+
+// OImageControlModel
+
+typedef ::cppu::ImplHelper2 < css::form::XImageProducerSupplier
+ , css::awt::XImageProducer
+ > OImageControlModel_Base;
+
+class OImageControlModel final
+ :public OImageControlModel_Base
+ ,public OBoundControlModel
+{
+ rtl::Reference<ImageProducer> m_xImageProducer;
+ bool m_bExternalGraphic;
+ bool m_bReadOnly;
+ OUString m_sImageURL;
+ css::uno::Reference< css::graphic::XGraphicObject >
+ m_xGraphicObject;
+ OUString m_sDocumentURL;
+
+ // UNO binding
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+ ImageProducer* GetImageProducer() { return m_xImageProducer.get(); }
+
+public:
+ OImageControlModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OImageControlModel(
+ const OImageControlModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OImageControlModel() override;
+
+ 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;
+
+ // UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(OImageControlModel, OBoundControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OImageControlModel"; }
+
+ virtual css::uno::Sequence<OUString> 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;
+
+ // XImageProducerSupplier
+ virtual css::uno::Reference< css::awt::XImageProducer> SAL_CALL getImageProducer() override;
+
+ // XImageProducer
+ virtual void SAL_CALL addConsumer( const css::uno::Reference< css::awt::XImageConsumer >& xConsumer ) override;
+ virtual void SAL_CALL removeConsumer( const css::uno::Reference< css::awt::XImageConsumer >& xConsumer ) override;
+ virtual void SAL_CALL startProduction( ) override;
+
+ // OControlModel's property handling
+ virtual void describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OBoundControlModel::disposing;
+ using OBoundControlModel::getFastPropertyValue;
+
+private:
+ // OBoundControlModel overridables
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+ virtual void onDisconnectedDbColumn() override;
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Any getControlValue( ) const override;
+ virtual void doSetControlValue( const css::uno::Any& _rValue ) override;
+
+ virtual bool approveDbColumnType(sal_Int32 _nColumnType) override;
+
+ virtual void resetNoBroadcast() override;
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ void implConstruct();
+
+ /** displays the image described by the given URL
+ @precond
+ our own mutex is locked
+ */
+ void impl_handleNewImageURL_lck( ValueChangeInstigator _eInstigator );
+
+ /** updates the binary stream, created from loading the file which the given URL points to, into our
+ bound field, or the control itself if there is no bound field
+ */
+ bool impl_updateStreamForURL_lck( const OUString& _rURL, ValueChangeInstigator _eInstigator );
+
+ DECL_LINK( OnImageImportDone, ::Graphic*, void );
+};
+
+typedef ::cppu::ImplHelper2 < css::awt::XMouseListener
+ , css::util::XModifyBroadcaster
+ > OImageControlControl_Base;
+class OImageControlControl : public OBoundControl
+ , public OImageControlControl_Base
+{
+private:
+ ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener> m_aModifyListeners;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit OImageControlControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS( OImageControlControl, OBoundControl )
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OImageControlControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XMouseListener
+ virtual void SAL_CALL mousePressed(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseReleased(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseEntered(const css::awt::MouseEvent& e) override;
+ virtual void SAL_CALL mouseExited(const css::awt::MouseEvent& e) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+private:
+ void implClearGraphics( bool _bForce );
+ bool implInsertGraphics();
+
+ /** determines whether the control does currently have an empty graphic set
+ */
+ bool impl_isEmptyGraphics_nothrow() const;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ListBox.cxx b/forms/source/component/ListBox.cxx
new file mode 100644
index 0000000000..79d6be399d
--- /dev/null
+++ b/forms/source/component/ListBox.cxx
@@ -0,0 +1,2202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include "ListBox.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <frm_resource.hxx>
+#include <strings.hrc>
+#include "BaseListBox.hxx"
+#include <componenttools.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/formattedcolumnvalue.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+#include <unotools/sharedunocomponent.hxx>
+
+#include <optional>
+
+#include <algorithm>
+#include <iterator>
+#include <climits>
+
+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;
+ using namespace ::dbtools;
+
+ using ::connectivity::ORowSetValue;
+
+ const ::connectivity::ORowSetValue OListBoxModel::s_aEmptyValue;
+ const ::connectivity::ORowSetValue OListBoxModel::s_aEmptyStringValue = OUString();
+
+ //= helper
+
+ namespace
+ {
+
+ struct RowSetValueToString
+ {
+ OUString operator()( const ORowSetValue& _value ) const
+ {
+ return _value.getString();
+ }
+ };
+
+
+ struct AppendRowSetValueString
+ {
+ explicit AppendRowSetValueString( OUString& _string )
+ :m_string( _string )
+ {
+ }
+
+ void operator()( const ORowSetValue& _append )
+ {
+ m_string += _append.getString();
+ }
+
+ private:
+ OUString& m_string;
+ };
+
+
+ Sequence< OUString > lcl_convertToStringSequence( const ValueList& _values )
+ {
+ Sequence< OUString > aStrings( _values.size() );
+ ::std::transform(
+ _values.begin(),
+ _values.end(),
+ aStrings.getArray(),
+ RowSetValueToString()
+ );
+ return aStrings;
+ }
+ }
+
+
+ //= ItemEventDescription
+
+ typedef ::comphelper::EventHolder< ItemEvent > ItemEventDescription;
+
+
+ //= OListBoxModel
+
+ Sequence< Type> OListBoxModel::_getTypes()
+ {
+ return TypeBag(
+ OBoundControlModel::_getTypes(),
+ OEntryListHelper::getTypes(),
+ OErrorBroadcaster::getTypes()
+ ).getTypes();
+ }
+
+ // stuff common to all constructors
+ void OListBoxModel::init()
+ {
+ startAggregatePropertyListening( PROPERTY_STRINGITEMLIST );
+ startAggregatePropertyListening( PROPERTY_TYPEDITEMLIST );
+ }
+
+
+ OListBoxModel::OListBoxModel(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_LISTBOX, FRM_SUN_CONTROL_LISTBOX, true, true, true )
+ // use the old control name for compatibility reasons
+ ,OEntryListHelper( static_cast<OControlModel&>(*this) )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,m_nConvertedBoundValuesType(0)
+ ,m_nNULLPos(-1)
+ ,m_nBoundColumnType( DataType::SQLNULL )
+ {
+
+ m_nClassId = FormComponentType::LISTBOX;
+ m_eListSourceType = ListSourceType_VALUELIST;
+ m_aBoundColumn <<= sal_Int16(1);
+ initValueProperty( PROPERTY_SELECT_SEQ, PROPERTY_ID_SELECT_SEQ);
+
+ init();
+ }
+
+
+ OListBoxModel::OListBoxModel( const OListBoxModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ ,OEntryListHelper( *_pOriginal, static_cast<OControlModel&>(*this) )
+ ,OErrorBroadcaster( OComponentHelper::rBHelper )
+ ,m_eListSourceType( _pOriginal->m_eListSourceType )
+ ,m_aBoundColumn( _pOriginal->m_aBoundColumn )
+ ,m_aListSourceValues( _pOriginal->m_aListSourceValues )
+ ,m_aBoundValues( _pOriginal->m_aBoundValues )
+ ,m_nConvertedBoundValuesType(0)
+ ,m_aDefaultSelectSeq( _pOriginal->m_aDefaultSelectSeq )
+ ,m_nNULLPos(-1)
+ ,m_nBoundColumnType( DataType::SQLNULL )
+ {
+
+ init();
+ }
+
+
+ OListBoxModel::~OListBoxModel()
+ {
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+ }
+
+ // XCloneable
+
+ css::uno::Reference< css::util::XCloneable > SAL_CALL OListBoxModel::createClone()
+{
+ rtl::Reference<OListBoxModel> pClone = new OListBoxModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+ // XServiceInfo
+
+ css::uno::Sequence<OUString> SAL_CALL OListBoxModel::getSupportedServiceNames()
+ {
+ css::uno::Sequence<OUString> 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_LISTBOX;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_LISTBOX;
+ *pStoreTo++ = BINDABLE_DATABASE_LIST_BOX;
+
+ *pStoreTo++ = FRM_COMPONENT_LISTBOX;
+
+ return aSupported;
+ }
+
+
+ Any SAL_CALL OListBoxModel::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;
+ }
+
+ // OComponentHelper
+
+ void OListBoxModel::disposing()
+ {
+ OBoundControlModel::disposing();
+ OEntryListHelper::disposing();
+ OErrorBroadcaster::disposing();
+ }
+
+
+ void OListBoxModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
+ {
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_BOUNDCOLUMN:
+ _rValue = m_aBoundColumn;
+ break;
+
+ case PROPERTY_ID_LISTSOURCETYPE:
+ _rValue <<= m_eListSourceType;
+ break;
+
+ case PROPERTY_ID_LISTSOURCE:
+ _rValue <<= lcl_convertToStringSequence( m_aListSourceValues );
+ break;
+
+ case PROPERTY_ID_VALUE_SEQ:
+ _rValue <<= lcl_convertToStringSequence( m_aBoundValues );
+ break;
+
+ case PROPERTY_ID_SELECT_VALUE_SEQ:
+ _rValue <<= getCurrentMultiValue();
+ break;
+
+ case PROPERTY_ID_SELECT_VALUE:
+ _rValue = getCurrentSingleValue();
+ break;
+
+ case PROPERTY_ID_DEFAULT_SELECT_SEQ:
+ _rValue <<= m_aDefaultSelectSeq;
+ break;
+
+ case PROPERTY_ID_STRINGITEMLIST:
+ _rValue <<= comphelper::containerToSequence(getStringItemList());
+ break;
+
+ case PROPERTY_ID_TYPEDITEMLIST:
+ _rValue <<= getTypedItemList();
+ break;
+
+ default:
+ OBoundControlModel::getFastPropertyValue(_rValue, _nHandle);
+ }
+ }
+
+
+ void OListBoxModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+ {
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_BOUNDCOLUMN :
+ DBG_ASSERT((_rValue.getValueType().getTypeClass() == TypeClass_SHORT) || (_rValue.getValueType().getTypeClass() == TypeClass_VOID),
+ "OListBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ m_aBoundColumn = _rValue;
+ break;
+
+ case PROPERTY_ID_LISTSOURCETYPE :
+ DBG_ASSERT(_rValue.getValueType().equals(::cppu::UnoType<ListSourceType>::get()),
+ "OComboBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_eListSourceType;
+ break;
+
+ case PROPERTY_ID_LISTSOURCE:
+ {
+ // extract
+ Sequence< OUString > aListSource;
+ OSL_VERIFY( _rValue >>= aListSource );
+
+ // copy to member
+ ValueList().swap(m_aListSourceValues);
+ ::std::copy(
+ std::cbegin(aListSource),
+ std::cend(aListSource),
+ ::std::insert_iterator< ValueList >( m_aListSourceValues, m_aListSourceValues.end() )
+ );
+
+ // propagate
+ if ( m_eListSourceType == ListSourceType_VALUELIST )
+ {
+ setBoundValues(std::vector(m_aListSourceValues));
+ }
+ else
+ {
+ if ( m_xCursor.is() && !hasField() && !hasExternalListSource() )
+ // listbox is already connected to a database, and no external list source
+ // data source changed -> refresh
+ loadData( false );
+ }
+ }
+ break;
+
+ case PROPERTY_ID_VALUE_SEQ :
+ SAL_WARN( "forms.component", "ValueItemList is read-only!" );
+ throw PropertyVetoException();
+
+ case PROPERTY_ID_SELECT_VALUE_SEQ :
+ {
+ Sequence< const Any > v;
+ _rValue >>= v;
+ Any newSelectSeq(translateBindingValuesToControlValue(v));
+ setControlValue( newSelectSeq, eOther );
+ }
+ break;
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ case PROPERTY_ID_SELECT_VALUE :
+ {
+ ORowSetValue v;
+ v.fill(_rValue);
+ Any newSelectSeq(translateDbValueToControlValue(v));
+ setControlValue( newSelectSeq, eOther );
+ }
+ break;
+#endif
+ case PROPERTY_ID_DEFAULT_SELECT_SEQ :
+ DBG_ASSERT(_rValue.getValueType().equals(cppu::UnoType<Sequence<sal_Int16>>::get()),
+ "OListBoxModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ _rValue >>= m_aDefaultSelectSeq;
+
+ DBG_ASSERT(m_xAggregateFastSet.is(), "OListBoxModel::setFastPropertyValue_NoBroadcast(DEFAULT_SELECT_SEQ) : invalid aggregate !");
+ if ( m_xAggregateFastSet.is() )
+ setControlValue( _rValue, eOther );
+ break;
+
+ case PROPERTY_ID_STRINGITEMLIST:
+ {
+ ControlModelLock aLock( *this );
+ setNewStringItemList( _rValue, aLock );
+ // TODO: 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.
+ }
+ resetNoBroadcast();
+ break;
+
+ case PROPERTY_ID_TYPEDITEMLIST:
+ {
+ ControlModelLock aLock( *this );
+ setNewTypedItemList( _rValue, aLock );
+ // Same TODO as above.
+ }
+ resetNoBroadcast();
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
+ }
+ }
+
+
+ sal_Bool OListBoxModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue)
+ {
+ bool bModified(false);
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_BOUNDCOLUMN :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aBoundColumn, ::cppu::UnoType<sal_Int16>::get());
+ break;
+
+ case PROPERTY_ID_LISTSOURCETYPE:
+ bModified = tryPropertyValueEnum(_rConvertedValue, _rOldValue, _rValue, m_eListSourceType);
+ break;
+
+ case PROPERTY_ID_LISTSOURCE:
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, lcl_convertToStringSequence( m_aListSourceValues ) );
+ break;
+
+ case PROPERTY_ID_VALUE_SEQ :
+ SAL_WARN( "forms.component", "ValueItemList is read-only!" );
+ throw IllegalArgumentException();
+
+ case PROPERTY_ID_SELECT_VALUE_SEQ :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, getCurrentMultiValue());
+ break;
+
+ case PROPERTY_ID_SELECT_VALUE :
+ {
+ // Any from connectivity::ORowSetValue
+ Any _rCurrentValue = getCurrentSingleValue();
+ if (_rCurrentValue != _rValue)
+ {
+ _rOldValue = _rCurrentValue;
+ _rConvertedValue = _rValue;
+ bModified = true;
+ }
+ break;
+ }
+ case PROPERTY_ID_DEFAULT_SELECT_SEQ :
+ bModified = tryPropertyValue(_rConvertedValue, _rOldValue, _rValue, m_aDefaultSelectSeq);
+ 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:
+ return OBoundControlModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue);
+ }
+ return bModified;
+ }
+
+
+ void SAL_CALL OListBoxModel::setPropertyValues( const Sequence< OUString >& _rPropertyNames, const Sequence< Any >& _rValues )
+ {
+ // if both SelectedItems and StringItemList are set, care for this
+ // #i27024#
+ const Any* pSelectSequenceValue = nullptr;
+
+ const OUString* pSelectedItemsPos = std::find(
+ _rPropertyNames.begin(), _rPropertyNames.end(), PROPERTY_SELECT_SEQ
+ );
+ auto aStringItemListExists = std::any_of(
+ _rPropertyNames.begin(), _rPropertyNames.end(),
+ [](OUString const & s) { return s == PROPERTY_STRINGITEMLIST; }
+ );
+ if ( ( pSelectedItemsPos != _rPropertyNames.end() ) && aStringItemListExists )
+ {
+ if (_rPropertyNames.getLength() != _rValues.getLength())
+ throw css::lang::IllegalArgumentException("lengths do not match",
+ static_cast<cppu::OWeakObject*>(this), -1);
+
+ // both properties are present
+ // -> remember the value for the select sequence
+ pSelectSequenceValue = _rValues.getConstArray() + ( pSelectedItemsPos - _rPropertyNames.begin() );
+ }
+
+ OBoundControlModel::setPropertyValues( _rPropertyNames, _rValues );
+
+ if ( pSelectSequenceValue )
+ {
+ setPropertyValue( PROPERTY_SELECT_SEQ, *pSelectSequenceValue );
+ // Note that this is the only reliable way, since one of the properties is implemented
+ // by ourself, and one is implemented by the aggregate, we cannot rely on any particular
+ // results when setting them both - too many undocumented behavior in all the involved
+
+ }
+ }
+
+
+ void OListBoxModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OBoundControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 10);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_BOUNDCOLUMN, PROPERTY_ID_BOUNDCOLUMN, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCETYPE, PROPERTY_ID_LISTSOURCETYPE, cppu::UnoType<ListSourceType>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_LISTSOURCE, PROPERTY_ID_LISTSOURCE, cppu::UnoType<css::uno::Sequence<OUString>>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_VALUE_SEQ, PROPERTY_ID_VALUE_SEQ, cppu::UnoType<css::uno::Sequence<OUString>>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_SELECT_VALUE_SEQ, PROPERTY_ID_SELECT_VALUE_SEQ, cppu::UnoType<Sequence< Any >>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_SELECT_VALUE, PROPERTY_ID_SELECT_VALUE, cppu::UnoType<Any>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_SELECT_SEQ, PROPERTY_ID_DEFAULT_SELECT_SEQ, cppu::UnoType<Sequence<sal_Int16>>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_STRINGITEMLIST, PROPERTY_ID_STRINGITEMLIST, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TYPEDITEMLIST, PROPERTY_ID_TYPEDITEMLIST, cppu::UnoType<Sequence< Any >>::get(), css::beans::PropertyAttribute::OPTIONAL);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+ }
+
+
+ void OListBoxModel::_propertyChanged( const PropertyChangeEvent& i_rEvent )
+ {
+ if ( i_rEvent.PropertyName == PROPERTY_STRINGITEMLIST )
+ {
+ ControlModelLock aLock( *this );
+ // SYNCHRONIZED ----->
+ // our aggregate internally changed its StringItemList property - reflect this in our "overridden"
+ // version of the property
+ setNewStringItemList( i_rEvent.NewValue, aLock );
+ // <----- SYNCHRONIZED
+ return;
+ }
+ else if ( i_rEvent.PropertyName == PROPERTY_TYPEDITEMLIST )
+ {
+ ControlModelLock aLock( *this );
+ // SYNCHRONIZED ----->
+ // our aggregate internally changed its TypedItemList property - reflect this in our "overridden"
+ // version of the property
+ setNewTypedItemList( i_rEvent.NewValue, aLock );
+ // <----- SYNCHRONIZED
+ return;
+ }
+ OBoundControlModel::_propertyChanged( i_rEvent );
+ }
+
+
+ void OListBoxModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+ {
+ OBoundControlModel::describeAggregateProperties( _rAggregateProps );
+
+ // superseded properties:
+ RemoveProperty( _rAggregateProps, PROPERTY_STRINGITEMLIST );
+ RemoveProperty( _rAggregateProps, PROPERTY_TYPEDITEMLIST );
+ }
+
+
+ OUString SAL_CALL OListBoxModel::getServiceName()
+ {
+ return FRM_COMPONENT_LISTBOX; // old (non-sun) name for compatibility !
+ }
+
+
+ void SAL_CALL OListBoxModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+ {
+ OBoundControlModel::write(_rxOutStream);
+
+ // Dummy sequence, to stay compatible if SelectSeq is not saved anymore
+ Sequence<sal_Int16> aDummySeq;
+
+ // Version
+ // Version 0x0002: ListSource becomes StringSeq
+ _rxOutStream->writeShort(0x0004);
+
+ // Masking for any
+ sal_uInt16 nAnyMask = 0;
+ if (m_aBoundColumn.getValueType().getTypeClass() != TypeClass_VOID)
+ nAnyMask |= BOUNDCOLUMN;
+
+ _rxOutStream << nAnyMask;
+
+ _rxOutStream << lcl_convertToStringSequence( m_aListSourceValues );
+ _rxOutStream << static_cast<sal_Int16>(m_eListSourceType);
+ _rxOutStream << aDummySeq;
+ _rxOutStream << m_aDefaultSelectSeq;
+
+ if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
+ {
+ sal_Int16 nBoundColumn = 0;
+ m_aBoundColumn >>= nBoundColumn;
+ _rxOutStream << nBoundColumn;
+ }
+ writeHelpTextCompatibly(_rxOutStream);
+
+ // from version 0x0004 : common properties
+ writeCommonProperties(_rxOutStream);
+ }
+
+
+ void SAL_CALL OListBoxModel::read(const Reference<XObjectInputStream>& _rxInStream)
+ {
+ // We need to respect dependencies for certain variables.
+ // Therefore, we need to set them explicitly via setPropertyValue().
+
+ 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, "OListBoxModel::read : version 0 ? this should never have been written !");
+
+ if (nVersion > 0x0004)
+ {
+ SAL_WARN( "forms.component", "OListBoxModel::read : invalid (means unknown) version !");
+ ValueList().swap(m_aListSourceValues);
+ m_aBoundColumn <<= sal_Int16(0);
+ clearBoundValues();
+ m_eListSourceType = ListSourceType_VALUELIST;
+ m_aDefaultSelectSeq.realloc(0);
+ defaultCommonProperties();
+ return;
+ }
+
+ // Masking for any
+ sal_uInt16 nAnyMask;
+ _rxInStream >> nAnyMask;
+
+ // ListSourceSeq
+ css::uno::Sequence<OUString> aListSourceSeq;
+ if (nVersion == 0x0001)
+ {
+ // Create ListSourceSeq from String
+ OUString sListSource;
+ _rxInStream >> sListSource;
+
+ const sal_Int32 nTokens{ comphelper::string::getTokenCount(sListSource, ';') };
+ aListSourceSeq.realloc( nTokens );
+ sal_Int32 nIdx{ 0 };
+ for (sal_Int32 i=0; i<nTokens; ++i)
+ {
+ aListSourceSeq.getArray()[i] = sListSource.getToken(0, ';', nIdx);
+ }
+ }
+ else
+ _rxInStream >> aListSourceSeq;
+
+ sal_Int16 nListSourceType;
+ _rxInStream >> nListSourceType;
+ m_eListSourceType = static_cast<ListSourceType>(nListSourceType);
+ Any aListSourceSeqAny;
+ aListSourceSeqAny <<= aListSourceSeq;
+
+ setFastPropertyValue(PROPERTY_ID_LISTSOURCE, aListSourceSeqAny );
+
+ // Dummy sequence, to stay compatible if SelectSeq is not saved anymore
+ Sequence<sal_Int16> aDummySeq;
+ _rxInStream >> aDummySeq;
+
+ // DefaultSelectSeq
+ Sequence<sal_Int16> aDefaultSelectSeq;
+ _rxInStream >> aDefaultSelectSeq;
+ Any aDefaultSelectSeqAny;
+ aDefaultSelectSeqAny <<= aDefaultSelectSeq;
+ setFastPropertyValue(PROPERTY_ID_DEFAULT_SELECT_SEQ, aDefaultSelectSeqAny);
+
+ // BoundColumn
+ if ((nAnyMask & BOUNDCOLUMN) == BOUNDCOLUMN)
+ {
+ sal_Int16 nValue;
+ _rxInStream >> nValue;
+ m_aBoundColumn <<= nValue;
+ }
+ else // the constructor initialises to 1, so if it is empty,
+ // we must explicitly set to empty
+ {
+ m_aBoundColumn = Any();
+ }
+
+ if (nVersion > 2)
+ readHelpTextCompatibly(_rxInStream);
+
+ // if our string list is not filled from the value list, we must empty it
+ // this can be the case when somebody saves in alive mode
+ if ( ( m_eListSourceType != ListSourceType_VALUELIST )
+ && !hasExternalListSource()
+ )
+ {
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence<OUString>() ) );
+ setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
+ }
+
+ if (nVersion > 3)
+ readCommonProperties(_rxInStream);
+
+ // Display the default values after reading
+ if ( !getControlSource().isEmpty() )
+ // (not if we don't have a control source - the "State" property acts like it is persistent, then
+ resetNoBroadcast();
+ }
+
+
+ void OListBoxModel::loadData( bool _bForce )
+ {
+ SAL_INFO( "forms.component", "OListBoxModel::loadData" );
+ DBG_ASSERT( m_eListSourceType != ListSourceType_VALUELIST, "OListBoxModel::loadData: cannot load value list from DB!" );
+ DBG_ASSERT( !hasExternalListSource(), "OListBoxModel::loadData: cannot load from DB when I have an external list source!" );
+
+ const sal_Int16 nNULLPosBackup( m_nNULLPos );
+ const sal_Int32 nBoundColumnTypeBackup( m_nBoundColumnType );
+ m_nNULLPos = -1;
+ m_nBoundColumnType = DataType::SQLNULL;
+
+ // pre-requisites:
+ // PRE1: connection
+ Reference< XConnection > xConnection;
+ // is the active connection of our form
+ Reference< XPropertySet > xFormProps( m_xCursor, UNO_QUERY );
+ if ( xFormProps.is() )
+ xFormProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConnection;
+
+ // PRE2: list source
+ OUString sListSource;
+ // if our list source type is no value list, we need to concatenate
+ // the single list source elements
+ ::std::for_each(
+ m_aListSourceValues.begin(),
+ m_aListSourceValues.end(),
+ AppendRowSetValueString( sListSource )
+ );
+
+ // outta here if we don't have all pre-requisites
+ if ( !xConnection.is() || sListSource.isEmpty() )
+ {
+ clearBoundValues();
+ return;
+ }
+
+ ::std::optional< sal_Int16 > aBoundColumn(std::nullopt);
+ if ( m_aBoundColumn.getValueType().getTypeClass() == TypeClass_SHORT )
+ {
+ sal_Int16 nBoundColumn( 0 );
+ m_aBoundColumn >>= nBoundColumn;
+ aBoundColumn = nBoundColumn;
+ }
+
+ ::utl::SharedUNOComponent< XResultSet > xListCursor;
+ try
+ {
+ m_aListRowSet.setConnection( xConnection );
+
+ bool bExecute = false;
+ switch (m_eListSourceType)
+ {
+ case ListSourceType_TABLEFIELDS:
+ // don't work with a statement here, the fields will be collected below
+ break;
+
+ case ListSourceType_TABLE:
+ {
+ Reference<XNameAccess> xFieldsByName = getTableFields(xConnection, sListSource);
+ Reference<XIndexAccess> xFieldsByIndex(xFieldsByName, UNO_QUERY);
+
+ // do we have a bound column if yes we have to select it
+ // and the displayed column is the first column otherwise we act as a combobox
+ OUString aFieldName;
+ OUString aBoundFieldName;
+
+ if ( !!aBoundColumn && ( *aBoundColumn >= 0 ) && xFieldsByIndex.is() )
+ {
+ if ( *aBoundColumn >= xFieldsByIndex->getCount() )
+ break;
+
+ Reference<XPropertySet> xFieldAsSet(xFieldsByIndex->getByIndex( *aBoundColumn ),UNO_QUERY);
+ assert(xFieldAsSet.is());
+ xFieldAsSet->getPropertyValue(PROPERTY_NAME) >>= aBoundFieldName;
+ aBoundColumn = 1;
+
+ xFieldAsSet.set(xFieldsByIndex->getByIndex(0),UNO_QUERY);
+ xFieldAsSet->getPropertyValue(PROPERTY_NAME) >>= aFieldName;
+ }
+ else if (xFieldsByName.is())
+ {
+ if ( xFieldsByName->hasByName( getControlSource() ) )
+ aFieldName = getControlSource();
+ else
+ {
+ // otherwise look for the alias
+ Reference< XColumnsSupplier > xSupplyFields;
+ xFormProps->getPropertyValue("SingleSelectQueryComposer") >>= xSupplyFields;
+
+ // search the field
+ DBG_ASSERT(xSupplyFields.is(), "OListBoxModel::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<XDatabaseMetaData> xMeta = xConnection->getMetaData();
+ OUString aQuote = xMeta->getIdentifierQuoteString();
+ OUString aStatement("SELECT ");
+ if (aBoundFieldName.isEmpty()) // act like a combobox
+ aStatement += "DISTINCT ";
+
+ aStatement += quoteName(aQuote,aFieldName);
+ if (!aBoundFieldName.isEmpty())
+ {
+ aStatement += ", " + quoteName(aQuote, aBoundFieldName);
+ }
+ aStatement += " FROM ";
+
+ OUString sCatalog, sSchema, sTable;
+ qualifiedNameComponents( xMeta, sListSource, sCatalog, sSchema, sTable, EComposeRule::InDataManipulation );
+ aStatement += composeTableNameForSelect( xConnection, sCatalog, sSchema, sTable );
+
+ m_aListRowSet.setEscapeProcessing( false );
+ m_aListRowSet.setCommand( aStatement );
+ bExecute = true;
+ }
+ break;
+
+ case ListSourceType_QUERY:
+ m_aListRowSet.setCommandFromQuery( sListSource );
+ bExecute = true;
+ break;
+
+ default:
+ m_aListRowSet.setEscapeProcessing( ListSourceType_SQLPASSTHROUGH != m_eListSourceType );
+ m_aListRowSet.setCommand( sListSource );
+ bExecute = true;
+ break;
+ }
+
+ if (bExecute)
+ {
+ 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.
+ m_nNULLPos = nNULLPosBackup;
+ m_nBoundColumnType = nBoundColumnTypeBackup;
+ return;
+ }
+ xListCursor.reset( m_aListRowSet.execute() );
+ }
+ }
+ catch(const SQLException& eSQL)
+ {
+ onError(eSQL, ResourceManager::loadString(RID_BASELISTBOX_ERROR_FILLLIST));
+ return;
+ }
+ catch(const Exception&)
+ {
+ return;
+ }
+
+ // Fill display and value lists
+ ValueList aDisplayList, aValueList;
+ bool bUseNULL = hasField() && !isRequired();
+
+ // empty BoundColumn is treated as BoundColumn==0,
+ if(!aBoundColumn)
+ aBoundColumn = 0;
+
+ try
+ {
+ OSL_ENSURE( xListCursor.is() || ( ListSourceType_TABLEFIELDS == m_eListSourceType ),
+ "OListBoxModel::loadData: logic error!" );
+ if ( !xListCursor.is() && ( ListSourceType_TABLEFIELDS != m_eListSourceType ) )
+ return;
+
+ switch (m_eListSourceType)
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ case ListSourceType_SQL:
+ case ListSourceType_SQLPASSTHROUGH:
+ case ListSourceType_TABLE:
+ case ListSourceType_QUERY:
+ {
+ // Get field of the ResultSet's 1st column
+ Reference<XColumnsSupplier> xSupplyCols(xListCursor, UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "OListBoxModel::loadData : cursor supports the row set service but is no column supplier?!");
+ Reference<XIndexAccess> xColumns;
+ if (xSupplyCols.is())
+ {
+ xColumns.set(xSupplyCols->getColumns(), UNO_QUERY);
+ DBG_ASSERT(xColumns.is(), "OListBoxModel::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 );
+
+ // Get the field of BoundColumn of the ResultSet
+ m_nBoundColumnType = DataType::SQLNULL;
+ if ( *aBoundColumn >= 0 )
+ {
+ try
+ {
+ Reference< XPropertySet > xBoundField( xColumns->getByIndex( *aBoundColumn ), UNO_QUERY_THROW );
+ OSL_VERIFY( xBoundField->getPropertyValue("Type") >>= m_nBoundColumnType );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+ else if ( *aBoundColumn == -1)
+ m_nBoundColumnType = DataType::SMALLINT;
+
+ // If the LB is bound to a field and empty entries are valid, we remember the position
+ // for an empty entry
+ SAL_INFO( "forms.component", "OListBoxModel::loadData: string collection" );
+ OUString aStr;
+ sal_Int16 entryPos = 0;
+ ORowSetValue aBoundValue;
+ Reference< XRow > xCursorRow( xListCursor, UNO_QUERY_THROW );
+ while ( xListCursor->next() && ( entryPos++ < SHRT_MAX ) ) // SHRT_MAX is the maximum number of entries
+ {
+ aStr = aValueFormatter.getFormattedValue();
+ aDisplayList.emplace_back(aStr );
+
+ if(*aBoundColumn >= 0)
+ aBoundValue.fill( *aBoundColumn + 1, m_nBoundColumnType, xCursorRow );
+ else
+ // -1 because getRow() is 1-indexed, but ListBox positions are 0-indexed
+ aBoundValue = static_cast<sal_Int16>(xListCursor->getRow()-1);
+ aValueList.push_back( aBoundValue );
+
+ if ( m_nNULLPos == -1 && aBoundValue.isNull() )
+ m_nNULLPos = sal_Int16( aDisplayList.size() - 1 );
+ if ( bUseNULL && ( m_nNULLPos == -1 ) && aStr.isEmpty() )
+ // There is already a non-NULL entry with empty display string;
+ // adding another one for NULL would make things confusing,
+ // so back off.
+ bUseNULL = false;
+ }
+ }
+ break;
+#endif
+ case ListSourceType_TABLEFIELDS:
+ {
+ Reference<XNameAccess> xFieldNames = getTableFields(xConnection, sListSource);
+ if (xFieldNames.is())
+ {
+ const css::uno::Sequence<OUString> seqNames = xFieldNames->getElementNames();
+ ::std::copy(
+ seqNames.begin(),
+ seqNames.end(),
+ ::std::insert_iterator< ValueList >( aDisplayList, aDisplayList.end() )
+ );
+ if(*aBoundColumn == -1)
+ {
+ // the type of i matters! It will be the type of the ORowSetValue pushed to aValueList!
+ for(size_t i=0; i < aDisplayList.size(); ++i)
+ {
+ aValueList.emplace_back(sal_Int16(i));
+ }
+ }
+ else
+ {
+ aValueList = aDisplayList;
+ }
+ }
+ }
+ break;
+ default:
+ SAL_WARN( "forms.component", "OListBoxModel::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;
+ }
+
+
+ // Create Values sequence
+ // Add NULL entry
+ if (bUseNULL && m_nNULLPos == -1)
+ {
+ aValueList.insert( aValueList.begin(), ORowSetValue() );
+
+ aDisplayList.insert( aDisplayList.begin(), ORowSetValue( OUString() ) );
+ m_nNULLPos = 0;
+ }
+
+ setBoundValues(std::move(aValueList));
+
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( lcl_convertToStringSequence( aDisplayList ) ) );
+ setFastPropertyValue( PROPERTY_ID_TYPEDITEMLIST, Any( css::uno::Sequence<css::uno::Any>() ) );
+ }
+
+
+ void OListBoxModel::onConnectedDbColumn( const Reference< XInterface >& /*_rxForm*/ )
+ {
+ // list boxes which are bound to a db column don't have multi selection
+ // - this would be unable to reflect in the db column
+ if ( hasField() )
+ {
+ setFastPropertyValue( PROPERTY_ID_MULTISELECTION, css::uno::Any(false) );
+ }
+
+ if ( !hasExternalListSource() )
+ impl_refreshDbEntryList( false );
+ }
+
+
+ void OListBoxModel::onDisconnectedDbColumn()
+ {
+ clearBoundValues();
+ m_nNULLPos = -1;
+ m_nBoundColumnType = DataType::SQLNULL;
+
+ if ( m_eListSourceType != ListSourceType_VALUELIST )
+ {
+ if ( !hasExternalListSource() )
+ setFastPropertyValue( PROPERTY_ID_STRINGITEMLIST, Any( css::uno::Sequence<OUString>() ) );
+
+ m_aListRowSet.dispose();
+ }
+ }
+
+
+ void OListBoxModel::setBoundValues(ValueList && l)
+ {
+ m_aConvertedBoundValues.clear();
+ m_aBoundValues = std::move(l);
+ }
+
+
+ void OListBoxModel::clearBoundValues()
+ {
+ ValueList().swap(m_aConvertedBoundValues);
+ ValueList().swap(m_aBoundValues);
+ }
+
+
+ void OListBoxModel::convertBoundValues(const sal_Int32 nFieldType) const
+ {
+ assert(s_aEmptyValue.isNull());
+ m_nNULLPos = -1;
+ m_aConvertedBoundValues.resize(m_aBoundValues.size());
+ ValueList::iterator dst = m_aConvertedBoundValues.begin();
+ sal_Int16 nPos = 0;
+ for (auto const& src : m_aBoundValues)
+ {
+ if(m_nNULLPos == -1 &&
+ !isRequired() &&
+ (src == s_aEmptyStringValue || src == s_aEmptyValue || src.isNull()) )
+ {
+ m_nNULLPos = nPos;
+ dst->setNull();
+ }
+ else
+ {
+ *dst = src;
+ }
+ dst->setTypeKind(nFieldType);
+ ++dst;
+ ++nPos;
+ }
+ m_nConvertedBoundValuesType = nFieldType;
+ OSL_ENSURE(dst == m_aConvertedBoundValues.end(), "OListBoxModel::convertBoundValues expected to have overwritten all of m_aConvertedBoundValues, but did not.");
+ assert(dst == m_aConvertedBoundValues.end());
+ }
+
+ sal_Int32 OListBoxModel::getValueType() const
+ {
+ return (m_nBoundColumnType != css::sdbc::DataType::SQLNULL) ?
+ m_nBoundColumnType :
+ ( hasField() ? getFieldType() : DataType::VARCHAR);
+ }
+
+ ValueList OListBoxModel::impl_getValues() const
+ {
+ const sal_Int32 nFieldType = getValueType();
+
+ if ( !m_aConvertedBoundValues.empty() && m_nConvertedBoundValuesType == nFieldType )
+ return m_aConvertedBoundValues;
+
+ if ( !m_aBoundValues.empty() )
+ {
+ convertBoundValues(nFieldType);
+ return m_aConvertedBoundValues;
+ }
+
+ const std::vector< OUString >& aStringItems( getStringItemList() );
+ ValueList aValues( aStringItems.size() );
+ ValueList::iterator dst = aValues.begin();
+ for (auto const& src : aStringItems)
+ {
+ *dst = src;
+ dst->setTypeKind(nFieldType);
+ ++dst;
+ }
+ m_nConvertedBoundValuesType = nFieldType;
+ OSL_ENSURE(dst == aValues.end(), "OListBoxModel::impl_getValues expected to have set all of aValues, but did not.");
+ assert(dst == aValues.end());
+ return aValues;
+ }
+
+ ORowSetValue OListBoxModel::getFirstSelectedValue() const
+ {
+ DBG_ASSERT( m_xAggregateFastSet.is(), "OListBoxModel::getFirstSelectedValue: invalid aggregate!" );
+ if ( !m_xAggregateFastSet.is() )
+ return s_aEmptyValue;
+
+ Sequence< sal_Int16 > aSelectedIndices;
+ OSL_VERIFY( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) >>= aSelectedIndices );
+ if ( !aSelectedIndices.hasElements() )
+ // nothing selected at all
+ return s_aEmptyValue;
+
+ if ( ( m_nNULLPos != -1 ) && ( aSelectedIndices[0] == m_nNULLPos ) )
+ // the dedicated "NULL" entry is selected
+ return s_aEmptyValue;
+
+ ValueList aValues( impl_getValues() );
+
+ size_t selectedValue = aSelectedIndices[0];
+ if ( selectedValue >= aValues.size() )
+ {
+ SAL_WARN( "forms.component", "OListBoxModel::getFirstSelectedValue: inconsistent selection/valuelist!" );
+ return s_aEmptyValue;
+ }
+
+ return aValues[ selectedValue ];
+ }
+
+
+ bool OListBoxModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+ {
+ // current selection list
+ const ORowSetValue aCurrentValue( getFirstSelectedValue() );
+ if ( aCurrentValue != m_aSaveValue )
+ {
+ if ( aCurrentValue.isNull() )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ m_xColumnUpdate->updateObject( aCurrentValue.makeAny() );
+ }
+ catch ( const Exception& )
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aCurrentValue;
+ }
+ return true;
+ }
+
+
+ Sequence< sal_Int16 > OListBoxModel::translateDbValueToControlValue(const ORowSetValue &i_aValue) const
+ {
+ Sequence< sal_Int16 > aSelectionIndicies;
+
+ // reset selection for NULL values
+ if ( i_aValue.isNull() )
+ {
+ if ( m_nNULLPos != -1 )
+ {
+ aSelectionIndicies = { m_nNULLPos };
+ }
+ }
+ else
+ {
+ ValueList aValues( impl_getValues() );
+ assert( m_nConvertedBoundValuesType == getValueType());
+ ORowSetValue v(i_aValue);
+ v.setTypeKind( m_nConvertedBoundValuesType );
+ ValueList::const_iterator curValuePos = ::std::find( aValues.begin(), aValues.end(), v );
+ if ( curValuePos != aValues.end() )
+ {
+ aSelectionIndicies = { o3tl::narrowing<sal_Int16>(curValuePos - aValues.begin()) };
+ }
+ }
+
+ return aSelectionIndicies;
+ }
+
+ Sequence< sal_Int16 > OListBoxModel::translateBindingValuesToControlValue(const Sequence< const Any > &i_aValues) const
+ {
+ const ValueList aValues( impl_getValues() );
+ assert( m_nConvertedBoundValuesType == getValueType());
+ Sequence< sal_Int16 > aSelectionIndicies(i_aValues.getLength());
+
+ sal_Int32 nCount(0);
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ sal_Int16 *pIndex = aSelectionIndicies.getArray();
+ for ( auto const & value : i_aValues)
+ {
+ if ( value.hasValue() )
+ {
+ ORowSetValue v;
+ v.fill(value);
+ v.setTypeKind( m_nConvertedBoundValuesType );
+ ValueList::const_iterator curValuePos = ::std::find( aValues.begin(), aValues.end(), v );
+ if ( curValuePos != aValues.end() )
+ {
+ *pIndex = curValuePos - aValues.begin();
+ ++pIndex;
+ ++nCount;
+ }
+ }
+ else
+ {
+ if ( m_nNULLPos != -1 )
+ {
+ *pIndex = m_nNULLPos;
+ ++pIndex;
+ ++nCount;
+ }
+ }
+ }
+ assert(aSelectionIndicies.getArray() + nCount == pIndex);
+#endif
+ aSelectionIndicies.realloc(nCount);
+ return aSelectionIndicies;
+ }
+
+ Any OListBoxModel::translateDbColumnToControlValue()
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ Reference< XPropertySet > xBoundField( getField() );
+ if ( !xBoundField.is() )
+ {
+ SAL_WARN( "forms.component", "OListBoxModel::translateDbColumnToControlValue: no field? How could that happen?!" );
+ return Any();
+ }
+
+ ORowSetValue aCurrentValue;
+ aCurrentValue.fill( getValueType(), m_xColumn );
+
+ m_aSaveValue = aCurrentValue;
+
+ return Any( translateDbValueToControlValue(aCurrentValue) );
+#else
+ return Any();
+#endif
+ }
+
+ // XReset
+
+ Any OListBoxModel::getDefaultForReset() const
+ {
+ Any aValue;
+ if (m_aDefaultSelectSeq.hasElements())
+ aValue <<= m_aDefaultSelectSeq;
+ else if (m_nNULLPos != -1) // bound Listbox
+ {
+ Sequence<sal_Int16> aSeq { m_nNULLPos };
+ aValue <<= aSeq;
+ }
+ else
+ {
+ Sequence<sal_Int16> aSeq;
+ aValue <<= aSeq;
+ }
+
+ return aValue;
+ }
+
+
+ void OListBoxModel::resetNoBroadcast()
+ {
+ OBoundControlModel::resetNoBroadcast();
+ m_aSaveValue.setNull();
+ }
+
+
+ void SAL_CALL OListBoxModel::disposing( const EventObject& _rSource )
+ {
+ if ( !OEntryListHelper::handleDisposing( _rSource ) )
+ OBoundControlModel::disposing( _rSource );
+ }
+
+
+ namespace
+ {
+ // The type of how we should transfer our selection to external value bindings
+ enum ExchangeType
+ {
+ eIndexList, /// as list of indexes of selected entries
+ eIndex, /// as index of the selected entry
+ eEntryList, /// as list of string representations of selected *display* entries
+ eEntry, /// as string representation of the selected *display* entry
+ eValueList, /// as list of string representations of selected values
+ eValue /// as string representation of the selected value
+ };
+
+
+ ExchangeType lcl_getCurrentExchangeType( const Type& _rExchangeType )
+ {
+ switch ( _rExchangeType.getTypeClass() )
+ {
+ case TypeClass_ANY:
+ return eValue;
+ case TypeClass_STRING:
+ return eEntry;
+ case TypeClass_LONG:
+ return eIndex;
+ case TypeClass_SEQUENCE:
+ {
+ Type aElementType = ::comphelper::getSequenceElementType( _rExchangeType );
+ switch ( aElementType.getTypeClass() )
+ {
+ case TypeClass_ANY:
+ return eValueList;
+ case TypeClass_STRING:
+ return eEntryList;
+ case TypeClass_LONG:
+ return eIndexList;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ SAL_WARN( "forms.component", "lcl_getCurrentExchangeType: unsupported (unexpected) exchange type!" );
+ return eEntry;
+ }
+ }
+
+
+ Any OListBoxModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+ {
+ Sequence< sal_Int16 > aSelectIndexes;
+
+ switch ( lcl_getCurrentExchangeType( getExternalValueType() ) )
+ {
+ case eValueList:
+ {
+ Sequence< const Any > aExternalValues;
+ OSL_VERIFY( _rExternalValue >>= aExternalValues );
+ aSelectIndexes = translateBindingValuesToControlValue( aExternalValues );
+ }
+ break;
+
+ case eValue:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ {
+ ORowSetValue v;
+ v.fill(_rExternalValue);
+ aSelectIndexes = translateDbValueToControlValue(v);
+ }
+#endif
+ break;
+
+ case eIndexList:
+ {
+ // unfortunately, our select sequence is a sequence<short>, while the external binding
+ // supplies sequence<int> only -> transform this
+ Sequence< sal_Int32 > aSelectIndexesPure;
+ OSL_VERIFY( _rExternalValue >>= aSelectIndexesPure );
+ aSelectIndexes.realloc( aSelectIndexesPure.getLength() );
+ ::std::copy(
+ std::cbegin(aSelectIndexesPure),
+ std::cend(aSelectIndexesPure),
+ aSelectIndexes.getArray()
+ );
+ }
+ break;
+
+ case eIndex:
+ {
+ sal_Int32 nSelectIndex = -1;
+ OSL_VERIFY( _rExternalValue >>= nSelectIndex );
+ if ( ( nSelectIndex >= 0 ) && ( o3tl::make_unsigned(nSelectIndex) < getStringItemList().size() ) )
+ {
+ aSelectIndexes = { o3tl::narrowing<sal_Int16>(nSelectIndex) };
+ }
+ }
+ break;
+
+ case eEntryList:
+ {
+ // we can retrieve a string list from the binding for multiple selection
+ Sequence< OUString > aSelectEntries;
+ OSL_VERIFY( _rExternalValue >>= aSelectEntries );
+
+ ::std::set< sal_Int16 > aSelectionSet;
+
+ // find the selection entries in our item list
+ for ( OUString const & selectEntry : std::as_const(aSelectEntries) )
+ {
+ int idx = 0;
+ for(const OUString& s : getStringItemList())
+ {
+ if (s==selectEntry)
+ aSelectionSet.insert(idx);
+ ++idx;
+ }
+ }
+
+ // copy the indexes to the sequence
+ aSelectIndexes = comphelper::containerToSequence( aSelectionSet );
+ }
+ break;
+
+ case eEntry:
+ {
+ OUString sStringToSelect;
+ OSL_VERIFY( _rExternalValue >>= sStringToSelect );
+ ::std::set< sal_Int16 > aSelectionSet;
+ int idx = 0;
+ for(const OUString& s : getStringItemList())
+ {
+ if (s==sStringToSelect)
+ aSelectionSet.insert(idx);
+ ++idx;
+ }
+
+ aSelectIndexes = comphelper::containerToSequence( aSelectionSet );
+ }
+ break;
+ }
+
+ return Any( aSelectIndexes );
+ }
+
+
+ namespace
+ {
+
+ struct ExtractStringFromSequence_Safe
+ {
+ protected:
+ const std::vector< OUString >& m_rList;
+
+ public:
+ explicit ExtractStringFromSequence_Safe( const std::vector< OUString >& _rList ) : m_rList( _rList ) { }
+
+ OUString operator ()( sal_Int16 _nIndex )
+ {
+ OSL_ENSURE( _nIndex < static_cast<sal_Int32>(m_rList.size()), "ExtractStringFromSequence_Safe: inconsistence!" );
+ if ( _nIndex < static_cast<sal_Int32>(m_rList.size()) )
+ return m_rList[ _nIndex ];
+ return OUString();
+ }
+ };
+
+
+ Any lcl_getSingleSelectedEntryTyped( const Sequence< sal_Int16 >& _rSelectSequence, const Sequence<Any>& _rTypedList )
+ {
+ Any aReturn;
+
+ // by definition, multiple selected entries are transferred as NULL if the
+ // binding does not support lists
+ if ( _rSelectSequence.getLength() <= 1 )
+ {
+ if ( _rSelectSequence.getLength() == 1 )
+ {
+ sal_Int32 nIndex = _rSelectSequence[0];
+ if (0 <= nIndex && nIndex < _rTypedList.getLength())
+ aReturn = _rTypedList[nIndex];
+ }
+ }
+
+ return aReturn;
+ }
+
+
+ Any lcl_getSingleSelectedEntry( const Sequence< sal_Int16 >& _rSelectSequence, const std::vector< OUString >& _rStringList )
+ {
+ Any aReturn;
+
+ // by definition, multiple selected entries are transferred as NULL if the
+ // binding does not support string lists
+ if ( _rSelectSequence.getLength() <= 1 )
+ {
+ OUString sSelectedEntry;
+
+ if ( _rSelectSequence.getLength() == 1 )
+ sSelectedEntry = ExtractStringFromSequence_Safe( _rStringList )( _rSelectSequence[0] );
+
+ aReturn <<= sSelectedEntry;
+ }
+
+ return aReturn;
+ }
+
+
+ Any lcl_getMultiSelectedEntries( const Sequence< sal_Int16 >& _rSelectSequence, const std::vector< OUString >& _rStringList )
+ {
+ Sequence< OUString > aSelectedEntriesTexts( _rSelectSequence.getLength() );
+ ::std::transform(
+ _rSelectSequence.begin(),
+ _rSelectSequence.end(),
+ aSelectedEntriesTexts.getArray(),
+ ExtractStringFromSequence_Safe( _rStringList )
+ );
+ return Any( aSelectedEntriesTexts );
+ }
+
+
+ struct ExtractAnyFromValueList_Safe
+ {
+ protected:
+ const ValueList& m_rList;
+
+ public:
+ explicit ExtractAnyFromValueList_Safe( const ValueList& _rList ) : m_rList( _rList ) { }
+
+ Any operator ()( sal_Int16 _nIndex )
+ {
+ OSL_ENSURE( o3tl::make_unsigned(_nIndex) < m_rList.size(), "ExtractAnyFromValueList: inconsistence!" );
+ if ( o3tl::make_unsigned(_nIndex) < m_rList.size() )
+ return m_rList[ _nIndex ].makeAny();
+ return Any();
+ }
+ };
+
+
+ Any lcl_getSingleSelectedEntryAny( const Sequence< sal_Int16 >& _rSelectSequence, const ValueList& _rStringList )
+ {
+ Any aReturn;
+
+ // by definition, multiple selected entries are transferred as NULL if the
+ // binding does not support string lists
+ if ( _rSelectSequence.getLength() <= 1 )
+ {
+ if ( _rSelectSequence.getLength() == 1 )
+ aReturn = ExtractAnyFromValueList_Safe( _rStringList )( _rSelectSequence[0] );
+ }
+
+ return aReturn;
+ }
+
+
+ Sequence< Any > lcl_getMultiSelectedEntriesAny( const Sequence< sal_Int16 >& _rSelectSequence, const ValueList& _rStringList )
+ {
+ Sequence< Any > aSelectedEntriesValues( _rSelectSequence.getLength() );
+ ::std::transform(
+ _rSelectSequence.begin(),
+ _rSelectSequence.end(),
+ aSelectedEntriesValues.getArray(),
+ ExtractAnyFromValueList_Safe( _rStringList )
+ );
+ return aSelectedEntriesValues;
+ }
+ }
+
+
+ Any OListBoxModel::translateControlValueToExternalValue( ) const
+ {
+ OSL_PRECOND( hasExternalValueBinding(), "OListBoxModel::translateControlValueToExternalValue: no binding!" );
+
+ Sequence< sal_Int16 > aSelectSequence;
+ OSL_VERIFY( getControlValue() >>= aSelectSequence );
+
+ Any aReturn;
+ switch ( lcl_getCurrentExchangeType( getExternalValueType() ) )
+ {
+ case eValueList:
+ aReturn <<= getCurrentMultiValue();
+ break;
+
+ case eValue:
+ aReturn = getCurrentSingleValue();
+ break;
+
+ case eIndexList:
+ {
+ // unfortunately, the select sequence is a sequence<short>, but our binding
+ // expects int's
+ Sequence< sal_Int32 > aTransformed( aSelectSequence.getLength() );
+ ::std::copy(
+ std::cbegin(aSelectSequence),
+ std::cend(aSelectSequence),
+ aTransformed.getArray()
+ );
+ aReturn <<= aTransformed;
+ }
+ break;
+
+ case eIndex:
+ if ( aSelectSequence.getLength() <= 1 )
+ {
+ sal_Int32 nIndex = -1;
+
+ if ( aSelectSequence.getLength() == 1 )
+ nIndex = aSelectSequence[0];
+
+ aReturn <<= nIndex;
+ }
+ break;
+
+ case eEntryList:
+ aReturn = lcl_getMultiSelectedEntries( aSelectSequence, getStringItemList() );
+ break;
+
+ case eEntry:
+ {
+ const std::vector<OUString>& rStrings = getStringItemList();
+ const Sequence<Any>& rValues = getTypedItemList();
+ if (rStrings.size() == static_cast<size_t>(rValues.getLength()))
+ aReturn = lcl_getSingleSelectedEntryTyped( aSelectSequence, rValues );
+ else
+ aReturn = lcl_getSingleSelectedEntry( aSelectSequence, rStrings );
+ }
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ Any OListBoxModel::translateControlValueToValidatableValue( ) const
+ {
+ OSL_PRECOND( hasValidator(), "OListBoxModel::translateControlValueToValidatableValue: no validator, so why should I?" );
+ return getCurrentFormComponentValue();
+ }
+
+
+ Any OListBoxModel::getCurrentSingleValue() const
+ {
+ Any aCurrentValue;
+
+ try
+ {
+ Sequence< sal_Int16 > aSelectSequence;
+ OSL_VERIFY( getControlValue() >>= aSelectSequence );
+ aCurrentValue = lcl_getSingleSelectedEntryAny( aSelectSequence, impl_getValues() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return aCurrentValue;
+ }
+
+ Sequence< Any > OListBoxModel::getCurrentMultiValue() const
+ {
+ Sequence< Any > aCurrentValue;
+
+ try
+ {
+ Sequence< sal_Int16 > aSelectSequence;
+ OSL_VERIFY( getControlValue() >>= aSelectSequence );
+ aCurrentValue = lcl_getMultiSelectedEntriesAny( aSelectSequence, impl_getValues() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return aCurrentValue;
+ }
+
+ Any OListBoxModel::getCurrentFormComponentValue() const
+ {
+ {
+ Reference< css::form::validation::XValidator > vtor (const_cast<OListBoxModel*>(this)->getValidator());
+ Reference< XValueBinding > extBinding (const_cast<OListBoxModel*>(this)->getValueBinding());
+ if ( vtor.is() && vtor == extBinding )
+ return translateControlValueToExternalValue();
+ }
+
+ Any aCurrentValue;
+
+ try
+ {
+ bool bMultiSelection( false );
+ OSL_VERIFY( const_cast< OListBoxModel* >( this )->getPropertyValue( PROPERTY_MULTISELECTION ) >>= bMultiSelection );
+
+ if ( bMultiSelection )
+ aCurrentValue <<= getCurrentMultiValue();
+ else
+ aCurrentValue = getCurrentSingleValue();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+
+ return aCurrentValue;
+ }
+
+
+ Sequence< Type > OListBoxModel::getSupportedBindingTypes()
+ {
+ return
+ {
+ cppu::UnoType<Sequence< Any >>::get(),
+ cppu::UnoType<Any>::get(),
+ cppu::UnoType<Sequence< sal_Int32 >>::get(),
+ cppu::UnoType<sal_Int32>::get(),
+ cppu::UnoType<Sequence< OUString >>::get(),
+ cppu::UnoType<OUString>::get()
+ };
+ }
+
+
+ void OListBoxModel::stringItemListChanged( ControlModelLock& _rInstanceLock )
+ {
+ if ( !m_xAggregateSet.is() )
+ return;
+
+ suspendValueListening();
+ try
+ {
+ m_xAggregateSet->setPropertyValue( PROPERTY_STRINGITEMLIST, Any( comphelper::containerToSequence(getStringItemList()) ) );
+ m_xAggregateSet->setPropertyValue( PROPERTY_TYPEDITEMLIST, Any( getTypedItemList() ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ resumeValueListening();
+
+ // update the selection here
+ if ( hasExternalValueBinding( ) )
+ transferExternalValueToControl( _rInstanceLock );
+ else
+ {
+ if ( hasField() )
+ {
+ // TODO: update the selection in case we're bound to a database column
+ }
+ else
+ {
+ if ( m_aDefaultSelectSeq.hasElements() )
+ setControlValue( Any( m_aDefaultSelectSeq ), eOther );
+ }
+ }
+ }
+
+
+ void OListBoxModel::impl_refreshDbEntryList( bool _bForce )
+ {
+ DBG_ASSERT( !hasExternalListSource(), "OListBoxModel::impl_refreshDbEntryList: invalid call!" );
+
+ if ( !hasExternalListSource( )
+ && ( m_eListSourceType != ListSourceType_VALUELIST )
+ && ( m_xCursor.is() )
+ )
+ {
+ loadData( _bForce );
+ }
+ }
+
+
+ void OListBoxModel::refreshInternalEntryList()
+ {
+ impl_refreshDbEntryList( true );
+ if ( hasField() && m_xCursor.is() )
+ initFromField( m_xCursor );
+ }
+
+
+ // OListBoxControl
+
+ Sequence< Type> OListBoxControl::_getTypes()
+ {
+ return TypeBag(
+ OBoundControl::_getTypes(),
+ OListBoxControl_BASE::getTypes()
+ ).getTypes();
+ }
+
+
+ Any SAL_CALL OListBoxControl::queryAggregation(const Type& _rType)
+ {
+ Any aReturn = OListBoxControl_BASE::queryInterface( _rType );
+
+ if ( !aReturn.hasValue()
+ || _rType.equals( cppu::UnoType<XTypeProvider>::get() )
+ )
+ aReturn = OBoundControl::queryAggregation( _rType );
+
+ return aReturn;
+ }
+
+
+ OListBoxControl::OListBoxControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl( _rxFactory, VCL_CONTROL_LISTBOX, false )
+ ,m_aChangeListeners( m_aMutex )
+ ,m_aItemListeners( m_aMutex )
+ ,m_aChangeIdle("forms OListBoxControl m_aChangedIdle")
+ {
+
+ osl_atomic_increment(&m_refCount);
+ {
+ // Register as FocusListener
+ Reference<XWindow> xComp;
+ if (query_aggregation(m_xAggregate, xComp))
+ xComp->addFocusListener(this);
+
+ // Register as ItemListener
+ if ( query_aggregation( m_xAggregate, m_xAggregateListBox ) )
+ m_xAggregateListBox->addItemListener(this);
+ }
+ // Refcount at 2 for registered Listener
+ osl_atomic_decrement(&m_refCount);
+
+ doSetDelegator();
+
+ m_aChangeIdle.SetPriority(TaskPriority::LOWEST);
+ m_aChangeIdle.SetInvokeHandler(LINK(this,OListBoxControl,OnTimeout));
+ }
+
+
+ OListBoxControl::~OListBoxControl()
+ {
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+ doResetDelegator();
+ m_xAggregateListBox.clear();
+
+ }
+
+
+ css::uno::Sequence<OUString> SAL_CALL OListBoxControl::getSupportedServiceNames()
+ {
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_LISTBOX;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_LISTBOX;
+ return aSupported;
+ }
+
+
+ // XFocusListener
+
+ void SAL_CALL OListBoxControl::focusGained(const FocusEvent& /*_rEvent*/)
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ if ( m_aChangeListeners.getLength() ) // only if there are listeners
+ {
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (xSet.is())
+ {
+ // memorize the current selection for posting the change event
+ m_aCurrentSelection = xSet->getPropertyValue(PROPERTY_SELECT_SEQ);
+ }
+ }
+ }
+
+
+ void SAL_CALL OListBoxControl::focusLost(const FocusEvent& /*_rEvent*/)
+ {
+ m_aCurrentSelection.clear();
+ }
+
+ // XItemListener
+
+ void SAL_CALL OListBoxControl::itemStateChanged(const ItemEvent& _rEvent)
+ {
+ // forward this to our listeners
+ Reference< XChild > xChild( getModel(), UNO_QUERY );
+ if ( xChild.is() && xChild->getParent().is() )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_aItemListeners.getLength() )
+ {
+ if ( !m_pItemBroadcaster.is() )
+ {
+ m_pItemBroadcaster.set(
+ new ::comphelper::AsyncEventNotifier("ListBox"));
+ m_pItemBroadcaster->launch();
+ }
+ m_pItemBroadcaster->addEvent( new ItemEventDescription( _rEvent ), this );
+ }
+ }
+ else
+ m_aItemListeners.notifyEach( &XItemListener::itemStateChanged, _rEvent );
+
+ // and do the handling for the ChangeListeners
+ osl::MutexGuard aGuard(m_aMutex);
+ if ( m_aChangeIdle.IsActive() )
+ {
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ m_aCurrentSelection = xSet->getPropertyValue(PROPERTY_SELECT_SEQ);
+
+ m_aChangeIdle.Stop();
+ m_aChangeIdle.Start();
+ }
+ else
+ {
+ if ( m_aChangeListeners.getLength() && m_aCurrentSelection.hasValue() )
+ {
+ Reference<XPropertySet> xSet(getModel(), UNO_QUERY);
+ if (xSet.is())
+ {
+ // Has the selection been changed?
+ bool bModified(false);
+ Any aValue = xSet->getPropertyValue(PROPERTY_SELECT_SEQ);
+
+ Sequence<sal_Int16> const & rSelection = *o3tl::doAccess<Sequence<sal_Int16>>(aValue);
+ Sequence<sal_Int16> const & rOldSelection = *o3tl::doAccess<Sequence<sal_Int16>>(m_aCurrentSelection);
+ sal_Int32 nLen = rSelection.getLength();
+ if (nLen != rOldSelection.getLength())
+ bModified = true;
+ else
+ {
+ const sal_Int16* pVal = rSelection.getConstArray();
+ const sal_Int16* pCompVal = rOldSelection.getConstArray();
+
+ while (nLen-- && !bModified)
+ bModified = pVal[nLen] != pCompVal[nLen];
+ }
+
+ if (bModified)
+ {
+ m_aCurrentSelection = aValue;
+ m_aChangeIdle.Start();
+ }
+ }
+ }
+ else if (m_aCurrentSelection.hasValue())
+ m_aCurrentSelection.clear();
+ }
+ }
+
+ // XEventListener
+
+ void SAL_CALL OListBoxControl::disposing(const EventObject& _rSource)
+ {
+ OBoundControl::disposing(_rSource);
+ }
+
+ // XChangeBroadcaster
+
+ void SAL_CALL OListBoxControl::addChangeListener(const Reference<XChangeListener>& _rxListener)
+ {
+ m_aChangeListeners.addInterface( _rxListener );
+ }
+
+
+ void SAL_CALL OListBoxControl::removeChangeListener(const Reference<XChangeListener>& _rxListener)
+ {
+ m_aChangeListeners.removeInterface( _rxListener );
+ }
+
+ // OComponentHelper
+
+ void OListBoxControl::disposing()
+ {
+ if (m_aChangeIdle.IsActive())
+ m_aChangeIdle.Stop();
+
+ EventObject aEvent( *this );
+ m_aChangeListeners.disposeAndClear( aEvent );
+ m_aItemListeners.disposeAndClear( aEvent );
+
+ rtl::Reference< comphelper::AsyncEventNotifier > t;
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_pItemBroadcaster.is() )
+ {
+ t = m_pItemBroadcaster;
+ m_pItemBroadcaster->removeEventsForProcessor( this );
+ m_pItemBroadcaster->terminate();
+ m_pItemBroadcaster = nullptr;
+ }
+ }
+ if (t.is()) {
+ t->join();
+ }
+
+ OBoundControl::disposing();
+ }
+
+
+ void OListBoxControl::processEvent( const AnyEvent& _rEvent )
+ {
+ Reference< XListBox > xKeepAlive( this );
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( OComponentHelper::rBHelper.bDisposed )
+ return;
+ }
+ const ItemEventDescription& rItemEvent = static_cast< const ItemEventDescription& >( _rEvent );
+ m_aItemListeners.notifyEach( &XItemListener::itemStateChanged, rItemEvent.getEventObject() );
+ }
+
+
+ IMPL_LINK_NOARG(OListBoxControl, OnTimeout, Timer*, void)
+ {
+ m_aChangeListeners.notifyEach( &XChangeListener::changed, EventObject( *this ) );
+ }
+
+
+ void SAL_CALL OListBoxControl::addItemListener( const Reference< XItemListener >& l )
+ {
+ m_aItemListeners.addInterface( l );
+ }
+
+
+ void SAL_CALL OListBoxControl::removeItemListener( const Reference< XItemListener >& l )
+ {
+ m_aItemListeners.removeInterface( l );
+ }
+
+
+ void SAL_CALL OListBoxControl::addActionListener( const Reference< XActionListener >& l )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->addActionListener( l );
+ }
+
+
+ void SAL_CALL OListBoxControl::removeActionListener( const Reference< XActionListener >& l )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->removeActionListener( l );
+ }
+
+
+ void SAL_CALL OListBoxControl::addItem( const OUString& aItem, ::sal_Int16 nPos )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->addItem( aItem, nPos );
+ }
+
+
+ void SAL_CALL OListBoxControl::addItems( const Sequence< OUString >& aItems, ::sal_Int16 nPos )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->addItems( aItems, nPos );
+ }
+
+
+ void SAL_CALL OListBoxControl::removeItems( ::sal_Int16 nPos, ::sal_Int16 nCount )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->removeItems( nPos, nCount );
+ }
+
+
+ ::sal_Int16 SAL_CALL OListBoxControl::getItemCount( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getItemCount();
+ return 0;
+ }
+
+
+ OUString SAL_CALL OListBoxControl::getItem( ::sal_Int16 nPos )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getItem( nPos );
+ return OUString( );
+ }
+
+
+ Sequence< OUString > SAL_CALL OListBoxControl::getItems( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getItems();
+ return Sequence< OUString >( );
+ }
+
+
+ ::sal_Int16 SAL_CALL OListBoxControl::getSelectedItemPos( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getSelectedItemPos();
+ return 0;
+ }
+
+
+ Sequence< ::sal_Int16 > SAL_CALL OListBoxControl::getSelectedItemsPos( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getSelectedItemsPos();
+ return Sequence< ::sal_Int16 >( );
+ }
+
+
+ OUString SAL_CALL OListBoxControl::getSelectedItem( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getSelectedItem();
+ return OUString( );
+ }
+
+
+ Sequence< OUString > SAL_CALL OListBoxControl::getSelectedItems( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getSelectedItems();
+ return Sequence< OUString >( );
+ }
+
+
+ void SAL_CALL OListBoxControl::selectItemPos( ::sal_Int16 nPos, sal_Bool bSelect )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->selectItemPos( nPos, bSelect );
+ }
+
+
+ void SAL_CALL OListBoxControl::selectItemsPos( const Sequence< ::sal_Int16 >& aPositions, sal_Bool bSelect )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->selectItemsPos( aPositions, bSelect );
+ }
+
+
+ void SAL_CALL OListBoxControl::selectItem( const OUString& aItem, sal_Bool bSelect )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->selectItem( aItem, bSelect );
+ }
+
+
+ sal_Bool SAL_CALL OListBoxControl::isMutipleMode( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->isMutipleMode();
+ return false;
+ }
+
+
+ void SAL_CALL OListBoxControl::setMultipleMode( sal_Bool bMulti )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->setMultipleMode( bMulti );
+ }
+
+
+ ::sal_Int16 SAL_CALL OListBoxControl::getDropDownLineCount( )
+ {
+ if ( m_xAggregateListBox.is() )
+ return m_xAggregateListBox->getDropDownLineCount();
+ return 0;
+ }
+
+
+ void SAL_CALL OListBoxControl::setDropDownLineCount( ::sal_Int16 nLines )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->setDropDownLineCount( nLines );
+ }
+
+
+ void SAL_CALL OListBoxControl::makeVisible( ::sal_Int16 nEntry )
+ {
+ if ( m_xAggregateListBox.is() )
+ m_xAggregateListBox->makeVisible( nEntry );
+ }
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OListBoxModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OListBoxModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OListBoxControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OListBoxControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/ListBox.hxx b/forms/source/component/ListBox.hxx
new file mode 100644
index 0000000000..cad8cc708b
--- /dev/null
+++ b/forms/source/component/ListBox.hxx
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include "cachedrowset.hxx"
+#include "errorbroadcaster.hxx"
+#include "entrylisthelper.hxx"
+
+#include <com/sun/star/form/ListSourceType.hpp>
+#include <com/sun/star/awt/XItemListener.hpp>
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/form/XChangeBroadcaster.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/asyncnotification.hxx>
+#include <connectivity/FValue.hxx>
+#include <cppuhelper/implbase4.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+
+#include <vector>
+
+using namespace comphelper;
+
+/** ListBox is a bit confusing / different from other form components,
+ so here are a few notes:
+
+ The general design philosophy is that a ListBox is a mechanism
+ to translate back and forth between:
+ 1) *display* values (strings that the user sees and chooses)
+ 2) *binding* values, which is what the program (for a dialog),
+ the database, ... cares about.
+
+ A non-data aware ListBox exposes this mechanism through
+ com.sun.star.awt.XItemList (get|set)ItemData.
+
+ In a data-aware ListBox, this is naturally embodied by the
+ StringItemList on the one hand, and the ValueList on the other
+ hand (where, depending on ListSourceType, the ValueList is
+ possibly automatically filled from the BoundColumn of the
+ ListSource).
+
+ This source file implements data-aware ListBox, and the rest
+ of this comment applies to data-aware ListBox (only).
+
+ In all public APIs of the *model* (OListBoxModel),
+ the value of the control is the *binding* value.
+ That is what the bound database field gets,
+ that is what a validator validates,
+ that is what an external value binding
+ (com.sun.star.form.binding.XValueBinding)
+ exchanges with the control.
+
+ As an *implementation* choice, we keep the current value of the
+ ListBox as a sequence of *indices* in the value list, and do the
+ lookup on demand:
+
+ - ListBox's content property (or value property, sorry the
+ terminology is not always consistent) is SelectedItems which is
+ a sequence of *indices* in the value list.
+
+ - That is used to synchronise with our peer (UnoControlListBoxModel).
+
+ In particular, note that getCurrentValue() is a public API (and
+ deals with bound values), but getControlValue and
+ (do)setControlValue are *internal* implementation helpers that
+ deal with *indices*.
+
+ Note that the *view* (OListBoxControl) presents a different story
+ than the model. E.g. the "SelectedItems" property is *display* *values*.
+*/
+
+
+namespace frm
+{
+
+typedef ::std::vector< ::connectivity::ORowSetValue > ValueList;
+
+class OListBoxModel final :public OBoundControlModel
+ ,public OEntryListHelper
+ ,public OErrorBroadcaster
+{
+
+ CachedRowSet m_aListRowSet; // the row set to fill the list
+ ::connectivity::ORowSetValue m_aSaveValue;
+
+ // <properties>
+ css::form::ListSourceType m_eListSourceType; // type of list source
+ css::uno::Any m_aBoundColumn;
+ ValueList m_aListSourceValues;
+ ValueList m_aBoundValues; // do not write directly; use setBoundValues()
+ mutable ValueList m_aConvertedBoundValues;
+ mutable sal_Int32 m_nConvertedBoundValuesType;
+ css::uno::Sequence<sal_Int16> m_aDefaultSelectSeq; // DefaultSelected
+ // </properties>
+
+ mutable sal_Int16 m_nNULLPos; // position of the NULL value in our list
+ sal_Int32 m_nBoundColumnType;
+
+private:
+ ::connectivity::ORowSetValue getFirstSelectedValue() const;
+
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ OListBoxModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OListBoxModel(
+ const OListBoxModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OListBoxModel() override;
+
+// XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OListBoxModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+// UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(OListBoxModel, OBoundControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+// OComponentHelper
+ 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;
+
+private:
+ static const ::connectivity::ORowSetValue s_aEmptyValue;
+ static const ::connectivity::ORowSetValue s_aEmptyStringValue;
+
+ // XMultiPropertySet
+ virtual void SAL_CALL setPropertyValues(const css::uno::Sequence< OUString >& PropertyNames, const css::uno::Sequence< css::uno::Any >& Values) 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;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged( const css::beans::PropertyChangeEvent& _rEvt ) override;
+
+ // prevent method hiding
+ using OBoundControlModel::getFastPropertyValue;
+ using OBoundControlModel::setPropertyValues;
+
+ // OBoundControlModel overridables
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+ virtual css::uno::Any translateControlValueToValidatableValue( ) const 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;
+
+ virtual css::uno::Any getCurrentFormComponentValue() const override;
+
+ // OEntryListHelper overridables
+ virtual void stringItemListChanged( ControlModelLock& _rInstanceLock ) override;
+ virtual void refreshInternalEntryList() override;
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ void init();
+ css::uno::Any getCurrentSingleValue() const;
+ css::uno::Sequence<css::uno::Any> getCurrentMultiValue() const;
+ css::uno::Sequence< sal_Int16 > translateBindingValuesToControlValue(
+ const css::uno::Sequence< const css::uno::Any > &i_aValues)
+ const;
+ css::uno::Sequence< sal_Int16 > translateDbValueToControlValue(
+ const ::connectivity::ORowSetValue &aValue)
+ const;
+
+ void loadData( bool _bForce );
+
+ /** refreshes the list boxes list data
+ @precond we don't actually have an external list source
+ */
+ void impl_refreshDbEntryList( bool _bForce );
+
+ void setBoundValues(ValueList &&);
+ void clearBoundValues();
+
+ ValueList impl_getValues() const;
+
+ sal_Int32 getValueType() const;
+
+ void convertBoundValues(sal_Int32 nType) const;
+};
+
+
+//= OListBoxControl
+
+typedef ::cppu::ImplHelper4 < css::awt::XFocusListener
+ , css::awt::XItemListener
+ , css::awt::XListBox
+ , css::form::XChangeBroadcaster
+ > OListBoxControl_BASE;
+
+class OListBoxControl :public OBoundControl
+ ,public OListBoxControl_BASE
+ ,public IEventProcessor
+{
+private:
+ ::comphelper::OInterfaceContainerHelper3<css::form::XChangeListener> m_aChangeListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XItemListener> m_aItemListeners;
+
+ css::uno::Any m_aCurrentSelection;
+ Idle m_aChangeIdle;
+
+ css::uno::Reference< css::awt::XListBox >
+ m_xAggregateListBox;
+
+ ::rtl::Reference< ::comphelper::AsyncEventNotifier >
+ m_pItemBroadcaster;
+
+protected:
+ // UNO binding
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit OListBoxControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ virtual ~OListBoxControl() override;
+
+ // UNO binding
+ DECLARE_UNO3_AGG_DEFAULTS(OListBoxControl, OBoundControl)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+// XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OListBoxControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+// XChangeBroadcaster
+ virtual void SAL_CALL addChangeListener(const css::uno::Reference< css::form::XChangeListener>& _rxListener) override;
+ virtual void SAL_CALL removeChangeListener(const css::uno::Reference< css::form::XChangeListener>& _rxListener) override;
+
+// XFocusListener
+ virtual void SAL_CALL focusGained(const css::awt::FocusEvent& _rEvent) override;
+ virtual void SAL_CALL focusLost(const css::awt::FocusEvent& _rEvent) override;
+
+// XItemListener
+ virtual void SAL_CALL itemStateChanged(const css::awt::ItemEvent& _rEvent) override;
+
+// XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// XListBox
+ virtual void SAL_CALL addItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override;
+ virtual void SAL_CALL removeItemListener( const css::uno::Reference< css::awt::XItemListener >& l ) override;
+ virtual void SAL_CALL addActionListener( const css::uno::Reference< css::awt::XActionListener >& l ) override;
+ virtual void SAL_CALL removeActionListener( const css::uno::Reference< css::awt::XActionListener >& l ) override;
+ virtual void SAL_CALL addItem( const OUString& aItem, ::sal_Int16 nPos ) override;
+ virtual void SAL_CALL addItems( const css::uno::Sequence< OUString >& aItems, ::sal_Int16 nPos ) override;
+ virtual void SAL_CALL removeItems( ::sal_Int16 nPos, ::sal_Int16 nCount ) override;
+ virtual ::sal_Int16 SAL_CALL getItemCount( ) override;
+ virtual OUString SAL_CALL getItem( ::sal_Int16 nPos ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getItems( ) override;
+ virtual ::sal_Int16 SAL_CALL getSelectedItemPos( ) override;
+ virtual css::uno::Sequence< ::sal_Int16 > SAL_CALL getSelectedItemsPos( ) override;
+ virtual OUString SAL_CALL getSelectedItem( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSelectedItems( ) override;
+ virtual void SAL_CALL selectItemPos( ::sal_Int16 nPos, sal_Bool bSelect ) override;
+ virtual void SAL_CALL selectItemsPos( const css::uno::Sequence< ::sal_Int16 >& aPositions, sal_Bool bSelect ) override;
+ virtual void SAL_CALL selectItem( const OUString& aItem, sal_Bool bSelect ) override;
+ virtual sal_Bool SAL_CALL isMutipleMode( ) override;
+ virtual void SAL_CALL setMultipleMode( sal_Bool bMulti ) override;
+ virtual ::sal_Int16 SAL_CALL getDropDownLineCount( ) override;
+ virtual void SAL_CALL setDropDownLineCount( ::sal_Int16 nLines ) override;
+ virtual void SAL_CALL makeVisible( ::sal_Int16 nEntry ) override;
+
+protected:
+ // IEventProcessor
+ virtual void processEvent( const ::comphelper::AnyEvent& _rEvent ) override;
+
+private:
+ DECL_LINK( OnTimeout, Timer*, void );
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Numeric.cxx b/forms/source/component/Numeric.cxx
new file mode 100644
index 0000000000..7ef4086478
--- /dev/null
+++ b/forms/source/component/Numeric.cxx
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Numeric.hxx"
+#include <services.hxx>
+#include <property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+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;
+
+ONumericControl::ONumericControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_NUMERICFIELD)
+{
+}
+
+
+css::uno::Sequence<OUString> ONumericControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_NUMERICFIELD;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_NUMERICFIELD;
+ return aSupported;
+}
+
+// ONumericModel
+
+ONumericModel::ONumericModel(const Reference<XComponentContext>& _rxFactory)
+ :OEditBaseModel( _rxFactory, VCL_CONTROLMODEL_NUMERICFIELD, FRM_SUN_CONTROL_NUMERICFIELD, true, true )
+ // use the old control name for compytibility reasons
+{
+
+ m_nClassId = FormComponentType::NUMERICFIELD;
+ initValueProperty( PROPERTY_VALUE, PROPERTY_ID_VALUE );
+}
+
+
+ONumericModel::ONumericModel( const ONumericModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+{
+}
+
+
+ONumericModel::~ONumericModel()
+{
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL ONumericModel::createClone()
+{
+ rtl::Reference<ONumericModel> pClone = new ONumericModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> ONumericModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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_NUMERICFIELD;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_NUMERICFIELD;
+ *pStoreTo++ = BINDABLE_DATABASE_NUMERIC_FIELD;
+
+ *pStoreTo++ = FRM_COMPONENT_NUMERICFIELD;
+
+ return aSupported;
+}
+
+
+void ONumericModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 2);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_VALUE, PROPERTY_ID_DEFAULT_VALUE, cppu::UnoType<double>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL ONumericModel::getServiceName()
+{
+ return FRM_COMPONENT_NUMERICFIELD; // old (non-sun) name for compatibility !
+}
+
+
+bool ONumericModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+ if ( aControlValue != m_aSaveValue )
+ {
+ if ( !aControlValue.hasValue() )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ m_xColumnUpdate->updateDouble( getDouble( aControlValue ) );
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aControlValue;
+ }
+ return true;
+}
+
+
+Any ONumericModel::translateDbColumnToControlValue()
+{
+ m_aSaveValue <<= m_xColumn->getDouble();
+ if ( m_xColumn->wasNull() )
+ m_aSaveValue.clear();
+
+ return m_aSaveValue;
+}
+
+
+Any ONumericModel::getDefaultForReset() const
+{
+ Any aValue;
+ if (m_aDefault.getValueType().getTypeClass() == TypeClass_DOUBLE)
+ aValue = m_aDefault;
+
+ return aValue;
+}
+
+
+void ONumericModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aSaveValue.clear();
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ONumericModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ONumericModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ONumericControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ONumericControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Numeric.hxx b/forms/source/component/Numeric.hxx
new file mode 100644
index 0000000000..0b5cb703ec
--- /dev/null
+++ b/forms/source/component/Numeric.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+
+
+namespace frm
+{
+
+class ONumericModel
+ :public OEditBaseModel
+{
+private:
+ css::uno::Any m_aSaveValue;
+
+public:
+ ONumericModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ ONumericModel(
+ const ONumericModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~ONumericModel() override;
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ONumericModel"; }
+
+ virtual css::uno::Sequence<OUString> 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;
+
+protected:
+ // 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;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+class ONumericControl: public OBoundControl
+{
+public:
+ explicit ONumericControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ONumericControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Pattern.cxx b/forms/source/component/Pattern.cxx
new file mode 100644
index 0000000000..351db1b52a
--- /dev/null
+++ b/forms/source/component/Pattern.cxx
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Pattern.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::XComponentContext;
+using ::com::sun::star::beans::Property;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::sdbc::XRowSet;
+using ::com::sun::star::uno::UNO_QUERY;
+
+namespace FormComponentType = ::com::sun::star::form::FormComponentType;
+
+namespace frm
+{
+
+OPatternControl::OPatternControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_PATTERNFIELD)
+{
+}
+
+css::uno::Sequence<OUString> OPatternControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_PATTERNFIELD;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_PATTERNFIELD;
+ return aSupported;
+}
+
+
+// OPatternModel
+
+
+OPatternModel::OPatternModel(const Reference<XComponentContext>& _rxFactory)
+ :OEditBaseModel( _rxFactory, VCL_CONTROLMODEL_PATTERNFIELD, FRM_SUN_CONTROL_PATTERNFIELD, false, false )
+ // use the old control name for compytibility reasons
+{
+
+ m_nClassId = FormComponentType::PATTERNFIELD;
+ initValueProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT );
+}
+
+
+OPatternModel::OPatternModel( const OPatternModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OEditBaseModel( _pOriginal, _rxFactory )
+{
+}
+
+
+OPatternModel::~OPatternModel()
+{
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OPatternModel::createClone()
+{
+ rtl::Reference<OPatternModel> pClone = new OPatternModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OPatternModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 3);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-3] = FRM_SUN_COMPONENT_DATABASE_PATTERNFIELD;
+ pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_PATTERNFIELD;
+ pArray[aSupported.getLength()-1] = FRM_COMPONENT_PATTERNFIELD;
+ return aSupported;
+}
+
+
+void OPatternModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 4);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FILTERPROPOSAL, PROPERTY_ID_FILTERPROPOSAL, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL OPatternModel::getServiceName()
+{
+ return FRM_COMPONENT_PATTERNFIELD; // old (non-sun) name for compatibility !
+}
+
+
+bool OPatternModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aNewValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+
+ if ( aNewValue == m_aLastKnownValue )
+ return true;
+
+ OUString sNewValue;
+ aNewValue >>= sNewValue;
+
+ if ( !aNewValue.hasValue()
+ || ( sNewValue.isEmpty() // an empty string
+ && m_bEmptyIsNull // which should be interpreted as NULL
+ )
+ )
+ {
+ m_xColumnUpdate->updateNull();
+ }
+ else
+ {
+ OSL_ENSURE(m_pFormattedValue,
+ "OPatternModel::commitControlValueToDbColumn: no value helper!");
+ if (!m_pFormattedValue)
+ return false;
+
+ if ( !m_pFormattedValue->setFormattedValue( sNewValue ) )
+ return false;
+ }
+
+ m_aLastKnownValue = aNewValue;
+
+ return true;
+}
+
+
+void OPatternModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ OEditBaseModel::onConnectedDbColumn( _rxForm );
+
+ Reference< XPropertySet > xField( getField() );
+ if ( !xField.is() )
+ return;
+
+ m_pFormattedValue.reset( new ::dbtools::FormattedColumnValue( getContext(), Reference< XRowSet >( _rxForm, UNO_QUERY ), xField ) );
+}
+
+
+void OPatternModel::onDisconnectedDbColumn()
+{
+ OEditBaseModel::onDisconnectedDbColumn();
+ m_pFormattedValue.reset();
+}
+
+// XPropertyChangeListener
+
+Any OPatternModel::translateDbColumnToControlValue()
+{
+ OSL_PRECOND(m_pFormattedValue,
+ "OPatternModel::translateDbColumnToControlValue: no value helper!");
+
+ if (m_pFormattedValue)
+ {
+ OUString sValue( m_pFormattedValue->getFormattedValue() );
+ if ( sValue.isEmpty()
+ && m_pFormattedValue->getColumn().is()
+ && m_pFormattedValue->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)
+}
+
+// XReset
+
+Any OPatternModel::getDefaultForReset() const
+{
+ return Any( m_aDefaultText );
+}
+
+void OPatternModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aLastKnownValue.clear();
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OPatternModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OPatternModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OPatternControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OPatternControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Pattern.hxx b/forms/source/component/Pattern.hxx
new file mode 100644
index 0000000000..d0923c13cf
--- /dev/null
+++ b/forms/source/component/Pattern.hxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+
+#include <connectivity/formattedcolumnvalue.hxx>
+
+#include <memory>
+
+
+namespace frm
+{
+
+class OPatternModel
+ :public OEditBaseModel
+{
+private:
+ css::uno::Any m_aLastKnownValue;
+ ::std::unique_ptr< ::dbtools::FormattedColumnValue >
+ m_pFormattedValue;
+
+public:
+ OPatternModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OPatternModel(
+ const OPatternModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OPatternModel() override;
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OPatternModel"; }
+
+ virtual css::uno::Sequence<OUString> 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;
+
+protected:
+ // 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;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+class OPatternControl: public OBoundControl
+{
+public:
+ explicit OPatternControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OPatternControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/RadioButton.cxx b/forms/source/component/RadioButton.cxx
new file mode 100644
index 0000000000..a700f645b2
--- /dev/null
+++ b/forms/source/component/RadioButton.cxx
@@ -0,0 +1,402 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "RadioButton.hxx"
+#include "GroupManager.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <comphelper/basicio.hxx>
+#include <comphelper/property.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+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;
+
+
+css::uno::Sequence<OUString> SAL_CALL ORadioButtonControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString* pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_RADIOBUTTON;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_RADIOBUTTON;
+ return aSupported;
+}
+
+
+ORadioButtonControl::ORadioButtonControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_RADIOBUTTON)
+{
+}
+
+
+ORadioButtonModel::ORadioButtonModel(const Reference<XComponentContext>& _rxFactory)
+ :OReferenceValueComponent( _rxFactory, VCL_CONTROLMODEL_RADIOBUTTON, FRM_SUN_CONTROL_RADIOBUTTON )
+ // use the old control name for compytibility reasons
+{
+
+ m_nClassId = FormComponentType::RADIOBUTTON;
+ m_aLabelServiceName = FRM_SUN_COMPONENT_GROUPBOX;
+ initValueProperty( PROPERTY_STATE, PROPERTY_ID_STATE );
+ startAggregatePropertyListening( PROPERTY_GROUP_NAME );
+}
+
+
+ORadioButtonModel::ORadioButtonModel( const ORadioButtonModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OReferenceValueComponent( _pOriginal, _rxFactory )
+{
+}
+
+
+ORadioButtonModel::~ORadioButtonModel()
+{
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL ORadioButtonModel::createClone()
+{
+ rtl::Reference<ORadioButtonModel> pClone = new ORadioButtonModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL ORadioButtonModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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_RADIOBUTTON;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_RADIOBUTTON;
+ *pStoreTo++ = BINDABLE_DATABASE_RADIO_BUTTON;
+
+ *pStoreTo++ = FRM_COMPONENT_RADIOBUTTON;
+
+ return aSupported;
+}
+
+
+void ORadioButtonModel::SetSiblingPropsTo(const OUString& rPropName, const Any& rValue)
+{
+ // my name
+ OUString sMyGroup;
+ if (hasProperty(PROPERTY_GROUP_NAME, this))
+ getPropertyValue(PROPERTY_GROUP_NAME) >>= sMyGroup;
+ if (sMyGroup.isEmpty())
+ sMyGroup = m_aName;
+
+ // Iterate over my siblings
+ Reference<XIndexAccess> xIndexAccess(getParent(), UNO_QUERY);
+ if (!xIndexAccess.is())
+ return;
+
+ Reference<XPropertySet> xMyProps = this;
+ OUString sCurrentGroup;
+ sal_Int32 nNumSiblings = xIndexAccess->getCount();
+ for (sal_Int32 i=0; i<nNumSiblings; ++i)
+ {
+ Reference<XPropertySet> xSiblingProperties(xIndexAccess->getByIndex(i), UNO_QUERY);
+ if (!xSiblingProperties.is())
+ continue;
+ if (xMyProps == xSiblingProperties)
+ continue; // do not set myself
+
+ // Only if it's a RadioButton
+ if (!hasProperty(PROPERTY_CLASSID, xSiblingProperties))
+ continue;
+ sal_Int16 nType = 0;
+ xSiblingProperties->getPropertyValue(PROPERTY_CLASSID) >>= nType;
+ if (nType != FormComponentType::RADIOBUTTON)
+ continue;
+
+ // The group association is attached to the name
+ sCurrentGroup = OGroupManager::GetGroupName( xSiblingProperties );
+ if (sCurrentGroup == sMyGroup)
+ xSiblingProperties->setPropertyValue(rPropName, rValue);
+ }
+}
+
+
+void ORadioButtonModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+{
+ OReferenceValueComponent::setFastPropertyValue_NoBroadcast( nHandle, rValue );
+
+ // if the label control changed ...
+ if (nHandle == PROPERTY_ID_CONTROLLABEL)
+ { // ... forward this to our siblings
+ SetSiblingPropsTo(PROPERTY_CONTROLLABEL, rValue);
+ }
+
+ // If the ControlSource property has changed ...
+ if (nHandle == PROPERTY_ID_CONTROLSOURCE)
+ { // ... I have to pass the new ControlSource to my siblings, which are in the same RadioButton group as I am
+ SetSiblingPropsTo(PROPERTY_CONTROLSOURCE, rValue);
+ }
+
+ // The other way: if my name changes ...
+ if (nHandle == PROPERTY_ID_NAME)
+ {
+ setControlSource();
+ }
+
+ if (nHandle != PROPERTY_ID_DEFAULT_STATE)
+ return;
+
+ sal_Int16 nValue;
+ rValue >>= nValue;
+ if (1 == nValue)
+ { // Reset the 'default checked' for all Radios of the same group.
+ // Because (as the Highlander already knew): "There can be only one"
+ Any aZero;
+ nValue = 0;
+ aZero <<= nValue;
+ SetSiblingPropsTo(PROPERTY_DEFAULT_STATE, aZero);
+ }
+}
+
+void ORadioButtonModel::setControlSource()
+{
+ Reference<XIndexAccess> xIndexAccess(getParent(), UNO_QUERY);
+ if (!xIndexAccess.is())
+ return;
+
+ OUString sName, sGroupName;
+
+ if (hasProperty(PROPERTY_GROUP_NAME, this))
+ getPropertyValue(PROPERTY_GROUP_NAME) >>= sGroupName;
+ getPropertyValue(PROPERTY_NAME) >>= sName;
+
+ Reference<XPropertySet> xMyProps = this;
+ for (sal_Int32 i=0; i<xIndexAccess->getCount(); ++i)
+ {
+ Reference<XPropertySet> xSiblingProperties(xIndexAccess->getByIndex(i), UNO_QUERY);
+ if (!xSiblingProperties.is())
+ continue;
+
+ if (xMyProps == xSiblingProperties)
+ // Only if I didn't find myself
+ continue;
+
+ sal_Int16 nType = 0;
+ xSiblingProperties->getPropertyValue(PROPERTY_CLASSID) >>= nType;
+ if (nType != FormComponentType::RADIOBUTTON)
+ // Only RadioButtons
+ continue;
+
+ OUString sSiblingName, sSiblingGroupName;
+ if (hasProperty(PROPERTY_GROUP_NAME, xSiblingProperties))
+ xSiblingProperties->getPropertyValue(PROPERTY_GROUP_NAME) >>= sSiblingGroupName;
+ xSiblingProperties->getPropertyValue(PROPERTY_NAME) >>= sSiblingName;
+
+ if ((sGroupName.isEmpty() && sSiblingGroupName.isEmpty() && // (no group name
+ sName == sSiblingName) || // names match) or
+ (!sGroupName.isEmpty() && !sSiblingGroupName.isEmpty() && // (have group name
+ sGroupName == sSiblingGroupName)) // they match)
+ {
+ setPropertyValue(PROPERTY_CONTROLSOURCE, xSiblingProperties->getPropertyValue(PROPERTY_CONTROLSOURCE));
+ break;
+ }
+ }
+}
+
+
+void ORadioButtonModel::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<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+OUString SAL_CALL ORadioButtonModel::getServiceName()
+{
+ return FRM_COMPONENT_RADIOBUTTON; // old (non-sun) name for compatibility !
+}
+
+
+void SAL_CALL ORadioButtonModel::write(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ OReferenceValueComponent::write(_rxOutStream);
+
+ // Version
+ _rxOutStream->writeShort(0x0003);
+
+ // Properties
+ _rxOutStream << getReferenceValue();
+ _rxOutStream << static_cast<sal_Int16>(getDefaultChecked());
+ writeHelpTextCompatibly(_rxOutStream);
+
+ // from version 0x0003 : common properties
+ writeCommonProperties(_rxOutStream);
+}
+
+
+void SAL_CALL ORadioButtonModel::read(const Reference<XObjectInputStream>& _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;
+ _rxInStream >> nDefaultChecked;
+ break;
+ case 0x0002 :
+ _rxInStream >> sReferenceValue;
+ _rxInStream >> nDefaultChecked;
+ readHelpTextCompatibly(_rxInStream);
+ break;
+ case 0x0003 :
+ _rxInStream >> sReferenceValue;
+ _rxInStream >> nDefaultChecked;
+ readHelpTextCompatibly(_rxInStream);
+ readCommonProperties(_rxInStream);
+ break;
+ default :
+ OSL_FAIL("ORadioButtonModel::read : unknown version !");
+ defaultCommonProperties();
+ break;
+ }
+
+ setReferenceValue( sReferenceValue );
+ setDefaultChecked( static_cast<ToggleState>(nDefaultChecked) );
+
+ // Display default values after read
+ if ( !getControlSource().isEmpty() )
+ // (not if we don't have a control source - the "State" property acts like it is persistent, then
+ resetNoBroadcast();
+}
+
+
+void ORadioButtonModel::_propertyChanged(const PropertyChangeEvent& _rEvent)
+{
+ if ( _rEvent.PropertyName == PROPERTY_STATE )
+ {
+ if ( _rEvent.NewValue == sal_Int16(1) )
+ {
+ // If my status has changed to 'checked', I have to reset all my siblings, which are in the same group as I am
+ Any aZero;
+ aZero <<= sal_Int16(0);
+ SetSiblingPropsTo( PROPERTY_STATE, aZero );
+ }
+ }
+ else if ( _rEvent.PropertyName == PROPERTY_GROUP_NAME )
+ {
+ setControlSource();
+ // Can't call OReferenceValueComponent::_propertyChanged(), as it
+ // doesn't know what to do with the GroupName property.
+ return;
+ }
+
+ OReferenceValueComponent::_propertyChanged( _rEvent );
+}
+
+
+Any ORadioButtonModel::translateDbColumnToControlValue()
+{
+ return Any( static_cast<sal_Int16>( ( m_xColumn->getString() == getReferenceValue() ) ? TRISTATE_TRUE : TRISTATE_FALSE )
+ );
+}
+
+
+Any ORadioButtonModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ Any aControlValue = OReferenceValueComponent::translateExternalValueToControlValue( _rExternalValue );
+ sal_Int16 nState = TRISTATE_FALSE;
+ if ( ( aControlValue >>= nState ) && ( nState == TRISTATE_INDET ) )
+ // radio buttons do not have the DONTKNOW state
+ aControlValue <<= sal_Int16(TRISTATE_FALSE);
+ return aControlValue;
+}
+
+
+bool ORadioButtonModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Reference< XPropertySet > xField( getField() );
+ OSL_PRECOND( xField.is(), "ORadioButtonModel::commitControlValueToDbColumn: not bound!" );
+ if ( xField.is() )
+ {
+ try
+ {
+ sal_Int16 nValue = 0;
+ m_xAggregateSet->getPropertyValue( PROPERTY_STATE ) >>= nValue;
+ if ( nValue == 1 )
+ xField->setPropertyValue( PROPERTY_VALUE, Any( getReferenceValue() ) );
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("ORadioButtonModel::commitControlValueToDbColumn: could not commit !");
+ }
+ }
+ return true;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ORadioButtonModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ORadioButtonModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ORadioButtonControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ORadioButtonControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/RadioButton.hxx b/forms/source/component/RadioButton.hxx
new file mode 100644
index 0000000000..0e50acfc42
--- /dev/null
+++ b/forms/source/component/RadioButton.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "refvaluecomponent.hxx"
+
+
+namespace frm
+{
+
+class ORadioButtonModel final : public OReferenceValueComponent
+{
+public:
+ ORadioButtonModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ ORadioButtonModel(
+ const ORadioButtonModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~ORadioButtonModel() override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ORadioButtonModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // OPropertySetHelper
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) 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;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& evt) 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 translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+
+ void SetSiblingPropsTo(const OUString& rPropName, const css::uno::Any& rValue);
+
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ void setControlSource();
+};
+
+class ORadioButtonControl: public OBoundControl
+{
+public:
+ explicit ORadioButtonControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.ORadioButtonControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Time.cxx b/forms/source/component/Time.cxx
new file mode 100644
index 0000000000..fffccb83af
--- /dev/null
+++ b/forms/source/component/Time.cxx
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Time.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <connectivity/dbconversion.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+using namespace dbtools;
+
+namespace frm
+{
+
+using namespace ::com::sun::star;
+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::util;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+
+
+//=
+
+OTimeControl::OTimeControl(const Reference<XComponentContext>& _rxFactory)
+ :OBoundControl(_rxFactory, VCL_CONTROL_TIMEFIELD)
+{
+}
+
+
+Sequence<Type> OTimeControl::_getTypes()
+{
+ return OBoundControl::_getTypes();
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OTimeControl::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames();
+ aSupported.realloc(aSupported.getLength() + 2);
+
+ OUString*pArray = aSupported.getArray();
+ pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_TIMEFIELD;
+ pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_TIMEFIELD;
+ return aSupported;
+}
+
+
+//= OTimeModel
+
+// XServiceInfo
+
+css::uno::Sequence<OUString> SAL_CALL OTimeModel::getSupportedServiceNames()
+{
+ css::uno::Sequence<OUString> 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_TIMEFIELD;
+ *pStoreTo++ = FRM_SUN_COMPONENT_DATABASE_TIMEFIELD;
+ *pStoreTo++ = BINDABLE_DATABASE_TIME_FIELD;
+
+ *pStoreTo++ = FRM_COMPONENT_TIMEFIELD;
+
+ return aSupported;
+}
+
+
+Sequence<Type> OTimeModel::_getTypes()
+{
+ return OBoundControlModel::_getTypes();
+}
+
+
+OTimeModel::OTimeModel(const Reference<XComponentContext>& _rxFactory)
+ : OEditBaseModel(_rxFactory, VCL_CONTROLMODEL_TIMEFIELD,
+ FRM_SUN_CONTROL_TIMEFIELD, true, true)
+ // use the old control name for compatibility reasons
+ , OLimitedFormats(_rxFactory, FormComponentType::TIMEFIELD)
+ , m_bDateTimeField(false)
+{
+ m_nClassId = FormComponentType::TIMEFIELD;
+ initValueProperty( PROPERTY_TIME, PROPERTY_ID_TIME );
+
+ setAggregateSet(m_xAggregateFastSet, getOriginalHandle(PROPERTY_ID_TIMEFORMAT));
+}
+
+
+OTimeModel::OTimeModel(const OTimeModel* _pOriginal, const Reference<XComponentContext>& _rxFactory)
+ : OEditBaseModel(_pOriginal, _rxFactory)
+ , OLimitedFormats(_rxFactory, FormComponentType::TIMEFIELD)
+ , m_bDateTimeField(false)
+{
+ setAggregateSet( m_xAggregateFastSet, getOriginalHandle( PROPERTY_ID_TIMEFORMAT ) );
+}
+
+
+OTimeModel::~OTimeModel( )
+{
+ setAggregateSet(Reference< XFastPropertySet >(), -1);
+}
+
+// XCloneable
+
+css::uno::Reference< css::util::XCloneable > SAL_CALL OTimeModel::createClone()
+{
+ rtl::Reference<OTimeModel> pClone = new OTimeModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+OUString SAL_CALL OTimeModel::getServiceName()
+{
+ return FRM_COMPONENT_TIMEFIELD; // old (non-sun) name for compatibility !
+}
+
+// XPropertySet
+
+void OTimeModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+{
+ OEditBaseModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 4);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_TIME, PROPERTY_ID_DEFAULT_TIME, cppu::UnoType<util::Time>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_FORMATKEY, PROPERTY_ID_FORMATKEY, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::TRANSIENT);
+ *pProperties++ = css::beans::Property(PROPERTY_FORMATSSUPPLIER, PROPERTY_ID_FORMATSSUPPLIER, cppu::UnoType<XNumberFormatsSupplier>::get(),
+ css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+}
+
+
+void SAL_CALL OTimeModel::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle ) const
+{
+ switch (_nHandle)
+ {
+ case PROPERTY_ID_FORMATKEY:
+ getFormatKeyPropertyValue(_rValue);
+ break;
+ case PROPERTY_ID_FORMATSSUPPLIER:
+ _rValue <<= getFormatsSupplier();
+ break;
+ default:
+ OEditBaseModel::getFastPropertyValue(_rValue, _nHandle);
+ break;
+ }
+}
+
+
+sal_Bool SAL_CALL OTimeModel::convertFastPropertyValue(Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle, const Any& _rValue )
+{
+ if (PROPERTY_ID_FORMATKEY == _nHandle)
+ return convertFormatKeyPropertyValue(_rConvertedValue, _rOldValue, _rValue);
+ else
+ return OEditBaseModel::convertFastPropertyValue(_rConvertedValue, _rOldValue, _nHandle, _rValue );
+}
+
+
+void SAL_CALL OTimeModel::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ if (PROPERTY_ID_FORMATKEY == _nHandle)
+ setFormatKeyPropertyValue(_rValue);
+ else
+ OEditBaseModel::setFastPropertyValue_NoBroadcast(_nHandle, _rValue);
+}
+
+// XLoadListener
+
+void OTimeModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm )
+{
+ OBoundControlModel::onConnectedDbColumn( _rxForm );
+ Reference<XPropertySet> xField = getField();
+ if (!xField.is())
+ return;
+
+ m_bDateTimeField = false;
+ try
+ {
+ sal_Int32 nFieldType = 0;
+ xField->getPropertyValue(PROPERTY_FIELDTYPE) >>= nFieldType;
+ m_bDateTimeField = (nFieldType == DataType::TIMESTAMP);
+ }
+ catch(const Exception&)
+ {
+ }
+}
+
+
+bool OTimeModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+{
+ Any aControlValue( m_xAggregateFastSet->getFastPropertyValue( getValuePropertyAggHandle() ) );
+ if ( aControlValue == m_aSaveValue )
+ return true;
+
+ if ( !aControlValue.hasValue() )
+ m_xColumnUpdate->updateNull();
+ else
+ {
+ try
+ {
+ util::Time aTime;
+ if ( !( aControlValue >>= aTime ) )
+ {
+ sal_Int64 nAsInt(0);
+ aControlValue >>= nAsInt;
+ aTime = DBTypeConversion::toTime(nAsInt);
+ }
+
+ if (!m_bDateTimeField)
+ m_xColumnUpdate->updateTime(aTime);
+ else
+ {
+ util::DateTime aDateTime = m_xColumn->getTimestamp();
+ if (aDateTime.Year == 0 && aDateTime.Month == 0 && aDateTime.Day == 0)
+ aDateTime = ::com::sun::star::util::DateTime(0,0,0,0,30,12,1899, false);
+ aDateTime.NanoSeconds = aTime.NanoSeconds;
+ aDateTime.Seconds = aTime.Seconds;
+ aDateTime.Minutes = aTime.Minutes;
+ aDateTime.Hours = aTime.Hours;
+ m_xColumnUpdate->updateTimestamp(aDateTime);
+ }
+ }
+ catch(const Exception&)
+ {
+ return false;
+ }
+ }
+ m_aSaveValue = aControlValue;
+ return true;
+}
+
+
+Any OTimeModel::translateControlValueToExternalValue( ) const
+{
+ return getControlValue();
+}
+
+
+Any OTimeModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+{
+ return _rExternalValue;
+}
+
+
+Any OTimeModel::translateControlValueToValidatableValue( ) const
+{
+ return getControlValue();
+}
+
+
+Any OTimeModel::translateDbColumnToControlValue()
+{
+ util::Time aTime = m_xColumn->getTime();
+ if ( m_xColumn->wasNull() )
+ m_aSaveValue.clear();
+ else
+ m_aSaveValue <<= aTime;
+
+ return m_aSaveValue;
+}
+
+
+Any OTimeModel::getDefaultForReset() const
+{
+ return m_aDefault;
+}
+
+
+void OTimeModel::resetNoBroadcast()
+{
+ OEditBaseModel::resetNoBroadcast();
+ m_aSaveValue.clear();
+}
+
+
+Sequence< Type > OTimeModel::getSupportedBindingTypes()
+{
+ return Sequence< Type >( & cppu::UnoType<util::Time>::get(), 1 );
+}
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OTimeModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OTimeModel(component));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_OTimeControl_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OTimeControl(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/Time.hxx b/forms/source/component/Time.hxx
new file mode 100644
index 0000000000..3a6793fa5c
--- /dev/null
+++ b/forms/source/component/Time.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "EditBase.hxx"
+#include <limitedformats.hxx>
+
+
+namespace frm
+{
+
+class OTimeModel
+ :public OEditBaseModel
+ ,public OLimitedFormats
+{
+private:
+ css::uno::Any m_aSaveValue;
+ bool m_bDateTimeField;
+
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ OTimeModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OTimeModel(
+ const OTimeModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OTimeModel() override;
+
+ // css::io::XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // css::beans::XPropertySet
+ 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;
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OTimeModel"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OBoundControlModel::getFastPropertyValue;
+
+protected:
+ // OBoundControlModel overridables
+ virtual css::uno::Any
+ translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+
+ virtual css::uno::Any translateControlValueToValidatableValue( ) const override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+ virtual void resetNoBroadcast() override;
+
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm ) override;
+
+protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+};
+
+class OTimeControl: public OBoundControl
+{
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+public:
+ explicit OTimeControl(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ DECLARE_UNO3_AGG_DEFAULTS(OTimeControl, OBoundControl)
+
+ // css::lang::XServiceInfo
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.OTimeControl"; }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/cachedrowset.cxx b/forms/source/component/cachedrowset.cxx
new file mode 100644
index 0000000000..a2cfaf2529
--- /dev/null
+++ b/forms/source/component/cachedrowset.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "cachedrowset.hxx"
+#include <frm_strings.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/ResultSetType.hpp>
+
+#include <comphelper/diagnose_ex.hxx>
+
+
+namespace frm
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::sdbc::XConnection;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::sdbc::SQLException;
+ using ::com::sun::star::sdb::XQueriesSupplier;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::sdbc::XResultSet;
+ using ::com::sun::star::sdbc::XStatement;
+
+ namespace ResultSetType = ::com::sun::star::sdbc::ResultSetType;
+
+ struct CachedRowSet_Data
+ {
+ OUString sCommand;
+ bool bEscapeProcessing;
+ Reference< XConnection > xConnection;
+
+ bool bStatementDirty;
+
+ CachedRowSet_Data()
+ :bEscapeProcessing( false )
+ ,bStatementDirty( true )
+ {
+ }
+ };
+
+ CachedRowSet::CachedRowSet()
+ :m_pData( new CachedRowSet_Data )
+ {
+ }
+
+
+ CachedRowSet::~CachedRowSet()
+ {
+ dispose();
+ }
+
+
+ void CachedRowSet::setCommand( const OUString& _rCommand )
+ {
+ if ( m_pData->sCommand == _rCommand )
+ return;
+
+ m_pData->sCommand = _rCommand;
+ m_pData->bStatementDirty = true;
+ }
+
+
+ void CachedRowSet::setCommandFromQuery( const OUString& _rQueryName )
+ {
+ Reference< XQueriesSupplier > xSupplyQueries( m_pData->xConnection, UNO_QUERY_THROW );
+ Reference< XNameAccess > xQueries ( xSupplyQueries->getQueries(), UNO_SET_THROW );
+ Reference< XPropertySet > xQuery ( xQueries->getByName( _rQueryName ), UNO_QUERY_THROW );
+
+ bool bEscapeProcessing( false );
+ OSL_VERIFY( xQuery->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bEscapeProcessing );
+ setEscapeProcessing( bEscapeProcessing );
+
+ OUString sCommand;
+ OSL_VERIFY( xQuery->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand );
+ setCommand( sCommand );
+ }
+
+
+ void CachedRowSet::setEscapeProcessing ( const bool _bEscapeProcessing )
+ {
+ if ( m_pData->bEscapeProcessing == _bEscapeProcessing )
+ return;
+
+ m_pData->bEscapeProcessing = _bEscapeProcessing;
+ m_pData->bStatementDirty = true;
+ }
+
+
+ void CachedRowSet::setConnection( const Reference< XConnection >& _rxConnection )
+ {
+ if ( m_pData->xConnection == _rxConnection )
+ return;
+
+ m_pData->xConnection = _rxConnection;
+ m_pData->bStatementDirty = true;
+ }
+
+
+ Reference< XResultSet > CachedRowSet::execute()
+ {
+ Reference< XResultSet > xResult;
+ try
+ {
+ OSL_PRECOND( m_pData->xConnection.is(), "CachedRowSet::execute: how am I expected to do this without a connection?" );
+ if ( !m_pData->xConnection.is() )
+ return xResult;
+
+ Reference< XStatement > xStatement( m_pData->xConnection->createStatement(), UNO_SET_THROW );
+ Reference< XPropertySet > xStatementProps( xStatement, UNO_QUERY_THROW );
+ xStatementProps->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, Any( m_pData->bEscapeProcessing ) );
+ xStatementProps->setPropertyValue( PROPERTY_RESULTSET_TYPE, Any( ResultSetType::FORWARD_ONLY ) );
+
+ xResult.set( xStatement->executeQuery( m_pData->sCommand ), UNO_SET_THROW );
+ m_pData->bStatementDirty = false;
+ }
+ catch( const SQLException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ return xResult;
+ }
+
+
+ bool CachedRowSet::isDirty() const
+ {
+ return m_pData->bStatementDirty;
+ }
+
+
+ void CachedRowSet::dispose()
+ {
+ try
+ {
+ m_pData.reset( new CachedRowSet_Data );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/cachedrowset.hxx b/forms/source/component/cachedrowset.hxx
new file mode 100644
index 0000000000..f87ee2c07d
--- /dev/null
+++ b/forms/source/component/cachedrowset.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+
+#include <memory>
+
+
+namespace frm
+{
+
+ struct CachedRowSet_Data;
+
+ /// caches a result set obtained from a SQL statement
+ class CachedRowSet
+ {
+ public:
+ CachedRowSet();
+ ~CachedRowSet();
+
+ public:
+ /** executes the statement
+
+ @return
+ the result set produced by the statement. The caller takes ownership of the
+ given object.
+
+ @throws css::sdbc::SQLException
+ if such an exception is thrown when executing the statement
+ */
+ css::uno::Reference< css::sdbc::XResultSet >
+ execute();
+
+ /// determines whether the row set properties are dirty, i.e. have changed since the last call to execute
+ bool isDirty() const;
+
+ /// disposes the instance and frees all associated resources
+ void dispose();
+
+ /** sets the command of a query as command to be executed
+
+ A connection must have been set before.
+
+ @throws Exception
+ */
+ void setCommandFromQuery ( const OUString& _rQueryName );
+
+ void setCommand ( const OUString& _rCommand );
+ void setEscapeProcessing ( const bool _bEscapeProcessing );
+ void setConnection ( const css::uno::Reference< css::sdbc::XConnection >& _rxConnection );
+
+ private:
+ ::std::unique_ptr< CachedRowSet_Data > m_pData;
+ };
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/clickableimage.cxx b/forms/source/component/clickableimage.cxx
new file mode 100644
index 0000000000..e1f6f068fa
--- /dev/null
+++ b/forms/source/component/clickableimage.cxx
@@ -0,0 +1,838 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "clickableimage.hxx"
+#include <controlfeatureinterception.hxx>
+#include <urltransformer.hxx>
+#include <componenttools.hxx>
+#include <com/sun/star/form/XSubmit.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/awt/ActionEvent.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/GraphicObject.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <osl/mutex.hxx>
+#include <property.hxx>
+#include <services.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <svtools/imageresourceaccess.hxx>
+#define LOCAL_URL_PREFIX '#'
+
+
+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::frame;
+ using namespace ::com::sun::star::form::submission;
+ using namespace ::com::sun::star::graphic;
+ using ::com::sun::star::awt::MouseEvent;
+ using ::com::sun::star::task::XInteractionHandler;
+
+
+ // OClickableImageBaseControl
+
+
+ Sequence<Type> OClickableImageBaseControl::_getTypes()
+ {
+ static Sequence<Type> const aTypes =
+ concatSequences(OControl::_getTypes(), OClickableImageBaseControl_BASE::getTypes());
+ return aTypes;
+ }
+
+
+ OClickableImageBaseControl::OClickableImageBaseControl(const Reference<XComponentContext>& _rxFactory, const OUString& _aService)
+ :OControl(_rxFactory, _aService)
+ ,m_aSubmissionVetoListeners( m_aMutex )
+ ,m_aFeatureInterception( _rxFactory )
+ ,m_aApproveActionListeners( m_aMutex )
+ ,m_aActionListeners( m_aMutex )
+ {
+ }
+
+
+ OClickableImageBaseControl::~OClickableImageBaseControl()
+ {
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+ // UNO Binding
+
+ Any SAL_CALL OClickableImageBaseControl::queryAggregation(const Type& _rType)
+ {
+ Any aReturn = OControl::queryAggregation(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OClickableImageBaseControl_BASE::queryInterface(_rType);
+ return aReturn;
+ }
+
+ // XApproveActionBroadcaster
+
+ void OClickableImageBaseControl::addApproveActionListener(
+ const Reference<XApproveActionListener>& l)
+ {
+ m_aApproveActionListeners.addInterface(l);
+ }
+
+
+ void OClickableImageBaseControl::removeApproveActionListener(
+ const Reference<XApproveActionListener>& l)
+ {
+ m_aApproveActionListeners.removeInterface(l);
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::registerDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ m_aFeatureInterception.registerDispatchProviderInterceptor( _rxInterceptor );
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::releaseDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ m_aFeatureInterception.releaseDispatchProviderInterceptor( _rxInterceptor );
+ }
+
+ // OComponentHelper
+
+ void OClickableImageBaseControl::disposing()
+ {
+ EventObject aEvent( static_cast< XWeak* >( this ) );
+ m_aApproveActionListeners.disposeAndClear( aEvent );
+ m_aActionListeners.disposeAndClear( aEvent );
+ m_aSubmissionVetoListeners.disposeAndClear( aEvent );
+ m_aFeatureInterception.dispose();
+
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_pThread.clear();
+ }
+
+ OControl::disposing();
+ }
+
+
+ OImageProducerThread_Impl* OClickableImageBaseControl::getImageProducerThread()
+ {
+ if ( !m_pThread.is() )
+ {
+ m_pThread = new OImageProducerThread_Impl( this );
+ m_pThread->create();
+ }
+ return m_pThread.get();
+ }
+
+
+ bool OClickableImageBaseControl::approveAction( )
+ {
+ bool bCancelled = false;
+ EventObject aEvent( static_cast< XWeak* >( this ) );
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aApproveActionListeners );
+ while( !bCancelled && aIter.hasMoreElements() )
+ {
+ // Every approveAction method must be thread-safe!
+ if( !aIter.next()->approveAction( aEvent ) )
+ bCancelled = true;
+ }
+
+ return !bCancelled;
+ }
+
+
+ // This method is also called from a thread and thus must be thread-safe.
+ void OClickableImageBaseControl::actionPerformed_Impl(bool bNotifyListener, const MouseEvent& rEvt)
+ {
+ if( bNotifyListener )
+ {
+ if ( !approveAction() )
+ return;
+ }
+
+ // Whether the rest of the code is thread-safe, one can't tell. Therefore
+ // we do most of the work on a locked solar mutex.
+ Reference<XPropertySet> xSet;
+ Reference< XInterface > xModelsParent;
+ FormButtonType eButtonType = FormButtonType_PUSH;
+ {
+ SolarMutexGuard aGuard;
+
+ // Get parent
+ Reference<XFormComponent> xComp(getModel(), UNO_QUERY);
+ if (!xComp.is())
+ return;
+
+ xModelsParent = xComp->getParent();
+ if (!xModelsParent.is())
+ return;
+
+ // Which button type?
+ xSet.set(xComp, css::uno::UNO_QUERY);
+ if ( !xSet.is() )
+ return;
+ xSet->getPropertyValue(PROPERTY_BUTTONTYPE) >>= eButtonType;
+ }
+
+ switch (eButtonType)
+ {
+ case FormButtonType_RESET:
+ {
+ // Reset methods must be thread-safe!
+ Reference<XReset> xReset(xModelsParent, UNO_QUERY);
+ if (!xReset.is())
+ return;
+
+ xReset->reset();
+ }
+ break;
+
+ case FormButtonType_SUBMIT:
+ {
+ // if some outer component can provide an interaction handler, use it
+ Reference< XInteractionHandler > xHandler( m_aFeatureInterception.queryDispatch( "private:/InteractionHandler" ), UNO_QUERY );
+ try
+ {
+ implSubmit( rEvt, xHandler );
+ }
+ catch( const Exception& )
+ {
+ // ignore
+ }
+ }
+ break;
+
+ case FormButtonType_URL:
+ {
+ SolarMutexGuard aGuard;
+
+ Reference< XModel > xModel = getXModel(xModelsParent);
+ if (!xModel.is())
+ return;
+
+
+ // Execute URL now
+ Reference< XController > xController = xModel->getCurrentController();
+ if (!xController.is())
+ return;
+
+ Reference< XFrame > xFrame = xController->getFrame();
+ if( !xFrame.is() )
+ return;
+
+ URL aURL;
+ aURL.Complete =
+ getString(xSet->getPropertyValue(PROPERTY_TARGET_URL));
+
+ if (!aURL.Complete.isEmpty() && (LOCAL_URL_PREFIX == aURL.Complete[0]))
+ { // FIXME: The URL contains a local URL only. Since the URLTransformer does not handle this case correctly
+ // (it can't: it does not know the document URL), we have to take care for this ourself.
+ // The real solution would be to not allow such relative URLs (there is a rule that at runtime, all
+ // URLs have to be absolute), but for compatibility reasons this is no option.
+ // The more as the user does not want to see a local URL as "file://<path>/<document>#mark" if it
+ // could be "#mark" as well.
+ // If we someday say that this hack (yes, it's kind of a hack) is not sustainable anymore, the complete
+ // solution would be:
+ // * recognize URLs consisting of a mark only while _reading_ the document
+ // * for this, allow the INetURLObject (which at the moment is invoked when reading URLs) to
+ // transform such mark-only URLs into correct absolute URLs
+ // * at the UI, show only the mark
+ // * !!! recognize every SAVEAS on the document, so the absolute URL can be adjusted. This seems
+ // rather impossible !!!
+ aURL.Mark = aURL.Complete;
+ aURL.Complete = xModel->getURL();
+ aURL.Complete += aURL.Mark;
+ }
+
+ bool bDispatchUrlInternal = false;
+ xSet->getPropertyValue(PROPERTY_DISPATCHURLINTERNAL) >>= bDispatchUrlInternal;
+ if ( bDispatchUrlInternal )
+ {
+ m_aFeatureInterception.getTransformer().parseSmartWithProtocol( aURL, INET_FILE_SCHEME );
+
+ OUString aTargetFrame;
+ xSet->getPropertyValue(PROPERTY_TARGET_FRAME) >>= aTargetFrame;
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch( aURL, aTargetFrame,
+ FrameSearchFlag::SELF | FrameSearchFlag::PARENT |
+ FrameSearchFlag::SIBLINGS | FrameSearchFlag::CREATE );
+
+ Sequence<PropertyValue> aArgs { comphelper::makePropertyValue("Referer", xModel->getURL()) };
+
+ if (xDisp.is())
+ xDisp->dispatch( aURL, aArgs );
+ }
+ else
+ {
+ URL aHyperLink = m_aFeatureInterception.getTransformer().getStrictURL( ".uno:OpenHyperlink" );
+
+ Reference< XDispatch > xDisp = Reference< XDispatchProvider > (xFrame,UNO_QUERY_THROW)->queryDispatch(aHyperLink, OUString() , 0);
+
+ if ( xDisp.is() )
+ {
+ Sequence<PropertyValue> aProps{
+ comphelper::makePropertyValue("URL", aURL.Complete),
+ comphelper::makePropertyValue(
+ "FrameName", xSet->getPropertyValue(PROPERTY_TARGET_FRAME)),
+ comphelper::makePropertyValue("Referer", xModel->getURL())
+ };
+
+ xDisp->dispatch( aHyperLink, aProps );
+ }
+ }
+ } break;
+ default:
+ {
+ // notify the action listeners for a push button
+ ActionEvent aEvt(static_cast<XWeak*>(this), m_aActionCommand);
+ m_aActionListeners.notifyEach( &XActionListener::actionPerformed, aEvt );
+ }
+ }
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::addSubmissionVetoListener( const Reference< submission::XSubmissionVetoListener >& listener )
+ {
+ m_aSubmissionVetoListeners.addInterface( listener );
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::removeSubmissionVetoListener( const Reference< submission::XSubmissionVetoListener >& listener )
+ {
+ m_aSubmissionVetoListeners.removeInterface( listener );
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::submitWithInteraction( const Reference< XInteractionHandler >& _rxHandler )
+ {
+ implSubmit( MouseEvent(), _rxHandler );
+ }
+
+
+ void SAL_CALL OClickableImageBaseControl::submit( )
+ {
+ implSubmit( MouseEvent(), nullptr );
+ }
+
+
+ Sequence< OUString > SAL_CALL OClickableImageBaseControl::getSupportedServiceNames( )
+ {
+ Sequence< OUString > aSupported = OControl::getSupportedServiceNames();
+ aSupported.realloc( aSupported.getLength() + 1 );
+
+ OUString* pArray = aSupported.getArray();
+ pArray[ aSupported.getLength() - 1 ] = FRM_SUN_CONTROL_SUBMITBUTTON;
+
+ return aSupported;
+ }
+
+
+ void OClickableImageBaseControl::implSubmit( const MouseEvent& _rEvent, const Reference< XInteractionHandler >& _rxHandler )
+ {
+ try
+ {
+ // allow the veto listeners to join the game
+ m_aSubmissionVetoListeners.notifyEach( &XSubmissionVetoListener::submitting, EventObject( *this ) );
+
+ // see whether there's an "submit interceptor" set at our model
+ Reference< submission::XSubmissionSupplier > xSubmissionSupp( getModel(), UNO_QUERY );
+ Reference< XSubmission > xSubmission;
+ if ( xSubmissionSupp.is() )
+ xSubmission = xSubmissionSupp->getSubmission();
+
+ if ( xSubmission.is() )
+ {
+ if ( !_rxHandler.is() )
+ xSubmission->submit();
+ else
+ xSubmission->submitWithInteraction( _rxHandler );
+ }
+ else
+ {
+ // no "interceptor" -> ordinary (old-way) submission
+ Reference< XChild > xChild( getModel(), UNO_QUERY );
+ Reference< XSubmit > xParentSubmission;
+ if ( xChild.is() )
+ xParentSubmission.set(xChild->getParent(), css::uno::UNO_QUERY);
+ if ( xParentSubmission.is() )
+ xParentSubmission->submit( this, _rEvent );
+ }
+ }
+ catch( const VetoException& )
+ {
+ // allowed to leave
+ throw;
+ }
+ catch( const RuntimeException& )
+ {
+ // allowed to leave
+ throw;
+ }
+ catch( const WrappedTargetException& )
+ {
+ // allowed to leave
+ throw;
+ }
+ catch( const Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ TOOLS_WARN_EXCEPTION( "forms.component", "OClickableImageBaseControl::implSubmit: caught an unknown exception!" );
+ throw WrappedTargetException( OUString(), *this, anyEx );
+ }
+ }
+
+
+ // OClickableImageBaseModel
+
+
+ Sequence<Type> OClickableImageBaseModel::_getTypes()
+ {
+ return concatSequences(
+ OControlModel::_getTypes(),
+ OClickableImageBaseModel_Base::getTypes()
+ );
+ }
+
+
+ OClickableImageBaseModel::OClickableImageBaseModel( const Reference< XComponentContext >& _rxFactory, const OUString& _rUnoControlModelTypeName,
+ const OUString& rDefault )
+ :OControlModel( _rxFactory, _rUnoControlModelTypeName, rDefault )
+ ,OPropertyChangeListener(m_aMutex)
+ ,m_bDispatchUrlInternal(false)
+ ,m_bProdStarted(false)
+ {
+ implConstruct();
+ m_eButtonType = FormButtonType_PUSH;
+ }
+
+
+ OClickableImageBaseModel::OClickableImageBaseModel( const OClickableImageBaseModel* _pOriginal, const Reference<XComponentContext>& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+ ,OPropertyChangeListener( m_aMutex )
+ ,m_xGraphicObject( _pOriginal->m_xGraphicObject )
+ ,m_bDispatchUrlInternal(false)
+ ,m_bProdStarted( false )
+ {
+ implConstruct();
+
+ // copy properties
+ m_eButtonType = _pOriginal->m_eButtonType;
+ m_sTargetURL = _pOriginal->m_sTargetURL;
+ m_sTargetFrame = _pOriginal->m_sTargetFrame;
+ m_bDispatchUrlInternal = _pOriginal->m_bDispatchUrlInternal;
+ }
+
+
+ void OClickableImageBaseModel::implInitializeImageURL( )
+ {
+ osl_atomic_increment( &m_refCount );
+ {
+ // simulate a propertyChanged event for the ImageURL
+ Any aImageURL;
+ getFastPropertyValue( aImageURL, PROPERTY_ID_IMAGE_URL );
+ _propertyChanged( PropertyChangeEvent( *this, PROPERTY_IMAGE_URL, false, PROPERTY_ID_IMAGE_URL, Any( ), aImageURL ) );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ void OClickableImageBaseModel::implConstruct()
+ {
+ m_xProducer = new ImageProducer;
+ m_xProducer->SetDoneHdl( LINK( this, OClickableImageBaseModel, OnImageImportDone ) );
+ osl_atomic_increment( &m_refCount );
+ {
+ if ( m_xAggregateSet.is() )
+ {
+ rtl::Reference<OPropertyChangeMultiplexer> pMultiplexer = new OPropertyChangeMultiplexer( this, m_xAggregateSet );
+ pMultiplexer->addProperty( PROPERTY_IMAGE_URL );
+ }
+ }
+ osl_atomic_decrement(&m_refCount);
+ }
+
+
+ OClickableImageBaseModel::~OClickableImageBaseModel()
+ {
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+ DBG_ASSERT(m_pMedium == nullptr, "OClickableImageBaseModel::~OClickableImageBaseModel : leaving a memory leak ...");
+ // This should be cleaned up at least in the dispose
+
+ }
+
+ // XImageProducer
+
+ void SAL_CALL OClickableImageBaseModel::addConsumer( const Reference< XImageConsumer >& _rxConsumer )
+ {
+ ImageModelMethodGuard aGuard( *this );
+ GetImageProducer()->addConsumer( _rxConsumer );
+ }
+
+
+ void SAL_CALL OClickableImageBaseModel::removeConsumer( const Reference< XImageConsumer >& _rxConsumer )
+ {
+ ImageModelMethodGuard aGuard( *this );
+ GetImageProducer()->removeConsumer( _rxConsumer );
+ }
+
+
+ void SAL_CALL OClickableImageBaseModel::startProduction( )
+ {
+ ImageModelMethodGuard aGuard( *this );
+ GetImageProducer()->startProduction();
+ }
+
+
+ Reference< submission::XSubmission > SAL_CALL OClickableImageBaseModel::getSubmission()
+ {
+ return m_xSubmissionDelegate;
+ }
+
+
+ void SAL_CALL OClickableImageBaseModel::setSubmission( const Reference< submission::XSubmission >& _submission )
+ {
+ m_xSubmissionDelegate = _submission;
+ }
+
+
+ Sequence< OUString > SAL_CALL OClickableImageBaseModel::getSupportedServiceNames( )
+ {
+ Sequence< OUString > aSupported = OControlModel::getSupportedServiceNames();
+ aSupported.realloc( aSupported.getLength() + 1 );
+
+ OUString* pArray = aSupported.getArray();
+ pArray[ aSupported.getLength() - 1 ] = FRM_SUN_COMPONENT_SUBMITBUTTON;
+
+ return aSupported;
+ }
+
+ // OComponentHelper
+
+ void OClickableImageBaseModel::disposing()
+ {
+ OControlModel::disposing();
+ m_pMedium.reset();
+ m_xProducer.clear();
+ }
+
+
+ Any SAL_CALL OClickableImageBaseModel::queryAggregation(const Type& _rType)
+ {
+ // order matters:
+ // we definitely want to "override" the XImageProducer interface of our aggregate,
+ // thus check OClickableImageBaseModel_Base (which provides this) first
+ Any aReturn = OClickableImageBaseModel_Base::queryInterface( _rType );
+
+ // BUT: _don't_ let it feel responsible for the XTypeProvider interface
+ // (as this is implemented by our base class in the proper way)
+ if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() )
+ || !aReturn.hasValue()
+ )
+ aReturn = OControlModel::queryAggregation( _rType );
+
+ return aReturn;
+ }
+
+
+ void OClickableImageBaseModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_BUTTONTYPE : rValue <<= m_eButtonType; break;
+ case PROPERTY_ID_TARGET_URL : rValue <<= m_sTargetURL; break;
+ case PROPERTY_ID_TARGET_FRAME : rValue <<= m_sTargetFrame; break;
+ case PROPERTY_ID_DISPATCHURLINTERNAL : rValue <<= m_bDispatchUrlInternal; break;
+ default:
+ OControlModel::getFastPropertyValue(rValue, nHandle);
+ }
+ }
+
+
+ void OClickableImageBaseModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue)
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_BUTTONTYPE :
+ DBG_ASSERT(rValue.has<FormButtonType>(), "OClickableImageBaseModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_eButtonType;
+ break;
+
+ case PROPERTY_ID_TARGET_URL :
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "OClickableImageBaseModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_sTargetURL;
+ break;
+
+ case PROPERTY_ID_TARGET_FRAME :
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_STRING, "OClickableImageBaseModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_sTargetFrame;
+ break;
+
+ case PROPERTY_ID_DISPATCHURLINTERNAL:
+ DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, "OClickableImageBaseModel::setFastPropertyValue_NoBroadcast : invalid type !" );
+ rValue >>= m_bDispatchUrlInternal;
+ break;
+
+ default:
+ OControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue);
+ }
+ }
+
+
+ sal_Bool OClickableImageBaseModel::convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue)
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_BUTTONTYPE :
+ return tryPropertyValueEnum( rConvertedValue, rOldValue, rValue, m_eButtonType );
+
+ case PROPERTY_ID_TARGET_URL :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sTargetURL);
+
+ case PROPERTY_ID_TARGET_FRAME :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_sTargetFrame);
+
+ case PROPERTY_ID_DISPATCHURLINTERNAL :
+ return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bDispatchUrlInternal);
+
+ default:
+ return OControlModel::convertFastPropertyValue(rConvertedValue, rOldValue, nHandle, rValue);
+ }
+ }
+
+
+ void OClickableImageBaseModel::StartProduction()
+ {
+ ImageProducer *pImgProd = GetImageProducer();
+ // grab the ImageURL
+ OUString sURL;
+ getPropertyValue("ImageURL") >>= sURL;
+ if (!m_pMedium)
+ {
+ if ( ::svt::GraphicAccess::isSupportedURL( sURL ) )
+ pImgProd->SetImage( sURL );
+ else
+ // caution: the medium may be NULL if somebody gave us an invalid URL to work with
+ pImgProd->SetImage(OUString());
+ return;
+ }
+ if (m_pMedium->GetErrorCode()==ERRCODE_NONE)
+ {
+ SvStream* pStream = m_pMedium->GetInStream();
+
+ pImgProd->SetImage(*pStream);
+ pImgProd->startProduction();
+ m_bProdStarted = true;
+ }
+ else
+ {
+ pImgProd->SetImage(OUString());
+ m_pMedium.reset();
+ }
+ }
+
+ SfxObjectShell* OClickableImageBaseModel::GetObjectShell()
+ {
+ // Find the XModel to get to the Object shell or at least the
+ // Referer.
+ // There's only a Model if we load HTML documents and the URL is
+ // changed in a document that is already loaded. There's no way
+ // we can get to the Model during loading.
+ Reference< XModel > xModel;
+ css::uno::Reference<css::uno::XInterface> xIfc( *this );
+ while( !xModel.is() && xIfc.is() )
+ {
+ Reference<XChild> xChild( xIfc, UNO_QUERY );
+ xIfc = xChild->getParent();
+ xModel.set(xIfc, css::uno::UNO_QUERY);
+ }
+
+ // Search for the Object shell by iterating over all Object shells
+ // and comparing their XModel to ours.
+ // As an optimization, we try the current Object shell first.
+ SfxObjectShell *pObjSh = nullptr;
+
+ if( xModel.is() )
+ {
+ SfxObjectShell *pTestObjSh = SfxObjectShell::Current();
+ if( pTestObjSh )
+ {
+ Reference< XModel > xTestModel = pTestObjSh->GetModel();
+ if( xTestModel == xModel )
+ pObjSh = pTestObjSh;
+ }
+ if( !pObjSh )
+ {
+ pTestObjSh = SfxObjectShell::GetFirst();
+ while( !pObjSh && pTestObjSh )
+ {
+ Reference< XModel > xTestModel = pTestObjSh->GetModel();
+ if( xTestModel == xModel )
+ pObjSh = pTestObjSh;
+ else
+ pTestObjSh = SfxObjectShell::GetNext( *pTestObjSh );
+ }
+ }
+ }
+
+ return pObjSh;
+ }
+
+ void OClickableImageBaseModel::SetURL( const OUString& rURL )
+ {
+ if (m_pMedium || rURL.isEmpty())
+ {
+ // Free the stream at the Producer, before the medium is deleted
+ GetImageProducer()->SetImage(OUString());
+ m_pMedium.reset();
+ }
+
+ // the SfxMedium is not allowed to be created with an invalid URL, so we have to check this first
+ INetURLObject aUrl(rURL);
+ if (INetProtocol::NotValid == aUrl.GetProtocol())
+ // we treat an invalid URL like we would treat no URL
+ return;
+
+ if (!rURL.isEmpty() && !::svt::GraphicAccess::isSupportedURL( rURL ) )
+ {
+ m_pMedium.reset(new SfxMedium(rURL, StreamMode::STD_READ));
+
+ SfxObjectShell *pObjSh = GetObjectShell();
+
+ if( pObjSh )
+ {
+ // Transfer target frame, so that javascript: URLs
+ // can also be "loaded"
+ const SfxMedium *pShMedium = pObjSh->GetMedium();
+ if( pShMedium )
+ m_pMedium->SetLoadTargetFrame(pShMedium->GetLoadTargetFrame());
+ }
+
+ m_bProdStarted = false;
+
+ // Kick off download (caution: can be synchronous).
+ m_pMedium->Download(LINK(this, OClickableImageBaseModel, DownloadDoneLink));
+ }
+ else
+ {
+ if ( ::svt::GraphicAccess::isSupportedURL( rURL ) )
+ GetImageProducer()->SetImage( rURL );
+ GetImageProducer()->startProduction();
+ }
+ }
+
+
+ void OClickableImageBaseModel::DataAvailable()
+ {
+ if (!m_bProdStarted)
+ StartProduction();
+
+ GetImageProducer()->NewDataAvailable();
+ }
+
+
+ IMPL_LINK_NOARG( OClickableImageBaseModel, DownloadDoneLink, void*, void )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ DataAvailable();
+ }
+
+
+ void OClickableImageBaseModel::_propertyChanged( const PropertyChangeEvent& rEvt )
+ {
+ // If a URL was set, it needs to be passed onto the ImageProducer.
+ ::osl::MutexGuard aGuard(m_aMutex);
+ SetURL( getString(rEvt.NewValue) );
+ }
+
+
+ Any OClickableImageBaseModel::getPropertyDefaultByHandle( sal_Int32 nHandle ) const
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_BUTTONTYPE : return Any( FormButtonType_PUSH );
+ case PROPERTY_ID_TARGET_URL :
+ case PROPERTY_ID_TARGET_FRAME : return Any( OUString() );
+ case PROPERTY_ID_DISPATCHURLINTERNAL : return Any( false );
+ default:
+ return OControlModel::getPropertyDefaultByHandle(nHandle);
+ }
+ }
+
+ IMPL_LINK( OClickableImageBaseModel, OnImageImportDone, Graphic*, i_pGraphic, void )
+ {
+ const Reference< XGraphic > xGraphic( i_pGraphic != nullptr ? Graphic(i_pGraphic->GetBitmapEx()).GetXGraphic() : nullptr );
+ if ( !xGraphic.is() )
+ {
+ m_xGraphicObject.clear();
+ }
+ else
+ {
+ m_xGraphicObject = css::graphic::GraphicObject::create( m_xContext );
+ m_xGraphicObject->setGraphic( xGraphic );
+ }
+ }
+
+
+ // OImageProducerThread_Impl
+
+ void OImageProducerThread_Impl::processEvent( ::cppu::OComponentHelper *pCompImpl,
+ const EventObject* pEvt,
+ const Reference<XControl>&,
+ bool )
+ {
+ static_cast<OClickableImageBaseControl *>(pCompImpl)->actionPerformed_Impl( true, *static_cast<const MouseEvent *>(pEvt) );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/clickableimage.hxx b/forms/source/component/clickableimage.hxx
new file mode 100644
index 0000000000..2ec7b92122
--- /dev/null
+++ b/forms/source/component/clickableimage.hxx
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <FormComponent.hxx>
+#include "EventThread.hxx"
+#include "imgprod.hxx"
+#include <controlfeatureinterception.hxx>
+#include <tools/link.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propmultiplex.hxx>
+#include <com/sun/star/awt/XActionListener.hpp>
+#include <com/sun/star/form/XImageProducerSupplier.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/form/XApproveActionListener.hpp>
+#include <com/sun/star/form/XApproveActionBroadcaster.hpp>
+#include <com/sun/star/form/submission/XSubmissionSupplier.hpp>
+#include <com/sun/star/form/submission/XSubmission.hpp>
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+#include <com/sun/star/graphic/XGraphicObject.hpp>
+#include <cppuhelper/implbase3.hxx>
+
+class SfxMedium;
+class SfxObjectShell;
+
+namespace frm
+{
+
+
+ class OImageProducerThread_Impl;
+
+ // OClickableImageBaseModel
+
+ typedef ::cppu::ImplHelper3 < css::form::XImageProducerSupplier
+ , css::awt::XImageProducer
+ , css::form::submission::XSubmissionSupplier
+ > OClickableImageBaseModel_Base;
+
+ class OClickableImageBaseModel :public OClickableImageBaseModel_Base
+ ,public OControlModel
+ ,public OPropertyChangeListener
+ {
+ protected:
+ css::form::FormButtonType m_eButtonType; // Type of the button (push, submit, reset)
+ OUString m_sTargetURL; // URL for the URL button
+ OUString m_sTargetFrame; // TargetFrame to open
+
+ // ImageProducer stuff
+ // Store the image in a graphic object to make it accessible via graphic cache using graphic ID.
+ css::uno::Reference< css::graphic::XGraphicObject > m_xGraphicObject;
+ std::unique_ptr<SfxMedium> m_pMedium; // Download medium
+ rtl::Reference<ImageProducer> m_xProducer;
+ bool m_bDispatchUrlInternal; // property: is not allowed to set : 1
+ bool m_bProdStarted : 1;
+
+ // XSubmission stuff
+ css::uno::Reference< css::form::submission::XSubmission >
+ m_xSubmissionDelegate;
+
+ DECL_LINK( DownloadDoneLink, void*, void );
+
+ ImageProducer* GetImageProducer() { return m_xProducer.get(); }
+
+ void StartProduction();
+ void SetURL(const OUString& rURL);
+ void DataAvailable();
+
+ css::uno::Sequence< css::uno::Type> _getTypes() override;
+ bool isDispatchUrlInternal() const { return m_bDispatchUrlInternal; }
+ void setDispatchUrlInternal(bool _bDispatch) { m_bDispatchUrlInternal = _bDispatch; }
+
+ public:
+ OClickableImageBaseModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ const OUString& _rUnoControlModelTypeName,
+ const OUString& _rDefault
+ );
+
+ OClickableImageBaseModel (
+ const OClickableImageBaseModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+
+ virtual ~OClickableImageBaseModel() override;
+
+ // UNO Binding
+ DECLARE_UNO3_AGG_DEFAULTS(OClickableImageBaseModel, OControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ protected:
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // css::form::XImageProducerSupplier
+ virtual css::uno::Reference< css::awt::XImageProducer> SAL_CALL getImageProducer() override { return m_xProducer; }
+
+ // 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;
+
+ using ::cppu::OPropertySetHelper::getFastPropertyValue;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent&) override;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+ // XImageProducer
+ virtual void SAL_CALL addConsumer( const css::uno::Reference< css::awt::XImageConsumer >& xConsumer ) override;
+ virtual void SAL_CALL removeConsumer( const css::uno::Reference< css::awt::XImageConsumer >& xConsumer ) override;
+ virtual void SAL_CALL startProduction( ) override;
+
+ // XSubmissionSupplier
+ virtual css::uno::Reference< css::form::submission::XSubmission > SAL_CALL getSubmission() override;
+ virtual void SAL_CALL setSubmission( const css::uno::Reference< css::form::submission::XSubmission >& _submission ) override;
+
+ // XServiceInfo
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XEventListener
+ using OControlModel::disposing;
+
+ public:
+ struct GuardAccess { friend class ImageModelMethodGuard; private: GuardAccess() { } };
+ ::osl::Mutex& getMutex( GuardAccess ) { return m_aMutex; }
+ ImageProducer* getImageProducer( GuardAccess ) { return m_xProducer.get(); }
+
+ protected:
+ using OControlModel::getMutex;
+
+ void implConstruct();
+
+ // to be called from within the cloning-ctor of your derived class
+ void implInitializeImageURL( );
+
+ SfxObjectShell* GetObjectShell();
+
+ DECL_LINK( OnImageImportDone, ::Graphic*, void );
+ };
+
+ class ImageModelMethodGuard : public ::osl::MutexGuard
+ {
+ public:
+ explicit ImageModelMethodGuard( OClickableImageBaseModel& _rModel )
+ : ::osl::MutexGuard( _rModel.getMutex( OClickableImageBaseModel::GuardAccess() ) )
+ {
+ if ( nullptr == _rModel.getImageProducer( OClickableImageBaseModel::GuardAccess() ) )
+ throw css::lang::DisposedException(
+ OUString(),
+ static_cast< css::form::XImageProducerSupplier* >( &_rModel )
+ );
+ }
+ };
+
+
+ // OClickableImageBaseControl
+
+ typedef ::cppu::ImplHelper3 < css::form::XApproveActionBroadcaster
+ , css::form::submission::XSubmission
+ , css::frame::XDispatchProviderInterception
+ > OClickableImageBaseControl_BASE;
+
+ class OClickableImageBaseControl :public OClickableImageBaseControl_BASE
+ ,public OControl
+ {
+ friend class OImageProducerThread_Impl;
+
+ private:
+ rtl::Reference<OImageProducerThread_Impl> m_pThread;
+ ::comphelper::OInterfaceContainerHelper3<css::form::submission::XSubmissionVetoListener>
+ m_aSubmissionVetoListeners;
+ ControlFeatureInterception m_aFeatureInterception;
+
+ protected:
+ ::comphelper::OInterfaceContainerHelper3<css::form::XApproveActionListener> m_aApproveActionListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XActionListener> m_aActionListeners;
+ OUString m_aActionCommand;
+
+ // XSubmission
+ virtual void SAL_CALL submit( ) override;
+ virtual void SAL_CALL submitWithInteraction( const css::uno::Reference< css::task::XInteractionHandler >& aHandler ) override;
+ virtual void SAL_CALL addSubmissionVetoListener( const css::uno::Reference< css::form::submission::XSubmissionVetoListener >& listener ) override;
+ virtual void SAL_CALL removeSubmissionVetoListener( const css::uno::Reference< css::form::submission::XSubmissionVetoListener >& listener ) override;
+
+ // XServiceInfo
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XEventListener
+ using OControl::disposing;
+
+ public:
+ OClickableImageBaseControl(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ const OUString& _aService);
+ virtual ~OClickableImageBaseControl() override;
+
+ protected:
+ // UNO Binding
+ DECLARE_UNO3_AGG_DEFAULTS(OClickableImageBaseControl, OControl)
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // css::form::XApproveActionBroadcaster
+ virtual void SAL_CALL addApproveActionListener(const css::uno::Reference< css::form::XApproveActionListener>& _rxListener) override;
+ virtual void SAL_CALL removeApproveActionListener(const css::uno::Reference< css::form::XApproveActionListener>& _rxListener) override;
+
+ // XDispatchProviderInterception
+ 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;
+
+ protected:
+ virtual void actionPerformed_Impl( bool bNotifyListener, const css::awt::MouseEvent& rEvt );
+
+ css::uno::Sequence< css::uno::Type > _getTypes() override;
+
+ /** approves the action by calling the approve listeners
+ @return <TRUE/> if and only if the action has <em>not</em> been cancelled by a listener
+ */
+ bool approveAction( );
+
+ /** retrieves (and if necessary creates) the image producer thread.
+
+ Must be called with our mutex locked
+ */
+ OImageProducerThread_Impl* getImageProducerThread();
+
+ private:
+ void implSubmit(
+ const css::awt::MouseEvent& _rEvent,
+ const css::uno::Reference< css::task::XInteractionHandler >& aHandler
+ );
+ };
+
+ class OImageProducerThread_Impl: public OComponentEventThread
+ {
+ protected:
+
+ // Process an Event.
+ // The mutex is not locked, pCompImpl stays valid in any case
+ virtual void processEvent( ::cppu::OComponentHelper *pCompImpl,
+ const css::lang::EventObject*,
+ const css::uno::Reference< css::awt::XControl>&,
+ bool ) override;
+
+ public:
+ explicit OImageProducerThread_Impl( OClickableImageBaseControl *pControl ) :
+ OComponentEventThread( pControl )
+ {}
+
+ void addEvent() { OComponentEventThread::addEvent( std::make_unique<css::lang::EventObject>() ); }
+
+ protected:
+ using OComponentEventThread::addEvent;
+ };
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/cloneable.cxx b/forms/source/component/cloneable.cxx
new file mode 100644
index 0000000000..9770019c23
--- /dev/null
+++ b/forms/source/component/cloneable.cxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cloneable.hxx>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <comphelper/uno3.hxx>
+#include <tools/debug.hxx>
+
+
+namespace frm
+{
+
+
+ using namespace ::comphelper;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::util;
+
+
+ //= OCloneableAggregation
+
+
+ Reference< XAggregation > OCloneableAggregation::createAggregateClone( const OCloneableAggregation* _pOriginal )
+ {
+ Reference< XCloneable > xAggregateCloneable; // will be the aggregate's XCloneable
+ Reference< XAggregation > xAggregateClone; // will be the aggregate's clone
+
+ if ( query_aggregation( _pOriginal->m_xAggregate, xAggregateCloneable ) )
+ {
+ xAggregateClone.set(xAggregateCloneable->createClone(), css::uno::UNO_QUERY);
+ DBG_ASSERT( xAggregateClone.is(), "OCloneableAggregation::createAggregateClone: invalid clone returned by the aggregate!" );
+ }
+ else {
+ DBG_ASSERT( !_pOriginal->m_xAggregate.is(), "OCloneableAggregation::createAggregateClone: aggregate is not cloneable!" );
+ }
+
+ return xAggregateClone;
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/entrylisthelper.cxx b/forms/source/component/entrylisthelper.cxx
new file mode 100644
index 0000000000..0f78107632
--- /dev/null
+++ b/forms/source/component/entrylisthelper.cxx
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "entrylisthelper.hxx"
+#include <FormComponent.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/sequence.hxx>
+#include <comphelper/property.hxx>
+#include <com/sun/star/form/binding/XListEntryTypedSource.hpp>
+#include <algorithm>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::form::binding;
+
+ OEntryListHelper::OEntryListHelper( OControlModel& _rControlModel )
+ :m_rControlModel( _rControlModel )
+ ,m_aRefreshListeners( _rControlModel.getInstanceMutex() )
+ {
+ }
+
+
+ OEntryListHelper::OEntryListHelper( const OEntryListHelper& _rSource, OControlModel& _rControlModel )
+ :m_rControlModel( _rControlModel )
+ ,m_xListSource ( _rSource.m_xListSource )
+ ,m_aStringItems( _rSource.m_aStringItems )
+ ,m_aRefreshListeners( _rControlModel.getInstanceMutex() )
+ {
+ }
+
+
+ OEntryListHelper::~OEntryListHelper( )
+ {
+ }
+
+
+ void SAL_CALL OEntryListHelper::setListEntrySource( const Reference< XListEntrySource >& _rxSource )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ // disconnect from the current external list source
+ disconnectExternalListSource();
+
+ // and connect to the new one
+ if ( _rxSource.is() )
+ connectExternalListSource( _rxSource, aLock );
+ }
+
+
+ Reference< XListEntrySource > SAL_CALL OEntryListHelper::getListEntrySource( )
+ {
+ return m_xListSource;
+ }
+
+
+ void SAL_CALL OEntryListHelper::entryChanged( const ListEntryEvent& _rEvent )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ OSL_ENSURE( _rEvent.Source == m_xListSource,
+ "OEntryListHelper::entryChanged: where did this come from?" );
+ OSL_ENSURE( ( _rEvent.Position >= 0 ) && ( o3tl::make_unsigned(_rEvent.Position) < m_aStringItems.size() ),
+ "OEntryListHelper::entryChanged: invalid index!" );
+ OSL_ENSURE( _rEvent.Entries.getLength() == 1,
+ "OEntryListHelper::entryChanged: invalid string list!" );
+
+ if ( ( _rEvent.Position >= 0 )
+ && ( o3tl::make_unsigned(_rEvent.Position) < m_aStringItems.size() )
+ && _rEvent.Entries.hasElements()
+ )
+ {
+ m_aStringItems[ _rEvent.Position ] = _rEvent.Entries[ 0 ];
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ stringItemListChanged( aLock );
+ }
+ }
+
+
+ void SAL_CALL OEntryListHelper::entryRangeInserted( const ListEntryEvent& _rEvent )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ OSL_ENSURE( _rEvent.Source == m_xListSource,
+ "OEntryListHelper::entryRangeInserted: where did this come from?" );
+ OSL_ENSURE( ( _rEvent.Position > 0 ) && ( o3tl::make_unsigned(_rEvent.Position) < m_aStringItems.size() ) && _rEvent.Entries.hasElements(),
+ "OEntryListHelper::entryRangeRemoved: invalid count and/or position!" );
+
+ if ( ( _rEvent.Position > 0 )
+ && ( o3tl::make_unsigned(_rEvent.Position) < m_aStringItems.size() )
+ && _rEvent.Entries.hasElements()
+ )
+ {
+ m_aStringItems.insert(m_aStringItems.begin() + _rEvent.Position, _rEvent.Entries.begin(), _rEvent.Entries.end());
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ stringItemListChanged( aLock );
+ }
+ }
+
+
+ void SAL_CALL OEntryListHelper::entryRangeRemoved( const ListEntryEvent& _rEvent )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ OSL_ENSURE( _rEvent.Source == m_xListSource,
+ "OEntryListHelper::entryRangeRemoved: where did this come from?" );
+ OSL_ENSURE( ( _rEvent.Position > 0 ) && ( _rEvent.Count > 0 ) && ( _rEvent.Position + _rEvent.Count <= static_cast<sal_Int32>(m_aStringItems.size()) ),
+ "OEntryListHelper::entryRangeRemoved: invalid count and/or position!" );
+
+ if ( !(( _rEvent.Position > 0 )
+ && ( _rEvent.Count > 0 )
+ && ( _rEvent.Position + _rEvent.Count <= static_cast<sal_Int32>(m_aStringItems.size()) ))
+ )
+ return;
+
+ m_aStringItems.erase(m_aStringItems.begin() + _rEvent.Position,
+ m_aStringItems.begin() + _rEvent.Position + _rEvent.Count );
+ if (_rEvent.Position + _rEvent.Count <= m_aTypedItems.getLength())
+ {
+ Sequence<Any> aTmp( m_aTypedItems.getLength() - _rEvent.Count );
+ auto aTmpRange = asNonConstRange(aTmp);
+ sal_Int32 nStop = _rEvent.Position;
+ sal_Int32 i = 0;
+ for ( ; i < nStop; ++i)
+ {
+ aTmpRange[i] = m_aTypedItems[i];
+ }
+ nStop = aTmp.getLength();
+ for (sal_Int32 j = _rEvent.Position + _rEvent.Count; i < nStop; ++i, ++j)
+ {
+ aTmpRange[i] = m_aTypedItems[j];
+ }
+ m_aTypedItems = aTmp;
+ }
+ else if (m_aTypedItems.hasElements())
+ {
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ }
+ stringItemListChanged( aLock );
+ }
+
+
+ void SAL_CALL OEntryListHelper::allEntriesChanged( const EventObject& _rEvent )
+ {
+ ControlModelLock aLock( m_rControlModel );
+
+ OSL_ENSURE( _rEvent.Source == m_xListSource,
+ "OEntryListHelper::allEntriesChanged: where did this come from?" );
+
+ if ( _rEvent.Source == m_xListSource )
+ {
+ impl_lock_refreshList( aLock );
+ }
+ }
+
+ // XRefreshable
+
+ void SAL_CALL OEntryListHelper::addRefreshListener(const Reference<XRefreshListener>& _rxListener)
+ {
+ if ( _rxListener.is() )
+ m_aRefreshListeners.addInterface( _rxListener );
+ }
+
+
+ void SAL_CALL OEntryListHelper::removeRefreshListener(const Reference<XRefreshListener>& _rxListener)
+ {
+ if ( _rxListener.is() )
+ m_aRefreshListeners.removeInterface( _rxListener );
+ }
+
+
+ void SAL_CALL OEntryListHelper::refresh()
+ {
+ {
+ ControlModelLock aLock( m_rControlModel );
+ impl_lock_refreshList( aLock );
+ }
+
+ EventObject aEvt( static_cast< XRefreshable* >( this ) );
+ m_aRefreshListeners.notifyEach( &XRefreshListener::refreshed, aEvt );
+ }
+
+
+ void OEntryListHelper::impl_lock_refreshList( ControlModelLock& _rInstanceLock )
+ {
+ if ( hasExternalListSource() )
+ obtainListSourceEntries( _rInstanceLock );
+ else
+ refreshInternalEntryList();
+ }
+
+
+ bool OEntryListHelper::handleDisposing( const EventObject& _rEvent )
+ {
+ if ( m_xListSource .is() && ( _rEvent.Source == m_xListSource ) )
+ {
+ disconnectExternalListSource( );
+ return true;
+ }
+ return false;
+ }
+
+
+ void OEntryListHelper::disposing( )
+ {
+ EventObject aEvt( static_cast< XRefreshable* >( this ) );
+ m_aRefreshListeners.disposeAndClear(aEvt);
+
+ if ( hasExternalListSource( ) )
+ disconnectExternalListSource( );
+ }
+
+
+ void OEntryListHelper::disconnectExternalListSource( )
+ {
+ if ( m_xListSource.is() )
+ m_xListSource->removeListEntryListener( this );
+
+ m_xListSource.clear();
+ }
+
+
+ void OEntryListHelper::connectExternalListSource( const Reference< XListEntrySource >& _rxSource, ControlModelLock& _rInstanceLock )
+ {
+ OSL_ENSURE( !hasExternalListSource(), "OEntryListHelper::connectExternalListSource: only to be called if no external source is active!" );
+ OSL_ENSURE( _rxSource.is(), "OEntryListHelper::connectExternalListSource: invalid list source!" );
+
+ // remember it
+ m_xListSource = _rxSource;
+
+ // initially fill our item list
+ if ( m_xListSource.is() )
+ {
+ // be notified when the list changes ...
+ m_xListSource->addListEntryListener( this );
+
+ obtainListSourceEntries( _rInstanceLock );
+ }
+ }
+
+
+ void OEntryListHelper::obtainListSourceEntries( ControlModelLock& _rInstanceLock )
+ {
+ Reference< XListEntryTypedSource > xTyped;
+ xTyped.set( m_xListSource, UNO_QUERY);
+ if (xTyped.is())
+ {
+ comphelper::sequenceToContainer( m_aStringItems, xTyped->getAllListEntriesTyped( m_aTypedItems));
+ }
+ else
+ {
+ comphelper::sequenceToContainer( m_aStringItems, m_xListSource->getAllListEntries());
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>();
+ }
+ stringItemListChanged( _rInstanceLock );
+ }
+
+
+ bool OEntryListHelper::convertNewListSourceProperty( Any& _rConvertedValue,
+ Any& _rOldValue, const Any& _rValue )
+ {
+ if ( hasExternalListSource() )
+ throw IllegalArgumentException( );
+ // TODO: error message
+
+ return ::comphelper::tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, comphelper::containerToSequence(m_aStringItems) );
+ }
+
+
+ void OEntryListHelper::setNewStringItemList( const css::uno::Any& _rValue, ControlModelLock& _rInstanceLock )
+ {
+ OSL_PRECOND( !hasExternalListSource(), "OEntryListHelper::setNewStringItemList: this should never have survived convertNewListSourceProperty!" );
+ css::uno::Sequence<OUString> aTmp;
+ OSL_VERIFY( _rValue >>= aTmp );
+ comphelper::sequenceToContainer(m_aStringItems, aTmp);
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ stringItemListChanged( _rInstanceLock );
+ }
+
+
+ void OEntryListHelper::setNewTypedItemList( const css::uno::Any& _rValue, ControlModelLock& _rInstanceLock )
+ {
+ OSL_PRECOND( !hasExternalListSource(), "OEntryListHelper::setNewTypedItemList: this should never have survived convertNewListSourceProperty!" );
+ if (!(_rValue >>= m_aTypedItems ))
+ {
+ OSL_VERIFY(false);
+ if (m_aTypedItems.hasElements())
+ m_aTypedItems = Sequence<Any>(); // doesn't match anymore
+ }
+ // Sets both properties, assuming that TypedItemList belongs to StringItemList.
+ stringItemListChanged( _rInstanceLock );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/entrylisthelper.hxx b/forms/source/component/entrylisthelper.hxx
new file mode 100644
index 0000000000..cb0e0789a4
--- /dev/null
+++ b/forms/source/component/entrylisthelper.hxx
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <com/sun/star/form/binding/XListEntryListener.hpp>
+
+#include <cppuhelper/implbase3.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+
+
+namespace frm
+{
+
+
+ class OControlModel;
+ class ControlModelLock;
+
+
+ //= OEntryListHelper
+
+ typedef ::cppu::ImplHelper3 < css::form::binding::XListEntrySink
+ , css::form::binding::XListEntryListener
+ , css::util::XRefreshable
+ > OEntryListHelper_BASE;
+
+ class OEntryListHelper : public OEntryListHelper_BASE
+ {
+ private:
+ OControlModel& m_rControlModel;
+
+ css::uno::Reference< css::form::binding::XListEntrySource >
+ m_xListSource; /// our external list source
+ std::vector< OUString >
+ m_aStringItems; /// "overridden" StringItemList property value
+ css::uno::Sequence< css::uno::Any >
+ m_aTypedItems; /// "overridden" TypedItemList property value
+ ::comphelper::OInterfaceContainerHelper3<css::util::XRefreshListener>
+ m_aRefreshListeners;
+
+
+ protected:
+ explicit OEntryListHelper( OControlModel& _rControlModel );
+ OEntryListHelper( const OEntryListHelper& _rSource, OControlModel& _rControlModel );
+ virtual ~OEntryListHelper( );
+
+ /// returns the current string item list
+ const std::vector< OUString >&
+ getStringItemList() const { return m_aStringItems; }
+
+ /// returns the current typed item list
+ const css::uno::Sequence< css::uno::Any >&
+ getTypedItemList() const { return m_aTypedItems; }
+
+ /// determines whether we actually have an external list source
+ bool hasExternalListSource( ) const { return m_xListSource.is(); }
+
+ /** handling the XEventListener::disposing call for the case where
+ our list source is being disposed
+ @return
+ <TRUE/> if and only if the disposed object was our list source, and so the
+ event was handled
+ */
+ bool handleDisposing( const css::lang::EventObject& _rEvent );
+
+ /** to be called by derived classes' instances when they're being disposed
+ */
+ void disposing( );
+
+ // prevent method hiding
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override = 0;
+
+ /** helper for implementing convertFastPropertyValue( StringItemList )
+
+ <p>The signature of this method and the return type have the same semantics
+ as convertFastPropertyValue.</p>
+ */
+ bool convertNewListSourceProperty(
+ css::uno::Any& _rConvertedValue,
+ css::uno::Any& _rOldValue,
+ const css::uno::Any& _rValue
+ );
+
+ /** helper for implementing setFastPropertyValueNoBroadcast
+
+ <p>Will internally call stringItemListChanged after the new item list
+ has been set.</p>
+
+ @precond
+ not to be called when we have an external list source
+ @see hasExternalListSource
+ */
+ void setNewStringItemList( const css::uno::Any& _rValue, ControlModelLock& _rInstanceLock );
+
+ /** helper for implementing setFastPropertyValueNoBroadcast
+
+ <p>Will internally call stringItemListChanged after the new item list
+ has been set.</p>
+
+ @precond
+ not to be called when we have an external list source
+ @see hasExternalListSource
+ */
+ void setNewTypedItemList( const css::uno::Any& _rValue, ControlModelLock& _rInstanceLock );
+
+ /** announces that the list of entries has changed.
+
+ <p>Derived classes have to override this. Most probably, they'll set the new
+ as model property.</p>
+
+ @pure
+ @see getStringItemList
+ */
+ virtual void stringItemListChanged( ControlModelLock& _rInstanceLock ) = 0;
+
+ /** called when XRefreshable::refresh has been called, and we do *not* have an external
+ list source
+ */
+ virtual void refreshInternalEntryList() = 0;
+
+ private:
+ // XListEntrySink
+ virtual void SAL_CALL setListEntrySource( const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource ) override;
+ virtual css::uno::Reference< css::form::binding::XListEntrySource > SAL_CALL getListEntrySource( ) override;
+
+ // XListEntryListener
+ virtual void SAL_CALL entryChanged( const css::form::binding::ListEntryEvent& _rSource ) override;
+ virtual void SAL_CALL entryRangeInserted( const css::form::binding::ListEntryEvent& _rSource ) override;
+ virtual void SAL_CALL entryRangeRemoved( const css::form::binding::ListEntryEvent& _rSource ) override;
+ virtual void SAL_CALL allEntriesChanged( const css::lang::EventObject& _rSource ) override;
+
+ // XRefreshable
+ virtual void SAL_CALL refresh() override;
+ virtual void SAL_CALL addRefreshListener(const css::uno::Reference< css::util::XRefreshListener>& _rxListener) override;
+ virtual void SAL_CALL removeRefreshListener(const css::uno::Reference< css::util::XRefreshListener>& _rxListener) override;
+
+ private:
+ /** disconnects from the active external list source, if present
+ @see connectExternalListSource
+ */
+ void disconnectExternalListSource( );
+
+ /** connects to a new external list source
+ @param _rxSource
+ the new list source. Must not be <NULL/>
+ @see disconnectExternalListSource
+ */
+ void connectExternalListSource(
+ const css::uno::Reference< css::form::binding::XListEntrySource >& _rxSource,
+ ControlModelLock& _rInstanceLock
+ );
+
+ /** obtains list entries and possibly data values from list source
+
+ @precond
+ m_xListSource has to hold an external list source
+ */
+ void obtainListSourceEntries( ControlModelLock& _rInstanceLock );
+
+ /** refreshes our list entries
+
+ In case we have an external list source, it's used to obtain the new entries, and then
+ stringItemListChanged is called to give the derived class the possibility to
+ react on this.
+
+ In case we do not have an external list source, refreshInternalEntryList is called.
+ */
+ void impl_lock_refreshList( ControlModelLock& _rInstanceLock );
+
+ private:
+ OEntryListHelper( const OEntryListHelper& ) = delete;
+ OEntryListHelper& operator=( const OEntryListHelper& ) = delete;
+ };
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/errorbroadcaster.cxx b/forms/source/component/errorbroadcaster.cxx
new file mode 100644
index 0000000000..7f37e4e0e9
--- /dev/null
+++ b/forms/source/component/errorbroadcaster.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "errorbroadcaster.hxx"
+#include <connectivity/dbtools.hxx>
+#include <sal/log.hxx>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::dbtools;
+
+ OErrorBroadcaster::OErrorBroadcaster( ::cppu::OBroadcastHelper& _rBHelper )
+ :m_rBHelper( _rBHelper )
+ ,m_aErrorListeners( _rBHelper.rMutex )
+ {
+ }
+
+
+ OErrorBroadcaster::~OErrorBroadcaster( )
+ {
+ SAL_WARN_IF( !m_rBHelper.bDisposed && !m_rBHelper.bInDispose, "forms.component",
+ "OErrorBroadcaster::~OErrorBroadcaster: not disposed!" );
+ // herein, we don't have a chance to do the dispose ourself...
+
+ SAL_WARN_IF( m_aErrorListeners.getLength(), "forms.component",
+ "OErrorBroadcaster::~OErrorBroadcaster: still have listeners!" );
+ // either we're not disposed, or the derived class did not call our dispose from within their dispose
+ }
+
+
+ void OErrorBroadcaster::disposing()
+ {
+ EventObject aDisposeEvent( static_cast< XSQLErrorBroadcaster* >( this ) );
+ m_aErrorListeners.disposeAndClear( aDisposeEvent );
+ }
+
+
+ void OErrorBroadcaster::onError( const SQLException& _rException, const OUString& _rContextDescription )
+ {
+ Any aError;
+ if ( !_rContextDescription.isEmpty() )
+ aError <<= prependErrorInfo( _rException, static_cast< XSQLErrorBroadcaster* >( this ), _rContextDescription );
+ else
+ aError <<= _rException;
+
+ onError( SQLErrorEvent( static_cast< XSQLErrorBroadcaster* >( this ), aError ) );
+ }
+
+
+ void OErrorBroadcaster::onError( const css::sdb::SQLErrorEvent& _rError )
+ {
+ m_aErrorListeners.notifyEach( &XSQLErrorListener::errorOccured, _rError );
+ }
+
+
+ void SAL_CALL OErrorBroadcaster::addSQLErrorListener( const Reference< XSQLErrorListener >& _rxListener )
+ {
+ m_aErrorListeners.addInterface( _rxListener );
+ }
+
+
+ void SAL_CALL OErrorBroadcaster::removeSQLErrorListener( const Reference< XSQLErrorListener >& _rxListener )
+ {
+ m_aErrorListeners.removeInterface( _rxListener );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/errorbroadcaster.hxx b/forms/source/component/errorbroadcaster.hxx
new file mode 100644
index 0000000000..17fcb4d0f2
--- /dev/null
+++ b/forms/source/component/errorbroadcaster.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase1.hxx>
+#include <com/sun/star/sdb/XSQLErrorBroadcaster.hpp>
+#include <cppuhelper/interfacecontainer.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdb/SQLErrorEvent.hpp>
+
+
+namespace frm
+{
+
+ typedef ::cppu::ImplHelper1 < css::sdb::XSQLErrorBroadcaster
+ > OErrorBroadcaster_BASE;
+
+ class OErrorBroadcaster : public OErrorBroadcaster_BASE
+ {
+ private:
+ ::cppu::OBroadcastHelper& m_rBHelper;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XSQLErrorListener> m_aErrorListeners;
+
+ protected:
+ explicit OErrorBroadcaster( ::cppu::OBroadcastHelper& _rBHelper );
+ virtual ~OErrorBroadcaster( );
+
+ void disposing();
+
+ void onError( const css::sdbc::SQLException& _rException, const OUString& _rContextDescription );
+ void onError( const css::sdb::SQLErrorEvent& _rException );
+
+ protected:
+ // XSQLErrorBroadcaster
+ virtual void SAL_CALL addSQLErrorListener( const css::uno::Reference< css::sdb::XSQLErrorListener >& _rListener ) override;
+ virtual void SAL_CALL removeSQLErrorListener( const css::uno::Reference< css::sdb::XSQLErrorListener >& _rListener ) override;
+ };
+
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/findpos.cxx b/forms/source/component/findpos.cxx
new file mode 100644
index 0000000000..6b2503ac0b
--- /dev/null
+++ b/forms/source/component/findpos.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/config.h>
+
+#include "findpos.hxx"
+
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#include <algorithm>
+
+namespace detail {
+
+sal_Int32 findPos(
+ const OUString& aStr,
+ const css::uno::Sequence< OUString >& rList)
+{
+ const OUString* pStrList = rList.getConstArray();
+ const OUString* pResult = ::std::lower_bound(
+ pStrList, pStrList + rList.getLength(), aStr );
+ if ( ( pResult != pStrList + rList.getLength() ) && ( *pResult == aStr ) )
+ return ( pResult - pStrList );
+
+ return -1;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/findpos.hxx b/forms/source/component/findpos.hxx
new file mode 100644
index 0000000000..2a40bc3ca9
--- /dev/null
+++ b/forms/source/component/findpos.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <sal/types.h>
+
+namespace detail
+{
+sal_Int32 findPos(const OUString& aStr, const css::uno::Sequence<OUString>& rList);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/formcontrolfont.cxx b/forms/source/component/formcontrolfont.cxx
new file mode 100644
index 0000000000..bbb15303b4
--- /dev/null
+++ b/forms/source/component/formcontrolfont.cxx
@@ -0,0 +1,578 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formcontrolfont.hxx>
+#include <frm_strings.hxx>
+#include <property.hxx>
+#include <cppuhelper/propshlp.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/color.hxx>
+#include <sal/log.hxx>
+#include <toolkit/helper/emptyfontdescriptor.hxx>
+#include <com/sun/star/awt/FontRelief.hpp>
+#include <com/sun/star/awt/FontEmphasisMark.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+
+namespace frm
+{
+
+
+ using namespace ::comphelper;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+
+
+ namespace
+ {
+ Any lcl_extractFontDescriptorAggregate( sal_Int32 _nHandle, const FontDescriptor& _rFont )
+ {
+ Any aValue;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_FONT_NAME:
+ aValue <<= _rFont.Name;
+ break;
+
+ case PROPERTY_ID_FONT_STYLENAME:
+ aValue <<= _rFont.StyleName;
+ break;
+
+ case PROPERTY_ID_FONT_FAMILY:
+ aValue <<= _rFont.Family;
+ break;
+
+ case PROPERTY_ID_FONT_CHARSET:
+ aValue <<= _rFont.CharSet;
+ break;
+
+ case PROPERTY_ID_FONT_CHARWIDTH:
+ aValue <<= _rFont.CharacterWidth;
+ break;
+
+ case PROPERTY_ID_FONT_KERNING:
+ aValue <<= _rFont.Kerning;
+ break;
+
+ case PROPERTY_ID_FONT_ORIENTATION:
+ aValue <<= _rFont.Orientation;
+ break;
+
+ case PROPERTY_ID_FONT_PITCH:
+ aValue <<= _rFont.Pitch;
+ break;
+
+ case PROPERTY_ID_FONT_TYPE:
+ aValue <<= _rFont.Type;
+ break;
+
+ case PROPERTY_ID_FONT_WIDTH:
+ aValue <<= _rFont.Width;
+ break;
+
+ case PROPERTY_ID_FONT_HEIGHT:
+ aValue <<= static_cast<float>( _rFont.Height );
+ break;
+
+ case PROPERTY_ID_FONT_WEIGHT:
+ aValue <<= _rFont.Weight;
+ break;
+
+ case PROPERTY_ID_FONT_SLANT:
+ aValue <<= _rFont.Slant;
+ break;
+
+ case PROPERTY_ID_FONT_UNDERLINE:
+ aValue <<= _rFont.Underline;
+ break;
+
+ case PROPERTY_ID_FONT_STRIKEOUT:
+ aValue <<= _rFont.Strikeout;
+ break;
+
+ case PROPERTY_ID_FONT_WORDLINEMODE:
+ aValue <<= _rFont.WordLineMode;
+ break;
+
+ default:
+ OSL_FAIL( "lcl_extractFontDescriptorAggregate: invalid handle!" );
+ break;
+ }
+ return aValue;
+ }
+ }
+
+ FontControlModel::FontControlModel( bool _bToolkitCompatibleDefaults )
+ :m_nFontRelief( css::awt::FontRelief::NONE )
+ ,m_nFontEmphasis( css::awt::FontEmphasisMark::NONE )
+ ,m_bToolkitCompatibleDefaults( _bToolkitCompatibleDefaults )
+ {
+ }
+
+
+ FontControlModel::FontControlModel( const FontControlModel* _pOriginal )
+ {
+ m_aFont = _pOriginal->m_aFont;
+ m_nFontRelief = _pOriginal->m_nFontRelief;
+ m_nFontEmphasis = _pOriginal->m_nFontEmphasis;
+ m_aTextLineColor = _pOriginal->m_aTextLineColor;
+ m_aTextColor = _pOriginal->m_aTextColor;
+ m_bToolkitCompatibleDefaults = _pOriginal->m_bToolkitCompatibleDefaults;
+ }
+
+
+ bool FontControlModel::isFontRelatedProperty( sal_Int32 _nPropertyHandle )
+ {
+ return isFontAggregateProperty( _nPropertyHandle )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONTEMPHASISMARK )
+ || ( _nPropertyHandle == PROPERTY_ID_FONTRELIEF )
+ || ( _nPropertyHandle == PROPERTY_ID_TEXTLINECOLOR )
+ || ( _nPropertyHandle == PROPERTY_ID_TEXTCOLOR );
+ }
+
+
+ bool FontControlModel::isFontAggregateProperty( sal_Int32 _nPropertyHandle )
+ {
+ return ( _nPropertyHandle == PROPERTY_ID_FONT_CHARWIDTH )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_ORIENTATION )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_WIDTH )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_NAME )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_STYLENAME )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_FAMILY )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_CHARSET )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_HEIGHT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_WEIGHT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_SLANT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_UNDERLINE )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_STRIKEOUT )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_WORDLINEMODE )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_PITCH )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_KERNING )
+ || ( _nPropertyHandle == PROPERTY_ID_FONT_TYPE );
+ }
+
+
+ Color FontControlModel::getTextColor( ) const
+ {
+ Color nColor = COL_TRANSPARENT;
+ m_aTextColor >>= nColor;
+ return nColor;
+ }
+
+
+ Color FontControlModel::getTextLineColor( ) const
+ {
+ Color nColor = COL_TRANSPARENT;
+ m_aTextLineColor >>= nColor;
+ return nColor;
+ }
+
+
+ void FontControlModel::describeFontRelatedProperties( Sequence< Property >& /* [out] */ _rProps)
+ {
+ sal_Int32 nPos = _rProps.getLength();
+ _rProps.realloc( nPos + 21 );
+ Property* pProperties = _rProps.getArray();
+
+ *pProperties++ = css::beans::Property(PROPERTY_FONT, PROPERTY_ID_FONT, cppu::UnoType<FontDescriptor>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONTEMPHASISMARK, PROPERTY_ID_FONTEMPHASISMARK, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONTRELIEF, PROPERTY_ID_FONTRELIEF, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_TEXTCOLOR, PROPERTY_ID_TEXTCOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+ *pProperties++ = css::beans::Property(PROPERTY_TEXTLINECOLOR, PROPERTY_ID_TEXTLINECOLOR, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT | css::beans::PropertyAttribute::MAYBEVOID);
+
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_CHARWIDTH, PROPERTY_ID_FONT_CHARWIDTH, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_KERNING, PROPERTY_ID_FONT_KERNING, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_ORIENTATION, PROPERTY_ID_FONT_ORIENTATION, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_PITCH, PROPERTY_ID_FONT_PITCH, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_TYPE, PROPERTY_ID_FONT_TYPE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WIDTH, PROPERTY_ID_FONT_WIDTH, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_NAME, PROPERTY_ID_FONT_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_STYLENAME, PROPERTY_ID_FONT_STYLENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_FAMILY, PROPERTY_ID_FONT_FAMILY, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_CHARSET, PROPERTY_ID_FONT_CHARSET, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_HEIGHT, PROPERTY_ID_FONT_HEIGHT, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WEIGHT, PROPERTY_ID_FONT_WEIGHT, cppu::UnoType<float>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_SLANT, PROPERTY_ID_FONT_SLANT, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_UNDERLINE, PROPERTY_ID_FONT_UNDERLINE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_STRIKEOUT, PROPERTY_ID_FONT_STRIKEOUT, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::MAYBEDEFAULT);
+ *pProperties++ = css::beans::Property(PROPERTY_FONT_WORDLINEMODE, PROPERTY_ID_FONT_WORDLINEMODE, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::MAYBEDEFAULT);
+ }
+
+
+ void FontControlModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ switch( _nHandle )
+ {
+ case PROPERTY_ID_TEXTCOLOR:
+ _rValue = m_aTextColor;
+ break;
+
+ case PROPERTY_ID_FONTEMPHASISMARK:
+ _rValue <<= m_nFontEmphasis;
+ break;
+
+ case PROPERTY_ID_FONTRELIEF:
+ _rValue <<= m_nFontRelief;
+ break;
+
+ case PROPERTY_ID_TEXTLINECOLOR:
+ _rValue = m_aTextLineColor;
+ break;
+
+ case PROPERTY_ID_FONT:
+ _rValue <<= m_aFont;
+ break;
+
+ default:
+ _rValue = lcl_extractFontDescriptorAggregate( _nHandle, m_aFont );
+ break;
+ }
+ }
+
+
+ bool FontControlModel::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified = false;
+ switch( _nHandle )
+ {
+ case PROPERTY_ID_TEXTCOLOR:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aTextColor, cppu::UnoType<sal_Int32>::get() );
+ break;
+
+ case PROPERTY_ID_TEXTLINECOLOR:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aTextLineColor, cppu::UnoType<sal_Int32>::get() );
+ break;
+
+ case PROPERTY_ID_FONTEMPHASISMARK:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_nFontEmphasis );
+ break;
+
+ case PROPERTY_ID_FONTRELIEF:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_nFontRelief );
+ break;
+
+ case PROPERTY_ID_FONT:
+ {
+ Any aWorkAroundGccLimitation( m_aFont );
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, aWorkAroundGccLimitation, cppu::UnoType<decltype(m_aFont)>::get() );
+ }
+ break;
+
+ case PROPERTY_ID_FONT_NAME:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Name );
+ break;
+
+ case PROPERTY_ID_FONT_STYLENAME:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.StyleName );
+ break;
+
+ case PROPERTY_ID_FONT_FAMILY:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Family );
+ break;
+
+ case PROPERTY_ID_FONT_CHARSET:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.CharSet );
+ break;
+
+ case PROPERTY_ID_FONT_CHARWIDTH:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.CharacterWidth );
+ break;
+
+ case PROPERTY_ID_FONT_KERNING:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Kerning );
+ break;
+
+ case PROPERTY_ID_FONT_ORIENTATION:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Orientation );
+ break;
+
+ case PROPERTY_ID_FONT_PITCH:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Pitch );
+ break;
+
+ case PROPERTY_ID_FONT_TYPE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Type );
+ break;
+
+ case PROPERTY_ID_FONT_WIDTH:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Width );
+ break;
+
+ case PROPERTY_ID_FONT_HEIGHT:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, float( m_aFont.Height ) );
+ break;
+
+ case PROPERTY_ID_FONT_WEIGHT:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Weight );
+ break;
+
+ case PROPERTY_ID_FONT_SLANT:
+ bModified = tryPropertyValueEnum( _rConvertedValue, _rOldValue, _rValue, m_aFont.Slant );
+ break;
+
+ case PROPERTY_ID_FONT_UNDERLINE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Underline );
+ break;
+
+ case PROPERTY_ID_FONT_STRIKEOUT:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.Strikeout );
+ break;
+
+ case PROPERTY_ID_FONT_WORDLINEMODE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_aFont.WordLineMode );
+ break;
+
+ default:
+ OSL_FAIL( "FontControlModel::convertFastPropertyValue: no font aggregate!" );
+ }
+ return bModified;
+ }
+
+ /// @throws Exception
+ static void setFastPropertyValue_NoBroadcast_implimpl(
+ FontDescriptor & rFont,
+ sal_Int32 nHandle, const Any& rValue)
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_FONT_NAME:
+ rValue >>= rFont.Name;
+ break;
+
+ case PROPERTY_ID_FONT_STYLENAME:
+ rValue >>= rFont.StyleName;
+ break;
+
+ case PROPERTY_ID_FONT_FAMILY:
+ rValue >>= rFont.Family;
+ break;
+
+ case PROPERTY_ID_FONT_CHARSET:
+ rValue >>= rFont.CharSet;
+ break;
+
+ case PROPERTY_ID_FONT_CHARWIDTH:
+ rValue >>= rFont.CharacterWidth;
+ break;
+
+ case PROPERTY_ID_FONT_KERNING:
+ rValue >>= rFont.Kerning;
+ break;
+
+ case PROPERTY_ID_FONT_ORIENTATION:
+ rValue >>= rFont.Orientation;
+ break;
+
+ case PROPERTY_ID_FONT_PITCH:
+ rValue >>= rFont.Pitch;
+ break;
+
+ case PROPERTY_ID_FONT_TYPE:
+ rValue >>= rFont.Type;
+ break;
+
+ case PROPERTY_ID_FONT_WIDTH:
+ rValue >>= rFont.Width;
+ break;
+
+ case PROPERTY_ID_FONT_HEIGHT:
+ {
+ float nHeight = 0;
+ rValue >>= nHeight;
+ rFont.Height = static_cast<sal_Int16>(nHeight);
+ }
+ break;
+
+ case PROPERTY_ID_FONT_WEIGHT:
+ rValue >>= rFont.Weight;
+ break;
+
+ case PROPERTY_ID_FONT_SLANT:
+ rValue >>= rFont.Slant;
+ break;
+
+ case PROPERTY_ID_FONT_UNDERLINE:
+ rValue >>= rFont.Underline;
+ break;
+
+ case PROPERTY_ID_FONT_STRIKEOUT:
+ rValue >>= rFont.Strikeout;
+ break;
+
+ case PROPERTY_ID_FONT_WORDLINEMODE:
+ {
+ bool bWordLineMode = false;
+ rValue >>= bWordLineMode;
+ rFont.WordLineMode = bWordLineMode;
+ }
+ break;
+
+ default:
+ assert(false); // isFontAggregateProperty
+ }
+ }
+
+ void FontControlModel::setFastPropertyValue_NoBroadcast_impl(
+ ::cppu::OPropertySetHelper & rBase,
+ void (::cppu::OPropertySetHelper::*pSet)(sal_Int32, Any const&),
+ sal_Int32 nHandle, const Any& rValue)
+ {
+ if (isFontAggregateProperty(nHandle))
+ {
+ // need to fire an event for PROPERTY_ID_FONT too apparently, so:
+ FontDescriptor font(getFont());
+
+ // first set new value on backup copy
+ setFastPropertyValue_NoBroadcast_implimpl(font, nHandle, rValue);
+
+ // then set that as the actual property - will eventually call
+ // this method recursively again...
+ (rBase.*pSet)(PROPERTY_ID_FONT, Any(font));
+#ifndef NDEBUG
+ // verify that the nHandle property has the new value
+ Any tmp;
+ getFastPropertyValue(tmp, nHandle);
+ assert(tmp == rValue || PROPERTY_ID_FONT_HEIGHT == nHandle /*rounded*/);
+#endif
+ }
+ else
+ {
+ switch (nHandle)
+ {
+ case PROPERTY_ID_TEXTCOLOR:
+ m_aTextColor = rValue;
+ break;
+
+ case PROPERTY_ID_TEXTLINECOLOR:
+ m_aTextLineColor = rValue;
+ break;
+
+ case PROPERTY_ID_FONTEMPHASISMARK:
+ rValue >>= m_nFontEmphasis;
+ break;
+
+ case PROPERTY_ID_FONTRELIEF:
+ rValue >>= m_nFontRelief;
+ break;
+
+ case PROPERTY_ID_FONT:
+ rValue >>= m_aFont;
+ break;
+
+ default:
+ SAL_WARN("forms.component", "invalid property: " << nHandle);
+ }
+ }
+ }
+
+
+ Any FontControlModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aReturn;
+ // some defaults which are the same, not matter if we have toolkit-compatible
+ // defaults or not
+ bool bHandled = false;
+ switch( _nHandle )
+ {
+ case PROPERTY_ID_TEXTCOLOR:
+ case PROPERTY_ID_TEXTLINECOLOR:
+ // void
+ bHandled = true;
+ break;
+
+ case PROPERTY_ID_FONTEMPHASISMARK:
+ aReturn <<= css::awt::FontEmphasisMark::NONE;
+ bHandled = true;
+ break;
+
+ case PROPERTY_ID_FONTRELIEF:
+ aReturn <<= css::awt::FontRelief::NONE;
+ bHandled = true;
+ break;
+ }
+ if ( bHandled )
+ return aReturn;
+
+ if ( m_bToolkitCompatibleDefaults )
+ {
+ EmptyFontDescriptor aEmpty;
+ if ( PROPERTY_ID_FONT == _nHandle )
+ return Any( FontDescriptor(aEmpty) );
+ return lcl_extractFontDescriptorAggregate( _nHandle, aEmpty );
+ }
+
+ switch( _nHandle )
+ {
+ case PROPERTY_ID_FONT:
+ aReturn <<= ::comphelper::getDefaultFont();
+ break;
+
+ case PROPERTY_ID_FONT_WORDLINEMODE:
+ aReturn <<= false;
+ break;
+
+ case PROPERTY_ID_FONT_NAME:
+ case PROPERTY_ID_FONT_STYLENAME:
+ aReturn <<= OUString();
+ break;
+
+ case PROPERTY_ID_FONT_FAMILY:
+ case PROPERTY_ID_FONT_CHARSET:
+ case PROPERTY_ID_FONT_SLANT:
+ case PROPERTY_ID_FONT_UNDERLINE:
+ case PROPERTY_ID_FONT_STRIKEOUT:
+ aReturn <<= sal_Int16(1);
+ break;
+
+ case PROPERTY_ID_FONT_KERNING:
+ aReturn <<= false;
+ break;
+
+ case PROPERTY_ID_FONT_PITCH:
+ case PROPERTY_ID_FONT_TYPE:
+ case PROPERTY_ID_FONT_WIDTH:
+ aReturn <<= sal_Int16(0);
+ break;
+
+ case PROPERTY_ID_FONT_HEIGHT:
+ case PROPERTY_ID_FONT_WEIGHT:
+ case PROPERTY_ID_FONT_CHARWIDTH:
+ case PROPERTY_ID_FONT_ORIENTATION:
+ aReturn <<= float(0);
+ break;
+
+ default:
+ OSL_FAIL( "FontControlModel::getPropertyDefaultByHandle: invalid property!" );
+ }
+
+ return aReturn;
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/imgprod.cxx b/forms/source/component/imgprod.cxx
new file mode 100644
index 0000000000..002ef0cc56
--- /dev/null
+++ b/forms/source/component/imgprod.cxx
@@ -0,0 +1,505 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "imgprod.hxx"
+
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+#include <utility>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/awt/ImageStatus.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include <svtools/imageresourceaccess.hxx>
+#include <comphelper/processfactory.hxx>
+
+namespace {
+
+class ImgProdLockBytes : public SvLockBytes
+{
+ css::uno::Reference< css::io::XInputStream > xStmRef;
+ css::uno::Sequence<sal_Int8> maSeq;
+
+public:
+
+ ImgProdLockBytes( SvStream* pStm, bool bOwner );
+ explicit ImgProdLockBytes( css::uno::Reference< css::io::XInputStream > xStreamRef );
+
+ virtual ErrCode ReadAt( sal_uInt64 nPos, void* pBuffer, std::size_t nCount, std::size_t * pRead ) const override;
+ virtual ErrCode WriteAt( sal_uInt64 nPos, const void* pBuffer, std::size_t nCount, std::size_t * pWritten ) override;
+ virtual ErrCode Flush() const override;
+ virtual ErrCode SetSize( sal_uInt64 nSize ) override;
+ virtual ErrCode Stat( SvLockBytesStat* ) const override;
+};
+
+}
+
+ImgProdLockBytes::ImgProdLockBytes( SvStream* pStm, bool bOwner ) :
+ SvLockBytes( pStm, bOwner )
+{
+}
+
+
+ImgProdLockBytes::ImgProdLockBytes( css::uno::Reference< css::io::XInputStream > _xStmRef ) :
+ xStmRef(std::move( _xStmRef ))
+{
+ if( !xStmRef.is() )
+ return;
+
+ const sal_uInt32 nBytesToRead = 65535;
+ sal_uInt32 nRead;
+
+ do
+ {
+ css::uno::Sequence< sal_Int8 > aReadSeq;
+
+ nRead = xStmRef->readSomeBytes( aReadSeq, nBytesToRead );
+
+ if( nRead )
+ {
+ const sal_uInt32 nOldLength = maSeq.getLength();
+ maSeq.realloc( nOldLength + nRead );
+ memcpy( maSeq.getArray() + nOldLength, aReadSeq.getConstArray(), aReadSeq.getLength() );
+ }
+ }
+ while( nBytesToRead == nRead );
+}
+
+ErrCode ImgProdLockBytes::ReadAt(sal_uInt64 const nPos,
+ void* pBuffer, std::size_t nCount, std::size_t * pRead) const
+{
+ if( GetStream() )
+ {
+ const_cast<SvStream*>(GetStream())->ResetError();
+ const ErrCode nErr = SvLockBytes::ReadAt( nPos, pBuffer, nCount, pRead );
+ const_cast<SvStream*>(GetStream())->ResetError();
+ return nErr;
+ }
+ else
+ {
+ const std::size_t nSeqLen = maSeq.getLength();
+
+ if( nPos < nSeqLen )
+ {
+ if( ( nPos + nCount ) > nSeqLen )
+ nCount = nSeqLen - nPos;
+
+ memcpy( pBuffer, maSeq.getConstArray() + nPos, nCount );
+ *pRead = nCount;
+ }
+ else
+ *pRead = 0;
+
+ return ERRCODE_NONE;
+ }
+}
+
+
+ErrCode ImgProdLockBytes::WriteAt(sal_uInt64 const nPos,
+ const void* pBuffer, std::size_t nCount, std::size_t * pWritten)
+{
+ if( GetStream() )
+ return SvLockBytes::WriteAt( nPos, pBuffer, nCount, pWritten );
+ else
+ {
+ DBG_ASSERT( xStmRef.is(), "ImgProdLockBytes::WriteAt: xInputStream has no reference..." );
+ return ERRCODE_IO_CANTWRITE;
+ }
+}
+
+
+ErrCode ImgProdLockBytes::Flush() const
+{
+ return ERRCODE_NONE;
+}
+
+
+ErrCode ImgProdLockBytes::SetSize(sal_uInt64 const nSize)
+{
+ if( GetStream() )
+ return SvLockBytes::SetSize( nSize );
+ else
+ {
+ OSL_FAIL( "ImgProdLockBytes::SetSize not supported for xInputStream..." );
+ return ERRCODE_IO_CANTWRITE;
+ }
+}
+
+
+ErrCode ImgProdLockBytes::Stat( SvLockBytesStat* pStat ) const
+{
+ if( GetStream() )
+ return SvLockBytes::Stat( pStat );
+ else
+ {
+ DBG_ASSERT( xStmRef.is(), "ImgProdLockBytes::Stat: xInputStream has no reference..." );
+ pStat->nSize = maSeq.getLength();
+ return ERRCODE_NONE;
+ }
+}
+
+
+ImageProducer::ImageProducer()
+ : mnTransIndex(0)
+ , mbConsInit(false)
+{
+ moGraphic.emplace();
+}
+
+ImageProducer::~ImageProducer()
+{
+}
+
+
+// XInterface
+css::uno::Any ImageProducer::queryInterface( const css::uno::Type & rType )
+{
+ css::uno::Any aRet = ::cppu::queryInterface( rType,
+ static_cast< css::lang::XInitialization* >(this),
+ static_cast< css::lang::XServiceInfo* >(this),
+ static_cast< css::awt::XImageProducer* >(this) );
+ return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ));
+}
+
+
+void ImageProducer::addConsumer( const css::uno::Reference< css::awt::XImageConsumer >& rxConsumer )
+{
+ DBG_ASSERT( rxConsumer.is(), "::AddConsumer(...): No consumer referenced!" );
+ if( rxConsumer.is() )
+ maConsList.push_back( rxConsumer );
+}
+
+
+void ImageProducer::removeConsumer( const css::uno::Reference< css::awt::XImageConsumer >& rxConsumer )
+{
+ ConsumerList_t::reverse_iterator riter = std::find(maConsList.rbegin(),maConsList.rend(),rxConsumer);
+
+ if (riter != maConsList.rend())
+ maConsList.erase(riter.base()-1);
+}
+
+
+void ImageProducer::SetImage( const OUString& rPath )
+{
+ maURL = rPath;
+ moGraphic->Clear();
+ mbConsInit = false;
+ mpStm.reset();
+
+ if ( ::svt::GraphicAccess::isSupportedURL( maURL ) )
+ {
+ mpStm = ::svt::GraphicAccess::getImageStream( ::comphelper::getProcessComponentContext(), maURL );
+ }
+ else if( !maURL.isEmpty() )
+ {
+ std::unique_ptr<SvStream> pIStm = ::utl::UcbStreamHelper::CreateStream( maURL, StreamMode::STD_READ );
+ if (pIStm)
+ mpStm.reset( new SvStream( new ImgProdLockBytes( pIStm.release(), true ) ) );
+ }
+}
+
+
+void ImageProducer::SetImage( SvStream& rStm )
+{
+ maURL.clear();
+ moGraphic->Clear();
+ mbConsInit = false;
+
+ mpStm.reset( new SvStream( new ImgProdLockBytes( &rStm, false ) ) );
+}
+
+
+void ImageProducer::setImage( css::uno::Reference< css::io::XInputStream > const & rInputStmRef )
+{
+ maURL.clear();
+ moGraphic->Clear();
+ mbConsInit = false;
+ mpStm.reset();
+
+ if( rInputStmRef.is() )
+ mpStm.reset( new SvStream( new ImgProdLockBytes( rInputStmRef ) ) );
+}
+
+
+void ImageProducer::NewDataAvailable()
+{
+ if( ( GraphicType::NONE == moGraphic->GetType() ) || moGraphic->GetReaderContext() )
+ startProduction();
+}
+
+
+void ImageProducer::startProduction()
+{
+ if( maConsList.empty() && !maDoneHdl.IsSet() )
+ return;
+
+ bool bNotifyEmptyGraphics = false;
+
+ // valid stream or filled graphic? => update consumers
+ if( mpStm || ( moGraphic->GetType() != GraphicType::NONE ) )
+ {
+ // if we already have a graphic, we don't have to import again;
+ // graphic is cleared if a new Stream is set
+ if( ( moGraphic->GetType() == GraphicType::NONE ) || moGraphic->GetReaderContext() )
+ {
+ if ( ImplImportGraphic( *moGraphic ) )
+ maDoneHdl.Call( &*moGraphic );
+ }
+
+ if( moGraphic->GetType() != GraphicType::NONE )
+ ImplUpdateData( *moGraphic );
+ else
+ bNotifyEmptyGraphics = true;
+ }
+ else
+ bNotifyEmptyGraphics = true;
+
+ if ( !bNotifyEmptyGraphics )
+ return;
+
+ // reset image
+ // create temporary list to hold interfaces
+ ConsumerList_t aTmp = maConsList;
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ {
+ elem->init( 0, 0 );
+ elem->complete( css::awt::ImageStatus::IMAGESTATUS_STATICIMAGEDONE, this );
+ }
+
+ maDoneHdl.Call( nullptr );
+}
+
+
+bool ImageProducer::ImplImportGraphic( Graphic& rGraphic )
+{
+ if (!mpStm)
+ return false;
+
+ if( ERRCODE_IO_PENDING == mpStm->GetError() )
+ mpStm->ResetError();
+
+ mpStm->Seek( 0 );
+
+ bool bRet = GraphicConverter::Import( *mpStm, rGraphic ) == ERRCODE_NONE;
+
+ if( ERRCODE_IO_PENDING == mpStm->GetError() )
+ mpStm->ResetError();
+
+ return bRet;
+}
+
+
+void ImageProducer::ImplUpdateData( const Graphic& rGraphic )
+{
+ ImplInitConsumer( rGraphic );
+
+ if( mbConsInit && !maConsList.empty() )
+ {
+ // create temporary list to hold interfaces
+ ConsumerList_t aTmp = maConsList;
+
+ ImplUpdateConsumer( rGraphic );
+ mbConsInit = false;
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ elem->complete( css::awt::ImageStatus::IMAGESTATUS_STATICIMAGEDONE, this );
+ }
+}
+
+
+void ImageProducer::ImplInitConsumer( const Graphic& rGraphic )
+{
+ sal_uInt32 nRMask = 0;
+ sal_uInt32 nGMask = 0;
+ sal_uInt32 nBMask = 0;
+ sal_uInt32 nAMask = 0;
+ sal_uInt32 nWidth = 0;
+ sal_uInt32 nHeight = 0;
+ sal_uInt8 nBitCount = 0;
+ css::uno::Sequence< sal_Int32 > aRGBPal;
+ rGraphic.GetBitmapEx().GetColorModel(aRGBPal, nRMask, nGMask, nBMask, nAMask, mnTransIndex, nWidth, nHeight, nBitCount);
+
+ // create temporary list to hold interfaces
+ ConsumerList_t aTmp = maConsList;
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ {
+ elem->init( nWidth, nHeight );
+ elem->setColorModel( nBitCount,aRGBPal, nRMask, nGMask, nBMask, nAMask );
+ }
+
+ mbConsInit = true;
+}
+
+
+void ImageProducer::ImplUpdateConsumer( const Graphic& rGraphic )
+{
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+ Bitmap aBmp( aBmpEx.GetBitmap() );
+ BitmapScopedReadAccess pBmpAcc(aBmp);
+
+ if( !pBmpAcc )
+ return;
+
+ AlphaMask aMask( aBmpEx.GetAlphaMask() );
+ BitmapScopedReadAccess pMskAcc;
+ if (!aMask.IsEmpty())
+ pMskAcc = aMask;
+ const tools::Long nWidth = pBmpAcc->Width();
+ const tools::Long nHeight = pBmpAcc->Height();
+ const tools::Long nStartX = 0;
+ const tools::Long nEndX = nWidth - 1;
+ const tools::Long nStartY = 0;
+ const tools::Long nEndY = nHeight - 1;
+ const tools::Long nPartWidth = nEndX - nStartX + 1;
+ const tools::Long nPartHeight = nEndY - nStartY + 1;
+
+ if( !pMskAcc )
+ {
+ aMask = AlphaMask(aBmp.GetSizePixel());
+ aMask.Erase( 0 );
+ pMskAcc = aMask;
+ }
+
+ // create temporary list to hold interfaces
+ ConsumerList_t aTmp = maConsList;
+
+ if( pBmpAcc->HasPalette() )
+ {
+ const BitmapColor aWhite( pMskAcc->GetBestMatchingColor( COL_ALPHA_TRANSPARENT ) );
+
+ if( mnTransIndex < 256 )
+ {
+ css::uno::Sequence<sal_Int8> aData( nPartWidth * nPartHeight );
+ sal_Int8* pTmp = aData.getArray();
+
+ for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ Scanline pScanlineMask = pMskAcc->GetScanline( nY );
+ Scanline pScanline = pBmpAcc->GetScanline( nY );
+ for( tools::Long nX = nStartX; nX <= nEndX; nX++ )
+ {
+ if( pMskAcc->GetPixelFromData( pScanlineMask, nX ) == aWhite )
+ *pTmp++ = sal::static_int_cast< sal_Int8 >(
+ mnTransIndex );
+ else
+ *pTmp++ = pBmpAcc->GetPixelFromData( pScanline, nX ).GetIndex();
+ }
+ }
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ elem->setPixelsByBytes( nStartX, nStartY, nPartWidth, nPartHeight, aData, 0UL, nPartWidth );
+ }
+ else
+ {
+ css::uno::Sequence<sal_Int32> aData( nPartWidth * nPartHeight );
+ sal_Int32* pTmp = aData.getArray();
+
+ for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ Scanline pScanlineMask = pMskAcc->GetScanline( nY );
+ Scanline pScanline = pBmpAcc->GetScanline( nY );
+ for( tools::Long nX = nStartX; nX <= nEndX; nX++ )
+ {
+ if( pMskAcc->GetPixelFromData( pScanlineMask, nX ) == aWhite )
+ *pTmp++ = mnTransIndex;
+ else
+ *pTmp++ = pBmpAcc->GetPixelFromData( pScanline, nX ).GetIndex();
+ }
+ }
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ elem->setPixelsByLongs( nStartX, nStartY, nPartWidth, nPartHeight, aData, 0UL, nPartWidth );
+ }
+ }
+ else
+ {
+ css::uno::Sequence<sal_Int32> aData( nPartWidth * nPartHeight );
+ const BitmapColor aWhite( pMskAcc->GetBestMatchingColor( COL_WHITE ) );
+ sal_Int32* pTmp = aData.getArray();
+
+ for( tools::Long nY = nStartY; nY <= nEndY; nY++ )
+ {
+ Scanline pScanlineMask = pMskAcc->GetScanline( nY );
+ Scanline pScanline = pBmpAcc->GetScanline( nY );
+ for( tools::Long nX = nStartX; nX <= nEndX; nX++, pTmp++ )
+ {
+ const BitmapColor aCol( pBmpAcc->GetPixelFromData( pScanline, nX ) );
+
+ *pTmp = static_cast<sal_Int32>(aCol.GetRed()) << 24;
+ *pTmp |= static_cast<sal_Int32>(aCol.GetGreen()) << 16;
+ *pTmp |= static_cast<sal_Int32>(aCol.GetBlue()) << 8;
+
+ if( pMskAcc->GetPixelFromData( pScanlineMask, nX ) != aWhite )
+ *pTmp |= 0x000000ffUL;
+ }
+ }
+
+ // iterate through interfaces
+ for (auto const& elem : aTmp)
+ elem->setPixelsByLongs( nStartX, nStartY, nPartWidth, nPartHeight, aData, 0UL, nPartWidth );
+ }
+}
+
+
+void ImageProducer::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ if ( aArguments.getLength() == 1 )
+ {
+ css::uno::Any aArg = aArguments.getConstArray()[0];
+ OUString aURL;
+ if ( aArg >>= aURL )
+ {
+ SetImage( aURL );
+ }
+ }
+}
+
+OUString ImageProducer::getImplementationName() {
+ return "com.sun.star.form.ImageProducer";
+}
+
+sal_Bool ImageProducer::supportsService(OUString const & ServiceName) {
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> ImageProducer::getSupportedServiceNames() {
+ return {"com.sun.star.awt.ImageProducer"};
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_ImageProducer_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ImageProducer());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/imgprod.hxx b/forms/source/component/imgprod.hxx
new file mode 100644
index 0000000000..bfcb2c66b0
--- /dev/null
+++ b/forms/source/component/imgprod.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/link.hxx>
+#include <com/sun/star/awt/XImageConsumer.hpp>
+#include <com/sun/star/awt/XImageProducer.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/weak.hxx>
+#include <vcl/graph.hxx>
+#include <memory>
+#include <optional>
+#include <vector>
+
+
+class SvStream;
+namespace com::sun::star::io { class XInputStream; }
+
+
+class ImageProducer : public css::awt::XImageProducer,
+ public css::lang::XInitialization,
+ public css::lang::XServiceInfo,
+ public ::cppu::OWeakObject
+{
+private:
+
+ typedef std::vector< css::uno::Reference< css::awt::XImageConsumer > > ConsumerList_t;
+
+ OUString maURL;
+ ConsumerList_t maConsList;
+ std::optional<Graphic>
+ moGraphic;
+ std::unique_ptr<SvStream>
+ mpStm;
+ sal_uInt32 mnTransIndex;
+ bool mbConsInit;
+ Link<Graphic*,void> maDoneHdl;
+
+ bool ImplImportGraphic( Graphic& rGraphic );
+ void ImplUpdateData( const Graphic& rGraphic );
+ void ImplInitConsumer( const Graphic& rGraphic );
+ void ImplUpdateConsumer( const Graphic& rGraphic );
+
+public:
+
+ ImageProducer();
+ virtual ~ImageProducer() override;
+
+ void SetImage( const OUString& rPath );
+ void SetImage( SvStream& rStm );
+
+ void NewDataAvailable();
+
+ void SetDoneHdl( const Link<Graphic*,void>& i_rHdl ) { maDoneHdl = i_rHdl; }
+
+ // css::uno::XInterface
+ css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); }
+ void SAL_CALL release() noexcept override { OWeakObject::release(); }
+
+ // MT: ???
+ void setImage( css::uno::Reference< css::io::XInputStream > const & rStmRef );
+
+ // css::awt::XImageProducer
+ void SAL_CALL addConsumer( const css::uno::Reference< css::awt::XImageConsumer >& rxConsumer ) override;
+ void SAL_CALL removeConsumer( const css::uno::Reference< css::awt::XImageConsumer >& rxConsumer ) override;
+ void SAL_CALL startProduction( ) override;
+
+ // css::lang::XInitialization
+ void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/navigationbar.cxx b/forms/source/component/navigationbar.cxx
new file mode 100644
index 0000000000..6209952b6a
--- /dev/null
+++ b/forms/source/component/navigationbar.cxx
@@ -0,0 +1,487 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "navigationbar.hxx"
+#include <property.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+
+#include <comphelper/streamsection.hxx>
+#include <comphelper/basicio.hxx>
+#include <tools/debug.hxx>
+
+using namespace comphelper;
+
+namespace frm
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ 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::container;
+ using namespace ::comphelper;
+
+ namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+
+ ONavigationBarModel::ONavigationBarModel( const Reference< XComponentContext >& _rxFactory )
+ :OControlModel( _rxFactory, OUString() )
+ ,FontControlModel( true )
+ {
+
+ m_nClassId = FormComponentType::NAVIGATIONBAR;
+ implInitPropertyContainer();
+
+ getPropertyDefaultByHandle( PROPERTY_ID_DEFAULTCONTROL ) >>= m_sDefaultControl;
+ getPropertyDefaultByHandle( PROPERTY_ID_ICONSIZE ) >>= m_nIconSize;
+ getPropertyDefaultByHandle( PROPERTY_ID_BORDER ) >>= m_nBorder;
+ getPropertyDefaultByHandle( PROPERTY_ID_DELAY ) >>= m_nDelay;
+ getPropertyDefaultByHandle( PROPERTY_ID_ENABLED ) >>= m_bEnabled;
+ getPropertyDefaultByHandle( PROPERTY_ID_ENABLEVISIBLE ) >>= m_bEnableVisible;
+ getPropertyDefaultByHandle( PROPERTY_ID_SHOW_POSITION ) >>= m_bShowPosition;
+ getPropertyDefaultByHandle( PROPERTY_ID_SHOW_NAVIGATION ) >>= m_bShowNavigation;
+ getPropertyDefaultByHandle( PROPERTY_ID_SHOW_RECORDACTIONS ) >>= m_bShowActions;
+ getPropertyDefaultByHandle( PROPERTY_ID_SHOW_FILTERSORT ) >>= m_bShowFilterSort;
+ getPropertyDefaultByHandle( PROPERTY_ID_WRITING_MODE ) >>= m_nWritingMode;
+ getPropertyDefaultByHandle( PROPERTY_ID_CONTEXT_WRITING_MODE ) >>= m_nContextWritingMode;
+ }
+
+
+ ONavigationBarModel::ONavigationBarModel( const ONavigationBarModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OControlModel( _pOriginal, _rxFactory )
+ ,FontControlModel( _pOriginal )
+ {
+
+ implInitPropertyContainer();
+
+ m_aTabStop = _pOriginal->m_aTabStop;
+ m_aBackgroundColor = _pOriginal->m_aBackgroundColor;
+ m_sDefaultControl = _pOriginal->m_sDefaultControl;
+ m_sHelpText = _pOriginal->m_sHelpText;
+ m_sHelpURL = _pOriginal->m_sHelpURL;
+ m_bEnabled = _pOriginal->m_bEnabled;
+ m_bEnableVisible = _pOriginal->m_bEnableVisible;
+ m_nIconSize = _pOriginal->m_nIconSize;
+ m_nBorder = _pOriginal->m_nBorder;
+ m_nDelay = _pOriginal->m_nDelay;
+ m_bShowPosition = _pOriginal->m_bShowPosition;
+ m_bShowNavigation = _pOriginal->m_bShowNavigation;
+ m_bShowActions = _pOriginal->m_bShowActions;
+ m_bShowFilterSort = _pOriginal->m_bShowFilterSort;
+ m_nWritingMode = _pOriginal->m_nWritingMode;
+ m_nContextWritingMode = _pOriginal->m_nContextWritingMode;
+ }
+
+
+ void ONavigationBarModel::implInitPropertyContainer()
+ {
+ registerProperty( PROPERTY_DEFAULTCONTROL, PROPERTY_ID_DEFAULTCONTROL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sDefaultControl, cppu::UnoType<decltype(m_sDefaultControl)>::get() );
+ registerProperty( PROPERTY_HELPTEXT, PROPERTY_ID_HELPTEXT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sHelpText, cppu::UnoType<decltype(m_sHelpText)>::get() );
+ registerProperty( PROPERTY_HELPURL, PROPERTY_ID_HELPURL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sHelpURL, cppu::UnoType<decltype(m_sHelpURL)>::get() );
+ registerProperty( PROPERTY_ENABLED, PROPERTY_ID_ENABLED, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bEnabled, cppu::UnoType<decltype(m_bEnabled)>::get() );
+ registerProperty( PROPERTY_ENABLEVISIBLE, PROPERTY_ID_ENABLEVISIBLE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bEnableVisible, cppu::UnoType<decltype(m_bEnableVisible)>::get() );
+ registerProperty( PROPERTY_ICONSIZE, PROPERTY_ID_ICONSIZE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nIconSize, cppu::UnoType<decltype(m_nIconSize)>::get() );
+ registerProperty( PROPERTY_BORDER, PROPERTY_ID_BORDER, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nBorder, cppu::UnoType<decltype(m_nBorder)>::get() );
+ registerProperty( PROPERTY_DELAY, PROPERTY_ID_DELAY, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nDelay, cppu::UnoType<decltype(m_nDelay)>::get() );
+ registerProperty( PROPERTY_SHOW_POSITION, PROPERTY_ID_SHOW_POSITION, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bShowPosition, cppu::UnoType<decltype(m_bShowPosition)>::get() );
+ registerProperty( PROPERTY_SHOW_NAVIGATION, PROPERTY_ID_SHOW_NAVIGATION, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bShowNavigation, cppu::UnoType<decltype(m_bShowNavigation)>::get() );
+ registerProperty( PROPERTY_SHOW_RECORDACTIONS, PROPERTY_ID_SHOW_RECORDACTIONS, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bShowActions, cppu::UnoType<decltype(m_bShowActions)>::get() );
+ registerProperty( PROPERTY_SHOW_FILTERSORT, PROPERTY_ID_SHOW_FILTERSORT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bShowFilterSort, cppu::UnoType<decltype(m_bShowFilterSort)>::get() );
+ registerProperty( PROPERTY_WRITING_MODE, PROPERTY_ID_WRITING_MODE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nWritingMode, cppu::UnoType<decltype(m_nWritingMode)>::get() );
+
+ registerProperty( PROPERTY_CONTEXT_WRITING_MODE, PROPERTY_ID_CONTEXT_WRITING_MODE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT | PropertyAttribute::TRANSIENT,
+ &m_nContextWritingMode, cppu::UnoType<decltype(m_nContextWritingMode)>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_TABSTOP, PROPERTY_ID_TABSTOP, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT | PropertyAttribute::MAYBEVOID,
+ &m_aTabStop, cppu::UnoType<sal_Bool>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_BACKGROUNDCOLOR, PROPERTY_ID_BACKGROUNDCOLOR, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT | PropertyAttribute::MAYBEVOID,
+ &m_aBackgroundColor, cppu::UnoType<sal_Int32>::get() );
+ }
+
+
+ ONavigationBarModel::~ONavigationBarModel()
+ {
+ if ( !OComponentHelper::rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+
+ }
+
+
+ Any SAL_CALL ONavigationBarModel::queryAggregation( const Type& _rType )
+ {
+ Any aReturn = ONavigationBarModel_BASE::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = OControlModel::queryAggregation( _rType );
+
+ return aReturn;
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( ONavigationBarModel, OControlModel, ONavigationBarModel_BASE )
+
+
+ css::uno::Reference< css::util::XCloneable > SAL_CALL ONavigationBarModel::createClone()
+{
+ rtl::Reference<ONavigationBarModel> pClone = new ONavigationBarModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+ OUString SAL_CALL ONavigationBarModel::getImplementationName()
+ {
+ return "com.sun.star.comp.form.ONavigationBarModel";
+ }
+
+
+ Sequence< OUString > SAL_CALL ONavigationBarModel::getSupportedServiceNames()
+ {
+ Sequence< OUString > aSupported = OControlModel::getSupportedServiceNames_Static();
+ aSupported.realloc( aSupported.getLength() + 2 );
+
+ OUString* pArray = aSupported.getArray();
+ pArray[ aSupported.getLength() - 2 ] = "com.sun.star.awt.UnoControlModel";
+ pArray[ aSupported.getLength() - 1 ] = FRM_SUN_COMPONENT_NAVTOOLBAR;
+ return aSupported;
+ }
+
+ OUString SAL_CALL ONavigationBarModel::getServiceName()
+ {
+ return FRM_SUN_COMPONENT_NAVTOOLBAR;
+ }
+
+ #define PERSIST_TABSTOP 0x0001
+ #define PERSIST_BACKGROUND 0x0002
+ #define PERSIST_TEXTCOLOR 0x0004
+ #define PERSIST_TEXTLINECOLOR 0x0008
+
+ #define PERSIST_ENABLED 0x0001
+ #define PERSIST_LARGEICONS 0x0002
+ // leaf a leap here - this will allow for two more icon size values to be stored compatibly
+ #define PERSIST_SHOW_POSITION 0x0008
+ #define PERSIST_SHOW_NAVIGATION 0x0010
+ #define PERSIST_SHOW_ACTIONS 0x0020
+ #define PERSIST_SHOW_FILTERSORT 0x0040
+
+
+ void SAL_CALL ONavigationBarModel::write( const Reference< XObjectOutputStream >& _rxOutStream )
+ {
+ // open a section for compatibility - if we later on write additional members,
+ // then older versions can skip them
+ OStreamSection aEnsureBlockCompat( _rxOutStream );
+
+ // base class
+ OControlModel::write( _rxOutStream );
+
+ {
+ OStreamSection aEnsureCompat( _rxOutStream );
+ // determine which properties are not void and need to be written
+ sal_Int32 nNonVoids = 0;
+ if ( m_aTabStop.hasValue() )
+ nNonVoids |= PERSIST_TABSTOP;
+ if ( m_aBackgroundColor.hasValue() )
+ nNonVoids |= PERSIST_BACKGROUND;
+ if ( hasTextColor() )
+ nNonVoids |= PERSIST_TEXTCOLOR;
+ if ( hasTextLineColor() )
+ nNonVoids |= PERSIST_TEXTLINECOLOR;
+
+ _rxOutStream->writeLong( nNonVoids );
+
+ // the maybeboid anys
+ if ( nNonVoids & PERSIST_TABSTOP )
+ {
+ bool bTabStop( false );
+ m_aTabStop >>= bTabStop;
+ _rxOutStream->writeBoolean( bTabStop );
+ }
+ if ( nNonVoids & PERSIST_BACKGROUND )
+ {
+ sal_Int32 nBackgroundColor = 0;
+ m_aBackgroundColor >>= nBackgroundColor;
+ _rxOutStream->writeLong( nBackgroundColor );
+ }
+ if ( nNonVoids & PERSIST_TEXTCOLOR )
+ {
+ _rxOutStream->writeLong( sal_Int32(getTextColor()) );
+ }
+ if ( nNonVoids & PERSIST_TEXTLINECOLOR )
+ {
+ _rxOutStream->writeLong( sal_Int32(getTextLineColor()) );
+ }
+ }
+
+ {
+ OStreamSection aEnsureCompat( _rxOutStream );
+ ::comphelper::operator<<( _rxOutStream, getFont() );
+ }
+
+ // our boolean flags
+ sal_Int32 nFlags = 0;
+ if ( m_bEnabled ) nFlags |= PERSIST_ENABLED;
+ if ( m_nIconSize ) nFlags |= PERSIST_LARGEICONS; // at the moment, this is quasi boolean
+ if ( m_bShowPosition ) nFlags |= PERSIST_SHOW_POSITION;
+ if ( m_bShowNavigation ) nFlags |= PERSIST_SHOW_NAVIGATION;
+ if ( m_bShowActions ) nFlags |= PERSIST_SHOW_ACTIONS;
+ if ( m_bShowFilterSort ) nFlags |= PERSIST_SHOW_FILTERSORT;
+ _rxOutStream->writeLong( nFlags );
+
+ // our strings
+ _rxOutStream->writeUTF( m_sHelpText );
+ _rxOutStream->writeUTF( m_sHelpURL );
+ _rxOutStream->writeUTF( m_sDefaultControl );
+
+ // misc
+ _rxOutStream->writeShort( m_nBorder );
+ _rxOutStream->writeLong ( m_nDelay );
+ }
+
+
+ void SAL_CALL ONavigationBarModel::read( const Reference< XObjectInputStream >& _rxInStream )
+ {
+ OStreamSection aEnsureBlockCompat( _rxInStream );
+
+ // base class
+ OControlModel::read( _rxInStream );
+
+ {
+ OStreamSection aEnsureCompat( _rxInStream );
+ // determine which properties were non-void
+ sal_Int32 nNonVoids = _rxInStream->readLong( );
+
+ // the maybeboid anys
+ if ( nNonVoids & PERSIST_TABSTOP )
+ m_aTabStop <<= _rxInStream->readBoolean();
+ else
+ m_aTabStop.clear();
+
+ if ( nNonVoids & PERSIST_BACKGROUND )
+ m_aBackgroundColor <<= _rxInStream->readLong();
+ else
+ m_aBackgroundColor.clear();
+
+ if ( nNonVoids & PERSIST_TEXTCOLOR )
+ setTextColor( ::Color(ColorTransparency, _rxInStream->readLong()) );
+ else
+ clearTextColor();
+
+ if ( nNonVoids & PERSIST_TEXTLINECOLOR )
+ setTextLineColor( ::Color(ColorTransparency, _rxInStream->readLong()) );
+ else
+ clearTextLineColor();
+ }
+
+ {
+ OStreamSection aEnsureCompat( _rxInStream );
+ FontDescriptor aFont;
+ ::comphelper::operator>>( _rxInStream, aFont );
+ setFont( aFont );
+ }
+
+ // our boolean flags
+ sal_Int32 nFlags = _rxInStream->readLong( );
+ m_bEnabled = ( nFlags & PERSIST_ENABLED ) != 0;
+ m_nIconSize = ( nFlags & PERSIST_LARGEICONS ) ? 1 : 0;
+ m_bShowPosition = ( nFlags & PERSIST_SHOW_POSITION ) != 0;
+ m_bShowNavigation = ( nFlags & PERSIST_SHOW_NAVIGATION ) != 0;
+ m_bShowActions = ( nFlags & PERSIST_SHOW_ACTIONS ) != 0;
+ m_bShowFilterSort = ( nFlags & PERSIST_SHOW_FILTERSORT ) != 0;
+
+ // our strings
+ m_sHelpText = _rxInStream->readUTF( );
+ m_sHelpURL = _rxInStream->readUTF( );
+ m_sDefaultControl = _rxInStream->readUTF( );
+
+ // misc
+ m_nBorder = _rxInStream->readShort();
+ m_nDelay = _rxInStream->readLong();
+ }
+
+
+ void SAL_CALL ONavigationBarModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ if ( isRegisteredProperty( _nHandle ) )
+ {
+ OPropertyContainerHelper::getFastPropertyValue( _rValue, _nHandle );
+ }
+ else if ( isFontRelatedProperty( _nHandle ) )
+ {
+ FontControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ else
+ {
+ OControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ }
+
+
+ sal_Bool SAL_CALL ONavigationBarModel::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue,
+ sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified = false;
+
+ if ( isRegisteredProperty( _nHandle ) )
+ {
+ bModified = OPropertyContainerHelper::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ }
+ else if ( isFontRelatedProperty( _nHandle ) )
+ {
+ bModified = FontControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ }
+ else
+ {
+ bModified = OControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ }
+
+ return bModified;
+ }
+
+
+ void SAL_CALL ONavigationBarModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ if ( isRegisteredProperty( _nHandle ) )
+ {
+ OPropertyContainerHelper::setFastPropertyValue( _nHandle, _rValue );
+ }
+ else if ( isFontRelatedProperty( _nHandle ) )
+ {
+ FontControlModel::setFastPropertyValue_NoBroadcast_impl(
+ *this, &ONavigationBarModel::setDependentFastPropertyValue,
+ _nHandle, _rValue);
+ }
+ else
+ {
+ OControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ }
+ }
+
+
+ Any ONavigationBarModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aDefault;
+
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_TABSTOP:
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ /* void */
+ break;
+ case PROPERTY_ID_WRITING_MODE:
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ aDefault <<= WritingMode2::CONTEXT;
+ break;
+
+ case PROPERTY_ID_ENABLED:
+ case PROPERTY_ID_ENABLEVISIBLE:
+ case PROPERTY_ID_SHOW_POSITION:
+ case PROPERTY_ID_SHOW_NAVIGATION:
+ case PROPERTY_ID_SHOW_RECORDACTIONS:
+ case PROPERTY_ID_SHOW_FILTERSORT:
+ aDefault <<= true;
+ break;
+
+ case PROPERTY_ID_ICONSIZE:
+ aDefault <<= sal_Int16(0);
+ break;
+
+ case PROPERTY_ID_DEFAULTCONTROL:
+ aDefault <<= OUString( "com.sun.star.form.control.NavigationToolBar" );
+ break;
+
+ case PROPERTY_ID_HELPTEXT:
+ case PROPERTY_ID_HELPURL:
+ aDefault <<= OUString();
+ break;
+
+ case PROPERTY_ID_BORDER:
+ aDefault <<= sal_Int16(0);
+ break;
+
+ case PROPERTY_ID_DELAY:
+ aDefault <<= sal_Int32(20);
+ break;
+
+ default:
+ if ( isFontRelatedProperty( _nHandle ) )
+ aDefault = FontControlModel::getPropertyDefaultByHandle( _nHandle );
+ else
+ aDefault = OControlModel::getPropertyDefaultByHandle( _nHandle );
+ }
+ return aDefault;
+ }
+
+
+ void ONavigationBarModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OControlModel::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<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+
+ // properties which the OPropertyContainerHelper is responsible for
+ Sequence< Property > aContainedProperties;
+ describeProperties( aContainedProperties );
+
+ // properties which the FontControlModel is responsible for
+ Sequence< Property > aFontProperties;
+ describeFontRelatedProperties( aFontProperties );
+
+ _rProps = concatSequences(
+ aContainedProperties,
+ aFontProperties,
+ _rProps
+ );
+ }
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_form_ONavigationBarModel_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ONavigationBarModel(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/navigationbar.hxx b/forms/source/component/navigationbar.hxx
new file mode 100644
index 0000000000..ceb84dbe8e
--- /dev/null
+++ b/forms/source/component/navigationbar.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include <comphelper/propertycontainerhelper.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <formcontrolfont.hxx>
+
+
+using namespace comphelper;
+
+namespace frm
+{
+
+ typedef ::cppu::ImplHelper1 < css::awt::XControlModel
+ > ONavigationBarModel_BASE;
+
+ class ONavigationBarModel
+ :public OControlModel
+ ,public FontControlModel
+ ,public OPropertyContainerHelper
+ ,public ONavigationBarModel_BASE
+ {
+ // <properties>
+ css::uno::Any m_aTabStop;
+ css::uno::Any m_aBackgroundColor;
+ OUString m_sDefaultControl;
+ OUString m_sHelpText;
+ OUString m_sHelpURL;
+ sal_Int16 m_nIconSize;
+ sal_Int16 m_nBorder;
+ sal_Int32 m_nDelay;
+ bool m_bEnabled;
+ bool m_bEnableVisible;
+ bool m_bShowPosition;
+ bool m_bShowNavigation;
+ bool m_bShowActions;
+ bool m_bShowFilterSort;
+ sal_Int16 m_nWritingMode;
+ sal_Int16 m_nContextWritingMode;
+ // </properties>
+
+ public:
+ ONavigationBarModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ ONavigationBarModel(
+ const ONavigationBarModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~ONavigationBarModel() override;
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS( ONavigationBarModel, OControlModel )
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+
+ // 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;
+
+ // XPropertySet
+ 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;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+ // prevent method hiding
+ using OControlModel::disposing;
+ using OControlModel::getFastPropertyValue;
+
+ protected:
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ private:
+ void implInitPropertyContainer();
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/propertybaghelper.cxx b/forms/source/component/propertybaghelper.cxx
new file mode 100644
index 0000000000..2bfcca91a3
--- /dev/null
+++ b/forms/source/component/propertybaghelper.cxx
@@ -0,0 +1,338 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <propertybaghelper.hxx>
+
+#include <property.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/NotRemoveableException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+
+#include <comphelper/diagnose_ex.hxx>
+
+#include <comphelper/sequence.hxx>
+
+
+#define NEW_HANDLE_BASE 10000
+
+
+namespace frm
+{
+
+
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::beans::PropertyExistException;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::beans::XMultiPropertySet;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::beans::NotRemoveableException;
+ using ::com::sun::star::beans::UnknownPropertyException;
+
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+
+
+ //= helper
+
+ namespace
+ {
+
+ ::comphelper::IPropertyInfoService& lcl_getPropertyInfos()
+ {
+ static ConcreteInfoService s_aPropInfos;
+ return s_aPropInfos;
+ }
+ }
+
+ PropertyBagHelper::PropertyBagHelper( IPropertyBagHelperContext& _rContext )
+ :m_rContext( _rContext )
+ ,m_bDisposed( false )
+ {
+ }
+
+
+ PropertyBagHelper::~PropertyBagHelper()
+ {
+ }
+
+
+ void PropertyBagHelper::dispose()
+ {
+ m_bDisposed = true;
+ }
+
+
+ void PropertyBagHelper::impl_nts_checkDisposed_throw() const
+ {
+ if ( m_bDisposed )
+ throw DisposedException();
+ }
+
+
+ void PropertyBagHelper::impl_nts_invalidatePropertySetInfo()
+ {
+ m_pPropertyArrayHelper.reset();
+ }
+
+
+ sal_Int32 PropertyBagHelper::impl_findFreeHandle( const OUString& _rPropertyName )
+ {
+ ::comphelper::OPropertyArrayAggregationHelper& rPropInfo( impl_ts_getArrayHelper() );
+
+ // check the preferred handle
+ sal_Int32 nHandle = lcl_getPropertyInfos().getPreferredPropertyId( _rPropertyName );
+ if ( ( nHandle != -1 ) && rPropInfo.fillPropertyMembersByHandle( nullptr, nullptr, nHandle ) )
+ nHandle = -1;
+
+ // search a free handle in <math>F_1009</math>
+ if ( nHandle == -1 )
+ {
+ sal_Int32 const nPrime = 1009;
+ sal_Int32 nFactor = 11;
+ sal_Int32 nNum = nFactor;
+ while ( nNum != 1 )
+ {
+ if ( !rPropInfo.fillPropertyMembersByHandle( nullptr, nullptr, nNum + NEW_HANDLE_BASE ) )
+ {
+ // handle not used, yet
+ nHandle = nNum + NEW_HANDLE_BASE;
+ break;
+ }
+ nNum = ( nNum * nFactor ) % nPrime;
+ }
+ }
+
+ // search a free handle greater NEW_HANDLE_BASE
+ if ( nHandle == -1 )
+ {
+ nHandle = NEW_HANDLE_BASE + 1009;
+ while ( rPropInfo.fillPropertyMembersByHandle( nullptr, nullptr, nHandle ) )
+ ++nHandle;
+ }
+
+ return nHandle;
+ }
+
+
+ ::comphelper::OPropertyArrayAggregationHelper& PropertyBagHelper::impl_ts_getArrayHelper() const
+ {
+ OPropertyArrayAggregationHelper* p = m_pPropertyArrayHelper.get();
+ if ( !p )
+ {
+ ::osl::MutexGuard aGuard( m_rContext.getMutex() );
+ p = m_pPropertyArrayHelper.get();
+ if ( !p )
+ {
+ // our own fixed and our aggregate's properties
+ Sequence< Property > aFixedProps;
+ Sequence< Property > aAggregateProps;
+ m_rContext.describeFixedAndAggregateProperties( aFixedProps, aAggregateProps );
+
+ // our dynamic properties
+ Sequence< Property > aDynamicProps;
+ m_aDynamicProperties.describeProperties( aDynamicProps );
+
+ Sequence< Property > aOwnProps(
+ ::comphelper::concatSequences( aFixedProps, aDynamicProps ) );
+
+ p = new OPropertyArrayAggregationHelper( aOwnProps, aAggregateProps, &lcl_getPropertyInfos(), NEW_HANDLE_BASE );
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ const_cast< PropertyBagHelper* >( this )->m_pPropertyArrayHelper.reset( p );
+ }
+ } // if ( !p )
+ else
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+ return *p;
+ }
+
+
+ void PropertyBagHelper::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
+ {
+ ::osl::MutexGuard aGuard( m_rContext.getMutex() );
+ impl_nts_checkDisposed_throw();
+
+
+ // check name sanity
+ ::comphelper::OPropertyArrayAggregationHelper& aPropInfo( impl_ts_getArrayHelper() );
+ if ( aPropInfo.hasPropertyByName( _rName ) )
+ throw PropertyExistException( _rName, m_rContext.getPropertiesInterface() );
+
+
+ // normalize the REMOVABLE attribute - the FormComponent service
+ // requires that all dynamic properties are REMOVABLE
+ _nAttributes |= PropertyAttribute::REMOVABLE;
+
+
+ // find a free handle
+ sal_Int32 nHandle = impl_findFreeHandle( _rName );
+
+
+ // register the property, and invalidate our property meta data
+ m_aDynamicProperties.addProperty( _rName, nHandle, _nAttributes, _rInitialValue );
+ impl_nts_invalidatePropertySetInfo();
+ }
+
+
+ void PropertyBagHelper::removeProperty( const OUString& _rName )
+ {
+ ::osl::MutexGuard aGuard( m_rContext.getMutex() );
+ impl_nts_checkDisposed_throw();
+
+ // check whether it's removable at all
+ Reference< XMultiPropertySet > xMe( m_rContext.getPropertiesInterface(), css::uno::UNO_SET_THROW );
+ Reference< XPropertySetInfo > xPSI( xMe->getPropertySetInfo(), css::uno::UNO_SET_THROW );
+ Property aProperty( xPSI->getPropertyByName( _rName ) );
+ if ( ( aProperty.Attributes & PropertyAttribute::REMOVABLE ) == 0 )
+ throw NotRemoveableException( _rName, xMe );
+
+ m_aDynamicProperties.removeProperty( _rName );
+ impl_nts_invalidatePropertySetInfo();
+ }
+
+
+ namespace
+ {
+
+ struct SelectNameOfProperty
+ {
+ const OUString& operator()( const Property& _rProp ) const { return _rProp.Name; }
+ };
+
+
+ struct SelectNameOfPropertyValue
+ {
+ const OUString& operator()( const PropertyValue& _rProp ) const { return _rProp.Name; }
+ };
+
+
+ struct SelectValueOfPropertyValue
+ {
+ const Any& operator()( const PropertyValue& _rProp ) const { return _rProp.Value; }
+ };
+
+
+ struct PropertyValueLessByName
+ {
+ bool operator()( const PropertyValue& _lhs, const PropertyValue& _rhs ) const
+ {
+ return _lhs.Name < _rhs.Name;
+ }
+ };
+ }
+
+
+ Sequence< PropertyValue > PropertyBagHelper::getPropertyValues()
+ {
+ ::osl::MutexGuard aGuard( m_rContext.getMutex() );
+ impl_nts_checkDisposed_throw();
+
+ Reference< XMultiPropertySet > xMe( m_rContext.getPropertiesInterface(), css::uno::UNO_SET_THROW );
+ Reference< XPropertySetInfo > xPSI( xMe->getPropertySetInfo(), css::uno::UNO_SET_THROW );
+
+ const Sequence< Property > aProperties( xPSI->getProperties() );
+ Sequence< OUString > aPropertyNames( aProperties.getLength() );
+ ::std::transform( aProperties.begin(), aProperties.end(),
+ aPropertyNames.getArray(), SelectNameOfProperty() );
+
+ Sequence< Any > aValues;
+ try
+ {
+ aValues = xMe->getPropertyValues( aPropertyNames );
+
+ if ( aValues.getLength() != aPropertyNames.getLength() )
+ throw RuntimeException();
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ Sequence< PropertyValue > aPropertyValues( aValues.getLength() );
+ PropertyValue* pPropertyValue = aPropertyValues.getArray();
+
+ const OUString* pName = aPropertyNames.getConstArray();
+ const OUString* pNameEnd = aPropertyNames.getConstArray() + aPropertyNames.getLength();
+ const Any* pValue = aValues.getConstArray();
+ for ( ; pName != pNameEnd; ++pName, ++pValue, ++pPropertyValue )
+ {
+ pPropertyValue->Name = *pName;
+ pPropertyValue->Value = *pValue;
+ }
+
+ return aPropertyValues;
+ }
+
+
+ void PropertyBagHelper::setPropertyValues( const Sequence< PropertyValue >& _rProps )
+ {
+ ::osl::ClearableMutexGuard aGuard( m_rContext.getMutex() );
+ impl_nts_checkDisposed_throw();
+
+ sal_Int32 nPropertyValues = _rProps.getLength();
+
+ // XMultiPropertySet::setPropertyValues expects its arguments to be sorted by name
+ // while XPropertyAccess::setPropertyValues doesn't. So first of all, sort.
+ Sequence< PropertyValue > aSortedProps( _rProps );
+ ::std::sort( aSortedProps.getArray(), aSortedProps.getArray() + nPropertyValues, PropertyValueLessByName() );
+
+ // also, XPropertyAccess::setPropertyValues is expected to throw an UnknownPropertyException
+ // for unsupported properties, while XMultiPropertySet::setPropertyValues is expected to ignore
+ // those. So, check for unsupported properties first.
+ ::comphelper::OPropertyArrayAggregationHelper& rArrayHelper( impl_ts_getArrayHelper() );
+ for ( const PropertyValue* pProperties = aSortedProps.getConstArray();
+ pProperties != aSortedProps.getConstArray() + nPropertyValues;
+ ++pProperties
+ )
+ {
+ if ( !rArrayHelper.hasPropertyByName( pProperties->Name ) )
+ throw UnknownPropertyException( pProperties->Name, m_rContext.getPropertiesInterface() );
+ }
+
+ // Now finally split into a Name and a Value sequence, and forward to
+ // XMultiPropertySet::setPropertyValues
+ Sequence< OUString > aNames( nPropertyValues );
+ ::std::transform( aSortedProps.getConstArray(), aSortedProps.getConstArray() + nPropertyValues,
+ aNames.getArray(), SelectNameOfPropertyValue() );
+
+ Sequence< Any > aValues( nPropertyValues );
+ ::std::transform( aSortedProps.getConstArray(), aSortedProps.getConstArray() + nPropertyValues,
+ aValues.getArray(), SelectValueOfPropertyValue() );
+
+ Reference< XMultiPropertySet > xMe( m_rContext.getPropertiesInterface(), css::uno::UNO_SET_THROW );
+
+ aGuard.clear();
+ xMe->setPropertyValues( aNames, aValues );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/refvaluecomponent.cxx b/forms/source/component/refvaluecomponent.cxx
new file mode 100644
index 0000000000..b0ff7e3803
--- /dev/null
+++ b/forms/source/component/refvaluecomponent.cxx
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "refvaluecomponent.hxx"
+#include <property.hxx>
+
+#include <comphelper/property.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <vector>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form::binding;
+
+
+ //=
+
+
+ OReferenceValueComponent::OReferenceValueComponent( const Reference< XComponentContext >& _rxFactory, const OUString& _rUnoControlModelTypeName, const OUString& _rDefault )
+ :OBoundControlModel( _rxFactory, _rUnoControlModelTypeName, _rDefault, false, true, true )
+ ,m_eDefaultChecked( TRISTATE_FALSE )
+ {
+ }
+
+
+ OReferenceValueComponent::OReferenceValueComponent( const OReferenceValueComponent* _pOriginal, const Reference< XComponentContext>& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ {
+ m_sReferenceValue = _pOriginal->m_sReferenceValue;
+ m_sNoCheckReferenceValue = _pOriginal->m_sNoCheckReferenceValue;
+ m_eDefaultChecked = _pOriginal->m_eDefaultChecked;
+
+ calculateExternalValueType();
+ }
+
+
+ OReferenceValueComponent::~OReferenceValueComponent()
+ {
+ }
+
+
+ void OReferenceValueComponent::setReferenceValue( const OUString& _rRefValue )
+ {
+ m_sReferenceValue = _rRefValue;
+ calculateExternalValueType();
+ }
+
+
+ void SAL_CALL OReferenceValueComponent::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_REFVALUE: _rValue <<= m_sReferenceValue; break;
+ case PROPERTY_ID_DEFAULT_STATE: _rValue <<= static_cast<sal_Int16>(m_eDefaultChecked); break;
+
+ case PROPERTY_ID_UNCHECKED_REFVALUE:
+ _rValue <<= m_sNoCheckReferenceValue;
+ break;
+
+ default:
+ OBoundControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ }
+
+
+ void SAL_CALL OReferenceValueComponent::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_REFVALUE :
+ OSL_VERIFY( _rValue >>= m_sReferenceValue );
+ calculateExternalValueType();
+ break;
+
+ case PROPERTY_ID_UNCHECKED_REFVALUE:
+ OSL_VERIFY( _rValue >>= m_sNoCheckReferenceValue );
+ break;
+
+ case PROPERTY_ID_DEFAULT_STATE:
+ {
+ sal_Int16 nDefaultChecked;
+ if (!(_rValue >>= nDefaultChecked) || nDefaultChecked < 0
+ || nDefaultChecked > 2)
+ {
+ throw css::lang::IllegalArgumentException(
+ ("DefaultState property value must be a SHORT in the range"
+ " 0--2"),
+ css::uno::Reference<css::uno::XInterface>(), -1);
+ }
+ m_eDefaultChecked = static_cast<ToggleState>(nDefaultChecked);
+ resetNoBroadcast();
+ }
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ }
+ }
+
+
+ sal_Bool SAL_CALL OReferenceValueComponent::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified = false;
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_REFVALUE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_sReferenceValue );
+ break;
+
+ case PROPERTY_ID_UNCHECKED_REFVALUE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_sNoCheckReferenceValue );
+ break;
+
+ case PROPERTY_ID_DEFAULT_STATE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, static_cast<sal_Int16>(m_eDefaultChecked) );
+ break;
+
+ default:
+ bModified = OBoundControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ break;
+ }
+ return bModified;
+ }
+
+
+ Any OReferenceValueComponent::getDefaultForReset() const
+ {
+ return Any( static_cast<sal_Int16>(m_eDefaultChecked) );
+ }
+
+
+ void OReferenceValueComponent::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OBoundControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 3);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_REFVALUE, PROPERTY_ID_REFVALUE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_STATE, PROPERTY_ID_DEFAULT_STATE, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_UNCHECKED_REFVALUE, PROPERTY_ID_UNCHECKED_REFVALUE, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+ }
+
+
+ Sequence< Type > OReferenceValueComponent::getSupportedBindingTypes()
+ {
+ ::std::vector< Type > aTypes;
+
+ if ( !m_sReferenceValue.isEmpty() )
+ aTypes.push_back( cppu::UnoType<OUString>::get() );
+
+ aTypes.push_back( cppu::UnoType<sal_Bool>::get() );
+
+ return comphelper::containerToSequence(aTypes);
+ }
+
+
+ Any OReferenceValueComponent::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+ {
+ sal_Int16 nState = TRISTATE_INDET;
+
+ bool bExternalState = false;
+ OUString sExternalValue;
+ if ( _rExternalValue >>= bExternalState )
+ {
+ nState = ::sal::static_int_cast< sal_Int16 >( bExternalState ? TRISTATE_TRUE : TRISTATE_FALSE );
+ }
+ else if ( _rExternalValue >>= sExternalValue )
+ {
+ if ( sExternalValue == m_sReferenceValue )
+ nState = TRISTATE_TRUE;
+ else
+ {
+ if ( sExternalValue == m_sNoCheckReferenceValue )
+ nState = TRISTATE_FALSE;
+ else
+ nState = TRISTATE_INDET;
+ }
+ }
+ else if ( !_rExternalValue.hasValue() )
+ {
+ nState = TRISTATE_INDET;
+ }
+ else
+ {
+ OSL_FAIL( "OReferenceValueComponent::translateExternalValueToControlValue: unexpected value type!" );
+ }
+
+ return Any( nState );
+ }
+
+
+ Any OReferenceValueComponent::translateControlValueToExternalValue( ) const
+ {
+ Any aExternalValue;
+
+ try
+ {
+ Any aControlValue( m_xAggregateSet->getPropertyValue( PROPERTY_STATE ) );
+ sal_Int16 nControlValue = TRISTATE_INDET;
+ aControlValue >>= nControlValue;
+
+ bool bBooleanExchange = getExternalValueType().getTypeClass() == TypeClass_BOOLEAN;
+ bool bStringExchange = getExternalValueType().getTypeClass() == TypeClass_STRING;
+ OSL_ENSURE( bBooleanExchange || bStringExchange,
+ "OReferenceValueComponent::translateControlValueToExternalValue: unexpected value exchange type!" );
+
+ switch( nControlValue )
+ {
+ case TRISTATE_TRUE:
+ if ( bBooleanExchange )
+ {
+ aExternalValue <<= true;
+ }
+ else if ( bStringExchange )
+ {
+ aExternalValue <<= m_sReferenceValue;
+ }
+ break;
+
+ case TRISTATE_FALSE:
+ if ( bBooleanExchange )
+ {
+ aExternalValue <<= false;
+ }
+ else if ( bStringExchange )
+ {
+ aExternalValue <<= m_sNoCheckReferenceValue;
+ }
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.component", "OReferenceValueComponent::translateControlValueToExternalValue" );
+ }
+
+ return aExternalValue;
+ }
+
+
+ Any OReferenceValueComponent::translateControlValueToValidatableValue( ) const
+ {
+ if ( !m_xAggregateSet.is() )
+ return Any();
+
+ Any aControlValue( m_xAggregateSet->getPropertyValue( PROPERTY_STATE ) );
+ sal_Int16 nControlValue = TRISTATE_INDET;
+ aControlValue >>= nControlValue;
+
+ Any aValidatableValue;
+ switch ( nControlValue )
+ {
+ case TRISTATE_TRUE:
+ aValidatableValue <<= true;
+ break;
+ case TRISTATE_FALSE:
+ aValidatableValue <<= false;
+ break;
+ }
+ return aValidatableValue;
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/refvaluecomponent.hxx b/forms/source/component/refvaluecomponent.hxx
new file mode 100644
index 0000000000..79f343888c
--- /dev/null
+++ b/forms/source/component/refvaluecomponent.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <FormComponent.hxx>
+#include <togglestate.hxx>
+
+
+namespace frm
+{
+
+ /** an OBoundControlModel which features the exchange of a reference value
+ */
+ class OReferenceValueComponent : public OBoundControlModel
+ {
+ private:
+ // <properties>
+ OUString m_sReferenceValue; // the reference value to use for data exchange
+ OUString m_sNoCheckReferenceValue; // the reference value to be exchanged when the control is not checked
+ ToggleState m_eDefaultChecked; // the default check state
+ // </properties>
+
+ protected:
+ const OUString& getReferenceValue() const { return m_sReferenceValue; }
+ void setReferenceValue( const OUString& _rRefValue );
+
+ const OUString& getNoCheckReferenceValue() const { return m_sNoCheckReferenceValue; }
+
+ ToggleState getDefaultChecked() const { return m_eDefaultChecked; }
+ void setDefaultChecked( ToggleState _eChecked ) { m_eDefaultChecked = _eChecked; }
+
+ protected:
+ OReferenceValueComponent(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ const OUString& _rUnoControlModelTypeName,
+ const OUString& _rDefault
+ );
+ OReferenceValueComponent(
+ const OReferenceValueComponent* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+
+ virtual ~OReferenceValueComponent() override;
+
+ // OPropertySet 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 void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+ using ::cppu::OPropertySetHelper::getFastPropertyValue;
+
+ // OBoundControlModel overridables
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+
+ virtual css::uno::Any translateControlValueToValidatableValue( ) const override;
+
+ virtual css::uno::Any getDefaultForReset() const override;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/scrollbar.cxx b/forms/source/component/scrollbar.cxx
new file mode 100644
index 0000000000..f39bb32a62
--- /dev/null
+++ b/forms/source/component/scrollbar.cxx
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "scrollbar.hxx"
+#include <property.hxx>
+#include <services.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/streamsection.hxx>
+#include <comphelper/basicio.hxx>
+#include <rtl/math.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+namespace frm
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::form::binding;
+
+
+ //= helper
+
+ Any translateExternalDoubleToControlIntValue(
+ const Any& _rExternalValue, const Reference< XPropertySet >& _rxProperties,
+ const OUString& _rMinValueName, const OUString& _rMaxValueName )
+ {
+ OSL_ENSURE( _rxProperties.is(), "translateExternalDoubleToControlIntValue: no aggregate!?" );
+
+ sal_Int32 nControlValue( 0 );
+ double nExternalValue = 0;
+ if ( _rExternalValue >>= nExternalValue )
+ {
+ if ( std::isinf( nExternalValue ) )
+ {
+ // set the minimum or maximum of the scroll values
+ OUString sLimitPropertyName = std::signbit( nExternalValue )
+ ? _rMinValueName : _rMaxValueName;
+ if ( _rxProperties.is() )
+ _rxProperties->getPropertyValue( sLimitPropertyName ) >>= nControlValue;
+ }
+ else
+ {
+ nControlValue = static_cast<sal_Int32>(::rtl::math::round( nExternalValue ));
+ }
+ }
+ else
+ {
+ if ( _rxProperties.is() )
+ _rxProperties->getPropertyValue( _rMinValueName ) >>= nControlValue;
+ }
+
+ return Any( nControlValue );
+ }
+
+
+ Any translateControlIntToExternalDoubleValue( const Any& _rControlIntValue )
+ {
+ Any aExternalDoubleValue;
+ sal_Int32 nScrollValue = 0;
+ if ( _rControlIntValue >>= nScrollValue )
+ aExternalDoubleValue <<= static_cast<double>(nScrollValue);
+ else
+ {
+ OSL_FAIL( "translateControlIntToExternalDoubleValue: no integer scroll value!" );
+ // aExternalDoubleValue is void here, which is okay for this purpose ...
+ }
+
+ return aExternalDoubleValue;
+ }
+
+ OScrollBarModel::OScrollBarModel( const Reference<XComponentContext>& _rxFactory )
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_SCROLLBAR, VCL_CONTROL_SCROLLBAR, true, true, false )
+ ,m_nDefaultScrollValue( 0 )
+ {
+
+ m_nClassId = FormComponentType::SCROLLBAR;
+ initValueProperty( PROPERTY_SCROLL_VALUE, PROPERTY_ID_SCROLL_VALUE );
+ }
+
+
+ OScrollBarModel::OScrollBarModel( const OScrollBarModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ {
+ m_nDefaultScrollValue = _pOriginal->m_nDefaultScrollValue;
+ }
+
+
+ OScrollBarModel::~OScrollBarModel( )
+ {
+ }
+
+ OUString SAL_CALL OScrollBarModel::getImplementationName()
+ {
+ return "com.sun.star.comp.forms.OScrollBarModel";
+ }
+
+ // note that we're passing OControlModel as "base class". This is because
+ // OBoundControlModel, our real base class, claims to support the DataAwareControlModel
+ // service, which isn't really true for us. We only derive from this class
+ // to benefit from the functionality for binding to spreadsheet cells
+ Sequence< OUString > SAL_CALL OScrollBarModel::getSupportedServiceNames()
+ {
+ Sequence< OUString > aOwnNames { FRM_SUN_COMPONENT_SCROLLBAR, BINDABLE_INTEGER_VALUE_RANGE };
+
+ return ::comphelper::combineSequences(
+ getAggregateServiceNames(),
+ ::comphelper::concatSequences(
+ OControlModel::getSupportedServiceNames_Static(),
+ aOwnNames)
+ );
+ }
+
+ css::uno::Reference< css::util::XCloneable > SAL_CALL OScrollBarModel::createClone()
+{
+ rtl::Reference<OScrollBarModel> pClone = new OScrollBarModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+ void OScrollBarModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 3);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_SCROLL_VALUE, PROPERTY_ID_DEFAULT_SCROLL_VALUE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCEPROPERTY, PROPERTY_ID_CONTROLSOURCEPROPERTY, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+ }
+
+
+ void OScrollBarModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SCROLL_VALUE:
+ _rValue <<= m_nDefaultScrollValue;
+ break;
+
+ default:
+ OBoundControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ }
+
+
+ void OScrollBarModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SCROLL_VALUE:
+ OSL_VERIFY( _rValue >>= m_nDefaultScrollValue );
+ resetNoBroadcast();
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ }
+ }
+
+
+ sal_Bool OScrollBarModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified( false );
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SCROLL_VALUE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_nDefaultScrollValue );
+ break;
+
+ default:
+ bModified = OBoundControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ break;
+ }
+ return bModified;
+ }
+
+
+ Any OScrollBarModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aReturn;
+
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SCROLL_VALUE:
+ aReturn <<= sal_Int32(0);
+ break;
+
+ default:
+ aReturn = OBoundControlModel::getPropertyDefaultByHandle( _nHandle );
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ Any OScrollBarModel::translateDbColumnToControlValue( )
+ {
+ OSL_FAIL( "OScrollBarModel::commitControlValueToDbColumn: never to be called (we're not bound)!" );
+ return Any();
+ }
+
+
+ bool OScrollBarModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+ {
+ OSL_FAIL( "OScrollBarModel::commitControlValueToDbColumn: never to be called (we're not bound)!" );
+ return true;
+ }
+
+
+ Any OScrollBarModel::getDefaultForReset() const
+ {
+ return Any( m_nDefaultScrollValue );
+ }
+
+
+ OUString SAL_CALL OScrollBarModel::getServiceName()
+ {
+ return FRM_SUN_COMPONENT_SCROLLBAR;
+ }
+
+
+ void SAL_CALL OScrollBarModel::write( const Reference< XObjectOutputStream >& _rxOutStream )
+ {
+ OBoundControlModel::write( _rxOutStream );
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OStreamSection aSection( _rxOutStream );
+
+ // version
+ _rxOutStream->writeShort( 0x0001 );
+
+ // properties
+ _rxOutStream << m_nDefaultScrollValue;
+ writeHelpTextCompatibly( _rxOutStream );
+ }
+
+
+ void SAL_CALL OScrollBarModel::read( const Reference< XObjectInputStream>& _rxInStream )
+ {
+ OBoundControlModel::read( _rxInStream );
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // version
+ {
+ OStreamSection aSection( _rxInStream );
+
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ if ( nVersion == 0x0001 )
+ {
+ _rxInStream >> m_nDefaultScrollValue;
+ readHelpTextCompatibly( _rxInStream );
+ }
+ else
+ defaultCommonProperties();
+
+ // here, everything in the stream section which is left will be skipped
+ }
+ }
+
+
+ Any OScrollBarModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+ {
+ return translateExternalDoubleToControlIntValue( _rExternalValue, m_xAggregateSet,
+ "ScrollValueMin",
+ "ScrollValueMax" );
+ }
+
+
+ Any OScrollBarModel::translateControlValueToExternalValue( ) const
+ {
+ // by definition, the base class simply obtains the property value
+ return translateControlIntToExternalDoubleValue( OBoundControlModel::translateControlValueToExternalValue() );
+ }
+
+
+ Sequence< Type > OScrollBarModel::getSupportedBindingTypes()
+ {
+ return Sequence< Type >( & cppu::UnoType<double>::get(), 1 );
+ }
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_OScrollBarModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OScrollBarModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/scrollbar.hxx b/forms/source/component/scrollbar.hxx
new file mode 100644
index 0000000000..0acb91a3a0
--- /dev/null
+++ b/forms/source/component/scrollbar.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <FormComponent.hxx>
+
+namespace frm
+{
+
+ class OScrollBarModel :public OBoundControlModel
+ {
+ private:
+ // <properties>
+ sal_Int32 m_nDefaultScrollValue;
+ // </properties>
+
+ public:
+ OScrollBarModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OScrollBarModel(
+ const OScrollBarModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OScrollBarModel() override;
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual ::css::uno::Sequence< OUString > 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;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 _nHandle ) const override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const 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;
+
+ // OBoundControlModel
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+
+ // prevent method hiding
+ using OBoundControlModel::disposing;
+ using OBoundControlModel::getFastPropertyValue;
+
+ };
+
+ css::uno::Any translateExternalDoubleToControlIntValue(
+ const css::uno::Any& _rExternalValue, const css::uno::Reference< css::beans::XPropertySet >& _rxProperties,
+ const OUString& _rMinValueName, const OUString& _rMaxValueName );
+
+ css::uno::Any translateControlIntToExternalDoubleValue( const css::uno::Any& _rControlIntValue );
+
+} // namespacefrm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/spinbutton.cxx b/forms/source/component/spinbutton.cxx
new file mode 100644
index 0000000000..a20b17573e
--- /dev/null
+++ b/forms/source/component/spinbutton.cxx
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "spinbutton.hxx"
+#include "scrollbar.hxx"
+#include <comphelper/property.hxx>
+#include <comphelper/streamsection.hxx>
+#include <comphelper/basicio.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <property.hxx>
+#include <services.hxx>
+
+
+namespace frm
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::form::binding;
+
+
+ //= OSpinButtonModel
+
+ OSpinButtonModel::OSpinButtonModel( const Reference<XComponentContext>& _rxFactory )
+ :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_SPINBUTTON, VCL_CONTROL_SPINBUTTON, true, true, false )
+ ,m_nDefaultSpinValue( 0 )
+ {
+
+ m_nClassId = FormComponentType::SPINBUTTON;
+ initValueProperty( PROPERTY_SPIN_VALUE, PROPERTY_ID_SPIN_VALUE );
+ }
+
+
+ OSpinButtonModel::OSpinButtonModel( const OSpinButtonModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OBoundControlModel( _pOriginal, _rxFactory )
+ {
+ m_nDefaultSpinValue = _pOriginal->m_nDefaultSpinValue;
+ }
+
+
+ OSpinButtonModel::~OSpinButtonModel( )
+ {
+ }
+
+ OUString SAL_CALL OSpinButtonModel::getImplementationName()
+ {
+ return "com.sun.star.comp.forms.OSpinButtonModel";
+ }
+
+ // note that we're passing OControlModel as "base class". This is because
+ // OBoundControlModel, our real base class, claims to support the DataAwareControlModel
+ // service, which isn't really true for us. We only derive from this class
+ // to benefit from the functionality for binding to spreadsheet cells
+ Sequence< OUString > SAL_CALL OSpinButtonModel::getSupportedServiceNames()
+ {
+ Sequence< OUString > aOwnNames { FRM_SUN_COMPONENT_SPINBUTTON, BINDABLE_INTEGER_VALUE_RANGE };
+
+ return ::comphelper::combineSequences(
+ getAggregateServiceNames(),
+ ::comphelper::concatSequences(
+ OControlModel::getSupportedServiceNames_Static(),
+ aOwnNames
+ )
+ );
+ }
+
+ css::uno::Reference< css::util::XCloneable > SAL_CALL OSpinButtonModel::createClone()
+{
+ rtl::Reference<OSpinButtonModel> pClone = new OSpinButtonModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+ void OSpinButtonModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OControlModel::describeFixedProperties( _rProps );
+ sal_Int32 nOldCount = _rProps.getLength();
+ _rProps.realloc( nOldCount + 3);
+ css::beans::Property* pProperties = _rProps.getArray() + nOldCount;
+ *pProperties++ = css::beans::Property(PROPERTY_DEFAULT_SPIN_VALUE, PROPERTY_ID_DEFAULT_SPIN_VALUE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND);
+ *pProperties++ = css::beans::Property(PROPERTY_CONTROLSOURCEPROPERTY, PROPERTY_ID_CONTROLSOURCEPROPERTY, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+ }
+
+
+ void OSpinButtonModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SPIN_VALUE:
+ _rValue <<= m_nDefaultSpinValue;
+ break;
+
+ default:
+ OBoundControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ }
+
+
+ void OSpinButtonModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SPIN_VALUE:
+ OSL_VERIFY( _rValue >>= m_nDefaultSpinValue );
+ resetNoBroadcast();
+ break;
+
+ default:
+ OBoundControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ }
+ }
+
+
+ sal_Bool OSpinButtonModel::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified( false );
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SPIN_VALUE:
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, m_nDefaultSpinValue );
+ break;
+
+ default:
+ bModified = OBoundControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ break;
+ }
+ return bModified;
+ }
+
+
+ Any OSpinButtonModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aReturn;
+
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_DEFAULT_SPIN_VALUE:
+ aReturn <<= sal_Int32(0);
+ break;
+
+ default:
+ aReturn = OBoundControlModel::getPropertyDefaultByHandle( _nHandle );
+ break;
+ }
+
+ return aReturn;
+ }
+
+
+ Any OSpinButtonModel::translateDbColumnToControlValue( )
+ {
+ OSL_FAIL( "OSpinButtonModel::commitControlValueToDbColumn: never to be called (we're not bound)!" );
+ return Any();
+ }
+
+
+ bool OSpinButtonModel::commitControlValueToDbColumn( bool /*_bPostReset*/ )
+ {
+ OSL_FAIL( "OSpinButtonModel::commitControlValueToDbColumn: never to be called (we're not bound)!" );
+ return true;
+ }
+
+
+ Any OSpinButtonModel::getDefaultForReset() const
+ {
+ return Any( m_nDefaultSpinValue );
+ }
+
+
+ OUString SAL_CALL OSpinButtonModel::getServiceName()
+ {
+ return FRM_SUN_COMPONENT_SPINBUTTON;
+ }
+
+
+ void SAL_CALL OSpinButtonModel::write( const Reference< XObjectOutputStream >& _rxOutStream )
+ {
+ OBoundControlModel::write( _rxOutStream );
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OStreamSection aSection( _rxOutStream );
+
+ // version
+ _rxOutStream->writeShort( 0x0001 );
+
+ // properties
+ _rxOutStream << m_nDefaultSpinValue;
+ writeHelpTextCompatibly( _rxOutStream );
+ }
+
+
+ void SAL_CALL OSpinButtonModel::read( const Reference< XObjectInputStream>& _rxInStream )
+ {
+ OBoundControlModel::read( _rxInStream );
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // version
+ {
+ OStreamSection aSection( _rxInStream );
+
+ sal_uInt16 nVersion = _rxInStream->readShort();
+ if ( nVersion == 0x0001 )
+ {
+ _rxInStream >> m_nDefaultSpinValue;
+ readHelpTextCompatibly( _rxInStream );
+ }
+ else
+ defaultCommonProperties();
+
+ // here, everything in the stream section which is left will be skipped
+ }
+ }
+
+
+ Any OSpinButtonModel::translateExternalValueToControlValue( const Any& _rExternalValue ) const
+ {
+ return translateExternalDoubleToControlIntValue( _rExternalValue, m_xAggregateSet,
+ "SpinValueMin",
+ "SpinValueMax" );
+ }
+
+
+ Any OSpinButtonModel::translateControlValueToExternalValue( ) const
+ {
+ // by definition, the base class simply obtains the property value
+ return translateControlIntToExternalDoubleValue( OBoundControlModel::translateControlValueToExternalValue() );
+ }
+
+
+ Sequence< Type > OSpinButtonModel::getSupportedBindingTypes()
+ {
+ return Sequence< Type >( &cppu::UnoType<double>::get(), 1 );
+ }
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_OSpinButtonModel_get_implementation(css::uno::XComponentContext* component,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::OSpinButtonModel(component));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/component/spinbutton.hxx b/forms/source/component/spinbutton.hxx
new file mode 100644
index 0000000000..b9800274f0
--- /dev/null
+++ b/forms/source/component/spinbutton.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <FormComponent.hxx>
+
+
+namespace frm
+{
+
+ class OSpinButtonModel :public OBoundControlModel
+ {
+ private:
+ // <properties>
+ sal_Int32 m_nDefaultSpinValue;
+ // </properties>
+
+ public:
+ OSpinButtonModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ OSpinButtonModel(
+ const OSpinButtonModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~OSpinButtonModel() override;
+
+ protected:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual ::css::uno::Sequence< OUString > 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;
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 _nHandle ) const override;
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const 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;
+
+ // OBoundControlModel
+ virtual css::uno::Any translateDbColumnToControlValue( ) override;
+ virtual bool commitControlValueToDbColumn( bool _bPostReset ) override;
+ virtual css::uno::Any getDefaultForReset() const override;
+
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes() override;
+ virtual css::uno::Any translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const override;
+ virtual css::uno::Any translateControlValueToExternalValue( ) const override;
+
+ // prevent method hiding
+ using OBoundControlModel::disposing;
+ using OBoundControlModel::getFastPropertyValue;
+
+ };
+
+} // namespacefrm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/helper/commandimageprovider.cxx b/forms/source/helper/commandimageprovider.cxx
new file mode 100644
index 0000000000..dce1f50e24
--- /dev/null
+++ b/forms/source/helper/commandimageprovider.cxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <commandimageprovider.hxx>
+
+#include <com/sun/star/ui/XImageManager.hpp>
+#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/ui/ImageType.hpp>
+
+#include <comphelper/diagnose_ex.hxx>
+
+
+namespace frm
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::frame::XModel;
+ using ::com::sun::star::ui::XUIConfigurationManagerSupplier;
+ using ::com::sun::star::ui::XUIConfigurationManager;
+ using ::com::sun::star::ui::XModuleUIConfigurationManagerSupplier;
+ using ::com::sun::star::ui::theModuleUIConfigurationManagerSupplier;
+ using ::com::sun::star::frame::ModuleManager;
+ using ::com::sun::star::frame::XModuleManager2;
+ using ::com::sun::star::graphic::XGraphic;
+
+ namespace ImageType = ::com::sun::star::ui::ImageType;
+
+ DocumentCommandImageProvider::DocumentCommandImageProvider( const Reference<XComponentContext>& _rContext, const Reference< XModel >& _rxDocument )
+ {
+ OSL_ENSURE( _rxDocument.is(), "DocumentCommandImageProvider::impl_init_nothrow: no document => no images!" );
+ if ( !_rxDocument.is() )
+ return;
+
+ // obtain the image manager of the document
+ try
+ {
+ Reference< XUIConfigurationManagerSupplier > xSuppUIConfig( _rxDocument, UNO_QUERY_THROW );
+ Reference< XUIConfigurationManager > xUIConfig = xSuppUIConfig->getUIConfigurationManager();
+ m_xDocumentImageManager.set( xUIConfig->getImageManager(), UNO_QUERY_THROW );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.helper");
+ }
+
+ // obtain the image manager or the module
+ try
+ {
+ Reference< XModuleManager2 > xModuleManager( ModuleManager::create(_rContext) );
+ OUString sModuleID = xModuleManager->identify( _rxDocument );
+
+ Reference< XModuleUIConfigurationManagerSupplier > xSuppUIConfig(
+ theModuleUIConfigurationManagerSupplier::get(_rContext) );
+ Reference< XUIConfigurationManager > xUIConfig(
+ xSuppUIConfig->getUIConfigurationManager( sModuleID ), UNO_SET_THROW );
+ m_xModuleImageManager.set( xUIConfig->getImageManager(), UNO_QUERY_THROW );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.helper");
+ }
+ }
+
+
+ std::vector<Image> DocumentCommandImageProvider::getCommandImages( const css::uno::Sequence< OUString >& _rCommandURLs, const bool _bLarge ) const
+ {
+ const size_t nCommandCount = _rCommandURLs.getLength();
+ std::vector<Image> aImages( nCommandCount );
+ try
+ {
+ const sal_Int16 nImageType = ImageType::COLOR_NORMAL
+ + ( _bLarge ? ImageType::SIZE_LARGE : ImageType::SIZE_DEFAULT );
+
+ Sequence< Reference< XGraphic > > aDocImages( nCommandCount );
+ Sequence< Reference< XGraphic > > aModImages( nCommandCount );
+
+ // first try the document image manager
+ if ( m_xDocumentImageManager.is() )
+ aDocImages = m_xDocumentImageManager->getImages( nImageType, _rCommandURLs );
+
+ // then the module's image manager
+ if ( m_xModuleImageManager.is() )
+ aModImages = m_xModuleImageManager->getImages( nImageType, _rCommandURLs );
+
+ ENSURE_OR_THROW( static_cast<size_t>(aDocImages.getLength()) == nCommandCount, "illegal array size returned by getImages (document image manager)" );
+ ENSURE_OR_THROW( static_cast<size_t>(aModImages.getLength()) == nCommandCount, "illegal array size returned by getImages (module image manager)" );
+
+ for ( size_t i=0; i<nCommandCount; ++i )
+ {
+ if ( aDocImages[i].is() )
+ aImages[i] = Image( aDocImages[i] );
+ else
+ aImages[i] = Image( aModImages[i] );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.helper");
+ }
+ return aImages;
+ }
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/helper/controlfeatureinterception.cxx b/forms/source/helper/controlfeatureinterception.cxx
new file mode 100644
index 0000000000..091af550cc
--- /dev/null
+++ b/forms/source/helper/controlfeatureinterception.cxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <controlfeatureinterception.hxx>
+#include <urltransformer.hxx>
+#include <osl/diagnose.h>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::lang;
+
+ ControlFeatureInterception::ControlFeatureInterception( const Reference< XComponentContext >& _rxORB )
+ :m_pUrlTransformer( new UrlTransformer( _rxORB ) )
+ {
+ }
+
+
+ void ControlFeatureInterception::registerDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ if ( !_rxInterceptor.is() )
+ {
+ OSL_FAIL( "ControlFeatureInterception::registerDispatchProviderInterceptor: invalid interceptor!" );
+ return;
+ }
+
+ if ( m_xFirstDispatchInterceptor.is() )
+ {
+ // there is already an interceptor; the new one will become its master
+ _rxInterceptor->setSlaveDispatchProvider( m_xFirstDispatchInterceptor );
+ m_xFirstDispatchInterceptor->setMasterDispatchProvider( m_xFirstDispatchInterceptor );
+ }
+
+ // we are the master of the chain's first interceptor
+ m_xFirstDispatchInterceptor = _rxInterceptor;
+ m_xFirstDispatchInterceptor->setMasterDispatchProvider( nullptr );
+ // it's the first of the interceptor chain
+ }
+
+
+ void ControlFeatureInterception::releaseDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ if ( !_rxInterceptor.is() )
+ {
+ OSL_FAIL( "ControlFeatureInterception::releaseDispatchProviderInterceptor: invalid interceptor!" );
+ return;
+ }
+
+ Reference< XDispatchProviderInterceptor > xChainWalk( m_xFirstDispatchInterceptor );
+
+ if ( m_xFirstDispatchInterceptor == _rxInterceptor )
+ { // our chain will have a new first element
+ Reference< XDispatchProviderInterceptor > xSlave( m_xFirstDispatchInterceptor->getSlaveDispatchProvider(), UNO_QUERY );
+ m_xFirstDispatchInterceptor = xSlave;
+ }
+ // do this before removing the interceptor from the chain as we won't know it's slave afterwards)
+
+ while ( xChainWalk.is() )
+ {
+ // walk along the chain of interceptors and look for the interceptor that has to be removed
+ Reference< XDispatchProviderInterceptor > xSlave( xChainWalk->getSlaveDispatchProvider(), UNO_QUERY );
+
+ if ( xChainWalk == _rxInterceptor )
+ {
+ // old master may be an interceptor too
+ Reference< XDispatchProviderInterceptor > xMaster( xChainWalk->getMasterDispatchProvider(), UNO_QUERY );
+
+ // unchain the interceptor that has to be removed
+ xChainWalk->setSlaveDispatchProvider( nullptr );
+ xChainWalk->setMasterDispatchProvider( nullptr );
+
+ // reconnect the chain
+ if ( xMaster.is() )
+ {
+ xMaster->setSlaveDispatchProvider( xSlave );
+ }
+
+ // if somebody has registered the same interceptor twice, then we will remove
+ // it once per call ...
+ break;
+ }
+
+ xChainWalk = xSlave;
+ }
+ }
+
+
+ void ControlFeatureInterception::dispose()
+ {
+ // release all interceptors
+ Reference< XDispatchProviderInterceptor > xInterceptor( m_xFirstDispatchInterceptor );
+ m_xFirstDispatchInterceptor.clear();
+ while ( xInterceptor.is() )
+ {
+ // tell the interceptor it has a new (means no) predecessor
+ xInterceptor->setMasterDispatchProvider( nullptr );
+
+ // ask for its successor
+ Reference< XDispatchProvider > xSlave = xInterceptor->getSlaveDispatchProvider();
+ // and give it the new (means no) successoert
+ xInterceptor->setSlaveDispatchProvider( nullptr );
+
+ // start over with the next chain element
+ xInterceptor.set(xSlave, css::uno::UNO_QUERY);
+ }
+ }
+
+ Reference< XDispatch > ControlFeatureInterception::queryDispatch( const URL& _rURL )
+ {
+ Reference< XDispatch > xDispatcher;
+ if ( m_xFirstDispatchInterceptor.is() )
+ xDispatcher = m_xFirstDispatchInterceptor->queryDispatch( _rURL, OUString(), 0 );
+ return xDispatcher;
+ }
+
+
+ Reference< XDispatch > ControlFeatureInterception::queryDispatch( const char* _pAsciiURL )
+ {
+ return queryDispatch( m_pUrlTransformer->getStrictURLFromAscii( _pAsciiURL ) );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/helper/formnavigation.cxx b/forms/source/helper/formnavigation.cxx
new file mode 100644
index 0000000000..be87c063e3
--- /dev/null
+++ b/forms/source/helper/formnavigation.cxx
@@ -0,0 +1,447 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formnavigation.hxx>
+#include <urltransformer.hxx>
+#include <controlfeatureinterception.hxx>
+#include <frm_strings.hxx>
+
+#include <com/sun/star/form/runtime/FormFeature.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <tools/debug.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::frame;
+ namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
+
+ OFormNavigationHelper::OFormNavigationHelper( const Reference< XComponentContext >& _rxORB )
+ :m_xORB( _rxORB )
+ ,m_aFeatureInterception( m_xORB )
+ ,m_nConnectedFeatures( 0 )
+ {
+ }
+
+
+ OFormNavigationHelper::~OFormNavigationHelper()
+ {
+ }
+
+
+ void OFormNavigationHelper::dispose( )
+ {
+ m_aFeatureInterception.dispose();
+ disconnectDispatchers();
+ }
+
+
+ void OFormNavigationHelper::interceptorsChanged( )
+ {
+ updateDispatches();
+ }
+
+
+ void OFormNavigationHelper::featureStateChanged( sal_Int16 /*_nFeatureId*/, bool /*_bEnabled*/ )
+ {
+ // not interested in
+ }
+
+
+ void OFormNavigationHelper::allFeatureStatesChanged( )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL OFormNavigationHelper::registerDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ m_aFeatureInterception.registerDispatchProviderInterceptor( _rxInterceptor );
+ interceptorsChanged();
+ }
+
+
+ void SAL_CALL OFormNavigationHelper::releaseDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ m_aFeatureInterception.releaseDispatchProviderInterceptor( _rxInterceptor );
+ interceptorsChanged();
+ }
+
+
+ void SAL_CALL OFormNavigationHelper::statusChanged( const FeatureStateEvent& _rState )
+ {
+ for (auto & feature : m_aSupportedFeatures)
+ {
+ if ( feature.second.aURL.Main == _rState.FeatureURL.Main )
+ {
+ if ( ( feature.second.bCachedState != bool(_rState.IsEnabled) )
+ || ( feature.second.aCachedAdditionalState != _rState.State )
+ )
+ {
+ // change the cached state
+ feature.second.bCachedState = _rState.IsEnabled;
+ feature.second.aCachedAdditionalState = _rState.State;
+ // tell derivees what happened
+ featureStateChanged( feature.first, _rState.IsEnabled );
+ }
+ return;
+ }
+ }
+
+ // unreachable
+ OSL_FAIL( "OFormNavigationHelper::statusChanged: huh? An invalid/unknown URL?" );
+ }
+
+
+ void SAL_CALL OFormNavigationHelper::disposing( const EventObject& _rSource )
+ {
+ // was it one of our external dispatchers?
+ if ( !m_nConnectedFeatures )
+ return;
+
+ for (auto & feature : m_aSupportedFeatures)
+ {
+ if ( feature.second.xDispatcher == _rSource.Source )
+ {
+ feature.second.xDispatcher->removeStatusListener( static_cast< XStatusListener* >( this ), feature.second.aURL );
+ feature.second.xDispatcher = nullptr;
+ feature.second.bCachedState = false;
+ feature.second.aCachedAdditionalState.clear();
+ --m_nConnectedFeatures;
+
+ featureStateChanged( feature.first, false );
+ break;
+ }
+ }
+ }
+
+
+ void OFormNavigationHelper::updateDispatches()
+ {
+ if ( !m_nConnectedFeatures )
+ { // we don't have any dispatchers yet -> do the initial connect
+ connectDispatchers();
+ return;
+ }
+
+ initializeSupportedFeatures();
+
+ m_nConnectedFeatures = 0;
+
+ Reference< XDispatch > xNewDispatcher;
+ Reference< XDispatch > xCurrentDispatcher;
+
+ for (auto & feature : m_aSupportedFeatures)
+ {
+ xNewDispatcher = queryDispatch( feature.second.aURL );
+ xCurrentDispatcher = feature.second.xDispatcher;
+ if ( xNewDispatcher != xCurrentDispatcher )
+ {
+ // the dispatcher for this particular URL changed
+ if ( xCurrentDispatcher.is() )
+ xCurrentDispatcher->removeStatusListener( static_cast< XStatusListener* >( this ), feature.second.aURL );
+
+ xCurrentDispatcher = feature.second.xDispatcher = xNewDispatcher;
+
+ if ( xCurrentDispatcher.is() )
+ xCurrentDispatcher->addStatusListener( static_cast< XStatusListener* >( this ), feature.second.aURL );
+ }
+
+ if ( xCurrentDispatcher.is() )
+ ++m_nConnectedFeatures;
+ else
+ feature.second.bCachedState = false;
+ }
+
+ // notify derivee that (potentially) all features changed their state
+ allFeatureStatesChanged( );
+ }
+
+
+ void OFormNavigationHelper::connectDispatchers()
+ {
+ if ( m_nConnectedFeatures )
+ { // already connected -> just do an update
+ updateDispatches();
+ return;
+ }
+
+ initializeSupportedFeatures();
+
+ m_nConnectedFeatures = 0;
+
+ for (auto & feature : m_aSupportedFeatures)
+ {
+ feature.second.bCachedState = false;
+ feature.second.aCachedAdditionalState.clear();
+ feature.second.xDispatcher = queryDispatch( feature.second.aURL );
+ if ( feature.second.xDispatcher.is() )
+ {
+ ++m_nConnectedFeatures;
+ feature.second.xDispatcher->addStatusListener( static_cast< XStatusListener* >( this ), feature.second.aURL );
+ }
+ }
+
+ // notify derivee that (potentially) all features changed their state
+ allFeatureStatesChanged( );
+ }
+
+
+ void OFormNavigationHelper::disconnectDispatchers()
+ {
+ if ( m_nConnectedFeatures )
+ {
+ for (auto & feature : m_aSupportedFeatures)
+ {
+ if ( feature.second.xDispatcher.is() )
+ feature.second.xDispatcher->removeStatusListener( static_cast< XStatusListener* >( this ), feature.second.aURL );
+
+ feature.second.xDispatcher = nullptr;
+ feature.second.bCachedState = false;
+ feature.second.aCachedAdditionalState.clear();
+ }
+
+ m_nConnectedFeatures = 0;
+ }
+
+ // notify derivee that (potentially) all features changed their state
+ allFeatureStatesChanged( );
+ }
+
+
+ void OFormNavigationHelper::initializeSupportedFeatures( )
+ {
+ if ( !m_aSupportedFeatures.empty() )
+ return;
+
+ // ask the derivee which feature ids it wants us to support
+ ::std::vector< sal_Int16 > aFeatureIds;
+ getSupportedFeatures( aFeatureIds );
+
+ OFormNavigationMapper aUrlMapper( m_xORB );
+
+ for (auto const& feature : aFeatureIds)
+ {
+ FeatureInfo aFeatureInfo;
+
+ bool bKnownId =
+ aUrlMapper.getFeatureURL( feature, aFeatureInfo.aURL );
+ DBG_ASSERT( bKnownId, "OFormNavigationHelper::initializeSupportedFeatures: unknown feature id!" );
+
+ if ( bKnownId )
+ // add to our map
+ m_aSupportedFeatures.emplace( feature, aFeatureInfo );
+ }
+ }
+
+
+ Reference< XDispatch > OFormNavigationHelper::queryDispatch( const URL& _rURL )
+ {
+ return m_aFeatureInterception.queryDispatch( _rURL );
+ }
+
+
+ void OFormNavigationHelper::dispatchWithArgument( sal_Int16 _nFeatureId, const char* _pParamAsciiName,
+ const Any& _rParamValue ) const
+ {
+ FeatureMap::const_iterator aInfo = m_aSupportedFeatures.find( _nFeatureId );
+ if ( m_aSupportedFeatures.end() != aInfo )
+ {
+ if ( aInfo->second.xDispatcher.is() )
+ {
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString::createFromAscii( _pParamAsciiName ), _rParamValue) };
+
+ aInfo->second.xDispatcher->dispatch( aInfo->second.aURL, aArgs );
+ }
+ }
+ }
+
+
+ void OFormNavigationHelper::dispatch( sal_Int16 _nFeatureId ) const
+ {
+ FeatureMap::const_iterator aInfo = m_aSupportedFeatures.find( _nFeatureId );
+ if ( m_aSupportedFeatures.end() != aInfo )
+ {
+ if ( aInfo->second.xDispatcher.is() )
+ {
+ Sequence< PropertyValue > aEmptyArgs;
+ aInfo->second.xDispatcher->dispatch( aInfo->second.aURL, aEmptyArgs );
+ }
+ }
+ }
+
+
+ bool OFormNavigationHelper::isEnabled( sal_Int16 _nFeatureId ) const
+ {
+ FeatureMap::const_iterator aInfo = m_aSupportedFeatures.find( _nFeatureId );
+ if ( m_aSupportedFeatures.end() != aInfo )
+ return aInfo->second.bCachedState;
+
+ return false;
+ }
+
+
+ bool OFormNavigationHelper::getBooleanState( sal_Int16 _nFeatureId ) const
+ {
+ bool bState = false;
+
+ FeatureMap::const_iterator aInfo = m_aSupportedFeatures.find( _nFeatureId );
+ if ( m_aSupportedFeatures.end() != aInfo )
+ aInfo->second.aCachedAdditionalState >>= bState;
+
+ return bState;
+ }
+
+
+ OUString OFormNavigationHelper::getStringState( sal_Int16 _nFeatureId ) const
+ {
+ OUString sState;
+
+ FeatureMap::const_iterator aInfo = m_aSupportedFeatures.find( _nFeatureId );
+ if ( m_aSupportedFeatures.end() != aInfo )
+ aInfo->second.aCachedAdditionalState >>= sState;
+
+ return sState;
+ }
+
+
+ sal_Int32 OFormNavigationHelper::getIntegerState( sal_Int16 _nFeatureId ) const
+ {
+ sal_Int32 nState = 0;
+
+ FeatureMap::const_iterator aInfo = m_aSupportedFeatures.find( _nFeatureId );
+ if ( m_aSupportedFeatures.end() != aInfo )
+ aInfo->second.aCachedAdditionalState >>= nState;
+
+ return nState;
+ }
+
+
+ void OFormNavigationHelper::invalidateSupportedFeaturesSet()
+ {
+ disconnectDispatchers( );
+ // no supported features anymore:
+ FeatureMap().swap(m_aSupportedFeatures);
+ }
+
+ OFormNavigationMapper::OFormNavigationMapper( const Reference< XComponentContext >& _rxORB )
+ {
+ m_pUrlTransformer.reset( new UrlTransformer( _rxORB ) );
+ }
+
+
+ OFormNavigationMapper::~OFormNavigationMapper( )
+ {
+ }
+
+
+ bool OFormNavigationMapper::getFeatureURL( sal_Int16 _nFeatureId, URL& /* [out] */ _rURL )
+ {
+ // get the ascii version of the URL
+ const char* pAsciiURL = getFeatureURLAscii( _nFeatureId );
+ if ( pAsciiURL )
+ _rURL = m_pUrlTransformer->getStrictURLFromAscii( pAsciiURL );
+
+ return ( pAsciiURL != nullptr );
+ }
+
+
+ namespace
+ {
+ struct FeatureURL
+ {
+ const sal_Int16 nFormFeature;
+ const char* pAsciiURL;
+
+ FeatureURL( const sal_Int16 _nFormFeature, const char* _pAsciiURL )
+ :nFormFeature( _nFormFeature )
+ ,pAsciiURL( _pAsciiURL )
+ {
+ }
+ };
+ const FeatureURL* lcl_getFeatureTable()
+ {
+ static const FeatureURL s_aFeatureURLs[] =
+ {
+ FeatureURL( FormFeature::MoveAbsolute, URL_FORM_POSITION ),
+ FeatureURL( FormFeature::TotalRecords, URL_FORM_RECORDCOUNT ),
+ FeatureURL( FormFeature::MoveToFirst, URL_RECORD_FIRST ),
+ FeatureURL( FormFeature::MoveToPrevious, URL_RECORD_PREV ),
+ FeatureURL( FormFeature::MoveToNext, URL_RECORD_NEXT ),
+ FeatureURL( FormFeature::MoveToLast, URL_RECORD_LAST ),
+ FeatureURL( FormFeature::SaveRecordChanges, URL_RECORD_SAVE ),
+ FeatureURL( FormFeature::UndoRecordChanges, URL_RECORD_UNDO ),
+ FeatureURL( FormFeature::MoveToInsertRow, URL_RECORD_NEW ),
+ FeatureURL( FormFeature::DeleteRecord, URL_RECORD_DELETE ),
+ FeatureURL( FormFeature::ReloadForm, URL_FORM_REFRESH ),
+ FeatureURL( FormFeature::RefreshCurrentControl, URL_FORM_REFRESH_CURRENT_CONTROL ),
+ FeatureURL( FormFeature::SortAscending, URL_FORM_SORT_UP ),
+ FeatureURL( FormFeature::SortDescending, URL_FORM_SORT_DOWN ),
+ FeatureURL( FormFeature::InteractiveSort, URL_FORM_SORT ),
+ FeatureURL( FormFeature::AutoFilter, URL_FORM_AUTO_FILTER ),
+ FeatureURL( FormFeature::InteractiveFilter, URL_FORM_FILTER ),
+ FeatureURL( FormFeature::ToggleApplyFilter, URL_FORM_APPLY_FILTER ),
+ FeatureURL( FormFeature::RemoveFilterAndSort, URL_FORM_REMOVE_FILTER ),
+ FeatureURL( 0, nullptr )
+ };
+ return s_aFeatureURLs;
+ }
+ }
+
+
+ const char* OFormNavigationMapper::getFeatureURLAscii( sal_Int16 _nFeatureId )
+ {
+ const FeatureURL* pFeatures = lcl_getFeatureTable();
+ while ( pFeatures->pAsciiURL )
+ {
+ if ( pFeatures->nFormFeature == _nFeatureId )
+ return pFeatures->pAsciiURL;
+ ++pFeatures;
+ }
+ return nullptr;
+ }
+
+
+ sal_Int16 OFormNavigationMapper::getFeatureId( std::u16string_view _rCompleteURL )
+ {
+ const FeatureURL* pFeatures = lcl_getFeatureTable();
+ while ( pFeatures->pAsciiURL )
+ {
+ if ( o3tl::equalsAscii( _rCompleteURL, pFeatures->pAsciiURL ) )
+ return pFeatures->nFormFeature;
+ ++pFeatures;
+ }
+ return -1;
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/helper/resettable.cxx b/forms/source/helper/resettable.cxx
new file mode 100644
index 0000000000..8994254a6e
--- /dev/null
+++ b/forms/source/helper/resettable.cxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <resettable.hxx>
+
+#include <cppuhelper/weak.hxx>
+
+
+namespace frm
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::form::XResetListener;
+ using ::com::sun::star::lang::EventObject;
+
+
+ //= ResetHelper
+
+
+ void ResetHelper::addResetListener( const Reference< XResetListener >& _listener )
+ {
+ m_aResetListeners.addInterface( _listener );
+ }
+
+
+ void ResetHelper::removeResetListener( const Reference< XResetListener >& _listener )
+ {
+ m_aResetListeners.removeInterface( _listener );
+ }
+
+
+ bool ResetHelper::approveReset()
+ {
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aResetListeners );
+ EventObject aResetEvent( m_rParent );
+
+ bool bContinue = true;
+ while ( aIter.hasMoreElements() && bContinue )
+ bContinue = aIter.next()->approveReset( aResetEvent );
+
+ return bContinue;
+ }
+
+
+ void ResetHelper::notifyResetted()
+ {
+ EventObject aResetEvent( m_rParent );
+ m_aResetListeners.notifyEach( &XResetListener::resetted, aResetEvent );
+ }
+
+
+ void ResetHelper::disposing()
+ {
+ EventObject aEvent( m_rParent );
+ m_aResetListeners.disposeAndClear( aEvent );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/helper/urltransformer.cxx b/forms/source/helper/urltransformer.cxx
new file mode 100644
index 0000000000..e112041c22
--- /dev/null
+++ b/forms/source/helper/urltransformer.cxx
@@ -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 .
+ */
+
+#include <urltransformer.hxx>
+
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <tools/debug.hxx>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::lang;
+
+ UrlTransformer::UrlTransformer( const Reference< XComponentContext >& _rxORB )
+ :m_xORB( _rxORB )
+ ,m_bTriedToCreateTransformer( false )
+ {
+ DBG_ASSERT( _rxORB.is(), "UrlTransformer::UrlTransformer: invalid service factory!" );
+ }
+
+
+ bool UrlTransformer::implEnsureTransformer() const
+ {
+ // create the transformer, if not already attempted to do so
+ if ( !m_xTransformer.is() && !m_bTriedToCreateTransformer )
+ {
+ if ( m_xORB.is() )
+ {
+ m_xTransformer.set(URLTransformer::create(m_xORB));
+ }
+
+ m_bTriedToCreateTransformer = true;
+ }
+ return m_xTransformer.is();
+ }
+
+
+ URL UrlTransformer::getStrictURL( const OUString& _rURL ) const
+ {
+ URL aReturn;
+ aReturn.Complete = _rURL;
+ if ( implEnsureTransformer() )
+ m_xTransformer->parseStrict( aReturn );
+ return aReturn;
+ }
+
+
+ URL UrlTransformer::getStrictURLFromAscii( const char* _pAsciiURL ) const
+ {
+ return getStrictURL( OUString::createFromAscii( _pAsciiURL ) );
+ }
+
+
+ void UrlTransformer::parseSmartWithProtocol( css::util::URL& _rURL, const OUString& _rProtocol ) const
+ {
+ if ( implEnsureTransformer() )
+ m_xTransformer->parseSmart( _rURL, _rProtocol );
+ }
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/helper/windowstateguard.cxx b/forms/source/helper/windowstateguard.cxx
new file mode 100644
index 0000000000..ed7d2932ab
--- /dev/null
+++ b/forms/source/helper/windowstateguard.cxx
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <windowstateguard.hxx>
+#include <frm_strings.hxx>
+
+#include <com/sun/star/awt/XWindowListener2.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+
+namespace frm
+{
+
+
+ using ::com::sun::star::awt::XWindowListener2;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::awt::XWindow2;
+ using ::com::sun::star::awt::WindowEvent;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::awt::XControlModel;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Exception;
+
+ typedef ::cppu::WeakImplHelper < XWindowListener2
+ > WindowStateGuard_Impl_Base;
+ class WindowStateGuard_Impl : public WindowStateGuard_Impl_Base
+ {
+ private:
+ ::osl::Mutex m_aMutex;
+ Reference< XWindow2 > m_xWindow;
+ Reference< XPropertySet > m_xModelProps;
+
+ public:
+ /** constructs the instance
+ @param _rxWindow
+ the window at which to listen. Must not be <NULL/>.
+ @param _rxModel
+ the model which acts as the reference for the states to be enforced. Must not be <NULL/>.
+ */
+ WindowStateGuard_Impl( const Reference< XWindow2 >& _rxWindow, const Reference< XPropertySet >& _rxMdelProps );
+
+ void dispose();
+
+ protected:
+ // XWindowListener2
+ virtual void SAL_CALL windowEnabled( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL windowDisabled( const css::lang::EventObject& e ) override;
+
+ // XWindowListener
+ virtual void SAL_CALL windowResized( const css::awt::WindowEvent& e ) override;
+ virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& e ) override;
+ virtual void SAL_CALL windowShown( const css::lang::EventObject& e ) override;
+ virtual void SAL_CALL windowHidden( const css::lang::EventObject& e ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ private:
+ /** ensures that the window's Enabled state matches what is described at the model
+ @precond
+ our mutex is locked
+ */
+ void impl_ensureEnabledState_nothrow_nolck();
+ };
+
+
+ WindowStateGuard_Impl::WindowStateGuard_Impl( const Reference< XWindow2 >& _rxWindow, const Reference< XPropertySet >& _rxMdelProps )
+ :m_xWindow( _rxWindow )
+ ,m_xModelProps( _rxMdelProps )
+ {
+ if ( !m_xWindow.is() || !m_xModelProps.is() )
+ throw RuntimeException();
+
+ osl_atomic_increment( &m_refCount );
+ {
+ m_xWindow->addWindowListener( this );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ void WindowStateGuard_Impl::dispose()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_xWindow.is() )
+ // already disposed
+ return;
+
+ m_xWindow->removeWindowListener( this );
+ m_xWindow.clear();
+ }
+
+
+ void WindowStateGuard_Impl::impl_ensureEnabledState_nothrow_nolck()
+ {
+ try
+ {
+ Reference< XWindow2 > xWindow;
+ Reference< XPropertySet > xModelProps;
+ bool bShouldBeEnabled = false;
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_xWindow.is() || !m_xModelProps.is() )
+ return;
+ xWindow = m_xWindow;
+ xModelProps = m_xModelProps;
+ }
+ // fdo#42157: do not lock m_aMutex to prevent deadlock
+ bool const bEnabled = xWindow->isEnabled();
+ OSL_VERIFY( xModelProps->getPropertyValue( PROPERTY_ENABLED )
+ >>= bShouldBeEnabled );
+
+ if ( !bShouldBeEnabled && bEnabled )
+ xWindow->setEnable( false );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.helper");
+ }
+ }
+
+
+ void SAL_CALL WindowStateGuard_Impl::windowEnabled( const EventObject& /*e*/ )
+ {
+ impl_ensureEnabledState_nothrow_nolck();
+ }
+
+
+ void SAL_CALL WindowStateGuard_Impl::windowDisabled( const EventObject& /*e*/ )
+ {
+ impl_ensureEnabledState_nothrow_nolck();
+ }
+
+
+ void SAL_CALL WindowStateGuard_Impl::windowResized( const WindowEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL WindowStateGuard_Impl::windowMoved( const WindowEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL WindowStateGuard_Impl::windowShown( const EventObject& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL WindowStateGuard_Impl::windowHidden( const EventObject& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL WindowStateGuard_Impl::disposing( const EventObject& Source )
+ {
+ OSL_ENSURE( Source.Source == m_xWindow, "WindowStateGuard_Impl::disposing: where does this come from?" );
+ dispose();
+ }
+
+ WindowStateGuard::WindowStateGuard()
+ {
+ }
+
+
+ WindowStateGuard::~WindowStateGuard()
+ {
+ }
+
+
+ void WindowStateGuard::attach( const Reference< XWindow2 >& _rxWindow, const Reference< XControlModel >& _rxModel )
+ {
+ if ( m_pImpl.is() )
+ {
+ m_pImpl->dispose();
+ m_pImpl = nullptr;
+ }
+
+ Reference< XPropertySet > xModelProps( _rxModel, UNO_QUERY );
+ OSL_ENSURE( xModelProps.is() || !_rxModel.is(), "WindowStateGuard::attach: a model which is no XPropertySet?" );
+ if ( _rxWindow.is() && xModelProps.is() )
+ m_pImpl = new WindowStateGuard_Impl( _rxWindow, xModelProps );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/FormComponent.hxx b/forms/source/inc/FormComponent.hxx
new file mode 100644
index 0000000000..0a0ec19be5
--- /dev/null
+++ b/forms/source/inc/FormComponent.hxx
@@ -0,0 +1,1207 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "cloneable.hxx"
+#include "propertybaghelper.hxx"
+#include "resettable.hxx"
+#include "windowstateguard.hxx"
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
+#include <com/sun/star/form/validation/XValidityConstraintListener.hpp>
+#include <com/sun/star/form/XBoundComponent.hpp>
+#include <com/sun/star/form/XBoundControl.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/XLoadListener.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <com/sun/star/sdb/XColumnUpdate.hpp>
+#include <com/sun/star/sdb/XRowSetChangeListener.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/uno/XAggregation.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propagg.hxx>
+#include <comphelper/propmultiplex.hxx>
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/component.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <cppuhelper/implbase3.hxx>
+#include <cppuhelper/implbase4.hxx>
+#include <cppuhelper/implbase7.hxx>
+#include <cppuhelper/propshlp.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+
+
+namespace frm
+{
+
+
+ // default tab index for components
+ const sal_Int16 FRM_DEFAULT_TABINDEX = 0;
+
+ class OControlModel;
+
+
+ //= ControlModelLock
+
+ /** class whose instances lock an OControlModel
+
+ Locking here merely means locking the OControlModel's mutex.
+
+ In addition to the locking facility, the class is also able to fire property
+ change notifications. This happens when the last ControlModelLock instance on a stack
+ dies.
+ */
+ class ControlModelLock
+ {
+ public:
+ ControlModelLock( OControlModel& _rModel )
+ :m_rModel( _rModel )
+ ,m_bLocked( false )
+ {
+ acquire();
+ }
+
+ ~ControlModelLock()
+ {
+ if ( m_bLocked )
+ release();
+ }
+ inline void acquire();
+ inline void release();
+
+ OControlModel& getModel() const { return m_rModel; };
+
+ /** adds a property change notification, which is to be fired when the last lock on the model
+ (in the current thread) is released.
+ */
+ void addPropertyNotification(
+ const sal_Int32 _nHandle,
+ const css::uno::Any& _rOldValue,
+ const css::uno::Any& _rNewValue
+ );
+
+ private:
+ void impl_notifyAll_nothrow();
+
+ OControlModel& m_rModel;
+ bool m_bLocked;
+ std::vector< sal_Int32 > m_aHandles;
+ std::vector< css::uno::Any > m_aOldValues;
+ std::vector< css::uno::Any > m_aNewValues;
+
+ ControlModelLock( const ControlModelLock& ) = delete;
+ ControlModelLock& operator=( const ControlModelLock& ) = delete;
+ };
+
+
+//= OControl
+//= base class for form layer controls
+
+typedef ::cppu::ImplHelper3 < css::awt::XControl
+ , css::lang::XEventListener
+ , css::lang::XServiceInfo
+ > OControl_BASE;
+
+class OControl :public ::cppu::OComponentHelper
+ ,public OControl_BASE
+{
+protected:
+ ::osl::Mutex m_aMutex;
+ css::uno::Reference< css::awt::XControl > m_xControl;
+ css::uno::Reference< css::uno::XAggregation>
+ m_xAggregate;
+
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xContext;
+ WindowStateGuard m_aWindowStateGuard;
+
+public:
+ /** constructs a control
+
+ @param _rFactory
+ the service factory for this control
+ @param _rAggregateService
+ the service name of the component to aggregate
+ @param _bSetDelegator
+ set this to <FALSE/> if you don't want the constructor to set the delegator at
+ the aggregate. In this case, you <em>have</em> to call doSetDelegator within your
+ own constructor.
+
+ This is helpful, if your derived class wants to cache an interface of the aggregate.
+ In this case, the aggregate needs to be queried for this interface <b>before</b> the
+ <member scope="css::uno">XAggregation::setDelegator</member> call.
+
+ In such a case, pass <FALSE/> to this parameter. Then, cache the aggregate's interface(s)
+ as needed. Afterwards, call <member>doSetDelegator</member>.
+
+ In your destructor, you need to call <member>doResetDelegator</member> before
+ resetting the cached interfaces. This will reset the aggregates delegator to <NULL/>,
+ which will ensure that the <member scope="css::uno">XInterface::release</member>
+ calls on the cached interfaces are really applied to the aggregate, instead of
+ the <type>OControl</type> itself.
+ */
+ OControl(
+ const css::uno::Reference< css::uno::XComponentContext >& _rFactory,
+ const OUString& _rAggregateService,
+ const bool _bSetDelegator = true
+ );
+
+protected:
+ virtual ~OControl() override;
+
+ /** sets the control as delegator at the aggregate
+
+ This has to be called from within your derived class' constructor, if and only
+ if you passed <FALSE/> to the <arg>_bSetDelegator</arg> parameter of the
+ <type>OControl</type> constructor.
+ */
+ void doSetDelegator();
+ void doResetDelegator();
+
+// UNO
+ DECLARE_UNO3_AGG_DEFAULTS(OControl, OComponentHelper)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+// XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type> SAL_CALL getTypes() override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// XComponent (as base of XControl)
+ virtual void SAL_CALL dispose( ) override
+ { OComponentHelper::dispose(); }
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener>& _rxListener) override
+ { OComponentHelper::addEventListener(_rxListener); }
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener>& _rxListener) override
+ { OComponentHelper::removeEventListener(_rxListener); }
+
+// XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+// XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+ virtual OUString SAL_CALL getImplementationName() override = 0;
+
+// XControl
+ virtual void SAL_CALL setContext(const css::uno::Reference<css::uno::XInterface>& Context) override;
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getContext() override;
+ virtual void SAL_CALL createPeer(const css::uno::Reference<css::awt::XToolkit>& Toolkit, const css::uno::Reference<css::awt::XWindowPeer>& Parent) override;
+ virtual css::uno::Reference<css::awt::XWindowPeer> SAL_CALL getPeer() override;
+ virtual sal_Bool SAL_CALL setModel(const css::uno::Reference<css::awt::XControlModel>& Model) override;
+ virtual css::uno::Reference<css::awt::XControlModel> SAL_CALL getModel() override;
+ virtual css::uno::Reference<css::awt::XView> SAL_CALL getView() override;
+ virtual void SAL_CALL setDesignMode(sal_Bool bOn) override;
+ virtual sal_Bool SAL_CALL isDesignMode() override;
+ virtual sal_Bool SAL_CALL isTransparent() override;
+
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes();
+ // overwrite this and call the base class if you have additional types
+
+ css::uno::Sequence< OUString > getAggregateServiceNames() const;
+
+private:
+ void impl_resetStateGuard_nothrow();
+};
+
+// a form control implementing the XBoundControl interface
+typedef ::cppu::ImplHelper1 < css::form::XBoundControl
+ > OBoundControl_BASE;
+class OBoundControl :public OControl
+ ,public OBoundControl_BASE
+{
+ bool m_bLocked : 1;
+
+public:
+ OBoundControl(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ const OUString& _rAggregateService,
+ const bool _bSetDelegator = true
+ );
+
+ virtual ~OBoundControl() override;
+
+ DECLARE_UNO3_AGG_DEFAULTS(OBoundControl, OControl)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XBoundControl
+ virtual sal_Bool SAL_CALL getLock() override;
+ virtual void SAL_CALL setLock(sal_Bool _bLock) override;
+ // default implementation just disables the controls, overwrite _setLock to change this behaviour
+
+ // XControl
+ virtual sal_Bool SAL_CALL setModel(const css::uno::Reference< css::awt::XControlModel >& Model) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+protected:
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+ // implement the lock setting
+ void _setLock(bool _bLock);
+};
+
+
+//= OControlModel
+//= model of a form layer control
+
+//added for exporting OCX control
+#define INVALID_OBJ_ID_IN_MSO 0xFFFF
+
+typedef ::cppu::ImplHelper7 < css::form::XFormComponent
+ , css::io::XPersistObject
+ , css::container::XNamed
+ , css::lang::XServiceInfo
+ , css::util::XCloneable
+ , css::beans::XPropertyContainer
+ , css::beans::XPropertyAccess
+ > OControlModel_BASE;
+
+class OControlModel :public ::cppu::OComponentHelper
+ ,public comphelper::OPropertySetAggregationHelper
+ ,public OControlModel_BASE
+ ,public OCloneableAggregation
+ ,public IPropertyBagHelperContext
+{
+
+protected:
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+
+ ::osl::Mutex m_aMutex;
+ oslInterlockedCount m_lockCount;
+
+ css::uno::Reference<css::uno::XInterface> m_xParent; // ParentComponent
+ PropertyBagHelper m_aPropertyBagHelper;
+
+ const css::uno::Reference<css::uno::XComponentContext>&
+ getContext() const { return m_xContext; }
+
+// <properties>
+ OUString m_aName; // name of the control
+ OUString m_aTag; // tag for additional data
+ sal_Int16 m_nTabIndex; // index within the taborder
+ sal_Int16 m_nClassId; // type of the control
+ bool m_bNativeLook; // should the control use the native platform look?
+ bool m_bStandardTheme; // should the default control colors be 'standard' or use the native platform theme?
+ bool m_bGenerateVbEvents; // should the control generate fake vba events
+ //added for exporting OCX control
+ sal_Int16 m_nControlTypeinMSO; //keep the MS office control type for exporting to MS binary file
+ sal_uInt16 m_nObjIDinMSO; //keep the OCX control obj id for exporting to MS binary file
+// </properties>
+
+
+protected:
+ OControlModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rFactory, // factory to create the aggregate with
+ const OUString& _rUnoControlModelTypeName, // service name of te model to aggregate
+ const OUString& rDefault = OUString(), // service name of the default control
+ const bool _bSetDelegator = true // set to sal_False if you want to call setDelegator later (after returning from this ctor)
+ );
+ OControlModel(
+ const OControlModel* _pOriginal, // the original object to clone
+ const css::uno::Reference< css::uno::XComponentContext>& _rFactory, // factory to create the aggregate with
+ const bool _bCloneAggregate = true, // should the aggregate of the original be cloned, too?
+ const bool _bSetDelegator = true // set to sal_False if you want to call setDelegator later (after returning from this ctor)
+ );
+ virtual ~OControlModel() override;
+
+ /** to be called after an OBoundControlModel (a derivee, respectively) has been cloned
+
+ <p>This method contains late initializations which cannot be done in the
+ constructor of this base class, since the virtual method of derived classes do
+ not yet work there.</p>
+ */
+ virtual void clonedFrom( const OControlModel* _pOriginal );
+
+ using OComponentHelper::rBHelper;
+
+ virtual css::uno::Sequence< css::uno::Type> _getTypes();
+
+ void readHelpTextCompatibly(const css::uno::Reference< css::io::XObjectInputStream >& _rxInStream);
+ void writeHelpTextCompatibly(const css::uno::Reference< css::io::XObjectOutputStream >& _rxOutStream);
+
+ void doSetDelegator();
+ void doResetDelegator();
+
+ css::uno::Sequence< OUString > getAggregateServiceNames() const;
+
+public:
+ DECLARE_UNO3_AGG_DEFAULTS(OControl, OComponentHelper)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+// XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type> SAL_CALL getTypes() override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName(const OUString& aName) override;
+
+// XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+ virtual OUString SAL_CALL getImplementationName() override = 0;
+
+// XServiceInfo - static version(s)
+ /// @throws css::uno::RuntimeException
+ static css::uno::Sequence<OUString> getSupportedServiceNames_Static();
+
+// XPersistObject
+ virtual OUString SAL_CALL getServiceName() override = 0;
+ 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;
+
+// XChild (base of XFormComponent)
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getParent() override;
+ virtual void SAL_CALL setParent(const css::uno::Reference<css::uno::XInterface>& Parent) override;
+
+// XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+// XPropertySet
+ 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 ::cppu::OPropertySetHelper::getFastPropertyValue;
+
+// css::beans::XPropertyState
+ virtual css::beans::PropertyState getPropertyStateByHandle(sal_Int32 nHandle) override;
+ virtual void setPropertyToDefaultByHandle(sal_Int32 nHandle) override;
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+// XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override = 0;
+
+// XPropertyContainer
+ virtual void SAL_CALL addProperty( const OUString& Name, ::sal_Int16 Attributes, const css::uno::Any& DefaultValue ) override;
+ virtual void SAL_CALL removeProperty( const OUString& Name ) override;
+
+// XPropertyAccess
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getPropertyValues( ) override;
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& aProps ) override;
+
+protected:
+ using OPropertySetAggregationHelper::setPropertyValues;
+ using OPropertySetAggregationHelper::getPropertyValues;
+
+protected:
+ virtual void writeAggregate( const css::uno::Reference< css::io::XObjectOutputStream >& _rxOutStream ) const;
+ virtual void readAggregate( const css::uno::Reference< css::io::XObjectInputStream >& _rxInStream );
+
+protected:
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ // OPropertySetHelper
+ virtual cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ /** describes the properties provided by this class, or its respective
+ derived class
+
+ Derived classes usually call the base class first, and then append own properties.
+ */
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const;
+
+ // IPropertyBagHelperContext
+ virtual ::osl::Mutex& getMutex() override;
+ virtual void describeFixedAndAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& _out_rFixedProperties,
+ css::uno::Sequence< css::beans::Property >& _out_rAggregateProperties
+ ) const override;
+ virtual css::uno::Reference< css::beans::XMultiPropertySet >
+ getPropertiesInterface() override;
+
+ /** describes the properties of our aggregate
+
+ The default implementation simply asks m_xAggregateSet for its properties.
+
+ You usually only need to override this method if you want to filter the
+ aggregate properties.
+ */
+ virtual void describeAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const;
+
+public:
+ struct LockAccess { friend class ControlModelLock; private: LockAccess() { } };
+
+ void lockInstance( LockAccess );
+ oslInterlockedCount unlockInstance( LockAccess );
+
+ void firePropertyChanges(
+ const std::vector< sal_Int32 >& _rHandles,
+ const std::vector< css::uno::Any >& _rOldValues,
+ const std::vector< css::uno::Any >& _rNewValues,
+ LockAccess
+ );
+
+ ::osl::Mutex&
+ getInstanceMutex() { return m_aMutex; }
+};
+
+//= OBoundControlModel
+//= model of a form layer control which is bound to a data source field
+
+typedef ::cppu::ImplHelper4 < css::form::XLoadListener
+ , css::form::XReset
+ , css::beans::XPropertyChangeListener
+ , css::sdb::XRowSetChangeListener
+ > OBoundControlModel_BASE1;
+
+// separated into an own base class since derivees can disable the support for this
+// interface, thus we want to easily exclude it in the queryInterface and getTypes
+typedef ::cppu::ImplHelper1 < css::form::XBoundComponent
+ > OBoundControlModel_COMMITTING;
+
+// ditto
+typedef ::cppu::ImplHelper2 < css::form::binding::XBindableValue
+ , css::util::XModifyListener
+ > OBoundControlModel_BINDING;
+
+// ditto
+typedef ::cppu::ImplHelper2 < css::form::validation::XValidityConstraintListener
+ , css::form::validation::XValidatableFormComponent
+ > OBoundControlModel_VALIDATION;
+
+class OBoundControlModel :public OControlModel
+ ,public OBoundControlModel_BASE1
+ ,public OBoundControlModel_COMMITTING
+ ,public OBoundControlModel_BINDING
+ ,public OBoundControlModel_VALIDATION
+ ,public ::comphelper::OPropertyChangeListener
+{
+protected:
+ enum ValueChangeInstigator
+ {
+ eDbColumnBinding,
+ eExternalBinding,
+ eOther
+ };
+
+private:
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xField;
+ // the form which controls supplies the field we bind to.
+ css::uno::Reference< css::form::XLoadable >
+ m_xAmbientForm;
+
+ OUString m_sValuePropertyName;
+ sal_Int32 m_nValuePropertyAggregateHandle;
+ sal_Int32 m_nFieldType;
+ css::uno::Type m_aValuePropertyType;
+ bool m_bValuePropertyMayBeVoid;
+
+ ResetHelper m_aResetHelper;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XUpdateListener>
+ m_aUpdateListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::validation::XFormComponentValidityListener>
+ m_aFormComponentListeners;
+
+ css::uno::Reference< css::form::binding::XValueBinding >
+ m_xExternalBinding;
+ css::uno::Reference< css::form::validation::XValidator >
+ m_xValidator;
+ css::uno::Type m_aExternalValueType;
+
+// <properties>
+ OUString m_aControlSource; // data source, name of the field
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xLabelControl; // reference to a sibling control (model) which is our label
+ bool m_bInputRequired;
+// </properties>
+
+ rtl::Reference<::comphelper::OPropertyChangeMultiplexer>
+ m_pAggPropMultiplexer;
+
+ bool m_bFormListening : 1; // are we currently a XLoadListener at our ambient form?
+ bool m_bLoaded : 1;
+ bool m_bRequired : 1;
+ const bool m_bCommitable : 1; // do we support XBoundComponent?
+ const bool m_bSupportsExternalBinding : 1; // do we support XBindableValue?
+ const bool m_bSupportsValidation : 1; // do we support XValidatable?
+ bool m_bForwardValueChanges : 1; // do we currently handle changes in the bound database field?
+ bool m_bTransferringValue : 1; // true if we're currently transferring our value to an external binding
+ bool m_bIsCurrentValueValid : 1; // flag specifying whether our current value is valid, relative to our external validator
+ bool m_bBindingControlsRO : 1; // is our ReadOnly property currently controlled by our external binding?
+ bool m_bBindingControlsEnable : 1; // is our Enabled property currently controlled by our external binding?
+
+ ValueChangeInstigator m_eControlValueChangeInstigator;
+
+protected:
+ OUString m_aLabelServiceName;
+ // when setting the label for our control (property FM_PROP_CONTROLLABEL, member m_xLabelControl),
+ // we accept only objects supporting an XControlModel interface, an XServiceInfo interface and
+ // support for a service (XServiceInfo::supportsService) determined by this string.
+ // Any other arguments will throw an IllegalArgumentException.
+ // The default value is FM_COMPONENT_FIXEDTEXT.
+
+ css::uno::Reference< css::sdbc::XRowSet >
+ m_xCursor;
+ css::uno::Reference< css::sdb::XColumnUpdate >
+ m_xColumnUpdate;
+ css::uno::Reference< css::sdb::XColumn >
+ m_xColumn;
+
+protected:
+ sal_Int32 getValuePropertyAggHandle( ) const { return m_nValuePropertyAggregateHandle; }
+ const OUString& getControlSource( ) const { return m_aControlSource; }
+ bool isRequired() const { return m_bRequired; }
+ bool isLoaded() const { return m_bLoaded; }
+
+protected:
+
+ OBoundControlModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxContext,
+ // factory to create the aggregate with
+ const OUString& _rUnoControlModelTypeName, // service name of te model to aggregate
+ const OUString& _rDefault, // service name of the default control
+ const bool _bCommitable, // is the control (model) committable?
+ const bool _bSupportExternalBinding, // set to sal_True if you want to support XBindableValue
+ const bool _bSupportsValidation // set to sal_True if you want to support XValidatable
+ );
+ OBoundControlModel(
+ const OBoundControlModel* _pOriginal, // the original object to clone
+ const css::uno::Reference< css::uno::XComponentContext>& _rxContext
+ // factory to create the aggregate with
+ );
+ virtual ~OBoundControlModel() override;
+
+ /// late ctor after cloning
+ virtual void clonedFrom( const OControlModel* _pOriginal ) override;
+
+ /** initializes the part of the class which is related to the control value.
+
+ <p>Kind of late ctor, to be called for derivees which have a dedicated value property.<br/>
+ The value property is the property which's value is synced with either the database
+ column the object is bound to, or with the external value binding, if present.<br/>
+ E.g. for a text control model, this property will most probably be "Text".</p>
+
+ <p>Derived classes are strongly recommended to call this method - at least the
+ "DataFieldProperty" (exposed in getFastPropertyValue) relies on the information
+ given herein, and needs to be supplied otherwise else.</p>
+
+ <p>If this method has been called properly, then <member>setControlValue</member>
+ does not need to be overridden - it will simply set the property value at the
+ aggregate then.</p>
+
+ @precond
+ The method has not be called before during the life time of the object.
+
+ @param _rValuePropertyName
+ the name of the value property
+ @param _nValuePropertyExternalHandle
+ the handle of the property, as exposed to external components.<br/>
+ Normally, this information can be obtained dynamically (e.g. from describeFixedProperties),
+ but since this method is to be called from within the constructor of derived classes,
+ we prefer to be on the *really* safe side here...
+
+ @see setControlValue
+ @see suspendValueListening
+ @see resumeValueListening
+ @see describeFixedProperties
+ */
+ void initValueProperty(
+ const OUString& _rValuePropertyName,
+ sal_Int32 _nValuePropertyExternalHandle
+ );
+
+ /** initializes the part of the class which is related to the control value.
+
+ <p>In opposite to ->initValueProperty, this method is to be used for value properties which are <em>not</em>
+ implemented by our aggregate, but by ourselves.</p>
+
+ <p>Certain functionality is not available when using own value properties. This includes binding to an external
+ value and external validation. (This is not a conceptual limit, but simply missing implementation.)</p>
+ */
+ void initOwnValueProperty(
+ const OUString& i_rValuePropertyName
+ );
+
+ /** suspends listening at the value property
+
+ <p>As long as this listening is suspended, changes in the value property will not be
+ recognized and not be handled.</p>
+
+ @see initValueProperty
+ @see resumeValueListening
+ */
+ void suspendValueListening( );
+
+ /** resumes listening at the value property
+
+ <p>As long as this listening is suspended, changes in the value property will not be
+ recognized and not be handled.</p>
+
+ @precond
+ listening at the value property is currently suspended
+
+ @see initValueProperty
+ @see resumeValueListening
+ */
+ void resumeValueListening( );
+
+ /** (to be) called when the value property changed
+
+ Normally, this is done automatically, since the value property is a property of our aggregate, and we're
+ a listener at this property.
+ However, in some cases the value property might not be an aggregate property, but a property of the
+ delegator instance. In this case, you'll need to call <code>onValuePropertyChange</code> whenever this
+ property changes.
+ */
+ void onValuePropertyChange( ControlModelLock& i_rControLock );
+
+ /** starts listening at the aggregate, for changes in the given property
+
+ <p>The OBoundControlModel automatically registers a multiplexer which listens for
+ changes in the aggregate property values. By default, only the control value property
+ is observed. You may add additional properties to be observed with this method.</p>
+
+ @see initValueProperty
+ @see _propertyChanged
+ */
+ void startAggregatePropertyListening( const OUString& _rPropertyName );
+
+ /** returns the default which should be used when resetting the control
+
+ <p>The default implementation returns an empty Any.</p>
+
+ @see resetNoBroadcast
+ */
+ virtual css::uno::Any
+ getDefaultForReset() const;
+
+ /** translates a db column value into a control value.
+
+ <p>Must transform the very current value of the database column we're bound to
+ (<member>m_xColumn</member>) into a value which can be used as current value
+ for the control.</p>
+
+ @see setControlValue
+ @pure
+ */
+ virtual css::uno::Any
+ translateDbColumnToControlValue( ) = 0;
+
+ /** returns the data types which the control could use to exchange data with
+ an external value binding
+
+ The types returned here are completely independent from the concrete value binding,
+ they're just candidates which depend on the control type, and possible the concrete state
+ of the control (i.e. some property value).
+
+ If a control implementation supports multiple types, the ordering in the returned
+ sequence indicates preference: Preferred types are mentioned first.
+
+ The default implementation returns the type of our value property.
+ */
+ virtual css::uno::Sequence< css::uno::Type >
+ getSupportedBindingTypes();
+
+ /** translates the given value, which was obtained from the current external value binding,
+ to a value which can be used in setControlValue
+
+ <p>The default implementation returns the value itself, exception when it is VOID, and
+ our value property is not allowed to be void - in this case, the returned value is a
+ default-constructed value of the type required by our value property.
+
+ @see hasExternalValueBinding
+ @see getExternalValueType
+ */
+ virtual css::uno::Any
+ translateExternalValueToControlValue( const css::uno::Any& _rExternalValue ) const;
+
+ /** commits the current control value to our external value binding
+
+ <p>The default implementation simply calls getControlValue.</p>
+
+ @see hasExternalValueBinding
+ @see initValueProperty
+ */
+ virtual css::uno::Any
+ translateControlValueToExternalValue( ) const;
+
+ /** commits the current control value to the database column we're bound to
+ @precond
+ we're properly bound to a database column, especially <member>m_xColumnUpdate</member>
+ is not <NULL/>
+ @param _bPostReset
+ <TRUE/> if and only if the current control value results from a reset (<member>getDefaultForReset</member>)
+ @pure
+ */
+ virtual bool commitControlValueToDbColumn(
+ bool _bPostReset
+ ) = 0;
+
+ /** sets the given value as new current value for the control
+
+ Besides some administrative work (such as caring for <member>m_eControlValueChangeInstigator</member>),
+ this method simply calls <member>doSetControlValue</member>.
+
+ @precond
+ Our own mutex is locked.
+ @param _rValue
+ The value to set. This value is guaranteed to be created by
+ <member>translateDbColumnToControlValue</member> or
+ <member>translateExternalValueToControlValue</member>
+ @param _eInstigator
+ the instigator of the value change
+ */
+ void setControlValue(
+ const css::uno::Any& _rValue,
+ ValueChangeInstigator _eInstigator
+ );
+ /**
+ <p>The default implementation will forward the given value to the aggregate, using
+ m_nValuePropertyAggregateHandle and/or m_sValuePropertyName.</p>
+
+ @precond
+ Our own mutex is locked.
+ @param _rValue
+ The value to set. This value is guaranteed to be created by
+ <member>translateDbColumnToControlValue</member> or
+ <member>translateExternalValueToControlValue</member>
+ */
+ virtual void doSetControlValue(
+ const css::uno::Any& _rValue
+ );
+
+ /** retrieves the current value of the control
+
+ <p>The default implementation will ask the aggregate for the property value
+ determined by either m_nValuePropertyAggregateHandle and/or m_sValuePropertyName.</p>
+
+ @precond
+ Our own mutex is locked.
+ */
+ virtual css::uno::Any
+ getControlValue( ) const;
+
+ /** called whenever a connection to a database column has been established
+ */
+ virtual void onConnectedDbColumn( const css::uno::Reference< css::uno::XInterface >& _rxForm );
+ /** called whenever a connection to a database column has been suspended
+ */
+ virtual void onDisconnectedDbColumn();
+
+ /** called whenever a connection to an external supplier of values (XValueBinding) has been established
+ @see m_xExternalBinding
+ */
+ virtual void onConnectedExternalValue( );
+
+ /** called whenever an external validator has been registered
+ */
+ void onConnectedValidator( );
+ /** called whenever an external validator has been revoked
+ */
+ void onDisconnectedValidator( );
+
+ /** nFieldType is the type of the field, on which the model will be linked.
+ The linking happens when sal_True is returned.
+ The default-implementation allows everything but the three binary types
+ and FieldType_OTHER.
+ */
+ virtual bool approveDbColumnType(sal_Int32 _nColumnType);
+
+ /** retrieves the current value of the control, in a shape which can be used with our
+ external validator.
+
+ The default implementation simply calls <member>>translateControlValueToExternalValue</member>.
+
+ @precond
+ Our own mutex is locked.
+ */
+ virtual css::uno::Any
+ translateControlValueToValidatableValue( ) const;
+
+ /** retrieves the current value of the form component
+
+ This is the implementation method for XValidatableFormComponent::getCurrentValue. The default implementation
+ calls translateControlValueToValidatableValue if a validator is present, otherwise getControlValue.
+
+ @precond
+ our mutex is locked when this method is called
+ */
+ virtual css::uno::Any
+ getCurrentFormComponentValue() const;
+
+ /** We can't write (new) common properties in this base class, as the file format doesn't allow this
+ (unfortunately). So derived classes may use the following two methods. They secure the written
+ data with marks, so any new common properties in newer versions will be skipped by older ones.
+ */
+ void writeCommonProperties(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream);
+ void readCommonProperties(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream);
+ // the next method may be used in derived classes's read when an unknown version is encountered
+ void defaultCommonProperties();
+
+ /** called to reset the control to some kind of default.
+
+ <p>The semantics of "default" is finally defined by the derived class (in particular,
+ by <member>getDefaultForReset</member>).</p>
+
+ <p>No listener notification needs to be done in the derived class.</p>
+
+ <p>Normally, you won't override this method, but <member>getDefaultForReset</member> instead.</p>
+
+ @see getDefaultForReset
+ */
+ virtual void resetNoBroadcast();
+
+ virtual css::uno::Sequence< css::uno::Type> _getTypes() override;
+
+ /// sets m_xField to the given new value, without notifying our listeners
+ void impl_setField_noNotify(
+ const css::uno::Reference< css::beans::XPropertySet>& _rxField
+ );
+ bool hasField() const
+ {
+ return m_xField.is();
+ }
+ sal_Int32 getFieldType() const
+ {
+ return m_nFieldType;
+ }
+
+ // OControlModel's property handling
+ virtual void describeFixedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps
+ ) const override;
+
+public:
+ const css::uno::Reference< css::beans::XPropertySet>& getField() const
+ {
+ return m_xField;
+ }
+
+public:
+ // UNO link
+ DECLARE_UNO3_AGG_DEFAULTS(OBoundControlModel, OControlModel)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() 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;
+
+ // XServiceInfo
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames( ) override;
+
+ // XServiceInfo - static version
+ /// @throws css::uno::RuntimeException
+ static css::uno::Sequence<OUString> getSupportedServiceNames_Static();
+
+ // XChild
+ virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override;
+
+ // XPersistObject
+ virtual void SAL_CALL write( const css::uno::Reference< css::io::XObjectOutputStream >& OutStream ) override;
+ virtual void SAL_CALL read( const css::uno::Reference< css::io::XObjectInputStream >& InStream ) override;
+
+ // XBoundComponent
+ virtual sal_Bool SAL_CALL commit() override;
+
+ // XUpdateBroadcaster (base of XBoundComponent)
+ virtual void SAL_CALL addUpdateListener( const css::uno::Reference< css::form::XUpdateListener >& aListener ) override;
+ virtual void SAL_CALL removeUpdateListener( const css::uno::Reference< css::form::XUpdateListener >& aListener ) override;
+
+ // XPropertySet
+ 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 ::cppu::OPropertySetHelper::getFastPropertyValue;
+
+// css::beans::XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override;
+
+// XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+// XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override;
+
+ // XRowSetChangeListener
+ virtual void SAL_CALL onRowSetChanged( const css::lang::EventObject& i_Event ) override;
+
+// XLoadListener
+ virtual void SAL_CALL loaded( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL unloading( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL unloaded( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL reloading( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL reloaded( const css::lang::EventObject& aEvent ) override;
+
+protected:
+ // XBindableValue
+ virtual void SAL_CALL setValueBinding( const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding ) override;
+ virtual css::uno::Reference< css::form::binding::XValueBinding > SAL_CALL getValueBinding( ) override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& _rEvent ) override;
+
+ // XValidatable
+ virtual void SAL_CALL setValidator( const css::uno::Reference< css::form::validation::XValidator >& Validator ) override;
+ virtual css::uno::Reference< css::form::validation::XValidator > SAL_CALL getValidator( ) override;
+
+ // XValidityConstraintListener
+ virtual void SAL_CALL validityConstraintChanged( const css::lang::EventObject& Source ) override;
+
+ // XValidatableFormComponent
+ virtual sal_Bool SAL_CALL isValid( ) override;
+ virtual css::uno::Any SAL_CALL getCurrentValue( ) override;
+ virtual void SAL_CALL addFormComponentValidityListener( const css::uno::Reference< css::form::validation::XFormComponentValidityListener >& Listener ) override;
+ virtual void SAL_CALL removeFormComponentValidityListener( const css::uno::Reference< css::form::validation::XFormComponentValidityListener >& Listener ) override;
+
+protected:
+ // OPropertyChangeListener
+ virtual void
+ _propertyChanged( const css::beans::PropertyChangeEvent& _rEvt ) override;
+
+ /// checks whether we currently have an external value binding in place
+ bool hasExternalValueBinding() const { return m_xExternalBinding.is(); }
+
+ // checks whether we currently have an external validator
+ bool hasValidator() const { return m_xValidator.is(); }
+
+ /** transfers the very current value of the db column we're bound to the control
+ @precond
+ our own mutex is locked
+ @precond
+ we don't have an external binding in place
+ */
+ void transferDbValueToControl( );
+
+ /** transfers the current value of the active external binding to the control
+ @precond
+ we do have an active external binding in place
+ */
+ void transferExternalValueToControl( ControlModelLock& _rInstanceLock );
+
+ /** transfers the control value to the external binding
+ @precond
+ our own mutex is locked, and _rInstanceLock is the guard locking it
+ @precond
+ we do have an active external binding in place
+ */
+ void transferControlValueToExternal( ControlModelLock& _rInstanceLock );
+
+ /** calculates the type which is to be used to communicate with the current external binding,
+ and stores it in m_aExternalValueType
+
+ The method checks the possible type candidates as returned by getSupportedBindingTypes,
+ and the types supported by the current external binding, if any.
+ */
+ void calculateExternalValueType();
+
+ /** returns the type which should be used to exchange data with our external value binding
+
+ @see initValueProperty
+ */
+ const css::uno::Type&
+ getExternalValueType() const { return m_aExternalValueType; }
+
+ /** initializes the control from m_xField
+
+ Basically, this method calls transferDbValueToControl - but only if our cursor is positioned
+ on a valid row. Otherwise, the control is reset.
+
+ @precond
+ m_xField is not <NULL/>
+ */
+ void initFromField( const css::uno::Reference< css::sdbc::XRowSet>& _rxForm );
+
+private:
+ void connectToField( const css::uno::Reference< css::sdbc::XRowSet>& _rxForm );
+ void resetField();
+
+ /** does a new validation of the control value
+
+ If necessary, our <member>m_bIsCurrentValueValid</member> member will be adjusted,
+ and changes will be notified.
+
+ Note that it's not necessary that we're connected to a validator. If we are not,
+ it's assumed that our value is valid, and this is handled appropriately.
+
+ Use this method if there is a potential that <b>only</b> the validity flag changed. If
+ any of the other aspects (our current value, or our current text) changed, then
+ pass <TRUE/> for <member>_bForceNotification</member>.
+
+ @param _bForceNotification
+ if <TRUE/>, then the validity listeners will be notified, not matter whether the validity
+ changed.
+ */
+ void recheckValidity( bool _bForceNotification );
+
+ /// initializes m_pAggPropMultiplexer
+ void implInitAggMultiplexer( );
+
+ /// initializes listening at the value property
+ void implInitValuePropertyListening( ) const;
+
+ /** adds or removes the component as load listener to/from our form, and (if necessary) as RowSetChange listener at
+ our parent.
+
+ @precond there must no external value binding be in place
+ */
+ void doFormListening( const bool _bStart );
+
+ bool isFormListening() const { return m_bFormListening; }
+
+ /** determines the new value of m_xAmbientForm
+ */
+ void impl_determineAmbientForm_nothrow();
+
+ /** connects to a value supplier which is a database column.
+
+ The column is taken from our parent, which must be a database form respectively row set.
+
+ @precond The control does not have an external value supplier
+
+ @param _bFromReload
+ Determines whether the connection is made after the row set has been loaded (<FALSE/>)
+ or reloaded (<TRUE/>)
+
+ @see impl_disconnectDatabaseColumn_noNotify
+ */
+ void impl_connectDatabaseColumn_noNotify(
+ bool _bFromReload
+ );
+
+ /** disconnects from a value supplier which is a database column
+
+ @precond The control does not have an external value supplier
+ @see impl_connectDatabaseColumn_noNotify
+ */
+ void impl_disconnectDatabaseColumn_noNotify();
+
+ /** connects to an external value binding
+
+ <p>Note that by definition, external data bindings supersede the SQL data binding which
+ is defined by our RowSet-column-related properties. This means that in case we're currently
+ connected to a database column when this is called, this connection is suspended.</p>
+
+ @precond
+ the new external binding has already been approved (see <member>impl_approveValueBinding_nolock</member>)
+ @precond
+ there currently is no external binding in place
+ */
+ void connectExternalValueBinding(
+ const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding,
+ ControlModelLock& _rInstanceLock
+ );
+
+ /** disconnects from an external value binding
+
+ @precond
+ there currently is an external binding in place
+ */
+ void disconnectExternalValueBinding( );
+
+ /** connects the component to an external validator
+
+ @precond
+ there currently is no active validator
+ @precond
+ our mutex is currently locked exactly once
+ */
+ void connectValidator(
+ const css::uno::Reference< css::form::validation::XValidator >& _rxValidator
+ );
+
+ /** disconnects the component from its current an external validator
+
+ @precond
+ there currently is an active validator
+ @precond
+ our mutex is currently locked exactly once
+ */
+ void disconnectValidator( );
+
+ /** called from within <member scope="css:::form::binding">XBindableValue::setValueBinding</member>
+ to approve the new binding
+
+ The default implementation approves the binding if and only if it is not <NULL/>, and supports
+ the type returned by getExternalValueType.
+
+ @param _rxBinding
+ the binding which applies for being responsible for our value, Must not be
+ <NULL/>
+ @return
+ <TRUE/> if and only if the given binding can supply values in the proper type
+
+ @seealso getExternalValueType
+ */
+ bool impl_approveValueBinding_nolock(
+ const css::uno::Reference< css::form::binding::XValueBinding >& _rxBinding
+ );
+};
+
+
+ //= inlines
+
+ inline void ControlModelLock::acquire()
+ {
+ m_rModel.lockInstance( OControlModel::LockAccess() );
+ m_bLocked = true;
+ }
+ inline void ControlModelLock::release()
+ {
+ OSL_ENSURE( m_bLocked, "ControlModelLock::release: not locked!" );
+ m_bLocked = false;
+
+ if ( 0 == m_rModel.unlockInstance( OControlModel::LockAccess() ) )
+ impl_notifyAll_nothrow();
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/InterfaceContainer.hxx b/forms/source/inc/InterfaceContainer.hxx
new file mode 100644
index 0000000000..66135a02e4
--- /dev/null
+++ b/forms/source/inc/InterfaceContainer.hxx
@@ -0,0 +1,301 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <comphelper/uno3.hxx>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <osl/mutex.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/component.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <cppuhelper/implbase8.hxx>
+#include <unordered_map>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace comphelper;
+
+
+namespace frm
+{
+
+
+ struct ElementDescription
+ {
+ public:
+ css::uno::Reference< css::uno::XInterface > xInterface;
+ css::uno::Reference< css::beans::XPropertySet > xPropertySet;
+ css::uno::Reference< css::container::XChild > xChild;
+ css::uno::Any aElementTypeInterface;
+
+ public:
+ ElementDescription( );
+
+ private:
+ ElementDescription( const ElementDescription& ) = delete;
+ ElementDescription& operator=( const ElementDescription& ) = delete;
+ };
+
+typedef std::vector<css::uno::Reference<css::uno::XInterface>> OInterfaceArray;
+typedef std::unordered_multimap< OUString, css::uno::Reference<css::uno::XInterface> > OInterfaceMap;
+
+
+// OInterfaceContainer
+// implements a container for form components
+
+typedef ::cppu::ImplHelper8 < css::container::XNameContainer
+ , css::container::XIndexContainer
+ , css::container::XContainer
+ , css::container::XEnumerationAccess
+ , css::script::XEventAttacherManager
+ , css::beans::XPropertyChangeListener
+ , css::io::XPersistObject
+ , css::util::XCloneable
+ > OInterfaceContainer_BASE;
+
+class OInterfaceContainer : public OInterfaceContainer_BASE
+{
+protected:
+ ::osl::Mutex& m_rMutex;
+
+ OInterfaceArray m_aItems;
+ OInterfaceMap m_aMap;
+ ::comphelper::OInterfaceContainerHelper3<css::container::XContainerListener> m_aContainerListeners;
+
+ const css::uno::Type m_aElementType;
+
+ css::uno::Reference< css::uno::XComponentContext> m_xContext;
+
+
+ // EventManager
+ css::uno::Reference< css::script::XEventAttacherManager> m_xEventAttacher;
+
+public:
+ OInterfaceContainer(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory,
+ ::osl::Mutex& _rMutex,
+ const css::uno::Type& _rElementType);
+
+ OInterfaceContainer( ::osl::Mutex& _rMutex, const OInterfaceContainer& _cloneSource );
+
+ // late constructor for cloning
+ /// @throws css::uno::RuntimeException
+ void clonedFrom(const OInterfaceContainer& _cloneSource);
+
+protected:
+ virtual ~OInterfaceContainer();
+
+public:
+// css::io::XPersistObject
+ virtual OUString SAL_CALL getServiceName( ) override = 0;
+ virtual void SAL_CALL write( const css::uno::Reference< css::io::XObjectOutputStream >& OutStream ) override;
+ virtual void SAL_CALL read( const css::uno::Reference< css::io::XObjectInputStream >& InStream ) override;
+
+// css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& _rSource) override;
+
+// css::beans::XPropertyChangeListener
+ virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override;
+
+// css::container::XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override ;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+// css::container::XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration> SAL_CALL createEnumeration() override;
+
+// css::container::XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+// css::container::XNameReplace
+ virtual void SAL_CALL replaceByName(const OUString& Name, const css::uno::Any& _rElement) override;
+
+// css::container::XNameContainer
+ virtual void SAL_CALL insertByName(const OUString& Name, const css::uno::Any& _rElement) override;
+ virtual void SAL_CALL removeByName(const OUString& Name) override;
+
+// css::container::XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 _nIndex) override;
+
+// css::container::XIndexReplace
+ virtual void SAL_CALL replaceByIndex(sal_Int32 _nIndex, const css::uno::Any& _rElement) override;
+
+// css::container::XIndexContainer
+ virtual void SAL_CALL insertByIndex(sal_Int32 _nIndex, const css::uno::Any& Element) override;
+ virtual void SAL_CALL removeByIndex(sal_Int32 _nIndex) override;
+
+// css::container::XContainer
+ virtual void SAL_CALL addContainerListener(const css::uno::Reference< css::container::XContainerListener>& _rxListener) override;
+ virtual void SAL_CALL removeContainerListener(const css::uno::Reference< css::container::XContainerListener>& _rxListener) override;
+
+// css::script::XEventAttacherManager
+ virtual void SAL_CALL registerScriptEvent( sal_Int32 nIndex, const css::script::ScriptEventDescriptor& aScriptEvent ) override;
+ virtual void SAL_CALL registerScriptEvents( sal_Int32 nIndex, const css::uno::Sequence< css::script::ScriptEventDescriptor >& aScriptEvents ) override;
+ virtual void SAL_CALL revokeScriptEvent( sal_Int32 nIndex, const OUString& aListenerType, const OUString& aEventMethod, const OUString& aRemoveListenerParam ) override;
+ virtual void SAL_CALL revokeScriptEvents( sal_Int32 nIndex ) override;
+ virtual void SAL_CALL insertEntry( sal_Int32 nIndex ) override;
+ virtual void SAL_CALL removeEntry( sal_Int32 nIndex ) override;
+ virtual css::uno::Sequence< css::script::ScriptEventDescriptor > SAL_CALL getScriptEvents( sal_Int32 Index ) override;
+ virtual void SAL_CALL attach( sal_Int32 nIndex, const css::uno::Reference< css::uno::XInterface >& xObject, const css::uno::Any& aHelper ) override;
+ virtual void SAL_CALL detach( sal_Int32 nIndex, const css::uno::Reference< css::uno::XInterface >& xObject ) override;
+ virtual void SAL_CALL addScriptListener( const css::uno::Reference< css::script::XScriptListener >& xListener ) override;
+ virtual void SAL_CALL removeScriptListener( const css::uno::Reference< css::script::XScriptListener >& Listener ) override;
+
+protected:
+ // helper
+ virtual void SAL_CALL disposing();
+ void removeElementsNoEvents();
+
+ /** to be overridden if elements which are to be inserted into the container shall be checked
+
+ <p>the ElementDescription given can be used to cache information about the object - it will be passed
+ later on to implInserted/implReplaced.</p>
+ */
+ virtual void approveNewElement(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxObject,
+ ElementDescription* _pElement
+ );
+
+ virtual ElementDescription* createElementMetaData( );
+
+ /** inserts an object into our internal structures
+
+ @param _nIndex
+ the index at which position it should be inserted
+ @param _bEvents
+ if <TRUE/>, event knittings will be done
+ @param _pApprovalResult
+ must contain the result of an approveNewElement call. Can be <NULL/>, in this case, the approval
+ is done within implInsert.
+ @param _bFire
+ if <TRUE/>, a notification about the insertion will be fired
+ @throws css::lang::IllegalArgumentException
+ */
+ void implInsert(
+ sal_Int32 _nIndex,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxObject,
+ bool _bEvents /* = sal_True */,
+ ElementDescription* _pApprovalResult /* = NULL */ ,
+ bool _bFire /* = sal_True */
+ );
+
+ // called after the object is inserted, but before the "real listeners" are notified
+ virtual void implInserted( const ElementDescription* _pElement );
+ // called after the object is removed, but before the "real listeners" are notified
+ virtual void implRemoved(const css::uno::Reference<css::uno::XInterface>& _rxObject);
+
+ /** called after an object was replaced. The default implementation notifies our listeners, after releasing
+ the instance lock.
+ */
+ virtual void impl_replacedElement(
+ const css::container::ContainerEvent& _rEvent,
+ ::osl::ClearableMutexGuard& _rInstanceLock
+ );
+
+ void SAL_CALL writeEvents(const css::uno::Reference< css::io::XObjectOutputStream>& _rxOutStream);
+ void SAL_CALL readEvents(const css::uno::Reference< css::io::XObjectInputStream>& _rxInStream);
+
+ /** replace an element, specified by position
+
+ @precond <arg>_nIndex</arg> is a valid index
+ @precond our mutex is locked exactly once, by the guard specified with <arg>_rClearBeforeNotify</arg>
+
+ */
+ void implReplaceByIndex(
+ const sal_Int32 _nIndex,
+ const css::uno::Any& _rNewElement,
+ ::osl::ClearableMutexGuard& _rClearBeforeNotify
+ );
+
+ /** removes an element, specified by position
+
+ @precond <arg>_nIndex</arg> is a valid index
+ @precond our mutex is locked exactly once, by the guard specified with <arg>_rClearBeforeNotify</arg>
+
+ */
+ void implRemoveByIndex(
+ const sal_Int32 _nIndex,
+ ::osl::ClearableMutexGuard& _rClearBeforeNotify
+ );
+
+ /** validates the given index
+ @throws css::lang::IndexOutOfBoundsException
+ if the given index does not denote a valid position in our children array
+ */
+ void implCheckIndex( const sal_Int32 _nIndex );
+
+private:
+ // hack for Vba Events
+ void impl_addVbEvents_nolck_nothrow( const sal_Int32 i_nIndex );
+
+ void transformEvents();
+
+ void impl_createEventAttacher_nothrow();
+};
+
+typedef ::cppu::ImplHelper1< css::form::XFormComponent> OFormComponents_BASE;
+class OFormComponents :public ::cppu::OComponentHelper
+ ,public OInterfaceContainer
+ ,public OFormComponents_BASE
+{
+protected:
+ ::osl::Mutex m_aMutex;
+ css::uno::Reference<css::uno::XInterface> m_xParent;
+
+public:
+ OFormComponents(const css::uno::Reference< css::uno::XComponentContext>& _rxFactory);
+ OFormComponents( const OFormComponents& _cloneSource );
+ virtual ~OFormComponents() override;
+
+ DECLARE_UNO3_AGG_DEFAULTS(OFormComponents, ::cppu::OComponentHelper)
+
+ virtual css::uno::Any SAL_CALL queryAggregation(const css::uno::Type& _rType) override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// css::form::XFormComponent
+ virtual css::uno::Reference<css::uno::XInterface> SAL_CALL getParent() override;
+ virtual void SAL_CALL setParent(const css::uno::Reference<css::uno::XInterface>& Parent) override;
+
+ // XEventListener
+ using OInterfaceContainer::disposing;
+};
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/cloneable.hxx b/forms/source/inc/cloneable.hxx
new file mode 100644
index 0000000000..e161ad8c09
--- /dev/null
+++ b/forms/source/inc/cloneable.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/XAggregation.hpp>
+
+
+namespace frm
+{
+
+ class OCloneableAggregation
+ {
+ protected:
+ css::uno::Reference< css::uno::XAggregation> m_xAggregate;
+
+ protected:
+ static css::uno::Reference< css::uno::XAggregation > createAggregateClone( const OCloneableAggregation* _pOriginal );
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/commandimageprovider.hxx b/forms/source/inc/commandimageprovider.hxx
new file mode 100644
index 0000000000..02742b5a99
--- /dev/null
+++ b/forms/source/inc/commandimageprovider.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/XImageManager.hpp>
+
+#include <vcl/image.hxx>
+
+#include <memory>
+
+
+namespace frm
+{
+
+ class DocumentCommandImageProvider
+ {
+ public:
+ DocumentCommandImageProvider( const css::uno::Reference<css::uno::XComponentContext>& _rContext, const css::uno::Reference< css::frame::XModel >& _rxDocument );
+
+ std::vector<Image> getCommandImages( const css::uno::Sequence< OUString >& _rCommandURLs, bool _bLarge ) const;
+
+ private:
+ css::uno::Reference< css::ui::XImageManager > m_xDocumentImageManager;
+ css::uno::Reference< css::ui::XImageManager > m_xModuleImageManager;
+ };
+
+
+ typedef std::shared_ptr< const DocumentCommandImageProvider > PCommandImageProvider;
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/componenttools.hxx b/forms/source/inc/componenttools.hxx
new file mode 100644
index 0000000000..8c7fd8a8d5
--- /dev/null
+++ b/forms/source/inc/componenttools.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <set>
+
+
+namespace frm
+{
+
+
+ struct TypeCompareLess
+ {
+ bool operator()( const css::uno::Type& _rLHS, const css::uno::Type& _rRHS ) const
+ {
+ return _rLHS.getTypeName() < _rRHS.getTypeName();
+ }
+ };
+
+ /** a helper class which merges sequences of <type scope="css::uno">Type</type>s,
+ so that the resulting sequence contains every type at most once
+ */
+ class TypeBag
+ {
+ public:
+ typedef css::uno::Sequence< css::uno::Type > TypeSequence;
+ typedef ::std::set< css::uno::Type, TypeCompareLess > TypeSet;
+
+ private:
+ TypeSet m_aTypes;
+
+ public:
+ TypeBag(
+ const TypeSequence& _rTypes1
+ );
+
+ TypeBag(
+ const TypeSequence& _rTypes1,
+ const TypeSequence& _rTypes2
+ );
+ TypeBag(
+ const TypeSequence& _rTypes1,
+ const TypeSequence& _rTypes2,
+ const TypeSequence& _rTypes3
+ );
+
+ void addType( const css::uno::Type& i_rType );
+ void addTypes( const TypeSequence& _rTypes );
+ void removeType( const css::uno::Type& i_rType );
+
+ /** returns the types represented by this bag
+ */
+ TypeSequence getTypes() const;
+ };
+
+ css::uno::Reference< css::frame::XModel > getXModel(
+ const css::uno::Reference< css::uno::XInterface >& _rxComponent );
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/controlfeatureinterception.hxx b/forms/source/inc/controlfeatureinterception.hxx
new file mode 100644
index 0000000000..1e58ffb632
--- /dev/null
+++ b/forms/source/inc/controlfeatureinterception.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <memory>
+
+#include "urltransformer.hxx"
+
+
+namespace frm
+{
+
+
+ class UrlTransformer;
+
+ //= ControlFeatureInterception
+
+ /** helper class for controls which allow some of their features to be intercepted
+ by external instances
+
+ For using this class, instantiate it as member, derive yourself from
+ <type scope="css::frame">XDispatchProviderInterception</type>, and forward all
+ respective methods to this member.
+
+ Additionally, don't forget to call <member>dispose</member> when your class is disposed itself.
+ */
+ class ControlFeatureInterception
+ {
+ private:
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor >
+ m_xFirstDispatchInterceptor;
+ ::std::unique_ptr< UrlTransformer > m_pUrlTransformer;
+
+ public:
+ /** retrieves our URL transformer, so our clients may use it, too
+ */
+ const UrlTransformer& getTransformer() const { return *m_pUrlTransformer; }
+
+ public:
+ ControlFeatureInterception( const css::uno::Reference< css::uno::XComponentContext >& _rxORB );
+
+ // XDispatchProviderInterception
+ /// @throws css::uno::RuntimeException
+ void registerDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& Interceptor );
+ /// @throws css::uno::RuntimeException
+ void releaseDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& Interceptor );
+
+ // XComponent
+ void dispose();
+
+ /** queries the interceptor chain for the given dispatch, with a blank target frame and no frame search flags
+ */
+ css::uno::Reference< css::frame::XDispatch >
+ queryDispatch( const css::util::URL& _rURL );
+
+ /** queries the interceptor chain for the URL given as ASCII string,
+ with a blank target frame and no frame search flags
+ */
+ css::uno::Reference< css::frame::XDispatch >
+ queryDispatch( const char* _pAsciiURL );
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/featuredispatcher.hxx b/forms/source/inc/featuredispatcher.hxx
new file mode 100644
index 0000000000..33f4610be4
--- /dev/null
+++ b/forms/source/inc/featuredispatcher.hxx
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+
+namespace frm
+{
+
+ class IFeatureDispatcher
+ {
+ public:
+ /** dispatches a feature
+
+ @param _nFeatureId
+ the id of the feature to dispatch
+ */
+ virtual void dispatch( sal_Int16 _nFeatureId ) const = 0;
+
+ /** dispatches a feature, with an additional named parameter
+
+ @param _nFeatureId
+ the id of the feature to dispatch
+
+ @param _pParamAsciiName
+ the Ascii name of the parameter of the dispatch call
+
+ @param _rParamValue
+ the value of the parameter of the dispatch call
+ */
+ virtual void dispatchWithArgument(
+ sal_Int16 _nFeatureId,
+ const char* _pParamName,
+ const css::uno::Any& _rParamValue
+ ) const = 0;
+
+ /** checks whether a given feature is enabled
+ */
+ virtual bool isEnabled( sal_Int16 _nFeatureId ) const = 0;
+
+ /** returns the boolean state of a feature
+
+ Certain features may support more status information than only the enabled/disabled
+ state. The type of such additional information is fixed relative to a given feature, but
+ may differ between different features.
+
+ This method allows retrieving status information about features which have an additional
+ boolean information associated with it.
+ */
+ virtual bool getBooleanState( sal_Int16 _nFeatureId ) const = 0;
+
+ /** returns the string state of a feature
+
+ Certain features may support more status information than only the enabled/disabled
+ state. The type of such additional information is fixed relative to a given feature, but
+ may differ between different features.
+
+ This method allows retrieving status information about features which have an additional
+ string information associated with it.
+ */
+ virtual OUString getStringState( sal_Int16 _nFeatureId ) const = 0;
+
+ /** returns the integer state of a feature
+
+ Certain features may support more status information than only the enabled/disabled
+ state. The type of such additional information is fixed relative to a given feature, but
+ may differ between different features.
+
+ This method allows retrieving status information about features which have an additional
+ integer information associated with it.
+ */
+ virtual sal_Int32 getIntegerState( sal_Int16 _nFeatureId ) const = 0;
+
+ protected:
+ ~IFeatureDispatcher() {}
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/formcontrolfont.hxx b/forms/source/inc/formcontrolfont.hxx
new file mode 100644
index 0000000000..2cb2a92013
--- /dev/null
+++ b/forms/source/inc/formcontrolfont.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <tools/color.hxx>
+
+namespace cppu {
+ class OPropertySetHelper;
+}
+
+
+namespace frm
+{
+
+ class FontControlModel
+ {
+ private:
+ // <properties>
+ css::awt::FontDescriptor m_aFont;
+ sal_Int16 m_nFontRelief;
+ sal_Int16 m_nFontEmphasis;
+ css::uno::Any m_aTextLineColor;
+ css::uno::Any m_aTextColor;
+ // </properties>
+
+ bool m_bToolkitCompatibleDefaults;
+
+ protected:
+ const css::awt::FontDescriptor& getFont() const { return m_aFont; }
+ void setFont( const css::awt::FontDescriptor& _rFont ) { m_aFont = _rFont; }
+
+ void setTextColor( Color _nColor ) { m_aTextColor <<= _nColor; }
+ void clearTextColor( ) { m_aTextColor.clear(); }
+ bool hasTextColor( ) const { return m_aTextColor.hasValue(); }
+ Color getTextColor( ) const;
+
+ void setTextLineColor( Color _nColor ) { m_aTextLineColor <<= _nColor; }
+ void clearTextLineColor( ) { m_aTextLineColor.clear(); }
+ bool hasTextLineColor( ) const { return m_aTextLineColor.hasValue(); }
+ Color getTextLineColor( ) const;
+
+ protected:
+ FontControlModel( bool _bToolkitCompatibleDefaults );
+ FontControlModel( const FontControlModel* _pOriginal );
+
+ protected:
+ static bool isFontRelatedProperty( sal_Int32 _nPropertyHandle );
+ static bool isFontAggregateProperty( sal_Int32 _nPropertyHandle );
+
+ /// appends (!) the description of all font related properties to the given sequence
+ static void describeFontRelatedProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps );
+
+ void getFastPropertyValue ( css::uno::Any& _rValue, sal_Int32 _nHandle ) const;
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ bool convertFastPropertyValue ( css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue );
+ /// @throws css::uno::Exception
+ void setFastPropertyValue_NoBroadcast_impl(
+ ::cppu::OPropertySetHelper & rBase,
+ void (::cppu::OPropertySetHelper::*pSet)( sal_Int32, css::uno::Any const&),
+ sal_Int32 nHandle, const css::uno::Any& rValue);
+ css::uno::Any
+ getPropertyDefaultByHandle ( sal_Int32 _nHandle ) const;
+
+ private:
+
+ private:
+ FontControlModel( const FontControlModel& ) = delete;
+ FontControlModel& operator=( const FontControlModel& ) = delete;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/formnavigation.hxx b/forms/source/inc/formnavigation.hxx
new file mode 100644
index 0000000000..03715d28ab
--- /dev/null
+++ b/forms/source/inc/formnavigation.hxx
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase2.hxx>
+#include "featuredispatcher.hxx"
+#include "controlfeatureinterception.hxx"
+#include <vector>
+#include <map>
+#include <memory>
+
+
+namespace frm
+{
+
+
+ class UrlTransformer;
+
+
+ //= OFormNavigationHelper
+
+ typedef ::cppu::ImplHelper2 < css::frame::XDispatchProviderInterception
+ , css::frame::XStatusListener
+ > OFormNavigationHelper_Base;
+
+ class OFormNavigationHelper
+ :public OFormNavigationHelper_Base
+ ,public IFeatureDispatcher
+ {
+ private:
+ struct FeatureInfo
+ {
+ css::util::URL aURL;
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+ bool bCachedState;
+ css::uno::Any aCachedAdditionalState;
+
+ FeatureInfo() : bCachedState( false ) { }
+ };
+ typedef ::std::map< sal_Int16, FeatureInfo > FeatureMap;
+
+ private:
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xORB;
+ ControlFeatureInterception
+ m_aFeatureInterception;
+
+ // all supported features
+ FeatureMap m_aSupportedFeatures;
+ // all features which we have an external dispatcher for
+ sal_Int32 m_nConnectedFeatures;
+
+ protected:
+ OFormNavigationHelper( const css::uno::Reference< css::uno::XComponentContext >& _rxORB );
+ virtual ~OFormNavigationHelper();
+
+ // XComponent
+ /// @throws css::uno::RuntimeException
+ void dispose( );
+
+ // XDispatchProviderInterception
+ 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;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& State ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // IFeatureDispatcher
+ virtual void dispatch( sal_Int16 _nFeatureId ) const override;
+ virtual void dispatchWithArgument( sal_Int16 _nFeatureId, const char* _pParamName, const css::uno::Any& _rParamValue ) const override;
+ virtual bool isEnabled( sal_Int16 _nFeatureId ) const override;
+ virtual bool getBooleanState( sal_Int16 _nFeatureId ) const override;
+ virtual OUString getStringState( sal_Int16 _nFeatureId ) const override;
+ virtual sal_Int32 getIntegerState( sal_Int16 _nFeatureId ) const override;
+
+ // own overridables
+ /** is called when the interceptors have.
+ <p>The default implementations simply calls <member>updateDispatches</member>,
+ derived classes can prevent this in certain cases, or do additional handling.</p>
+ */
+ virtual void interceptorsChanged( );
+
+ /** called when the status of a feature changed
+
+ <p>The default implementation does nothing.</p>
+
+ <p>If the feature in question does support more state information that just the
+ enabled/disabled state, then this additional information is to be retrieved in
+ a separate call.</p>
+
+ @param _nFeatureId
+ the id of the feature
+ @param _bEnabled
+ determines if the features is enabled or disabled
+ @see getBooleanState
+ */
+ virtual void featureStateChanged( sal_Int16 _nFeatureId, bool _bEnabled );
+
+ /** notification for (potential) changes in the state of all features
+ <p>The base class implementation does nothing. Derived classes could force
+ their peer to update it's state, depending on the result of calls to
+ <member>IFeatureDispatcher::isEnabled</member>.</p>
+ */
+ virtual void allFeatureStatesChanged( );
+
+ /** retrieves the list of supported features
+ <p>To be overridden by derived classes</p>
+ @param _rFeatureIds
+ the array of features to support. Out parameter to fill by the derivee's implementation
+ @pure
+ */
+ virtual void getSupportedFeatures( ::std::vector< sal_Int16 >& /* [out] */ _rFeatureIds ) = 0;
+
+ protected:
+ /** update all our dispatches which are controlled by our dispatch interceptors
+ */
+ void updateDispatches();
+
+ /** connect to the dispatch interceptors
+ */
+ void connectDispatchers();
+
+ /** disconnect from the dispatch interceptors
+ */
+ void disconnectDispatchers();
+
+ /** queries the interceptor chain for a dispatcher for the given URL
+ */
+ css::uno::Reference< css::frame::XDispatch >
+ queryDispatch( const css::util::URL& _rURL );
+
+ /** invalidates the set of supported features
+
+ <p>This will invalidate all structures which are tied to the set of supported
+ features. All dispatches will be disconnected.<br/>
+ No automatic re-connection to potential external dispatchers is done, instead,
+ you have to call updateDispatches explicitly, if necessary.</p>
+ */
+ void invalidateSupportedFeaturesSet();
+
+ private:
+ /** initialize m_aSupportedFeatures, if necessary
+ */
+ void initializeSupportedFeatures();
+ };
+
+ /** helper class mapping between feature ids and feature URLs
+ */
+ class OFormNavigationMapper
+ {
+ private:
+ ::std::unique_ptr< UrlTransformer > m_pUrlTransformer;
+
+ public:
+ OFormNavigationMapper(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxORB
+ );
+ ~OFormNavigationMapper( );
+
+ /** retrieves the ASCII representation of a feature URL belonging to an id
+
+ @complexity O(log n)
+ @return NULL if the given id is not a known feature id (which is a valid usage)
+ */
+ static const char* getFeatureURLAscii( sal_Int16 _nFeatureId );
+
+ /** retrieves the feature URL belonging to a feature id
+
+ @complexity O(log n), with n being the number of all potentially known URLs
+ @return
+ <TRUE/> if and only if the given id is a known feature id
+ (which is a valid usage)
+ */
+ bool getFeatureURL( sal_Int16 _nFeatureId, css::util::URL& /* [out] */ _rURL );
+
+ /** retrieves the feature id belonging to a feature URL
+
+ @complexity O(n), with n being the number of all potentially known URLs
+ @return
+ the id of the feature URL, or -1 if the URl is not known
+ (which is a valid usage)
+ */
+ static sal_Int16 getFeatureId( std::u16string_view _rCompleteURL );
+
+ private:
+ OFormNavigationMapper( const OFormNavigationMapper& ) = delete;
+ OFormNavigationMapper& operator=( const OFormNavigationMapper& ) = delete;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/frm_resource.hxx b/forms/source/inc/frm_resource.hxx
new file mode 100644
index 0000000000..711018a158
--- /dev/null
+++ b/forms/source/inc/frm_resource.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <unotools/resmgr.hxx>
+
+namespace frm
+{
+
+ // handling resources within the FormLayer library
+ namespace ResourceManager
+ {
+ /** loads the string with the specified resource id from the FormLayer mo file
+ */
+ OUString loadString(TranslateId aResId);
+ };
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/frm_strings.hxx b/forms/source/inc/frm_strings.hxx
new file mode 100644
index 0000000000..90edf195d2
--- /dev/null
+++ b/forms/source/inc/frm_strings.hxx
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+//- properties
+
+inline constexpr OUString PROPERTY_TABINDEX = u"TabIndex"_ustr;
+inline constexpr OUString PROPERTY_TAG = u"Tag"_ustr;
+inline constexpr OUString PROPERTY_NAME = u"Name"_ustr;
+inline constexpr OUString PROPERTY_GROUP_NAME = u"GroupName"_ustr;
+inline constexpr OUString PROPERTY_CLASSID = u"ClassId"_ustr;
+inline constexpr OUString PROPERTY_FETCHSIZE = u"FetchSize"_ustr;
+inline constexpr OUString PROPERTY_VALUE = u"Value"_ustr;
+inline constexpr OUString PROPERTY_TEXT = u"Text"_ustr;
+inline constexpr OUString PROPERTY_LABEL = u"Label"_ustr;
+inline constexpr OUString PROPERTY_NAVIGATION = u"NavigationBarMode"_ustr;
+inline constexpr OUString PROPERTY_HASNAVIGATION = u"HasNavigationBar"_ustr;
+inline constexpr OUString PROPERTY_CYCLE = u"Cycle"_ustr;
+inline constexpr OUString PROPERTY_CONTROLSOURCE = u"DataField"_ustr;
+inline constexpr OUString PROPERTY_ENABLED = u"Enabled"_ustr;
+inline constexpr OUString PROPERTY_ENABLEVISIBLE = u"EnableVisible"_ustr;
+inline constexpr OUString PROPERTY_READONLY = u"ReadOnly"_ustr;
+inline constexpr OUString PROPERTY_RELEVANT = u"Relevant"_ustr;
+inline constexpr OUString PROPERTY_ISREADONLY = u"IsReadOnly"_ustr;
+inline constexpr OUString PROPERTY_FILTER = u"Filter"_ustr;
+inline constexpr OUString PROPERTY_HAVINGCLAUSE = u"HavingClause"_ustr;
+inline constexpr OUString PROPERTY_WIDTH = u"Width"_ustr;
+inline constexpr OUString PROPERTY_SEARCHABLE = u"IsSearchable"_ustr;
+inline constexpr OUString PROPERTY_MULTILINE = u"MultiLine"_ustr;
+inline constexpr OUString PROPERTY_TARGET_URL = u"TargetURL"_ustr;
+inline constexpr OUString PROPERTY_TARGET_FRAME = u"TargetFrame"_ustr;
+inline constexpr OUString PROPERTY_DEFAULTCONTROL = u"DefaultControl"_ustr;
+inline constexpr OUString PROPERTY_MAXTEXTLEN = u"MaxTextLen"_ustr;
+inline constexpr OUString PROPERTY_EDITMASK = u"EditMask"_ustr;
+inline constexpr OUString PROPERTY_SIZE = u"Size"_ustr;
+inline constexpr OUString PROPERTY_SPIN = u"Spin"_ustr;
+inline constexpr OUString PROPERTY_DATE = u"Date"_ustr;
+inline constexpr OUString PROPERTY_TIME = u"Time"_ustr;
+inline constexpr OUString PROPERTY_STATE = u"State"_ustr;
+inline constexpr OUString PROPERTY_TRISTATE = u"TriState"_ustr;
+inline constexpr OUString PROPERTY_HIDDEN_VALUE = u"HiddenValue"_ustr;
+inline constexpr OUString PROPERTY_BUTTONTYPE = u"ButtonType"_ustr;
+inline constexpr OUString PROPERTY_STRINGITEMLIST = u"StringItemList"_ustr;
+inline constexpr OUString PROPERTY_TYPEDITEMLIST = u"TypedItemList"_ustr;
+inline constexpr OUString PROPERTY_DEFAULT_TEXT = u"DefaultText"_ustr;
+inline constexpr OUString PROPERTY_DEFAULT_STATE = u"DefaultState"_ustr;
+inline constexpr OUString PROPERTY_FORMATKEY = u"FormatKey"_ustr;
+inline constexpr OUString PROPERTY_FORMATSSUPPLIER = u"FormatsSupplier"_ustr;
+inline constexpr OUString PROPERTY_SUBMIT_ACTION = u"SubmitAction"_ustr;
+inline constexpr OUString PROPERTY_SUBMIT_TARGET = u"SubmitTarget"_ustr;
+inline constexpr OUString PROPERTY_SUBMIT_METHOD = u"SubmitMethod"_ustr;
+inline constexpr OUString PROPERTY_SUBMIT_ENCODING = u"SubmitEncoding"_ustr;
+inline constexpr OUString PROPERTY_IMAGE_URL = u"ImageURL"_ustr;
+inline constexpr OUString PROPERTY_GRAPHIC = u"Graphic"_ustr;
+inline constexpr OUString PROPERTY_IMAGE_POSITION = u"ImagePosition"_ustr;
+inline constexpr OUString PROPERTY_EMPTY_IS_NULL = u"ConvertEmptyToNull"_ustr;
+inline constexpr OUString PROPERTY_LISTSOURCETYPE = u"ListSourceType"_ustr;
+inline constexpr OUString PROPERTY_LISTSOURCE = u"ListSource"_ustr;
+inline constexpr OUString PROPERTY_SELECT_SEQ = u"SelectedItems"_ustr;
+inline constexpr OUString PROPERTY_VALUE_SEQ = u"ValueItemList"_ustr;
+inline constexpr OUString PROPERTY_SELECT_VALUE_SEQ = u"SelectedValues"_ustr;
+inline constexpr OUString PROPERTY_SELECT_VALUE = u"SelectedValue"_ustr;
+inline constexpr OUString PROPERTY_DEFAULT_SELECT_SEQ = u"DefaultSelection"_ustr;
+inline constexpr OUString PROPERTY_MULTISELECTION = u"MultiSelection"_ustr;
+inline constexpr OUString PROPERTY_ALIGN = u"Align"_ustr;
+inline constexpr OUString PROPERTY_VERTICAL_ALIGN = u"VerticalAlign"_ustr;
+inline constexpr OUString PROPERTY_DEFAULT_DATE = u"DefaultDate"_ustr;
+inline constexpr OUString PROPERTY_DEFAULT_TIME = u"DefaultTime"_ustr;
+inline constexpr OUString PROPERTY_DEFAULT_VALUE = u"DefaultValue"_ustr;
+inline constexpr OUString PROPERTY_DECIMAL_ACCURACY = u"DecimalAccuracy"_ustr;
+inline constexpr OUString PROPERTY_FIELDTYPE = u"Type"_ustr;
+inline constexpr OUString PROPERTY_DECIMALS = u"Decimals"_ustr;
+inline constexpr OUString PROPERTY_REFVALUE = u"RefValue"_ustr;
+inline constexpr OUString PROPERTY_UNCHECKED_REFVALUE = u"SecondaryRefValue"_ustr;
+inline constexpr OUString PROPERTY_VALUEMIN = u"ValueMin"_ustr;
+inline constexpr OUString PROPERTY_VALUEMAX = u"ValueMax"_ustr;
+inline constexpr OUString PROPERTY_STRICTFORMAT = u"StrictFormat"_ustr;
+inline constexpr OUString PROPERTY_ALLOWADDITIONS = u"AllowInserts"_ustr;
+inline constexpr OUString PROPERTY_ALLOWEDITS = u"AllowUpdates"_ustr;
+inline constexpr OUString PROPERTY_ALLOWDELETIONS = u"AllowDeletes"_ustr;
+inline constexpr OUString PROPERTY_MASTERFIELDS = u"MasterFields"_ustr;
+inline constexpr OUString PROPERTY_ISPASSTHROUGH = u"IsPassThrough"_ustr;
+inline constexpr OUString PROPERTY_QUERY = u"Query"_ustr;
+inline constexpr OUString PROPERTY_LITERALMASK = u"LiteralMask"_ustr;
+inline constexpr OUString PROPERTY_VALUESTEP = u"ValueStep"_ustr;
+inline constexpr OUString PROPERTY_SHOWTHOUSANDSEP = u"ShowThousandsSeparator"_ustr;
+inline constexpr OUString PROPERTY_CURRENCYSYMBOL = u"CurrencySymbol"_ustr;
+inline constexpr OUString PROPERTY_DATEFORMAT = u"DateFormat"_ustr;
+inline constexpr OUString PROPERTY_DATEMIN = u"DateMin"_ustr;
+inline constexpr OUString PROPERTY_DATEMAX = u"DateMax"_ustr;
+inline constexpr OUString PROPERTY_DATE_SHOW_CENTURY = u"DateShowCentury"_ustr;
+inline constexpr OUString PROPERTY_TIMEFORMAT = u"TimeFormat"_ustr;
+inline constexpr OUString PROPERTY_TIMEMIN = u"TimeMin"_ustr;
+inline constexpr OUString PROPERTY_TIMEMAX = u"TimeMax"_ustr;
+inline constexpr OUString PROPERTY_LINECOUNT = u"LineCount"_ustr;
+inline constexpr OUString PROPERTY_BOUNDCOLUMN = u"BoundColumn"_ustr;
+inline constexpr OUString PROPERTY_FONT = u"FontDescriptor"_ustr;
+inline constexpr OUString PROPERTY_FILLCOLOR = u"FillColor"_ustr;
+inline constexpr OUString PROPERTY_LINECOLOR = u"LineColor"_ustr;
+inline constexpr OUString PROPERTY_DROPDOWN = u"Dropdown"_ustr;
+inline constexpr OUString PROPERTY_HSCROLL = u"HScroll"_ustr;
+inline constexpr OUString PROPERTY_VSCROLL = u"VScroll"_ustr;
+inline constexpr OUString PROPERTY_TABSTOP = u"Tabstop"_ustr;
+inline constexpr OUString PROPERTY_AUTOCOMPLETE = u"Autocomplete"_ustr;
+inline constexpr OUString PROPERTY_HARDLINEBREAKS = u"HardLineBreaks"_ustr;
+inline constexpr OUString PROPERTY_PRINTABLE = u"Printable"_ustr;
+inline constexpr OUString PROPERTY_ECHO_CHAR = u"EchoChar"_ustr;
+inline constexpr OUString PROPERTY_ROWHEIGHT = u"RowHeight"_ustr;
+inline constexpr OUString PROPERTY_HELPTEXT = u"HelpText"_ustr;
+inline constexpr OUString PROPERTY_FONT_NAME = u"FontName"_ustr;
+inline constexpr OUString PROPERTY_FONT_STYLENAME = u"FontStyleName"_ustr;
+inline constexpr OUString PROPERTY_FONT_FAMILY = u"FontFamily"_ustr;
+inline constexpr OUString PROPERTY_FONT_CHARSET = u"FontCharset"_ustr;
+inline constexpr OUString PROPERTY_FONT_HEIGHT = u"FontHeight"_ustr;
+inline constexpr OUString PROPERTY_FONT_WEIGHT = u"FontWeight"_ustr;
+inline constexpr OUString PROPERTY_FONT_SLANT = u"FontSlant"_ustr;
+inline constexpr OUString PROPERTY_FONT_UNDERLINE = u"FontUnderline"_ustr;
+inline constexpr OUString PROPERTY_FONT_WORDLINEMODE = u"FontWordLineMode"_ustr;
+inline constexpr OUString PROPERTY_FONT_STRIKEOUT = u"FontStrikeout"_ustr;
+inline constexpr OUString PROPERTY_FONTEMPHASISMARK = u"FontEmphasisMark"_ustr;
+inline constexpr OUString PROPERTY_FONTRELIEF = u"FontRelief"_ustr;
+inline constexpr OUString PROPERTY_FONT_CHARWIDTH = u"FontCharWidth"_ustr;
+inline constexpr OUString PROPERTY_FONT_KERNING = u"FontKerning"_ustr;
+inline constexpr OUString PROPERTY_FONT_ORIENTATION = u"FontOrientation"_ustr;
+inline constexpr OUString PROPERTY_FONT_PITCH = u"FontPitch"_ustr;
+inline constexpr OUString PROPERTY_FONT_TYPE = u"FontType"_ustr;
+inline constexpr OUString PROPERTY_FONT_WIDTH = u"FontWidth"_ustr;
+inline constexpr OUString PROPERTY_HELPURL = u"HelpURL"_ustr;
+inline constexpr OUString PROPERTY_RECORDMARKER = u"HasRecordMarker"_ustr;
+inline constexpr OUString PROPERTY_BOUNDFIELD = u"BoundField"_ustr;
+inline constexpr OUString PROPERTY_INPUT_REQUIRED = u"InputRequired"_ustr;
+inline constexpr OUString PROPERTY_TREATASNUMERIC = u"TreatAsNumber"_ustr;
+inline constexpr OUString PROPERTY_EFFECTIVE_VALUE = u"EffectiveValue"_ustr;
+inline constexpr OUString PROPERTY_EFFECTIVE_DEFAULT = u"EffectiveDefault"_ustr;
+inline constexpr OUString PROPERTY_EFFECTIVE_MIN = u"EffectiveMin"_ustr;
+inline constexpr OUString PROPERTY_EFFECTIVE_MAX = u"EffectiveMax"_ustr;
+inline constexpr OUString PROPERTY_HIDDEN = u"Hidden"_ustr;
+inline constexpr OUString PROPERTY_FILTERPROPOSAL = u"UseFilterValueProposal"_ustr;
+inline constexpr OUString PROPERTY_FIELDSOURCE = u"FieldSource"_ustr;
+inline constexpr OUString PROPERTY_TABLENAME = u"TableName"_ustr;
+inline constexpr OUString PROPERTY_CONTROLLABEL = u"LabelControl"_ustr;
+inline constexpr OUString PROPERTY_CURRSYM_POSITION = u"PrependCurrencySymbol"_ustr;
+inline constexpr OUString PROPERTY_CURSORCOLOR = u"CursorColor"_ustr;
+inline constexpr OUString PROPERTY_ALWAYSSHOWCURSOR = u"AlwaysShowCursor"_ustr;
+inline constexpr OUString PROPERTY_DISPLAYSYNCHRON = u"DisplayIsSynchron"_ustr;
+inline constexpr OUString PROPERTY_TEXTCOLOR = u"TextColor"_ustr;
+inline constexpr OUString PROPERTY_DELAY = u"RepeatDelay"_ustr;
+inline constexpr OUString PROPERTY_DEFAULT_SCROLL_VALUE = u"DefaultScrollValue"_ustr;
+inline constexpr OUString PROPERTY_SCROLL_VALUE = u"ScrollValue"_ustr;
+inline constexpr OUString PROPERTY_DEFAULT_SPIN_VALUE = u"DefaultSpinValue"_ustr;
+inline constexpr OUString PROPERTY_SPIN_VALUE = u"SpinValue"_ustr;
+inline constexpr OUString PROPERTY_REFERENCE_DEVICE = u"ReferenceDevice"_ustr;
+inline constexpr OUString PROPERTY_ISMODIFIED = u"IsModified"_ustr;
+inline constexpr OUString PROPERTY_ISNEW = u"IsNew"_ustr;
+inline constexpr OUString PROPERTY_PRIVILEGES = u"Privileges"_ustr;
+inline constexpr OUString PROPERTY_COMMAND = u"Command"_ustr;
+inline constexpr OUString PROPERTY_COMMANDTYPE = u"CommandType"_ustr;
+inline constexpr OUString PROPERTY_RESULTSET_CONCURRENCY = u"ResultSetConcurrency"_ustr;
+inline constexpr OUString PROPERTY_INSERTONLY = u"IgnoreResult"_ustr;
+inline constexpr OUString PROPERTY_RESULTSET_TYPE = u"ResultSetType"_ustr;
+inline constexpr OUString PROPERTY_ESCAPE_PROCESSING = u"EscapeProcessing"_ustr;
+inline constexpr OUString PROPERTY_APPLYFILTER = u"ApplyFilter"_ustr;
+inline constexpr OUString PROPERTY_ROWCOUNT = u"RowCount"_ustr;
+inline constexpr OUString PROPERTY_ROWCOUNTFINAL = u"IsRowCountFinal"_ustr;
+
+inline constexpr OUString PROPERTY_ISNULLABLE = u"IsNullable"_ustr;
+inline constexpr OUString PROPERTY_ACTIVECOMMAND = u"ActiveCommand"_ustr;
+inline constexpr OUString PROPERTY_ISCURRENCY = u"IsCurrency"_ustr;
+inline constexpr OUString PROPERTY_URL = u"URL"_ustr;
+inline constexpr OUString PROPERTY_TITLE = u"Title"_ustr;
+inline constexpr OUString PROPERTY_ACTIVE_CONNECTION = u"ActiveConnection"_ustr;
+inline constexpr OUString PROPERTY_SCALE = u"Scale"_ustr;
+inline constexpr OUString PROPERTY_SORT = u"Order"_ustr;
+inline constexpr OUString PROPERTY_DATASOURCE = u"DataSourceName"_ustr;
+inline constexpr OUString PROPERTY_DETAILFIELDS = u"DetailFields"_ustr;
+
+inline constexpr OUString PROPERTY_COLUMNSERVICENAME = u"ColumnServiceName"_ustr;
+inline constexpr OUString PROPERTY_REALNAME = u"RealName"_ustr;
+inline constexpr OUString PROPERTY_CONTROLSOURCEPROPERTY = u"DataFieldProperty"_ustr;
+inline constexpr OUString PROPERTY_USER = u"User"_ustr;
+inline constexpr OUString PROPERTY_PASSWORD = u"Password"_ustr;
+inline constexpr OUString PROPERTY_DISPATCHURLINTERNAL = u"DispatchURLInternal"_ustr;
+inline constexpr OUString PROPERTY_PERSISTENCE_MAXTEXTLENGTH = u"PersistenceMaxTextLength"_ustr;
+inline constexpr OUString PROPERTY_RICH_TEXT = u"RichText"_ustr;
+inline constexpr OUString PROPERTY_ENFORCE_FORMAT = u"EnforceFormat"_ustr;
+inline constexpr OUString PROPERTY_LINEEND_FORMAT = u"LineEndFormat"_ustr;
+inline constexpr OUString PROPERTY_WRITING_MODE = u"WritingMode"_ustr;
+inline constexpr OUString PROPERTY_CONTEXT_WRITING_MODE = u"ContextWritingMode"_ustr;
+
+inline constexpr OUString PROPERTY_NATIVE_LOOK = u"NativeWidgetLook"_ustr;
+inline constexpr OUString PROPERTY_BORDER = u"Border"_ustr;
+inline constexpr OUString PROPERTY_BORDERCOLOR = u"BorderColor"_ustr;
+inline constexpr OUString PROPERTY_BACKGROUNDCOLOR = u"BackgroundColor"_ustr;
+inline constexpr OUString PROPERTY_ICONSIZE = u"IconSize"_ustr;
+inline constexpr OUString PROPERTY_TEXTLINECOLOR = u"TextLineColor"_ustr;
+inline constexpr OUString PROPERTY_HIDEINACTIVESELECTION = u"HideInactiveSelection"_ustr;
+
+inline constexpr OUString PROPERTY_STANDARD_THEME = u"StandardTheme"_ustr;
+
+inline constexpr OUString PROPERTY_SHOW_POSITION = u"ShowPosition"_ustr;
+inline constexpr OUString PROPERTY_SHOW_NAVIGATION = u"ShowNavigation"_ustr;
+inline constexpr OUString PROPERTY_SHOW_RECORDACTIONS = u"ShowRecordActions"_ustr;
+inline constexpr OUString PROPERTY_SHOW_FILTERSORT = u"ShowFilterSort"_ustr;
+
+inline constexpr OUString PROPERTY_XSD_WHITESPACE = u"WhiteSpace"_ustr;
+inline constexpr OUString PROPERTY_XSD_PATTERN = u"Pattern"_ustr;
+inline constexpr OUString PROPERTY_XSD_LENGTH = u"Length"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_LENGTH = u"MinLength"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_LENGTH = u"MaxLength"_ustr;
+inline constexpr OUString PROPERTY_XSD_TOTAL_DIGITS = u"TotalDigits"_ustr;
+inline constexpr OUString PROPERTY_XSD_FRACTION_DIGITS = u"FractionDigits"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_INCLUSIVE_INT = u"MaxInclusiveInt"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_EXCLUSIVE_INT = u"MaxExclusiveInt"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_INCLUSIVE_INT = u"MinInclusiveInt"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_EXCLUSIVE_INT = u"MinExclusiveInt"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_INCLUSIVE_DOUBLE = u"MaxInclusiveDouble"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_EXCLUSIVE_DOUBLE = u"MaxExclusiveDouble"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_INCLUSIVE_DOUBLE = u"MinInclusiveDouble"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_EXCLUSIVE_DOUBLE = u"MinExclusiveDouble"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_INCLUSIVE_DATE = u"MaxInclusiveDate"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_EXCLUSIVE_DATE = u"MaxExclusiveDate"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_INCLUSIVE_DATE = u"MinInclusiveDate"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_EXCLUSIVE_DATE = u"MinExclusiveDate"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_INCLUSIVE_TIME = u"MaxInclusiveTime"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_EXCLUSIVE_TIME = u"MaxExclusiveTime"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_INCLUSIVE_TIME = u"MinInclusiveTime"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_EXCLUSIVE_TIME = u"MinExclusiveTime"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_INCLUSIVE_DATE_TIME = u"MaxInclusiveDateTime"_ustr;
+inline constexpr OUString PROPERTY_XSD_MAX_EXCLUSIVE_DATE_TIME = u"MaxExclusiveDateTime"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_INCLUSIVE_DATE_TIME = u"MinInclusiveDateTime"_ustr;
+inline constexpr OUString PROPERTY_XSD_MIN_EXCLUSIVE_DATE_TIME = u"MinExclusiveDateTime"_ustr;
+inline constexpr OUString PROPERTY_XSD_IS_BASIC = u"IsBasic"_ustr;
+inline constexpr OUString PROPERTY_XSD_TYPE_CLASS = u"TypeClass"_ustr;
+
+inline constexpr OUString PROPERTY_DYNAMIC_CONTROL_BORDER = u"DynamicControlBorder"_ustr;
+inline constexpr OUString PROPERTY_CONTROL_BORDER_COLOR_FOCUS = u"ControlBorderColorOnFocus"_ustr;
+inline constexpr OUString PROPERTY_CONTROL_BORDER_COLOR_MOUSE = u"ControlBorderColorOnHover"_ustr;
+inline constexpr OUString PROPERTY_CONTROL_BORDER_COLOR_INVALID = u"ControlBorderColorOnInvalid"_ustr;
+inline constexpr OUString PROPERTY_GENERATEVBAEVENTS = u"GenerateVbaEvents"_ustr;
+inline constexpr OUString PROPERTY_CONTROL_TYPE_IN_MSO = u"ControlTypeinMSO"_ustr;
+inline constexpr OUString PROPERTY_OBJ_ID_IN_MSO = u"ObjIDinMSO"_ustr;
+
+
+//- URLs
+
+#define URL_FORM_POSITION ".uno:FormController/positionForm"
+#define URL_FORM_RECORDCOUNT ".uno:FormController/RecordCount"
+#define URL_RECORD_FIRST ".uno:FormController/moveToFirst"
+#define URL_RECORD_PREV ".uno:FormController/moveToPrev"
+#define URL_RECORD_NEXT ".uno:FormController/moveToNext"
+#define URL_RECORD_LAST ".uno:FormController/moveToLast"
+#define URL_RECORD_SAVE ".uno:FormController/saveRecord"
+#define URL_RECORD_UNDO ".uno:FormController/undoRecord"
+#define URL_RECORD_NEW ".uno:FormController/moveToNew"
+#define URL_RECORD_DELETE ".uno:FormController/deleteRecord"
+#define URL_FORM_REFRESH ".uno:FormController/refreshForm"
+#define URL_FORM_REFRESH_CURRENT_CONTROL ".uno:FormController/refreshCurrentControl"
+
+#define URL_FORM_SORT_UP ".uno:FormController/sortUp"
+#define URL_FORM_SORT_DOWN ".uno:FormController/sortDown"
+#define URL_FORM_SORT ".uno:FormController/sort"
+#define URL_FORM_AUTO_FILTER ".uno:FormController/autoFilter"
+#define URL_FORM_FILTER ".uno:FormController/filter"
+#define URL_FORM_APPLY_FILTER ".uno:FormController/applyFilter"
+#define URL_FORM_REMOVE_FILTER ".uno:FormController/removeFilterOrder"
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/limitedformats.hxx b/forms/source/inc/limitedformats.hxx
new file mode 100644
index 0000000000..9ef4534423
--- /dev/null
+++ b/forms/source/inc/limitedformats.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <osl/mutex.hxx>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+
+
+namespace frm
+{
+
+
+ //= OLimitedFormats
+
+ /** maintains translation tables format key <-> enum value
+ <p>Used for controls which provide a limited number for (standard) formats, which
+ should be available as format keys.</p>
+ */
+ class OLimitedFormats
+ {
+ private:
+ static sal_Int32 s_nInstanceCount;
+ static ::osl::Mutex s_aMutex;
+ static css::uno::Reference< css::util::XNumberFormatsSupplier >
+ s_xStandardFormats;
+
+ sal_Int32 m_nFormatEnumPropertyHandle;
+ const sal_Int16 m_nTableId;
+ css::uno::Reference< css::beans::XFastPropertySet >
+ m_xAggregate;
+
+ protected:
+ /** ctor
+ <p>The class id is used to determine the translation table to use. All instances which
+ pass the same value here share one table.</p>
+ */
+ OLimitedFormats(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxContext,
+ const sal_Int16 _nClassId
+ );
+ ~OLimitedFormats();
+
+ protected:
+ void setAggregateSet(
+ const css::uno::Reference< css::beans::XFastPropertySet >& _rxAggregate,
+ sal_Int32 _nOriginalPropertyHandle
+ );
+
+ protected:
+ void getFormatKeyPropertyValue( css::uno::Any& _rValue ) const;
+ bool convertFormatKeyPropertyValue(
+ css::uno::Any& _rConvertedValue,
+ css::uno::Any& _rOldValue,
+ const css::uno::Any& _rNewValue
+ );
+ void setFormatKeyPropertyValue( const css::uno::Any& _rNewValue );
+ // setFormatKeyPropertyValue should only be called with a value got from convertFormatKeyPropertyValue!
+
+ const css::uno::Reference< css::util::XNumberFormatsSupplier >&
+ getFormatsSupplier() const { return s_xStandardFormats; }
+
+ private:
+ void acquireSupplier(const css::uno::Reference< css::uno::XComponentContext >& _rxContext);
+ void releaseSupplier();
+
+ static void ensureTableInitialized(const sal_Int16 _nTableId);
+ static void clearTable(const sal_Int16 _nTableId);
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/property.hxx b/forms/source/inc/property.hxx
new file mode 100644
index 0000000000..00b2075cce
--- /dev/null
+++ b/forms/source/inc/property.hxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <unordered_map>
+
+#include <comphelper/propagg.hxx>
+
+#include "frm_strings.hxx"
+
+using namespace comphelper;
+
+//= property helper classes
+
+namespace frm
+{
+
+// PropertyId's, who have a mapping to a PropertyName
+#define PROPERTY_ID_START 0
+
+#define PROPERTY_ID_NAME (PROPERTY_ID_START + 1)
+#define PROPERTY_ID_TABINDEX (PROPERTY_ID_START + 2)
+#define PROPERTY_ID_CONTROLSOURCE (PROPERTY_ID_START + 3)
+#define PROPERTY_ID_MASTERFIELDS (PROPERTY_ID_START + 4)
+#define PROPERTY_ID_DATASOURCE (PROPERTY_ID_START + 6)
+#define PROPERTY_ID_CLASSID (PROPERTY_ID_START + 9)
+#define PROPERTY_ID_CURSORTYPE (PROPERTY_ID_START + 10)
+#define PROPERTY_ID_READONLY (PROPERTY_ID_START + 11)
+#define PROPERTY_ID_NAVIGATION (PROPERTY_ID_START + 13)
+#define PROPERTY_ID_CYCLE (PROPERTY_ID_START + 14)
+#define PROPERTY_ID_ALLOWADDITIONS (PROPERTY_ID_START + 15)
+#define PROPERTY_ID_ALLOWEDITS (PROPERTY_ID_START + 16)
+#define PROPERTY_ID_ALLOWDELETIONS (PROPERTY_ID_START + 17)
+#define PROPERTY_ID_NATIVE_LOOK (PROPERTY_ID_START + 18)
+#define PROPERTY_ID_INPUT_REQUIRED (PROPERTY_ID_START + 19)
+#define PROPERTY_ID_WRITING_MODE (PROPERTY_ID_START + 20)
+#define PROPERTY_ID_CONTEXT_WRITING_MODE (PROPERTY_ID_START + 21)
+#define PROPERTY_ID_VERTICAL_ALIGN (PROPERTY_ID_START + 22)
+#define PROPERTY_ID_GRAPHIC (PROPERTY_ID_START + 23)
+#define PROPERTY_ID_GROUP_NAME (PROPERTY_ID_START + 24)
+#define PROPERTY_ID_STANDARD_THEME (PROPERTY_ID_START + 25)
+ // free
+ // free
+ // free
+ // free
+ // free
+#define PROPERTY_ID_VALUE (PROPERTY_ID_START + 31) // INT32
+ // free
+#define PROPERTY_ID_FORMATKEY (PROPERTY_ID_START + 33) // UINT32
+ // free
+ // free
+ // free
+#define PROPERTY_ID_SIZE (PROPERTY_ID_START + 37) // UINT32
+#define PROPERTY_ID_REFERENCE_DEVICE (PROPERTY_ID_START + 38) // XDevice
+ // free
+ // free
+ // free
+#define PROPERTY_ID_WIDTH (PROPERTY_ID_START + 42) // UINT16
+#define PROPERTY_ID_DEFAULTCONTROL (PROPERTY_ID_START + 43) // string
+#define PROPERTY_ID_BOUNDCOLUMN (PROPERTY_ID_START + 44) // UINT16 may be null
+#define PROPERTY_ID_LISTSOURCETYPE (PROPERTY_ID_START + 45) // UINT16
+#define PROPERTY_ID_LISTSOURCE (PROPERTY_ID_START + 46) // string
+ // FREE
+#define PROPERTY_ID_TEXT (PROPERTY_ID_START + 48) // string
+#define PROPERTY_ID_STRINGITEMLIST (PROPERTY_ID_START + 49) // wsstringsequence
+#define PROPERTY_ID_LABEL (PROPERTY_ID_START + 50) // string
+#define PROPERTY_ID_HIDEINACTIVESELECTION (PROPERTY_ID_START + 51) // sal_Bool
+#define PROPERTY_ID_STATE (PROPERTY_ID_START + 52) // UINT16
+#define PROPERTY_ID_DELAY (PROPERTY_ID_START + 53) // sal_Int32
+#define PROPERTY_ID_FONT (PROPERTY_ID_START + 54) // font
+#define PROPERTY_ID_HASNAVIGATION (PROPERTY_ID_START + 55)
+#define PROPERTY_ID_BORDERCOLOR (PROPERTY_ID_START + 56) // sal_Int32
+#define PROPERTY_ID_ROWHEIGHT (PROPERTY_ID_START + 57) // UINT16
+#define PROPERTY_ID_BACKGROUNDCOLOR (PROPERTY_ID_START + 58) // sal_Int32
+#define PROPERTY_ID_FILLCOLOR (PROPERTY_ID_START + 59) // UINT32
+#define PROPERTY_ID_TEXTCOLOR (PROPERTY_ID_START + 60) // UINT32
+#define PROPERTY_ID_LINECOLOR (PROPERTY_ID_START + 61) // UINT32
+#define PROPERTY_ID_BORDER (PROPERTY_ID_START + 62) // UINT16
+#define PROPERTY_ID_ALIGN (PROPERTY_ID_START + 63) // UINT16
+#define PROPERTY_ID_DROPDOWN (PROPERTY_ID_START + 64) // BOOL
+#define PROPERTY_ID_UNCHECKED_REFVALUE (PROPERTY_ID_START + 65) // OUString
+#define PROPERTY_ID_HSCROLL (PROPERTY_ID_START + 66) // BOOL
+#define PROPERTY_ID_VSCROLL (PROPERTY_ID_START + 67) // BOOL
+#define PROPERTY_ID_TABSTOP (PROPERTY_ID_START + 68) // BOOL
+#define PROPERTY_ID_REFVALUE (PROPERTY_ID_START + 69) // OUString
+#define PROPERTY_ID_BUTTONTYPE (PROPERTY_ID_START + 70) // UINT16
+#define PROPERTY_ID_DEFAULT_TEXT (PROPERTY_ID_START + 71) // OUString
+#define PROPERTY_ID_SUBMIT_ACTION (PROPERTY_ID_START + 72) // string
+#define PROPERTY_ID_SUBMIT_METHOD (PROPERTY_ID_START + 73) // FmSubmitMethod
+#define PROPERTY_ID_SUBMIT_ENCODING (PROPERTY_ID_START + 74) // FmSubmitEncoding
+#define PROPERTY_ID_DEFAULT_VALUE (PROPERTY_ID_START + 75) // OUString
+#define PROPERTY_ID_SUBMIT_TARGET (PROPERTY_ID_START + 76) // OUString
+#define PROPERTY_ID_DEFAULT_STATE (PROPERTY_ID_START + 77) // UINT16
+#define PROPERTY_ID_VALUE_SEQ (PROPERTY_ID_START + 78) // StringSeq
+#define PROPERTY_ID_IMAGE_URL (PROPERTY_ID_START + 79) // OUString
+#define PROPERTY_ID_SELECT_VALUE (PROPERTY_ID_START + 80) // StringSeq
+#define PROPERTY_ID_SELECT_VALUE_SEQ (PROPERTY_ID_START + 81) // StringSeq
+ // free
+ // free
+ // free
+ // free
+ // free
+ // free
+ // free
+ // free
+ // free
+#define PROPERTY_ID_SELECT_SEQ (PROPERTY_ID_START + 91) // INT16Seq
+#define PROPERTY_ID_DEFAULT_SELECT_SEQ (PROPERTY_ID_START + 92) // INT16Seq
+#define PROPERTY_ID_MULTISELECTION (PROPERTY_ID_START + 93) // BOOL
+#define PROPERTY_ID_MULTILINE (PROPERTY_ID_START + 94) // BOOL
+#define PROPERTY_ID_DATE (PROPERTY_ID_START + 95) // UINT32
+#define PROPERTY_ID_DATEMIN (PROPERTY_ID_START + 96) // UINT32
+#define PROPERTY_ID_DATEMAX (PROPERTY_ID_START + 97) // UINT32
+#define PROPERTY_ID_DATEFORMAT (PROPERTY_ID_START + 98) // UINT16
+#define PROPERTY_ID_TIME (PROPERTY_ID_START + 99) // UINT32
+#define PROPERTY_ID_TIMEMIN (PROPERTY_ID_START +100) // UINT32
+#define PROPERTY_ID_TIMEMAX (PROPERTY_ID_START +101) // UINT32
+#define PROPERTY_ID_TIMEFORMAT (PROPERTY_ID_START +102) // UINT16
+#define PROPERTY_ID_VALUEMIN (PROPERTY_ID_START +103) // INT32
+#define PROPERTY_ID_VALUEMAX (PROPERTY_ID_START +104) // INT32
+#define PROPERTY_ID_VALUESTEP (PROPERTY_ID_START +105) // INT32
+#define PROPERTY_ID_CURRENCYSYMBOL (PROPERTY_ID_START +106) // OUString
+#define PROPERTY_ID_EDITMASK (PROPERTY_ID_START +107) // OUString
+#define PROPERTY_ID_LITERALMASK (PROPERTY_ID_START +108) // OUString
+#define PROPERTY_ID_ENABLED (PROPERTY_ID_START +109) // BOOL
+#define PROPERTY_ID_AUTOCOMPLETE (PROPERTY_ID_START +110) // BOOL
+#define PROPERTY_ID_LINECOUNT (PROPERTY_ID_START +111) // UINT16
+#define PROPERTY_ID_MAXTEXTLEN (PROPERTY_ID_START +112) // UINT16
+#define PROPERTY_ID_SPIN (PROPERTY_ID_START +113) // BOOL
+#define PROPERTY_ID_STRICTFORMAT (PROPERTY_ID_START +114) // BOOL
+#define PROPERTY_ID_SHOWTHOUSANDSEP (PROPERTY_ID_START +115) // BOOL
+#define PROPERTY_ID_HARDLINEBREAKS (PROPERTY_ID_START +116) // BOOL
+#define PROPERTY_ID_PRINTABLE (PROPERTY_ID_START +117) // BOOL
+#define PROPERTY_ID_TARGET_URL (PROPERTY_ID_START +118) // OUString
+#define PROPERTY_ID_TARGET_FRAME (PROPERTY_ID_START +119) // OUString
+#define PROPERTY_ID_TAG (PROPERTY_ID_START +120) // OUString
+#define PROPERTY_ID_ECHO_CHAR (PROPERTY_ID_START +121) // UINT16
+#define PROPERTY_ID_SHOW_POSITION (PROPERTY_ID_START +122) // sal_Bool
+#define PROPERTY_ID_SHOW_NAVIGATION (PROPERTY_ID_START +123) // sal_Bool
+#define PROPERTY_ID_SHOW_RECORDACTIONS (PROPERTY_ID_START +124) // sal_Bool
+#define PROPERTY_ID_SHOW_FILTERSORT (PROPERTY_ID_START +125) // sal_Bool
+#define PROPERTY_ID_EMPTY_IS_NULL (PROPERTY_ID_START +126) // Bool
+#define PROPERTY_ID_DECIMAL_ACCURACY (PROPERTY_ID_START +127) // UINT16
+#define PROPERTY_ID_DATE_SHOW_CENTURY (PROPERTY_ID_START +128) // Bool
+#define PROPERTY_ID_TRISTATE (PROPERTY_ID_START +129) // Bool
+#define PROPERTY_ID_DEFAULT_BUTTON (PROPERTY_ID_START +130) // Bool
+#define PROPERTY_ID_HIDDEN_VALUE (PROPERTY_ID_START +131) // OUString
+#define PROPERTY_ID_DECIMALS (PROPERTY_ID_START +132) // UINT16
+#define PROPERTY_ID_AUTOINCREMENT (PROPERTY_ID_START +133) // UINT16
+ // free
+#define PROPERTY_ID_FILTER (PROPERTY_ID_START +135) // OUString
+#define PROPERTY_ID_HAVINGCLAUSE (PROPERTY_ID_START +136) // OUString
+#define PROPERTY_ID_QUERY (PROPERTY_ID_START +137) // OUString
+#define PROPERTY_ID_DEFAULT_LONG_VALUE (PROPERTY_ID_START +138) // Double
+#define PROPERTY_ID_DEFAULT_DATE (PROPERTY_ID_START +139) // UINT32
+#define PROPERTY_ID_DEFAULT_TIME (PROPERTY_ID_START +140)
+#define PROPERTY_ID_HELPTEXT (PROPERTY_ID_START +141)
+#define PROPERTY_ID_FONT_NAME (PROPERTY_ID_START +142)
+#define PROPERTY_ID_FONT_STYLENAME (PROPERTY_ID_START +143)
+#define PROPERTY_ID_FONT_FAMILY (PROPERTY_ID_START +144)
+#define PROPERTY_ID_FONT_CHARSET (PROPERTY_ID_START +145)
+#define PROPERTY_ID_FONT_HEIGHT (PROPERTY_ID_START +146)
+#define PROPERTY_ID_FONT_WEIGHT (PROPERTY_ID_START +147)
+#define PROPERTY_ID_FONT_SLANT (PROPERTY_ID_START +148)
+#define PROPERTY_ID_FONT_UNDERLINE (PROPERTY_ID_START +149)
+#define PROPERTY_ID_FONT_STRIKEOUT (PROPERTY_ID_START +150)
+#define PROPERTY_ID_ISPASSTHROUGH (PROPERTY_ID_START +151)
+#define PROPERTY_ID_HELPURL (PROPERTY_ID_START +152) // OUString
+#define PROPERTY_ID_RECORDMARKER (PROPERTY_ID_START +153)
+#define PROPERTY_ID_BOUNDFIELD (PROPERTY_ID_START +154)
+#define PROPERTY_ID_FORMATSSUPPLIER (PROPERTY_ID_START +155) // XNumberFormatsSupplier
+#define PROPERTY_ID_TREATASNUMERIC (PROPERTY_ID_START +156) // BOOL
+#define PROPERTY_ID_EFFECTIVE_VALUE (PROPERTY_ID_START +157) // ANY (string or double)
+#define PROPERTY_ID_EFFECTIVE_DEFAULT (PROPERTY_ID_START +158) // ditto
+#define PROPERTY_ID_EFFECTIVE_MIN (PROPERTY_ID_START +159) // ditto
+#define PROPERTY_ID_EFFECTIVE_MAX (PROPERTY_ID_START +160) // ditto
+#define PROPERTY_ID_HIDDEN (PROPERTY_ID_START +161) // BOOL
+#define PROPERTY_ID_FILTERPROPOSAL (PROPERTY_ID_START +162) // BOOL
+#define PROPERTY_ID_FIELDSOURCE (PROPERTY_ID_START +163) // String
+#define PROPERTY_ID_TABLENAME (PROPERTY_ID_START +164) // String
+#define PROPERTY_ID_ENABLEVISIBLE (PROPERTY_ID_START +165) // BOOL
+ // FREE
+ // FREE
+ // FREE
+ // FREE
+#define PROPERTY_ID_CONTROLLABEL (PROPERTY_ID_START +171) // XPropertySet
+#define PROPERTY_ID_CURRSYM_POSITION (PROPERTY_ID_START +172) // String
+ // FREE
+#define PROPERTY_ID_CURSORCOLOR (PROPERTY_ID_START +174) // INT32
+#define PROPERTY_ID_ALWAYSSHOWCURSOR (PROPERTY_ID_START +175) // BOOL
+#define PROPERTY_ID_DISPLAYSYNCHRON (PROPERTY_ID_START +176) // BOOL
+#define PROPERTY_ID_ISMODIFIED (PROPERTY_ID_START +177) // BOOL
+#define PROPERTY_ID_ISNEW (PROPERTY_ID_START +178) // BOOL
+#define PROPERTY_ID_PRIVILEGES (PROPERTY_ID_START +179) // INT32
+#define PROPERTY_ID_DETAILFIELDS (PROPERTY_ID_START +180) // Sequence< OUString >
+#define PROPERTY_ID_COMMAND (PROPERTY_ID_START +181) // String
+#define PROPERTY_ID_COMMANDTYPE (PROPERTY_ID_START +182) // INT32 (css::sdb::CommandType)
+#define PROPERTY_ID_RESULTSET_CONCURRENCY (PROPERTY_ID_START +183)// INT32 (css::sdbc::ResultSetConcurrency)
+#define PROPERTY_ID_INSERTONLY (PROPERTY_ID_START +184) // BOOL
+#define PROPERTY_ID_RESULTSET_TYPE (PROPERTY_ID_START +185) // INT32 (css::sdbc::ResultSetType)
+#define PROPERTY_ID_ESCAPE_PROCESSING (PROPERTY_ID_START +186) // BOOL
+#define PROPERTY_ID_APPLYFILTER (PROPERTY_ID_START +187) // BOOL
+
+#define PROPERTY_ID_ISNULLABLE (PROPERTY_ID_START +188) // BOOL
+#define PROPERTY_ID_ACTIVECOMMAND (PROPERTY_ID_START +189) // String
+#define PROPERTY_ID_ISCURRENCY (PROPERTY_ID_START +190) // BOOL
+#define PROPERTY_ID_URL (PROPERTY_ID_START +192) // String
+#define PROPERTY_ID_TITLE (PROPERTY_ID_START +193) // String
+#define PROPERTY_ID_ACTIVE_CONNECTION (PROPERTY_ID_START +194) // css::sdbc::XConnection
+#define PROPERTY_ID_SCALE (PROPERTY_ID_START +195) // INT32
+#define PROPERTY_ID_SORT (PROPERTY_ID_START +196) // String
+
+ // free
+ // free
+#define PROPERTY_ID_FETCHSIZE (PROPERTY_ID_START +199)
+ // free
+#define PROPERTY_ID_SEARCHABLE (PROPERTY_ID_START +201)
+#define PROPERTY_ID_ISREADONLY (PROPERTY_ID_START +202)
+ // free
+#define PROPERTY_ID_FIELDTYPE (PROPERTY_ID_START +204)
+#define PROPERTY_ID_COLUMNSERVICENAME (PROPERTY_ID_START +205)
+#define PROPERTY_ID_CONTROLSOURCEPROPERTY (PROPERTY_ID_START +206)
+#define PROPERTY_ID_REALNAME (PROPERTY_ID_START +207)
+#define PROPERTY_ID_FONT_WORDLINEMODE (PROPERTY_ID_START +208)
+#define PROPERTY_ID_TEXTLINECOLOR (PROPERTY_ID_START +209)
+#define PROPERTY_ID_FONTEMPHASISMARK (PROPERTY_ID_START +210)
+#define PROPERTY_ID_FONTRELIEF (PROPERTY_ID_START +211)
+
+#define PROPERTY_ID_DISPATCHURLINTERNAL ( PROPERTY_ID_START + 212 ) // sal_Bool
+#define PROPERTY_ID_PERSISTENCE_MAXTEXTLENGTH ( PROPERTY_ID_START + 213 ) // sal_Int16
+#define PROPERTY_ID_DEFAULT_SCROLL_VALUE ( PROPERTY_ID_START + 214 ) // sal_Int32
+#define PROPERTY_ID_DEFAULT_SPIN_VALUE ( PROPERTY_ID_START + 215 ) // sal_Int32
+#define PROPERTY_ID_SCROLL_VALUE ( PROPERTY_ID_START + 216 ) // sal_Int32
+#define PROPERTY_ID_SPIN_VALUE ( PROPERTY_ID_START + 217 ) // sal_Int32
+#define PROPERTY_ID_ICONSIZE ( PROPERTY_ID_START + 218 ) // sal_Int16
+
+#define PROPERTY_ID_FONT_CHARWIDTH ( PROPERTY_ID_START + 219 ) // float
+#define PROPERTY_ID_FONT_KERNING ( PROPERTY_ID_START + 220 ) // sal_Bool
+#define PROPERTY_ID_FONT_ORIENTATION ( PROPERTY_ID_START + 221 ) // float
+#define PROPERTY_ID_FONT_PITCH ( PROPERTY_ID_START + 222 ) // sal_Int16
+#define PROPERTY_ID_FONT_TYPE ( PROPERTY_ID_START + 223 ) // sal_Int16
+#define PROPERTY_ID_FONT_WIDTH ( PROPERTY_ID_START + 224 ) // sal_Int16
+#define PROPERTY_ID_RICH_TEXT ( PROPERTY_ID_START + 225 ) // sal_Bool
+
+#define PROPERTY_ID_DYNAMIC_CONTROL_BORDER ( PROPERTY_ID_START + 226 ) // sal_Bool
+#define PROPERTY_ID_CONTROL_BORDER_COLOR_FOCUS ( PROPERTY_ID_START + 227 ) // sal_Int32
+#define PROPERTY_ID_CONTROL_BORDER_COLOR_MOUSE ( PROPERTY_ID_START + 228 ) // sal_Int32
+#define PROPERTY_ID_CONTROL_BORDER_COLOR_INVALID ( PROPERTY_ID_START + 229 ) // sal_Int32
+
+#define PROPERTY_ID_XSD_PATTERN ( PROPERTY_ID_START + 230 )
+#define PROPERTY_ID_XSD_WHITESPACE ( PROPERTY_ID_START + 231 )
+#define PROPERTY_ID_XSD_LENGTH ( PROPERTY_ID_START + 232 )
+#define PROPERTY_ID_XSD_MIN_LENGTH ( PROPERTY_ID_START + 233 )
+#define PROPERTY_ID_XSD_MAX_LENGTH ( PROPERTY_ID_START + 234 )
+#define PROPERTY_ID_XSD_TOTAL_DIGITS ( PROPERTY_ID_START + 235 )
+#define PROPERTY_ID_XSD_FRACTION_DIGITS ( PROPERTY_ID_START + 236 )
+#define PROPERTY_ID_XSD_MAX_INCLUSIVE_INT ( PROPERTY_ID_START + 237 )
+#define PROPERTY_ID_XSD_MAX_EXCLUSIVE_INT ( PROPERTY_ID_START + 238 )
+#define PROPERTY_ID_XSD_MIN_INCLUSIVE_INT ( PROPERTY_ID_START + 239 )
+#define PROPERTY_ID_XSD_MIN_EXCLUSIVE_INT ( PROPERTY_ID_START + 240 )
+#define PROPERTY_ID_XSD_MAX_INCLUSIVE_DOUBLE ( PROPERTY_ID_START + 241 )
+#define PROPERTY_ID_XSD_MAX_EXCLUSIVE_DOUBLE ( PROPERTY_ID_START + 242 )
+#define PROPERTY_ID_XSD_MIN_INCLUSIVE_DOUBLE ( PROPERTY_ID_START + 243 )
+#define PROPERTY_ID_XSD_MIN_EXCLUSIVE_DOUBLE ( PROPERTY_ID_START + 244 )
+#define PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE ( PROPERTY_ID_START + 245 )
+#define PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE ( PROPERTY_ID_START + 246 )
+#define PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE ( PROPERTY_ID_START + 247 )
+#define PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE ( PROPERTY_ID_START + 248 )
+#define PROPERTY_ID_XSD_MAX_INCLUSIVE_TIME ( PROPERTY_ID_START + 249 )
+#define PROPERTY_ID_XSD_MAX_EXCLUSIVE_TIME ( PROPERTY_ID_START + 250 )
+#define PROPERTY_ID_XSD_MIN_INCLUSIVE_TIME ( PROPERTY_ID_START + 251 )
+#define PROPERTY_ID_XSD_MIN_EXCLUSIVE_TIME ( PROPERTY_ID_START + 252 )
+#define PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE_TIME ( PROPERTY_ID_START + 253 )
+#define PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE_TIME ( PROPERTY_ID_START + 254 )
+#define PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE_TIME ( PROPERTY_ID_START + 255 )
+#define PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE_TIME ( PROPERTY_ID_START + 256 )
+#define PROPERTY_ID_XSD_IS_BASIC ( PROPERTY_ID_START + 257 )
+#define PROPERTY_ID_XSD_TYPE_CLASS ( PROPERTY_ID_START + 258 )
+
+#define PROPERTY_ID_LINEEND_FORMAT ( PROPERTY_ID_START + 259 ) // css.awt.LineEndFormat
+#define PROPERTY_ID_GENERATEVBAEVENTS ( PROPERTY_ID_START + 260 )
+#define PROPERTY_ID_CONTROL_TYPE_IN_MSO ( PROPERTY_ID_START + 261 )
+#define PROPERTY_ID_OBJ_ID_IN_MSO ( PROPERTY_ID_START + 262 )
+
+#define PROPERTY_ID_TYPEDITEMLIST ( PROPERTY_ID_START + 263 ) // Sequence<Any>
+
+// start ID for aggregated properties
+#define PROPERTY_ID_AGGREGATE_ID (PROPERTY_ID_START + 10000)
+
+//= assignment property handle <-> property name
+//= used by the PropertySetAggregationHelper
+
+
+class PropertyInfoService
+{
+ typedef std::unordered_map<OUString, sal_Int32> PropertyMap;
+ static PropertyMap s_AllKnownProperties;
+
+public:
+ PropertyInfoService() = delete;
+
+ static sal_Int32 getPropertyId(const OUString& _rName);
+
+private:
+ static void initialize();
+};
+
+
+// a class implementing the comphelper::IPropertyInfoService
+class ConcreteInfoService final : public ::comphelper::IPropertyInfoService
+{
+public:
+ virtual ~ConcreteInfoService() {}
+
+ virtual sal_Int32 getPreferredPropertyId(const OUString& _rName) override;
+};
+
+}
+//... namespace frm .......................................................
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/propertybaghelper.hxx b/forms/source/inc/propertybaghelper.hxx
new file mode 100644
index 0000000000..542cef9a6d
--- /dev/null
+++ b/forms/source/inc/propertybaghelper.hxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertybag.hxx>
+#include <comphelper/propagg.hxx>
+#include <memory>
+
+
+namespace frm
+{
+
+
+ //= class IPropertyBagHelperContext
+
+ class SAL_NO_VTABLE IPropertyBagHelperContext
+ {
+ public:
+ virtual ::osl::Mutex& getMutex() = 0;
+
+ virtual void describeFixedAndAggregateProperties(
+ css::uno::Sequence< css::beans::Property >& _out_rFixedProperties,
+ css::uno::Sequence< css::beans::Property >& _out_rAggregateProperties
+ ) const = 0;
+
+ virtual css::uno::Reference< css::beans::XMultiPropertySet >
+ getPropertiesInterface() = 0;
+
+ protected:
+ ~IPropertyBagHelperContext() {}
+ };
+
+ class PropertyBagHelper
+ {
+ private:
+ IPropertyBagHelperContext& m_rContext;
+ std::unique_ptr<::comphelper::OPropertyArrayAggregationHelper>
+ m_pPropertyArrayHelper;
+ ::comphelper::PropertyBag m_aDynamicProperties;
+ bool m_bDisposed;
+
+ public:
+ PropertyBagHelper( IPropertyBagHelperContext& _rContext );
+ ~PropertyBagHelper();
+ PropertyBagHelper(const PropertyBagHelper&) = delete;
+ PropertyBagHelper& operator=(const PropertyBagHelper&) = delete;
+
+ // XComponent equivalent
+ void dispose();
+
+ // OPropertySetHelper equivalent
+ inline ::comphelper::OPropertyArrayAggregationHelper& getInfoHelper() const;
+
+ // XPropertyContainer equivalent
+ void addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const css::uno::Any& _rInitialValue );
+ void removeProperty( const OUString& _rName );
+
+ // XPropertyAccess equivalent
+ css::uno::Sequence< css::beans::PropertyValue > getPropertyValues();
+ void setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& _rProps );
+
+ // forwards to m_aDynamicProperties
+ inline void getDynamicFastPropertyValue( sal_Int32 _nHandle, css::uno::Any& _out_rValue ) const;
+ inline bool convertDynamicFastPropertyValue( sal_Int32 _nHandle, const css::uno::Any& _rNewValue, css::uno::Any& _out_rConvertedValue, css::uno::Any& _out_rCurrentValue ) const;
+ inline void setDynamicFastPropertyValue( sal_Int32 _nHandle, const css::uno::Any& _rValue );
+ inline void getDynamicPropertyDefaultByHandle( sal_Int32 _nHandle, css::uno::Any& _out_rValue ) const;
+ inline bool hasDynamicPropertyByHandle( sal_Int32 _nHandle ) const;
+
+ private:
+ void impl_nts_checkDisposed_throw() const;
+
+ /** invalidates our property set info, so subsequent calls to impl_ts_getArrayHelper and thus
+ getInfoHelper will return a newly created instance
+ */
+ void impl_nts_invalidatePropertySetInfo();
+
+ /** returns the IPropertyArrayHelper instance used by |this|
+ */
+ ::comphelper::OPropertyArrayAggregationHelper& impl_ts_getArrayHelper() const;
+
+ /** finds a free property handle
+ @param _rPropertyName
+ the name of the property to find a handle for. If possible, the handle as determined by
+ our ConcreteInfoService instance will be used
+ */
+ sal_Int32 impl_findFreeHandle( const OUString& _rPropertyName );
+ };
+
+
+ inline ::comphelper::OPropertyArrayAggregationHelper& PropertyBagHelper::getInfoHelper() const
+ {
+ return impl_ts_getArrayHelper();
+ }
+
+
+ inline void PropertyBagHelper::getDynamicFastPropertyValue( sal_Int32 _nHandle, css::uno::Any& _out_rValue ) const
+ {
+ m_aDynamicProperties.getFastPropertyValue( _nHandle, _out_rValue );
+ }
+
+
+ inline bool PropertyBagHelper::convertDynamicFastPropertyValue( sal_Int32 _nHandle, const css::uno::Any& _rNewValue, css::uno::Any& _out_rConvertedValue, css::uno::Any& _out_rCurrentValue ) const
+ {
+ return m_aDynamicProperties.convertFastPropertyValue( _nHandle, _rNewValue, _out_rConvertedValue, _out_rCurrentValue );
+ }
+
+
+ inline void PropertyBagHelper::setDynamicFastPropertyValue( sal_Int32 _nHandle, const css::uno::Any& _rValue )
+ {
+ m_aDynamicProperties.setFastPropertyValue( _nHandle, _rValue );
+ }
+
+
+ inline void PropertyBagHelper::getDynamicPropertyDefaultByHandle( sal_Int32 _nHandle, css::uno::Any& _out_rValue ) const
+ {
+ m_aDynamicProperties.getPropertyDefaultByHandle( _nHandle, _out_rValue );
+ }
+
+ inline bool PropertyBagHelper::hasDynamicPropertyByHandle( sal_Int32 _nHandle ) const
+ {
+ return m_aDynamicProperties.hasPropertyByHandle( _nHandle );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/resettable.hxx b/forms/source/inc/resettable.hxx
new file mode 100644
index 0000000000..cbbc13e50b
--- /dev/null
+++ b/forms/source/inc/resettable.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/form/XResetListener.hpp>
+
+#include <comphelper/interfacecontainer3.hxx>
+
+namespace cppu
+{
+ class OWeakObject;
+}
+
+
+namespace frm
+{
+
+ class ResetHelper
+ {
+ public:
+ ResetHelper( ::cppu::OWeakObject& _parent, ::osl::Mutex& _mutex )
+ :m_rParent( _parent )
+ ,m_aResetListeners( _mutex )
+ {
+ }
+
+ // XReset equivalents
+ void addResetListener( const css::uno::Reference< css::form::XResetListener >& _listener );
+ void removeResetListener( const css::uno::Reference< css::form::XResetListener >& _listener );
+
+ // calling listeners
+ bool approveReset();
+ void notifyResetted();
+ void disposing();
+
+ private:
+ ::cppu::OWeakObject& m_rParent;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XResetListener> m_aResetListeners;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/services.hxx b/forms/source/inc/services.hxx
new file mode 100644
index 0000000000..acd15cd636
--- /dev/null
+++ b/forms/source/inc/services.hxx
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+inline constexpr OUString VCL_CONTROL_LISTBOX = u"stardiv.vcl.control.ListBox"_ustr;
+inline constexpr OUString VCL_CONTROL_COMBOBOX = u"stardiv.vcl.control.ComboBox"_ustr;
+inline constexpr OUString VCL_CONTROL_RADIOBUTTON = u"stardiv.vcl.control.RadioButton"_ustr;
+inline constexpr OUString VCL_CONTROL_GROUPBOX = u"stardiv.vcl.control.GroupBox"_ustr;
+inline constexpr OUString VCL_CONTROL_COMMANDBUTTON = u"stardiv.vcl.control.Button"_ustr;
+inline constexpr OUString VCL_CONTROL_CHECKBOX = u"stardiv.vcl.control.CheckBox"_ustr;
+inline constexpr OUString VCL_CONTROL_IMAGEBUTTON = u"stardiv.vcl.control.ImageButton"_ustr;
+inline constexpr OUString VCL_CONTROL_TIMEFIELD = u"stardiv.vcl.control.TimeField"_ustr;
+inline constexpr OUString VCL_CONTROL_DATEFIELD = u"stardiv.vcl.control.DateField"_ustr;
+inline constexpr OUString VCL_CONTROL_NUMERICFIELD = u"stardiv.vcl.control.NumericField"_ustr;
+inline constexpr OUString VCL_CONTROL_CURRENCYFIELD = u"stardiv.vcl.control.CurrencyField"_ustr;
+inline constexpr OUString VCL_CONTROL_PATTERNFIELD = u"stardiv.vcl.control.PatternField"_ustr;
+inline constexpr OUString VCL_CONTROL_FORMATTEDFIELD = u"stardiv.vcl.control.FormattedField"_ustr;
+inline constexpr OUString VCL_CONTROL_IMAGECONTROL = u"stardiv.vcl.control.ImageControl"_ustr;
+
+inline constexpr OUString VCL_CONTROLMODEL_EDIT = u"stardiv.vcl.controlmodel.Edit"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_LISTBOX = u"stardiv.vcl.controlmodel.ListBox"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_COMBOBOX = u"stardiv.vcl.controlmodel.ComboBox"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_RADIOBUTTON = u"stardiv.vcl.controlmodel.RadioButton"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_GROUPBOX = u"stardiv.vcl.controlmodel.GroupBox"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_FIXEDTEXT = u"stardiv.vcl.controlmodel.FixedText"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_COMMANDBUTTON = u"stardiv.vcl.controlmodel.Button"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_CHECKBOX = u"stardiv.vcl.controlmodel.CheckBox"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_IMAGEBUTTON = u"stardiv.vcl.controlmodel.ImageButton"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_FILECONTROL = u"stardiv.vcl.controlmodel.FileControl"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_TIMEFIELD = u"stardiv.vcl.controlmodel.TimeField"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_DATEFIELD = u"stardiv.vcl.controlmodel.DateField"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_NUMERICFIELD = u"stardiv.vcl.controlmodel.NumericField"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_CURRENCYFIELD = u"stardiv.vcl.controlmodel.CurrencyField"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_PATTERNFIELD = u"stardiv.vcl.controlmodel.PatternField"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_FORMATTEDFIELD = u"stardiv.vcl.controlmodel.FormattedField"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_IMAGECONTROL = u"stardiv.vcl.controlmodel.ImageControl"_ustr;
+
+inline constexpr OUString VCL_CONTROLMODEL_SCROLLBAR = u"com.sun.star.awt.UnoControlScrollBarModel"_ustr;
+inline constexpr OUString VCL_CONTROL_SCROLLBAR = u"com.sun.star.awt.UnoControlScrollBar"_ustr;
+inline constexpr OUString VCL_CONTROLMODEL_SPINBUTTON = u"com.sun.star.awt.UnoControlSpinButtonModel"_ustr;
+inline constexpr OUString VCL_CONTROL_SPINBUTTON = u"com.sun.star.awt.UnoControlSpinButton"_ustr;
+
+// service names for compatibility
+
+inline constexpr OUString FRM_COMPONENT_FORM = u"stardiv.one.form.component.Form"_ustr;
+inline constexpr OUString FRM_COMPONENT_EDIT = u"stardiv.one.form.component.Edit"_ustr; // compatibility
+inline constexpr OUString FRM_COMPONENT_TEXTFIELD = u"stardiv.one.form.component.TextField"_ustr;
+inline constexpr OUString FRM_COMPONENT_LISTBOX = u"stardiv.one.form.component.ListBox"_ustr;
+inline constexpr OUString FRM_COMPONENT_COMBOBOX = u"stardiv.one.form.component.ComboBox"_ustr;
+inline constexpr OUString FRM_COMPONENT_RADIOBUTTON = u"stardiv.one.form.component.RadioButton"_ustr;
+inline constexpr OUString FRM_COMPONENT_GROUPBOX = u"stardiv.one.form.component.GroupBox"_ustr; // compatibility
+inline constexpr OUString FRM_COMPONENT_FIXEDTEXT = u"stardiv.one.form.component.FixedText"_ustr; // compatibility
+inline constexpr OUString FRM_COMPONENT_COMMANDBUTTON = u"stardiv.one.form.component.CommandButton"_ustr;
+inline constexpr OUString FRM_COMPONENT_CHECKBOX = u"stardiv.one.form.component.CheckBox"_ustr;
+inline constexpr OUString FRM_COMPONENT_GRID = u"stardiv.one.form.component.Grid"_ustr; // compatibility
+inline constexpr OUString FRM_COMPONENT_GRIDCONTROL = u"stardiv.one.form.component.GridControl"_ustr;
+inline constexpr OUString FRM_COMPONENT_IMAGEBUTTON = u"stardiv.one.form.component.ImageButton"_ustr;
+inline constexpr OUString FRM_COMPONENT_FILECONTROL = u"stardiv.one.form.component.FileControl"_ustr;
+inline constexpr OUString FRM_COMPONENT_TIMEFIELD = u"stardiv.one.form.component.TimeField"_ustr;
+inline constexpr OUString FRM_COMPONENT_DATEFIELD = u"stardiv.one.form.component.DateField"_ustr;
+inline constexpr OUString FRM_COMPONENT_NUMERICFIELD = u"stardiv.one.form.component.NumericField"_ustr;
+inline constexpr OUString FRM_COMPONENT_CURRENCYFIELD = u"stardiv.one.form.component.CurrencyField"_ustr;
+inline constexpr OUString FRM_COMPONENT_PATTERNFIELD = u"stardiv.one.form.component.PatternField"_ustr;
+inline constexpr OUString FRM_COMPONENT_HIDDEN = u"stardiv.one.form.component.Hidden"_ustr;
+inline constexpr OUString FRM_COMPONENT_HIDDENCONTROL = u"stardiv.one.form.component.HiddenControl"_ustr;
+inline constexpr OUString FRM_COMPONENT_IMAGECONTROL = u"stardiv.one.form.component.ImageControl"_ustr;
+inline constexpr OUString FRM_COMPONENT_FORMATTEDFIELD = u"stardiv.one.form.component.FormattedField"_ustr;
+
+// <compatibility_I>
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_COMMANDBUTTON = u"stardiv.one.form.control.CommandButton"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_RADIOBUTTON = u"stardiv.one.form.control.RadioButton"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_CHECKBOX = u"stardiv.one.form.control.CheckBox"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_EDIT = u"stardiv.one.form.control.Edit"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_LISTBOX = u"stardiv.one.form.control.ListBox"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_COMBOBOX = u"stardiv.one.form.control.ComboBox"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_GROUPBOX = u"stardiv.one.form.control.GroupBox"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_TEXTFIELD = u"stardiv.one.form.control.TextField"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_GRID = u"stardiv.one.form.control.Grid"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_IMAGEBUTTON = u"stardiv.one.form.control.ImageButton"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_TIMEFIELD = u"stardiv.one.form.control.TimeField"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_DATEFIELD = u"stardiv.one.form.control.DateField"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_NUMERICFIELD = u"stardiv.one.form.control.NumericField"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_CURRENCYFIELD = u"stardiv.one.form.control.CurrencyField"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_PATTERNFIELD = u"stardiv.one.form.control.PatternField"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_IMAGECONTROL = u"stardiv.one.form.control.ImageControl"_ustr;
+inline constexpr OUString STARDIV_ONE_FORM_CONTROL_FORMATTEDFIELD = u"stardiv.one.form.control.FormattedField"_ustr;
+// </compatibility_I>
+
+// new (sun) service names
+
+inline constexpr OUString FRM_SUN_COMPONENT_FORM = u"com.sun.star.form.component.Form"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_HTMLFORM = u"com.sun.star.form.component.HTMLForm"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATAFORM = u"com.sun.star.form.component.DataForm"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_TEXTFIELD = u"com.sun.star.form.component.TextField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_LISTBOX = u"com.sun.star.form.component.ListBox"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_COMBOBOX = u"com.sun.star.form.component.ComboBox"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_RADIOBUTTON = u"com.sun.star.form.component.RadioButton"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_GROUPBOX = u"com.sun.star.form.component.GroupBox"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_FIXEDTEXT = u"com.sun.star.form.component.FixedText"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_COMMANDBUTTON = u"com.sun.star.form.component.CommandButton"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_CHECKBOX = u"com.sun.star.form.component.CheckBox"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_GRIDCONTROL = u"com.sun.star.form.component.GridControl"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_IMAGEBUTTON = u"com.sun.star.form.component.ImageButton"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_FILECONTROL = u"com.sun.star.form.component.FileControl"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_TIMEFIELD = u"com.sun.star.form.component.TimeField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATEFIELD = u"com.sun.star.form.component.DateField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_NUMERICFIELD = u"com.sun.star.form.component.NumericField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_CURRENCYFIELD = u"com.sun.star.form.component.CurrencyField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_PATTERNFIELD = u"com.sun.star.form.component.PatternField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_HIDDENCONTROL = u"com.sun.star.form.component.HiddenControl"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_FORMATTEDFIELD = u"com.sun.star.form.component.FormattedField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_SCROLLBAR = u"com.sun.star.form.component.ScrollBar"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_SPINBUTTON = u"com.sun.star.form.component.SpinButton"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_RICHTEXTCONTROL = u"com.sun.star.form.component.RichTextControl"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_NAVTOOLBAR = u"com.sun.star.form.component.NavigationToolBar"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_SUBMITBUTTON = u"com.sun.star.form.component.SubmitButton"_ustr;
+
+inline constexpr OUString FRM_SUN_COMPONENT_IMAGECONTROL = u"com.sun.star.form.component.DatabaseImageControl"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_RADIOBUTTON = u"com.sun.star.form.component.DatabaseRadioButton"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_CHECKBOX = u"com.sun.star.form.component.DatabaseCheckBox"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_LISTBOX = u"com.sun.star.form.component.DatabaseListBox"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_COMBOBOX = u"com.sun.star.form.component.DatabaseComboBox"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_FORMATTEDFIELD = u"com.sun.star.form.component.DatabaseFormattedField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_TEXTFIELD = u"com.sun.star.form.component.DatabaseTextField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_DATEFIELD = u"com.sun.star.form.component.DatabaseDateField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_TIMEFIELD = u"com.sun.star.form.component.DatabaseTimeField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_NUMERICFIELD = u"com.sun.star.form.component.DatabaseNumericField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_CURRENCYFIELD = u"com.sun.star.form.component.DatabaseCurrencyField"_ustr;
+inline constexpr OUString FRM_SUN_COMPONENT_DATABASE_PATTERNFIELD = u"com.sun.star.form.component.DatabasePatternField"_ustr;
+
+inline constexpr OUString FRM_SUN_CONTROL_TEXTFIELD = u"com.sun.star.form.control.TextField"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_LISTBOX = u"com.sun.star.form.control.ListBox"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_COMBOBOX = u"com.sun.star.form.control.ComboBox"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_RADIOBUTTON = u"com.sun.star.form.control.RadioButton"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_GROUPBOX = u"com.sun.star.form.control.GroupBox"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_COMMANDBUTTON = u"com.sun.star.form.control.CommandButton"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_CHECKBOX = u"com.sun.star.form.control.CheckBox"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_GRIDCONTROL = u"com.sun.star.form.control.GridControl"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_IMAGEBUTTON = u"com.sun.star.form.control.ImageButton"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_TIMEFIELD = u"com.sun.star.form.control.TimeField"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_DATEFIELD = u"com.sun.star.form.control.DateField"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_NUMERICFIELD = u"com.sun.star.form.control.NumericField"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_CURRENCYFIELD = u"com.sun.star.form.control.CurrencyField"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_PATTERNFIELD = u"com.sun.star.form.control.PatternField"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_IMAGECONTROL = u"com.sun.star.form.control.ImageControl"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_FORMATTEDFIELD = u"com.sun.star.form.control.FormattedField"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_RICHTEXTCONTROL = u"com.sun.star.form.control.RichTextControl"_ustr;
+inline constexpr OUString FRM_SUN_CONTROL_SUBMITBUTTON = u"com.sun.star.form.control.SubmitButton"_ustr;
+
+inline constexpr OUString BINDABLE_DATABASE_CHECK_BOX = u"com.sun.star.form.binding.BindableDatabaseCheckBox"_ustr;
+inline constexpr OUString BINDABLE_DATABASE_COMBO_BOX = u"com.sun.star.form.binding.BindableDatabaseComboBox"_ustr;
+inline constexpr OUString BINDABLE_DATABASE_FORMATTED_FIELD = u"com.sun.star.form.binding.BindableDatabaseFormattedField"_ustr;
+inline constexpr OUString BINDABLE_DATABASE_LIST_BOX = u"com.sun.star.form.binding.BindableDatabaseListBox"_ustr;
+inline constexpr OUString BINDABLE_DATABASE_NUMERIC_FIELD = u"com.sun.star.form.binding.BindableDatabaseNumericField"_ustr;
+inline constexpr OUString BINDABLE_DATABASE_RADIO_BUTTON = u"com.sun.star.form.binding.BindableDatabaseRadioButton"_ustr;
+inline constexpr OUString BINDABLE_DATABASE_TEXT_FIELD = u"com.sun.star.form.binding.BindableDatabaseTextField"_ustr;
+inline constexpr OUString BINDABLE_DATABASE_DATE_FIELD = u"com.sun.star.form.binding.BindableDatabaseDateField"_ustr;
+inline constexpr OUString BINDABLE_DATABASE_TIME_FIELD = u"com.sun.star.form.binding.BindableDatabaseTimeField"_ustr;
+
+inline constexpr OUString BINDABLE_CONTROL_MODEL = u"com.sun.star.form.binding.BindableControlModel"_ustr;
+inline constexpr OUString BINDABLE_INTEGER_VALUE_RANGE = u"com.sun.star.form.binding.BindableIntegerValueRange"_ustr;
+inline constexpr OUString BINDABLE_DATA_AWARE_CONTROL_MODEL = u"com.sun.star.form.binding.BindableDataAwareControlModel"_ustr;
+inline constexpr OUString DATA_AWARE_CONTROL_MODEL = u"com.sun.star.form.binding.DataAwareControlModel"_ustr;
+inline constexpr OUString VALIDATABLE_CONTROL_MODEL = u"com.sun.star.form.binding.ValidatableControlModel"_ustr;
+inline constexpr OUString VALIDATABLE_BINDABLE_CONTROL_MODEL = u"com.sun.star.form.binding.ValidatableBindableControlModel"_ustr;
+
+// common
+
+inline constexpr OUString FRM_SUN_FORMCOMPONENT = u"com.sun.star.form.FormComponent"_ustr;
+
+// misc
+
+inline constexpr OUString SRV_SDB_ROWSET = u"com.sun.star.sdb.RowSet"_ustr;
+inline constexpr OUString SRV_SDB_CONNECTION = u"com.sun.star.sdb.Connection"_ustr;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/togglestate.hxx b/forms/source/inc/togglestate.hxx
new file mode 100644
index 0000000000..aeacabc515
--- /dev/null
+++ b/forms/source/inc/togglestate.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+
+namespace frm
+{
+
+
+ enum ToggleState { TRISTATE_FALSE = 0, TRISTATE_TRUE = 1, TRISTATE_INDET = 2 };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/urltransformer.hxx b/forms/source/inc/urltransformer.hxx
new file mode 100644
index 0000000000..3cfe225945
--- /dev/null
+++ b/forms/source/inc/urltransformer.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/URL.hpp>
+
+
+namespace frm
+{
+
+ class UrlTransformer
+ {
+ private:
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xORB;
+ mutable css::uno::Reference< css::util::XURLTransformer >
+ m_xTransformer;
+ mutable bool m_bTriedToCreateTransformer;
+
+ public:
+ UrlTransformer( const css::uno::Reference< css::uno::XComponentContext >& _rxORB );
+
+ /** returns a URL object for the given URL string
+ */
+ css::util::URL
+ getStrictURL( const OUString& _rURL ) const;
+
+ /** returns a URL object for the given URL ASCII string
+ */
+ css::util::URL
+ getStrictURLFromAscii( const char* _pAsciiURL ) const;
+
+ /** parses a given URL smartly, with a given protocol
+ */
+ void
+ parseSmartWithProtocol( css::util::URL& _rURL, const OUString& _rProtocol ) const;
+
+ private:
+ /** ensures that we have a URLTransformer instance in <member>m_xTransformer</member>
+
+ @return
+ <TRUE/> if and only if m_xTransformer is not <NULL/>
+ */
+ bool implEnsureTransformer() const;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/inc/windowstateguard.hxx b/forms/source/inc/windowstateguard.hxx
new file mode 100644
index 0000000000..f1a449c39e
--- /dev/null
+++ b/forms/source/inc/windowstateguard.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <rtl/ref.hxx>
+
+
+namespace frm
+{
+
+
+ //= WindowStateGuard
+
+ class WindowStateGuard_Impl;
+
+ /** a helper class which monitors certain states of an XWindow2, and ensures
+ that they're consistent with respective properties at an XModel.
+
+ For form controls, window states - such as "Enabled" - can be set by various
+ means. You can set the respective control model property, you can directly manipulate
+ the XWindow2, or the state can change implicitly due to VCL actions. In any case,
+ we need to ensure that the state does not contradict the model property "too much".
+
+ As an example, consider a form control which, according to its model's property, is disabled.
+ Now when the parent VCL window of the control's VCL window is enabled, then the control's
+ window is enabled, too - which contradicts the model property.
+
+ A WindowStateGuard helps you preventing such inconsistent states.
+
+ The class is not threadsafe.
+ */
+ class WindowStateGuard
+ {
+ private:
+ ::rtl::Reference< WindowStateGuard_Impl > m_pImpl;
+
+ public:
+ WindowStateGuard();
+ ~WindowStateGuard();
+
+ void attach(
+ const css::uno::Reference< css::awt::XWindow2 >& _rxWindow,
+ const css::uno::Reference< css::awt::XControlModel >& _rxModel
+ );
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/misc/InterfaceContainer.cxx b/forms/source/misc/InterfaceContainer.cxx
new file mode 100644
index 0000000000..90a918f5ed
--- /dev/null
+++ b/forms/source/misc/InterfaceContainer.cxx
@@ -0,0 +1,1306 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include <frm_strings.hxx>
+#include <InterfaceContainer.hxx>
+#include <componenttools.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/io/WrongFormatException.hpp>
+#include <com/sun/star/io/XMarkableStream.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/form/XForm.hpp>
+
+#include <comphelper/enumhelper.hxx>
+#include <comphelper/eventattachermgr.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <memory>
+
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/document/XCodeNameQuery.hpp>
+#include <ooo/vba/XVBAToOOEventDescGen.hpp>
+
+namespace frm
+{
+
+
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+
+static bool
+lcl_hasVbaEvents( const Sequence< ScriptEventDescriptor >& sEvents )
+{
+ for ( auto const& rDesc : sEvents )
+ {
+ if ( rDesc.ScriptType == "VBAInterop" )
+ return true;
+ }
+ return false;
+}
+
+static Sequence< ScriptEventDescriptor >
+lcl_stripVbaEvents( const Sequence< ScriptEventDescriptor >& sEvents )
+{
+ Sequence< ScriptEventDescriptor > sStripped( sEvents.getLength() );
+ ScriptEventDescriptor* pStripped = sStripped.getArray();
+
+ sal_Int32 nCopied = 0;
+ for ( auto const& rDesc : sEvents )
+ {
+ if ( rDesc.ScriptType != "VBAInterop" )
+ {
+ pStripped[ nCopied++ ] = rDesc;
+ }
+ }
+ sStripped.realloc( nCopied );
+ return sStripped;
+}
+
+void OInterfaceContainer::impl_addVbEvents_nolck_nothrow( const sal_Int32 i_nIndex )
+{
+ // we are dealing with form controls
+ try
+ {
+ do
+ {
+ Reference< XModel > xDoc( getXModel( static_cast< XContainer *> ( this ) ) );
+ if ( !xDoc.is() )
+ break;
+
+ Reference< XMultiServiceFactory > xDocFac( xDoc, UNO_QUERY_THROW );
+ Reference< XCodeNameQuery > xNameQuery( xDocFac->createInstance("ooo.vba.VBACodeNameProvider"), UNO_QUERY );
+ if ( !xNameQuery.is() )
+ break;
+
+ ::osl::MutexGuard aGuard( m_rMutex );
+ bool hasVBABindings = lcl_hasVbaEvents( m_xEventAttacher->getScriptEvents( i_nIndex ) );
+ if ( hasVBABindings )
+ break;
+
+ Reference< XInterface > xElement( getByIndex( i_nIndex ) , UNO_QUERY_THROW );
+ Reference< XForm > xElementAsForm( xElement, UNO_QUERY );
+ if ( xElementAsForm.is() )
+ break;
+
+ // Try getting the code name from the container first (faster),
+ // then from the element if that fails (slower).
+ Reference<XInterface> xThis = static_cast<XContainer*>(this);
+ OUString sCodeName = xNameQuery->getCodeNameForContainer(xThis);
+ if (sCodeName.isEmpty())
+ sCodeName = xNameQuery->getCodeNameForObject(xElement);
+
+ Reference< XPropertySet > xProps( xElement, UNO_QUERY_THROW );
+ OUString sServiceName;
+ xProps->getPropertyValue("DefaultControl") >>= sServiceName;
+
+ Reference< ooo::vba::XVBAToOOEventDescGen > xDescSupplier( m_xContext->getServiceManager()->createInstanceWithContext("ooo.vba.VBAToOOEventDesc", m_xContext), UNO_QUERY_THROW );
+ Sequence< ScriptEventDescriptor > vbaEvents = xDescSupplier->getEventDescriptions( sServiceName , sCodeName );
+
+ // register the vba script events
+ m_xEventAttacher->registerScriptEvents( i_nIndex, vbaEvents );
+ }
+ while ( false );
+ }
+ catch ( const ServiceNotRegisteredException& )
+ {
+ // silence this, not all document types support the ooo.vba.VBACodeNameProvider service
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.misc");
+ }
+
+}
+
+ElementDescription::ElementDescription( )
+{
+}
+
+
+OInterfaceContainer::OInterfaceContainer(
+ const Reference<XComponentContext>& _rxContext,
+ ::osl::Mutex& _rMutex,
+ const Type& _rElementType)
+ :OInterfaceContainer_BASE()
+ ,m_rMutex(_rMutex)
+ ,m_aContainerListeners(_rMutex)
+ ,m_aElementType(_rElementType)
+ ,m_xContext(_rxContext)
+{
+ impl_createEventAttacher_nothrow();
+}
+
+
+OInterfaceContainer::OInterfaceContainer( ::osl::Mutex& _rMutex, const OInterfaceContainer& _cloneSource )
+ :OInterfaceContainer_BASE()
+ ,m_rMutex( _rMutex )
+ ,m_aContainerListeners( _rMutex )
+ ,m_aElementType( _cloneSource.m_aElementType )
+ ,m_xContext( _cloneSource.m_xContext )
+{
+ impl_createEventAttacher_nothrow();
+}
+
+void OInterfaceContainer::clonedFrom(const OInterfaceContainer& _cloneSource)
+{
+ try
+ {
+ const Reference< XIndexAccess > xSourceHierarchy( const_cast< OInterfaceContainer* >( &_cloneSource ) );
+ const sal_Int32 nCount = xSourceHierarchy->getCount();
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ Reference< XCloneable > xCloneable( xSourceHierarchy->getByIndex( i ), UNO_QUERY_THROW );
+ Reference< XInterface > xClone( xCloneable->createClone() );
+ insertByIndex( i, Any( xClone ) );
+ }
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ throw WrappedTargetRuntimeException(
+ "Could not clone the given interface hierarchy.",
+ static_cast< XIndexContainer* >( const_cast< OInterfaceContainer* >( &_cloneSource ) ),
+ ::cppu::getCaughtException()
+ );
+ }
+}
+
+void OInterfaceContainer::impl_createEventAttacher_nothrow()
+{
+ try
+ {
+ m_xEventAttacher.set( ::comphelper::createEventAttacherManager( m_xContext ), UNO_SET_THROW );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.misc");
+ }
+}
+
+
+OInterfaceContainer::~OInterfaceContainer()
+{
+}
+
+
+void OInterfaceContainer::disposing()
+{
+ // dispose all elements
+ for (sal_Int32 i = m_aItems.size(); i > 0; --i)
+ {
+ Reference<XPropertySet> xSet(m_aItems[i - 1], UNO_QUERY);
+ if (xSet.is())
+ xSet->removePropertyChangeListener(PROPERTY_NAME, this);
+
+ // revoke event knittings
+ if ( m_xEventAttacher.is() )
+ {
+ m_xEventAttacher->detach( i - 1, Reference<XInterface>(xSet, UNO_QUERY) );
+ m_xEventAttacher->removeEntry( i - 1 );
+ }
+
+ Reference<XComponent> xComponent(xSet, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ m_aMap.clear();
+ m_aItems.clear();
+
+ css::lang::EventObject aEvt(static_cast<XContainer*>(this));
+ m_aContainerListeners.disposeAndClear(aEvt);
+}
+
+// XPersistObject
+
+namespace
+{
+
+ void lcl_saveEvents( ::std::vector< Sequence< ScriptEventDescriptor > >& _rSave,
+ const Reference< XEventAttacherManager >& _rxManager, const sal_Int32 _nItemCount )
+ {
+ OSL_ENSURE( _rxManager.is(), "lcl_saveEvents: invalid event attacher manager!" );
+ if ( !_rxManager.is() )
+ return;
+
+ // reserve the space needed
+ _rSave.reserve( _nItemCount );
+
+ // copy the events
+ for (sal_Int32 i=0; i<_nItemCount; ++i)
+ _rSave.push_back(_rxManager->getScriptEvents( i ));
+ }
+
+
+ void lcl_restoreEvents( const ::std::vector< Sequence< ScriptEventDescriptor > >& _rSave,
+ const Reference< XEventAttacherManager >& _rxManager )
+ {
+ OSL_ENSURE( _rxManager.is(), "lcl_restoreEvents: invalid event attacher manager!" );
+ if ( !_rxManager.is() )
+ return;
+
+ sal_Int32 i=0;
+ for (auto const& elem : _rSave)
+ {
+ _rxManager->revokeScriptEvents( i );
+ _rxManager->registerScriptEvents(i, elem);
+ ++i;
+ }
+ }
+}
+
+
+void SAL_CALL OInterfaceContainer::writeEvents(const Reference<XObjectOutputStream>& _rxOutStream)
+{
+ // We're writing a document in SO 5.2 format (or even from earlier versions)
+ // -> convert the events from the new runtime format to the format of the 5.2 files
+ // but before, remember the current script events set for our children
+ ::std::vector< Sequence< ScriptEventDescriptor > > aSave;
+ if ( m_xEventAttacher.is() )
+ lcl_saveEvents( aSave, m_xEventAttacher, m_aItems.size() );
+
+ transformEvents();
+
+ try
+ {
+ Reference<XMarkableStream> xMark(_rxOutStream, UNO_QUERY);
+ sal_Int32 nMark = xMark->createMark();
+
+ sal_Int32 nObjLen = 0;
+ _rxOutStream->writeLong(nObjLen);
+
+ Reference<XPersistObject> xScripts(m_xEventAttacher, UNO_QUERY);
+ if (xScripts.is())
+ xScripts->write(_rxOutStream);
+
+ // Determine length
+ nObjLen = xMark->offsetToMark(nMark) - 4;
+ xMark->jumpToMark(nMark);
+ _rxOutStream->writeLong(nObjLen);
+ xMark->jumpToFurthest();
+ xMark->deleteMark(nMark);
+ }
+ catch( const Exception& )
+ {
+ // restore the events
+ if ( m_xEventAttacher.is() )
+ lcl_restoreEvents( aSave, m_xEventAttacher );
+ throw;
+ }
+
+ // restore the events
+ if ( m_xEventAttacher.is() )
+ lcl_restoreEvents( aSave, m_xEventAttacher );
+}
+
+namespace {
+
+struct TransformEventTo52Format
+{
+ void operator()( ScriptEventDescriptor& _rDescriptor )
+ {
+ if ( _rDescriptor.ScriptType != "StarBasic" )
+ return;
+
+ // it's a starbasic macro
+ sal_Int32 nPrefixLength = _rDescriptor.ScriptCode.indexOf( ':' );
+ if ( 0 <= nPrefixLength )
+ { // the macro name does not already contain a :
+#ifdef DBG_UTIL
+ const OUString sPrefix = _rDescriptor.ScriptCode.copy( 0, nPrefixLength );
+ DBG_ASSERT( sPrefix == "document"
+ || sPrefix == "application",
+ "TransformEventTo52Format: invalid (unknown) prefix!" );
+#endif
+ // cut the prefix
+ _rDescriptor.ScriptCode = _rDescriptor.ScriptCode.copy( nPrefixLength + 1 );
+ }
+ }
+};
+
+}
+
+void OInterfaceContainer::transformEvents()
+{
+ OSL_ENSURE( m_xEventAttacher.is(), "OInterfaceContainer::transformEvents: no event attacher manager!" );
+ if ( !m_xEventAttacher.is() )
+ return;
+
+ try
+ {
+ // loop through all our children
+ sal_Int32 nItems = m_aItems.size();
+ Sequence< ScriptEventDescriptor > aChildEvents;
+
+ for (sal_Int32 i=0; i<nItems; ++i)
+ {
+ // get the script events for this object
+ aChildEvents = m_xEventAttacher->getScriptEvents( i );
+
+ if ( aChildEvents.hasElements() )
+ {
+ // do the transformation
+ auto [begin, end] = asNonConstRange(aChildEvents);
+ ::std::for_each( begin, end, TransformEventTo52Format() );
+
+ // revoke the script events
+ m_xEventAttacher->revokeScriptEvents( i );
+ // and re-register them
+ m_xEventAttacher->registerScriptEvents( i, aChildEvents );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.misc");
+ }
+}
+
+
+void SAL_CALL OInterfaceContainer::readEvents(const Reference<XObjectInputStream>& _rxInStream)
+{
+ ::osl::MutexGuard aGuard( m_rMutex );
+
+ // Read scripting info
+ Reference<XMarkableStream> xMark(_rxInStream, UNO_QUERY);
+ sal_Int32 nObjLen = _rxInStream->readLong();
+ if (nObjLen)
+ {
+ sal_Int32 nMark = xMark->createMark();
+ Reference<XPersistObject> xObj(m_xEventAttacher, UNO_QUERY);
+ if (xObj.is())
+ xObj->read(_rxInStream);
+ xMark->jumpToMark(nMark);
+ _rxInStream->skipBytes(nObjLen);
+ xMark->deleteMark(nMark);
+ }
+
+ // Read Attachment
+ if ( m_xEventAttacher.is() )
+ {
+ sal_Int32 i=0;
+ for (auto const& item : m_aItems)
+ {
+ Reference< XInterface > xAsIFace( item, UNO_QUERY ); // important to normalize this...
+ Reference< XPropertySet > xAsSet( xAsIFace, UNO_QUERY );
+ m_xEventAttacher->attach( i++, xAsIFace, Any( xAsSet ) );
+ }
+ }
+}
+
+
+void SAL_CALL OInterfaceContainer::write( const Reference< XObjectOutputStream >& _rxOutStream )
+{
+ ::osl::MutexGuard aGuard( m_rMutex );
+ sal_Int32 nLen = m_aItems.size();
+
+ // Write length
+ _rxOutStream->writeLong(nLen);
+
+ if (!nLen)
+ return;
+
+ // 1. Version
+ _rxOutStream->writeShort(0x0001);
+
+ // 2. Objects
+ for (sal_Int32 i = 0; i < nLen; i++)
+ {
+ Reference<XPersistObject> xObj(m_aItems[i], UNO_QUERY);
+ if (xObj.is())
+ _rxOutStream->writeObject(xObj);
+ else
+ {
+ // Error
+ }
+ }
+
+ // 3. Scripts
+ writeEvents(_rxOutStream);
+}
+
+
+namespace
+{
+ Reference< XPersistObject > lcl_createPlaceHolder( const Reference< XComponentContext >& _rxORB )
+ {
+ Reference< XPersistObject > xObject( _rxORB->getServiceManager()->createInstanceWithContext(FRM_COMPONENT_HIDDENCONTROL, _rxORB), UNO_QUERY );
+ DBG_ASSERT( xObject.is(), "lcl_createPlaceHolder: could not create a substitute for the unknown object!" );
+ if ( xObject.is() )
+ {
+ // set some properties describing what we did
+ Reference< XPropertySet > xObjProps( xObject, UNO_QUERY );
+ if ( xObject.is() )
+ {
+ try
+ {
+ xObjProps->setPropertyValue( PROPERTY_NAME, Any( ResourceManager::loadString(RID_STR_CONTROL_SUBSTITUTED_NAME) ) );
+ xObjProps->setPropertyValue( PROPERTY_TAG, Any( ResourceManager::loadString(RID_STR_CONTROL_SUBSTITUTED_EPXPLAIN) ) );
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ }
+ return xObject;
+ }
+}
+
+
+void SAL_CALL OInterfaceContainer::read( const Reference< XObjectInputStream >& _rxInStream )
+{
+ ::osl::MutexGuard aGuard( m_rMutex );
+
+ // after ::read the object is expected to be in the state it was when ::write was called, so we have
+ // to empty ourself here
+ while (getCount())
+ removeByIndex(0);
+
+ // Only writes depending on the length
+ sal_Int32 nLen = _rxInStream->readLong();
+
+ if (nLen)
+ {
+ // 1. Version
+ _rxInStream->readShort();
+
+ // 2. Objects
+ for (sal_Int32 i = 0; i < nLen; i++)
+ {
+ Reference<XPersistObject> xObj;
+ try
+ {
+ xObj = _rxInStream->readObject();
+ }
+ catch(const WrongFormatException&)
+ {
+ // the object could not be read
+ // create an object (so the readEvents below will assign the events to the right controls)
+ xObj = lcl_createPlaceHolder( m_xContext );
+ if ( !xObj.is() )
+ // couldn't handle it
+ throw;
+ }
+ catch(const Exception&)
+ {
+ // Clear the map
+ while (!m_aItems.empty())
+ removeElementsNoEvents();
+
+ // Rethrow the exception
+ throw;
+ }
+
+ if ( xObj.is() )
+ {
+ Reference< XPropertySet > xElement( xObj, UNO_QUERY );
+ try
+ {
+ implInsert(
+ m_aItems.size(), // position
+ xElement, // element to insert
+ false, // no event attacher manager handling
+ nullptr, // not yet approved - let implInsert do it
+ true // fire the event
+ );
+ }
+ catch( const Exception& )
+ {
+ SAL_WARN("forms.misc", "OInterfaceContainerHelper3::read: reading succeeded, but not inserting!" );
+ // create a placeholder
+ xElement.set(lcl_createPlaceHolder( m_xContext ), css::uno::UNO_QUERY);
+ if ( !xElement.is() )
+ // couldn't handle it
+ throw;
+ // insert the placeholder
+ implInsert( m_aItems.size(), xElement, false, nullptr, true );
+ }
+ }
+ }
+
+ readEvents(_rxInStream);
+ }
+ else
+ {
+ try
+ {
+ m_xEventAttacher = ::comphelper::createEventAttacherManager( m_xContext );
+ OSL_ENSURE( m_xEventAttacher.is(), "OInterfaceContainer::read: could not create an event attacher manager!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.misc");
+ }
+ }
+}
+
+// XContainer
+
+void SAL_CALL OInterfaceContainer::addContainerListener(const Reference<XContainerListener>& _rxListener)
+{
+ m_aContainerListeners.addInterface(_rxListener);
+}
+
+
+void SAL_CALL OInterfaceContainer::removeContainerListener(const Reference<XContainerListener>& _rxListener)
+{
+ m_aContainerListeners.removeInterface(_rxListener);
+}
+
+// XEventListener
+
+void SAL_CALL OInterfaceContainer::disposing(const css::lang::EventObject& _rSource)
+{
+ ::osl::MutexGuard aGuard( m_rMutex );
+
+ Reference< XInterface > xSource( _rSource.Source, UNO_QUERY );
+ // normalized source
+
+ OInterfaceArray::iterator j;
+ for ( j = m_aItems.begin(); j != m_aItems.end(); ++j )
+ {
+ DBG_ASSERT( j->get() == Reference< XInterface >( *j, UNO_QUERY ).get(),
+ "OInterfaceContainer::disposing: vector element not normalized!" );
+
+ if ( xSource.get() == j->get() )
+ // found the element
+ break;
+ }
+
+ if ( m_aItems.end() == j )
+ return;
+
+ m_aItems.erase(j);
+
+ // look up in, and erase from, m_aMap, too
+ OInterfaceMap::iterator i = m_aMap.begin();
+ while ( i != m_aMap.end() )
+ {
+ DBG_ASSERT( i->second.get() == Reference< XInterface >( i->second, UNO_QUERY ).get(),
+ "OInterfaceContainer::disposing: map element not normalized!" );
+
+ if ( i->second.get() == xSource.get() )
+ {
+ // found it
+ m_aMap.erase(i);
+ break;
+ }
+
+ ++i;
+
+ DBG_ASSERT( i != m_aMap.end(), "OInterfaceContainer::disposing: inconsistency: the element was in m_aItems, but not in m_aMap!" );
+ }
+}
+
+// XPropertyChangeListener
+
+void OInterfaceContainer::propertyChange(const PropertyChangeEvent& evt) {
+ if (evt.PropertyName != PROPERTY_NAME)
+ return;
+
+ ::osl::MutexGuard aGuard( m_rMutex );
+ auto range = m_aMap.equal_range(::comphelper::getString(evt.OldValue));
+ for (auto it = range.first; it != range.second; ++it)
+ if (it->second == evt.Source)
+ {
+ css::uno::Reference<css::uno::XInterface> xCorrectType(it->second);
+ m_aMap.erase(it);
+ m_aMap.insert(::std::pair<const OUString, css::uno::Reference<css::uno::XInterface> >(::comphelper::getString(evt.NewValue),xCorrectType));
+ break;
+ }
+}
+
+// XElementAccess
+
+sal_Bool SAL_CALL OInterfaceContainer::hasElements()
+{
+ return !m_aMap.empty();
+}
+
+
+Type SAL_CALL OInterfaceContainer::getElementType()
+{
+ return m_aElementType;
+}
+
+// XEnumerationAccess
+
+Reference<XEnumeration> SAL_CALL OInterfaceContainer::createEnumeration()
+{
+ ::osl::MutexGuard aGuard( m_rMutex );
+ return new ::comphelper::OEnumerationByIndex(static_cast<XIndexAccess*>(this));
+}
+
+// XNameAccess
+
+Any SAL_CALL OInterfaceContainer::getByName( const OUString& _rName )
+{
+ ::std::pair <OInterfaceMap::iterator,
+ OInterfaceMap::iterator> aPair = m_aMap.equal_range(_rName);
+
+ if (aPair.first == aPair.second)
+ throw NoSuchElementException();
+
+ return (*aPair.first).second->queryInterface( m_aElementType );
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL OInterfaceContainer::getElementNames()
+{
+ return comphelper::mapKeysToSequence(m_aMap);
+}
+
+
+sal_Bool SAL_CALL OInterfaceContainer::hasByName( const OUString& _rName )
+{
+ ::std::pair <OInterfaceMap::iterator,
+ OInterfaceMap::iterator> aPair = m_aMap.equal_range(_rName);
+ return aPair.first != aPair.second;
+}
+
+// XIndexAccess
+
+sal_Int32 OInterfaceContainer::getCount()
+{
+ return m_aItems.size();
+}
+
+
+Any OInterfaceContainer::getByIndex(sal_Int32 _nIndex)
+{
+ if (_nIndex < 0 || (o3tl::make_unsigned(_nIndex) >= m_aItems.size()))
+ throw IndexOutOfBoundsException();
+
+ return m_aItems[_nIndex]->queryInterface( m_aElementType );
+}
+
+
+void OInterfaceContainer::approveNewElement( const Reference< XPropertySet >& _rxObject, ElementDescription* _pElement )
+{
+ // it has to be non-NULL
+ if ( !_rxObject.is() )
+ throw IllegalArgumentException(ResourceManager::loadString(RID_STR_NEED_NON_NULL_OBJECT), static_cast<XContainer*>(this), 1);
+
+ // it has to support our element type interface
+ Any aCorrectType = _rxObject->queryInterface( m_aElementType );
+ if ( !aCorrectType.hasValue() )
+ throw IllegalArgumentException();
+
+ // it has to have a "Name" property
+ if ( !hasProperty( PROPERTY_NAME, _rxObject ) )
+ throw IllegalArgumentException();
+
+ // it has to be a child, and it must not have a parent already
+ Reference< XChild > xChild( _rxObject, UNO_QUERY );
+ if ( !xChild.is() || xChild->getParent().is() )
+ {
+ throw IllegalArgumentException();
+ }
+
+ // passed all tests. cache the information we have so far
+ DBG_ASSERT( _pElement, "OInterfaceContainer::approveNewElement: invalid event descriptor!" );
+ if ( _pElement )
+ {
+ _pElement->xPropertySet = _rxObject;
+ _pElement->xChild = xChild;
+ _pElement->aElementTypeInterface = aCorrectType;
+ _pElement->xInterface = Reference< XInterface >( _rxObject, UNO_QUERY ); // normalized XInterface
+ }
+}
+
+
+void OInterfaceContainer::implInsert(sal_Int32 _nIndex, const Reference< XPropertySet >& _rxElement,
+ bool _bEvents, ElementDescription* _pApprovalResult, bool _bFire )
+{
+ const bool bHandleEvents = _bEvents && m_xEventAttacher.is();
+
+ // SYNCHRONIZED ----->
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+
+ std::unique_ptr< ElementDescription > aAutoDeleteMetaData;
+ ElementDescription* pElementMetaData = _pApprovalResult;
+ if ( !pElementMetaData )
+ { // not yet approved by the caller -> do ourself
+ pElementMetaData = createElementMetaData();
+ DBG_ASSERT( pElementMetaData, "OInterfaceContainer::implInsert: createElementMetaData returned nonsense!" );
+
+ // ensure that the meta data structure will be deleted later on
+ aAutoDeleteMetaData.reset( pElementMetaData );
+
+ // will throw an exception if necessary
+ approveNewElement( _rxElement, pElementMetaData );
+ }
+
+
+ // approveNewElement (no matter if called here or outside) has ensure that all relevant interfaces
+ // exist
+
+ // set the name, and add as change listener for the name
+ OUString sName;
+ _rxElement->getPropertyValue(PROPERTY_NAME) >>= sName;
+ _rxElement->addPropertyChangeListener(PROPERTY_NAME, this);
+
+ // insert the object into our internal structures
+ if (_nIndex > static_cast<sal_Int32>(m_aItems.size())) // Calculate the actual index
+ {
+ _nIndex = m_aItems.size();
+ m_aItems.push_back( pElementMetaData->xInterface );
+ }
+ else
+ m_aItems.insert( m_aItems.begin() + _nIndex, pElementMetaData->xInterface );
+
+ m_aMap.insert( ::std::pair< const OUString, css::uno::Reference<css::uno::XInterface> >( sName, pElementMetaData->xInterface ) );
+
+ // announce ourself as parent to the new element
+ pElementMetaData->xChild->setParent(static_cast<XContainer*>(this));
+
+ // handle the events
+ if ( bHandleEvents )
+ {
+ m_xEventAttacher->insertEntry(_nIndex);
+ m_xEventAttacher->attach( _nIndex, pElementMetaData->xInterface, Any( _rxElement ) );
+ }
+
+ // notify derived classes
+ implInserted( pElementMetaData );
+
+ aGuard.clear();
+ // <----- SYNCHRONIZED
+
+ // insert faked VBA events?
+ bool bHandleVbaEvents = false;
+ try
+ {
+ _rxElement->getPropertyValue("GenerateVbaEvents") >>= bHandleVbaEvents;
+ }
+ catch( const Exception& )
+ {
+ }
+ if ( bHandleVbaEvents )
+ {
+ Reference< XEventAttacherManager > xMgr ( pElementMetaData->xInterface, UNO_QUERY );
+ OInterfaceContainer* pIfcMgr = xMgr.is() ? dynamic_cast<OInterfaceContainer*>(xMgr.get()) : nullptr;
+ if (pIfcMgr)
+ {
+ sal_Int32 nLen = pIfcMgr->getCount();
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ // add fake events to the control at index i
+ pIfcMgr->impl_addVbEvents_nolck_nothrow( i );
+ }
+ }
+ else
+ {
+ // add fake events to the control at index i
+ impl_addVbEvents_nolck_nothrow( _nIndex );
+ }
+ }
+
+ // fire the notification about the change
+ if ( _bFire )
+ {
+ // notify listeners
+ ContainerEvent aEvt;
+ aEvt.Source = static_cast<XContainer*>(this);
+ aEvt.Accessor <<= _nIndex;
+ aEvt.Element = pElementMetaData->aElementTypeInterface;
+
+ m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvt );
+ }
+}
+
+
+void OInterfaceContainer::removeElementsNoEvents()
+{
+ OInterfaceArray::iterator i = m_aItems.begin();
+ css::uno::Reference<css::uno::XInterface> xElement(*i);
+
+ OInterfaceMap::iterator j = std::find_if(m_aMap.begin(), m_aMap.end(),
+ [&xElement](const OInterfaceMap::value_type& rEntry) { return rEntry.second == xElement; });
+
+ m_aItems.erase(i);
+ m_aMap.erase(j);
+
+ Reference<XPropertySet> xSet(xElement, UNO_QUERY);
+ if (xSet.is())
+ xSet->removePropertyChangeListener(PROPERTY_NAME, this);
+
+ Reference<XChild> xChild(xElement, UNO_QUERY);
+ if (xChild.is())
+ xChild->setParent(css::uno::Reference<css::uno::XInterface> ());
+}
+
+
+void OInterfaceContainer::implInserted( const ElementDescription* /*_pElement*/ )
+{
+ // not interested in
+}
+
+
+void OInterfaceContainer::implRemoved( const css::uno::Reference<css::uno::XInterface>& /*_rxObject*/ )
+{
+ // not interested in
+}
+
+
+void OInterfaceContainer::impl_replacedElement( const ContainerEvent& _rEvent, ::osl::ClearableMutexGuard& _rInstanceLock )
+{
+ _rInstanceLock.clear();
+ m_aContainerListeners.notifyEach( &XContainerListener::elementReplaced, _rEvent );
+}
+
+// XIndexContainer
+
+void SAL_CALL OInterfaceContainer::insertByIndex( sal_Int32 _nIndex, const Any& _rElement )
+{
+ Reference< XPropertySet > xElement;
+ _rElement >>= xElement;
+ implInsert( _nIndex, xElement, true /* event handling */ , nullptr /* not yet approved */ , true /* notification */ );
+}
+
+
+void OInterfaceContainer::implReplaceByIndex( const sal_Int32 _nIndex, const Any& _rNewElement, ::osl::ClearableMutexGuard& _rClearBeforeNotify )
+{
+ OSL_PRECOND( ( _nIndex >= 0 ) && ( o3tl::make_unsigned(_nIndex) < m_aItems.size() ), "OInterfaceContainer::implReplaceByIndex: precondition not met (index)!" );
+
+ // approve the new object
+ std::unique_ptr< ElementDescription > aElementMetaData( createElementMetaData() );
+ DBG_ASSERT(aElementMetaData,
+ "OInterfaceContainer::implReplaceByIndex: createElementMetaData returned nonsense!");
+ {
+ Reference< XPropertySet > xElementProps;
+ _rNewElement >>= xElementProps;
+ approveNewElement( xElementProps, aElementMetaData.get() );
+ }
+
+ // get the old element
+ css::uno::Reference<css::uno::XInterface> xOldElement( m_aItems[ _nIndex ] );
+ DBG_ASSERT( xOldElement.get() == Reference< XInterface >( xOldElement, UNO_QUERY ).get(),
+ "OInterfaceContainer::implReplaceByIndex: elements should be held normalized!" );
+
+ // locate the old element in the map
+ OInterfaceMap::iterator j = std::find_if(m_aMap.begin(), m_aMap.end(),
+ [&xOldElement](const OInterfaceMap::value_type& rEntry) { return rEntry.second.get() == xOldElement.get(); });
+
+ // remove event knittings
+ if ( m_xEventAttacher.is() )
+ {
+ css::uno::Reference<css::uno::XInterface> xNormalized( xOldElement, UNO_QUERY );
+ m_xEventAttacher->detach( _nIndex, xNormalized );
+ m_xEventAttacher->removeEntry( _nIndex );
+ }
+
+ // don't listen for property changes anymore
+ Reference<XPropertySet> xSet( xOldElement, UNO_QUERY );
+ if (xSet.is())
+ xSet->removePropertyChangeListener(PROPERTY_NAME, this);
+
+ // give the old element a new (void) parent
+ Reference<XChild> xChild(xOldElement, UNO_QUERY);
+ if (xChild.is())
+ xChild->setParent(css::uno::Reference<css::uno::XInterface> ());
+
+ // remove the old one
+ m_aMap.erase(j);
+
+ // examine the new element
+ OUString sName;
+ DBG_ASSERT(aElementMetaData->xPropertySet.is(),
+ "OInterfaceContainer::implReplaceByIndex: what did approveNewElement do?");
+
+ aElementMetaData->xPropertySet->getPropertyValue(PROPERTY_NAME) >>= sName;
+ aElementMetaData->xPropertySet->addPropertyChangeListener(PROPERTY_NAME, this);
+
+ // insert the new one
+ m_aMap.insert(::std::pair<const OUString, css::uno::Reference<css::uno::XInterface>>(
+ sName, aElementMetaData->xInterface));
+ m_aItems[_nIndex] = aElementMetaData->xInterface;
+
+ aElementMetaData->xChild->setParent(static_cast<XContainer*>(this));
+
+ if ( m_xEventAttacher.is() )
+ {
+ m_xEventAttacher->insertEntry( _nIndex );
+ m_xEventAttacher->attach(_nIndex, aElementMetaData->xInterface,
+ Any(aElementMetaData->xPropertySet));
+ }
+
+ ContainerEvent aReplaceEvent;
+ aReplaceEvent.Source = static_cast< XContainer* >( this );
+ aReplaceEvent.Accessor <<= _nIndex;
+ aReplaceEvent.Element = aElementMetaData->xInterface->queryInterface(m_aElementType);
+ aReplaceEvent.ReplacedElement = xOldElement->queryInterface( m_aElementType );
+
+ impl_replacedElement( aReplaceEvent, _rClearBeforeNotify );
+}
+
+
+void OInterfaceContainer::implCheckIndex( const sal_Int32 _nIndex )
+{
+ if (_nIndex < 0 || o3tl::make_unsigned(_nIndex) >= m_aItems.size())
+ throw IndexOutOfBoundsException();
+}
+
+
+void SAL_CALL OInterfaceContainer::replaceByIndex(sal_Int32 _nIndex, const Any& Element)
+{
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+ // check the index
+ implCheckIndex( _nIndex );
+ // do the replace
+ implReplaceByIndex( _nIndex, Element, aGuard );
+}
+
+
+void OInterfaceContainer::implRemoveByIndex( const sal_Int32 _nIndex, ::osl::ClearableMutexGuard& _rClearBeforeNotify )
+{
+ OSL_PRECOND( ( _nIndex >= 0 ) && ( o3tl::make_unsigned(_nIndex) < m_aItems.size() ), "OInterfaceContainer::implRemoveByIndex: precondition not met (index)!" );
+
+ OInterfaceArray::iterator i = m_aItems.begin() + _nIndex;
+ css::uno::Reference<css::uno::XInterface> xElement(*i);
+
+ OInterfaceMap::iterator j = std::find_if(m_aMap.begin(), m_aMap.end(),
+ [&xElement](const OInterfaceMap::value_type& rEntry) { return rEntry.second == xElement; });
+
+ m_aItems.erase(i);
+ m_aMap.erase(j);
+
+ // remove event knittings
+ if ( m_xEventAttacher.is() )
+ {
+ css::uno::Reference<css::uno::XInterface> xNormalized( xElement, UNO_QUERY );
+ m_xEventAttacher->detach( _nIndex, xNormalized );
+ m_xEventAttacher->removeEntry( _nIndex );
+ }
+
+ Reference<XPropertySet> xSet(xElement, UNO_QUERY);
+ if (xSet.is())
+ xSet->removePropertyChangeListener(PROPERTY_NAME, this);
+
+ Reference<XChild> xChild(xElement, UNO_QUERY);
+ if (xChild.is())
+ xChild->setParent(css::uno::Reference<css::uno::XInterface> ());
+
+ // notify derived classes
+ implRemoved(xElement);
+
+ // notify listeners
+ ContainerEvent aEvt;
+ aEvt.Source = static_cast<XContainer*>(this);
+ aEvt.Element = xElement->queryInterface( m_aElementType );
+ aEvt.Accessor <<= _nIndex;
+
+ _rClearBeforeNotify.clear();
+ m_aContainerListeners.notifyEach( &XContainerListener::elementRemoved, aEvt );
+}
+
+
+void SAL_CALL OInterfaceContainer::removeByIndex(sal_Int32 _nIndex)
+{
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+ // check the index
+ implCheckIndex( _nIndex );
+ // do the removal
+ implRemoveByIndex( _nIndex, aGuard );
+}
+
+
+ElementDescription* OInterfaceContainer::createElementMetaData( )
+{
+ return new ElementDescription;
+}
+
+
+void SAL_CALL OInterfaceContainer::insertByName(const OUString& _rName, const Any& _rElement)
+{
+ Reference< XPropertySet > xElementProps;
+
+ std::unique_ptr< ElementDescription > aElementMetaData( createElementMetaData() );
+ DBG_ASSERT(aElementMetaData,
+ "OInterfaceContainer::insertByName: createElementMetaData returned nonsense!");
+
+ // ensure the correct name of the element
+ try
+ {
+ _rElement >>= xElementProps;
+ approveNewElement( xElementProps, aElementMetaData.get() );
+
+ xElementProps->setPropertyValue( PROPERTY_NAME, Any( _rName ) );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ throw; // allowed to leave
+ }
+ catch( const ElementExistException& )
+ {
+ throw; // allowed to leave
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("forms.misc", "OInterfaceContainer::insertByName" );
+ }
+ implInsert( m_aItems.size(), xElementProps, true, aElementMetaData.get(), true );
+}
+
+
+void SAL_CALL OInterfaceContainer::replaceByName(const OUString& Name, const Any& Element)
+{
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+ ::std::pair <OInterfaceMap::iterator,
+ OInterfaceMap::iterator> aPair = m_aMap.equal_range(Name);
+ if (aPair.first == aPair.second)
+ throw NoSuchElementException();
+
+ if (Element.getValueType().getTypeClass() != TypeClass_INTERFACE)
+ throw IllegalArgumentException();
+
+ Reference<XPropertySet> xSet;
+ Element >>= xSet;
+ if (xSet.is())
+ {
+ if (!hasProperty(PROPERTY_NAME, xSet))
+ throw IllegalArgumentException();
+
+ xSet->setPropertyValue(PROPERTY_NAME, Any(Name));
+ }
+
+ // determine the element pos
+ sal_Int32 nPos = ::std::find(m_aItems.begin(), m_aItems.end(), (*aPair.first).second) - m_aItems.begin();
+
+ implReplaceByIndex( nPos, Element, aGuard );
+}
+
+
+void SAL_CALL OInterfaceContainer::removeByName(const OUString& Name)
+{
+ ::osl::MutexGuard aGuard( m_rMutex );
+ ::std::pair <OInterfaceMap::iterator,
+ OInterfaceMap::iterator> aPair = m_aMap.equal_range(Name);
+ if (aPair.first == aPair.second)
+ throw NoSuchElementException();
+
+ sal_Int32 nPos = ::std::find(m_aItems.begin(), m_aItems.end(), (*aPair.first).second) - m_aItems.begin();
+ removeByIndex(nPos);
+}
+
+
+// XEventAttacherManager
+
+void SAL_CALL OInterfaceContainer::registerScriptEvent( sal_Int32 nIndex, const ScriptEventDescriptor& aScriptEvent )
+{
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+ if ( m_xEventAttacher.is() )
+ {
+ m_xEventAttacher->registerScriptEvent( nIndex, aScriptEvent );
+ aGuard.clear();
+ impl_addVbEvents_nolck_nothrow( nIndex ); // add fake vba events
+ }
+}
+
+
+void SAL_CALL OInterfaceContainer::registerScriptEvents( sal_Int32 nIndex, const Sequence< ScriptEventDescriptor >& aScriptEvents )
+{
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+ if ( m_xEventAttacher.is() )
+ {
+ m_xEventAttacher->registerScriptEvents( nIndex, aScriptEvents );
+ aGuard.clear();
+ impl_addVbEvents_nolck_nothrow( nIndex ); // add fake vba events
+ }
+}
+
+
+void SAL_CALL OInterfaceContainer::revokeScriptEvent( sal_Int32 nIndex, const OUString& aListenerType, const OUString& aEventMethod, const OUString& aRemoveListenerParam )
+{
+ if ( m_xEventAttacher.is() )
+ m_xEventAttacher->revokeScriptEvent( nIndex, aListenerType, aEventMethod, aRemoveListenerParam );
+}
+
+
+void SAL_CALL OInterfaceContainer::revokeScriptEvents( sal_Int32 nIndex )
+{
+ if ( m_xEventAttacher.is() )
+ m_xEventAttacher->revokeScriptEvents( nIndex );
+}
+
+
+void SAL_CALL OInterfaceContainer::insertEntry( sal_Int32 nIndex )
+{
+ if ( m_xEventAttacher.is() )
+ m_xEventAttacher->insertEntry( nIndex );
+}
+
+
+void SAL_CALL OInterfaceContainer::removeEntry( sal_Int32 nIndex )
+{
+ if ( m_xEventAttacher.is() )
+ m_xEventAttacher->removeEntry( nIndex );
+}
+
+
+Sequence< ScriptEventDescriptor > SAL_CALL OInterfaceContainer::getScriptEvents( sal_Int32 nIndex )
+{
+ Sequence< ScriptEventDescriptor > aReturn;
+ if ( m_xEventAttacher.is() )
+ {
+ aReturn = m_xEventAttacher->getScriptEvents( nIndex );
+ if ( lcl_hasVbaEvents( aReturn ) )
+ {
+ aReturn = lcl_stripVbaEvents( aReturn );
+ }
+ }
+ return aReturn;
+}
+
+
+void SAL_CALL OInterfaceContainer::attach( sal_Int32 nIndex, const Reference< XInterface >& xObject, const Any& aHelper )
+{
+ if ( m_xEventAttacher.is() )
+ m_xEventAttacher->attach( nIndex, xObject, aHelper );
+}
+
+
+void SAL_CALL OInterfaceContainer::detach( sal_Int32 nIndex, const Reference< XInterface >& xObject )
+{
+ if ( m_xEventAttacher.is() )
+ m_xEventAttacher->detach( nIndex, xObject );
+}
+
+
+void SAL_CALL OInterfaceContainer::addScriptListener( const Reference< XScriptListener >& xListener )
+{
+ if ( m_xEventAttacher.is() )
+ m_xEventAttacher->addScriptListener( xListener );
+}
+
+
+void SAL_CALL OInterfaceContainer::removeScriptListener( const Reference< XScriptListener >& xListener )
+{
+ if ( m_xEventAttacher.is() )
+ m_xEventAttacher->removeScriptListener( xListener );
+}
+
+
+//= OFormComponents
+
+
+Any SAL_CALL OFormComponents::queryAggregation(const Type& _rType)
+{
+ Any aReturn = OFormComponents_BASE::queryInterface(_rType);
+ if (!aReturn.hasValue())
+ {
+ aReturn = OInterfaceContainer::queryInterface(_rType);
+
+ if (!aReturn.hasValue())
+ aReturn = ::cppu::OComponentHelper::queryAggregation(_rType);
+ }
+
+ return aReturn;
+}
+
+
+Sequence<Type> SAL_CALL OFormComponents::getTypes()
+{
+ return ::comphelper::concatSequences(OInterfaceContainer::getTypes(), ::cppu::OComponentHelper::getTypes(), OFormComponents_BASE::getTypes());
+}
+
+
+OFormComponents::OFormComponents(const Reference<XComponentContext>& _rxFactory)
+ : ::cppu::OComponentHelper( m_aMutex )
+ ,OInterfaceContainer( _rxFactory, m_aMutex, cppu::UnoType<XFormComponent>::get() )
+ ,OFormComponents_BASE()
+{
+}
+
+
+OFormComponents::OFormComponents( const OFormComponents& _cloneSource )
+ : ::cppu::OComponentHelper( m_aMutex )
+ ,OInterfaceContainer( m_aMutex, _cloneSource )
+ ,OFormComponents_BASE()
+{
+}
+
+
+OFormComponents::~OFormComponents()
+{
+ if (! ::cppu::OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+// OComponentHelper
+
+void OFormComponents::disposing()
+{
+ OInterfaceContainer::disposing();
+ ::cppu::OComponentHelper::disposing();
+ m_xParent = nullptr;
+}
+
+//XChild
+
+void OFormComponents::setParent(const css::uno::Reference<css::uno::XInterface>& Parent)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_xParent = Parent;
+}
+
+
+css::uno::Reference<css::uno::XInterface> OFormComponents::getParent()
+{
+ return m_xParent;
+}
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/misc/componenttools.cxx b/forms/source/misc/componenttools.cxx
new file mode 100644
index 0000000000..41a0621881
--- /dev/null
+++ b/forms/source/misc/componenttools.cxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <componenttools.hxx>
+
+#include <com/sun/star/container/XChild.hpp>
+#include <comphelper/sequence.hxx>
+
+#include <algorithm>
+#include <iterator>
+
+
+namespace frm
+{
+
+
+ using ::com::sun::star::frame::XModel;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::container::XChild;
+
+ TypeBag::TypeBag( const TypeSequence& _rTypes1 )
+ {
+ addTypes( _rTypes1 );
+ }
+
+
+ TypeBag::TypeBag( const TypeSequence& _rTypes1, const TypeSequence& _rTypes2 )
+ {
+ addTypes( _rTypes1 );
+ addTypes( _rTypes2 );
+ }
+
+
+ TypeBag::TypeBag( const TypeSequence& _rTypes1, const TypeSequence& _rTypes2, const TypeSequence& _rTypes3 )
+ {
+ addTypes( _rTypes1 );
+ addTypes( _rTypes2 );
+ addTypes( _rTypes3 );
+ }
+
+
+ void TypeBag::addTypes( const TypeSequence& _rTypes )
+ {
+ ::std::copy(
+ _rTypes.begin(),
+ _rTypes.end(),
+ ::std::insert_iterator< TypeSet >( m_aTypes, m_aTypes.begin() )
+ );
+ }
+
+
+ void TypeBag::addType( const css::uno::Type& i_rType )
+ {
+ m_aTypes.insert( i_rType );
+ }
+
+
+ void TypeBag::removeType( const css::uno::Type& i_rType )
+ {
+ m_aTypes.erase( i_rType );
+ }
+
+
+ TypeBag::TypeSequence TypeBag::getTypes() const
+ {
+ return comphelper::containerToSequence(m_aTypes);
+ }
+
+
+ Reference< XModel > getXModel( const Reference< XInterface >& _rxComponent )
+ {
+ Reference< XInterface > xParent = _rxComponent;
+ Reference< XModel > xModel( xParent, UNO_QUERY );
+ while ( xParent.is() && !xModel.is() )
+ {
+ Reference< XChild > xChild( xParent, UNO_QUERY );
+ xParent.set( xChild.is() ? xChild->getParent() : Reference< XInterface >(), UNO_QUERY );
+ xModel.set( xParent, UNO_QUERY );
+ }
+ return xModel;
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/misc/limitedformats.cxx b/forms/source/misc/limitedformats.cxx
new file mode 100644
index 0000000000..b7a0d5abcb
--- /dev/null
+++ b/forms/source/misc/limitedformats.cxx
@@ -0,0 +1,379 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <limitedformats.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/types.hxx>
+#include <comphelper/extract.hxx>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/util/NumberFormatsSupplier.hpp>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::beans;
+
+ sal_Int32 OLimitedFormats::s_nInstanceCount(0);
+ ::osl::Mutex OLimitedFormats::s_aMutex;
+ Reference< XNumberFormatsSupplier > OLimitedFormats::s_xStandardFormats;
+
+
+ //=
+
+ namespace {
+
+ enum LocaleType
+ {
+ ltEnglishUS,
+ ltGerman,
+ ltSystem
+ };
+
+ }
+
+ static const Locale& getLocale(LocaleType _eType)
+ {
+ static const Locale s_aEnglishUS( "en", "us", OUString() );
+ static const Locale s_aGerman( "de", "DE", OUString() );
+ static const Locale s_aSystem( "", "", "" );
+
+ switch (_eType)
+ {
+ case ltEnglishUS:
+ return s_aEnglishUS;
+
+ case ltGerman:
+ return s_aGerman;
+
+ case ltSystem:
+ return s_aSystem;
+ }
+
+ OSL_FAIL("getLocale: invalid enum value!");
+ return s_aSystem;
+ }
+
+ namespace {
+
+ struct FormatEntry
+ {
+ const char* pDescription;
+ sal_Int32 nKey;
+ LocaleType eLocale;
+ };
+
+ }
+
+ static FormatEntry* lcl_getFormatTable(sal_Int16 nTableId)
+ {
+ switch (nTableId)
+ {
+ case FormComponentType::TIMEFIELD:
+ {
+ static FormatEntry s_aFormats[] = {
+ { "HH:MM", -1, ltEnglishUS },
+ { "HH:MM:SS", -1, ltEnglishUS },
+ { "HH:MM AM/PM", -1, ltEnglishUS },
+ { "HH:MM:SS AM/PM", -1, ltEnglishUS },
+ { nullptr, -1, ltSystem }
+ };
+ return s_aFormats;
+ }
+ case FormComponentType::DATEFIELD:
+ {
+ static FormatEntry s_aFormats[] = {
+ { "T-M-JJ", -1, ltGerman },
+ { "TT-MM-JJ", -1, ltGerman },
+ { "TT-MM-JJJJ", -1, ltGerman },
+ { "NNNNT. MMMM JJJJ", -1, ltGerman },
+
+ { "DD/MM/YY", -1, ltEnglishUS },
+ { "MM/DD/YY", -1, ltEnglishUS },
+ { "YY/MM/DD", -1, ltEnglishUS },
+ { "DD/MM/YYYY", -1, ltEnglishUS },
+ { "MM/DD/YYYY", -1, ltEnglishUS },
+ { "YYYY/MM/DD", -1, ltEnglishUS },
+
+ { "JJ-MM-TT", -1, ltGerman },
+ { "JJJJ-MM-TT", -1, ltGerman },
+
+ { nullptr, -1, ltSystem }
+ };
+ return s_aFormats;
+ }
+ }
+
+ OSL_FAIL("lcl_getFormatTable: invalid id!");
+ return nullptr;
+ }
+
+ OLimitedFormats::OLimitedFormats(const Reference< XComponentContext >& _rxContext, const sal_Int16 _nClassId)
+ :m_nFormatEnumPropertyHandle(-1)
+ ,m_nTableId(_nClassId)
+ {
+ OSL_ENSURE(_rxContext.is(), "OLimitedFormats::OLimitedFormats: invalid service factory!");
+ acquireSupplier(_rxContext);
+ ensureTableInitialized(m_nTableId);
+ }
+
+
+ OLimitedFormats::~OLimitedFormats()
+ {
+ releaseSupplier();
+ }
+
+
+ void OLimitedFormats::ensureTableInitialized(const sal_Int16 _nTableId)
+ {
+ FormatEntry* pFormatTable = lcl_getFormatTable(_nTableId);
+ if (-1 != pFormatTable->nKey)
+ return;
+
+ ::osl::MutexGuard aGuard(s_aMutex);
+ if (-1 != pFormatTable->nKey)
+ return;
+
+ // initialize the keys
+ Reference<XNumberFormats> xStandardFormats;
+ if (s_xStandardFormats.is())
+ xStandardFormats = s_xStandardFormats->getNumberFormats();
+ OSL_ENSURE(xStandardFormats.is(), "OLimitedFormats::ensureTableInitialized: don't have a formats supplier!");
+
+ if (!xStandardFormats.is())
+ return;
+
+ // loop through the table
+ FormatEntry* pLoopFormats = pFormatTable;
+ while (pLoopFormats->pDescription)
+ {
+ // get the key for the description
+ pLoopFormats->nKey = xStandardFormats->queryKey(
+ OUString::createFromAscii(pLoopFormats->pDescription),
+ getLocale(pLoopFormats->eLocale),
+ false
+ );
+
+ if (-1 == pLoopFormats->nKey)
+ {
+ pLoopFormats->nKey = xStandardFormats->addNew(
+ OUString::createFromAscii(pLoopFormats->pDescription),
+ getLocale(pLoopFormats->eLocale)
+ );
+#ifdef DBG_UTIL
+ try
+ {
+ xStandardFormats->getByKey(pLoopFormats->nKey);
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("OLimitedFormats::ensureTableInitialized: adding the key to the formats collection failed!");
+ }
+#endif
+ }
+
+ // next
+ ++pLoopFormats;
+ }
+ }
+
+
+ void OLimitedFormats::clearTable(const sal_Int16 _nTableId)
+ {
+ ::osl::MutexGuard aGuard(s_aMutex);
+ FormatEntry* pFormats = lcl_getFormatTable(_nTableId);
+ FormatEntry* pResetLoop = pFormats;
+ while (pResetLoop->pDescription)
+ {
+ pResetLoop->nKey = -1;
+ ++pResetLoop;
+ }
+ }
+
+
+ void OLimitedFormats::setAggregateSet(const Reference< XFastPropertySet >& _rxAggregate, sal_Int32 _nOriginalPropertyHandle)
+ {
+ // changes (NULL -> not NULL) and (not NULL -> NULL) are allowed
+ OSL_ENSURE(!m_xAggregate.is() || !_rxAggregate.is(), "OLimitedFormats::setAggregateSet: already have an aggregate!");
+ OSL_ENSURE(_rxAggregate.is() || m_xAggregate.is(), "OLimitedFormats::setAggregateSet: invalid new aggregate!");
+
+ m_xAggregate = _rxAggregate;
+ m_nFormatEnumPropertyHandle = _nOriginalPropertyHandle;
+#ifdef DBG_UTIL
+ if (m_xAggregate.is())
+ {
+ try
+ {
+ m_xAggregate->getFastPropertyValue(m_nFormatEnumPropertyHandle);
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("OLimitedFormats::setAggregateSet: invalid handle!");
+ }
+ }
+#endif
+ }
+
+
+ void OLimitedFormats::getFormatKeyPropertyValue( Any& _rValue ) const
+ {
+ _rValue.clear();
+
+ OSL_ENSURE(m_xAggregate.is() && (-1 != m_nFormatEnumPropertyHandle), "OLimitedFormats::getFormatKeyPropertyValue: not initialized!");
+ if (!m_xAggregate.is())
+ return;
+
+ // get the aggregate's enum property value
+ Any aEnumPropertyValue = m_xAggregate->getFastPropertyValue(m_nFormatEnumPropertyHandle);
+ sal_Int32 nValue = -1;
+ ::cppu::enum2int(nValue, aEnumPropertyValue);
+
+ // get the translation table
+ const FormatEntry* pFormats = lcl_getFormatTable(m_nTableId);
+
+ // seek to the nValue'th entry
+ sal_Int32 nLookup = 0;
+ for ( ;
+ (nullptr != pFormats->pDescription) && (nLookup < nValue);
+ ++pFormats, ++nLookup
+ )
+ ;
+ OSL_ENSURE(nullptr != pFormats->pDescription, "OLimitedFormats::getFormatKeyPropertyValue: did not find the value!");
+ if (pFormats->pDescription)
+ _rValue <<= pFormats->nKey;
+
+ // TODO: should use a standard format for the control type we're working for
+ }
+
+
+ bool OLimitedFormats::convertFormatKeyPropertyValue(Any& _rConvertedValue, Any& _rOldValue, const Any& _rNewValue)
+ {
+ OSL_ENSURE(m_xAggregate.is() && (-1 != m_nFormatEnumPropertyHandle), "OLimitedFormats::convertFormatKeyPropertyValue: not initialized!");
+
+ if (!m_xAggregate)
+ return false;
+
+ // the new format key to set
+ sal_Int32 nNewFormat = 0;
+ if (!(_rNewValue >>= nNewFormat))
+ throw IllegalArgumentException();
+
+ // get the old (enum) value from the aggregate
+ Any aEnumPropertyValue = m_xAggregate->getFastPropertyValue(m_nFormatEnumPropertyHandle);
+ sal_Int32 nOldEnumValue = -1;
+ ::cppu::enum2int(nOldEnumValue, aEnumPropertyValue);
+
+ // get the translation table
+ const FormatEntry* pFormats = lcl_getFormatTable(m_nTableId);
+
+ _rOldValue.clear();
+ _rConvertedValue.clear();
+
+ // look for the entry with the given format key
+ sal_Int32 nTablePosition = 0;
+ for ( ;
+ (nullptr != pFormats->pDescription) && (nNewFormat != pFormats->nKey);
+ ++pFormats, ++nTablePosition
+ )
+ {
+ if (nTablePosition == nOldEnumValue)
+ _rOldValue <<= pFormats->nKey;
+ }
+
+ bool bFoundIt = (nullptr != pFormats->pDescription);
+ bool bModified = false;
+ if (bFoundIt)
+ {
+ _rConvertedValue <<= static_cast<sal_Int16>(nTablePosition);
+ bModified = nTablePosition != nOldEnumValue;
+ }
+
+ if (!_rOldValue.hasValue())
+ { // did not reach the end of the table (means we found nNewFormat)
+ // -> go to the end to ensure that _rOldValue is set
+ while (pFormats->pDescription)
+ {
+ if (nTablePosition == nOldEnumValue)
+ {
+ _rOldValue <<= pFormats->nKey;
+ break;
+ }
+
+ ++pFormats;
+ ++nTablePosition;
+ }
+ }
+
+ OSL_ENSURE(_rOldValue.hasValue(), "OLimitedFormats::convertFormatKeyPropertyValue: did not find the old enum value in the table!");
+
+ if (!bFoundIt)
+ { // somebody gave us a format which we can't translate
+ throw IllegalArgumentException("This control supports only a very limited number of formats.", nullptr, 2);
+ }
+
+ return bModified;
+ }
+
+
+ void OLimitedFormats::setFormatKeyPropertyValue( const Any& _rNewValue )
+ {
+ OSL_ENSURE(m_xAggregate.is() && (-1 != m_nFormatEnumPropertyHandle), "OLimitedFormats::setFormatKeyPropertyValue: not initialized!");
+
+ if (m_xAggregate.is())
+ { // this is to be called after convertFormatKeyPropertyValue, where
+ // we translated the format key into an enum value.
+ // So now we can simply forward this enum value to our aggregate
+ m_xAggregate->setFastPropertyValue(m_nFormatEnumPropertyHandle, _rNewValue);
+ }
+ }
+
+
+ void OLimitedFormats::acquireSupplier(const Reference< XComponentContext >& _rxContext)
+ {
+ ::osl::MutexGuard aGuard(s_aMutex);
+ if (1 == ++s_nInstanceCount)
+ { // create the standard formatter
+ s_xStandardFormats = NumberFormatsSupplier::createWithLocale(_rxContext, getLocale(ltEnglishUS));
+ }
+ }
+
+
+ void OLimitedFormats::releaseSupplier()
+ {
+ ::osl::MutexGuard aGuard(s_aMutex);
+ if (0 == --s_nInstanceCount)
+ {
+ ::comphelper::disposeComponent(s_xStandardFormats);
+ s_xStandardFormats = nullptr;
+
+ clearTable(FormComponentType::TIMEFIELD);
+ clearTable(FormComponentType::DATEFIELD);
+ }
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/misc/property.cxx b/forms/source/misc/property.cxx
new file mode 100644
index 0000000000..6200b34386
--- /dev/null
+++ b/forms/source/misc/property.cxx
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <frm_strings.hxx>
+#include <property.hxx>
+
+namespace frm
+{
+
+//= PropertyInfoService
+
+PropertyInfoService::PropertyMap PropertyInfoService::s_AllKnownProperties;
+
+sal_Int32 PropertyInfoService::getPropertyId(const OUString& _rName)
+{
+ initialize();
+
+ sal_Int32 nHandle = -1;
+ const auto foundProperty = s_AllKnownProperties.find(_rName);
+ if (foundProperty != s_AllKnownProperties.end())
+ nHandle = foundProperty->second;
+ return nHandle;
+}
+
+
+sal_Int32 ConcreteInfoService::getPreferredPropertyId(const OUString& _rName)
+{
+ return PropertyInfoService::getPropertyId(_rName);
+}
+
+
+void PropertyInfoService::initialize()
+{
+ if (!s_AllKnownProperties.empty())
+ return;
+
+ s_AllKnownProperties.insert({
+ { PROPERTY_NAME, PROPERTY_ID_NAME },
+ { PROPERTY_TAG, PROPERTY_ID_TAG },
+ { PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX },
+ { PROPERTY_CLASSID, PROPERTY_ID_CLASSID },
+ { PROPERTY_ALIGN, PROPERTY_ID_ALIGN },
+ { PROPERTY_FETCHSIZE, PROPERTY_ID_FETCHSIZE },
+ { PROPERTY_VALUE, PROPERTY_ID_VALUE },
+ { PROPERTY_VALUEMIN, PROPERTY_ID_VALUEMIN },
+ { PROPERTY_VALUEMAX, PROPERTY_ID_VALUEMAX },
+ { PROPERTY_VALUESTEP, PROPERTY_ID_VALUESTEP },
+ { PROPERTY_TEXT, PROPERTY_ID_TEXT },
+ { PROPERTY_LABEL, PROPERTY_ID_LABEL },
+ { PROPERTY_NAVIGATION, PROPERTY_ID_NAVIGATION },
+ { PROPERTY_CYCLE, PROPERTY_ID_CYCLE },
+ { PROPERTY_CONTROLSOURCE, PROPERTY_ID_CONTROLSOURCE },
+ { PROPERTY_ENABLED, PROPERTY_ID_ENABLED },
+ { PROPERTY_ENABLEVISIBLE, PROPERTY_ID_ENABLEVISIBLE },
+ { PROPERTY_SPIN, PROPERTY_ID_SPIN },
+ { PROPERTY_READONLY, PROPERTY_ID_READONLY },
+ { PROPERTY_FILTER, PROPERTY_ID_FILTER },
+ { PROPERTY_WIDTH, PROPERTY_ID_WIDTH },
+ { PROPERTY_SEARCHABLE, PROPERTY_ID_SEARCHABLE },
+ { PROPERTY_MULTILINE, PROPERTY_ID_MULTILINE },
+ { PROPERTY_TARGET_URL, PROPERTY_ID_TARGET_URL },
+ { PROPERTY_DEFAULTCONTROL, PROPERTY_ID_DEFAULTCONTROL },
+ { PROPERTY_MAXTEXTLEN, PROPERTY_ID_MAXTEXTLEN },
+ { PROPERTY_SIZE, PROPERTY_ID_SIZE },
+ { PROPERTY_DATE, PROPERTY_ID_DATE },
+ { PROPERTY_TIME, PROPERTY_ID_TIME },
+ { PROPERTY_STATE, PROPERTY_ID_STATE },
+ { PROPERTY_TRISTATE, PROPERTY_ID_TRISTATE },
+ { PROPERTY_HIDDEN_VALUE, PROPERTY_ID_HIDDEN_VALUE },
+ { PROPERTY_TARGET_FRAME, PROPERTY_ID_TARGET_FRAME },
+ { PROPERTY_BUTTONTYPE, PROPERTY_ID_BUTTONTYPE },
+ { PROPERTY_STRINGITEMLIST, PROPERTY_ID_STRINGITEMLIST },
+ { PROPERTY_TYPEDITEMLIST, PROPERTY_ID_TYPEDITEMLIST },
+ { PROPERTY_DEFAULT_TEXT, PROPERTY_ID_DEFAULT_TEXT },
+ { PROPERTY_DEFAULT_STATE, PROPERTY_ID_DEFAULT_STATE },
+ { PROPERTY_DEFAULT_DATE, PROPERTY_ID_DEFAULT_DATE },
+ { PROPERTY_DEFAULT_TIME, PROPERTY_ID_DEFAULT_TIME },
+ { PROPERTY_DEFAULT_VALUE, PROPERTY_ID_DEFAULT_VALUE },
+ { PROPERTY_FORMATKEY, PROPERTY_ID_FORMATKEY },
+ { PROPERTY_FORMATSSUPPLIER, PROPERTY_ID_FORMATSSUPPLIER },
+ { PROPERTY_SUBMIT_ACTION, PROPERTY_ID_SUBMIT_ACTION },
+ { PROPERTY_SUBMIT_TARGET, PROPERTY_ID_SUBMIT_TARGET },
+ { PROPERTY_SUBMIT_METHOD, PROPERTY_ID_SUBMIT_METHOD },
+ { PROPERTY_SUBMIT_ENCODING, PROPERTY_ID_SUBMIT_ENCODING },
+ { PROPERTY_IMAGE_URL, PROPERTY_ID_IMAGE_URL },
+ { PROPERTY_GRAPHIC, PROPERTY_ID_GRAPHIC },
+ { PROPERTY_EMPTY_IS_NULL, PROPERTY_ID_EMPTY_IS_NULL },
+ { PROPERTY_LISTSOURCETYPE, PROPERTY_ID_LISTSOURCETYPE },
+ { PROPERTY_LISTSOURCE, PROPERTY_ID_LISTSOURCE },
+ { PROPERTY_SELECT_SEQ, PROPERTY_ID_SELECT_SEQ },
+ { PROPERTY_VALUE_SEQ, PROPERTY_ID_VALUE_SEQ },
+ { PROPERTY_SELECT_VALUE, PROPERTY_ID_SELECT_VALUE },
+ { PROPERTY_SELECT_VALUE_SEQ, PROPERTY_ID_SELECT_VALUE_SEQ },
+ { PROPERTY_DEFAULT_SELECT_SEQ, PROPERTY_ID_DEFAULT_SELECT_SEQ },
+ { PROPERTY_MULTISELECTION, PROPERTY_ID_MULTISELECTION },
+ { PROPERTY_DECIMAL_ACCURACY, PROPERTY_ID_DECIMAL_ACCURACY },
+ { PROPERTY_EDITMASK, PROPERTY_ID_EDITMASK },
+ { PROPERTY_ISREADONLY, PROPERTY_ID_ISREADONLY },
+ { PROPERTY_FIELDTYPE, PROPERTY_ID_FIELDTYPE },
+ { PROPERTY_DECIMALS, PROPERTY_ID_DECIMALS },
+ { PROPERTY_REFVALUE, PROPERTY_ID_REFVALUE },
+ { PROPERTY_STRICTFORMAT, PROPERTY_ID_STRICTFORMAT },
+ { PROPERTY_DATASOURCE, PROPERTY_ID_DATASOURCE },
+ { PROPERTY_ALLOWADDITIONS, PROPERTY_ID_ALLOWADDITIONS },
+ { PROPERTY_ALLOWEDITS, PROPERTY_ID_ALLOWEDITS },
+ { PROPERTY_ALLOWDELETIONS, PROPERTY_ID_ALLOWDELETIONS },
+ { PROPERTY_MASTERFIELDS, PROPERTY_ID_MASTERFIELDS },
+ { PROPERTY_ISPASSTHROUGH, PROPERTY_ID_ISPASSTHROUGH },
+ { PROPERTY_QUERY, PROPERTY_ID_QUERY },
+ { PROPERTY_LITERALMASK, PROPERTY_ID_LITERALMASK },
+ { PROPERTY_SHOWTHOUSANDSEP, PROPERTY_ID_SHOWTHOUSANDSEP },
+ { PROPERTY_CURRENCYSYMBOL, PROPERTY_ID_CURRENCYSYMBOL },
+ { PROPERTY_DATEFORMAT, PROPERTY_ID_DATEFORMAT },
+ { PROPERTY_DATEMIN, PROPERTY_ID_DATEMIN },
+ { PROPERTY_DATEMAX, PROPERTY_ID_DATEMAX },
+ { PROPERTY_DATE_SHOW_CENTURY, PROPERTY_ID_DATE_SHOW_CENTURY },
+ { PROPERTY_TIMEFORMAT, PROPERTY_ID_TIMEFORMAT },
+ { PROPERTY_TIMEMIN, PROPERTY_ID_TIMEMIN },
+ { PROPERTY_TIMEMAX, PROPERTY_ID_TIMEMAX },
+ { PROPERTY_LINECOUNT, PROPERTY_ID_LINECOUNT },
+ { PROPERTY_BOUNDCOLUMN, PROPERTY_ID_BOUNDCOLUMN },
+ { PROPERTY_HASNAVIGATION, PROPERTY_ID_HASNAVIGATION },
+ { PROPERTY_FONT, PROPERTY_ID_FONT },
+ { PROPERTY_BACKGROUNDCOLOR, PROPERTY_ID_BACKGROUNDCOLOR },
+ { PROPERTY_FILLCOLOR, PROPERTY_ID_FILLCOLOR },
+ { PROPERTY_TEXTCOLOR, PROPERTY_ID_TEXTCOLOR },
+ { PROPERTY_LINECOLOR, PROPERTY_ID_LINECOLOR },
+ { PROPERTY_BORDER, PROPERTY_ID_BORDER },
+ { PROPERTY_DROPDOWN, PROPERTY_ID_DROPDOWN },
+ { PROPERTY_HSCROLL, PROPERTY_ID_HSCROLL },
+ { PROPERTY_VSCROLL, PROPERTY_ID_VSCROLL },
+ { PROPERTY_TABSTOP, PROPERTY_ID_TABSTOP },
+ { PROPERTY_AUTOCOMPLETE, PROPERTY_ID_AUTOCOMPLETE },
+ { PROPERTY_HARDLINEBREAKS, PROPERTY_ID_HARDLINEBREAKS },
+ { PROPERTY_PRINTABLE, PROPERTY_ID_PRINTABLE },
+ { PROPERTY_ECHO_CHAR, PROPERTY_ID_ECHO_CHAR },
+ { PROPERTY_ROWHEIGHT, PROPERTY_ID_ROWHEIGHT },
+ { PROPERTY_HELPTEXT, PROPERTY_ID_HELPTEXT },
+ { PROPERTY_FONT_NAME, PROPERTY_ID_FONT_NAME },
+ { PROPERTY_FONT_STYLENAME, PROPERTY_ID_FONT_STYLENAME },
+ { PROPERTY_FONT_FAMILY, PROPERTY_ID_FONT_FAMILY },
+ { PROPERTY_FONT_CHARSET, PROPERTY_ID_FONT_CHARSET },
+ { PROPERTY_FONT_HEIGHT, PROPERTY_ID_FONT_HEIGHT },
+ { PROPERTY_FONT_WEIGHT, PROPERTY_ID_FONT_WEIGHT },
+ { PROPERTY_FONT_SLANT, PROPERTY_ID_FONT_SLANT },
+ { PROPERTY_FONT_UNDERLINE, PROPERTY_ID_FONT_UNDERLINE },
+ { PROPERTY_FONT_WORDLINEMODE, PROPERTY_ID_FONT_WORDLINEMODE },
+ { PROPERTY_FONT_STRIKEOUT, PROPERTY_ID_FONT_STRIKEOUT },
+ { PROPERTY_TEXTLINECOLOR, PROPERTY_ID_TEXTLINECOLOR },
+ { PROPERTY_FONTEMPHASISMARK, PROPERTY_ID_FONTEMPHASISMARK },
+ { PROPERTY_FONTRELIEF, PROPERTY_ID_FONTRELIEF },
+ { PROPERTY_HELPURL, PROPERTY_ID_HELPURL },
+ { PROPERTY_RECORDMARKER, PROPERTY_ID_RECORDMARKER },
+ { PROPERTY_BOUNDFIELD, PROPERTY_ID_BOUNDFIELD },
+ { PROPERTY_INPUT_REQUIRED, PROPERTY_ID_INPUT_REQUIRED },
+ { PROPERTY_TREATASNUMERIC, PROPERTY_ID_TREATASNUMERIC },
+ { PROPERTY_EFFECTIVE_VALUE, PROPERTY_ID_EFFECTIVE_VALUE },
+ { PROPERTY_EFFECTIVE_DEFAULT, PROPERTY_ID_EFFECTIVE_DEFAULT },
+ { PROPERTY_EFFECTIVE_MIN, PROPERTY_ID_EFFECTIVE_MIN },
+ { PROPERTY_EFFECTIVE_MAX, PROPERTY_ID_EFFECTIVE_MAX },
+ { PROPERTY_HIDDEN, PROPERTY_ID_HIDDEN },
+ { PROPERTY_FILTERPROPOSAL, PROPERTY_ID_FILTERPROPOSAL },
+ { PROPERTY_FIELDSOURCE, PROPERTY_ID_FIELDSOURCE },
+ { PROPERTY_TABLENAME, PROPERTY_ID_TABLENAME },
+ { PROPERTY_CONTROLLABEL, PROPERTY_ID_CONTROLLABEL },
+ { PROPERTY_CURRSYM_POSITION, PROPERTY_ID_CURRSYM_POSITION },
+ { PROPERTY_CURSORCOLOR, PROPERTY_ID_CURSORCOLOR },
+ { PROPERTY_ALWAYSSHOWCURSOR, PROPERTY_ID_ALWAYSSHOWCURSOR },
+ { PROPERTY_DISPLAYSYNCHRON, PROPERTY_ID_DISPLAYSYNCHRON },
+ { PROPERTY_ISMODIFIED, PROPERTY_ID_ISMODIFIED },
+ { PROPERTY_ISNEW, PROPERTY_ID_ISNEW },
+ { PROPERTY_PRIVILEGES, PROPERTY_ID_PRIVILEGES },
+ { PROPERTY_DETAILFIELDS, PROPERTY_ID_DETAILFIELDS },
+ { PROPERTY_COMMAND, PROPERTY_ID_COMMAND },
+ { PROPERTY_COMMANDTYPE, PROPERTY_ID_COMMANDTYPE },
+ { PROPERTY_RESULTSET_CONCURRENCY, PROPERTY_ID_RESULTSET_CONCURRENCY },
+ { PROPERTY_INSERTONLY, PROPERTY_ID_INSERTONLY },
+ { PROPERTY_RESULTSET_TYPE, PROPERTY_ID_RESULTSET_TYPE },
+ { PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING },
+ { PROPERTY_APPLYFILTER, PROPERTY_ID_APPLYFILTER },
+ { PROPERTY_ISNULLABLE, PROPERTY_ID_ISNULLABLE },
+ { PROPERTY_ACTIVECOMMAND, PROPERTY_ID_ACTIVECOMMAND },
+ { PROPERTY_ISCURRENCY, PROPERTY_ID_ISCURRENCY },
+ { PROPERTY_URL, PROPERTY_ID_URL },
+ { PROPERTY_TITLE, PROPERTY_ID_TITLE },
+ { PROPERTY_ACTIVE_CONNECTION, PROPERTY_ID_ACTIVE_CONNECTION },
+ { PROPERTY_SCALE, PROPERTY_ID_SCALE },
+ { PROPERTY_SORT, PROPERTY_ID_SORT },
+ { PROPERTY_PERSISTENCE_MAXTEXTLENGTH, PROPERTY_ID_PERSISTENCE_MAXTEXTLENGTH },
+ { PROPERTY_SCROLL_VALUE, PROPERTY_ID_SCROLL_VALUE },
+ { PROPERTY_SPIN_VALUE, PROPERTY_ID_SPIN_VALUE },
+ { PROPERTY_DEFAULT_SCROLL_VALUE, PROPERTY_ID_DEFAULT_SCROLL_VALUE },
+ { PROPERTY_DEFAULT_SPIN_VALUE, PROPERTY_ID_DEFAULT_SPIN_VALUE },
+ { PROPERTY_WRITING_MODE , PROPERTY_ID_WRITING_MODE },
+ { PROPERTY_CONTEXT_WRITING_MODE , PROPERTY_ID_CONTEXT_WRITING_MODE },
+ { PROPERTY_GENERATEVBAEVENTS , PROPERTY_ID_GENERATEVBAEVENTS }
+ });
+}
+
+
+}
+//... namespace frm .......................................................
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/resource/frm_resource.cxx b/forms/source/resource/frm_resource.cxx
new file mode 100644
index 0000000000..ee29836643
--- /dev/null
+++ b/forms/source/resource/frm_resource.cxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <frm_resource.hxx>
+
+namespace frm::ResourceManager
+{
+ OUString loadString(TranslateId aResId)
+ {
+ return Translate::get(aResId, Translate::Create("frm"));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/attributedispatcher.cxx b/forms/source/richtext/attributedispatcher.cxx
new file mode 100644
index 0000000000..29dbbf6162
--- /dev/null
+++ b/forms/source/richtext/attributedispatcher.cxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "attributedispatcher.hxx"
+
+#include <editeng/editview.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::beans;
+
+ OAttributeDispatcher::OAttributeDispatcher( EditView& _rView, AttributeId _nAttributeId, const URL& _rURL,
+ IMultiAttributeDispatcher* _pMasterDispatcher )
+ :ORichTextFeatureDispatcher( _rView, _rURL )
+ ,m_pMasterDispatcher( _pMasterDispatcher )
+ ,m_nAttributeId( _nAttributeId )
+ {
+ OSL_ENSURE( m_pMasterDispatcher, "OAttributeDispatcher::OAttributeDispatcher: invalid master dispatcher!" );
+ }
+
+
+ OAttributeDispatcher::~OAttributeDispatcher( )
+ {
+ acquire();
+ dispose();
+ }
+
+
+ void OAttributeDispatcher::disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify )
+ {
+ m_pMasterDispatcher = nullptr;
+ ORichTextFeatureDispatcher::disposing( _rClearBeforeNotify );
+ }
+
+
+ void OAttributeDispatcher::fillFeatureEventFromAttributeState( FeatureStateEvent& _rEvent, const AttributeState& _rState ) const
+ {
+ if ( _rState.eSimpleState == eChecked )
+ _rEvent.State <<= true;
+ else if ( _rState.eSimpleState == eUnchecked )
+ _rEvent.State <<= false;
+ }
+
+
+ FeatureStateEvent OAttributeDispatcher::buildStatusEvent() const
+ {
+ FeatureStateEvent aEvent( ORichTextFeatureDispatcher::buildStatusEvent() );
+ aEvent.IsEnabled = getEditView() && !getEditView()->IsReadOnly();
+
+ AttributeState aState;
+ if ( m_pMasterDispatcher )
+ aState = m_pMasterDispatcher->getState( m_nAttributeId );
+
+ fillFeatureEventFromAttributeState( aEvent, aState );
+
+ return aEvent;
+ }
+
+
+ void SAL_CALL OAttributeDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArguments )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ checkDisposed();
+
+ OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "OAttributeDispatcher::dispatch: invalid URL!" );
+ SAL_WARN_IF( _rArguments.hasElements(), "forms.richtext",
+ "OAttributeDispatcher::dispatch: found arguments, but can't handle arguments at all"
+ " (URL: " << _rURL.Complete << ")");
+
+ if ( m_pMasterDispatcher )
+ m_pMasterDispatcher->executeAttribute( m_nAttributeId, nullptr );
+ }
+
+
+ void OAttributeDispatcher::onAttributeStateChanged( AttributeId _nAttributeId )
+ {
+ OSL_ENSURE( _nAttributeId == m_nAttributeId, "OAttributeDispatcher::onAttributeStateChanged: wrong attribute!" );
+
+ FeatureStateEvent aEvent( buildStatusEvent() );
+ ::comphelper::OInterfaceIteratorHelper3 aIter( getStatusListeners() );
+ while ( aIter.hasMoreElements() )
+ doNotify( aIter.next(), aEvent );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/attributedispatcher.hxx b/forms/source/richtext/attributedispatcher.hxx
new file mode 100644
index 0000000000..9c6769b601
--- /dev/null
+++ b/forms/source/richtext/attributedispatcher.hxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "featuredispatcher.hxx"
+#include "rtattributes.hxx"
+#include "textattributelistener.hxx"
+
+
+namespace frm
+{
+
+ class OAttributeDispatcher :public ORichTextFeatureDispatcher
+ ,public ITextAttributeListener
+ {
+ protected:
+ IMultiAttributeDispatcher* m_pMasterDispatcher;
+ AttributeId m_nAttributeId;
+
+ public:
+ /** ctor
+ @param _nAttributeId
+ the id of the attribute which this instance is responsible for
+ @param _rURL
+ the URL of the feature which this instance is responsible for
+ @param _pMasterDispatcher
+ the dispatcher which can execute the given attribute
+ @param _pConverter
+ an instance which is able to convert between SfxPoolItems and XDispatch-Parameters
+ If not <NULL/>, the parametrized version of IMultiAttributeDispatcher::executeAttribute
+ will be used.
+ */
+ OAttributeDispatcher(
+ EditView& _rView,
+ AttributeId _nAttributeId,
+ const css::util::URL& _rURL,
+ IMultiAttributeDispatcher* _pMasterDispatcher
+ );
+
+ protected:
+ virtual ~OAttributeDispatcher( ) override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override;
+
+ // ITextAttributeListener
+ virtual void onAttributeStateChanged( AttributeId _nAttributeId ) override;
+
+ // ORichTextFeatureDispatcher
+ virtual void disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify ) override;
+
+ // ORichTextFeatureDispatcher
+ virtual css::frame::FeatureStateEvent buildStatusEvent() const override;
+
+ // own overridables
+ virtual void fillFeatureEventFromAttributeState( css::frame::FeatureStateEvent& _rEvent, const AttributeState& _rState ) const;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/clipboarddispatcher.cxx b/forms/source/richtext/clipboarddispatcher.cxx
new file mode 100644
index 0000000000..70313edb33
--- /dev/null
+++ b/forms/source/richtext/clipboarddispatcher.cxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "clipboarddispatcher.hxx"
+#include <editeng/editview.hxx>
+
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <svtools/cliplistener.hxx>
+#include <vcl/transfer.hxx>
+#include <osl/diagnose.h>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::beans;
+
+
+ namespace
+ {
+ URL createClipboardURL( OClipboardDispatcher::ClipboardFunc _eFunc )
+ {
+ URL aURL;
+ switch ( _eFunc )
+ {
+ case OClipboardDispatcher::eCut:
+ aURL.Complete = ".uno:Cut";
+ break;
+ case OClipboardDispatcher::eCopy:
+ aURL.Complete = ".uno:Copy";
+ break;
+ case OClipboardDispatcher::ePaste:
+ aURL.Complete = ".uno:Paste";
+ break;
+ }
+ return aURL;
+ }
+ }
+
+ OClipboardDispatcher::OClipboardDispatcher( EditView& _rView, ClipboardFunc _eFunc )
+ :ORichTextFeatureDispatcher( _rView, createClipboardURL( _eFunc ) )
+ ,m_eFunc( _eFunc )
+ ,m_bLastKnownEnabled( true )
+ {
+ }
+
+
+ bool OClipboardDispatcher::implIsEnabled( ) const
+ {
+ bool bEnabled = false;
+ switch ( m_eFunc )
+ {
+ case eCut:
+ bEnabled = !getEditView()->IsReadOnly() && getEditView()->HasSelection();
+ break;
+
+ case eCopy:
+ bEnabled = getEditView()->HasSelection();
+ break;
+
+ case ePaste:
+ bEnabled = !getEditView()->IsReadOnly();
+ break;
+ }
+ return bEnabled;
+ }
+
+
+ FeatureStateEvent OClipboardDispatcher::buildStatusEvent() const
+ {
+ FeatureStateEvent aEvent( ORichTextFeatureDispatcher::buildStatusEvent() );
+ aEvent.IsEnabled = implIsEnabled();
+ return aEvent;
+ }
+
+
+ void OClipboardDispatcher::invalidateFeatureState_Broadcast()
+ {
+ bool bEnabled = implIsEnabled();
+ if ( m_bLastKnownEnabled == bEnabled )
+ // nothing changed -> no notification
+ return;
+ m_bLastKnownEnabled = bEnabled;
+
+ ORichTextFeatureDispatcher::invalidateFeatureState_Broadcast();
+ }
+
+
+ void SAL_CALL OClipboardDispatcher::dispatch( const URL& /*_rURL*/, const Sequence< PropertyValue >& /*Arguments*/ )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !getEditView() )
+ throw DisposedException();
+
+ switch ( m_eFunc )
+ {
+ case eCut:
+ getEditView()->Cut();
+ break;
+
+ case eCopy:
+ getEditView()->Copy();
+ break;
+
+ case ePaste:
+ getEditView()->Paste();
+ break;
+ }
+ }
+
+ OPasteClipboardDispatcher::OPasteClipboardDispatcher( EditView& _rView )
+ :OClipboardDispatcher( _rView, ePaste )
+ ,m_bPastePossible( false )
+ {
+ m_pClipListener = new TransferableClipboardListener( LINK( this, OPasteClipboardDispatcher, OnClipboardChanged ) );
+ m_pClipListener->AddListener( _rView.GetWindow() );
+
+ // initial state
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( _rView.GetWindow() ) );
+ m_bPastePossible = ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ||
+ aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) );
+ }
+
+
+ OPasteClipboardDispatcher::~OPasteClipboardDispatcher()
+ {
+ if ( !isDisposed() )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ IMPL_LINK( OPasteClipboardDispatcher, OnClipboardChanged, TransferableDataHelper*, _pDataHelper, void )
+ {
+ OSL_ENSURE( _pDataHelper, "OPasteClipboardDispatcher::OnClipboardChanged: ooops!" );
+ m_bPastePossible = _pDataHelper->HasFormat( SotClipboardFormatId::STRING )
+ || _pDataHelper->HasFormat( SotClipboardFormatId::RTF )
+ || _pDataHelper->HasFormat( SotClipboardFormatId::RICHTEXT );
+
+ invalidate();
+ }
+
+
+ void OPasteClipboardDispatcher::disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify )
+ {
+ OSL_ENSURE( getEditView() && getEditView()->GetWindow(), "OPasteClipboardDispatcher::disposing: EditView should not (yet) be disfunctional here!" );
+ if (m_pClipListener.is())
+ {
+ if (getEditView() && getEditView()->GetWindow())
+ m_pClipListener->RemoveListener( getEditView()->GetWindow() );
+
+ m_pClipListener.clear();
+ }
+
+ OClipboardDispatcher::disposing( _rClearBeforeNotify );
+ }
+
+
+ bool OPasteClipboardDispatcher::implIsEnabled( ) const
+ {
+ return m_bPastePossible && OClipboardDispatcher::implIsEnabled();
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/clipboarddispatcher.hxx b/forms/source/richtext/clipboarddispatcher.hxx
new file mode 100644
index 0000000000..47e561c676
--- /dev/null
+++ b/forms/source/richtext/clipboarddispatcher.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "featuredispatcher.hxx"
+#include <tools/link.hxx>
+#include <rtl/ref.hxx>
+
+class TransferableClipboardListener;
+class TransferableDataHelper;
+
+namespace frm
+{
+
+ class OClipboardDispatcher : public ORichTextFeatureDispatcher
+ {
+ public:
+ enum ClipboardFunc
+ {
+ eCut,
+ eCopy,
+ ePaste
+ };
+
+ private:
+ ClipboardFunc m_eFunc;
+ bool m_bLastKnownEnabled;
+
+ public:
+ OClipboardDispatcher( EditView& _rView, ClipboardFunc _eFunc );
+
+ protected:
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override;
+
+ // ORichTextFeatureDispatcher
+ virtual void invalidateFeatureState_Broadcast() override;
+ virtual css::frame::FeatureStateEvent
+ buildStatusEvent() const override;
+
+ protected:
+ /** determines whether our functionality is currently available
+ to be overridden for ePaste
+ */
+ virtual bool implIsEnabled( ) const;
+ };
+
+ class OPasteClipboardDispatcher : public OClipboardDispatcher
+ {
+ private:
+ rtl::Reference<TransferableClipboardListener> m_pClipListener;
+ bool m_bPastePossible;
+
+ public:
+ explicit OPasteClipboardDispatcher( EditView& _rView );
+
+ protected:
+ virtual ~OPasteClipboardDispatcher() override;
+
+ // OClipboardDispatcher
+ virtual bool implIsEnabled( ) const override;
+
+ // ORichTextFeatureDispatcher
+ virtual void disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify ) override;
+
+ private:
+ DECL_LINK( OnClipboardChanged, TransferableDataHelper*, void );
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/featuredispatcher.cxx b/forms/source/richtext/featuredispatcher.cxx
new file mode 100644
index 0000000000..f583a8e7c6
--- /dev/null
+++ b/forms/source/richtext/featuredispatcher.cxx
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "featuredispatcher.hxx"
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <utility>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+
+ ORichTextFeatureDispatcher::ORichTextFeatureDispatcher( EditView& _rView, URL _aURL )
+ :m_aFeatureURL(std::move( _aURL ))
+ ,m_aStatusListeners( m_aMutex )
+ ,m_pEditView( &_rView )
+ ,m_bDisposed( false )
+ {
+ }
+
+
+ ORichTextFeatureDispatcher::~ORichTextFeatureDispatcher( )
+ {
+ if ( !m_bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ void ORichTextFeatureDispatcher::dispose()
+ {
+ EventObject aEvent( *this );
+ m_aStatusListeners.disposeAndClear( aEvent );
+
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ m_bDisposed = true;
+ disposing( aGuard );
+ }
+
+
+ void ORichTextFeatureDispatcher::disposing( ::osl::ClearableMutexGuard& /*_rClearBeforeNotify*/ )
+ {
+ m_pEditView = nullptr;
+ }
+
+
+ void SAL_CALL ORichTextFeatureDispatcher::addStatusListener( const Reference< XStatusListener >& _rxControl, const URL& _rURL )
+ {
+ OSL_ENSURE( !m_bDisposed, "ORichTextFeatureDispatcher::addStatusListener: already disposed!" );
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "ORichTextFeatureDispatcher::addStatusListener: invalid URL!" );
+ if ( _rURL.Complete == getFeatureURL().Complete )
+ if ( _rxControl.is() )
+ {
+ m_aStatusListeners.addInterface( _rxControl );
+ doNotify( _rxControl, buildStatusEvent() );
+ }
+ }
+
+
+ void SAL_CALL ORichTextFeatureDispatcher::removeStatusListener( const Reference< XStatusListener >& _rxControl, const URL& /*_rURL*/ )
+ {
+ m_aStatusListeners.removeInterface( _rxControl );
+ }
+
+
+ void ORichTextFeatureDispatcher::invalidate()
+ {
+ invalidateFeatureState_Broadcast();
+ }
+
+
+ FeatureStateEvent ORichTextFeatureDispatcher::buildStatusEvent() const
+ {
+ FeatureStateEvent aEvent;
+ aEvent.IsEnabled = false;
+ aEvent.Source = *const_cast< ORichTextFeatureDispatcher* >( this );
+ aEvent.FeatureURL = getFeatureURL();
+ aEvent.Requery = false;
+ return aEvent;
+ }
+
+
+ void ORichTextFeatureDispatcher::invalidateFeatureState_Broadcast()
+ {
+ FeatureStateEvent aEvent( buildStatusEvent() );
+ ::comphelper::OInterfaceIteratorHelper3 aIter( getStatusListeners() );
+ while ( aIter.hasMoreElements() )
+ doNotify( aIter.next(), aEvent );
+ }
+
+
+ void ORichTextFeatureDispatcher::doNotify( const Reference< XStatusListener >& _rxListener, const FeatureStateEvent& _rEvent )
+ {
+ OSL_PRECOND( _rxListener.is(), "ORichTextFeatureDispatcher::doNotify: invalid listener!" );
+ if ( _rxListener.is() )
+ {
+ try
+ {
+ _rxListener->statusChanged( _rEvent );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.richtext", "ORichTextFeatureDispatcher::doNotify" );
+ }
+ }
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/featuredispatcher.hxx b/forms/source/richtext/featuredispatcher.hxx
new file mode 100644
index 0000000000..5c863f10f8
--- /dev/null
+++ b/forms/source/richtext/featuredispatcher.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+class EditView;
+
+namespace frm
+{
+
+ typedef ::cppu::WeakImplHelper < css::frame::XDispatch
+ > ORichTextFeatureDispatcher_Base;
+
+ class ORichTextFeatureDispatcher :public ::cppu::BaseMutex
+ ,public ORichTextFeatureDispatcher_Base
+ {
+ private:
+ css::util::URL m_aFeatureURL;
+ ::comphelper::OInterfaceContainerHelper3<css::frame::XStatusListener> m_aStatusListeners;
+ EditView* m_pEditView;
+ bool m_bDisposed;
+
+ protected:
+ EditView* getEditView() { return m_pEditView; }
+ const EditView* getEditView() const { return m_pEditView; }
+
+ protected:
+ const css::util::URL& getFeatureURL() const { return m_aFeatureURL; }
+ ::comphelper::OInterfaceContainerHelper3<css::frame::XStatusListener>& getStatusListeners() { return m_aStatusListeners; }
+ bool isDisposed() const { return m_bDisposed; }
+ void checkDisposed() const { if ( isDisposed() ) throw css::lang::DisposedException(); }
+
+ protected:
+ ORichTextFeatureDispatcher( EditView& _rView, css::util::URL _aURL );
+ virtual ~ORichTextFeatureDispatcher( ) override;
+
+ public:
+ /// clean up resources associated with this instance
+ void dispose();
+
+ // invalidate the feature, re-retrieve it's state, and broadcast changes, if necessary
+ void invalidate();
+
+ protected:
+ // overridables
+ virtual void disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify );
+ virtual void invalidateFeatureState_Broadcast();
+
+ // to be overridden, and filled with the info special do your derived class
+ virtual css::frame::FeatureStateEvent
+ buildStatusEvent() const;
+
+ static void doNotify(
+ const css::uno::Reference< css::frame::XStatusListener >& _rxListener,
+ const css::frame::FeatureStateEvent& _rEvent
+ );
+
+ // XDispatch
+ virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& _rxControl, const css::util::URL& _rURL ) override;
+ virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& _rxControl, const css::util::URL& _rURL ) override;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/parametrizedattributedispatcher.cxx b/forms/source/richtext/parametrizedattributedispatcher.cxx
new file mode 100644
index 0000000000..d6666e2b02
--- /dev/null
+++ b/forms/source/richtext/parametrizedattributedispatcher.cxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "parametrizedattributedispatcher.hxx"
+#include <editeng/editids.hrc>
+#include <editeng/editview.hxx>
+#include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
+#include <osl/diagnose.h>
+
+#include <sfx2/sfxuno.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::beans;
+
+ OParametrizedAttributeDispatcher::OParametrizedAttributeDispatcher( EditView& _rView, AttributeId _nAttributeId, const URL& _rURL, IMultiAttributeDispatcher* _pMasterDispatcher )
+ :OAttributeDispatcher( _rView, _nAttributeId, _rURL, _pMasterDispatcher )
+ {
+ }
+
+
+ OParametrizedAttributeDispatcher::~OParametrizedAttributeDispatcher()
+ {
+ acquire();
+ dispose();
+ }
+
+
+ namespace
+ {
+ SfxSlotId lcl_normalizeLatinScriptSlotId( SfxSlotId _nSlotId )
+ {
+ switch ( _nSlotId )
+ {
+ case SID_ATTR_CHAR_LATIN_FONT: return SID_ATTR_CHAR_FONT;
+ case SID_ATTR_CHAR_LATIN_LANGUAGE: return SID_ATTR_CHAR_LANGUAGE;
+ case SID_ATTR_CHAR_LATIN_POSTURE: return SID_ATTR_CHAR_POSTURE;
+ case SID_ATTR_CHAR_LATIN_WEIGHT: return SID_ATTR_CHAR_WEIGHT;
+ case SID_ATTR_CHAR_LATIN_FONTHEIGHT:return SID_ATTR_CHAR_FONTHEIGHT;
+ }
+ return _nSlotId;
+ }
+ }
+
+
+ void OParametrizedAttributeDispatcher::fillFeatureEventFromAttributeState( FeatureStateEvent& _rEvent, const AttributeState& _rState ) const
+ {
+ OSL_ENSURE( getEditView(), "OParametrizedAttributeDispatcher::notifyState: already disposed!" );
+ if ( !getEditView() )
+ return;
+
+ SfxItemSet aEmptySet(getEditView()->GetEmptyItemSet());
+ Sequence< PropertyValue > aUnoStateDescription;
+ if ( _rState.getItem() )
+ {
+ aEmptySet.Put( *_rState.getItem() );
+ SfxSlotId nSlotId = aEmptySet.GetPool()->GetSlotId( _rState.getItem()->Which() );
+ TransformItems( nSlotId, aEmptySet, aUnoStateDescription );
+ _rEvent.State <<= aUnoStateDescription;
+ }
+ else
+ OAttributeDispatcher::fillFeatureEventFromAttributeState( _rEvent, _rState );
+ }
+
+
+ const SfxPoolItem* OParametrizedAttributeDispatcher::convertDispatchArgsToItem( const Sequence< PropertyValue >& _rArguments )
+ {
+ // get the real slot id. This may differ from our attribute id: for instance, both
+ // SID_ATTR_CHAR_HEIGHT and SID_ATTR_CHAR_LATIN_HEIGHT are mapped to the same which id
+ SfxSlotId nSlotId = lcl_normalizeLatinScriptSlotId( static_cast<SfxSlotId>(m_nAttributeId) );
+
+ SfxAllItemSet aParameterSet( getEditView()->GetEmptyItemSet() );
+ TransformParameters( nSlotId, _rArguments, aParameterSet );
+
+ const SfxPoolItem* pArgument = nullptr;
+ if ( aParameterSet.Count() )
+ {
+ OSL_ENSURE( aParameterSet.Count() == 1, "OParametrizedAttributeDispatcher::convertDispatchArgsToItem: Arguments which form more than 1 item? How this?" );
+ WhichId nAttributeWhich = aParameterSet.GetPool()->GetWhich( nSlotId );
+ pArgument = aParameterSet.GetItem( nAttributeWhich );
+ OSL_ENSURE( pArgument, "OParametrizedAttributeDispatcher::convertDispatchArgsToItem: suspicious: there were arguments, but they're not for my slot!" );
+ }
+
+ return pArgument;
+ }
+
+
+ void SAL_CALL OParametrizedAttributeDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArguments )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "OParametrizedAttributeDispatcher::dispatch: invalid URL!" );
+ if ( m_pMasterDispatcher )
+ {
+ const SfxPoolItem* pConvertedArgument = convertDispatchArgsToItem( _rArguments );
+ m_pMasterDispatcher->executeAttribute( m_nAttributeId, pConvertedArgument );
+ }
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/parametrizedattributedispatcher.hxx b/forms/source/richtext/parametrizedattributedispatcher.hxx
new file mode 100644
index 0000000000..6984473161
--- /dev/null
+++ b/forms/source/richtext/parametrizedattributedispatcher.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "attributedispatcher.hxx"
+
+class SfxPoolItem;
+
+namespace frm
+{
+
+ class OParametrizedAttributeDispatcher :public OAttributeDispatcher
+ {
+ public:
+ OParametrizedAttributeDispatcher(
+ EditView& _rView,
+ AttributeId _nAttributeId,
+ const css::util::URL& _rURL,
+ IMultiAttributeDispatcher* _pMasterDispatcher
+ );
+
+ protected:
+ virtual ~OParametrizedAttributeDispatcher() override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override;
+
+ // OAttributeDispatcher
+ virtual void fillFeatureEventFromAttributeState( css::frame::FeatureStateEvent& _rEvent, const AttributeState& _rState ) const override;
+
+ protected:
+ // own overridables
+ /** convert the arguments as got in a XDispatch::dispatch call into an SfxPoolItem, which can
+ be used with a IMultiAttributeDispatcher::executeAttribute
+ */
+ virtual const SfxPoolItem* convertDispatchArgsToItem(
+ const css::uno::Sequence< css::beans::PropertyValue >& _rArguments );
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextcontrol.cxx b/forms/source/richtext/richtextcontrol.cxx
new file mode 100644
index 0000000000..b481b6bfc9
--- /dev/null
+++ b/forms/source/richtext/richtextcontrol.cxx
@@ -0,0 +1,652 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "richtextcontrol.hxx"
+#include <frm_strings.hxx>
+#include <services.hxx>
+
+#include "richtextmodel.hxx"
+#include "richtextvclcontrol.hxx"
+#include "clipboarddispatcher.hxx"
+#include "parametrizedattributedispatcher.hxx"
+#include "specialdispatchers.hxx"
+
+#include <com/sun/star/awt/PosSize.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+
+#include <svx/svxids.hrc>
+#include <editeng/editview.hxx>
+#include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/msg.hxx>
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::frame;
+
+ ORichTextControl::ORichTextControl()
+ {
+ }
+
+
+ ORichTextControl::~ORichTextControl()
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORichTextControl, UnoEditControl, ORichTextControl_Base )
+
+
+ Any SAL_CALL ORichTextControl::queryAggregation( const Type& _rType )
+ {
+ Any aReturn = UnoEditControl::queryAggregation( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = ORichTextControl_Base::queryInterface( _rType );
+
+ return aReturn;
+ }
+
+
+ namespace
+ {
+
+ void implAdjustTriStateFlag( const Reference< XPropertySet >& _rxProps, const OUString& _rPropertyName,
+ WinBits& _rAllBits, WinBits _nPositiveFlag, WinBits nNegativeFlag )
+ {
+ bool bFlagValue = false;
+ if ( _rxProps->getPropertyValue( _rPropertyName ) >>= bFlagValue )
+ _rAllBits |= ( bFlagValue ? _nPositiveFlag : nNegativeFlag );
+ }
+
+
+ void implAdjustTwoStateFlag( const Any& _rValue, WinBits& _rAllBits, WinBits _nFlag, bool _bInvert )
+ {
+ bool bFlagValue = false;
+ if ( _rValue >>= bFlagValue )
+ {
+ if ( _bInvert )
+ bFlagValue = !bFlagValue;
+ if ( bFlagValue )
+ _rAllBits |= _nFlag;
+ else
+ _rAllBits &= ~_nFlag;
+ }
+ }
+
+
+ void implAdjustTwoStateFlag( const Reference< XPropertySet >& _rxProps, const OUString& _rPropertyName,
+ WinBits& _rAllBits, WinBits _nFlag, bool _bInvert = false )
+ {
+ implAdjustTwoStateFlag( _rxProps->getPropertyValue( _rPropertyName ), _rAllBits, _nFlag, _bInvert );
+ }
+
+
+ void adjustTwoStateWinBit( vcl::Window* _pWindow, const Any& _rValue, WinBits _nFlag, bool _bInvert = false )
+ {
+ WinBits nBits = _pWindow->GetStyle();
+ implAdjustTwoStateFlag( _rValue, nBits, _nFlag, _bInvert );
+ _pWindow->SetStyle( nBits );
+ }
+
+
+ WinBits getWinBits( const Reference< XControlModel >& _rxModel )
+ {
+ WinBits nBits = 0;
+ try
+ {
+ Reference< XPropertySet > xProps( _rxModel, UNO_QUERY );
+ if ( xProps.is() )
+ {
+ sal_Int16 nBorder = 0;
+ xProps->getPropertyValue( PROPERTY_BORDER ) >>= nBorder;
+ if ( nBorder )
+ nBits |= WB_BORDER;
+
+ implAdjustTriStateFlag( xProps, PROPERTY_TABSTOP, nBits, WB_TABSTOP, WB_NOTABSTOP );
+ implAdjustTwoStateFlag( xProps, PROPERTY_HSCROLL, nBits, WB_HSCROLL );
+ implAdjustTwoStateFlag( xProps, PROPERTY_VSCROLL, nBits, WB_VSCROLL );
+ implAdjustTwoStateFlag( xProps, PROPERTY_HARDLINEBREAKS, nBits, WB_WORDBREAK, true );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.richtext");
+ }
+ return nBits;
+ }
+ }
+
+
+ void SAL_CALL ORichTextControl::createPeer( const Reference< XToolkit >& _rToolkit, const Reference< XWindowPeer >& _rParentPeer )
+ {
+ bool bReallyActAsRichText = false;
+ try
+ {
+ Reference< XPropertySet > xModelProps( getModel(), UNO_QUERY_THROW );
+ xModelProps->getPropertyValue( PROPERTY_RICH_TEXT ) >>= bReallyActAsRichText;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.richtext");
+ }
+
+ if ( !bReallyActAsRichText )
+ {
+ UnoEditControl::createPeer( _rToolkit, _rParentPeer );
+ return;
+ }
+
+ SolarMutexGuard aGuard;
+
+ if (getPeer().is())
+ return;
+
+ mbCreatingPeer = true;
+
+ // determine the VCL window for the parent
+ vcl::Window* pParentWin = nullptr;
+ if ( _rParentPeer.is() )
+ {
+ VCLXWindow* pParentXWin = dynamic_cast<VCLXWindow*>( _rParentPeer.get() );
+ if ( pParentXWin )
+ pParentWin = pParentXWin->GetWindow();
+ DBG_ASSERT( pParentWin, "ORichTextControl::createPeer: could not obtain the VCL-level parent window!" );
+ }
+
+ // create the peer
+ Reference< XControlModel > xModel( getModel() );
+ rtl::Reference<ORichTextPeer> pPeer = ORichTextPeer::Create( xModel, pParentWin, getWinBits( xModel ) );
+ DBG_ASSERT( pPeer, "ORichTextControl::createPeer: invalid peer returned!" );
+ if ( pPeer )
+ {
+ // announce the peer to the base class
+ setPeer( pPeer );
+
+ // initialize ourself (and thus the peer) with the model properties
+ updateFromModel();
+
+ Reference< XView > xPeerView( getPeer(), UNO_QUERY );
+ if ( xPeerView.is() )
+ {
+ xPeerView->setZoom( maComponentInfos.nZoomX, maComponentInfos.nZoomY );
+ xPeerView->setGraphics( mxGraphics );
+ }
+
+ // a lot of initial settings from our component infos
+ setPosSize( maComponentInfos.nX, maComponentInfos.nY, maComponentInfos.nWidth, maComponentInfos.nHeight, PosSize::POSSIZE );
+
+ pPeer->setVisible ( maComponentInfos.bVisible && !mbDesignMode );
+ pPeer->setEnable ( maComponentInfos.bEnable );
+ pPeer->setDesignMode( mbDesignMode );
+
+ peerCreated();
+ }
+
+ mbCreatingPeer = false;
+ }
+
+ OUString SAL_CALL ORichTextControl::getImplementationName()
+ {
+ return "com.sun.star.comp.form.ORichTextControl";
+ }
+
+ Sequence< OUString > SAL_CALL ORichTextControl::getSupportedServiceNames()
+ {
+ return { "com.sun.star.awt.UnoControl",
+ "com.sun.star.awt.UnoControlEdit",
+ FRM_SUN_CONTROL_RICHTEXTCONTROL };
+ }
+
+ Reference< XDispatch > SAL_CALL ORichTextControl::queryDispatch( const css::util::URL& _rURL, const OUString& _rTargetFrameName, sal_Int32 _nSearchFlags )
+ {
+ Reference< XDispatch > aReturn;
+ Reference< XDispatchProvider > xTypedPeer( getPeer(), UNO_QUERY );
+ if ( xTypedPeer.is() )
+ {
+ aReturn = xTypedPeer->queryDispatch( _rURL, _rTargetFrameName, _nSearchFlags );
+ }
+ return aReturn;
+ }
+
+ Sequence< Reference< XDispatch > > SAL_CALL ORichTextControl::queryDispatches( const Sequence< DispatchDescriptor >& _rRequests )
+ {
+ Reference<XDispatchProvider> xTypedPeer(getPeer(), UNO_QUERY);
+ if (xTypedPeer.is())
+ return xTypedPeer->queryDispatches(_rRequests);
+ return Sequence<Reference<XDispatch>>();
+ }
+
+ bool ORichTextControl::requiresNewPeer( const OUString& _rPropertyName ) const
+ {
+ return UnoControl::requiresNewPeer( _rPropertyName ) || _rPropertyName == PROPERTY_RICH_TEXT;
+ }
+
+ // ORichTextPeer
+ rtl::Reference<ORichTextPeer> ORichTextPeer::Create( const Reference< XControlModel >& _rxModel, vcl::Window* _pParentWindow, WinBits _nStyle )
+ {
+ DBG_TESTSOLARMUTEX();
+
+ // the EditEngine of the model
+ RichTextEngine* pEngine = ORichTextModel::getEditEngine( _rxModel );
+ OSL_ENSURE( pEngine, "ORichTextPeer::Create: could not obtain the edit engine from the model!" );
+ if ( !pEngine )
+ return nullptr;
+
+ // the peer itself
+ rtl::Reference<ORichTextPeer> pPeer(new ORichTextPeer);
+
+ // the VCL control for the peer
+ VclPtrInstance<RichTextControl> pRichTextControl( pEngine, _pParentWindow, _nStyle, nullptr, pPeer.get() );
+
+ // some knittings
+ pRichTextControl->SetComponentInterface( pPeer );
+
+ // outta here
+ return pPeer;
+ }
+
+
+ ORichTextPeer::ORichTextPeer()
+ {
+ }
+
+
+ ORichTextPeer::~ORichTextPeer()
+ {
+ }
+
+
+ void ORichTextPeer::dispose( )
+ {
+ {
+ SolarMutexGuard aGuard;
+ VclPtr< RichTextControl > pRichTextControl = GetAs< RichTextControl >();
+
+ if ( pRichTextControl )
+ {
+ for (auto const& dispatcher : m_aDispatchers)
+ {
+ pRichTextControl->disableAttributeNotification(dispatcher.first);
+ dispatcher.second->dispose();
+ }
+ }
+
+ AttributeDispatchers().swap(m_aDispatchers);
+ }
+
+ VCLXWindow::dispose();
+ }
+
+
+ void SAL_CALL ORichTextPeer::draw( sal_Int32 _nX, sal_Int32 _nY )
+ {
+ SolarMutexGuard aGuard;
+
+ VclPtr< RichTextControl > pControl = GetAs< RichTextControl >();
+ if ( !pControl )
+ return;
+
+ OutputDevice* pTargetDevice = VCLUnoHelper::GetOutputDevice( getGraphics() );
+ OSL_ENSURE( pTargetDevice != nullptr, "ORichTextPeer::draw: no graphics -> no drawing!" );
+ if ( !pTargetDevice )
+ return;
+
+ const MapUnit eTargetUnit = pTargetDevice->GetMapMode().GetMapUnit();
+ ::Point aPos( _nX, _nY );
+ // the XView::draw API talks about pixels, always ...
+ if ( eTargetUnit != MapUnit::MapPixel )
+ aPos = pTargetDevice->PixelToLogic( aPos );
+
+ pControl->Draw( pTargetDevice, aPos, SystemTextColorFlags::NoControls );
+ }
+
+
+ void SAL_CALL ORichTextPeer::setProperty( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ SolarMutexGuard g;
+
+ if ( !GetWindow() )
+ {
+ VCLXWindow::setProperty( _rPropertyName, _rValue );
+ return;
+ }
+
+ if ( _rPropertyName == PROPERTY_BACKGROUNDCOLOR )
+ {
+ VclPtr< RichTextControl > pControl = GetAs< RichTextControl >();
+ if ( !_rValue.hasValue() )
+ {
+ pControl->SetBackgroundColor( );
+ }
+ else
+ {
+ Color nColor = COL_TRANSPARENT;
+ _rValue >>= nColor;
+ pControl->SetBackgroundColor( nColor );
+ }
+ }
+ else if ( _rPropertyName == PROPERTY_HSCROLL )
+ {
+ adjustTwoStateWinBit( GetWindow(), _rValue, WB_HSCROLL );
+ }
+ else if ( _rPropertyName == PROPERTY_VSCROLL )
+ {
+ adjustTwoStateWinBit( GetWindow(), _rValue, WB_VSCROLL );
+ }
+ else if ( _rPropertyName == PROPERTY_HARDLINEBREAKS )
+ {
+ adjustTwoStateWinBit( GetWindow(), _rValue, WB_WORDBREAK, true );
+ }
+ else if ( _rPropertyName == PROPERTY_READONLY )
+ {
+ VclPtr< RichTextControl > pControl = GetAs< RichTextControl >();
+ bool bReadOnly( pControl->IsReadOnly() );
+ OSL_VERIFY( _rValue >>= bReadOnly );
+ pControl->SetReadOnly( bReadOnly );
+
+ // update the dispatchers
+ for (auto const& dispatcher : m_aDispatchers)
+ {
+ dispatcher.second->invalidate();
+ }
+ }
+ else if ( _rPropertyName == PROPERTY_HIDEINACTIVESELECTION )
+ {
+ VclPtr< RichTextControl > pRichTextControl = GetAs< RichTextControl >();
+ bool bHide = pRichTextControl->GetHideInactiveSelection();
+ OSL_VERIFY( _rValue >>= bHide );
+ pRichTextControl->SetHideInactiveSelection( bHide );
+ }
+ else
+ VCLXWindow::setProperty( _rPropertyName, _rValue );
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( ORichTextPeer, VCLXWindow, ORichTextPeer_Base )
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORichTextPeer, VCLXWindow, ORichTextPeer_Base )
+
+
+ namespace
+ {
+ SfxSlotId lcl_translateConflictingSlot( SfxSlotId _nIDFromPool )
+ {
+ // HACK HACK HACK
+ // unfortunately, some of our applications have some conflicting slots,
+ // i.e. slots which have the same UNO name as an existing other (common)
+ // slot.
+ // For instance, both the slots SID_SET_SUPER_SCRIPT (from SVX) and FN_SET_SUPER_SCRIPT
+ // (from SW) have the UNO name "SuperScript".
+ // Now, if the controls lives in a text document, and asks the SfxSlotPool for
+ // the id belonging to "SuperScript", it gets the FN_SET_SUPER_SCRIPT - which
+ // is completely unknown to the EditEngine.
+ // So, we need to translate such conflicting ids.
+
+ // Note that the real solution would be to fix the applications to
+ // *not* define conflicting slots. Alternatively, if SFX would provide a slot pool
+ // which is *static* (i.e. independent on the active application), then we
+ // would also never encounter such a conflict.
+ SfxSlotId nReturn( _nIDFromPool );
+ switch ( _nIDFromPool )
+ {
+ case 20411: /* FM_SET_SUPER_SCRIPT, originating in SW */
+ nReturn = SID_SET_SUPER_SCRIPT;
+ break;
+ case 20412: /* FN_SET_SUB_SCRIPT, originating in SW */
+ nReturn = SID_SET_SUB_SCRIPT;
+ break;
+ }
+ return nReturn;
+ }
+ }
+
+
+ ORichTextPeer::SingleAttributeDispatcher ORichTextPeer::implCreateDispatcher( SfxSlotId _nSlotId, const css::util::URL& _rURL )
+ {
+ VclPtr< RichTextControl > pRichTextControl = GetAs< RichTextControl >();
+ OSL_PRECOND( pRichTextControl, "ORichTextPeer::implCreateDispatcher: invalid window!" );
+ if ( !pRichTextControl )
+ return SingleAttributeDispatcher( nullptr );
+
+ rtl::Reference<ORichTextFeatureDispatcher> pDispatcher;
+ rtl::Reference<OAttributeDispatcher> pAttributeDispatcher;
+ switch ( _nSlotId )
+ {
+ case SID_CUT:
+ pDispatcher = new OClipboardDispatcher( pRichTextControl->getView(), OClipboardDispatcher::eCut );
+ break;
+
+ case SID_COPY:
+ pDispatcher = new OClipboardDispatcher( pRichTextControl->getView(), OClipboardDispatcher::eCopy );
+ break;
+
+ case SID_PASTE:
+ pDispatcher = new OPasteClipboardDispatcher( pRichTextControl->getView() );
+ break;
+
+ case SID_SELECTALL:
+ pDispatcher = new OSelectAllDispatcher( pRichTextControl->getView(), _rURL );
+ break;
+
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ pAttributeDispatcher = new OParagraphDirectionDispatcher( pRichTextControl->getView(), _nSlotId, _rURL, pRichTextControl );
+ break;
+
+ case SID_TEXTDIRECTION_TOP_TO_BOTTOM:
+ case SID_TEXTDIRECTION_LEFT_TO_RIGHT:
+ pDispatcher = new OTextDirectionDispatcher( pRichTextControl->getView(), _rURL );
+ break;
+
+ case SID_ATTR_PARA_HANGPUNCTUATION:
+ case SID_ATTR_PARA_FORBIDDEN_RULES:
+ case SID_ATTR_PARA_SCRIPTSPACE:
+ pAttributeDispatcher = new OAsianFontLayoutDispatcher( pRichTextControl->getView(), _nSlotId, _rURL, pRichTextControl );
+ break;
+
+ default:
+ {
+ const SfxItemPool& rPool = *pRichTextControl->getView().GetEmptyItemSet().GetPool();
+ bool bSupportedSlot = rPool.IsInRange( rPool.GetWhich( _nSlotId ) );
+
+ if ( !bSupportedSlot )
+ bSupportedSlot = RichTextControl::isMappableSlot( _nSlotId );
+
+ if ( bSupportedSlot )
+ { // it's really a slot which is supported by the EditEngine
+
+ bool bNeedParametrizedDispatcher = true;
+ if ( ( _nSlotId == SID_ATTR_CHAR_POSTURE )
+ || ( _nSlotId == SID_ATTR_CHAR_CJK_POSTURE )
+ || ( _nSlotId == SID_ATTR_CHAR_CTL_POSTURE )
+ || ( _nSlotId == SID_ATTR_CHAR_LATIN_POSTURE )
+ || ( _nSlotId == SID_ATTR_CHAR_WEIGHT )
+ || ( _nSlotId == SID_ATTR_CHAR_CJK_WEIGHT )
+ || ( _nSlotId == SID_ATTR_CHAR_CTL_WEIGHT )
+ || ( _nSlotId == SID_ATTR_CHAR_LATIN_WEIGHT )
+ || ( _nSlotId == SID_ATTR_CHAR_LANGUAGE )
+ || ( _nSlotId == SID_ATTR_CHAR_CJK_LANGUAGE )
+ || ( _nSlotId == SID_ATTR_CHAR_CTL_LANGUAGE )
+ || ( _nSlotId == SID_ATTR_CHAR_LATIN_LANGUAGE )
+ || ( _nSlotId == SID_ATTR_CHAR_CONTOUR )
+ || ( _nSlotId == SID_ATTR_CHAR_SHADOWED )
+ || ( _nSlotId == SID_ATTR_CHAR_WORDLINEMODE )
+ || ( _nSlotId == SID_ATTR_CHAR_COLOR )
+ || ( _nSlotId == SID_ATTR_CHAR_RELIEF )
+ || ( _nSlotId == SID_ATTR_CHAR_KERNING )
+ || ( _nSlotId == SID_ATTR_CHAR_AUTOKERN )
+ || ( _nSlotId == SID_ATTR_CHAR_SCALEWIDTH )
+ )
+ {
+ bNeedParametrizedDispatcher = true;
+ }
+ else
+ {
+ SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool();
+ const SfxSlot* pSlot = rSlotPool.GetSlot( _nSlotId );
+ const SfxType* pType = pSlot ? pSlot->GetType() : nullptr;
+ if ( pType )
+ {
+ bNeedParametrizedDispatcher = ( pType->nAttribs > 0 );
+ }
+ }
+
+ if ( bNeedParametrizedDispatcher )
+ {
+ pAttributeDispatcher = new OParametrizedAttributeDispatcher( pRichTextControl->getView(), _nSlotId, _rURL, pRichTextControl );
+ }
+ else
+ {
+ pAttributeDispatcher = new OAttributeDispatcher( pRichTextControl->getView(), _nSlotId, _rURL, pRichTextControl );
+ }
+ }
+ else
+ {
+ SAL_WARN("forms.richtext", "ORichTextPeer::implCreateDispatcher: not creating dispatcher (unsupported slot) for "
+ << _rURL.Complete);
+ }
+ }
+ break;
+ }
+
+ SingleAttributeDispatcher xDispatcher( pDispatcher );
+ if ( pAttributeDispatcher )
+ {
+ xDispatcher = SingleAttributeDispatcher( pAttributeDispatcher );
+ pRichTextControl->enableAttributeNotification( _nSlotId, pAttributeDispatcher.get() );
+ }
+
+ return xDispatcher;
+ }
+
+
+ namespace
+ {
+ SfxSlotId lcl_getSlotFromUnoName( SfxSlotPool const & _rSlotPool, const OUString& _rUnoSlotName )
+ {
+ const SfxSlot* pSlot = _rSlotPool.GetUnoSlot( _rUnoSlotName );
+ if ( pSlot )
+ {
+ // okay, there's a slot with the given UNO name
+ return lcl_translateConflictingSlot( pSlot->GetSlotId() );
+ }
+
+ // some hard-coded slots, which do not have a UNO name at SFX level, but which
+ // we nevertheless need to transport via UNO mechanisms, so we need a name
+ if ( _rUnoSlotName == "AllowHangingPunctuation" )
+ return SID_ATTR_PARA_HANGPUNCTUATION;
+ if ( _rUnoSlotName == "ApplyForbiddenCharacterRules" )
+ return SID_ATTR_PARA_FORBIDDEN_RULES;
+ if ( _rUnoSlotName == "UseScriptSpacing" )
+ return SID_ATTR_PARA_SCRIPTSPACE;
+
+ OSL_ENSURE( pSlot, "lcl_getSlotFromUnoName: unknown UNO slot name!" );
+ return 0;
+ }
+ }
+
+
+ Reference< XDispatch > SAL_CALL ORichTextPeer::queryDispatch( const css::util::URL& _rURL, const OUString& /*_rTargetFrameName*/, sal_Int32 /*_nSearchFlags*/ )
+ {
+ Reference< XDispatch > xReturn;
+ if ( !GetWindow() )
+ {
+ OSL_FAIL( "ORichTextPeer::queryDispatch: already disposed?" );
+ return xReturn;
+ }
+
+ // is it a UNO slot?
+ static constexpr std::u16string_view sUnoProtocolPrefix( u".uno:" );
+ if ( _rURL.Complete.startsWith( sUnoProtocolPrefix ) )
+ {
+ OUString sUnoSlotName = _rURL.Complete.copy( sUnoProtocolPrefix.size() );
+ SfxSlotId nSlotId = lcl_getSlotFromUnoName( SfxSlotPool::GetSlotPool(), sUnoSlotName );
+ if ( nSlotId > 0 )
+ {
+ // do we already have a dispatcher for this?
+ AttributeDispatchers::const_iterator aDispatcherPos = m_aDispatchers.find( nSlotId );
+ if ( aDispatcherPos == m_aDispatchers.end() )
+ {
+ SingleAttributeDispatcher pDispatcher = implCreateDispatcher( nSlotId, _rURL );
+ if ( pDispatcher.is() )
+ {
+ aDispatcherPos = m_aDispatchers.emplace( nSlotId, pDispatcher ).first;
+ }
+ }
+
+ if ( aDispatcherPos != m_aDispatchers.end() )
+ xReturn = aDispatcherPos->second.get();
+ }
+ }
+
+ return xReturn;
+ }
+
+
+ Sequence< Reference< XDispatch > > SAL_CALL ORichTextPeer::queryDispatches( const Sequence< DispatchDescriptor >& _rRequests )
+ {
+ Sequence< Reference< XDispatch > > aReturn( _rRequests.getLength() );
+ Reference< XDispatch >* pReturn = aReturn.getArray();
+
+ const DispatchDescriptor* pRequest = _rRequests.getConstArray();
+ const DispatchDescriptor* pRequestEnd = pRequest + _rRequests.getLength();
+ for ( ; pRequest != pRequestEnd; ++pRequest, ++pReturn )
+ {
+ *pReturn = queryDispatch( pRequest->FeatureURL, pRequest->FrameName, pRequest->SearchFlags );
+ }
+ return aReturn;
+ }
+
+
+ void ORichTextPeer::onSelectionChanged()
+ {
+ AttributeDispatchers::iterator aDispatcherPos = m_aDispatchers.find( SID_COPY );
+ if ( aDispatcherPos != m_aDispatchers.end() )
+ aDispatcherPos->second->invalidate();
+
+ aDispatcherPos = m_aDispatchers.find( SID_CUT );
+ if ( aDispatcherPos != m_aDispatchers.end() )
+ aDispatcherPos->second->invalidate();
+ }
+
+
+} // namespace frm
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_form_ORichTextControl_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ORichTextControl());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextcontrol.hxx b/forms/source/richtext/richtextcontrol.hxx
new file mode 100644
index 0000000000..81e0ad2a0f
--- /dev/null
+++ b/forms/source/richtext/richtextcontrol.hxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <toolkit/controls/unocontrols.hxx>
+#include <toolkit/awt/vclxwindow.hxx>
+
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <rtl/ref.hxx>
+#include <vcl/wintypes.hxx>
+#include "rtattributes.hxx"
+#include "textattributelistener.hxx"
+
+#include <map>
+
+
+namespace frm
+{
+
+ class ORichTextFeatureDispatcher;
+
+ typedef ::cppu::ImplHelper1 < css::frame::XDispatchProvider
+ > ORichTextControl_Base;
+ class ORichTextControl :public UnoEditControl
+ ,public ORichTextControl_Base
+ {
+ public:
+ ORichTextControl();
+
+ protected:
+ virtual ~ORichTextControl() override;
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS( ORichTextControl, UnoEditControl )
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XControl
+ virtual void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& _rToolkit, const css::uno::Reference< css::awt::XWindowPeer >& _rParent ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+
+ // XDispatchProvider
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& _rURL, const OUString& _rTargetFrameName, sal_Int32 _rSearchFlags ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& Requests ) override;
+
+ // UnoControl
+ virtual bool requiresNewPeer( const OUString& _rPropertyName ) const override;
+ };
+
+ typedef ::cppu::ImplHelper1 < css::frame::XDispatchProvider
+ > ORichTextPeer_Base;
+ class ORichTextPeer final :public VCLXWindow
+ ,public ORichTextPeer_Base
+ ,public ITextSelectionListener
+ {
+ private:
+ typedef rtl::Reference<ORichTextFeatureDispatcher> SingleAttributeDispatcher;
+ typedef ::std::map< SfxSlotId, SingleAttributeDispatcher > AttributeDispatchers;
+ AttributeDispatchers m_aDispatchers;
+
+ public:
+ /** factory method
+ */
+ static rtl::Reference<ORichTextPeer> Create(
+ const css::uno::Reference< css::awt::XControlModel >& _rxModel,
+ vcl::Window* _pParentWindow,
+ WinBits _nStyle
+ );
+
+ // XInterface
+ DECLARE_XINTERFACE( )
+
+ private:
+ ORichTextPeer();
+ virtual ~ORichTextPeer() override;
+
+ // XView
+ void SAL_CALL draw( sal_Int32 nX, sal_Int32 nY ) override;
+
+ // XVclWindowPeer
+ virtual void SAL_CALL setProperty( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER( )
+
+ // XComponent
+ virtual void SAL_CALL dispose( ) override;
+
+ // XDispatchProvider
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& _rURL, const OUString& _rTargetFrameName, sal_Int32 _rSearchFlags ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& Requests ) override;
+
+ // ITextSelectionListener
+ virtual void onSelectionChanged() override;
+
+ private:
+ SingleAttributeDispatcher implCreateDispatcher( SfxSlotId _nSlotId, const css::util::URL& _rURL );
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextengine.cxx b/forms/source/richtext/richtextengine.cxx
new file mode 100644
index 0000000000..14f50a6fca
--- /dev/null
+++ b/forms/source/richtext/richtextengine.cxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "richtextengine.hxx"
+#include <svl/itempool.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/langitem.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/mapunit.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+#include <unotools/lingucfg.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <memory>
+
+namespace frm
+{
+ //= RichTextEngine
+
+
+ RichTextEngine* RichTextEngine::Create()
+ {
+ SolarMutexGuard g;
+
+ rtl::Reference<SfxItemPool> pPool = EditEngine::CreatePool();
+ pPool->FreezeIdRanges();
+
+ RichTextEngine* pReturn = new RichTextEngine( pPool.get() );
+ OutputDevice* pOutputDevice = pReturn->GetRefDevice();
+ const MapMode& aDeviceMapMode( pOutputDevice->GetMapMode() );
+
+ pReturn->SetStatusEventHdl( LINK( pReturn, RichTextEngine, EditEngineStatusChanged ) );
+
+ pPool->SetDefaultMetric(aDeviceMapMode.GetMapUnit());
+
+ // defaults
+ vcl::Font aFont = Application::GetSettings().GetStyleSettings().GetAppFont();
+ aFont.SetFamilyName( "Times New Roman" );
+ pPool->SetPoolDefaultItem( SvxFontItem( aFont.GetFamilyType(), aFont.GetFamilyName(), OUString(), aFont.GetPitch(), aFont.GetCharSet(), EE_CHAR_FONTINFO ) );
+
+ // 12 pt font size
+ MapMode aPointMapMode( MapUnit::MapPoint );
+ Size a12PointSize( OutputDevice::LogicToLogic( Size( 12, 0 ), aPointMapMode, aDeviceMapMode ) );
+ pPool->SetPoolDefaultItem( SvxFontHeightItem( a12PointSize.Width(), 100, EE_CHAR_FONTHEIGHT ) );
+
+ // font languages
+ SvtLinguOptions aLinguOpt;
+ pPool->SetPoolDefaultItem( SvxLanguageItem( aLinguOpt.nDefaultLanguage, EE_CHAR_LANGUAGE ) );
+ pPool->SetPoolDefaultItem( SvxLanguageItem( aLinguOpt.nDefaultLanguage_CJK, EE_CHAR_LANGUAGE_CJK ) );
+ pPool->SetPoolDefaultItem( SvxLanguageItem( aLinguOpt.nDefaultLanguage_CTL, EE_CHAR_LANGUAGE_CTL ) );
+
+ return pReturn;
+ }
+
+
+ RichTextEngine* RichTextEngine::Clone()
+ {
+ RichTextEngine* pClone( nullptr );
+ {
+ SolarMutexGuard aGuard;
+ std::unique_ptr<EditTextObject> pMyText(CreateTextObject());
+ OSL_ENSURE( pMyText, "RichTextEngine::Clone: CreateTextObject returned nonsense!" );
+
+ pClone = Create();
+
+ if ( pMyText )
+ pClone->SetText( *pMyText );
+ }
+
+ return pClone;
+ }
+
+
+ RichTextEngine::RichTextEngine( SfxItemPool* _pPool )
+ :EditEngine( _pPool )
+ {
+ }
+
+
+ RichTextEngine::~RichTextEngine( )
+ {
+ }
+
+
+ void RichTextEngine::registerEngineStatusListener( IEngineStatusListener* _pListener )
+ {
+ OSL_ENSURE( _pListener, "RichTextEngine::registerEngineStatusListener: invalid listener!" );
+ if ( _pListener )
+ m_aStatusListeners.push_back( _pListener );
+ }
+
+
+ void RichTextEngine::revokeEngineStatusListener( IEngineStatusListener const * _pListener )
+ {
+ ::std::vector< IEngineStatusListener* >::iterator aPos = ::std::find(
+ m_aStatusListeners.begin(),
+ m_aStatusListeners.end(),
+ _pListener
+ );
+ OSL_ENSURE( aPos != m_aStatusListeners.end(), "RichTextEngine::revokeEngineStatusListener: listener not registered!" );
+ if ( aPos != m_aStatusListeners.end() )
+ m_aStatusListeners.erase( aPos );
+ }
+
+
+ IMPL_LINK( RichTextEngine, EditEngineStatusChanged, EditStatus&, _rStatus, void )
+ {
+ for (auto const& statusListener : m_aStatusListeners)
+ statusListener->EditEngineStatusChanged( _rStatus );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextengine.hxx b/forms/source/richtext/richtextengine.hxx
new file mode 100644
index 0000000000..c9413e6bd0
--- /dev/null
+++ b/forms/source/richtext/richtextengine.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <editeng/editeng.hxx>
+#include <tools/link.hxx>
+
+#include <vector>
+
+class SfxItemPool;
+class EditStatus;
+
+namespace frm
+{
+
+ class IEngineStatusListener
+ {
+ public:
+ virtual void EditEngineStatusChanged( const EditStatus& _rStatus ) = 0;
+
+ protected:
+ ~IEngineStatusListener() {}
+ };
+
+ class RichTextEngine final : public EditEngine
+ {
+ private:
+ ::std::vector< IEngineStatusListener* > m_aStatusListeners;
+
+ public:
+ static RichTextEngine* Create();
+ RichTextEngine* Clone();
+
+ virtual ~RichTextEngine( ) override;
+
+ // for multiplexing the StatusChanged events of the edit engine
+ void registerEngineStatusListener( IEngineStatusListener* _pListener );
+ void revokeEngineStatusListener( IEngineStatusListener const * _pListener );
+
+ private:
+ /** constructs a new RichTextEngine. The instances takes the ownership of the given SfxItemPool
+ */
+ explicit RichTextEngine( SfxItemPool* _pPool );
+
+ RichTextEngine( const RichTextEngine& ) = delete;
+ RichTextEngine& operator=( const RichTextEngine& ) = delete;
+
+ DECL_LINK( EditEngineStatusChanged, EditStatus&, void );
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextimplcontrol.cxx b/forms/source/richtext/richtextimplcontrol.cxx
new file mode 100644
index 0000000000..790a3f88b2
--- /dev/null
+++ b/forms/source/richtext/richtextimplcontrol.cxx
@@ -0,0 +1,639 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "richtextimplcontrol.hxx"
+#include "textattributelistener.hxx"
+#include "richtextengine.hxx"
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/editview.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/scripttypeitem.hxx>
+
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <tools/mapunit.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandevent.hxx>
+
+#define EMPTY_PAPER_SIZE 0x7FFFFFFF
+
+
+namespace frm
+{
+
+ RichTextControlImpl::RichTextControlImpl( Control* _pAntiImpl, RichTextEngine* _pEngine, ITextAttributeListener* _pTextAttrListener, ITextSelectionListener* _pSelectionListener )
+ :m_pAntiImpl ( _pAntiImpl )
+ ,m_pViewport ( nullptr )
+ ,m_pHScroll ( nullptr )
+ ,m_pVScroll ( nullptr )
+ ,m_pEngine ( _pEngine )
+ ,m_pTextAttrListener ( _pTextAttrListener )
+ ,m_pSelectionListener ( _pSelectionListener )
+ ,m_bHasEverBeenShown ( false )
+ {
+ OSL_ENSURE( m_pAntiImpl, "RichTextControlImpl::RichTextControlImpl: invalid window!" );
+ OSL_ENSURE( m_pEngine, "RichTextControlImpl::RichTextControlImpl: invalid edit engine! This will *definitely* crash!" );
+
+ m_pViewport = VclPtr<RichTextViewPort>::Create( m_pAntiImpl );
+ m_pViewport->setAttributeInvalidationHandler( LINK( this, RichTextControlImpl, OnInvalidateAllAttributes ) );
+ m_pViewport->Show();
+
+ // ensure that both the window and the reference device have the same map unit
+ MapMode aRefDeviceMapMode( m_pEngine->GetRefDevice()->GetMapMode() );
+ m_pAntiImpl->SetMapMode( aRefDeviceMapMode );
+ m_pViewport->SetMapMode( aRefDeviceMapMode );
+
+ m_pView.reset(new EditView( m_pEngine, m_pViewport ));
+ m_pEngine->InsertView( m_pView.get() );
+ m_pViewport->setView( *m_pView );
+
+ m_pEngine->registerEngineStatusListener( this );
+
+ {
+ EVControlBits nViewControlWord = m_pView->GetControlWord();
+ nViewControlWord |= EVControlBits::AUTOSCROLL;
+ m_pView->SetControlWord( nViewControlWord );
+ }
+
+ // ensure that it's initially scrolled to the upper left
+ m_pView->SetVisArea( tools::Rectangle( Point( ), m_pViewport->GetOutDev()->GetOutputSize() ) );
+
+ ensureScrollbars();
+
+ m_pAntiImpl->SetBackground( Wallpaper( m_pAntiImpl->GetSettings().GetStyleSettings().GetFieldColor() ) );
+ }
+
+ RichTextControlImpl::~RichTextControlImpl( )
+ {
+ m_pEngine->RemoveView( m_pView.get() );
+ m_pEngine->revokeEngineStatusListener( this );
+ m_pView.reset();
+ m_pViewport.disposeAndClear();
+ m_pHScroll.disposeAndClear();
+ m_pVScroll.disposeAndClear();
+ }
+
+ void RichTextControlImpl::implUpdateAttribute( const AttributeHandlerPool::const_iterator& _pHandler )
+ {
+ if ( ( _pHandler->first == sal_uInt16(SID_ATTR_CHAR_WEIGHT) )
+ || ( _pHandler->first == sal_uInt16(SID_ATTR_CHAR_POSTURE) )
+ || ( _pHandler->first == sal_uInt16(SID_ATTR_CHAR_FONT) )
+ || ( _pHandler->first == sal_uInt16(SID_ATTR_CHAR_FONTHEIGHT) )
+ )
+ {
+ // these are attributes whose value depends on the current script type.
+ // I.e., in real, there are *three* items in the ItemSet: One for each script
+ // type (Latin, Asian, Complex). However, if we have an observer who is interested
+ // in the state of this attribute, we have to kind of *merge* the three attributes
+ // to only one.
+ // This is useful in case the observer is for instance a toolbox which contains only
+ // an, e.g., "bold" slot, and thus not interested in the particular script type of the
+ // current selection.
+ SvxScriptSetItem aNormalizedSet( static_cast<WhichId>(_pHandler->first), *m_pView->GetAttribs().GetPool() );
+ normalizeScriptDependentAttribute( aNormalizedSet );
+
+ implCheckUpdateCache( _pHandler->first, _pHandler->second->getState( aNormalizedSet.GetItemSet() ) );
+ }
+ else
+ implCheckUpdateCache( _pHandler->first, _pHandler->second->getState( m_pView->GetAttribs() ) );
+ }
+
+
+ void RichTextControlImpl::updateAttribute( AttributeId _nAttribute )
+ {
+ AttributeHandlerPool::const_iterator pHandler = m_aAttributeHandlers.find( _nAttribute );
+ if ( pHandler != m_aAttributeHandlers.end() )
+ implUpdateAttribute( pHandler );
+ }
+
+
+ void RichTextControlImpl::updateAllAttributes( )
+ {
+ for ( AttributeHandlerPool::const_iterator pHandler = m_aAttributeHandlers.begin();
+ pHandler != m_aAttributeHandlers.end();
+ ++pHandler
+ )
+ {
+ implUpdateAttribute( pHandler );
+ }
+
+ // notify changes of the selection, if necessary
+ if ( m_pSelectionListener && m_pView )
+ {
+ ESelection aCurrentSelection = m_pView->GetSelection();
+ if ( aCurrentSelection != m_aLastKnownSelection )
+ {
+ m_aLastKnownSelection = aCurrentSelection;
+ m_pSelectionListener->onSelectionChanged();
+ }
+ }
+ }
+
+
+ AttributeState RichTextControlImpl::getAttributeState( AttributeId _nAttributeId ) const
+ {
+ StateCache::const_iterator aCachedStatePos = m_aLastKnownStates.find( _nAttributeId );
+ if ( aCachedStatePos == m_aLastKnownStates.end() )
+ {
+ OSL_FAIL( "RichTextControlImpl::getAttributeState: Don't ask for the state of an attribute which I never encountered!" );
+ return AttributeState( eIndetermined );
+ }
+ return aCachedStatePos->second;
+ }
+
+
+ bool RichTextControlImpl::executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rAttribs, AttributeId _nAttribute, const SfxPoolItem* _pArgument, SvtScriptType _nForScriptType )
+ {
+ // let's see whether we have a handler for this attribute
+ AttributeHandlerPool::const_iterator aHandlerPos = m_aAttributeHandlers.find( _nAttribute );
+ if ( aHandlerPos != m_aAttributeHandlers.end() )
+ {
+ aHandlerPos->second->executeAttribute( _rCurrentAttribs, _rAttribs, _pArgument, _nForScriptType );
+ return true;
+ }
+ return false;
+ }
+
+
+ void RichTextControlImpl::enableAttributeNotification( AttributeId _nAttributeId, ITextAttributeListener* _pListener )
+ {
+ AttributeHandlerPool::const_iterator aHandlerPos = m_aAttributeHandlers.find( _nAttributeId );
+ if ( aHandlerPos == m_aAttributeHandlers.end() )
+ {
+ ::rtl::Reference< AttributeHandler > aHandler = AttributeHandlerFactory::getHandlerFor( _nAttributeId, *m_pEngine->GetEmptyItemSet().GetPool() );
+ OSL_ENSURE( aHandler.is(), "RichTextControlImpl::enableAttributeNotification: no handler available for this attribute!" );
+ if ( !aHandler.is() )
+ return;
+ SAL_WARN_IF( _nAttributeId != aHandler->getAttributeId(), "forms.richtext", "RichTextControlImpl::enableAttributeNotification: suspicious handler!" );
+
+ aHandlerPos = m_aAttributeHandlers.emplace( _nAttributeId , aHandler ).first;
+ }
+
+ // remember the listener
+ if ( _pListener )
+ m_aAttributeListeners.emplace( _nAttributeId, _pListener );
+
+ // update (and broadcast) the state of this attribute
+ updateAttribute( _nAttributeId );
+ }
+
+
+ void RichTextControlImpl::disableAttributeNotification( AttributeId _nAttributeId )
+ {
+ // forget the handler for this attribute
+ AttributeHandlerPool::iterator aHandlerPos = m_aAttributeHandlers.find( _nAttributeId );
+ if ( aHandlerPos != m_aAttributeHandlers.end() )
+ m_aAttributeHandlers.erase( aHandlerPos );
+
+ // as well as the listener
+ AttributeListenerPool::iterator aListenerPos = m_aAttributeListeners.find( _nAttributeId );
+ if ( aListenerPos != m_aAttributeListeners.end() )
+ m_aAttributeListeners.erase( aListenerPos );
+ }
+
+
+ void RichTextControlImpl::normalizeScriptDependentAttribute( SvxScriptSetItem& _rScriptSetItem )
+ {
+ _rScriptSetItem.GetItemSet().Put( m_pView->GetAttribs(), false );
+ const SfxPoolItem* pNormalizedItem = _rScriptSetItem.GetItemOfScript( getSelectedScriptType() );
+
+ WhichId nNormalizedWhichId = _rScriptSetItem.GetItemSet().GetPool()->GetWhich( _rScriptSetItem.Which() );
+ if ( pNormalizedItem )
+ {
+ _rScriptSetItem.GetItemSet().Put( pNormalizedItem->CloneSetWhich(nNormalizedWhichId) );
+ }
+ else
+ _rScriptSetItem.GetItemSet().InvalidateItem( nNormalizedWhichId );
+ }
+
+
+ void RichTextControlImpl::implCheckUpdateCache( AttributeId _nAttribute, const AttributeState& _rState )
+ {
+ StateCache::iterator aCachePos = m_aLastKnownStates.find( _nAttribute );
+ if ( aCachePos == m_aLastKnownStates.end() )
+ { // nothing known about this attribute, yet
+ m_aLastKnownStates.emplace( _nAttribute, _rState );
+ }
+ else
+ {
+ if ( aCachePos->second == _rState )
+ {
+ // nothing to do
+ return;
+ }
+ aCachePos->second = _rState;
+ }
+
+ // is there a dedicated listener for this particular attribute?
+ AttributeListenerPool::const_iterator aListenerPos = m_aAttributeListeners.find( _nAttribute );
+ if ( aListenerPos != m_aAttributeListeners.end( ) )
+ aListenerPos->second->onAttributeStateChanged( _nAttribute );
+
+ // call our global listener, if there is one
+ if ( m_pTextAttrListener )
+ m_pTextAttrListener->onAttributeStateChanged( _nAttribute );
+ }
+
+
+ SvtScriptType RichTextControlImpl::getSelectedScriptType() const
+ {
+ SvtScriptType nScript = m_pView->GetSelectedScriptType();
+ if ( nScript == SvtScriptType::NONE )
+ nScript = SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() );
+ return nScript;
+ }
+
+
+ void RichTextControlImpl::EditEngineStatusChanged( const EditStatus& _rStatus )
+ {
+ EditStatusFlags nStatusWord( _rStatus.GetStatusWord() );
+ if ( ( nStatusWord & EditStatusFlags::TEXTWIDTHCHANGED )
+ || ( nStatusWord & EditStatusFlags::TextHeightChanged )
+ )
+ {
+ if ( ( nStatusWord & EditStatusFlags::TextHeightChanged ) && windowHasAutomaticLineBreak() )
+ m_pEngine->SetPaperSize( Size( m_pEngine->GetPaperSize().Width(), m_pEngine->GetTextHeight() ) );
+
+ updateScrollbars();
+ }
+
+ bool bHScroll = bool( nStatusWord & EditStatusFlags::HSCROLL );
+ bool bVScroll = bool( nStatusWord & EditStatusFlags::VSCROLL );
+
+ // In case of *no* automatic line breaks, we also need to check for the *range* here.
+ // Normally, we would do this only after an EditStatusFlags::TEXTWIDTHCHANGED. However, due to a bug
+ // in the EditEngine (I believe so) this is not fired when the engine does not have
+ // the AutoPaperSize bits set.
+ // So in order to be properly notified, we would need the AutoPaperSize. But, with
+ // AutoPaperSize, other things do not work anymore: Either, when we set a MaxAutoPaperSize,
+ // then the view does automatic soft line breaks at the paper end - which we definitely do
+ // want. Or, if we did not set a MaxAutoPaperSize, then the view does not automatically scroll
+ // anymore in horizontal direction.
+ // So this is some kind of lose-lose situation ... :(
+ if ( !windowHasAutomaticLineBreak() && bHScroll )
+ {
+ updateScrollbars();
+ return;
+ }
+
+ if ( bHScroll && m_pHScroll )
+ m_pHScroll->SetThumbPos( m_pView->GetVisArea().Left() );
+ if ( bVScroll && m_pVScroll )
+ m_pVScroll->SetThumbPos( m_pView->GetVisArea().Top() );
+ }
+
+ IMPL_LINK_NOARG( RichTextControlImpl, OnInvalidateAllAttributes, LinkParamNone*, void )
+ {
+ updateAllAttributes();
+ }
+
+ IMPL_LINK( RichTextControlImpl, OnHScroll, weld::Scrollbar&, rScrollbar, void )
+ {
+ auto nDiff = m_pView->GetVisArea().Left() - rScrollbar.adjustment_get_value();
+ m_pView->Scroll(nDiff, 0, ScrollRangeCheck::PaperWidthTextSize);
+ }
+
+ IMPL_LINK(RichTextControlImpl, OnVScroll, weld::Scrollbar&, rScrollbar, void)
+ {
+ auto nDiff = m_pView->GetVisArea().Top() - rScrollbar.adjustment_get_value();
+ m_pView->Scroll(0, nDiff, ScrollRangeCheck::PaperWidthTextSize);
+ }
+
+ void RichTextControlImpl::ensureScrollbars()
+ {
+ bool bNeedVScroll = 0 != ( m_pAntiImpl->GetStyle() & WB_VSCROLL );
+ bool bNeedHScroll = 0 != ( m_pAntiImpl->GetStyle() & WB_HSCROLL );
+
+ if ( ( bNeedVScroll == hasVScrollBar() ) && ( bNeedHScroll == hasHScrollBar( ) ) )
+ // nothing to do
+ return;
+
+ // create or delete the scrollbars, as necessary
+ if ( !bNeedVScroll )
+ {
+ m_pVScroll.disposeAndClear();
+ }
+ else
+ {
+ m_pVScroll = VclPtr<ScrollAdaptor>::Create( m_pAntiImpl, false );
+ m_pVScroll->SetScrollHdl ( LINK( this, RichTextControlImpl, OnVScroll ) );
+ m_pVScroll->Show();
+ }
+
+ if ( !bNeedHScroll )
+ {
+ m_pHScroll.disposeAndClear();
+ }
+ else
+ {
+ m_pHScroll = VclPtr<ScrollAdaptor>::Create( m_pAntiImpl, true );
+ m_pHScroll->SetScrollHdl ( LINK( this, RichTextControlImpl, OnHScroll ) );
+ m_pHScroll->Show();
+ }
+
+ layoutWindow();
+ }
+
+ void RichTextControlImpl::ensureLineBreakSetting()
+ {
+ if ( !windowHasAutomaticLineBreak() )
+ m_pEngine->SetPaperSize( Size( EMPTY_PAPER_SIZE, EMPTY_PAPER_SIZE ) );
+
+ layoutWindow();
+ }
+
+ void RichTextControlImpl::layoutWindow()
+ {
+ if ( !m_bHasEverBeenShown )
+ // no need to do anything. Especially, no need to set the paper size on the
+ // EditEngine to anything...
+ return;
+
+ const StyleSettings& rStyleSettings = m_pAntiImpl->GetSettings().GetStyleSettings();
+
+ tools::Long nScrollBarWidth = m_pVScroll ? rStyleSettings.GetScrollBarSize() : 0;
+ tools::Long nScrollBarHeight = m_pHScroll ? rStyleSettings.GetScrollBarSize() : 0;
+
+ if ( m_pAntiImpl->IsZoom() )
+ {
+ nScrollBarWidth = m_pAntiImpl->CalcZoom( nScrollBarWidth );
+ nScrollBarHeight = m_pAntiImpl->CalcZoom( nScrollBarHeight );
+ }
+
+ // the overall size we can use
+ Size aPlaygroundSizePixel( m_pAntiImpl->GetOutputSizePixel() );
+
+ // the size of the viewport - note that the viewport does *not* occupy all the place
+ // which is left when subtracting the scrollbar width/height
+ Size aViewportPlaygroundPixel( aPlaygroundSizePixel );
+ aViewportPlaygroundPixel.setWidth( ::std::max( tools::Long( 10 ), tools::Long( aViewportPlaygroundPixel.Width() - nScrollBarWidth ) ) );
+ aViewportPlaygroundPixel.setHeight( ::std::max( tools::Long( 10 ), tools::Long( aViewportPlaygroundPixel.Height() - nScrollBarHeight ) ) );
+ Size aViewportPlaygroundLogic( m_pViewport->PixelToLogic( aViewportPlaygroundPixel ) );
+
+ const tools::Long nOffset = 2;
+ Size aViewportSizePixel( aViewportPlaygroundPixel.Width() - 2 * nOffset, aViewportPlaygroundPixel.Height() - 2 * nOffset );
+ Size aViewportSizeLogic( m_pViewport->PixelToLogic( aViewportSizePixel ) );
+
+ // position the viewport
+ m_pViewport->SetPosSizePixel( Point( nOffset, nOffset ), aViewportSizePixel );
+ // position the scrollbars
+ if ( m_pVScroll )
+ {
+ m_pVScroll->SetThickness(nScrollBarWidth);
+ m_pVScroll->SetPosSizePixel( Point( aViewportPlaygroundPixel.Width(), 0 ), Size( nScrollBarWidth, aViewportPlaygroundPixel.Height() ) );
+ }
+ if ( m_pHScroll )
+ {
+ m_pHScroll->SetThickness(nScrollBarHeight);
+ m_pHScroll->SetPosSizePixel( Point( 0, aViewportPlaygroundPixel.Height() ), Size( aViewportPlaygroundPixel.Width(), nScrollBarHeight ) );
+ }
+
+ // paper size
+ if ( windowHasAutomaticLineBreak() )
+ m_pEngine->SetPaperSize( Size( aViewportSizeLogic.Width(), m_pEngine->GetTextHeight() ) );
+
+ // output area of the view
+ m_pView->SetOutputArea( tools::Rectangle( Point( ), aViewportSizeLogic ) );
+ m_pView->SetVisArea( tools::Rectangle( Point( ), aViewportSizeLogic ) );
+
+ if ( m_pVScroll )
+ {
+ m_pVScroll->SetVisibleSize( aViewportPlaygroundLogic.Height() );
+
+ // the default height of a text line...
+ tools::Long nFontHeight = m_pEngine->GetStandardFont(0).GetFontSize().Height();
+ // ... is the scroll size for the vertical scrollbar
+ m_pVScroll->SetLineSize( nFontHeight );
+ // the viewport width, minus one line, is the page scroll size
+ m_pVScroll->SetPageSize( ::std::max( nFontHeight, aViewportPlaygroundLogic.Height() - nFontHeight ) );
+ }
+
+ // the font width
+ if ( m_pHScroll )
+ {
+ m_pHScroll->SetVisibleSize( aViewportPlaygroundLogic.Width() );
+
+ tools::Long nFontWidth = m_pEngine->GetStandardFont(0).GetFontSize().Width();
+ if ( !nFontWidth )
+ {
+ m_pViewport->GetOutDev()->Push( vcl::PushFlags::FONT );
+ m_pViewport->SetFont( m_pEngine->GetStandardFont(0) );
+ nFontWidth = m_pViewport->GetTextWidth( "x" );
+ m_pViewport->GetOutDev()->Pop();
+ }
+ // ... is the scroll size for the horizontal scrollbar
+ m_pHScroll->SetLineSize( 5 * nFontWidth );
+ // the viewport height, minus one character, is the page scroll size
+ m_pHScroll->SetPageSize( ::std::max( nFontWidth, aViewportPlaygroundLogic.Width() - nFontWidth ) );
+ }
+
+ // update range and position of the scrollbars
+ updateScrollbars();
+ }
+
+
+ void RichTextControlImpl::updateScrollbars()
+ {
+ if ( m_pVScroll )
+ {
+ tools::Long nOverallTextHeight = m_pEngine->GetTextHeight();
+ m_pVScroll->SetRange( Range( 0, nOverallTextHeight ) );
+ m_pVScroll->SetThumbPos( m_pView->GetVisArea().Top() );
+ }
+
+ if ( m_pHScroll )
+ {
+ Size aPaperSize( m_pEngine->GetPaperSize() );
+ tools::Long nOverallTextWidth = ( aPaperSize.Width() == EMPTY_PAPER_SIZE ) ? m_pEngine->CalcTextWidth() : aPaperSize.Width();
+ m_pHScroll->SetRange( Range( 0, nOverallTextWidth ) );
+ m_pHScroll->SetThumbPos( m_pView->GetVisArea().Left() );
+ }
+ }
+
+
+ void RichTextControlImpl::notifyInitShow()
+ {
+ if ( !m_bHasEverBeenShown )
+ {
+ m_bHasEverBeenShown = true;
+ layoutWindow();
+ }
+ }
+
+
+ void RichTextControlImpl::notifyStyleChanged()
+ {
+ ensureScrollbars();
+ ensureLineBreakSetting();
+ }
+
+
+ void RichTextControlImpl::notifyZoomChanged()
+ {
+ const Fraction& rZoom = m_pAntiImpl->GetZoom();
+
+ MapMode aMapMode( m_pAntiImpl->GetMapMode() );
+ aMapMode.SetScaleX( rZoom );
+ aMapMode.SetScaleY( rZoom );
+ m_pAntiImpl->SetMapMode( aMapMode );
+
+ m_pViewport->SetZoom( rZoom );
+ m_pViewport->SetMapMode( aMapMode );
+
+ layoutWindow();
+ }
+
+
+ bool RichTextControlImpl::windowHasAutomaticLineBreak()
+ {
+ return ( m_pAntiImpl->GetStyle() & WB_WORDBREAK ) != 0;
+ }
+
+
+ void RichTextControlImpl::SetReadOnly( bool _bReadOnly )
+ {
+ m_pView->SetReadOnly( _bReadOnly );
+ }
+
+
+ bool RichTextControlImpl::IsReadOnly() const
+ {
+ return m_pView->IsReadOnly( );
+ }
+
+
+ namespace
+ {
+ void lcl_inflate( tools::Rectangle& _rRect, tools::Long _nInflateX, tools::Long _nInflateY )
+ {
+ _rRect.AdjustLeft( -_nInflateX );
+ _rRect.AdjustRight(_nInflateX );
+ _rRect.AdjustTop( -_nInflateY );
+ _rRect.AdjustBottom(_nInflateY );
+ }
+ }
+
+ bool RichTextControlImpl::HandleCommand( const CommandEvent& _rEvent )
+ {
+ if ( ( _rEvent.GetCommand() == CommandEventId::Wheel )
+ || ( _rEvent.GetCommand() == CommandEventId::StartAutoScroll )
+ || ( _rEvent.GetCommand() == CommandEventId::AutoScroll )
+ )
+ {
+ m_pAntiImpl->HandleScrollCommand( _rEvent, m_pHScroll, m_pVScroll );
+ return true;
+ }
+ return false;
+ }
+
+
+ void RichTextControlImpl::Draw( OutputDevice* _pDev, const Point& _rPos, const Size& _rSize )
+ {
+ // need to normalize the map mode of the device - every paint operation on any device needs
+ // to use the same map mode
+ _pDev->Push( vcl::PushFlags::MAPMODE | vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+
+ // enforce our "normalize map mode" on the device
+ MapMode aRefMapMode( m_pEngine->GetRefDevice()->GetMapMode() );
+ MapMode aOriginalMapMode( _pDev->GetMapMode() );
+ MapMode aNormalizedMapMode( aRefMapMode.GetMapUnit(), aRefMapMode.GetOrigin(), aOriginalMapMode.GetScaleX(), aOriginalMapMode.GetScaleY() );
+ _pDev->SetMapMode( aNormalizedMapMode );
+
+ // translate coordinates
+ Point aPos( _rPos );
+ Size aSize( _rSize );
+ if ( aOriginalMapMode.GetMapUnit() == MapUnit::MapPixel )
+ {
+ aPos = _pDev->PixelToLogic( _rPos, aNormalizedMapMode );
+ aSize = _pDev->PixelToLogic( _rSize, aNormalizedMapMode );
+ }
+ else
+ {
+ aPos = OutputDevice::LogicToLogic( _rPos, aOriginalMapMode, aNormalizedMapMode );
+ aSize = OutputDevice::LogicToLogic( _rSize, aOriginalMapMode, aNormalizedMapMode );
+ }
+
+ tools::Rectangle aPlayground( aPos, aSize );
+ Size aOnePixel( _pDev->PixelToLogic( Size( 1, 1 ) ) );
+ aPlayground.AdjustRight( -(aOnePixel.Width()) );
+ aPlayground.AdjustBottom( -(aOnePixel.Height()) );
+
+ // background
+ _pDev->SetLineColor();
+ _pDev->DrawRect( aPlayground );
+
+ // do we need to draw a border?
+ bool bBorder = ( m_pAntiImpl->GetStyle() & WB_BORDER );
+ if ( bBorder )
+ _pDev->SetLineColor( m_pAntiImpl->GetSettings().GetStyleSettings().GetMonoColor() );
+ else
+ _pDev->SetLineColor();
+ _pDev->SetFillColor( m_pAntiImpl->GetBackground().GetColor() );
+ _pDev->DrawRect( aPlayground );
+
+ if ( bBorder )
+ // don't draw the text over the border
+ lcl_inflate( aPlayground, -aOnePixel.Width(), -aOnePixel.Height() );
+
+ // leave a space of two pixels between the "surroundings" of the control
+ // and the content
+ lcl_inflate( aPlayground, -aOnePixel.Width(), -aOnePixel.Height() );
+ lcl_inflate( aPlayground, -aOnePixel.Width(), -aOnePixel.Height() );
+
+ // actually draw the content
+ m_pEngine->Draw(*_pDev, aPlayground, Point(), true);
+
+ _pDev->Pop();
+ }
+
+
+ void RichTextControlImpl::SetBackgroundColor( )
+ {
+ SetBackgroundColor( Application::GetSettings().GetStyleSettings().GetFieldColor() );
+ }
+
+
+ void RichTextControlImpl::SetBackgroundColor( const Color& _rColor )
+ {
+ Wallpaper aWallpaper( _rColor );
+ m_pAntiImpl->SetBackground( aWallpaper );
+ m_pViewport->SetBackground( aWallpaper );
+ }
+
+
+ void RichTextControlImpl::SetHideInactiveSelection( bool _bHide )
+ {
+ m_pViewport->SetHideInactiveSelection( _bHide );
+ }
+
+
+ bool RichTextControlImpl::GetHideInactiveSelection() const
+ {
+ return m_pViewport->GetHideInactiveSelection( );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextimplcontrol.hxx b/forms/source/richtext/richtextimplcontrol.hxx
new file mode 100644
index 0000000000..86b1a18316
--- /dev/null
+++ b/forms/source/richtext/richtextimplcontrol.hxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "rtattributehandler.hxx"
+#include "richtextviewport.hxx"
+#include "richtextengine.hxx"
+#include <svtools/scrolladaptor.hxx>
+#include <editeng/editdata.hxx>
+
+#include <map>
+
+class EditView;
+class EditStatus;
+namespace vcl { class Window; }
+class SvxScriptSetItem;
+
+namespace frm
+{
+
+
+ class ITextAttributeListener;
+ class ITextSelectionListener;
+ class RichTextViewPort;
+
+ class RichTextControlImpl : public IEngineStatusListener
+ {
+ typedef ::std::map< AttributeId, AttributeState > StateCache;
+ typedef ::std::map< AttributeId, ::rtl::Reference< AttributeHandler > > AttributeHandlerPool;
+ typedef ::std::map< AttributeId, ITextAttributeListener* > AttributeListenerPool;
+
+ StateCache m_aLastKnownStates;
+ AttributeHandlerPool m_aAttributeHandlers;
+ AttributeListenerPool m_aAttributeListeners;
+
+ ESelection m_aLastKnownSelection;
+
+ VclPtr<Control> m_pAntiImpl;
+ VclPtr<RichTextViewPort> m_pViewport;
+ VclPtr<ScrollAdaptor> m_pHScroll;
+ VclPtr<ScrollAdaptor> m_pVScroll;
+ RichTextEngine* m_pEngine;
+ std::unique_ptr<EditView> m_pView;
+ ITextAttributeListener* m_pTextAttrListener;
+ ITextSelectionListener* m_pSelectionListener;
+ bool m_bHasEverBeenShown;
+
+ public:
+ struct GrantAccess { friend class RichTextControl; private: GrantAccess() { } };
+ EditView* getView( const GrantAccess& ) const { return m_pView.get(); }
+ RichTextEngine* getEngine( const GrantAccess& ) const { return m_pEngine; }
+ vcl::Window* getViewport( const GrantAccess& ) const { return m_pViewport; }
+
+ public:
+ RichTextControlImpl( Control* _pAntiImpl, RichTextEngine* _pEngine,
+ ITextAttributeListener* _pTextAttrListener, ITextSelectionListener* _pSelectionListener );
+ virtual ~RichTextControlImpl();
+
+ /** updates the cache with the state of all attribute values from the given set, notifies
+ the listener if the state changed
+ */
+ void updateAllAttributes( );
+
+ /** updates the cache with the state of the attribute given by which id, notifies
+ the listener if the state changed
+ */
+ void updateAttribute( AttributeId _nAttribute );
+
+ /// enables the callback for a particular attribute
+ void enableAttributeNotification( AttributeId _nAttributeId, ITextAttributeListener* _pListener );
+
+ /// disables the change notifications for a particular attribute
+ void disableAttributeNotification( AttributeId _nAttributeId );
+
+ /// executes a toggle of the given attribute
+ bool executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, AttributeId _nAttribute, const SfxPoolItem* _pArgument, SvtScriptType _nForScriptType );
+
+ /// retrieves the state of the given attribute from the cache
+ AttributeState getAttributeState( AttributeId _nAttributeId ) const;
+
+ /** normalizes the given item so that the state of script dependent attributes
+ is correct considering the current script type
+
+ There are some attributes which are script dependent, e.g. the CharPosture. This means
+ that in real, there are 3 attributes for this, one for every possible script type (latin,
+ asian, complex). However, to the out world, we behave as if there is only one attribute:
+ E.g., if the outer world asks for the state of the "CharPosture" attribute, we return
+ the state of either CharPostureLatin, CharPostureAsian, or CharPostureComplex, depending
+ on the script type of the current selection. (In real, it may be more complex since
+ the current selection may contain more than one script type.)
+
+ This method normalizes a script dependent attribute, so that it's state takes into account
+ the currently selected script type.
+ */
+ void normalizeScriptDependentAttribute( SvxScriptSetItem& _rScriptSetItem );
+
+ // gets the script type of the selection in our edit view (with fallback)
+ SvtScriptType getSelectedScriptType() const;
+
+ /** re-arranges the view and the scrollbars
+ */
+ void layoutWindow();
+
+ /** to be called when the style of our window changed
+ */
+ void notifyStyleChanged();
+
+ /** to be called when the zoom of our window changed
+ */
+ void notifyZoomChanged();
+
+ /** to be called when the StateChangedType::InitShow event arrives
+ */
+ void notifyInitShow();
+
+ // VCL "overrides"
+ void SetBackgroundColor( );
+ void SetBackgroundColor( const Color& _rColor );
+
+ void SetReadOnly( bool _bReadOnly );
+ bool IsReadOnly() const;
+
+ void SetHideInactiveSelection( bool _bHide );
+ bool GetHideInactiveSelection() const;
+
+ /// draws the control onto a given output device
+ void Draw( OutputDevice* _pDev, const Point& _rPos, const Size& _rSize );
+
+ /// handles command events arrived at the anti-impl control
+ bool HandleCommand( const CommandEvent& _rEvent );
+
+ private:
+ // updates the cache with the state provided by the given attribute handler
+ void implUpdateAttribute( const AttributeHandlerPool::const_iterator& _pHandler );
+
+ // updates the cache with the given state, and calls listeners (if necessary)
+ void implCheckUpdateCache( AttributeId _nAttribute, const AttributeState& _rState );
+
+ // updates range and position of our scrollbars
+ void updateScrollbars();
+
+ // determines whether automatic (soft) line breaks are ON
+ bool windowHasAutomaticLineBreak();
+
+ /// hides or shows our scrollbars, according to the current WinBits of the window
+ void ensureScrollbars();
+
+ /// ensures that our "automatic line break" setting matches the current WinBits of the window
+ void ensureLineBreakSetting();
+
+ bool hasVScrollBar( ) const { return m_pVScroll != nullptr; }
+ bool hasHScrollBar( ) const { return m_pHScroll != nullptr; }
+
+ // IEngineStatusListener overridables
+ virtual void EditEngineStatusChanged( const EditStatus& _rStatus ) override;
+
+ private:
+ DECL_LINK( OnInvalidateAllAttributes, LinkParamNone*, void );
+ DECL_LINK( OnHScroll, weld::Scrollbar&, void );
+ DECL_LINK( OnVScroll, weld::Scrollbar&, void );
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextmodel.cxx b/forms/source/richtext/richtextmodel.cxx
new file mode 100644
index 0000000000..d9e01def22
--- /dev/null
+++ b/forms/source/richtext/richtextmodel.cxx
@@ -0,0 +1,641 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "richtextmodel.hxx"
+#include "richtextengine.hxx"
+#include "richtextunowrapper.hxx"
+
+#include <property.hxx>
+#include <services.hxx>
+
+#include <com/sun/star/awt/LineEndFormat.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+
+#include <comphelper/guarding.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <toolkit/awt/vclxdevice.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <editeng/editstat.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+
+
+namespace frm
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::style;
+
+ namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+
+ ORichTextModel::ORichTextModel( const Reference< XComponentContext >& _rxFactory )
+ :OControlModel ( _rxFactory, OUString() )
+ ,FontControlModel ( true )
+ ,m_pEngine ( RichTextEngine::Create() )
+ ,m_bSettingEngineText( false )
+ ,m_aModifyListeners ( m_aMutex )
+ {
+ m_nClassId = FormComponentType::TEXTFIELD;
+
+ getPropertyDefaultByHandle( PROPERTY_ID_DEFAULTCONTROL ) >>= m_sDefaultControl;
+ // Default to 'flat' instead of '3D Look' for form controls, but don't change
+ // getPropertyDefaultByHandle, see tdf#152974
+ m_nBorder = 2;
+ getPropertyDefaultByHandle( PROPERTY_ID_ENABLED ) >>= m_bEnabled;
+ getPropertyDefaultByHandle( PROPERTY_ID_ENABLEVISIBLE ) >>= m_bEnableVisible;
+ getPropertyDefaultByHandle( PROPERTY_ID_HARDLINEBREAKS ) >>= m_bHardLineBreaks;
+ getPropertyDefaultByHandle( PROPERTY_ID_HSCROLL ) >>= m_bHScroll;
+ getPropertyDefaultByHandle( PROPERTY_ID_VSCROLL ) >>= m_bVScroll;
+ getPropertyDefaultByHandle( PROPERTY_ID_READONLY ) >>= m_bReadonly;
+ getPropertyDefaultByHandle( PROPERTY_ID_PRINTABLE ) >>= m_bPrintable;
+ m_aAlign = getPropertyDefaultByHandle( PROPERTY_ID_ALIGN );
+ getPropertyDefaultByHandle( PROPERTY_ID_ECHO_CHAR ) >>= m_nEchoChar;
+ getPropertyDefaultByHandle( PROPERTY_ID_MAXTEXTLEN ) >>= m_nMaxTextLength;
+ getPropertyDefaultByHandle( PROPERTY_ID_MULTILINE ) >>= m_bMultiLine;
+ getPropertyDefaultByHandle( PROPERTY_ID_RICH_TEXT ) >>= m_bReallyActAsRichText;
+ getPropertyDefaultByHandle( PROPERTY_ID_HIDEINACTIVESELECTION ) >>= m_bHideInactiveSelection;
+ getPropertyDefaultByHandle( PROPERTY_ID_LINEEND_FORMAT ) >>= m_nLineEndFormat;
+ getPropertyDefaultByHandle( PROPERTY_ID_WRITING_MODE ) >>= m_nTextWritingMode;
+ getPropertyDefaultByHandle( PROPERTY_ID_CONTEXT_WRITING_MODE ) >>= m_nContextWritingMode;
+
+ implInit();
+ }
+
+
+ ORichTextModel::ORichTextModel( const ORichTextModel* _pOriginal, const Reference< XComponentContext >& _rxFactory )
+ :OControlModel ( _pOriginal, _rxFactory, false )
+ ,FontControlModel ( _pOriginal )
+ ,m_bSettingEngineText( false )
+ ,m_aModifyListeners ( m_aMutex )
+ {
+
+ m_aTabStop = _pOriginal->m_aTabStop;
+ m_aBackgroundColor = _pOriginal->m_aBackgroundColor;
+ m_aBorderColor = _pOriginal->m_aBorderColor;
+ m_aVerticalAlignment = _pOriginal->m_aVerticalAlignment;
+ m_sDefaultControl = _pOriginal->m_sDefaultControl;
+ m_sHelpText = _pOriginal->m_sHelpText;
+ m_sHelpURL = _pOriginal->m_sHelpURL;
+ m_nBorder = _pOriginal->m_nBorder;
+ m_bEnabled = _pOriginal->m_bEnabled;
+ m_bEnableVisible = _pOriginal->m_bEnableVisible;
+ m_bHardLineBreaks = _pOriginal->m_bHardLineBreaks;
+ m_bHScroll = _pOriginal->m_bHScroll;
+ m_bVScroll = _pOriginal->m_bVScroll;
+ m_bReadonly = _pOriginal->m_bReadonly;
+ m_bPrintable = _pOriginal->m_bPrintable;
+ m_bReallyActAsRichText = _pOriginal->m_bReallyActAsRichText;
+ m_bHideInactiveSelection = _pOriginal->m_bHideInactiveSelection;
+ m_nLineEndFormat = _pOriginal->m_nLineEndFormat;
+ m_nTextWritingMode = _pOriginal->m_nTextWritingMode;
+ m_nContextWritingMode = _pOriginal->m_nContextWritingMode;
+
+ m_aAlign = _pOriginal->m_aAlign;
+ m_nEchoChar = _pOriginal->m_nEchoChar;
+ m_nMaxTextLength = _pOriginal->m_nMaxTextLength;
+ m_bMultiLine = _pOriginal->m_bMultiLine;
+
+ m_pEngine.reset(_pOriginal->m_pEngine->Clone());
+ m_sLastKnownEngineText = m_pEngine->GetText();
+
+ implInit();
+ }
+
+
+ void ORichTextModel::implInit()
+ {
+ OSL_ENSURE(m_pEngine, "ORichTextModel::implInit: where's the engine?");
+ if (m_pEngine)
+ {
+ m_pEngine->SetModifyHdl( LINK( this, ORichTextModel, OnEngineContentModified ) );
+
+ EEControlBits nEngineControlWord = m_pEngine->GetControlWord();
+ nEngineControlWord = nEngineControlWord & ~EEControlBits::AUTOPAGESIZE;
+ m_pEngine->SetControlWord( nEngineControlWord );
+
+ rtl::Reference<VCLXDevice> pUnoRefDevice = new VCLXDevice;
+ {
+ SolarMutexGuard g;
+ pUnoRefDevice->SetOutputDevice( m_pEngine->GetRefDevice() );
+ }
+ m_xReferenceDevice = pUnoRefDevice;
+ }
+
+ implDoAggregation();
+ implRegisterProperties();
+ }
+
+
+ void ORichTextModel::implDoAggregation()
+ {
+ osl_atomic_increment( &m_refCount );
+
+ {
+ m_xAggregate = new ORichTextUnoWrapper( *m_pEngine, this );
+ setAggregation( m_xAggregate );
+ doSetDelegator();
+ }
+
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ void ORichTextModel::implRegisterProperties()
+ {
+ registerProperty( PROPERTY_DEFAULTCONTROL, PROPERTY_ID_DEFAULTCONTROL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sDefaultControl, cppu::UnoType<decltype(m_sDefaultControl)>::get() );
+ registerProperty( PROPERTY_HELPTEXT, PROPERTY_ID_HELPTEXT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sHelpText, cppu::UnoType<decltype(m_sHelpText)>::get() );
+ registerProperty( PROPERTY_HELPURL, PROPERTY_ID_HELPURL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sHelpURL, cppu::UnoType<decltype(m_sHelpURL)>::get() );
+ registerProperty( PROPERTY_ENABLED, PROPERTY_ID_ENABLED, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bEnabled, cppu::UnoType<decltype(m_bEnabled)>::get() );
+ registerProperty( PROPERTY_ENABLEVISIBLE, PROPERTY_ID_ENABLEVISIBLE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bEnableVisible, cppu::UnoType<decltype(m_bEnableVisible)>::get() );
+ registerProperty( PROPERTY_BORDER, PROPERTY_ID_BORDER, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nBorder, cppu::UnoType<decltype(m_nBorder)>::get() );
+ registerProperty( PROPERTY_HARDLINEBREAKS, PROPERTY_ID_HARDLINEBREAKS, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bHardLineBreaks, cppu::UnoType<decltype(m_bHardLineBreaks)>::get() );
+ registerProperty( PROPERTY_HSCROLL, PROPERTY_ID_HSCROLL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bHScroll, cppu::UnoType<decltype(m_bHScroll)>::get() );
+ registerProperty( PROPERTY_VSCROLL, PROPERTY_ID_VSCROLL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bVScroll, cppu::UnoType<decltype(m_bVScroll)>::get() );
+ registerProperty( PROPERTY_READONLY, PROPERTY_ID_READONLY, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bReadonly, cppu::UnoType<decltype(m_bReadonly)>::get() );
+ registerProperty( PROPERTY_PRINTABLE, PROPERTY_ID_PRINTABLE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bPrintable, cppu::UnoType<decltype(m_bPrintable)>::get() );
+ registerProperty( PROPERTY_REFERENCE_DEVICE, PROPERTY_ID_REFERENCE_DEVICE, PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT,
+ &m_xReferenceDevice, cppu::UnoType<decltype(m_xReferenceDevice)>::get() );
+ registerProperty( PROPERTY_RICH_TEXT, PROPERTY_ID_RICH_TEXT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bReallyActAsRichText, cppu::UnoType<decltype(m_bReallyActAsRichText)>::get() );
+ registerProperty( PROPERTY_HIDEINACTIVESELECTION, PROPERTY_ID_HIDEINACTIVESELECTION, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bHideInactiveSelection, cppu::UnoType<decltype(m_bHideInactiveSelection)>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_TABSTOP, PROPERTY_ID_TABSTOP, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_aTabStop, cppu::UnoType<sal_Bool>::get() );
+ registerMayBeVoidProperty( PROPERTY_BACKGROUNDCOLOR, PROPERTY_ID_BACKGROUNDCOLOR, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_aBackgroundColor, cppu::UnoType<sal_Int32>::get() );
+ registerMayBeVoidProperty( PROPERTY_BORDERCOLOR, PROPERTY_ID_BORDERCOLOR, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_aBorderColor, cppu::UnoType<sal_Int32>::get() );
+ registerMayBeVoidProperty( PROPERTY_VERTICAL_ALIGN, PROPERTY_ID_VERTICAL_ALIGN, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_aVerticalAlignment, cppu::UnoType<VerticalAlignment>::get() );
+
+ // properties which exist only for compatibility with the css.swt.UnoControlEditModel,
+ // since we replace the default implementation for this service
+ registerProperty( PROPERTY_ECHO_CHAR, PROPERTY_ID_ECHO_CHAR, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nEchoChar, cppu::UnoType<decltype(m_nEchoChar)>::get() );
+ registerProperty( PROPERTY_MAXTEXTLEN, PROPERTY_ID_MAXTEXTLEN, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nMaxTextLength, cppu::UnoType<decltype(m_nMaxTextLength)>::get() );
+ registerProperty( PROPERTY_MULTILINE, PROPERTY_ID_MULTILINE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_bMultiLine, cppu::UnoType<decltype(m_bMultiLine)>::get() );
+ registerProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_sLastKnownEngineText, cppu::UnoType<decltype(m_sLastKnownEngineText)>::get() );
+ registerProperty( PROPERTY_LINEEND_FORMAT, PROPERTY_ID_LINEEND_FORMAT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nLineEndFormat, cppu::UnoType<decltype(m_nLineEndFormat)>::get() );
+ registerProperty( PROPERTY_WRITING_MODE, PROPERTY_ID_WRITING_MODE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_nTextWritingMode, cppu::UnoType<decltype(m_nTextWritingMode)>::get() );
+
+ registerProperty( PROPERTY_CONTEXT_WRITING_MODE, PROPERTY_ID_CONTEXT_WRITING_MODE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT |
+ PropertyAttribute::TRANSIENT, &m_nContextWritingMode, cppu::UnoType<decltype(m_nContextWritingMode)>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_ALIGN, PROPERTY_ID_ALIGN, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT,
+ &m_aAlign, cppu::UnoType<sal_Int16>::get() );
+ }
+
+
+ ORichTextModel::~ORichTextModel( )
+ {
+ if ( !OComponentHelper::rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+ if (m_pEngine)
+ {
+ SolarMutexGuard g;
+ m_pEngine.reset();
+ }
+ }
+
+
+ Any SAL_CALL ORichTextModel::queryAggregation( const Type& _rType )
+ {
+ Any aReturn = ORichTextModel_BASE::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = OControlModel::queryAggregation( _rType );
+
+ return aReturn;
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORichTextModel, OControlModel, ORichTextModel_BASE )
+
+ OUString SAL_CALL ORichTextModel::getImplementationName()
+ {
+ return "com.sun.star.comp.forms.ORichTextModel";
+ }
+
+ Sequence< OUString > SAL_CALL ORichTextModel::getSupportedServiceNames()
+ {
+ Sequence< OUString > aOwnNames {
+ FRM_SUN_COMPONENT_RICHTEXTCONTROL,
+ "com.sun.star.text.TextRange",
+ "com.sun.star.style.CharacterProperties",
+ "com.sun.star.style.ParagraphProperties",
+ "com.sun.star.style.CharacterPropertiesAsian",
+ "com.sun.star.style.CharacterPropertiesComplex",
+ "com.sun.star.style.ParagraphPropertiesAsian",
+ "com.sun.star.style.ParagraphPropertiesComplex" };
+
+ return ::comphelper::combineSequences(
+ getAggregateServiceNames(),
+ ::comphelper::concatSequences(
+ OControlModel::getSupportedServiceNames_Static(),
+ aOwnNames)
+ );
+ }
+
+ css::uno::Reference< css::util::XCloneable > SAL_CALL ORichTextModel::createClone()
+{
+ rtl::Reference<ORichTextModel> pClone = new ORichTextModel(this, getContext());
+ pClone->clonedFrom(this);
+ return pClone;
+}
+
+
+ void SAL_CALL ORichTextModel::disposing()
+ {
+ m_aModifyListeners.disposeAndClear( EventObject( *this ) );
+ OControlModel::disposing();
+ }
+
+
+ namespace
+ {
+ void lcl_removeProperty( Sequence< Property >& _rSeq, std::u16string_view _rPropertyName )
+ {
+ Property* pLoop = _rSeq.getArray();
+ Property* pEnd = _rSeq.getArray() + _rSeq.getLength();
+ while ( pLoop != pEnd )
+ {
+ if ( pLoop->Name == _rPropertyName )
+ {
+ ::std::copy( pLoop + 1, pEnd, pLoop );
+ _rSeq.realloc( _rSeq.getLength() - 1 );
+ break;
+ }
+ ++pLoop;
+ }
+ }
+ }
+
+ void ORichTextModel::describeFixedProperties( Sequence< Property >& _rProps ) const
+ {
+ OControlModel::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<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT);
+ DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
+
+ // properties which the OPropertyContainerHelper is responsible for
+ Sequence< Property > aContainedProperties;
+ describeProperties( aContainedProperties );
+
+ // properties which the FontControlModel is responsible for
+ Sequence< Property > aFontProperties;
+ describeFontRelatedProperties( aFontProperties );
+
+ _rProps = concatSequences( aContainedProperties, aFontProperties, _rProps );
+ }
+
+
+ void ORichTextModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const
+ {
+ OControlModel::describeAggregateProperties( _rAggregateProps );
+
+ // our aggregate (the SvxUnoText) declares a FontDescriptor property, as does
+ // our FormControlFont base class. We remove it from the base class' sequence
+ // here, and later on care for both instances being in sync
+ lcl_removeProperty( _rAggregateProps, PROPERTY_FONT );
+
+ // similar, the WritingMode property is declared in our aggregate, too, but we override
+ // it, since the aggregate does no proper PropertyState handling.
+ lcl_removeProperty( _rAggregateProps, PROPERTY_WRITING_MODE );
+ }
+
+
+ void SAL_CALL ORichTextModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ if ( isRegisteredProperty( _nHandle ) )
+ {
+ OPropertyContainerHelper::getFastPropertyValue( _rValue, _nHandle );
+ }
+ else if ( isFontRelatedProperty( _nHandle ) )
+ {
+ FontControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ else
+ {
+ OControlModel::getFastPropertyValue( _rValue, _nHandle );
+ }
+ }
+
+
+ sal_Bool SAL_CALL ORichTextModel::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ bool bModified = false;
+
+ if ( isRegisteredProperty( _nHandle ) )
+ {
+ bModified = OPropertyContainerHelper::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ }
+ else if ( isFontRelatedProperty( _nHandle ) )
+ {
+ bModified = FontControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ }
+ else
+ {
+ bModified = OControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+ }
+
+ return bModified;
+ }
+
+
+ void SAL_CALL ORichTextModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ if ( isRegisteredProperty( _nHandle ) )
+ {
+ OPropertyContainerHelper::setFastPropertyValue( _nHandle, _rValue );
+
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_REFERENCE_DEVICE:
+ {
+ #if OSL_DEBUG_LEVEL > 0
+ MapMode aOldMapMode = m_pEngine->GetRefDevice()->GetMapMode();
+ #endif
+
+ OutputDevice* pRefDevice = VCLUnoHelper::GetOutputDevice( m_xReferenceDevice );
+ OSL_ENSURE( pRefDevice, "ORichTextModel::setFastPropertyValue_NoBroadcast: empty reference device?" );
+ m_pEngine->SetRefDevice( pRefDevice );
+
+ #if OSL_DEBUG_LEVEL > 0
+ MapMode aNewMapMode = m_pEngine->GetRefDevice()->GetMapMode();
+ OSL_ENSURE( aNewMapMode.GetMapUnit() == aOldMapMode.GetMapUnit(),
+ "ORichTextModel::setFastPropertyValue_NoBroadcast: You should not tamper with the MapUnit of the ref device!" );
+ // if this assertion here is triggered, then we would need to adjust all
+ // items in all text portions in all paragraphs in the attributes of the EditEngine,
+ // as long as they are MapUnit-dependent. This holds at least for the FontSize.
+ #endif
+ }
+ break;
+
+ case PROPERTY_ID_TEXT:
+ {
+ MutexRelease aReleaseMutex( m_aMutex );
+ impl_smlock_setEngineText( m_sLastKnownEngineText );
+ }
+ break;
+ } // switch ( _nHandle )
+ }
+ else if ( isFontRelatedProperty( _nHandle ) )
+ {
+ FontControlModel::setFastPropertyValue_NoBroadcast_impl(
+ *this, &ORichTextModel::setDependentFastPropertyValue,
+ _nHandle, _rValue);
+ }
+ else
+ {
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_WRITING_MODE:
+ {
+ // forward to our aggregate, so the EditEngine knows about it
+ if ( m_xAggregateSet.is() )
+ m_xAggregateSet->setPropertyValue( "WritingMode", _rValue );
+ }
+ break;
+
+ default:
+ OControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ break;
+ }
+ }
+ }
+
+ // note tdf#152974, we can't simply change a default here because properties
+ // that match the default are not exported, so for compatibility these
+ // can't be changed without some sort of solution for that
+ Any ORichTextModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aDefault;
+
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_WRITING_MODE:
+ case PROPERTY_ID_CONTEXT_WRITING_MODE:
+ aDefault <<= WritingMode2::CONTEXT;
+ break;
+
+ case PROPERTY_ID_LINEEND_FORMAT:
+ aDefault <<= sal_Int16(LineEndFormat::LINE_FEED);
+ break;
+
+ case PROPERTY_ID_ECHO_CHAR:
+ case PROPERTY_ID_ALIGN:
+ case PROPERTY_ID_MAXTEXTLEN:
+ aDefault <<= sal_Int16(0);
+ break;
+
+ case PROPERTY_ID_TABSTOP:
+ case PROPERTY_ID_BACKGROUNDCOLOR:
+ case PROPERTY_ID_BORDERCOLOR:
+ case PROPERTY_ID_VERTICAL_ALIGN:
+ /* void */
+ break;
+
+ case PROPERTY_ID_ENABLED:
+ case PROPERTY_ID_ENABLEVISIBLE:
+ case PROPERTY_ID_PRINTABLE:
+ case PROPERTY_ID_HIDEINACTIVESELECTION:
+ aDefault <<= true;
+ break;
+
+ case PROPERTY_ID_HARDLINEBREAKS:
+ case PROPERTY_ID_HSCROLL:
+ case PROPERTY_ID_VSCROLL:
+ case PROPERTY_ID_READONLY:
+ case PROPERTY_ID_MULTILINE:
+ case PROPERTY_ID_RICH_TEXT:
+ aDefault <<= false;
+ break;
+
+ case PROPERTY_ID_DEFAULTCONTROL:
+ aDefault <<= FRM_SUN_CONTROL_RICHTEXTCONTROL;
+ break;
+
+ case PROPERTY_ID_HELPTEXT:
+ case PROPERTY_ID_HELPURL:
+ case PROPERTY_ID_TEXT:
+ aDefault <<= OUString();
+ break;
+
+ case PROPERTY_ID_BORDER:
+ aDefault <<= sal_Int16(1);
+ break;
+
+ default:
+ if ( isFontRelatedProperty( _nHandle ) )
+ aDefault = FontControlModel::getPropertyDefaultByHandle( _nHandle );
+ else
+ aDefault = OControlModel::getPropertyDefaultByHandle( _nHandle );
+ }
+
+ return aDefault;
+ }
+
+
+ void ORichTextModel::impl_smlock_setEngineText( const OUString& _rText )
+ {
+ if (m_pEngine)
+ {
+ SolarMutexGuard aSolarGuard;
+ m_bSettingEngineText = true;
+ m_pEngine->SetText( _rText );
+ m_bSettingEngineText = false;
+ }
+ }
+
+
+ OUString SAL_CALL ORichTextModel::getServiceName()
+ {
+ return FRM_SUN_COMPONENT_RICHTEXTCONTROL;
+ }
+
+
+ RichTextEngine* ORichTextModel::getEditEngine( const Reference< XControlModel >& _rxModel )
+ {
+ RichTextEngine* pEngine = nullptr;
+
+ Reference< XUnoTunnel > xTunnel( _rxModel, UNO_QUERY );
+ OSL_ENSURE( xTunnel.is(), "ORichTextModel::getEditEngine: invalid model!" );
+ if ( xTunnel.is() )
+ {
+ try
+ {
+ pEngine = comphelper::getSomething_cast<RichTextEngine>(xTunnel->getSomething(getUnoTunnelId()));
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.richtext", "ORichTextModel::getEditEngine" );
+ }
+ }
+ return pEngine;
+ }
+
+
+ const Sequence<sal_Int8> & ORichTextModel::getUnoTunnelId()
+ {
+ static const comphelper::UnoIdInit aId;
+ return aId.getSeq();
+ }
+
+
+ IMPL_LINK_NOARG( ORichTextModel, OnEngineContentModified, LinkParamNone*, void )
+ {
+ if ( !m_bSettingEngineText )
+ {
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) );
+
+ potentialTextChange();
+ // is this a good idea? It may become expensive in case of larger texts,
+ // and this method here is called for every single changed character ...
+ // On the other hand, the API *requires* us to notify changes in the "Text"
+ // property immediately ...
+ }
+ }
+
+
+ sal_Int64 SAL_CALL ORichTextModel::getSomething( const Sequence< sal_Int8 >& _rId )
+ {
+ if (comphelper::isUnoTunnelId<ORichTextModel>(_rId))
+ return comphelper::getSomething_cast(m_pEngine.get()); // Note returning a different type
+
+ Reference< XUnoTunnel > xAggTunnel;
+ if ( query_aggregation( m_xAggregate, xAggTunnel ) )
+ return xAggTunnel->getSomething( _rId );
+
+ return 0;
+ }
+
+
+ void SAL_CALL ORichTextModel::addModifyListener( const Reference< XModifyListener >& _rxListener )
+ {
+ m_aModifyListeners.addInterface( _rxListener );
+ }
+
+
+ void SAL_CALL ORichTextModel::removeModifyListener( const Reference< XModifyListener >& _rxListener )
+ {
+ m_aModifyListeners.removeInterface( _rxListener );
+ }
+
+
+ void ORichTextModel::potentialTextChange( )
+ {
+ OUString sCurrentEngineText;
+ if (m_pEngine)
+ sCurrentEngineText = m_pEngine->GetText();
+
+ if ( sCurrentEngineText != m_sLastKnownEngineText )
+ {
+ sal_Int32 nHandle = PROPERTY_ID_TEXT;
+ Any aOldValue; aOldValue <<= m_sLastKnownEngineText;
+ Any aNewValue; aNewValue <<= sCurrentEngineText;
+ fire( &nHandle, &aNewValue, &aOldValue, 1, false );
+
+ m_sLastKnownEngineText = sCurrentEngineText;
+ }
+ }
+
+
+} // namespace frm
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_ORichTextModel_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ORichTextModel(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextmodel.hxx b/forms/source/richtext/richtextmodel.hxx
new file mode 100644
index 0000000000..cf0bae7db4
--- /dev/null
+++ b/forms/source/richtext/richtextmodel.hxx
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <FormComponent.hxx>
+#include <formcontrolfont.hxx>
+#include "richtextunowrapper.hxx"
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propertycontainerhelper.hxx>
+
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <cppuhelper/implbase3.hxx>
+#include <tools/link.hxx>
+#include <memory>
+
+class EditEngine;
+
+namespace frm
+{
+
+
+ class RichTextEngine;
+
+ //= ORichTextModel
+
+ typedef ::cppu::ImplHelper3 < css::awt::XControlModel
+ , css::lang::XUnoTunnel
+ , css::util::XModifyBroadcaster
+ > ORichTextModel_BASE;
+
+ class ORichTextModel
+ :public OControlModel
+ ,public FontControlModel
+ ,public IEngineTextChangeListener
+ ,public ::comphelper::OPropertyContainerHelper
+ ,public ORichTextModel_BASE
+ {
+ public:
+ ORichTextModel(
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ ORichTextModel(
+ const ORichTextModel* _pOriginal,
+ const css::uno::Reference< css::uno::XComponentContext>& _rxFactory
+ );
+ virtual ~ORichTextModel() override;
+
+ private:
+ // <properties>
+ css::uno::Reference< css::awt::XDevice >
+ m_xReferenceDevice;
+ css::uno::Any m_aTabStop;
+ css::uno::Any m_aBackgroundColor;
+ css::uno::Any m_aBorderColor;
+ css::uno::Any m_aVerticalAlignment;
+ OUString m_sDefaultControl;
+ OUString m_sHelpText;
+ OUString m_sHelpURL;
+ OUString m_sLastKnownEngineText;
+ sal_Int16 m_nLineEndFormat;
+ sal_Int16 m_nTextWritingMode;
+ sal_Int16 m_nContextWritingMode;
+ sal_Int16 m_nBorder;
+ bool m_bEnabled;
+ bool m_bEnableVisible;
+ bool m_bHardLineBreaks;
+ bool m_bHScroll;
+ bool m_bVScroll;
+ bool m_bReadonly;
+ bool m_bPrintable;
+ bool m_bReallyActAsRichText; // despite the class name, the RichTextControl later on
+ // will create "ordinary" text peers depending on this property
+ bool m_bHideInactiveSelection;
+ // </properties>
+
+ // <properties_for_awt_edit_compatibility>
+ css::uno::Any m_aAlign;
+ sal_Int16 m_nEchoChar;
+ sal_Int16 m_nMaxTextLength;
+ bool m_bMultiLine;
+ // </properties_for_awt_edit_compatibility>
+
+ ::std::unique_ptr<RichTextEngine>
+ m_pEngine;
+ bool m_bSettingEngineText;
+
+ ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener>
+ m_aModifyListeners;
+
+ public:
+ static RichTextEngine* getEditEngine( const css::uno::Reference< css::awt::XControlModel >& _rxModel );
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS( ORichTextModel, OControlModel )
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual ::css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPersistObject
+ virtual OUString SAL_CALL getServiceName() override;
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+
+ // XCloneable
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override;
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+ static const css::uno::Sequence<sal_Int8> & getUnoTunnelId();
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ // XPropertySet and friends
+ 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;
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const 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;
+
+ // prevent method hiding
+ using OControlModel::disposing;
+ using OControlModel::getFastPropertyValue;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // IEngineTextChangeListener
+ virtual void potentialTextChange( ) override;
+
+ private:
+ void implInit();
+ void implDoAggregation();
+ void implRegisterProperties();
+
+ /** propagates a new text to the EditEngine
+
+ This method needs to lock the global solar mutex, so our own mutex must not
+ be locked when calling.
+
+ @precond
+ our mutex is not locked
+ */
+ void impl_smlock_setEngineText( const OUString& _rText );
+
+ DECL_LINK( OnEngineContentModified, LinkParamNone*, void );
+
+ private:
+ ORichTextModel( const ORichTextModel& ) = delete;
+ ORichTextModel& operator=( const ORichTextModel& ) = delete;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextunowrapper.cxx b/forms/source/richtext/richtextunowrapper.cxx
new file mode 100644
index 0000000000..f75b9a7943
--- /dev/null
+++ b/forms/source/richtext/richtextunowrapper.cxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "richtextunowrapper.hxx"
+
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <editeng/unofored.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/unoipset.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/svdobj.hxx>
+#include <editeng/unoprnms.hxx>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::container;
+
+
+ namespace
+ {
+ const SvxItemPropertySet* getTextEnginePropertySet()
+ {
+ // property map for an outliner text
+ static const SfxItemPropertyMapEntry aTextEnginePropertyMap[] =
+ {
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_FONT_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ { u"TextUserDefinedAttributes"_ustr, EE_CHAR_XMLATTRIBS, cppu::UnoType<XNameContainer>::get(), 0, 0 },
+ { u"ParaUserDefinedAttributes"_ustr, EE_PARA_XMLATTRIBS, cppu::UnoType<XNameContainer>::get(), 0, 0 },
+ };
+ static SvxItemPropertySet aTextEnginePropertySet( aTextEnginePropertyMap, SdrObject::GetGlobalDrawObjectItemPool() );
+ return &aTextEnginePropertySet;
+ }
+ }
+
+ ORichTextUnoWrapper::ORichTextUnoWrapper( EditEngine& _rEngine, IEngineTextChangeListener* _pTextChangeListener )
+ :SvxUnoText( getTextEnginePropertySet() )
+ {
+ SetEditSource( new RichTextEditSource( _rEngine, _pTextChangeListener ) );
+ }
+
+
+ ORichTextUnoWrapper::~ORichTextUnoWrapper() noexcept
+ {
+ }
+
+ RichTextEditSource::RichTextEditSource( EditEngine& _rEngine, IEngineTextChangeListener* _pTextChangeListener )
+ :m_rEngine ( _rEngine )
+ ,m_pTextForwarder ( new SvxEditEngineForwarder( _rEngine ) )
+ ,m_pTextChangeListener ( _pTextChangeListener )
+ {
+ }
+
+
+ RichTextEditSource::~RichTextEditSource()
+ {
+ }
+
+
+ std::unique_ptr<SvxEditSource> RichTextEditSource::Clone() const
+ {
+ return std::unique_ptr<SvxEditSource>(new RichTextEditSource( m_rEngine, m_pTextChangeListener ));
+ }
+
+
+ SvxTextForwarder* RichTextEditSource::GetTextForwarder()
+ {
+ return m_pTextForwarder.get();
+ }
+
+
+ void RichTextEditSource::UpdateData()
+ {
+ // this means that the content of the EditEngine changed via the UNO API
+ // to reflect this in the views, we need to update them
+ sal_uInt16 viewCount = m_rEngine.GetViewCount();
+ for ( sal_uInt16 view = 0; view < viewCount; ++view )
+ {
+ EditView* pView = m_rEngine.GetView( view );
+ if ( pView )
+ pView->ForceLayoutCalculation();
+ }
+
+ if ( m_pTextChangeListener )
+ m_pTextChangeListener->potentialTextChange();
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextunowrapper.hxx b/forms/source/richtext/richtextunowrapper.hxx
new file mode 100644
index 0000000000..0acb3c53d3
--- /dev/null
+++ b/forms/source/richtext/richtextunowrapper.hxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <editeng/unotext.hxx>
+#include <editeng/unoedsrc.hxx>
+
+
+namespace frm
+{
+
+ class IEngineTextChangeListener
+ {
+ public:
+ virtual void potentialTextChange( ) = 0;
+
+ protected:
+ ~IEngineTextChangeListener() {}
+ };
+
+ class ORichTextUnoWrapper : public SvxUnoText
+ {
+ public:
+ ORichTextUnoWrapper( EditEngine& _rEngine, IEngineTextChangeListener* _pTextChangeListener );
+
+ protected:
+ virtual ~ORichTextUnoWrapper() noexcept override;
+
+
+ private:
+ ORichTextUnoWrapper( const ORichTextUnoWrapper& ) = delete;
+ ORichTextUnoWrapper& operator=( const ORichTextUnoWrapper& ) = delete;
+ };
+
+ class RichTextEditSource : public SvxEditSource
+ {
+ private:
+ EditEngine& m_rEngine;
+ std::unique_ptr<SvxTextForwarder>
+ m_pTextForwarder;
+ IEngineTextChangeListener* m_pTextChangeListener;
+
+ public:
+ RichTextEditSource( EditEngine& _rEngine, IEngineTextChangeListener* _pTextChangeListener );
+
+ // SvxEditSource
+ virtual std::unique_ptr<SvxEditSource> Clone() const override;
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual void UpdateData() override;
+
+ protected:
+ virtual ~RichTextEditSource() override;
+
+ private:
+ RichTextEditSource( const RichTextEditSource& _rSource ) = delete;
+ RichTextEditSource& operator=( const RichTextEditSource& ) = delete;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextvclcontrol.cxx b/forms/source/richtext/richtextvclcontrol.cxx
new file mode 100644
index 0000000000..e4f1f0d1f7
--- /dev/null
+++ b/forms/source/richtext/richtextvclcontrol.cxx
@@ -0,0 +1,351 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "richtextvclcontrol.hxx"
+#include "richtextimplcontrol.hxx"
+#include <svl/itemset.hxx>
+#if OSL_DEBUG_LEVEL > 0
+ #include <unotools/ucbstreamhelper.hxx>
+ #include <sfx2/filedlghelper.hxx>
+ #include <tools/urlobj.hxx>
+ #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#endif
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editids.hrc>
+#include <svx/svxids.hrc>
+#include <osl/diagnose.h>
+#include <vcl/event.hxx>
+
+namespace frm
+{
+
+ RichTextControl::RichTextControl( RichTextEngine* _pEngine, vcl::Window* _pParent, WinBits _nStyle,
+ ITextAttributeListener* _pTextAttribListener, ITextSelectionListener* _pSelectionListener )
+ :Control( _pParent, implInitStyle( _nStyle ) )
+ {
+ implInit( _pEngine, _pTextAttribListener, _pSelectionListener );
+ }
+
+ void RichTextControl::implInit( RichTextEngine* _pEngine, ITextAttributeListener* _pTextAttribListener, ITextSelectionListener* _pSelectionListener )
+ {
+ m_pImpl.reset( new RichTextControlImpl( this, _pEngine, _pTextAttribListener, _pSelectionListener ) );
+ SetCompoundControl( true );
+ }
+
+ RichTextControl::~RichTextControl( )
+ {
+ disposeOnce();
+ }
+
+ void RichTextControl::dispose()
+ {
+ m_pImpl.reset();
+ Control::dispose();
+ }
+
+
+ AttributeState RichTextControl::getState( AttributeId _nAttributeId ) const
+ {
+ return m_pImpl->getAttributeState( _nAttributeId );
+ }
+
+
+ void RichTextControl::executeAttribute( AttributeId _nAttributeId, const SfxPoolItem* _pArgument )
+ {
+ SfxItemSet aToApplyAttributes( getView().GetEmptyItemSet() );
+ if ( !m_pImpl->executeAttribute( getView().GetAttribs(), aToApplyAttributes, _nAttributeId, _pArgument, m_pImpl->getSelectedScriptType() ) )
+ {
+ OSL_FAIL( "RichTextControl::executeAttribute: cannot handle the given attribute!" );
+ return;
+ }
+
+ applyAttributes( aToApplyAttributes );
+ }
+
+
+ void RichTextControl::applyAttributes( const SfxItemSet& _rAttributesToApply )
+ {
+ // apply
+ if ( HasChildPathFocus() )
+ getView().HideCursor();
+
+ // TODO: guard?
+ bool bOldUpdateMode = getEngine().SetUpdateLayout( false );
+
+ getView().SetAttribs( _rAttributesToApply );
+
+ getEngine().SetUpdateLayout( bOldUpdateMode );
+ getView().Invalidate();
+
+ if ( HasChildPathFocus() )
+ getView().ShowCursor();
+
+ m_pImpl->updateAllAttributes();
+ // TODO: maybe we should have a list of attributes which need to be updated
+ // (the handler for the just executed attribute should know)
+ }
+
+
+ void RichTextControl::enableAttributeNotification( AttributeId _nAttributeId, ITextAttributeListener* _pListener )
+ {
+ m_pImpl->enableAttributeNotification( _nAttributeId, _pListener );
+ }
+
+
+ void RichTextControl::disableAttributeNotification( AttributeId _nAttributeId )
+ {
+ m_pImpl->disableAttributeNotification( _nAttributeId );
+ }
+
+
+ bool RichTextControl::isMappableSlot( SfxSlotId _nSlotId )
+ {
+ switch ( _nSlotId )
+ {
+ case SID_ATTR_PARA_ADJUST_LEFT:
+ case SID_ATTR_PARA_ADJUST_CENTER:
+ case SID_ATTR_PARA_ADJUST_RIGHT:
+ case SID_ATTR_PARA_ADJUST_BLOCK:
+ case SID_SET_SUPER_SCRIPT:
+ case SID_SET_SUB_SCRIPT:
+ case SID_ATTR_PARA_LINESPACE_10:
+ case SID_ATTR_PARA_LINESPACE_15:
+ case SID_ATTR_PARA_LINESPACE_20:
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ case SID_TEXTDIRECTION_TOP_TO_BOTTOM:
+ case SID_TEXTDIRECTION_LEFT_TO_RIGHT:
+ case SID_ATTR_CHAR_LATIN_FONT:
+ case SID_ATTR_CHAR_LATIN_FONTHEIGHT:
+ case SID_ATTR_CHAR_LATIN_LANGUAGE:
+ case SID_ATTR_CHAR_LATIN_POSTURE:
+ case SID_ATTR_CHAR_LATIN_WEIGHT:
+ return true;
+ }
+ return false;
+ }
+
+
+ void RichTextControl::Resize()
+ {
+ m_pImpl->layoutWindow();
+ Invalidate();
+ }
+
+
+ void RichTextControl::GetFocus()
+ {
+ m_pImpl->getViewport( RichTextControlImpl::GrantAccess() )->GrabFocus();
+ }
+
+
+ WinBits RichTextControl::implInitStyle( WinBits nStyle )
+ {
+ if ( !( nStyle & WB_NOTABSTOP ) )
+ nStyle |= WB_TABSTOP;
+ return nStyle;
+ }
+
+
+ void RichTextControl::StateChanged( StateChangedType _nStateChange )
+ {
+ if ( _nStateChange == StateChangedType::Style )
+ {
+ SetStyle( implInitStyle( GetStyle() ) );
+ m_pImpl->notifyStyleChanged();
+ }
+ else if ( _nStateChange == StateChangedType::Zoom )
+ {
+ m_pImpl->notifyZoomChanged();
+ }
+ else if ( _nStateChange == StateChangedType::InitShow )
+ {
+ m_pImpl->notifyInitShow();
+ }
+ Control::StateChanged( _nStateChange );
+ }
+
+
+ bool RichTextControl::PreNotify( NotifyEvent& _rNEvt )
+ {
+ if ( IsWindowOrChild( _rNEvt.GetWindow() ) )
+ {
+ if ( NotifyEventType::KEYINPUT == _rNEvt.GetType() )
+ {
+ const ::KeyEvent* pKeyEvent = _rNEvt.GetKeyEvent();
+
+ sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
+ bool bShift = pKeyEvent->GetKeyCode().IsShift();
+ bool bCtrl = pKeyEvent->GetKeyCode().IsMod1();
+ bool bAlt = pKeyEvent->GetKeyCode().IsMod2();
+ if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
+ {
+ // Ctrl-Tab is used to step out of the control
+ // -> build a new key event without the Ctrl-key, and let the very base class handle it
+ vcl::KeyCode aNewCode( KEY_TAB, bShift, false, false, false );
+ ::KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
+ Control::KeyInput( aNewEvent );
+ return true; // handled
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ if ( ( ( KEY_F12 == nCode )
+ || ( KEY_F11 == nCode )
+ )
+ && bCtrl
+ && bAlt
+ )
+ {
+ bool bLoad = KEY_F11 == nCode;
+ static struct
+ {
+ const char* pDescription;
+ const char* pExtension;
+ EETextFormat eFormat;
+ } const aExportFormats[] =
+ {
+ { "OASIS OpenDocument (*.xml)", "*.xml", EETextFormat::Xml },
+ { "HyperText Markup Language (*.html)", "*.html", EETextFormat::Html },
+ { "Rich Text format (*.rtf)", "*.rtf", EETextFormat::Rtf },
+ { "Text (*.txt)", "*.txt", EETextFormat::Text }
+ };
+
+ ::sfx2::FileDialogHelper aFP( bLoad ? css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE : css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, GetFrameWeld() );
+
+ for (auto & aExportFormat : aExportFormats)
+ {
+ aFP.AddFilter(
+ OUString::createFromAscii( aExportFormat.pDescription ),
+ OUString::createFromAscii( aExportFormat.pExtension ) );
+ }
+ ErrCode nResult = aFP.Execute();
+ if ( nResult == ERRCODE_NONE )
+ {
+ OUString sFileName = aFP.GetPath();
+ std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream(
+ sFileName, ( bLoad ? StreamMode::READ : StreamMode::WRITE | StreamMode::TRUNC ) | StreamMode::SHARE_DENYALL
+ );
+ if ( pStream )
+ {
+ EETextFormat eFormat = EETextFormat::Xml;
+ OUString sFilter = aFP.GetCurrentFilter();
+ for (auto & aExportFormat : aExportFormats)
+ {
+ if ( sFilter.equalsAscii( aExportFormat.pDescription ) )
+ {
+ eFormat = aExportFormat.eFormat;
+ break;
+ }
+ }
+ if ( bLoad )
+ {
+ INetURLObject aURL( sFileName );
+ aURL.removeSegment();
+ getEngine().Read( *pStream, aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), eFormat );
+ }
+ else
+ {
+ getEngine().Write( *pStream, eFormat );
+ }
+ }
+ }
+ return true; // handled
+ }
+#endif
+ }
+ }
+ return Control::PreNotify( _rNEvt );
+ }
+
+
+ bool RichTextControl::EventNotify( NotifyEvent& _rNEvt )
+ {
+ bool bDone = false;
+ if ( _rNEvt.GetType() == NotifyEventType::COMMAND )
+ {
+ const CommandEvent& rEvent = *_rNEvt.GetCommandEvent();
+ bDone = m_pImpl->HandleCommand( rEvent );
+ }
+ return bDone || Control::EventNotify(_rNEvt);
+ }
+
+ void RichTextControl::Draw( OutputDevice* _pDev, const Point& _rPos, SystemTextColorFlags /*_nFlags*/ )
+ {
+ m_pImpl->Draw( _pDev, _rPos, _pDev->PixelToLogic(GetSizePixel()) );
+ }
+
+ EditView& RichTextControl::getView()
+ {
+ return *m_pImpl->getView( RichTextControlImpl::GrantAccess() );
+ }
+
+
+ const EditView& RichTextControl::getView() const
+ {
+ return *m_pImpl->getView( RichTextControlImpl::GrantAccess() );
+ }
+
+
+ EditEngine& RichTextControl::getEngine() const
+ {
+ return *m_pImpl->getEngine( RichTextControlImpl::GrantAccess() );
+ }
+
+
+ void RichTextControl::SetReadOnly( bool _bReadOnly )
+ {
+ m_pImpl->SetReadOnly( _bReadOnly );
+ }
+
+
+ bool RichTextControl::IsReadOnly() const
+ {
+ return m_pImpl->IsReadOnly();
+ }
+
+
+ void RichTextControl::SetBackgroundColor( )
+ {
+ m_pImpl->SetBackgroundColor( );
+ }
+
+
+ void RichTextControl::SetBackgroundColor( const Color& _rColor )
+ {
+ m_pImpl->SetBackgroundColor( _rColor );
+ }
+
+
+ void RichTextControl::SetHideInactiveSelection( bool _bHide )
+ {
+ m_pImpl->SetHideInactiveSelection( _bHide );
+ }
+
+
+ bool RichTextControl::GetHideInactiveSelection() const
+ {
+ return m_pImpl->GetHideInactiveSelection( );
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextvclcontrol.hxx b/forms/source/richtext/richtextvclcontrol.hxx
new file mode 100644
index 0000000000..92d0c047d7
--- /dev/null
+++ b/forms/source/richtext/richtextvclcontrol.hxx
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/ctrl.hxx>
+#include "rtattributes.hxx"
+#include "textattributelistener.hxx"
+#include <memory>
+
+class EditView;
+class EditEngine;
+class SfxItemSet;
+
+namespace frm
+{
+
+ class RichTextControlImpl;
+ class RichTextEngine;
+
+ class RichTextControl : public Control, public IMultiAttributeDispatcher
+ {
+ private:
+ std::unique_ptr<RichTextControlImpl> m_pImpl;
+
+ public:
+ RichTextControl(
+ RichTextEngine* _pEngine,
+ vcl::Window* _pParent,
+ WinBits _nStyle,
+ ITextAttributeListener* _pTextAttribListener,
+ ITextSelectionListener* _pSelectionListener
+ );
+
+ virtual ~RichTextControl( ) override;
+ virtual void dispose() override;
+
+ /* enables the change notifications for a particular attribute
+
+ If you want to be notified of any changes in the state of an attribute, you need to call enableAttributeNotification.
+
+ If you provide a dedicated listener for this attribute, this listener is called for every change in the state of
+ the attribute.
+
+ No matter whether you provide such a dedicated listener, the "global" listener which you specified
+ in the constructor of the control is also called for all changes in the attribute state.
+
+ If you previously already enabled the notification for this attribute, and specified a different listener,
+ then the previous listener will be replaced with the new listener, provided the latter is not <NULL/>.
+ */
+ void enableAttributeNotification( AttributeId _nAttributeId, ITextAttributeListener* _pListener );
+
+ /** disables the change notifications for a particular attribute
+
+ If there was a listener dedicated to this attribute, it will not be referenced and used anymore
+ after this method had been called
+ */
+ void disableAttributeNotification( AttributeId _nAttributeId );
+
+ /** determines whether a given slot can be mapped to an aspect of an attribute of the EditEngine
+
+ E.g. SID_ATTR_PARA_ADJUST_LEFT can, though it's not part of the EditEngine pool, be mapped
+ to the SID_ATTR_PARA_ADJUST slot, which in fact *is* usable with the EditEngine.
+ */
+ static bool isMappableSlot( SfxSlotId _nSlotId );
+
+ // IMultiAttributeDispatcher
+ virtual AttributeState getState( AttributeId _nAttributeId ) const override;
+ virtual void executeAttribute( AttributeId _nAttributeId, const SfxPoolItem* _pArgument ) override;
+
+ void SetBackgroundColor( );
+ void SetBackgroundColor( const Color& _rColor );
+
+ void SetReadOnly( bool _bReadOnly );
+ bool IsReadOnly() const;
+
+ void SetHideInactiveSelection( bool _bHide );
+ bool GetHideInactiveSelection() const;
+
+ const EditView& getView() const;
+ EditView& getView();
+
+ // Window overridables
+ virtual void Draw( OutputDevice* _pDev, const Point& _rPos, SystemTextColorFlags _nFlags ) override;
+
+ protected:
+ // Window overridables
+ virtual void Resize() override;
+ virtual void GetFocus() override;
+ virtual void StateChanged( StateChangedType nStateChange ) override;
+ virtual bool PreNotify( NotifyEvent& _rNEvt ) override;
+ virtual bool EventNotify( NotifyEvent& _rNEvt ) override;
+
+ private:
+ void applyAttributes( const SfxItemSet& _rAttributesToApply );
+ void implInit( RichTextEngine* _pEngine, ITextAttributeListener* _pTextAttribListener, ITextSelectionListener* _pSelectionListener );
+ static WinBits implInitStyle( WinBits nStyle );
+
+ private:
+ EditEngine& getEngine() const;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextviewport.cxx b/forms/source/richtext/richtextviewport.cxx
new file mode 100644
index 0000000000..b4fda6652c
--- /dev/null
+++ b/forms/source/richtext/richtextviewport.cxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "richtextviewport.hxx"
+#include <editeng/editview.hxx>
+
+namespace frm
+{
+ RichTextViewPort::RichTextViewPort( vcl::Window* _pParent )
+ : Control ( _pParent )
+ , m_pView(nullptr)
+ , m_bHideInactiveSelection( true )
+ {
+ }
+
+ void RichTextViewPort::setView( EditView& _rView )
+ {
+ m_pView = &_rView;
+ SetPointer( _rView.GetPointer() );
+ }
+
+ void RichTextViewPort::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& _rRect )
+ {
+ m_pView->Paint(_rRect, &rRenderContext);
+ }
+
+ void RichTextViewPort::GetFocus()
+ {
+ Control::GetFocus();
+ if (m_pView)
+ {
+ m_pView->SetSelectionMode( EESelectionMode::Std );
+ m_pView->ShowCursor();
+ }
+ }
+
+ void RichTextViewPort::LoseFocus()
+ {
+ if (m_pView)
+ {
+ m_pView->HideCursor();
+ m_pView->SetSelectionMode( m_bHideInactiveSelection ? EESelectionMode::Hidden : EESelectionMode::Std );
+ }
+ Control::LoseFocus();
+ }
+
+ void RichTextViewPort::KeyInput( const KeyEvent& _rKEvt )
+ {
+ if ( !m_pView->PostKeyEvent( _rKEvt ) )
+ Control::KeyInput( _rKEvt );
+ else
+ implInvalidateAttributes();
+ }
+
+ void RichTextViewPort::MouseMove( const MouseEvent& _rMEvt )
+ {
+ Control::MouseMove( _rMEvt );
+ m_pView->MouseMove( _rMEvt );
+ }
+
+ void RichTextViewPort::MouseButtonDown( const MouseEvent& _rMEvt )
+ {
+ Control::MouseButtonDown( _rMEvt );
+ m_pView->MouseButtonDown( _rMEvt );
+ GrabFocus();
+ }
+
+ void RichTextViewPort::MouseButtonUp( const MouseEvent& _rMEvt )
+ {
+ Control::MouseButtonUp( _rMEvt );
+ m_pView->MouseButtonUp( _rMEvt );
+ implInvalidateAttributes();
+ }
+
+ void RichTextViewPort::SetHideInactiveSelection( bool _bHide )
+ {
+ if ( m_bHideInactiveSelection == _bHide )
+ return;
+ m_bHideInactiveSelection = _bHide;
+ if ( !HasFocus() )
+ m_pView->SetSelectionMode( m_bHideInactiveSelection ? EESelectionMode::Hidden : EESelectionMode::Std );
+ }
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/richtextviewport.hxx b/forms/source/richtext/richtextviewport.hxx
new file mode 100644
index 0000000000..90df8cb0b4
--- /dev/null
+++ b/forms/source/richtext/richtextviewport.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/ctrl.hxx>
+
+class EditView;
+
+namespace frm
+{
+ class RichTextViewPort : public Control
+ {
+ private:
+ EditView* m_pView;
+ Link<LinkParamNone*,void> m_aInvalidationHandler;
+ bool m_bHideInactiveSelection;
+
+ public:
+ explicit RichTextViewPort( vcl::Window* _pParent );
+
+ void setView( EditView& _rView );
+
+ void setAttributeInvalidationHandler( const Link<LinkParamNone*,void>& _rHandler ) { m_aInvalidationHandler = _rHandler; }
+
+ void SetHideInactiveSelection( bool _bHide );
+ bool GetHideInactiveSelection() const { return m_bHideInactiveSelection; }
+
+ protected:
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+ virtual void KeyInput( const KeyEvent& _rKEvt ) override;
+ virtual void MouseMove( const MouseEvent& _rMEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& _rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& _rMEvt ) override;
+
+ private:
+ void implInvalidateAttributes() const
+ {
+ m_aInvalidationHandler.Call( nullptr );
+ }
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/rtattributehandler.cxx b/forms/source/richtext/rtattributehandler.cxx
new file mode 100644
index 0000000000..19ae60dc89
--- /dev/null
+++ b/forms/source/richtext/rtattributehandler.cxx
@@ -0,0 +1,441 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "rtattributehandler.hxx"
+
+#include <osl/diagnose.h>
+#include <svx/svxids.hrc>
+#include <editeng/eeitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
+#include <tools/mapunit.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/outdev.hxx>
+
+#include <editeng/adjustitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/scripttypeitem.hxx>
+
+
+namespace frm
+{
+
+
+ AttributeHandler::AttributeHandler( AttributeId _nAttributeId, WhichId _nWhichId )
+ :m_nAttribute( _nAttributeId )
+ ,m_nWhich ( _nWhichId )
+ {
+ }
+
+
+ AttributeHandler::~AttributeHandler()
+ {
+ }
+
+
+ AttributeId AttributeHandler::getAttributeId( ) const
+ {
+ return getAttribute();
+ }
+
+
+ AttributeCheckState AttributeHandler::implGetCheckState( const SfxPoolItem& /*_rItem*/ ) const
+ {
+ OSL_FAIL( "AttributeHandler::implGetCheckState: not to be called!" );
+ return eIndetermined;
+ }
+
+
+ void AttributeHandler::putItemForScript( SfxItemSet& _rAttribs, const SfxPoolItem& _rItem, SvtScriptType _nForScriptType ) const
+ {
+ SvxScriptSetItem aSetItem( static_cast<WhichId>(getAttributeId()), *_rAttribs.GetPool() );
+ aSetItem.PutItemForScriptType( _nForScriptType, _rItem );
+ _rAttribs.Put( aSetItem.GetItemSet(), false );
+ }
+
+
+ AttributeCheckState AttributeHandler::getCheckState( const SfxItemSet& _rAttribs ) const
+ {
+ AttributeCheckState eSimpleState( eIndetermined );
+ const SfxPoolItem* pItem = _rAttribs.GetItem( getWhich() );
+ if ( pItem )
+ eSimpleState = implGetCheckState( *pItem );
+ return eSimpleState;
+ }
+
+
+ AttributeState AttributeHandler::getState( const SfxItemSet& _rAttribs ) const
+ {
+ AttributeState aState( eIndetermined );
+ aState.eSimpleState = getCheckState( _rAttribs );
+ return aState;
+ }
+
+
+ //= AttributeHandlerFactory
+
+
+ namespace
+ {
+ WhichId lcl_implGetWhich( const SfxItemPool& _rPool, AttributeId _nAttributeId )
+ {
+ WhichId nWhich = 0;
+ switch ( _nAttributeId )
+ {
+ case SID_ATTR_CHAR_LATIN_FONTHEIGHT:nWhich = EE_CHAR_FONTHEIGHT;break;
+ case SID_ATTR_CHAR_LATIN_FONT: nWhich = EE_CHAR_FONTINFO; break;
+ case SID_ATTR_CHAR_LATIN_LANGUAGE: nWhich = EE_CHAR_LANGUAGE; break;
+ case SID_ATTR_CHAR_LATIN_POSTURE: nWhich = EE_CHAR_ITALIC; break;
+ case SID_ATTR_CHAR_LATIN_WEIGHT: nWhich = EE_CHAR_WEIGHT; break;
+
+ default:
+ nWhich = _rPool.GetWhich( static_cast<SfxSlotId>(_nAttributeId) );
+ }
+ return nWhich;
+ }
+ }
+
+ ::rtl::Reference< AttributeHandler > AttributeHandlerFactory::getHandlerFor( AttributeId _nAttributeId, const SfxItemPool& _rEditEnginePool )
+ {
+ ::rtl::Reference< AttributeHandler > pReturn;
+ switch ( _nAttributeId )
+ {
+ case SID_ATTR_PARA_ADJUST_LEFT :
+ case SID_ATTR_PARA_ADJUST_CENTER:
+ case SID_ATTR_PARA_ADJUST_RIGHT :
+ case SID_ATTR_PARA_ADJUST_BLOCK :
+ pReturn = new ParaAlignmentHandler( _nAttributeId );
+ break;
+
+ case SID_ATTR_PARA_LINESPACE_10:
+ case SID_ATTR_PARA_LINESPACE_15:
+ case SID_ATTR_PARA_LINESPACE_20:
+ pReturn = new LineSpacingHandler( _nAttributeId );
+ break;
+
+ case SID_SET_SUPER_SCRIPT:
+ case SID_SET_SUB_SCRIPT:
+ pReturn = new EscapementHandler( _nAttributeId );
+ break;
+
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ case SID_ATTR_CHAR_CTL_FONTHEIGHT:
+ case SID_ATTR_CHAR_CJK_FONTHEIGHT:
+ case SID_ATTR_CHAR_LATIN_FONTHEIGHT:
+ pReturn = new FontSizeHandler( _nAttributeId, lcl_implGetWhich( _rEditEnginePool, _nAttributeId ) );
+ break;
+
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ pReturn = new ParagraphDirectionHandler( _nAttributeId );
+ break;
+
+ case SID_ATTR_PARA_HANGPUNCTUATION:
+ case SID_ATTR_PARA_FORBIDDEN_RULES:
+ case SID_ATTR_PARA_SCRIPTSPACE:
+ pReturn = new BooleanHandler( _nAttributeId, lcl_implGetWhich( _rEditEnginePool, _nAttributeId ) );
+ break;
+
+ default:
+ pReturn = new SlotHandler( static_cast<SfxSlotId>(_nAttributeId), lcl_implGetWhich( _rEditEnginePool, _nAttributeId ) );
+ break;
+
+ }
+
+ return pReturn;
+ }
+
+ ParaAlignmentHandler::ParaAlignmentHandler( AttributeId _nAttributeId )
+ :AttributeHandler( _nAttributeId, EE_PARA_JUST )
+ ,m_eAdjust( SvxAdjust::Center )
+ {
+ switch ( getAttribute() )
+ {
+ case SID_ATTR_PARA_ADJUST_LEFT : m_eAdjust = SvxAdjust::Left; break;
+ case SID_ATTR_PARA_ADJUST_CENTER: m_eAdjust = SvxAdjust::Center; break;
+ case SID_ATTR_PARA_ADJUST_RIGHT : m_eAdjust = SvxAdjust::Right; break;
+ case SID_ATTR_PARA_ADJUST_BLOCK : m_eAdjust = SvxAdjust::Block; break;
+ default:
+ OSL_FAIL( "ParaAlignmentHandler::ParaAlignmentHandler: invalid slot!" );
+ break;
+ }
+ }
+
+
+ AttributeCheckState ParaAlignmentHandler::implGetCheckState( const SfxPoolItem& _rItem ) const
+ {
+ assert( dynamic_cast<const SvxAdjustItem*>( &_rItem) && "ParaAlignmentHandler::implGetCheckState: invalid pool item!" );
+ SvxAdjust eAdjust = static_cast< const SvxAdjustItem& >( _rItem ).GetAdjust();
+ return ( eAdjust == m_eAdjust ) ? eChecked : eUnchecked;
+ }
+
+
+ void ParaAlignmentHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType /*_nForScriptType*/ ) const
+ {
+ OSL_ENSURE( !_pAdditionalArg, "ParaAlignmentHandler::executeAttribute: this is a simple toggle attribute - no args possible!" );
+ _rNewAttribs.Put( SvxAdjustItem( m_eAdjust, getWhich() ) );
+ }
+
+ LineSpacingHandler::LineSpacingHandler( AttributeId _nAttributeId )
+ :AttributeHandler( _nAttributeId, EE_PARA_SBL )
+ ,m_nLineSpace( 100 )
+ {
+ switch ( getAttribute() )
+ {
+ case SID_ATTR_PARA_LINESPACE_10: m_nLineSpace = 100; break;
+ case SID_ATTR_PARA_LINESPACE_15: m_nLineSpace = 150; break;
+ case SID_ATTR_PARA_LINESPACE_20: m_nLineSpace = 200; break;
+ default:
+ OSL_FAIL( "LineSpacingHandler::LineSpacingHandler: invalid slot!" );
+ break;
+ }
+ }
+
+
+ AttributeCheckState LineSpacingHandler::implGetCheckState( const SfxPoolItem& _rItem ) const
+ {
+ assert( dynamic_cast<const SvxLineSpacingItem*>( &_rItem) && "LineSpacingHandler::implGetCheckState: invalid pool item!" );
+ sal_uInt16 nLineSpace = static_cast< const SvxLineSpacingItem& >( _rItem ).GetPropLineSpace();
+ return ( nLineSpace == m_nLineSpace ) ? eChecked : eUnchecked;
+ }
+
+
+ void LineSpacingHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType /*_nForScriptType*/ ) const
+ {
+ OSL_ENSURE( !_pAdditionalArg, "LineSpacingHandler::executeAttribute: this is a simple toggle attribute - no args possible!" );
+
+ SvxLineSpacingItem aLineSpacing( m_nLineSpace, getWhich() );
+ aLineSpacing.SetLineSpaceRule( SvxLineSpaceRule::Auto );
+ if ( 100 == m_nLineSpace )
+ aLineSpacing.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off );
+ else
+ aLineSpacing.SetPropLineSpace( m_nLineSpace );
+
+ _rNewAttribs.Put( aLineSpacing );
+ }
+
+ EscapementHandler::EscapementHandler( AttributeId _nAttributeId )
+ :AttributeHandler( _nAttributeId, EE_CHAR_ESCAPEMENT )
+ ,m_eEscapement( SvxEscapement::Off )
+ {
+ switch ( getAttribute() )
+ {
+ case SID_SET_SUPER_SCRIPT : m_eEscapement = SvxEscapement::Superscript; break;
+ case SID_SET_SUB_SCRIPT : m_eEscapement = SvxEscapement::Subscript; break;
+ default:
+ OSL_FAIL( "EscapementHandler::EscapementHandler: invalid slot!" );
+ break;
+ }
+ }
+
+
+ AttributeCheckState EscapementHandler::implGetCheckState( const SfxPoolItem& _rItem ) const
+ {
+ assert( dynamic_cast<const SvxEscapementItem*>( &_rItem) && "EscapementHandler::getState: invalid pool item!" );
+ SvxEscapement eEscapement = static_cast< const SvxEscapementItem& >( _rItem ).GetEscapement();
+ return ( eEscapement == m_eEscapement ) ? eChecked : eUnchecked;
+ }
+
+
+ void EscapementHandler::executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType /*_nForScriptType*/ ) const {
+ OSL_ENSURE( !_pAdditionalArg, "EscapementHandler::executeAttribute: this is a simple toggle attribute - no args possible!" );
+ // well, in theory we could allow an SvxEscapementItem here, but this is not needed
+
+ bool bIsChecked = getCheckState( _rCurrentAttribs ) == eChecked;
+ _rNewAttribs.Put( SvxEscapementItem( bIsChecked ? SvxEscapement::Off : m_eEscapement, getWhich() ) );
+ }
+
+ SlotHandler::SlotHandler( AttributeId _nAttributeId, WhichId _nWhichId )
+ :AttributeHandler( _nAttributeId, _nWhichId )
+ ,m_bScriptDependent( false )
+ {
+ m_bScriptDependent = ( sal_uInt16(SID_ATTR_CHAR_WEIGHT) == _nAttributeId )
+ || ( sal_uInt16(SID_ATTR_CHAR_POSTURE) == _nAttributeId )
+ || ( sal_uInt16(SID_ATTR_CHAR_FONT) == _nAttributeId );
+ }
+
+
+ AttributeState SlotHandler::getState( const SfxItemSet& _rAttribs ) const
+ {
+ AttributeState aState( eIndetermined );
+
+ const SfxPoolItem* pItem = _rAttribs.GetItem( getWhich() );
+ if ( pItem )
+ aState.setItem( pItem );
+
+ return aState;
+ }
+
+
+ void SlotHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const
+ {
+ if ( _pAdditionalArg )
+ {
+ std::unique_ptr<SfxPoolItem> pCorrectWich(_pAdditionalArg->CloneSetWhich(getWhich()));
+
+ if ( m_bScriptDependent )
+ putItemForScript( _rNewAttribs, *pCorrectWich, _nForScriptType );
+ else
+ _rNewAttribs.Put( std::move(pCorrectWich) );
+ }
+ else
+ OSL_FAIL( "SlotHandler::executeAttribute: need attributes to do something!" );
+ }
+
+ FontSizeHandler::FontSizeHandler( AttributeId _nAttributeId, WhichId _nWhichId )
+ :AttributeHandler( _nAttributeId, _nWhichId )
+ {
+ OSL_ENSURE( ( _nAttributeId == sal_uInt16(SID_ATTR_CHAR_FONTHEIGHT) ) || ( _nAttributeId == sal_uInt16(SID_ATTR_CHAR_CTL_FONTHEIGHT) )
+ || ( _nAttributeId == sal_uInt16(SID_ATTR_CHAR_CJK_FONTHEIGHT) ) || ( _nAttributeId == sal_uInt16(SID_ATTR_CHAR_LATIN_FONTHEIGHT) ),
+ "FontSizeHandler::FontSizeHandler: invalid attribute id!" );
+ }
+
+
+ AttributeState FontSizeHandler::getState( const SfxItemSet& _rAttribs ) const
+ {
+ AttributeState aState( eIndetermined );
+
+ const SfxPoolItem* pItem = _rAttribs.GetItem( getWhich() );
+ const SvxFontHeightItem* pFontHeightItem = dynamic_cast<const SvxFontHeightItem*>( pItem );
+ OSL_ENSURE( pFontHeightItem || !pItem, "FontSizeHandler::getState: invalid item!" );
+ if ( pFontHeightItem )
+ {
+ // by definition, the item should have the unit twip
+ sal_uInt32 nHeight = pFontHeightItem->GetHeight();
+ if ( _rAttribs.GetPool()->GetMetric( getWhich() ) != MapUnit::MapTwip )
+ {
+ nHeight = OutputDevice::LogicToLogic(
+ Size( 0, nHeight ),
+ MapMode( _rAttribs.GetPool()->GetMetric( getWhich() ) ),
+ MapMode( MapUnit::MapTwip )
+ ).Height();
+ }
+
+ SvxFontHeightItem* pNewItem = new SvxFontHeightItem( nHeight, 100, getWhich() );
+ pNewItem->SetProp( pFontHeightItem->GetProp(), pFontHeightItem->GetPropUnit() );
+ aState.setItem( pNewItem );
+ }
+
+ return aState;
+ }
+
+
+ void FontSizeHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const
+ {
+ const SvxFontHeightItem* pFontHeightItem = dynamic_cast<const SvxFontHeightItem*>( _pAdditionalArg );
+ OSL_ENSURE( pFontHeightItem, "FontSizeHandler::executeAttribute: need a FontHeightItem!" );
+
+ if ( !pFontHeightItem )
+ return;
+
+ sal_uInt32 nHeight = pFontHeightItem->GetHeight();
+ if ( _rNewAttribs.GetPool()->GetMetric( getWhich() ) != MapUnit::MapTwip )
+ {
+ nHeight = OutputDevice::LogicToLogic(
+ Size( 0, nHeight ),
+ MapMode( MapUnit::MapTwip ),
+ MapMode( _rNewAttribs.GetPool()->GetMetric( getWhich() ) )
+ ).Height();
+ }
+
+ SvxFontHeightItem aNewItem( nHeight, 100, getWhich() );
+ aNewItem.SetProp( pFontHeightItem->GetProp(), pFontHeightItem->GetPropUnit() );
+
+ if ( ( getAttributeId() == sal_uInt16(SID_ATTR_CHAR_FONTHEIGHT) ) && _nForScriptType != SvtScriptType::NONE)
+ putItemForScript( _rNewAttribs, aNewItem, _nForScriptType );
+ else
+ _rNewAttribs.Put( aNewItem );
+ }
+
+ ParagraphDirectionHandler::ParagraphDirectionHandler( AttributeId _nAttributeId )
+ :AttributeHandler( _nAttributeId, EE_PARA_WRITINGDIR )
+ ,m_eParagraphDirection( SvxFrameDirection::Horizontal_LR_TB )
+ ,m_eDefaultAdjustment( SvxAdjust::Right )
+ ,m_eOppositeDefaultAdjustment( SvxAdjust::Left )
+ {
+ switch ( getAttributeId() )
+ {
+ case SID_ATTR_PARA_LEFT_TO_RIGHT: m_eParagraphDirection = SvxFrameDirection::Horizontal_LR_TB; m_eDefaultAdjustment = SvxAdjust::Left; break;
+ case SID_ATTR_PARA_RIGHT_TO_LEFT: m_eParagraphDirection = SvxFrameDirection::Horizontal_RL_TB; m_eDefaultAdjustment = SvxAdjust::Right; break;
+ default:
+ OSL_FAIL( "ParagraphDirectionHandler::ParagraphDirectionHandler: invalid attribute id!" );
+ }
+
+ if ( SvxAdjust::Right == m_eDefaultAdjustment )
+ m_eOppositeDefaultAdjustment = SvxAdjust::Left;
+ else
+ m_eOppositeDefaultAdjustment = SvxAdjust::Right;
+ }
+
+
+ AttributeCheckState ParagraphDirectionHandler::implGetCheckState( const SfxPoolItem& _rItem ) const
+ {
+ assert( dynamic_cast<const SvxFrameDirectionItem*>( &_rItem) && "ParagraphDirectionHandler::implGetCheckState: invalid pool item!" );
+ SvxFrameDirection eDirection = static_cast< const SvxFrameDirectionItem& >( _rItem ).GetValue();
+ return ( eDirection == m_eParagraphDirection ) ? eChecked : eUnchecked;
+ }
+
+
+ void ParagraphDirectionHandler::executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* /*_pAdditionalArg*/, SvtScriptType /*_nForScriptType*/ ) const
+ {
+ _rNewAttribs.Put( SvxFrameDirectionItem( m_eParagraphDirection, getWhich() ) );
+
+ // if the current adjustment of the was the default adjustment for the *previous* text direction,
+ // then we toggle the adjustment, too
+ SvxAdjust eCurrentAdjustment = SvxAdjust::Left;
+ if ( const SvxAdjustItem* pCurrentAdjustment = _rCurrentAttribs.GetItemIfSet( EE_PARA_JUST ) )
+ eCurrentAdjustment = pCurrentAdjustment->GetAdjust();
+
+ if ( eCurrentAdjustment == m_eOppositeDefaultAdjustment )
+ _rNewAttribs.Put( SvxAdjustItem( m_eDefaultAdjustment, EE_PARA_JUST ) );
+ }
+
+ BooleanHandler::BooleanHandler( AttributeId _nAttributeId, WhichId _nWhichId )
+ :AttributeHandler( _nAttributeId, _nWhichId )
+ {
+ }
+
+
+ AttributeCheckState BooleanHandler::implGetCheckState( const SfxPoolItem& _rItem ) const
+ {
+ OSL_ENSURE( dynamic_cast<const SfxBoolItem*>( &_rItem) != nullptr, "BooleanHandler::implGetCheckState: invalid item!" );
+ if ( auto pBoolItem = dynamic_cast<const SfxBoolItem*>( &_rItem) )
+ return pBoolItem->GetValue() ? eChecked : eUnchecked;
+
+ return eIndetermined;
+ }
+
+
+ void BooleanHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType /*_nForScriptType*/ ) const
+ {
+ OSL_ENSURE( dynamic_cast<const SfxBoolItem*>( _pAdditionalArg) != nullptr, "BooleanHandler::executeAttribute: invalid argument!" );
+ if ( _pAdditionalArg )
+ {
+ _rNewAttribs.Put( _pAdditionalArg->CloneSetWhich(getWhich()) );
+ }
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/rtattributehandler.hxx b/forms/source/richtext/rtattributehandler.hxx
new file mode 100644
index 0000000000..0f2badd640
--- /dev/null
+++ b/forms/source/richtext/rtattributehandler.hxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "rtattributes.hxx"
+#include <rtl/ref.hxx>
+#include <svl/languageoptions.hxx>
+#include <editeng/svxenum.hxx>
+#include <editeng/frmdir.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+class SfxItemSet;
+class SfxPoolItem;
+class SfxItemPool;
+
+namespace frm
+{
+
+ class AttributeHandler : public salhelper::SimpleReferenceObject
+ {
+ private:
+ AttributeId m_nAttribute;
+ WhichId m_nWhich;
+
+ protected:
+ AttributeId getAttribute() const { return m_nAttribute; }
+ WhichId getWhich() const { return m_nWhich; }
+
+ public:
+ AttributeHandler( AttributeId _nAttributeId, WhichId _nWhichId );
+
+ AttributeId getAttributeId( ) const;
+ virtual AttributeState getState( const SfxItemSet& _rAttribs ) const;
+ virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const = 0;
+
+ protected:
+ /// helper method calling implGetCheckState
+ AttributeCheckState getCheckState( const SfxItemSet& _rAttribs ) const;
+
+ /// helper method putting an item into a set, respecting a script type
+ void putItemForScript( SfxItemSet& _rAttribs, const SfxPoolItem& _rItem, SvtScriptType _nForScriptType ) const;
+
+ // pseudo-abstract
+ virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const;
+
+ protected:
+ virtual ~AttributeHandler() override;
+ };
+
+ namespace AttributeHandlerFactory
+ {
+ ::rtl::Reference< AttributeHandler > getHandlerFor( AttributeId _nAttributeId, const SfxItemPool& _rEditEnginePool );
+ }
+
+ class ParaAlignmentHandler : public AttributeHandler
+ {
+ private:
+ SvxAdjust m_eAdjust;
+
+ public:
+ explicit ParaAlignmentHandler( AttributeId _nAttributeId );
+
+ public:
+ virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override;
+ virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override;
+ };
+
+ class LineSpacingHandler : public AttributeHandler
+ {
+ private:
+ sal_uInt16 m_nLineSpace;
+
+ public:
+ explicit LineSpacingHandler( AttributeId _nAttributeId );
+
+ public:
+ virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override;
+ virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override;
+ };
+
+ class EscapementHandler : public AttributeHandler
+ {
+ private:
+ SvxEscapement m_eEscapement;
+
+ public:
+ explicit EscapementHandler( AttributeId _nAttributeId );
+
+ public:
+ virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override;
+ virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override;
+ };
+
+ class SlotHandler : public AttributeHandler
+ {
+ private:
+ bool m_bScriptDependent;
+
+ public:
+ SlotHandler( AttributeId _nAttributeId, WhichId _nWhichId );
+
+ public:
+ virtual AttributeState getState( const SfxItemSet& _rAttribs ) const override;
+ virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override;
+ };
+
+ class BooleanHandler : public AttributeHandler
+ {
+ public:
+ BooleanHandler( AttributeId _nAttributeId, WhichId _nWhichId );
+
+ public:
+ virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override;
+ virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override;
+ };
+
+ class FontSizeHandler : public AttributeHandler
+ {
+ public:
+ FontSizeHandler( AttributeId _nAttributeId, WhichId _nWhichId );
+
+ public:
+ virtual AttributeState getState( const SfxItemSet& _rAttribs ) const override;
+ virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override;
+ };
+
+ class ParagraphDirectionHandler : public AttributeHandler
+ {
+ private:
+ SvxFrameDirection m_eParagraphDirection;
+ SvxAdjust m_eDefaultAdjustment;
+ SvxAdjust m_eOppositeDefaultAdjustment;
+
+ public:
+ explicit ParagraphDirectionHandler( AttributeId _nAttributeId );
+
+ public:
+ virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override;
+ virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/rtattributes.hxx b/forms/source/richtext/rtattributes.hxx
new file mode 100644
index 0000000000..fa00add450
--- /dev/null
+++ b/forms/source/richtext/rtattributes.hxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <svl/poolitem.hxx>
+
+namespace frm
+{
+
+ /// the id of an attribute
+ typedef sal_Int32 AttributeId;
+ /// the "which id" of an item in an SfxItemSet
+ typedef sal_uInt16 WhichId;
+ /// a SFX slot id
+ typedef sal_uInt16 SfxSlotId;
+
+ enum AttributeCheckState
+ {
+ eChecked,
+ eUnchecked,
+ eIndetermined
+ };
+
+ struct AttributeState
+ {
+ private:
+ std::unique_ptr<SfxPoolItem> pItemHandleItem;
+
+ public:
+ AttributeCheckState eSimpleState;
+
+ inline AttributeState( );
+ inline explicit AttributeState( AttributeCheckState _eCheckState );
+ inline AttributeState( const AttributeState& _rSource );
+
+ inline AttributeState& operator=( const AttributeState& _rSource );
+
+ inline bool operator==( const AttributeState& _rRHS );
+
+ inline const SfxPoolItem* getItem() const;
+ inline void setItem( const SfxPoolItem* _pItem );
+ };
+
+ inline AttributeState::AttributeState( )
+ :eSimpleState( eIndetermined )
+ {
+ }
+
+ inline AttributeState::AttributeState( AttributeCheckState _eCheckState )
+ :eSimpleState( _eCheckState )
+ {
+ }
+
+ inline AttributeState::AttributeState( const AttributeState& _rSource )
+ :eSimpleState( eIndetermined )
+ {
+ operator=( _rSource );
+ }
+
+ inline AttributeState& AttributeState::operator=( const AttributeState& _rSource )
+ {
+ if ( &_rSource == this )
+ return *this;
+
+ eSimpleState = _rSource.eSimpleState;
+ setItem( _rSource.getItem() );
+ return *this;
+ }
+
+ inline const SfxPoolItem* AttributeState::getItem() const
+ {
+ return pItemHandleItem.get();
+ }
+
+ inline void AttributeState::setItem( const SfxPoolItem* _pItem )
+ {
+ if ( _pItem )
+ pItemHandleItem.reset(_pItem->Clone());
+ else
+ pItemHandleItem.reset();
+ }
+
+ inline bool AttributeState::operator==( const AttributeState& _rRHS )
+ {
+ if ( eSimpleState != _rRHS.eSimpleState )
+ return false;
+
+ if ( pItemHandleItem && !_rRHS.pItemHandleItem )
+ return false;
+
+ if ( !pItemHandleItem && _rRHS.pItemHandleItem )
+ return false;
+
+ if ( pItemHandleItem == _rRHS.pItemHandleItem )
+ return true;
+
+ assert(pItemHandleItem && _rRHS.pItemHandleItem && "otherwise have already returned");
+
+ return *pItemHandleItem == *_rRHS.pItemHandleItem;
+ }
+
+ class IMultiAttributeDispatcher
+ {
+ public:
+ virtual AttributeState getState( AttributeId _nAttributeId ) const = 0;
+ virtual void executeAttribute( AttributeId _nAttributeId, const SfxPoolItem* _pArgument ) = 0;
+
+ protected:
+ ~IMultiAttributeDispatcher() {}
+ };
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/specialdispatchers.cxx b/forms/source/richtext/specialdispatchers.cxx
new file mode 100644
index 0000000000..d75e06590f
--- /dev/null
+++ b/forms/source/richtext/specialdispatchers.cxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "specialdispatchers.hxx"
+#include <editeng/editeng.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/editview.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <osl/diagnose.h>
+
+
+namespace frm
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::beans;
+
+ OSelectAllDispatcher::OSelectAllDispatcher( EditView& _rView, const URL& _rURL )
+ :ORichTextFeatureDispatcher( _rView, _rURL )
+ {
+ }
+
+
+ OSelectAllDispatcher::~OSelectAllDispatcher( )
+ {
+ if ( !isDisposed() )
+ {
+ acquire();
+ dispose();
+ }
+ }
+
+
+ void SAL_CALL OSelectAllDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& /*_rArguments*/ )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "OSelectAllDispatcher::dispatch: invalid URL!" );
+
+ checkDisposed();
+
+ EditEngine* pEngine = getEditView() ? getEditView()->GetEditEngine() : nullptr;
+ OSL_ENSURE( pEngine, "OSelectAllDispatcher::dispatch: no edit engine - but not yet disposed?" );
+ if ( !pEngine )
+ return;
+
+ sal_Int32 nParagraphs = pEngine->GetParagraphCount();
+ if ( nParagraphs )
+ {
+ sal_Int32 nLastParaNumber = nParagraphs - 1;
+ sal_Int32 nParaLen = pEngine->GetTextLen( nLastParaNumber );
+ getEditView()->SetSelection( ESelection( 0, 0, nLastParaNumber, nParaLen ) );
+ }
+ }
+
+
+ FeatureStateEvent OSelectAllDispatcher::buildStatusEvent() const
+ {
+ FeatureStateEvent aEvent( ORichTextFeatureDispatcher::buildStatusEvent() );
+ aEvent.IsEnabled = true;
+ return aEvent;
+ }
+
+ OParagraphDirectionDispatcher::OParagraphDirectionDispatcher( EditView& _rView, AttributeId _nAttributeId, const URL& _rURL,
+ IMultiAttributeDispatcher* _pMasterDispatcher )
+ :OAttributeDispatcher( _rView, _nAttributeId, _rURL, _pMasterDispatcher )
+ {
+ }
+
+
+ FeatureStateEvent OParagraphDirectionDispatcher::buildStatusEvent() const
+ {
+ FeatureStateEvent aEvent( OAttributeDispatcher::buildStatusEvent() );
+
+ EditEngine* pEngine = getEditView() ? getEditView()->GetEditEngine() : nullptr;
+ OSL_ENSURE( pEngine, "OParagraphDirectionDispatcher::dispatch: no edit engine - but not yet disposed?" );
+ if ( pEngine && pEngine->IsEffectivelyVertical() )
+ aEvent.IsEnabled = false;
+
+ return aEvent;
+ }
+
+ OTextDirectionDispatcher::OTextDirectionDispatcher( EditView& _rView, const URL& _rURL )
+ :ORichTextFeatureDispatcher( _rView, _rURL )
+ {
+ }
+
+
+ void SAL_CALL OTextDirectionDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& /*_rArguments*/ )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "OTextDirectionDispatcher::dispatch: invalid URL!" );
+
+ checkDisposed();
+
+ EditEngine* pEngine = getEditView() ? getEditView()->GetEditEngine() : nullptr;
+ OSL_ENSURE( pEngine, "OTextDirectionDispatcher::dispatch: no edit engine - but not yet disposed?" );
+ if ( !pEngine )
+ return;
+
+ pEngine->SetVertical( !pEngine->IsEffectivelyVertical() );
+ }
+
+
+ FeatureStateEvent OTextDirectionDispatcher::buildStatusEvent() const
+ {
+ FeatureStateEvent aEvent( ORichTextFeatureDispatcher::buildStatusEvent() );
+
+ EditEngine* pEngine = getEditView() ? getEditView()->GetEditEngine() : nullptr;
+ OSL_ENSURE( pEngine, "OTextDirectionDispatcher::dispatch: no edit engine - but not yet disposed?" );
+
+ aEvent.IsEnabled = true;
+ aEvent.State <<= pEngine && pEngine->IsEffectivelyVertical();
+
+ return aEvent;
+ }
+
+ OAsianFontLayoutDispatcher::OAsianFontLayoutDispatcher( EditView& _rView, AttributeId _nAttributeId, const URL& _rURL, IMultiAttributeDispatcher* _pMasterDispatcher )
+ :OParametrizedAttributeDispatcher( _rView, _nAttributeId, _rURL, _pMasterDispatcher )
+ {
+ }
+
+
+ const SfxPoolItem* OAsianFontLayoutDispatcher::convertDispatchArgsToItem( const Sequence< PropertyValue >& _rArguments )
+ {
+ // look for the "Enable" parameter
+ const PropertyValue* pLookup = _rArguments.getConstArray();
+ const PropertyValue* pLookupEnd = _rArguments.getConstArray() + _rArguments.getLength();
+ while ( pLookup != pLookupEnd )
+ {
+ if ( pLookup->Name == "Enable" )
+ break;
+ ++pLookup;
+ }
+ if ( pLookup != pLookupEnd )
+ {
+ bool bEnable = true;
+ OSL_VERIFY( pLookup->Value >>= bEnable );
+ if ( m_nAttributeId == sal_uInt16(SID_ATTR_PARA_SCRIPTSPACE) )
+ return new SvxScriptSpaceItem( bEnable, static_cast<WhichId>(m_nAttributeId) );
+ return new SfxBoolItem( static_cast<WhichId>(m_nAttributeId), bEnable );
+ }
+
+ OSL_FAIL( "OAsianFontLayoutDispatcher::convertDispatchArgsToItem: did not find the one and only argument!" );
+ return nullptr;
+ }
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/specialdispatchers.hxx b/forms/source/richtext/specialdispatchers.hxx
new file mode 100644
index 0000000000..9b4bfa1b79
--- /dev/null
+++ b/forms/source/richtext/specialdispatchers.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "parametrizedattributedispatcher.hxx"
+
+
+namespace frm
+{
+
+ class OSelectAllDispatcher : public ORichTextFeatureDispatcher
+ {
+ public:
+ OSelectAllDispatcher( EditView& _rView, const css::util::URL& _rURL );
+
+ protected:
+ virtual ~OSelectAllDispatcher() override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override;
+
+ // ORichTextFeatureDispatcher
+ virtual css::frame::FeatureStateEvent buildStatusEvent() const override;
+ };
+
+ class OParagraphDirectionDispatcher : public OAttributeDispatcher
+ {
+ public:
+ OParagraphDirectionDispatcher(
+ EditView& _rView,
+ AttributeId _nAttributeId,
+ const css::util::URL& _rURL,
+ IMultiAttributeDispatcher* _pMasterDispatcher
+ );
+
+ protected:
+ // ORichTextFeatureDispatcher
+ virtual css::frame::FeatureStateEvent buildStatusEvent() const override;
+ };
+
+ class OTextDirectionDispatcher : public ORichTextFeatureDispatcher
+ {
+ public:
+ OTextDirectionDispatcher( EditView& _rView, const css::util::URL& _rURL );
+
+ protected:
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override;
+
+ // ORichTextFeatureDispatcher
+ virtual css::frame::FeatureStateEvent buildStatusEvent() const override;
+ };
+
+ class OAsianFontLayoutDispatcher : public OParametrizedAttributeDispatcher
+ {
+ public:
+ OAsianFontLayoutDispatcher(
+ EditView& _rView,
+ AttributeId _nAttributeId,
+ const css::util::URL& _rURL,
+ IMultiAttributeDispatcher* _pMasterDispatcher
+ );
+
+ protected:
+ // OParametrizedAttributeDispatcher
+ virtual const SfxPoolItem* convertDispatchArgsToItem(
+ const css::uno::Sequence< css::beans::PropertyValue >& _rArguments ) override;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/richtext/textattributelistener.hxx b/forms/source/richtext/textattributelistener.hxx
new file mode 100644
index 0000000000..3025d7b5d6
--- /dev/null
+++ b/forms/source/richtext/textattributelistener.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "rtattributes.hxx"
+
+struct ESelection;
+
+namespace frm
+{
+
+ class ITextAttributeListener
+ {
+ public:
+ virtual void onAttributeStateChanged( AttributeId _nAttributeId ) = 0;
+
+ protected:
+ ~ITextAttributeListener() {}
+ };
+
+
+ //= ITextAttributeListener
+
+ class ITextSelectionListener
+ {
+ public:
+ virtual void onSelectionChanged() = 0;
+
+ protected:
+ ~ITextSelectionListener() {}
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/runtime/formoperations.cxx b/forms/source/runtime/formoperations.cxx
new file mode 100644
index 0000000000..1e3df2857e
--- /dev/null
+++ b/forms/source/runtime/formoperations.cxx
@@ -0,0 +1,1766 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include "formoperations.hxx"
+#include <frm_strings.hxx>
+#include <frm_resource.hxx>
+#include <strings.hrc>
+
+#include <com/sun/star/ucb/AlreadyInitializedException.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/form/runtime/FormFeature.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/form/XGrid.hpp>
+#include <com/sun/star/form/XBoundControl.hpp>
+#include <com/sun/star/form/XBoundComponent.hpp>
+#include <com/sun/star/sdbcx/XRowLocate.hpp>
+#include <com/sun/star/form/XConfirmDeleteListener.hpp>
+#include <com/sun/star/sdb/RowChangeEvent.hpp>
+#include <com/sun/star/sdb/RowChangeAction.hpp>
+#include <com/sun/star/sdb/OrderDialog.hpp>
+#include <com/sun/star/sdb/FilterDialog.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
+
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbexception.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/weld.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/container.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+
+
+namespace frm
+{
+
+
+ using ::dbtools::SQLExceptionInfo;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::sdbc::XRowSet;
+ using ::com::sun::star::sdbc::XResultSetUpdate;
+ using ::com::sun::star::form::runtime::XFormController;
+ using ::com::sun::star::form::runtime::XFormOperations;
+ using ::com::sun::star::form::runtime::XFeatureInvalidation;
+ using ::com::sun::star::form::runtime::FeatureState;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::sdbc::SQLException;
+ using namespace ::com::sun::star::sdbc;
+ using ::com::sun::star::form::XForm;
+ using ::com::sun::star::ucb::AlreadyInitializedException;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::lang::XMultiServiceFactory;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::form::XGrid;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::form::XBoundControl;
+ using ::com::sun::star::form::XBoundComponent;
+ using ::com::sun::star::sdbcx::XRowLocate;
+ using ::com::sun::star::form::XConfirmDeleteListener;
+ using ::com::sun::star::sdb::RowChangeEvent;
+ using namespace ::com::sun::star::sdb;
+ using ::com::sun::star::form::XReset;
+ using ::com::sun::star::beans::XMultiPropertySet;
+ using ::com::sun::star::lang::WrappedTargetException;
+ using ::com::sun::star::ui::dialogs::XExecutableDialog;
+ using ::com::sun::star::beans::NamedValue;
+ using ::com::sun::star::util::XRefreshable;
+ using ::com::sun::star::awt::XControlModel;
+
+ namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
+ namespace RowChangeAction = ::com::sun::star::sdb::RowChangeAction;
+
+ FormOperations::FormOperations( const Reference< XComponentContext >& _rxContext )
+ :FormOperations_Base( m_aMutex )
+ ,m_xContext( _rxContext )
+ ,m_bInitializedParser( false )
+ ,m_bActiveControlModified( false )
+ ,m_bConstructed( false )
+ #ifdef DBG_UTIL
+ ,m_nMethodNestingLevel( 0 )
+ #endif
+ {
+ }
+
+ FormOperations::~FormOperations()
+ {
+ }
+
+ void SAL_CALL FormOperations::initialize( const Sequence< Any >& _arguments )
+ {
+ if ( m_bConstructed )
+ throw AlreadyInitializedException();
+
+ if ( _arguments.getLength() == 1 )
+ {
+ Reference< XFormController > xController;
+ Reference< XForm > xForm;
+ if ( _arguments[0] >>= xController )
+ createWithFormController( xController );
+ else if ( _arguments[0] >>= xForm )
+ createWithForm( xForm );
+ else
+ throw IllegalArgumentException( OUString(), *this, 1 );
+ return;
+ }
+
+ throw IllegalArgumentException( OUString(), *this, 0 );
+ }
+
+ OUString SAL_CALL FormOperations::getImplementationName( )
+ {
+ return "com.sun.star.comp.forms.FormOperations";
+ }
+
+ sal_Bool SAL_CALL FormOperations::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL FormOperations::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.runtime.FormOperations" };
+ }
+
+ Reference< XRowSet > SAL_CALL FormOperations::getCursor()
+ {
+ MethodGuard aGuard( *this );
+ return m_xCursor;
+ }
+
+ Reference< XResultSetUpdate > SAL_CALL FormOperations::getUpdateCursor()
+ {
+ MethodGuard aGuard( *this );
+ return m_xUpdateCursor;
+ }
+
+
+ Reference< XFormController > SAL_CALL FormOperations::getController()
+ {
+ MethodGuard aGuard( *this );
+ return m_xController;
+ }
+
+
+ Reference< XFeatureInvalidation > SAL_CALL FormOperations::getFeatureInvalidation()
+ {
+ MethodGuard aGuard( *this );
+ return m_xFeatureInvalidation;
+ }
+
+
+ void SAL_CALL FormOperations::setFeatureInvalidation( const Reference< XFeatureInvalidation > & _rxFeatureInvalidation )
+ {
+ MethodGuard aGuard( *this );
+ m_xFeatureInvalidation = _rxFeatureInvalidation;
+ }
+
+
+ FeatureState SAL_CALL FormOperations::getState( ::sal_Int16 _nFeature )
+ {
+ MethodGuard aGuard( *this );
+
+ FeatureState aState;
+ aState.Enabled = false;
+
+ try
+ {
+ // some checks for basic pre-requisites
+ if ( !m_xLoadableForm.is()
+ || !m_xLoadableForm->isLoaded()
+ || !m_xCursorProperties.is()
+ )
+ {
+ return aState;
+ }
+
+ switch ( _nFeature )
+ {
+ case FormFeature::MoveToFirst:
+ case FormFeature::MoveToPrevious:
+ aState.Enabled = impl_canMoveLeft_throw( );
+ break;
+
+ case FormFeature::MoveToNext:
+ aState.Enabled = impl_canMoveRight_throw();
+ break;
+
+ case FormFeature::MoveToLast:
+ aState.Enabled = impl_getRowCount_throw() && ( !m_xCursor->isLast() || impl_isInsertionRow_throw() );
+ break;
+
+ case FormFeature::DeleteRecord:
+ // already deleted ?
+ if ( m_xCursor->rowDeleted() )
+ aState.Enabled = false;
+ else
+ {
+ // allowed to delete the row ?
+ aState.Enabled = !impl_isInsertionRow_throw() && ::dbtools::canDelete( m_xCursorProperties );
+ }
+ break;
+
+ case FormFeature::MoveToInsertRow:
+ // if we are inserting we can move to the next row if the current record or control is modified
+ aState.Enabled = impl_isInsertionRow_throw()
+ ? impl_isModifiedRow_throw() || m_bActiveControlModified
+ : ::dbtools::canInsert( m_xCursorProperties );
+ break;
+
+ case FormFeature::ReloadForm:
+ {
+ // there must be an active connection
+ aState.Enabled = ::dbtools::getConnection( m_xCursor ).is();
+
+ // and an active command
+ OUString sActiveCommand;
+ m_xCursorProperties->getPropertyValue( PROPERTY_ACTIVECOMMAND ) >>= sActiveCommand;
+ aState.Enabled = aState.Enabled && !sActiveCommand.isEmpty();
+ }
+ break;
+
+ case FormFeature::RefreshCurrentControl:
+ {
+ Reference< XRefreshable > xControlModelRefresh( impl_getCurrentControlModel_throw(), UNO_QUERY );
+ aState.Enabled = xControlModelRefresh.is();
+ }
+ break;
+
+ case FormFeature::SaveRecordChanges:
+ case FormFeature::UndoRecordChanges:
+ aState.Enabled = impl_isModifiedRow_throw() || m_bActiveControlModified;
+ break;
+
+ case FormFeature::RemoveFilterAndSort:
+ if ( impl_isParseable_throw() && impl_hasFilterOrOrder_throw() )
+ aState.Enabled = !impl_isInsertOnlyForm_throw();
+ break;
+
+ case FormFeature::SortAscending:
+ case FormFeature::SortDescending:
+ case FormFeature::AutoFilter:
+ if ( m_xController.is() && impl_isParseable_throw() )
+ {
+ bool bIsDeleted = m_xCursor->rowDeleted();
+
+ if ( !bIsDeleted && !impl_isInsertOnlyForm_throw() )
+ {
+ Reference< XPropertySet > xBoundField = impl_getCurrentBoundField_nothrow( );
+ if ( xBoundField.is() )
+ xBoundField->getPropertyValue( PROPERTY_SEARCHABLE ) >>= aState.Enabled;
+ }
+ }
+ break;
+
+ case FormFeature::InteractiveSort:
+ case FormFeature::InteractiveFilter:
+ if ( impl_isParseable_throw() )
+ aState.Enabled = !impl_isInsertOnlyForm_throw();
+ break;
+
+ case FormFeature::ToggleApplyFilter:
+ {
+ OUString sFilter;
+ OUString sHaving;
+ m_xCursorProperties->getPropertyValue( PROPERTY_FILTER ) >>= sFilter;
+ m_xCursorProperties->getPropertyValue( PROPERTY_HAVINGCLAUSE ) >>= sHaving;
+ if ( ! (sFilter.isEmpty() && sHaving.isEmpty()) )
+ {
+ aState.State = m_xCursorProperties->getPropertyValue( PROPERTY_APPLYFILTER );
+ aState.Enabled = !impl_isInsertOnlyForm_throw();
+ }
+ else
+ aState.State <<= false;
+ }
+ break;
+
+ case FormFeature::MoveAbsolute:
+ {
+ sal_Int32 nPosition = m_xCursor->getRow();
+ bool bIsNew = impl_isInsertionRow_throw();
+ sal_Int32 nCount = impl_getRowCount_throw();
+ bool bFinalCount = impl_isRowCountFinal_throw();
+
+ if ( ( nPosition >= 0 ) || bIsNew )
+ {
+ if ( bFinalCount )
+ {
+ // special case: there are no records at all, and we
+ // can't insert records -> disabled
+ if ( !nCount && !::dbtools::canInsert( m_xCursorProperties ) )
+ {
+ aState.Enabled = false;
+ }
+ else
+ {
+ if ( bIsNew )
+ nPosition = ++nCount;
+ aState.State <<= nPosition;
+ aState.Enabled = true;
+ }
+ }
+ else
+ {
+ aState.State <<= nPosition;
+ aState.Enabled = true;
+ }
+ }
+ }
+ break;
+
+ case FormFeature::TotalRecords:
+ {
+ bool bIsNew = impl_isInsertionRow_throw();
+ sal_Int32 nCount = impl_getRowCount_throw();
+ bool bFinalCount = impl_isRowCountFinal_throw();
+
+ if ( bIsNew )
+ ++nCount;
+
+ OUString sValue = OUString::number( nCount );
+ if ( !bFinalCount )
+ sValue += " *";
+
+ aState.State <<= sValue;
+ aState.Enabled = true;
+ }
+ break;
+
+ default:
+ OSL_FAIL( "FormOperations::getState: unknown feature id!" );
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.runtime", "FormOperations::getState" );
+ }
+
+ return aState;
+ }
+
+
+ sal_Bool SAL_CALL FormOperations::isEnabled( ::sal_Int16 _nFeature )
+ {
+ MethodGuard aGuard( *this );
+
+ FeatureState aState( getState( _nFeature ) );
+ return aState.Enabled;
+ }
+
+
+ namespace
+ {
+ bool lcl_needConfirmCommit( sal_Int32 _nFeature )
+ {
+ return ( ( _nFeature == FormFeature::ReloadForm )
+ || ( _nFeature == FormFeature::RemoveFilterAndSort )
+ || ( _nFeature == FormFeature::ToggleApplyFilter )
+ || ( _nFeature == FormFeature::SortAscending )
+ || ( _nFeature == FormFeature::SortDescending )
+ || ( _nFeature == FormFeature::AutoFilter )
+ || ( _nFeature == FormFeature::InteractiveSort )
+ || ( _nFeature == FormFeature::InteractiveFilter )
+ );
+ }
+ bool lcl_requiresArguments( sal_Int32 _nFeature )
+ {
+ return ( _nFeature == FormFeature::MoveAbsolute );
+ }
+ bool lcl_isExecutableFeature( sal_Int32 _nFeature )
+ {
+ return ( _nFeature != FormFeature::TotalRecords );
+ }
+
+ template < typename TYPE >
+ TYPE lcl_safeGetPropertyValue_throw( const Reference< XPropertySet >& _rxProperties, const OUString& _rPropertyName, TYPE Default )
+ {
+ TYPE value( Default );
+ OSL_PRECOND( _rxProperties.is(), "FormOperations::<foo>: no cursor (already disposed?)!" );
+ if ( _rxProperties.is() )
+ OSL_VERIFY( _rxProperties->getPropertyValue( _rPropertyName ) >>= value );
+ return value;
+ }
+
+ // returns false if parent should *abort* (user pressed cancel)
+ bool checkConfirmation(bool &needConfirmation, bool &shouldCommit)
+ {
+ if(!needConfirmation)
+ return true;
+ // TODO: shouldn't this be done with an interaction handler?
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ResourceManager::loadString(RID_STR_QUERY_SAVE_MODIFIED_ROW)));
+ xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
+ xQueryBox->set_default_response(RET_YES);
+
+ switch (xQueryBox->run())
+ {
+ case RET_NO:
+ shouldCommit = false;
+ [[fallthrough]]; // don't ask again!
+ case RET_YES:
+ needConfirmation = false;
+ return true;
+ case RET_CANCEL:
+ return false;
+ }
+ return true;
+ }
+
+ bool commit1Form(const Reference< XFormController >& xCntrl, bool &needConfirmation, bool &shouldCommit)
+ {
+ Reference< XFormOperations > xFrmOps(xCntrl->getFormOperations());
+ if (!xFrmOps->commitCurrentControl())
+ return false;
+
+ if(xFrmOps->isModifiedRow())
+ {
+ if(!checkConfirmation(needConfirmation, shouldCommit))
+ return false;
+ sal_Bool bTmp;
+ if (shouldCommit && !xFrmOps->commitCurrentRecord(bTmp))
+ return false;
+ }
+ return true;
+ }
+
+ bool commitFormAndSubforms(const Reference< XFormController >& xCntrl, bool needConfirmation)
+ {
+ bool shouldCommit(true);
+ assert(xCntrl.is());
+ if(xCntrl.is())
+ {
+ const sal_Int32 cnt = xCntrl->getCount();
+ for(int i=0; i < cnt; ++i)
+ {
+ Reference< XFormController > xSubForm(xCntrl->getByIndex(i), UNO_QUERY);
+ assert(xSubForm.is());
+ if (xSubForm.is())
+ {
+ if (!commit1Form(xSubForm, needConfirmation, shouldCommit))
+ return false;
+ }
+ }
+ }
+
+ return commit1Form(xCntrl, needConfirmation, shouldCommit);
+ }
+
+ bool commit1Form(const Reference< XForm >& xFrm, bool &needConfirmation, bool &shouldCommit)
+ {
+ Reference< XPropertySet > xProps(xFrm, UNO_QUERY_THROW);
+ // nothing to do if the record is not modified
+ if(!lcl_safeGetPropertyValue_throw( xProps, PROPERTY_ISMODIFIED, false ))
+ return true;
+
+ if(!checkConfirmation(needConfirmation, shouldCommit))
+ return false;
+ if(shouldCommit)
+ {
+ Reference< XResultSetUpdate > xUpd(xFrm, UNO_QUERY_THROW);
+ // insert respectively update the row
+ if ( lcl_safeGetPropertyValue_throw( xProps, PROPERTY_ISNEW, false ) )
+ xUpd->insertRow();
+ else
+ xUpd->updateRow();
+ }
+ return true;
+ }
+
+ bool commitFormAndSubforms(const Reference< XForm >& xFrm, bool needConfirmation)
+ {
+ // No control... do what we can with the models
+ bool shouldCommit(true);
+ Reference< XIndexAccess > xFormComps(xFrm, UNO_QUERY_THROW);
+
+ const sal_Int32 cnt = xFormComps->getCount();
+ for(int i=0; i < cnt; ++i)
+ {
+ Reference< XForm > xSubForm(xFormComps->getByIndex(i), UNO_QUERY);
+ if(xSubForm.is())
+ {
+ if(!commit1Form(xSubForm, needConfirmation, shouldCommit))
+ return false;
+ }
+ }
+
+ return commit1Form(xFrm, needConfirmation, shouldCommit);
+ }
+ }
+
+ void SAL_CALL FormOperations::execute( ::sal_Int16 _nFeature )
+ {
+ SolarMutexGuard aSolarGuard;
+ MethodGuard aGuard( *this );
+
+ if ( ( _nFeature != FormFeature::DeleteRecord ) && ( _nFeature != FormFeature::UndoRecordChanges ) )
+ {
+
+
+ if(m_xController.is())
+ {
+ if(!commitFormAndSubforms(m_xController, lcl_needConfirmCommit( _nFeature )))
+ return;
+ }
+ else if(m_xCursor.is())
+ {
+ Reference< XForm > xForm(m_xCursor, UNO_QUERY);
+ assert(xForm.is());
+ if(!commitFormAndSubforms(xForm, lcl_needConfirmCommit( _nFeature )))
+ return;
+ }
+ else
+ {
+ SAL_WARN( "forms.runtime", "No cursor, but trying to execute form operation " << _nFeature );
+ }
+ }
+
+ try
+ {
+ switch ( _nFeature )
+ {
+ case FormFeature::MoveToFirst:
+ m_xCursor->first();
+ break;
+
+ case FormFeature::MoveToNext:
+ impl_moveRight_throw( );
+ break;
+
+ case FormFeature::MoveToPrevious:
+ impl_moveLeft_throw( );
+ break;
+
+ case FormFeature::MoveToLast:
+ {
+/*
+ // TODO: re-implement this...
+ // run in an own thread if...
+ // ... the data source is thread safe...
+ sal_Bool bAllowOwnThread = sal_False;
+ if ( ::comphelper::hasProperty( PROPERTY_THREADSAFE, m_xCursorProperties ) )
+ m_xCursorProperties->getPropertyValue( PROPERTY_THREADSAFE ) >>= bAllowOwnThread;
+
+ // ... the record count is unknown
+ sal_Bool bNeedOwnThread sal_False;
+ if ( ::comphelper::hasProperty( PROPERTY_ROWCOUNTFINAL, m_xCursorProperties ) )
+ m_xCursorProperties->getPropertyValue( PROPERTY_ROWCOUNTFINAL ) >>= bNeedOwnThread;
+
+ if ( bNeedOwnThread && bAllowOwnThread )
+ ;
+ else
+*/
+ m_xCursor->last();
+ }
+ break;
+
+ case FormFeature::ReloadForm:
+ if ( m_xLoadableForm.is() )
+ {
+ weld::WaitObject aWO(Application::GetFrameWeld(GetDialogParent()));
+ m_xLoadableForm->reload();
+
+ // refresh all controls in the form (and sub forms) which can be refreshed
+ // #i90914#
+ ::comphelper::IndexAccessIterator aIter( m_xLoadableForm );
+ Reference< XInterface > xElement( aIter.Next() );
+ while ( xElement.is() )
+ {
+ Reference< XRefreshable > xRefresh( xElement, UNO_QUERY );
+ if ( xRefresh.is() )
+ xRefresh->refresh();
+ xElement = aIter.Next();
+ }
+ }
+ break;
+
+ case FormFeature::RefreshCurrentControl:
+ {
+ Reference< XRefreshable > xControlModelRefresh( impl_getCurrentControlModel_throw(), UNO_QUERY );
+ OSL_ENSURE( xControlModelRefresh.is(), "FormOperations::execute: how did you reach this?" );
+ if ( xControlModelRefresh.is() )
+ xControlModelRefresh->refresh();
+ }
+ break;
+
+ case FormFeature::DeleteRecord:
+ {
+ sal_uInt32 nCount = impl_getRowCount_throw();
+
+ // next position
+ bool bLeft = m_xCursor->isLast() && ( nCount > 1 );
+ bool bRight= !m_xCursor->isLast();
+ bool bSuccess = false;
+ try
+ {
+ // ask for confirmation
+ Reference< XConfirmDeleteListener > xConfirmDelete( m_xController, UNO_QUERY );
+
+ if ( xConfirmDelete.is() )
+ {
+ RowChangeEvent aEvent;
+ aEvent.Source.set( m_xCursor, UNO_QUERY );
+ aEvent.Action = RowChangeAction::DELETE;
+ aEvent.Rows = 1;
+ bSuccess = xConfirmDelete->confirmDelete( aEvent );
+ }
+
+ // delete it
+ if ( bSuccess )
+ m_xUpdateCursor->deleteRow();
+ }
+ catch( const Exception& )
+ {
+ bSuccess = false;
+ }
+
+ if ( bSuccess )
+ {
+ if ( bLeft || bRight )
+ m_xCursor->relative( bRight ? 1 : -1 );
+ else
+ {
+ bool bCanInsert = ::dbtools::canInsert( m_xCursorProperties );
+ // is it possible to insert another record?
+ if ( bCanInsert )
+ m_xUpdateCursor->moveToInsertRow();
+ else
+ // move record to update status
+ m_xCursor->first();
+ }
+ }
+ }
+ break;
+
+ case FormFeature::SaveRecordChanges:
+ case FormFeature::UndoRecordChanges:
+ {
+ bool bInserting = impl_isInsertionRow_throw();
+
+ if ( FormFeature::UndoRecordChanges == _nFeature )
+ {
+ if ( !bInserting )
+ m_xUpdateCursor->cancelRowUpdates();
+
+ // reset all controls for this form
+ impl_resetAllControls_nothrow( );
+
+ if ( bInserting ) // back to insertion mode for this form
+ m_xUpdateCursor->moveToInsertRow();
+ }
+ else
+ {
+ if ( bInserting )
+ {
+ m_xUpdateCursor->insertRow();
+ m_xCursor->last();
+ }
+ else
+ m_xUpdateCursor->updateRow();
+ }
+ }
+ break;
+
+ case FormFeature::MoveToInsertRow:
+ // move to the last row before moving to the insert row
+ m_xCursor->last();
+ m_xUpdateCursor->moveToInsertRow();
+ break;
+
+ case FormFeature::RemoveFilterAndSort:
+ {
+ // simultaneously reset Filter and Order property
+ Reference< XMultiPropertySet > xProperties( m_xCursorProperties, UNO_QUERY );
+ OSL_ENSURE( xProperties.is(), "FormOperations::execute: no multi property access!" );
+ if ( xProperties.is() )
+ {
+ Sequence< OUString > aNames{ PROPERTY_FILTER, PROPERTY_HAVINGCLAUSE,
+ PROPERTY_SORT };
+
+ Sequence< Any> aValues{ Any(OUString()), Any(OUString()), Any(OUString()) };
+
+ weld::WaitObject aWO(Application::GetFrameWeld(GetDialogParent()));
+ xProperties->setPropertyValues( aNames, aValues );
+
+ if ( m_xLoadableForm.is() )
+ m_xLoadableForm->reload();
+ }
+ }
+ break;
+
+ case FormFeature::ToggleApplyFilter:
+ if ( impl_commitCurrentControl_throw() && impl_commitCurrentRecord_throw() )
+ {
+ // simply toggle the value
+ bool bApplied = false;
+ m_xCursorProperties->getPropertyValue( PROPERTY_APPLYFILTER ) >>= bApplied;
+ m_xCursorProperties->setPropertyValue( PROPERTY_APPLYFILTER, Any( !bApplied ) );
+
+ // and reload
+ weld::WaitObject aWO(Application::GetFrameWeld(GetDialogParent()));
+ m_xLoadableForm->reload();
+ }
+ break;
+
+ case FormFeature::SortAscending:
+ impl_executeAutoSort_throw( true );
+ break;
+
+ case FormFeature::SortDescending:
+ impl_executeAutoSort_throw( false );
+ break;
+
+ case FormFeature::AutoFilter:
+ impl_executeAutoFilter_throw();
+ break;
+
+ case FormFeature::InteractiveSort:
+ impl_executeFilterOrSort_throw( false );
+ break;
+
+ case FormFeature::InteractiveFilter:
+ impl_executeFilterOrSort_throw( true );
+ break;
+
+ default:
+ {
+ TranslateId pErrorResourceId = RID_STR_FEATURE_UNKNOWN;
+ if ( lcl_requiresArguments( _nFeature ) )
+ pErrorResourceId = RID_STR_FEATURE_REQUIRES_PARAMETERS;
+ else if ( !lcl_isExecutableFeature( _nFeature ) )
+ pErrorResourceId = RID_STR_FEATURE_NOT_EXECUTABLE;
+ throw IllegalArgumentException( ResourceManager::loadString(pErrorResourceId), *this, 1 );
+ }
+ } // switch
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const SQLException& ) { throw; }
+ catch( const Exception& )
+ {
+ throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() );
+ }
+
+ impl_invalidateAllSupportedFeatures_nothrow( aGuard );
+ }
+
+
+ void SAL_CALL FormOperations::executeWithArguments( ::sal_Int16 _nFeature, const Sequence< NamedValue >& _rArguments )
+ {
+ if ( !lcl_requiresArguments( _nFeature ) )
+ {
+ execute( _nFeature );
+ return;
+ }
+
+ SolarMutexGuard aSolarGuard;
+ MethodGuard aGuard( *this );
+
+ // at the moment we have only one feature which supports execution parameters
+ if ( !lcl_isExecutableFeature( _nFeature ) )
+ throw IllegalArgumentException( ResourceManager::loadString(RID_STR_FEATURE_NOT_EXECUTABLE), *this, 1 );
+
+ switch ( _nFeature )
+ {
+ case FormFeature::MoveAbsolute:
+ {
+ sal_Int32 nPosition = -1;
+
+ ::comphelper::NamedValueCollection aArguments( _rArguments );
+ aArguments.get_ensureType( "Position", nPosition );
+
+ if ( nPosition < 1 )
+ nPosition = 1;
+
+ try
+ {
+ // commit before doing anything else
+ if ( m_xController.is() && !impl_commitCurrentControl_throw() )
+ return;
+ if ( !impl_commitCurrentRecord_throw() )
+ return;
+
+ sal_Int32 nCount = impl_getRowCount_throw();
+ bool bFinalCount = impl_isRowCountFinal_throw();
+
+ if ( bFinalCount && ( nPosition > nCount ) )
+ nPosition = nCount;
+
+ m_xCursor->absolute( nPosition );
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const SQLException& ) { throw; }
+ catch( const Exception& )
+ {
+ throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() );
+ }
+ }
+ break;
+ default:
+ throw IllegalArgumentException( ResourceManager::loadString(RID_STR_FEATURE_UNKNOWN), *this, 1 );
+ } // switch
+ }
+
+
+ sal_Bool SAL_CALL FormOperations::commitCurrentRecord( sal_Bool& _out_rRecordInserted )
+ {
+ MethodGuard aGuard( *this );
+ _out_rRecordInserted = false;
+
+ return impl_commitCurrentRecord_throw( &_out_rRecordInserted );
+ }
+
+
+ bool FormOperations::impl_commitCurrentRecord_throw( sal_Bool* _pRecordInserted ) const
+ {
+#ifdef DBG_UTIL
+ DBG_ASSERT( m_nMethodNestingLevel, "FormOperations::impl_commitCurrentRecord_throw: to be called within a MethodGuard'ed section only!" );
+#endif
+
+ if ( !impl_hasCursor_nothrow() )
+ return false;
+
+ // nothing to do if the record is not modified
+ bool bResult = !impl_isModifiedRow_throw();
+ if ( !bResult )
+ {
+ // insert respectively update the row
+ if ( impl_isInsertionRow_throw() )
+ {
+ m_xUpdateCursor->insertRow();
+ if ( _pRecordInserted )
+ *_pRecordInserted = true;
+ }
+ else
+ m_xUpdateCursor->updateRow();
+ bResult = true;
+ }
+ return bResult;
+ }
+
+
+ sal_Bool SAL_CALL FormOperations::commitCurrentControl()
+ {
+ MethodGuard aGuard( *this );
+ return impl_commitCurrentControl_throw();
+ }
+
+
+ bool FormOperations::impl_commitCurrentControl_throw() const
+ {
+#ifdef DBG_UTIL
+ DBG_ASSERT( m_nMethodNestingLevel, "FormOperations::impl_commitCurrentControl_throw: to be called within a MethodGuard'ed section only!" );
+#endif
+ OSL_PRECOND( m_xController.is(), "FormOperations::commitCurrentControl: no controller!" );
+ if ( !m_xController.is() )
+ return false;
+
+ bool bSuccess = false;
+ try
+ {
+ Reference< XControl > xCurrentControl( m_xController->getCurrentControl() );
+
+ // check whether the control is locked
+ Reference< XBoundControl > xCheckLock( xCurrentControl, UNO_QUERY );
+ bool bControlIsLocked = xCheckLock.is() && xCheckLock->getLock();
+
+ // commit if necessary
+ bSuccess = true;
+ if ( xCurrentControl.is() && !bControlIsLocked )
+ {
+ // both the control and its model can be committable, so try both
+ Reference< XBoundComponent > xBound( xCurrentControl, UNO_QUERY );
+ if ( !xBound.is() )
+ xBound.set(xCurrentControl->getModel(), css::uno::UNO_QUERY);
+ // and now really commit
+ if ( xBound.is() )
+ bSuccess = xBound->commit();
+ }
+
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const SQLException& ) { throw; }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.runtime");
+ bSuccess = false;
+ }
+
+ return bSuccess;
+ }
+
+
+ sal_Bool SAL_CALL FormOperations::isInsertionRow()
+ {
+ bool bIs = false;
+ try
+ {
+ bIs = impl_isInsertionRow_throw();
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() );
+ }
+ return bIs;
+ }
+
+
+ sal_Bool SAL_CALL FormOperations::isModifiedRow()
+ {
+ bool bIs = false;
+ try
+ {
+ bIs = impl_isModifiedRow_throw();
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() );
+ }
+ return bIs;
+ }
+
+
+ void SAL_CALL FormOperations::cursorMoved( const EventObject& /*_Event*/ )
+ {
+ MethodGuard aGuard( *this );
+ m_bActiveControlModified = false;
+
+ impl_invalidateAllSupportedFeatures_nothrow( aGuard );
+ }
+
+
+ void SAL_CALL FormOperations::rowChanged( const EventObject& /*_Event*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL FormOperations::rowSetChanged( const EventObject& /*_Event*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL FormOperations::modified( const EventObject& /*_Source*/ )
+ {
+ MethodGuard aGuard( *this );
+
+ OSL_ENSURE( m_xCursor.is(), "FormOperations::modified: already disposed!" );
+ if ( !m_bActiveControlModified )
+ {
+ m_bActiveControlModified = true;
+ impl_invalidateModifyDependentFeatures_nothrow( aGuard );
+ }
+ }
+
+
+ void SAL_CALL FormOperations::propertyChange( const PropertyChangeEvent& _rEvent )
+ {
+ MethodGuard aGuard( *this );
+
+ if ( m_xCursor.is() && ( m_xCursor == _rEvent.Source ) )
+ {
+ if ( ( _rEvent.PropertyName == PROPERTY_ISMODIFIED )
+ || ( _rEvent.PropertyName == PROPERTY_ISNEW )
+ )
+ {
+ bool bIs = false;
+ if ( ( _rEvent.NewValue >>= bIs ) && !bIs )
+ m_bActiveControlModified = false;
+ }
+ impl_invalidateAllSupportedFeatures_nothrow( aGuard );
+ }
+
+ if ( !(m_xParser.is() && ( m_xCursor == _rEvent.Source )) )
+ return;
+
+ try
+ {
+ OUString sNewValue;
+ _rEvent.NewValue >>= sNewValue;
+ if ( _rEvent.PropertyName == PROPERTY_ACTIVECOMMAND )
+ {
+ m_xParser->setElementaryQuery( sNewValue );
+ }
+ else if ( _rEvent.PropertyName == PROPERTY_FILTER )
+ {
+ if ( m_xParser->getFilter() != sNewValue )
+ m_xParser->setFilter( sNewValue );
+ }
+ else if ( _rEvent.PropertyName == PROPERTY_HAVINGCLAUSE )
+ {
+ if ( m_xParser->getHavingClause() != sNewValue )
+ m_xParser->setHavingClause( sNewValue );
+ }
+ else if ( _rEvent.PropertyName == PROPERTY_SORT )
+ {
+ _rEvent.NewValue >>= sNewValue;
+ if ( m_xParser->getOrder() != sNewValue )
+ m_xParser->setOrder( sNewValue );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.runtime", "FormOperations::propertyChange: caught an exception while updating the parser!" );
+ }
+ impl_invalidateAllSupportedFeatures_nothrow( aGuard );
+ }
+
+
+ void SAL_CALL FormOperations::disposing( const EventObject& /*_Source*/ )
+ {
+ // TODO: should we react on this? Or is this the responsibility of our owner to dispose us?
+ }
+
+
+ void SAL_CALL FormOperations::disposing()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ impl_disposeParser_nothrow();
+
+ try
+ {
+ // revoke various listeners
+ if ( m_xCursor.is() )
+ m_xCursor->removeRowSetListener( this );
+
+ if ( m_xCursorProperties.is() )
+ {
+ m_xCursorProperties->removePropertyChangeListener( PROPERTY_ISMODIFIED,this );
+ m_xCursorProperties->removePropertyChangeListener( PROPERTY_ISNEW, this );
+ }
+
+ if ( m_xController.is() )
+ m_xController->removeModifyListener( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.runtime");
+ }
+
+ m_xController.clear();
+ m_xCursor.clear();
+ m_xUpdateCursor.clear();
+ m_xCursorProperties.clear();
+ m_xLoadableForm.clear();
+ m_xFeatureInvalidation.clear();
+
+ m_bActiveControlModified = true;
+ }
+
+
+ void FormOperations::impl_checkDisposed_throw() const
+ {
+ if ( !m_xCursor.is() )
+ throw DisposedException( OUString(), *const_cast< FormOperations* >( this ) );
+ }
+
+
+ void FormOperations::impl_initFromController_throw()
+ {
+ OSL_PRECOND( m_xController.is(), "FormOperations::impl_initFromController_throw: invalid controller!" );
+ m_xCursor.set(m_xController->getModel(), css::uno::UNO_QUERY);
+ if ( !m_xCursor.is() )
+ throw IllegalArgumentException( OUString(), *this, 0 );
+
+ impl_initFromForm_throw();
+
+ if ( m_xController.is() )
+ m_xController->addModifyListener( this );
+ }
+
+
+ void FormOperations::impl_initFromForm_throw()
+ {
+ OSL_PRECOND( m_xCursor.is(), "FormOperations::impl_initFromForm_throw: invalid form!" );
+ m_xCursorProperties.set(m_xCursor, css::uno::UNO_QUERY);
+ m_xUpdateCursor.set(m_xCursor, css::uno::UNO_QUERY);
+ m_xLoadableForm.set(m_xCursor, css::uno::UNO_QUERY);
+
+ if ( !m_xCursor.is() || !m_xCursorProperties.is() || !m_xLoadableForm.is() )
+ throw IllegalArgumentException( OUString(), *this, 0 );
+
+ m_xCursor->addRowSetListener( this );
+ m_xCursorProperties->addPropertyChangeListener( PROPERTY_ISMODIFIED,this );
+ m_xCursorProperties->addPropertyChangeListener( PROPERTY_ISNEW, this );
+ }
+
+
+ void FormOperations::createWithFormController( const Reference< XFormController >& _rxController )
+ {
+ m_xController = _rxController;
+ if ( !m_xController.is() )
+ throw IllegalArgumentException( OUString(), *this, 0 );
+
+ impl_initFromController_throw();
+
+ m_bConstructed = true;
+ }
+
+
+ void FormOperations::createWithForm( const Reference< XForm >& _rxForm )
+ {
+ m_xCursor.set(_rxForm, css::uno::UNO_QUERY);
+ if ( !m_xCursor.is() )
+ throw IllegalArgumentException( OUString(), *this, 0 );
+
+ impl_initFromForm_throw();
+
+ m_bConstructed = true;
+ }
+
+
+ void FormOperations::impl_invalidateAllSupportedFeatures_nothrow( MethodGuard& _rClearForCallback ) const
+ {
+ if ( !m_xFeatureInvalidation.is() )
+ // nobody's interested in ...
+ return;
+
+ Reference< XFeatureInvalidation > xInvalidation = m_xFeatureInvalidation;
+ _rClearForCallback.clear();
+ xInvalidation->invalidateAllFeatures();
+ }
+
+
+ void FormOperations::impl_invalidateModifyDependentFeatures_nothrow( MethodGuard& _rClearForCallback ) const
+ {
+ if ( !m_xFeatureInvalidation.is() )
+ // nobody's interested in ...
+ return;
+
+ static Sequence< sal_Int16 > const s_aModifyDependentFeatures
+ {
+ FormFeature::MoveToNext,
+ FormFeature::MoveToInsertRow,
+ FormFeature::SaveRecordChanges,
+ FormFeature::UndoRecordChanges
+ };
+
+ Reference< XFeatureInvalidation > xInvalidation = m_xFeatureInvalidation;
+ _rClearForCallback.clear();
+
+ xInvalidation->invalidateFeatures( s_aModifyDependentFeatures );
+ }
+
+
+ void FormOperations::impl_ensureInitializedParser_nothrow()
+ {
+ OSL_PRECOND( m_xCursorProperties.is(), "FormOperations::impl_ensureInitializedParser_nothrow: we're disposed!" );
+ if ( m_bInitializedParser )
+ return;
+
+ try
+ {
+ bool bUseEscapeProcessing = false;
+ m_xCursorProperties->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bUseEscapeProcessing;
+ if ( bUseEscapeProcessing )
+ {
+ Reference< XMultiServiceFactory > xFactory( ::dbtools::getConnection( m_xCursor ), UNO_QUERY );
+ if ( xFactory.is() )
+ {
+ m_xParser.set( xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"), UNO_QUERY );
+ OSL_ENSURE( m_xParser.is(), "FormOperations::impl_ensureInitializedParser_nothrow: factory did not create a parser for us!" );
+ }
+ }
+
+ if ( m_xParser.is() )
+ {
+ if ( m_xLoadableForm.is() && m_xLoadableForm->isLoaded() )
+ {
+ OUString sStatement;
+ OUString sFilter;
+ OUString sHaving;
+ OUString sSort;
+
+ m_xCursorProperties->getPropertyValue( PROPERTY_ACTIVECOMMAND ) >>= sStatement;
+ m_xCursorProperties->getPropertyValue( PROPERTY_FILTER ) >>= sFilter;
+ m_xCursorProperties->getPropertyValue( PROPERTY_HAVINGCLAUSE ) >>= sHaving;
+ m_xCursorProperties->getPropertyValue( PROPERTY_SORT ) >>= sSort;
+
+ m_xParser->setElementaryQuery( sStatement );
+ m_xParser->setFilter ( sFilter );
+ m_xParser->setHavingClause ( sHaving );
+ m_xParser->setOrder ( sSort );
+ }
+
+ // start listening at the order/sort properties at the form, so
+ // we can keep our parser in sync
+ m_xCursorProperties->addPropertyChangeListener( PROPERTY_ACTIVECOMMAND, this );
+ m_xCursorProperties->addPropertyChangeListener( PROPERTY_FILTER, this );
+ m_xCursorProperties->addPropertyChangeListener( PROPERTY_HAVINGCLAUSE, this );
+ m_xCursorProperties->addPropertyChangeListener( PROPERTY_SORT, this );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.runtime", "FormOperations::impl_ensureInitializedParser_nothrow" );
+ }
+
+ m_bInitializedParser = true;
+ }
+
+
+ void FormOperations::impl_disposeParser_nothrow()
+ {
+ try
+ {
+ // if we have a parser (and a cursor), then we're listening at the cursor's
+ // properties to keep the parser in sync with the cursor
+ if ( m_xParser.is() && m_xCursorProperties.is() )
+ {
+ m_xCursorProperties->removePropertyChangeListener( PROPERTY_FILTER, this );
+ m_xCursorProperties->removePropertyChangeListener( PROPERTY_HAVINGCLAUSE, this );
+ m_xCursorProperties->removePropertyChangeListener( PROPERTY_ACTIVECOMMAND, this );
+ m_xCursorProperties->removePropertyChangeListener( PROPERTY_SORT, this );
+ }
+
+ Reference< XComponent > xParserComp( m_xParser, UNO_QUERY );
+ if ( xParserComp.is() )
+ xParserComp->dispose();
+ m_xParser.clear();
+
+ m_bInitializedParser = false;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.runtime", "FormOperations::impl_disposeParser_nothrow" );
+ }
+ }
+
+
+ bool FormOperations::impl_canMoveLeft_throw( ) const
+ {
+ if ( !impl_hasCursor_nothrow() )
+ return false;
+
+ return impl_getRowCount_throw() && ( !m_xCursor->isFirst() || impl_isInsertionRow_throw() );
+ }
+
+
+ bool FormOperations::impl_canMoveRight_throw( ) const
+ {
+ if ( !impl_hasCursor_nothrow() )
+ return false;
+
+ bool bIsNew = impl_isInsertionRow_throw();
+
+ if ( impl_getRowCount_throw() && !m_xCursor->isLast() && !bIsNew )
+ return true;
+
+ if ( ::dbtools::canInsert( m_xCursorProperties ) )
+ if ( !bIsNew || impl_isModifiedRow_throw() )
+ return true;
+
+ if ( bIsNew && m_bActiveControlModified )
+ return true;
+
+ return false;
+ }
+
+
+ bool FormOperations::impl_isInsertionRow_throw() const
+ {
+ return lcl_safeGetPropertyValue_throw( m_xCursorProperties, PROPERTY_ISNEW, false );
+ }
+
+
+ sal_Int32 FormOperations::impl_getRowCount_throw() const
+ {
+ return lcl_safeGetPropertyValue_throw( m_xCursorProperties, PROPERTY_ROWCOUNT, sal_Int32(0) );
+ }
+
+ bool FormOperations::impl_isRowCountFinal_throw() const
+ {
+ return lcl_safeGetPropertyValue_throw( m_xCursorProperties, PROPERTY_ROWCOUNTFINAL, false );
+ }
+
+
+ bool FormOperations::impl_isModifiedRow_throw() const
+ {
+ return lcl_safeGetPropertyValue_throw( m_xCursorProperties, PROPERTY_ISMODIFIED, false );
+ }
+
+
+ bool FormOperations::impl_isParseable_throw() const
+ {
+ const_cast< FormOperations* >( this )->impl_ensureInitializedParser_nothrow();
+ return m_xParser.is() && !m_xParser->getQuery().isEmpty();
+ }
+
+
+ bool FormOperations::impl_hasFilterOrOrder_throw() const
+ {
+ return impl_isParseable_throw() && ( !m_xParser->getFilter().isEmpty() ||
+ !m_xParser->getHavingClause().isEmpty() ||
+ !m_xParser->getOrder().isEmpty() );
+ }
+
+
+ bool FormOperations::impl_isInsertOnlyForm_throw() const
+ {
+ return lcl_safeGetPropertyValue_throw( m_xCursorProperties, PROPERTY_INSERTONLY, true );
+ }
+
+
+ Reference< XControlModel > FormOperations::impl_getCurrentControlModel_throw() const
+ {
+ Reference< XControl > xControl( m_xController->getCurrentControl() );
+
+ // special handling for grid controls
+ Reference< XGrid > xGrid( xControl, UNO_QUERY );
+ Reference< XControlModel > xControlModel;
+
+ if ( xGrid.is() )
+ {
+ Reference< XIndexAccess > xColumns( xControl->getModel(), UNO_QUERY_THROW );
+ sal_Int32 nCurrentPos = impl_gridView2ModelPos_nothrow( xColumns, xGrid->getCurrentColumnPosition() );
+
+ if ( nCurrentPos != -1 )
+ xColumns->getByIndex( nCurrentPos ) >>= xControlModel;
+ }
+ else if ( xControl.is() )
+ {
+ xControlModel = xControl->getModel();
+ }
+ return xControlModel;
+ }
+
+
+ Reference< XPropertySet > FormOperations::impl_getCurrentBoundField_nothrow( ) const
+ {
+ OSL_PRECOND( m_xController.is(), "FormOperations::impl_getCurrentBoundField_nothrow: no controller -> no control!" );
+ if ( !m_xController.is() )
+ return nullptr;
+
+ Reference< XPropertySet > xField;
+ try
+ {
+ Reference< XPropertySet > xControlModel( impl_getCurrentControlModel_throw(), UNO_QUERY );
+
+ if ( xControlModel.is() && ::comphelper::hasProperty( PROPERTY_BOUNDFIELD, xControlModel ) )
+ xControlModel->getPropertyValue( PROPERTY_BOUNDFIELD ) >>= xField;
+
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.runtime");
+ }
+
+ return xField;
+ }
+
+
+ sal_Int32 FormOperations::impl_gridView2ModelPos_nothrow( const Reference< XIndexAccess >& _rxColumns, sal_Int16 _nViewPos )
+ {
+ OSL_PRECOND( _rxColumns.is(), "FormOperations::impl_gridView2ModelPos_nothrow: invalid columns container!" );
+ try
+ {
+ // loop through all columns
+ sal_Int32 col = 0;
+ Reference< XPropertySet > xCol;
+ bool bHidden( false );
+ for ( col = 0; col < _rxColumns->getCount(); ++col )
+ {
+ _rxColumns->getByIndex( col ) >>= xCol;
+ OSL_VERIFY( xCol->getPropertyValue( PROPERTY_HIDDEN ) >>= bHidden );
+ if ( bHidden )
+ continue;
+
+ // for every visible col : if nViewPos is greater zero, decrement it, else we
+ // have found the model position
+ if ( !_nViewPos )
+ break;
+ else
+ --_nViewPos;
+ }
+ if ( col < _rxColumns->getCount() )
+ return col;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.runtime");
+ }
+ return -1;
+ }
+
+
+ void FormOperations::impl_moveLeft_throw( ) const
+ {
+ OSL_PRECOND( impl_hasCursor_nothrow(), "FormOperations::impl_moveLeft_throw: no cursor!" );
+ if ( !impl_hasCursor_nothrow() )
+ return;
+
+ sal_Bool bRecordInserted = false;
+ bool bSuccess = impl_commitCurrentRecord_throw( &bRecordInserted );
+
+ if ( !bSuccess )
+ return;
+
+ if ( bRecordInserted )
+ {
+ // retrieve the bookmark of the new record and move to the record preceding this bookmark
+ Reference< XRowLocate > xLocate( m_xCursor, UNO_QUERY );
+ OSL_ENSURE( xLocate.is(), "FormOperations::impl_moveLeft_throw: no XRowLocate!" );
+ if ( xLocate.is() )
+ xLocate->moveRelativeToBookmark( xLocate->getBookmark(), -1 );
+ }
+ else
+ {
+ if ( impl_isInsertionRow_throw() )
+ {
+ // we assume that the inserted record is now the last record in the
+ // result set
+ m_xCursor->last();
+ }
+ else
+ m_xCursor->previous();
+ }
+ }
+
+
+ void FormOperations::impl_moveRight_throw( ) const
+ {
+ OSL_PRECOND( impl_hasCursor_nothrow(), "FormOperations::impl_moveRight_throw: no cursor!" );
+ if ( !impl_hasCursor_nothrow() )
+ return;
+
+ sal_Bool bRecordInserted = false;
+ bool bSuccess = impl_commitCurrentRecord_throw( &bRecordInserted );
+
+ if ( !bSuccess )
+ return;
+
+ if ( bRecordInserted )
+ {
+ // go to insert row
+ m_xUpdateCursor->moveToInsertRow();
+ }
+ else
+ {
+ if ( m_xCursor->isLast() )
+ m_xUpdateCursor->moveToInsertRow();
+ else
+ (void)m_xCursor->next();
+ }
+ }
+
+
+ void FormOperations::impl_resetAllControls_nothrow() const
+ {
+ Reference< XIndexAccess > xContainer( m_xCursor, UNO_QUERY );
+ if ( !xContainer.is() )
+ return;
+
+ try
+ {
+ Reference< XReset > xReset;
+ sal_Int32 nCount( xContainer->getCount() );
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ if ( xContainer->getByIndex( i ) >>= xReset )
+ {
+ // no resets on sub forms
+ Reference< XForm > xAsForm( xReset, UNO_QUERY );
+ if ( !xAsForm.is() )
+ xReset->reset();
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.runtime");
+ }
+ }
+
+
+ void FormOperations::impl_executeAutoSort_throw( bool _bUp ) const
+ {
+ OSL_PRECOND( m_xController.is(), "FormOperations::impl_executeAutoSort_throw: need a controller for this!" );
+ OSL_PRECOND( impl_hasCursor_nothrow(), "FormOperations::impl_executeAutoSort_throw: need a cursor for this!" );
+ OSL_PRECOND( impl_isParseable_throw(), "FormOperations::impl_executeAutoSort_throw: need a parseable statement for this!" );
+ if ( !m_xController.is() || !impl_hasCursor_nothrow() || !impl_isParseable_throw() )
+ return;
+
+ try
+ {
+ Reference< XControl > xControl = m_xController->getCurrentControl();
+ if ( !xControl.is() || !impl_commitCurrentControl_throw() || !impl_commitCurrentRecord_throw() )
+ return;
+
+ Reference< XPropertySet > xBoundField( impl_getCurrentBoundField_nothrow() );
+ if ( !xBoundField.is() )
+ return;
+
+ OUString sOriginalSort;
+ m_xCursorProperties->getPropertyValue( PROPERTY_SORT ) >>= sOriginalSort;
+
+ // automatic sort by field is expected to always resets the previous sort order
+ m_xParser->setOrder( OUString() );
+
+ impl_appendOrderByColumn_throw aAction(this, xBoundField, _bUp);
+ impl_doActionInSQLContext_throw(std::move(aAction), RID_STR_COULD_NOT_SET_ORDER );
+
+ weld::WaitObject aWO(Application::GetFrameWeld(GetDialogParent()));
+ try
+ {
+ m_xCursorProperties->setPropertyValue( PROPERTY_SORT, Any( m_xParser->getOrder() ) );
+ m_xLoadableForm->reload();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.runtime", "FormOperations::impl_executeAutoSort_throw: caught an exception while setting the parser properties!" );
+ }
+
+
+ if ( !m_xLoadableForm->isLoaded() )
+ { // something went wrong -> restore the original state
+ try
+ {
+ m_xParser->setOrder( sOriginalSort );
+ m_xCursorProperties->setPropertyValue( PROPERTY_SORT, Any( m_xParser->getOrder() ) );
+ m_xLoadableForm->reload();
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "FormOperations::impl_executeAutoSort_throw: could not reset the form to its original state!" );
+ }
+
+ }
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const SQLException& ) { throw; }
+ catch( const Exception& )
+ {
+ throw WrappedTargetException( OUString(), *const_cast< FormOperations* >( this ), ::cppu::getCaughtException() );
+ }
+ }
+
+
+ void FormOperations::impl_executeAutoFilter_throw( ) const
+ {
+ OSL_PRECOND( m_xController.is(), "FormOperations::impl_executeAutoFilter_throw: need a controller for this!" );
+ OSL_PRECOND( impl_hasCursor_nothrow(), "FormOperations::impl_executeAutoFilter_throw: need a cursor for this!" );
+ OSL_PRECOND( impl_isParseable_throw(), "FormOperations::impl_executeAutoFilter_throw: need a parseable statement for this!" );
+ if ( !m_xController.is() || !impl_hasCursor_nothrow() || !impl_isParseable_throw() )
+ return;
+
+ try
+ {
+ Reference< XControl > xControl = m_xController->getCurrentControl();
+ if ( !xControl.is() || !impl_commitCurrentControl_throw() || !impl_commitCurrentRecord_throw() )
+ return;
+
+ Reference< XPropertySet > xBoundField( impl_getCurrentBoundField_nothrow() );
+ if ( !xBoundField.is() )
+ return;
+
+ OUString sOriginalFilter;
+ OUString sOriginalHaving;
+ m_xCursorProperties->getPropertyValue( PROPERTY_FILTER ) >>= sOriginalFilter;
+ m_xCursorProperties->getPropertyValue( PROPERTY_HAVINGCLAUSE ) >>= sOriginalHaving;
+ bool bApplied = true;
+ m_xCursorProperties->getPropertyValue( PROPERTY_APPLYFILTER ) >>= bApplied;
+
+ // if we have a filter, but it's not applied, then we have to overwrite it, else append one
+ if ( !bApplied )
+ {
+ m_xParser->setFilter( OUString() );
+ m_xParser->setHavingClause( OUString() );
+ }
+
+ impl_appendFilterByColumn_throw aAction(this, m_xParser, xBoundField);
+ impl_doActionInSQLContext_throw( std::move(aAction), RID_STR_COULD_NOT_SET_FILTER );
+
+ weld::WaitObject aWO(Application::GetFrameWeld(GetDialogParent()));
+ try
+ {
+ m_xCursorProperties->setPropertyValue( PROPERTY_FILTER, Any( m_xParser->getFilter() ) );
+ m_xCursorProperties->setPropertyValue( PROPERTY_HAVINGCLAUSE, Any( m_xParser->getHavingClause() ) );
+ m_xCursorProperties->setPropertyValue( PROPERTY_APPLYFILTER, Any( true ) );
+
+ m_xLoadableForm->reload();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.runtime", "FormOperations::impl_executeAutoFilter_throw: caught an exception while setting the parser properties!" );
+ }
+
+
+ if ( !m_xLoadableForm->isLoaded() )
+ { // something went wrong -> restore the original state
+ try
+ {
+ m_xParser->setFilter ( sOriginalFilter );
+ m_xParser->setHavingClause( sOriginalHaving );
+ m_xCursorProperties->setPropertyValue( PROPERTY_APPLYFILTER, Any( bApplied ) );
+ m_xCursorProperties->setPropertyValue( PROPERTY_FILTER, Any( m_xParser->getFilter() ) );
+ m_xCursorProperties->setPropertyValue( PROPERTY_HAVINGCLAUSE, Any( m_xParser->getHavingClause() ) );
+ m_xLoadableForm->reload();
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "FormOperations::impl_executeAutoFilter_throw: could not reset the form to its original state!" );
+ }
+
+ }
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const SQLException& ) { throw; }
+ catch( const Exception& )
+ {
+ throw WrappedTargetException( OUString(), *const_cast< FormOperations* >( this ), ::cppu::getCaughtException() );
+ }
+ }
+
+ css::uno::Reference<css::awt::XWindow> FormOperations::GetDialogParent() const
+ {
+ css::uno::Reference<css::awt::XWindow> xDialogParent;
+
+ //tdf#122152 extract parent for dialog
+ if (m_xController.is())
+ {
+ css::uno::Reference<css::awt::XControl> xContainerControl(m_xController->getContainer(), css::uno::UNO_QUERY);
+ if (xContainerControl.is())
+ {
+ css::uno::Reference<css::awt::XWindowPeer> xContainerPeer = xContainerControl->getPeer();
+ xDialogParent = css::uno::Reference<css::awt::XWindow>(xContainerPeer, css::uno::UNO_QUERY);
+ }
+ }
+
+ return xDialogParent;
+ }
+
+ void FormOperations::impl_executeFilterOrSort_throw( bool _bFilter ) const
+ {
+ OSL_PRECOND( m_xController.is(), "FormOperations::impl_executeFilterOrSort_throw: need a controller for this!" );
+ OSL_PRECOND( impl_hasCursor_nothrow(), "FormOperations::impl_executeFilterOrSort_throw: need a cursor for this!" );
+ OSL_PRECOND( impl_isParseable_throw(), "FormOperations::impl_executeFilterOrSort_throw: need a parseable statement for this!" );
+ if ( !m_xController.is() || !impl_hasCursor_nothrow() || !impl_isParseable_throw() )
+ return;
+
+ if ( !impl_commitCurrentControl_throw() || !impl_commitCurrentRecord_throw() )
+ return;
+ try
+ {
+ css::uno::Reference<css::awt::XWindow> xDialogParent(GetDialogParent());
+
+ Reference< XExecutableDialog> xDialog;
+ if ( _bFilter )
+ {
+ xDialog = css::sdb::FilterDialog::createWithQuery(m_xContext, m_xParser, m_xCursor,
+ xDialogParent);
+ }
+ else
+ {
+ xDialog = css::sdb::OrderDialog::createWithQuery(m_xContext, m_xParser, m_xCursorProperties,
+ xDialogParent);
+ }
+
+ if ( RET_OK == xDialog->execute() )
+ {
+ weld::WaitObject aWO(Application::GetFrameWeld(xDialogParent));
+ if ( _bFilter )
+ {
+ m_xCursorProperties->setPropertyValue( PROPERTY_FILTER, Any( m_xParser->getFilter() ) );
+ m_xCursorProperties->setPropertyValue( PROPERTY_HAVINGCLAUSE, Any( m_xParser->getHavingClause() ) );
+ }
+ else
+ m_xCursorProperties->setPropertyValue( PROPERTY_SORT, Any( m_xParser->getOrder() ) );
+ m_xLoadableForm->reload();
+ }
+
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const SQLException& ) { throw; }
+ catch( const Exception& )
+ {
+ throw WrappedTargetException( OUString(), *const_cast< FormOperations* >( this ), ::cppu::getCaughtException() );
+ }
+ }
+
+
+ template < typename FunctObj >
+ void FormOperations::impl_doActionInSQLContext_throw( FunctObj f, TranslateId pErrorResourceId ) const
+ {
+ try
+ {
+ f();
+ }
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ catch( const SQLException& )
+ {
+ if (!pErrorResourceId) // no information to prepend
+ throw;
+
+ SQLExceptionInfo aInfo( ::cppu::getCaughtException() );
+ OUString sAdditionalError( ResourceManager::loadString(pErrorResourceId) );
+ aInfo.prepend( sAdditionalError );
+ aInfo.doThrow();
+ }
+#endif
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ OUString sAdditionalError( ResourceManager::loadString(pErrorResourceId) );
+ throw WrappedTargetException( sAdditionalError, *const_cast< FormOperations* >( this ), ::cppu::getCaughtException() );
+ }
+ }
+
+
+} // namespace frm
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_forms_FormOperations_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::FormOperations(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/runtime/formoperations.hxx b/forms/source/runtime/formoperations.hxx
new file mode 100644
index 0000000000..a8d0ec45a4
--- /dev/null
+++ b/forms/source/runtime/formoperations.hxx
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/form/runtime/XFormOperations.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/sdb/SQLFilterOperator.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <connectivity/dbtools.hxx>
+#include <tools/long.hxx>
+#include <unotools/resmgr.hxx>
+#include <utility>
+
+namespace frm
+{
+
+
+ //= FormOperations
+
+ typedef ::cppu::WeakComponentImplHelper < css::form::runtime::XFormOperations
+ , css::lang::XInitialization
+ , css::lang::XServiceInfo
+ , css::beans::XPropertyChangeListener
+ , css::util::XModifyListener
+ , css::sdbc::XRowSetListener
+ > FormOperations_Base;
+
+ class FormOperations :public ::cppu::BaseMutex
+ ,public FormOperations_Base
+ {
+ public:
+ class MethodGuard;
+
+ private:
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ css::uno::Reference< css::form::runtime::XFormController > m_xController;
+ css::uno::Reference< css::sdbc::XRowSet > m_xCursor;
+ css::uno::Reference< css::sdbc::XResultSetUpdate > m_xUpdateCursor;
+ css::uno::Reference< css::beans::XPropertySet > m_xCursorProperties;
+ css::uno::Reference< css::form::XLoadable > m_xLoadableForm;
+ css::uno::Reference< css::form::runtime::XFeatureInvalidation > m_xFeatureInvalidation;
+ mutable css::uno::Reference< css::sdb::XSingleSelectQueryComposer > m_xParser;
+
+ bool m_bInitializedParser;
+ bool m_bActiveControlModified;
+ bool m_bConstructed;
+ #ifdef DBG_UTIL
+ mutable tools::Long
+ m_nMethodNestingLevel;
+ #endif
+
+ public:
+ explicit FormOperations( const css::uno::Reference< css::uno::XComponentContext >& _rxContext );
+
+ struct MethodAccess { friend class MethodGuard; private: MethodAccess() { } };
+
+ void enterMethod( MethodAccess ) const
+ {
+ m_aMutex.acquire();
+ impl_checkDisposed_throw();
+ #ifdef DBG_UTIL
+ ++m_nMethodNestingLevel;
+ #endif
+ }
+
+ void leaveMethod( MethodAccess ) const
+ {
+ m_aMutex.release();
+ #ifdef DBG_UTIL
+ --m_nMethodNestingLevel;
+ #endif
+ }
+
+ protected:
+ virtual ~FormOperations() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XFormOperations
+ virtual css::uno::Reference< css::sdbc::XRowSet > SAL_CALL getCursor() override;
+ virtual css::uno::Reference< css::sdbc::XResultSetUpdate > SAL_CALL getUpdateCursor() override;
+ virtual css::uno::Reference< css::form::runtime::XFormController > SAL_CALL getController() override;
+ virtual css::uno::Reference< css::form::runtime::XFeatureInvalidation > SAL_CALL getFeatureInvalidation() override;
+ virtual void SAL_CALL setFeatureInvalidation(const css::uno::Reference< css::form::runtime::XFeatureInvalidation > & the_value) override;
+ virtual css::form::runtime::FeatureState SAL_CALL getState(::sal_Int16 Feature) override;
+ virtual sal_Bool SAL_CALL isEnabled(::sal_Int16 Feature) override;
+ virtual void SAL_CALL execute(::sal_Int16 Feature) override;
+ virtual void SAL_CALL executeWithArguments(::sal_Int16 Feature, const css::uno::Sequence< css::beans::NamedValue >& Arguments) override;
+ virtual sal_Bool SAL_CALL commitCurrentRecord(sal_Bool & RecordInserted) override;
+ virtual sal_Bool SAL_CALL commitCurrentControl() override;
+ virtual sal_Bool SAL_CALL isInsertionRow() override;
+ virtual sal_Bool SAL_CALL isModifiedRow() override;
+
+ // XRowSetListener
+ virtual void SAL_CALL cursorMoved( const css::lang::EventObject& event ) override;
+ virtual void SAL_CALL rowChanged( const css::lang::EventObject& event ) override;
+ virtual void SAL_CALL rowSetChanged( const css::lang::EventObject& event ) override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& _rSource ) override;
+
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XComponent/OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ private:
+ // service constructors
+ void createWithFormController( const css::uno::Reference< css::form::runtime::XFormController >& _rxController );
+ void createWithForm( const css::uno::Reference< css::form::XForm >& _rxForm );
+
+ /** checks whether the instance is already disposed, and throws an exception if so
+ */
+ void impl_checkDisposed_throw() const;
+
+ /** initializes the instance after m_xController has been set
+ @precond
+ m_xController is not <NULL/>
+ */
+ void impl_initFromController_throw();
+
+ /** initializes the instance after m_xCursor has been set
+ @precond
+ m_xCursor is not <NULL/>
+ */
+ void impl_initFromForm_throw();
+
+ /// invalidate the full palette of features which we know
+ void impl_invalidateAllSupportedFeatures_nothrow( MethodGuard& _rClearForCallback ) const;
+
+ /** invalidate the features which depend on the "modified" state of the current control
+ of our controller
+ */
+ void impl_invalidateModifyDependentFeatures_nothrow( MethodGuard& _rClearForCallback ) const;
+
+ /** ensures that our parse is initialized, or at least that we attempted to do so
+ @precond
+ we're not disposed
+ */
+ void impl_ensureInitializedParser_nothrow();
+
+ /// disposes our parser, if we have one
+ void impl_disposeParser_nothrow();
+
+ /** determines whether our cursor can be moved left
+ @precond hasCursor()
+ */
+ bool impl_canMoveLeft_throw() const;
+
+ /** determines whether our cursor can be moved right
+ @precond hasCursor()
+ */
+ bool impl_canMoveRight_throw() const;
+
+ /// determines whether we're positioned on the insertion row
+ bool impl_isInsertionRow_throw() const;
+
+ /// retrieves the RowCount property of the form
+ sal_Int32 impl_getRowCount_throw() const;
+
+ /// retrieves the RowCountFinal property of the form
+ bool impl_isRowCountFinal_throw() const;
+
+ /// retrieves the IsModified property of the form
+ bool impl_isModifiedRow_throw() const;
+
+ /// determines whether we can parse the query of our form
+ bool impl_isParseable_throw() const;
+
+ /// determines if we have an active filter or order condition
+ bool impl_hasFilterOrOrder_throw() const;
+
+ /// determines whether our form is in "insert-only" mode
+ bool impl_isInsertOnlyForm_throw() const;
+
+ /** retrieves the column to which the current control of our controller is bound
+ @precond
+ m_xController.is()
+ */
+ css::uno::Reference< css::beans::XPropertySet >
+ impl_getCurrentBoundField_nothrow( ) const;
+
+ /** returns the control model of the current control
+
+ If the current control is a grid control, then the returned model is the
+ model of the current <em>column</em> in the grid.
+
+ @precond
+ m_xController.is()
+ */
+ css::uno::Reference< css::awt::XControlModel >
+ impl_getCurrentControlModel_throw() const;
+
+ /// determines if we have a valid cursor
+ bool impl_hasCursor_nothrow() const { return m_xCursorProperties.is(); }
+
+ /** determines the model position from a grid control column's view position
+
+ A grid control can have columns which are currently hidden, so the index of a
+ column in the view is not necessarily the same as its index in the model.
+ */
+ static sal_Int32 impl_gridView2ModelPos_nothrow( const css::uno::Reference< css::container::XIndexAccess >& _rxColumns, sal_Int16 _nViewPos );
+
+ /** moves our cursor one position to the left, caring for different possible
+ cursor states.
+
+ Before the movement is done, the current row is saved, if necessary.
+
+ @precond
+ canMoveLeft()
+ */
+ void impl_moveLeft_throw() const;
+
+ /** moves our cursor one position to the right, caring for different possible
+ cursor states.
+
+ Before the movement is done, the current row is saved, if necessary.
+
+ @precond
+ canMoveRight()
+ */
+ void impl_moveRight_throw( ) const;
+
+ /** impl-version of commitCurrentRecord, which can be called without caring for
+ an output parameter, and within const-contexts
+
+ @precond
+ our mutex is locked
+ */
+ bool impl_commitCurrentRecord_throw( sal_Bool* _pRecordInserted = nullptr ) const;
+
+ /** impl-version of commitCurrentControl, which can be called in const-contexts
+
+ @precond
+ our mutex is locked
+ */
+ bool impl_commitCurrentControl_throw() const;
+
+ /// resets all control models in our own form
+ void impl_resetAllControls_nothrow() const;
+
+ /// executes the "auto sort ascending" and "auto sort descending" features
+ void impl_executeAutoSort_throw( bool _bUp ) const;
+
+ /// executes the "auto filter" feature
+ void impl_executeAutoFilter_throw( ) const;
+
+ /// executes the interactive sort resp. filter feature
+ void impl_executeFilterOrSort_throw( bool _bFilter ) const;
+
+ private:
+ /** calls a (member) function, catches SQLExceptions, extends them with additional context information,
+ and rethrows them
+
+ @param f
+ a functionoid with no arguments to do the work
+ @param pErrorResourceId
+ the id of the resources string to use as error message
+ */
+ template < typename FunctObj >
+ void impl_doActionInSQLContext_throw( FunctObj f, TranslateId pErrorResourceId ) const;
+
+ // functionoid to call appendOrderByColumn
+ class impl_appendOrderByColumn_throw
+ {
+ public:
+ impl_appendOrderByColumn_throw(const FormOperations *pFO,
+ css::uno::Reference< css::beans::XPropertySet > xField,
+ bool bUp)
+ : m_pFO(pFO)
+ , m_xField(std::move(xField))
+ , m_bUp(bUp)
+ {};
+
+ void operator()() { m_pFO->m_xParser->appendOrderByColumn(m_xField, m_bUp); }
+ private:
+ const FormOperations *m_pFO;
+ css::uno::Reference< css::beans::XPropertySet > m_xField;
+ bool m_bUp;
+ };
+
+ // functionoid to call appendFilterByColumn
+ class impl_appendFilterByColumn_throw
+ {
+ public:
+ impl_appendFilterByColumn_throw(const FormOperations *pFO,
+ css::uno::Reference< css::sdb::XSingleSelectQueryComposer > xParser,
+ css::uno::Reference< css::beans::XPropertySet > xField)
+ : m_pFO(pFO)
+ , m_xParser(std::move(xParser))
+ , m_xField(std::move(xField))
+ {};
+
+ void operator()() {
+ if (dbtools::isAggregateColumn( m_xParser, m_xField ))
+ m_pFO->m_xParser->appendHavingClauseByColumn( m_xField, true, css::sdb::SQLFilterOperator::EQUAL );
+ else
+ m_pFO->m_xParser->appendFilterByColumn( m_xField, true, css::sdb::SQLFilterOperator::EQUAL );
+ }
+ private:
+ const FormOperations *m_pFO;
+ css::uno::Reference< css::sdb::XSingleSelectQueryComposer > m_xParser;
+ css::uno::Reference< css::beans::XPropertySet > m_xField;
+ };
+
+ private:
+ FormOperations( const FormOperations& ) = delete;
+ FormOperations& operator=( const FormOperations& ) = delete;
+
+ public:
+
+ css::uno::Reference<css::awt::XWindow> GetDialogParent() const;
+
+ class MethodGuard
+ {
+ FormOperations& m_rOwner;
+ bool m_bCleared;
+
+ public:
+ explicit MethodGuard( FormOperations& _rOwner )
+ :m_rOwner( _rOwner )
+ ,m_bCleared( false )
+ {
+ m_rOwner.enterMethod( FormOperations::MethodAccess() );
+ }
+
+ ~MethodGuard()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ if ( !m_bCleared )
+ m_rOwner.leaveMethod( FormOperations::MethodAccess() );
+ m_bCleared = true;
+ }
+ };
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/solar/component/navbarcontrol.cxx b/forms/source/solar/component/navbarcontrol.cxx
new file mode 100644
index 0000000000..ebea47d469
--- /dev/null
+++ b/forms/source/solar/component/navbarcontrol.cxx
@@ -0,0 +1,500 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "navbarcontrol.hxx"
+#include <frm_strings.hxx>
+#include <componenttools.hxx>
+#include <navtoolbar.hxx>
+#include <commandimageprovider.hxx>
+
+#include <com/sun/star/awt/XView.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/form/runtime/FormFeature.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+
+namespace frm
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::graphic;
+ namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
+
+ ONavigationBarControl::ONavigationBarControl( const Reference< XComponentContext >& _rxORB)
+ : m_xContext(_rxORB)
+ {
+ }
+
+
+ ONavigationBarControl::~ONavigationBarControl()
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( ONavigationBarControl, UnoControl, ONavigationBarControl_Base )
+
+
+ Any SAL_CALL ONavigationBarControl::queryAggregation( const Type& _rType )
+ {
+ Any aReturn = UnoControl::queryAggregation( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = ONavigationBarControl_Base::queryInterface( _rType );
+
+ return aReturn;
+ }
+
+
+ namespace
+ {
+
+ WinBits lcl_getWinBits_nothrow( const Reference< XControlModel >& _rxModel )
+ {
+ WinBits nBits = 0;
+ try
+ {
+ Reference< XPropertySet > xProps( _rxModel, UNO_QUERY );
+ if ( xProps.is() )
+ {
+ sal_Int16 nBorder = 0;
+ xProps->getPropertyValue( PROPERTY_BORDER ) >>= nBorder;
+ if ( nBorder )
+ nBits |= WB_BORDER;
+
+ bool bTabStop = false;
+ if ( xProps->getPropertyValue( PROPERTY_TABSTOP ) >>= bTabStop )
+ nBits |= ( bTabStop ? WB_TABSTOP : WB_NOTABSTOP );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.component");
+ }
+ return nBits;
+ }
+ }
+
+
+ void SAL_CALL ONavigationBarControl::createPeer( const Reference< XToolkit >& /*_rToolkit*/, const Reference< XWindowPeer >& _rParentPeer )
+ {
+ SolarMutexGuard aGuard;
+
+ if (getPeer().is())
+ return;
+
+ mbCreatingPeer = true;
+
+ // determine the VCL window for the parent
+ vcl::Window* pParentWin = nullptr;
+ if ( _rParentPeer.is() )
+ {
+ VCLXWindow* pParentXWin = dynamic_cast<VCLXWindow*>( _rParentPeer.get() );
+ if ( pParentXWin )
+ pParentWin = pParentXWin->GetWindow();
+ DBG_ASSERT( pParentWin, "ONavigationBarControl::createPeer: could not obtain the VCL-level parent window!" );
+ }
+
+ // create the peer
+ rtl::Reference<ONavigationBarPeer> pPeer = ONavigationBarPeer::Create( m_xContext, pParentWin, getModel() );
+ assert(pPeer && "ONavigationBarControl::createPeer: invalid peer returned!");
+
+ // announce the peer to the base class
+ setPeer( pPeer );
+
+ // initialize ourself (and thus the peer) with the model properties
+ updateFromModel();
+
+ Reference< XView > xPeerView( getPeer(), UNO_QUERY );
+ if ( xPeerView.is() )
+ {
+ xPeerView->setZoom( maComponentInfos.nZoomX, maComponentInfos.nZoomY );
+ xPeerView->setGraphics( mxGraphics );
+ }
+
+ // a lot of initial settings from our component infos
+ setPosSize( maComponentInfos.nX, maComponentInfos.nY, maComponentInfos.nWidth, maComponentInfos.nHeight, PosSize::POSSIZE );
+
+ pPeer->setVisible ( maComponentInfos.bVisible && !mbDesignMode );
+ pPeer->setEnable ( maComponentInfos.bEnable );
+ pPeer->setDesignMode( mbDesignMode );
+
+ peerCreated();
+
+ mbCreatingPeer = false;
+ }
+
+
+ OUString SAL_CALL ONavigationBarControl::getImplementationName()
+ {
+ return "com.sun.star.comp.form.ONavigationBarControl";
+ }
+
+
+ Sequence< OUString > SAL_CALL ONavigationBarControl::getSupportedServiceNames()
+ {
+ return { "com.sun.star.awt.UnoControl",
+ "com.sun.star.form.control.NavigationToolBar" };
+ }
+
+
+ void SAL_CALL ONavigationBarControl::registerDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ Reference< XDispatchProviderInterception > xTypedPeer(getPeer(), UNO_QUERY);
+ if (xTypedPeer.is())
+ {
+ xTypedPeer->registerDispatchProviderInterceptor(_rxInterceptor);
+ }
+ }
+
+
+ void SAL_CALL ONavigationBarControl::releaseDispatchProviderInterceptor( const Reference< XDispatchProviderInterceptor >& _rxInterceptor )
+ {
+ Reference< XDispatchProviderInterception > xTypedPeer(getPeer(), UNO_QUERY);
+ if (xTypedPeer.is())
+ {
+ xTypedPeer->releaseDispatchProviderInterceptor(_rxInterceptor);
+ }
+ }
+
+
+ void SAL_CALL ONavigationBarControl::setDesignMode( sal_Bool _bOn )
+ {
+ UnoControl::setDesignMode( _bOn );
+ Reference< XVclWindowPeer > xTypedPeer(getPeer(), UNO_QUERY);
+ if (xTypedPeer.is())
+ {
+ xTypedPeer->setDesignMode(_bOn);
+ }
+ }
+
+
+ // ONavigationBarPeer
+
+
+ rtl::Reference<ONavigationBarPeer> ONavigationBarPeer::Create( const Reference< XComponentContext >& _rxORB,
+ vcl::Window* _pParentWindow, const Reference< XControlModel >& _rxModel )
+ {
+ DBG_TESTSOLARMUTEX();
+
+ // the peer itself
+ rtl::Reference<ONavigationBarPeer> pPeer(new ONavigationBarPeer( _rxORB ));
+
+ // the VCL control for the peer
+ Reference< XModel > xContextDocument( getXModel( _rxModel ) );
+ Reference< XModuleManager2 > xModuleManager( ModuleManager::create(_rxORB) );
+ OUString sModuleID = xModuleManager->identify( xContextDocument );
+
+ VclPtrInstance<NavigationToolBar> pNavBar(
+ _pParentWindow,
+ lcl_getWinBits_nothrow( _rxModel ),
+ std::make_shared<DocumentCommandImageProvider>( _rxORB, xContextDocument ),
+ sModuleID
+ );
+
+ // some knittings
+ pNavBar->setDispatcher( pPeer.get() );
+ pNavBar->SetComponentInterface( pPeer );
+
+ // we want a faster repeating rate for the slots in this
+ // toolbox
+ AllSettings aSettings = pNavBar->GetSettings();
+ MouseSettings aMouseSettings = aSettings.GetMouseSettings();
+ aMouseSettings.SetButtonRepeat( 10 );
+ aSettings.SetMouseSettings( aMouseSettings );
+ pNavBar->SetSettings( aSettings, true );
+
+ // outta here
+ return pPeer;
+ }
+
+
+ ONavigationBarPeer::ONavigationBarPeer( const Reference< XComponentContext >& _rxORB )
+ :OFormNavigationHelper( _rxORB )
+ {
+ }
+
+
+ ONavigationBarPeer::~ONavigationBarPeer()
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( ONavigationBarPeer, VCLXWindow, OFormNavigationHelper )
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( ONavigationBarPeer, VCLXWindow, OFormNavigationHelper )
+
+
+ void SAL_CALL ONavigationBarPeer::dispose( )
+ {
+ VCLXWindow::dispose();
+ OFormNavigationHelper::dispose();
+ }
+
+
+ void SAL_CALL ONavigationBarPeer::setProperty( const OUString& _rPropertyName, const Any& _rValue )
+ {
+ SolarMutexGuard aGuard;
+
+ VclPtr< NavigationToolBar > pNavBar = GetAs< NavigationToolBar >();
+ if ( !pNavBar )
+ {
+ VCLXWindow::setProperty( _rPropertyName, _rValue );
+ return;
+ }
+
+ bool bVoid = !_rValue.hasValue();
+
+ bool bBoolValue = false;
+ Color nColor = COL_TRANSPARENT;
+
+ // TODO: more generic mechanisms for this (the grid control implementation,
+ // when used herein, will do the same stuff for lot of these)
+
+ if ( _rPropertyName == PROPERTY_BACKGROUNDCOLOR )
+ {
+ if ( bVoid )
+ {
+ pNavBar->SetBackground( pNavBar->GetSettings().GetStyleSettings().GetFaceColor() );
+ pNavBar->SetControlBackground();
+ }
+ else
+ {
+ OSL_VERIFY( _rValue >>= nColor );
+ Color aColor( nColor );
+ pNavBar->SetBackground( aColor );
+ pNavBar->SetControlBackground( aColor );
+ }
+ }
+ else if ( _rPropertyName == PROPERTY_TEXTLINECOLOR )
+ {
+ if ( bVoid )
+ {
+ pNavBar->SetTextLineColor();
+ }
+ else
+ {
+ OSL_VERIFY( _rValue >>= nColor );
+ pNavBar->SetTextLineColor( nColor );
+ }
+ }
+ else if ( _rPropertyName == PROPERTY_ICONSIZE )
+ {
+ sal_Int16 nInt16Value = 0;
+ OSL_VERIFY( _rValue >>= nInt16Value );
+ pNavBar->SetImageSize( nInt16Value ? NavigationToolBar::eLarge : NavigationToolBar::eSmall );
+ }
+ else if ( _rPropertyName == PROPERTY_SHOW_POSITION )
+ {
+ OSL_VERIFY( _rValue >>= bBoolValue );
+ pNavBar->ShowFunctionGroup( NavigationToolBar::ePosition, bBoolValue );
+ }
+ else if ( _rPropertyName == PROPERTY_SHOW_NAVIGATION )
+ {
+ OSL_VERIFY( _rValue >>= bBoolValue );
+ pNavBar->ShowFunctionGroup( NavigationToolBar::eNavigation, bBoolValue );
+ }
+ else if ( _rPropertyName == PROPERTY_SHOW_RECORDACTIONS )
+ {
+ OSL_VERIFY( _rValue >>= bBoolValue );
+ pNavBar->ShowFunctionGroup( NavigationToolBar::eRecordActions, bBoolValue );
+ }
+ else if ( _rPropertyName == PROPERTY_SHOW_FILTERSORT )
+ {
+ OSL_VERIFY( _rValue >>= bBoolValue );
+ pNavBar->ShowFunctionGroup( NavigationToolBar::eFilterSort, bBoolValue );
+ }
+ else
+ {
+ VCLXWindow::setProperty( _rPropertyName, _rValue );
+ }
+ }
+
+
+ Any SAL_CALL ONavigationBarPeer::getProperty( const OUString& _rPropertyName )
+ {
+ SolarMutexGuard aGuard;
+
+ Any aReturn;
+ VclPtr< NavigationToolBar > pNavBar = GetAs< NavigationToolBar >();
+
+ if ( _rPropertyName == PROPERTY_BACKGROUNDCOLOR )
+ {
+ aReturn <<= pNavBar->GetControlBackground();
+ }
+ else if ( _rPropertyName == PROPERTY_TEXTLINECOLOR )
+ {
+ aReturn <<= pNavBar->GetTextLineColor();
+ }
+ else if ( _rPropertyName == PROPERTY_ICONSIZE )
+ {
+ sal_Int16 nIconType = ( NavigationToolBar::eLarge == pNavBar->GetImageSize() )
+ ? 1 : 0;
+ aReturn <<= nIconType;
+ }
+ else if ( _rPropertyName == PROPERTY_SHOW_POSITION )
+ {
+ aReturn <<= pNavBar->IsFunctionGroupVisible( NavigationToolBar::ePosition );
+ }
+ else if ( _rPropertyName == PROPERTY_SHOW_NAVIGATION )
+ {
+ aReturn <<= pNavBar->IsFunctionGroupVisible( NavigationToolBar::eNavigation );
+ }
+ else if ( _rPropertyName == PROPERTY_SHOW_RECORDACTIONS )
+ {
+ aReturn <<= pNavBar->IsFunctionGroupVisible( NavigationToolBar::eRecordActions );
+ }
+ else if ( _rPropertyName == PROPERTY_SHOW_FILTERSORT )
+ {
+ aReturn <<= pNavBar->IsFunctionGroupVisible( NavigationToolBar::eFilterSort );
+ }
+ else
+ aReturn = VCLXWindow::getProperty( _rPropertyName );
+
+ return aReturn;
+ }
+
+
+ void ONavigationBarPeer::interceptorsChanged( )
+ {
+ if ( isDesignMode() )
+ // not interested in if we're in design mode
+ return;
+
+ OFormNavigationHelper::interceptorsChanged();
+ }
+
+
+ void ONavigationBarPeer::featureStateChanged( sal_Int16 _nFeatureId, bool _bEnabled )
+ {
+ // enable this button on the toolbox
+ VclPtr< NavigationToolBar > pNavBar = GetAs< NavigationToolBar >();
+ if ( pNavBar )
+ {
+ pNavBar->enableFeature( _nFeatureId, _bEnabled );
+
+ // is it a feature with additional state information?
+ if ( _nFeatureId == FormFeature::ToggleApplyFilter )
+ { // additional boolean state
+ pNavBar->checkFeature( _nFeatureId, getBooleanState( _nFeatureId ) );
+ }
+ else if ( _nFeatureId == FormFeature::TotalRecords )
+ {
+ pNavBar->setFeatureText( _nFeatureId, getStringState( _nFeatureId ) );
+ }
+ else if ( _nFeatureId == FormFeature::MoveAbsolute )
+ {
+ pNavBar->setFeatureText( _nFeatureId, OUString::number(getIntegerState(_nFeatureId)) );
+ }
+ }
+
+ // base class
+ OFormNavigationHelper::featureStateChanged( _nFeatureId, _bEnabled );
+ }
+
+
+ void ONavigationBarPeer::allFeatureStatesChanged( )
+ {
+ {
+ // force the control to update it's states
+ SolarMutexGuard g;
+ VclPtr< NavigationToolBar > pNavBar = GetAs< NavigationToolBar >();
+ if ( pNavBar )
+ pNavBar->setDispatcher( this );
+ }
+
+ // base class
+ OFormNavigationHelper::allFeatureStatesChanged( );
+ }
+
+
+ bool ONavigationBarPeer::isEnabled( sal_Int16 _nFeatureId ) const
+ {
+ if ( const_cast< ONavigationBarPeer* >( this )->isDesignMode() )
+ return false;
+
+ return OFormNavigationHelper::isEnabled( _nFeatureId );
+ }
+
+
+ void SAL_CALL ONavigationBarPeer::setDesignMode( sal_Bool _bOn )
+ {
+ VCLXWindow::setDesignMode( _bOn );
+
+ if ( _bOn )
+ disconnectDispatchers();
+ else
+ connectDispatchers();
+ // this will connect if not already connected and just update else
+ }
+
+
+ void SAL_CALL ONavigationBarPeer::disposing( const EventObject& _rSource )
+ {
+ VCLXWindow::disposing( _rSource );
+ OFormNavigationHelper::disposing( _rSource );
+ }
+
+
+ void ONavigationBarPeer::getSupportedFeatures( ::std::vector< sal_Int16 >& _rFeatureIds )
+ {
+ _rFeatureIds.push_back( FormFeature::MoveAbsolute );
+ _rFeatureIds.push_back( FormFeature::TotalRecords );
+ _rFeatureIds.push_back( FormFeature::MoveToFirst );
+ _rFeatureIds.push_back( FormFeature::MoveToPrevious );
+ _rFeatureIds.push_back( FormFeature::MoveToNext );
+ _rFeatureIds.push_back( FormFeature::MoveToLast );
+ _rFeatureIds.push_back( FormFeature::SaveRecordChanges );
+ _rFeatureIds.push_back( FormFeature::UndoRecordChanges );
+ _rFeatureIds.push_back( FormFeature::MoveToInsertRow );
+ _rFeatureIds.push_back( FormFeature::DeleteRecord );
+ _rFeatureIds.push_back( FormFeature::ReloadForm );
+ _rFeatureIds.push_back( FormFeature::RefreshCurrentControl );
+ _rFeatureIds.push_back( FormFeature::SortAscending );
+ _rFeatureIds.push_back( FormFeature::SortDescending );
+ _rFeatureIds.push_back( FormFeature::InteractiveSort );
+ _rFeatureIds.push_back( FormFeature::AutoFilter );
+ _rFeatureIds.push_back( FormFeature::InteractiveFilter );
+ _rFeatureIds.push_back( FormFeature::ToggleApplyFilter );
+ _rFeatureIds.push_back( FormFeature::RemoveFilterAndSort );
+ }
+
+} // namespace frm
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_form_ONavigationBarControl_get_implementation (css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new frm::ONavigationBarControl(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/solar/component/navbarcontrol.hxx b/forms/source/solar/component/navbarcontrol.hxx
new file mode 100644
index 0000000000..e6e8cc1fe5
--- /dev/null
+++ b/forms/source/solar/component/navbarcontrol.hxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <formnavigation.hxx>
+
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+
+#include <toolkit/controls/unocontrol.hxx>
+#include <toolkit/awt/vclxwindow.hxx>
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/implbase1.hxx>
+
+
+namespace frm
+{
+
+ typedef ::cppu::ImplHelper1 < css::frame::XDispatchProviderInterception
+ > ONavigationBarControl_Base;
+
+ class ONavigationBarControl
+ :public UnoControl
+ ,public ONavigationBarControl_Base
+ {
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ public:
+ explicit ONavigationBarControl(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxORB
+ );
+
+ protected:
+ virtual ~ONavigationBarControl() override;
+
+ // UNO
+ DECLARE_UNO3_AGG_DEFAULTS( ONavigationBarControl, UnoControl )
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ // XControl
+ virtual void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& _rToolkit, const css::uno::Reference< css::awt::XWindowPeer >& _rParent ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+
+ // XVclWindowPeer
+ virtual void SAL_CALL setDesignMode( sal_Bool _bOn ) override;
+
+ // XDispatchProviderInterception
+ 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;
+ };
+
+ class ONavigationBarPeer final
+ :public VCLXWindow
+ ,public OFormNavigationHelper
+ {
+ public:
+ /** factory method
+ */
+ static rtl::Reference<ONavigationBarPeer> Create(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxORB,
+ vcl::Window* _pParentWindow,
+ const css::uno::Reference< css::awt::XControlModel >& _rxModel
+ );
+
+ // XInterface
+ DECLARE_XINTERFACE( )
+
+ // XVclWindowPeer
+ virtual void SAL_CALL setDesignMode( sal_Bool _bOn ) override;
+
+ // XWindow2
+ using VCLXWindow::isEnabled;
+
+ private:
+ explicit ONavigationBarPeer(
+ const css::uno::Reference< css::uno::XComponentContext >& _rxORB
+ );
+ virtual ~ONavigationBarPeer() override;
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER( )
+
+ // XComponent
+ void SAL_CALL dispose( ) override;
+
+ // XVclWindowPeer
+ void SAL_CALL setProperty( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override;
+ css::uno::Any SAL_CALL getProperty( const OUString& _rPropertyName ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // OFormNavigationHelper overriables
+ virtual void interceptorsChanged( ) override;
+ virtual void featureStateChanged( sal_Int16 _nFeatureId, bool _bEnabled ) override;
+ virtual void allFeatureStatesChanged( ) override;
+ virtual void getSupportedFeatures( ::std::vector< sal_Int16 >& /* [out] */ _rFeatureIds ) override;
+
+ // IFeatureDispatcher overriables
+ virtual bool isEnabled( sal_Int16 _nFeatureId ) const override;
+ };
+
+
+} // namespace frm
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/solar/control/navtoolbar.cxx b/forms/source/solar/control/navtoolbar.cxx
new file mode 100644
index 0000000000..7193ac4532
--- /dev/null
+++ b/forms/source/solar/control/navtoolbar.cxx
@@ -0,0 +1,641 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <navtoolbar.hxx>
+#include <frm_resource.hxx>
+#include <featuredispatcher.hxx>
+#include <strings.hrc>
+#include <commandimageprovider.hxx>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/form/runtime/FormFeature.hpp>
+
+#include <svx/labelitemwindow.hxx>
+
+#include <utility>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/toolbox.hxx>
+
+#include <sal/macros.h>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+
+#define LID_RECORD_LABEL 1000
+#define LID_RECORD_FILLER 1001
+
+
+namespace frm
+{
+
+
+ using ::com::sun::star::uno::Any;
+ namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
+
+
+ namespace
+ {
+ bool isArtificialItem( sal_Int16 _nFeatureId )
+ {
+ return ( _nFeatureId == LID_RECORD_LABEL )
+ || ( _nFeatureId == LID_RECORD_FILLER );
+ }
+
+ OUString getLabelString(TranslateId pResId)
+ {
+ OUString sLabel( " " + ResourceManager::loadString(pResId) + " " );
+ return sLabel;
+ }
+
+ OUString lcl_getCommandURL( const sal_Int16 _nFormFeature )
+ {
+ const char* pAsciiCommandName = nullptr;
+ switch ( _nFormFeature )
+ {
+ case FormFeature::MoveAbsolute : pAsciiCommandName = "AbsoluteRecord"; break;
+ case FormFeature::TotalRecords : pAsciiCommandName = "RecTotal"; break;
+ case FormFeature::MoveToFirst : pAsciiCommandName = "FirstRecord"; break;
+ case FormFeature::MoveToPrevious : pAsciiCommandName = "PrevRecord"; break;
+ case FormFeature::MoveToNext : pAsciiCommandName = "NextRecord"; break;
+ case FormFeature::MoveToLast : pAsciiCommandName = "LastRecord"; break;
+ case FormFeature::SaveRecordChanges : pAsciiCommandName = "RecSave"; break;
+ case FormFeature::UndoRecordChanges : pAsciiCommandName = "RecUndo"; break;
+ case FormFeature::MoveToInsertRow : pAsciiCommandName = "NewRecord"; break;
+ case FormFeature::DeleteRecord : pAsciiCommandName = "DeleteRecord"; break;
+ case FormFeature::ReloadForm : pAsciiCommandName = "Refresh"; break;
+ case FormFeature::RefreshCurrentControl : pAsciiCommandName = "RefreshFormControl"; break;
+ case FormFeature::SortAscending : pAsciiCommandName = "Sortup"; break;
+ case FormFeature::SortDescending : pAsciiCommandName = "SortDown"; break;
+ case FormFeature::InteractiveSort : pAsciiCommandName = "OrderCrit"; break;
+ case FormFeature::AutoFilter : pAsciiCommandName = "AutoFilter"; break;
+ case FormFeature::InteractiveFilter : pAsciiCommandName = "FilterCrit"; break;
+ case FormFeature::ToggleApplyFilter : pAsciiCommandName = "FormFiltered"; break;
+ case FormFeature::RemoveFilterAndSort : pAsciiCommandName = "RemoveFilterSort"; break;
+ }
+ if ( pAsciiCommandName != nullptr )
+ return ".uno:" + OUString::createFromAscii( pAsciiCommandName );
+
+ return OUString();
+ }
+ }
+
+ class ImplNavToolBar : public ToolBox
+ {
+ protected:
+ const IFeatureDispatcher* m_pDispatcher;
+
+ public:
+ explicit ImplNavToolBar( vcl::Window* _pParent )
+ :ToolBox( _pParent, WB_3DLOOK )
+ ,m_pDispatcher( nullptr )
+ {
+ }
+
+ void setDispatcher( const IFeatureDispatcher* _pDispatcher )
+ {
+ m_pDispatcher = _pDispatcher;
+ }
+
+ protected:
+ // ToolBox overridables
+ virtual void Select() override;
+
+ };
+
+
+ void ImplNavToolBar::Select()
+ {
+ if ( m_pDispatcher )
+ {
+ sal_Int16 nFeatureId = sal_uInt16(GetCurItemId());
+ if ( !m_pDispatcher->isEnabled( nFeatureId ) )
+ // the toolbox is a little bit buggy: With ToolBoxItemBits::REPEAT, it sometimes
+ // happens that a select is reported, even though the respective
+ // item has just been disabled.
+ return;
+ m_pDispatcher->dispatch( nFeatureId );
+ }
+ }
+
+ NavigationToolBar::NavigationToolBar( vcl::Window* _pParent, WinBits _nStyle,
+ PCommandImageProvider _pImageProvider,
+ OUString sModuleId )
+ :Window( _pParent, _nStyle )
+ ,m_pDispatcher( nullptr )
+ ,m_pImageProvider(std::move( _pImageProvider ))
+ ,m_eImageSize( eSmall )
+ ,m_pToolbar( nullptr )
+ ,m_sModuleId(std::move( sModuleId ))
+ {
+ implInit( );
+ }
+
+ NavigationToolBar::~NavigationToolBar( )
+ {
+ disposeOnce();
+ }
+
+ void NavigationToolBar::dispose()
+ {
+ for (auto & childWin : m_aChildWins)
+ childWin.disposeAndClear();
+ m_aChildWins.clear();
+ m_pToolbar.disposeAndClear();
+ vcl::Window::dispose();
+ }
+
+ void NavigationToolBar::setDispatcher( const IFeatureDispatcher* _pDispatcher )
+ {
+ m_pDispatcher = _pDispatcher;
+
+ m_pToolbar->setDispatcher( _pDispatcher );
+
+ RecordPositionInput* pPositionWindow = static_cast< RecordPositionInput* >( m_pToolbar->GetItemWindow( ToolBoxItemId(FormFeature::MoveAbsolute) ) );
+ OSL_ENSURE( pPositionWindow, "NavigationToolBar::setDispatcher: can't forward the dispatcher to the position window!" );
+ if ( pPositionWindow )
+ pPositionWindow->setDispatcher( _pDispatcher );
+
+ // update feature states
+ for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < m_pToolbar->GetItemCount(); ++nPos )
+ {
+ sal_uInt16 nItemId = sal_uInt16(m_pToolbar->GetItemId( nPos ));
+
+ if ( ( nItemId == LID_RECORD_LABEL ) || ( nItemId == LID_RECORD_FILLER ) )
+ continue;
+
+ // is this item enabled?
+ bool bEnabled = m_pDispatcher && m_pDispatcher->isEnabled( nItemId );
+ implEnableItem( nItemId, bEnabled );
+ }
+ }
+
+ void NavigationToolBar::implEnableItem( sal_uInt16 _nItemId, bool _bEnabled )
+ {
+ m_pToolbar->EnableItem( ToolBoxItemId(_nItemId), _bEnabled );
+
+ if ( _nItemId == FormFeature::MoveAbsolute )
+ m_pToolbar->EnableItem( ToolBoxItemId(LID_RECORD_LABEL), _bEnabled );
+
+ if ( _nItemId == FormFeature::TotalRecords )
+ m_pToolbar->EnableItem( ToolBoxItemId(LID_RECORD_FILLER), _bEnabled );
+ }
+
+ void NavigationToolBar::enableFeature( sal_Int16 _nFeatureId, bool _bEnabled )
+ {
+ DBG_ASSERT( m_pToolbar->GetItemPos( ToolBoxItemId(_nFeatureId) ) != ToolBox::ITEM_NOTFOUND,
+ "NavigationToolBar::enableFeature: invalid id!" );
+
+ implEnableItem( static_cast<sal_uInt16>(_nFeatureId), _bEnabled );
+ }
+
+ void NavigationToolBar::checkFeature( sal_Int16 _nFeatureId, bool _bEnabled )
+ {
+ DBG_ASSERT( m_pToolbar->GetItemPos( ToolBoxItemId(_nFeatureId) ) != ToolBox::ITEM_NOTFOUND,
+ "NavigationToolBar::checkFeature: invalid id!" );
+
+ m_pToolbar->CheckItem( ToolBoxItemId(_nFeatureId), _bEnabled );
+ }
+
+ void NavigationToolBar::setFeatureText( sal_Int16 _nFeatureId, const OUString& _rText )
+ {
+ DBG_ASSERT( m_pToolbar->GetItemPos( ToolBoxItemId(_nFeatureId) ) != ToolBox::ITEM_NOTFOUND,
+ "NavigationToolBar::checkFeature: invalid id!" );
+
+ vcl::Window* pItemWindow = m_pToolbar->GetItemWindow( ToolBoxItemId(_nFeatureId) );
+ if ( pItemWindow )
+ {
+ if (_nFeatureId == FormFeature::TotalRecords)
+ static_cast<LabelItemWindow*>(pItemWindow)->set_label(_rText);
+ else if (_nFeatureId == FormFeature::MoveAbsolute)
+ static_cast<RecordPositionInput*>(pItemWindow)->set_text(_rText);
+ }
+ else
+ m_pToolbar->SetItemText( ToolBoxItemId(_nFeatureId), _rText );
+ }
+
+ void NavigationToolBar::implInit( )
+ {
+ m_pToolbar = VclPtr<ImplNavToolBar>::Create( this );
+ m_pToolbar->Show();
+
+ // need the SfxApplication for retrieving information about our
+ // items. We could duplicate all the information here in our lib
+ // (such as the item text and the image), but why should we?
+
+ static struct FeatureDescription
+ {
+ sal_uInt16 nId;
+ bool bRepeat;
+ bool bItemWindow;
+ } const aSupportedFeatures[] =
+ {
+ { LID_RECORD_LABEL, false, true },
+ { FormFeature::MoveAbsolute, false, true },
+ { LID_RECORD_FILLER, false, true },
+ { FormFeature::TotalRecords, false, true },
+ { FormFeature::MoveToFirst, true, false },
+ { FormFeature::MoveToPrevious, true, false },
+ { FormFeature::MoveToNext, true, false },
+ { FormFeature::MoveToLast, true, false },
+ { FormFeature::MoveToInsertRow, false, false },
+ { 0, false, false },
+ { FormFeature::SaveRecordChanges, false, false },
+ { FormFeature::UndoRecordChanges, false, false },
+ { FormFeature::DeleteRecord, false, false },
+ { FormFeature::ReloadForm, false, false },
+ { FormFeature::RefreshCurrentControl, false, false },
+ { 0, false, false },
+ { FormFeature::SortAscending, false, false },
+ { FormFeature::SortDescending, false, false },
+ { FormFeature::InteractiveSort, false, false },
+ { FormFeature::AutoFilter, false, false },
+ { FormFeature::InteractiveFilter, false, false },
+ { FormFeature::ToggleApplyFilter, false, false },
+ { FormFeature::RemoveFilterAndSort, false, false },
+ };
+
+ FeatureDescription const * pSupportedFeatures = aSupportedFeatures;
+ FeatureDescription const * pSupportedFeaturesEnd = aSupportedFeatures + SAL_N_ELEMENTS( aSupportedFeatures );
+ for ( ; pSupportedFeatures < pSupportedFeaturesEnd; ++pSupportedFeatures )
+ {
+ if ( pSupportedFeatures->nId )
+ { // it's _not_ a separator
+
+ // insert the entry
+ OUString sCommandURL( lcl_getCommandURL( pSupportedFeatures->nId ) );
+ m_pToolbar->InsertItem( ToolBoxItemId(pSupportedFeatures->nId), OUString(), sCommandURL, pSupportedFeatures->bRepeat ? ToolBoxItemBits::REPEAT : ToolBoxItemBits::NONE );
+ m_pToolbar->SetQuickHelpText( ToolBoxItemId(pSupportedFeatures->nId), OUString() ); // TODO
+
+ if ( !isArtificialItem( pSupportedFeatures->nId ) )
+ {
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sCommandURL, m_sModuleId);
+ m_pToolbar->SetQuickHelpText(ToolBoxItemId(pSupportedFeatures->nId),
+ vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
+ }
+
+ if ( pSupportedFeatures->bItemWindow )
+ {
+ vcl::Window* pItemWindow = nullptr;
+ if ( FormFeature::MoveAbsolute == pSupportedFeatures->nId )
+ {
+ pItemWindow = VclPtr<RecordPositionInput>::Create( m_pToolbar );
+ static_cast< RecordPositionInput* >( pItemWindow )->setDispatcher( m_pDispatcher );
+ }
+ else if (pSupportedFeatures->nId == LID_RECORD_FILLER)
+ pItemWindow = VclPtr<LabelItemWindow>::Create(m_pToolbar, getLabelString(RID_STR_LABEL_OF));
+ else if (pSupportedFeatures->nId == LID_RECORD_LABEL)
+ pItemWindow = VclPtr<LabelItemWindow>::Create(m_pToolbar, getLabelString(RID_STR_LABEL_RECORD));
+ else if (pSupportedFeatures->nId == FormFeature::TotalRecords)
+ pItemWindow = VclPtr<LabelItemWindow>::Create(m_pToolbar, "");
+
+ m_aChildWins.emplace_back(pItemWindow );
+ m_pToolbar->SetItemWindow( ToolBoxItemId(pSupportedFeatures->nId), pItemWindow );
+ }
+ }
+ else
+ { // a separator
+ m_pToolbar->InsertSeparator( );
+ }
+ }
+
+ forEachItemWindow( &NavigationToolBar::adjustItemWindowWidth );
+
+ implUpdateImages();
+ }
+
+
+ void NavigationToolBar::implUpdateImages()
+ {
+ OSL_ENSURE( m_pImageProvider, "NavigationToolBar::implUpdateImages: no image provider => no images!" );
+ if ( !m_pImageProvider )
+ return;
+
+ const ToolBox::ImplToolItems::size_type nItemCount = m_pToolbar->GetItemCount();
+
+ // collect the FormFeatures in the toolbar
+ std::vector<sal_Int16> aFormFeatures;
+ aFormFeatures.reserve( nItemCount );
+
+ for ( ToolBox::ImplToolItems::size_type i=0; i<nItemCount; ++i )
+ {
+ ToolBoxItemId nId = m_pToolbar->GetItemId( i );
+ if ( ( ToolBoxItemType::BUTTON == m_pToolbar->GetItemType( i ) ) && !isArtificialItem( sal_uInt16(nId) ) )
+ aFormFeatures.push_back( sal_uInt16(nId) );
+ }
+
+ // translate them into command URLs
+ css::uno::Sequence< OUString > aCommandURLs( aFormFeatures.size() );
+ auto aCommandURLsRange = asNonConstRange(aCommandURLs);
+ size_t i = 0;
+ for (auto const& formFeature : aFormFeatures)
+ {
+ aCommandURLsRange[i++] = lcl_getCommandURL(formFeature);
+ }
+
+ // retrieve the images for the command URLs
+ std::vector<Image> aCommandImages = m_pImageProvider->getCommandImages( aCommandURLs, m_eImageSize == eLarge );
+
+ // and set them at the toolbar
+ auto commandImage = aCommandImages.begin();
+ for (sal_Int16 formFeature : aFormFeatures)
+ {
+ m_pToolbar->SetItemImage( ToolBoxItemId(formFeature), *commandImage );
+ ++commandImage;
+ }
+
+ // parts of our layout is dependent on the size of our icons
+ Resize();
+ }
+
+
+ void NavigationToolBar::implSetImageSize( ImageSize _eSize )
+ {
+ if ( _eSize != m_eImageSize )
+ {
+ m_eImageSize = _eSize;
+ implUpdateImages();
+ }
+ }
+
+
+ void NavigationToolBar::SetImageSize( ImageSize _eSize )
+ {
+ implSetImageSize( _eSize );
+ }
+
+
+ void NavigationToolBar::ShowFunctionGroup( FunctionGroup _eGroup, bool _bShow )
+ {
+ const sal_uInt16* pGroupIds = nullptr;
+
+ switch ( _eGroup )
+ {
+ case ePosition:
+ {
+ static const sal_uInt16 aPositionIds[] = {
+ LID_RECORD_LABEL, FormFeature::MoveAbsolute, LID_RECORD_FILLER, FormFeature::TotalRecords, 0
+ };
+ pGroupIds = aPositionIds;
+ }
+ break;
+ case eNavigation:
+ {
+ static const sal_uInt16 aNavigationIds[] = {
+ FormFeature::MoveToFirst, FormFeature::MoveToPrevious, FormFeature::MoveToNext, FormFeature::MoveToLast, FormFeature::MoveToInsertRow, 0
+ };
+ pGroupIds = aNavigationIds;
+ }
+ break;
+ case eRecordActions:
+ {
+ static const sal_uInt16 aActionIds[] = {
+ FormFeature::SaveRecordChanges, FormFeature::UndoRecordChanges, FormFeature::DeleteRecord, FormFeature::ReloadForm, FormFeature::RefreshCurrentControl, 0
+ };
+ pGroupIds = aActionIds;
+ }
+ break;
+ case eFilterSort:
+ {
+ static const sal_uInt16 aFilterSortIds[] = {
+ FormFeature::SortAscending, FormFeature::SortDescending, FormFeature::InteractiveSort, FormFeature::AutoFilter, FormFeature::InteractiveFilter, FormFeature::ToggleApplyFilter, FormFeature::RemoveFilterAndSort, 0
+ };
+ pGroupIds = aFilterSortIds;
+ }
+ break;
+ default:
+ OSL_FAIL( "NavigationToolBar::ShowFunctionGroup: invalid group id!" );
+ }
+
+ if ( pGroupIds )
+ while ( *pGroupIds )
+ m_pToolbar->ShowItem( ToolBoxItemId(*pGroupIds++), _bShow );
+ }
+
+
+ bool NavigationToolBar::IsFunctionGroupVisible( FunctionGroup _eGroup )
+ {
+ sal_uInt16 nIndicatorItem = 0;
+ switch ( _eGroup )
+ {
+ case ePosition : nIndicatorItem = LID_RECORD_LABEL; break;
+ case eNavigation : nIndicatorItem = FormFeature::MoveToFirst; break;
+ case eRecordActions : nIndicatorItem = FormFeature::SaveRecordChanges; break;
+ case eFilterSort : nIndicatorItem = FormFeature::SortAscending; break;
+ default:
+ OSL_FAIL( "NavigationToolBar::IsFunctionGroupVisible: invalid group id!" );
+ }
+
+ return m_pToolbar->IsItemVisible( ToolBoxItemId(nIndicatorItem) );
+ }
+
+
+ void NavigationToolBar::StateChanged( StateChangedType nType )
+ {
+ Window::StateChanged( nType );
+
+ switch ( nType )
+ {
+ case StateChangedType::Zoom:
+// m_pToolbar->SetZoom( GetZoom() );
+// forEachItemWindow( setItemWindowZoom, NULL );
+ // the ToolBox class is not zoomable at the moment, so
+ // we better have no zoom at all instead of only half a zoom ...
+ break;
+
+ case StateChangedType::ControlFont:
+ forEachItemWindow( &NavigationToolBar::setItemControlFont );
+ forEachItemWindow( &NavigationToolBar::adjustItemWindowWidth );
+ break;
+
+ case StateChangedType::ControlForeground:
+ forEachItemWindow( &NavigationToolBar::setItemControlForeground );
+ break;
+
+ case StateChangedType::Mirroring:
+ {
+ sal_Bool bIsRTLEnabled( IsRTLEnabled() );
+ m_pToolbar->EnableRTL( bIsRTLEnabled );
+ forEachItemWindow( &NavigationToolBar::enableItemRTL, &bIsRTLEnabled );
+ Resize();
+ }
+ break;
+ default:;
+ }
+ }
+
+ void NavigationToolBar::Resize()
+ {
+ // resize/position the toolbox as a whole
+ sal_Int32 nToolbarHeight = m_pToolbar->CalcWindowSizePixel().Height();
+
+ sal_Int32 nMyHeight = GetOutputSizePixel().Height();
+ m_pToolbar->SetPosSizePixel( Point( 0, ( nMyHeight - nToolbarHeight ) / 2 ),
+ Size( GetSizePixel().Width(), nToolbarHeight ) );
+
+ Window::Resize();
+ }
+
+ void NavigationToolBar::SetControlBackground()
+ {
+ Window::SetControlBackground();
+ m_pToolbar->SetControlBackground();
+ forEachItemWindow( &NavigationToolBar::setItemBackground, nullptr );
+
+ implUpdateImages();
+ }
+
+ void NavigationToolBar::SetControlBackground( const Color& _rColor )
+ {
+ Window::SetControlBackground( _rColor );
+ m_pToolbar->SetControlBackground( _rColor );
+ forEachItemWindow( &NavigationToolBar::setItemBackground, &_rColor );
+
+ implUpdateImages();
+ }
+
+ void NavigationToolBar::SetTextLineColor( )
+ {
+ Window::SetTextLineColor( );
+ m_pToolbar->SetTextLineColor( );
+ forEachItemWindow( &NavigationToolBar::setTextLineColor, nullptr );
+ }
+
+ void NavigationToolBar::SetTextLineColor( const Color& _rColor )
+ {
+ Window::SetTextLineColor( _rColor );
+ m_pToolbar->SetTextLineColor( _rColor );
+ forEachItemWindow( &NavigationToolBar::setTextLineColor, &_rColor );
+ }
+
+ void NavigationToolBar::forEachItemWindow( ItemWindowHandler _handler )
+ {
+ for ( ToolBox::ImplToolItems::size_type item = 0; item < m_pToolbar->GetItemCount(); ++item )
+ {
+ ToolBoxItemId nItemId = m_pToolbar->GetItemId( item );
+ vcl::Window* pItemWindow = m_pToolbar->GetItemWindow( nItemId );
+ if ( pItemWindow )
+ (this->*_handler)( sal_uInt16(nItemId), pItemWindow );
+ }
+ }
+
+ void NavigationToolBar::forEachItemWindow( ItemWindowHandler2 _handler, const void* _pParam )
+ {
+ for ( ToolBox::ImplToolItems::size_type item = 0; item < m_pToolbar->GetItemCount(); ++item )
+ {
+ ToolBoxItemId nItemId = m_pToolbar->GetItemId( item );
+ vcl::Window* pItemWindow = m_pToolbar->GetItemWindow( nItemId );
+ if ( pItemWindow )
+ (*_handler)( sal_uInt16(nItemId), pItemWindow, _pParam );
+ }
+ }
+
+ void NavigationToolBar::setItemBackground( sal_uInt16 /* _nItemId */, vcl::Window* _pItemWindow, const void* _pColor )
+ {
+ if ( _pColor )
+ _pItemWindow->SetControlBackground( *static_cast< const Color* >( _pColor ) );
+ else
+ _pItemWindow->SetControlBackground();
+ }
+
+ void NavigationToolBar::setTextLineColor( sal_uInt16 /* _nItemId */, vcl::Window* _pItemWindow, const void* _pColor )
+ {
+ if ( _pColor )
+ _pItemWindow->SetTextLineColor( *static_cast< const Color* >( _pColor ) );
+ else
+ _pItemWindow->SetTextLineColor();
+ }
+
+ void NavigationToolBar::setItemControlFont( sal_uInt16 /* _nItemId */, vcl::Window* _pItemWindow ) const
+ {
+ if ( IsControlFont() )
+ _pItemWindow->SetControlFont( GetControlFont() );
+ else
+ _pItemWindow->SetControlFont( );
+ }
+
+ void NavigationToolBar::setItemControlForeground( sal_uInt16 /* _nItemId */, vcl::Window* _pItemWindow ) const
+ {
+ if ( IsControlForeground() )
+ _pItemWindow->SetControlForeground( GetControlForeground() );
+ else
+ _pItemWindow->SetControlForeground( );
+ _pItemWindow->SetTextColor( GetTextColor() );
+ }
+
+ void NavigationToolBar::adjustItemWindowWidth( sal_uInt16 _nItemId, vcl::Window* _pItemWindow ) const
+ {
+ int nHeight = 0;
+
+ OUString sItemText;
+ switch ( _nItemId )
+ {
+ case LID_RECORD_LABEL:
+ sItemText = getLabelString( RID_STR_LABEL_RECORD );
+ break;
+
+ case LID_RECORD_FILLER:
+ sItemText = getLabelString( RID_STR_LABEL_OF );
+ break;
+
+ case FormFeature::MoveAbsolute:
+ sItemText = "12345678";
+ nHeight = _pItemWindow->get_preferred_size().Height();
+ break;
+
+ case FormFeature::TotalRecords:
+ sItemText = "123456";
+ break;
+ }
+
+ if (nHeight == 0)
+ nHeight = _pItemWindow->GetTextHeight() + 4;
+
+ Size aSize(_pItemWindow->GetTextWidth(sItemText), nHeight);
+ aSize.AdjustWidth(6 );
+ _pItemWindow->SetSizePixel( aSize );
+
+ m_pToolbar->SetItemWindow( ToolBoxItemId(_nItemId), _pItemWindow );
+ }
+
+ void NavigationToolBar::enableItemRTL( sal_uInt16 /*_nItemId*/, vcl::Window* _pItemWindow, const void* _pIsRTLEnabled )
+ {
+ _pItemWindow->EnableRTL( *static_cast< const sal_Bool* >( _pIsRTLEnabled ) );
+ }
+
+ RecordPositionInput::RecordPositionInput(vcl::Window* pParent)
+ : RecordItemWindow(pParent)
+ , m_pDispatcher( nullptr )
+ {
+ }
+
+ void RecordPositionInput::setDispatcher( const IFeatureDispatcher* _pDispatcher )
+ {
+ m_pDispatcher = _pDispatcher;
+ }
+
+ void RecordPositionInput::PositionFired(sal_Int64 nRecord)
+ {
+ if (!m_pDispatcher)
+ return;
+ m_pDispatcher->dispatchWithArgument( FormFeature::MoveAbsolute, "Position", Any( static_cast<sal_Int32>(nRecord) ) );
+ }
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/solar/inc/navtoolbar.hxx b/forms/source/solar/inc/navtoolbar.hxx
new file mode 100644
index 0000000000..18e0e806df
--- /dev/null
+++ b/forms/source/solar/inc/navtoolbar.hxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svtools/recorditemwindow.hxx>
+#include <memory>
+
+namespace frm
+{
+ class IFeatureDispatcher;
+ class DocumentCommandImageProvider;
+ class ICommandDescriptionProvider;
+ class ImplNavToolBar;
+ typedef std::shared_ptr< const DocumentCommandImageProvider > PCommandImageProvider;
+
+ class NavigationToolBar final : public vcl::Window
+ {
+ public:
+ enum ImageSize
+ {
+ eSmall,
+ eLarge
+ };
+
+ enum FunctionGroup
+ {
+ ePosition,
+ eNavigation,
+ eRecordActions,
+ eFilterSort
+ };
+
+ private:
+ const IFeatureDispatcher* m_pDispatcher;
+ const std::shared_ptr< const DocumentCommandImageProvider >
+ m_pImageProvider;
+ ImageSize m_eImageSize;
+ VclPtr<ImplNavToolBar> m_pToolbar;
+ ::std::vector< VclPtr<vcl::Window> > m_aChildWins;
+ const OUString m_sModuleId;
+
+ public:
+ NavigationToolBar(
+ vcl::Window* _pParent,
+ WinBits _nStyle,
+ PCommandImageProvider _pImageProvider,
+ OUString sModuleId
+ );
+ virtual ~NavigationToolBar( ) override;
+ virtual void dispose() override;
+
+ /** sets the dispatcher which is to be used for the features
+
+ If the dispatcher is the same as the one which is currently set,
+ then the states of the features are updated
+
+ @param _pDispatcher
+ the new (or old) dispatcher. The caller is responsible for
+ ensuring the life time of the object does exceed the life time
+ of the tool bar instance.
+ */
+ void setDispatcher( const IFeatureDispatcher* _pDispatcher );
+
+ /// enables or disables a given feature
+ void enableFeature( sal_Int16 _nFeatureId, bool _bEnabled );
+
+ /// checks or unchecks a given feature
+ void checkFeature( sal_Int16 _nFeatureId, bool _bEnabled );
+
+ /// sets the text of a given feature
+ void setFeatureText( sal_Int16 _nFeatureId, const OUString& _rText );
+
+ /** retrieves the current image size
+ */
+ ImageSize GetImageSize( ) const { return m_eImageSize; }
+
+ /** sets the size of the images
+ */
+ void SetImageSize( ImageSize _eSize );
+
+ /** shows or hides a function group
+ */
+ void ShowFunctionGroup( FunctionGroup _eGroup, bool _bShow );
+
+ /** determines whether or not a given function group is currently visible
+ */
+ bool IsFunctionGroupVisible( FunctionGroup _eGroup );
+
+ // Window "overridables" (hiding the respective Window methods)
+ void SetControlBackground();
+ void SetControlBackground( const Color& rColor );
+ void SetTextLineColor( );
+ void SetTextLineColor( const Color& rColor );
+
+ private:
+ // Window overridables
+ virtual void Resize() override;
+ virtual void StateChanged( StateChangedType nType ) override;
+
+ /// ctor implementation
+ void implInit( );
+
+ /// impl version of SetImageSize
+ void implSetImageSize( ImageSize _eSize );
+
+ /// updates the images of our items
+ void implUpdateImages();
+
+ /// enables or disables an item, plus possible dependent items
+ void implEnableItem( sal_uInt16 _nItemId, bool _bEnabled );
+
+ // iterating through item windows
+ typedef void (NavigationToolBar::*ItemWindowHandler) (sal_uInt16, vcl::Window*) const;
+ void forEachItemWindow( ItemWindowHandler _handler );
+ typedef void (*ItemWindowHandler2) (sal_uInt16, vcl::Window*, const void*);
+ void forEachItemWindow( ItemWindowHandler2 _handler, const void* _pParam );
+
+ static void setItemBackground( sal_uInt16 /* _nItemId */, vcl::Window* _pItemWindow, const void* _pColor );
+ static void setTextLineColor( sal_uInt16 /* _nItemId */, vcl::Window* _pItemWindow, const void* _pColor );
+#if 0
+ void setItemWindowZoom( sal_uInt16 /* _nItemId */, vcl::Window* _pItemWindow, const void* /* _pParam */ ) const;
+#endif
+ void setItemControlFont( sal_uInt16 /* _nItemId */, vcl::Window* _pItemWindow ) const;
+ void setItemControlForeground( sal_uInt16 /* _nItemId */, vcl::Window* _pItemWindow ) const;
+ void adjustItemWindowWidth( sal_uInt16 _nItemId, vcl::Window* _pItemWindow ) const;
+ static void enableItemRTL( sal_uInt16 /*_nItemId*/, vcl::Window* _pItemWindow, const void* _pIsRTLEnabled );
+ };
+
+ class RecordPositionInput final : public RecordItemWindow
+ {
+ private:
+ const IFeatureDispatcher* m_pDispatcher;
+
+ public:
+ RecordPositionInput( vcl::Window* _pParent );
+
+ /** sets the dispatcher which is to be used for the features
+ */
+ void setDispatcher( const IFeatureDispatcher* _pDispatcher );
+
+ private:
+ virtual void PositionFired(sal_Int64 nRecord) override;
+ };
+
+} // namespace frm
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/NameContainer.hxx b/forms/source/xforms/NameContainer.hxx
new file mode 100644
index 0000000000..7c1b50993d
--- /dev/null
+++ b/forms/source/xforms/NameContainer.hxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <map>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Type.hxx>
+#include <osl/diagnose.h>
+
+typedef cppu::WeakImplHelper<
+ css::container::XNameContainer
+> NameContainer_t;
+
+template<class T>
+class NameContainer : public NameContainer_t
+{
+ typedef std::map<OUString,T> map_t;
+ map_t maItems;
+
+protected:
+ typename map_t::const_iterator findItem( const OUString& rName )
+ {
+ return maItems.find( rName );
+ }
+
+ bool hasItem( const OUString& rName )
+ {
+ return findItem( rName ) != maItems.end();
+ }
+
+ void replace( const OUString& rName,
+ const T& aElement )
+ {
+ OSL_ENSURE( hasItem( rName ), "unknown item" );
+ maItems[ rName ] = aElement;
+ }
+
+ void insert( const OUString& rName,
+ const T& aElement )
+ {
+ OSL_ENSURE( ! hasItem( rName ), "item already in set" );
+ maItems[ rName ] = aElement;
+ }
+
+ void remove( const OUString& rName )
+ {
+ OSL_ENSURE( hasItem( rName ), "item not in set" );
+ maItems.erase( rName );
+ }
+
+
+public:
+
+ NameContainer() {}
+
+
+ // methods for XElementAccess
+
+
+ virtual css::uno::Type SAL_CALL getElementType() override
+ {
+ return cppu::UnoType<T>::get();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return ! maItems.empty();
+ }
+
+
+ // methods for XNameAccess (inherits XElementAccess)
+
+
+ virtual css::uno::Any SAL_CALL getByName(
+ const OUString& rName ) override
+ {
+ typename map_t::const_iterator aIter = findItem( rName );
+ if( aIter == maItems.end() )
+ throw css::container::NoSuchElementException();
+ return css::uno::Any( aIter->second );
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getElementNames() override
+ {
+ return comphelper::mapKeysToSequence(maItems);
+ }
+
+ virtual sal_Bool SAL_CALL hasByName(
+ const OUString& rName ) override
+ {
+ return hasItem( rName );
+ }
+
+
+ // methods for XNameReplace (inherits XNameAccess)
+
+
+ virtual void SAL_CALL replaceByName(
+ const OUString& rName,
+ const css::uno::Any& aElement ) override
+ {
+ T aItem;
+ if( !(aElement >>= aItem) )
+ throw css::lang::IllegalArgumentException();
+ if( !hasByName( rName ) )
+ throw css::container::NoSuchElementException();
+ replace( rName, aItem );
+ }
+
+
+ // methods for XNameContainer (inherits XNameReplace)
+
+
+ virtual void SAL_CALL insertByName(
+ const OUString& rName,
+ const css::uno::Any& aElement ) override
+ {
+ T aItem;
+ if( !(aElement >>= aItem) )
+ throw css::lang::IllegalArgumentException();
+ if( hasByName( rName ) )
+ throw css::container::ElementExistException();
+ insert( rName, aItem );
+ }
+
+ virtual void SAL_CALL removeByName(
+ const OUString& rName ) override
+ {
+ if( !hasByName( rName ) )
+ throw css::container::NoSuchElementException();
+ remove( rName );
+ }
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/binding.cxx b/forms/source/xforms/binding.cxx
new file mode 100644
index 0000000000..c739126642
--- /dev/null
+++ b/forms/source/xforms/binding.cxx
@@ -0,0 +1,1275 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "binding.hxx"
+
+#include "model.hxx"
+#include "unohelper.hxx"
+#include "NameContainer.hxx"
+#include "evaluationcontext.hxx"
+#include "convert.hxx"
+#include "resourcehelper.hxx"
+#include "xmlhelper.hxx"
+#include "xformsevent.hxx"
+#include <strings.hrc>
+
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <comphelper/diagnose_ex.hxx>
+
+#include <algorithm>
+#include <functional>
+
+#include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
+#include <com/sun/star/form/binding/InvalidBindingStateException.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/xml/dom/events/XEventTarget.hpp>
+#include <com/sun/star/xml/dom/events/XEventListener.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+
+#include <comphelper/servicehelper.hxx>
+
+using namespace com::sun::star::xml::xpath;
+using namespace com::sun::star::xml::dom::events;
+
+using std::vector;
+using xforms::Binding;
+using xforms::MIP;
+using xforms::Model;
+using xforms::getResource;
+using xforms::EvaluationContext;
+using com::sun::star::beans::XPropertySet;
+using com::sun::star::container::XNameAccess;
+using com::sun::star::form::binding::IncompatibleTypesException;
+using com::sun::star::form::binding::InvalidBindingStateException;
+using com::sun::star::form::binding::XValueBinding;
+using com::sun::star::lang::EventObject;
+using com::sun::star::lang::IndexOutOfBoundsException;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::UNO_QUERY;
+using com::sun::star::uno::UNO_QUERY_THROW;
+using com::sun::star::uno::XInterface;
+using com::sun::star::uno::Exception;
+using com::sun::star::util::XModifyListener;
+using com::sun::star::xforms::XDataTypeRepository;
+using com::sun::star::xml::dom::NodeType_ATTRIBUTE_NODE;
+using com::sun::star::xml::dom::NodeType_TEXT_NODE;
+using com::sun::star::xml::dom::XNode;
+using com::sun::star::xml::dom::XNodeList;
+using com::sun::star::xml::dom::events::XEventListener;
+using com::sun::star::xml::dom::events::XEventTarget;
+using com::sun::star::xsd::XDataType;
+
+#define HANDLE_BindingID 0
+#define HANDLE_BindingExpression 1
+#define HANDLE_Model 2
+#define HANDLE_ModelID 3
+#define HANDLE_BindingNamespaces 4
+#define HANDLE_ReadonlyExpression 5
+#define HANDLE_RelevantExpression 6
+#define HANDLE_RequiredExpression 7
+#define HANDLE_ConstraintExpression 8
+#define HANDLE_CalculateExpression 9
+#define HANDLE_Type 10
+#define HANDLE_ReadOnly 11 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
+#define HANDLE_Relevant 12 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
+#define HANDLE_ModelNamespaces 13
+#define HANDLE_ExternalData 14
+
+
+Binding::Binding() :
+ mxNamespaces( new NameContainer<OUString>() ),
+ mbInCalculate( false ),
+ mnDeferModifyNotifications( 0 ),
+ mbValueModified( false ),
+ mbBindingModified( false )
+{
+ initializePropertySet();
+}
+
+Binding::~Binding()
+{
+ _setModel(nullptr);
+}
+
+void Binding::_setModel( const rtl::Reference<Model>& xModel )
+{
+ PropertyChangeNotifier aNotifyModelChange( *this, HANDLE_Model );
+ PropertyChangeNotifier aNotifyModelIDChange( *this, HANDLE_ModelID );
+
+ // prepare binding for removal of old model
+ clear(); // remove all cached data (e.g. XPath evaluation results)
+ css::uno::Reference<css::container::XNameContainer> xNamespaces = getModelNamespaces(); // save namespaces
+
+ mxModel = xModel;
+
+ // set namespaces (and move to model, if appropriate)
+ setBindingNamespaces( xNamespaces );
+ _checkBindingID();
+
+ notifyAndCachePropertyValue( HANDLE_ExternalData );
+}
+
+
+OUString Binding::getModelID() const
+{
+ return ( mxModel == nullptr ) ? OUString() : mxModel->getID();
+}
+
+
+css::uno::Reference<css::xml::dom::XNodeList> Binding::getXNodeList()
+{
+ // first make sure we are bound
+ if( ! maBindingExpression.hasValue() )
+ bind();
+
+ return maBindingExpression.getXNodeList();
+}
+
+bool Binding::isSimpleBinding() const
+{
+ return maBindingExpression.isSimpleExpression()
+ && maReadonly.isSimpleExpression()
+ && maRelevant.isSimpleExpression()
+ && maRequired.isSimpleExpression()
+ && maConstraint.isSimpleExpression()
+ && maCalculate.isSimpleExpression();
+}
+
+bool Binding::isSimpleBindingExpression() const
+{
+ return maBindingExpression.isSimpleExpression();
+}
+
+void Binding::update()
+{
+ // clear all expressions (to remove cached node references)
+ maBindingExpression.clear();
+ maReadonly.clear();
+ maRelevant.clear();
+ maRequired.clear();
+ maConstraint.clear();
+ maCalculate.clear();
+
+ // let's just pretend the binding has been modified -> full rebind()
+ bindingModified();
+}
+
+void Binding::deferNotifications( bool bDefer )
+{
+ mnDeferModifyNotifications += ( bDefer ? 1 : -1 );
+ OSL_ENSURE( mnDeferModifyNotifications >= 0, "you're deferring too much" );
+
+ if( mnDeferModifyNotifications == 0 )
+ {
+ if( mbBindingModified )
+ bindingModified();
+ if( mbValueModified )
+ valueModified();
+ }
+
+ OSL_ENSURE( ( mnDeferModifyNotifications > 0 )
+ || ( ! mbBindingModified && ! mbValueModified ),
+ "deferred modifications not delivered?" );
+}
+
+bool Binding::isValid() const
+{
+ // TODO: determine whether node is suitable, not just whether it exists
+ return maBindingExpression.getNode().is() &&
+ (
+ // tdf#155121, validity rules should be apply when field is required or
+ // when the field is not required but not empty
+ // so if the field is not required and empty, do not check validity
+ (! maMIP.isRequired() && maBindingExpression.hasValue()
+ && maBindingExpression.getString().isEmpty() ) ||
+ isValid_DataType()
+ ) &&
+ maMIP.isConstraint() &&
+ ( ! maMIP.isRequired() ||
+ ( maBindingExpression.hasValue() &&
+ !maBindingExpression.getString().isEmpty() ) );
+}
+
+bool Binding::isUseful() const
+{
+ // we are useful, if
+ // 0) we don't have a model
+ // (at least, in this case we shouldn't be removed from the model)
+ // 1) we have a proper name
+ // 2) we have some MIPs,
+ // 3) we are bound to some control
+ // (this can be assumed if some listeners are set)
+ bool bUseful =
+ mxModel == nullptr
+// || msBindingID.getLength() > 0
+ || ! msTypeName.isEmpty()
+ || ! maReadonly.isEmptyExpression()
+ || ! maRelevant.isEmptyExpression()
+ || ! maRequired.isEmptyExpression()
+ || ! maConstraint.isEmptyExpression()
+ || ! maCalculate.isEmptyExpression()
+ || ! maModifyListeners.empty()
+ || ! maListEntryListeners.empty()
+ || ! maValidityListeners.empty();
+
+ return bUseful;
+}
+
+OUString Binding::explainInvalid()
+{
+ OUString sReason;
+ if( ! maBindingExpression.getNode().is() )
+ {
+ sReason = ( maBindingExpression.getExpression().isEmpty() )
+ ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION )
+ : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION );
+ }
+ else if( ! isValid_DataType() )
+ {
+ sReason = explainInvalid_DataType();
+ if( sReason.isEmpty() )
+ {
+ // no explanation given by data type? Then give generic message
+ sReason = getResource( RID_STR_XFORMS_INVALID_VALUE,
+ maMIP.getTypeName() );
+ }
+ }
+ else if( ! maMIP.isConstraint() )
+ {
+ sReason = maMIP.getConstraintExplanation();
+ }
+ else if( maMIP.isRequired() && maBindingExpression.hasValue() &&
+ maBindingExpression.getString().isEmpty() )
+ {
+ sReason = getResource( RID_STR_XFORMS_REQUIRED );
+ }
+ // else: no explanation given; should only happen if data is valid
+
+ OSL_ENSURE( sReason.isEmpty() == isValid(),
+ "invalid data should have an explanation!" );
+
+ return sReason;
+}
+
+
+EvaluationContext Binding::getEvaluationContext() const
+{
+ OSL_ENSURE( mxModel != nullptr, "need model impl" );
+ EvaluationContext aContext = mxModel->getEvaluationContext();
+ aContext.mxNamespaces = getBindingNamespaces();
+ return aContext;
+}
+
+::std::vector<EvaluationContext> Binding::getMIPEvaluationContexts()
+{
+ OSL_ENSURE( mxModel != nullptr, "need model impl" );
+
+ // bind (in case we were not bound before)
+ bind();
+ return _getMIPEvaluationContexts();
+}
+
+
+css::uno::Sequence<sal_Int8> Binding::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit aImplementationId;
+ return aImplementationId.getSeq();
+}
+
+
+void Binding::setBindingID( const OUString& sBindingID )
+{
+ msBindingID = sBindingID;
+}
+
+OUString Binding::getBindingExpression() const
+{
+ return maBindingExpression.getExpression();
+}
+
+void Binding::setBindingExpression( const OUString& sBindingExpression)
+{
+ maBindingExpression.setExpression( sBindingExpression );
+ bindingModified();
+}
+
+OUString Binding::getReadonlyExpression() const
+{
+ return maReadonly.getExpression();
+}
+
+void Binding::setReadonlyExpression( const OUString& sReadonly)
+{
+ maReadonly.setExpression( sReadonly );
+ bindingModified();
+}
+
+OUString Binding::getRelevantExpression() const
+{
+ return maRelevant.getExpression();
+}
+
+void Binding::setRelevantExpression( const OUString& sRelevant )
+{
+ maRelevant.setExpression( sRelevant );
+ bindingModified();
+}
+
+OUString Binding::getRequiredExpression() const
+{
+ return maRequired.getExpression();
+}
+
+void Binding::setRequiredExpression( const OUString& sRequired )
+{
+ maRequired.setExpression( sRequired );
+ bindingModified();
+}
+
+OUString Binding::getConstraintExpression() const
+{
+ return maConstraint.getExpression();
+}
+
+void Binding::setConstraintExpression( const OUString& sConstraint )
+{
+ maConstraint.setExpression( sConstraint );
+ msExplainConstraint = getResource( RID_STR_XFORMS_INVALID_CONSTRAINT,
+ sConstraint );
+
+ // TODO: This should only re-evaluate the constraint, and notify
+ // the validity constraint listeners; instead we currently pretend
+ // the entire binding was notified, which does a little too much.
+ bindingModified();
+}
+
+OUString Binding::getCalculateExpression() const
+{
+ return maCalculate.getExpression();
+}
+
+void Binding::setCalculateExpression( const OUString& sCalculate )
+{
+ maCalculate.setExpression( sCalculate );
+ bindingModified();
+}
+
+
+void Binding::setType( const OUString& sTypeName )
+{
+ msTypeName = sTypeName;
+ bindingModified();
+}
+
+void Binding::setBindingNamespaces( const css::uno::Reference<css::container::XNameContainer>& rNamespaces )
+{
+ _setNamespaces( rNamespaces, true );
+}
+
+css::uno::Reference<css::container::XNameContainer> Binding::getModelNamespaces() const
+{
+ return _getNamespaces();
+}
+
+void Binding::setModelNamespaces( const css::uno::Reference<css::container::XNameContainer>& rNamespaces )
+{
+ _setNamespaces( rNamespaces, false );
+}
+
+bool Binding::getReadOnly() const
+{
+ return maMIP.isReadonly();
+}
+
+bool Binding::getRelevant() const
+{
+ return maMIP.isRelevant();
+}
+
+bool Binding::getExternalData() const
+{
+ bool bExternalData = true;
+ if ( !mxModel.is() )
+ return bExternalData;
+
+ try
+ {
+ OSL_VERIFY(
+ mxModel->getPropertyValue( "ExternalData" ) >>= bExternalData );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.xforms");
+ }
+ return bExternalData;
+}
+
+
+void Binding::checkLive()
+{
+ if( ! isLive() )
+ throw RuntimeException("Binding not initialized", static_cast<XValueBinding*>(this));
+}
+
+bool Binding::isLive() const
+{
+ return mxModel && mxModel->isInitialized();
+}
+
+
+static void lcl_addListenerToNode( const Reference<XNode>& xNode,
+ const Reference<XEventListener>& xListener )
+{
+ Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
+ if( !xTarget.is() )
+ return;
+
+ xTarget->addEventListener( "DOMCharacterDataModified",
+ xListener, false );
+ xTarget->addEventListener( "DOMCharacterDataModified",
+ xListener, true );
+ xTarget->addEventListener( "DOMAttrModified",
+ xListener, false );
+ xTarget->addEventListener( "DOMAttrModified",
+ xListener, true );
+ xTarget->addEventListener( "xforms-generic",
+ xListener, true );
+}
+
+static void lcl_removeListenerFromNode( const Reference<XNode>& xNode,
+ const Reference<XEventListener>& xListener )
+{
+ Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
+ if( !xTarget.is() )
+ return;
+
+ xTarget->removeEventListener( "DOMCharacterDataModified",
+ xListener, false );
+ xTarget->removeEventListener( "DOMCharacterDataModified",
+ xListener, true );
+ xTarget->removeEventListener( "DOMAttrModified",
+ xListener, false );
+ xTarget->removeEventListener( "DOMAttrModified",
+ xListener, true );
+ xTarget->removeEventListener( "xforms-generic",
+ xListener, true );
+}
+
+::std::vector<EvaluationContext> Binding::_getMIPEvaluationContexts() const
+{
+ OSL_ENSURE( mxModel != nullptr, "need model impl" );
+
+ // iterate over nodes of bind expression and create
+ // EvaluationContext for each
+ PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
+ ::std::vector<EvaluationContext> aVector;
+ for (auto const& node : aNodes)
+ {
+ OSL_ENSURE( node.is(), "no node?" );
+
+ // create proper evaluation context for this MIP
+ aVector.emplace_back( node, mxModel, getBindingNamespaces() );
+ }
+ return aVector;
+}
+
+void Binding::bind( bool bForceRebind )
+{
+ if( ! mxModel.is() )
+ throw RuntimeException("Binding has no Model", static_cast<XValueBinding*>(this));
+
+ // bind() will evaluate this binding as follows:
+ // 1) evaluate the binding expression
+ // 1b) if necessary, create node according to 'lazy author' rules
+ // 2) register suitable listeners on the instance (and remove old ones)
+ // 3) remove old MIPs defined by this binding
+ // 4) for every node in the binding nodeset do:
+ // 1) create proper evaluation context for this MIP
+ // 2) evaluate calculate expression (and push value into instance)
+ // 3) evaluate remaining MIPs
+ // 4) evaluate the locally defined MIPs, and push them to the model
+
+
+ // 1) evaluate the binding expression
+ EvaluationContext aContext = getEvaluationContext();
+ maBindingExpression.evaluate( aContext );
+ if( ! maBindingExpression.getNode().is() )
+ {
+ // 1b) create node (if valid element name)
+ if( isValidQName( maBindingExpression.getExpression(),
+ aContext.mxNamespaces ) )
+ {
+ aContext.mxContextNode->appendChild(
+ aContext.mxContextNode->getOwnerDocument()->createElement(
+ maBindingExpression.getExpression() ) );
+ maBindingExpression.evaluate( aContext );
+ OSL_ENSURE( maBindingExpression.getNode().is(),
+ "we should bind to the newly inserted node!" );
+ }
+ }
+ PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
+
+ // 2) register suitable listeners on the instance (and remove old ones)
+ if( maEventNodes.empty() || bForceRebind )
+ {
+ for (auto const& eventNode : maEventNodes)
+ lcl_removeListenerFromNode( eventNode, this );
+ maEventNodes.clear();
+ if( isSimpleBinding() )
+ maEventNodes.insert(maEventNodes.end(), aNodes.begin(), aNodes.end());
+ else
+ maEventNodes.emplace_back( aContext.mxContextNode->getOwnerDocument(),
+ UNO_QUERY_THROW );
+ for (auto const& eventNode : maEventNodes)
+ lcl_addListenerToNode( eventNode, this );
+ }
+
+ // 3) remove old MIPs defined by this binding
+ OSL_ENSURE( mxModel != nullptr, "need model" );
+ mxModel->removeMIPs( this );
+
+ // 4) calculate all MIPs
+ ::std::vector<EvaluationContext> aMIPContexts = _getMIPEvaluationContexts();
+ for (auto & context : aMIPContexts)
+ {
+ EvaluationContext& rContext = context;
+
+ // evaluate calculate expression (and push value into instance)
+ // (prevent recursion using mbInCalculate
+ if( ! maCalculate.isEmptyExpression() )
+ {
+ if( ! mbInCalculate )
+ {
+ mbInCalculate = true;
+ maCalculate.evaluate( rContext );
+ mxModel->setSimpleContent( rContext.mxContextNode,
+ maCalculate.getString() );
+ mbInCalculate = false;
+ }
+ }
+
+ // now evaluate remaining MIPs in the appropriate context
+ maReadonly.evaluate( rContext );
+ maRelevant.evaluate( rContext );
+ maRequired.evaluate( rContext );
+ maConstraint.evaluate( rContext );
+ // type is static; does not need updating
+
+ // evaluate the locally defined MIPs, and push them to the model
+ mxModel->addMIP( this, rContext.mxContextNode, getLocalMIP() );
+ }
+}
+
+
+// helper for Binding::valueModified
+static void lcl_modified( const css::uno::Reference<css::util::XModifyListener>& xListener,
+ const Reference<XInterface>& xSource )
+{
+ OSL_ENSURE( xListener.is(), "no listener?" );
+ xListener->modified( EventObject( xSource ) );
+}
+
+// helper for Binding::valueModified
+static void lcl_listentry( const css::uno::Reference<css::form::binding::XListEntryListener>& xListener,
+ const Reference<XInterface>& xSource )
+{
+ OSL_ENSURE( xListener.is(), "no listener?" );
+ // TODO: send fine granular events
+ xListener->allEntriesChanged( EventObject( xSource ) );
+}
+
+// helper for Binding::valueModified
+static void lcl_validate( const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener,
+ const Reference<XInterface>& xSource )
+{
+ OSL_ENSURE( xListener.is(), "no listener?" );
+ xListener->validityConstraintChanged( EventObject( xSource ) );
+}
+
+
+void Binding::valueModified()
+{
+ // defer notifications, if so desired
+ if( mnDeferModifyNotifications > 0 )
+ {
+ mbValueModified = true;
+ return;
+ }
+ mbValueModified = false;
+
+ // query MIP used by our first node (also note validity)
+ Reference<XNode> xNode = maBindingExpression.getNode();
+ maMIP = mxModel->queryMIP( xNode );
+
+ // distribute MIPs _used_ by this binding
+ if( xNode.is() )
+ {
+ notifyAndCachePropertyValue( HANDLE_ReadOnly );
+ notifyAndCachePropertyValue( HANDLE_Relevant );
+ }
+
+ // iterate over _value_ listeners and send each a modified signal,
+ // using this object as source (will also update validity, because
+ // control will query once the value has changed)
+ Reference<XInterface> xSource = static_cast<XPropertySet*>( this );
+ ::std::for_each( maModifyListeners.begin(),
+ maModifyListeners.end(),
+ ::std::bind( lcl_modified, std::placeholders::_1, xSource ) );
+ ::std::for_each( maListEntryListeners.begin(),
+ maListEntryListeners.end(),
+ ::std::bind( lcl_listentry, std::placeholders::_1, xSource ) );
+ ::std::for_each( maValidityListeners.begin(),
+ maValidityListeners.end(),
+ ::std::bind( lcl_validate, std::placeholders::_1, xSource ) );
+
+ // now distribute MIPs to children
+ if( xNode.is() )
+ distributeMIP( xNode->getFirstChild() );
+}
+
+void Binding::distributeMIP( const css::uno::Reference<css::xml::dom::XNode> & rxNode ) {
+
+ rtl::Reference<css::xforms::XFormsEventConcrete> pEvent = new css::xforms::XFormsEventConcrete;
+ pEvent->initXFormsEvent("xforms-generic", true, false);
+
+ // naive depth-first traversal
+ css::uno::Reference<css::xml::dom::XNode> xNode( rxNode );
+ while(xNode.is()) {
+
+ // notifications should be triggered at the
+ // leaf nodes first, bubbling upwards the hierarchy.
+ css::uno::Reference<css::xml::dom::XNode> child(xNode->getFirstChild());
+ if(child.is())
+ distributeMIP(child);
+
+ // we're standing at a particular node somewhere
+ // below the one which changed a property (MIP).
+ // bindings which are listening at this node will receive
+ // a notification message about what exactly happened.
+ Reference< XEventTarget > target(xNode,UNO_QUERY);
+ target->dispatchEvent(pEvent);
+
+ xNode = xNode->getNextSibling();
+ }
+}
+
+void Binding::bindingModified()
+{
+ // defer notifications, if so desired
+ if( mnDeferModifyNotifications > 0 )
+ {
+ mbBindingModified = true;
+ return;
+ }
+ mbBindingModified = false;
+
+ // rebind (if live); then call valueModified
+ // A binding should be inert until its model is fully constructed.
+ if( isLive() )
+ {
+ bind( true );
+ valueModified();
+ }
+}
+
+
+MIP Binding::getLocalMIP() const
+{
+ MIP aMIP;
+
+ if( maReadonly.hasValue() )
+ aMIP.setReadonly( maReadonly.getBool() );
+ if( maRelevant.hasValue() )
+ aMIP.setRelevant( maRelevant.getBool( true ) );
+ if( maRequired.hasValue() )
+ aMIP.setRequired( maRequired.getBool() );
+ if( maConstraint.hasValue() )
+ {
+ aMIP.setConstraint( maConstraint.getBool( true ) );
+ if( ! aMIP.isConstraint() )
+ aMIP.setConstraintExplanation( msExplainConstraint );
+ }
+ if( !msTypeName.isEmpty() )
+ aMIP.setTypeName( msTypeName );
+
+ // calculate: only handle presence of calculate; value set elsewhere
+ aMIP.setHasCalculate( !maCalculate.isEmptyExpression() );
+
+ return aMIP;
+}
+
+css::uno::Reference<css::xsd::XDataType> Binding::getDataType() const
+{
+ OSL_ENSURE( mxModel.is(), "need model" );
+ OSL_ENSURE( mxModel->getDataTypeRepository().is(), "need types" );
+
+ Reference<XDataTypeRepository> xRepository =
+ mxModel->getDataTypeRepository();
+ OUString sTypeName = maMIP.getTypeName();
+
+ return ( xRepository.is() && xRepository->hasByName( sTypeName ) )
+ ? Reference<XDataType>( xRepository->getByName( sTypeName ), UNO_QUERY)
+ : Reference<XDataType>( nullptr );
+}
+
+bool Binding::isValid_DataType() const
+{
+ Reference<XDataType> xDataType = getDataType();
+ return !xDataType.is()
+ || xDataType->validate( maBindingExpression.getString() );
+}
+
+OUString Binding::explainInvalid_DataType()
+{
+ Reference<XDataType> xDataType = getDataType();
+ return xDataType.is()
+ ? xDataType->explainInvalid( maBindingExpression.getString() )
+ : OUString();
+}
+
+void Binding::clear()
+{
+ // remove MIPs contributed by this binding
+ if( mxModel != nullptr )
+ mxModel->removeMIPs( this );
+
+ // remove all references
+ for (auto const& eventNode : maEventNodes)
+ lcl_removeListenerFromNode( eventNode, this );
+ maEventNodes.clear();
+
+ // clear expressions
+ maBindingExpression.clear();
+ maReadonly.clear();
+ maRelevant.clear();
+ maRequired.clear();
+ maConstraint.clear();
+ maCalculate.clear();
+
+ // TODO: what about our listeners?
+}
+
+
+static void lcl_removeOtherNamespaces( const css::uno::Reference<css::container::XNameContainer>& xFrom,
+ css::uno::Reference<css::container::XNameContainer> const & xTo )
+{
+ OSL_ENSURE( xFrom.is(), "no source" );
+ OSL_ENSURE( xTo.is(), "no target" );
+
+ // iterate over name in source
+ Sequence<OUString> aNames = xTo->getElementNames();
+ sal_Int32 nNames = aNames.getLength();
+ const OUString* pNames = aNames.getConstArray();
+ for( sal_Int32 i = 0; i < nNames; i++ )
+ {
+ const OUString& rName = pNames[i];
+
+ if( ! xFrom->hasByName( rName ) )
+ xTo->removeByName( rName );
+ }
+}
+
+/** copy namespaces from one namespace container into another
+ * @param bOverwrite true: overwrite namespaces in target
+ * false: do not overwrite namespaces in target
+ * @param bMove true: move namespaces (i.e., delete in source)
+ * false: copy namespaces (do not modify source)
+ * @param bFromSource true: use elements from source
+ * false: use only elements from target
+ */
+static void lcl_copyNamespaces( const css::uno::Reference<css::container::XNameContainer>& xFrom,
+ css::uno::Reference<css::container::XNameContainer> const & xTo,
+ bool bOverwrite )
+{
+ OSL_ENSURE( xFrom.is(), "no source" );
+ OSL_ENSURE( xTo.is(), "no target" );
+
+ // iterate over name in source
+ Sequence<OUString> aNames = xFrom->getElementNames();
+ sal_Int32 nNames = aNames.getLength();
+ const OUString* pNames = aNames.getConstArray();
+ for( sal_Int32 i = 0; i < nNames; i++ )
+ {
+ const OUString& rName = pNames[i];
+
+ // determine whether to copy the value, and whether to delete
+ // it in the source:
+
+ bool bInTarget = xTo->hasByName( rName );
+
+ // we copy: if property is in target, and
+ // if bOverwrite is set, or when the namespace prefix is free
+ bool bCopy = bOverwrite || ! bInTarget;
+
+ // and now... ACTION!
+ if( bCopy )
+ {
+ if( bInTarget )
+ xTo->replaceByName( rName, xFrom->getByName( rName ) );
+ else
+ xTo->insertByName( rName, xFrom->getByName( rName ) );
+ }
+ }
+}
+
+// implement get*Namespaces()
+// (identical for both variants)
+css::uno::Reference<css::container::XNameContainer> Binding::_getNamespaces() const
+{
+ css::uno::Reference<css::container::XNameContainer> xNamespaces = new NameContainer<OUString>();
+ lcl_copyNamespaces( mxNamespaces, xNamespaces, true );
+
+ // merge model's with binding's own namespaces
+ if( mxModel != nullptr )
+ lcl_copyNamespaces( mxModel->getNamespaces(), xNamespaces, false );
+
+ return xNamespaces;
+}
+
+// implement set*Namespaces()
+// bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces
+void Binding::_setNamespaces( const css::uno::Reference<css::container::XNameContainer>& rNamespaces,
+ bool bBinding )
+{
+ css::uno::Reference<css::container::XNameContainer> xModelNamespaces = ( mxModel != nullptr )
+ ? mxModel->getNamespaces()
+ : nullptr;
+ OSL_ENSURE( ( mxModel != nullptr ) == xModelNamespaces.is(), "no model nmsp?");
+
+ // remove deleted namespaces
+ lcl_removeOtherNamespaces( rNamespaces, mxNamespaces );
+ if( !bBinding && xModelNamespaces.is() )
+ lcl_removeOtherNamespaces( rNamespaces, xModelNamespaces );
+
+ // copy namespaces as appropriate
+ Sequence<OUString> aNames = rNamespaces->getElementNames();
+ sal_Int32 nNames = aNames.getLength();
+ const OUString* pNames = aNames.getConstArray();
+ for( sal_Int32 i = 0; i < nNames; i++ )
+ {
+ const OUString& rName = pNames[i];
+ Any aValue = rNamespaces->getByName( rName );
+
+ // determine whether the namespace should go into model's or
+ // into binding's namespaces
+ bool bLocal =
+ ! xModelNamespaces.is()
+ || mxNamespaces->hasByName( rName )
+ || ( bBinding
+ && xModelNamespaces.is()
+ && xModelNamespaces->hasByName( rName ) );
+
+ // write namespace into the appropriate namespace container
+ css::uno::Reference<css::container::XNameContainer>& rWhich = bLocal ? mxNamespaces : xModelNamespaces;
+ OSL_ENSURE( rWhich.is(), "whoops" );
+ if( rWhich->hasByName( rName ) )
+ rWhich->replaceByName( rName, aValue );
+ else
+ rWhich->insertByName( rName, aValue );
+
+ // always 'promote' namespaces from binding to model, if equal
+ if( xModelNamespaces.is()
+ && xModelNamespaces->hasByName( rName )
+ && mxNamespaces->hasByName( rName )
+ && xModelNamespaces->getByName( rName ) == mxNamespaces->getByName( rName ) )
+ {
+ mxNamespaces->removeByName( rName );
+ }
+ }
+
+ // ... done. But we modified the binding!
+ bindingModified();
+}
+
+void Binding::_checkBindingID()
+{
+ if( !mxModel.is() )
+ return;
+
+ Reference<XNameAccess> xBindings( mxModel->getBindings(), UNO_QUERY_THROW );
+ if( !msBindingID.isEmpty() )
+ return;
+
+ // no binding ID? then make one up!
+ OUString sIDPrefix = getResource( RID_STR_XFORMS_BINDING_UI_NAME ) + " ";
+ sal_Int32 nNumber = 0;
+ OUString sName;
+ do
+ {
+ nNumber++;
+ sName = sIDPrefix + OUString::number( nNumber );
+ }
+ while( xBindings->hasByName( sName ) );
+ setBindingID( sName );
+}
+
+
+// XValueBinding
+
+
+css::uno::Sequence<css::uno::Type> Binding::getSupportedValueTypes()
+{
+ return Convert::get().getTypes();
+}
+
+sal_Bool Binding::supportsType( const css::uno::Type& rType )
+{
+ return Convert::get().hasType( rType );
+}
+
+css::uno::Any Binding::getValue( const css::uno::Type& rType )
+{
+ // first, check for model
+ checkLive();
+
+ // second, check for type
+ if( ! supportsType( rType ) )
+ throw IncompatibleTypesException("type unsupported", static_cast<XValueBinding*>(this));
+
+ // return string value (if present; else return empty Any)
+ css::uno::Any result;
+ if(maBindingExpression.hasValue()) {
+ OUString pathExpr(maBindingExpression.getString());
+ Convert &rConvert = Convert::get();
+ result = rConvert.toAny(pathExpr,rType);
+ }
+
+ return result;
+}
+
+void Binding::setValue( const css::uno::Any& aValue )
+{
+ // first, check for model
+ checkLive();
+
+ // check for supported type
+ if( ! supportsType( aValue.getValueType() ) )
+ throw IncompatibleTypesException("type unsupported", static_cast<XValueBinding*>(this));
+
+ if( !maBindingExpression.hasValue() )
+ throw InvalidBindingStateException("no suitable node found", static_cast<XValueBinding*>(this));
+
+ css::uno::Reference<css::xml::dom::XNode> xNode = maBindingExpression.getNode();
+ if( !xNode.is() )
+ throw InvalidBindingStateException("no suitable node found", static_cast<XValueBinding*>(this));
+
+ OUString sValue = Convert::get().toXSD( aValue );
+ bool bSuccess = mxModel->setSimpleContent( xNode, sValue );
+ if( ! bSuccess )
+ throw InvalidBindingStateException("can't set value", static_cast<XValueBinding*>(this));
+
+
+}
+
+
+// XListEntry Source
+
+
+sal_Int32 Binding::getListEntryCount()
+{
+ // first, check for model
+ checkLive();
+
+ // return size of node list
+ return maBindingExpression.getNodeList().size();
+}
+
+static void lcl_getString( const Reference<XNode>& xNode, OUStringBuffer& rBuffer )
+{
+ if( xNode->getNodeType() == NodeType_TEXT_NODE
+ || xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
+ {
+ rBuffer.append( xNode->getNodeValue() );
+ }
+ else
+ {
+ for( Reference<XNode> xChild = xNode->getFirstChild();
+ xChild.is();
+ xChild = xChild->getNextSibling() )
+ {
+ lcl_getString( xChild, rBuffer );
+ }
+ }
+}
+
+static OUString lcl_getString( const Reference<XNode>& xNode )
+{
+ OUStringBuffer aBuffer;
+ lcl_getString( xNode, aBuffer );
+ return aBuffer.makeStringAndClear();
+}
+
+OUString Binding::getListEntry( sal_Int32 nPosition )
+{
+ // first, check for model
+ checkLive();
+
+ // check bounds and return proper item
+ PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
+ if( nPosition < 0 || o3tl::make_unsigned(nPosition) >= aNodes.size() )
+ throw IndexOutOfBoundsException("", static_cast<XValueBinding*>(this));
+ return lcl_getString( aNodes[ nPosition ] );
+}
+
+Sequence<OUString> Binding::getAllListEntries()
+{
+ // first, check for model
+ checkLive();
+
+ // create sequence of string values
+ PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
+ Sequence<OUString> aSequence( aNodes.size() );
+ OUString* pSequence = aSequence.getArray();
+ for( sal_Int32 n = 0; n < aSequence.getLength(); n++ )
+ {
+ pSequence[n] = lcl_getString( aNodes[n] );
+ }
+
+ return aSequence;
+}
+
+void Binding::addListEntryListener( const css::uno::Reference<css::form::binding::XListEntryListener>& xListener )
+{
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ if( ::std::find( maListEntryListeners.begin(),
+ maListEntryListeners.end(),
+ xListener)
+ == maListEntryListeners.end() )
+ maListEntryListeners.push_back( xListener );
+}
+
+void Binding::removeListEntryListener( const css::uno::Reference<css::form::binding::XListEntryListener>& xListener )
+{
+ XListEntryListeners_t::iterator aIter =
+ ::std::find( maListEntryListeners.begin(), maListEntryListeners.end(),
+ xListener );
+ if( aIter != maListEntryListeners.end() )
+ maListEntryListeners.erase( aIter );
+}
+
+
+// XValidator
+
+
+sal_Bool Binding::isValid( const css::uno::Any& )
+{
+ // first, check for model
+ checkLive();
+
+ // ignore value; determine validate only on current data
+ return isValid();
+}
+
+OUString Binding::explainInvalid(
+ const css::uno::Any& /*Value*/ )
+{
+ // first, check for model
+ checkLive();
+
+ // ignore value; determine explanation only on current data
+ return explainInvalid();
+}
+
+void Binding::addValidityConstraintListener(
+ const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener )
+{
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ if( ::std::find(maValidityListeners.begin(), maValidityListeners.end(), xListener)
+ == maValidityListeners.end() )
+ maValidityListeners.push_back( xListener );
+}
+
+void Binding::removeValidityConstraintListener(
+ const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener )
+{
+ XValidityConstraintListeners_t::iterator aIter =
+ ::std::find( maValidityListeners.begin(), maValidityListeners.end(),
+ xListener );
+ if( aIter != maValidityListeners.end() )
+ maValidityListeners.erase( aIter );
+}
+
+
+// xml::dom::event::XEventListener
+
+
+void Binding::handleEvent( const css::uno::Reference<css::xml::dom::events::XEvent>& xEvent )
+{
+ OUString sType(xEvent->getType());
+ //OUString sEventMIPChanged("xforms-generic");
+ //if(sType.equals(sEventMIPChanged)) {
+ if(sType == "xforms-generic") {
+
+ // the modification of the 'mnDeferModifyNotifications'-member
+ // is necessary to prevent infinite notification looping.
+ // This can happened in case the binding which caused
+ // the notification chain is listening to those events
+ // as well...
+ bool bPreserveValueModified = mbValueModified;
+ mnDeferModifyNotifications++;
+ valueModified();
+ --mnDeferModifyNotifications;
+ mbValueModified = bPreserveValueModified;
+ return;
+ }
+
+ // if we're a dynamic binding, we better re-bind, too!
+ bind();
+
+ // our value was maybe modified
+ valueModified();
+}
+
+
+// lang::XUnoTunnel
+
+
+sal_Int64 Binding::getSomething( const css::uno::Sequence<sal_Int8>& xId )
+{
+ return comphelper::getSomethingImpl(xId, this);
+}
+
+
+// XCloneable
+
+
+css::uno::Reference<css::util::XCloneable> SAL_CALL Binding::createClone()
+{
+ Reference< XPropertySet > xClone;
+
+ if ( mxModel )
+ xClone = mxModel->cloneBinding( this );
+ else
+ {
+ xClone = new Binding;
+ copy( this, xClone );
+ }
+ return css::uno::Reference<css::util::XCloneable>( xClone, UNO_QUERY );
+}
+
+css::uno::Reference<css::xforms::XModel> Binding::getModel() const
+{
+ return mxModel;
+}
+
+// property set implementations
+
+void Binding::initializePropertySet()
+{
+ registerProperty( css::beans::Property("BindingID", HANDLE_BindingID, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setBindingID, &Binding::getBindingID));
+
+ registerProperty( css::beans::Property("BindingExpression", HANDLE_BindingExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setBindingExpression, &Binding::getBindingExpression));
+
+ registerProperty( css::beans::Property("Model", HANDLE_Model, cppu::UnoType<css::uno::Reference<css::xforms::XModel>>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new DirectPropertyAccessor< Binding, css::uno::Reference<css::xforms::XModel> >(this, nullptr, &Binding::getModel));
+
+ registerProperty( css::beans::Property("BindingNamespaces", HANDLE_BindingNamespaces, cppu::UnoType<css::uno::Reference<css::container::XNameContainer>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, css::uno::Reference<css::container::XNameContainer> >(this, &Binding::setBindingNamespaces, &Binding::getBindingNamespaces));
+
+ registerProperty( css::beans::Property("ModelNamespaces", HANDLE_ModelNamespaces, cppu::UnoType<css::uno::Reference<css::container::XNameContainer>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, css::uno::Reference<css::container::XNameContainer> >(this, &Binding::setModelNamespaces, &Binding::getModelNamespaces));
+
+ registerProperty( css::beans::Property("ModelID", HANDLE_ModelID, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new DirectPropertyAccessor< Binding, OUString >(this, nullptr, &Binding::getModelID));
+
+ registerProperty( css::beans::Property("ReadonlyExpression", HANDLE_ReadonlyExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setReadonlyExpression, &Binding::getReadonlyExpression));
+
+ registerProperty( css::beans::Property("RelevantExpression", HANDLE_RelevantExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setRelevantExpression, &Binding::getRelevantExpression));
+
+ registerProperty( css::beans::Property("RequiredExpression", HANDLE_RequiredExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setRequiredExpression, &Binding::getRequiredExpression));
+
+ registerProperty( css::beans::Property("ConstraintExpression", HANDLE_ConstraintExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setConstraintExpression, &Binding::getConstraintExpression));
+
+ registerProperty( css::beans::Property("CalculateExpression", HANDLE_CalculateExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setCalculateExpression, &Binding::getCalculateExpression));
+
+ registerProperty( css::beans::Property("Type", HANDLE_Type, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setType, &Binding::getType));
+
+ registerProperty( css::beans::Property("ReadOnly", HANDLE_ReadOnly, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new DirectPropertyAccessor< Binding, bool >(this, nullptr, &Binding::getReadOnly));
+
+ registerProperty( css::beans::Property("Relevant", HANDLE_Relevant, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new DirectPropertyAccessor< Binding, bool >(this, nullptr, &Binding::getRelevant));
+
+ registerProperty( css::beans::Property("ExternalData", HANDLE_ExternalData, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new BooleanPropertyAccessor< Binding >(this, nullptr, &Binding::getExternalData));
+
+ initializePropertyValueCache( HANDLE_ReadOnly );
+ initializePropertyValueCache( HANDLE_Relevant );
+ initializePropertyValueCache( HANDLE_ExternalData );
+}
+
+void Binding::addModifyListener(
+ const css::uno::Reference<css::util::XModifyListener>& xListener )
+{
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ if( ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener )
+ == maModifyListeners.end() )
+ maModifyListeners.push_back( xListener );
+
+ // HACK: currently, we have to 'push' some MIPs to the control
+ // (read-only, relevant, etc.) To enable this, we need to update
+ // the control at least once when it registers here.
+ valueModified();
+}
+
+void Binding::removeModifyListener(
+ const css::uno::Reference<css::util::XModifyListener>& xListener )
+{
+ ModifyListeners_t::iterator aIter =
+ ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener );
+ if( aIter != maModifyListeners.end() )
+ maModifyListeners.erase( aIter );
+}
+
+
+OUString Binding::getName()
+{
+ return getBindingID();
+}
+
+void SAL_CALL Binding::setName( const OUString& rName )
+{
+ // use the XPropertySet methods, so the change in the name is notified to the
+ // property listeners
+ setFastPropertyValue( HANDLE_BindingID, Any( rName ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/binding.hxx b/forms/source/xforms/binding.hxx
new file mode 100644
index 0000000000..76b478b944
--- /dev/null
+++ b/forms/source/xforms/binding.hxx
@@ -0,0 +1,406 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <cppuhelper/implbase.hxx>
+#include "propertysetbase.hxx"
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/form/binding/XListEntrySource.hpp>
+#include <com/sun/star/form/validation/XValidator.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/xml/dom/events/XEventListener.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+
+#include "pathexpression.hxx"
+#include "boolexpression.hxx"
+#include "mip.hxx"
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <vector>
+
+// forward declaractions
+namespace xforms
+{
+ class Model;
+ class EvaluationContext;
+}
+namespace com::sun::star {
+ namespace xml {
+ namespace xpath { class XXPathAPI; }
+ namespace dom
+ {
+ class XNode;
+ class XNodeList;
+ }
+ }
+ namespace container { class XNameContainer; }
+ namespace xforms { class XModel; }
+ namespace xsd { class XDataType; }
+}
+
+
+namespace xforms
+{
+
+/** An XForms Binding. Contains:
+ * # a connection to its model
+ * # an ID
+ * # a binding expression
+ * # model item properties
+ * # (NOT YET IMPLEMENTED) child bindings (sequence of)
+ *
+ * See http://www.w3.org/TR/xforms/ for more information.
+ */
+
+typedef cppu::ImplInheritanceHelper<
+ PropertySetBase,
+ css::form::binding::XValueBinding,
+ css::form::binding::XListEntrySource,
+ css::form::validation::XValidator,
+ css::util::XModifyBroadcaster,
+ css::container::XNamed,
+ css::xml::dom::events::XEventListener,
+ css::lang::XUnoTunnel,
+ css::util::XCloneable
+> Binding_t;
+
+class Binding : public Binding_t
+{
+public:
+ typedef std::vector<css::uno::Reference<css::util::XModifyListener> > ModifyListeners_t;
+ typedef std::vector<css::uno::Reference<css::form::validation::XValidityConstraintListener> > XValidityConstraintListeners_t;
+ typedef std::vector<css::uno::Reference<css::form::binding::XListEntryListener> > XListEntryListeners_t;
+
+
+private:
+
+ /// the Model to which this Binding belongs; may be NULL
+ rtl::Reference<Model> mxModel;
+
+ /// binding-ID. A document-wide unique ID for this binding element.
+ OUString msBindingID;
+
+ /// an XPath-expression to be instantiated on the data instance
+ PathExpression maBindingExpression;
+
+ /// an XPath-expression to determine read-only status
+ BoolExpression maReadonly;
+
+ /// an XPath-expression to determine relevance
+ BoolExpression maRelevant;
+
+ /// an XPath-expression to determine if item is required
+ BoolExpression maRequired;
+
+ /// an XPath-expression to determine if item is valid
+ BoolExpression maConstraint;
+
+ /// user-readable explanation of the constraint
+ OUString msExplainConstraint;
+
+ /// an XPath-expression to calculate values
+ ComputedExpression maCalculate;
+
+ /// the XML namespaces used for XML names/XPath-expressions in this binding
+ css::uno::Reference<css::container::XNameContainer> mxNamespaces;
+
+ /// a type name
+ OUString msTypeName;
+
+ /// modify listeners
+ ModifyListeners_t maModifyListeners;
+
+ /// list entry listener
+ XListEntryListeners_t maListEntryListeners;
+
+ /// validity listeners;
+ XValidityConstraintListeners_t maValidityListeners;
+
+ /// nodes on which we are listening for events
+ std::vector<css::uno::Reference<css::xml::dom::XNode> > maEventNodes;
+
+ /// the current MIP object for the first node we are bound to
+ MIP maMIP;
+
+ /// flag to detect recursions in calculate
+ bool mbInCalculate;
+
+ // flags to manage deferred notifications:
+ /// if >0, valueModified() and bindingModified() will only set flags
+ sal_Int32 mnDeferModifyNotifications;
+ bool mbValueModified; /// if true, valueModified needs to be called
+ bool mbBindingModified; /// if true, bindingModified needs to be called
+
+
+ void initializePropertySet();
+
+
+public:
+ Binding();
+ virtual ~Binding() override;
+
+
+ // property methods: get/set value
+
+
+ /// get the model implementation
+ css::uno::Reference<css::xforms::XModel> getModel() const; /// get XForms model
+ void _setModel( const rtl::Reference<Model>& ); /// set XForms model (only called by Model)
+
+
+ OUString getModelID() const; /// get ID of XForms model
+
+ OUString getBindingID() const { return msBindingID;} /// get ID for this binding
+ void setBindingID( const OUString& ); /// set ID for this binding
+
+ OUString getBindingExpression() const; /// get binding expression
+ void setBindingExpression( const OUString& ); /// set binding exp.
+
+ // MIPs (model item properties)
+
+ OUString getReadonlyExpression() const; /// get read-only MIP
+ void setReadonlyExpression( const OUString& ); /// set read-only MIP
+
+ OUString getRelevantExpression() const; /// get relevant MIP
+ void setRelevantExpression( const OUString& ); /// set relevant MIP
+
+ OUString getRequiredExpression() const; /// get required MIP
+ void setRequiredExpression( const OUString& ); /// set required MIP
+
+ OUString getConstraintExpression() const; /// get constraint MIP
+ void setConstraintExpression( const OUString& );/// set constraint MIP
+
+ OUString getCalculateExpression() const; /// get calculate MIP
+ void setCalculateExpression( const OUString& ); /// set calculate MIP
+
+ OUString getType() const { return msTypeName;} /// get type name MIP (static)
+ void setType( const OUString& ); /// set type name MIP (static)
+
+ // a binding expression can only be interpreted with respect to
+ // suitable namespace declarations. We collect those in the model and in a binding.
+
+ // access to a binding's namespace
+ // (set-method only changes local namespaces (but may add to model))
+ css::uno::Reference<css::container::XNameContainer> getBindingNamespaces() const { return mxNamespaces; }
+ void setBindingNamespaces( const css::uno::Reference<css::container::XNameContainer>& ); /// get binding nmsp.
+
+ // access to the model's namespaces
+ // (set-method changes model's namespaces (unless a local one is present))
+ css::uno::Reference<css::container::XNameContainer> getModelNamespaces() const; /// set model namespaces
+ void setModelNamespaces( const css::uno::Reference<css::container::XNameContainer>& ); /// get model nmsp.
+
+
+ // read-only properties that map MIPs to control data source properties
+ bool getReadOnly() const; // MIP readonly
+ bool getRelevant() const; // MIP relevant
+ bool getExternalData() const; // mapped from model's ExternalData property
+
+
+ // missing binding properties:
+ // - type (static; default: xsd:string)
+ // - minOccurs/maxOccurs (computed XPath; default: 0/inf)
+ // - p3ptype (static; no default)
+
+
+ /// get this binding's context node
+ xforms::EvaluationContext getEvaluationContext() const;
+
+ /// get evaluation contexts for this binding's MIPs
+ std::vector<xforms::EvaluationContext> getMIPEvaluationContexts();
+
+ /// get nodeset the bind is bound to
+ css::uno::Reference<css::xml::dom::XNodeList> getXNodeList();
+
+ /// heuristically determine whether this binding is simple binding
+ /// (here: simple binding == does not depend on other parts of the
+ /// instance, it's not a 'dynamic' binding)
+ bool isSimpleBinding() const;
+
+ /// heuristically determine whether this binding's binding
+ /// expression is simple
+ bool isSimpleBindingExpression() const;
+
+ /// update this binding (e.g. called by model for refresh )
+ void update();
+
+ /// prevent change notifications being sent to controls
+ void deferNotifications( bool );
+
+ /// is this binding valid? (are constraint, type and required MIPs ok?)
+ bool isValid() const;
+
+ /// determine whether this binding currently performs a useful
+ /// function, r whether is may be discarded
+ bool isUseful() const;
+
+ /// explain why binding is invalid
+ OUString explainInvalid();
+
+
+ // the ID for XUnoTunnel calls
+ static css::uno::Sequence<sal_Int8> getUnoTunnelId();
+
+
+private:
+ /// check whether object is live, and throw suitable exception if not
+ /// (to be used be API methods before acting on the object)
+ ///
+ /// @throws css::uno::RuntimeException
+ void checkLive();
+
+ /// determine whether object is live
+ /// live: has model, and model has been initialized
+ bool isLive() const;
+
+ /// get MIP evaluation contexts
+ /// (only valid if control has already been bound)
+ std::vector<xforms::EvaluationContext> _getMIPEvaluationContexts() const;
+
+ /// bind this binding, and pre-compute the affected nodes
+ void bind( bool bForceRebind = false );
+
+ /// the binding value has been changed:
+ /// trigger a modified event on all modified listeners
+ void valueModified();
+
+ /// the binding itself has changed:
+ /// force rebind, then call valueModified()
+ void bindingModified();
+
+
+ /// set MIPs defined by this binding on MIP item
+ MIP getLocalMIP() const;
+
+ /// get the data type that applies to this binding
+ css::uno::Reference<css::xsd::XDataType> getDataType() const;
+
+ /// determine whether binding is valid according to the given data type
+ bool isValid_DataType() const;
+
+ /// explain validity of binding with respect to the given data type
+ OUString explainInvalid_DataType();
+
+ /// 'clear' this binding - remove all listeners, etc.
+ void clear();
+
+ /// distribute MIPs from current node recursively to children
+ void distributeMIP( const css::uno::Reference<css::xml::dom::XNode> &rxNode );
+
+ /// implement get*Namespaces()
+ css::uno::Reference<css::container::XNameContainer> _getNamespaces() const;
+
+ /// implement set*Namespaces()
+ void _setNamespaces( const css::uno::Reference<css::container::XNameContainer>&, bool bBinding );
+
+ /// set a useful default binding ID (if none is set)
+ void _checkBindingID();
+
+public:
+
+ virtual css::uno::Sequence<css::uno::Type> SAL_CALL getSupportedValueTypes() override;
+
+ virtual sal_Bool SAL_CALL supportsType( const css::uno::Type& aType ) override;
+
+ virtual css::uno::Any SAL_CALL getValue( const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL setValue( const css::uno::Any& aValue ) override;
+
+
+ // XListEntrySource
+
+
+ virtual sal_Int32 SAL_CALL getListEntryCount() override;
+
+ virtual OUString SAL_CALL getListEntry( sal_Int32 nPosition ) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getAllListEntries() override;
+
+ virtual void SAL_CALL addListEntryListener( const css::uno::Reference<css::form::binding::XListEntryListener>& ) override;
+
+ virtual void SAL_CALL removeListEntryListener( const css::uno::Reference<css::form::binding::XListEntryListener>&) override;
+
+
+ // XValidator:
+
+
+ virtual sal_Bool SAL_CALL isValid(
+ const css::uno::Any& ) override;
+
+ virtual OUString SAL_CALL explainInvalid(
+ const css::uno::Any& ) override;
+
+ virtual void SAL_CALL addValidityConstraintListener(
+ const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener ) override;
+
+ virtual void SAL_CALL removeValidityConstraintListener(
+ const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener ) override;
+
+
+ // XModifyBroadcaster & friends:
+ // inform listeners about changes in our values
+
+
+public:
+
+ virtual void SAL_CALL addModifyListener(
+ const css::uno::Reference<css::util::XModifyListener>& xListener ) override;
+
+ virtual void SAL_CALL removeModifyListener(
+ const css::uno::Reference<css::util::XModifyListener>& xListener ) override;
+
+
+ // XNamed:
+ // get/set name
+
+
+public:
+
+ virtual OUString SAL_CALL getName() override;
+
+ virtual void SAL_CALL setName( const OUString& ) override;
+
+
+ // xml::dom::event::XEventListener
+ // receive an event if our node changed
+
+
+ virtual void SAL_CALL handleEvent(
+ const css::uno::Reference<css::xml::dom::events::XEvent>& xEvent ) override;
+
+
+ // XUnoTunnel
+
+
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence<sal_Int8>& ) override;
+
+
+ // XCloneable
+
+
+ virtual css::uno::Reference<css::util::XCloneable> SAL_CALL createClone() override;
+};
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/boolexpression.cxx b/forms/source/xforms/boolexpression.cxx
new file mode 100644
index 0000000000..af45c8c38f
--- /dev/null
+++ b/forms/source/xforms/boolexpression.cxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "boolexpression.hxx"
+
+namespace xforms
+{
+/** BoolExpression represents a computed XPath expression that returns
+ * a bool value and caches the results.
+ *
+ * As this class has no virtual methods, it should never be used
+ * polymorphically. */
+
+BoolExpression::BoolExpression() {}
+
+BoolExpression::~BoolExpression() {}
+
+void BoolExpression::setExpression(const OUString& rExpression)
+{
+ ComputedExpression::setExpression(rExpression);
+ mbIsSimple = _checkExpression(" *(true)|(false) *\\( *\\) *");
+}
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/boolexpression.hxx b/forms/source/xforms/boolexpression.hxx
new file mode 100644
index 0000000000..ddbc13ce70
--- /dev/null
+++ b/forms/source/xforms/boolexpression.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "computedexpression.hxx"
+
+namespace xforms
+{
+/** BoolExpression represents a computed XPath expression that returns
+ * a bool value and caches the results.
+ *
+ * As this class has no virtual methods, it should never be used
+ * polymorphically. */
+class BoolExpression : public ComputedExpression
+{
+public:
+ BoolExpression();
+ ~BoolExpression();
+
+ /// set the expression string
+ /// (overridden for new definition of a simple expression)
+ void setExpression(const OUString& rExpression);
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/collection.hxx b/forms/source/xforms/collection.hxx
new file mode 100644
index 0000000000..00c7bdc39f
--- /dev/null
+++ b/forms/source/xforms/collection.hxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "enumeration.hxx"
+
+#include <cppuhelper/implbase.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/container/XSet.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Type.hxx>
+#include <vector>
+
+
+typedef cppu::WeakImplHelper<
+ css::container::XIndexReplace,
+ css::container::XSet,
+ css::container::XContainer>
+Collection_t;
+
+template<class ELEMENT_TYPE>
+class Collection : public Collection_t
+{
+public:
+ typedef ELEMENT_TYPE T;
+ typedef std::vector<css::uno::Reference<css::container::XContainerListener> > Listeners_t;
+
+protected:
+ std::vector<T> maItems;
+ Listeners_t maListeners;
+
+public:
+
+ Collection() {}
+
+ const T& getItem( sal_Int32 n ) const
+ {
+ OSL_ENSURE( isValidIndex(n), "invalid index" );
+ OSL_ENSURE( isValid( maItems[n] ), "invalid item found" );
+ return maItems[n];
+ }
+
+ void setItem( sal_Int32 n, const T& t)
+ {
+ OSL_ENSURE( isValidIndex(n), "invalid index" );
+ OSL_ENSURE( isValid ( t ), "invalid item" );
+
+ T& aRef = maItems[ n ];
+ _elementReplaced( n, t );
+ _remove( aRef );
+ aRef = t;
+ _insert( t );
+ }
+
+ bool hasItem( const T& t ) const
+ {
+ return maItems.end() != std::find( maItems.begin(), maItems.end(), t );
+ }
+
+ sal_Int32 addItem( const T& t )
+ {
+ OSL_ENSURE( !hasItem( t ), "item to be added already present" );
+ OSL_ENSURE( isValid( t ), "invalid item" );
+
+ maItems.push_back( t );
+ _insert( t );
+ _elementInserted( maItems.size() - 1 );
+ return ( maItems.size() - 1 );
+ }
+
+ void removeItem( const T& t )
+ {
+ OSL_ENSURE( hasItem( t ), "item to be removed not present" );
+ OSL_ENSURE( isValid( t ), "an invalid item, funny that!" );
+
+ _elementRemoved( t );
+ _remove( t );
+ maItems.erase( std::find( maItems.begin(), maItems.end(), t ) );
+ }
+
+ bool hasItems() const
+ {
+ return maItems.size() != 0;
+ }
+
+ sal_Int32 countItems() const
+ {
+ return static_cast<sal_Int32>( maItems.size() );
+ }
+
+ bool isValidIndex( sal_Int32 n ) const
+ {
+ return n >= 0 && n < static_cast<sal_Int32>( maItems.size() );
+ }
+
+
+ // the following method may be overridden by sub-classes for
+ // customized behaviour
+
+ /// called before insertion to determine whether item is valid
+ virtual bool isValid( const T& ) const { return true; }
+
+
+protected:
+
+ // the following methods may be overridden by sub-classes for
+ // customized behaviour
+
+ /// called after item has been inserted into the collection
+ virtual void _insert( const T& ) { }
+
+ /// called before item is removed from the collection
+ virtual void _remove( const T& ) { }
+
+public:
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override
+ {
+ return cppu::UnoType<T>::get();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return hasItems();
+ }
+
+ // XIndexAccess : XElementAccess
+ virtual sal_Int32 SAL_CALL getCount() override
+ {
+ return countItems();
+ }
+
+ virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 nIndex ) override
+ {
+ if( !isValidIndex( nIndex ) )
+ throw css::lang::IndexOutOfBoundsException();
+ return css::uno::Any( getItem( nIndex ) );
+ }
+
+ // XIndexReplace : XIndexAccess
+ virtual void SAL_CALL replaceByIndex( sal_Int32 nIndex,
+ const css::uno::Any& aElement ) override
+ {
+ T t;
+ if( !isValidIndex( nIndex) )
+ throw css::lang::IndexOutOfBoundsException();
+ if( !( aElement >>= t ) || !isValid( t ) )
+ throw css::lang::IllegalArgumentException();
+ setItem( nIndex, t );
+ }
+
+ // XEnumerationAccess : XElementAccess
+ virtual css::uno::Reference<css::container::XEnumeration> SAL_CALL createEnumeration() override
+ {
+ return new Enumeration( this );
+ }
+
+
+ // XSet : XEnumerationAccess
+ virtual sal_Bool SAL_CALL has( const css::uno::Any& aElement ) override
+ {
+ T t;
+ return ( aElement >>= t ) && hasItem( t );
+ }
+
+ virtual void SAL_CALL insert( const css::uno::Any& aElement ) override
+ {
+ T t;
+ if( !( aElement >>= t ) || !isValid( t ) )
+ throw css::lang::IllegalArgumentException();
+ if( hasItem( t ) )
+ throw css::container::ElementExistException();
+ addItem( t );
+ }
+
+ virtual void SAL_CALL remove( const css::uno::Any& aElement ) override
+ {
+ T t;
+ if( !(aElement >>= t) )
+ throw css::lang::IllegalArgumentException();
+ if( !hasItem( t ) )
+ throw css::container::NoSuchElementException();
+ removeItem( t );
+ }
+
+
+ // XContainer
+ virtual void SAL_CALL addContainerListener(
+ const css::uno::Reference<css::container::XContainerListener>& xListener ) override
+ {
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ if( std::find( maListeners.begin(), maListeners.end(), xListener)
+ == maListeners.end() )
+ maListeners.push_back( xListener );
+ }
+
+ virtual void SAL_CALL removeContainerListener(
+ const css::uno::Reference<css::container::XContainerListener>& xListener ) override
+ {
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ Listeners_t::iterator aIter =
+ std::find( maListeners.begin(), maListeners.end(), xListener );
+ if( aIter != maListeners.end() )
+ maListeners.erase( aIter );
+ }
+
+protected:
+
+ // call listeners:
+ void _elementInserted( sal_Int32 nPos )
+ {
+ OSL_ENSURE( isValidIndex(nPos), "invalid index" );
+ css::container::ContainerEvent aEvent(
+ static_cast<css::container::XIndexReplace*>( this ),
+ css::uno::Any( nPos ),
+ css::uno::Any( getItem( nPos ) ),
+ css::uno::Any() );
+ for (auto const& listener : maListeners)
+ {
+ listener->elementInserted( aEvent );
+ }
+ }
+
+ void _elementRemoved( const T& aOld )
+ {
+ css::container::ContainerEvent aEvent(
+ static_cast<css::container::XIndexReplace*>( this ),
+ css::uno::Any(),
+ css::uno::Any( aOld ),
+ css::uno::Any() );
+ for (auto const& listener : maListeners)
+ {
+ listener->elementRemoved( aEvent );
+ }
+ }
+
+ void _elementReplaced( const sal_Int32 nPos, const T& aNew )
+ {
+ OSL_ENSURE( isValidIndex(nPos), "invalid index" );
+ css::container::ContainerEvent aEvent(
+ static_cast<css::container::XIndexReplace*>( this ),
+ css::uno::Any( nPos ),
+ css::uno::Any( getItem( nPos ) ),
+ css::uno::Any( aNew ) );
+ for (auto const& listener : maListeners)
+ {
+ listener->elementReplaced( aEvent );
+ }
+ }
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/computedexpression.cxx b/forms/source/xforms/computedexpression.cxx
new file mode 100644
index 0000000000..add8d4350a
--- /dev/null
+++ b/forms/source/xforms/computedexpression.cxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "computedexpression.hxx"
+#include "evaluationcontext.hxx"
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/xpath/XPathExtension.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+
+#include <osl/diagnose.h>
+
+#include <i18nutil/searchopt.hxx>
+#include <unotools/textsearch.hxx>
+#include <comphelper/processfactory.hxx>
+
+using namespace com::sun::star::uno;
+using com::sun::star::xml::xpath::XPathAPI;
+using com::sun::star::xml::xpath::XXPathAPI;
+using com::sun::star::xml::xpath::XPathExtension;
+using com::sun::star::xml::xpath::XXPathExtension;
+using com::sun::star::xml::xpath::XPathObjectType_XPATH_UNDEFINED;
+
+
+namespace xforms
+{
+
+ComputedExpression::ComputedExpression()
+ : mbIsEmpty( true ),
+ mbIsSimple( true )
+{
+}
+
+ComputedExpression::~ComputedExpression()
+{
+}
+
+
+void ComputedExpression::setExpression( const OUString& rExpression )
+{
+ // set new expression, and clear pre-computed results
+ msExpression = rExpression;
+ mbIsEmpty = _checkExpression( " *" );
+ mbIsSimple = false;
+ mxResult.clear();
+}
+
+
+bool ComputedExpression::_checkExpression( const char* pExpression ) const
+{
+ assert(pExpression && "no expression?");
+
+ // call RegExp engine
+ i18nutil::SearchOptions2 aSearchOptions;
+ aSearchOptions.AlgorithmType2 = css::util::SearchAlgorithms2::REGEXP;
+ aSearchOptions.searchString = OUString( pExpression, strlen(pExpression), RTL_TEXTENCODING_ASCII_US );
+ utl::TextSearch aTextSearch( aSearchOptions );
+
+ sal_Int32 nLength = msExpression.getLength();
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = nLength;
+ bool bSearch = aTextSearch.SearchForward( msExpression, &nStart, &nEnd );
+
+ // our expression is static only if 1) we found our regexp, and 2)
+ // the regexp goes from beginning to end.
+ return ( nLength == 0 || bSearch )
+ && ( nStart == 0 && nEnd == nLength );
+}
+
+bool ComputedExpression::isSimpleExpression() const
+{
+ // actual work is done by setExpression
+ return mbIsEmpty || mbIsSimple;
+}
+
+
+bool ComputedExpression::_evaluate(
+ const xforms::EvaluationContext& rContext,
+ const OUString& sExpression )
+{
+ OSL_ENSURE( rContext.mxContextNode.is(), "no context node in context" );
+
+ // obtain value by evaluating XPath expression
+ mxResult.clear();
+ try
+ {
+ mxResult = _getXPathAPI(rContext)->eval( rContext.mxContextNode,
+ sExpression );
+ }
+ catch( const Exception& )
+ {
+ ; // ignore exception -> mxResult will be empty
+ }
+
+ return hasValue();
+}
+
+bool ComputedExpression::evaluate( const EvaluationContext& rContext )
+{
+ // for simple expression we don't need to re-evaluate (if we have
+ // an older result); neither for empty expressions
+ if( mbIsEmpty || (mxResult.is() && mbIsSimple) )
+ return true;
+
+ return _evaluate( rContext, _getExpressionForEvaluation() );
+}
+
+
+bool ComputedExpression::hasValue() const
+{
+ return mxResult.is() &&
+ mxResult->getObjectType() != XPathObjectType_XPATH_UNDEFINED;
+}
+
+void ComputedExpression::clear()
+{
+ mxResult.clear();
+}
+
+
+OUString ComputedExpression::getString() const
+{
+ return mxResult.is() ? mxResult->getString() : OUString();
+}
+
+bool ComputedExpression::getBool( bool bDefault ) const
+{
+ return mxResult.is() ? mxResult->getBoolean() : bDefault;
+}
+
+
+Reference<XXPathAPI> ComputedExpression::_getXPathAPI(const xforms::EvaluationContext& aContext)
+{
+ // create XPath API, then register namespaces
+ Reference<XXPathAPI> xXPath( XPathAPI::create( comphelper::getProcessComponentContext() ) );
+
+ // register xforms extension#
+ Reference< XComponentContext > aComponentContext = comphelper::getProcessComponentContext();
+ Reference< XXPathExtension > aExtension = XPathExtension::createWithModel(aComponentContext, aContext.mxModel, aContext.mxContextNode);
+ xXPath->registerExtensionInstance(aExtension);
+
+ // register namespaces
+ if( aContext.mxNamespaces.is() )
+ {
+ Sequence<OUString> aPrefixes =aContext.mxNamespaces->getElementNames();
+ sal_Int32 nCount = aPrefixes.getLength();
+ const OUString* pPrefixes = aPrefixes.getConstArray();
+ for( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ const OUString* pNamePrefix = &pPrefixes[i];
+ OUString sNameURL;
+ aContext.mxNamespaces->getByName( *pNamePrefix ) >>= sNameURL;
+ xXPath->registerNS( *pNamePrefix, sNameURL );
+ }
+ }
+
+ // done, so return xXPath-object
+ return xXPath;
+}
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/computedexpression.hxx b/forms/source/xforms/computedexpression.hxx
new file mode 100644
index 0000000000..17f39cd21a
--- /dev/null
+++ b/forms/source/xforms/computedexpression.hxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+
+// forward declaractions
+namespace com::sun::star
+{
+ namespace xml
+ {
+ namespace dom { class XNode; }
+ namespace dom { class XNodeset; }
+ namespace xpath { class XXPathAPI; }
+ namespace xpath { class XXPathObject; }
+ }
+ namespace container { class XNameContainer; }
+}
+namespace xforms { class EvaluationContext; }
+
+
+namespace xforms
+{
+
+/** ComputedExpression represents an XPath Expression and caches results.
+ *
+ * As this class has no virtual methods, it should never be used
+ * polymorphically. */
+class ComputedExpression
+{
+ /// the expression string
+ OUString msExpression;
+
+ /// is msExpression empty?
+ bool mbIsEmpty;
+
+protected:
+ /// is msExpression a simple expression?
+ bool mbIsSimple;
+
+ /// the result from the last bind
+ css::uno::Reference<css::xml::xpath::XXPathObject> mxResult;
+
+
+ /// implementation of isSimpleExpression
+ bool _checkExpression( const char* pExpression ) const;
+
+ /// allow manipulation of the expression before it is evaluated
+ // the default implementation is to do nothing...
+ const OUString& _getExpressionForEvaluation() const { return msExpression; }
+
+ /// obtain a (suitable) XPathAPI implementation
+ static css::uno::Reference<css::xml::xpath::XXPathAPI> _getXPathAPI(const xforms::EvaluationContext& aContext);
+
+ /// evaluate the expression relative to the content node.
+ bool _evaluate( const xforms::EvaluationContext& rContext,
+ const OUString& sExpression );
+
+
+public:
+ ComputedExpression();
+ ~ComputedExpression();
+
+
+ /// get the expression string
+ const OUString& getExpression() const { return msExpression;}
+
+ /// set a new expression string
+ void setExpression( const OUString& rExpression );
+
+ /// do we have an actual expression?
+ bool isEmptyExpression() const { return mbIsEmpty;}
+
+ /// heuristically determine whether this expression is 'simple',
+ /// i.e. whether its value will change depending on the values
+ /// of other nodes
+ bool isSimpleExpression() const;
+
+
+ /// evaluate the expression relative to the content node.
+ bool evaluate( const xforms::EvaluationContext& rContext );
+
+
+ /// does this expression have a value?
+ bool hasValue() const;
+
+
+ /// remove value/evaluate results
+ void clear();
+
+
+ // get the result of this expression as string/bool/...
+ // (Results will be based on the last call of evaluate(..). The caller
+ // must call evaluate to ensure current results.)
+ css::uno::Reference<css::xml::xpath::XXPathObject> const & getXPath() const { return mxResult;}
+ bool getBool( bool bDefault = false ) const;
+ OUString getString() const;
+
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/convert.cxx b/forms/source/xforms/convert.cxx
new file mode 100644
index 0000000000..0f7193422f
--- /dev/null
+++ b/forms/source/xforms/convert.cxx
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "convert.hxx"
+
+#include <sstream>
+#include <rtl/math.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <tools/date.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <comphelper/sequence.hxx>
+#include <unotools/datetime.hxx>
+
+using xforms::Convert;
+using com::sun::star::uno::Any;
+using namespace utl;
+
+Convert::Convert()
+{
+ init();
+}
+
+namespace
+{
+
+ OUString lcl_toXSD_OUString( const Any& rAny )
+ { OUString sStr; rAny >>= sStr; return sStr; }
+
+
+ Any lcl_toAny_OUString( const OUString& rStr )
+ { return Any(rStr); }
+
+ OUString lcl_toXSD_bool( const Any& rAny )
+ { bool b = false; rAny >>= b; return b ? OUString("true") : OUString("false"); }
+
+ Any lcl_toAny_bool( const OUString& rStr )
+ {
+ bool b = ( rStr == "true" || rStr == "1" );
+ return Any( b );
+ }
+
+ OUString lcl_toXSD_double( const Any& rAny )
+ {
+ double f = 0.0;
+ rAny >>= f;
+
+ return std::isfinite( f )
+ ? rtl::math::doubleToUString( f, rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.',
+ true )
+ : OUString();
+ }
+
+
+ Any lcl_toAny_double( const OUString& rString )
+ {
+ rtl_math_ConversionStatus eStatus;
+ double f = rtl::math::stringToDouble(
+ rString.replace(',','.'), '.', ',', &eStatus );
+ return ( eStatus == rtl_math_ConversionStatus_Ok ) ? Any( f ) : Any();
+ }
+
+ void lcl_appendInt32ToBuffer( const sal_Int32 _nValue, OUStringBuffer& _rBuffer, sal_Int16 _nMinDigits )
+ {
+ if ( ( _nMinDigits >= 4 ) && ( _nValue < 1000 ) )
+ _rBuffer.append( '0' );
+ if ( ( _nMinDigits >= 3 ) && ( _nValue < 100 ) )
+ _rBuffer.append( '0' );
+ if ( ( _nMinDigits >= 2 ) && ( _nValue < 10 ) )
+ _rBuffer.append( '0' );
+ _rBuffer.append( _nValue );
+ }
+
+
+ OUString lcl_toXSD_UNODate_typed( const css::util::Date& rDate )
+ {
+
+ OUStringBuffer sInfo;
+ lcl_appendInt32ToBuffer( rDate.Year, sInfo, 4 );
+ sInfo.append( "-" );
+ lcl_appendInt32ToBuffer( rDate.Month, sInfo, 2 );
+ sInfo.append( "-" );
+ lcl_appendInt32ToBuffer( rDate.Day, sInfo, 2 );
+
+ return sInfo.makeStringAndClear();
+ }
+
+
+ OUString lcl_toXSD_UNODate( const Any& rAny )
+ {
+ css::util::Date aDate;
+ OSL_VERIFY( rAny >>= aDate );
+ return lcl_toXSD_UNODate_typed( aDate );
+ }
+
+
+ css::util::Date lcl_toUNODate( std::u16string_view rString )
+ {
+ css::util::Date aDate( 1, 1, 1900 );
+
+ bool bWellformed = ISO8601parseDate(rString, aDate);
+
+ // sanity checks
+ if ( ( aDate.Year > 9999 ) || ( aDate.Month < 1 ) || ( aDate.Month > 12 ) || ( aDate.Day < 1 ) || ( aDate.Day > 31 ) )
+ bWellformed = false;
+ else
+ {
+ ::Date aDateCheck( 1, aDate.Month, aDate.Year );
+ if ( aDate.Day > aDateCheck.GetDaysInMonth() )
+ bWellformed = false;
+ }
+
+ // all okay?
+ if ( !bWellformed )
+ throw com::sun::star::lang::IllegalArgumentException();
+
+ return aDate;
+ }
+
+
+ Any lcl_toAny_UNODate( const OUString& rString )
+ {
+ return Any( lcl_toUNODate( rString ) );
+ }
+
+
+ OUString lcl_toXSD_UNOTime_typed( const css::util::Time& rTime )
+ {
+
+ OUStringBuffer sInfo;
+ lcl_appendInt32ToBuffer( rTime.Hours, sInfo, 2 );
+ sInfo.append( ":" );
+ lcl_appendInt32ToBuffer( rTime.Minutes, sInfo, 2 );
+ sInfo.append( ":" );
+ lcl_appendInt32ToBuffer( rTime.Seconds, sInfo, 2 );
+ if ( rTime.NanoSeconds != 0 )
+ {
+ OSL_ENSURE(rTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
+ sInfo.append('.');
+ std::ostringstream ostr;
+ ostr.fill('0');
+ ostr.width(9);
+ ostr << rTime.NanoSeconds;
+ sInfo.appendAscii(ostr.str().c_str());
+ }
+
+ return sInfo.makeStringAndClear();
+ }
+
+
+ OUString lcl_toXSD_UNOTime( const Any& rAny )
+ {
+ css::util::Time aTime;
+ OSL_VERIFY( rAny >>= aTime );
+ return lcl_toXSD_UNOTime_typed( aTime );
+ }
+
+
+ css::util::Time lcl_toUNOTime( std::u16string_view rString )
+ {
+ css::util::Time aTime;
+
+ bool bWellformed = ISO8601parseTime(rString, aTime);
+
+ // sanity checks
+ // note that Seconds == 60 denotes leap seconds. Normally, they're not allowed everywhere,
+ // but we accept them all the time for simplicity reasons
+ if ( ( aTime.Hours > 24 )
+ || ( aTime.Minutes > 59 )
+ || ( aTime.Seconds > 60 )
+ )
+ bWellformed = false;
+
+ if ( bWellformed
+ && ( aTime.Hours == 24 )
+ && ( ( aTime.Minutes != 0 )
+ || ( aTime.Seconds != 0 )
+ || ( aTime.NanoSeconds != 0 )
+ )
+ )
+ bWellformed = false;
+
+ // all okay?
+ if ( !bWellformed )
+ throw com::sun::star::lang::IllegalArgumentException();
+
+ return aTime;
+ }
+
+
+ Any lcl_toAny_UNOTime( const OUString& rString )
+ {
+ return Any( lcl_toUNOTime( rString ) );
+ }
+
+
+ OUString lcl_toXSD_UNODateTime( const Any& rAny )
+ {
+ css::util::DateTime aDateTime;
+ OSL_VERIFY( rAny >>= aDateTime );
+
+ css::util::Date aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year );
+ OUString sDate = lcl_toXSD_UNODate_typed( aDate );
+
+ css::util::Time const aTime( aDateTime.NanoSeconds, aDateTime.Seconds,
+ aDateTime.Minutes, aDateTime.Hours, aDateTime.IsUTC);
+ OUString sTime = lcl_toXSD_UNOTime_typed( aTime );
+
+ OUString sRet = sDate + "T" + sTime;
+ return sRet;
+ }
+
+
+ Any lcl_toAny_UNODateTime( const OUString& rString )
+ {
+ // separate the date from the time part
+ sal_Int32 nDateTimeSep = rString.indexOf( 'T' );
+ if ( nDateTimeSep == -1 )
+ nDateTimeSep = rString.indexOf( 't' );
+
+ css::util::Date aDate;
+ css::util::Time aTime;
+ if ( nDateTimeSep == -1 )
+ { // no time part
+ aDate = lcl_toUNODate( rString );
+ }
+ else
+ {
+ aDate = lcl_toUNODate( rString.subView( 0, nDateTimeSep ) );
+ aTime = lcl_toUNOTime( rString.subView( nDateTimeSep + 1 ) );
+ }
+ css::util::DateTime aDateTime(
+ aTime.NanoSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours,
+ aDate.Day, aDate.Month, aDate.Year, aTime.IsUTC
+ );
+ return Any( aDateTime );
+ }
+}
+
+
+void Convert::init()
+{
+ maMap[ cppu::UnoType<OUString>::get() ] = Convert_t(&lcl_toXSD_OUString, &lcl_toAny_OUString);
+ maMap[ cppu::UnoType<bool>::get() ] = Convert_t(&lcl_toXSD_bool, &lcl_toAny_bool);
+ maMap[ cppu::UnoType<double>::get() ] = Convert_t(&lcl_toXSD_double, &lcl_toAny_double);
+ maMap[ cppu::UnoType<css::util::Date>::get() ] = Convert_t( &lcl_toXSD_UNODate, &lcl_toAny_UNODate );
+ maMap[ cppu::UnoType<css::util::Time>::get() ] = Convert_t( &lcl_toXSD_UNOTime, &lcl_toAny_UNOTime );
+ maMap[ cppu::UnoType<css::util::DateTime>::get() ] = Convert_t( &lcl_toXSD_UNODateTime, &lcl_toAny_UNODateTime );
+}
+
+
+Convert& Convert::get()
+{
+ // create our Singleton instance on demand
+ static Convert aConvert;
+ return aConvert;
+}
+
+bool Convert::hasType( const css::uno::Type& rType )
+{
+ return maMap.find( rType ) != maMap.end();
+}
+
+css::uno::Sequence<css::uno::Type> Convert::getTypes() const
+{
+ return comphelper::mapKeysToSequence( maMap );
+}
+
+OUString Convert::toXSD( const css::uno::Any& rAny )
+{
+ Map_t::iterator aIter = maMap.find( rAny.getValueType() );
+ return aIter != maMap.end() ? aIter->second.first( rAny ) : OUString();
+}
+
+css::uno::Any Convert::toAny( const OUString& rValue,
+ const css::uno::Type& rType )
+{
+ Map_t::iterator aIter = maMap.find( rType );
+ return aIter != maMap.end() ? aIter->second.second( rValue ) : css::uno::Any();
+}
+
+
+OUString Convert::collapseWhitespace( const OUString& _rString )
+{
+ sal_Int32 nLength = _rString.getLength();
+ OUStringBuffer aBuffer( nLength );
+ const sal_Unicode* pStr = _rString.getStr();
+ bool bStrip = true;
+ for( sal_Int32 i = 0; i < nLength; i++ )
+ {
+ sal_Unicode c = pStr[i];
+ if( c == u'\x0008' ||
+ c == u'\x000A' ||
+ c == u'\x000D' ||
+ c == u' ' )
+ {
+ if( ! bStrip )
+ {
+ aBuffer.append( u' ' );
+ bStrip = true;
+ }
+ }
+ else
+ {
+ bStrip = false;
+ aBuffer.append( c );
+ }
+ }
+ if( aBuffer[ aBuffer.getLength() - 1 ] == u' ' )
+ aBuffer.setLength( aBuffer.getLength() - 1 );
+ return aBuffer.makeStringAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/convert.hxx b/forms/source/xforms/convert.hxx
new file mode 100644
index 0000000000..c7073f7c36
--- /dev/null
+++ b/forms/source/xforms/convert.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <map>
+
+namespace com::sun::star::uno
+{
+ class Any;
+ class Type;
+}
+
+namespace xforms
+{
+
+struct TypeLess
+{
+ bool operator()( const css::uno::Type& rType1,
+ const css::uno::Type& rType2 ) const
+ { return rType1.getTypeName() < rType2.getTypeName(); }
+};
+
+class Convert
+{
+ // hold conversion objects
+ typedef OUString (*fn_toXSD)( const css::uno::Any& );
+ typedef css::uno::Any (*fn_toAny)( const OUString& );
+ typedef std::pair<fn_toXSD,fn_toAny> Convert_t;
+ typedef std::map<css::uno::Type, Convert_t, TypeLess> Map_t;
+ Map_t maMap;
+
+ Convert();
+
+ void init();
+
+public:
+ /** get/create Singleton class */
+ static Convert& get();
+
+ /// can we convert this type?
+ bool hasType( const css::uno::Type& );
+
+ /// get list of convertible types
+ css::uno::Sequence<css::uno::Type> getTypes() const;
+
+ /// convert any to XML representation
+ OUString toXSD( const css::uno::Any& rAny );
+
+ /// convert XML representation to Any of given type
+ css::uno::Any toAny( const OUString&, const css::uno::Type& );
+
+ /** replace all sequences of 0x08, 0x0A, 0x0D, 0x20 with a single 0x20.
+ also strip leading/trailing whitespace.
+ */
+ static OUString collapseWhitespace( const OUString& _rString );
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/datatyperepository.cxx b/forms/source/xforms/datatyperepository.cxx
new file mode 100644
index 0000000000..aba911c3f7
--- /dev/null
+++ b/forms/source/xforms/datatyperepository.cxx
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "datatyperepository.hxx"
+#include "datatypes.hxx"
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include <frm_strings.hxx>
+#include <property.hxx>
+
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+#include <comphelper/enumhelper.hxx>
+
+namespace xforms
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::util::VetoException;
+ using ::com::sun::star::container::NoSuchElementException;
+ using ::com::sun::star::container::ElementExistException;
+ using ::com::sun::star::container::XEnumeration;
+ using ::com::sun::star::xsd::XDataType;
+ using namespace frm;
+
+ ODataTypeRepository::ODataTypeRepository( )
+ {
+
+ // insert some basic types
+ OUString sName( ResourceManager::loadString(RID_STR_DATATYPE_STRING) );
+ m_aRepository[ sName ] = new OStringType( sName, css::xsd::DataTypeClass::STRING );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_URL);
+ m_aRepository[ sName ] = new OAnyURIType( sName, css::xsd::DataTypeClass::anyURI );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_BOOLEAN);
+ m_aRepository[ sName ] = new OBooleanType( sName );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DECIMAL);
+ m_aRepository[ sName ] = new ODecimalType( sName, css::xsd::DataTypeClass::DECIMAL );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_FLOAT);
+ m_aRepository[ sName ] = new ODecimalType( sName, css::xsd::DataTypeClass::FLOAT );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DOUBLE);
+ m_aRepository[ sName ] = new ODecimalType( sName, css::xsd::DataTypeClass::DOUBLE );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DATE);
+ m_aRepository[ sName ] = new ODateType( sName );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_TIME);
+ m_aRepository[ sName ] = new OTimeType( sName );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DATETIME);
+ m_aRepository[ sName ] = new ODateTimeType( sName );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_YEAR);
+ m_aRepository[ sName ] = new OShortIntegerType( sName, css::xsd::DataTypeClass::gYear );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_MONTH);
+ m_aRepository[ sName ] = new OShortIntegerType( sName, css::xsd::DataTypeClass::gMonth );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DAY);
+ m_aRepository[ sName ] = new OShortIntegerType( sName, css::xsd::DataTypeClass::gDay );
+ }
+
+
+ ODataTypeRepository::~ODataTypeRepository( )
+ {
+ }
+
+
+ ODataTypeRepository::Repository::iterator ODataTypeRepository::implLocate( const OUString& _rName, bool _bAllowMiss )
+ {
+ Repository::iterator aTypePos = m_aRepository.find( _rName );
+ if ( aTypePos == m_aRepository.end() && !_bAllowMiss )
+ throw NoSuchElementException( OUString(), *this );
+
+ return aTypePos;
+ }
+
+
+ Reference< XDataType > SAL_CALL ODataTypeRepository::getBasicDataType( sal_Int16 dataTypeClass )
+ {
+ Reference< XDataType > xReturn;
+
+ for ( Repository::const_iterator lookup = m_aRepository.begin();
+ ( lookup != m_aRepository.end() ) && ! xReturn.is();
+ ++lookup
+ )
+ {
+ if ( lookup->second->getIsBasic() && ( lookup->second->getTypeClass() == dataTypeClass ) )
+ xReturn = lookup->second.get();
+ }
+
+ if ( !xReturn.is() )
+ throw NoSuchElementException( OUString(), *this );
+
+ return xReturn;
+ }
+
+
+ Reference< XDataType > SAL_CALL ODataTypeRepository::cloneDataType( const OUString& sourceName, const OUString& newName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Repository::iterator aTypePos = implLocate( newName, true );
+ if ( aTypePos != m_aRepository.end() )
+ throw ElementExistException( OUString(), *this );
+
+ aTypePos = implLocate( sourceName );
+ rtl::Reference<OXSDDataType> pClone = aTypePos->second->clone( newName );
+ m_aRepository[ newName ] = pClone;
+
+ return pClone;
+ }
+
+
+ void SAL_CALL ODataTypeRepository::revokeDataType( const OUString& typeName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Repository::iterator aTypePos = implLocate( typeName );
+ if ( aTypePos->second->getIsBasic() )
+ // "This is a built-in type and cannot be removed."
+ throw VetoException(ResourceManager::loadString(RID_STR_XFORMS_CANT_REMOVE_TYPE), *this );
+
+ m_aRepository.erase( aTypePos );
+ }
+
+
+ Reference< XDataType > SAL_CALL ODataTypeRepository::getDataType( const OUString& typeName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return implLocate( typeName )->second;
+ }
+
+
+ Reference< XEnumeration > SAL_CALL ODataTypeRepository::createEnumeration( )
+ {
+ return new ::comphelper::OEnumerationByName( this );
+ }
+
+
+ Any SAL_CALL ODataTypeRepository::getByName( const OUString& aName )
+ {
+ return Any( getDataType( aName ) );
+ }
+
+
+ Sequence< OUString > SAL_CALL ODataTypeRepository::getElementNames( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return comphelper::mapKeysToSequence( m_aRepository );
+ }
+
+
+ sal_Bool SAL_CALL ODataTypeRepository::hasByName( const OUString& aName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_aRepository.find( aName ) != m_aRepository.end();
+ }
+
+
+ Type SAL_CALL ODataTypeRepository::getElementType( )
+ {
+ return cppu::UnoType<XDataType>::get();
+ }
+
+
+ sal_Bool SAL_CALL ODataTypeRepository::hasElements( )
+ {
+ return !m_aRepository.empty();
+ }
+
+
+ // type specific implementation of registerProperties, using explicit
+ // template instantiations
+
+ template<>
+ void OValueLimitedType<css::util::Date>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_DATE, PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_DATE, PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_DATE, PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_DATE, PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+ template<>
+ void OValueLimitedType<css::util::Time>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_TIME, PROPERTY_ID_XSD_MAX_INCLUSIVE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_TIME, PROPERTY_ID_XSD_MAX_EXCLUSIVE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_TIME, PROPERTY_ID_XSD_MIN_INCLUSIVE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_TIME, PROPERTY_ID_XSD_MIN_EXCLUSIVE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+ template<>
+ void OValueLimitedType<css::util::DateTime>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_DATE_TIME, PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_DATE_TIME, PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_DATE_TIME, PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_DATE_TIME, PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+ template<>
+ void OValueLimitedType<double>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_DOUBLE, PROPERTY_ID_XSD_MAX_INCLUSIVE_DOUBLE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_DOUBLE, PROPERTY_ID_XSD_MAX_EXCLUSIVE_DOUBLE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_DOUBLE, PROPERTY_ID_XSD_MIN_INCLUSIVE_DOUBLE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_DOUBLE, PROPERTY_ID_XSD_MIN_EXCLUSIVE_DOUBLE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+ template<>
+ void OValueLimitedType<sal_Int16>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_INT, PROPERTY_ID_XSD_MAX_INCLUSIVE_INT, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_INT, PROPERTY_ID_XSD_MAX_EXCLUSIVE_INT, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_INT, PROPERTY_ID_XSD_MIN_INCLUSIVE_INT, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_INT, PROPERTY_ID_XSD_MIN_EXCLUSIVE_INT, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+} // namespace xforms
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/datatyperepository.hxx b/forms/source/xforms/datatyperepository.hxx
new file mode 100644
index 0000000000..cd5ea944d6
--- /dev/null
+++ b/forms/source/xforms/datatyperepository.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/xforms/XDataTypeRepository.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <map>
+
+
+namespace xforms
+{
+
+ class OXSDDataType;
+
+ typedef ::cppu::WeakImplHelper < css::xforms::XDataTypeRepository
+ > ODataTypeRepository_Base;
+ class ODataTypeRepository : public ODataTypeRepository_Base
+ {
+ private:
+ typedef ::rtl::Reference< OXSDDataType > DataType;
+ typedef ::std::map< OUString, DataType > Repository;
+
+ ::osl::Mutex m_aMutex;
+ Repository m_aRepository;
+
+ public:
+ ODataTypeRepository( );
+
+ protected:
+ virtual ~ODataTypeRepository( ) override;
+
+ // XDataTypeRepository
+ virtual css::uno::Reference< css::xsd::XDataType > SAL_CALL getBasicDataType( sal_Int16 dataTypeClass ) override;
+ virtual css::uno::Reference< css::xsd::XDataType > SAL_CALL cloneDataType( const OUString& sourceName, const OUString& newName ) override;
+ virtual void SAL_CALL revokeDataType( const OUString& typeName ) override;
+ virtual css::uno::Reference< css::xsd::XDataType > SAL_CALL getDataType( const OUString& typeName ) override;
+
+ // XEnumerationAccess (base of XDataTypeRepository)
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration( ) override;
+
+ // XNameAccess (base of XDataTypeRepository)
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess (base of XEnumerationAccess and XNameAccess)
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+
+ private:
+ ODataTypeRepository( const ODataTypeRepository& ) = delete;
+ ODataTypeRepository& operator=( const ODataTypeRepository& ) = delete;
+
+ private:
+ /** locates the type with the given name in our repository, or throws an exception if there is no such type
+ */
+ Repository::iterator implLocate( const OUString& _rName, bool _bAllowMiss = false );
+ };
+
+
+} // namespace xforms
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/datatypes.cxx b/forms/source/xforms/datatypes.cxx
new file mode 100644
index 0000000000..e2ad59e177
--- /dev/null
+++ b/forms/source/xforms/datatypes.cxx
@@ -0,0 +1,1199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "datatypes.hxx"
+#include "resourcehelper.hxx"
+#include <frm_strings.hxx>
+#include <property.hxx>
+#include <strings.hrc>
+#include "convert.hxx"
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+#include <com/sun/star/xsd/WhiteSpaceTreatment.hpp>
+#include <o3tl/string_view.hxx>
+#include <tools/datetime.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+#include <utility>
+
+
+namespace xforms
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::util::Date;
+ using ::com::sun::star::util::Time;
+ using ::com::sun::star::util::DateTime;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::beans::XPropertyChangeListener;
+ using ::com::sun::star::beans::XVetoableChangeListener;
+
+ using ::com::sun::star::beans::PropertyAttribute::BOUND;
+ using ::com::sun::star::beans::PropertyAttribute::READONLY;
+
+ using namespace ::com::sun::star::xsd;
+ using namespace ::frm;
+ U_NAMESPACE_USE
+
+ OXSDDataType::OXSDDataType( OUString _aName, sal_Int16 _nTypeClass )
+ :OXSDDataType_PBase( m_aBHelper )
+ ,m_bIsBasic( true )
+ ,m_nTypeClass( _nTypeClass )
+ ,m_sName(std::move( _aName ))
+ ,m_nWST( WhiteSpaceTreatment::Preserve )
+ ,m_bPatternMatcherDirty( true )
+ {
+ }
+
+
+ OXSDDataType::~OXSDDataType()
+ {
+ }
+
+
+ void OXSDDataType::registerProperties()
+ {
+ registerProperty( PROPERTY_NAME, PROPERTY_ID_NAME, BOUND, &m_sName, cppu::UnoType<decltype(m_sName)>::get() );
+ registerProperty( PROPERTY_XSD_WHITESPACE, PROPERTY_ID_XSD_WHITESPACE, BOUND, &m_nWST, cppu::UnoType<cppu::UnoUnsignedShortType>::get() );
+ registerProperty( PROPERTY_XSD_PATTERN, PROPERTY_ID_XSD_PATTERN, BOUND, &m_sPattern, cppu::UnoType<decltype(m_sPattern)>::get() );
+
+ registerProperty( PROPERTY_XSD_IS_BASIC, PROPERTY_ID_XSD_IS_BASIC, READONLY, &m_bIsBasic, cppu::UnoType<decltype(m_bIsBasic)>::get() );
+ registerProperty( PROPERTY_XSD_TYPE_CLASS, PROPERTY_ID_XSD_TYPE_CLASS, READONLY, &m_nTypeClass, cppu::UnoType<decltype(m_nTypeClass)>::get() );
+ }
+
+
+ void OXSDDataType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ m_bIsBasic = false;
+ m_nTypeClass = _rCloneSource.m_nTypeClass;
+ m_sPattern = _rCloneSource.m_sPattern;
+ m_nWST = _rCloneSource.m_nWST;
+ }
+
+
+ rtl::Reference<OXSDDataType> OXSDDataType::clone( const OUString& _rNewName ) const
+ {
+ rtl::Reference<OXSDDataType> pClone = createClone( _rNewName );
+ pClone->initializeClone( *this );
+ return pClone;
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OXSDDataType, OXSDDataType_Base, ::comphelper::OPropertyContainer )
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OXSDDataType, OXSDDataType_Base, ::comphelper::OPropertyContainer )
+
+ OUString SAL_CALL OXSDDataType::getName( )
+ {
+ return m_sName;
+ }
+
+
+ void SAL_CALL OXSDDataType::setName( const OUString& aName )
+ {
+ // TODO: check the name for conflicts in the repository
+ setFastPropertyValue( PROPERTY_ID_NAME, Any(aName) );
+ SAL_WARN_IF( m_sName != aName, "forms.misc", "OXSDDataType::setName: inconsistency!" );
+ }
+
+
+ OUString SAL_CALL OXSDDataType::getPattern()
+ {
+ return m_sPattern;
+ }
+
+
+ void SAL_CALL OXSDDataType::setPattern( const OUString& _pattern )
+ {
+ setFastPropertyValue( PROPERTY_ID_XSD_PATTERN, Any(_pattern) );
+ SAL_WARN_IF( m_sPattern != _pattern, "forms.misc", "OXSDDataType::setPattern: inconsistency!" );
+ }
+
+
+ sal_Int16 SAL_CALL OXSDDataType::getWhiteSpaceTreatment()
+ {
+ return m_nWST;
+ }
+
+
+ void SAL_CALL OXSDDataType::setWhiteSpaceTreatment( sal_Int16 _whitespacetreatment )
+ {
+ setFastPropertyValue( PROPERTY_ID_XSD_WHITESPACE, Any(_whitespacetreatment) );
+ SAL_WARN_IF( m_nWST != _whitespacetreatment, "forms.misc", "OXSDDataType::setWhiteSpaceTreatment: inconsistency!" );
+ }
+
+
+ sal_Bool SAL_CALL OXSDDataType::getIsBasic()
+ {
+ return m_bIsBasic;
+ }
+
+
+ sal_Int16 SAL_CALL OXSDDataType::getTypeClass()
+ {
+ return m_nTypeClass;
+ }
+
+
+ sal_Bool OXSDDataType::validate( const OUString& sValue )
+ {
+ return bool(!_validate( sValue ));
+ }
+
+
+ OUString OXSDDataType::explainInvalid( const OUString& sValue )
+ {
+ // get reason
+ TranslateId pReason = _validate( sValue );
+
+ // get resource and return localized string
+ return (!pReason)
+ ? OUString()
+ : getResource( pReason, sValue,
+ _explainInvalid( pReason ) );
+ }
+
+ OUString OXSDDataType::_explainInvalid(TranslateId rReason)
+ {
+ if ( RID_STR_XFORMS_PATTERN_DOESNT_MATCH == rReason )
+ {
+ OSL_ENSURE( !m_sPattern.isEmpty(), "OXSDDataType::_explainInvalid: how can this error occur without a regular expression?" );
+ return m_sPattern;
+ }
+ return OUString();
+ }
+
+ namespace
+ {
+ void lcl_initializePatternMatcher( ::std::unique_ptr< RegexMatcher >& _rpMatcher, const OUString& _rPattern )
+ {
+ UErrorCode nMatchStatus = U_ZERO_ERROR;
+ UnicodeString aIcuPattern( reinterpret_cast<const UChar *>(_rPattern.getStr()), _rPattern.getLength() );
+ _rpMatcher.reset( new RegexMatcher( aIcuPattern, 0, nMatchStatus ) );
+ OSL_ENSURE( U_SUCCESS( nMatchStatus ), "lcl_initializePatternMatcher: invalid pattern property!" );
+ // if asserts, then something changed our pattern without going to convertFastPropertyValue/checkPropertySanity
+ }
+
+ bool lcl_matchString( RegexMatcher& _rMatcher, const OUString& _rText )
+ {
+ UErrorCode nMatchStatus = U_ZERO_ERROR;
+ UnicodeString aInput( reinterpret_cast<const UChar *>(_rText.getStr()), _rText.getLength() );
+ _rMatcher.reset( aInput );
+ if ( _rMatcher.matches( nMatchStatus ) )
+ {
+ int32_t nStart = _rMatcher.start( nMatchStatus );
+ int32_t nEnd = _rMatcher.end ( nMatchStatus );
+ if ( ( nStart == 0 ) && ( nEnd == _rText.getLength() ) )
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ TranslateId OXSDDataType::_validate( const OUString& _rValue )
+ {
+ // care for the regular expression
+ if ( !m_sPattern.isEmpty() )
+ {
+ // ensure our pattern matcher is up to date
+ if ( m_bPatternMatcherDirty )
+ {
+ lcl_initializePatternMatcher( m_pPatternMatcher, m_sPattern );
+ m_bPatternMatcherDirty = false;
+ }
+
+ // let it match the string
+ if (!lcl_matchString(*m_pPatternMatcher, _rValue))
+ return RID_STR_XFORMS_PATTERN_DOESNT_MATCH;
+ }
+
+ return {};
+ }
+
+
+ sal_Bool OXSDDataType::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ // let the base class do the conversion
+ if ( !OXSDDataType_PBase::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue ) )
+ return false;
+
+ // sanity checks
+ OUString sErrorMessage;
+ if ( !checkPropertySanity( _nHandle, _rConvertedValue, sErrorMessage ) )
+ {
+ throw IllegalArgumentException(sErrorMessage, *this, 0);
+ }
+
+ return true;
+ }
+
+
+ void SAL_CALL OXSDDataType::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ OXSDDataType_PBase::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ if ( _nHandle == PROPERTY_ID_XSD_PATTERN )
+ m_bPatternMatcherDirty = true;
+ }
+
+
+ bool OXSDDataType::checkPropertySanity( sal_Int32 _nHandle, const css::uno::Any& _rNewValue, OUString& _rErrorMessage )
+ {
+ if ( _nHandle == PROPERTY_ID_XSD_PATTERN )
+ {
+ OUString sPattern;
+ OSL_VERIFY( _rNewValue >>= sPattern );
+
+ UnicodeString aIcuPattern( reinterpret_cast<const UChar *>(sPattern.getStr()), sPattern.getLength() );
+ UErrorCode nMatchStatus = U_ZERO_ERROR;
+ RegexMatcher aMatcher( aIcuPattern, 0, nMatchStatus );
+ if ( U_FAILURE( nMatchStatus ) )
+ {
+ _rErrorMessage = "This is no valid pattern.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ void SAL_CALL OXSDDataType::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+ {
+ OXSDDataType_PBase::setPropertyValue( aPropertyName, aValue );
+ }
+
+
+ Any SAL_CALL OXSDDataType::getPropertyValue( const OUString& PropertyName )
+ {
+ return OXSDDataType_PBase::getPropertyValue( PropertyName );
+ }
+
+
+ void SAL_CALL OXSDDataType::addPropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& xListener )
+ {
+ OXSDDataType_PBase::addPropertyChangeListener( aPropertyName, xListener );
+ }
+
+
+ void SAL_CALL OXSDDataType::removePropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& aListener )
+ {
+ OXSDDataType_PBase::removePropertyChangeListener( aPropertyName, aListener );
+ }
+
+
+ void SAL_CALL OXSDDataType::addVetoableChangeListener( const OUString& PropertyName, const Reference< XVetoableChangeListener >& aListener )
+ {
+ OXSDDataType_PBase::addVetoableChangeListener( PropertyName, aListener );
+ }
+
+
+ void SAL_CALL OXSDDataType::removeVetoableChangeListener( const OUString& PropertyName, const Reference< XVetoableChangeListener >& aListener )
+ {
+ OXSDDataType_PBase::removeVetoableChangeListener( PropertyName, aListener );
+ }
+
+ OValueLimitedType_Base::OValueLimitedType_Base( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OXSDDataType( _rName, _nTypeClass )
+ ,m_fCachedMaxInclusive( 0 )
+ ,m_fCachedMaxExclusive( 0 )
+ ,m_fCachedMinInclusive( 0 )
+ ,m_fCachedMinExclusive( 0 )
+ {
+ }
+
+
+ void OValueLimitedType_Base::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OXSDDataType::initializeClone( _rCloneSource );
+ initializeTypedClone( static_cast< const OValueLimitedType_Base& >( _rCloneSource ) );
+ }
+
+
+ void OValueLimitedType_Base::initializeTypedClone( const OValueLimitedType_Base& _rCloneSource )
+ {
+ m_aMaxInclusive = _rCloneSource.m_aMaxInclusive;
+ m_aMaxExclusive = _rCloneSource.m_aMaxExclusive;
+ m_aMinInclusive = _rCloneSource.m_aMinInclusive;
+ m_aMinExclusive = _rCloneSource.m_aMinExclusive;
+ m_fCachedMaxInclusive = _rCloneSource.m_fCachedMaxInclusive;
+ m_fCachedMaxExclusive = _rCloneSource.m_fCachedMaxExclusive;
+ m_fCachedMinInclusive = _rCloneSource.m_fCachedMinInclusive;
+ m_fCachedMinExclusive = _rCloneSource.m_fCachedMinExclusive;
+ }
+
+
+ void SAL_CALL OValueLimitedType_Base::setFastPropertyValue_NoBroadcast(
+ sal_Int32 _nHandle, const css::uno::Any& _rValue )
+ {
+ OXSDDataType::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+
+ // if one of our limit properties has been set, translate it into a double
+ // value, for later efficient validation
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE_TIME:
+ if ( m_aMaxInclusive.hasValue() )
+ normalizeValue( m_aMaxInclusive, m_fCachedMaxInclusive );
+ else
+ m_fCachedMaxInclusive = 0;
+ break;
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE_TIME:
+ if ( m_aMaxExclusive.hasValue() )
+ normalizeValue( m_aMaxExclusive, m_fCachedMaxExclusive );
+ else
+ m_fCachedMaxExclusive = 0;
+ break;
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE_TIME:
+ if ( m_aMinInclusive.hasValue() )
+ normalizeValue( m_aMinInclusive, m_fCachedMinInclusive );
+ else
+ m_fCachedMinInclusive = 0;
+ break;
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE_TIME:
+ if ( m_aMinExclusive.hasValue() )
+ normalizeValue( m_aMinExclusive, m_fCachedMinExclusive );
+ else
+ m_fCachedMinExclusive = 0;
+ break;
+ }
+ }
+
+
+ bool OValueLimitedType_Base::_getValue( const OUString& rValue, double& fValue )
+ {
+ // convert to double
+ rtl_math_ConversionStatus eStatus;
+ sal_Int32 nEnd;
+ double f = ::rtl::math::stringToDouble(
+ rValue.replace(',','.'), '.', u'\0', &eStatus, &nEnd );
+
+ // error checking...
+ bool bReturn = false;
+ if( eStatus == rtl_math_ConversionStatus_Ok
+ && nEnd == rValue.getLength() )
+ {
+ bReturn = true;
+ fValue = f;
+ }
+ return bReturn;
+ }
+
+ TranslateId OValueLimitedType_Base::_validate( const OUString& rValue )
+ {
+ TranslateId pReason = OXSDDataType::_validate( rValue );
+ if (!pReason)
+ {
+
+ // convert value and check format
+ double f;
+ if( ! _getValue( rValue, f ) )
+ pReason = RID_STR_XFORMS_VALUE_IS_NOT_A;
+
+ // check range
+ else if( ( m_aMaxInclusive.hasValue() ) && f > m_fCachedMaxInclusive )
+ pReason = RID_STR_XFORMS_VALUE_MAX_INCL;
+ else if( ( m_aMaxExclusive.hasValue() ) && f >= m_fCachedMaxExclusive )
+ pReason = RID_STR_XFORMS_VALUE_MAX_EXCL;
+ else if( ( m_aMinInclusive.hasValue() ) && f < m_fCachedMinInclusive )
+ pReason = RID_STR_XFORMS_VALUE_MIN_INCL;
+ else if( ( m_aMinExclusive.hasValue() ) && f <= m_fCachedMinExclusive )
+ pReason = RID_STR_XFORMS_VALUE_MIN_EXCL;
+ }
+ return pReason;
+ }
+
+ OUString OValueLimitedType_Base::_explainInvalid(TranslateId rReason)
+ {
+ OUStringBuffer sInfo;
+ if (rReason == RID_STR_XFORMS_VALUE_IS_NOT_A)
+ sInfo.append( getName() );
+ else if (rReason == RID_STR_XFORMS_VALUE_MAX_INCL)
+ sInfo.append( typedValueAsHumanReadableString( m_aMaxInclusive ) );
+ else if (rReason == RID_STR_XFORMS_VALUE_MAX_EXCL)
+ sInfo.append( typedValueAsHumanReadableString( m_aMaxExclusive ) );
+ else if (rReason == RID_STR_XFORMS_VALUE_MIN_INCL)
+ sInfo.append( typedValueAsHumanReadableString( m_aMinInclusive ) );
+ else if (rReason == RID_STR_XFORMS_VALUE_MIN_EXCL)
+ sInfo.append( typedValueAsHumanReadableString( m_aMinExclusive ) );
+ return sInfo.makeStringAndClear();
+ }
+
+ OStringType::OStringType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OStringType_Base( _rName, _nTypeClass )
+ {
+ }
+
+
+ void OStringType::registerProperties()
+ {
+ OStringType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_LENGTH, PROPERTY_ID_XSD_LENGTH, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aLength, cppu::UnoType<sal_Int32>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_LENGTH, PROPERTY_ID_XSD_MIN_LENGTH, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinLength, cppu::UnoType<sal_Int32>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_LENGTH, PROPERTY_ID_XSD_MAX_LENGTH, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxLength, cppu::UnoType<sal_Int32>::get() );
+ }
+
+
+ rtl::Reference<OXSDDataType> OStringType::createClone( const OUString& _rName ) const
+ {
+ return new OStringType( _rName, getTypeClass() );
+ }
+ void OStringType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OStringType_Base::initializeClone( _rCloneSource );
+ initializeTypedClone( static_cast< const OStringType& >( _rCloneSource ) );
+ }
+
+
+
+ void OStringType::initializeTypedClone( const OStringType& _rCloneSource )
+ {
+ m_aLength = _rCloneSource.m_aLength;
+ m_aMinLength = _rCloneSource.m_aMinLength;
+ m_aMaxLength = _rCloneSource.m_aMaxLength;
+ }
+
+
+ bool OStringType::checkPropertySanity( sal_Int32 _nHandle, const Any& _rNewValue, OUString& _rErrorMessage )
+ {
+ // let the base class do the conversion
+ if ( !OStringType_Base::checkPropertySanity( _nHandle, _rNewValue, _rErrorMessage ) )
+ return false;
+
+ _rErrorMessage.clear();
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_XSD_LENGTH:
+ case PROPERTY_ID_XSD_MIN_LENGTH:
+ case PROPERTY_ID_XSD_MAX_LENGTH:
+ {
+ sal_Int32 nValue( 0 );
+ OSL_VERIFY( _rNewValue >>= nValue );
+ if ( nValue < 0 )
+ _rErrorMessage = "Length limits must denote positive integer values.";
+ // TODO/eforms: localize the error message
+ }
+ break;
+ }
+
+ return _rErrorMessage.isEmpty();
+ }
+
+
+ TranslateId OStringType::_validate( const OUString& rValue )
+ {
+ // check regexp, whitespace etc. in parent class
+ TranslateId pReason = OStringType_Base::_validate( rValue );
+
+ if (!pReason)
+ {
+ // check string constraints
+ sal_Int32 nLength = rValue.getLength();
+ sal_Int32 nLimit = 0;
+ if ( m_aLength >>= nLimit )
+ {
+ if ( nLimit != nLength )
+ pReason = RID_STR_XFORMS_VALUE_LENGTH;
+ }
+ else
+ {
+ if ( ( m_aMaxLength >>= nLimit ) && ( nLength > nLimit ) )
+ pReason = RID_STR_XFORMS_VALUE_MAX_LENGTH;
+ else if ( ( m_aMinLength >>= nLimit ) && ( nLength < nLimit ) )
+ pReason = RID_STR_XFORMS_VALUE_MIN_LENGTH;
+ }
+ }
+ return pReason;
+ }
+
+ OUString OStringType::_explainInvalid(TranslateId rReason)
+ {
+ sal_Int32 nValue = 0;
+ OUStringBuffer sInfo;
+ if (rReason == RID_STR_XFORMS_VALUE_LENGTH)
+ {
+ if( m_aLength >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason == RID_STR_XFORMS_VALUE_MAX_LENGTH)
+ {
+ if( m_aMaxLength >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason == RID_STR_XFORMS_VALUE_MIN_LENGTH)
+ {
+ if( m_aMinLength >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason)
+ {
+ sInfo.append(OStringType_Base::_explainInvalid(rReason));
+ }
+ return sInfo.makeStringAndClear();
+ }
+
+
+ OAnyURIType::OAnyURIType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OAnyURIType_Base( _rName, _nTypeClass )
+ {
+ m_xURLTransformer = css::util::URLTransformer::create(::comphelper::getProcessComponentContext());
+ }
+
+
+ void OAnyURIType::registerProperties()
+ {
+ OAnyURIType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_LENGTH, PROPERTY_ID_XSD_LENGTH, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aLength, cppu::UnoType<sal_Int32>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_LENGTH, PROPERTY_ID_XSD_MIN_LENGTH, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinLength, cppu::UnoType<sal_Int32>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_LENGTH, PROPERTY_ID_XSD_MAX_LENGTH, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxLength, cppu::UnoType<sal_Int32>::get() );
+ }
+
+
+ rtl::Reference<OXSDDataType> OAnyURIType::createClone( const OUString& _rName ) const
+ {
+ return new OAnyURIType( _rName, getTypeClass() );
+ }
+ void OAnyURIType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OAnyURIType_Base::initializeClone( _rCloneSource );
+ initializeTypedClone( static_cast< const OAnyURIType& >( _rCloneSource ) );
+ }
+
+
+
+ void OAnyURIType::initializeTypedClone( const OAnyURIType& _rCloneSource )
+ {
+ m_aLength = _rCloneSource.m_aLength;
+ m_aMinLength = _rCloneSource.m_aMinLength;
+ m_aMaxLength = _rCloneSource.m_aMaxLength;
+ }
+
+
+ bool OAnyURIType::checkPropertySanity( sal_Int32 _nHandle, const Any& _rNewValue, OUString& _rErrorMessage )
+ {
+ // let the base class do the conversion
+ if ( !OAnyURIType_Base::checkPropertySanity( _nHandle, _rNewValue, _rErrorMessage ) )
+ return false;
+
+ _rErrorMessage.clear();
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_XSD_LENGTH:
+ case PROPERTY_ID_XSD_MIN_LENGTH:
+ case PROPERTY_ID_XSD_MAX_LENGTH:
+ {
+ sal_Int32 nValue( 0 );
+ OSL_VERIFY( _rNewValue >>= nValue );
+ if ( nValue < 0 )
+ _rErrorMessage = "Length limits must denote positive integer values.";
+ // TODO/eforms: localize the error message
+ }
+ break;
+ }
+
+ return _rErrorMessage.isEmpty();
+ }
+
+
+ TranslateId OAnyURIType::_validate( const OUString& rValue )
+ {
+ // check regexp, whitespace etc. in parent class
+ TranslateId pReason = OAnyURIType_Base::_validate( rValue );
+
+ if (!pReason)
+ {
+ // check AnyURI constraints
+ sal_Int32 nLength = rValue.getLength();
+ sal_Int32 nLimit = 0;
+ if ( m_aLength >>= nLimit )
+ {
+ if ( nLimit != nLength )
+ pReason = RID_STR_XFORMS_VALUE_LENGTH;
+ }
+ else
+ {
+ if ( ( m_aMaxLength >>= nLimit ) && ( nLength > nLimit ) )
+ pReason = RID_STR_XFORMS_VALUE_MAX_LENGTH;
+ else if ( ( m_aMinLength >>= nLimit ) && ( nLength < nLimit ) )
+ pReason = RID_STR_XFORMS_VALUE_MIN_LENGTH;
+ }
+ // check URL
+ css::util::URL aCommandURL;
+ aCommandURL.Complete = rValue;
+ if (!m_xURLTransformer->parseStrict(aCommandURL))
+ pReason = RID_STR_XFORMS_INVALID_VALUE;
+
+ }
+ return pReason;
+ }
+
+ OUString OAnyURIType::_explainInvalid(TranslateId rReason)
+ {
+ sal_Int32 nValue = 0;
+ OUStringBuffer sInfo;
+ if (rReason == RID_STR_XFORMS_VALUE_LENGTH)
+ {
+ if( m_aLength >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason == RID_STR_XFORMS_VALUE_MAX_LENGTH)
+ {
+ if( m_aMaxLength >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason == RID_STR_XFORMS_VALUE_MIN_LENGTH)
+ {
+ if( m_aMinLength >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason)
+ {
+ sInfo.append(OAnyURIType_Base::_explainInvalid(rReason));
+ }
+ return sInfo.makeStringAndClear();
+ }
+
+ OBooleanType::OBooleanType( const OUString& _rName )
+ :OBooleanType_Base( _rName, DataTypeClass::BOOLEAN )
+ {
+ }
+
+ rtl::Reference<OXSDDataType> OBooleanType::createClone( const OUString& _rName ) const
+ {
+ return new OBooleanType( _rName );
+ }
+
+ void OBooleanType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OBooleanType_Base::initializeClone( _rCloneSource );
+ }
+
+ TranslateId OBooleanType::_validate( const OUString& sValue )
+ {
+ TranslateId pInvalidityReason = OBooleanType_Base::_validate( sValue );
+ if ( pInvalidityReason )
+ return pInvalidityReason;
+
+ bool bValid = sValue == "0" || sValue == "1" || sValue == "true" || sValue == "false";
+ return bValid ? TranslateId() : RID_STR_XFORMS_INVALID_VALUE;
+ }
+
+ OUString OBooleanType::_explainInvalid(TranslateId rReason)
+ {
+ return !rReason ? OUString() : getName();
+ }
+
+ ODecimalType::ODecimalType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :ODecimalType_Base( _rName, _nTypeClass )
+ {
+ }
+
+ rtl::Reference<OXSDDataType> ODecimalType::createClone( const OUString& _rName ) const
+ {
+ return new ODecimalType( _rName, getTypeClass() );
+ }
+ void ODecimalType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ ODecimalType_Base::initializeClone( _rCloneSource );
+ initializeTypedClone( static_cast< const ODecimalType& >( _rCloneSource ) );
+ }
+
+ void ODecimalType::initializeTypedClone( const ODecimalType& _rCloneSource )
+ {
+ m_aTotalDigits = _rCloneSource.m_aTotalDigits;
+ m_aFractionDigits = _rCloneSource.m_aFractionDigits;
+ }
+
+
+ void ODecimalType::registerProperties()
+ {
+ ODecimalType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_TOTAL_DIGITS, PROPERTY_ID_XSD_TOTAL_DIGITS, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aTotalDigits, cppu::UnoType<sal_Int32>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_FRACTION_DIGITS, PROPERTY_ID_XSD_FRACTION_DIGITS, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aFractionDigits, cppu::UnoType<sal_Int32>::get() );
+ }
+
+
+ // validate decimals and return code for which facets failed
+ // to be used by: ODecimalType::validate and ODecimalType::explainInvalid
+ TranslateId ODecimalType::_validate( const OUString& rValue )
+ {
+ TranslateId pReason = ODecimalType_Base::_validate( rValue );
+
+ // check digits (if no other cause is available so far)
+ if (!pReason)
+ {
+ sal_Int32 nLength = rValue.getLength();
+ sal_Int32 n = 0;
+ sal_Int32 nTotalDigits = 0;
+ sal_Int32 nFractionDigits = 0;
+ const sal_Unicode* pValue = rValue.getStr();
+ for( ; n < nLength && pValue[n] != '.'; n++ )
+ if( pValue[n] >= '0'
+ && pValue[n] <= '9')
+ nTotalDigits++;
+ for( ; n < nLength; n++ )
+ if( pValue[n] >= '0'
+ && pValue[n] <= '9')
+ nFractionDigits++;
+ nTotalDigits += nFractionDigits;
+
+ sal_Int32 nValue = 0;
+ if( ( m_aTotalDigits >>= nValue ) && nTotalDigits > nValue )
+ pReason = RID_STR_XFORMS_VALUE_TOTAL_DIGITS;
+ else if( ( m_aFractionDigits >>= nValue ) &&
+ ( nFractionDigits > nValue ) )
+ pReason = RID_STR_XFORMS_VALUE_FRACTION_DIGITS;
+ }
+
+ return pReason;
+ }
+
+ OUString ODecimalType::_explainInvalid(TranslateId rReason)
+ {
+ sal_Int32 nValue = 0;
+ OUStringBuffer sInfo;
+ if (rReason == RID_STR_XFORMS_VALUE_TOTAL_DIGITS)
+ {
+ if( m_aTotalDigits >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason == RID_STR_XFORMS_VALUE_FRACTION_DIGITS)
+ {
+ if( m_aFractionDigits >>= nValue )
+ sInfo.append( nValue );
+ }
+ else
+ {
+ sInfo.append(ODecimalType_Base::_explainInvalid(rReason));
+ }
+ return sInfo.makeStringAndClear();
+ }
+
+ OUString ODecimalType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ double fValue( 0 );
+ normalizeValue( _rValue, fValue );
+ return OUString::number( fValue );
+ }
+
+
+ void ODecimalType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ OSL_VERIFY( _rValue >>= _rDoubleValue );
+ }
+
+
+ ODateType::ODateType(const OUString& _rName)
+ :ODateType_Base(_rName, DataTypeClass::DATE)
+ {
+ }
+ rtl::Reference<OXSDDataType> ODateType::createClone(const OUString& _rName) const
+ {
+ return new ODateType(_rName);
+ }
+ void ODateType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ ODateType_Base::initializeClone(_rCloneSource);
+ initializeTypedClone(static_cast< const ODateType& >(_rCloneSource));
+ }
+
+ TranslateId ODateType::_validate( const OUString& _rValue )
+ {
+ return ODateType_Base::_validate( _rValue );
+ }
+
+ bool ODateType::_getValue( const OUString& value, double& fValue )
+ {
+ Any aTypeValue;
+ try
+ {
+ aTypeValue = Convert::get().toAny( value, getCppuType() );
+ }
+ catch (com::sun::star::lang::IllegalArgumentException)
+ {
+ return false;
+ }
+
+ Date aValue;
+ if ( !( aTypeValue >>= aValue ) )
+ return false;
+
+ ::Date aToolsDate( aValue.Day, aValue.Month, aValue.Year );
+ fValue = aToolsDate.GetDate();
+ return true;
+ }
+
+
+ OUString ODateType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ OSL_PRECOND( _rValue.getValueType().equals( getCppuType() ), "ODateType::typedValueAsHumanReadableString: unexpected type" );
+ return Convert::get().toXSD( _rValue );
+ }
+
+
+ void ODateType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ Date aValue;
+ OSL_VERIFY( _rValue >>= aValue );
+ ::Date aToolsDate( aValue.Day, aValue.Month, aValue.Year );
+ _rDoubleValue = aToolsDate.GetDate();
+ }
+
+
+ OTimeType::OTimeType(const OUString& _rName)
+ :OTimeType_Base(_rName, DataTypeClass::TIME)
+ {
+ }
+ rtl::Reference<OXSDDataType> OTimeType::createClone(const OUString& _rName) const
+ {
+ return new OTimeType(_rName);
+ }
+ void OTimeType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OTimeType_Base::initializeClone(_rCloneSource);
+ initializeTypedClone(static_cast< const OTimeType& >(_rCloneSource));
+ }
+
+ TranslateId OTimeType::_validate( const OUString& _rValue )
+ {
+ return OTimeType_Base::_validate( _rValue );
+ }
+
+ bool OTimeType::_getValue( const OUString& value, double& fValue )
+ {
+ Any aTypedValue;
+ try
+ {
+ aTypedValue = Convert::get().toAny( value, getCppuType() );
+ }
+ catch (com::sun::star::lang::IllegalArgumentException)
+ {
+ return false;
+ }
+
+ css::util::Time aValue;
+ if ( !( aTypedValue >>= aValue ) )
+ return false;
+
+ ::tools::Time aToolsTime( aValue );
+ // no loss/rounding; IEEE 754 double-precision floating-point
+ // has a mantissa of 53 bits; we need at the very most 50 bits:
+ // format of aToolsTime.GetTime() is (in decimal) hhmmssnnnnnnnnn
+ // and 999999999999999 = 0x38D7EA4C67FFF
+ // in reality I doubt we need (much) more than
+ // 240000000000000 = 0x0DA475ABF0000
+ // that is 48 bits
+ fValue = aToolsTime.GetTime();
+ return true;
+ }
+
+
+ OUString OTimeType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ OSL_PRECOND( _rValue.getValueType().equals( getCppuType() ), "OTimeType::typedValueAsHumanReadableString: unexpected type" );
+ return Convert::get().toXSD( _rValue );
+ }
+
+
+ void OTimeType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ css::util::Time aValue;
+ OSL_VERIFY( _rValue >>= aValue );
+ ::tools::Time aToolsTime( aValue );
+ _rDoubleValue = aToolsTime.GetTime();
+ }
+
+
+ ODateTimeType::ODateTimeType(const OUString& _rName)
+ :ODateTimeType_Base(_rName, DataTypeClass::DATETIME)
+ {
+ }
+ rtl::Reference<OXSDDataType> ODateTimeType::createClone(const OUString& _rName) const
+ {
+ return new ODateTimeType(_rName);
+ }
+ void ODateTimeType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ ODateTimeType_Base::initializeClone(_rCloneSource);
+ initializeTypedClone(static_cast< const ODateTimeType& >(_rCloneSource));
+ }
+
+ TranslateId ODateTimeType::_validate( const OUString& _rValue )
+ {
+ return ODateTimeType_Base::_validate( _rValue );
+ }
+
+ namespace
+ {
+ double lcl_normalizeDateTime( const DateTime& _rValue )
+ {
+ ::DateTime aToolsValue(_rValue);
+
+ double fValue = 0;
+ // days since 1.1.1900 (which is relatively arbitrary but fixed date)
+ fValue += ::Date( aToolsValue ) - ::Date( 1, 1, 1900 );
+ // time
+ fValue += aToolsValue.GetTimeInDays();
+ return fValue;
+ }
+ }
+
+
+ bool ODateTimeType::_getValue( const OUString& value, double& fValue )
+ {
+ Any aTypedValue;
+ try
+ {
+ aTypedValue = Convert::get().toAny( value, getCppuType() );
+ }
+ catch (com::sun::star::uno::RuntimeException)
+ {
+ return false;
+ }
+
+ DateTime aValue;
+ if ( !( aTypedValue >>= aValue ) )
+ return false;
+
+ fValue = lcl_normalizeDateTime( aValue );
+ return true;
+ }
+
+
+ OUString ODateTimeType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ OSL_PRECOND( _rValue.getValueType().equals( getCppuType() ), "OTimeType::typedValueAsHumanReadableString: unexpected type" );
+ OUString sString = Convert::get().toXSD( _rValue );
+
+ // ISO 8601 notation has a "T" to separate between date and time. Our only concession
+ // to the "human readable" in the method name is to replace this T with a whitespace.
+ OSL_ENSURE( sString.indexOf( 'T' ) != -1, "ODateTimeType::typedValueAsHumanReadableString: hmm - no ISO notation?" );
+ return sString.replace( 'T', ' ' );
+ }
+
+
+ void ODateTimeType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ DateTime aValue;
+ OSL_VERIFY( _rValue >>= aValue );
+ _rDoubleValue = lcl_normalizeDateTime( aValue );
+ }
+
+ OShortIntegerType::OShortIntegerType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OShortIntegerType_Base( _rName, _nTypeClass )
+ {
+ }
+
+ rtl::Reference<OXSDDataType> OShortIntegerType::createClone( const OUString& _rName ) const
+ {
+ return new OShortIntegerType( _rName, getTypeClass() );
+ }
+ void OShortIntegerType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OShortIntegerType_Base::initializeClone( _rCloneSource );
+ initializeTypedClone( static_cast< const OShortIntegerType& >( _rCloneSource ) );
+ }
+
+ static bool lcl_getValueYear( std::u16string_view value, double& fValue )
+ {
+ if (value.size() > 4)
+ {
+ fValue = 0;
+ return false;
+ }
+ if (o3tl::equalsAscii(value, "0"))
+ {
+ fValue = 0;
+ return true;
+ }
+ sal_Int32 int32Value = o3tl::toInt32(value);
+ if (
+ int32Value == 0 ||
+ int32Value < 0 ||
+ int32Value > 10000
+ )
+ {
+ fValue = 0;
+ return false;
+ }
+ fValue = static_cast<double>(static_cast<sal_Int16>(int32Value));
+ return true;
+ }
+
+ static bool lcl_getValueMonth( std::u16string_view value, double& fValue )
+ {
+ if (value.size() > 2)
+ {
+ fValue = 0;
+ return false;
+ }
+ sal_Int32 int32Value = o3tl::toInt32(value);
+ if (
+ int32Value == 0 ||
+ int32Value < 1 ||
+ int32Value > 12
+ )
+ {
+ fValue = 0;
+ return false;
+ }
+ fValue = static_cast<double>(static_cast<sal_Int16>(int32Value));
+ return true;
+ }
+
+ static bool lcl_getValueDay( std::u16string_view value, double& fValue )
+ {
+ if (value.size() > 2)
+ {
+ fValue = 0;
+ return false;
+ }
+ sal_Int32 int32Value = o3tl::toInt32(value);
+ if (
+ int32Value == 0 ||
+ int32Value < 1 ||
+ int32Value > 31
+ )
+ {
+ fValue = 0;
+ return false;
+ }
+ fValue = static_cast<double>(static_cast<sal_Int16>(int32Value));
+ return true;
+ }
+
+ bool OShortIntegerType::_getValue( const OUString& value, double& fValue )
+ {
+ switch (this->getTypeClass())
+ {
+ case css::xsd::DataTypeClass::gYear:
+ return lcl_getValueYear(value, fValue);
+
+ case css::xsd::DataTypeClass::gMonth:
+ return lcl_getValueMonth(value, fValue);
+
+ case css::xsd::DataTypeClass::gDay:
+ return lcl_getValueDay(value, fValue);
+ default:
+ // for the moment, the only types which derive from OShortIntegerType are:
+ // gYear, gMonth and gDay, see ODataTypeRepository ctr
+ return false;
+ }
+ }
+
+
+ OUString OShortIntegerType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ sal_Int16 nValue( 0 );
+ OSL_VERIFY( _rValue >>= nValue );
+ return OUString::number( nValue );
+ }
+
+
+ void OShortIntegerType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ sal_Int16 nValue( 0 );
+ OSL_VERIFY( _rValue >>= nValue );
+ _rDoubleValue = nValue;
+ }
+
+
+template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS >
+ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::ODerivedDataType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :SUPERCLASS( _rName, _nTypeClass )
+ ,m_bPropertiesRegistered( false )
+{
+}
+
+
+template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS >
+::cppu::IPropertyArrayHelper* ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::createArrayHelper( ) const
+{
+ css::uno::Sequence< css::beans::Property > aProps;
+ ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::describeProperties( aProps );
+ return new ::cppu::OPropertyArrayHelper( aProps );
+}
+
+
+template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS >
+css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::getPropertySetInfo()
+{
+ return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
+}
+
+
+template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS >
+::cppu::IPropertyArrayHelper& SAL_CALL ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::getInfoHelper()
+{
+ if ( !m_bPropertiesRegistered )
+ {
+ this->registerProperties();
+ m_bPropertiesRegistered = true;
+ }
+
+ return *ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::getArrayHelper();
+}
+
+
+template< typename VALUE_TYPE >
+OValueLimitedType< VALUE_TYPE >::OValueLimitedType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OValueLimitedType_Base( _rName, _nTypeClass )
+{
+}
+
+} // namespace xforms
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/datatypes.hxx b/forms/source/xforms/datatypes.hxx
new file mode 100644
index 0000000000..69d6274030
--- /dev/null
+++ b/forms/source/xforms/datatypes.hxx
@@ -0,0 +1,422 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/xsd/XDataType.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/uno3.hxx>
+#include <comphelper/propertycontainer.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/broadcasthelper.hxx>
+#include <rtl/ref.hxx>
+#include <unotools/resmgr.hxx>
+
+#include <unicode/regex.h>
+
+#include <memory>
+
+#include <com/sun/star/util/URLTransformer.hpp>
+
+
+namespace xforms
+{
+
+
+ //= OXSDDataType
+
+ typedef ::cppu::WeakImplHelper < css::xsd::XDataType
+ > OXSDDataType_Base;
+ typedef ::comphelper::OMutexAndBroadcastHelper OXSDDataType_BBase;
+ typedef ::comphelper::OPropertyContainer OXSDDataType_PBase;
+
+ class OXSDDataType :public OXSDDataType_Base
+ ,public OXSDDataType_BBase // order matters: OMutexAndBroadcastHelper before
+ ,public OXSDDataType_PBase // OPropertyContainer
+ {
+ private:
+ // <properties>
+ bool m_bIsBasic;
+ sal_Int16 m_nTypeClass;
+ OUString m_sName;
+ OUString m_sPattern;
+ sal_uInt16 m_nWST;
+ // </properties>
+
+ ::std::unique_ptr< U_NAMESPACE_QUALIFIER RegexMatcher >
+ m_pPatternMatcher;
+ bool m_bPatternMatcherDirty;
+
+ protected:
+ sal_Int16 getTypeClass() const { return m_nTypeClass; }
+
+ private:
+ OXSDDataType( const OXSDDataType& ) = delete;
+ OXSDDataType& operator=( const OXSDDataType& ) = delete;
+
+ protected:
+ // create basic data type
+ OXSDDataType( OUString _aName, sal_Int16 _nTypeClass );
+ virtual ~OXSDDataType() override;
+
+ public:
+ DECLARE_XINTERFACE()
+ DECLARE_XTYPEPROVIDER()
+
+ virtual OUString SAL_CALL getName( ) override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+ virtual OUString SAL_CALL getPattern() override;
+ virtual void SAL_CALL setPattern( const OUString& _pattern ) override;
+ virtual sal_Int16 SAL_CALL getWhiteSpaceTreatment() override;
+ virtual void SAL_CALL setWhiteSpaceTreatment( sal_Int16 _whitespacetreatment ) override;
+ virtual sal_Bool SAL_CALL getIsBasic() override;
+ virtual sal_Int16 SAL_CALL getTypeClass() override;
+
+ virtual sal_Bool SAL_CALL validate( const OUString& value ) override;
+ virtual OUString SAL_CALL explainInvalid( const OUString& value ) override;
+
+ // XPropertySet - is a base of XDataType and needs to be disambiguated
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ public:
+ rtl::Reference<OXSDDataType> clone( const OUString& _rNewName ) const;
+
+ protected:
+ // XPropertySet and friends
+ 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;
+
+ // --- own overridables ---
+ // helper for implementing cloning of data types
+ virtual rtl::Reference<OXSDDataType> createClone( const OUString& _rName ) const = 0;
+ virtual void initializeClone( const OXSDDataType& _rCloneSource );
+
+ // helper method for validate and explainInvalid
+ virtual TranslateId _validate( const OUString& value );
+ virtual OUString _explainInvalid( TranslateId rReason );
+
+ // helper method for checking properties values which are to be set
+ virtual bool checkPropertySanity( sal_Int32 _nHandle, const css::uno::Any& _rNewValue, OUString& _rErrorMessage );
+
+ // register properties implemented by this instance - call the base class when overriding
+ virtual void registerProperties();
+ };
+
+
+ //= helper for deriving from OXSDDataType
+
+ class OValueLimitedType_Base : public OXSDDataType
+ {
+ protected:
+ css::uno::Any m_aMaxInclusive;
+ css::uno::Any m_aMaxExclusive;
+ css::uno::Any m_aMinInclusive;
+ css::uno::Any m_aMinExclusive;
+
+ double m_fCachedMaxInclusive;
+ double m_fCachedMaxExclusive;
+ double m_fCachedMinInclusive;
+ double m_fCachedMinExclusive;
+
+ protected:
+ OValueLimitedType_Base( const OUString& _rName, sal_Int16 _nTypeClass );
+
+ virtual void initializeClone( const OXSDDataType& _rCloneSource ) override;
+ void initializeTypedClone( const OValueLimitedType_Base& _rCloneSource );
+
+ // XPropertySet and friends
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle,
+ const css::uno::Any& rValue
+ ) override;
+
+ // OXSDDataType overridables
+ virtual bool _getValue( const OUString& value, double& fValue );
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual OUString _explainInvalid( TranslateId rReason ) override;
+
+ // own overridables
+ /** translate a given value into a human-readable string
+
+ The value is guaranteed to be not <NULL/>, and is of type <member>ValueType</member>
+ */
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const = 0;
+
+ /** translates a <member>ValueType</member> value into a double value
+
+ The normalization must respect the "<" and "==" relations on the value
+ space. That is, if two values are equal, their normalizations must be equal, too.
+ Similarly, if <code>foo</code> is less than <code>bar</code>, the same
+ must hold for their normalizations.
+
+ @param _rValue
+ the value to translate. Guaranteed to be not <NULL/>, and of type <member>ValueType</member>
+ @param _rDoubleValue
+ output parameter to hold the resulting double value
+ */
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const = 0;
+ };
+
+ template < typename VALUE_TYPE >
+ class OValueLimitedType : public OValueLimitedType_Base
+ {
+ protected:
+ typedef VALUE_TYPE ValueType;
+ const css::uno::Type&
+ getCppuType() const { return cppu::UnoType<ValueType>::get(); }
+
+ protected:
+ OValueLimitedType( const OUString& _rName, sal_Int16 _nTypeClass );
+
+ // OXSDDataType overridables
+ virtual void registerProperties() override;
+ };
+
+ /** helper class for implementing interfaces derived from XDataType
+ */
+ template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS = OXSDDataType >
+ class ODerivedDataType :public SUPERCLASS
+ ,public ::comphelper::OPropertyArrayUsageHelper< CONCRETE_DATA_TYPE_IMPL >
+ {
+ private:
+ bool m_bPropertiesRegistered;
+
+ protected:
+ ODerivedDataType( const OUString& _rName, sal_Int16 _nTypeClass );
+
+ protected:
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override;
+
+ // XPropertySet
+ virtual css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ };
+
+ class OBooleanType;
+ typedef ODerivedDataType< OBooleanType > OBooleanType_Base;
+ class OBooleanType : public OBooleanType_Base
+ {
+ public:
+ explicit OBooleanType( const OUString& _rName );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ // OXSDDataType overridables
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual OUString _explainInvalid( TranslateId rReason ) override;
+ };
+
+ class OStringType;
+ typedef ODerivedDataType< OStringType > OStringType_Base;
+ class OStringType :public OStringType_Base
+ {
+ // <properties>
+ css::uno::Any m_aLength;
+ css::uno::Any m_aMinLength;
+ css::uno::Any m_aMaxLength;
+ // </properties>
+
+ public:
+ OStringType( const OUString& _rName, sal_Int16 _nTypeClass /* = css::xsd::DataTypeClass::STRING */ );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+ void initializeTypedClone( const OStringType& _rCloneSource );
+
+ // OXSDDataType overridables
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual OUString _explainInvalid( TranslateId rReason ) override;
+ virtual bool checkPropertySanity( sal_Int32 _nHandle, const css::uno::Any& _rNewValue, OUString& _rErrorMessage ) override;
+ virtual void registerProperties() override;
+ };
+
+ class OAnyURIType;
+ typedef ODerivedDataType< OAnyURIType > OAnyURIType_Base;
+ class OAnyURIType :public OAnyURIType_Base
+ {
+ // <properties>
+ css::uno::Any m_aLength;
+ css::uno::Any m_aMinLength;
+ css::uno::Any m_aMaxLength;
+ // </properties>
+ // helper to check URL validity
+ css::uno::Reference<css::util::XURLTransformer> m_xURLTransformer;
+
+ public:
+ OAnyURIType( const OUString& _rName, sal_Int16 _nTypeClass /* = css::xsd::DataTypeClass::anyURI */ );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+ void initializeTypedClone( const OAnyURIType& _rCloneSource );
+
+ // OXSDDataType overridables
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual OUString _explainInvalid( TranslateId rReason ) override;
+ virtual bool checkPropertySanity( sal_Int32 _nHandle, const css::uno::Any& _rNewValue, OUString& _rErrorMessage ) override;
+ virtual void registerProperties() override;
+ };
+
+ class ODecimalType;
+ typedef ODerivedDataType< ODecimalType, OValueLimitedType< double > > ODecimalType_Base;
+ class ODecimalType : public ODecimalType_Base
+ {
+ css::uno::Any m_aTotalDigits;
+ css::uno::Any m_aFractionDigits;
+
+ public:
+ ODecimalType( const OUString& _rName, sal_Int16 _nTypeClass /* = css::xsd::DataTypeClass::DECIMAL */ );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+ void initializeTypedClone( const ODecimalType& _rCloneSource );
+
+ // OXSDDataType overridables
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual OUString _explainInvalid( TranslateId rReason ) override;
+ virtual void registerProperties() override;
+
+ // OValueLimitedType overridables
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using ODecimalType_Base::initializeTypedClone;
+ };
+
+
+ //=
+
+ class ODateType;
+ typedef ODerivedDataType< ODateType, OValueLimitedType< css::util::Date > > ODateType_Base;
+ class ODateType : public ODateType_Base
+ {
+ public:
+ explicit ODateType( const OUString& _rName );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ /* OXSDDataType overridables */
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual bool _getValue( const OUString& value, double& fValue ) override;
+
+ /* OValueLimitedType overridables */
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using ODateType_Base::initializeTypedClone;
+ };
+
+
+ class OTimeType;
+ typedef ODerivedDataType< OTimeType, OValueLimitedType< css::util::Time > > OTimeType_Base;
+ class OTimeType : public OTimeType_Base
+ {
+ public:
+ explicit OTimeType( const OUString& _rName );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ /* OXSDDataType overridables */
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual bool _getValue( const OUString& value, double& fValue ) override;
+
+ /* OValueLimitedType overridables */
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using OTimeType_Base::initializeTypedClone;
+ };
+
+
+
+ class ODateTimeType;
+ typedef ODerivedDataType< OTimeType, OValueLimitedType< css::util::DateTime > > ODateTimeType_Base;
+ class ODateTimeType : public ODateTimeType_Base
+ {
+ public:
+ explicit ODateTimeType( const OUString& _rName );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ /* OXSDDataType overridables */
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual bool _getValue( const OUString& value, double& fValue ) override;
+
+ /* OValueLimitedType overridables */
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using ODateTimeType_Base::initializeTypedClone;
+ };
+
+
+ class OShortIntegerType;
+ typedef ODerivedDataType< OShortIntegerType, OValueLimitedType< sal_Int16 > > OShortIntegerType_Base;
+ class OShortIntegerType : public OShortIntegerType_Base
+ {
+ public:
+ OShortIntegerType( const OUString& _rName, sal_Int16 _nTypeClass );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ // OXSDDataType overridables
+ virtual bool _getValue( const OUString& value, double& fValue ) override;
+
+ // OValueLimitedType overridables
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using OShortIntegerType_Base::initializeTypedClone;
+ };
+
+
+} // namespace xforms
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/enumeration.cxx b/forms/source/xforms/enumeration.cxx
new file mode 100644
index 0000000000..e02881fbbf
--- /dev/null
+++ b/forms/source/xforms/enumeration.cxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "enumeration.hxx"
+
+#include <osl/diagnose.h>
+
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+
+using com::sun::star::container::NoSuchElementException;
+using com::sun::star::container::XIndexAccess;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::RuntimeException;
+
+Enumeration::Enumeration(XIndexAccess* pContainer)
+ : mxContainer(pContainer)
+ , mnIndex(0)
+{
+ OSL_ENSURE(mxContainer.is(), "no container?");
+}
+
+sal_Bool Enumeration::hasMoreElements()
+{
+ if (!mxContainer.is())
+ throw RuntimeException();
+
+ return mnIndex < mxContainer->getCount();
+}
+
+Any Enumeration::nextElement()
+{
+ if (!mxContainer.is())
+ throw RuntimeException();
+ if (mnIndex >= mxContainer->getCount())
+ throw NoSuchElementException();
+
+ return mxContainer->getByIndex(mnIndex++);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/enumeration.hxx b/forms/source/xforms/enumeration.hxx
new file mode 100644
index 0000000000..6374e0c8ef
--- /dev/null
+++ b/forms/source/xforms/enumeration.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star {
+ namespace container { class XIndexAccess; }
+ namespace uno { class Any; }
+ namespace container { class NoSuchElementException; }
+ namespace lang { class WrappedTargetException; }
+ namespace uno { class RuntimeException; }
+}
+
+/** implement XEnumeration based on container::XIndexAccess */
+class Enumeration
+ : public cppu::WeakImplHelper<css::container::XEnumeration>
+{
+ css::uno::Reference<css::container::XIndexAccess> mxContainer;
+ sal_Int32 mnIndex;
+
+public:
+ explicit Enumeration( css::container::XIndexAccess* );
+
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+
+ virtual css::uno::Any SAL_CALL nextElement() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/evaluationcontext.hxx b/forms/source/xforms/evaluationcontext.hxx
new file mode 100644
index 0000000000..e25c852404
--- /dev/null
+++ b/forms/source/xforms/evaluationcontext.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <utility>
+
+namespace xforms
+{
+
+
+/** define the context for the evaluation of an XPath expression */
+class EvaluationContext
+{
+public:
+ EvaluationContext()
+ : mxContextNode(),
+ mxModel(),
+ mxNamespaces()
+ { }
+
+ EvaluationContext(
+ css::uno::Reference<css::xml::dom::XNode> xContextNode,
+ css::uno::Reference<css::xforms::XModel> xModel,
+ css::uno::Reference<css::container::XNameContainer> xNamespaces )
+ : mxContextNode(std::move( xContextNode )),
+ mxModel(std::move( xModel )),
+ mxNamespaces(std::move( xNamespaces ))
+ { }
+
+ css::uno::Reference<css::xml::dom::XNode> mxContextNode;
+ css::uno::Reference<css::xforms::XModel> mxModel;
+ css::uno::Reference<css::container::XNameContainer> mxNamespaces;
+};
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/mip.cxx b/forms/source/xforms/mip.cxx
new file mode 100644
index 0000000000..b1759ecbe7
--- /dev/null
+++ b/forms/source/xforms/mip.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "mip.hxx"
+
+namespace xforms
+{
+
+MIP::MIP()
+{
+ resetReadonly();
+ resetRequired();
+ resetRelevant();
+ resetConstraint();
+ mbHasCalculate = false;
+ resetTypeName();
+}
+
+void MIP::inherit( const MIP& rMip )
+{
+ if( ! mbHasReadonly )
+ {
+ mbHasReadonly = rMip.mbHasReadonly;
+ mbReadonly = rMip.isReadonly();
+ }
+ if( ! mbHasRequired )
+ {
+ mbHasRequired = rMip.mbHasRequired;
+ mbRequired = rMip.isRequired();
+ }
+ if( ! mbHasRelevant )
+ {
+ mbHasRelevant = rMip.mbHasRelevant;
+ mbRelevant = rMip.isRelevant();
+ }
+ if( ! mbHasConstraint )
+ {
+ mbHasConstraint = rMip.mbHasConstraint;
+ mbConstraint = rMip.isConstraint();
+ msConstraintExplanation = rMip.getConstraintExplanation();
+ }
+ if( ! mbHasCalculate )
+ {
+ mbHasCalculate = rMip.mbHasCalculate;
+ }
+ if( ! mbHasTypeName )
+ {
+ mbHasTypeName = rMip.mbHasTypeName;
+ msTypeName = rMip.getTypeName();
+ }
+}
+
+void MIP::join( const MIP& rMip )
+{
+ // TODO: inherit only inheritable MIPs...
+ inherit( rMip );
+}
+
+bool MIP::isReadonly() const { return mbHasReadonly ? mbReadonly : mbHasCalculate; }
+void MIP::setReadonly( bool b ) { mbHasReadonly = true; mbReadonly = b; }
+void MIP::resetReadonly() { mbHasReadonly = false; mbReadonly = false; }
+
+void MIP::setRequired( bool b ) { mbHasRequired = true; mbRequired = b; }
+void MIP::resetRequired() { mbHasRequired = false; mbRequired = false; }
+
+void MIP::setRelevant( bool b ) { mbHasRelevant = true; mbRelevant = b; }
+void MIP::resetRelevant() { mbHasRelevant = false; mbRelevant = true; }
+
+void MIP::setConstraint( bool b ) { mbHasConstraint = true; mbConstraint = b; msConstraintExplanation.clear(); }
+void MIP::resetConstraint() { mbHasConstraint = false; mbConstraint = true; msConstraintExplanation.clear(); }
+
+void MIP::setConstraintExplanation( const OUString& s ) { msConstraintExplanation = s; }
+
+void MIP::setHasCalculate( bool b ) { mbHasCalculate = b; }
+
+void MIP::setTypeName( const OUString& s ) { msTypeName = s; mbHasTypeName = true; }
+void MIP::resetTypeName() { msTypeName.clear(); mbHasTypeName = false; }
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/mip.hxx b/forms/source/xforms/mip.hxx
new file mode 100644
index 0000000000..97b98d04ec
--- /dev/null
+++ b/forms/source/xforms/mip.hxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+namespace xforms
+{
+/** represents the XForms *m*odel *i*tem *p*roperties (MIPs) for a
+ * given XNode in the instance data at a given point in time. The
+ * values will not be updated; for updated values new MIP objects have
+ * to be created/queried. */
+class MIP
+{
+ bool mbHasReadonly;
+ bool mbReadonly;
+
+ bool mbHasRequired;
+ bool mbRequired;
+
+ bool mbHasRelevant;
+ bool mbRelevant;
+
+ bool mbHasConstraint;
+ bool mbConstraint;
+
+ bool mbHasCalculate;
+
+ bool mbHasTypeName;
+ OUString msTypeName;
+
+ OUString msConstraintExplanation;
+
+public:
+ MIP();
+
+ /// inherit from upper-level MIPs
+ void inherit(const MIP&);
+
+ /// join with same-level MIPs
+ void join(const MIP&);
+
+ // - type (static; default: xsd:string)
+ // (currently default implemented as empty string)
+ const OUString& getTypeName() const { return msTypeName; }
+ void setTypeName(const OUString&);
+ void resetTypeName();
+
+ // - readonly (computed XPath; default: false; true if calculate exists)
+ bool isReadonly() const;
+ void setReadonly(bool);
+ void resetReadonly();
+
+ // - required (computed XPath; default: false)
+ bool isRequired() const { return mbRequired; }
+ void setRequired(bool);
+ void resetRequired();
+
+ // - relevant (computed XPath; default: true)
+ bool isRelevant() const { return mbRelevant; }
+ void setRelevant(bool);
+ void resetRelevant();
+
+ // - constraint (computed XPath; default: true)
+ bool isConstraint() const { return mbConstraint; }
+ void setConstraint(bool);
+ void resetConstraint();
+
+ // explain _why_ a constraint failed
+ void setConstraintExplanation(const OUString&);
+ const OUString& getConstraintExplanation() const { return msConstraintExplanation; }
+
+ // - calculate (computed XPath; default: has none (false))
+ // (for calculate, we only store whether a calculate MIP is present;
+ // the actual calculate value is handled my changing the instance
+ // directly)
+ void setHasCalculate(bool);
+
+ // - minOccurs/maxOccurs (computed XPath; default: 0/inf)
+ // - p3ptype (static; no default)
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/model.cxx b/forms/source/xforms/model.cxx
new file mode 100644
index 0000000000..bbb460f182
--- /dev/null
+++ b/forms/source/xforms/model.cxx
@@ -0,0 +1,606 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "model.hxx"
+
+#include "model_helper.hxx"
+#include "unohelper.hxx"
+#include "binding.hxx"
+#include "submission.hxx"
+#include "mip.hxx"
+#include "evaluationcontext.hxx"
+#include "xmlhelper.hxx"
+#include "datatyperepository.hxx"
+#include "NameContainer.hxx"
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/debug.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <algorithm>
+
+// UNO classes
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/XCharacterData.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+
+using com::sun::star::beans::XPropertySet;
+using com::sun::star::beans::PropertyValue;
+using com::sun::star::ucb::SimpleFileAccess;
+using com::sun::star::io::XInputStream;
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::xml::dom;
+using namespace xforms;
+
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+#define DBG_INVARIANT_TYPE(TYPE) class DBG_##TYPE { const TYPE* mpT; void check() { mpT->dbg_assertInvariant(); } public: DBG_##TYPE(const TYPE* pT) : mpT(pT) { check(); } ~DBG_##TYPE() { check(); } } _DBG_##TYPE(this);
+
+#define DBG_INVARIANT() DBG_INVARIANT_TYPE(Model)
+#else
+#define DBG_INVARIANT()
+#endif
+
+
+// The Model
+
+
+void Model::ensureAtLeastOneInstance()
+{
+ if( ! mxInstances->hasItems() )
+ {
+ // create a default instance
+ newInstance( OUString(), OUString(), true );
+ }
+}
+
+
+/** Model default constructor; create empty model */
+Model::Model() :
+ mxInstances( new InstanceCollection ),
+ mxNamespaces( new NameContainer<OUString>() ),
+ mbInitialized( false ),
+ mbExternalData( true )
+{
+ initializePropertySet();
+
+ // initialize bindings collections
+ // (not in initializer list to avoid use of incomplete 'this')
+ mxBindings = new BindingCollection( this );
+ mxSubmissions = new SubmissionCollection( this );
+
+ // invariant only holds after construction
+ DBG_INVARIANT();
+}
+
+Model::~Model() noexcept
+{
+}
+
+EvaluationContext Model::getEvaluationContext()
+{
+ // the default context is the top-level element node. A default
+ // node (instanceData' is inserted when there is no default node
+ Reference<XDocument> xInstance = getDefaultInstance();
+ Reference<XNode> xElement = xInstance->getDocumentElement();
+
+ // no element found? Then insert default element 'instanceData'
+ if( ! xElement.is() )
+ {
+ xElement.set( xInstance->createElement( "instanceData" ), UNO_QUERY_THROW );
+ xInstance->appendChild( xElement );
+ }
+
+ OSL_ENSURE( xElement.is() &&
+ xElement->getNodeType() == NodeType_ELEMENT_NODE,
+ "no element in evaluation context" );
+
+ return EvaluationContext( xElement, this, mxNamespaces );
+}
+
+
+void Model::setForeignSchema( const css::uno::Reference<css::xml::dom::XDocument>& rDocument )
+{
+ mxForeignSchema = rDocument;
+}
+
+
+void Model::setSchemaRef( const OUString& rSchemaRef )
+{
+ msSchemaRef = rSchemaRef;
+}
+
+
+void Model::setNamespaces( const css::uno::Reference<css::container::XNameContainer>& rNamespaces )
+{
+ if( rNamespaces.is() )
+ mxNamespaces = rNamespaces;
+}
+
+
+void Model::setExternalData( bool _bData )
+{
+ mbExternalData = _bData;
+}
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+void Model::dbg_assertInvariant() const
+{
+ assert(mxInstances && "no instances found");
+ assert(mxBindings && "no bindings element");
+ assert(mxSubmissions && "no submissions element");
+}
+#endif
+
+
+// MIP management
+void Model::addMIP( void* pTag, const XNode_t& xNode, const MIP& rMIP )
+{
+ OSL_ENSURE( pTag != nullptr, "empty tag?" );
+ OSL_ENSURE( xNode.is(), "no node" );
+
+ MIPs_t::value_type aValue( xNode, ::std::pair<void*,MIP>( pTag, rMIP ) );
+ maMIPs.insert( aValue );
+}
+
+void Model::removeMIPs( void const * pTag )
+{
+ OSL_ENSURE( pTag != nullptr, "empty tag?" );
+
+ for( MIPs_t::iterator aIter = maMIPs.begin();
+ aIter != maMIPs.end(); )
+ {
+ if( aIter->second.first == pTag )
+ {
+ aIter = maMIPs.erase( aIter );
+ }
+ else
+ ++aIter;
+ }
+}
+
+MIP Model::queryMIP( const XNode_t& xNode ) const
+{
+ // travel up inheritance chain and inherit MIPs
+ MIP aRet;
+ for( XNode_t xCurrent = xNode;
+ xCurrent.is();
+ xCurrent = xCurrent->getParentNode() )
+ {
+ // iterate over all MIPs for this node, and join MIPs
+ MIP aMIP;
+ MIPs_t::const_iterator aEnd = maMIPs.upper_bound( xCurrent );
+ MIPs_t::const_iterator aIter = maMIPs.lower_bound( xCurrent );
+ for( ; aIter != aEnd; ++aIter )
+ aMIP.join( aIter->second.second );
+
+ // inherit from current node (or set if we are at the start node)
+ if( xCurrent == xNode )
+ aRet = aMIP;
+ else
+ aRet.inherit( aMIP );
+ }
+
+ return aRet;
+}
+
+
+void Model::rebind()
+{
+ OSL_ENSURE( mxBindings, "bindings?" );
+
+ // iterate over all bindings and call update
+ sal_Int32 nCount = mxBindings->countItems();
+ for( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ Binding* pBind = comphelper::getFromUnoTunnel<Binding>( mxBindings->Collection<XPropertySet_t>::getItem( i ) );
+ OSL_ENSURE( pBind != nullptr, "binding?" );
+ pBind->update();
+ }
+}
+
+
+void Model::deferNotifications( bool bDefer )
+{
+ // iterate over all bindings and defer notifications
+ sal_Int32 nCount = mxBindings->countItems();
+ for( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ Binding* pBind = comphelper::getFromUnoTunnel<Binding>( mxBindings->Collection<XPropertySet_t>::getItem( i ) );
+ OSL_ENSURE( pBind != nullptr, "binding?" );
+ pBind->deferNotifications( bDefer );
+ }
+}
+
+
+bool Model::setSimpleContent( const XNode_t& xConstNode,
+ const OUString& sValue )
+{
+ OSL_ENSURE( xConstNode.is(), "need node to set data" );
+
+ bool bRet = false;
+ if( xConstNode.is() )
+ {
+ // non-const node reference so we can assign children (if necessary)
+ XNode_t xNode( xConstNode );
+
+ switch( xNode->getNodeType() )
+ {
+ case NodeType_ELEMENT_NODE:
+ {
+ // find first text node child
+ Reference<XNode> xChild;
+ for( xChild = xNode->getFirstChild();
+ xChild.is() && xChild->getNodeType() != NodeType_TEXT_NODE;
+ xChild = xChild->getNextSibling() )
+ ; // empty loop; only find first text node child
+
+ // create text node, if none is found
+ if( ! xChild.is() )
+ {
+ xChild.set(
+ xNode->getOwnerDocument()->createTextNode( OUString() ),
+ UNO_QUERY_THROW );
+ xNode->appendChild( xChild );
+ }
+ xNode = xChild;
+
+ OSL_ENSURE( xNode.is() &&
+ xNode->getNodeType() == NodeType_TEXT_NODE,
+ "text node creation failed?" );
+ [[fallthrough]]; // continue as with text node:
+ }
+
+ case NodeType_TEXT_NODE:
+ case NodeType_ATTRIBUTE_NODE:
+ {
+ // set the node value (defer notifications)
+ if( xNode->getNodeValue() != sValue )
+ {
+ deferNotifications( true );
+ xNode->setNodeValue( sValue );
+ deferNotifications( false );
+ }
+ bRet = true;
+ }
+ break;
+
+ default:
+ {
+ OSL_FAIL( "bound to unknown node type?" );
+ }
+ break;
+
+ }
+ }
+ return bRet;
+}
+
+void Model::loadInstance( sal_Int32 nInstance )
+{
+ Sequence<PropertyValue> aSequence = mxInstances->getItem( nInstance );
+
+ // find URL from instance
+ OUString sURL;
+ bool bOnce = false;
+ getInstanceData( aSequence, nullptr, nullptr, &sURL, &bOnce );
+
+ // if we have a URL, load the document and set it into the instance
+ if( sURL.isEmpty() )
+ return;
+
+ try
+ {
+ Reference<XInputStream> xInput =
+ SimpleFileAccess::create( ::comphelper::getProcessComponentContext() )->openFileRead( sURL );
+ if( xInput.is() )
+ {
+ Reference<XDocument> xInstance =
+ getDocumentBuilder()->parse( xInput );
+ if( xInstance.is() )
+ {
+ OUString sEmpty;
+ setInstanceData( aSequence, nullptr, &xInstance,
+ bOnce ? &sEmpty : &sURL, nullptr);
+ mxInstances->setItem( nInstance, aSequence );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ // couldn't load the instance -> ignore!
+ }
+}
+
+void Model::loadInstances()
+{
+ // iterate over instance array to get PropertyValue-Sequence
+ const sal_Int32 nInstances = mxInstances->countItems();
+ for( sal_Int32 nInstance = 0; nInstance < nInstances; nInstance++ )
+ {
+ loadInstance( nInstance );
+ }
+}
+
+
+bool Model::isValid() const
+{
+ bool bValid = true;
+ sal_Int32 nCount = mxBindings->countItems();
+ for( sal_Int32 i = 0; bValid && i < nCount; i++ )
+ {
+ Binding* pBind = comphelper::getFromUnoTunnel<Binding>( mxBindings->Collection<XPropertySet_t>::getItem( i ) );
+ OSL_ENSURE( pBind != nullptr, "binding?" );
+ bValid = pBind->isValid();
+ }
+ return bValid;
+}
+
+
+// implement xforms::XModel
+
+
+OUString Model::getID()
+{
+ DBG_INVARIANT();
+ return msID;
+}
+
+void Model::setID( const OUString& sID )
+{
+ DBG_INVARIANT();
+ msID = sID;
+}
+
+void Model::initialize()
+{
+ DBG_ASSERT( ! mbInitialized, "model already initialized" );
+
+ // load instances
+ loadInstances();
+
+ // let's pretend we're initialized and rebind all bindings
+ mbInitialized = true;
+ rebind();
+}
+
+void Model::rebuild()
+{
+ if( ! mbInitialized )
+ initialize();
+ else
+ rebind();
+}
+
+void Model::recalculate()
+{
+ rebind();
+}
+
+void Model::revalidate()
+{
+ // do nothing. We don't validate anyways!
+}
+
+void Model::refresh()
+{
+ rebind();
+}
+
+
+void SAL_CALL Model::submitWithInteraction(
+ const OUString& sID,
+ const css::uno::Reference<css::task::XInteractionHandler>& _rxHandler )
+{
+ DBG_INVARIANT();
+
+ if( mxSubmissions->hasItem( sID ) )
+ {
+ Submission* pSubmission =
+ dynamic_cast<Submission*>( mxSubmissions->getItem( sID ).get() );
+ assert(pSubmission && "no submission?");
+ OSL_ENSURE( pSubmission->getModelImpl() == this,
+ "wrong model" );
+
+ // submit. All exceptions are allowed to leave.
+ pSubmission->submitWithInteraction( _rxHandler );
+ }
+}
+
+void Model::submit( const OUString& sID )
+{
+ submitWithInteraction( sID, nullptr );
+}
+
+css::uno::Reference<css::xforms::XDataTypeRepository> SAL_CALL Model::getDataTypeRepository( )
+{
+ if ( !mxDataTypes.is() )
+ mxDataTypes = new ODataTypeRepository;
+
+ return mxDataTypes;
+}
+
+
+// instance management
+
+
+css::uno::Reference<css::container::XSet> Model::getInstances()
+{
+ return mxInstances;
+}
+
+css::uno::Reference<css::xml::dom::XDocument> Model::getInstanceDocument( const OUString& rName )
+{
+ ensureAtLeastOneInstance();
+ Reference<XDocument> aInstance;
+ sal_Int32 nInstance = lcl_findInstance( mxInstances.get(), rName );
+ if( nInstance != -1 )
+ getInstanceData( mxInstances->getItem( nInstance ),
+ nullptr, &aInstance, nullptr, nullptr );
+ return aInstance;
+}
+
+css::uno::Reference<css::xml::dom::XDocument> SAL_CALL Model::getDefaultInstance()
+{
+ ensureAtLeastOneInstance();
+ DBG_ASSERT( mxInstances->countItems() > 0, "no instance?" );
+ Reference<XDocument> aInstance;
+ getInstanceData( mxInstances->getItem( 0 ), nullptr, &aInstance, nullptr, nullptr );
+ return aInstance;
+}
+
+
+// bindings management
+
+
+css::uno::Reference<css::beans::XPropertySet> SAL_CALL Model::createBinding()
+{
+ DBG_INVARIANT();
+ return new Binding();
+}
+
+css::uno::Reference<css::beans::XPropertySet> Model::cloneBinding( const css::uno::Reference<css::beans::XPropertySet>& xBinding )
+{
+ DBG_INVARIANT();
+ XPropertySet_t xNewBinding = createBinding();
+ copy( xBinding, xNewBinding );
+ return xNewBinding;
+}
+
+css::uno::Reference<css::beans::XPropertySet> Model::getBinding( const OUString& sId )
+{
+ DBG_INVARIANT();
+ return mxBindings->hasItem( sId ) ? mxBindings->getItem( sId ) : nullptr;
+}
+
+css::uno::Reference<css::container::XSet> Model::getBindings()
+{
+ DBG_INVARIANT();
+ return mxBindings;
+}
+
+
+// submission management
+
+
+css::uno::Reference<css::xforms::XSubmission> Model::createSubmission()
+{
+ DBG_INVARIANT();
+ return new Submission();
+}
+
+css::uno::Reference<css::xforms::XSubmission> Model::cloneSubmission(const css::uno::Reference<css::beans::XPropertySet>& xSubmission)
+{
+ DBG_INVARIANT();
+ css::uno::Reference<css::xforms::XSubmission> xNewSubmission = createSubmission();
+ XPropertySet_t xAsPropertySet( xNewSubmission );
+ copy( xSubmission, xAsPropertySet );
+ return xNewSubmission;
+}
+
+css::uno::Reference<css::xforms::XSubmission> Model::getSubmission( const OUString& sId )
+{
+ DBG_INVARIANT();
+ css::uno::Reference<css::xforms::XSubmission> xSubmission;
+ if ( mxSubmissions->hasItem( sId ) )
+ xSubmission.set(mxSubmissions->getItem( sId ), css::uno::UNO_QUERY);
+ return xSubmission;
+}
+
+css::uno::Reference<css::container::XSet> Model::getSubmissions()
+{
+ DBG_INVARIANT();
+ return mxSubmissions;
+}
+
+
+// implement XPropertySet & friends
+
+
+#define HANDLE_ID 0
+#define HANDLE_ForeignSchema 3
+#define HANDLE_SchemaRef 4
+#define HANDLE_Namespaces 5
+#define HANDLE_ExternalData 6
+
+void Model::initializePropertySet()
+{
+ registerProperty( css::beans::Property("ID", HANDLE_ID, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new APIPropertyAccessor< Model, OUString >(this, &Model::setID, &Model::getID) );
+ registerProperty( css::beans::Property("ForeignSchema", HANDLE_ForeignSchema, cppu::UnoType<css::uno::Reference<css::xml::dom::XDocument>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Model, css::uno::Reference<css::xml::dom::XDocument> >( this, &Model::setForeignSchema, &Model::getForeignSchema) );
+
+ registerProperty( css::beans::Property("SchemaRef", HANDLE_SchemaRef, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Model, OUString >( this, &Model::setSchemaRef, &Model::getSchemaRef) );
+
+ registerProperty( css::beans::Property("Namespaces", HANDLE_Namespaces, cppu::UnoType<css::uno::Reference<css::container::XNameContainer>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Model, css::uno::Reference<css::container::XNameContainer> >( this, &Model::setNamespaces, &Model::getNamespaces) );
+
+ registerProperty( css::beans::Property("ExternalData", HANDLE_ExternalData, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND ),
+ new BooleanPropertyAccessor< Model >( this, &Model::setExternalData, &Model::getExternalData ) );
+}
+
+void Model::update()
+{
+ rebuild();
+}
+
+
+Sequence<sal_Int8> Model::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+OUString Model::getImplementationName()
+{
+ return "com.sun.star.form.Model";
+}
+
+sal_Bool Model::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> Model::getSupportedServiceNames()
+{
+ return {"com.sun.star.xforms.Model"};
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_Model_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new xforms::Model());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/model.hxx b/forms/source/xforms/model.hxx
new file mode 100644
index 0000000000..59fa7450fb
--- /dev/null
+++ b/forms/source/xforms/model.hxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include "propertysetbase.hxx"
+#include <com/sun/star/xforms/XModel2.hpp>
+#include <com/sun/star/xforms/XFormsUIHelper1.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ref.hxx>
+#include "mip.hxx"
+#include <map>
+
+
+// forward declaractions
+namespace com::sun::star
+{
+ namespace xml::dom { class XDocument; }
+ namespace xml::dom { class XNode; }
+ namespace uno { template<typename T> class Sequence; }
+ namespace lang { class IndexOutOfBoundsException; }
+ namespace lang { class IllegalArgumentException; }
+ namespace beans { class XPropertySet; }
+ namespace container { class XSet; }
+ namespace container { class XNameContainer; }
+ namespace frame { class XModel; }
+}
+namespace xforms
+{
+ class BindingCollection;
+ class SubmissionCollection;
+ class InstanceCollection;
+ class EvaluationContext;
+}
+
+
+namespace xforms
+{
+
+/** An XForms Model. Contains:
+ * # (set of) instance data (XML DOM tree)
+ * # (set of) bindings
+ * # (set of) submissions
+ * # (NOT YET IMPLEMENTED) actions (set of)
+ *
+ * See http://www.w3.org/TR/xforms/ for more information.
+ */
+typedef cppu::ImplInheritanceHelper<
+ PropertySetBase,
+ css::xforms::XModel2,
+ css::xforms::XFormsUIHelper1,
+ css::util::XUpdatable,
+ css::lang::XServiceInfo
+> Model_t;
+class Model : public Model_t
+{
+ // a number of local typedefs, to make the remaining header readable
+ typedef css::uno::Reference<css::xml::dom::XNode> XNode_t;
+ typedef css::uno::Reference<css::beans::XPropertySet> XPropertySet_t;
+
+ typedef std::multimap<XNode_t,std::pair<void*,MIP> > MIPs_t;
+
+
+private:
+
+ OUString msID; /// the model ID
+ rtl::Reference<BindingCollection> mxBindings; /// the bindings
+ rtl::Reference<SubmissionCollection> mxSubmissions; /// the submissions
+ rtl::Reference<InstanceCollection> mxInstances; /// the instance(s)
+
+ css::uno::Reference<css::xforms::XDataTypeRepository> mxDataTypes; /// the XSD data-types used
+ css::uno::Reference<css::xml::dom::XDocument> mxForeignSchema; /// the XSD-schema part we cannot
+ /// map onto data types
+ OUString msSchemaRef; /// xforms:model/@schema attribute
+
+ css::uno::Reference<css::container::XNameContainer> mxNamespaces; /// namespaces for entire model
+
+ MIPs_t maMIPs; /// map nodes to their MIPs
+
+ bool mbInitialized; /// has model been initialized ?
+ bool mbExternalData; /// is the data of this model to be considered an integral part of the document?
+
+ void initializePropertySet();
+
+ void ensureAtLeastOneInstance();
+
+
+public:
+
+ /// create a new model with an empty, default instance
+ Model();
+ virtual ~Model() noexcept override;
+
+ xforms::EvaluationContext getEvaluationContext();
+
+ // get/set that part of the schema, that we can't interpret as data types
+ css::uno::Reference<css::xml::dom::XDocument> getForeignSchema() const { return mxForeignSchema;}
+ void setForeignSchema( const css::uno::Reference<css::xml::dom::XDocument>& );
+
+ // get/set the xforms:model/@schema attribute
+ OUString getSchemaRef() const { return msSchemaRef;}
+ void setSchemaRef( const OUString& );
+
+ // get/set namespaces for entire model
+ css::uno::Reference<css::container::XNameContainer> getNamespaces() const { return mxNamespaces;}
+ void setNamespaces( const css::uno::Reference<css::container::XNameContainer>& );
+
+ // get/set the ExternalData property
+ bool getExternalData() const { return mbExternalData;}
+ void setExternalData( bool _bData );
+
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ void dbg_assertInvariant() const;
+#endif
+
+
+ // MIP (model item property) management
+
+
+ // register MIPs which apply to a given node; only to be called by bindings
+ // (The pTag parameter serves only to be able to remove the MIPs
+ // that were added using the same tag. No functions will be
+ // performed on it; hence the void* type.)
+ void addMIP( void* pTag, const XNode_t&, const MIP& );
+ void removeMIPs( void const * pTag );
+
+ /// query which MIPs apply to the given node
+ MIP queryMIP( const XNode_t& xNode ) const;
+
+ /// re-bind all bindings
+ void rebind();
+
+ /// call defer notifications on all bindings
+ void deferNotifications( bool );
+
+ /// set a data value in the instance
+ /// (also defers notifications)
+ bool setSimpleContent( const XNode_t&, const OUString& );
+
+ /// load instance data
+ void loadInstance( sal_Int32 nInstance );
+ void loadInstances();
+
+ /// has model been initialized?
+ bool isInitialized() const { return mbInitialized;}
+
+ /// is model currently valid (for submission)?
+ bool isValid() const;
+
+
+ // XModel
+ // implement the xforms::XModel implementation
+
+
+ virtual OUString SAL_CALL getID() override;
+
+ virtual void SAL_CALL setID( const OUString& sID ) override;
+
+ virtual void SAL_CALL initialize() override;
+
+ virtual void SAL_CALL rebuild() override;
+
+ virtual void SAL_CALL recalculate() override;
+
+ virtual void SAL_CALL revalidate() override;
+
+ virtual void SAL_CALL refresh() override;
+
+ virtual void SAL_CALL submit( const OUString& sID ) override;
+
+ virtual void SAL_CALL submitWithInteraction( const OUString& id, const css::uno::Reference<css::task::XInteractionHandler>& _rxHandler ) override;
+
+ virtual css::uno::Reference<css::xforms::XDataTypeRepository> SAL_CALL getDataTypeRepository( ) override;
+
+
+ // XModel: instance management
+
+ virtual css::uno::Reference<css::container::XSet> SAL_CALL getInstances() override;
+
+ virtual css::uno::Reference<css::xml::dom::XDocument> SAL_CALL getInstanceDocument( const OUString& ) override;
+
+ virtual css::uno::Reference<css::xml::dom::XDocument> SAL_CALL getDefaultInstance() override;
+
+
+ // XModel: binding management
+
+ virtual css::uno::Reference<css::beans::XPropertySet> SAL_CALL createBinding() override;
+
+ virtual css::uno::Reference<css::beans::XPropertySet> SAL_CALL cloneBinding( const css::uno::Reference<css::beans::XPropertySet>& ) override;
+
+ virtual css::uno::Reference<css::beans::XPropertySet> SAL_CALL getBinding( const OUString& ) override;
+
+ virtual css::uno::Reference<css::container::XSet> SAL_CALL getBindings() override;
+
+
+ // XModel: submission management
+
+ virtual css::uno::Reference<css::xforms::XSubmission> SAL_CALL createSubmission() override;
+
+ virtual css::uno::Reference<css::xforms::XSubmission> SAL_CALL cloneSubmission( const css::uno::Reference<css::beans::XPropertySet>& ) override;
+
+ virtual css::uno::Reference<css::xforms::XSubmission> SAL_CALL getSubmission( const OUString& ) override;
+
+ virtual css::uno::Reference<css::container::XSet> SAL_CALL getSubmissions() override;
+
+ // XPropertySet
+
+ virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p) override
+ { return PropertySetBase::getPropertyValue(p); }
+
+ virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { PropertySetBase::addPropertyChangeListener(p1, p2); }
+
+ virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { PropertySetBase::removePropertyChangeListener(p1, p2); }
+
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { PropertySetBase::addVetoableChangeListener(p1, p2); }
+
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { PropertySetBase::removeVetoableChangeListener(p1, p2); }
+
+ virtual css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override
+ { return PropertySetBase::getPropertySetInfo(); }
+
+ virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override
+ { PropertySetBase::setPropertyValue(p1, p2); }
+
+
+ // XFormsUIHelper1 & friends:
+ // (implementation in model_ui.cxx)
+
+
+ /// determine a reasonable control service for a given node
+ /// (based on data type MIP assigned to the node)
+ virtual OUString SAL_CALL getDefaultServiceNameForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode ) override;
+
+ /// call getDefaultBindingExpressionForNode with default evaluation context
+ virtual OUString SAL_CALL getDefaultBindingExpressionForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode ) override;
+
+ /// determine a reasonable default binding expression for a given node
+ /// and a given evaluation context
+ /// @returns expression, or empty string if no expression could be derived
+ OUString getDefaultBindingExpressionForNode(
+ const XNode_t&,
+ const EvaluationContext& );
+
+ virtual OUString SAL_CALL getNodeDisplayName( const css::uno::Reference<css::xml::dom::XNode>&,
+ sal_Bool bDetail ) override;
+
+ virtual OUString SAL_CALL getNodeName( const css::uno::Reference<css::xml::dom::XNode>& ) override;
+
+ virtual OUString SAL_CALL getBindingName( const css::uno::Reference< ::css::beans::XPropertySet >&,
+ sal_Bool bDetail ) override;
+
+ virtual OUString SAL_CALL getSubmissionName( const css::uno::Reference< ::css::beans::XPropertySet >&,
+ sal_Bool bDetail ) override;
+
+ virtual css::uno::Reference< ::css::beans::XPropertySet > SAL_CALL cloneBindingAsGhost( const css::uno::Reference< ::css::beans::XPropertySet >& ) override;
+
+ virtual void SAL_CALL removeBindingIfUseless( const css::uno::Reference< ::css::beans::XPropertySet >& ) override;
+
+ virtual css::uno::Reference<css::xml::dom::XDocument> SAL_CALL newInstance( const OUString& sName,
+ const OUString& sURL,
+ sal_Bool bURLOnce ) override;
+
+ virtual void SAL_CALL renameInstance( const OUString& sFrom,
+ const OUString& sTo,
+ const OUString& sURL,
+ sal_Bool bURLOnce ) override;
+
+ virtual void SAL_CALL removeInstance( const OUString& sName ) override;
+
+
+ virtual css::uno::Reference<css::xforms::XModel> SAL_CALL newModel( const css::uno::Reference<css::frame::XModel>& xComponent,
+ const OUString& sName ) override;
+ virtual void SAL_CALL renameModel( const css::uno::Reference<css::frame::XModel>& xComponent,
+ const OUString& sFrom,
+ const OUString& sTo ) override;
+
+ virtual void SAL_CALL removeModel( const css::uno::Reference<css::frame::XModel>& xComponent,
+ const OUString& sName ) override;
+
+
+ virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL createElement(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xParent,
+ const OUString& sName ) override;
+
+ virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL createAttribute(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xParent,
+ const OUString& sName ) override;
+
+ virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL renameNode(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xNode,
+ const OUString& sName ) override;
+
+ virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL getBindingForNode( const
+ css::uno::Reference<css::xml::dom::XNode>&,
+ sal_Bool bCreate ) override;
+
+ virtual void SAL_CALL removeBindingForNode( const css::uno::Reference< ::css::xml::dom::XNode >& ) override;
+
+ virtual OUString SAL_CALL getResultForExpression(
+ const css::uno::Reference< css::beans::XPropertySet >& xBinding,
+ sal_Bool bIsBindingExpression,
+ const OUString& sExpression ) override;
+
+ virtual sal_Bool SAL_CALL isValidXMLName( const OUString& sName ) override;
+
+ virtual sal_Bool SAL_CALL isValidPrefixName( const OUString& sName ) override;
+
+ virtual void SAL_CALL setNodeValue(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xNode,
+ const OUString& sValue ) override;
+
+
+ // XUpdatable
+
+
+public:
+ virtual void SAL_CALL update() override;
+
+
+ // XTypeProvider::getImplementationId
+
+
+public:
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/model_helper.hxx b/forms/source/xforms/model_helper.hxx
new file mode 100644
index 0000000000..08d7f01399
--- /dev/null
+++ b/forms/source/xforms/model_helper.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+
+// some helper definitions that must be available for model.cxx and
+// model_ui.cxx
+
+
+#include "namedcollection.hxx"
+#include "binding.hxx"
+#include "submission.hxx"
+#include "unohelper.hxx"
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/servicehelper.hxx>
+
+namespace xforms
+{
+ class Model;
+}
+
+
+// BindingCollection
+
+
+namespace xforms
+{
+
+class BindingCollection : public NamedCollection<css::uno::Reference<css::beans::XPropertySet> >
+{
+ Model* mpModel;
+
+public:
+ explicit BindingCollection( Model* pModel ) : mpModel( pModel ) {}
+
+ virtual bool isValid( const T& t ) const override
+ {
+ return comphelper::getFromUnoTunnel<Binding>( t ) != nullptr;
+ }
+
+protected:
+ virtual void _insert( const T& t ) override
+ {
+ auto pBinding = comphelper::getFromUnoTunnel<Binding>( t );
+ OSL_ENSURE( pBinding != nullptr, "invalid item?" );
+ pBinding->_setModel( mpModel );
+ }
+
+ virtual void _remove( const T& t ) override
+ {
+ auto pBinding = comphelper::getFromUnoTunnel<Binding>( t );
+ OSL_ENSURE( pBinding != nullptr, "invalid item?" );
+ pBinding->_setModel( nullptr );
+ }
+};
+
+class SubmissionCollection : public NamedCollection<css::uno::Reference<css::beans::XPropertySet> >
+{
+ Model* mpModel;
+
+public:
+ explicit SubmissionCollection( Model* pModel ) : mpModel( pModel ) {}
+
+ virtual bool isValid( const T& t ) const override
+ {
+ return dynamic_cast<Submission*>( t.get() ) != nullptr;
+ }
+
+protected:
+ virtual void _insert( const T& t ) override
+ {
+ auto pSubmission = dynamic_cast<Submission*>( t.get() );
+ assert(pSubmission && "invalid item?");
+ pSubmission->setModel( mpModel );
+ }
+
+ virtual void _remove( const T& t ) override
+ {
+ auto pSubmission = dynamic_cast<Submission*>( t.get() );
+ assert(pSubmission && "invalid item?");
+ pSubmission->setModel( nullptr );
+ }
+};
+
+class InstanceCollection : public Collection<css::uno::Sequence<css::beans::PropertyValue> >
+{
+public:
+ virtual bool isValid( const T& t ) const override
+ {
+ for( const css::beans::PropertyValue& rProp : t )
+ {
+ if (rProp.Name == "Instance" )
+ return true;
+ }
+ return false;
+ }
+};
+
+
+// helper functions
+
+
+sal_Int32 lcl_findInstance( const InstanceCollection*,
+ std::u16string_view );
+
+
+// get values from Sequence<PropertyValue> describing an Instance
+void getInstanceData(
+ const css::uno::Sequence<css::beans::PropertyValue>&,
+ OUString* pID,
+ css::uno::Reference<css::xml::dom::XDocument>*,
+ OUString* pURL,
+ bool* pURLOnce );
+
+// set values on Sequence<PropertyValue> for an Instance
+void setInstanceData(
+ css::uno::Sequence<css::beans::PropertyValue>&,
+ const OUString* pID,
+ const css::uno::Reference<css::xml::dom::XDocument>*,
+ const OUString* pURL,
+ const bool* pURLOnce );
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/model_ui.cxx b/forms/source/xforms/model_ui.cxx
new file mode 100644
index 0000000000..5d56e8b3ba
--- /dev/null
+++ b/forms/source/xforms/model_ui.cxx
@@ -0,0 +1,1004 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "model.hxx"
+#include "model_helper.hxx"
+#include "mip.hxx"
+#include "evaluationcontext.hxx"
+#include "unohelper.hxx"
+#include "submission/serialization_app_xml.hxx"
+#include "resourcehelper.hxx"
+#include "xmlhelper.hxx"
+#include "convert.hxx"
+#include <strings.hrc>
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/processfactory.hxx>
+
+// UNO classes
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+#include <com/sun/star/xml/dom/XNamedNodeMap.hpp>
+#include <com/sun/star/xml/xpath/XXPathObject.hpp>
+#include <com/sun/star/xml/xpath/XPathObjectType.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/io/TextInputStream.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/xforms/XFormsSupplier.hpp>
+#include <com/sun/star/xforms/XDataTypeRepository.hpp>
+#include <com/sun/star/xsd/XDataType.hpp>
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+
+
+using com::sun::star::beans::PropertyValue;
+using com::sun::star::io::TextInputStream;
+using com::sun::star::io::XTextInputStream2;
+using com::sun::star::container::XEnumeration;
+using com::sun::star::container::XNameContainer;
+using com::sun::star::xforms::XFormsSupplier;
+
+using namespace xforms;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::xml::dom;
+using namespace com::sun::star::xml::xpath;
+
+
+// implement XFormsUIHelper1
+
+
+OUString Model::getDefaultServiceNameForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode )
+{
+ // determine service for control. string/text field is default.
+ OUString sService = "com.sun.star.form.component.TextField";
+
+ // query repository for suitable type
+ OSL_ENSURE( mxDataTypes.is(), "no type repository?" );
+ OUString sTypeName = queryMIP( xNode ).getTypeName();
+ if( mxDataTypes->hasByName( sTypeName ) )
+ {
+ OSL_ENSURE( mxDataTypes->getDataType( sTypeName ).is(),
+ "has or has not?" );
+
+ switch( mxDataTypes->getDataType( sTypeName )->getTypeClass() )
+ {
+ case css::xsd::DataTypeClass::BOOLEAN:
+ sService = "com.sun.star.form.component.CheckBox";
+ break;
+ case css::xsd::DataTypeClass::DOUBLE:
+ case css::xsd::DataTypeClass::DECIMAL:
+ case css::xsd::DataTypeClass::FLOAT:
+ sService = "com.sun.star.form.component.NumericField";
+ break;
+
+ case css::xsd::DataTypeClass::STRING:
+ case css::xsd::DataTypeClass::DURATION:
+ case css::xsd::DataTypeClass::DATETIME:
+ case css::xsd::DataTypeClass::TIME:
+ case css::xsd::DataTypeClass::DATE:
+ case css::xsd::DataTypeClass::gYearMonth:
+ case css::xsd::DataTypeClass::gYear:
+ case css::xsd::DataTypeClass::gMonthDay:
+ case css::xsd::DataTypeClass::gDay:
+ case css::xsd::DataTypeClass::gMonth:
+ case css::xsd::DataTypeClass::hexBinary:
+ case css::xsd::DataTypeClass::base64Binary:
+ case css::xsd::DataTypeClass::anyURI:
+ case css::xsd::DataTypeClass::QName:
+ case css::xsd::DataTypeClass::NOTATION:
+ default:
+ // keep default
+ break;
+ }
+ }
+
+ return sService;
+}
+
+
+static void lcl_OutPosition( OUStringBuffer& rBuffer,
+ const Reference<XNode>& xNode )
+{
+ OSL_ENSURE( xNode->getParentNode().is(), "need parent" );
+
+ // count # of occurrences of this node
+ sal_Int32 nFound = 0;
+ sal_Int32 nPosition = -1;
+ if( xNode->getParentNode().is() )
+ {
+ for( Reference<XNode> xIter = xNode->getParentNode()->getFirstChild();
+ xIter != nullptr;
+ xIter = xIter->getNextSibling() )
+ {
+ if( xIter->getNodeType() == xNode->getNodeType() &&
+ xIter->getNodeName() == xNode->getNodeName() &&
+ xIter->getNamespaceURI() == xNode->getNamespaceURI() )
+ {
+ nFound++;
+ if( xIter == xNode )
+ nPosition = nFound;
+ }
+ }
+ }
+ OSL_ENSURE( nFound > 0 && nPosition > 0, "node not found???" );
+
+ // output position (if necessary)
+ if( nFound > 1 )
+ {
+ rBuffer.insert( 0, ']' );
+ rBuffer.insert( 0, nPosition );
+ rBuffer.insert( 0, '[' );
+ }
+}
+
+static void lcl_OutName( OUStringBuffer& rBuffer,
+ const Reference<XNode>& xNode )
+{
+ rBuffer.insert( 0, xNode->getNodeName() );
+ OUString sPrefix = xNode->getPrefix();
+ if( !sPrefix.isEmpty() )
+ {
+ rBuffer.insert( 0, sPrefix + ":" );
+ }
+}
+
+static void lcl_OutInstance( OUStringBuffer& rBuffer,
+ const Reference<XNode>& xNode,
+ Model* pModel )
+{
+ Reference<XDocument> xDoc = xNode->getOwnerDocument();
+
+ if( xDoc == pModel->getDefaultInstance() )
+ return;
+
+ rBuffer.insert( 0, "')" );
+
+ // iterate over instances, and find the right one
+ OUString sInstanceName;
+ Reference<XEnumeration> xEnum =
+ pModel->getInstances()->createEnumeration();
+ while( sInstanceName.isEmpty() && xEnum->hasMoreElements() )
+ {
+ Sequence<PropertyValue> aValues;
+ xEnum->nextElement() >>= aValues;
+
+ // get ID and instance
+ OUString sId;
+ Reference<XDocument> xInstance;
+ getInstanceData( aValues, &sId, &xInstance, nullptr, nullptr );
+
+ // now check whether this was our instance:
+ if( xInstance == xDoc )
+ sInstanceName = sId;
+ }
+
+ rBuffer.insert( 0, "instance('" + sInstanceName );
+}
+
+OUString Model::getDefaultBindingExpressionForNode(
+ const XNode_t& xNode,
+ const EvaluationContext& rContext)
+{
+ OSL_ENSURE( xNode.is(), "need node" );
+
+ // iterate upwards and put sections into the expression buffer.
+ // Stop iteration either at context node (relative expression) or
+ // at document root, whichever occurs first.
+ OUStringBuffer aBuffer;
+ for( Reference<XNode> xCurrent = xNode;
+ xCurrent.is() && xCurrent != rContext.mxContextNode;
+ xCurrent = xCurrent->getParentNode() )
+ {
+ // insert a '/' for every step except the first
+ if( !aBuffer.isEmpty() )
+ aBuffer.insert( 0, '/' );
+
+ switch( xCurrent->getNodeType() )
+ {
+ case NodeType_ELEMENT_NODE:
+ lcl_OutPosition( aBuffer, xCurrent );
+ lcl_OutName( aBuffer, xCurrent );
+ break;
+
+ case NodeType_TEXT_NODE:
+ lcl_OutPosition( aBuffer, xCurrent );
+ aBuffer.insert( 0, "text()" );
+ break;
+
+ case NodeType_ATTRIBUTE_NODE:
+ lcl_OutName( aBuffer, xCurrent );
+ aBuffer.insert( 0, '@' );
+ break;
+
+ case NodeType_DOCUMENT_NODE:
+ // check for which instance we have
+ lcl_OutInstance( aBuffer, xCurrent, this );
+ break;
+
+ default:
+ // unknown type? fail!
+ OSL_FAIL( "unknown node type!" );
+ return OUString();
+ }
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+
+OUString Model::getDefaultBindingExpressionForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode )
+{
+ return getDefaultBindingExpressionForNode( xNode, getEvaluationContext() );
+}
+
+static bool lcl_isWhitespace( const OUString& rString )
+{
+ sal_Int32 nLength = rString.getLength();
+ const sal_Unicode* pStr = rString.getStr();
+
+ bool bWhitespace = true;
+ for( sal_Int32 i = 0; bWhitespace && ( i < nLength ); i++ )
+ {
+ sal_Unicode c = pStr[i];
+ bWhitespace = ( c == u'\x0009' ||
+ c == u'\x000A' ||
+ c == u'\x000D' ||
+ c == u' ' );
+ }
+ return bWhitespace;
+}
+
+OUString Model::getNodeDisplayName( const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ sal_Bool bDetail )
+{
+ OUStringBuffer aBuffer;
+
+ switch( xNode->getNodeType() )
+ {
+ case NodeType_ELEMENT_NODE:
+ lcl_OutName( aBuffer, xNode );
+ break;
+
+ case NodeType_TEXT_NODE:
+ {
+ OUString sContent = xNode->getNodeValue();
+ if( bDetail || ! lcl_isWhitespace( sContent ) )
+ {
+ aBuffer.append("\"" + Convert::collapseWhitespace( sContent ) + "\"");
+ }
+ }
+ break;
+
+ case NodeType_ATTRIBUTE_NODE:
+ lcl_OutName( aBuffer, xNode );
+ aBuffer.insert( 0, '@' );
+ break;
+
+ case NodeType_DOCUMENT_NODE:
+ if( xNode == getDefaultInstance() )
+ aBuffer.append( '/' );
+ else
+ lcl_OutInstance( aBuffer, xNode, this );
+ break;
+
+ default:
+ // unknown type? fail!
+ OSL_FAIL( "unknown node type!" );
+ break;
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+OUString Model::getNodeName( const css::uno::Reference<css::xml::dom::XNode>& xNode )
+{
+ OUStringBuffer aBuffer;
+
+ switch( xNode->getNodeType() )
+ {
+ case NodeType_ELEMENT_NODE:
+ case NodeType_ATTRIBUTE_NODE:
+ lcl_OutName( aBuffer, xNode );
+ break;
+
+ case NodeType_TEXT_NODE:
+ case NodeType_DOCUMENT_NODE:
+ default:
+ // unknown type? fail!
+ OSL_FAIL( "no name for this node type!" );
+ break;
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+OUString Model::getBindingName( const css::uno::Reference< ::css::beans::XPropertySet >& xBinding,
+ sal_Bool /*bDetail*/ )
+{
+ OUString sID;
+ xBinding->getPropertyValue( "BindingID" ) >>= sID;
+ OUString sExpression;
+ xBinding->getPropertyValue( "BindingExpression" ) >>= sExpression;
+
+ OUString sRet;
+ if( !sID.isEmpty() )
+ {
+ sRet = sID + " (" + sExpression + ") ";
+ }
+ else
+ sRet = sExpression;
+
+ return sRet;
+}
+
+OUString Model::getSubmissionName( const css::uno::Reference< ::css::beans::XPropertySet >& xSubmission,
+ sal_Bool /*bDetail*/ )
+{
+ OUString sID;
+ xSubmission->getPropertyValue( "ID" ) >>= sID;
+ return sID;
+}
+
+css::uno::Reference< ::css::beans::XPropertySet > Model::cloneBindingAsGhost( const css::uno::Reference< ::css::beans::XPropertySet > &xBinding )
+{
+ // Create a new binding instance first...
+ rtl::Reference<Binding> pBinding = new Binding();
+
+ // ...and bump up the "deferred notification counter"
+ // to prevent this binding from contributing to the
+ // MIPs table...
+ pBinding->deferNotifications(true);
+
+ // Copy the propertyset and return result...
+ XPropertySet_t xNewBinding(pBinding);
+ copy( xBinding, xNewBinding );
+ return xNewBinding;
+}
+
+void Model::removeBindingIfUseless( const css::uno::Reference< ::css::beans::XPropertySet >& xBinding )
+{
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>( xBinding );
+ if( pBinding != nullptr )
+ {
+ if( ! pBinding->isUseful() )
+ mxBindings->removeItem( pBinding );
+ }
+}
+
+css::uno::Reference<css::xml::dom::XDocument> Model::newInstance( const OUString& sName,
+ const OUString& sURL,
+ sal_Bool bURLOnce )
+{
+ // create a default instance with <instanceData> element
+ css::uno::Reference<css::xml::dom::XDocument> xInstance = getDocumentBuilder()->newDocument();
+ DBG_ASSERT( xInstance.is(), "failed to create DOM instance" );
+
+ Reference<XNode>( xInstance, UNO_QUERY_THROW )->appendChild(
+ Reference<XNode>( xInstance->createElement( "instanceData" ),
+ UNO_QUERY_THROW ) );
+
+ Sequence<PropertyValue> aSequence;
+ bool bOnce = bURLOnce; // bool, so we can take address in setInstanceData
+ setInstanceData( aSequence, &sName, &xInstance, &sURL, &bOnce );
+ sal_Int32 nInstance = mxInstances->addItem( aSequence );
+ loadInstance( nInstance );
+
+ return xInstance;
+}
+
+static sal_Int32 lcl_findProp( const PropertyValue* pValues,
+ sal_Int32 nLength,
+ std::u16string_view rName )
+{
+ bool bFound = false;
+ sal_Int32 n = 0;
+ for( ; !bFound && n < nLength; n++ )
+ {
+ bFound = ( pValues[n].Name == rName );
+ }
+ return bFound ? ( n - 1) : -1;
+}
+
+sal_Int32 xforms::lcl_findInstance( const InstanceCollection* pInstances,
+ std::u16string_view rName )
+{
+ sal_Int32 nLength = pInstances->countItems();
+ sal_Int32 n = 0;
+ bool bFound = false;
+ for( ; !bFound && n < nLength; n++ )
+ {
+ OUString sName;
+ getInstanceData( pInstances->getItem( n ), &sName, nullptr, nullptr, nullptr );
+ bFound = ( sName == rName );
+ }
+ return bFound ? ( n - 1 ) : -1;
+}
+
+void Model::renameInstance( const OUString& sFrom,
+ const OUString& sTo,
+ const OUString& sURL,
+ sal_Bool bURLOnce )
+{
+ sal_Int32 nPos = lcl_findInstance( mxInstances.get(), sFrom );
+ if( nPos == -1 )
+ return;
+
+ Sequence<PropertyValue> aSeq = mxInstances->getItem( nPos );
+ PropertyValue* pSeq = aSeq.getArray();
+ sal_Int32 nLength = aSeq.getLength();
+
+ sal_Int32 nProp = lcl_findProp( pSeq, nLength, u"ID" );
+ if( nProp == -1 )
+ {
+ // add name property
+ aSeq.realloc( nLength + 1 );
+ pSeq = aSeq.getArray();
+ pSeq[ nLength ].Name = "ID";
+ nProp = nLength;
+ }
+
+ // change name
+ pSeq[ nProp ].Value <<= sTo;
+
+ // change url
+ nProp = lcl_findProp( pSeq, nLength, u"URL" );
+ if(nProp != -1)
+ pSeq[ nProp ].Value <<= sURL;
+
+ // change urlonce
+ nProp = lcl_findProp( pSeq, nLength, u"URLOnce" );
+ if(nProp != -1)
+ pSeq[ nProp ].Value <<= bURLOnce;
+
+ // set instance
+ mxInstances->setItem( nPos, aSeq );
+}
+
+void Model::removeInstance( const OUString& sName )
+{
+ sal_Int32 nPos = lcl_findInstance( mxInstances.get(), sName );
+ if( nPos != -1 )
+ mxInstances->removeItem( mxInstances->getItem( nPos ) );
+}
+
+static Reference<XNameContainer> lcl_getModels(
+ const Reference<css::frame::XModel>& xComponent )
+{
+ Reference<XNameContainer> xRet;
+ Reference<XFormsSupplier> xSupplier( xComponent, UNO_QUERY );
+ if( xSupplier.is() )
+ {
+ xRet = xSupplier->getXForms();
+ }
+ return xRet;
+}
+
+css::uno::Reference<css::xforms::XModel> Model::newModel( const Reference<css::frame::XModel>& xCmp,
+ const OUString& sName )
+{
+ css::uno::Reference<css::xforms::XModel> xModel;
+ Reference<XNameContainer> xModels = lcl_getModels( xCmp );
+ if( xModels.is()
+ && ! xModels->hasByName( sName ) )
+ {
+ rtl::Reference<Model> pModel = new Model();
+ xModel.set( pModel );
+
+ pModel->setID( sName );
+ pModel->newInstance( OUString(), OUString(), false );
+ pModel->initialize();
+ xModels->insertByName( sName, Any( xModel ) );
+ }
+
+ return xModel;
+}
+
+void Model::renameModel( const Reference<css::frame::XModel>& xCmp,
+ const OUString& sFrom,
+ const OUString& sTo )
+{
+ Reference<XNameContainer> xModels = lcl_getModels( xCmp );
+ if( xModels.is()
+ && xModels->hasByName( sFrom )
+ && ! xModels->hasByName( sTo ) )
+ {
+ Reference<XModel> xModel( xModels->getByName( sFrom ), UNO_QUERY );
+ xModel->setID( sTo );
+ xModels->insertByName( sTo, Any( xModel ) );
+ xModels->removeByName( sFrom );
+ }
+}
+
+void Model::removeModel( const Reference<css::frame::XModel>& xCmp,
+ const OUString& sName )
+{
+ Reference<XNameContainer> xModels = lcl_getModels( xCmp );
+ if( xModels.is()
+ && xModels->hasByName( sName ) )
+ {
+ xModels->removeByName( sName );
+ }
+}
+
+css::uno::Reference<css::xml::dom::XNode> Model::createElement( const css::uno::Reference<css::xml::dom::XNode>& xParent,
+ const OUString& sName )
+{
+ Reference<XNode> xNode;
+ if( xParent.is()
+ && isValidXMLName( sName ) )
+ {
+ // TODO: implement proper namespace handling
+ xNode = xParent->getOwnerDocument()->createElement( sName );
+ }
+ return xNode;
+}
+
+css::uno::Reference<css::xml::dom::XNode> Model::createAttribute( const css::uno::Reference<css::xml::dom::XNode>& xParent,
+ const OUString& sName )
+{
+ Reference<XNode> xNode;
+ Reference<XElement> xElement( xParent, UNO_QUERY );
+ if( xParent.is()
+ && xElement.is()
+ && isValidXMLName( sName ) )
+ {
+ // handle case where attribute already exists
+ sal_Int32 nCount = 0;
+ OUString sUniqueName = sName;
+ while( xElement->hasAttribute( sUniqueName ) )
+ {
+ nCount++;
+ sUniqueName = sName + OUString::number( nCount );
+ }
+
+ // TODO: implement proper namespace handling
+ xNode = xParent->getOwnerDocument()->createAttribute( sUniqueName );
+ }
+ return xNode;
+}
+
+css::uno::Reference<css::xml::dom::XNode> Model::renameNode( const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ const OUString& sName )
+{
+ // early out if we don't have to change the name
+ if( xNode->getNodeName() == sName )
+ return xNode;
+
+ // refuse to change name if it's an attribute, and the name is already used
+ if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE
+ && xNode->getParentNode().is()
+ && Reference<XElement>(xNode->getParentNode(), UNO_QUERY_THROW)->hasAttribute( sName ) )
+ return xNode;
+
+ // note old binding expression so we can adjust bindings below
+ OUString sOldDefaultBindingExpression =
+ getDefaultBindingExpressionForNode( xNode );
+
+ Reference<XDocument> xDoc = xNode->getOwnerDocument();
+ Reference<XNode> xNew;
+ if( xNode->getNodeType() == NodeType_ELEMENT_NODE )
+ {
+ Reference<XElement> xElem = xDoc->createElement( sName );
+ xNew = xElem;
+
+ // iterate over all attributes and append them to the new element
+ Reference<XElement> xOldElem( xNode, UNO_QUERY );
+ OSL_ENSURE( xNode.is(), "no element?" );
+
+ Reference<XNamedNodeMap> xMap = xNode->getAttributes();
+ sal_Int32 nLength = xMap.is() ? xMap->getLength() : 0;
+ // looping until nLength is suspicious wrt removeAttributeNode
+ // presumably shrinking XNamedNodeMap::getLength by 1
+ for( sal_Int32 n = 0; n < nLength; n++ )
+ {
+ Reference<XAttr> xAttr( xMap->item(n), UNO_QUERY );
+ xElem->setAttributeNode( xOldElem->removeAttributeNode( xAttr ) );
+ }
+
+ // iterate over all children and append them to the new element
+ for( Reference<XNode> xCurrent = xNode->getFirstChild();
+ xCurrent.is();
+ xCurrent = xNode->getFirstChild() )
+ {
+ xNew->appendChild( xNode->removeChild( xCurrent ) );
+ }
+
+ xNode->getParentNode()->replaceChild( xNew, xNode );
+ }
+ else if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
+ {
+ // create new attribute
+ Reference<XAttr> xAttr = xDoc->createAttribute( sName );
+ xAttr->setValue( xNode->getNodeValue() );
+
+ // replace node
+ Reference<XNode> xParent = xNode->getParentNode();
+ xParent->removeChild( xNode );
+ xNew = xParent->appendChild( xAttr );
+ }
+ else
+ {
+ OSL_FAIL( "can't rename this node type" );
+ }
+
+ // adjust bindings (if necessary):
+ if( xNew.is() )
+ {
+ // iterate over bindings and replace default expressions
+ OUString sNewDefaultBindingExpression =
+ getDefaultBindingExpressionForNode( xNew );
+ for( sal_Int32 n = 0; n < mxBindings->countItems(); n++ )
+ {
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>(
+ mxBindings->Collection<XPropertySet_t>::getItem( n ) );
+
+ if( pBinding->getBindingExpression()
+ == sOldDefaultBindingExpression )
+ pBinding->setBindingExpression( sNewDefaultBindingExpression );
+ }
+ }
+
+ // return node; return old node if renaming failed
+ return xNew.is() ? xNew : xNode;
+}
+
+css::uno::Reference< ::css::beans::XPropertySet > Model::getBindingForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ sal_Bool bCreate )
+{
+ OSL_ENSURE( xNode.is(), "no node?" );
+
+ // We will iterate over all bindings and determine the
+ // appropriateness of the respective binding for this node. The
+ // best one will be used. If we don't find any and bCreate is set,
+ // then we will create a suitable binding.
+ rtl::Reference<Binding> pBestBinding;
+ sal_Int32 nBestScore = 0;
+
+ for( sal_Int32 n = 0; n < mxBindings->countItems(); n++ )
+ {
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>(
+ mxBindings->Collection<XPropertySet_t>::getItem( n ) );
+
+ OSL_ENSURE( pBinding != nullptr, "no binding?" );
+ Reference<XNodeList> xNodeList = pBinding->getXNodeList();
+
+ sal_Int32 nNodes = xNodeList.is() ? xNodeList->getLength() : 0;
+ if( nNodes > 0 && xNodeList->item( 0 ) == xNode )
+ {
+ // allright, we found a suitable node. Let's determine how
+ // well it fits. Score:
+ // - bind to exactly this node is better than whole nodeset
+ // - simple binding expressions is better than complex ones
+ sal_Int32 nScore = 0;
+ if( nNodes == 1 )
+ nScore ++;
+ if( pBinding->isSimpleBindingExpression() )
+ nScore ++;
+
+ // if we found a better binding, remember it
+ if( nScore > nBestScore )
+ {
+ pBestBinding = pBinding;
+ nBestScore = nScore;
+ }
+ }
+ }
+
+ // create binding, if none was found and bCreate is set
+ OSL_ENSURE( ( nBestScore == 0 ) == ( pBestBinding == nullptr ),
+ "score != binding?" );
+ if( bCreate && pBestBinding == nullptr )
+ {
+ pBestBinding = new Binding();
+ pBestBinding->setBindingExpression(
+ getDefaultBindingExpressionForNode( xNode ) );
+ mxBindings->addItem( pBestBinding );
+ }
+
+ return pBestBinding;
+}
+
+void Model::removeBindingForNode( const css::uno::Reference<css::xml::dom::XNode>& )
+{
+ // determine whether suitable binding is still used
+}
+
+static OUString lcl_serializeForDisplay( const Reference< XAttr >& _rxAttrNode )
+{
+ OUString sResult;
+ OSL_ENSURE( _rxAttrNode.is(), "lcl_serializeForDisplay( attr ): invalid argument!" );
+ if ( _rxAttrNode.is() )
+ {
+ OUString sValue = _rxAttrNode->getValue();
+ sal_Unicode nQuote = '"';
+ if ( sValue.indexOf( nQuote ) >= 0 )
+ nQuote = '\'';
+
+ sResult = _rxAttrNode->getName() + "=" + OUStringChar(nQuote) + sValue + OUStringChar(nQuote) + " ";
+ }
+ return sResult;
+}
+
+static OUString lcl_serializeForDisplay( const Reference<XNodeList>& xNodes )
+{
+ OUStringBuffer sResult;
+
+ // create document fragment
+ Reference<XDocument> xDocument( getDocumentBuilder()->newDocument() );
+ Reference<XDocumentFragment> xFragment(
+ xDocument->createDocumentFragment() );
+ OSL_ENSURE( xFragment.is(), "xFragment" );
+
+ sal_Int32 nAttributeNodes = 0;
+
+ // attach nodelist to fragment
+ sal_Int32 nLength = xNodes->getLength();
+ for( sal_Int32 i = 0; i < nLength; i++ )
+ {
+ Reference<XNode> xCurrent = xNodes->item( i );
+
+ switch ( xCurrent->getNodeType() )
+ {
+ case NodeType_DOCUMENT_NODE:
+ // special-case documents: use top-level element instead
+ xCurrent = xCurrent->getFirstChild();
+ break;
+ case NodeType_ATTRIBUTE_NODE:
+ {
+ Reference< XAttr > xAttr( xCurrent, UNO_QUERY );
+ if ( xAttr.is() )
+ {
+ sResult.append(lcl_serializeForDisplay( xAttr ));
+ ++nAttributeNodes;
+ }
+ }
+ continue;
+
+ default:
+ break;
+ }
+
+ // append node
+ xFragment->appendChild( xDocument->importNode( xCurrent, true ) );
+ }
+ OSL_ENSURE( ( nAttributeNodes == 0 ) || ( nAttributeNodes == nLength ),
+ "lcl_serializeForDisplay: mixed attribute and non-attribute nodes?" );
+ if ( nAttributeNodes )
+ // had only attribute nodes
+ return sResult.makeStringAndClear();
+
+ // serialize fragment
+ CSerializationAppXML aSerialization;
+ aSerialization.setSource( xFragment );
+ aSerialization.serialize();
+
+ // copy stream into buffer
+ Reference<XTextInputStream2> xTextInputStream = TextInputStream::create( comphelper::getProcessComponentContext() );
+ xTextInputStream->setInputStream( aSerialization.getInputStream() );
+
+ /* WORK AROUND for problem in serialization: currently, multiple
+ XML declarations (<?xml...?>) are being written out and we don't
+ want them. When this is fixed, the code below is nice and
+ simple. The current code filters out the declarations.
+ OUString sResult = xTextInputStream->readString( Sequence<sal_Unicode>(),
+ sal_True );
+ */
+
+ // well, the serialization prepends XML header(s) that we need to
+ // remove first.
+ sResult.setLength(0);
+ while( ! xTextInputStream->isEOF() )
+ {
+ OUString sLine = xTextInputStream->readLine();
+ if( !sLine.isEmpty()
+ && !sLine.startsWith( "<?xml" ) )
+ {
+ sResult.append( sLine + "\n" );
+ }
+ }
+
+ return sResult.makeStringAndClear();
+}
+
+static OUString lcl_serializeForDisplay( const Reference<XXPathObject>& xResult )
+{
+ // error handling first
+ if( ! xResult.is() )
+ return getResource( RID_STR_XFORMS_CANT_EVALUATE );
+
+ // TODO: localize
+ switch( xResult->getObjectType() )
+ {
+ case XPathObjectType_XPATH_BOOLEAN:
+ return OUString::boolean(xResult->getBoolean());
+
+ case XPathObjectType_XPATH_STRING:
+ return "\"" + xResult->getString() + "\"";
+
+ case XPathObjectType_XPATH_NODESET:
+ return lcl_serializeForDisplay( xResult->getNodeList() );
+
+ case XPathObjectType_XPATH_NUMBER:
+ return OUString::number(xResult->getDouble());
+
+ case XPathObjectType_XPATH_UNDEFINED:
+ case XPathObjectType_XPATH_POINT:
+ case XPathObjectType_XPATH_RANGE:
+ case XPathObjectType_XPATH_LOCATIONSET:
+ case XPathObjectType_XPATH_USERS:
+ case XPathObjectType_XPATH_XSLT_TREE:
+ default:
+ // TODO: localized error message?
+ return OUString();
+ }
+}
+
+OUString Model::getResultForExpression(
+ const css::uno::Reference< ::css::beans::XPropertySet >& xBinding,
+ sal_Bool bIsBindingExpression,
+ const OUString& sExpression )
+{
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>( xBinding );
+ if( pBinding == nullptr )
+ throw RuntimeException();
+
+ // prepare & evaluate expression
+ OUStringBuffer aBuffer;
+ ComputedExpression aExpression;
+ aExpression.setExpression( sExpression );
+ if( bIsBindingExpression )
+ {
+ // binding: use binding context and evaluation
+ aExpression.evaluate( pBinding->getEvaluationContext() );
+ aBuffer.append( lcl_serializeForDisplay( aExpression.getXPath() ) );
+ }
+ else
+ {
+ // MIP (not binding): iterate over bindings contexts
+ std::vector<EvaluationContext> aContext =
+ pBinding->getMIPEvaluationContexts();
+ for (auto const& elem : aContext)
+ {
+ aExpression.evaluate(elem);
+ aBuffer.append( lcl_serializeForDisplay(aExpression.getXPath()) );
+ aBuffer.append( '\n' );
+ }
+ }
+ return aBuffer.makeStringAndClear();
+}
+
+sal_Bool Model::isValidXMLName( const OUString& sName )
+{
+ return isValidQName( sName, nullptr );
+}
+
+sal_Bool Model::isValidPrefixName( const OUString& sName )
+{
+ return ::isValidPrefixName( sName, nullptr );
+}
+
+void Model::setNodeValue(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xNode,
+ const OUString& sValue )
+{
+ setSimpleContent( xNode, sValue );
+}
+
+
+// helper functions from model_helper.hxx
+
+
+void xforms::getInstanceData(
+ const Sequence<PropertyValue>& aValues,
+ OUString* pID,
+ Reference<XDocument>* pInstance,
+ OUString* pURL,
+ bool* pURLOnce )
+{
+ sal_Int32 nValues = aValues.getLength();
+ const PropertyValue* pValues = aValues.getConstArray();
+ for( sal_Int32 n = 0; n < nValues; n++ )
+ {
+ const PropertyValue& rValue = pValues[n];
+ if( pID != nullptr && rValue.Name == "ID")
+ rValue.Value >>= *pID;
+ if( pInstance != nullptr && rValue.Name == "Instance")
+ rValue.Value >>= *pInstance;
+ if( pURL != nullptr && rValue.Name == "URL")
+ rValue.Value >>= *pURL;
+ if( pURLOnce != nullptr && rValue.Name == "URLOnce")
+ rValue.Value >>= *pURLOnce;
+ }
+}
+
+void xforms::setInstanceData(
+ Sequence<PropertyValue>& aSequence,
+ const OUString* _pID,
+ const Reference<XDocument>* _pInstance,
+ const OUString* _pURL,
+ const bool* _pURLOnce )
+{
+ // get old instance data
+ OUString sID;
+ Reference<XDocument> xInstance;
+ OUString sURL;
+ bool bURLOnce = false;
+ getInstanceData( aSequence, &sID, &xInstance, &sURL, &bURLOnce );
+ const OUString* pID = !sID.isEmpty() ? &sID : nullptr;
+ const Reference<XDocument>* pInstance = xInstance.is() ? &xInstance : nullptr;
+ const OUString* pURL = !sURL.isEmpty() ? &sURL : nullptr;
+ const bool* pURLOnce = ( bURLOnce && pURL != nullptr ) ? &bURLOnce : nullptr;
+
+ // determine new instance data
+ if (_pID != nullptr)
+ pID = _pID;
+ if (_pInstance != nullptr)
+ pInstance = _pInstance;
+ if (_pURL != nullptr)
+ pURL = _pURL;
+ if (_pURLOnce != nullptr)
+ pURLOnce = _pURLOnce;
+
+ // count # of values we want to set
+ sal_Int32 nCount = 0;
+ if (pID != nullptr)
+ ++nCount;
+ if (pInstance != nullptr)
+ ++nCount;
+ if (pURL != nullptr)
+ ++nCount;
+ if (pURLOnce != nullptr)
+ ++nCount;
+
+ // realloc sequence and enter values;
+ aSequence.realloc( nCount );
+ PropertyValue* pSequence = aSequence.getArray();
+ sal_Int32 nIndex = 0;
+ if(pID != nullptr)
+ {
+ pSequence[ nIndex ].Name = "ID";
+ pSequence[ nIndex ].Value <<= *pID;
+ nIndex++;
+ }
+ if(pInstance != nullptr)
+ {
+ pSequence[ nIndex ].Name = "Instance";
+ pSequence[ nIndex ].Value <<= *pInstance;
+ nIndex++;
+ }
+ if(pURL != nullptr)
+ {
+ pSequence[ nIndex ].Name = "URL";
+ pSequence[ nIndex ].Value <<= *pURL;
+ nIndex++;
+ }
+ if(pURLOnce != nullptr)
+ {
+ pSequence[ nIndex ].Name = "URLOnce";
+ pSequence[ nIndex ].Value <<= *pURLOnce;
+ nIndex++;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/namedcollection.hxx b/forms/source/xforms/namedcollection.hxx
new file mode 100644
index 0000000000..4cbd99eb43
--- /dev/null
+++ b/forms/source/xforms/namedcollection.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "collection.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+
+#include <algorithm>
+
+template<class T>
+class NamedCollection : public cppu::ImplInheritanceHelper<
+ Collection<T>,
+ css::container::XNameAccess>
+{
+ using Collection<T>::maItems;
+ using Collection<T>::getItem;
+ using Collection<T>::hasItem;
+
+public:
+
+ const T& getItem( const OUString& rName ) const
+ {
+ OSL_ENSURE( hasItem( rName ), "invalid name" );
+ return *findItem( rName );
+ }
+
+ bool hasItem( const OUString& rName ) const
+ {
+ return findItem( rName ) != maItems.end();
+ }
+
+ css::uno::Sequence<OUString> getNames() const
+ {
+ // iterate over members, and collect all those that have names
+ std::vector<OUString> aNames;
+ for( const T& rItem : maItems )
+ {
+ css::uno::Reference<css::container::XNamed>
+ xNamed( rItem, css::uno::UNO_QUERY );
+ if( xNamed.is() )
+ aNames.push_back( xNamed->getName() );
+ }
+
+ return comphelper::containerToSequence(aNames);
+ }
+
+protected:
+ typename std::vector<T>::const_iterator findItem( const OUString& rName ) const
+ {
+ return std::find_if(maItems.begin(), maItems.end(), [&rName](const T& rItem) {
+ css::uno::Reference<css::container::XNamed>
+ xNamed( rItem, css::uno::UNO_QUERY );
+ return xNamed.is() && xNamed->getName() == rName;
+ });
+ }
+
+public:
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override
+ {
+ return Collection<T>::getElementType();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return Collection<T>::hasElements();
+ }
+
+ // XNameAccess : XElementAccess
+ virtual css::uno::Any SAL_CALL getByName(
+ const OUString& aName ) override
+ {
+ if( !hasItem( aName ) )
+ throw css::container::NoSuchElementException();
+ return css::uno::Any( getItem( aName ) );
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getElementNames() override
+ {
+ return getNames();
+ }
+
+ virtual sal_Bool SAL_CALL hasByName(
+ const OUString& aName ) override
+ {
+ return hasItem( aName );
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/pathexpression.cxx b/forms/source/xforms/pathexpression.cxx
new file mode 100644
index 0000000000..c26d13d5ad
--- /dev/null
+++ b/forms/source/xforms/pathexpression.cxx
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "pathexpression.hxx"
+#include "unohelper.hxx"
+#include "evaluationcontext.hxx"
+
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/xpath/XXPathObject.hpp>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+
+using com::sun::star::uno::Reference;
+using com::sun::star::xml::dom::XNode;
+using com::sun::star::xml::dom::XNodeList;
+
+
+namespace xforms
+{
+
+PathExpression::PathExpression()
+{
+}
+
+PathExpression::~PathExpression()
+{
+}
+
+
+void PathExpression::setExpression( const OUString& rExpression )
+{
+ // set new expression, and clear pre-computed results
+ ComputedExpression::setExpression( rExpression );
+
+ // check expression against regular expression to determine
+ // whether it contains only 'simple' (i.e. static) conditions. For
+ // now, we check whether it only contains number positions.
+ // (TODO: Only works for names containing only ASCII letters+digits.)
+ mbIsSimple =
+ _checkExpression( "( */@?[a-zA-Z0-9:]+( *\\[ *[0-9 ]+ *\\] *)?)+" );
+
+ maNodes.clear();
+}
+
+OUString PathExpression::_getExpressionForEvaluation() const
+{
+ OUString sExpr = ComputedExpression::_getExpressionForEvaluation();
+ if( sExpr.isEmpty())
+ sExpr = ".";
+ return sExpr;
+}
+
+void PathExpression::evaluate( const EvaluationContext& rContext )
+{
+ // for simple expression we don't need to re-bind (if we were bound before)
+ // (we will evaluate empty expressions, since they are interpreted as ".")
+ if( mxResult.is() && isSimpleExpression() )
+ return;
+
+ _evaluate( rContext, _getExpressionForEvaluation() );
+
+ // clear old result, and copy new
+ maNodes.clear();
+ if( mxResult.is() )
+ {
+ // copy node list
+ Reference<XNodeList> xNodeList = mxResult->getNodeList();
+ OSL_ENSURE( xNodeList.is(), "empty object (instead of empty list)" );
+ sal_Int32 nLength = xNodeList.is() ? xNodeList->getLength() : 0;
+ for( sal_Int32 n = 0; n < nLength; n++ )
+ maNodes.push_back( xNodeList->item( n ) );
+ }
+}
+
+
+Reference<XNode> PathExpression::getNode() const
+{
+ Reference<XNode> xResult;
+ if( ! maNodes.empty() )
+ xResult = *maNodes.begin();
+ return xResult;
+}
+
+
+Reference<XNodeList> PathExpression::getXNodeList() const
+{
+ return mxResult.is() ? mxResult->getNodeList() : Reference<XNodeList>();
+}
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/pathexpression.hxx b/forms/source/xforms/pathexpression.hxx
new file mode 100644
index 0000000000..3192301602
--- /dev/null
+++ b/forms/source/xforms/pathexpression.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "computedexpression.hxx"
+
+#include <vector>
+
+// forward declaractions
+namespace com::sun::star::xml::dom
+{
+class XNodeList;
+namespace events
+{
+class XEventListener;
+}
+}
+
+namespace xforms
+{
+/** PathExpression represents an XPath Expression and caches results */
+class PathExpression final : public ComputedExpression
+{
+public:
+ typedef std::vector<css::uno::Reference<css::xml::dom::XNode>> NodeVector_t;
+
+private:
+ /// the node-list result from the last bind (cached from mxResult)
+ NodeVector_t maNodes;
+
+ /// get expression for evaluation
+ OUString _getExpressionForEvaluation() const;
+
+public:
+ PathExpression();
+ ~PathExpression();
+
+ /// set the expression string
+ /// (overridden to do remove old listeners)
+ /// (also defines simple expressions)
+ void setExpression(const OUString& rExpression);
+
+ /// evaluate the expression relative to the content node.
+ void evaluate(const xforms::EvaluationContext& rContext);
+
+ // get the result of this expression as node/node list/...
+ css::uno::Reference<css::xml::dom::XNode> getNode() const;
+ const NodeVector_t& getNodeList() const { return maNodes; }
+ css::uno::Reference<css::xml::dom::XNodeList> getXNodeList() const;
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/propertysetbase.cxx b/forms/source/xforms/propertysetbase.cxx
new file mode 100644
index 0000000000..0e19bfe968
--- /dev/null
+++ b/forms/source/xforms/propertysetbase.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "propertysetbase.hxx"
+
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+using com::sun::star::uno::Any;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::Exception;
+using com::sun::star::lang::IllegalArgumentException;
+using com::sun::star::beans::Property;
+using com::sun::star::beans::XPropertySetInfo;
+
+PropertyAccessorBase::~PropertyAccessorBase()
+{
+}
+
+PropertySetBase::PropertySetBase( )
+{
+}
+
+PropertySetBase::~PropertySetBase( )
+{
+}
+
+cppu::IPropertyArrayHelper& SAL_CALL PropertySetBase::getInfoHelper()
+{
+ if ( !m_pProperties )
+ {
+ OSL_ENSURE( !m_aProperties.empty(), "PropertySetBase::getInfoHelper: no registered properties!" );
+ m_pProperties.reset(new cppu::OPropertyArrayHelper( m_aProperties.data(), m_aProperties.size(), false ));
+ }
+ return *m_pProperties;
+}
+
+Reference< XPropertySetInfo > SAL_CALL PropertySetBase::getPropertySetInfo( )
+{
+ return cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
+}
+
+void PropertySetBase::registerProperty( const Property& rProperty,
+ const ::rtl::Reference< PropertyAccessorBase >& rAccessor )
+{
+ OSL_ENSURE(rAccessor,
+ "PropertySetBase::registerProperty: invalid property accessor, this will crash!");
+ m_aAccessors.emplace( rProperty.Handle, rAccessor );
+
+ OSL_ENSURE( rAccessor->isWriteable()
+ == ( ( rProperty.Attributes & css::beans::PropertyAttribute::READONLY ) == 0 ),
+ "PropertySetBase::registerProperty: inconsistence!" );
+
+ m_aProperties.push_back( rProperty );
+}
+
+void PropertySetBase::notifyAndCachePropertyValue( sal_Int32 nHandle )
+{
+ ::osl::ClearableMutexGuard aGuard( GetMutex() );
+
+ PropertyValueCache::iterator aPos = m_aCache.find( nHandle );
+ if ( aPos == m_aCache.end() )
+ { // method has never before been invoked for this property
+ try
+ {
+ // determine the type of this property
+ ::cppu::IPropertyArrayHelper& rPropertyMetaData = getInfoHelper();
+ OUString sPropName;
+ OSL_VERIFY( rPropertyMetaData.fillPropertyMembersByHandle( &sPropName, nullptr, nHandle ) );
+ Property aProperty = rPropertyMetaData.getPropertyByName( sPropName );
+ // default construct a value of this type
+ Any aEmptyValue( nullptr, aProperty.Type );
+ // insert into the cache
+ aPos = m_aCache.emplace( nHandle, aEmptyValue ).first;
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "PropertySetBase::notifyAndCachePropertyValue: this is not expected to fail!" );
+ }
+ }
+ Any aOldValue = aPos->second;
+ // determine the current value
+ Any aNewValue;
+ getFastPropertyValue( aNewValue, nHandle );
+ // remember the old value
+ aPos->second = aNewValue;
+
+ aGuard.clear();
+ if ( aNewValue != aOldValue )
+ firePropertyChange( nHandle, aNewValue, aOldValue );
+}
+
+void PropertySetBase::initializePropertyValueCache( sal_Int32 nHandle )
+{
+ Any aCurrentValue;
+ getFastPropertyValue( aCurrentValue, nHandle );
+
+ ::std::pair< PropertyValueCache::iterator, bool > aInsertResult =
+ m_aCache.emplace( nHandle, aCurrentValue );
+ OSL_ENSURE( aInsertResult.second, "PropertySetBase::initializePropertyValueCache: already cached a value for this property!" );
+}
+
+PropertyAccessorBase& PropertySetBase::locatePropertyHandler( sal_Int32 nHandle ) const
+{
+ PropertyAccessors::const_iterator aPropertyPos = m_aAccessors.find( nHandle );
+ assert( aPropertyPos != m_aAccessors.end() && aPropertyPos->second &&
+ "PropertySetBase::locatePropertyHandler: accessor map is corrupted!" );
+ // neither should this be called for handles where there is no accessor, nor should a
+ // NULL accessor be in the map
+ return *aPropertyPos->second;
+}
+
+sal_Bool SAL_CALL PropertySetBase::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle,
+ const Any& rValue )
+{
+ PropertyAccessorBase& rAccessor = locatePropertyHandler( nHandle );
+ if ( !rAccessor.approveValue( rValue ) )
+ throw IllegalArgumentException( OUString(), *this, 0 );
+
+ rAccessor.getValue( rOldValue );
+ if ( rOldValue != rValue )
+ {
+ rConvertedValue = rValue; // no conversion at all
+ return true;
+ }
+ return false;
+}
+
+void SAL_CALL PropertySetBase::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ PropertyAccessorBase& rAccessor = locatePropertyHandler( nHandle );
+ rAccessor.setValue( rValue );
+}
+
+void SAL_CALL PropertySetBase::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+{
+ PropertyAccessorBase& rAccessor = locatePropertyHandler( nHandle );
+ rAccessor.getValue( rValue );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/propertysetbase.hxx b/forms/source/xforms/propertysetbase.hxx
new file mode 100644
index 0000000000..d6ae974058
--- /dev/null
+++ b/forms/source/xforms/propertysetbase.hxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <comphelper/propstate.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+// include for inlined helper function below
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <map>
+#include <memory>
+
+namespace com::sun::star::uno {
+ class Any;
+ class RuntimeException;
+ template<class T> class Sequence;
+}
+
+/** base class which encapsulates accessing (reading/writing) concrete property values
+*/
+class PropertyAccessorBase : public salhelper::SimpleReferenceObject
+{
+protected:
+ PropertyAccessorBase() { }
+ virtual ~PropertyAccessorBase() override;
+
+public:
+
+ virtual bool approveValue( const css::uno::Any& rValue ) const = 0;
+ virtual void setValue( const css::uno::Any& rValue ) = 0;
+ virtual void getValue( css::uno::Any& rValue ) const = 0;
+ virtual bool isWriteable() const = 0;
+};
+
+
+/** helper class for implementing property accessors through public member functions
+*/
+template< typename CLASS, typename VALUE, class WRITER, class READER >
+class GenericPropertyAccessor : public PropertyAccessorBase
+{
+public:
+ typedef WRITER Writer;
+ typedef READER Reader;
+
+private:
+ CLASS* m_pInstance;
+ Writer m_pWriter;
+ Reader m_pReader;
+
+public:
+ GenericPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader )
+ :m_pInstance( pInstance )
+ ,m_pWriter( pWriter )
+ ,m_pReader( pReader )
+ {
+ }
+
+ virtual bool approveValue( const css::uno::Any& rValue ) const override
+ {
+ VALUE aVal;
+ return ( rValue >>= aVal );
+ }
+
+ virtual void setValue( const css::uno::Any& rValue ) override
+ {
+ VALUE aTypedVal = VALUE();
+ OSL_VERIFY( rValue >>= aTypedVal );
+ (m_pInstance->*m_pWriter)( aTypedVal );
+ }
+
+ virtual void getValue( css::uno::Any& rValue ) const override
+ {
+ rValue = css::uno::Any( (m_pInstance->*m_pReader)() );
+ }
+
+ virtual bool isWriteable() const override
+ {
+ return m_pWriter != nullptr;
+ }
+};
+
+/** helper class for implementing property accessors via non-UNO methods
+*/
+template< typename CLASS, typename VALUE >
+class DirectPropertyAccessor
+ :public GenericPropertyAccessor < CLASS
+ , VALUE
+ , void (CLASS::*)( const VALUE& )
+ , VALUE (CLASS::*)() const
+ >
+{
+protected:
+ typedef void (CLASS::*Writer)( const VALUE& );
+ typedef VALUE (CLASS::*Reader)() const;
+public:
+ DirectPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader )
+ :GenericPropertyAccessor< CLASS, VALUE, Writer, Reader >( pInstance, pWriter, pReader )
+ {
+ }
+};
+
+/** helper class for implementing non-UNO accessors to a boolean property
+*/
+template< typename CLASS >
+class BooleanPropertyAccessor
+ :public GenericPropertyAccessor < CLASS
+ , bool
+ , void (CLASS::*)( bool )
+ , bool (CLASS::*)() const
+ >
+{
+protected:
+ typedef void (CLASS::*Writer)( bool );
+ typedef bool (CLASS::*Reader)() const;
+public:
+ BooleanPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader )
+ :GenericPropertyAccessor< CLASS, bool, Writer, Reader >( pInstance, pWriter, pReader )
+ {
+ }
+};
+
+/** helper class for implementing property accessors via UNO methods
+*/
+template< typename CLASS, typename VALUE >
+class APIPropertyAccessor
+ :public GenericPropertyAccessor < CLASS
+ , VALUE
+ , void (SAL_CALL CLASS::*)( const VALUE& )
+ , VALUE (SAL_CALL CLASS::*)()
+ >
+{
+protected:
+ typedef void (SAL_CALL CLASS::*Writer)( const VALUE& );
+ typedef VALUE (SAL_CALL CLASS::*Reader)();
+public:
+ APIPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader )
+ :GenericPropertyAccessor< CLASS, VALUE, Writer, Reader >( pInstance, pWriter, pReader )
+ {
+ }
+};
+
+/** bridges two XPropertySet helper implementations
+
+ The <type scope="comphelper">OStatefulPropertySet</type> (basically, the
+ <type scope="cppu">OPropertySetHelper</type>) implements a comprehensive framework
+ for property sets, including property change notifications.
+ However, it lacks some easy possibilities to declare the supported properties.
+ Other helper structs and classes allow for this, but are lacking needed features
+ such as property change notifications.
+
+ The <type>PropertySetBase</type> bridges various implementations,
+ so you have the best of both worlds.
+ */
+class PropertySetBase : public ::comphelper::OStatefulPropertySet
+{
+private:
+ typedef ::std::map< const sal_Int32, ::rtl::Reference< PropertyAccessorBase > > PropertyAccessors;
+ typedef ::std::vector< css::beans::Property > PropertyArray;
+ typedef ::std::map< const sal_Int32, css::uno::Any > PropertyValueCache;
+
+ PropertyArray m_aProperties;
+ std::unique_ptr<cppu::IPropertyArrayHelper> m_pProperties;
+ PropertyAccessors m_aAccessors;
+ PropertyValueCache m_aCache;
+
+protected:
+ PropertySetBase();
+ virtual ~PropertySetBase() override;
+
+ /** registers a new property to be supported by this instance
+ @param rProperty
+ the descriptor for the to-be-supported property
+ @param rAccessor
+ an instance which is able to provide read and possibly write access to
+ the property.
+ @precond
+ Must not be called after any of the property set related UNO interfaces
+ has been used. Usually, you will do a number of <member>registerProperty</member>
+ calls in the constructor of your class.
+ */
+ void registerProperty(
+ const css::beans::Property& rProperty,
+ const ::rtl::Reference< PropertyAccessorBase >& rAccessor
+ );
+
+ /** notifies a change in a given property value, if necessary
+
+ The necessity of the notification is determined by a cached value for the given
+ property. Caching happens after notification.
+
+ That is, when you call <member>notifyAndCachePropertyValue</member> for the first time,
+ a value for the given property is default constructed, and considered to be the "old value".
+ If this value differs from the current value, then this change is notified to all interested
+ listeners. Finally, the current value is remembered.
+
+ Subsequent calls to <member>notifyAndCachePropertyValue</member> use the remembered value as
+ "old value", and from then on behave as the first call.
+
+ @param nHandle
+ the handle of the property. Must denote a property supported by this instance, i.e.
+ one previously registered via <member>registerProperty</member>.
+
+ @precond
+ our ref count must not be 0. The reason is that during this method's execution,
+ the instance might be acquired and released, which would immediately destroy
+ the instance if it has a ref count of 0.
+
+ @seealso initializePropertyValueCache
+ */
+ void notifyAndCachePropertyValue( sal_Int32 nHandle );
+
+ /** initializes the property value cache for the given property, with its current value
+
+ Usually used to initialize the cache with values which are different from default
+ constructed values. Say you have a boolean property whose initial state
+ is <TRUE/>. Say you call <member>notifyAndCachePropertyValue</member> the first time: it will
+ default construct the "old value" for this property as <FALSE/>, and thus <b>not</b> do
+ any notifications if the "current value" is also <FALSE/> - which might be wrong, since
+ the guessing of the "old value" differed from the real initial value which was <TRUE/>.
+
+ Too confusing? Okay, then just call this method for every property you have.
+
+ @param nHandle
+ the handle of the property. Must denote a property supported by this instance, i.e.
+ one previously registered via <member>registerProperty</member>.
+ @param rValue
+ the value to cache
+ @seealso notifyAndCachePropertyValue
+ */
+ void initializePropertyValueCache( sal_Int32 nHandle );
+
+ /// OPropertysetHelper methods
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& rConvertedValue, css::uno::Any& rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+
+ virtual cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+
+public:
+ /// helper struct for granting selective access to some notification-related methods
+ struct NotifierAccess { friend struct PropertyChangeNotifier; private: NotifierAccess() { } };
+ /** retrieves the current property value for the given handle
+ @param nHandle
+ the handle of the property. Must denote a property supported by this instance, i.e.
+ one previously registered via <member>registerProperty</member>.
+ @see registerProperty
+ */
+ void getCurrentPropertyValueByHandle( sal_Int32 nHandle, css::uno::Any& /* [out] */ rValue, const NotifierAccess& ) const
+ {
+ getFastPropertyValue( rValue, nHandle );
+ }
+
+ /** notifies a change in a given property to all interested listeners
+ */
+ void notifyPropertyChange( sal_Int32 nHandle, const css::uno::Any& rOldValue, const css::uno::Any& rNewValue, const NotifierAccess& ) const
+ {
+ const_cast< PropertySetBase* >( this )->firePropertyChange( nHandle, rNewValue, rOldValue );
+ }
+
+ using ::comphelper::OStatefulPropertySet::getFastPropertyValue;
+
+private:
+ /** locates a property given by handle
+ @param nHandle
+ the handle of the property. Must denote a property supported by this instance, i.e.
+ one previously registered via <member>registerProperty</member>.
+ @see registerProperty
+ */
+ PropertyAccessorBase& locatePropertyHandler( sal_Int32 nHandle ) const;
+};
+
+/** a helper class for notifying property changes in a <type>PropertySetBase</type> instance.
+
+ You can create an instance of this class on the stack of a method which is to programmatically
+ change the value of a property. In its constructor, the instance will acquire the current property
+ value, and in its destructor, it will notify the change of this property's value (if necessary).
+
+ You do not need this class if you are modifying property values by using the X(Fast|Multi)PropertSet
+ methods, since those already care for property notifications. You only need it if you're changing
+ the internal representation of your property directly.
+
+ Also note that usually, notifications in the UNO world should be done without a locked mutex. So
+ if you use this class in conjunction with a <type>MutexGuard</type>, ensure that you <b>first</b>
+ instantiate the <type>PropertyChangeNotifier</type>, and <b>then</b> the <type>MutexGuard</type>,
+ so your mutex is released before the notification happens.
+*/
+struct PropertyChangeNotifier
+{
+private:
+ const PropertySetBase& m_rPropertySet;
+ sal_Int32 m_nHandle;
+ css::uno::Any m_aOldValue;
+
+public:
+ /** constructs a PropertyChangeNotifier
+ @param rPropertySet
+ the property set implementation whose property is going to be changed. Note
+ that this property set implementation must live at least as long as the
+ PropertyChangeNotifier instance does.
+ @param nHandle
+ the handle of the property which is going to be changed. Must be a valid property
+ handle for the given <arg>rPropertySet</arg>
+ */
+ PropertyChangeNotifier( const PropertySetBase& rPropertySet, sal_Int32 nHandle )
+ :m_rPropertySet( rPropertySet )
+ ,m_nHandle( nHandle )
+ {
+ m_rPropertySet.getCurrentPropertyValueByHandle( m_nHandle, m_aOldValue, PropertySetBase::NotifierAccess() );
+ }
+ ~PropertyChangeNotifier()
+ {
+ css::uno::Any aNewValue;
+ m_rPropertySet.getCurrentPropertyValueByHandle( m_nHandle, aNewValue, PropertySetBase::NotifierAccess() );
+ if ( aNewValue != m_aOldValue )
+ {
+ m_rPropertySet.notifyPropertyChange( m_nHandle, m_aOldValue, aNewValue, PropertySetBase::NotifierAccess() );
+ }
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/resourcehelper.cxx b/forms/source/xforms/resourcehelper.cxx
new file mode 100644
index 0000000000..011d806f59
--- /dev/null
+++ b/forms/source/xforms/resourcehelper.cxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "resourcehelper.hxx"
+#include <frm_resource.hxx>
+
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+
+
+namespace xforms
+{
+
+OUString getResource(TranslateId pResourceId)
+{
+ return getResource(pResourceId, {}, {}, {});
+}
+
+OUString getResource(TranslateId pResourceId,
+ std::u16string_view rInfo1)
+{
+ return getResource(pResourceId, rInfo1, {}, {});
+}
+
+OUString getResource(TranslateId pResourceId,
+ std::u16string_view rInfo1,
+ std::u16string_view rInfo2)
+{
+ return getResource(pResourceId, rInfo1, rInfo2, {});
+}
+
+OUString getResource(TranslateId pResourceId,
+ std::u16string_view rInfo1,
+ std::u16string_view rInfo2,
+ std::u16string_view rInfo3)
+{
+ OUString sResource = frm::ResourceManager::loadString(pResourceId);
+ OSL_ENSURE( !sResource.isEmpty(), "resource not found?" );
+
+ OUString sString = sResource.replaceAll( "$1", rInfo1 );
+ sString = sString.replaceAll( "$2", rInfo2 );
+ sString = sString.replaceAll( "$3", rInfo3 );
+ return sString;
+}
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/resourcehelper.hxx b/forms/source/xforms/resourcehelper.hxx
new file mode 100644
index 0000000000..1453a7e4ca
--- /dev/null
+++ b/forms/source/xforms/resourcehelper.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+#include <unotools/resmgr.hxx>
+
+namespace xforms
+{
+ /// get a resource string for the current language
+ OUString getResource(TranslateId);
+
+ // overloaded: get a resource string, and substitute parameters
+ OUString getResource(TranslateId, std::u16string_view);
+ OUString getResource(TranslateId, std::u16string_view,
+ std::u16string_view);
+ OUString getResource(TranslateId, std::u16string_view,
+ std::u16string_view,
+ std::u16string_view);
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission.cxx b/forms/source/xforms/submission.cxx
new file mode 100644
index 0000000000..e0d312aa63
--- /dev/null
+++ b/forms/source/xforms/submission.cxx
@@ -0,0 +1,616 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "submission.hxx"
+
+#include "model.hxx"
+#include "binding.hxx"
+#include "mip.hxx"
+#include "evaluationcontext.hxx"
+#include "submission/submission_put.hxx"
+#include "submission/submission_post.hxx"
+#include "submission/submission_get.hxx"
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <com/sun/star/task/XInteractionContinuation.hpp>
+#include <com/sun/star/xforms/InvalidDataOnSubmitException.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/interaction.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <frm_resource.hxx>
+#include <strings.hrc>
+#include <memory>
+#include <string_view>
+
+using com::sun::star::util::VetoException;
+using com::sun::star::form::submission::XSubmissionVetoListener;
+using com::sun::star::lang::WrappedTargetException;
+using com::sun::star::lang::NoSupportException;
+using com::sun::star::task::XInteractionHandler;
+using com::sun::star::xforms::XModel;
+using com::sun::star::xforms::InvalidDataOnSubmitException;
+using com::sun::star::xml::xpath::XXPathObject;
+using com::sun::star::frame::XFrame;
+using xforms::Submission;
+using xforms::Model;
+using xforms::MIP;
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xml::dom;
+
+Submission::Submission() :
+ mbIndent(),
+ mbOmitXmlDeclaration(),
+ mbStandalone(),
+ msReplace( "none" )
+{
+ initializePropertySet();
+}
+
+Submission::~Submission() noexcept
+{
+}
+
+
+void Submission::setModel( const Reference<XModel>& xModel )
+{
+ mxModel = dynamic_cast<Model*>(xModel.get());
+ assert(bool(mxModel)==bool(xModel) && "we only support an instance of Model here");
+}
+
+
+void Submission::setID( const OUString& sID )
+{
+ msID = sID;
+}
+
+
+void Submission::setBind( const OUString& sBind )
+{
+ msBind = sBind;
+}
+
+OUString Submission::getRef() const
+{
+ return maRef.getExpression();
+}
+
+void Submission::setRef( const OUString& sRef )
+{
+ maRef.setExpression( sRef );
+}
+
+
+void Submission::setAction( const OUString& sAction )
+{
+ msAction = sAction;
+}
+
+
+void Submission::setMethod( const OUString& sMethod )
+{
+ msMethod = sMethod;
+}
+
+
+void Submission::setVersion( const OUString& sVersion )
+{
+ msVersion = sVersion;
+}
+
+
+void Submission::setIndent( bool bIndent )
+{
+ mbIndent = bIndent;
+}
+
+
+void Submission::setMediaType( const OUString& sMediaType )
+{
+ msMediaType = sMediaType;
+}
+
+
+void Submission::setEncoding( const OUString& sEncoding )
+{
+ msEncoding = sEncoding;
+}
+
+
+void Submission::setOmitXmlDeclaration( bool bOmitXmlDeclaration )
+{
+ mbOmitXmlDeclaration = bOmitXmlDeclaration;
+}
+
+
+void Submission::setStandalone( bool bStandalone )
+{
+ mbStandalone = bStandalone;
+}
+
+
+void Submission::setCDataSectionElement( const OUString& sCDataSectionElement )
+{
+ msCDataSectionElement = sCDataSectionElement;
+}
+
+
+void Submission::setReplace( const OUString& sReplace )
+{
+ msReplace = sReplace;
+}
+
+
+void Submission::setSeparator( const OUString& sSeparator )
+{
+ msSeparator = sSeparator;
+}
+
+
+void Submission::setIncludeNamespacePrefixes( const Sequence< OUString >& rIncludeNamespacePrefixes )
+{
+ msIncludeNamespacePrefixes = rIncludeNamespacePrefixes;
+}
+
+bool Submission::doSubmit( const Reference< XInteractionHandler >& xHandler )
+{
+ liveCheck();
+
+ // construct XXPathObject for submission doc; use bind in preference of ref
+ EvaluationContext aEvalContext;
+ ComputedExpression aExpression;
+ if( !msBind.isEmpty() )
+ {
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>( mxModel->getBinding(msBind) );
+ if( pBinding != nullptr )
+ {
+ aExpression.setExpression( pBinding->getBindingExpression() );
+ aEvalContext = pBinding->getEvaluationContext();
+ }
+ // TODO: else: illegal binding name -> raise error
+ }
+ else if( !maRef.getExpression().isEmpty() )
+ {
+ aExpression.setExpression( maRef.getExpression() );
+ aEvalContext = mxModel->getEvaluationContext();
+ }
+ else
+ {
+ aExpression.setExpression( "/" );
+ aEvalContext = mxModel->getEvaluationContext();
+ }
+ aExpression.evaluate( aEvalContext );
+ Reference<XXPathObject> xResult = aExpression.getXPath();
+ OSL_ENSURE( xResult.is(), "no result?" );
+
+ // early out if we have not obtained any result
+ if( ! xResult.is() )
+ return false;
+
+
+ // Reference< XNodeList > aList = xResult->getNodeList();
+ OUString aMethod = getMethod();
+
+ // strip whitespace-only text node for get submission
+ Reference< XDocumentFragment > aFragment = createSubmissionDocument(
+ xResult, aMethod.equalsIgnoreAsciiCase("get"));
+
+ // submit result; set encoding, etc.
+ std::unique_ptr<CSubmission> xSubmission;
+ if (aMethod.equalsIgnoreAsciiCase("PUT"))
+ xSubmission.reset(new CSubmissionPut( getAction(), aFragment));
+ else if (aMethod.equalsIgnoreAsciiCase("post"))
+ xSubmission.reset(new CSubmissionPost( getAction(), aFragment));
+ else if (aMethod.equalsIgnoreAsciiCase("get"))
+ xSubmission.reset(new CSubmissionGet( getAction(), aFragment));
+ else
+ {
+ OSL_FAIL("Unsupported xforms submission method");
+ return false;
+ }
+
+ const INetURLObject& rURLObject = xSubmission->GetURLObject();
+ INetProtocol eProtocol = rURLObject.GetProtocol();
+ // tdf#154337 continue to allow submitting to http[s]: without further
+ // interaction. Don't allow for other protocols, except for file:
+ // where the user has to agree first.
+ if (eProtocol != INetProtocol::Http && eProtocol != INetProtocol::Https)
+ {
+ if (eProtocol != INetProtocol::File)
+ return false;
+ else
+ {
+ Reference<css::form::runtime::XFormController> xFormController(xHandler, UNO_QUERY);
+ Reference<css::awt::XControl> xContainerControl(xFormController ? xFormController->getContainer() : nullptr, UNO_QUERY);
+ Reference<css::awt::XWindow> xParent(xContainerControl ? xContainerControl->getPeer() : nullptr, UNO_QUERY);
+
+ OUString aFileName(rURLObject.getFSysPath(FSysStyle::Detect));
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(Application::GetFrameWeld(xParent),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ frm::ResourceManager::loadString(RID_STR_XFORMS_WARN_TARGET_IS_FILE).replaceFirst("$", aFileName)));
+ xQueryBox->set_default_response(RET_NO);
+
+ if (xQueryBox->run() != RET_YES)
+ return false;
+ }
+ }
+
+ CSubmission::SubmissionResult aResult = xSubmission->submit( xHandler );
+
+ if (aResult == CSubmission::SUCCESS)
+ {
+ Reference< XDocument > aInstanceDoc = getInstanceDocument(xResult);
+ aResult = xSubmission->replace(getReplace(), aInstanceDoc, Reference< XFrame >());
+ }
+
+ return ( aResult == CSubmission::SUCCESS );
+}
+
+void Submission::liveCheck()
+{
+ bool bValid = mxModel.is();
+
+ if( ! bValid )
+ throw RuntimeException();
+}
+
+css::uno::Reference<XModel> Submission::getModel() const
+{
+ return mxModel;
+}
+
+
+// Property-Set implementation
+
+
+#define HANDLE_ID 0
+#define HANDLE_Bind 1
+#define HANDLE_Ref 2
+#define HANDLE_Action 3
+#define HANDLE_Method 4
+#define HANDLE_Version 5
+#define HANDLE_Indent 6
+#define HANDLE_MediaType 7
+#define HANDLE_Encoding 8
+#define HANDLE_OmitXmlDeclaration 9
+#define HANDLE_Standalone 10
+#define HANDLE_CDataSectionElement 11
+#define HANDLE_Replace 12
+#define HANDLE_Separator 13
+#define HANDLE_IncludeNamespacePrefixes 14
+#define HANDLE_Model 15
+
+void Submission::initializePropertySet()
+{
+ registerProperty( css::beans::Property("ID", HANDLE_ID, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setID, &Submission::getID) );
+
+ registerProperty( css::beans::Property("Bind", HANDLE_Bind, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setBind, &Submission::getBind) );
+
+ registerProperty( css::beans::Property("Ref", HANDLE_Ref, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setRef, &Submission::getRef) );
+
+ registerProperty( css::beans::Property("Action", HANDLE_Action, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setAction, &Submission::getAction) );
+
+ registerProperty( css::beans::Property("Method", HANDLE_Method, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setMethod, &Submission::getMethod) );
+
+ registerProperty( css::beans::Property("Version", HANDLE_Version, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setVersion, &Submission::getVersion) );
+
+ registerProperty( css::beans::Property("Indent", HANDLE_Indent, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND ),
+ new BooleanPropertyAccessor< Submission >(this, &Submission::setIndent, &Submission::getIndent));
+
+ registerProperty( css::beans::Property("MediaType", HANDLE_MediaType, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setMediaType, &Submission::getMediaType) );
+
+ registerProperty( css::beans::Property("Encoding", HANDLE_Encoding, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setEncoding, &Submission::getEncoding) );
+
+ registerProperty( css::beans::Property("OmitXmlDeclaration", HANDLE_OmitXmlDeclaration, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND ),
+ new BooleanPropertyAccessor< Submission >(this, &Submission::setOmitXmlDeclaration, &Submission::getOmitXmlDeclaration));
+
+ registerProperty( css::beans::Property("Standalone", HANDLE_Standalone, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND ),
+ new BooleanPropertyAccessor< Submission >(this, &Submission::setStandalone, &Submission::getStandalone));
+
+ registerProperty( css::beans::Property("CDataSectionElement", HANDLE_CDataSectionElement, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setCDataSectionElement, &Submission::getCDataSectionElement) );
+
+ registerProperty( css::beans::Property("Replace", HANDLE_Replace, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setReplace, &Submission::getReplace) );
+
+ registerProperty( css::beans::Property("Separator", HANDLE_Separator, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setSeparator, &Submission::getSeparator) );
+
+ registerProperty( css::beans::Property("IncludeNamespacePrefixes", HANDLE_IncludeNamespacePrefixes, cppu::UnoType<Sequence<OUString>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, Sequence<OUString> >(this, &Submission::setIncludeNamespacePrefixes, &Submission::getIncludeNamespacePrefixes) );
+
+ registerProperty( css::beans::Property("Model", HANDLE_Model, cppu::UnoType<Reference<XModel>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, Reference<XModel> >(this, &Submission::setModel, &Submission::getModel) );
+
+ initializePropertyValueCache( HANDLE_Indent );
+ initializePropertyValueCache( HANDLE_OmitXmlDeclaration );
+ initializePropertyValueCache( HANDLE_Standalone );
+}
+
+sal_Bool SAL_CALL Submission::convertFastPropertyValue(
+ Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue )
+{
+ if ( nHandle == HANDLE_IncludeNamespacePrefixes )
+ {
+ // for convenience reasons (????), we accept a string which contains
+ // a comma-separated list of namespace prefixes
+ OUString sTokenList;
+ if ( rValue >>= sTokenList )
+ {
+ std::vector< OUString > aPrefixes;
+ sal_Int32 p = 0;
+ while ( p >= 0 )
+ aPrefixes.push_back( sTokenList.getToken( 0, ',', p ) );
+
+ Sequence< OUString > aConvertedPrefixes( aPrefixes.data(), aPrefixes.size() );
+ return PropertySetBase::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, Any( aConvertedPrefixes ) );
+ }
+ }
+
+ return PropertySetBase::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue );
+}
+
+OUString SAL_CALL Submission::getName()
+{
+ return getID();
+}
+
+void SAL_CALL Submission::setName( const OUString& sID )
+{
+ setID( sID );
+}
+
+
+static OUString lcl_message( std::u16string_view rID, std::u16string_view rText )
+{
+ OUString aMessage = OUString::Concat("XForms submission '") + rID + "' failed" + rText + ".";
+ return aMessage;
+}
+
+void SAL_CALL Submission::submitWithInteraction(
+ const Reference<XInteractionHandler>& _rxHandler )
+{
+ // as long as this class is not really threadsafe, we need to copy
+ // the members we're interested in
+ rtl::Reference< Model > xModel( mxModel );
+ OUString sID( msID );
+
+ if ( !xModel.is() || msID.isEmpty() )
+ throw RuntimeException(
+ "This is not a valid submission object.",
+ *this
+ );
+
+ // #i36765# #i47248# warning on submission of illegal data
+ // check for validity (and query user if invalid)
+ bool bValid = xModel->isValid();
+ if( ! bValid )
+ {
+ InvalidDataOnSubmitException aInvalidDataException(
+ lcl_message(sID, u" due to invalid data" ), *this );
+
+ if( _rxHandler.is() )
+ {
+ // laboriously create interaction request
+ rtl::Reference<comphelper::OInteractionRequest> pRequest
+ = new comphelper::OInteractionRequest(
+ Any( aInvalidDataException ) );
+
+ rtl::Reference<comphelper::OInteractionApprove> pContinue
+ = new comphelper::OInteractionApprove();
+ pRequest->addContinuation( pContinue );
+
+ rtl::Reference<comphelper::OInteractionDisapprove> pCancel
+ = new comphelper::OInteractionDisapprove();
+ pRequest->addContinuation( pCancel );
+
+ // ask the handler...
+ _rxHandler->handle( pRequest );
+ OSL_ENSURE( pContinue->wasSelected() || pCancel->wasSelected(),
+ "handler didn't select" );
+
+ // and continue, if user chose 'continue'
+ if( pContinue->wasSelected() )
+ bValid = true;
+ }
+
+ // abort if invalid (and user didn't tell us to continue)
+ if( ! bValid )
+ throw aInvalidDataException;
+ }
+
+ // attempt submission
+ bool bResult = false;
+ try
+ {
+ bResult = doSubmit( _rxHandler );
+ }
+ catch( const VetoException& )
+ {
+ OSL_FAIL( "Model::submit: Hmm. How can a single submission have a veto right?" );
+ // allowed to leave
+ throw;
+ }
+ catch( const Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ // exception caught: re-throw as wrapped target exception
+ throw WrappedTargetException(
+ lcl_message( sID, u" due to exception being thrown" ),
+ *this, anyEx );
+ }
+
+ if( !bResult )
+ {
+ // other failure: throw wrapped target exception, too.
+ throw WrappedTargetException(
+ lcl_message( sID, std::u16string_view() ), *this, Any() );
+ }
+ mxModel->rebuild();
+}
+
+void SAL_CALL Submission::submit( )
+{
+ submitWithInteraction( nullptr );
+}
+
+void SAL_CALL Submission::addSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ )
+{
+ // TODO
+ throw NoSupportException();
+}
+
+void SAL_CALL Submission::removeSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ )
+{
+ // TODO
+ throw NoSupportException();
+}
+
+static bool isIgnorable(const Reference< XNode >& aNode)
+{
+ // ignore whitespace-only textnodes
+ if (aNode->getNodeType() == NodeType_TEXT_NODE)
+ {
+ OUString aTrimmedValue = aNode->getNodeValue().trim();
+ if (aTrimmedValue.isEmpty()) return true;
+ }
+
+ return false;
+}
+
+// recursively copy relevant nodes from A to B
+static void cloneNodes(Model& aModel, const Reference< XNode >& dstParent, const Reference< XNode >& source, bool bRemoveWSNodes)
+{
+ if (!source.is()) return;
+
+ Reference< XNode > cur = source;
+ Reference< XDocument > dstDoc = dstParent->getOwnerDocument();
+ Reference< XNode > imported;
+
+ if (!cur.is())
+ return;
+
+ // is this node relevant?
+ MIP mip = aModel.queryMIP(cur);
+ if(mip.isRelevant() && !(bRemoveWSNodes && isIgnorable(cur)))
+ {
+ imported = dstDoc->importNode(cur, false);
+ imported = dstParent->appendChild(imported);
+ // append source children to new imported parent
+ for( cur = cur->getFirstChild(); cur.is(); cur = cur->getNextSibling() )
+ cloneNodes(aModel, imported, cur, bRemoveWSNodes);
+ }
+}
+Reference< XDocument > Submission::getInstanceDocument(const Reference< XXPathObject >& aObj)
+{
+ using namespace css::xml::xpath;
+ // result
+ Reference< XDocument > aDocument;
+
+ if (aObj->getObjectType() == XPathObjectType_XPATH_NODESET)
+ {
+ Reference< XNodeList > aList = aObj->getNodeList();
+ if (aList->getLength() > 0)
+ aDocument = aList->item(0)->getOwnerDocument();
+ }
+ return aDocument;
+}
+
+Reference< XDocumentFragment > Submission::createSubmissionDocument(const Reference< XXPathObject >& aObj, bool bRemoveWSNodes)
+{
+ using namespace css::xml::xpath;
+ Reference< XDocumentBuilder > aDocBuilder = DocumentBuilder::create(comphelper::getProcessComponentContext());
+ Reference< XDocument > aDocument = aDocBuilder->newDocument();
+ Reference< XDocumentFragment > aFragment = aDocument->createDocumentFragment();
+
+
+ if (aObj->getObjectType() == XPathObjectType_XPATH_NODESET)
+ {
+ Reference< XNodeList > aList = aObj->getNodeList();
+ Reference< XNode > aListItem;
+ for (sal_Int32 i=0; i < aList->getLength(); i++)
+ {
+ aListItem = aList->item(i);
+ if (aListItem->getNodeType()==NodeType_DOCUMENT_NODE)
+ aListItem = (Reference< XDocument >(aListItem, UNO_QUERY))->getDocumentElement();
+ // copy relevant nodes from instance into fragment
+ cloneNodes(*getModelImpl(), aFragment, aListItem, bRemoveWSNodes);
+ }
+ }
+ return aFragment;
+}
+
+// some forwarding: XPropertySet is implemented in our base class,
+// but also available as base of XSubmission
+Reference< css::beans::XPropertySetInfo > SAL_CALL Submission::getPropertySetInfo( )
+{
+ return PropertySetBase::getPropertySetInfo();
+}
+void SAL_CALL Submission::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ PropertySetBase::setPropertyValue( aPropertyName, aValue );
+}
+Any SAL_CALL Submission::getPropertyValue( const OUString& PropertyName )
+{
+ return PropertySetBase::getPropertyValue( PropertyName );
+}
+void SAL_CALL Submission::addPropertyChangeListener( const OUString& aPropertyName, const Reference< css::beans::XPropertyChangeListener >& xListener )
+{
+ PropertySetBase::addPropertyChangeListener( aPropertyName, xListener );
+}
+void SAL_CALL Submission::removePropertyChangeListener( const OUString& aPropertyName, const Reference< css::beans::XPropertyChangeListener >& aListener )
+{
+ PropertySetBase::removePropertyChangeListener( aPropertyName, aListener );
+}
+void SAL_CALL Submission::addVetoableChangeListener( const OUString& PropertyName, const Reference< css::beans::XVetoableChangeListener >& aListener )
+{
+ PropertySetBase::addVetoableChangeListener( PropertyName, aListener );
+}
+void SAL_CALL Submission::removeVetoableChangeListener( const OUString& PropertyName, const Reference< css::beans::XVetoableChangeListener >& aListener )
+{
+ PropertySetBase::removeVetoableChangeListener( PropertyName, aListener );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission.hxx b/forms/source/xforms/submission.hxx
new file mode 100644
index 0000000000..b8f7fb11d8
--- /dev/null
+++ b/forms/source/xforms/submission.hxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include "propertysetbase.hxx"
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+#include <com/sun/star/xml/xpath/XXPathObject.hpp>
+#include <com/sun/star/xforms/XSubmission.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include "computedexpression.hxx"
+
+// forward declaractions
+namespace com::sun::star {
+ namespace xforms { class XModel; }
+ namespace uno { class Any; }
+ namespace beans { class UnknownPropertyException;
+ class PropertyVetoException; }
+ namespace lang { class IllegalArgumentException;
+ class WrappedTargetException; }
+}
+namespace xforms { class Model; }
+
+
+namespace xforms
+{
+
+/** An XForms submission.
+ *
+ * See http://www.w3.org/TR/xforms/ for more information.
+ */
+typedef cppu::ImplInheritanceHelper<
+ PropertySetBase,
+ css::xforms::XSubmission
+> Submission_t;
+
+class Submission : public Submission_t
+{
+ // submission properties
+ OUString msID;
+ OUString msBind;
+ ComputedExpression maRef;
+ OUString msAction;
+ OUString msMethod;
+ OUString msVersion;
+ bool mbIndent;
+ OUString msMediaType;
+ OUString msEncoding;
+ bool mbOmitXmlDeclaration;
+ bool mbStandalone;
+ OUString msCDataSectionElement;
+ OUString msReplace;
+ OUString msSeparator;
+ css::uno::Sequence< OUString > msIncludeNamespacePrefixes;
+
+private:
+
+ /// the Model to which this Submission belongs; may be NULL
+ rtl::Reference<Model> mxModel;
+
+ // this will extract the document from the model that will be submitted
+ css::uno::Reference< css::xml::dom::XDocumentFragment >
+ createSubmissionDocument(const css::uno::Reference< css::xml::xpath::XXPathObject >& aObject,
+ bool bRemoveWSNodes);
+ static css::uno::Reference< css::xml::dom::XDocument >
+ getInstanceDocument(const css::uno::Reference< css::xml::xpath::XXPathObject >& aObject);
+
+public:
+ Submission();
+ virtual ~Submission() noexcept override;
+
+
+ // property methods: get/set value
+
+
+ /// get XForms model
+ css::uno::Reference<css::xforms::XModel> getModel() const;
+
+ /// get the model implementation
+ xforms::Model* getModelImpl() const { return mxModel.get(); }
+
+ /// set XForms model
+ void setModel(
+ const css::uno::Reference<css::xforms::XModel>& );
+
+ OUString getID() const { return msID;} /// get ID for this submission
+ void setID( const OUString& ); /// set ID for this submission
+
+ OUString getBind() const { return msBind;}
+ void setBind( const OUString& );
+
+ OUString getRef() const;
+ void setRef( const OUString& );
+
+ OUString getAction() const { return msAction;}
+ void setAction( const OUString& );
+
+ OUString getMethod() const { return msMethod;}
+ void setMethod( const OUString& );
+
+ OUString getVersion() const { return msVersion;}
+ void setVersion( const OUString& );
+
+ bool getIndent() const { return mbIndent;}
+ void setIndent( bool );
+
+ OUString getMediaType() const { return msMediaType;}
+ void setMediaType( const OUString& );
+
+ OUString getEncoding() const { return msEncoding;}
+ void setEncoding( const OUString& );
+
+ bool getOmitXmlDeclaration() const { return mbOmitXmlDeclaration;}
+ void setOmitXmlDeclaration( bool );
+
+ bool getStandalone() const { return mbStandalone;}
+ void setStandalone( bool );
+
+ OUString getCDataSectionElement() const { return msCDataSectionElement;}
+ void setCDataSectionElement( const OUString& );
+
+ OUString getReplace() const { return msReplace;}
+ void setReplace( const OUString& );
+
+ OUString getSeparator() const { return msSeparator;}
+ void setSeparator( const OUString& );
+
+ css::uno::Sequence< OUString > getIncludeNamespacePrefixes() const { return msIncludeNamespacePrefixes;}
+ void setIncludeNamespacePrefixes( const css::uno::Sequence< OUString >& );
+
+ /** perform the submission
+ * @returns if submission was successful */
+ bool doSubmit( const css::uno::Reference< css::task::XInteractionHandler >& aHandler );
+
+private:
+
+ /// check whether object is live, and throw suitable exception if not
+ /// (to be used be API methods before acting on the object)
+ ///
+ /// @throws css::uno::RuntimeException
+ void liveCheck();
+
+protected:
+
+
+ // XPropertySet & friends:
+ // implement abstract methods from PropertySetHelper
+
+
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(
+ css::uno::Any& rConvertedValue,
+ css::uno::Any& rOldValue,
+ sal_Int32 nHandle,
+ const css::uno::Any& rValue ) override;
+
+private:
+ void initializePropertySet();
+
+
+public:
+
+
+ // XNamed:
+ // get/set name
+
+
+ virtual OUString SAL_CALL getName() override;
+
+ virtual void SAL_CALL setName( const OUString& ) override;
+
+
+ // XSubmission
+
+
+ virtual void SAL_CALL submit( ) override;
+ virtual void SAL_CALL submitWithInteraction(
+ const css::uno::Reference< css::task::XInteractionHandler >& aHandler ) override;
+ virtual void SAL_CALL addSubmissionVetoListener(
+ const css::uno::Reference< css::form::submission::XSubmissionVetoListener >& listener ) override;
+ virtual void SAL_CALL removeSubmissionVetoListener(
+ const css::uno::Reference< css::form::submission::XSubmissionVetoListener >& listener ) override;
+
+
+ // XPropertySet
+ // (need to disambiguate this)
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+};
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/replace.cxx b/forms/source/xforms/submission/replace.cxx
new file mode 100644
index 0000000000..ca91176c4c
--- /dev/null
+++ b/forms/source/xforms/submission/replace.cxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "submission.hxx"
+#include "serialization_app_xml.hxx"
+
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::task;
+using namespace com::sun::star::xml::dom;
+
+CSubmission::SubmissionResult CSubmission::replace(std::u16string_view aReplace, const Reference<XDocument>& aDocument, const Reference<XFrame>& aFrame)
+{
+ if (!m_aResultStream.is())
+ return CSubmission::UNKNOWN_ERROR;
+
+ try {
+ Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+ if (o3tl::equalsIgnoreAsciiCase(aReplace, u"all")
+ || o3tl::equalsIgnoreAsciiCase(aReplace, u"document")) {
+ Reference< XComponentLoader > xLoader;
+ if (aFrame.is())
+ xLoader.set(aFrame, UNO_QUERY);
+
+ if (!xLoader.is())
+ xLoader.set( Desktop::create(xContext), UNO_QUERY_THROW);
+
+ // open the stream from the result...
+ // build media descriptor
+ Sequence< PropertyValue > descriptor{
+ PropertyValue("InputStream",
+ -1, Any(m_aResultStream), PropertyState_DIRECT_VALUE),
+ PropertyValue("ReadOnly",
+ -1, Any(true), PropertyState_DIRECT_VALUE)
+ };
+
+ OUString aURL = m_aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ xLoader->loadComponentFromURL(aURL, "_default", FrameSearchFlag::ALL, descriptor);
+
+ return CSubmission::SUCCESS;
+
+ } else if (o3tl::equalsIgnoreAsciiCase(aReplace, u"instance")) {
+ if (aDocument.is()) {
+ // parse the result stream into a new document
+ Reference< XDocumentBuilder > xBuilder(DocumentBuilder::create(xContext));
+ Reference< XDocument > aNewDocument = xBuilder->parse(m_aResultStream);
+
+ if (aNewDocument.is()) {
+ // and replace the content of the current instance
+ Reference< XElement > oldRoot = aDocument->getDocumentElement();
+ Reference< XElement > newRoot = aNewDocument->getDocumentElement();
+
+ Reference< XNode > aImportedNode = aDocument->importNode(newRoot, true);
+ aDocument->replaceChild(aImportedNode, oldRoot);
+ return CSubmission::SUCCESS;
+ } else {
+ return CSubmission::UNKNOWN_ERROR;
+ }
+ } else {
+ // nothing to replace
+ return CSubmission::UNKNOWN_ERROR;
+ }
+ } else if (o3tl::equalsIgnoreAsciiCase(aReplace, u"none")) {
+ // do nothing \o/
+ return CSubmission::SUCCESS;
+ }
+ } catch (const Exception&) {
+ TOOLS_WARN_EXCEPTION( "forms.xforms", "Exception during replace");
+ }
+ return CSubmission::UNKNOWN_ERROR;
+}
+
+::std::unique_ptr< CSerialization > CSubmission::createSerialization(const Reference< XInteractionHandler >& _xHandler,Reference<XCommandEnvironment>& _rOutEnv)
+{
+ // PUT always uses application/xml
+ ::std::unique_ptr< CSerialization > apSerialization(new CSerializationAppXML());
+ apSerialization->setSource(m_aFragment);
+ apSerialization->serialize();
+
+ // create a commandEnvironment and use the default interaction handler
+ rtl::Reference<CCommandEnvironmentHelper> pHelper = new CCommandEnvironmentHelper;
+ if( _xHandler.is() )
+ pHelper->m_aInteractionHandler = _xHandler;
+ else
+ pHelper->m_aInteractionHandler.set(
+ InteractionHandler::createWithParent(m_xContext, nullptr), UNO_QUERY_THROW);
+
+ rtl::Reference<CProgressHandlerHelper> pProgressHelper = new CProgressHandlerHelper;
+ pHelper->m_aProgressHandler.set(pProgressHelper);
+
+ // UCB has ownership of environment...
+ _rOutEnv = pHelper;
+ return apSerialization;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization.hxx b/forms/source/xforms/submission/serialization.hxx
new file mode 100644
index 0000000000..19f072b0bc
--- /dev/null
+++ b/forms/source/xforms/submission/serialization.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+
+/**
+Serialize an XObject
+*/
+
+class CSerialization
+{
+protected:
+ css::uno::Reference<css::xml::dom::XDocumentFragment> m_aFragment;
+
+public:
+ virtual ~CSerialization() {}
+
+ /**
+ sets the XObject that is to serialized
+ */
+ void setSource(const css::uno::Reference<css::xml::dom::XDocumentFragment>& aFragment)
+ {
+ m_aFragment = aFragment;
+ }
+
+ /**
+ start the serialization process
+ */
+ virtual void serialize() = 0;
+
+ /**
+ get the serialized bytes.
+ reads up to buffer->getLength() bytes and returns the number of
+ bytes read.
+ returns -1 on error
+ */
+ virtual css::uno::Reference<css::io::XInputStream> getInputStream() = 0;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization_app_xml.cxx b/forms/source/xforms/submission/serialization_app_xml.cxx
new file mode 100644
index 0000000000..0e34f3d418
--- /dev/null
+++ b/forms/source/xforms/submission/serialization_app_xml.cxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "serialization_app_xml.hxx"
+
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+
+#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/processfactory.hxx>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::xml::dom::DocumentBuilder;
+using ::com::sun::star::xml::dom::XNode;
+using ::com::sun::star::xml::dom::XDocument;
+using ::com::sun::star::xml::sax::XSAXSerializable;
+using ::com::sun::star::beans::StringPair;
+using ::com::sun::star::xml::dom::NodeType_DOCUMENT_NODE;
+using ::com::sun::star::xml::dom::NodeType_ELEMENT_NODE;
+using ::com::sun::star::xml::dom::XDocumentBuilder;
+using ::com::sun::star::xml::sax::Writer;
+using ::com::sun::star::xml::sax::XDocumentHandler;
+
+CSerializationAppXML::CSerializationAppXML()
+ : m_xBuffer(css::io::Pipe::create(comphelper::getProcessComponentContext()))
+{
+}
+
+Reference< css::io::XInputStream >
+CSerializationAppXML::getInputStream()
+{
+ // The pipes output is provided through it's
+ // XOutputStream interface aspect
+ return m_xBuffer;
+}
+
+void
+CSerializationAppXML::serialize_node(const Reference< XNode >& rNode)
+{
+ try
+ {
+ Reference< XSAXSerializable > xSerializer( rNode, UNO_QUERY );
+ if ( !xSerializer.is() )
+ {
+ // ensure we have a "real" node
+ Reference< XNode > xNode = rNode;
+ if ( xNode->getNodeType() == NodeType_DOCUMENT_NODE )
+ {
+ Reference< XDocument > const xDoc( xNode, UNO_QUERY_THROW );
+ xNode.set( xDoc->getDocumentElement(), UNO_QUERY_THROW );
+ }
+ ENSURE_OR_RETURN_VOID( xNode->getNodeType() == NodeType_ELEMENT_NODE,
+ "CSerializationAppXML::serialize_node: invalid node type!" );
+
+ // create a new document
+ Reference< XDocumentBuilder > const xDocBuilder = DocumentBuilder::create( comphelper::getProcessComponentContext() );
+ Reference< XDocument > const xDocument( xDocBuilder->newDocument(), UNO_SET_THROW );
+
+ // copy the to-be-serialized node
+ Reference< XNode > const xImportedNode( xDocument->importNode( xNode, true ), UNO_SET_THROW );
+ xDocument->appendChild( xImportedNode );
+
+ // ask the doc for the serializer
+ xSerializer.set( xDocument, UNO_QUERY );
+ }
+
+ ENSURE_OR_RETURN_VOID( xSerializer.is(),
+ "CSerializationAppXML::serialize_node: no serialization access to the node/document!" );
+
+ // create a SAXWriter to take the serialization events, and connect it to our pipe
+ Reference< css::xml::sax::XWriter > const xSaxWriter = Writer::create( comphelper::getProcessComponentContext() );
+ xSaxWriter->setOutputStream( Reference< css::io::XOutputStream >( m_xBuffer, UNO_QUERY_THROW) );
+
+ // do the serialization
+ xSerializer->serialize( Reference< XDocumentHandler >(xSaxWriter, UNO_QUERY_THROW), Sequence< StringPair >() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.xforms");
+ }
+}
+
+void
+CSerializationAppXML::serialize()
+{
+ if (!m_aFragment.is()) return;
+
+ Reference< XNode > cur = m_aFragment->getFirstChild();
+ while (cur.is())
+ {
+ serialize_node(cur);
+ cur = cur->getNextSibling();
+ }
+ m_xBuffer->closeOutput();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization_app_xml.hxx b/forms/source/xforms/submission/serialization_app_xml.hxx
new file mode 100644
index 0000000000..51fb1eb543
--- /dev/null
+++ b/forms/source/xforms/submission/serialization_app_xml.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/io/XPipe.hpp>
+
+#include "serialization.hxx"
+
+class CSerializationAppXML : public CSerialization
+{
+private:
+ css::uno::Reference<css::io::XPipe> m_xBuffer;
+
+ void serialize_node(const css::uno::Reference<css::xml::dom::XNode>& aNode);
+
+public:
+ CSerializationAppXML();
+
+ virtual void serialize() override;
+ virtual css::uno::Reference<css::io::XInputStream> getInputStream() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization_urlencoded.cxx b/forms/source/xforms/submission/serialization_urlencoded.cxx
new file mode 100644
index 0000000000..22a625ccb4
--- /dev/null
+++ b/forms/source/xforms/submission/serialization_urlencoded.cxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/xml/xpath/XPathObjectType.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XText.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <rtl/character.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/strbuf.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <stdio.h>
+
+#include "serialization_urlencoded.hxx"
+
+using namespace css::uno;
+using namespace css::io;
+using namespace css::xml::xpath;
+using namespace css::xml::dom;
+
+CSerializationURLEncoded::CSerializationURLEncoded()
+ : m_aPipe(Pipe::create(comphelper::getProcessComponentContext()))
+{
+}
+
+
+/*
+ rfc2396
+ reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ "$" | ","
+ mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+ unreserved = alphanum | mark
+*/
+bool CSerializationURLEncoded::is_unreserved(char c)
+{
+ if (rtl::isAsciiAlphanumeric(static_cast<unsigned char>(c)))
+ return true;
+ switch (c) {
+ case '-':
+ case '_':
+ case '.':
+ case '!':
+ case '~':
+ case '*':
+ case '\'':
+ case '(':
+ case ')':
+ return true;
+ }
+ return false;
+}
+void CSerializationURLEncoded::encode_and_append(
+ std::u16string_view aString, OStringBuffer& aBuffer)
+{
+ OString utf8String = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
+ const sal_uInt8 *pString = reinterpret_cast< const sal_uInt8 * >( utf8String.getStr() );
+ char tmpChar[4];
+
+ while( *pString != 0)
+ {
+ if( *pString < 0x80 )
+ {
+ if ( is_unreserved(*pString) ) {
+ aBuffer.append(char(*pString));
+ } else if (*pString == 0x20) {
+ aBuffer.append('+');
+ } else if (*pString == 0x0d && *(pString+1) == 0x0a) {
+ aBuffer.append("%0D%0A");
+ pString++;
+ } else if (*pString == 0x0a) {
+ aBuffer.append("%0D%0A");
+ } else {
+ snprintf(tmpChar, 4, "%%%X", *pString % 0x100);
+ aBuffer.append(tmpChar);
+ }
+ } else {
+ snprintf(tmpChar, 4, "%%%X", *pString % 0x100);
+ aBuffer.append(tmpChar);
+ while (*pString >= 0x80) {
+ // continuation...
+ pString++;
+ snprintf(tmpChar, 4, "%%%X", *pString % 0x100);
+ aBuffer.append(tmpChar);
+ }
+ }
+ pString++;
+ }
+}
+
+void CSerializationURLEncoded::serialize_node(const Reference< XNode >& aNode)
+{
+ // serialize recursive
+ // every element node E that has a text child T will be serialized in document order
+ // <E1>T1<E2>T2</E2></E1><E3>T3</E3> -> E1=T2&E2=T2&E3=T3 (En := local name)
+
+ // this node
+ Reference< XNodeList > aChildList = aNode->getChildNodes();
+ Reference< XNode > aChild;
+ // is this an element node?
+ if (aNode->getNodeType() == NodeType_ELEMENT_NODE)
+ {
+ OUString aName = aNode->getNodeName();
+ // find any text children
+ OUStringBuffer aValue;
+ Reference< XText > aText;
+ for(sal_Int32 i=0; i < aChildList->getLength(); i++)
+ {
+ aChild = aChildList->item(i);
+ if (aChild->getNodeType() == NodeType_TEXT_NODE)
+ {
+ aText.set(aChild, UNO_QUERY);
+ aValue.append(aText->getData());
+ }
+ }
+
+ // found anything?
+ if (!aValue.isEmpty())
+ {
+ OUString aUnencValue = aValue.makeStringAndClear();
+ OStringBuffer aEncodedBuffer;
+ encode_and_append(aName, aEncodedBuffer);
+ aEncodedBuffer.append("=");
+ encode_and_append(aUnencValue, aEncodedBuffer);
+ aEncodedBuffer.append("&");
+ sal_Int8 const *pData = reinterpret_cast<sal_Int8 const *>(aEncodedBuffer.getStr());
+ Sequence< sal_Int8 > sData(pData, aEncodedBuffer.getLength());
+ m_aPipe->writeBytes(sData);
+ }
+ }
+
+ // element children...
+ for(sal_Int32 i=0; i < aChildList->getLength(); i++)
+ {
+ aChild = aChildList->item(i);
+ // if this is an element node, it might be a candidate for serialization
+ if (aChild.is() && aChild->getNodeType() == NodeType_ELEMENT_NODE)
+ serialize_node(aChild);
+ }
+}
+
+void CSerializationURLEncoded::serialize()
+{
+
+ // output stream to the pipe buffer
+
+ css::uno::Reference< css::xml::dom::XNode > cur = m_aFragment->getFirstChild();
+ while (cur.is())
+ {
+ serialize_node(cur);
+ cur = cur->getNextSibling();
+ }
+ m_aPipe->closeOutput();
+}
+
+Reference< XInputStream > CSerializationURLEncoded::getInputStream()
+{
+ return m_aPipe;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization_urlencoded.hxx b/forms/source/xforms/submission/serialization_urlencoded.hxx
new file mode 100644
index 0000000000..2ae91c1f18
--- /dev/null
+++ b/forms/source/xforms/submission/serialization_urlencoded.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/io/XPipe.hpp>
+
+#include <rtl/strbuf.hxx>
+
+#include "serialization.hxx"
+
+class CSerializationURLEncoded : public CSerialization
+{
+private:
+ css::uno::Reference<css::io::XPipe> m_aPipe;
+
+ static bool is_unreserved(char);
+ static void encode_and_append(std::u16string_view aString, OStringBuffer& aBuffer);
+ void serialize_node(const css::uno::Reference<css::xml::dom::XNode>& aNode);
+
+public:
+ CSerializationURLEncoded();
+ virtual void serialize() override;
+ virtual css::uno::Reference<css::io::XInputStream> getInputStream() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission.hxx b/forms/source/xforms/submission/submission.hxx
new file mode 100644
index 0000000000..26a2514c82
--- /dev/null
+++ b/forms/source/xforms/submission/submission.hxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/urlobj.hxx>
+#include <osl/conditn.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+
+#include <com/sun/star/task/XInteractionHandler.hpp>
+
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include "serialization.hxx"
+
+#include <memory>
+#include <mutex>
+#include <utility>
+
+class CSubmissionPut;
+class CSubmissionPost;
+class CSubmissionGet;
+
+class CCommandEnvironmentHelper final : public cppu::WeakImplHelper< css::ucb::XCommandEnvironment >
+{
+ friend class CSubmissionPut;
+ friend class CSubmissionPost;
+ friend class CSubmissionGet;
+ friend class CSubmission;
+
+ css::uno::Reference< css::task::XInteractionHandler > m_aInteractionHandler;
+ css::uno::Reference< css::ucb::XProgressHandler > m_aProgressHandler;
+
+public:
+ virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL getInteractionHandler() override
+ {
+ return m_aInteractionHandler;
+ }
+ virtual css::uno::Reference< css::ucb::XProgressHandler > SAL_CALL getProgressHandler() override
+ {
+ return m_aProgressHandler;
+ }
+};
+
+class CProgressHandlerHelper final : public cppu::WeakImplHelper< css::ucb::XProgressHandler >
+{
+ friend class CSubmissionPut;
+ friend class CSubmissionPost;
+ friend class CSubmissionGet;
+ osl::Condition m_cFinished;
+ std::mutex m_mLock;
+ sal_Int32 m_count;
+public:
+ CProgressHandlerHelper()
+ : m_count(0)
+ {}
+ virtual void SAL_CALL push( const css::uno::Any& /*aStatus*/) override
+ {
+ std::unique_lock g(m_mLock);
+ m_count++;
+ }
+ virtual void SAL_CALL update(const css::uno::Any& /*aStatus*/) override
+ {
+ }
+ virtual void SAL_CALL pop() override
+ {
+ std::unique_lock g(m_mLock);
+ m_count--;
+ if (m_count == 0)
+ m_cFinished.set();
+ }
+};
+
+class CSubmission
+{
+
+protected:
+ INetURLObject m_aURLObj;
+ css::uno::Reference< css::xml::dom::XDocumentFragment > m_aFragment;
+ css::uno::Reference< css::io::XInputStream > m_aResultStream;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ ::std::unique_ptr< CSerialization > createSerialization(const css::uno::Reference< css::task::XInteractionHandler >& aHandler
+ ,css::uno::Reference<css::ucb::XCommandEnvironment>& _rOutEnv);
+
+public:
+ enum SubmissionResult {
+ SUCCESS,
+ UNKNOWN_ERROR
+ };
+
+ CSubmission(std::u16string_view aURL, css::uno::Reference< css::xml::dom::XDocumentFragment > aFragment)
+ : m_aURLObj(aURL)
+ , m_aFragment(std::move(aFragment))
+ , m_xContext(::comphelper::getProcessComponentContext())
+ {}
+
+ const INetURLObject& GetURLObject() const { return m_aURLObj; }
+
+ virtual ~CSubmission() {}
+
+ virtual SubmissionResult submit(const css::uno::Reference< css::task::XInteractionHandler >& ) = 0;
+
+ SubmissionResult replace(std::u16string_view, const css::uno::Reference< css::xml::dom::XDocument >&, const css::uno::Reference< css::frame::XFrame>&);
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_get.cxx b/forms/source/xforms/submission/submission_get.cxx
new file mode 100644
index 0000000000..1ddcd529ef
--- /dev/null
+++ b/forms/source/xforms/submission/submission_get.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "submission_get.hxx"
+#include "serialization_urlencoded.hxx"
+
+#include <rtl/strbuf.hxx>
+#include <osl/diagnose.h>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace css::uno;
+using namespace css::ucb;
+using namespace css::task;
+using namespace css::io;
+using namespace osl;
+using namespace ucbhelper;
+
+
+CSubmissionGet::CSubmissionGet(std::u16string_view aURL, const css::uno::Reference< css::xml::dom::XDocumentFragment >& aFragment)
+ : CSubmission(aURL, aFragment)
+{
+}
+
+CSubmission::SubmissionResult CSubmissionGet::submit(const css::uno::Reference< css::task::XInteractionHandler >& aInteractionHandler)
+{
+ // GET always uses application/x-www-formurlencoded
+ CSerializationURLEncoded aSerialization;
+ aSerialization.setSource(m_aFragment);
+ aSerialization.serialize();
+
+ css::uno::Reference< XInputStream > aInStream = aSerialization.getInputStream();
+
+ // create a commandEnvironment and use the default interaction handler
+ rtl::Reference<CCommandEnvironmentHelper> pHelper = new CCommandEnvironmentHelper;
+ if( aInteractionHandler.is() )
+ pHelper->m_aInteractionHandler = aInteractionHandler;
+ else
+ pHelper->m_aInteractionHandler.set(
+ css::task::InteractionHandler::createWithParent(m_xContext, nullptr), UNO_QUERY_THROW);
+ rtl::Reference<CProgressHandlerHelper> pProgressHelper = new CProgressHandlerHelper;
+ pHelper->m_aProgressHandler.set(pProgressHelper);
+
+ // UCB has ownership of environment...
+ css::uno::Reference< XCommandEnvironment > aEnvironment(pHelper);
+
+ // append query string to the URL
+ try {
+ OStringBuffer aUTF8QueryURL(OUStringToOString(m_aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ RTL_TEXTENCODING_UTF8));
+ OStringBuffer aQueryString;
+
+ const sal_Int32 size = 1024;
+ sal_Int32 n = 0;
+ Sequence< sal_Int8 > aByteBuffer(size);
+ while ((n = aInStream->readSomeBytes(aByteBuffer, size-1)) != 0)
+ aQueryString.append(reinterpret_cast<char const *>(aByteBuffer.getConstArray()), n);
+ if (!aQueryString.isEmpty() && m_aURLObj.GetProtocol() != INetProtocol::File)
+ {
+ aUTF8QueryURL.append("?" + aQueryString);
+ }
+ OUString aQueryURL = OStringToOUString(aUTF8QueryURL, RTL_TEXTENCODING_UTF8);
+ ucbhelper::Content aContent(aQueryURL, aEnvironment, m_xContext);
+ // get reply
+ try {
+ m_aResultStream = aContent.openStream();
+ } catch (const Exception&) {
+ OSL_FAIL("Cannot open reply stream from content");
+ }
+ } catch (const Exception&)
+ {
+ // XXX
+ TOOLS_WARN_EXCEPTION( "forms.misc", "Exception during UCB operation.");
+ return UNKNOWN_ERROR;
+ }
+
+ return SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_get.hxx b/forms/source/xforms/submission/submission_get.hxx
new file mode 100644
index 0000000000..086fab25b2
--- /dev/null
+++ b/forms/source/xforms/submission/submission_get.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "submission.hxx"
+
+class CSubmissionGet : public CSubmission
+{
+public:
+ CSubmissionGet(std::u16string_view aURL,
+ const css::uno::Reference<css::xml::dom::XDocumentFragment>& aFragment);
+ virtual SubmissionResult
+ submit(const css::uno::Reference<css::task::XInteractionHandler>& aInteractionHandler) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_post.cxx b/forms/source/xforms/submission/submission_post.cxx
new file mode 100644
index 0000000000..14cf3ad2dc
--- /dev/null
+++ b/forms/source/xforms/submission/submission_post.cxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+
+#include "submission_post.hxx"
+
+#include <osl/diagnose.h>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/activedatasink.hxx>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace css::uno;
+using namespace css::ucb;
+using namespace css::task;
+using namespace css::io;
+using namespace osl;
+using namespace ucbhelper;
+
+
+CSubmissionPost::CSubmissionPost(std::u16string_view aURL, const css::uno::Reference< css::xml::dom::XDocumentFragment >& aFragment)
+ : CSubmission(aURL, aFragment)
+{
+}
+
+CSubmission::SubmissionResult CSubmissionPost::submit(const css::uno::Reference< css::task::XInteractionHandler >& aInteractionHandler)
+{
+ // PUT always uses application/xml
+ css::uno::Reference< XCommandEnvironment > aEnvironment;
+ std::unique_ptr< CSerialization > apSerialization(createSerialization(aInteractionHandler,aEnvironment));
+
+ try {
+ ucbhelper::Content aContent(m_aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), aEnvironment, comphelper::getProcessComponentContext());
+
+ // use post command
+ PostCommandArgument2 aPostArgument;
+ aPostArgument.Source = apSerialization->getInputStream();
+ css::uno::Reference< XActiveDataSink > aSink(new ucbhelper::ActiveDataSink);
+ aPostArgument.Sink = aSink;
+ aPostArgument.MediaType = "application/xml";
+ aPostArgument.Referer.clear();
+ Any aCommandArgument;
+ aCommandArgument <<= aPostArgument;
+ aContent.executeCommand( "post", aCommandArgument);
+
+ try {
+ m_aResultStream = aSink->getInputStream();
+ } catch (const Exception&) {
+ OSL_FAIL("Cannot open reply stream from content");
+ }
+ } catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "forms.misc", "Exception during UCB operation.");
+ return UNKNOWN_ERROR;
+ }
+
+ return SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_post.hxx b/forms/source/xforms/submission/submission_post.hxx
new file mode 100644
index 0000000000..0fb63472b1
--- /dev/null
+++ b/forms/source/xforms/submission/submission_post.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "submission.hxx"
+
+class CSubmissionPost : public CSubmission
+{
+public:
+ CSubmissionPost(std::u16string_view aURL, const css::uno::Reference< css::xml::dom::XDocumentFragment >& aFragment);
+ virtual SubmissionResult submit(const css::uno::Reference< css::task::XInteractionHandler >& aInteractionHandler) override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_put.cxx b/forms/source/xforms/submission/submission_put.cxx
new file mode 100644
index 0000000000..d7007f8df5
--- /dev/null
+++ b/forms/source/xforms/submission/submission_put.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+
+#include "submission_put.hxx"
+
+#include <comphelper/processfactory.hxx>
+#include <ucbhelper/content.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace css::uno;
+using namespace css::ucb;
+using namespace css::task;
+using namespace css::io;
+using namespace osl;
+using namespace ucbhelper;
+
+
+CSubmissionPut::CSubmissionPut(std::u16string_view aURL, const css::uno::Reference< css::xml::dom::XDocumentFragment >& aFragment)
+ : CSubmission(aURL, aFragment)
+{
+}
+
+CSubmission::SubmissionResult CSubmissionPut::submit(const css::uno::Reference< css::task::XInteractionHandler >& aInteractionHandler)
+{
+ css::uno::Reference< XCommandEnvironment > aEnvironment;
+ std::unique_ptr< CSerialization > apSerialization(createSerialization(aInteractionHandler,aEnvironment));
+
+ try {
+ ucbhelper::Content aContent(m_aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), aEnvironment, comphelper::getProcessComponentContext());
+
+ // insert serialized data to content -> PUT
+ css::uno::Reference< XInputStream > aInStream = apSerialization->getInputStream();
+ aContent.writeStream(aInStream, true);
+ //aContent.closeStream();
+
+ // no content as a result of put...
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.misc", "Exception during UCB operation." );
+ return UNKNOWN_ERROR;
+ }
+
+
+ return SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_put.hxx b/forms/source/xforms/submission/submission_put.hxx
new file mode 100644
index 0000000000..2dce84307b
--- /dev/null
+++ b/forms/source/xforms/submission/submission_put.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "submission.hxx"
+
+class CSubmissionPut : public CSubmission
+{
+public:
+ CSubmissionPut(std::u16string_view aURL,
+ const css::uno::Reference<css::xml::dom::XDocumentFragment>& aFragment);
+ virtual SubmissionResult
+ submit(const css::uno::Reference<css::task::XInteractionHandler>& aInteractionHandler) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/unohelper.cxx b/forms/source/xforms/unohelper.cxx
new file mode 100644
index 0000000000..8576e5e29e
--- /dev/null
+++ b/forms/source/xforms/unohelper.cxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "unohelper.hxx"
+
+#include <osl/diagnose.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::Exception;
+using com::sun::star::beans::Property;
+using com::sun::star::beans::XPropertySet;
+using com::sun::star::beans::XPropertySetInfo;
+using com::sun::star::beans::PropertyAttribute::READONLY;
+
+void xforms::copy(const Reference<XPropertySet>& xFrom, Reference<XPropertySet> const& xTo)
+{
+ OSL_ENSURE(xFrom.is(), "no source");
+ OSL_ENSURE(xTo.is(), "no target");
+
+ // get property names & infos, and iterate over target properties
+ Sequence<Property> aProperties = xTo->getPropertySetInfo()->getProperties();
+ sal_Int32 nProperties = aProperties.getLength();
+ const Property* pProperties = aProperties.getConstArray();
+ Reference<XPropertySetInfo> xFromInfo = xFrom->getPropertySetInfo();
+ for (sal_Int32 n = 0; n < nProperties; n++)
+ {
+ const OUString& rName = pProperties[n].Name;
+
+ // if both set have the property, copy the value
+ // (catch and ignore exceptions, if any)
+ if (xFromInfo->hasPropertyByName(rName))
+ {
+ try
+ {
+ Property aProperty = xFromInfo->getPropertyByName(rName);
+ if ((aProperty.Attributes & READONLY) == 0)
+ xTo->setPropertyValue(rName, xFrom->getPropertyValue(rName));
+ }
+ catch (const Exception&)
+ {
+ // ignore any errors; we'll copy as good as we can
+ }
+ }
+ // else: no property? then ignore.
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/unohelper.hxx b/forms/source/xforms/unohelper.hxx
new file mode 100644
index 0000000000..5aace2f3cf
--- /dev/null
+++ b/forms/source/xforms/unohelper.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <sal/types.h>
+
+namespace com::sun::star {
+ namespace uno {
+ class XInterface;
+ template<class T> class Reference;
+ }
+ namespace beans {
+ class XPropertySet;
+ }
+}
+
+namespace xforms
+{
+
+/** copy the properties from one PropertySet into the next */
+void copy( const css::uno::Reference<css::beans::XPropertySet>& , css::uno::Reference<css::beans::XPropertySet> const & );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xforms_services.cxx b/forms/source/xforms/xforms_services.cxx
new file mode 100644
index 0000000000..23a1ee400f
--- /dev/null
+++ b/forms/source/xforms/xforms_services.cxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "NameContainer.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class Implementation:
+ public cppu::ImplInheritanceHelper<
+ NameContainer<css::uno::Reference<css::beans::XPropertySet>>,
+ css::lang::XServiceInfo>
+{
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.XForms"; }
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.xforms.XForms"};
+ }
+};
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_form_XForms_get_implementation(uno::XComponentContext*,
+ uno::Sequence<uno::Any> const &)
+{
+ return cppu::acquire(new Implementation);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xformsevent.cxx b/forms/source/xforms/xformsevent.cxx
new file mode 100644
index 0000000000..fb9758f1ff
--- /dev/null
+++ b/forms/source/xforms/xformsevent.cxx
@@ -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 .
+ */
+
+
+#include "xformsevent.hxx"
+
+namespace com::sun::star::xforms {
+
+void SAL_CALL XFormsEventConcrete::initXFormsEvent(const OUString& typeArg,
+ sal_Bool canBubbleArg, sal_Bool cancelableArg)
+{
+ initEvent(typeArg, canBubbleArg, cancelableArg);
+}
+
+OUString SAL_CALL XFormsEventConcrete::getType()
+{
+ return m_eventType;
+}
+
+css::uno::Reference< css::xml::dom::events::XEventTarget > SAL_CALL XFormsEventConcrete::getTarget()
+{
+ return css::uno::Reference< css::xml::dom::events::XEventTarget >();
+}
+
+css::uno::Reference< css::xml::dom::events::XEventTarget > SAL_CALL XFormsEventConcrete::getCurrentTarget()
+{
+ return css::uno::Reference< css::xml::dom::events::XEventTarget >();
+}
+
+css::xml::dom::events::PhaseType SAL_CALL XFormsEventConcrete::getEventPhase()
+{
+ return css::xml::dom::events::PhaseType_CAPTURING_PHASE;
+}
+
+sal_Bool SAL_CALL XFormsEventConcrete::getBubbles()
+{
+ return m_bubbles;
+}
+
+sal_Bool SAL_CALL XFormsEventConcrete::getCancelable()
+{
+ return m_cancelable;
+}
+
+css::util::Time SAL_CALL XFormsEventConcrete::getTimeStamp()
+{
+ return css::util::Time();
+}
+
+void SAL_CALL XFormsEventConcrete::stopPropagation()
+{
+}
+void SAL_CALL XFormsEventConcrete::preventDefault()
+{
+}
+
+void SAL_CALL XFormsEventConcrete::initEvent(const OUString& eventTypeArg, sal_Bool canBubbleArg,
+ sal_Bool cancelableArg)
+{
+ m_eventType = eventTypeArg;
+ m_bubbles = canBubbleArg;
+ m_cancelable = cancelableArg;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xformsevent.hxx b/forms/source/xforms/xformsevent.hxx
new file mode 100644
index 0000000000..2622780be4
--- /dev/null
+++ b/forms/source/xforms/xformsevent.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <sal/types.h>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/xforms/XFormsEvent.hpp>
+#include <com/sun/star/xml/dom/events/XEventTarget.hpp>
+
+namespace com::sun::star::xforms {
+
+class XFormsEventConcrete : public cppu::WeakImplHelper< XFormsEvent > {
+
+ public:
+
+ XFormsEventConcrete()
+ : m_bubbles(false)
+ , m_cancelable(false)
+ {
+ }
+
+ virtual OUString SAL_CALL getType() override;
+ virtual css::uno::Reference< css::xml::dom::events::XEventTarget > SAL_CALL getTarget() override;
+ virtual css::uno::Reference< css::xml::dom::events::XEventTarget > SAL_CALL getCurrentTarget() override;
+ virtual css::xml::dom::events::PhaseType SAL_CALL getEventPhase() override;
+ virtual sal_Bool SAL_CALL getBubbles() override;
+ virtual sal_Bool SAL_CALL getCancelable() override;
+ virtual css::util::Time SAL_CALL getTimeStamp() override;
+ virtual void SAL_CALL stopPropagation() override;
+ virtual void SAL_CALL preventDefault() override;
+
+ virtual void SAL_CALL initXFormsEvent(
+ const OUString& typeArg,
+ sal_Bool canBubbleArg,
+ sal_Bool cancelableArg ) override;
+
+ virtual void SAL_CALL initEvent(
+ const OUString& eventTypeArg,
+ sal_Bool canBubbleArg,
+ sal_Bool cancelableArg) override;
+
+ private:
+
+ OUString m_eventType;
+ bool m_bubbles;
+ bool m_cancelable;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xmlhelper.cxx b/forms/source/xforms/xmlhelper.cxx
new file mode 100644
index 0000000000..75f0f92bd5
--- /dev/null
+++ b/forms/source/xforms/xmlhelper.cxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "xmlhelper.hxx"
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <comphelper/processfactory.hxx>
+
+using com::sun::star::uno::Reference;
+using com::sun::star::container::XNameContainer;
+using com::sun::star::xml::dom::DocumentBuilder;
+using com::sun::star::xml::dom::XDocumentBuilder;
+
+
+// determine valid XML name
+
+
+// character class:
+// 1: NameStartChar
+// 2: NameChar
+// 4: NCNameStartChar
+// 8: NCNameChar
+static sal_uInt8 lcl_getCharClass( sal_Unicode c )
+{
+ sal_uInt8 nClass = 0;
+
+ // NameStartChar
+ if( (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || (c >= 'a' && c <= 'z')
+ || (c >= 0x00C0 && c <= 0x00D6)
+ || (c >= 0x00D8 && c <= 0x00F6)
+ || (c >= 0x00F8 && c <= 0x02FF)
+ || (c >= 0x0370 && c <= 0x037D)
+ || (c >= 0x037F && c <= 0x1FFF)
+ || (c >= 0x200C && c <= 0x200D)
+ || (c >= 0x2070 && c <= 0x218F)
+ || (c >= 0x2C00 && c <= 0x2FEF)
+ || (c >= 0x3001 && c <= 0xD7FF)
+ || (c >= 0xF900 && c <= 0xFDCF)
+ || (c >= 0xFDF0 && c <= 0xFFFD)
+
+ // surrogates
+ || (c >= 0xD800 && c <= 0xDBFF)
+ || (c >= 0xDC00 && c <= 0xDFFF) )
+ {
+ nClass = 15;
+ }
+ else if( c == '-'
+ || c == '.'
+ || (c >= '0' && c <= '9')
+ || (c == 0x00B7)
+ || (c >= 0x0300 && c <= 0x036F)
+ || (c >= 0x203F && c <= 0x2040) )
+ {
+ nClass = 10;
+ }
+ else if( c == ':' )
+ {
+ nClass = 3;
+ }
+
+ return nClass;
+}
+
+bool isValidQName( const OUString& sName,
+ const Reference<XNameContainer>& /*xNamespaces*/ )
+{
+ sal_Int32 nLength = sName.getLength();
+ const sal_Unicode* pName = sName.getStr();
+
+ bool bRet = false;
+ sal_Int32 nColon = 0;
+ if( nLength > 0 )
+ {
+ bRet = ( ( lcl_getCharClass( pName[0] ) & 4 ) != 0 );
+ for( sal_Int32 n = 1; n < nLength; n++ )
+ {
+ sal_uInt8 nClass = lcl_getCharClass( pName[n] );
+ bRet &= ( ( nClass & 2 ) != 0 );
+ if( nClass == 3 )
+ nColon++;
+ }
+ }
+ if( nColon > 1 )
+ bRet = false;
+
+ return bRet;
+}
+
+bool isValidPrefixName( const OUString& sName,
+ const Reference<XNameContainer>& /*xNamespaces*/ )
+{
+ sal_Int32 nLength = sName.getLength();
+ const sal_Unicode* pName = sName.getStr();
+ bool bRet = false;
+
+ if( nLength > 0 )
+ {
+ bRet = ( ( lcl_getCharClass( pName[0] ) & 4 ) != 0 );
+ for( sal_Int32 n = 1; n < nLength; n++ )
+ bRet &= ( ( lcl_getCharClass( pName[n] ) & 8 ) != 0 );
+ }
+
+ return bRet;
+}
+
+Reference<XDocumentBuilder> getDocumentBuilder()
+{
+ Reference<XDocumentBuilder> xBuilder(DocumentBuilder::create(::comphelper::getProcessComponentContext()));
+ return xBuilder;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xmlhelper.hxx b/forms/source/xforms/xmlhelper.hxx
new file mode 100644
index 0000000000..e8995f764d
--- /dev/null
+++ b/forms/source/xforms/xmlhelper.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star {
+ namespace uno { template<typename T> class Reference; }
+ namespace container { class XNameContainer; }
+ namespace xml::dom { class XDocumentBuilder; }
+}
+
+
+bool isValidQName( const OUString& sName,
+ const css::uno::Reference<css::container::XNameContainer>& xNamespaces );
+
+bool isValidPrefixName( const OUString& sName,
+ const css::uno::Reference<css::container::XNameContainer>& xNamespaces );
+
+css::uno::Reference<css::xml::dom::XDocumentBuilder> getDocumentBuilder();
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xpathlib/extension.cxx b/forms/source/xforms/xpathlib/extension.cxx
new file mode 100644
index 0000000000..0af6a3eb17
--- /dev/null
+++ b/forms/source/xforms/xpathlib/extension.cxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/xml/xpath/Libxml2ExtensionHandle.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include "extension.hxx"
+#include "xpathlib.hxx"
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xforms;
+using namespace com::sun::star::xml::xpath;
+using namespace com::sun::star::beans;
+
+Libxml2ExtensionHandle SAL_CALL CLibxml2XFormsExtension::getLibxml2ExtensionHandle()
+{
+ Libxml2ExtensionHandle aHandle;
+ aHandle.functionLookupFunction = reinterpret_cast< sal_Int64 >( &xforms_lookupFunc );
+ aHandle.functionData = reinterpret_cast< sal_Int64 >( this );
+ aHandle.variableLookupFunction = sal_Int64(0);
+ aHandle.variableData = sal_Int64(0);
+ return aHandle;
+}
+
+void SAL_CALL CLibxml2XFormsExtension::initialize(const Sequence< Any >& aSequence)
+{
+ if (aSequence.getLength() == 2
+ && (aSequence[0] >>= m_aModel)
+ && (aSequence[1] >>= m_aContextNode))
+ {
+ return;
+ }
+
+ NamedValue aValue;
+ for (const Any& rArg : aSequence)
+ {
+ if (! (rArg >>= aValue))
+ throw RuntimeException();
+ if ( aValue.Name == "Model" )
+ aValue.Value >>= m_aModel;
+ else if ( aValue.Name == "ContextNode" )
+ aValue.Value >>= m_aContextNode;
+ }
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_xml_xpath_XFormsExtension_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new CLibxml2XFormsExtension());
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xpathlib/extension.hxx b/forms/source/xforms/xpathlib/extension.hxx
new file mode 100644
index 0000000000..267576b746
--- /dev/null
+++ b/forms/source/xforms/xpathlib/extension.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/xml/xpath/XXPathExtension.hpp>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+
+class CLibxml2XFormsExtension
+ : public cppu::WeakImplHelper<css::xml::xpath::XXPathExtension, css::lang::XInitialization>
+{
+private:
+ css::uno::Reference<css::xforms::XModel> m_aModel;
+ css::uno::Reference<css::xml::dom::XNode> m_aContextNode;
+
+public:
+ CLibxml2XFormsExtension() {}
+
+ const css::uno::Reference<css::xforms::XModel>& getModel() const { return m_aModel; }
+ const css::uno::Reference<css::xml::dom::XNode>& getContextNode() const
+ {
+ return m_aContextNode;
+ }
+
+ virtual css::xml::xpath::Libxml2ExtensionHandle SAL_CALL getLibxml2ExtensionHandle() override;
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& aSequence) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xpathlib/xpathlib.cxx b/forms/source/xforms/xpathlib/xpathlib.cxx
new file mode 100644
index 0000000000..ffae5a59ca
--- /dev/null
+++ b/forms/source/xforms/xpathlib/xpathlib.cxx
@@ -0,0 +1,542 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <string.h>
+
+#include <comphelper/servicehelper.hxx>
+#include <o3tl/string_view.hxx>
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+#include <rtl/strbuf.hxx>
+#include <tools/date.hxx>
+#include <tools/time.hxx>
+#include <tools/datetime.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+
+#include <boost/lexical_cast.hpp>
+#include <libxml/xpathInternals.h>
+
+#include "xpathlib.hxx"
+#include "extension.hxx"
+
+// C interface
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::xml::dom;
+using namespace com::sun::star::xforms;
+using namespace com::sun::star::lang;
+
+xmlXPathFunction xforms_lookupFunc(void *, const xmlChar *xname, const xmlChar *)
+{
+
+ const char *name = reinterpret_cast<char const *>(xname);
+ if (strcmp("boolean-from-string", name)==0)
+ return xforms_booleanFromStringFunction;
+ else if ((strcmp("if", name))==0)
+ return xforms_ifFunction;
+ else if ((strcmp("avg", name))==0)
+ return xforms_avgFunction;
+ else if ((strcmp("min", name))==0)
+ return xforms_minFunction;
+ else if ((strcmp("max", name))==0)
+ return xforms_maxFunction;
+ else if ((strcmp("count-non-empty", name))==0)
+ return xforms_countNonEmptyFunction;
+ else if ((strcmp("index", name))==0)
+ return xforms_indexFunction;
+ else if ((strcmp("property", name))==0)
+ return xforms_propertyFunction;
+ else if ((strcmp("now", name))==0)
+ return xforms_nowFunction;
+ else if ((strcmp("days-from-date", name))==0)
+ return xforms_daysFromDateFunction;
+ else if ((strcmp("seconds-from-dateTime", name))==0)
+ return xforms_secondsFromDateTimeFunction;
+ else if ((strcmp("seconds", name))==0)
+ return xforms_secondsFunction;
+ else if ((strcmp("months", name))==0)
+ return xforms_monthsFunction;
+ else if ((strcmp("instance", name))==0)
+ return xforms_instanceFunction;
+ else if ((strcmp("current", name))==0)
+ return xforms_currentFunction;
+ else
+ return nullptr;
+}
+
+// boolean functions
+void xforms_booleanFromStringFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar *pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+ if (aString.equalsIgnoreAsciiCase("true") ||
+ aString.equalsIgnoreAsciiCase("1"))
+ xmlXPathReturnTrue(ctxt);
+ else if (aString.equalsIgnoreAsciiCase("false") ||
+ aString.equalsIgnoreAsciiCase("0"))
+ xmlXPathReturnFalse(ctxt);
+ else
+ XP_ERROR(XPATH_NUMBER_ERROR);
+}
+
+void xforms_ifFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 3) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar *s2 = xmlXPathPopString(ctxt);
+
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ xmlChar *s1 = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ bool aBool = xmlXPathPopBoolean(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+
+ if (aBool)
+ xmlXPathReturnString(ctxt, s1);
+ else
+ xmlXPathReturnString(ctxt, s2);
+
+}
+
+// Number Functions
+void xforms_avgFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // use sum(), div() and count()
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+
+ // save nodeset
+ xmlXPathObjectPtr pObject = valuePop(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ //push back a copy
+ valuePush(ctxt, xmlXPathObjectCopy(pObject));
+ // get the Sum
+ xmlXPathSumFunction(ctxt, 1);
+ double nSum = xmlXPathPopNumber(ctxt);
+ // push a copy once more
+ valuePush(ctxt, xmlXPathObjectCopy(pObject));
+ xmlXPathCountFunction(ctxt, 1);
+ double nCount = xmlXPathPopNumber(ctxt);
+ // push args for div()
+ xmlXPathReturnNumber(ctxt, nSum);
+ xmlXPathReturnNumber(ctxt, nCount);
+ xmlXPathDivValues(ctxt);
+ // the result is now on the ctxt stack
+ xmlXPathFreeObject(pObject);
+}
+
+void xforms_minFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ double nMinimum = 0;
+ for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
+ {
+ double nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
+ if (xmlXPathIsNaN(nNumber))
+ {
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+ return;
+ }
+ if (i == 0)
+ nMinimum = nNumber;
+ else if (nNumber < nMinimum)
+ nMinimum = nNumber;
+ }
+ xmlXPathReturnNumber(ctxt, nMinimum);
+}
+
+void xforms_maxFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ double nMaximum = 0;
+ for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
+ {
+ double nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
+ if (xmlXPathIsNaN(nNumber))
+ {
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+ return;
+ }
+ if (i == 0)
+ nMaximum = nNumber;
+ else if (nNumber > nMaximum)
+ nMaximum = nNumber;
+ }
+ xmlXPathReturnNumber(ctxt, nMaximum);
+}
+void xforms_countNonEmptyFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ sal_Int32 nNotEmpty = 0;
+ for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
+ {
+ const xmlChar *aString = xmlXPathCastNodeToString(xmlXPathNodeSetItem(pNodeSet, i));
+ if (*aString != 0) nNotEmpty++;
+ }
+ xmlXPathReturnNumber(ctxt, nNotEmpty);
+}
+void xforms_indexFunction(xmlXPathParserContextPtr /*ctxt*/, int /*nargs*/)
+{
+ // function index takes a string argument that is the IDREF of a
+ // 'repeat' and returns the current 1-based position of the repeat
+ // index of the identified repeat -- see xforms/9.3.1
+
+ // doc.getElementByID
+ // (...)
+}
+
+// String Functions
+void xforms_propertyFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+ if (aString.equalsIgnoreAsciiCase("version"))
+ xmlXPathReturnString(ctxt, reinterpret_cast<xmlChar *>(const_cast<char *>("1.0")));
+ else if (aString.equalsIgnoreAsciiCase("conformance-level"))
+ xmlXPathReturnString(ctxt, reinterpret_cast<xmlChar *>(const_cast<char *>("conformance")));
+ else
+ xmlXPathReturnEmptyString(ctxt);
+}
+
+// Date and Time Functions
+
+static OString makeDateTimeString (const DateTime& aDateTime)
+{
+ OStringBuffer aDateTimeString;
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetYear()));
+ aDateTimeString.append('-');
+ if (aDateTime.GetMonth()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetMonth()));
+ aDateTimeString.append('-');
+ if (aDateTime.GetDay()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetDay()));
+ aDateTimeString.append('T');
+ if (aDateTime.GetHour()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetHour()));
+ aDateTimeString.append(':');
+ if (aDateTime.GetMin()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetMin()));
+ aDateTimeString.append(':');
+ if (aDateTime.GetSec()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetSec()));
+ aDateTimeString.append('Z');
+
+ return aDateTimeString.makeStringAndClear();
+}
+
+// returns current system date and time in canonical xsd:dateTime
+// format
+void xforms_nowFunction(xmlXPathParserContextPtr ctxt, int /*nargs*/)
+{
+ /*
+ A single lexical representation, which is a subset of the lexical representations
+ allowed by [ISO 8601], is allowed for dateTime. This lexical representation is the
+ [ISO 8601] extended format CCYY-MM-DDThh:mm:ss where "CC" represents the century,
+ "YY" the year, "MM" the month and "DD" the day, preceded by an optional leading "-"
+ sign to indicate a negative number. If the sign is omitted, "+" is assumed. The letter
+ "T" is the date/time separator and "hh", "mm", "ss" represent hour, minute and second
+ respectively.
+ */
+
+ /*
+ 3.2.7.2 Canonical representation
+ The canonical representation for dateTime is defined by prohibiting certain options
+ from the Lexical representation (par.3.2.7.1). Specifically, either the time zone must
+ be omitted or, if present, the time zone must be Coordinated Universal tools::Time (UTC)
+ indicated by a "Z".
+ */
+ OString aDateTimeString;
+ if (std::getenv("STABLE_FIELDS_HACK"))
+ aDateTimeString = makeDateTimeString(DateTime(DateTime::EMPTY));
+ else
+ aDateTimeString = makeDateTimeString(DateTime(DateTime::SYSTEM));
+ xmlChar *pString = static_cast<xmlChar*>(xmlMalloc(aDateTimeString.getLength()+1));
+ strncpy(reinterpret_cast<char*>(pString), aDateTimeString.getStr(), aDateTimeString.getLength());
+ pString[aDateTimeString.getLength()] = 0;
+ xmlXPathReturnString(ctxt, pString);
+}
+
+static bool parseDateTime(std::u16string_view aString, DateTime& aDateTime)
+{
+ // take apart a canonical literal xsd:dateTime string
+ //CCYY-MM-DDThh:mm:ss(Z)
+
+ OUString aDateTimeString( o3tl::trim(aString) );
+
+ // check length
+ if (aDateTimeString.getLength() < 19 || aDateTimeString.getLength() > 20)
+ return false;
+
+ sal_Int32 nIndex = 0;
+ sal_Int32 nYear = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, '-', nIndex));
+ sal_Int32 nMonth = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, '-', nIndex));
+ sal_Int32 nDay = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, 'T', nIndex));
+ sal_Int32 nHour = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, ':', nIndex));
+ sal_Int32 nMinute = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, ':', nIndex));
+ sal_Int32 nSecond = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, 'Z', nIndex));
+
+ Date tmpDate(static_cast<sal_uInt16>(nDay), static_cast<sal_uInt16>(nMonth), static_cast<sal_uInt16>(nYear));
+ tools::Time tmpTime(nHour, nMinute, nSecond);
+ DateTime tmpDateTime(tmpDate, tmpTime);
+ if (aString.rfind('Z') == std::u16string_view::npos)
+ tmpDateTime.ConvertToUTC();
+
+ aDateTime = tmpDateTime;
+
+ return true;
+}
+
+
+void xforms_daysFromDateFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // number of days from 1970-01-01 to supplied xsd:date(Time)
+
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+
+ DateTime aDateTime( DateTime::EMPTY );
+ if (parseDateTime(aString, aDateTime))
+ {
+ Date aReferenceDate(1, 1, 1970);
+ sal_Int32 nDays = aDateTime - aReferenceDate;
+ xmlXPathReturnNumber(ctxt, nDays);
+ }
+ else
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+
+
+}
+
+
+void xforms_secondsFromDateTimeFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // number of seconds from 1970-01-01T00:00:00Z to supplied xsd:date(Time)
+
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+
+ DateTime aDateTime( DateTime::EMPTY );
+
+ if (parseDateTime(aString, aDateTime))
+ {
+ Date aReferenceDate(1, 1, 1970);
+ sal_Int32 nDays = aDateTime - aReferenceDate;
+ sal_Int32 nSeconds = nDays * 24 * 60 * 60;
+ nSeconds += aDateTime.GetHour() * 60 * 60;
+ nSeconds += aDateTime.GetMin() * 60;
+ nSeconds += aDateTime.GetSec();
+ xmlXPathReturnNumber(ctxt, nSeconds);
+ }
+ else
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+
+}
+
+static bool parseDuration(const xmlChar* aString, bool& bNegative, sal_Int32& nYears, sal_Int32& nMonth, sal_Int32& nDays,
+ sal_Int32& nHours, sal_Int32& nMinutes, sal_Int32& nSeconds)
+{
+ bool bTime = false; // in part after T
+ const xmlChar *pString = aString;
+
+ if (pString[0] == '-') {
+ bNegative = true;
+ pString++;
+ }
+
+ if (pString[0] != 'P')
+ {
+ return false;
+ }
+
+ pString++;
+ const xmlChar* pToken = pString;
+ while(pToken[0] != 0)
+ {
+ switch(pToken[0]) {
+ case 'Y':
+ nYears = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'M':
+ if (!bTime)
+ nMonth = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ else
+ nMinutes = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'D':
+ nDays = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'H':
+ nHours = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'S':
+ nSeconds = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'T':
+ bTime = true;
+ pString = ++pToken;
+ break;
+ default:
+ pToken++;
+ }
+ }
+ return true;
+}
+
+void xforms_secondsFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // convert a xsd:duration to seconds
+ // (-)PnYnMnDTnHnMnS
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+
+ bool bNegative = false;
+ sal_Int32 nYears = 0;
+ sal_Int32 nMonths = 0;
+ sal_Int32 nDays = 0;
+ sal_Int32 nHours = 0;
+ sal_Int32 nMinutes = 0;
+ sal_Int32 nSeconds = 0;
+
+ if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
+ {
+ nSeconds += nMinutes*60;
+ nSeconds += nHours*60*60;
+ nSeconds += nDays*24*60*60;
+ // year and month are ignored according to spec
+ if (bNegative)
+ nSeconds = 0 - nSeconds;
+ xmlXPathReturnNumber(ctxt, nSeconds);
+ }
+ else
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+}
+
+void xforms_monthsFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // convert a xsd:duration to seconds
+ // (-)PnYnMnDTnHnMnS
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+
+ bool bNegative = false;
+ sal_Int32 nYears = 0;
+ sal_Int32 nMonths = 0;
+ sal_Int32 nDays = 0;
+ sal_Int32 nHours = 0;
+ sal_Int32 nMinutes = 0;
+ sal_Int32 nSeconds = 0;
+
+ if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
+ {
+ nMonths += nYears*12;
+ // Days, Hours, Minutes and seconds are ignored, see spec
+ if (bNegative)
+ nMonths = 0 - nMonths;
+ xmlXPathReturnNumber(ctxt, nMonths);
+ }
+ else
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+
+}
+
+// Node-set Functions
+void xforms_instanceFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar *pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+
+ Reference< XModel > aModel = static_cast<CLibxml2XFormsExtension*>(ctxt->context->funcLookupData)->getModel();
+ if (aModel.is())
+ {
+ Reference< XDocument > aInstance = aModel->getInstanceDocument(aString);
+ if (aInstance.is())
+ {
+ try {
+ // xmlXPathObjectPtr xmlXPathNewNodeSet (xmlNodePtr val);
+ Reference< XUnoTunnel > aTunnel(aInstance, UNO_QUERY_THROW);
+ xmlNodePtr pNode = comphelper::getSomething_cast<xmlNode>(aTunnel->getSomething(Sequence<sal_Int8>()));
+ xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
+ xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
+ } catch (const RuntimeException&)
+ {
+ xmlXPathReturnEmptyNodeSet(ctxt);
+ }
+ }
+ else
+ xmlXPathReturnEmptyNodeSet(ctxt);
+ }
+ else
+ xmlXPathReturnEmptyNodeSet(ctxt);
+
+}
+
+// Node-set Functions, XForms 1.1
+void xforms_currentFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 0) XP_ERROR(XPATH_INVALID_ARITY);
+
+ Reference< XNode > aNode = static_cast<CLibxml2XFormsExtension*>(ctxt->context->funcLookupData)->getContextNode();
+
+ if (aNode.is())
+ {
+ try {
+ Reference< XUnoTunnel > aTunnel(aNode, UNO_QUERY_THROW);
+ xmlNodePtr pNode = comphelper::getSomething_cast<xmlNode>(aTunnel->getSomething(Sequence<sal_Int8>()));
+ xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
+ xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
+ }
+ catch (const RuntimeException&)
+ {
+ xmlXPathReturnEmptyNodeSet(ctxt);
+ }
+ }
+ else
+ xmlXPathReturnEmptyNodeSet(ctxt);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xpathlib/xpathlib.hxx b/forms/source/xforms/xpathlib/xpathlib.hxx
new file mode 100644
index 0000000000..52a477c97e
--- /dev/null
+++ b/forms/source/xforms/xpathlib/xpathlib.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <libxml/xpath.h>
+
+/*
+ entry functions for libxml xpath engine
+
+*/
+
+/**
+ * xmlXPathParserContext:
+ *
+ * An XPath parser context. It contains pure parsing information,
+ * an xmlXPathContext, and the stack of objects.
+ */
+
+extern "C"
+{
+
+// XForms
+
+// XForms Core Functions
+// boolean functions
+void xforms_booleanFromStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_ifFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// Number Functions
+void xforms_avgFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_minFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_maxFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_countNonEmptyFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_indexFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// String Functions
+void xforms_propertyFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// Date and Time Functions
+void xforms_nowFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_daysFromDateFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_secondsFromDateTimeFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_secondsFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_monthsFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// Node-set Functions
+void xforms_instanceFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// Node-set Functions; XForms 1.1
+void xforms_currentFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// --- lookup ---
+xmlXPathFunction xforms_lookupFunc(void *ctxt, const xmlChar *name, const xmlChar *ns_uri);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/util/frm.component b/forms/util/frm.component
new file mode 100644
index 0000000000..2ccf1831bd
--- /dev/null
+++ b/forms/util/frm.component
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.form.ONavigationBarControl"
+ constructor="com_sun_star_comp_form_ONavigationBarControl_get_implementation">
+ <service name="com.sun.star.form.control.NavigationToolBar"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.form.ONavigationBarModel"
+ constructor="com_sun_star_comp_form_ONavigationBarModel_get_implementation">
+ <service name="com.sun.star.form.FormComponent"/>
+ <service name="com.sun.star.form.FormControlModel"/>
+ <service name="com.sun.star.form.component.NavigationToolBar"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.form.ORichTextControl"
+ constructor="com_sun_star_comp_form_ORichTextControl_get_implementation">
+ <service name="com.sun.star.form.control.RichTextControl"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.forms.FormOperations"
+ constructor="com_sun_star_comp_forms_FormOperations_get_implementation">
+ <service name="com.sun.star.form.runtime.FormOperations"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.forms.ODatabaseForm"
+ constructor="com_sun_star_comp_forms_ODatabaseForm_get_implementation">
+ <service name="com.sun.star.form.FormComponent"/>
+ <service name="com.sun.star.form.FormComponents"/>
+ <service name="com.sun.star.form.component.DataForm"/>
+ <service name="com.sun.star.form.component.Form"/>
+ <service name="com.sun.star.form.component.HTMLForm"/>
+ <service name="stardiv.one.form.component.Form"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.forms.OFilterControl"
+ constructor="com_sun_star_comp_forms_OFilterControl_get_implementation">
+ <service name="com.sun.star.awt.UnoControl"/>
+ <service name="com.sun.star.form.control.FilterControl"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.forms.OFormattedFieldWrapper_ForcedFormatted"
+ constructor="com_sun_star_comp_forms_OFormattedFieldWrapper_ForcedFormatted_get_implementation">
+ <service name="com.sun.star.form.binding.BindableDatabaseFormattedField"/>
+ <service name="com.sun.star.form.component.DatabaseFormattedField"/>
+ <service name="com.sun.star.form.component.FormattedField"/>
+ <service name="stardiv.one.form.component.FormattedField"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.forms.ORichTextModel"
+ constructor="com_sun_star_comp_forms_ORichTextModel_get_implementation">
+ <service name="com.sun.star.form.FormComponent"/>
+ <service name="com.sun.star.form.FormControlModel"/>
+ <service name="com.sun.star.form.component.RichTextControl"/>
+ <service name="com.sun.star.style.CharacterProperties"/>
+ <service name="com.sun.star.style.CharacterPropertiesAsian"/>
+ <service name="com.sun.star.style.CharacterPropertiesComplex"/>
+ <service name="com.sun.star.style.ParagraphProperties"/>
+ <service name="com.sun.star.style.ParagraphPropertiesComplex"/>
+ <service name="com.sun.star.text.TextRange"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.forms.OScrollBarModel"
+ constructor="com_sun_star_comp_forms_OScrollBarModel_get_implementation">
+ <service name="com.sun.star.form.FormComponent"/>
+ <service name="com.sun.star.form.FormControlModel"/>
+ <service name="com.sun.star.form.binding.BindableIntegerValueRange"/>
+ <service name="com.sun.star.form.component.ScrollBar"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.forms.OSpinButtonModel"
+ constructor="com_sun_star_comp_forms_OSpinButtonModel_get_implementation">
+ <service name="com.sun.star.form.FormComponent"/>
+ <service name="com.sun.star.form.FormControlModel"/>
+ <service name="com.sun.star.form.binding.BindableIntegerValueRange"/>
+ <service name="com.sun.star.form.component.SpinButton"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.xml.xpath.XFormsExtension"
+ constructor="com_sun_star_comp_xml_xpath_XFormsExtension_get_implementation">
+ <service name="com.sun.star.xml.xpath.XPathExtension"/>
+ </implementation>
+ <implementation name="com.sun.star.form.ImageProducer"
+ constructor="com_sun_star_form_ImageProducer_get_implementation">
+ <service name="com.sun.star.awt.ImageProducer"/>
+ </implementation>
+ <implementation name="com.sun.star.form.Model"
+ constructor="com_sun_star_form_Model_get_implementation">
+ <service name="com.sun.star.xforms.Model"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OButtonControl"
+ constructor="com_sun_star_form_OButtonControl_get_implementation">
+ <service name="com.sun.star.form.control.CommandButton"/>
+ <service name="stardiv.one.form.control.CommandButton"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OButtonModel"
+ constructor="com_sun_star_form_OButtonModel_get_implementation">
+ <service name="com.sun.star.form.component.CommandButton"/>
+ <service name="stardiv.one.form.component.CommandButton"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OCheckBoxControl"
+ constructor="com_sun_star_form_OCheckBoxControl_get_implementation">
+ <service name="com.sun.star.form.control.CheckBox"/>
+ <service name="stardiv.one.form.control.CheckBox"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OCheckBoxModel"
+ constructor="com_sun_star_form_OCheckBoxModel_get_implementation">
+ <service name="com.sun.star.form.binding.BindableDatabaseCheckBox"/>
+ <service name="com.sun.star.form.component.CheckBox"/>
+ <service name="com.sun.star.form.component.DatabaseCheckBox"/>
+ <service name="stardiv.one.form.component.CheckBox"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OComboBoxControl"
+ constructor="com_sun_star_form_OComboBoxControl_get_implementation">
+ <service name="com.sun.star.form.control.ComboBox"/>
+ <service name="stardiv.one.form.control.ComboBox"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OComboBoxModel"
+ constructor="com_sun_star_form_OComboBoxModel_get_implementation">
+ <service name="com.sun.star.form.binding.BindableDatabaseComboBox"/>
+ <service name="com.sun.star.form.component.ComboBox"/>
+ <service name="com.sun.star.form.component.DatabaseComboBox"/>
+ <service name="stardiv.one.form.component.ComboBox"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OCurrencyControl"
+ constructor="com_sun_star_form_OCurrencyControl_get_implementation">
+ <service name="com.sun.star.form.control.CurrencyField"/>
+ <service name="stardiv.one.form.control.CurrencyField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OCurrencyModel"
+ constructor="com_sun_star_form_OCurrencyModel_get_implementation">
+ <service name="com.sun.star.form.component.CurrencyField"/>
+ <service name="com.sun.star.form.component.DatabaseCurrencyField"/>
+ <service name="stardiv.one.form.component.CurrencyField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.ODateControl"
+ constructor="com_sun_star_form_ODateControl_get_implementation">
+ <service name="com.sun.star.form.control.DateField"/>
+ <service name="stardiv.one.form.control.DateField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.ODateModel"
+ constructor="com_sun_star_form_ODateModel_get_implementation">
+ <service name="com.sun.star.form.component.DatabaseDateField"/>
+ <service name="com.sun.star.form.component.DateField"/>
+ <service name="stardiv.one.form.component.DateField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OEditControl"
+ constructor="com_sun_star_form_OEditControl_get_implementation">
+ <service name="com.sun.star.form.control.TextField"/>
+ <service name="stardiv.one.form.control.Edit"/>
+ <service name="stardiv.one.form.control.TextField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OEditModel"
+ constructor="com_sun_star_form_OEditModel_get_implementation">
+ <service name="com.sun.star.form.binding.BindableDatabaseTextField"/>
+ <service name="com.sun.star.form.component.DatabaseTextField"/>
+ <service name="com.sun.star.form.component.TextField"/>
+ <service name="stardiv.one.form.component.TextField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OFileControlModel"
+ constructor="com_sun_star_form_OFileControlModel_get_implementation">
+ <service name="com.sun.star.form.component.FileControl"/>
+ <service name="stardiv.one.form.component.FileControl"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OFixedTextModel"
+ constructor="com_sun_star_form_OFixedTextModel_get_implementation">
+ <service name="com.sun.star.form.component.FixedText"/>
+ <service name="stardiv.one.form.component.FixedText"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OFormattedControl"
+ constructor="com_sun_star_form_OFormattedControl_get_implementation">
+ <service name="com.sun.star.form.control.FormattedField"/>
+ <service name="stardiv.one.form.control.FormattedField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OFormattedFieldWrapper"
+ constructor="com_sun_star_form_OFormattedFieldWrapper_get_implementation">
+ <service name="stardiv.one.form.component.Edit"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OFormsCollection"
+ constructor="com_sun_star_form_OFormsCollection_get_implementation">
+ <service name="com.sun.star.form.Forms"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OGridControlModel"
+ constructor="com_sun_star_form_OGridControlModel_get_implementation">
+ <service name="com.sun.star.form.component.GridControl"/>
+ <service name="stardiv.one.form.component.Grid"/>
+ <service name="stardiv.one.form.component.GridControl"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OGroupBoxControl"
+ constructor="com_sun_star_form_OGroupBoxControl_get_implementation">
+ <service name="com.sun.star.form.control.GroupBox"/>
+ <service name="stardiv.one.form.control.GroupBox"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OGroupBoxModel"
+ constructor="com_sun_star_form_OGroupBoxModel_get_implementation">
+ <service name="com.sun.star.form.component.GroupBox"/>
+ <service name="stardiv.one.form.component.GroupBox"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OHiddenModel"
+ constructor="com_sun_star_form_OHiddenModel_get_implementation">
+ <service name="com.sun.star.form.component.HiddenControl"/>
+ <service name="stardiv.one.form.component.Hidden"/>
+ <service name="stardiv.one.form.component.HiddenControl"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OImageButtonControl"
+ constructor="com_sun_star_form_OImageButtonControl_get_implementation">
+ <service name="com.sun.star.form.control.ImageButton"/>
+ <service name="stardiv.one.form.control.ImageButton"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OImageButtonModel"
+ constructor="com_sun_star_form_OImageButtonModel_get_implementation">
+ <service name="com.sun.star.form.component.ImageButton"/>
+ <service name="stardiv.one.form.component.ImageButton"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OImageControlControl"
+ constructor="com_sun_star_form_OImageControlControl_get_implementation">
+ <service name="com.sun.star.form.control.ImageControl"/>
+ <service name="stardiv.one.form.control.ImageControl"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OImageControlModel"
+ constructor="com_sun_star_form_OImageControlModel_get_implementation">
+ <service name="com.sun.star.form.component.DatabaseImageControl"/>
+ <service name="stardiv.one.form.component.ImageControl"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OListBoxControl"
+ constructor="com_sun_star_form_OListBoxControl_get_implementation">
+ <service name="com.sun.star.form.control.ListBox"/>
+ <service name="stardiv.one.form.control.ListBox"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OListBoxModel"
+ constructor="com_sun_star_form_OListBoxModel_get_implementation">
+ <service name="com.sun.star.form.binding.BindableDatabaseListBox"/>
+ <service name="com.sun.star.form.component.DatabaseListBox"/>
+ <service name="com.sun.star.form.component.ListBox"/>
+ <service name="stardiv.one.form.component.ListBox"/>
+ </implementation>
+ <implementation name="com.sun.star.form.ONumericControl"
+ constructor="com_sun_star_form_ONumericControl_get_implementation">
+ <service name="com.sun.star.form.control.NumericField"/>
+ <service name="stardiv.one.form.control.NumericField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.ONumericModel"
+ constructor="com_sun_star_form_ONumericModel_get_implementation">
+ <service name="com.sun.star.form.binding.BindableDatabaseNumericField"/>
+ <service name="com.sun.star.form.component.DatabaseNumericField"/>
+ <service name="com.sun.star.form.component.NumericField"/>
+ <service name="stardiv.one.form.component.NumericField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OPatternControl"
+ constructor="com_sun_star_form_OPatternControl_get_implementation">
+ <service name="com.sun.star.form.control.PatternField"/>
+ <service name="stardiv.one.form.control.PatternField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OPatternModel"
+ constructor="com_sun_star_form_OPatternModel_get_implementation">
+ <service name="com.sun.star.form.component.DatabasePatternField"/>
+ <service name="com.sun.star.form.component.PatternField"/>
+ <service name="stardiv.one.form.component.PatternField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.ORadioButtonControl"
+ constructor="com_sun_star_form_ORadioButtonControl_get_implementation">
+ <service name="com.sun.star.form.control.RadioButton"/>
+ <service name="stardiv.one.form.control.RadioButton"/>
+ </implementation>
+ <implementation name="com.sun.star.form.ORadioButtonModel"
+ constructor="com_sun_star_form_ORadioButtonModel_get_implementation">
+ <service name="com.sun.star.form.binding.BindableDatabaseRadioButton"/>
+ <service name="com.sun.star.form.component.DatabaseRadioButton"/>
+ <service name="com.sun.star.form.component.RadioButton"/>
+ <service name="stardiv.one.form.component.RadioButton"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OTimeControl"
+ constructor="com_sun_star_form_OTimeControl_get_implementation">
+ <service name="com.sun.star.form.control.TimeField"/>
+ <service name="stardiv.one.form.control.TimeField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.OTimeModel"
+ constructor="com_sun_star_form_OTimeModel_get_implementation">
+ <service name="com.sun.star.form.component.DatabaseTimeField"/>
+ <service name="com.sun.star.form.component.TimeField"/>
+ <service name="stardiv.one.form.component.TimeField"/>
+ </implementation>
+ <implementation name="com.sun.star.form.XForms"
+ constructor="com_sun_star_form_XForms_get_implementation">
+ <service name="com.sun.star.xforms.XForms"/>
+ </implementation>
+</component>
diff --git a/forms/workben/xforms/sickness.odt b/forms/workben/xforms/sickness.odt
new file mode 100644
index 0000000000..b48082212a
--- /dev/null
+++ b/forms/workben/xforms/sickness.odt
Binary files differ
diff --git a/forms/workben/xforms/simple_validation.odt b/forms/workben/xforms/simple_validation.odt
new file mode 100644
index 0000000000..216fba7382
--- /dev/null
+++ b/forms/workben/xforms/simple_validation.odt
Binary files differ
diff --git a/forms/workben/xforms/vacationrequest.odt b/forms/workben/xforms/vacationrequest.odt
new file mode 100644
index 0000000000..ce48542573
--- /dev/null
+++ b/forms/workben/xforms/vacationrequest.odt
Binary files differ
diff --git a/forms/workben/xforms/xforms-ubl.odt b/forms/workben/xforms/xforms-ubl.odt
new file mode 100644
index 0000000000..7ffaab9bc1
--- /dev/null
+++ b/forms/workben/xforms/xforms-ubl.odt
Binary files differ