summaryrefslogtreecommitdiffstats
path: root/kexi/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'kexi/plugins')
-rw-r--r--kexi/plugins/Makefile.am15
-rw-r--r--kexi/plugins/Makefile.common2
-rw-r--r--kexi/plugins/configure.in.in20
-rw-r--r--kexi/plugins/configure.in.mid26
-rw-r--r--kexi/plugins/forms/Makefile.am56
-rw-r--r--kexi/plugins/forms/kexiactionselectiondialog.cpp724
-rw-r--r--kexi/plugins/forms/kexiactionselectiondialog.h71
-rw-r--r--kexi/plugins/forms/kexiactionselectiondialog_p.h51
-rw-r--r--kexi/plugins/forms/kexidataawarewidgetinfo.cpp43
-rw-r--r--kexi/plugins/forms/kexidataawarewidgetinfo.h44
-rw-r--r--kexi/plugins/forms/kexidataprovider.cpp315
-rw-r--r--kexi/plugins/forms/kexidataprovider.h95
-rw-r--r--kexi/plugins/forms/kexidatasourcepage.cpp471
-rw-r--r--kexi/plugins/forms/kexidatasourcepage.h112
-rw-r--r--kexi/plugins/forms/kexidbfactory.cpp713
-rw-r--r--kexi/plugins/forms/kexidbfactory.h74
-rw-r--r--kexi/plugins/forms/kexidbtextwidgetinterface.cpp71
-rw-r--r--kexi/plugins/forms/kexidbtextwidgetinterface.h53
-rw-r--r--kexi/plugins/forms/kexiformdataiteminterface.cpp68
-rw-r--r--kexi/plugins/forms/kexiformdataiteminterface.h145
-rw-r--r--kexi/plugins/forms/kexiformeventhandler.cpp188
-rw-r--r--kexi/plugins/forms/kexiformeventhandler.h101
-rw-r--r--kexi/plugins/forms/kexiformhandler.desktop115
-rw-r--r--kexi/plugins/forms/kexiformmanager.cpp235
-rw-r--r--kexi/plugins/forms/kexiformmanager.h87
-rw-r--r--kexi/plugins/forms/kexiformpart.cpp550
-rw-r--r--kexi/plugins/forms/kexiformpart.h108
-rw-r--r--kexi/plugins/forms/kexiformpartinstui.rc77
-rw-r--r--kexi/plugins/forms/kexiformpartui.rc10
-rw-r--r--kexi/plugins/forms/kexiforms.cpp25
-rw-r--r--kexi/plugins/forms/kexiformscrollview.cpp587
-rw-r--r--kexi/plugins/forms/kexiformscrollview.h297
-rw-r--r--kexi/plugins/forms/kexiformview.cpp1278
-rw-r--r--kexi/plugins/forms/kexiformview.h231
-rw-r--r--kexi/plugins/forms/kformdesigner_kexidbfactory.desktop55
-rw-r--r--kexi/plugins/forms/widgets/Makefile.am28
-rw-r--r--kexi/plugins/forms/widgets/kexidbautofield.cpp846
-rw-r--r--kexi/plugins/forms/widgets/kexidbautofield.h210
-rw-r--r--kexi/plugins/forms/widgets/kexidbcheckbox.cpp175
-rw-r--r--kexi/plugins/forms/widgets/kexidbcheckbox.h99
-rw-r--r--kexi/plugins/forms/widgets/kexidbcombobox.cpp550
-rw-r--r--kexi/plugins/forms/widgets/kexidbcombobox.h181
-rw-r--r--kexi/plugins/forms/widgets/kexidbdateedit.cpp230
-rw-r--r--kexi/plugins/forms/widgets/kexidbdateedit.h118
-rw-r--r--kexi/plugins/forms/widgets/kexidbdatetimeedit.cpp243
-rw-r--r--kexi/plugins/forms/widgets/kexidbdatetimeedit.h106
-rw-r--r--kexi/plugins/forms/widgets/kexidbdoublespinbox.cpp113
-rw-r--r--kexi/plugins/forms/widgets/kexidbdoublespinbox.h79
-rw-r--r--kexi/plugins/forms/widgets/kexidbform.cpp714
-rw-r--r--kexi/plugins/forms/widgets/kexidbform.h139
-rw-r--r--kexi/plugins/forms/widgets/kexidbimagebox.cpp870
-rw-r--r--kexi/plugins/forms/widgets/kexidbimagebox.h275
-rw-r--r--kexi/plugins/forms/widgets/kexidbintspinbox.cpp114
-rw-r--r--kexi/plugins/forms/widgets/kexidbintspinbox.h80
-rw-r--r--kexi/plugins/forms/widgets/kexidblabel.cpp650
-rw-r--r--kexi/plugins/forms/widgets/kexidblabel.h140
-rw-r--r--kexi/plugins/forms/widgets/kexidblineedit.cpp417
-rw-r--r--kexi/plugins/forms/widgets/kexidblineedit.h170
-rw-r--r--kexi/plugins/forms/widgets/kexidbsubform.cpp131
-rw-r--r--kexi/plugins/forms/widgets/kexidbsubform.h52
-rw-r--r--kexi/plugins/forms/widgets/kexidbtextedit.cpp209
-rw-r--r--kexi/plugins/forms/widgets/kexidbtextedit.h113
-rw-r--r--kexi/plugins/forms/widgets/kexidbtimeedit.cpp156
-rw-r--r--kexi/plugins/forms/widgets/kexidbtimeedit.h87
-rw-r--r--kexi/plugins/forms/widgets/kexidbutils.cpp99
-rw-r--r--kexi/plugins/forms/widgets/kexidbutils.h71
-rw-r--r--kexi/plugins/forms/widgets/kexiframe.cpp77
-rw-r--r--kexi/plugins/forms/widgets/kexiframe.h84
-rw-r--r--kexi/plugins/forms/widgets/kexiframeutils_p.cpp232
-rw-r--r--kexi/plugins/forms/widgets/kexipushbutton.cpp32
-rw-r--r--kexi/plugins/forms/widgets/kexipushbutton.h55
-rw-r--r--kexi/plugins/importexport/Makefile.am1
-rw-r--r--kexi/plugins/importexport/csv/Makefile.am21
-rw-r--r--kexi/plugins/importexport/csv/kexicsv_importexporthandler.desktop51
-rw-r--r--kexi/plugins/importexport/csv/kexicsv_importexportpart.cpp87
-rw-r--r--kexi/plugins/importexport/csv/kexicsv_importexportpart.h44
-rw-r--r--kexi/plugins/importexport/csv/kexicsvexport.cpp271
-rw-r--r--kexi/plugins/importexport/csv/kexicsvexport.h58
-rw-r--r--kexi/plugins/importexport/csv/kexicsvexportwizard.cpp431
-rw-r--r--kexi/plugins/importexport/csv/kexicsvexportwizard.h113
-rw-r--r--kexi/plugins/importexport/csv/kexicsvimportdialog.cpp1662
-rw-r--r--kexi/plugins/importexport/csv/kexicsvimportdialog.h231
-rw-r--r--kexi/plugins/importexport/csv/kexicsvimportoptionsdlg.cpp140
-rw-r--r--kexi/plugins/importexport/csv/kexicsvimportoptionsdlg.h62
-rw-r--r--kexi/plugins/importexport/csv/kexicsvwidgets.cpp233
-rw-r--r--kexi/plugins/importexport/csv/kexicsvwidgets.h116
-rw-r--r--kexi/plugins/macros/Makefile.am9
-rw-r--r--kexi/plugins/macros/configure.in.in13
-rw-r--r--kexi/plugins/macros/kexiactions/Makefile.am27
-rw-r--r--kexi/plugins/macros/kexiactions/datatableaction.cpp185
-rw-r--r--kexi/plugins/macros/kexiactions/datatableaction.h76
-rw-r--r--kexi/plugins/macros/kexiactions/executeaction.cpp96
-rw-r--r--kexi/plugins/macros/kexiactions/executeaction.h78
-rw-r--r--kexi/plugins/macros/kexiactions/kexiaction.cpp48
-rw-r--r--kexi/plugins/macros/kexiactions/kexiaction.h75
-rw-r--r--kexi/plugins/macros/kexiactions/kexivariable.h76
-rw-r--r--kexi/plugins/macros/kexiactions/messageaction.cpp50
-rw-r--r--kexi/plugins/macros/kexiactions/messageaction.h66
-rw-r--r--kexi/plugins/macros/kexiactions/navigateaction.cpp158
-rw-r--r--kexi/plugins/macros/kexiactions/navigateaction.h78
-rw-r--r--kexi/plugins/macros/kexiactions/objectnamevariable.h76
-rw-r--r--kexi/plugins/macros/kexiactions/objectvariable.h87
-rw-r--r--kexi/plugins/macros/kexiactions/openaction.cpp154
-rw-r--r--kexi/plugins/macros/kexiactions/openaction.h79
-rw-r--r--kexi/plugins/macros/kexipart/Makefile.am32
-rw-r--r--kexi/plugins/macros/kexipart/keximacrodesignview.cpp497
-rw-r--r--kexi/plugins/macros/kexipart/keximacrodesignview.h129
-rw-r--r--kexi/plugins/macros/kexipart/keximacroerror.cpp130
-rw-r--r--kexi/plugins/macros/kexipart/keximacroerror.h89
-rw-r--r--kexi/plugins/macros/kexipart/keximacroerrorbase.ui213
-rw-r--r--kexi/plugins/macros/kexipart/keximacrohandler.desktop81
-rw-r--r--kexi/plugins/macros/kexipart/keximacropart.cpp172
-rw-r--r--kexi/plugins/macros/kexipart/keximacropart.h95
-rw-r--r--kexi/plugins/macros/kexipart/keximacroproperty.cpp626
-rw-r--r--kexi/plugins/macros/kexipart/keximacroproperty.h186
-rw-r--r--kexi/plugins/macros/kexipart/keximacrotextview.cpp90
-rw-r--r--kexi/plugins/macros/kexipart/keximacrotextview.h77
-rw-r--r--kexi/plugins/macros/kexipart/keximacroview.cpp175
-rw-r--r--kexi/plugins/macros/kexipart/keximacroview.h140
-rw-r--r--kexi/plugins/macros/lib/Makefile.am23
-rw-r--r--kexi/plugins/macros/lib/action.cpp170
-rw-r--r--kexi/plugins/macros/lib/action.h187
-rw-r--r--kexi/plugins/macros/lib/context.cpp261
-rw-r--r--kexi/plugins/macros/lib/context.h141
-rw-r--r--kexi/plugins/macros/lib/exception.cpp97
-rw-r--r--kexi/plugins/macros/lib/exception.h84
-rw-r--r--kexi/plugins/macros/lib/komacro_export.h39
-rw-r--r--kexi/plugins/macros/lib/macro.cpp126
-rw-r--r--kexi/plugins/macros/lib/macro.h130
-rw-r--r--kexi/plugins/macros/lib/macroitem.cpp217
-rw-r--r--kexi/plugins/macros/lib/macroitem.h142
-rw-r--r--kexi/plugins/macros/lib/manager.cpp170
-rw-r--r--kexi/plugins/macros/lib/manager.h219
-rw-r--r--kexi/plugins/macros/lib/metamethod.cpp344
-rw-r--r--kexi/plugins/macros/lib/metamethod.h150
-rw-r--r--kexi/plugins/macros/lib/metaobject.cpp151
-rw-r--r--kexi/plugins/macros/lib/metaobject.h118
-rw-r--r--kexi/plugins/macros/lib/metaparameter.cpp146
-rw-r--r--kexi/plugins/macros/lib/metaparameter.h136
-rw-r--r--kexi/plugins/macros/lib/variable.cpp246
-rw-r--r--kexi/plugins/macros/lib/variable.h222
-rw-r--r--kexi/plugins/macros/lib/xmlhandler.cpp226
-rw-r--r--kexi/plugins/macros/lib/xmlhandler.h77
-rw-r--r--kexi/plugins/macros/tests/Makefile.am28
-rw-r--r--kexi/plugins/macros/tests/actiontests.cpp211
-rw-r--r--kexi/plugins/macros/tests/actiontests.h89
-rw-r--r--kexi/plugins/macros/tests/commontests.cpp907
-rw-r--r--kexi/plugins/macros/tests/commontests.h118
-rw-r--r--kexi/plugins/macros/tests/komacrotest.cpp58
-rw-r--r--kexi/plugins/macros/tests/komacrotestbase.h90
-rw-r--r--kexi/plugins/macros/tests/komacrotestgui.cpp60
-rw-r--r--kexi/plugins/macros/tests/macroitemtests.cpp243
-rw-r--r--kexi/plugins/macros/tests/macroitemtests.h87
-rw-r--r--kexi/plugins/macros/tests/macrotests.cpp192
-rw-r--r--kexi/plugins/macros/tests/macrotests.h74
-rw-r--r--kexi/plugins/macros/tests/testaction.cpp61
-rw-r--r--kexi/plugins/macros/tests/testaction.h78
-rw-r--r--kexi/plugins/macros/tests/testobject.cpp117
-rw-r--r--kexi/plugins/macros/tests/testobject.h85
-rw-r--r--kexi/plugins/macros/tests/variabletests.cpp236
-rw-r--r--kexi/plugins/macros/tests/variabletests.h87
-rw-r--r--kexi/plugins/macros/tests/xmlhandlertests.cpp619
-rw-r--r--kexi/plugins/macros/tests/xmlhandlertests.h122
-rw-r--r--kexi/plugins/macros/tests/xmlhandlertests2.cpp1161
-rw-r--r--kexi/plugins/macros/tests/xmlhandlertests2.h132
-rw-r--r--kexi/plugins/migration/Makefile.am20
-rw-r--r--kexi/plugins/migration/keximigrationhandler.desktop102
-rw-r--r--kexi/plugins/migration/keximigrationpart.cpp46
-rw-r--r--kexi/plugins/migration/keximigrationpart.h38
-rw-r--r--kexi/plugins/queries/Makefile.am29
-rw-r--r--kexi/plugins/queries/kexiaddparamdialog.cpp47
-rw-r--r--kexi/plugins/queries/kexiaddparamdialog.h40
-rw-r--r--kexi/plugins/queries/kexiaddparamwidget.ui135
-rw-r--r--kexi/plugins/queries/kexidynamicqueryparameterdialog.cpp63
-rw-r--r--kexi/plugins/queries/kexidynamicqueryparameterdialog.h45
-rw-r--r--kexi/plugins/queries/kexiparameterlisteditor.ui88
-rw-r--r--kexi/plugins/queries/kexiquerydesignerguieditor.cpp1803
-rw-r--r--kexi/plugins/queries/kexiquerydesignerguieditor.h170
-rw-r--r--kexi/plugins/queries/kexiquerydesignersql.cpp542
-rw-r--r--kexi/plugins/queries/kexiquerydesignersql.h82
-rw-r--r--kexi/plugins/queries/kexiquerydesignersqlhistory.cpp373
-rw-r--r--kexi/plugins/queries/kexiquerydesignersqlhistory.h104
-rw-r--r--kexi/plugins/queries/kexiqueryhandler.desktop111
-rw-r--r--kexi/plugins/queries/kexiquerypart.cpp310
-rw-r--r--kexi/plugins/queries/kexiquerypart.h118
-rw-r--r--kexi/plugins/queries/kexiquerypartinstui.rc24
-rw-r--r--kexi/plugins/queries/kexiquerypartui.rc11
-rw-r--r--kexi/plugins/queries/kexiqueryview.cpp154
-rw-r--r--kexi/plugins/queries/kexiqueryview.h58
-rw-r--r--kexi/plugins/relations/Makefile.am28
-rw-r--r--kexi/plugins/relations/kexirelationhandler.desktop110
-rw-r--r--kexi/plugins/relations/kexirelationmaindlg.cpp81
-rw-r--r--kexi/plugins/relations/kexirelationmaindlg.h47
-rw-r--r--kexi/plugins/relations/kexirelationpartimpl.cpp85
-rw-r--r--kexi/plugins/relations/kexirelationpartimpl.h46
-rw-r--r--kexi/plugins/relations/kexirelationpartinstui.rc6
-rw-r--r--kexi/plugins/relations/kexirelationpartui.rc14
-rw-r--r--kexi/plugins/reports/Makefile.am51
-rw-r--r--kexi/plugins/reports/kexireportfactory.cpp227
-rw-r--r--kexi/plugins/reports/kexireportfactory.h62
-rw-r--r--kexi/plugins/reports/kexireportform.cpp188
-rw-r--r--kexi/plugins/reports/kexireportform.h60
-rw-r--r--kexi/plugins/reports/kexireporthandler.desktop108
-rw-r--r--kexi/plugins/reports/kexireportpart.cpp141
-rw-r--r--kexi/plugins/reports/kexireportpart.h88
-rw-r--r--kexi/plugins/reports/kexireportpartinstui.rc37
-rw-r--r--kexi/plugins/reports/kexireportpartui.rc6
-rw-r--r--kexi/plugins/reports/kexireports.cpp24
-rw-r--r--kexi/plugins/reports/kexireportview.cpp477
-rw-r--r--kexi/plugins/reports/kexireportview.h130
-rw-r--r--kexi/plugins/reports/kformdesigner_kexireportfactory.desktop53
-rw-r--r--kexi/plugins/reports/reportwidgets.cpp181
-rw-r--r--kexi/plugins/reports/reportwidgets.h117
-rw-r--r--kexi/plugins/scripting/Makefile.am1
-rw-r--r--kexi/plugins/scripting/README28
-rw-r--r--kexi/plugins/scripting/kexiapp/Makefile.am21
-rw-r--r--kexi/plugins/scripting/kexiapp/kexiappmainwindow.cpp106
-rw-r--r--kexi/plugins/scripting/kexiapp/kexiappmainwindow.h91
-rw-r--r--kexi/plugins/scripting/kexiapp/kexiappmodule.cpp97
-rw-r--r--kexi/plugins/scripting/kexiapp/kexiappmodule.h76
-rw-r--r--kexi/plugins/scripting/kexiapp/kexiapppart.cpp46
-rw-r--r--kexi/plugins/scripting/kexiapp/kexiapppart.h56
-rw-r--r--kexi/plugins/scripting/kexidb.doxyfile324
-rw-r--r--kexi/plugins/scripting/kexidb/Makefile.am30
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbconnection.cpp221
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbconnection.h194
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbconnectiondata.cpp112
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbconnectiondata.h126
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbcursor.cpp139
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbcursor.h159
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbdriver.cpp70
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbdriver.h114
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbdrivermanager.cpp178
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbdrivermanager.h105
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbfield.cpp147
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbfield.h148
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbfieldlist.cpp100
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbfieldlist.h104
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbmodule.cpp74
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbmodule.h69
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbparser.cpp77
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbparser.h95
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbschema.cpp197
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbschema.h134
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbtransaction.cpp52
-rw-r--r--kexi/plugins/scripting/kexidb/kexidbtransaction.h62
-rw-r--r--kexi/plugins/scripting/kexidb/readme.dox32
-rw-r--r--kexi/plugins/scripting/kexiscripting/Makefile.am37
-rw-r--r--kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.cpp337
-rw-r--r--kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.h124
-rw-r--r--kexi/plugins/scripting/kexiscripting/kexiscripteditor.cpp104
-rw-r--r--kexi/plugins/scripting/kexiscripting/kexiscripteditor.h77
-rw-r--r--kexi/plugins/scripting/kexiscripting/kexiscripthandler.desktop105
-rw-r--r--kexi/plugins/scripting/kexiscripting/kexiscriptpart.cpp201
-rw-r--r--kexi/plugins/scripting/kexiscripting/kexiscriptpart.h100
-rw-r--r--kexi/plugins/scripting/kexiscripting/kexiscriptpartinstui.rc10
-rw-r--r--kexi/plugins/scripting/kexiscripting/kexiscriptpartui.rc10
-rw-r--r--kexi/plugins/scripting/scripts/Makefile.am1
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/CopyCenter.py644
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc10
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py646
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py495
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/Makefile.am4
-rw-r--r--kexi/plugins/scripting/scripts/copycenter/readme.html20
-rw-r--r--kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py196
-rw-r--r--kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc8
-rw-r--r--kexi/plugins/scripting/scripts/exportxhtml/Makefile.am4
-rwxr-xr-xkexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py434
-rw-r--r--kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc8
-rw-r--r--kexi/plugins/scripting/scripts/importxhtml/Makefile.am4
-rw-r--r--kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am4
-rwxr-xr-xkexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py186
-rw-r--r--kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc8
-rw-r--r--kexi/plugins/scripting/scripts/python/Makefile.am2
-rw-r--r--kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am2
-rwxr-xr-xkexi/plugins/scripting/scripts/python/kexiapp/__init__.py25
-rw-r--r--kexi/plugins/tables/Makefile.am28
-rw-r--r--kexi/plugins/tables/kexilookupcolumnpage.cpp419
-rw-r--r--kexi/plugins/tables/kexilookupcolumnpage.h88
-rw-r--r--kexi/plugins/tables/kexitabledesigner_dataview.cpp79
-rw-r--r--kexi/plugins/tables/kexitabledesigner_dataview.h49
-rw-r--r--kexi/plugins/tables/kexitabledesignercommands.cpp281
-rw-r--r--kexi/plugins/tables/kexitabledesignercommands.h188
-rw-r--r--kexi/plugins/tables/kexitabledesignerview.cpp1943
-rw-r--r--kexi/plugins/tables/kexitabledesignerview.h258
-rw-r--r--kexi/plugins/tables/kexitabledesignerview_p.cpp294
-rw-r--r--kexi/plugins/tables/kexitabledesignerview_p.h191
-rw-r--r--kexi/plugins/tables/kexitablehandler.desktop118
-rw-r--r--kexi/plugins/tables/kexitablepart.cpp313
-rw-r--r--kexi/plugins/tables/kexitablepart.h100
-rw-r--r--kexi/plugins/tables/kexitablepartinstui.rc18
-rw-r--r--kexi/plugins/tables/kexitablepartui.rc7
292 files changed, 50064 insertions, 0 deletions
diff --git a/kexi/plugins/Makefile.am b/kexi/plugins/Makefile.am
new file mode 100644
index 00000000..6e0a7432
--- /dev/null
+++ b/kexi/plugins/Makefile.am
@@ -0,0 +1,15 @@
+#if compile_kexi_reports_plugin
+# REPORTS=reports
+#endif
+
+if compile_kexi_macros_plugin
+ MACRODIR=macros
+endif
+
+if compile_kross
+ SCRIPTINGDIR=scripting
+endif
+
+SUBDIRS = tables relations migration queries forms $(SCRIPTINGDIR) $(MACRODIR) importexport
+
+#$(REPORTS) importwizard relations
diff --git a/kexi/plugins/Makefile.common b/kexi/plugins/Makefile.common
new file mode 100644
index 00000000..d49a2a0f
--- /dev/null
+++ b/kexi/plugins/Makefile.common
@@ -0,0 +1,2 @@
+INCLUDES += $(LIB_KEXI_KMDI_INCLUDES)
+
diff --git a/kexi/plugins/configure.in.in b/kexi/plugins/configure.in.in
new file mode 100644
index 00000000..6d2cffcb
--- /dev/null
+++ b/kexi/plugins/configure.in.in
@@ -0,0 +1,20 @@
+# disabled
+#AC_ARG_ENABLE(kexi-reports,
+# AC_HELP_STRING([--enable-kexi-reports],
+# [build Kexi reports plugin (EXPERIMENTAL) [default=no]]),
+# compile_kexi_reports_plugin=$enableval, compile_kexi_reports_plugin=no)
+#AM_CONDITIONAL(compile_kexi_reports_plugin, test "x$compile_kexi_reports_plugin" != "xno")
+#
+#if test "$compile_kexi_reports_plugin" == "yes"; then
+# AC_DEFINE(KEXI_REPORTS_SUPPORT, 1, [build Kexi reports plugin])
+#fi
+
+AC_ARG_ENABLE(kexi-macros,
+ AC_HELP_STRING([--enable-kexi-macros],
+ [build Kexi macro plugin (EXPERIMENTAL) [default=yes]]),
+ compile_kexi_macros_plugin=$enableval, compile_kexi_macros_plugin=no)
+AM_CONDITIONAL(compile_kexi_macros_plugin, test "x$compile_kexi_macros_plugin" == "xyes")
+
+if test "$compile_kexi_macros_plugin" == "yes"; then
+ AC_DEFINE(KEXI_MACROS_SUPPORT, 1, [build Kexi macros plugin])
+fi
diff --git a/kexi/plugins/configure.in.mid b/kexi/plugins/configure.in.mid
new file mode 100644
index 00000000..c8ca24b1
--- /dev/null
+++ b/kexi/plugins/configure.in.mid
@@ -0,0 +1,26 @@
+if test -s $srcdir/inst-apps ; then
+ SUBDIRLIST=`cat $srcdir/inst-apps`
+else
+ SUBDIRLIST=`cat $srcdir/subdirs`
+fi
+
+# fallback (KDE_CREATE_SUBDIRLIST has this fallback, so I have put it here too.)
+if test -z "$SUBDIRLIST" ; then
+ SUBDIRLIST=`ls -1 $srcdir`
+fi
+
+# first check which main apllication we could compile
+for args in $SUBDIRLIST ; do
+ case $args in
+ kugar) COMPILE_PLUGIN_KUGAR="$args " ;;
+ esac
+done
+
+# now remove the applications the user has asked not to compile
+for args in $DO_NOT_COMPILE ; do
+ case $args in
+ kugar) COMPILE_PLUGIN_KUGAR= ;;
+ esac
+done
+
+AM_CONDITIONAL(compile_plugin_KUGAR, test -n "$COMPILE_PLUGIN_KUGAR")
diff --git a/kexi/plugins/forms/Makefile.am b/kexi/plugins/forms/Makefile.am
new file mode 100644
index 00000000..e01b4f6c
--- /dev/null
+++ b/kexi/plugins/forms/Makefile.am
@@ -0,0 +1,56 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_form.la kformdesigner_kexidbwidgets.la
+
+kexihandler_form_la_SOURCES = kexiforms.cpp
+
+kexihandler_form_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module -no-undefined
+kexihandler_form_la_LIBADD = $(top_builddir)/kexi/core/libkexicore.la \
+ $(top_builddir)/kexi/widget/utils/libkexiguiutils.la \
+ $(top_builddir)/kexi/widget/tableview/libkexidatatable.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/kexi/formeditor/libkformdesigner.la \
+ $(top_builddir)/lib/koproperty/libkoproperty.la \
+ ./libkexiformutils.la
+
+kformdesigner_kexidbwidgets_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module -no-undefined
+kformdesigner_kexidbwidgets_la_SOURCES = kexidbfactory.cpp
+kformdesigner_kexidbwidgets_la_LIBADD = $(top_builddir)/kexi/formeditor/libkformdesigner.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ ./libkexiformutils.la
+
+lib_LTLIBRARIES = libkexiformutils.la
+libkexiformutils_la_SOURCES = kexiformdataiteminterface.cpp kexidataawarewidgetinfo.cpp \
+ kexidataprovider.cpp kexiformscrollview.cpp kexiformeventhandler.cpp \
+ kexidbtextwidgetinterface.cpp kexiactionselectiondialog.cpp kexiformmanager.cpp \
+ kexidatasourcepage.cpp kexiformpart.cpp kexiformview.cpp
+libkexiformutils_la_LDFLAGS = $(all_libraries) $(VER_INFO) -no-undefined
+libkexiformutils_la_LIBADD = $(top_builddir)/kexi/core/libkexicore.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/kexi/formeditor/libkformdesigner.la \
+ $(top_builddir)/kexi/plugins/forms/widgets/libkexiformutilswidgets.la
+
+kformdesignerservicesdir=$(kde_servicesdir)/kformdesigner
+kformdesignerservices_DATA=kformdesigner_kexidbfactory.desktop
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=kexiformhandler.desktop
+
+rcdir = $(kde_datadir)/kexi
+rc_DATA = kexiformpartui.rc kexiformpartinstui.rc
+
+SUBDIRS = widgets .
+
+INCLUDES= -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget/utils \
+ -I$(top_srcdir)/kexi/widget \
+ -I$(top_srcdir)/kexi/formeditor \
+ -I$(top_srcdir)/lib -I$(top_srcdir)/lib/koproperty -I$(top_srcdir)/lib/kofficecore \
+ -I$(top_srcdir)/kexi/widget/tableview/private \
+ -I$(top_srcdir)/kexi/widget/tableview $(all_includes)
+
+METASOURCES = AUTO
+
+include ../Makefile.common
+noinst_HEADERS = kexidataprovider.h kexidbfactory.h \
+ kexiformpart.h kexiformscrollview.h kexiformview.h \ No newline at end of file
diff --git a/kexi/plugins/forms/kexiactionselectiondialog.cpp b/kexi/plugins/forms/kexiactionselectiondialog.cpp
new file mode 100644
index 00000000..26b4a9a6
--- /dev/null
+++ b/kexi/plugins/forms/kexiactionselectiondialog.cpp
@@ -0,0 +1,724 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiactionselectiondialog.h"
+#include "kexiactionselectiondialog_p.h"
+
+#include <keximainwindow.h>
+#include <kexipartitem.h>
+#include <kexiproject.h>
+#include <kexipartinfo.h>
+#include <kexipart.h>
+#include <kexiactioncategories.h>
+
+#include <klistview.h>
+#include <kaction.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kstdguiitem.h>
+#include <kpushbutton.h>
+
+#include <qbitmap.h>
+#include <qlabel.h>
+#include <qheader.h>
+#include <qvbox.h>
+#include <qtooltip.h>
+#include <qwidgetstack.h>
+
+#include <widget/utils/klistviewitemtemplate.h>
+#include <widget/kexibrowser.h>
+#include <widget/kexibrowseritem.h>
+#include <kexiutils/utils.h>
+
+typedef KListViewItemTemplate<QString> ActionSelectorDialogListItemBase;
+
+class ActionSelectorDialogListItem : public ActionSelectorDialogListItemBase
+{
+public:
+ ActionSelectorDialogListItem(const QString& data, QListView *parent, QString label1)
+ : ActionSelectorDialogListItemBase(data, parent, label1)
+ , fifoSorting(true)
+ {
+ m_sortKey.sprintf("%2.2d", parent->childCount());
+ }
+
+ ActionSelectorDialogListItem(const QString& data, QListViewItem *parent, QString label1)
+ : ActionSelectorDialogListItemBase(data, parent, label1)
+ , fifoSorting(true)
+ {
+ m_sortKey.sprintf("%2.2d", parent->childCount());
+ }
+
+ virtual QString key( int column, bool ascending ) const
+ {
+ return fifoSorting ? m_sortKey : ActionSelectorDialogListItemBase::key(column, ascending);
+ }
+
+ bool fifoSorting : 1;
+
+protected:
+ QString m_sortKey;
+};
+
+//---------------------------------------
+
+ActionsListViewBase::ActionsListViewBase(QWidget* parent)
+ : KListView(parent)
+{
+ setResizeMode(QListView::AllColumns);
+ addColumn("");
+ header()->hide();
+ setColumnWidthMode(0, QListView::Maximum);
+ setAllColumnsShowFocus(true);
+ setTooltipColumn(0);
+}
+
+ActionsListViewBase::~ActionsListViewBase()
+{
+}
+
+QListViewItem *ActionsListViewBase::itemForAction(const QString& actionName)
+{
+ for (QListViewItemIterator it(this); it.current(); ++it) {
+ ActionSelectorDialogListItem* item = dynamic_cast<ActionSelectorDialogListItem*>(it.current());
+ if (item && item->data == actionName)
+ return item;
+ }
+ return 0;
+}
+
+void ActionsListViewBase::selectAction(const QString& actionName)
+{
+ QListViewItem *item = itemForAction(actionName);
+ if (item) {
+ setSelected(item, true);
+ ensureItemVisible(firstChild());
+ ensureItemVisible(selectedItem());
+ }
+}
+
+//---------------------------------------
+
+KActionsListViewBase::KActionsListViewBase(QWidget* parent, KexiMainWindow* mainWin)
+ : ActionsListViewBase(parent)
+ , m_mainWin(mainWin)
+{
+}
+
+KActionsListViewBase::~KActionsListViewBase() {}
+
+void KActionsListViewBase::init()
+{
+ setSorting(0);
+ const QPixmap noIcon( KexiUtils::emptyIcon(KIcon::Small) );
+ KActionPtrList sharedActions( m_mainWin->allActions() );
+ const Kexi::ActionCategories *acat = Kexi::actionCategories();
+ foreach (KActionPtrList::ConstIterator, it, sharedActions) {
+// kdDebug() << (*it)->name() << " " << (*it)->text() << endl;
+ //! @todo group actions
+ //! @todo: store KAction* here?
+ const int actionCategories = acat->actionCategories((*it)->name());
+ if (actionCategories==-1) {
+ kexipluginswarn << "KActionsListViewBase(): no category declared for action \""
+ << (*it)->name() << "\"! Fix this!" << endl;
+ continue;
+ }
+ if (!isActionVisible((*it)->name(), actionCategories))
+ continue;
+ ActionSelectorDialogListItem *pitem = new ActionSelectorDialogListItem((*it)->name(),
+ this, (*it)->toolTip().isEmpty() ? (*it)->text().replace("&", "") : (*it)->toolTip() );
+ pitem->fifoSorting = false; //alpha sort
+ pitem->setPixmap( 0, (*it)->iconSet( KIcon::Small, 16 ).pixmap( QIconSet::Small, QIconSet::Active ) );
+ if (!pitem->pixmap(0) || pitem->pixmap(0)->isNull())
+ pitem->setPixmap( 0, noIcon );
+ }
+}
+
+//---------------------------------------
+
+//! @internal Used to display KActions (in column 2)
+class KActionsListView : public KActionsListViewBase
+{
+public:
+ KActionsListView(QWidget* parent, KexiMainWindow* mainWin)
+ : KActionsListViewBase(parent, mainWin)
+ {
+ }
+ virtual ~KActionsListView() {}
+
+ virtual bool isActionVisible(const char* actionName, int actionCategories) const {
+ Q_UNUSED(actionName);
+ return actionCategories & Kexi::GlobalActionCategory;
+ }
+};
+
+//! @internal Used to display KActions (in column 2)
+class CurrentFormActionsListView : public KActionsListViewBase
+{
+public:
+ CurrentFormActionsListView(QWidget* parent, KexiMainWindow* mainWin)
+ : KActionsListViewBase(parent, mainWin)
+ {
+ }
+ virtual ~CurrentFormActionsListView() {}
+
+ virtual bool isActionVisible(const char* actionName, int actionCategories) const {
+ return actionCategories & Kexi::WindowActionCategory
+ && Kexi::actionCategories()->actionSupportsObjectType(actionName, KexiPart::FormObjectType);
+ }
+};
+
+//! @internal a list view displaying action categories user can select from (column 1)
+class ActionCategoriesListView : public ActionsListViewBase
+{
+public:
+ ActionCategoriesListView(QWidget* parent) //, KexiProject& project)
+ : ActionsListViewBase(parent)
+ {
+ QListViewItem *item = new ActionSelectorDialogListItem("noaction", this, i18n("No action") );
+ const QPixmap noIcon( KexiUtils::emptyIcon(KIcon::Small) );
+ item->setPixmap(0, noIcon);
+ item = new ActionSelectorDialogListItem("kaction", this, i18n("Application actions") );
+ item->setPixmap(0, SmallIcon("form_action"));
+
+ KexiPart::PartInfoList *pl = Kexi::partManager().partInfoList();
+ for (KexiPart::Info *info = pl->first(); info; info = pl->next()) {
+ KexiPart::Part *part = Kexi::partManager().part(info);
+ if (!info->isVisibleInNavigator() || !part)
+ continue;
+ item = new KexiBrowserItem(this, info);
+ item->setText(0, part->instanceCaption());
+ }
+ QListViewItem *formItem = itemForAction("form");
+ if (formItem) {
+ item = new ActionSelectorDialogListItem("currentForm", formItem,
+ i18n("Current form's actions", "Current"));
+ }
+ adjustColumn(0);
+ setMinimumWidth( columnWidth(0) + 6 );
+ }
+
+ ~ActionCategoriesListView()
+ {
+ }
+
+ //! \return item for action \a actionName, reimplemented to support KexiBrowserItem items
+ virtual QListViewItem *itemForAction(const QString& actionName)
+ {
+ for (QListViewItemIterator it(this); it.current(); ++it) {
+ //simple case
+ ActionSelectorDialogListItem* item = dynamic_cast<ActionSelectorDialogListItem*>(it.current());
+ if (item) {
+ if (item->data == actionName)
+ return it.current();
+ continue;
+ }
+ KexiBrowserItem* bitem = dynamic_cast<KexiBrowserItem*>(it.current());
+ if (bitem) {
+ if (bitem->info()->objectName() == actionName)
+ return it.current();
+ }
+ }
+ return 0;
+ }
+};
+
+//! @internal Used to display list of actions available to executing (column 3)
+class ActionToExecuteListView : public ActionsListViewBase
+{
+ public:
+ ActionToExecuteListView(QWidget* parent)
+ : ActionsListViewBase(parent)
+ {
+ }
+
+ ~ActionToExecuteListView()
+ {
+ }
+
+ //! Updates actions
+ void showActionsForMimeType(const QString& mimeType) {
+ if (m_currentMimeType == mimeType)
+ return;
+ m_currentMimeType = mimeType;
+ clear();
+ KexiPart::Part *part = Kexi::partManager().partForMimeType( m_currentMimeType );
+ if (!part)
+ return;
+ int supportedViewModes = part->supportedViewModes();
+ ActionSelectorDialogListItem *item;
+ const QPixmap noIcon( KexiUtils::emptyIcon(KIcon::Small) );
+ if (supportedViewModes & Kexi::DataViewMode) {
+ item = new ActionSelectorDialogListItem("open", this, i18n("Open in Data View"));
+ item->setPixmap(0, SmallIcon("fileopen"));
+ }
+ if (part->info()->isExecuteSupported()) {
+ item = new ActionSelectorDialogListItem("execute", this, i18n("Execute"));
+ item->setPixmap(0, SmallIcon("player_play"));
+ }
+ if (part->info()->isPrintingSupported()) {
+ ActionSelectorDialogListItem *printItem = new ActionSelectorDialogListItem(
+ "print", this, i18n("Print"));
+ printItem->setPixmap(0, SmallIcon("fileprint"));
+ KAction *a = KStdAction::printPreview(0, 0, 0);
+ item = new ActionSelectorDialogListItem("printPreview", printItem,
+ a->text().replace("&", "").replace("...", ""));
+ item->setPixmap(0, SmallIcon(a->icon()));
+ delete a;
+ item = new ActionSelectorDialogListItem("pageSetup", printItem, i18n("Show Page Setup"));
+ item->setPixmap(0, noIcon);
+ setOpen(printItem, true);
+ printItem->setExpandable(false);
+ }
+ if (part->info()->isDataExportSupported()) {
+ ActionSelectorDialogListItem *exportItem = new ActionSelectorDialogListItem(
+ "exportToCSV", this,
+ i18n("Note: use multiple rows if needed", "Export to File\nAs Data Table"));
+ exportItem->setMultiLinesEnabled(true);
+ exportItem->setPixmap(0, SmallIcon("table"));
+ item = new ActionSelectorDialogListItem("copyToClipboardAsCSV",
+ exportItem,
+ i18n("Note: use multiple rows if needed", "Copy to Clipboard\nAs Data Table"));
+ item->setPixmap(0, SmallIcon("table"));
+ item->setMultiLinesEnabled(true);
+ setOpen(exportItem, true);
+ exportItem->setExpandable(false);
+ }
+ item = new ActionSelectorDialogListItem("new", this, i18n("Create New Object"));
+ item->setPixmap(0, SmallIcon("filenew"));
+ if (supportedViewModes & Kexi::DesignViewMode) {
+ item = new ActionSelectorDialogListItem("design", this, i18n("Open in Design View"));
+ item->setPixmap(0, SmallIcon("edit"));
+ }
+ if (supportedViewModes & Kexi::TextViewMode) {
+ item = new ActionSelectorDialogListItem("editText", this, i18n("Open in Text View"));
+ item->setPixmap(0, noIcon);
+ }
+ item = new ActionSelectorDialogListItem("close", this, i18n("Close View"));
+ item->setPixmap(0, SmallIcon("fileclose"));
+ updateWidth();
+ }
+
+ void updateWidth()
+ {
+ adjustColumn(0);
+ setMinimumWidth( columnWidth(0) );
+ }
+
+ QString m_currentMimeType;
+};
+
+//-------------------------------------
+
+//! @internal
+class KexiActionSelectionDialog::KexiActionSelectionDialogPrivate
+{
+public:
+ KexiActionSelectionDialogPrivate()
+ : kactionPageWidget(0), kactionListView(0), objectsListView(0)
+ , currentFormActionsPageWidget(0)
+ , currentFormActionsListView(0)
+ , secondAnd3rdColumnMainWidget(0)
+ , hideActionToExecuteListView(false)
+ {
+ }
+
+ void raiseWidget(QWidget *w)
+ {
+ secondAnd3rdColumnStack->raiseWidget( w );
+ selectActionToBeExecutedLbl->setBuddy(w);
+ }
+
+ void updateSelectActionToBeExecutedMessage(const QString& actionType)
+ {
+ QString msg;
+ if (actionType=="noaction")
+ msg = QString::null;
+ // hardcoded, but it's not that bad
+ else if (actionType=="macro")
+ msg = i18n("&Select macro to be executed after clicking \"%1\" button:").arg(actionWidgetName);
+ else if (actionType=="script")
+ msg = i18n("&Select script to be executed after clicking \"%1\" button:").arg(actionWidgetName);
+ //default: table/query/form/report...
+ else
+ msg = i18n("&Select object to be opened after clicking \"%1\" button:").arg(actionWidgetName);
+ selectActionToBeExecutedLbl->setText(msg);
+ }
+
+ // changes 3rd column visibility
+ void setActionToExecuteSectionVisible(bool visible, bool force = false)
+ {
+ if (!force && hideActionToExecuteListView != visible)
+ return;
+ hideActionToExecuteListView = !visible;
+ actionToExecuteListView->hide();
+ actionToExecuteLbl->hide();
+ actionToExecuteListView->show();
+ actionToExecuteLbl->show();
+ }
+
+ KexiMainWindow* mainWin;
+ QString actionWidgetName;
+ ActionCategoriesListView* actionCategoriesListView; //!< for column #1
+ QWidget *kactionPageWidget;
+ KActionsListView* kactionListView; //!< for column #2
+ KexiBrowser* objectsListView; //!< for column #2
+ QWidget *currentFormActionsPageWidget; //!< for column #2
+ CurrentFormActionsListView* currentFormActionsListView; //!< for column #2
+ QWidget *emptyWidget;
+ QLabel* selectActionToBeExecutedLbl;
+ ActionToExecuteListView* actionToExecuteListView;
+ QLabel *actionToExecuteLbl;
+ QWidget *secondAnd3rdColumnMainWidget;
+ QGridLayout *glyr;
+ QGridLayout *secondAnd3rdColumnGrLyr;
+ QWidgetStack *secondAnd3rdColumnStack, *secondColumnStack;
+ bool hideActionToExecuteListView;
+};
+
+//-------------------------------------
+
+KexiActionSelectionDialog::KexiActionSelectionDialog(KexiMainWindow* mainWin, QWidget *parent,
+ const KexiFormEventAction::ActionData& action, const QCString& actionWidgetName)
+ : KDialogBase(parent, "actionSelectorDialog", true, i18n("Assigning Action to Command Button"),
+ KDialogBase::Ok | KDialogBase::Cancel )
+ , d( new KexiActionSelectionDialogPrivate() )
+{
+ d->mainWin = mainWin;
+ d->actionWidgetName = actionWidgetName;
+ setButtonOK( KGuiItem(i18n("Assign action", "&Assign"), "button_ok", i18n("Assign action")) );
+
+ QWidget *mainWidget = new QWidget( this );
+ mainWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ setMainWidget(mainWidget);
+
+/* lbl 1
+ +------------+ +-------------------------------+
+ | | | [a] |
+ | 1st column | | +----------- + +------------+ |
+ | | | | 2nd column | | 3rd column | |
+ | | | + + + + |
+ | | | +------------+ +------------+ |
+ +------------+ +-------------------------------+
+ \______________________________________________/
+ glyr
+ [a]- QWidgetStack *secondAnd3rdColumnStack,
+ - for displaying KActions, the stack contains d->kactionPageWidget QWidget
+ - for displaying objects, the stack contains secondAnd3rdColumnMainWidget QWidget and QGridLayout *secondAnd3rdColumnGrLyr
+ - kactionPageWidget contains only a QVBoxLayout and label+kactionListView
+*/
+ d->glyr = new QGridLayout(mainWidget, 2, 2, KDialog::marginHint(), KDialog::spacingHint());
+ d->glyr->setRowStretch(1, 1);
+
+ // 1st column: action types
+ d->actionCategoriesListView = new ActionCategoriesListView(mainWidget);
+ d->actionCategoriesListView->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ d->glyr->addWidget(d->actionCategoriesListView, 1, 0);
+ connect( d->actionCategoriesListView, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(slotActionCategorySelected(QListViewItem*)));
+
+ QLabel *lbl = new QLabel(d->actionCategoriesListView, i18n("Action category:"), mainWidget);
+ lbl->setMinimumHeight(lbl->fontMetrics().height()*2);
+ lbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ lbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak);
+ d->glyr->addWidget(lbl, 0, 0, Qt::AlignTop|Qt::AlignLeft);
+
+ // widget stack for 2nd and 3rd column
+ d->secondAnd3rdColumnStack = new QWidgetStack(mainWidget);
+ d->secondAnd3rdColumnStack->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ d->glyr->addMultiCellWidget(d->secondAnd3rdColumnStack, 0, 1, 1, 1);//, Qt::AlignTop|Qt::AlignLeft);
+
+ d->secondAnd3rdColumnMainWidget = new QWidget(d->secondAnd3rdColumnStack);
+ d->secondAnd3rdColumnMainWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ d->secondAnd3rdColumnGrLyr = new QGridLayout(d->secondAnd3rdColumnMainWidget, 2, 2, 0, KDialog::spacingHint());
+ d->secondAnd3rdColumnGrLyr->setRowStretch(1, 2);
+ d->secondAnd3rdColumnStack->addWidget(d->secondAnd3rdColumnMainWidget);
+
+ // 2nd column: list of actions/objects
+ d->objectsListView = new KexiBrowser(d->secondAnd3rdColumnMainWidget, d->mainWin, 0/*features*/);
+ d->secondAnd3rdColumnGrLyr->addWidget(d->objectsListView, 1, 0);
+ connect(d->objectsListView, SIGNAL(selectionChanged(KexiPart::Item*)),
+ this, SLOT(slotItemForOpeningOrExecutingSelected(KexiPart::Item*)));
+
+ d->selectActionToBeExecutedLbl = new QLabel(d->secondAnd3rdColumnMainWidget);
+ d->selectActionToBeExecutedLbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ d->selectActionToBeExecutedLbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak);
+ d->selectActionToBeExecutedLbl->setMinimumHeight(d->selectActionToBeExecutedLbl->fontMetrics().height()*2);
+ d->secondAnd3rdColumnGrLyr->addWidget(d->selectActionToBeExecutedLbl, 0, 0, Qt::AlignTop|Qt::AlignLeft);
+
+ d->emptyWidget = new QWidget(d->secondAnd3rdColumnStack);
+ d->secondAnd3rdColumnStack->addWidget(d->emptyWidget);
+
+ // 3rd column: actions to execute
+ d->actionToExecuteListView = new ActionToExecuteListView(d->secondAnd3rdColumnMainWidget);
+ d->actionToExecuteListView->installEventFilter(this); //to be able to disable painting
+ d->actionToExecuteListView->viewport()->installEventFilter(this); //to be able to disable painting
+ d->actionToExecuteListView->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
+ connect(d->actionToExecuteListView, SIGNAL(executed(QListViewItem*)),
+ this, SLOT(slotActionToExecuteItemExecuted(QListViewItem*)));
+ connect(d->actionToExecuteListView, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(slotActionToExecuteItemSelected(QListViewItem*)));
+ d->secondAnd3rdColumnGrLyr->addWidget(d->actionToExecuteListView, 1, 1);
+
+ d->actionToExecuteLbl = new QLabel(d->actionToExecuteListView,
+ i18n("Action to execute:"), d->secondAnd3rdColumnMainWidget);
+ d->actionToExecuteLbl->installEventFilter(this); //to be able to disable painting
+ d->actionToExecuteLbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ d->actionToExecuteLbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak);
+ d->secondAnd3rdColumnGrLyr->addWidget(d->actionToExecuteLbl, 0, 1, Qt::AlignTop|Qt::AlignLeft);
+
+ // temporary show all sections to avoid resizing the dialog in the future
+ d->actionCategoriesListView->selectAction("table");
+ d->setActionToExecuteSectionVisible(true);
+ adjustSize();
+ resize(QMAX(700, width()), QMAX(450, height()));
+ d->actionToExecuteListView->updateWidth();
+
+ bool ok;
+ QString actionType, actionArg;
+ KexiPart::Info* partInfo = action.decodeString(actionType, actionArg, ok);
+ if (ok) {
+ d->actionCategoriesListView->selectAction(actionType);
+ if (actionType=="kaction") {
+ d->kactionListView->selectAction(actionArg);
+ d->kactionListView->setFocus();
+ }
+ else if (actionType=="currentForm") {
+ d->currentFormActionsListView->selectAction(actionArg);
+ d->currentFormActionsListView->setFocus();
+ }
+ else if (partInfo
+ && Kexi::partManager().part(partInfo)) // We use the Part Manager
+ // to determine whether the Kexi-plugin is installed and whether we like to show
+ // it in our list of actions.
+ {
+ KexiPart::Item *item = d->mainWin->project()->item(partInfo, actionArg);
+ if (d->objectsListView && item) {
+ d->objectsListView->selectItem(*item);
+ QString actionOption( action.option );
+ if (actionOption.isEmpty())
+ actionOption = "open"; // for backward compatibility
+ d->actionToExecuteListView->selectAction(actionOption);
+ d->objectsListView->setFocus();
+ }
+ }
+ }
+ else {//invalid assignment or 'noaction'
+ d->actionCategoriesListView->selectAction("noaction");
+ d->actionCategoriesListView->setFocus();
+ }
+}
+
+KexiActionSelectionDialog::~KexiActionSelectionDialog()
+{
+ delete d;
+}
+
+void KexiActionSelectionDialog::slotKActionItemExecuted(QListViewItem*)
+{
+ accept();
+}
+
+void KexiActionSelectionDialog::slotKActionItemSelected(QListViewItem*)
+{
+ d->setActionToExecuteSectionVisible(false);
+ updateOKButtonStatus();
+}
+
+void KexiActionSelectionDialog::slotCurrentFormActionItemExecuted(QListViewItem*)
+{
+ accept();
+}
+
+void KexiActionSelectionDialog::slotCurrentFormActionItemSelected(QListViewItem*)
+{
+ d->setActionToExecuteSectionVisible(false);
+ updateOKButtonStatus();
+}
+
+void KexiActionSelectionDialog::slotItemForOpeningOrExecutingSelected(KexiPart::Item* item)
+{
+ d->setActionToExecuteSectionVisible(item);
+}
+
+void KexiActionSelectionDialog::slotActionToExecuteItemExecuted(QListViewItem* item)
+{
+ if (!item)
+ return;
+ ActionSelectorDialogListItemBase *listItem = dynamic_cast<ActionSelectorDialogListItemBase*>(item);
+ if (listItem && !listItem->data.isEmpty())
+ accept();
+}
+
+void KexiActionSelectionDialog::slotActionToExecuteItemSelected(QListViewItem*)
+{
+ updateOKButtonStatus();
+}
+
+void KexiActionSelectionDialog::slotActionCategorySelected(QListViewItem* item)
+{
+ ActionSelectorDialogListItem *simpleItem = dynamic_cast<ActionSelectorDialogListItem*>(item);
+ // simple case: part-less item, e.g. kaction:
+ if (simpleItem) {
+ d->updateSelectActionToBeExecutedMessage(simpleItem->data);
+ QString selectActionToBeExecutedMsg(
+ i18n("&Select action to be executed after clicking \"%1\" button:")); // msg for a label
+ if (simpleItem->data == "kaction") {
+ if (!d->kactionPageWidget) {
+ //create lbl+list view with a vlayout
+ d->kactionPageWidget = new QWidget();
+ d->kactionPageWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ QVBoxLayout *vlyr = new QVBoxLayout(d->kactionPageWidget, 0, KDialog::spacingHint());
+ d->kactionListView = new KActionsListView(d->kactionPageWidget, d->mainWin);
+ d->kactionListView->init();
+ QLabel *lbl = new QLabel(d->kactionListView, selectActionToBeExecutedMsg.arg(d->actionWidgetName),
+ d->kactionPageWidget);
+ lbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak);
+ lbl->setMinimumHeight(lbl->fontMetrics().height()*2);
+ vlyr->addWidget(lbl);
+ vlyr->addWidget(d->kactionListView);
+ d->secondAnd3rdColumnStack->addWidget(d->kactionPageWidget);
+ connect(d->kactionListView, SIGNAL(executed(QListViewItem*)),
+ this, SLOT(slotKActionItemExecuted(QListViewItem*)));
+ connect( d->kactionListView, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(slotKActionItemSelected(QListViewItem*)));
+ }
+ d->setActionToExecuteSectionVisible(false);
+ d->raiseWidget(d->kactionPageWidget);
+ slotKActionItemSelected(d->kactionListView->selectedItem()); //to refresh column #3
+ }
+ else if (simpleItem->data == "currentForm") {
+ if (!d->currentFormActionsPageWidget) {
+ //create lbl+list view with a vlayout
+ d->currentFormActionsPageWidget = new QWidget();
+ d->currentFormActionsPageWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ QVBoxLayout *vlyr = new QVBoxLayout(d->currentFormActionsPageWidget, 0, KDialog::spacingHint());
+ d->currentFormActionsListView = new CurrentFormActionsListView(
+ d->currentFormActionsPageWidget, d->mainWin);
+ d->currentFormActionsListView->init();
+ QLabel *lbl = new QLabel(d->currentFormActionsListView,
+ selectActionToBeExecutedMsg.arg(d->actionWidgetName), d->currentFormActionsPageWidget);
+ lbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak);
+ lbl->setMinimumHeight(lbl->fontMetrics().height()*2);
+ vlyr->addWidget(lbl);
+ vlyr->addWidget(d->currentFormActionsListView);
+ d->secondAnd3rdColumnStack->addWidget(d->currentFormActionsPageWidget);
+ connect(d->currentFormActionsListView, SIGNAL(executed(QListViewItem*)),
+ this, SLOT(slotCurrentFormActionItemExecuted(QListViewItem*)));
+ connect( d->currentFormActionsListView, SIGNAL(selectionChanged(QListViewItem*)),
+ this, SLOT(slotCurrentFormActionItemSelected(QListViewItem*)));
+ }
+ d->setActionToExecuteSectionVisible(false);
+ d->raiseWidget(d->currentFormActionsPageWidget);
+ slotCurrentFormActionItemSelected(d->currentFormActionsListView->selectedItem()); //to refresh column #3
+ }
+ else if (simpleItem->data == "noaction") {
+ d->raiseWidget(d->emptyWidget);
+ d->objectsListView->clearSelection();
+ //hide column #3
+ d->setActionToExecuteSectionVisible(false);
+ }
+ d->actionCategoriesListView->update();
+ updateOKButtonStatus();
+ return;
+ }
+ // other case
+ KexiBrowserItem* browserItem = dynamic_cast<KexiBrowserItem*>(item);
+ if (browserItem) {
+ d->updateSelectActionToBeExecutedMessage(browserItem->info()->objectName());
+ if (d->objectsListView->itemsMimeType().latin1()!=browserItem->info()->mimeType()) {
+ d->objectsListView->setProject(d->mainWin->project(), browserItem->info()->mimeType());
+ d->actionToExecuteListView->showActionsForMimeType( browserItem->info()->mimeType() );
+ d->setActionToExecuteSectionVisible(false);
+ }
+ if (d->secondAnd3rdColumnStack->visibleWidget()!=d->secondAnd3rdColumnMainWidget) {
+ d->raiseWidget( d->secondAnd3rdColumnMainWidget );
+ d->objectsListView->clearSelection();
+ d->setActionToExecuteSectionVisible(false, true);
+ }
+ else
+ d->raiseWidget( d->secondAnd3rdColumnMainWidget );
+ }
+ d->actionCategoriesListView->update();
+ updateOKButtonStatus();
+}
+
+KexiMainWindow* KexiActionSelectionDialog::mainWin() const
+{
+ return d->mainWin;
+}
+
+KexiFormEventAction::ActionData KexiActionSelectionDialog::currentAction() const
+{
+ KexiFormEventAction::ActionData data;
+ ActionSelectorDialogListItem *simpleItem = dynamic_cast<ActionSelectorDialogListItem*>(
+ d->actionCategoriesListView->selectedItem());
+ // simple case: part-less item, e.g. kaction:
+ if (simpleItem) {
+ if (simpleItem->data == "kaction") {
+ if (d->kactionListView->selectedItem()) {
+ data.string = QString("kaction:")
+ + dynamic_cast<ActionSelectorDialogListItem*>( d->kactionListView->selectedItem() )->data;
+ return data;
+ }
+ }
+ else if (simpleItem->data == "currentForm") {
+ if (d->currentFormActionsListView->selectedItem()) {
+ data.string = QString("currentForm:")
+ + dynamic_cast<ActionSelectorDialogListItem*>(
+ d->currentFormActionsListView->selectedItem() )->data;
+ return data;
+ }
+ }
+ }
+ KexiBrowserItem* browserItem = dynamic_cast<KexiBrowserItem*>( d->actionCategoriesListView->selectedItem() );
+ if (browserItem) {
+ ActionSelectorDialogListItem *actionToExecute = dynamic_cast<ActionSelectorDialogListItem*>(
+ d->actionToExecuteListView->selectedItem());
+ if (d->objectsListView && actionToExecute && !actionToExecute->data.isEmpty()) {
+ KexiPart::Item* partItem = d->objectsListView->selectedPartItem();
+ KexiPart::Info* partInfo = partItem ? Kexi::partManager().infoForMimeType( partItem->mimeType() ) : 0;
+ if (partInfo) {
+ // opening or executing: table:name, query:name, form:name, macro:name, script:name, etc.
+ data.string = QString("%1:%2").arg(partInfo->objectName()).arg(partItem->name());
+ data.option = actionToExecute->data;
+ return data;
+ }
+ }
+ }
+ return data; // No Action
+}
+
+void KexiActionSelectionDialog::updateOKButtonStatus()
+{
+ QPushButton *btn = actionButton(Ok);
+ ActionSelectorDialogListItem *simpleItem = dynamic_cast<ActionSelectorDialogListItem*>(
+ d->actionCategoriesListView->selectedItem());
+ btn->setEnabled( (simpleItem && simpleItem->data == "noaction") || !currentAction().isEmpty() );
+}
+
+bool KexiActionSelectionDialog::eventFilter(QObject *o, QEvent *e)
+{
+ if (d->hideActionToExecuteListView)
+ return true;
+ return KDialogBase::eventFilter(o, e);
+}
+
+#include "kexiactionselectiondialog.moc"
+#include "kexiactionselectiondialog_p.moc"
diff --git a/kexi/plugins/forms/kexiactionselectiondialog.h b/kexi/plugins/forms/kexiactionselectiondialog.h
new file mode 100644
index 00000000..6b6a896b
--- /dev/null
+++ b/kexi/plugins/forms/kexiactionselectiondialog.h
@@ -0,0 +1,71 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIACTIONSELECTIONDIALOG_H
+#define KEXIACTIONSELECTIONDIALOG_H
+
+#include <kdialogbase.h>
+#include "kexiformeventhandler.h"
+
+class KexiMainWindow;
+class KListView;
+namespace KexiPart {
+ class Item;
+}
+
+//! @short A dialog for selecting an action to be executed for a form's command button
+/*! Available actions are:
+ - application's global actions like "edit->copy" (KAction-based)
+ - opening/printing/executing of selected object (table/query/form/script/macrto, etc.)
+*/
+class KEXIFORMUTILS_EXPORT KexiActionSelectionDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ KexiActionSelectionDialog(KexiMainWindow* mainWin, QWidget *parent,
+ const KexiFormEventAction::ActionData& action, const QCString& actionWidgetName);
+ ~KexiActionSelectionDialog();
+
+ /*! \return selected action data or empty action if dialog has been rejected
+ or "No action" has been selected. */
+ KexiFormEventAction::ActionData currentAction() const;
+
+ //! \return the \a KexiMainWindow instance.
+ KexiMainWindow* mainWin() const;
+
+ virtual bool eventFilter(QObject *o, QEvent *e);
+
+ protected slots:
+ void slotActionCategorySelected(QListViewItem* item);
+ void slotKActionItemExecuted(QListViewItem*);
+ void slotKActionItemSelected(QListViewItem*);
+ void slotActionToExecuteItemExecuted(QListViewItem* item);
+ void slotActionToExecuteItemSelected(QListViewItem*);
+ void slotCurrentFormActionItemExecuted(QListViewItem*);
+ void slotCurrentFormActionItemSelected(QListViewItem*);
+ void slotItemForOpeningOrExecutingSelected(KexiPart::Item* item);
+
+ protected:
+ void updateOKButtonStatus();
+
+ class KexiActionSelectionDialogPrivate;
+ KexiActionSelectionDialogPrivate* d;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexiactionselectiondialog_p.h b/kexi/plugins/forms/kexiactionselectiondialog_p.h
new file mode 100644
index 00000000..51f5c369
--- /dev/null
+++ b/kexi/plugins/forms/kexiactionselectiondialog_p.h
@@ -0,0 +1,51 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIACTIONSELECTIONDIALOG_P_H
+#define KEXIACTIONSELECTIONDIALOG_P_H
+
+#include <klistview.h>
+
+//! @internal
+class ActionsListViewBase : public KListView
+{
+ public:
+ ActionsListViewBase(QWidget* parent);
+ virtual ~ActionsListViewBase();
+
+ //! \return item for action \a actionName
+ virtual QListViewItem *itemForAction(const QString& actionName);
+ void selectAction(const QString& actionName);
+};
+
+//! @internal Used by KActionsListView and CurrentFormActionsListView (in column 2)
+class KActionsListViewBase : public ActionsListViewBase
+{
+ Q_OBJECT
+ public:
+ KActionsListViewBase(QWidget* parent, KexiMainWindow* mainWin);
+ virtual ~KActionsListViewBase();
+ void init();
+ virtual bool isActionVisible(const char* actionName, int actionCategories) const = 0;
+
+ protected:
+ KexiMainWindow* m_mainWin;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexidataawarewidgetinfo.cpp b/kexi/plugins/forms/kexidataawarewidgetinfo.cpp
new file mode 100644
index 00000000..a6033c70
--- /dev/null
+++ b/kexi/plugins/forms/kexidataawarewidgetinfo.cpp
@@ -0,0 +1,43 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidataawarewidgetinfo.h"
+
+KexiDataAwareWidgetInfo::KexiDataAwareWidgetInfo(KFormDesigner::WidgetFactory *f)
+ : KFormDesigner::WidgetInfo(f)
+{
+ init();
+}
+
+KexiDataAwareWidgetInfo::KexiDataAwareWidgetInfo(KFormDesigner::WidgetFactory *f,
+ const char* parentFactoryName, const char* inheritedClassName)
+ : KFormDesigner::WidgetInfo(f, parentFactoryName, inheritedClassName)
+{
+ init();
+}
+
+KexiDataAwareWidgetInfo::~KexiDataAwareWidgetInfo()
+{
+}
+
+void KexiDataAwareWidgetInfo::init()
+{
+ setAutoSyncForProperty( "dataSource", false );
+ setAutoSyncForProperty( "dataSourceMimeType", false );
+}
diff --git a/kexi/plugins/forms/kexidataawarewidgetinfo.h b/kexi/plugins/forms/kexidataawarewidgetinfo.h
new file mode 100644
index 00000000..41e67d85
--- /dev/null
+++ b/kexi/plugins/forms/kexidataawarewidgetinfo.h
@@ -0,0 +1,44 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIDATAAWAREWIDGETINFO_H
+#define KEXIDATAAWAREWIDGETINFO_H
+
+#include <formeditor/widgetfactory.h>
+
+//! A widget info for data-aware widgets
+/*! Used within factories just like KFormDesigner::WidgetInfo,
+ but also predefines specific behaviour,
+ e.g. sets autoSync flag to false for "dataSource" property.
+*/
+class KEXIFORMUTILS_EXPORT KexiDataAwareWidgetInfo : public KFormDesigner::WidgetInfo
+{
+ public:
+ KexiDataAwareWidgetInfo(KFormDesigner::WidgetFactory *f);
+
+ KexiDataAwareWidgetInfo(KFormDesigner::WidgetFactory *f,
+ const char* parentFactoryName, const char* inheritedClassName = 0);
+
+ virtual ~KexiDataAwareWidgetInfo();
+
+ protected:
+ void init();
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexidataprovider.cpp b/kexi/plugins/forms/kexidataprovider.cpp
new file mode 100644
index 00000000..6706f838
--- /dev/null
+++ b/kexi/plugins/forms/kexidataprovider.cpp
@@ -0,0 +1,315 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidataprovider.h"
+
+#include <qwidget.h>
+#include <qobjectlist.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <widget/tableview/kexitableitem.h>
+#include <widget/tableview/kexitableviewdata.h>
+#include <widget/tableview/kexicomboboxbase.h>
+#include <kexidb/queryschema.h>
+#include <kexiutils/utils.h>
+
+#include "widgets/kexidbform.h"
+
+KexiFormDataProvider::KexiFormDataProvider()
+ : KexiDataItemChangesListener()
+ , m_mainWidget(0)
+ , m_duplicatedItems(0)
+ , m_disableFillDuplicatedDataItems(false)
+{
+}
+
+KexiFormDataProvider::~KexiFormDataProvider()
+{
+ delete m_duplicatedItems;
+}
+
+void KexiFormDataProvider::setMainDataSourceWidget(QWidget* mainWidget)
+{
+ m_mainWidget = mainWidget;
+ m_dataItems.clear();
+ m_usedDataSources.clear();
+ m_fieldNumbersForDataItems.clear();
+ if (!m_mainWidget)
+ return;
+
+ //find widgets whose will work as data items
+ QObjectList *l = m_mainWidget->queryList( "QWidget" );
+ QObjectListIt it( *l );
+ QObject *obj;
+ QDict<char> tmpSources;
+ for ( ; (obj = it.current()) != 0; ++it ) {
+ KexiFormDataItemInterface* const formDataItem = dynamic_cast<KexiFormDataItemInterface*>(obj);
+ if (!formDataItem)
+ continue;
+ if (formDataItem->parentInterface()) //item with parent interface: collect parent instead...
+ continue;
+#if 0 //! @todo reenable when subform is moved to KexiDBForm
+ KexiDBForm *dbForm = KexiUtils::findParent<KexiDBForm>(obj, "KexiDBForm"); //form's surface...
+ if (dbForm!=m_mainWidget) //only set data for this form's data items
+ continue;
+#else
+ //tmp: reject widgets within subforms
+ if (KexiUtils::findParent<KexiDBForm>(obj, "KexiDBSubForm"))
+ continue;
+#endif
+ QString dataSource( formDataItem->dataSource().lower() );
+ if (dataSource.isEmpty())
+ continue;
+ kexipluginsdbg << obj->name() << endl;
+ m_dataItems.append( formDataItem );
+ formDataItem->installListener( this );
+ tmpSources.replace( dataSource, (char*)1 );
+ }
+ delete l;
+ //now we've got a set (unique list) of field names in tmpSources
+ //remember it in m_usedDataSources
+ for (QDictIterator<char> it(tmpSources); it.current(); ++it) {
+ m_usedDataSources += it.currentKey();
+ }
+}
+
+void KexiFormDataProvider::fillDataItems(KexiTableItem& row, bool cursorAtNewRow)
+{
+ kexipluginsdbg << "KexiFormDataProvider::fillDataItems() cnt=" << row.count() << endl;
+ for (KexiFormDataItemInterfaceToIntMap::ConstIterator it = m_fieldNumbersForDataItems.constBegin();
+ it!=m_fieldNumbersForDataItems.constEnd(); ++it)
+ {
+ KexiFormDataItemInterface *itemIface = it.key();
+ if (!itemIface->columnInfo()) {
+ kexipluginsdbg << "KexiFormDataProvider::fillDataItems(): itemIface->columnInfo() == 0" << endl;
+ continue;
+ }
+ //1. Is this a value with a combo box (lookup)?
+ int indexForVisibleLookupValue = itemIface->columnInfo()->indexForVisibleLookupValue();
+ if (indexForVisibleLookupValue<0 && indexForVisibleLookupValue>=(int)row.count()) //sanity
+ indexForVisibleLookupValue = -1; //no
+ const QVariant value(row.at(it.data()));
+ QVariant visibleLookupValue;
+ if (indexForVisibleLookupValue!=-1 && (int)row.size()>indexForVisibleLookupValue)
+ visibleLookupValue = row.at(indexForVisibleLookupValue);
+ kexipluginsdbg << "fill data of '" << itemIface->dataSource() << "' at idx=" << it.data()
+ << " data=" << value << (indexForVisibleLookupValue!=-1
+ ? QString(" SPECIAL: indexForVisibleLookupValue=%1 visibleValue=%2")
+ .arg(indexForVisibleLookupValue).arg(visibleLookupValue.toString())
+ : QString::null)
+ << endl;
+ const bool displayDefaultValue = cursorAtNewRow && (value.isNull() && visibleLookupValue.isNull())
+ && !itemIface->columnInfo()->field->defaultValue().isNull()
+ && !itemIface->columnInfo()->field->isAutoIncrement(); //no value to set but there is default value defined
+ itemIface->setValue(
+ displayDefaultValue ? itemIface->columnInfo()->field->defaultValue() : value,
+ QVariant(), /*add*/
+ /*!remove old*/false,
+ indexForVisibleLookupValue==-1 ? 0 : &visibleLookupValue //pass visible value if available
+ );
+ // now disable/enable "display default value" if needed (do it after setValue(), before setValue() turns it off)
+ if (itemIface->hasDisplayedDefaultValue() != displayDefaultValue)
+ itemIface->setDisplayDefaultValue( dynamic_cast<QWidget*>(itemIface), displayDefaultValue );
+ }
+}
+
+void KexiFormDataProvider::fillDuplicatedDataItems(
+ KexiFormDataItemInterface* item, const QVariant& value)
+{
+ if (m_disableFillDuplicatedDataItems)
+ return;
+ if (!m_duplicatedItems) {
+ //build (once) a set of duplicated data items (having the same fields assigned)
+ //so we can later check if an item is duplicated with a cost of o(1)
+ QMap<KexiDB::Field*,int> tmpDuplicatedItems;
+ QMapIterator<KexiDB::Field*,int> it_dup;
+ for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
+ if (!it.current()->columnInfo() || !it.current()->columnInfo()->field)
+ continue;
+ kdDebug() << " ** " << it.current()->columnInfo()->field->name() << endl;
+ it_dup = tmpDuplicatedItems.find( it.current()->columnInfo()->field );
+ uint count;
+ if (it_dup==tmpDuplicatedItems.end())
+ count = 0;
+ else
+ count = it_dup.data();
+ tmpDuplicatedItems.insert( it.current()->columnInfo()->field, ++count );
+ }
+ m_duplicatedItems = new QPtrDict<char>(101);
+ for (it_dup = tmpDuplicatedItems.begin(); it_dup!=tmpDuplicatedItems.end(); ++it_dup) {
+ if (it_dup.data() > 1) {
+ m_duplicatedItems->insert( it_dup.key(), (char*)1 );
+ kexipluginsdbg << "duplicated item: " << static_cast<KexiDB::Field*>(it_dup.key())->name()
+ << " (" << it_dup.data() << " times)" << endl;
+ }
+ }
+ }
+ if (item->columnInfo() && m_duplicatedItems->find( item->columnInfo()->field )) {
+ for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
+ if (it.current()!=item && item->columnInfo()->field == it.current()->columnInfo()->field) {
+ kexipluginsdbg << "- setting a copy of value for item '"
+ << dynamic_cast<QObject*>(it.current())->name() << "' == " << value << endl;
+ it.current()->setValue( value );
+ }
+ }
+ }
+}
+
+void KexiFormDataProvider::valueChanged(KexiDataItemInterface* item)
+{
+ Q_UNUSED( item );
+}
+
+bool KexiFormDataProvider::cursorAtNewRow() const
+{
+ return false;
+}
+
+void KexiFormDataProvider::invalidateDataSources( const QDict<char>& invalidSources,
+ KexiDB::QuerySchema* query)
+{
+ //fill m_fieldNumbersForDataItems mapping from data item to field number
+ //(needed for fillDataItems)
+ KexiDB::QueryColumnInfo::Vector fieldsExpanded;
+// uint dataFieldsCount; // == fieldsExpanded.count() if query is available or else == m_dataItems.count()
+
+ if (query) {
+ fieldsExpanded = query->fieldsExpanded( KexiDB::QuerySchema::WithInternalFields );
+// dataFieldsCount = fieldsExpanded.count();
+ QMap<KexiDB::QueryColumnInfo*,int> columnsOrder( query->columnsOrder() );
+ for (QMapConstIterator<KexiDB::QueryColumnInfo*,int> it = columnsOrder.constBegin(); it!=columnsOrder.constEnd(); ++it) {
+ kexipluginsdbg << "query->columnsOrder()[ " << it.key()->field->name() << " ] = " << it.data() << endl;
+ }
+ for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
+ KexiFormDataItemInterface *item = it.current();
+ KexiDB::QueryColumnInfo* ci = query->columnInfo( it.current()->dataSource() );
+ int index = ci ? columnsOrder[ ci ] : -1;
+ kexipluginsdbg << "query->columnsOrder()[ " << (ci ? ci->field->name() : "") << " ] = " << index
+ << " (dataSource: " << item->dataSource() << ", name=" << dynamic_cast<QObject*>(item)->name() << ")" << endl;
+ if (index!=-1 && !m_fieldNumbersForDataItems[ item ])
+ m_fieldNumbersForDataItems.insert( item, index );
+ //todo
+ //WRONG: not only used data sources can be fetched!
+ // m_fieldNumbersForDataItems.insert( it.current(),
+ // m_usedDataSources.findIndex(it.current()->dataSource().lower()) );
+ }
+ }
+ else {//!query
+// dataFieldsCount = m_dataItems.count();
+ }
+
+#if 0 //moved down
+ //in 'newIndices' let's collect new indices for every data source
+ foreach(QValueList<uint>::ConstIterator, it, invalidSources) {
+ //all previous indices have corresponding data source
+// for (; i < (*it); i++) {
+// newIndices[i] = number++;
+ //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl;
+// }
+ //this index have no corresponding data source
+// newIndices[i]=-1;
+ KexiFormDataItemInterface *item = m_dataItems.at( *it );
+ if (item)
+ item->setInvalidState( QString::fromLatin1("#") + i18n("NAME") + QString::fromLatin1("?") );
+ m_dataItems.remove(*it);
+ kexipluginsdbg << "invalidateDataSources(): " << (*it) << " -> " << -1 << endl;
+// i++;
+ }
+#endif
+ //fill remaining part of the vector
+// for (; i < dataFieldsCount; i++) { //m_dataItems.count(); i++) {
+ //newIndices[i] = number++;
+ //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl;
+ //}
+
+#if 0
+ //recreate m_fieldNumbersForDataItems and mark widgets with invalid data sources
+ KexiFormDataItemInterfaceToIntMap newFieldNumbersForDataItems;
+ foreach(KexiFormDataItemInterfaceToIntMap::ConstIterator, it, m_fieldNumbersForDataItems) {
+ bool ok;
+ const int newIndex = newIndices.at( it.data(), &ok );
+ if (ok && newIndex!=-1) {
+ kexipluginsdbg << "invalidateDataSources(): " << it.key()->dataSource() << ": " << it.data() << " -> " << newIndex << endl;
+ newFieldNumbersForDataItems.replace(it.key(), newIndex);
+ }
+ else {
+ kexipluginsdbg << "invalidateDataSources(): removing " << it.key()->dataSource() << endl;
+ m_dataItems.remove(it.key());
+ it.key()->setInvalidState( QString::fromLatin1("#") + i18n("NAME") + QString::fromLatin1("?") );
+ }
+ }
+#endif
+// m_fieldNumbersForDataItems = newFieldNumbersForDataItems;
+
+ //update data sources set (some of them may be removed)
+ QDict<char> tmpUsedDataSources(1013);
+
+ if (query)
+ query->debug();
+
+ //if (query && m_dataItems.count()!=query->fieldCount()) {
+ // kdWarning() << "KexiFormDataProvider::invalidateDataSources(): m_dataItems.count()!=query->fieldCount() ("
+ // << m_dataItems.count() << "," << query->fieldCount() << ")" << endl;
+ //}
+ //i = 0;
+ m_disableFillDuplicatedDataItems = true; // temporary disable fillDuplicatedDataItems()
+ // because setColumnInfo() can activate it
+ for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current();) {
+ KexiFormDataItemInterface * item = it.current();
+ if (invalidSources[ item->dataSource().lower() ]) {
+ item->setInvalidState( QString::fromLatin1("#") + i18n("NAME") + QString::fromLatin1("?") );
+ m_dataItems.remove(item);
+ continue;
+ }
+ uint fieldNumber = m_fieldNumbersForDataItems[ item ];
+ if (query) {
+ KexiDB::QueryColumnInfo *ci = fieldsExpanded[fieldNumber];
+ item->setColumnInfo(ci);
+ kexipluginsdbg << "- item=" << dynamic_cast<QObject*>(item)->name()
+ << " dataSource=" << item->dataSource()
+ << " field=" << ci->field->name() << endl;
+ const int indexForVisibleLookupValue = ci->indexForVisibleLookupValue();
+ if (-1 != indexForVisibleLookupValue && indexForVisibleLookupValue < (int)fieldsExpanded.count()) {
+ //there's lookup column defined: set visible column as well
+ KexiDB::QueryColumnInfo *visibleColumnInfo = fieldsExpanded[ indexForVisibleLookupValue ];
+ if (visibleColumnInfo) {
+ item->setVisibleColumnInfo( visibleColumnInfo );
+ if (dynamic_cast<KexiComboBoxBase*>(item) && m_mainWidget
+ && dynamic_cast<KexiComboBoxBase*>(item)->internalEditor())
+ {
+ // m_mainWidget (dbform) should filter the (just created using setVisibleColumnInfo())
+ // combo box' internal editor (actually, only if the combo is in 'editable' mode)
+ dynamic_cast<KexiComboBoxBase*>(item)->internalEditor()->installEventFilter(m_mainWidget);
+ }
+ kexipluginsdbg << " ALSO SET visibleColumn=" << visibleColumnInfo->debugString()
+ << "\n at position " << indexForVisibleLookupValue << endl;
+ }
+ }
+ }
+ tmpUsedDataSources.replace( item->dataSource().lower(), (char*)1 );
+ ++it;
+ }
+ m_disableFillDuplicatedDataItems = false;
+ m_usedDataSources.clear();
+ foreach_list(QDictIterator<char>, it, tmpUsedDataSources) {
+ m_usedDataSources += it.currentKey();
+ }
+}
diff --git a/kexi/plugins/forms/kexidataprovider.h b/kexi/plugins/forms/kexidataprovider.h
new file mode 100644
index 00000000..64019842
--- /dev/null
+++ b/kexi/plugins/forms/kexidataprovider.h
@@ -0,0 +1,95 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIFORMDATAPROVIDER_H
+#define KEXIFORMDATAPROVIDER_H
+
+#include "kexiformdataiteminterface.h"
+#include <qptrdict.h>
+#include <qdict.h>
+
+class KexiTableItem;
+namespace KexiDB {
+ class QuerySchema;
+}
+
+//! @short The KexiFormDataProvider class is a data provider for Kexi Forms
+/*! This provider collects data-aware widgets using setMainWidget().
+ Then, usedDataSources() unique list of required field names is available.
+ On every call of fillDataItems() method, the provider will fill data items
+ with appropriate data from a database cursor.
+
+ Field names are collected effectively, so eg. having widgets using data sources:
+ ("name", "surname", "surname", "name") - "name" and "surname" repeated - will only
+ return ("name", "surname") list, so the cursor's query can be simplified
+ and thus more effective.
+*/
+class KEXIFORMUTILS_EXPORT KexiFormDataProvider : public KexiDataItemChangesListener
+{
+ public:
+ KexiFormDataProvider();
+ virtual ~KexiFormDataProvider();
+
+ /*! sets \a mainWidget to be a main widget for this data provider.
+ Also find widgets whose will work as data items
+ (all of them must implement KexiFormDataItemInterface), so these could be
+ filled with data on demand. */
+ void setMainDataSourceWidget(QWidget* mainWidget);
+
+ QStringList usedDataSources() const { return m_usedDataSources; }
+
+ //unused QPtrList<KexiFormDataItemInterface>& dataItems() { return m_dataItems; }
+
+ /*! Fills data items with appropriate data fetched from \a cursor.
+ \a newRowEditing == true means that we are at new (not yet inserted) database row. */
+ void fillDataItems(KexiTableItem& row, bool cursorAtNewRow);
+
+ /*! Implementation for KexiDataItemChangesListener.
+ Reaction for change of \a item. Does nothing here. */
+ virtual void valueChanged(KexiDataItemInterface* item);
+
+ /*! Implementation for KexiDataItemChangesListener.
+ Implement this to return information whether we're currently at new row or now.
+ This can be used e.g. by data-aware widgets to determine if "(autonumber)"
+ label should be displayed. Returns false here. */
+ virtual bool cursorAtNewRow() const;
+
+ /*! Invalidates data sources collected by this provided.
+ \a invalidSources is the set of data sources that should
+ be omitted for fillDataItems().
+ Used by KexiFormView::initDataSource(). */
+ void invalidateDataSources( const QDict<char>& invalidSources,
+ KexiDB::QuerySchema* query = 0 );
+
+ /*! Fills the same data provided by \a value to every data item (other than \a item)
+ having the same data source as \a item. This method is called immediately when
+ \a value is changed, so duplicated data items are quickly updated. */
+ void fillDuplicatedDataItems(KexiFormDataItemInterface* item, const QVariant& value);
+
+ protected:
+ QWidget *m_mainWidget;
+ QPtrDict<char> *m_duplicatedItems;
+ typedef QMap<KexiFormDataItemInterface*,uint> KexiFormDataItemInterfaceToIntMap;
+ QPtrList<KexiFormDataItemInterface> m_dataItems;
+ QStringList m_usedDataSources;
+ KexiFormDataItemInterfaceToIntMap m_fieldNumbersForDataItems;
+ bool m_disableFillDuplicatedDataItems : 1;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexidatasourcepage.cpp b/kexi/plugins/forms/kexidatasourcepage.cpp
new file mode 100644
index 00000000..6c0de830
--- /dev/null
+++ b/kexi/plugins/forms/kexidatasourcepage.cpp
@@ -0,0 +1,471 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidatasourcepage.h"
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qheader.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+#include <ktoolbarbutton.h>
+#include <kdebug.h>
+#include <kpopupmenu.h>
+
+#include <widget/kexipropertyeditorview.h>
+#include <widget/kexidatasourcecombobox.h>
+#include <widget/kexifieldlistview.h>
+#include <widget/kexifieldcombobox.h>
+#include <widget/kexismalltoolbutton.h>
+#include <kexidb/connection.h>
+#include <kexiproject.h>
+
+#include <formeditor/commands.h>
+
+#include <koproperty/property.h>
+#include <koproperty/utils.h>
+
+KexiDataSourcePage::KexiDataSourcePage(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+ , m_insideClearDataSourceSelection(false)
+{
+ QVBoxLayout *vlyr = new QVBoxLayout(this);
+ m_objectInfoLabel = new KexiObjectInfoLabel(this, "KexiObjectInfoLabel");
+ vlyr->addWidget(m_objectInfoLabel);
+
+ m_noDataSourceAvailableSingleText = i18n("No data source could be assigned for this widget.");
+ m_noDataSourceAvailableMultiText = i18n("No data source could be assigned for multiple widgets.");
+
+ vlyr->addSpacing(8);
+
+ //Section 1: Form's/Widget's Data Source
+ KoProperty::GroupContainer *container = new KoProperty::GroupContainer(i18n("Data Source"), this);
+ vlyr->addWidget(container);
+
+ QWidget *contents = new QWidget(container);
+ container->setContents(contents);
+ QVBoxLayout *contentsVlyr = new QVBoxLayout(contents);
+
+ m_noDataSourceAvailableLabel = new QLabel(m_noDataSourceAvailableSingleText, contents);
+ m_noDataSourceAvailableLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ m_noDataSourceAvailableLabel->setMargin(2);
+ m_noDataSourceAvailableLabel->setAlignment(Qt::WordBreak | Qt::AlignBottom | Qt::AlignLeft);
+ contentsVlyr->addWidget(m_noDataSourceAvailableLabel);
+
+ //-Widget's Data Source
+ QHBoxLayout *hlyr = new QHBoxLayout(contentsVlyr);
+#if 0
+//! @todo unhide this when expression work
+// m_widgetDSLabel = new QLabel(i18n("Table Field, Query Field or Expression", "Source field or expression:"), this);
+#else
+ m_widgetDSLabel = new QLabel(i18n("Table Field or Query Field", "Widget's data source:"), contents);
+#endif
+ m_widgetDSLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_widgetDSLabel->setMargin(2);
+ m_widgetDSLabel->setMinimumHeight(IconSize(KIcon::Small)+4);
+ m_widgetDSLabel->setAlignment(AlignLeft|AlignBottom);
+ hlyr->addWidget(m_widgetDSLabel);
+
+ m_clearWidgetDSButton = new KexiSmallToolButton(contents, QString::null, "clear_left", "clearWidgetDSButton");
+ m_clearWidgetDSButton->setMinimumHeight(m_widgetDSLabel->minimumHeight());
+ QToolTip::add(m_clearWidgetDSButton, i18n("Clear widget's data source"));
+ hlyr->addWidget(m_clearWidgetDSButton);
+ connect(m_clearWidgetDSButton, SIGNAL(clicked()), this, SLOT(clearWidgetDataSourceSelection()));
+
+ m_sourceFieldCombo = new KexiFieldComboBox(contents, "sourceFieldCombo");
+ m_widgetDSLabel->setBuddy(m_sourceFieldCombo);
+ contentsVlyr->addWidget(m_sourceFieldCombo);
+
+/* m_dataSourceSeparator = new QFrame(contents);
+ m_dataSourceSeparator->setFrameShape(QFrame::HLine);
+ m_dataSourceSeparator->setFrameShadow(QFrame::Sunken);
+ contentsVlyr->addWidget(m_dataSourceSeparator);*/
+
+ contentsVlyr->addSpacing(8);
+
+ //- Form's Data Source
+ hlyr = new QHBoxLayout(contentsVlyr);
+ m_dataSourceLabel = new QLabel(i18n("Form's data source:"), contents);
+ m_dataSourceLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_dataSourceLabel->setMargin(2);
+ m_dataSourceLabel->setMinimumHeight(IconSize(KIcon::Small)+4);
+ m_dataSourceLabel->setAlignment(AlignLeft|AlignBottom);
+ hlyr->addWidget(m_dataSourceLabel);
+
+ m_gotoButton = new KexiSmallToolButton(contents, QString::null, "goto", "gotoButton");
+ m_gotoButton->setMinimumHeight(m_dataSourceLabel->minimumHeight());
+ QToolTip::add(m_gotoButton, i18n("Go to selected form's data source"));
+ hlyr->addWidget(m_gotoButton);
+ connect(m_gotoButton, SIGNAL(clicked()), this, SLOT(slotGotoSelected()));
+
+ m_clearDSButton = new KexiSmallToolButton(contents, QString::null, "clear_left", "clearDSButton");
+ m_clearDSButton->setMinimumHeight(m_dataSourceLabel->minimumHeight());
+ QToolTip::add(m_clearDSButton, i18n("Clear form's data source"));
+ hlyr->addWidget(m_clearDSButton);
+ connect(m_clearDSButton, SIGNAL(clicked()), this, SLOT(clearDataSourceSelection()));
+
+ m_dataSourceCombo = new KexiDataSourceComboBox(contents, "dataSourceCombo");
+ m_dataSourceLabel->setBuddy(m_dataSourceCombo);
+ contentsVlyr->addWidget(m_dataSourceCombo);
+
+#ifdef KEXI_NO_AUTOFIELD_WIDGET
+ m_availableFieldsLabel = 0;
+ m_addField = 0;
+// m_fieldListView = 0;
+ vlyr->addStretch();
+#else
+ vlyr->addSpacing(fontMetrics().height());
+/* QFrame *separator = new QFrame(this);
+ separator->setFrameShape(QFrame::HLine);
+ separator->setFrameShadow(QFrame::Sunken);
+ vlyr->addWidget(separator);*/
+/*
+ KPopupTitle *title = new KPopupTitle(this);
+ title->setTitle(i18n("Inserting fields"));
+ vlyr->addWidget(title);
+ vlyr->addSpacing(4);*/
+
+
+ //2. Inserting fields
+ container = new KoProperty::GroupContainer(i18n("Inserting Fields"), this);
+ vlyr->addWidget(container, 1);
+
+ //helper info
+//! @todo allow to hide such helpers by adding global option
+ contents = new QWidget(container);
+ container->setContents(contents);
+ contentsVlyr = new QVBoxLayout(contents);
+ hlyr = new QHBoxLayout(contentsVlyr);
+ m_mousePointerLabel = new QLabel(contents);
+ hlyr->addWidget(m_mousePointerLabel);
+ m_mousePointerLabel->setPixmap( SmallIcon("mouse_pointer") );
+ m_mousePointerLabel->setFixedWidth( m_mousePointerLabel->pixmap() ? m_mousePointerLabel->pixmap()->width() : 0);
+ m_availableFieldsDescriptionLabel = new QLabel(
+ i18n("Select fields from the list below and drag them onto a form or click the \"Insert\" button"), contents);
+ m_availableFieldsDescriptionLabel->setAlignment( Qt::AlignAuto | Qt::WordBreak );
+ hlyr->addWidget(m_availableFieldsDescriptionLabel);
+
+ //Available Fields
+ contentsVlyr->addSpacing(4);
+ hlyr = new QHBoxLayout(contentsVlyr);
+ m_availableFieldsLabel = new QLabel(i18n("Available fields:"), contents);
+ m_availableFieldsLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ m_availableFieldsLabel->setMargin(2);
+ m_availableFieldsLabel->setMinimumHeight(IconSize(KIcon::Small));
+ hlyr->addWidget(m_availableFieldsLabel);
+
+ m_addField = new KexiSmallToolButton(contents, i18n("Insert selected field into form", "Insert"),
+ "add_field", "addFieldButton");
+ m_addField->setMinimumHeight(m_availableFieldsLabel->minimumHeight());
+// m_addField->setTextPosition(QToolButton::Right);
+ m_addField->setFocusPolicy(StrongFocus);
+ QToolTip::add(m_addField, i18n("Insert selected fields into form"));
+ hlyr->addWidget(m_addField);
+ connect(m_addField, SIGNAL(clicked()), this, SLOT(slotInsertSelectedFields()));
+
+ m_fieldListView = new KexiFieldListView(contents, "fieldListView",
+ KexiFieldListView::ShowDataTypes | KexiFieldListView::AllowMultiSelection );
+ m_fieldListView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
+ m_availableFieldsLabel->setBuddy(m_fieldListView);
+ contentsVlyr->addWidget(m_fieldListView, 1);
+ connect(m_fieldListView, SIGNAL(selectionChanged()), this, SLOT(slotFieldListViewSelectionChanged()));
+ connect(m_fieldListView, SIGNAL(fieldDoubleClicked(const QString&, const QString&, const QString&)),
+ this, SLOT(slotFieldDoubleClicked(const QString&, const QString&, const QString&)));
+#endif
+
+ vlyr->addStretch(1);
+
+ connect(m_dataSourceCombo, SIGNAL(textChanged(const QString &)), this, SLOT(slotDataSourceTextChanged(const QString &)));
+ connect(m_dataSourceCombo, SIGNAL(dataSourceChanged()), this, SLOT(slotDataSourceChanged()));
+ connect(m_sourceFieldCombo, SIGNAL(selected()), this, SLOT(slotFieldSelected()));
+
+ clearDataSourceSelection();
+ slotFieldListViewSelectionChanged();
+}
+
+KexiDataSourcePage::~KexiDataSourcePage()
+{
+}
+
+void KexiDataSourcePage::setProject(KexiProject *prj)
+{
+ m_sourceFieldCombo->setProject(prj);
+ m_dataSourceCombo->setProject(prj);
+}
+
+void KexiDataSourcePage::clearDataSourceSelection(bool alsoClearComboBox)
+{
+ if (m_insideClearDataSourceSelection)
+ return;
+ m_insideClearDataSourceSelection = true;
+ if (alsoClearComboBox && !m_dataSourceCombo->selectedName().isEmpty())
+ m_dataSourceCombo->setDataSource("", "");
+// if (!m_dataSourceCombo->currentText().isEmpty()) {
+// m_dataSourceCombo->setCurrentText("");
+// emit m_dataSourceCombo->dataSourceSelected();
+// }
+ m_clearDSButton->setEnabled(false);
+ m_gotoButton->setEnabled(false);
+#ifndef KEXI_NO_AUTOFIELD_WIDGET
+ m_addField->setEnabled(false);
+ m_fieldListView->clear();
+#endif
+ m_insideClearDataSourceSelection = false;
+}
+
+void KexiDataSourcePage::clearWidgetDataSourceSelection()
+{
+ if (!m_sourceFieldCombo->currentText().isEmpty()) {
+ m_sourceFieldCombo->setCurrentText("");
+ m_sourceFieldCombo->setFieldOrExpression(QString::null);
+ slotFieldSelected();
+ }
+ m_clearWidgetDSButton->setEnabled(false);
+}
+
+void KexiDataSourcePage::slotGotoSelected()
+{
+ QCString mime = m_dataSourceCombo->selectedMimeType().latin1();
+ if (mime=="kexi/table" || mime=="kexi/query") {
+ if (m_dataSourceCombo->isSelectionValid())
+ emit jumpToObjectRequested(mime, m_dataSourceCombo->selectedName().latin1());
+ }
+}
+
+void KexiDataSourcePage::slotInsertSelectedFields()
+{
+#ifndef KEXI_NO_AUTOFIELD_WIDGET
+ QStringList selectedFieldNames(m_fieldListView->selectedFieldNames());
+ if (selectedFieldNames.isEmpty())
+ return;
+
+ emit insertAutoFields(m_fieldListView->schema()->table() ? "kexi/table" : "kexi/query",
+ m_fieldListView->schema()->name(), selectedFieldNames);
+#endif
+}
+
+void KexiDataSourcePage::slotFieldDoubleClicked(const QString& sourceMimeType, const QString& sourceName,
+ const QString& fieldName)
+{
+#ifndef KEXI_NO_AUTOFIELD_WIDGET
+ QStringList selectedFields;
+ selectedFields.append(fieldName);
+ emit insertAutoFields(sourceMimeType, sourceName, selectedFields);
+#endif
+}
+
+void KexiDataSourcePage::slotDataSourceTextChanged(const QString & string)
+{
+ Q_UNUSED(string);
+ const bool enable = m_dataSourceCombo->isSelectionValid(); //!string.isEmpty() && m_dataSourceCombo->selectedName() == string.latin1();
+ if (!enable) {
+ clearDataSourceSelection( m_dataSourceCombo->selectedName().isEmpty()/*alsoClearComboBox*/ );
+ }
+ updateSourceFieldWidgetsAvailability();
+/*#ifndef KEXI_NO_AUTOFIELD_WIDGET
+ m_fieldListView->setEnabled(enable);
+// m_addField->setEnabled(enable);
+ m_availableFieldsLabel->setEnabled(enable);
+#endif*/
+}
+
+void KexiDataSourcePage::slotDataSourceChanged()
+{
+ if (!m_dataSourceCombo->project())
+ return;
+ QCString mime = m_dataSourceCombo->selectedMimeType().latin1();
+ bool dataSourceFound = false;
+ QCString name = m_dataSourceCombo->selectedName().latin1();
+ if ((mime=="kexi/table" || mime=="kexi/query") && m_dataSourceCombo->isSelectionValid()) {
+ KexiDB::TableOrQuerySchema *tableOrQuery = new KexiDB::TableOrQuerySchema(
+ m_dataSourceCombo->project()->dbConnection(), name, mime=="kexi/table");
+ if (tableOrQuery->table() || tableOrQuery->query()) {
+#ifdef KEXI_NO_AUTOFIELD_WIDGET
+ m_tableOrQuerySchema = tableOrQuery;
+#else
+ m_fieldListView->setSchema( tableOrQuery );
+#endif
+ dataSourceFound = true;
+ m_sourceFieldCombo->setTableOrQuery(name, mime=="kexi/table");
+ }
+ else {
+ delete tableOrQuery;
+ }
+ }
+ if (!dataSourceFound) {
+ m_sourceFieldCombo->setTableOrQuery("", true);
+ }
+ //if (m_sourceFieldCombo->hasFocus())
+// m_dataSourceCombo->setFocus();
+ m_clearDSButton->setEnabled(dataSourceFound);
+ m_gotoButton->setEnabled(dataSourceFound);
+ if (dataSourceFound) {
+ slotFieldListViewSelectionChanged();
+ } else {
+#ifndef KEXI_NO_AUTOFIELD_WIDGET
+ m_addField->setEnabled(false);
+#endif
+ }
+ updateSourceFieldWidgetsAvailability();
+ emit formDataSourceChanged(mime, name);
+}
+
+void KexiDataSourcePage::slotFieldSelected()
+{
+ KexiDB::Field::Type dataType = KexiDB::Field::InvalidType;
+#ifdef KEXI_NO_AUTOFIELD_WIDGET
+ KexiDB::Field *field = m_tableOrQuerySchema->field( m_sourceFieldCombo->fieldOrExpression() ); //temp
+#else
+//! @todo this should also work for expressions
+ KexiDB::Field *field = m_fieldListView->schema()->field( m_sourceFieldCombo->fieldOrExpression() );
+#endif
+ if (field)
+ dataType = field->type();
+
+ m_clearWidgetDSButton->setEnabled( !m_sourceFieldCombo->fieldOrExpression().isEmpty() );
+
+ emit dataSourceFieldOrExpressionChanged(
+ m_sourceFieldCombo->fieldOrExpression(),
+ m_sourceFieldCombo->fieldOrExpressionCaption(),
+ dataType
+ );
+}
+
+void KexiDataSourcePage::setDataSource(const QCString& mimeType, const QCString& name)
+{
+ m_dataSourceCombo->setDataSource(mimeType, name);
+}
+
+void KexiDataSourcePage::assignPropertySet(KoProperty::Set* propertySet)
+{
+ QCString objectName;
+ if (propertySet && propertySet->contains("name"))
+ objectName = (*propertySet)["name"].value().toCString();
+ if (!objectName.isEmpty() && objectName == m_currentObjectName)
+ return; //the same object
+ m_currentObjectName = objectName;
+
+ QCString objectClassName;
+ if (propertySet && propertySet->contains("this:className"))
+ objectClassName = (*propertySet)["this:className"].value().toCString();
+/*moved if (propertySet) {
+ QCString iconName;
+ QString objectClassString;
+ if (propertySet->contains("this:iconName"))
+ iconName = (*propertySet)["this:iconName"].value().toCString();
+ if (propertySet->contains("this:classString"))
+ objectClassString = (*propertySet)["this:classString"].value().toString();
+ m_objectInfoLabel->setObjectName(objectName);
+ m_objectInfoLabel->setObjectClassIcon(iconName);
+ m_objectInfoLabel->setObjectClassName(objectClassString);
+ if (propertySet->contains("this:className"))
+ objectClassName = (*propertySet)["this:className"].value().toCString();
+ }*/
+ KexiPropertyEditorView::updateInfoLabelForPropertySet(
+ m_objectInfoLabel, propertySet);
+
+ const bool isForm = objectClassName=="KexiDBForm";
+// kdDebug() << "objectClassName=" << objectClassName << endl;
+// {
+/* //this is top level form's surface: data source means table or query
+ QCString dataSourceMimeType, dataSource;
+ if (buffer->hasProperty("dataSourceMimeType"))
+ dataSourceMimeType = (*buffer)["dataSourceMimeType"].value().toCString();
+ if (buffer->hasProperty("dataSource"))
+ dataSource = (*buffer)["dataSource"].value().toCString();
+ m_dataSourceCombo->setDataSource(dataSourceMimeType, dataSource);*/
+// }
+// else {
+
+ const bool multipleSelection = objectClassName=="special:multiple";
+ const bool hasDataSourceProperty = propertySet && propertySet->contains("dataSource") && !multipleSelection;
+
+ if (!isForm) {
+ //this is a widget
+ QCString dataSource;
+ if (hasDataSourceProperty) {
+ if (propertySet)
+ dataSource = (*propertySet)["dataSource"].value().toCString();
+ m_noDataSourceAvailableLabel->hide();
+ m_sourceFieldCombo->setFieldOrExpression(dataSource);
+ m_sourceFieldCombo->setEnabled(true);
+ m_clearWidgetDSButton->setEnabled(!m_sourceFieldCombo->currentText().isEmpty());
+ m_widgetDSLabel->show();
+ m_clearWidgetDSButton->show();
+ m_sourceFieldCombo->show();
+// m_dataSourceSeparator->hide();
+ updateSourceFieldWidgetsAvailability();
+ }
+ }
+
+ if (isForm) {
+ m_noDataSourceAvailableLabel->hide();
+// m_dataSourceSeparator->hide();
+ }
+ else if (!hasDataSourceProperty) {
+ if (multipleSelection)
+ m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableMultiText);
+ else
+ m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableSingleText);
+ m_noDataSourceAvailableLabel->show();
+// m_dataSourceSeparator->show();
+ //make 'No data source could be assigned' label's height the same as the 'source field' combo+label
+ m_noDataSourceAvailableLabel->setMinimumHeight(m_widgetDSLabel->height()
+ + m_sourceFieldCombo->height()/*-m_dataSourceSeparator->height()*/);
+ m_sourceFieldCombo->setCurrentText("");
+ }
+
+ if (isForm || !hasDataSourceProperty) {
+ //no source field can be set
+ m_widgetDSLabel->hide();
+ m_clearWidgetDSButton->hide();
+ m_sourceFieldCombo->hide();
+ }
+}
+
+void KexiDataSourcePage::slotFieldListViewSelectionChanged()
+{
+#ifndef KEXI_NO_AUTOFIELD_WIDGET
+ //update "add field" button's state
+ for (QListViewItemIterator it(m_fieldListView); it.current(); ++it) {
+ if (it.current()->isSelected()) {
+ m_addField->setEnabled(true);
+ return;
+ }
+ }
+ m_addField->setEnabled(false);
+#endif
+}
+
+void KexiDataSourcePage::updateSourceFieldWidgetsAvailability()
+{
+ const bool hasDataSource = m_dataSourceCombo->isSelectionValid(); //!m_dataSourceCombo->selectedName().isEmpty();
+ m_sourceFieldCombo->setEnabled( hasDataSource );
+ m_widgetDSLabel->setEnabled( hasDataSource );
+#ifndef KEXI_NO_AUTOFIELD_WIDGET
+ m_fieldListView->setEnabled( hasDataSource );
+ m_availableFieldsLabel->setEnabled( hasDataSource );
+ m_mousePointerLabel->setEnabled( hasDataSource );
+ m_availableFieldsDescriptionLabel->setEnabled( hasDataSource );
+#endif
+}
+
+#include "kexidatasourcepage.moc"
diff --git a/kexi/plugins/forms/kexidatasourcepage.h b/kexi/plugins/forms/kexidatasourcepage.h
new file mode 100644
index 00000000..0f113aa7
--- /dev/null
+++ b/kexi/plugins/forms/kexidatasourcepage.h
@@ -0,0 +1,112 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+#ifndef KEXIDATASOURCEPAGE_H
+#define KEXIDATASOURCEPAGE_H
+
+#include <qwidget.h>
+#include <kexidb/field.h>
+#include <kexidb/utils.h>
+#include <koproperty/set.h>
+
+class KCommand;
+class KexiObjectInfoLabel;
+class KexiDataSourceComboBox;
+class KexiFieldComboBox;
+class KexiFieldListView;
+class KexiProject;
+class QToolButton;
+class QLabel;
+class QFrame;
+
+//! A page within form designer's property tabbed pane, providing data source editor
+class KEXIFORMUTILS_EXPORT KexiDataSourcePage : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KexiDataSourcePage(QWidget *parent, const char *name = 0);
+ virtual ~KexiDataSourcePage();
+
+ KexiDataSourceComboBox* dataSourceCombo() const { return m_dataSourceCombo; }
+ KexiObjectInfoLabel* objectInfoLabel() const { return m_objectInfoLabel; }
+
+ public slots:
+ void setProject(KexiProject *prj);
+ void clearDataSourceSelection(bool alsoClearComboBox = true);
+ void clearWidgetDataSourceSelection();
+
+ //! Sets data source of a currently selected form.
+ //! This is performed on form initialization and on activating.
+ void setDataSource(const QCString& mimeType, const QCString& name);
+
+ //! Receives a pointer to a new property \a set (from KexiFormView::managerPropertyChanged())
+ void assignPropertySet(KoProperty::Set* propertySet);
+
+ signals:
+ //! Signal emitted when helper button 'go to selected data source' is clicked.
+ void jumpToObjectRequested(const QCString& mime, const QCString& name);
+
+ //! Signal emitted when form's data source has been changed. It's connected to the Form Manager.
+ void formDataSourceChanged(const QCString& mime, const QCString& name);
+
+ /*! Signal emitted when current widget's data source (field/expression)
+ has been changed. It's connected to the Form Manager.
+ \a caption for this field is also provided (e.g. AutoField form widget use it) */
+ void dataSourceFieldOrExpressionChanged(const QString& string, const QString& caption,
+ KexiDB::Field::Type type);
+
+ /*! Signal emitted when 'insert fields' button has been clicked */
+ void insertAutoFields(const QString& sourceMimeType, const QString& sourceName,
+ const QStringList& fields);
+
+ protected slots:
+ void slotDataSourceTextChanged(const QString & string);
+ void slotDataSourceChanged();
+ void slotFieldSelected();
+ void slotGotoSelected();
+ void slotInsertSelectedFields();
+ void slotFieldListViewSelectionChanged();
+ void slotFieldDoubleClicked(const QString& sourceMimeType, const QString& sourceName,
+ const QString& fieldName);
+
+ protected:
+ void updateSourceFieldWidgetsAvailability();
+
+ KexiFieldComboBox *m_sourceFieldCombo;
+ KexiObjectInfoLabel *m_objectInfoLabel;
+ KexiDataSourceComboBox* m_dataSourceCombo;
+ QLabel *m_dataSourceLabel, *m_noDataSourceAvailableLabel,
+ *m_widgetDSLabel, *m_availableFieldsLabel,
+ *m_mousePointerLabel, *m_availableFieldsDescriptionLabel;
+ QToolButton *m_clearWidgetDSButton, *m_clearDSButton, *m_gotoButton, *m_addField;
+ QFrame *m_dataSourceSeparator;
+ QString m_noDataSourceAvailableSingleText, m_noDataSourceAvailableMultiText;
+ bool m_insideClearDataSourceSelection : 1;
+#ifdef KEXI_NO_AUTOFIELD_WIDGET
+ KexiDB::TableOrQuerySchema *m_tableOrQuerySchema; //!< temp.
+#else
+ KexiFieldListView* m_fieldListView;
+#endif
+
+ //! Used only in assignPropertySet() to check whether we already have the set assigned
+ QCString m_currentObjectName;
+ //QGuardedPtr<KoProperty::Set> m_propertySet;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexidbfactory.cpp b/kexi/plugins/forms/kexidbfactory.cpp
new file mode 100644
index 00000000..4ab05d76
--- /dev/null
+++ b/kexi/plugins/forms/kexidbfactory.cpp
@@ -0,0 +1,713 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qpopupmenu.h>
+#include <qscrollview.h>
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qstyle.h>
+
+#include <kgenericfactory.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kactioncollection.h>
+#include <kstdaction.h>
+
+#include <formeditor/container.h>
+#include <formeditor/form.h>
+#include <formeditor/formIO.h>
+#include <formeditor/formmanager.h>
+#include <formeditor/objecttree.h>
+#include <formeditor/utils.h>
+#include <kexidb/utils.h>
+#include <kexidb/connection.h>
+#include <kexipart.h>
+#include <formeditor/widgetlibrary.h>
+#include <kexigradientwidget.h>
+#include <keximainwindow.h>
+#include <kexiutils/utils.h>
+#include <widget/kexicustompropertyfactory.h>
+#include <widget/utils/kexicontextmenuutils.h>
+
+#include "kexiformview.h"
+#include "widgets/kexidbautofield.h"
+#include "widgets/kexidbcheckbox.h"
+#include "widgets/kexidbimagebox.h"
+//#include "widgets/kexidbdoublespinbox.h"
+//#include "widgets/kexidbintspinbox.h"
+#include "widgets/kexiframe.h"
+#include "widgets/kexidblabel.h"
+#include "widgets/kexidblineedit.h"
+#include "widgets/kexidbtextedit.h"
+#include "widgets/kexidbcombobox.h"
+#include "widgets/kexipushbutton.h"
+#include "widgets/kexidbform.h"
+#include "widgets/kexidbsubform.h"
+#include "kexidataawarewidgetinfo.h"
+
+#include "kexidbfactory.h"
+#include <core/kexi.h>
+
+
+//////////////////////////////////////////
+
+KexiDBFactory::KexiDBFactory(QObject *parent, const char *name, const QStringList &)
+ : KFormDesigner::WidgetFactory(parent, name)
+{
+ KFormDesigner::WidgetInfo *wi;
+ wi = new KexiDataAwareWidgetInfo(this);
+ wi->setPixmap("form");
+ wi->setClassName("KexiDBForm");
+ wi->setName(i18n("Form"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "form"));
+ wi->setDescription(i18n("A data-aware form widget"));
+ addClass(wi);
+
+#ifndef KEXI_NO_SUBFORM
+ wi = new KexiDataAwareWidgetInfo(this);
+ wi->setPixmap("subform");
+ wi->setClassName("KexiDBSubForm");
+ wi->addAlternateClassName("KexiSubForm", true/*override*/); //older
+ wi->setName(i18n("Sub Form"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "subForm"));
+ wi->setDescription(i18n("A form widget included in another Form"));
+ wi->setAutoSyncForProperty( "formName", false );
+ addClass(wi);
+#endif
+
+ // inherited
+ wi = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KLineEdit");
+ wi->setPixmap("lineedit");
+ wi->setClassName("KexiDBLineEdit");
+ wi->addAlternateClassName("QLineEdit", true/*override*/);
+ wi->addAlternateClassName("KLineEdit", true/*override*/);
+ wi->setIncludeFileName("klineedit.h");
+ wi->setName(i18n("Text Box"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "textBox"));
+ wi->setDescription(i18n("A widget for entering and displaying text"));
+ addClass(wi);
+
+ // inherited
+ wi = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KTextEdit");
+ wi->setPixmap("textedit");
+ wi->setClassName("KexiDBTextEdit");
+ wi->addAlternateClassName("QTextEdit", true/*override*/);
+ wi->addAlternateClassName("KTextEdit", true/*override*/);
+ wi->setIncludeFileName("ktextedit.h");
+ wi->setName(i18n("Text Editor"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "textEditor"));
+ wi->setDescription(i18n("A multiline text editor"));
+ addClass(wi);
+
+ wi = new KFormDesigner::WidgetInfo(
+ this, "containers", "QFrame" /*we're inheriting to get i18n'd strings already translated there*/);
+ wi->setPixmap("frame");
+ wi->setClassName("KexiFrame");
+ wi->addAlternateClassName("QFrame", true/*override*/);
+ wi->setName(i18n("Frame"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "frame"));
+ wi->setDescription(i18n("A simple frame widget"));
+ addClass(wi);
+
+ wi = new KexiDataAwareWidgetInfo(
+ this, "stdwidgets", "QLabel" /*we're inheriting to get i18n'd strings already translated there*/);
+ wi->setPixmap("label");
+ wi->setClassName("KexiDBLabel");
+ wi->addAlternateClassName("QLabel", true/*override*/);
+ wi->addAlternateClassName("KexiLabel", true/*override*/); //older
+ wi->setName(i18n("Text Label", "Label"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "label"));
+ wi->setDescription(i18n("A widget for displaying text"));
+ addClass(wi);
+
+#ifndef KEXI_NO_IMAGEBOX_WIDGET
+ wi = new KexiDataAwareWidgetInfo(
+ this, "stdwidgets", "KexiPictureLabel" /*we're inheriting to get i18n'd strings already translated there*/);
+ wi->setPixmap("pixmaplabel");
+ wi->setClassName("KexiDBImageBox");
+ wi->addAlternateClassName("KexiPictureLabel", true/*override*/);
+ wi->addAlternateClassName("KexiImageBox", true/*override*/); //older
+ wi->setName(i18n("Image Box"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "image"));
+ wi->setDescription(i18n("A widget for displaying images"));
+// wi->setCustomTypeForProperty("pixmapData", KexiCustomPropertyFactory::PixmapData);
+ wi->setCustomTypeForProperty("pixmapId", KexiCustomPropertyFactory::PixmapId);
+ addClass(wi);
+
+ setInternalProperty("KexiDBImageBox", "dontStartEditingOnInserting", "1");
+// setInternalProperty("KexiDBImageBox", "forceShowAdvancedProperty:pixmap", "1");
+#endif
+
+#ifdef KEXI_DB_COMBOBOX_WIDGET
+ wi = new KexiDataAwareWidgetInfo(
+ this, "stdwidgets", "KComboBox" /*we're inheriting to get i18n'd strings already translated there*/);
+ wi->setPixmap("combo");
+ wi->setClassName("KexiDBComboBox");
+ wi->addAlternateClassName("KComboBox", true/*override*/);
+ wi->setName(i18n("Combo Box"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "comboBox"));
+ wi->setDescription(i18n("A combo box widget"));
+ addClass(wi);
+#endif
+
+ wi = new KexiDataAwareWidgetInfo(this, "stdwidgets", "QCheckBox");
+ wi->setPixmap("check");
+ wi->setClassName("KexiDBCheckBox");
+ wi->addAlternateClassName("QCheckBox", true/*override*/);
+ wi->setName(i18n("Check Box"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "checkBox"));
+ wi->setDescription(i18n("A check box with text label"));
+ addClass(wi);
+
+#ifndef KEXI_NO_AUTOFIELD_WIDGET
+ wi = new KexiDataAwareWidgetInfo(this);
+ wi->setPixmap("autofield");
+ wi->setClassName("KexiDBAutoField");
+ wi->addAlternateClassName("KexiDBFieldEdit", true/*override*/); //older
+ wi->setName(i18n("Auto Field"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters", "autoField"));
+ wi->setDescription(i18n("A widget containing an automatically selected editor "
+ "and a label to edit the value of a database field of any type."));
+ addClass(wi);
+#endif
+
+/*
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9)
+ KexiDataAwareWidgetInfo *wDate = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KDateWidget");
+#else
+ KexiDataAwareWidgetInfo *wDate = new KexiDataAwareWidgetInfo(this, "stdwidgets", "QDateEdit");
+#endif
+ wDate->setPixmap("dateedit");
+ wDate->setClassName("KexiDBDateEdit");
+ wDate->addAlternateClassName("QDateEdit", true);//override
+ wDate->addAlternateClassName("KDateWidget", true);//override
+ wDate->setName(i18n("Date Widget"));
+ wDate->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "dateWidget"));
+ wDate->setDescription(i18n("A widget to input and display a date"));
+ addClass(wDate);
+
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9)
+ KexiDataAwareWidgetInfo *wTime = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KTimeWidget");
+#else
+ KexiDataAwareWidgetInfo *wTime = new KexiDataAwareWidgetInfo(this, "stdwidgets", "QTimeEdit");
+#endif
+ wTime->setPixmap("timeedit");
+ wTime->setClassName("KexiDBTimeEdit");
+ wTime->addAlternateClassName("QTimeEdit", true);//override
+ wTime->addAlternateClassName("KTimeWidget", true);//override
+ wTime->setName(i18n("Time Widget"));
+ wTime->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "timeWidget"));
+ wTime->setDescription(i18n("A widget to input and display a time"));
+ addClass(wTime);
+
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9)
+ KexiDataAwareWidgetInfo *wDateTime = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KDateTimeWidget");
+#else
+ KexiDataAwareWidgetInfo *wDateTime = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KDateTimeWidget");
+#endif
+ wDateTime->setPixmap("datetimeedit");
+ wDateTime->setClassName("KexiDBDateTimeEdit");
+ wDateTime->addAlternateClassName("QDateTimeEdit", true);//override
+ wDateTime->addAlternateClassName("KDateTimeWidget", true);//override
+ wDateTime->setName(i18n("Date/Time Widget"));
+ wDateTime->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "dateTimeWidget"));
+ wDateTime->setDescription(i18n("A widget to input and display a date and time"));
+ addClass(wDateTime);
+*/
+
+/* KexiDataAwareWidgetInfo *wIntSpinBox = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KIntSpinBox");
+ wIntSpinBox->setPixmap("spin");
+ wIntSpinBox->setClassName("KexiDBIntSpinBox");
+ wIntSpinBox->addAlternateClassName("QSpinBox", true);
+ wIntSpinBox->addAlternateClassName("KIntSpinBox", true);
+ wIntSpinBox->setName(i18n("Integer Number Spin Box"));
+ wIntSpinBox->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "intSpinBox"));
+ wIntSpinBox->setDescription(i18n("A spin box widget to input and display integer numbers"));
+ addClass(wIntSpinBox);
+
+ KexiDataAwareWidgetInfo *wDoubleSpinBox = new KexiDataAwareWidgetInfo(this, "stdwidgets");
+ wDoubleSpinBox->setPixmap("spin");
+ wDoubleSpinBox->setClassName("KexiDBDoubleSpinBox");
+ wDoubleSpinBox->addAlternateClassName("KDoubleSpinBox", true);
+ wDoubleSpinBox->setName(i18n("Floating-point Number Spin Box"));
+ wDoubleSpinBox->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "dblSpinBox"));
+ wDoubleSpinBox->setDescription(i18n("A spin box widget to input and display floating-point numbers"));
+ addClass(wDoubleSpinBox);*/
+
+ // inherited
+ wi = new KFormDesigner::WidgetInfo(
+ this, "stdwidgets", "KPushButton");
+ wi->addAlternateClassName("KexiPushButton");
+ wi->setName(i18n("Command Button"));
+ wi->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. "
+ "It must _not_ contain white spaces and non latin1 characters.", "button"));
+ wi->setDescription(i18n("A command button to execute actions"));
+ addClass(wi);
+
+ m_propDesc["dataSource"] = i18n("Data Source");
+ m_propDesc["formName"] = i18n("Form Name");
+ m_propDesc["onClickAction"] = i18n("On Click");
+ m_propDesc["onClickActionOption"] = i18n("On Click Option");
+ m_propDesc["autoTabStops"] = i18n("Auto Tab Order");
+ m_propDesc["shadowEnabled"] = i18n("Shadow Enabled");
+ m_propDesc["on"] = i18n("On: button", "On");
+
+ m_propDesc["widgetType"] = i18n("Editor Type");
+ //for autofield's type: inherit i18n from KexiDB
+ m_propValDesc["Auto"] = i18n("AutoField editor's type", "Auto");
+ m_propValDesc["Text"] = KexiDB::Field::typeName(KexiDB::Field::Text);
+ m_propValDesc["Integer"] = KexiDB::Field::typeName(KexiDB::Field::Integer);
+ m_propValDesc["Double"] = KexiDB::Field::typeName(KexiDB::Field::Double);
+ m_propValDesc["Boolean"] = KexiDB::Field::typeName(KexiDB::Field::Boolean);
+ m_propValDesc["Date"] = KexiDB::Field::typeName(KexiDB::Field::Date);
+ m_propValDesc["Time"] = KexiDB::Field::typeName(KexiDB::Field::Time);
+ m_propValDesc["DateTime"] = KexiDB::Field::typeName(KexiDB::Field::DateTime);
+ m_propValDesc["MultiLineText"] = i18n("AutoField editor's type", "Multiline Text");
+ m_propValDesc["ComboBox"] = i18n("AutoField editor's type", "Drop-Down List");
+ m_propValDesc["Image"] = i18n("AutoField editor's type", "Image");
+
+// m_propDesc["labelCaption"] = i18n("Label Text");
+ m_propDesc["autoCaption"] = i18n("Auto Label");
+ m_propDesc["foregroundLabelColor"] = i18n("Label Text Color");
+ m_propDesc["backgroundLabelColor"] = i18n("(a property name, keep the text narrow!)",
+ "Label Background\nColor");
+
+ m_propDesc["labelPosition"] = i18n("Label Position");
+ m_propValDesc["Left"] = i18n("Label Position", "Left");
+ m_propValDesc["Top"] = i18n("Label Position", "Top");
+ m_propValDesc["NoLabel"] = i18n("Label Position", "No Label");
+
+ m_propDesc["sizeInternal"] = i18n("Size");
+// m_propDesc["pixmap"] = i18n("Image");
+ m_propDesc["pixmapId"] = i18n("Image");
+ m_propDesc["scaledContents"] = i18n("Scaled Contents");
+ m_propDesc["keepAspectRatio"] = i18n("Keep Aspect Ratio (short)", "Keep Ratio");
+
+ //hide classes that are replaced by db-aware versions
+ hideClass("KexiPictureLabel");
+ hideClass("KComboBox");
+
+ //used in labels, frames...
+ m_propDesc["frameColor"] = i18n("Frame Color");
+ m_propDesc["dropDownButtonVisible"] =
+ i18n("Drop-Down Button for Image Box Visible (a property name, keep the text narrow!)",
+ "Drop-Down\nButton Visible");
+
+ //for checkbox
+ m_propValDesc["TristateDefault"] = i18n("Tristate checkbox, default", "Default");
+ m_propValDesc["TristateOn"] = i18n("Tristate checkbox, yes", "Yes");
+ m_propValDesc["TristateOff"] = i18n("Tristate checkbox, no", "No");
+
+ //for combobox
+ m_propDesc["editable"] = i18n("Editable combobox", "Editable");
+}
+
+KexiDBFactory::~KexiDBFactory()
+{
+}
+
+QWidget*
+KexiDBFactory::createWidget(const QCString &c, QWidget *p, const char *n,
+ KFormDesigner::Container *container, int options)
+{
+ kexipluginsdbg << "KexiDBFactory::createWidget() " << this << endl;
+
+ QWidget *w=0;
+ QString text( container->form()->library()->textForWidgetName(n, c) );
+ const bool designMode = options & KFormDesigner::WidgetFactory::DesignViewMode;
+
+ if(c == "KexiDBSubForm")
+ w = new KexiDBSubForm(container->form(), p, n);
+ else if(c == "KexiDBLineEdit")
+ {
+ w = new KexiDBLineEdit(p, n);
+ if (designMode)
+ w->setCursor(QCursor(Qt::ArrowCursor));
+ }
+ else if(c == "KexiDBTextEdit")
+ {
+ w = new KexiDBTextEdit(p, n);
+ if (designMode)
+ w->setCursor(QCursor(Qt::ArrowCursor));
+ }
+ else if(c == "QFrame" || c == "KexiFrame")
+ {
+ w = new KexiFrame(p, n);
+ new KFormDesigner::Container(container, w, container);
+ }
+ else if(c == "KexiDBLabel")
+ w = new KexiDBLabel(text, p, n);
+#ifndef KEXI_NO_IMAGEBOX_WIDGET
+ else if(c == "KexiDBImageBox") {
+ w = new KexiDBImageBox(designMode, p, n);
+ connect(w, SIGNAL(idChanged(long)), this, SLOT(slotImageBoxIdChanged(long)));
+ }
+#endif
+#ifndef KEXI_NO_AUTOFIELD_WIDGET
+ else if(c == "KexiDBAutoField")
+ w = new KexiDBAutoField(p, n, designMode);
+#endif
+ else if(c == "KexiDBCheckBox")
+ w = new KexiDBCheckBox(text, p, n);
+ else if(c == "KexiDBComboBox")
+ w = new KexiDBComboBox(p, n, designMode);
+/* else if(c == "KexiDBTimeEdit")
+ w = new KexiDBTimeEdit(QTime::currentTime(), p, n);
+ else if(c == "KexiDBDateEdit")
+ w = new KexiDBDateEdit(QDate::currentDate(), p, n);
+ else if(c == "KexiDBDateTimeEdit")
+ w = new KexiDBDateTimeEdit(QDateTime::currentDateTime(), p, n);*/
+// else if(c == "KexiDBIntSpinBox")
+// w = new KexiDBIntSpinBox(p, n);
+// else if(c == "KexiDBDoubleSpinBox")
+// w = new KexiDBDoubleSpinBox(p, n);
+ else if(c == "KPushButton" || c == "KexiPushButton")
+ w = new KexiPushButton(text, p, n);
+
+ return w;
+}
+
+bool
+KexiDBFactory::createMenuActions(const QCString &classname, QWidget *w, QPopupMenu *menu,
+ KFormDesigner::Container *)
+{
+ if(classname == "QPushButton" || classname == "KPushButton" || classname == "KexiPushButton")
+ {
+/*! @todo also call createMenuActions() for inherited factory! */
+ m_assignAction->plug( menu );
+ return true;
+ }
+ else if(classname == "KexiDBImageBox")
+ {
+ KexiDBImageBox *imageBox = static_cast<KexiDBImageBox*>(w);
+ imageBox->contextMenu()->updateActionsAvailability();
+ KActionCollection *ac = imageBox->contextMenu()->actionCollection();
+ KPopupMenu *subMenu = new KPopupMenu();
+//! @todo make these actions undoable/redoable
+ menu->insertItem(i18n("&Image"), subMenu);
+ ac->action("insert")->plug(subMenu);
+ ac->action("file_save_as")->plug(subMenu);
+ subMenu->insertSeparator();
+ ac->action("edit_cut")->plug(subMenu);
+ ac->action("edit_copy")->plug(subMenu);
+ ac->action("edit_paste")->plug(subMenu);
+ ac->action("delete")->plug(subMenu);
+ if (ac->action("properties")) {
+ subMenu->insertSeparator();
+ ac->action("properties")->plug(subMenu);
+ }
+ }
+ return false;
+}
+
+void
+KexiDBFactory::createCustomActions(KActionCollection* col)
+{
+ //this will create shared instance action for design mode (special collection is provided)
+ m_assignAction = new KAction( i18n("&Assign Action..."), SmallIconSet("form_action"),
+ 0, 0, 0, col, "widget_assign_action");
+}
+
+bool
+KexiDBFactory::startEditing(const QCString &classname, QWidget *w, KFormDesigner::Container *container)
+{
+ m_container = container;
+ if(classname == "KexiDBLineEdit")
+ {
+//! @todo this code should not be copied here but
+//! just inherited StdWidgetFactory::clearWidgetContent() should be called
+ KLineEdit *lineedit = static_cast<KLineEdit*>(w);
+ createEditor(classname, lineedit->text(), lineedit, container,
+ lineedit->geometry(), lineedit->alignment(), true);
+ return true;
+ }
+ if(classname == "KexiDBTextEdit")
+ {
+//! @todo this code should not be copied here but
+//! just inherited StdWidgetFactory::clearWidgetContent() should be called
+ KTextEdit *textedit = static_cast<KTextEdit*>(w);
+ createEditor(classname, textedit->text(), textedit, container,
+ textedit->geometry(), textedit->alignment(), true, true);
+ //copy a few properties
+ KTextEdit *ed = dynamic_cast<KTextEdit *>( editor(w) );
+ ed->setWrapPolicy(textedit->wrapPolicy());
+ ed->setWordWrap(textedit->wordWrap());
+ ed->setTabStopWidth(textedit->tabStopWidth());
+ ed->setWrapColumnOrWidth(textedit->wrapColumnOrWidth());
+ ed->setLinkUnderline(textedit->linkUnderline());
+ ed->setTextFormat(textedit->textFormat());
+ ed->setHScrollBarMode(textedit->hScrollBarMode());
+ ed->setVScrollBarMode(textedit->vScrollBarMode());
+ return true;
+ }
+ else if ( classname == "KexiDBLabel" ) {
+ KexiDBLabel *label = static_cast<KexiDBLabel*>(w);
+ m_widget = w;
+ if(label->textFormat() == RichText)
+ {
+ QString text = label->text();
+ if ( editRichText( label, text ) )
+ {
+ changeProperty( "textFormat", "RichText", container->form() );
+ changeProperty( "text", text, container->form() );
+ }
+
+ if ( classname == "KexiDBLabel" )
+ w->resize(w->sizeHint());
+ }
+ else
+ {
+ createEditor(classname, label->text(), label, container,
+ label->geometry(), label->alignment(),
+ false, label->alignment() & Qt::WordBreak /*multiline*/);
+ }
+ return true;
+ }
+ else if (classname == "KexiDBSubForm") {
+ // open the form in design mode
+ KexiMainWindow *mainWin = KexiUtils::findParent<KexiMainWindow>(w, "KexiMainWindow");
+ KexiDBSubForm *subform = static_cast<KexiDBSubForm*>(w);
+ if(mainWin) {
+ bool openingCancelled;
+ mainWin->openObject("kexi/form", subform->formName(), Kexi::DesignViewMode,
+ openingCancelled);
+ }
+ return true;
+ }
+#if 0
+ else if( (classname == "KexiDBDateEdit") || (classname == "KexiDBDateTimeEdit") || (classname == "KexiDBTimeEdit")
+ /*|| (classname == "KexiDBIntSpinBox") || (classname == "KexiDBDoubleSpinBox")*/ ) {
+ disableFilter(w, container);
+ return true;
+ }
+#endif
+ else if(classname == "KexiDBAutoField") {
+ if(static_cast<KexiDBAutoField*>(w)->hasAutoCaption())
+ return false; // caption is auto, abort editing
+ QLabel *label = static_cast<KexiDBAutoField*>(w)->label();
+ createEditor(classname, label->text(), label, container, label->geometry(), label->alignment());
+ return true;
+ }
+ else if (classname == "KexiDBCheckBox") {
+ KexiDBCheckBox *cb = static_cast<KexiDBCheckBox*>(w);
+ QRect r( cb->geometry() );
+ r.setLeft( r.left() + 2 + cb->style().subRect( QStyle::SR_CheckBoxIndicator, cb ).width() );
+ createEditor(classname, cb->text(), cb, container, r, Qt::AlignAuto);
+ return true;
+ }
+ else if(classname == "KexiDBImageBox") {
+ KexiDBImageBox *image = static_cast<KexiDBImageBox*>(w);
+ image->insertFromFile();
+ return true;
+ }
+ return false;
+}
+
+bool
+KexiDBFactory::previewWidget(const QCString &, QWidget *, KFormDesigner::Container *)
+{
+ return false;
+}
+
+bool
+KexiDBFactory::clearWidgetContent(const QCString & /*classname*/, QWidget *w)
+{
+//! @todo this code should not be copied here but
+//! just inherited StdWidgetFactory::clearWidgetContent() should be called
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>(w);
+ if(iface)
+ iface->clear();
+ return true;
+}
+
+QValueList<QCString>
+KexiDBFactory::autoSaveProperties(const QCString & /*classname*/)
+{
+ QValueList<QCString> lst;
+// if(classname == "KexiDBSubForm")
+ //lst << "formName";
+// if(classname == "KexiDBLineEdit")
+// lst += "dataSource";
+// if(classname == "KexiDBAutoField")
+// lst << "labelCaption";
+ return lst;
+}
+
+bool
+KexiDBFactory::isPropertyVisibleInternal(const QCString& classname, QWidget *w,
+ const QCString& property, bool isTopLevel)
+{
+ //general
+ if (property=="dataSource" || property=="dataSourceMimeType") {
+ return false; //force
+ }
+
+ bool ok = true;
+
+ if(classname == "KexiPushButton") {
+ ok = property!="isDragEnabled"
+#ifdef KEXI_NO_UNFINISHED
+ && property!="onClickAction" /*! @todo reenable */
+ && property!="onClickActionOption" /*! @todo reenable */
+ && property!="iconSet" /*! @todo reenable */
+ && property!="stdItem" /*! @todo reenable stdItem */
+#endif
+ ;
+ }
+ else if(classname == "KexiDBLineEdit")
+ ok = property!="urlDropsEnabled"
+ && property!="vAlign"
+#ifdef KEXI_NO_UNFINISHED
+ && property!="inputMask"
+ && property!="maxLength" //!< we may want to integrate this with db schema
+#endif
+ ;
+ else if(classname == "KexiDBComboBox")
+ ok = property!="autoCaption"
+ && property!="labelPosition"
+ && property!="widgetType"
+ && property!="fieldTypeInternal"
+ && property!="fieldCaptionInternal"; //hide properties that come with KexiDBAutoField
+ else if(classname == "KexiDBTextEdit")
+ ok = property!="undoDepth"
+ && property!="undoRedoEnabled" //always true!
+ && property!="dragAutoScroll" //always true!
+ && property!="overwriteMode" //always false!
+ && property!="resizePolicy"
+ && property!="autoFormatting" //too complex
+#ifdef KEXI_NO_UNFINISHED
+ && property!="paper"
+#endif
+ ;
+ else if(classname == "KexiDBSubForm")
+ ok = property!="dragAutoScroll"
+ && property!="resizePolicy"
+ && property!="focusPolicy";
+ else if(classname == "KexiDBForm")
+ ok = property!="iconText"
+ && property!="geometry" /*nonsense for toplevel widget; for size, "size" property is used*/;
+ else if(classname == "KexiDBLabel")
+ ok = property!="focusPolicy";
+ else if(classname == "KexiDBAutoField") {
+ if (!isTopLevel && property=="caption")
+ return true; //force
+ if (property=="fieldTypeInternal" || property=="fieldCaptionInternal"
+//! @todo unhide in 2.0
+ || property=="widgetType")
+ return false;
+ ok = property!="text"; /* "text" is not needed as "caption" is used instead */
+ }
+ else if (classname == "KexiDBImageBox") {
+ ok = property!="font" && property!="wordbreak";
+ }
+ else if(classname == "KexiDBCheckBox") {
+ //hide text property if the widget is a child of an autofield beause there's already "caption" for this purpose
+ if (property=="text" && w && dynamic_cast<KFormDesigner::WidgetWithSubpropertiesInterface*>(w->parentWidget()))
+ return false;
+ ok = property!="autoRepeat";
+ }
+
+ return ok && WidgetFactory::isPropertyVisibleInternal(classname, w, property, isTopLevel);
+}
+
+bool
+KexiDBFactory::propertySetShouldBeReloadedAfterPropertyChange(const QCString& classname,
+ QWidget *w, const QCString& property)
+{
+ Q_UNUSED(classname);
+ Q_UNUSED(w);
+ if (property=="fieldTypeInternal" || property=="widgetType")
+ return true;
+ return false;
+}
+
+bool
+KexiDBFactory::changeText(const QString &text)
+{
+ KFormDesigner::Form *form = m_container ? m_container->form() : 0;
+ if (!form)
+ return false;
+ if (!form->selectedWidget())
+ return false;
+ QCString n( form->selectedWidget()->className() );
+// QWidget *w = WidgetFactory::widget();
+ if(n == "KexiDBAutoField") {
+ changeProperty("caption", text, form);
+ return true;
+ }
+ //! \todo check field's geometry
+ return false;
+}
+
+void
+KexiDBFactory::resizeEditor(QWidget *editor, QWidget *w, const QCString &classname)
+{
+ //QSize s = widget->size();
+ //QPoint p = widget->pos();
+
+ if(classname == "KexiDBAutoField")
+ editor->setGeometry( static_cast<KexiDBAutoField*>(w)->label()->geometry() );
+}
+
+void
+KexiDBFactory::slotImageBoxIdChanged(KexiBLOBBuffer::Id_t id)
+{
+//old KexiFormView *formView = KexiUtils::findParent<KexiFormView>((QWidget*)m_widget, "KexiFormView");
+
+ // (js) heh, porting to KFormDesigner::FormManager::self() singleton took me entire day of work...
+ KFormDesigner::Form *form = KFormDesigner::FormManager::self()->activeForm();
+ KexiFormView *formView = form ? KexiUtils::findParent<KexiFormView>((QWidget*)form->widget(), "KexiFormView") : 0;
+ if (formView) {
+ changeProperty("pixmapId", (uint)/*! @todo unsafe */id, form);
+//old formView->setUnsavedLocalBLOB(m_widget, id);
+ formView->setUnsavedLocalBLOB(form->selectedWidget(), id);
+ }
+}
+
+KFORMDESIGNER_WIDGET_FACTORY(KexiDBFactory, kexidbwidgets)
+
+#include "kexidbfactory.moc"
diff --git a/kexi/plugins/forms/kexidbfactory.h b/kexi/plugins/forms/kexidbfactory.h
new file mode 100644
index 00000000..6064a001
--- /dev/null
+++ b/kexi/plugins/forms/kexidbfactory.h
@@ -0,0 +1,74 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIDBFACTORY_H
+#define KEXIDBFACTORY_H
+
+#include <formeditor/widgetfactory.h>
+
+class KAction;
+
+namespace KFormDesigner {
+ class Form;
+ class FormManager;
+}
+
+//! Kexi Factory (DB widgets + subform)
+class KexiDBFactory : public KFormDesigner::WidgetFactory
+{
+ Q_OBJECT
+
+ public:
+ KexiDBFactory(QObject *parent, const char *name, const QStringList &args);
+ virtual ~KexiDBFactory();
+
+ virtual QWidget *createWidget(const QCString &classname, QWidget *parent, const char *name,
+ KFormDesigner::Container *container, int options = DefaultOptions );
+
+ virtual void createCustomActions(KActionCollection* col);
+ virtual bool createMenuActions(const QCString &classname, QWidget *w, QPopupMenu *menu,
+ KFormDesigner::Container *container);
+ virtual bool startEditing(const QCString &classname, QWidget *w, KFormDesigner::Container *container);
+ virtual bool previewWidget(const QCString &, QWidget *, KFormDesigner::Container *);
+ virtual bool clearWidgetContent(const QCString &classname, QWidget *w);
+
+ //virtual void saveSpecialProperty(const QString &classname, const QString &name, const QVariant &value, QWidget *w,
+ //QDomElement &parentNode, QDomDocument &parent) {}
+ //virtual void readSpecialProperty(const QCString &classname, QDomElement &node, QWidget *w, KFormDesigner::ObjectTreeItem *item) {}
+ virtual QValueList<QCString> autoSaveProperties(const QCString &classname);
+
+ protected slots:
+ void slotImageBoxIdChanged(long id); /*KexiBLOBBuffer::Id_t*/
+
+ protected:
+ virtual bool changeText(const QString &newText);
+ virtual void resizeEditor(QWidget *editor, QWidget *widget, const QCString &classname);
+
+ virtual bool isPropertyVisibleInternal(const QCString& classname, QWidget *w,
+ const QCString& property, bool isTopLevel);
+
+ //! Sometimes property sets should be reloaded when a given property value changed.
+ virtual bool propertySetShouldBeReloadedAfterPropertyChange(const QCString& classname, QWidget *w,
+ const QCString& property);
+
+ KAction* m_assignAction;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexidbtextwidgetinterface.cpp b/kexi/plugins/forms/kexidbtextwidgetinterface.cpp
new file mode 100644
index 00000000..47eabe9d
--- /dev/null
+++ b/kexi/plugins/forms/kexidbtextwidgetinterface.cpp
@@ -0,0 +1,71 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbtextwidgetinterface.h"
+#include "kexiformdataiteminterface.h"
+#include <kexidb/queryschema.h>
+#include <kexiutils/utils.h>
+#include <qframe.h>
+#include <qpainter.h>
+
+KexiDBTextWidgetInterface::KexiDBTextWidgetInterface()
+ : m_autonumberDisplayParameters(0)
+{
+}
+
+KexiDBTextWidgetInterface::~KexiDBTextWidgetInterface()
+{
+ delete m_autonumberDisplayParameters;
+}
+
+void KexiDBTextWidgetInterface::setColumnInfo(KexiDB::QueryColumnInfo* cinfo, QWidget *w)
+{
+ if (cinfo->field->isAutoIncrement()) {
+ if (!m_autonumberDisplayParameters)
+ m_autonumberDisplayParameters = new KexiDisplayUtils::DisplayParameters();
+ KexiDisplayUtils::initDisplayForAutonumberSign(*m_autonumberDisplayParameters, w);
+ }
+}
+
+void KexiDBTextWidgetInterface::paint( QFrame *w, QPainter* p, bool textIsEmpty, int alignment, bool hasFocus )
+{
+ KexiFormDataItemInterface *dataItemIface = dynamic_cast<KexiFormDataItemInterface*>(w);
+ KexiDB::QueryColumnInfo *columnInfo = dataItemIface ? dataItemIface->columnInfo() : 0;
+ if (columnInfo && columnInfo->field && dataItemIface->cursorAtNewRow() && textIsEmpty) {
+ const int margin = w->lineWidth() + w->midLineWidth();
+ if (columnInfo->field->isAutoIncrement() && m_autonumberDisplayParameters) {
+ if (w->hasFocus()) {
+ p->setPen(
+ KexiUtils::blendedColors(
+ m_autonumberDisplayParameters->textColor, w->palette().active().base(), 1, 3));
+ }
+ KexiDisplayUtils::paintAutonumberSign(*m_autonumberDisplayParameters, p,
+ 2 + margin + w->margin(), margin, w->width() - margin*2 -2-2,
+ w->height() - margin*2 -2, alignment, hasFocus);
+ }
+ }
+}
+
+void KexiDBTextWidgetInterface::event( QEvent * e, QWidget *w, bool textIsEmpty )
+{
+ if (e->type()==QEvent::FocusIn || e->type()==QEvent::FocusOut) {
+ if (m_autonumberDisplayParameters && textIsEmpty)
+ w->repaint();
+ }
+}
diff --git a/kexi/plugins/forms/kexidbtextwidgetinterface.h b/kexi/plugins/forms/kexidbtextwidgetinterface.h
new file mode 100644
index 00000000..ca10fc4e
--- /dev/null
+++ b/kexi/plugins/forms/kexidbtextwidgetinterface.h
@@ -0,0 +1,53 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBTextWidgetInterface_H
+#define KexiDBTextWidgetInterface_H
+
+#include <widget/utils/kexidisplayutils.h>
+
+namespace KexiDB {
+ class QueryColumnInfo;
+}
+class QFrame;
+
+//! @short An interface providing common text editor's functionality
+/*! Widgets (e.g. KexiDBLineEdit, KexiDBTextEdit) implementing KexiFormDataItemInterface
+ use this interface to customize painting and data handling. */
+class KEXIFORMUTILS_EXPORT KexiDBTextWidgetInterface
+{
+ public:
+ KexiDBTextWidgetInterface();
+ ~KexiDBTextWidgetInterface();
+
+ //! Called from KexiFormDataItemInterface::setColumnInfo(KexiDB::QueryColumnInfo* cinfo) implementation.
+ void setColumnInfo(KexiDB::QueryColumnInfo* cinfo, QWidget *w);
+
+ //! Called from paintEvent( QPaintEvent *pe ) method of the data aware widget.
+ void paint( QFrame *w, QPainter *p, bool textIsEmpty, int alignment, bool hasFocus );
+
+ //! Called from event( QEvent * e ) method of the data aware widget.
+ void event( QEvent * e, QWidget *w, bool textIsEmpty );
+
+ protected:
+ //! parameters for displaying autonumber sign
+ KexiDisplayUtils::DisplayParameters *m_autonumberDisplayParameters;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexiformdataiteminterface.cpp b/kexi/plugins/forms/kexiformdataiteminterface.cpp
new file mode 100644
index 00000000..c87a2dab
--- /dev/null
+++ b/kexi/plugins/forms/kexiformdataiteminterface.cpp
@@ -0,0 +1,68 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "kexiformdataiteminterface.h"
+#include "kexiformscrollview.h"
+#include <kexidb/queryschema.h>
+#include <kexiutils/utils.h>
+
+KexiFormDataItemInterface::KexiFormDataItemInterface()
+ : KexiDataItemInterface()
+ , m_columnInfo(0)
+ , m_displayParametersForEnteredValue(0)
+ , m_displayParametersForDefaultValue(0)
+ , m_displayDefaultValue(false)
+{
+}
+
+KexiFormDataItemInterface::~KexiFormDataItemInterface()
+{
+ delete m_displayParametersForEnteredValue;
+ delete m_displayParametersForDefaultValue;
+}
+
+void KexiFormDataItemInterface::undoChanges()
+{
+// m_disable_signalValueChanged = true;
+ setValueInternal(QString::null, false);
+// m_disable_signalValueChanged = false;
+}
+
+KexiDB::Field* KexiFormDataItemInterface::field() const
+{
+ return m_columnInfo ? m_columnInfo->field : 0;
+}
+
+void KexiFormDataItemInterface::setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue)
+{
+ m_displayDefaultValue = displayDefaultValue;
+ if (!m_displayParametersForDefaultValue) {
+ m_displayParametersForEnteredValue = new KexiDisplayUtils::DisplayParameters(widget);
+ m_displayParametersForDefaultValue = new KexiDisplayUtils::DisplayParameters();
+ KexiDisplayUtils::initDisplayForDefaultValue(*m_displayParametersForDefaultValue, widget);
+ }
+}
+
+void KexiFormDataItemInterface::cancelEditor()
+{
+ QWidget *parentWidget = dynamic_cast<QWidget*>(this)->parentWidget();
+ KexiFormScrollView* view = KexiUtils::findParent<KexiFormScrollView>(parentWidget, "KexiFormScrollView");
+ if (view)
+ view->cancelEditor();
+}
diff --git a/kexi/plugins/forms/kexiformdataiteminterface.h b/kexi/plugins/forms/kexiformdataiteminterface.h
new file mode 100644
index 00000000..99d20db4
--- /dev/null
+++ b/kexi/plugins/forms/kexiformdataiteminterface.h
@@ -0,0 +1,145 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KEXIFORMDATAITEMINTERFACE_H
+#define KEXIFORMDATAITEMINTERFACE_H
+
+#include <widget/utils/kexidisplayutils.h>
+#include <kexidataiteminterface.h>
+#include <qwidget.h>
+
+namespace KexiDB {
+ class Field;
+}
+
+//! An interface for declaring form widgets to be data-aware.
+class KEXIFORMUTILS_EXPORT KexiFormDataItemInterface : public KexiDataItemInterface
+{
+ public:
+ KexiFormDataItemInterface();
+ virtual ~KexiFormDataItemInterface();
+
+ //! \return the name of the data source for this widget.
+ //! Data source usually means here a table or query, a field name or an expression.
+ inline QString dataSource() const { return m_dataSource; }
+
+ //! Sets the name of the data source for this widget.
+ //! Data source usually means here a table or query or field name name.
+ inline void setDataSource(const QString &ds) { m_dataSource = ds; }
+
+ /*! \return the mime type of the data source for this widget.
+ Data source mime type means here types like "kexi/table" or "kexi/query"
+ in.the data source is set to object (as within form or subform) or is empty
+ if the data source is set to table field or query column. */
+ inline QCString dataSourceMimeType() const { return m_dataSourceMimeType; }
+
+ /*! Sets the mime type of the data source for this widget.
+ Data source usually means here a "kexi/table" or "kexi/query".
+ @see dataSourceMimeType() */
+ inline void setDataSourceMimeType(const QCString &ds) { m_dataSourceMimeType = ds; }
+
+ /*! If \a displayDefaultValue is true, the value set by KexiDataItemInterface::setValue()
+ is displayed in a special way. Used by KexiFormDataProvider::fillDataItems().
+ \a widget is equal to 'this'.
+ You can reimplement this in the widget. Always call the superclass' implementation.
+ setDisplayDefaultValue(.., false) is called in KexiFormScrollView::valueChanged()
+ as a response on data change performed by user. */
+ virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue);
+
+ /*! \return true if default value is displayed for this item. */
+ virtual bool hasDisplayedDefaultValue() const { return m_displayDefaultValue; }
+
+ /*! Convenience function: casts this item to a QWidget.
+ Can return 0 if the item is not a QWidget-derived object. */
+ virtual QWidget* widget() { return dynamic_cast<QWidget*>(this); }
+
+ /*! Sets 'invalid' state, e.g. a text editor widget should display
+ text \a displayText and become read only to prevent entering data,
+ because updating at the database backend is not available.
+ \a displayText is usually set to something i18n'd like "#NAME?".
+ Note: that even widgets that usualy do not display texts (e.g. pixmaps)
+ should display \a displayText too.
+ */
+ virtual void setInvalidState( const QString& displayText ) = 0;
+
+ /*! Changes 'read only' flag, for this widget.
+ Typically this flag can be passed to a widget itself,
+ e.g. KLineEdit::setReadOnly(bool). */
+ virtual void setReadOnly( bool readOnly ) = 0;
+
+ //! \return database column information for this item
+ virtual KexiDB::Field* field() const;
+
+ //! \return database column information for this item
+ virtual KexiDB::QueryColumnInfo* columnInfo() const { return m_columnInfo; }
+
+ /*! Used internally to set database column information.
+ Reimplement if you need to do additional actions,
+ e.g. set data validator based on field type. Don't forget about
+ calling superclass implementation. */
+ virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo) { m_columnInfo = cinfo; }
+
+ /*! Used internally to set visible database column information.
+ Reimplemented in KexiDBComboBox: except for combo box, this does nothing. */
+ virtual void setVisibleColumnInfo(KexiDB::QueryColumnInfo* cinfo) { Q_UNUSED(cinfo); }
+
+ /*! \return visible database column information for this item.
+ Except for combo box, this is exactly the same as columnInfo(). */
+ virtual KexiDB::QueryColumnInfo* visibleColumnInfo() const { return columnInfo(); }
+
+ /*! Does nothing, because within forms, widgets are always visible. */
+ virtual void hideWidget() { }
+
+ /*! Does nothing, because within forms, widgets are always visible. */
+ virtual void showWidget() { }
+
+ /*! Undoes changes made to this item - just resets the widget to original value.
+ Note: This is internal method called by KexiFormScrollView::cancelEditor().
+ To cancel editing of the widget's data from the widget's code,
+ use KexiFormDataItemInterface::cancelEditor().
+ Reimplemented in KexiDBComboBox to also revert the visible value (i.e. text) to the original state.
+ */
+ virtual void undoChanges();
+
+ /* Cancels editing of the widget's data. This method just looks for
+ the (grand)parent KexiFormScrollView object and calls
+ KexiFormScrollView::cancelEditor(). */
+ void cancelEditor();
+
+ /*! @internal
+ Called by top-level form on key press event.
+ Default implementation does nothing.
+ Implement this if you want to handle key presses from within the editor widget item.
+ \return true if \a ke should be accepted by the widget item.
+ This method is used e.g. in KexiDBImageBox for Key_Escape to if the popup is visible,
+ so the key press won't be consumed to perform "cancel editing". */
+ virtual bool keyPressed(QKeyEvent *ke) { Q_UNUSED(ke); return false; };
+
+ protected:
+ QString m_dataSource;
+ QCString m_dataSourceMimeType;
+ KexiDB::QueryColumnInfo* m_columnInfo;
+ KexiDisplayUtils::DisplayParameters *m_displayParametersForEnteredValue; //!< used in setDisplayDefaultValue()
+ KexiDisplayUtils::DisplayParameters *m_displayParametersForDefaultValue; //!< used in setDisplayDefaultValue()
+ bool m_displayDefaultValue : 1; //!< used by setDisplayDefaultValue()
+
+ friend class KexiDBAutoField;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexiformeventhandler.cpp b/kexi/plugins/forms/kexiformeventhandler.cpp
new file mode 100644
index 00000000..01bca201
--- /dev/null
+++ b/kexi/plugins/forms/kexiformeventhandler.cpp
@@ -0,0 +1,188 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiformeventhandler.h"
+
+#include <qwidget.h>
+#include <qobjectlist.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <tableview/kexitableitem.h>
+#include <tableview/kexitableviewdata.h>
+#include <kexidb/queryschema.h>
+#include <keximainwindow.h>
+#include <kexidialogbase.h>
+#include <kexipart.h>
+#include <kexipartinfo.h>
+#include <kexipartitem.h>
+
+KexiFormEventAction::ActionData::ActionData()
+{
+}
+
+bool KexiFormEventAction::ActionData::isEmpty() const
+{
+ return string.isEmpty();
+}
+
+KexiPart::Info* KexiFormEventAction::ActionData::decodeString(
+ QString& actionType, QString& actionArg, bool& ok) const
+{
+ const int idx = string.find(':');
+ ok = false;
+ if (idx==-1)
+ return 0;
+ const QString _actionType = string.left(idx);
+ const QString _actionArg = string.mid(idx+1);
+ if (_actionType.isEmpty() || _actionArg.isEmpty())
+ return 0;
+ KexiPart::Info *info = 0;
+ if (_actionType!="kaction" && _actionType!="currentForm") {
+ info = Kexi::partManager().infoForMimeType( QString("kexi/%1").arg(_actionType) );
+ if (!info)
+ return 0;
+ }
+ actionType = _actionType;
+ actionArg = _actionArg;
+ ok = true;
+ return info;
+}
+
+//-------------------------------------
+
+KexiFormEventAction::KexiFormEventAction(KexiMainWindow *mainWin, QObject* parent,
+ const QString& actionName, const QString& objectName, const QString& actionOption)
+ : KAction(parent), m_mainWin(mainWin), m_actionName(actionName), m_objectName(objectName)
+ , m_actionOption(actionOption)
+{
+}
+
+KexiFormEventAction::~KexiFormEventAction()
+{
+}
+
+void KexiFormEventAction::activate()
+{
+ KexiProject* project = m_mainWin->project();
+ if (!project)
+ return;
+ KexiPart::Part* part = Kexi::partManager().partForMimeType(
+ QString("kexi/%1").arg(m_actionName) );
+ if (!part)
+ return;
+ KexiPart::Item* item = project->item( part->info(), m_objectName );
+ if (!item)
+ return;
+ bool actionCancelled = false;
+ if (m_actionOption.isEmpty()) { // backward compatibility (good defaults)
+ if (part->info()->isExecuteSupported())
+ part->execute(item, parent());
+ else
+ m_mainWin->openObject(item, Kexi::DataViewMode, actionCancelled);
+ }
+ else {
+//! @todo react on failure...
+ if (m_actionOption == "open")
+ m_mainWin->openObject(item, Kexi::DataViewMode, actionCancelled);
+ else if (m_actionOption == "execute")
+ part->execute(item, parent());
+ else if (m_actionOption == "print") {
+ if (part->info()->isPrintingSupported())
+ m_mainWin->printItem(item);
+ }
+ else if (m_actionOption == "printPreview") {
+ if (part->info()->isPrintingSupported())
+ m_mainWin->printPreviewForItem(item);
+ }
+ else if (m_actionOption == "pageSetup") {
+ if (part->info()->isPrintingSupported())
+ m_mainWin->showPageSetupForItem(item);
+ }
+ else if (m_actionOption == "exportToCSV"
+ || m_actionOption == "copyToClipboardAsCSV")
+ {
+ if (part->info()->isDataExportSupported())
+ m_mainWin->executeCustomActionForObject(item, m_actionOption);
+ }
+ else if (m_actionOption == "new")
+ m_mainWin->newObject( part->info(), actionCancelled );
+ else if (m_actionOption == "design")
+ m_mainWin->openObject(item, Kexi::DesignViewMode, actionCancelled);
+ else if (m_actionOption == "editText")
+ m_mainWin->openObject(item, Kexi::TextViewMode, actionCancelled);
+ else if (m_actionOption == "close") {
+ tristate res = m_mainWin->closeObject(item);
+ if (~res)
+ actionCancelled = true;
+ }
+ }
+}
+
+//------------------------------------------
+
+KexiFormEventHandler::KexiFormEventHandler()
+ : m_mainWidget(0)
+{
+}
+
+KexiFormEventHandler::~KexiFormEventHandler()
+{
+}
+
+void KexiFormEventHandler::setMainWidgetForEventHandling(KexiMainWindow *mainWin, QWidget* mainWidget)
+{
+ m_mainWidget = mainWidget;
+ if (!m_mainWidget)
+ return;
+
+ //find widgets whose will work as data items
+//! @todo look for other widgets too
+ QObjectList *l = m_mainWidget->queryList( "KexiPushButton" );
+ QObjectListIt it( *l );
+ QObject *obj;
+ for ( ; (obj = it.current()) != 0; ++it ) {
+ bool ok;
+ KexiFormEventAction::ActionData data;
+ data.string = obj->property("onClickAction").toString();
+ data.option = obj->property("onClickActionOption").toString();
+ if (data.isEmpty())
+ continue;
+
+ QString actionType, actionArg;
+ KexiPart::Info* partInfo = data.decodeString(actionType, actionArg, ok);
+ if (!ok)
+ continue;
+ if (actionType=="kaction" || actionType=="currentForm") {
+ KAction *action = mainWin->actionCollection()->action( actionArg.latin1() );
+ if (!action)
+ continue;
+ QObject::disconnect( obj, SIGNAL(clicked()), action, SLOT(activate()) ); //safety
+ QObject::connect( obj, SIGNAL(clicked()), action, SLOT(activate()) );
+ }
+ else if (partInfo) { //'open or execute' action
+ KexiFormEventAction* action = new KexiFormEventAction(mainWin, obj, actionType, actionArg,
+ data.option);
+ QObject::disconnect( obj, SIGNAL(clicked()), action, SLOT(activate()) );
+ QObject::connect( obj, SIGNAL(clicked()), action, SLOT(activate()) );
+ }
+ }
+ delete l;
+}
diff --git a/kexi/plugins/forms/kexiformeventhandler.h b/kexi/plugins/forms/kexiformeventhandler.h
new file mode 100644
index 00000000..e92e9ff9
--- /dev/null
+++ b/kexi/plugins/forms/kexiformeventhandler.h
@@ -0,0 +1,101 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIFORMEVENTHANDLER_H
+#define KEXIFORMEVENTHANDLER_H
+
+#include <qwidget.h>
+#include <kaction.h>
+
+class KexiMainWindow;
+namespace KexiPart {
+ class Info;
+}
+
+//! The KexiFormEventHandler class handles events defined within Kexi Forms
+/*! For now only "onClickAction" property of Push Button widget is handled:
+ It's possible to connect this event to predefined global action.
+
+ Note: This interface will be extended in the future!
+
+ @see KexiFormPart::slotAssignAction()
+ */
+class KEXIFORMUTILS_EXPORT KexiFormEventHandler
+{
+ public:
+ KexiFormEventHandler();
+ virtual ~KexiFormEventHandler();
+
+ /*! Sets \a mainWidget to be a main widget for this handler.
+ Also find widgets having action assigned and connects them
+ to appropriate actions.
+ For now, all of them must be KexiPushButton).
+ \a mainWin is used to get action list. */
+ void setMainWidgetForEventHandling(KexiMainWindow *mainWin, QWidget* mainWidget);
+
+ protected:
+ QWidget *m_mainWidget;
+};
+
+//! @internal form-level action for handling "on click" actions
+class KEXIFORMUTILS_EXPORT KexiFormEventAction : public KAction
+{
+ public:
+ //! A structure used in currentActionName()
+ class KEXIFORMUTILS_EXPORT ActionData
+ {
+ public:
+ ActionData();
+
+ /*! Decodes action string into action type/action argument parts.
+ Action string has to be in a form of "actiontype:actionarg"
+ - Action type is passed to \a actionType on success. Action type can be "kaction"
+ or any of the part names (see KexiPart::Info::objectName()), e.g. "table", "query", etc.
+ - Action argument can be an action name in case of "kaction" type or object name
+ in case of action of type "table", "query", etc.
+ \a ok is set to true on success and to false on failure. On failure no other
+ values are passed.
+ \return part info if action type is "table", "query", etc., or 0 for "kaction" type. */
+ KexiPart::Info* decodeString(QString& actionType, QString& actionArg, bool& ok) const;
+
+ //! \return true if the action is empty
+ bool isEmpty() const;
+
+ QString string; //!< action string with prefix, like "kaction:edit_copy" or "table:<tableName>"
+
+ QString option; //!< option used when name is "table/query/etc.:\<objectName\>" is set;
+ //!< can be set to "open", "design", "editText", etc.
+ //!< @see ActionToExecuteListView::showActionsForMimeType()
+ };
+
+ KexiFormEventAction(KexiMainWindow *mainWin, QObject* parent, const QString& actionName,
+ const QString& objectName, const QString& actionOption);
+ virtual ~KexiFormEventAction();
+
+ public slots:
+ //! Activates the action. If the object supports executing (macro, script),
+ //! it is executed; otherwise (table, query, form,...) it is opened in its data view.
+ virtual void activate();
+
+ private:
+ KexiMainWindow *m_mainWin;
+ QString m_actionName, m_objectName, m_actionOption;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexiformhandler.desktop b/kexi/plugins/forms/kexiformhandler.desktop
new file mode 100644
index 00000000..3b476daa
--- /dev/null
+++ b/kexi/plugins/forms/kexiformhandler.desktop
@@ -0,0 +1,115 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Forms
+GenericName[bg]=Формуляри
+GenericName[br]=Paperennoù-reol
+GenericName[ca]=Formularis
+GenericName[cs]=Formuláře
+GenericName[cy]=Ffurflenni
+GenericName[da]=Formularer
+GenericName[de]=Formulare
+GenericName[el]=Φόρμες
+GenericName[eo]=Formularoj
+GenericName[es]=Formularios
+GenericName[et]=Vormid
+GenericName[eu]=Formularioak
+GenericName[fa]=برگه‌ها
+GenericName[fi]=Lomakkeet
+GenericName[fr]=Formulaires
+GenericName[fy]=Formulieren
+GenericName[ga]=Foirmeacha
+GenericName[gl]=Formularios
+GenericName[he]=טפסים
+GenericName[hi]=फॉर्म्स
+GenericName[hr]=Obrasci
+GenericName[hu]=Űrlapok
+GenericName[is]=Form
+GenericName[it]=Moduli
+GenericName[ja]=フォーム
+GenericName[km]=សំណុំបែបបទ
+GenericName[lt]=Formos
+GenericName[lv]=Formas
+GenericName[ms]=Borang
+GenericName[nb]=Skjema
+GenericName[nds]=Kiekwarken
+GenericName[ne]=फारमहरू
+GenericName[nn]=Skjema
+GenericName[pl]=Formularze
+GenericName[pt]=Formulários
+GenericName[pt_BR]=Formulários
+GenericName[ru]=Формы
+GenericName[se]=Skovit
+GenericName[sk]=Formuláre
+GenericName[sl]=Obrazci
+GenericName[sr]=Форме
+GenericName[sr@Latn]=Forme
+GenericName[sv]=Formulär
+GenericName[ta]=படிவங்கள்
+GenericName[tr]=Formlar
+GenericName[uk]=Форми
+GenericName[uz]=Shakllar
+GenericName[uz@cyrillic]=Шакллар
+GenericName[zh_CN]=表单
+GenericName[zh_TW]=表單
+Name=Forms
+Name[bg]=Формуляри
+Name[br]=Paperennoù-reol
+Name[ca]=Formularis
+Name[cs]=Formuláře
+Name[cy]=Ffurflenni
+Name[da]=Formularer
+Name[de]=Formulare
+Name[el]=Φόρμες
+Name[eo]=Formularoj
+Name[es]=Formularios
+Name[et]=Vormid
+Name[eu]=Formularioak
+Name[fa]=برگه‌ها
+Name[fi]=Lomakkeet
+Name[fr]=Formulaires
+Name[fy]=Formulieren
+Name[ga]=Foirmeacha
+Name[gl]=Formularios
+Name[he]=טפסים
+Name[hi]=फ़ॉर्म
+Name[hr]=Obrasci
+Name[hu]=Űrlapok
+Name[is]=Form
+Name[it]=Moduli
+Name[ja]=フォーム
+Name[km]=សំណុំបែបបទ
+Name[lt]=Formos
+Name[lv]=Formas
+Name[ms]=Borang
+Name[nb]=Skjema
+Name[nds]=Kiekwarken
+Name[ne]=फारमहरू
+Name[nn]=Skjema
+Name[pl]=Formularze
+Name[pt]=Formulários
+Name[pt_BR]=Formulários
+Name[ru]=Формы
+Name[se]=Skovit
+Name[sk]=Formuláre
+Name[sl]=Obrazci
+Name[sr]=Форме
+Name[sr@Latn]=Forme
+Name[sv]=Formulär
+Name[ta]=Kவிதி
+Name[tg]=Шаклҳо
+Name[tr]=Formlar
+Name[uk]=Форми
+Name[uz]=Shakllar
+Name[uz@cyrillic]=Шакллар
+Name[zh_CN]=表单
+Name[zh_TW]=表單
+X-KDE-Library=kexihandler_form
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=form
+X-Kexi-TypeMime=kexi/form
+X-Kexi-ItemIcon=form
+X-Kexi-SupportsDataExport=false
+X-Kexi-SupportsPrinting=false
diff --git a/kexi/plugins/forms/kexiformmanager.cpp b/kexi/plugins/forms/kexiformmanager.cpp
new file mode 100644
index 00000000..6134cfc8
--- /dev/null
+++ b/kexi/plugins/forms/kexiformmanager.cpp
@@ -0,0 +1,235 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiformmanager.h"
+#include "widgets/kexidbform.h"
+#include "widgets/kexidbautofield.h"
+#include "kexiformscrollview.h"
+#include "kexiformview.h"
+#include "kexidatasourcepage.h"
+
+#include <formeditor/formmanager.h>
+#include <formeditor/widgetpropertyset.h>
+#include <formeditor/form.h>
+#include <formeditor/widgetlibrary.h>
+#include <formeditor/commands.h>
+#include <formeditor/objecttree.h>
+
+#include <koproperty/set.h>
+#include <koproperty/property.h>
+#include <widget/kexicustompropertyfactory.h>
+
+KexiFormManager::KexiFormManager(KexiPart::Part *parent, const char* name)
+ : KFormDesigner::FormManager(parent,
+ KFormDesigner::FormManager::HideEventsInPopupMenu |
+ KFormDesigner::FormManager::SkipFileActions |
+ KFormDesigner::FormManager::HideSignalSlotConnections
+ , name)
+ , m_part(parent)
+{
+ m_emitSelectionSignalsUpdatesPropertySet = true;
+ KexiCustomPropertyFactory::init();
+}
+
+KexiFormManager::~KexiFormManager()
+{
+}
+
+KAction* KexiFormManager::action( const char* name )
+{
+ KActionCollection *col = m_part->actionCollectionForMode(Kexi::DesignViewMode);
+ if (!col)
+ return 0;
+ QCString n( translateName( name ).latin1() );
+ KAction *a = col->action(n);
+ if (a)
+ return a;
+ KexiDBForm *dbform;
+ if (!activeForm() || !activeForm()->designMode()
+ || !(dbform = dynamic_cast<KexiDBForm*>(activeForm()->formWidget())))
+ return 0;
+ KexiFormScrollView *scrollViewWidget = dynamic_cast<KexiFormScrollView*>(dbform->dataAwareObject());
+ if (!scrollViewWidget)
+ return 0;
+ KexiFormView* formViewWidget = dynamic_cast<KexiFormView*>(scrollViewWidget->parent());
+ if (!formViewWidget)
+ return 0;
+ return formViewWidget->parentDialog()->mainWin()->actionCollection()->action(n);
+}
+
+KexiFormView* KexiFormManager::activeFormViewWidget() const
+{
+ KexiDBForm *dbform;
+ if (!activeForm() || !activeForm()->designMode()
+ || !(dbform = dynamic_cast<KexiDBForm*>(activeForm()->formWidget())))
+ return 0;
+ KexiFormScrollView *scrollViewWidget = dynamic_cast<KexiFormScrollView*>(dbform->dataAwareObject());
+ if (!scrollViewWidget)
+ return 0;
+ return dynamic_cast<KexiFormView*>(scrollViewWidget->parent());
+}
+
+void KexiFormManager::enableAction( const char* name, bool enable )
+{
+ KexiFormView* formViewWidget = activeFormViewWidget();
+ if (!formViewWidget)
+ return;
+// if (QString(name)=="layout_menu")
+// kdDebug() << "!!!!!!!!!!! " << enable << endl;
+ formViewWidget->setAvailable(translateName( name ).latin1(), enable);
+}
+
+void KexiFormManager::setFormDataSource(const QCString& mime, const QCString& name)
+{
+ if (!activeForm())
+ return;
+ KexiDBForm* formWidget = dynamic_cast<KexiDBForm*>(activeForm()->widget());
+ if (!formWidget)
+ return;
+
+// setPropertyValueInDesignMode(formWidget, "dataSource", name);
+
+ QCString oldDataSourceMimeType( formWidget->dataSourceMimeType() );
+ QCString oldDataSource( formWidget->dataSource().latin1() );
+ if (mime!=oldDataSourceMimeType || name!=oldDataSource) {
+ QMap<QCString, QVariant> propValues;
+ propValues.insert("dataSource", name);
+ propValues.insert("dataSourceMimeType", mime);
+ KFormDesigner::CommandGroup *group
+ = new KFormDesigner::CommandGroup(i18n("Set Form's Data Source to \"%1\"").arg(name), propertySet());
+ propertySet()->createPropertyCommandsInDesignMode(formWidget, propValues, group, true /*addToActiveForm*/);
+ }
+
+/*
+ if (activeForm()->selectedWidget() == formWidget) {
+ //active form is selected: just use properties system
+ KFormDesigner::WidgetPropertySet *set = propertySet();
+ if (!set || !set->contains("dataSource"))
+ return;
+ (*set)["dataSource"].setValue(name);
+ if (set->contains("dataSourceMimeType"))
+ (*set)["dataSourceMimeType"].setValue(mime);
+ return;
+ }
+
+ //active form isn't selected: change it's data source and mime type by hand
+ QCString oldDataSourceMimeType( formWidget->dataSourceMimeType() );
+ QCString oldDataSource( formWidget->dataSource().latin1() );
+
+ if (mime!=oldDataSourceMimeType || name!=oldDataSource) {
+ formWidget->setDataSourceMimeType(mime);
+ formWidget->setDataSource(name);
+ emit dirty(activeForm(), true);
+
+ activeForm()->addCommand(
+ new KFormDesigner::PropertyCommand(propertySet(), QString(formWidget->name()),
+ oldDataSource, name, "dataSource"),
+ false );
+
+ // If the property is changed, we add it in ObjectTreeItem modifProp
+ KFormDesigner::ObjectTreeItem *fromTreeItem = activeForm()->objectTree()->lookup(formWidget->name());
+ fromTreeItem->addModifiedProperty("dataSourceMimeType", mime);
+ fromTreeItem->addModifiedProperty("dataSource", name);
+ }*/
+}
+
+void KexiFormManager::setDataSourceFieldOrExpression(const QString& string, const QString& caption,
+ KexiDB::Field::Type type)
+{
+ if (!activeForm())
+ return;
+// KexiFormDataItemInterface* dataWidget = dynamic_cast<KexiFormDataItemInterface*>(activeForm()->selectedWidget());
+// if (!dataWidget)
+// return;
+
+ KFormDesigner::WidgetPropertySet *set = propertySet();
+ if (!set || !set->contains("dataSource"))
+ return;
+
+ (*set)["dataSource"].setValue(string);
+
+ if (set->contains("autoCaption") && (*set)["autoCaption"].value().toBool()) {
+ if (set->contains("fieldCaptionInternal"))
+ (*set)["fieldCaptionInternal"].setValue(caption);
+ }
+ if (//type!=KexiDB::Field::InvalidType &&
+ set->contains("widgetType") && (*set)["widgetType"].value().toString()=="Auto")
+ {
+ if (set->contains("fieldTypeInternal"))
+ (*set)["fieldTypeInternal"].setValue(type);
+ }
+
+/* QString oldDataSource( dataWidget->dataSource() );
+ if (string!=oldDataSource) {
+ dataWidget->setDataSource(string);
+ emit dirty(activeForm(), true);
+
+ buffer
+ }*/
+}
+
+void KexiFormManager::insertAutoFields(const QString& sourceMimeType, const QString& sourceName,
+ const QStringList& fields)
+{
+ KexiFormView* formViewWidget = activeFormViewWidget();
+ if (!formViewWidget || !formViewWidget->form() || !formViewWidget->form()->activeContainer())
+ return;
+ formViewWidget->insertAutoFields(sourceMimeType, sourceName, fields,
+ formViewWidget->form()->activeContainer());
+}
+
+void KexiFormManager::slotHistoryCommandExecuted()
+{
+ const KFormDesigner::CommandGroup *group = dynamic_cast<const KFormDesigner::CommandGroup*>(sender());
+ if (group) {
+ if (group->commands().count()==2) {
+ KexiDBForm* formWidget = dynamic_cast<KexiDBForm*>(activeForm()->widget());
+ if (!formWidget)
+ return;
+ QPtrListIterator<KCommand> it(group->commands());
+ const KFormDesigner::PropertyCommand* pc1 = dynamic_cast<const KFormDesigner::PropertyCommand*>(it.current());
+ ++it;
+ const KFormDesigner::PropertyCommand* pc2 = dynamic_cast<const KFormDesigner::PropertyCommand*>(it.current());
+ if (pc1 && pc2 && pc1->property()=="dataSource" && pc2->property()=="dataSourceMimeType") {
+ const QMap<QCString, QVariant>::const_iterator it1( pc1->oldValues().constBegin() );
+ const QMap<QCString, QVariant>::const_iterator it2( pc2->oldValues().constBegin() );
+ if (it1.key()==formWidget->name() && it2.key()==formWidget->name())
+ static_cast<KexiFormPart*>(m_part)->dataSourcePage()->setDataSource(
+ formWidget->dataSourceMimeType(), formWidget->dataSource().latin1());
+ }
+ }
+ }
+}
+
+/*
+bool KexiFormManager::loadFormFromDomInternal(Form *form, QWidget *container, QDomDocument &inBuf)
+{
+ QMap<QCString,QString> customProperties;
+ FormIO::loadFormFromDom(myform, container, domDoc, &customProperties);
+}
+
+bool KexiFormManager::saveFormToStringInternal(Form *form, QString &dest, int indent)
+{
+ QMap<QCString,QString> customProperties;
+ return KFormDesigner::FormIO::saveFormToString(form, dest, indent, &customProperties);
+}
+
+*/
+
+#include "kexiformmanager.moc"
diff --git a/kexi/plugins/forms/kexiformmanager.h b/kexi/plugins/forms/kexiformmanager.h
new file mode 100644
index 00000000..1cc5f0c6
--- /dev/null
+++ b/kexi/plugins/forms/kexiformmanager.h
@@ -0,0 +1,87 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIFORMMANAGER_H
+#define KEXIFORMMANAGER_H
+
+#include <formmanager.h>
+#include <kexipart.h>
+
+class KCommand;
+class KexiFormView;
+
+//! @internal
+//! Used to customize KFormDesigner::FormManager behaviour.
+class KEXIFORMUTILS_EXPORT KexiFormManager : public KFormDesigner::FormManager
+{
+ Q_OBJECT
+
+ public:
+ KexiFormManager(KexiPart::Part *parent, const char* name = 0);
+ virtual ~KexiFormManager();
+
+ virtual KAction* action( const char* name );
+ virtual void enableAction( const char* name, bool enable );
+
+ public slots:
+ //! Receives signal from KexiDataSourcePage about changed form's data source
+ void setFormDataSource(const QCString& mime, const QCString& name);
+
+ /*! Receives signal from KexiDataSourcePage about changed widget's data source.
+ This is because we couldn't pass objects like KexiDB::QueryColumnInfo.
+
+ Also sets following things in KexiDBAutoField:
+ - caption related to the data source
+ - data type related to the data source */
+ void setDataSourceFieldOrExpression(const QString& string, const QString& caption,
+ KexiDB::Field::Type type);
+
+ /*! Receives signal from KexiDataSourcePage and inserts autofields onto the current form. */
+ void insertAutoFields(const QString& sourceMimeType, const QString& sourceName,
+ const QStringList& fields);
+
+ protected slots:
+ void slotHistoryCommandExecuted();
+
+ protected:
+ inline QString translateName( const char* name ) const;
+
+ private:
+ //! Helper: return active form's view widget or 0 if there's no active form having such widget
+ KexiFormView* activeFormViewWidget() const;
+
+// virtual bool loadFormFromDomInternal(Form *form, QWidget *container, QDomDocument &inBuf);
+// virtual bool saveFormToStringInternal(Form *form, QString &dest, int indent = 0);
+
+ KexiPart::Part* m_part;
+};
+
+QString KexiFormManager::translateName( const char* name ) const
+{
+ QString n( name );
+ //translate to our name space:
+ if (n.startsWith("align_") || n.startsWith("adjust_") || n.startsWith("layout_")
+ || n=="format_raise" || n=="format_raise" || n=="taborder" | n=="break_layout")
+ {
+ n.prepend("formpart_");
+ }
+ return n;
+}
+
+#endif
diff --git a/kexi/plugins/forms/kexiformpart.cpp b/kexi/plugins/forms/kexiformpart.cpp
new file mode 100644
index 00000000..8693cb5b
--- /dev/null
+++ b/kexi/plugins/forms/kexiformpart.cpp
@@ -0,0 +1,550 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kdialogbase.h>
+#include <klistview.h>
+#include <ktabwidget.h>
+#include <kiconloader.h>
+#include <kcombobox.h>
+#include <kapplication.h>
+#include <kconfig.h>
+
+#include <kexiviewbase.h>
+#include <keximainwindow.h>
+#include <kexiproject.h>
+#include <kexipartitem.h>
+#include <kexidialogbase.h>
+#include <kexidatasourcecombobox.h>
+#include <kexidb/connection.h>
+#include <kexidb/fieldlist.h>
+#include <kexidb/field.h>
+#include <kexiutils/utils.h>
+
+#include <form.h>
+#include <formIO.h>
+#include <widgetpropertyset.h>
+#include <widgetlibrary.h>
+#include <objecttreeview.h>
+#include <koproperty/property.h>
+
+#include "kexiformview.h"
+#include "widgets/kexidbform.h"
+#include "kexiformscrollview.h"
+#include "kexiactionselectiondialog.h"
+#include "kexiformmanager.h"
+#include "kexiformpart.h"
+#include "kexidatasourcepage.h"
+
+//! @todo #define KEXI_SHOW_SPLITTER_WIDGET
+
+KFormDesigner::WidgetLibrary* KexiFormPart::static_formsLibrary = 0L;
+
+//! @internal
+class KexiFormPart::Private
+{
+ public:
+ Private()
+ {
+ }
+ ~Private()
+ {
+ delete static_cast<KFormDesigner::ObjectTreeView*>(objectTreeView);
+ delete static_cast<KexiDataSourcePage*>(dataSourcePage);
+ }
+// QGuardedPtr<KFormDesigner::FormManager> manager;
+ QGuardedPtr<KFormDesigner::ObjectTreeView> objectTreeView;
+ QGuardedPtr<KexiDataSourcePage> dataSourcePage;
+ KexiDataSourceComboBox *dataSourceCombo;
+};
+
+KexiFormPart::KexiFormPart(QObject *parent, const char *name, const QStringList &l)
+ : KexiPart::Part(parent, name, l)
+ , d(new Private())
+{
+ // REGISTERED ID:
+ m_registeredPartID = (int)KexiPart::FormObjectType;
+
+ kexipluginsdbg << "KexiFormPart::KexiFormPart()" << endl;
+ m_names["instanceName"]
+ = i18n("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). "
+ "Use '_' character instead of spaces. First character should be a..z character. "
+ "If you cannot use latin characters in your language, use english word.",
+ "form");
+ m_names["instanceCaption"] = i18n("Form");
+ m_supportedViewModes = Kexi::DataViewMode | Kexi::DesignViewMode;
+ m_newObjectsAreDirty = true;
+
+ // Only create form manager if it's not yet created.
+ // KexiReportPart could have created it already.
+ KFormDesigner::FormManager *formManager = KFormDesigner::FormManager::self();
+ if (!formManager)
+ formManager = new KexiFormManager(this, "kexi_form_and_report_manager");
+
+ // Create and store a handle to forms' library. Reports will have their own library too.
+/* @todo add configuration for supported factory groups */
+ QStringList supportedFactoryGroups;
+ supportedFactoryGroups += "kexi";
+ static_formsLibrary = KFormDesigner::FormManager::createWidgetLibrary(
+ formManager, supportedFactoryGroups);
+ static_formsLibrary->setAdvancedPropertiesVisible(false);
+ connect(static_formsLibrary, SIGNAL(widgetCreated(QWidget*)),
+ this, SLOT(slotWidgetCreatedByFormsLibrary(QWidget*)));
+
+ connect(KFormDesigner::FormManager::self()->propertySet(), SIGNAL(widgetPropertyChanged(QWidget *, const QCString &, const QVariant&)),
+ this, SLOT(slotPropertyChanged(QWidget *, const QCString &, const QVariant&)));
+ connect(KFormDesigner::FormManager::self(), SIGNAL(autoTabStopsSet(KFormDesigner::Form*,bool)),
+ this, SLOT(slotAutoTabStopsSet(KFormDesigner::Form*,bool)));
+}
+
+KexiFormPart::~KexiFormPart()
+{
+ delete d;
+}
+
+KFormDesigner::WidgetLibrary* KexiFormPart::library()
+{
+ return static_formsLibrary;
+}
+
+#if 0
+void KexiFormPart::initPartActions(KActionCollection *collection)
+{
+//this is automatic? -no
+//create child guicilent: guiClient()->setXMLFile("kexidatatableui.rc");
+
+ kexipluginsdbg<<"FormPart INIT ACTIONS***********************************************************************"<<endl;
+ //TODO
+
+ //guiClient()->setXMLFile("kexiformui.rc");
+//js m_manager->createActions(collection, 0);
+}
+
+void KexiFormPart::initInstanceActions( int mode, KActionCollection *col )
+{
+ if (mode==Kexi::DesignViewMode) {
+ KFormDesigner::FormManager::self()->createActions(col, 0);
+ new KAction(i18n("Edit Tab Order..."), "tab_order", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(editTabOrder()), col, "taborder");
+ new KAction(i18n("Adjust Size"), "viewmagfit", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(ajustWidgetSize()), col, "adjust");
+ }
+ //TODO
+}
+#endif
+
+void KexiFormPart::initPartActions()
+{
+// new KAction(i18n("Show Form UI Code"), "show_form_ui", CTRL+Key_U, m_manager, SLOT(showFormUICode()),
+// guiClient()->actionCollection(), "show_form_ui");
+}
+
+void KexiFormPart::initInstanceActions()
+{
+#ifdef KEXI_DEBUG_GUI
+ kapp->config()->setGroup("General");
+ if (kapp->config()->readBoolEntry("showInternalDebugger", false)) {
+ new KAction(i18n("Show Form UI Code"), "compfile",
+ CTRL+Key_U, KFormDesigner::FormManager::self(), SLOT(showFormUICode()),
+ actionCollectionForMode(Kexi::DesignViewMode), "show_form_ui");
+ }
+#endif
+
+ KActionCollection *col = actionCollectionForMode(Kexi::DesignViewMode);
+ KFormDesigner::FormManager::self()->createActions( library(), col, (KXMLGUIClient*)col->parentGUIClient() ); //guiClient() );
+
+ //connect actions provided by widget factories
+ connect( col->action("widget_assign_action"), SIGNAL(activated()), this, SLOT(slotAssignAction()));
+
+ createSharedAction(Kexi::DesignViewMode, i18n("Clear Widget Contents"), "editclear", 0, "formpart_clear_contents");
+ createSharedAction(Kexi::DesignViewMode, i18n("Edit Tab Order..."), "tab_order", 0, "formpart_taborder");
+//TODO createSharedAction(Kexi::DesignViewMode, i18n("Edit Pixmap Collection"), "icons", 0, "formpart_pixmap_collection");
+//TODO createSharedAction(Kexi::DesignViewMode, i18n("Edit Form Connections"), "connections", 0, "formpart_connections");
+
+// KFormDesigner::CreateLayoutCommand
+
+ KAction *action = createSharedAction(Kexi::DesignViewMode, i18n("Layout Widgets"), "", 0, "formpart_layout_menu", "KActionMenu");
+ KActionMenu *menu = static_cast<KActionMenu*>(action);
+
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("&Horizontally"),
+ QString::null, 0, "formpart_layout_hbox"));
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("&Vertically"),
+ QString::null, 0, "formpart_layout_vbox"));
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("In &Grid"),
+ QString::null, 0, "formpart_layout_grid"));
+#ifdef KEXI_SHOW_SPLITTER_WIDGET
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("Horizontally in &Splitter"),
+ QString::null, 0, "formpart_layout_hsplitter"));
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("Verti&cally in Splitter"),
+ QString::null, 0, "formpart_layout_vsplitter"));
+#endif
+
+ createSharedAction(Kexi::DesignViewMode, i18n("&Break Layout"), QString::null, 0, "formpart_break_layout");
+/*
+ createSharedAction(Kexi::DesignViewMode, i18n("Lay Out Widgets &Horizontally"), QString::null, 0, "formpart_layout_hbox");
+ createSharedAction(Kexi::DesignViewMode, i18n("Lay Out Widgets &Vertically"), QString::null, 0, "formpart_layout_vbox");
+ createSharedAction(Kexi::DesignViewMode, i18n("Lay Out Widgets in &Grid"), QString::null, 0, "formpart_layout_grid");
+*/
+ createSharedAction(Kexi::DesignViewMode, i18n("Bring Widget to Front"), "raise", 0, "formpart_format_raise");
+ createSharedAction(Kexi::DesignViewMode, i18n("Send Widget to Back"), "lower", 0, "formpart_format_lower");
+
+#ifndef KEXI_NO_UNFINISHED
+ action = createSharedAction(Kexi::DesignViewMode, i18n("Other Widgets"), "", 0, "other_widgets_menu", "KActionMenu");
+#endif
+
+ action = createSharedAction(Kexi::DesignViewMode, i18n("Align Widgets Position"), "aoleft", 0, "formpart_align_menu", "KActionMenu");
+ menu = static_cast<KActionMenu*>(action);
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Left"), "aoleft", 0, "formpart_align_to_left") );
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Right"), "aoright", 0, "formpart_align_to_right") );
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Top"), "aotop", 0, "formpart_align_to_top") );
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Bottom"), "aobottom", 0, "formpart_align_to_bottom") );
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Grid"), "aopos2grid", 0, "formpart_align_to_grid") );
+
+ action = createSharedAction(Kexi::DesignViewMode, i18n("Adjust Widgets Size"), "aogrid", 0, "formpart_adjust_size_menu", "KActionMenu");
+ menu = static_cast<KActionMenu*>(action);
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Fit"), "aofit", 0, "formpart_adjust_to_fit") );
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Grid"), "aogrid", 0, "formpart_adjust_size_grid") );
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Shortest"), "aoshortest", 0, "formpart_adjust_height_small") );
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Tallest"), "aotallest", 0, "formpart_adjust_height_big") );
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Narrowest"), "aonarrowest", 0, "formpart_adjust_width_small") );
+ menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Widest"), "aowidest", 0, "formpart_adjust_width_big") );
+}
+
+KexiDialogTempData*
+KexiFormPart::createTempData(KexiDialogBase* dialog)
+{
+ return new KexiFormPart::TempData(dialog);
+}
+
+KexiViewBase* KexiFormPart::createView(QWidget *parent, KexiDialogBase* dialog,
+ KexiPart::Item &item, int viewMode, QMap<QString,QString>*)
+{
+ Q_UNUSED( viewMode );
+
+ kexipluginsdbg << "KexiFormPart::createView()" << endl;
+ KexiMainWindow *win = dialog->mainWin();
+ if (!win || !win->project() || !win->project()->dbConnection())
+ return 0;
+
+ KexiFormView *view = new KexiFormView(win, parent, item.name().latin1(),
+ win->project()->dbConnection() );
+
+ return view;
+}
+
+void
+KexiFormPart::generateForm(KexiDB::FieldList *list, QDomDocument &domDoc)
+{
+ //this form generates a .ui from FieldList list
+ //basically that is a Label and a LineEdit for each field
+ domDoc = QDomDocument("UI");
+ QDomElement uiElement = domDoc.createElement("UI");
+ domDoc.appendChild(uiElement);
+ uiElement.setAttribute("version", "3.1");
+ uiElement.setAttribute("stdsetdef", 1);
+
+ QDomElement baseClass = domDoc.createElement("class");
+ uiElement.appendChild(baseClass);
+ QDomText baseClassV = domDoc.createTextNode("QWidget");
+ baseClass.appendChild(baseClassV);
+ QDomElement baseWidget = domDoc.createElement("widget");
+ baseWidget.setAttribute("class", "QWidget");
+
+ int y=0;
+
+ for(unsigned int i=0; i < list->fieldCount(); i++)
+ {
+ QDomElement lclass = domDoc.createElement("widget");
+ baseWidget.appendChild(lclass);
+ lclass.setAttribute("class", "QLabel");
+ QDomElement lNameProperty = domDoc.createElement("property");
+ lNameProperty.setAttribute("name", "name");
+ QDomElement lType = domDoc.createElement("cstring");
+ QDomText lClassN = domDoc.createTextNode(QString("l%1").arg(list->field(i)->name()));
+ lType.appendChild(lClassN);
+ lNameProperty.appendChild(lType);
+ lclass.appendChild(lNameProperty);
+
+ QDomElement gNameProperty = domDoc.createElement("property");
+ gNameProperty.setAttribute("name", "geometry");
+ QDomElement lGType = domDoc.createElement("rect");
+
+ QDomElement lx = domDoc.createElement("x");
+ QDomText lxV = domDoc.createTextNode("10");
+ lx.appendChild(lxV);
+ QDomElement ly = domDoc.createElement("y");
+ QDomText lyV = domDoc.createTextNode(QString::number(y + 10));
+ ly.appendChild(lyV);
+ QDomElement lWidth = domDoc.createElement("width");
+ QDomText lWidthV = domDoc.createTextNode("100");
+ lWidth.appendChild(lWidthV);
+ QDomElement lHeight = domDoc.createElement("height");
+ QDomText lHeightV = domDoc.createTextNode("20");
+ lHeight.appendChild(lHeightV);
+
+ lGType.appendChild(lx);
+ lGType.appendChild(ly);
+ lGType.appendChild(lWidth);
+ lGType.appendChild(lHeight);
+
+ gNameProperty.appendChild(lGType);
+ lclass.appendChild(gNameProperty);
+
+ QDomElement tNameProperty = domDoc.createElement("property");
+ tNameProperty.setAttribute("name", "text");
+ QDomElement lTType = domDoc.createElement("string");
+ QDomText lTextV = domDoc.createTextNode(list->field(i)->name());
+ lTType.appendChild(lTextV);
+ tNameProperty.appendChild(lTType);
+ lclass.appendChild(tNameProperty);
+
+
+ ///line edit!
+
+
+ QDomElement vclass = domDoc.createElement("widget");
+ baseWidget.appendChild(vclass);
+ vclass.setAttribute("class", "KLineEdit");
+ QDomElement vNameProperty = domDoc.createElement("property");
+ vNameProperty.setAttribute("name", "name");
+ QDomElement vType = domDoc.createElement("cstring");
+ QDomText vClassN = domDoc.createTextNode(list->field(i)->name());
+ vType.appendChild(vClassN);
+ vNameProperty.appendChild(vType);
+ vclass.appendChild(vNameProperty);
+
+ QDomElement vgNameProperty = domDoc.createElement("property");
+ vgNameProperty.setAttribute("name", "geometry");
+ QDomElement vGType = domDoc.createElement("rect");
+
+ QDomElement vx = domDoc.createElement("x");
+ QDomText vxV = domDoc.createTextNode("110");
+ vx.appendChild(vxV);
+ QDomElement vy = domDoc.createElement("y");
+ QDomText vyV = domDoc.createTextNode(QString::number(y + 10));
+ vy.appendChild(vyV);
+ QDomElement vWidth = domDoc.createElement("width");
+ QDomText vWidthV = domDoc.createTextNode("200");
+ vWidth.appendChild(vWidthV);
+ QDomElement vHeight = domDoc.createElement("height");
+ QDomText vHeightV = domDoc.createTextNode("20");
+ vHeight.appendChild(vHeightV);
+
+ vGType.appendChild(vx);
+ vGType.appendChild(vy);
+ vGType.appendChild(vWidth);
+ vGType.appendChild(vHeight);
+
+ vgNameProperty.appendChild(vGType);
+ vclass.appendChild(vgNameProperty);
+
+ y += 20;
+ }
+
+ QDomElement lNameProperty = domDoc.createElement("property");
+ lNameProperty.setAttribute("name", "name");
+ QDomElement lType = domDoc.createElement("cstring");
+ QDomText lClassN = domDoc.createTextNode("DBForm");
+ lType.appendChild(lClassN);
+ lNameProperty.appendChild(lType);
+ baseWidget.appendChild(lNameProperty);
+
+ QDomElement wNameProperty = domDoc.createElement("property");
+ wNameProperty.setAttribute("name", "geometry");
+ QDomElement wGType = domDoc.createElement("rect");
+
+ QDomElement wx = domDoc.createElement("x");
+ QDomText wxV = domDoc.createTextNode("0");
+ wx.appendChild(wxV);
+ QDomElement wy = domDoc.createElement("y");
+ QDomText wyV = domDoc.createTextNode("0");
+ wy.appendChild(wyV);
+ QDomElement wWidth = domDoc.createElement("width");
+ QDomText wWidthV = domDoc.createTextNode("340");
+ wWidth.appendChild(wWidthV);
+ QDomElement wHeight = domDoc.createElement("height");
+ QDomText wHeightV = domDoc.createTextNode(QString::number(y + 30));
+ wHeight.appendChild(wHeightV);
+
+ wGType.appendChild(wx);
+ wGType.appendChild(wy);
+ wGType.appendChild(wWidth);
+ wGType.appendChild(wHeight);
+
+ wNameProperty.appendChild(wGType);
+ baseWidget.appendChild(wNameProperty);
+
+ uiElement.appendChild(baseWidget);
+}
+
+void KexiFormPart::slotAutoTabStopsSet(KFormDesigner::Form *form, bool set)
+{
+ Q_UNUSED( form );
+
+ KoProperty::Property &p = (*KFormDesigner::FormManager::self()->propertySet())["autoTabStops"];
+ if (!p.isNull())
+ p.setValue(QVariant(set, 4));
+}
+
+void KexiFormPart::slotAssignAction()
+{
+ KexiDBForm *dbform;
+ if (!KFormDesigner::FormManager::self()->activeForm() || !KFormDesigner::FormManager::self()->activeForm()->designMode()
+ || !(dbform = dynamic_cast<KexiDBForm*>(KFormDesigner::FormManager::self()->activeForm()->formWidget())))
+ return;
+
+ KFormDesigner::WidgetPropertySet * propSet = KFormDesigner::FormManager::self()->propertySet();
+
+ KoProperty::Property &onClickActionProp = propSet->property("onClickAction");
+ if (onClickActionProp.isNull())
+ return;
+ KoProperty::Property &onClickActionOptionProp = propSet->property("onClickActionOption");
+ KexiFormEventAction::ActionData data;
+ data.string = onClickActionProp.value().toString();
+ if (!onClickActionOptionProp.isNull())
+ data.option = onClickActionOptionProp.value().toString();
+
+ KexiFormScrollView *scrollViewWidget = dynamic_cast<KexiFormScrollView*>(dbform->dataAwareObject());
+ if (!scrollViewWidget)
+ return;
+ KexiFormView* formViewWidget = dynamic_cast<KexiFormView*>(scrollViewWidget->parent());
+ if (!formViewWidget)
+ return;
+
+ KexiMainWindow * mainWin = formViewWidget->parentDialog()->mainWin();
+ KexiActionSelectionDialog dlg(mainWin, dbform, data,
+ propSet->property("name").value().toCString());
+
+ if(dlg.exec() == QDialog::Accepted) {
+ data = dlg.currentAction();
+ //update property value
+ propSet->property("onClickAction").setValue(data.string);
+ propSet->property("onClickActionOption").setValue(data.option);
+ }
+}
+
+QString
+KexiFormPart::i18nMessage(const QCString& englishMessage, KexiDialogBase* dlg) const
+{
+ Q_UNUSED(dlg);
+ if (englishMessage=="Design of object \"%1\" has been modified.")
+ return i18n("Design of form \"%1\" has been modified.");
+ if (englishMessage=="Object \"%1\" already exists.")
+ return i18n("Form \"%1\" already exists.");
+
+ return englishMessage;
+}
+
+void
+KexiFormPart::slotPropertyChanged(QWidget *w, const QCString &name, const QVariant &value)
+{
+ Q_UNUSED( w );
+
+ if (!KFormDesigner::FormManager::self()->activeForm())
+ return;
+ if (name == "autoTabStops") {
+ //QWidget *w = KFormDesigner::FormManager::self()->activeForm()->selectedWidget();
+ //update autoTabStops setting at KFD::Form level
+ KFormDesigner::FormManager::self()->activeForm()->setAutoTabStops( value.toBool() );
+ }
+ if (KFormDesigner::FormManager::self()->activeForm()->widget() && name == "geometry") {
+ //fall back to sizeInternal property....
+ if (KFormDesigner::FormManager::self()->propertySet()->contains("sizeInternal"))
+ KFormDesigner::FormManager::self()->propertySet()->property("sizeInternal").setValue(value.toRect().size());
+ }
+}
+
+/*KFormDesigner::FormManager*
+KexiFormPart::manager() const
+{
+ return d->manager;
+}*/
+
+KexiDataSourcePage* KexiFormPart::dataSourcePage() const
+{
+ return d->dataSourcePage;
+}
+
+void KexiFormPart::setupCustomPropertyPanelTabs(KTabWidget *tab, KexiMainWindow* mainWin)
+{
+ if (!d->objectTreeView) {
+ d->objectTreeView = new KFormDesigner::ObjectTreeView(0, "KexiFormPart:ObjectTreeView");
+ KFormDesigner::FormManager::self()->setObjectTreeView(d->objectTreeView); //important: assign to manager
+ d->dataSourcePage = new KexiDataSourcePage(0, "dataSourcePage");
+ connect(d->dataSourcePage, SIGNAL(jumpToObjectRequested(const QCString&, const QCString&)),
+ mainWin, SLOT(highlightObject(const QCString&, const QCString&)));
+ connect(d->dataSourcePage, SIGNAL(formDataSourceChanged(const QCString&, const QCString&)),
+ KFormDesigner::FormManager::self(), SLOT(setFormDataSource(const QCString&, const QCString&)));
+ connect(d->dataSourcePage, SIGNAL(dataSourceFieldOrExpressionChanged(const QString&, const QString&, KexiDB::Field::Type)),
+ KFormDesigner::FormManager::self(), SLOT(setDataSourceFieldOrExpression(const QString&, const QString&, KexiDB::Field::Type)));
+ connect(d->dataSourcePage, SIGNAL(insertAutoFields(const QString&, const QString&, const QStringList&)),
+ KFormDesigner::FormManager::self(), SLOT(insertAutoFields(const QString&, const QString&, const QStringList&)));
+ }
+
+ KexiProject *prj = mainWin->project();
+ d->dataSourcePage->setProject(prj);
+
+ tab->addTab( d->dataSourcePage, SmallIconSet("database"), "");
+ tab->setTabToolTip( d->dataSourcePage, i18n("Data Source"));
+
+ tab->addTab( d->objectTreeView, SmallIconSet("widgets"), "");
+ tab->setTabToolTip( d->objectTreeView, i18n("Widgets"));
+}
+
+void KexiFormPart::slotWidgetCreatedByFormsLibrary(QWidget* widget)
+{
+ QStrList signalNames(widget->metaObject()->signalNames());
+ if (!signalNames.isEmpty()) {
+ const char *handleDragMoveEventSignal = "handleDragMoveEvent(QDragMoveEvent*)";
+ const char *handleDropEventSignal = "handleDropEvent(QDropEvent*)";
+
+ for (QStrListIterator it(signalNames); it.current(); ++it) {
+ if (0==qstrcmp(it.current(), handleDragMoveEventSignal)) {
+ kdDebug() << it.current() << endl;
+ KexiFormView *formView = KexiUtils::findParent<KexiFormView>(widget, "KexiFormView");
+ if (formView) {
+ connect(widget, SIGNAL(handleDragMoveEvent(QDragMoveEvent*)),
+ formView, SLOT(slotHandleDragMoveEvent(QDragMoveEvent*)));
+ }
+ }
+ else if (0==qstrcmp(it.current(), handleDropEventSignal)) {
+ kdDebug() << it.current() << endl;
+ KexiFormView *formView = KexiUtils::findParent<KexiFormView>(widget, "KexiFormView");
+ if (formView) {
+ connect(widget, SIGNAL(handleDropEvent(QDropEvent*)),
+ formView, SLOT(slotHandleDropEvent(QDropEvent*)));
+ }
+ }
+ }
+ }
+}
+
+//----------------
+
+KexiFormPart::TempData::TempData(QObject* parent)
+ : KexiDialogTempData(parent)
+{
+}
+
+KexiFormPart::TempData::~TempData()
+{
+}
+
+#include "kexiformpart.moc"
diff --git a/kexi/plugins/forms/kexiformpart.h b/kexi/plugins/forms/kexiformpart.h
new file mode 100644
index 00000000..1ddbab53
--- /dev/null
+++ b/kexi/plugins/forms/kexiformpart.h
@@ -0,0 +1,108 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIFORMPART_H
+#define KEXIFORMPART_H
+
+#include <qdom.h>
+#include <qcstring.h>
+
+#include <kexi.h>
+#include <kexipart.h>
+#include <kexidialogbase.h>
+#include <kexiblobbuffer.h>
+
+namespace KFormDesigner
+{
+ class WidgetLibrary;
+ class FormManager;
+ class Form;
+}
+
+namespace KexiDB
+{
+ class FieldList;
+}
+
+class KexiDataSourcePage;
+
+//! Kexi Form Plugin
+/*! It just creates a \ref KexiFormView. See there for most of code. */
+class KEXIFORMUTILS_EXPORT KexiFormPart : public KexiPart::Part
+{
+ Q_OBJECT
+
+ public:
+ KexiFormPart(QObject *parent, const char *name, const QStringList &);
+ virtual ~KexiFormPart();
+
+ //! \return a pointer to Forms Widget Library.
+ static KFormDesigner::WidgetLibrary* library();
+
+ KexiDataSourcePage* dataSourcePage() const;
+
+ void generateForm(KexiDB::FieldList *list, QDomDocument &domDoc);
+
+ class TempData : public KexiDialogTempData
+ {
+ public:
+ TempData(QObject* parent);
+ ~TempData();
+ QGuardedPtr<KFormDesigner::Form> form;
+ QGuardedPtr<KFormDesigner::Form> previewForm;
+ QString tempForm;
+ QPoint scrollViewContentsPos; //!< to preserve contents pos after switching to other view
+ int resizeMode; //!< form's window's resize mode -one of KexiFormView::ResizeMode items
+ //! Used in KexiFormView::setUnsavedLocalBLOBs()
+ QMap<QWidget*, KexiBLOBBuffer::Id_t> unsavedLocalBLOBs;
+ //! Used when loading a form from (temporary) XML in Data View
+ //! to get unsaved blobs collected at design mode.
+ QMap<QCString, KexiBLOBBuffer::Id_t> unsavedLocalBLOBsByName;
+ };
+
+ virtual QString i18nMessage(const QCString& englishMessage,
+ KexiDialogBase* dlg) const;
+
+ protected:
+ virtual KexiDialogTempData* createTempData(KexiDialogBase* dialog);
+
+ virtual KexiViewBase* createView(QWidget *parent, KexiDialogBase* dialog,
+ KexiPart::Item &item, int viewMode = Kexi::DataViewMode, QMap<QString,QString>* staticObjectArgs = 0);
+
+ virtual void initPartActions();
+ virtual void initInstanceActions();
+ virtual void setupCustomPropertyPanelTabs(KTabWidget *tab, KexiMainWindow* mainWin);
+
+ static KFormDesigner::WidgetLibrary* static_formsLibrary;
+
+ protected slots:
+ void slotAutoTabStopsSet(KFormDesigner::Form *form, bool set);
+ void slotAssignAction();
+ void slotPropertyChanged(QWidget *widget, const QCString &name, const QVariant &value);
+ void slotWidgetCreatedByFormsLibrary(QWidget* widget);
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#endif
+
diff --git a/kexi/plugins/forms/kexiformpartinstui.rc b/kexi/plugins/forms/kexiformpartinstui.rc
new file mode 100644
index 00000000..75f233f2
--- /dev/null
+++ b/kexi/plugins/forms/kexiformpartinstui.rc
@@ -0,0 +1,77 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexiformpartinst" version="13">
+
+<MenuBar>
+ <Menu name="edit">
+ <Action name="fompart_clear_contents"/>
+ <Separator />
+ <Action name="formpart_taborder"/>
+ <Action name="formpart_adjust_size"/>
+ <Action name="formpart_pixmap_collection"/>
+ <Action name="formpart_connections"/>
+ <!-- Action name="change_style"/ -->
+ </Menu>
+ <Menu name="format" noMerge="0">
+ <text>&amp;Format</text>
+ <Action name="snap_to_grid"/>
+ <Separator/>
+ <Action name="formpart_layout_menu"/>
+ <Action name="formpart_break_layout"/>
+ <Separator/>
+ <Action name="formpart_align_menu"/>
+ <Action name="formpart_adjust_size_menu"/>
+ <Separator/>
+ <Action name="formpart_format_raise"/>
+ <Action name="formpart_format_lower"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="widgets" fullWidth="false">
+ <text>Widgets</text>
+ <Action name="pointer"/>
+ <!-- Action name="drag_connection"/ -->
+ <Separator/>
+ <Action name="library_widget_KexiDBAutoField"/>
+ <Action name="library_widget_KexiDBLabel"/>
+ <Action name="library_widget_KexiPictureLabel"/>
+ <Action name="library_widget_KexiDBImageBox"/>
+ <Action name="library_widget_KexiDBLineEdit"/>
+ <Action name="library_widget_KexiDBTextEdit"/>
+ <Action name="library_widget_KPushButton"/>
+ <Action name="library_widget_KexiDBComboBox"/>
+ <!-- Action name="library_widget_QRadioButton"/ -->
+ <Action name="library_widget_KexiDBCheckBox"/>
+ <Action name="library_widget_Spacer"/>
+ <Action name="library_widget_Line"/>
+ <Separator/>
+ <Action name="library_widget_KexiFrame"/>
+ <Action name="library_widget_QGroupBox"/>
+ <Action name="library_widget_KFDTabWidget"/>
+ <!-- TODO Action name="library_widget_KexiDBSubForm"/ -->
+ <Separator/>
+ <Action name="library_widget_Spring"/>
+ <Separator/>
+ <Action name="other_widgets_menu"/>
+ <ActionList name="library_widgets" />
+ <Merge/>
+</ToolBar>
+<ToolBar name="format" fullWidth="false" noMerge="1">
+<text>Format</text>
+ <!-- Action name="formpart_layout_menu"/ -->
+ <Action name="formpart_align_menu"/>
+ <Action name="formpart_adjust_size_menu"/>
+ <Action name="show_form_ui" />
+</ToolBar>
+<!-- ToolBar name="tools" fullWidth="false">
+ <Action name="change_style"/>
+</ToolBar -->
+
+<Menu name="other_widgets_menu">
+ <Action name="library_widget_KexiDBIntSpinBox"/>
+ <Action name="library_widget_KexiDBDoubleSpinBox"/>
+ <Action name="library_widget_KexiDBDateEdit"/>
+ <Action name="library_widget_KexiDBTimeEdit"/>
+ <Action name="library_widget_KexiDBDateTimeEdit"/>
+</Menu>
+
+</kpartgui>
diff --git a/kexi/plugins/forms/kexiformpartui.rc b/kexi/plugins/forms/kexiformpartui.rc
new file mode 100644
index 00000000..20fd49d8
--- /dev/null
+++ b/kexi/plugins/forms/kexiformpartui.rc
@@ -0,0 +1,10 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexiformpart" version="6">
+
+<!-- ToolBar name="design" fullWidth="false" noMerge="0">
+ <text>Design</text>
+ <Action name="show_form_ui"/>
+</ToolBar -->
+
+</kpartgui>
+
diff --git a/kexi/plugins/forms/kexiforms.cpp b/kexi/plugins/forms/kexiforms.cpp
new file mode 100644
index 00000000..07c4726f
--- /dev/null
+++ b/kexi/plugins/forms/kexiforms.cpp
@@ -0,0 +1,25 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <kgenericfactory.h>
+
+#include "kexiformpart.h"
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_form, KGenericFactory<KexiFormPart>("kexihandler_form") )
+
diff --git a/kexi/plugins/forms/kexiformscrollview.cpp b/kexi/plugins/forms/kexiformscrollview.cpp
new file mode 100644
index 00000000..351a1e3e
--- /dev/null
+++ b/kexi/plugins/forms/kexiformscrollview.cpp
@@ -0,0 +1,587 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiformscrollview.h"
+//#include "kexiformview.h"
+
+#include <formeditor/form.h>
+#include <formeditor/formmanager.h>
+#include <formeditor/objecttree.h>
+#include <formeditor/commands.h>
+#include <widget/utils/kexirecordmarker.h>
+
+#include <kpopupmenu.h>
+#include <kdebug.h>
+
+KexiFormScrollView::KexiFormScrollView(QWidget *parent, bool preview)
+ : KexiScrollView(parent, preview)
+ , KexiRecordNavigatorHandler()
+ , KexiSharedActionClient()
+ , KexiDataAwareObjectInterface()
+ , KexiFormDataProvider()
+ , KexiFormEventHandler()
+{
+ m_currentLocalSortColumn = -1; /* no column */
+ m_localSortingOrder = -1; /* no sorting */
+ m_previousItem = 0;
+ m_navPanel = m_scrollViewNavPanel; //copy this pointer from KexiScrollView
+ if (preview) {
+ setRecordNavigatorVisible(true);
+//tmp
+// recordNavigator()->setEditingIndicatorEnabled(true);
+// recordNavigator()->showEditingIndicator(true);
+ }
+
+ connect(this, SIGNAL(resizingStarted()), this, SLOT(slotResizingStarted()));
+
+ m_popupMenu = new KPopupMenu(this, "contextMenu");
+
+// setFocusPolicy(NoFocus);
+}
+
+KexiFormScrollView::~KexiFormScrollView()
+{
+ if (m_owner)
+ delete m_data;
+ m_data = 0;
+}
+
+void
+KexiFormScrollView::show()
+{
+ KexiScrollView::show();
+
+#if 0 //moved to KexiFormView, OK?
+ //now get resize mode settings for entire form
+ if (m_preview) {
+ KexiFormView* fv = dynamic_cast<KexiFormView*>(parent());
+ int resizeMode = fv ? fv->resizeMode() : KexiFormView::ResizeAuto;
+ if (resizeMode == KexiFormView::ResizeAuto)
+ setResizePolicy(AutoOneFit);
+ }
+#endif
+}
+
+void
+KexiFormScrollView::slotResizingStarted()
+{
+ if(m_form && KFormDesigner::FormManager::self())
+ setSnapToGrid(KFormDesigner::FormManager::self()->snapWidgetsToGrid(), m_form->gridSize());
+ else
+ setSnapToGrid(false);
+}
+
+int KexiFormScrollView::rowsPerPage() const
+{
+ //! @todo
+ return 10;
+}
+
+void KexiFormScrollView::selectCellInternal()
+{
+ //m_currentItem is already set by KexiDataAwareObjectInterface::setCursorPosition()
+ if (m_currentItem) {
+ if (m_currentItem!=m_previousItem) {
+ fillDataItems(*m_currentItem, cursorAtNewRow());
+ m_previousItem = m_currentItem;
+ }
+ }
+ else {
+ m_previousItem = 0;
+ }
+}
+
+void KexiFormScrollView::ensureCellVisible(int row, int col/*=-1*/)
+{
+ Q_UNUSED( row );
+ Q_UNUSED( col );
+ //! @todo
+// if (m_currentItem)
+ //fillDataItems(*m_currentItem);
+
+// if (m_form->tabStops()->first() && m_form->tabStops()->first()->widget())
+// m_form->tabStops()->first()->widget()->setFocus();
+}
+
+void KexiFormScrollView::moveToRecordRequested(uint r)
+{
+ //! @todo
+ selectRow(r);
+}
+
+void KexiFormScrollView::moveToLastRecordRequested()
+{
+ //! @todo
+ selectLastRow();
+}
+
+void KexiFormScrollView::moveToPreviousRecordRequested()
+{
+ //! @todo
+ selectPrevRow();
+}
+
+void KexiFormScrollView::moveToNextRecordRequested()
+{
+ //! @todo
+ selectNextRow();
+}
+
+void KexiFormScrollView::moveToFirstRecordRequested()
+{
+ //! @todo
+ selectFirstRow();
+}
+
+void KexiFormScrollView::clearColumnsInternal(bool repaint)
+{
+ Q_UNUSED( repaint );
+ //! @todo
+}
+
+void KexiFormScrollView::addHeaderColumn(const QString& caption, const QString& description,
+ const QIconSet& icon, int width)
+{
+ Q_UNUSED( caption );
+ Q_UNUSED( description );
+ Q_UNUSED( icon );
+ Q_UNUSED( width );
+
+ //! @todo
+}
+
+int KexiFormScrollView::currentLocalSortingOrder() const
+{
+ //! @todo
+ return m_localSortingOrder;
+}
+
+int KexiFormScrollView::currentLocalSortColumn() const
+{
+ return m_currentLocalSortColumn;
+}
+
+void KexiFormScrollView::setLocalSortingOrder(int col, int order)
+{
+ //! @todo
+ m_currentLocalSortColumn = col;
+ m_localSortingOrder = order;
+}
+
+void KexiFormScrollView::sortColumnInternal(int col, int order)
+{
+ Q_UNUSED( col );
+ Q_UNUSED( order );
+ //! @todo
+}
+
+void KexiFormScrollView::updateGUIAfterSorting()
+{
+ //! @todo
+}
+
+void KexiFormScrollView::createEditor(int row, int col, const QString& addText,
+ bool removeOld)
+{
+ Q_UNUSED( row );
+ Q_UNUSED( addText );
+ Q_UNUSED( removeOld );
+
+ if (isReadOnly()) {
+ kexipluginsdbg << "KexiFormScrollView::createEditor(): DATA IS READ ONLY!"<<endl;
+ return;
+ }
+ if (column( col )->isReadOnly()) {
+ kexipluginsdbg << "KexiFormScrollView::createEditor(): COL IS READ ONLY!"<<endl;
+ return;
+ }
+
+ //! @todo
+ const bool startRowEdit = !m_rowEditing; //remember if we're starting row edit
+
+ if (!m_rowEditing) {
+ //we're starting row editing session
+ m_data->clearRowEditBuffer();
+
+ m_rowEditing = true;
+ //indicate on the vheader that we are editing:
+ if (m_verticalHeader)
+ m_verticalHeader->setEditRow(m_curRow);
+ if (isInsertingEnabled() && m_currentItem==m_insertItem) {
+ //we should know that we are in state "new row editing"
+ m_newRowEditing = true;
+ //'insert' row editing: show another row after that:
+ m_data->append( m_insertItem );
+ //new empty insert item
+ m_insertItem = m_data->createItem(); //new KexiTableItem(dataColumns());
+// updateContents();
+ if (m_verticalHeader)
+ m_verticalHeader->addLabel();
+// m_verticalHeaderAlreadyAdded = true;
+ updateWidgetContentsSize();
+ //refr. current and next row
+// updateContents(columnPos(0), rowPos(row), viewport()->width(), d->rowHeight*2);
+//js: warning this breaks behaviour (cursor is skipping, etc.): qApp->processEvents(500);
+// ensureVisible(columnPos(m_curCol), rowPos(row+1)+d->rowHeight-1, columnWidth(m_curCol), d->rowHeight);
+
+// m_verticalHeader->setOffset(contentsY());
+ }
+ }
+
+ m_editor = editor(col); //m_dataItems.at(col);
+ if (!m_editor)
+ return;
+
+ if (startRowEdit) {
+ recordNavigator()->showEditingIndicator(true);
+// recordNavigator()->updateButtons(); //refresh 'next btn'
+
+ emit rowEditStarted(m_curRow);
+ }
+}
+
+KexiDataItemInterface *KexiFormScrollView::editor( int col, bool ignoreMissingEditor )
+{
+ Q_UNUSED( ignoreMissingEditor );
+
+ if (!m_data || col<0 || col>=columns())
+ return 0;
+
+ return dynamic_cast<KexiFormDataItemInterface*>(dbFormWidget()->orderedDataAwareWidgets()->at( col ));
+// KexiFormDataItemInterface *item = m_dataItems.at(col);
+ //return item;
+
+/*
+ KexiTableViewColumn *tvcol = m_data->column(col);
+// int t = tvcol->field->type();
+
+ //find the editor for this column
+ KexiDataItemInterface *editor = d->editors[ tvcol ];
+ if (editor)
+ return editor;
+
+ //not found: create
+// editor = KexiCellEditorFactory::createEditor(*m_data->column(col)->field, this);
+ editor = KexiCellEditorFactory::createEditor(*m_data->column(col), this);
+ if (!editor) {//create error!
+ if (!ignoreMissingEditor) {
+ //js TODO: show error???
+ cancelRowEdit();
+ }
+ return 0;
+ }
+ editor->hide();
+ connect(editor,SIGNAL(editRequested()),this,SLOT(slotEditRequested()));
+ connect(editor,SIGNAL(cancelRequested()),this,SLOT(cancelEditor()));
+ connect(editor,SIGNAL(acceptRequested()),this,SLOT(acceptEditor()));
+
+ editor->resize(columnWidth(col)-1, rowHeight()-1);
+ editor->installEventFilter(this);
+ if (editor->widget())
+ editor->widget()->installEventFilter(this);
+ //store
+ d->editors.insert( tvcol, editor );
+ return editor;*/
+}
+
+void KexiFormScrollView::editorShowFocus( int row, int col )
+{
+ Q_UNUSED( row );
+ Q_UNUSED( col );
+ //! @todo
+// if (m_currentItem)
+// m_provider->fillDataItems(*m_currentItem);
+}
+
+void KexiFormScrollView::updateCell(int row, int col)
+{
+ Q_UNUSED( row );
+ Q_UNUSED( col );
+ //! @todo
+}
+
+void KexiFormScrollView::updateCurrentCell()
+{
+}
+
+void KexiFormScrollView::updateRow(int row)
+{
+ Q_UNUSED(row)
+ //! @todo
+}
+
+void KexiFormScrollView::updateWidgetContents()
+{
+ //! @todo
+}
+
+void KexiFormScrollView::updateWidgetContentsSize()
+{
+ //! @todo
+}
+
+void KexiFormScrollView::updateWidgetScrollBars()
+{
+ //! @todo
+}
+
+void KexiFormScrollView::slotRowRepaintRequested(KexiTableItem& item)
+{
+ Q_UNUSED( item );
+ //! @todo
+}
+
+/*void KexiFormScrollView::slotAboutToDeleteRow(KexiTableItem& item,
+ KexiDB::ResultInfo* result, bool repaint)
+{
+ //! @todo
+}*/
+
+/*void KexiFormScrollView::slotRowDeleted()
+{
+ //! @todo
+}*/
+
+void KexiFormScrollView::slotRowInserted(KexiTableItem *item, bool repaint)
+{
+ Q_UNUSED( item );
+ Q_UNUSED( repaint );
+ //! @todo
+}
+
+void KexiFormScrollView::slotRowInserted(KexiTableItem *item, uint row, bool repaint)
+{
+ Q_UNUSED( item );
+ Q_UNUSED( row );
+ Q_UNUSED( repaint );
+ //! @todo
+}
+
+void KexiFormScrollView::slotRowsDeleted( const QValueList<int> & )
+{
+ //! @todo
+}
+
+KexiDBForm* KexiFormScrollView::dbFormWidget() const
+{
+ return dynamic_cast<KexiDBForm*>(m_widget);
+}
+
+int KexiFormScrollView::columns() const
+{
+ return dbFormWidget()->orderedDataAwareWidgets()->count(); //m_dataItems.count();
+}
+
+/*uint KexiFormScrollView::fieldNumberForColumn(int col)
+{
+ KexiFormDataItemInterface *item = dynamic_cast<KexiFormDataItemInterface*>(dbFormWidget()->orderedDataAwareWidgets()->at( col ));
+ if (!item)
+ return -1;
+ KexiFormDataItemInterfaceToIntMap::ConstIterator it(m_fieldNumbersForDataItems.find( item ));
+ return it!=m_fieldNumbersForDataItems.constEnd() ? it.data() : -1;
+}*/
+
+bool KexiFormScrollView::columnEditable(int col)
+{
+ kexipluginsdbg << "KexiFormScrollView::columnEditable(" << col << ")" << endl;
+ foreach_list (QPtrListIterator<KexiFormDataItemInterface>, it, m_dataItems) {
+ kexipluginsdbg << (dynamic_cast<QWidget*>(it.current()) ? dynamic_cast<QWidget*>(it.current())->name() : "" )
+ << " " << it.current()->dataSource() << endl;
+ }
+ kexipluginsdbg << "-- focus widgets --" << endl;
+ foreach_list (QPtrListIterator<QWidget>, it, *dbFormWidget()->orderedFocusWidgets()) {
+ kexipluginsdbg << it.current()->name() << endl;
+ }
+ kexipluginsdbg << "-- data-aware widgets --" << endl;
+ foreach_list (QPtrListIterator<QWidget>, it, *dbFormWidget()->orderedDataAwareWidgets()) {
+ kexipluginsdbg << it.current()->name() << endl;
+ }
+
+ //int index = dbFormWidget()->indexForDataItem( item );
+// KexiFormDataItemInterface *item1 = dynamic_cast<KexiFormDataItemInterface*>(dbFormWidget()->orderedFocusWidgets()->at( col ));
+ KexiFormDataItemInterface *item = dynamic_cast<KexiFormDataItemInterface*>(dbFormWidget()->orderedDataAwareWidgets()->at( col ));
+
+ if (!item || item->isReadOnly())
+ return false;
+
+// KexiFormDataItemInterfaceToIntMap::ConstIterator it(m_fieldNumbersForDataItems.find( item ));
+// return KexiDataAwareObjectInterface::columnEditable( it!=m_fieldNumbersForDataItems.constEnd() ? it.data() : -1 );
+ return KexiDataAwareObjectInterface::columnEditable( col );
+}
+
+void KexiFormScrollView::valueChanged(KexiDataItemInterface* item)
+{
+ if (!item)
+ return;
+ //only signal start editing when no row editing was started already
+ kexipluginsdbg << "** KexiFormScrollView::valueChanged(): editedItem="
+ << (dbFormWidget()->editedItem ? dbFormWidget()->editedItem->value().toString() : QString::null)
+ << ", "
+ << (item ? item->value().toString() : QString::null)
+ << endl;
+ if (dbFormWidget()->editedItem!=item) {
+ kexipluginsdbg << "**>>> dbFormWidget()->editedItem = dynamic_cast<KexiFormDataItemInterface*>(item)" << endl;
+ dbFormWidget()->editedItem = dynamic_cast<KexiFormDataItemInterface*>(item);
+ startEditCurrentCell();
+ }
+ fillDuplicatedDataItems(dynamic_cast<KexiFormDataItemInterface*>(item), item->value());
+
+ //value changed: clear 'default value' mode (e.g. a blue italic text)
+ dynamic_cast<KexiFormDataItemInterface*>(item)->setDisplayDefaultValue(dynamic_cast<QWidget*>(item), false);
+}
+
+bool KexiFormScrollView::cursorAtNewRow() const
+{
+ return isInsertingEnabled() && ( m_currentItem==m_insertItem || m_newRowEditing );
+}
+
+void KexiFormScrollView::initDataContents()
+{
+ KexiDataAwareObjectInterface::initDataContents();
+
+ if (m_preview) {
+//! @todo here we can react if user wanted to show the navigator
+ setRecordNavigatorVisible(m_data);
+ recordNavigator()->setEnabled(m_data);
+ if (m_data) {
+ recordNavigator()->setEditingIndicatorEnabled( !isReadOnly() );
+ recordNavigator()->showEditingIndicator(false);
+ }
+
+ dbFormWidget()->updateReadOnlyFlags();
+ }
+}
+
+KexiTableViewColumn* KexiFormScrollView::column(int col)
+{
+ const int id = fieldNumberForColumn(col);
+ return (id >= 0) ? m_data->column( id ) : 0;
+}
+
+bool KexiFormScrollView::shouldDisplayDefaultValueForItem(KexiFormDataItemInterface* itemIface) const
+{
+ return cursorAtNewRow()
+ && !itemIface->columnInfo()->field->defaultValue().isNull()
+//?? && (m_editor ? m_editor->value()==itemIface->columnInfo()->field->defaultValue() : true)
+ && !itemIface->columnInfo()->field->isAutoIncrement(); // default value defined
+}
+
+bool KexiFormScrollView::cancelEditor()
+{
+ if (!dynamic_cast<KexiFormDataItemInterface*>(m_editor))
+ return false;
+
+ if (m_errorMessagePopup)
+ m_errorMessagePopup->close();
+
+ KexiFormDataItemInterface *itemIface = dynamic_cast<KexiFormDataItemInterface*>(m_editor);
+ itemIface->undoChanges();
+
+ const bool displayDefaultValue = shouldDisplayDefaultValueForItem(itemIface);
+ // now disable/enable "display default value" if needed (do it after setValue(), before setValue() turns it off)
+ if (itemIface->hasDisplayedDefaultValue() != displayDefaultValue)
+ itemIface->setDisplayDefaultValue( dynamic_cast<QWidget*>(itemIface), displayDefaultValue );
+
+ fillDuplicatedDataItems(itemIface, m_editor->value());
+
+ // this will clear editor pointer and close message popup (if present)
+ return KexiDataAwareObjectInterface::cancelEditor();
+}
+
+void KexiFormScrollView::updateAfterCancelRowEdit()
+{
+ for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
+ if (dynamic_cast<QWidget*>(it.current())) {
+ kexipluginsdbg << "KexiFormScrollView::updateAfterCancelRowEdit(): "
+ << dynamic_cast<QWidget*>(it.current())->className() << " "
+ << dynamic_cast<QWidget*>(it.current())->name() << endl;
+ }
+ KexiFormDataItemInterface *itemIface = it.current();
+ const bool displayDefaultValue = shouldDisplayDefaultValueForItem(itemIface);
+ itemIface->undoChanges();
+ if (itemIface->hasDisplayedDefaultValue() != displayDefaultValue)
+ itemIface->setDisplayDefaultValue( dynamic_cast<QWidget*>(itemIface), displayDefaultValue );
+ }
+ recordNavigator()->showEditingIndicator(false);
+ dbFormWidget()->editedItem = 0;
+}
+
+void KexiFormScrollView::updateAfterAcceptRowEdit()
+{
+ if (!m_currentItem)
+ return;
+ recordNavigator()->showEditingIndicator(false);
+ dbFormWidget()->editedItem = 0;
+ //update visible data because there could be auto-filled (eg. autonumber) fields
+ fillDataItems(*m_currentItem, cursorAtNewRow());
+ m_previousItem = m_currentItem;
+}
+
+void KexiFormScrollView::beforeSwitchView()
+{
+ m_editor = 0;
+}
+
+void KexiFormScrollView::refreshContentsSize()
+{
+ KexiScrollView::refreshContentsSize();
+ //only clear cmd history when KexiScrollView::refreshContentsSizeLater() has been called
+ if (!m_preview && sender()==&m_delayedResize) {
+ if (m_form)
+ m_form->clearCommandHistory();
+ }
+}
+
+void KexiFormScrollView::handleDataWidgetAction(const QString& actionName)
+{
+ QWidget *w = focusWidget();
+ KexiFormDataItemInterface *item = 0;
+ while (w) {
+ item = dynamic_cast<KexiFormDataItemInterface*>(w);
+ if (item)
+ break;
+ w = w->parentWidget();
+ }
+ if (item)
+ item->handleAction(actionName);
+}
+
+void KexiFormScrollView::copySelection()
+{
+ handleDataWidgetAction("edit_copy");
+}
+
+void KexiFormScrollView::cutSelection()
+{
+ handleDataWidgetAction("edit_cut");
+}
+
+void KexiFormScrollView::paste()
+{
+ handleDataWidgetAction("edit_paste");
+}
+
+int KexiFormScrollView::lastVisibleRow() const
+{
+//! @todo unimplemented for now, this will be used for continuous forms
+ return -1;
+}
+
+#include "kexiformscrollview.moc"
diff --git a/kexi/plugins/forms/kexiformscrollview.h b/kexi/plugins/forms/kexiformscrollview.h
new file mode 100644
index 00000000..12315761
--- /dev/null
+++ b/kexi/plugins/forms/kexiformscrollview.h
@@ -0,0 +1,297 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIFORMSCROLLVIEW_H
+#define KEXIFORMSCROLLVIEW_H
+
+#include "kexidataprovider.h"
+#include "kexiformeventhandler.h"
+#include "widgets/kexidbform.h"
+#include <widget/kexiscrollview.h>
+#include <widget/utils/kexirecordnavigator.h>
+#include <widget/utils/kexisharedactionclient.h>
+#include <widget/tableview/kexidataawareobjectiface.h>
+
+//! @short KexiFormScrollView class provides a widget for displaying data in a form view
+/*! This class also implements:
+ - record navigation handling (KexiRecordNavigatorHandler)
+ - shared actions handling (KexiSharedActionClient)
+ - data-aware behaviour (KexiDataAwareObjectInterface)
+ - data provider bound to data-aware widgets (KexiFormDataProvider)
+
+ @see KexiTableView
+*/
+class KEXIFORMUTILS_EXPORT KexiFormScrollView :
+ public KexiScrollView,
+ public KexiRecordNavigatorHandler,
+ public KexiSharedActionClient,
+ public KexiDataAwareObjectInterface,
+ public KexiFormDataProvider,
+ public KexiFormEventHandler
+{
+ Q_OBJECT
+ KEXI_DATAAWAREOBJECTINTERFACE
+
+ public:
+ KexiFormScrollView(QWidget *parent, bool preview);
+ virtual ~KexiFormScrollView();
+
+ void setForm(KFormDesigner::Form *form) { m_form = form; }
+
+ /*! Reimplemented from KexiDataAwareObjectInterface
+ for checking 'readOnly' flag from a widget
+ ('readOnly' flag from data member is still checked though). */
+ virtual bool columnEditable(int col);
+
+ /*! \return number of visible columns in this view.
+ There can be a number of duplicated columns defined,
+ so columns() can return greater or smaller number than dataColumns(). */
+ virtual int columns() const;
+
+ /*! \return column information for column number \a col.
+ Reimplemented for KexiDataAwareObjectInterface:
+ column data corresponding to widget number is used here
+ (see fieldNumberForColumn()). */
+ virtual KexiTableViewColumn* column(int col);
+
+ /*! \return field number within data model connected to a data-aware
+ widget at column \a col. */
+ virtual int fieldNumberForColumn(int col) {
+ KexiFormDataItemInterface *item = dynamic_cast<KexiFormDataItemInterface*>(
+ dbFormWidget()->orderedDataAwareWidgets()->at( col ));
+ if (!item)
+ return -1;
+ KexiFormDataItemInterfaceToIntMap::ConstIterator it(m_fieldNumbersForDataItems.find( item ));
+ return it!=m_fieldNumbersForDataItems.constEnd() ? (int)it.data() : -1;
+ }
+
+ /*! @internal Used by KexiFormView in view switching. */
+ void beforeSwitchView();
+
+ /*! \return last row visible on the screen (counting from 0).
+ The returned value is guaranteed to be smaller or equal to currentRow() or -1
+ if there are no rows.
+ Implemented for KexiDataAwareObjectInterface. */
+//! @todo unimplemented for now, this will be used for continuous forms
+ virtual int lastVisibleRow() const;
+
+ /*! \return vertical scrollbar. Implemented for KexiDataAwareObjectInterface. */
+ virtual QScrollBar* verticalScrollBar() const { return KexiScrollView::verticalScrollBar(); }
+
+ public slots:
+ /*! Reimplemented to update resize policy. */
+ virtual void show();
+
+ //virtual void setFocus();
+
+ //! Implementation for KexiDataAwareObjectInterface
+ //! \return arbitraty value of 10.
+ virtual int rowsPerPage() const;
+
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual void ensureCellVisible(int row, int col/*=-1*/);
+
+ virtual void moveToRecordRequested(uint r);
+ virtual void moveToLastRecordRequested();
+ virtual void moveToPreviousRecordRequested();
+ virtual void moveToNextRecordRequested();
+ virtual void moveToFirstRecordRequested();
+ virtual void addNewRecordRequested() { KexiDataAwareObjectInterface::addNewRecordRequested(); }
+
+ /*! Cancels changes made to the currently active editor.
+ Reverts the editor's value to old one.
+ \return true on success or false on failure (e.g. when editor does not exist) */
+ virtual bool cancelEditor();
+
+ public slots:
+ /*! Reimplemented to also clear command history right after final resize. */
+ virtual void refreshContentsSize();
+
+ /*! Handles verticalScrollBar()'s valueChanged(int) signal.
+ Called when vscrollbar's value has been changed. */
+//! @todo unused for now, will be used for continuous forms
+ virtual void vScrollBarValueChanged(int v) { KexiDataAwareObjectInterface::vScrollBarValueChanged(v); }
+
+ /*! Handles sliderReleased() signal of the verticalScrollBar(). Used to hide the "row number" tooltip. */
+//! @todo unused for now, will be used for continuous forms
+ virtual void vScrollBarSliderReleased() { KexiDataAwareObjectInterface::vScrollBarSliderReleased(); }
+
+ /*! Handles timeout() signal of the m_scrollBarTipTimer. If the tooltip is visible,
+ m_scrollBarTipTimerCnt is set to 0 and m_scrollBarTipTimerCnt is restarted;
+ else the m_scrollBarTipTimerCnt is just set to 0.*/
+//! @todo unused for now, will be used for continuous forms
+ virtual void scrollBarTipTimeout() { KexiDataAwareObjectInterface::scrollBarTipTimeout(); }
+
+ signals:
+ virtual void itemChanged(KexiTableItem *, int row, int col);
+ virtual void itemChanged(KexiTableItem *, int row, int col, QVariant oldValue);
+ virtual void itemDeleteRequest(KexiTableItem *, int row, int col);
+ virtual void currentItemDeleteRequest();
+ virtual void newItemAppendedForAfterDeletingInSpreadSheetMode(); //!< does nothing
+ virtual void dataRefreshed();
+ virtual void dataSet( KexiTableViewData *data );
+ virtual void itemSelected(KexiTableItem *);
+ virtual void cellSelected(int col, int row);
+ virtual void sortedColumnChanged(int col);
+ virtual void rowEditStarted(int row);
+ virtual void rowEditTerminated(int row);
+ virtual void reloadActions();
+
+ protected slots:
+ void slotResizingStarted();
+
+ //! Handles KexiTableViewData::rowRepaintRequested() signal
+ virtual void slotRowRepaintRequested(KexiTableItem& item);
+
+ //! Handles KexiTableViewData::aboutToDeleteRow() signal. Prepares info for slotRowDeleted().
+ virtual void slotAboutToDeleteRow(KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint)
+ { KexiDataAwareObjectInterface::slotAboutToDeleteRow(item, result, repaint); }
+
+ //! Handles KexiTableViewData::rowDeleted() signal to repaint when needed.
+ virtual void slotRowDeleted() { KexiDataAwareObjectInterface::slotRowDeleted(); }
+
+ //! Handles KexiTableViewData::rowInserted() signal to repaint when needed.
+ virtual void slotRowInserted(KexiTableItem *item, bool repaint);
+
+ //! Like above, not db-aware version
+ virtual void slotRowInserted(KexiTableItem *item, uint row, bool repaint);
+
+ virtual void slotRowsDeleted( const QValueList<int> & );
+
+ virtual void slotDataDestroying() { KexiDataAwareObjectInterface::slotDataDestroying(); }
+
+ /*! Reloads data for this widget.
+ Handles KexiTableViewData::reloadRequested() signal. */
+ virtual void reloadData() { KexiDataAwareObjectInterface::reloadData(); }
+
+ //! Copy current selection to a clipboard (e.g. cell)
+ virtual void copySelection();
+
+ //! Cut current selection to a clipboard (e.g. cell)
+ virtual void cutSelection();
+
+ //! Paste current clipboard contents (e.g. to a cell)
+ virtual void paste();
+
+ protected:
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual void clearColumnsInternal(bool repaint);
+
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual void addHeaderColumn(const QString& caption, const QString& description,
+ const QIconSet& icon, int width);
+
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual int currentLocalSortingOrder() const;
+
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual int currentLocalSortColumn() const;
+
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual void setLocalSortingOrder(int col, int order);
+
+ //! Implementation for KexiDataAwareObjectInterface
+ void sortColumnInternal(int col, int order = 0);
+
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual void updateGUIAfterSorting();
+
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual void createEditor(int row, int col, const QString& addText = QString::null,
+ bool removeOld = false);
+
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual KexiDataItemInterface *editor( int col, bool ignoreMissingEditor = false );
+
+ //! Implementation for KexiDataAwareObjectInterface
+ virtual void editorShowFocus( int row, int col );
+
+ /*! Implementation for KexiDataAwareObjectInterface
+ Redraws specified cell. */
+ virtual void updateCell(int row, int col);
+
+ /*! Redraws the current cell. Implemented after KexiDataAwareObjectInterface. */
+ virtual void updateCurrentCell();
+
+ /*! Implementation for KexiDataAwareObjectInterface
+ Redraws all cells of specified row. */
+ virtual void updateRow(int row);
+
+ /*! Implementation for KexiDataAwareObjectInterface
+ Updates contents of the widget. Just call update() here on your widget. */
+ virtual void updateWidgetContents();
+
+ /*! Implementation for KexiDataAwareObjectInterface
+ Implementation for KexiDataAwareObjectInterface
+ Updates widget's contents size e.g. using QScrollView::resizeContents(). */
+ virtual void updateWidgetContentsSize();
+
+ /*! Implementation for KexiDataAwareObjectInterface
+ Updates scrollbars of the widget.
+ QScrollView::updateScrollbars() will be usually called here. */
+ virtual void updateWidgetScrollBars();
+
+ KexiDBForm* dbFormWidget() const;
+
+ //! Reimplemented from KexiFormDataProvider. Reaction for change of \a item.
+ virtual void valueChanged(KexiDataItemInterface* item);
+
+ /*! Reimplemented from KexiFormDataProvider.
+ \return information whether we're currently at new row or now.
+ This can be used e.g. by data-aware widgets to determine if "(autonumber)"
+ label should be displayed. */
+ virtual bool cursorAtNewRow() const;
+
+ //! Implementation for KexiDataAwareObjectInterface
+ //! Called by KexiDataAwareObjectInterface::setCursorPosition()
+ //! if cursor's position is really changed.
+ inline virtual void selectCellInternal();
+
+ /*! Reimplementation: used to refresh "editing indicator" visibility. */
+ virtual void initDataContents();
+
+ /*! @internal
+ Updates row appearance after canceling row edit.
+ Reimplemented from KexiDataAwareObjectInterface: just undoes changes for every data item.
+ Used by cancelRowEdit(). */
+ virtual void updateAfterCancelRowEdit();
+
+ /*! @internal
+ Updates row appearance after accepting row edit.
+ Reimplemented from KexiDataAwareObjectInterface: just clears 'edit' indicator.
+ Used by cancelRowEdit(). */
+ virtual void updateAfterAcceptRowEdit();
+
+ /*! @internal
+ Used to invoke copy/paste/cut etc. actions at the focused widget's level. */
+ void handleDataWidgetAction(const QString& actionName);
+
+ /*! @internal */
+ bool shouldDisplayDefaultValueForItem(KexiFormDataItemInterface* itemIface) const;
+
+ //virtual bool focusNextPrevChild( bool next );
+
+ KFormDesigner::Form *m_form;
+ int m_currentLocalSortColumn, m_localSortingOrder;
+ //! Used in selectCellInternal() to avoid fetching the same record twice
+ KexiTableItem *m_previousItem;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kexiformview.cpp b/kexi/plugins/forms/kexiformview.cpp
new file mode 100644
index 00000000..7e52e5b6
--- /dev/null
+++ b/kexi/plugins/forms/kexiformview.cpp
@@ -0,0 +1,1278 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiformview.h"
+
+#include <qobjectlist.h>
+#include <qfileinfo.h>
+
+#include <formeditor/form.h>
+#include <formeditor/formIO.h>
+#include <formeditor/formmanager.h>
+#include <formeditor/objecttree.h>
+#include <formeditor/container.h>
+#include <formeditor/widgetpropertyset.h>
+#include <formeditor/commands.h>
+#include <formeditor/widgetwithsubpropertiesinterface.h>
+#include <formeditor/objecttree.h>
+
+#include <kexi.h>
+#include <kexidialogbase.h>
+#include <kexidragobjects.h>
+#include <kexidb/field.h>
+#include <kexidb/fieldlist.h>
+#include <kexidb/connection.h>
+#include <kexidb/cursor.h>
+#include <kexidb/utils.h>
+#include <kexidb/preparedstatement.h>
+#include <tableview/kexitableitem.h>
+#include <tableview/kexitableviewdata.h>
+#include <widget/kexipropertyeditorview.h>
+#include <widget/kexiqueryparameters.h>
+#include <kexiutils/utils.h>
+
+#include <koproperty/set.h>
+#include <koproperty/property.h>
+
+#include "widgets/kexidbform.h"
+#include "kexiformscrollview.h"
+#include "kexidatasourcepage.h"
+#include "widgets/kexidbautofield.h"
+
+#define NO_DSWIZARD
+
+//! @todo #define KEXI_SHOW_SPLITTER_WIDGET
+
+KexiFormView::KexiFormView(KexiMainWindow *mainWin, QWidget *parent,
+ const char *name, bool /*dbAware*/)
+ : KexiDataAwareView( mainWin, parent, name )
+ , m_propertySet(0)
+ , m_resizeMode(KexiFormView::ResizeDefault)
+ , m_query(0)
+ , m_queryIsOwned(false)
+ , m_cursor(0)
+// , m_firstFocusWidget(0)
+{
+ m_delayedFormContentsResizeOnShow = 0;
+
+ QHBoxLayout *l = new QHBoxLayout(this);
+ l->setAutoAdd(true);
+
+ m_scrollView = new KexiFormScrollView(this, viewMode()==Kexi::DataViewMode);
+
+//moved setViewWidget(m_scrollView);
+// m_scrollView->show();
+
+ m_dbform = new KexiDBForm(m_scrollView->viewport(), m_scrollView, name/*, conn*/);
+// m_dbform->resize( m_scrollView->viewport()->size() - QSize(20, 20) );
+// m_dbform->resize(QSize(400, 300));
+ m_scrollView->setWidget(m_dbform);
+ m_scrollView->setResizingEnabled(viewMode()!=Kexi::DataViewMode);
+
+// initForm();
+
+ if (viewMode()==Kexi::DataViewMode) {
+ m_scrollView->recordNavigator()->setRecordHandler( m_scrollView );
+ m_scrollView->viewport()->setPaletteBackgroundColor(m_dbform->palette().active().background());
+//moved to formmanager connect(formPart()->manager(), SIGNAL(noFormSelected()), SLOT(slotNoFormSelected()));
+ }
+ else
+ {
+ connect(KFormDesigner::FormManager::self(), SIGNAL(propertySetSwitched(KoProperty::Set*, bool, const QCString&)),
+ this, SLOT(slotPropertySetSwitched(KoProperty::Set*, bool, const QCString&)));
+ connect(KFormDesigner::FormManager::self(), SIGNAL(dirty(KFormDesigner::Form *, bool)),
+ this, SLOT(slotDirty(KFormDesigner::Form *, bool)));
+
+ connect(m_dbform, SIGNAL(handleDragMoveEvent(QDragMoveEvent*)),
+ this, SLOT(slotHandleDragMoveEvent(QDragMoveEvent*)));
+ connect(m_dbform, SIGNAL(handleDropEvent(QDropEvent*)),
+ this, SLOT(slotHandleDropEvent(QDropEvent*)));
+
+ // action stuff
+ plugSharedAction("formpart_taborder", KFormDesigner::FormManager::self(), SLOT(editTabOrder()));
+ plugSharedAction("formpart_adjust_size", KFormDesigner::FormManager::self(), SLOT(adjustWidgetSize()));
+//TODO plugSharedAction("formpart_pixmap_collection", formPart()->manager(), SLOT(editFormPixmapCollection()));
+//TODO plugSharedAction("formpart_connections", formPart()->manager(), SLOT(editConnections()));
+
+ plugSharedAction("edit_copy", KFormDesigner::FormManager::self(), SLOT(copyWidget()));
+ plugSharedAction("edit_cut", KFormDesigner::FormManager::self(), SLOT(cutWidget()));
+ plugSharedAction("edit_paste", KFormDesigner::FormManager::self(), SLOT(pasteWidget()));
+ plugSharedAction("edit_delete", KFormDesigner::FormManager::self(), SLOT(deleteWidget()));
+ plugSharedAction("edit_select_all", KFormDesigner::FormManager::self(), SLOT(selectAll()));
+ plugSharedAction("formpart_clear_contents", KFormDesigner::FormManager::self(), SLOT(clearWidgetContent()));
+ plugSharedAction("edit_undo", KFormDesigner::FormManager::self(), SLOT(undo()));
+ plugSharedAction("edit_redo", KFormDesigner::FormManager::self(), SLOT(redo()));
+
+ plugSharedAction("formpart_layout_menu", KFormDesigner::FormManager::self(), 0 );
+ plugSharedAction("formpart_layout_hbox", KFormDesigner::FormManager::self(), SLOT(layoutHBox()) );
+ plugSharedAction("formpart_layout_vbox", KFormDesigner::FormManager::self(), SLOT(layoutVBox()) );
+ plugSharedAction("formpart_layout_grid", KFormDesigner::FormManager::self(), SLOT(layoutGrid()) );
+#ifdef KEXI_SHOW_SPLITTER_WIDGET
+ plugSharedAction("formpart_layout_hsplitter", KFormDesigner::FormManager::self(), SLOT(layoutHSplitter()) );
+ plugSharedAction("formpart_layout_vsplitter", KFormDesigner::FormManager::self(), SLOT(layoutVSplitter()) );
+#endif
+ plugSharedAction("formpart_break_layout", KFormDesigner::FormManager::self(), SLOT(breakLayout()) );
+
+ plugSharedAction("formpart_format_raise", KFormDesigner::FormManager::self(), SLOT(bringWidgetToFront()) );
+ plugSharedAction("formpart_format_lower", KFormDesigner::FormManager::self(), SLOT(sendWidgetToBack()) );
+
+ plugSharedAction("other_widgets_menu", KFormDesigner::FormManager::self(), 0 );
+ setAvailable("other_widgets_menu", true);
+
+ plugSharedAction("formpart_align_menu", KFormDesigner::FormManager::self(), 0 );
+ plugSharedAction("formpart_align_to_left", KFormDesigner::FormManager::self(),SLOT(alignWidgetsToLeft()) );
+ plugSharedAction("formpart_align_to_right", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToRight()) );
+ plugSharedAction("formpart_align_to_top", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToTop()) );
+ plugSharedAction("formpart_align_to_bottom", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToBottom()) );
+ plugSharedAction("formpart_align_to_grid", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToGrid()) );
+
+ plugSharedAction("formpart_adjust_size_menu", KFormDesigner::FormManager::self(), 0 );
+ plugSharedAction("formpart_adjust_to_fit", KFormDesigner::FormManager::self(), SLOT(adjustWidgetSize()) );
+ plugSharedAction("formpart_adjust_size_grid", KFormDesigner::FormManager::self(), SLOT(adjustSizeToGrid()) );
+ plugSharedAction("formpart_adjust_height_small", KFormDesigner::FormManager::self(), SLOT(adjustHeightToSmall()) );
+ plugSharedAction("formpart_adjust_height_big", KFormDesigner::FormManager::self(), SLOT(adjustHeightToBig()) );
+ plugSharedAction("formpart_adjust_width_small", KFormDesigner::FormManager::self(), SLOT(adjustWidthToSmall()) );
+ plugSharedAction("formpart_adjust_width_big", KFormDesigner::FormManager::self(), SLOT(adjustWidthToBig()) );
+
+ plugSharedAction("format_font", KFormDesigner::FormManager::self(), SLOT(changeFont()) );
+ }
+
+ initForm();
+
+ KexiDataAwareView::init( m_scrollView, m_scrollView, m_scrollView,
+ /* skip data-awarness if design mode */ viewMode()==Kexi::DesignViewMode );
+
+ connect(this, SIGNAL(focus(bool)), this, SLOT(slotFocus(bool)));
+ /// @todo skip this if ther're no borders
+// m_dbform->resize( m_dbform->size()+QSize(m_scrollView->verticalScrollBar()->width(), m_scrollView->horizontalScrollBar()->height()) );
+}
+
+KexiFormView::~KexiFormView()
+{
+ if (m_cursor) {
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ conn->deleteCursor(m_cursor);
+ m_cursor = 0;
+ }
+ deleteQuery();
+
+ // Important: form window is closed.
+ // Set property set to 0 because there is *only one* instance of a property set class
+ // in Kexi, so the main window wouldn't know the set in fact has been changed.
+ m_propertySet = 0;
+ propertySetSwitched();
+}
+
+void
+KexiFormView::deleteQuery()
+{
+ if (m_cursor) {
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ conn->deleteCursor(m_cursor);
+ m_cursor = 0;
+ }
+
+ if (m_queryIsOwned) {
+ delete m_query;
+ } else {
+//! @todo remove this shared query from listened queries list
+ }
+ m_query = 0;
+}
+
+KFormDesigner::Form*
+KexiFormView::form() const
+{
+ if(viewMode()==Kexi::DataViewMode)
+ return tempData()->previewForm;
+ else
+ return tempData()->form;
+}
+
+void
+KexiFormView::setForm(KFormDesigner::Form *f)
+{
+ if(viewMode()==Kexi::DataViewMode)
+ tempData()->previewForm = f;
+ else
+ tempData()->form = f;
+}
+
+void
+KexiFormView::initForm()
+{
+ setForm( new KFormDesigner::Form(KexiFormPart::library(), 0, viewMode()==Kexi::DesignViewMode) );
+// if (viewMode()==Kexi::DataViewMode)
+ //form()->setDesignMode(false);
+ form()->createToplevel(m_dbform, m_dbform);
+
+ if (viewMode()==Kexi::DesignViewMode) {
+ //we want to be informed about executed commands
+ connect(form()->commandHistory(), SIGNAL(commandExecuted()),
+ KFormDesigner::FormManager::self(), SLOT(slotHistoryCommandExecuted()));
+ }
+
+ const bool newForm = parentDialog()->id() < 0;
+
+ KexiDB::FieldList *fields = 0;
+ if (newForm) {
+ // Show the form wizard if this is a new Form
+#ifndef NO_DSWIZARD
+ KexiDataSourceWizard *w = new KexiDataSourceWizard(mainWin(), (QWidget*)mainWin(), "datasource_wizard");
+ if(!w->exec())
+ fields = 0;
+ else
+ fields = w->fields();
+ delete w;
+#endif
+ }
+
+ if(fields)
+ {
+ QDomDocument dom;
+ formPart()->generateForm(fields, dom);
+ KFormDesigner::FormIO::loadFormFromDom(form(), m_dbform, dom);
+ //! @todo handle errors
+ }
+ else
+ loadForm();
+
+ if(form()->autoTabStops())
+ form()->autoAssignTabStops();
+
+ //collect tab order information
+ m_dbform->updateTabStopsOrder(form());
+
+// if (m_dbform->orderedFocusWidgets()->first())
+ // m_scrollView->setFocusProxy( m_dbform->orderedFocusWidgets()->first() );
+
+ KFormDesigner::FormManager::self()->importForm(form(), viewMode()==Kexi::DataViewMode);
+ m_scrollView->setForm(form());
+
+// m_dbform->updateTabStopsOrder(form());
+// QSize s = m_dbform->size();
+// QApplication::sendPostedEvents();
+// m_scrollView->resize( s );
+// m_dbform->resize(s);
+ m_scrollView->refreshContentsSize();
+// m_scrollView->refreshContentsSizeLater(true,true);
+
+ if (newForm && !fields) {
+ /* Our form's area will be resized more than once.
+ Let's resize form widget itself later. */
+ m_delayedFormContentsResizeOnShow = 3;
+ }
+
+ updateDataSourcePage();
+
+ if (!newForm && viewMode()==Kexi::DesignViewMode) {
+ form()->clearCommandHistory();
+ }
+}
+
+void KexiFormView::updateAutoFieldsDataSource()
+{
+//! @todo call this when form's data source is changed
+ //update autofields:
+ //-inherit captions
+ //-inherit data types
+ //(this data has not been stored in the form)
+ QString dataSourceString( m_dbform->dataSource() );
+ QCString dataSourceMimeTypeString( m_dbform->dataSourceMimeType() );
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ KexiDB::TableOrQuerySchema tableOrQuery(
+ conn, dataSourceString.latin1(), dataSourceMimeTypeString=="kexi/table");
+ if (!tableOrQuery.table() && !tableOrQuery.query())
+ return;
+ for (KFormDesigner::ObjectTreeDictIterator it(*form()->objectTree()->dict());
+ it.current(); ++it)
+ {
+ KexiDBAutoField *afWidget = dynamic_cast<KexiDBAutoField*>( it.current()->widget() );
+ if (afWidget) {
+ KexiDB::QueryColumnInfo *colInfo = tableOrQuery.columnInfo( afWidget->dataSource() );
+ if (colInfo) {
+ afWidget->setColumnInfo(colInfo);
+ //setFieldTypeInternal((int)colInfo->field->type());
+ //afWidget->setFieldCaptionInternal(colInfo->captionOrAliasOrName());
+ }
+ }
+ }
+}
+
+void KexiFormView::updateValuesForSubproperties()
+{
+//! @todo call this when form's data source is changed
+ //update autofields:
+ //-inherit captions
+ //-inherit data types
+ //(this data has not been stored in the form)
+ QString dataSourceString( m_dbform->dataSource() );
+ QCString dataSourceMimeTypeString( m_dbform->dataSourceMimeType() );
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ KexiDB::TableOrQuerySchema tableOrQuery(
+ conn, dataSourceString.latin1(), dataSourceMimeTypeString=="kexi/table");
+ if (!tableOrQuery.table() && !tableOrQuery.query())
+ return;
+
+ for (KFormDesigner::ObjectTreeDictIterator it(*form()->objectTree()->dict());
+ it.current(); ++it)
+ {
+ // (delayed) set values for subproperties
+//! @todo this could be at the KFD level, but KFD is going to be merged anyway with kexiforms, right?
+ KFormDesigner::WidgetWithSubpropertiesInterface* subpropIface
+ = dynamic_cast<KFormDesigner::WidgetWithSubpropertiesInterface*>( it.current()->widget() );
+ if (subpropIface && subpropIface->subwidget() && it.current()->subproperties() ) {
+ QWidget *subwidget = subpropIface->subwidget();
+ QMap<QString, QVariant>* subprops = it.current()->subproperties();
+ for (QMapConstIterator<QString, QVariant> subpropIt = subprops->constBegin(); subpropIt!=subprops->constEnd(); ++subpropIt) {
+ kexipluginsdbg << "KexiFormView::loadForm(): delayed setting of the subproperty: widget="
+ << it.current()->widget()->name() << " prop=" << subpropIt.key() << " val=" << subpropIt.data() << endl;
+
+ const int count = subwidget->metaObject()->findProperty(subpropIt.key().latin1(), true);
+ const QMetaProperty *meta = count!=-1 ? subwidget->metaObject()->property(count, true) : 0;
+ if (meta) {
+ // Special case: the property value of type enum (set) but is saved as a string list,
+ // not as int, so we need to translate it to int. It's been created as such
+ // by FormIO::readPropertyValue(). Example: "alignment" property.
+ if (meta->isSetType() && subpropIt.data().type()==QVariant::StringList) {
+ QStrList keys;
+ const QStringList list( subpropIt.data().toStringList() );
+ for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
+ keys.append((*it).latin1());
+ subwidget->setProperty( subpropIt.key().latin1(), meta->keysToValue(keys) );
+ }
+ else {
+ subwidget->setProperty( subpropIt.key().latin1(), subpropIt.data() );
+ }
+ }
+ }//for
+ }
+ }
+}
+
+//! Used in KexiFormView::loadForm()
+static void setUnsavedBLOBIdsForDataViewMode(
+ QWidget* widget, const QMap<QCString, KexiBLOBBuffer::Id_t>& unsavedLocalBLOBsByName)
+{
+ if (-1 != widget->metaObject()->findProperty("pixmapId")) {
+ const KexiBLOBBuffer::Id_t blobID = unsavedLocalBLOBsByName[ widget->name() ];
+ if (blobID > 0)
+ widget->setProperty("pixmapId", (uint /* KexiBLOBBuffer::Id_t is unsafe and unsupported by QVariant - will be fixed in Qt4*/)blobID);
+ }
+ const QObjectList *list = widget->children();
+ if (!list)
+ return;
+ for (QObjectListIterator it(*list); it.current(); ++it) {
+ if (dynamic_cast<QWidget*>(it.current()))
+ setUnsavedBLOBIdsForDataViewMode(dynamic_cast<QWidget*>(it.current()), unsavedLocalBLOBsByName);
+ }
+}
+
+void
+KexiFormView::loadForm()
+{
+//@todo also load m_resizeMode !
+
+ kexipluginsdbg << "KexiFormView::loadForm() Loading the form with id : " << parentDialog()->id() << endl;
+ // If we are previewing the Form, use the tempData instead of the form stored in the db
+ if(viewMode()==Kexi::DataViewMode && !tempData()->tempForm.isNull() )
+ {
+ KFormDesigner::FormIO::loadFormFromString(form(), m_dbform, tempData()->tempForm);
+ setUnsavedBLOBIdsForDataViewMode( m_dbform, tempData()->unsavedLocalBLOBsByName );
+ updateAutoFieldsDataSource();
+ updateValuesForSubproperties();
+ return;
+ }
+
+ // normal load
+ QString data;
+ loadDataBlock(data);
+ KFormDesigner::FormIO::loadFormFromString(form(), m_dbform, data);
+
+ //"autoTabStops" property is loaded -set it within the form tree as well
+ form()->setAutoTabStops( m_dbform->autoTabStops() );
+
+ updateAutoFieldsDataSource();
+ updateValuesForSubproperties();
+}
+
+void
+KexiFormView::slotPropertySetSwitched(KoProperty::Set *set, bool forceReload, const QCString& propertyToSelect)
+{
+// if (set && parentDialog()!=parentDialog()->mainWin()->currentDialog())
+ if (form() != KFormDesigner::FormManager::self()->activeForm())
+ return; //this is not the current form view
+ m_propertySet = set;
+ if (forceReload)
+ propertySetReloaded(true/*preservePrevSelection*/, propertyToSelect);
+ else
+ propertySetSwitched();
+
+ formPart()->dataSourcePage()->assignPropertySet(m_propertySet);
+}
+
+tristate
+KexiFormView::beforeSwitchTo(int mode, bool &dontStore)
+{
+ if (mode!=viewMode()) {
+ if (viewMode()==Kexi::DataViewMode) {
+ if (!m_scrollView->acceptRowEdit())
+ return cancelled;
+
+ m_scrollView->beforeSwitchView();
+ }
+ else {
+ //remember our pos
+ tempData()->scrollViewContentsPos
+ = QPoint(m_scrollView->contentsX(), m_scrollView->contentsY());
+ }
+ }
+
+ // we don't store on db, but in our TempData
+ dontStore = true;
+ if(dirty() && (mode == Kexi::DataViewMode) && form()->objectTree()) {
+ KexiFormPart::TempData* temp = tempData();
+ if (!KFormDesigner::FormIO::saveFormToString(form(), temp->tempForm))
+ return false;
+
+ //collect blobs from design mode by name for use in data view mode
+ temp->unsavedLocalBLOBsByName.clear();
+ for (QMapConstIterator<QWidget*, KexiBLOBBuffer::Id_t> it = temp->unsavedLocalBLOBs.constBegin();
+ it!=temp->unsavedLocalBLOBs.constEnd(); ++it)
+ {
+ if (!it.key())
+ continue;
+ temp->unsavedLocalBLOBsByName.insert( it.key()->name(), it.data() );
+ }
+ }
+
+ return true;
+}
+
+tristate
+KexiFormView::afterSwitchFrom(int mode)
+{
+ if (mode == 0 || mode == Kexi::DesignViewMode) {
+ if (parentDialog()->neverSaved()) {
+ m_dbform->resize(QSize(400, 300));
+ m_scrollView->refreshContentsSizeLater(true,true);
+ //m_delayedFormContentsResizeOnShow = false;
+ }
+ }
+
+ if (mode != 0 && mode != Kexi::DesignViewMode) {
+ //preserve contents pos after switching to other view
+ m_scrollView->setContentsPos(tempData()->scrollViewContentsPos.x(),
+ tempData()->scrollViewContentsPos.y());
+ }
+// if (mode == Kexi::DesignViewMode) {
+ //m_scrollView->move(0,0);
+ //m_scrollView->setContentsPos(0,0);
+ //m_scrollView->moveChild(m_dbform, 0, 0);
+// }
+
+ if((mode == Kexi::DesignViewMode) && viewMode()==Kexi::DataViewMode) {
+ // The form may have been modified, so we must recreate the preview
+ delete m_dbform; // also deletes form()
+ m_dbform = new KexiDBForm(m_scrollView->viewport(), m_scrollView, "KexiDBForm");
+ m_scrollView->setWidget(m_dbform);
+
+ initForm();
+//moved to formmanager slotNoFormSelected();
+
+ //reset position
+ m_scrollView->setContentsPos(0,0);
+ m_dbform->move(0,0);
+
+ }
+
+ //update tab stops if needed
+ if (viewMode()==Kexi::DataViewMode) {
+// //propagate current "autoTabStops" property value to the form tree
+// form()->setAutoTabStops( m_dbform->autoTabStops() );
+
+// if(form()->autoTabStops())
+// form()->autoAssignTabStops();
+ }
+ else {
+ //set "autoTabStops" property
+ m_dbform->setAutoTabStops( form()->autoTabStops() );
+ }
+
+ if (viewMode() == Kexi::DataViewMode) {
+//TMP!!
+ initDataSource();
+
+ //handle events for this form
+ m_scrollView->setMainWidgetForEventHandling(parentDialog()->mainWin(), m_dbform);
+
+ //set focus on 1st focusable widget which has valid dataSource property set
+ if (!m_dbform->orderedFocusWidgets()->isEmpty()) {
+// QWidget *www = focusWidget();
+ //if (Kexi::hasParent(this, qApp->focusWidget())) {
+ KexiUtils::unsetFocusWithReason(qApp->focusWidget(), QFocusEvent::Tab);
+ //}
+
+ QPtrListIterator<QWidget> it(*m_dbform->orderedFocusWidgets());
+ for (;it.current(); ++it) {
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>(it.current());
+ if (iface)
+ kexipluginsdbg << iface->dataSource() << endl;
+ if (iface && iface->columnInfo() && !iface->isReadOnly()
+/*! @todo add option for skipping autoincremented fields */
+ /* also skip autoincremented fields:*/
+ && !iface->columnInfo()->field->isAutoIncrement()) //!iface->dataSource().isEmpty()
+ break;
+ }
+ if (!it.current()) //eventually, focus first available widget if nothing other is available
+ it.toFirst();
+
+ it.current()->setFocus();
+ KexiUtils::setFocusWithReason(it.current(), QFocusEvent::Tab);
+ m_setFocusInternalOnce = it.current();
+ }
+
+ if (m_query)
+ m_scrollView->selectFirstRow();
+ }
+
+ //dirty only if it's a new object
+ if (mode == 0)
+ setDirty( parentDialog()->partItem()->neverSaved() );
+
+ if (mode==Kexi::DataViewMode && viewMode()==Kexi::DesignViewMode) {
+// slotPropertySetSwitched
+// emit KFormDesigner::FormManager::self()->propertySetSwitched( KFormDesigner::FormManager::self()->propertySet()->set(), true );
+ }
+
+ return true;
+}
+
+void KexiFormView::initDataSource()
+{
+ deleteQuery();
+ QString dataSourceString( m_dbform->dataSource() );
+ QCString dataSourceMimeTypeString( m_dbform->dataSourceMimeType() );
+//! @todo also handle anonymous (not stored) queries provided as statements here
+ bool ok = !dataSourceString.isEmpty();
+
+/* if (m_previousDataSourceString.lower()==dataSourceString.lower() && !m_cursor) {
+ //data source changed: delete previous cursor
+ m_conn->deleteCursor(m_cursor);
+ m_cursor = 0;
+ }*/
+
+ KexiDB::TableSchema *tableSchema = 0;
+ KexiDB::Connection *conn = 0;
+ QStringList sources;
+ bool forceReadOnlyDataSource = false;
+
+ if (ok) {
+// m_previousDataSourceString = dataSourceString;
+
+ //collect all data-aware widgets and create query schema
+ m_scrollView->setMainDataSourceWidget(m_dbform);
+ sources = m_scrollView->usedDataSources();
+ conn = parentDialog()->mainWin()->project()->dbConnection();
+ if (dataSourceMimeTypeString.isEmpty() /*table type is the default*/
+ || dataSourceMimeTypeString=="kexi/table")
+ {
+ tableSchema = conn->tableSchema( dataSourceString );
+ if (tableSchema) {
+ /* We will build a _minimum_ query schema from selected table fields. */
+ m_query = new KexiDB::QuerySchema();
+ m_queryIsOwned = true;
+
+ if (dataSourceMimeTypeString.isEmpty())
+ m_dbform->setDataSourceMimeType("kexi/table"); //update for compatibility
+ }
+ }
+
+ if (!tableSchema) {
+ if (dataSourceMimeTypeString.isEmpty() /*also try to find a query (for compatibility with Kexi<=0.9)*/
+ || dataSourceMimeTypeString=="kexi/query")
+ {
+ //try to find predefined query schema.
+ //Note: In general, we could not skip unused fields within this query because
+ // it can have GROUP BY clause.
+ //! @todo check if the query could have skipped unused fields (no GROUP BY, no joins, etc.)
+ m_query = conn->querySchema( dataSourceString );
+ m_queryIsOwned = false;
+ ok = m_query != 0;
+ if (ok && dataSourceMimeTypeString.isEmpty())
+ m_dbform->setDataSourceMimeType("kexi/query"); //update for compatibility
+ // query results are read-only
+//! @todo There can be read-write queries, e.g. simple "SELECT * FROM...". Add a checking function to KexiDB.
+ forceReadOnlyDataSource = true;
+ }
+ else //no other mime types supported
+ ok = false;
+ }
+ }
+
+ QDict<char> invalidSources(997);
+ if (ok) {
+ KexiDB::IndexSchema *pkey = tableSchema ? tableSchema->primaryKey() : 0;
+ if (pkey) {
+ //always add all fields from table's primary key
+ // (don't worry about duplicates, unique list will be computed later)
+ sources += pkey->names();
+ kexipluginsdbg << "KexiFormView::initDataSource(): pkey added to data sources: " << pkey->names() << endl;
+ }
+ kexipluginsdbg << "KexiFormView::initDataSource(): sources=" << sources << endl;
+
+ uint index = 0;
+ for (QStringList::ConstIterator it = sources.constBegin();
+ it!=sources.constEnd(); ++it, index++) {
+/*! @todo add expression support */
+ QString fieldName( (*it).lower() );
+ //remove "tablename." if it was prepended
+ if (tableSchema && fieldName.startsWith( tableSchema->name().lower()+"." ))
+ fieldName = fieldName.mid(tableSchema->name().length()+1);
+ //remove "queryname." if it was prepended
+ if (!tableSchema && fieldName.startsWith( m_query->name().lower()+"." ))
+ fieldName = fieldName.mid(m_query->name().length()+1);
+ KexiDB::Field *f = tableSchema ? tableSchema->field(fieldName) : m_query->field(fieldName);
+ if (!f) {
+/*! @todo show error */
+ //remove this widget from the set of data widgets in the provider
+/*! @todo fieldName is ok, but what about expressions? */
+ invalidSources.insert( fieldName, (const char*)1 ); // += index;
+ kexipluginsdbg << "KexiFormView::initDataSource(): invalidSources+=" << index << " ("
+ << (*it) << ")" << endl;
+ continue;
+ }
+ if (tableSchema) {
+ if (!m_query->hasField( f )) {
+ //we're building a new query: add this field
+ m_query->addField( f );
+ }
+ }
+ }
+ if (invalidSources.count()==sources.count()) {
+ //all data sources are invalid! don't execute the query
+ deleteQuery();
+ }
+ else {
+ KexiDB::debug( m_query->parameters() );
+ // like in KexiQueryView::executeQuery()
+ QValueList<QVariant> params;
+ {
+ KexiUtils::WaitCursorRemover remover;
+ params = KexiQueryParameters::getParameters(this, *conn->driver(), *m_query, ok);
+ }
+ if (ok) //input cancelled
+ m_cursor = conn->executeQuery( *m_query, params );
+ }
+ m_scrollView->invalidateDataSources( invalidSources, m_query );
+ ok = m_cursor!=0;
+ }
+
+ if (!invalidSources.isEmpty())
+ m_dbform->updateTabStopsOrder();
+
+ if (ok) {
+//! @todo PRIMITIVE!! data setting:
+//! @todo KexiTableViewData is not great name for data class here... rename/move?
+ KexiTableViewData* data = new KexiTableViewData(m_cursor);
+ if (forceReadOnlyDataSource)
+ data->setReadOnly(true);
+ data->preloadAllRows();
+
+///*! @todo few backends return result count for free! - no need to reopen() */
+// int resultCount = -1;
+// if (ok) {
+// resultCount = m_conn->resultCount(m_conn->selectStatement(*m_query));
+// ok = m_cursor->reopen();
+// }
+// if (ok)
+// ok = ! (!m_cursor->moveFirst() && m_cursor->error());
+
+ m_scrollView->setData( data, true /*owner*/ );
+ }
+ else
+ m_scrollView->setData( 0, false );
+}
+
+void
+KexiFormView::slotDirty(KFormDesigner::Form *dirtyForm, bool isDirty)
+{
+ if(dirtyForm == form())
+ KexiViewBase::setDirty(isDirty);
+}
+
+KexiDB::SchemaData*
+KexiFormView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ KexiDB::SchemaData *s = KexiViewBase::storeNewData(sdata, cancel);
+ kexipluginsdbg << "KexiDBForm::storeNewData(): new id:" << s->id() << endl;
+
+ if (!s || cancel) {
+ delete s;
+ return 0;
+ }
+ if (!storeData()) {
+ //failure: remove object's schema data to avoid garbage
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ conn->removeObject( s->id() );
+ delete s;
+ return 0;
+ }
+ return s;
+}
+
+tristate
+KexiFormView::storeData(bool dontAsk)
+{
+ Q_UNUSED(dontAsk);
+ kexipluginsdbg << "KexiDBForm::storeData(): " << parentDialog()->partItem()->name()
+ << " [" << parentDialog()->id() << "]" << endl;
+
+ //-- first, store local BLOBs, so identifiers can be updated
+//! @todo remove unused data stored previously
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ KexiDB::TableSchema *blobsTable = conn->tableSchema("kexi__blobs");
+ if (!blobsTable) { //compatibility check for older Kexi project versions
+//! @todo show message about missing kexi__blobs?
+ return false;
+ }
+ // Not all engines accept passing NULL to PKEY o_id, so we're omitting it.
+ QStringList blobsFieldNamesWithoutID(blobsTable->names());
+ blobsFieldNamesWithoutID.pop_front();
+ KexiDB::FieldList *blobsFieldsWithoutID = blobsTable->subList(blobsFieldNamesWithoutID);
+
+ KexiDB::PreparedStatement::Ptr st = conn->prepareStatement(
+ KexiDB::PreparedStatement::InsertStatement, *blobsFieldsWithoutID);
+ if (!st) {
+ delete blobsFieldsWithoutID;
+ //! @todo show message
+ return false;
+ }
+ KexiBLOBBuffer *blobBuf = KexiBLOBBuffer::self();
+ KexiFormView *designFormView
+ = dynamic_cast<KexiFormView*>( parentDialog()->viewForMode(Kexi::DesignViewMode) );
+ if (designFormView) {
+ for (QMapConstIterator<QWidget*, KexiBLOBBuffer::Id_t> it = tempData()->unsavedLocalBLOBs.constBegin();
+ it!=tempData()->unsavedLocalBLOBs.constEnd(); ++it)
+ {
+ if (!it.key()) {
+ kexipluginswarn << "KexiFormView::storeData(): it.key()==0 !" << endl;
+ continue;
+ }
+ kexipluginsdbg << "name=" << it.key()->name() << " dataID=" << it.data() << endl;
+ KexiBLOBBuffer::Handle h( blobBuf->objectForId(it.data(), /*!stored*/false) );
+ if (!h)
+ continue; //no BLOB assigned
+
+ QString originalFileName(h.originalFileName());
+ QFileInfo fi(originalFileName);
+ QString caption(fi.baseName().replace('_', " ").simplifyWhiteSpace());
+
+ if (st) {
+ *st /* << NO, (pgsql doesn't support this):QVariant()*/ /*id*/
+ << h.data() << originalFileName << caption
+ << h.mimeType() << (uint)/*! @todo unsafe */h.folderId();
+ if (!st->execute()) {
+ delete blobsFieldsWithoutID;
+ kexipluginsdbg << " execute error" << endl;
+ return false;
+ }
+ }
+ delete blobsFieldsWithoutID;
+ blobsFieldsWithoutID=0;
+ const Q_ULLONG storedBLOBID = conn->lastInsertedAutoIncValue("o_id", "kexi__blobs");
+ if ((Q_ULLONG)-1 == storedBLOBID) {
+ //! @todo show message?
+ return false;
+ }
+ kexipluginsdbg << " storedDataID=" << storedBLOBID << endl;
+ h.setStoredWidthID((KexiBLOBBuffer::Id_t /*unsafe - will be fixed in Qt4*/)storedBLOBID);
+ //set widget's internal property so it can be saved...
+ const QVariant oldStoredPixmapId( it.key()->property("storedPixmapId") );
+ it.key()->setProperty("storedPixmapId",
+ QVariant((uint /* KexiBLOBBuffer::Id_t is unsafe and unsupported by QVariant - will be fixed in Qt4*/)storedBLOBID));
+ KFormDesigner::ObjectTreeItem *widgetItem = designFormView->form()->objectTree()->lookup(it.key()->name()); //form()->objectTree()->lookup(it.key()->name());
+ if (widgetItem)
+ widgetItem->addModifiedProperty( "storedPixmapId", oldStoredPixmapId );
+ else
+ kexipluginswarn << "KexiFormView::storeData(): no '" << widgetItem->name() << "' widget found within a form" << endl;
+ }
+ }
+
+ //-- now, save form's XML
+ QString data;
+ if (!KFormDesigner::FormIO::saveFormToString(tempData()->form, data))
+ return false;
+ if (!storeDataBlock(data))
+ return false;
+
+ //all blobs are now saved
+ tempData()->unsavedLocalBLOBs.clear();
+
+ tempData()->tempForm = QString::null;
+ return true;
+}
+
+#if 0
+/// Action stuff /////////////////
+void
+KexiFormView::slotWidgetSelected(KFormDesigner::Form *f, bool multiple)
+{
+ if(f != form())
+ return;
+
+ enableFormActions();
+ // Enable edit actions
+ setAvailable("edit_copy", true);
+ setAvailable("edit_cut", true);
+ setAvailable("edit_clear", true);
+
+ // 'Align Widgets' menu
+ setAvailable("formpart_align_menu", multiple);
+ setAvailable("formpart_align_to_left", multiple);
+ setAvailable("formpart_align_to_right", multiple);
+ setAvailable("formpart_align_to_top", multiple);
+ setAvailable("formpart_align_to_bottom", multiple);
+
+ setAvailable("formpart_adjust_size_menu", true);
+ setAvailable("formpart_adjust_width_small", multiple);
+ setAvailable("formpart_adjust_width_big", multiple);
+ setAvailable("formpart_adjust_height_small", multiple);
+ setAvailable("formpart_adjust_height_big", multiple);
+
+ setAvailable("formpart_format_raise", true);
+ setAvailable("formpart_format_lower", true);
+
+ // If the widgets selected is a container, we enable layout actions
+ if(!multiple)
+ {
+ KFormDesigner::ObjectTreeItem *item = f->objectTree()->lookup( f->selectedWidgets()->first()->name() );
+ if(item && item->container())
+ multiple = true;
+ }
+ // Layout actions
+ setAvailable("formpart_layout_hbox", multiple);
+ setAvailable("formpart_layout_vbox", multiple);
+ setAvailable("formpart_layout_grid", multiple);
+
+ KFormDesigner::Container *container = f->activeContainer();
+ setAvailable("formpart_break_layout", container ?
+ (container->layoutType() != KFormDesigner::Container::NoLayout) : false );
+}
+
+void
+KexiFormView::slotFormWidgetSelected(KFormDesigner::Form *f)
+{
+ if(f != form())
+ return;
+
+ disableWidgetActions();
+ enableFormActions();
+
+ // Layout actions
+ setAvailable("formpart_layout_hbox", true);
+ setAvailable("formpart_layout_vbox", true);
+ setAvailable("formpart_layout_grid", true);
+ setAvailable("formpart_break_layout", (f->toplevelContainer()->layoutType() != KFormDesigner::Container::NoLayout));
+}
+
+void
+KexiFormView::slotNoFormSelected() // == form in preview mode
+{
+ disableWidgetActions();
+
+ // Disable paste action
+ setAvailable("edit_paste", false);
+ setAvailable("edit_undo", false);
+ setAvailable("edit_redo", false);
+
+ // Disable 'Tools' actions
+ setAvailable("formpart_pixmap_collection", false);
+ setAvailable("formpart_connections", false);
+ setAvailable("formpart_taborder", false);
+ setAvailable("formpart_change_style", false);
+}
+
+void
+KexiFormView::enableFormActions()
+{
+ // Enable 'Tools' actions
+ setAvailable("formpart_pixmap_collection", true);
+ setAvailable("formpart_connections", true);
+ setAvailable("formpart_taborder", true);
+
+ setAvailable("edit_paste", KFormDesigner::FormManager::self()->isPasteEnabled());
+}
+
+void
+KexiFormView::disableWidgetActions()
+{
+ // Disable edit actions
+ setAvailable("edit_copy", false);
+ setAvailable("edit_cut", false);
+ setAvailable("edit_clear", false);
+
+ // Disable format functions
+ setAvailable("formpart_align_menu", false);
+ setAvailable("formpart_align_to_left", false);
+ setAvailable("formpart_align_to_right", false);
+ setAvailable("formpart_align_to_top", false);
+ setAvailable("formpart_align_to_bottom", false);
+
+ setAvailable("formpart_adjust_size_menu", false);
+ setAvailable("formpart_adjust_width_small", false);
+ setAvailable("formpart_adjust_width_big", false);
+ setAvailable("formpart_adjust_height_small", false);
+ setAvailable("formpart_adjust_height_big", false);
+
+ setAvailable("formpart_format_raise", false);
+ setAvailable("formpart_format_lower", false);
+
+ setAvailable("formpart_layout_hbox", false);
+ setAvailable("formpart_layout_vbox", false);
+ setAvailable("formpart_layout_grid", false);
+ setAvailable("formpart_break_layout", false);
+}
+
+void
+KexiFormView::setUndoEnabled(bool enabled)
+{
+ setAvailable("edit_undo", enabled);
+}
+
+void
+KexiFormView::setRedoEnabled(bool enabled)
+{
+ setAvailable("edit_redo", enabled);
+}
+#endif //0
+
+QSize
+KexiFormView::preferredSizeHint(const QSize& otherSize)
+{
+ if (parentDialog()->neverSaved()) {
+ //ignore otherSize if possible
+// return KexiViewBase::preferredSizeHint( (parentDialog() && parentDialog()->mdiParent()) ? QSize(10000,10000) : otherSize);
+ }
+
+ return (m_dbform->size()
+ +QSize(m_scrollView->verticalScrollBar()->isVisible() ? m_scrollView->verticalScrollBar()->width()*3/2 : 10,
+ m_scrollView->horizontalScrollBar()->isVisible() ? m_scrollView->horizontalScrollBar()->height()*3/2 : 10))
+ .expandedTo( KexiViewBase::preferredSizeHint(otherSize) );
+}
+
+void
+KexiFormView::resizeEvent( QResizeEvent *e )
+{
+ if (viewMode()==Kexi::DataViewMode) {
+ m_scrollView->refreshContentsSizeLater(
+ e->size().width()!=e->oldSize().width(),
+ e->size().height()!=e->oldSize().height()
+ );
+ }
+ KexiViewBase::resizeEvent(e);
+ m_scrollView->updateNavPanelGeometry();
+ if (m_delayedFormContentsResizeOnShow>0) { // && isVisible()) {
+ m_delayedFormContentsResizeOnShow--;
+ m_dbform->resize( e->size() - QSize(30, 30) );
+ }
+}
+
+void
+KexiFormView::setFocusInternal()
+{
+ if (viewMode() == Kexi::DataViewMode) {
+ if (m_dbform->focusWidget()) {
+ //better-looking focus
+ if (m_setFocusInternalOnce) {
+ KexiUtils::setFocusWithReason(m_setFocusInternalOnce, QFocusEvent::Other);//Tab);
+ m_setFocusInternalOnce = 0;
+ }
+ else {
+ //ok? SET_FOCUS_USING_REASON(m_dbform->focusWidget(), QFocusEvent::Other);//Tab);
+ }
+ return;
+ }
+ }
+ QWidget::setFocus();
+}
+
+void
+KexiFormView::show()
+{
+ KexiDataAwareView::show();
+
+//moved from KexiFormScrollView::show():
+
+ //now get resize mode settings for entire form
+ // if (resizeMode() == KexiFormView::ResizeAuto)
+ if (viewMode()==Kexi::DataViewMode) {
+ if (resizeMode() == KexiFormView::ResizeAuto)
+ m_scrollView->setResizePolicy(QScrollView::AutoOneFit);
+ }
+}
+
+void
+KexiFormView::slotFocus(bool in)
+{
+ if(in && form() && KFormDesigner::FormManager::self() && KFormDesigner::FormManager::self()->activeForm() != form()) {
+ KFormDesigner::FormManager::self()->windowChanged(m_dbform);
+ updateDataSourcePage();
+ }
+}
+
+void
+KexiFormView::updateDataSourcePage()
+{
+ if (viewMode()==Kexi::DesignViewMode) {
+ QCString dataSourceMimeType, dataSource;
+ KFormDesigner::WidgetPropertySet *set = KFormDesigner::FormManager::self()->propertySet();
+ if (set->contains("dataSourceMimeType"))
+ dataSourceMimeType = (*set)["dataSourceMimeType"].value().toCString();
+ if (set->contains("dataSource"))
+ dataSource = (*set)["dataSource"].value().toCString();
+
+ formPart()->dataSourcePage()->setDataSource(dataSourceMimeType, dataSource);
+ }
+}
+
+void
+KexiFormView::slotHandleDragMoveEvent(QDragMoveEvent* e)
+{
+ if (KexiFieldDrag::canDecodeMultiple( e )) {
+ e->accept(true);
+ //dirty: drawRect(QRect( e->pos(), QSize(50, 20)), 2);
+ }
+}
+
+void
+KexiFormView::slotHandleDropEvent(QDropEvent* e)
+{
+ const QWidget *targetContainerWidget = dynamic_cast<const QWidget*>(sender());
+ KFormDesigner::ObjectTreeItem *targetContainerWidgetItem = targetContainerWidget
+ ? form()->objectTree()->lookup( targetContainerWidget->name() ) : 0;
+ if (targetContainerWidgetItem && targetContainerWidgetItem->container()
+ && KexiFieldDrag::canDecodeMultiple( e ))
+ {
+ QString sourceMimeType, sourceName;
+ QStringList fields;
+ if (!KexiFieldDrag::decodeMultiple( e, sourceMimeType, sourceName, fields ))
+ return;
+ insertAutoFields(sourceMimeType, sourceName, fields,
+ targetContainerWidgetItem->container(), e->pos());
+ }
+}
+
+void
+KexiFormView::insertAutoFields(const QString& sourceMimeType, const QString& sourceName,
+ const QStringList& fields, KFormDesigner::Container* targetContainer, const QPoint& _pos)
+{
+ if (fields.isEmpty())
+ return;
+
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ KexiDB::TableOrQuerySchema tableOrQuery(conn, sourceName.latin1(), sourceMimeType=="kexi/table");
+ if (!tableOrQuery.table() && !tableOrQuery.query()) {
+ kexipluginswarn << "KexiFormView::insertAutoFields(): no such table/query \""
+ << sourceName << "\"" << endl;
+ return;
+ }
+
+ QPoint pos(_pos);
+ //if pos is not specified, compute a new position:
+ if (pos==QPoint(-1,-1)) {
+ if (m_widgetGeometryForRecentInsertAutoFields.isValid()) {
+ pos = m_widgetGeometryForRecentInsertAutoFields.bottomLeft()
+ + QPoint(0,form()->gridSize());
+ }
+ else {
+ pos = QPoint(40, 40); //start here
+ }
+ }
+
+ // there will be many actions performed, do not update property pane until all that's finished
+ KFormDesigner::FormManager::self()->blockPropertyEditorUpdating(this);
+
+//! todo unnamed query colums are not supported
+
+// KFormDesigner::WidgetList* prevSelection = form()->selectedWidgets();
+ KFormDesigner::WidgetList widgetsToSelect;
+ KFormDesigner::CommandGroup *group = new KFormDesigner::CommandGroup(
+ fields.count()==1 ? i18n("Insert AutoField widget") : i18n("Insert %1 AutoField widgets").arg(fields.count()),
+ KFormDesigner::FormManager::self()->propertySet()
+ );
+
+ foreach( QStringList::ConstIterator, it, fields ) {
+ KexiDB::QueryColumnInfo* column = tableOrQuery.columnInfo(*it);
+ if (!column) {
+ kexipluginswarn << "KexiFormView::insertAutoFields(): no such field \""
+ << *it << "\" in table/query \"" << sourceName << "\"" << endl;
+ continue;
+ }
+//! todo add autolabel using field's caption or name
+ //KFormDesigner::Container *targetContainer;
+/* QWidget* targetContainerWidget = QApplication::widgetAt(pos, true);
+ while (targetContainerWidget
+ && !dynamic_cast<KFormDesigner::Container*>(targetContainerWidget))
+ {
+ targetContainerWidget = targetContainerWidget->parentWidget();
+ }
+ if (dynamic_cast<KFormDesigner::Container*>(targetContainerWidget))
+ targetContainer = dynamic_cast<KFormDesigner::Container*>(targetContainerWidget);
+ else
+ targetContainer = form()->toplevelContainer();*/
+ KFormDesigner::InsertWidgetCommand *insertCmd
+ = new KFormDesigner::InsertWidgetCommand(targetContainer,
+ //! todo this is hardcoded!
+ "KexiDBAutoField",
+ //! todo this name can be invalid for expressions: if so, fall back to a default class' prefix!
+ pos, column->aliasOrName()
+ );
+ insertCmd->execute();
+ group->addCommand(insertCmd, false/*don't exec twice*/);
+
+ KFormDesigner::ObjectTreeItem *newWidgetItem
+ = form()->objectTree()->dict()->find(insertCmd->widgetName());
+ KexiDBAutoField* newWidget
+ = newWidgetItem ? dynamic_cast<KexiDBAutoField*>(newWidgetItem->widget()) : 0;
+ widgetsToSelect.append(newWidget);
+//#if 0
+ KFormDesigner::CommandGroup *subGroup
+ = new KFormDesigner::CommandGroup("", KFormDesigner::FormManager::self()->propertySet());
+ QMap<QCString, QVariant> propValues;
+ propValues.insert("dataSource", column->aliasOrName());
+ propValues.insert("fieldTypeInternal", (int)column->field->type());
+ propValues.insert("fieldCaptionInternal", column->captionOrAliasOrName());
+ KFormDesigner::FormManager::self()->propertySet()->createPropertyCommandsInDesignMode(
+ newWidget, propValues, subGroup, false/*!addToActiveForm*/,
+ true /*!execFlagForSubCommands*/);
+ subGroup->execute();
+ group->addCommand( subGroup, false/*will not be executed on CommandGroup::execute()*/ );
+
+//#endif
+ //set data source and caption
+ //-we don't need to use PropertyCommand here beacause we don't need UNDO
+ // for these single commands
+// newWidget->setDataSource(column->aliasOrName());
+// newWidget->setFieldTypeInternal((int)column->field->type());
+// newWidget->setFieldCaptionInternal(column->captionOrAliasOrName());
+ //resize again because autofield's type changed what can lead to changed sizeHint()
+// newWidget->resize(newWidget->sizeHint());
+ KFormDesigner::WidgetList list;
+ list.append(newWidget);
+ KFormDesigner::AdjustSizeCommand *adjustCommand
+ = new KFormDesigner::AdjustSizeCommand(KFormDesigner::AdjustSizeCommand::SizeToFit,
+ list, form());
+ adjustCommand->execute();
+ group->addCommand( adjustCommand,
+ false/*will not be executed on CommandGroup::execute()*/
+ );
+
+ if (newWidget) {//move position down for next widget
+ pos.setY( pos.y() + newWidget->height() + form()->gridSize());
+ }
+ }
+ if (widgetsToSelect.last()) {
+ //resize form if needed
+ QRect oldFormRect( m_dbform->geometry() );
+ QRect newFormRect( oldFormRect );
+ newFormRect.setWidth(QMAX(m_dbform->width(), widgetsToSelect.last()->geometry().right()+1));
+ newFormRect.setHeight(QMAX(m_dbform->height(), widgetsToSelect.last()->geometry().bottom()+1));
+ if (newFormRect != oldFormRect) {
+ //1. resize by hand
+ m_dbform->setGeometry( newFormRect );
+ //2. store information about resize
+ KFormDesigner::PropertyCommand *resizeFormCommand = new KFormDesigner::PropertyCommand(
+ KFormDesigner::FormManager::self()->propertySet(), m_dbform->name(),
+ oldFormRect, newFormRect, "geometry");
+ group->addCommand(resizeFormCommand, true/*will be executed on CommandGroup::execute()*/);
+ }
+
+ //remember geometry of the last inserted widget
+ m_widgetGeometryForRecentInsertAutoFields = widgetsToSelect.last()->geometry();
+ }
+
+ //eventually, add entire command group to active form
+ form()->addCommand( group, true/*exec*/ );
+
+// group->debug();
+
+ //enable proper REDO usage
+ group->resetAllowExecuteFlags();
+
+ m_scrollView->repaint();
+ m_scrollView->viewport()->repaint();
+ m_scrollView->repaintContents();
+ m_scrollView->updateContents();
+ m_scrollView->clipper()->repaint();
+ m_scrollView->refreshContentsSize();
+
+ //select all inserted widgets, if multiple
+ if (widgetsToSelect.count()>1) {
+ form()->setSelectedWidget(0);
+ foreach_list (KFormDesigner::WidgetListIterator, it, widgetsToSelect)
+ form()->setSelectedWidget(it.current(), true/*add*/, true/*dontRaise*/);
+ }
+
+ // eventually, update property pane
+ KFormDesigner::FormManager::self()->unblockPropertyEditorUpdating(this, KFormDesigner::FormManager::self()->propertySet());
+}
+
+void
+KexiFormView::setUnsavedLocalBLOB(QWidget *widget, KexiBLOBBuffer::Id_t id)
+{
+//! @todo if there already was data assigned, remember it should be dereferenced
+ if (id==0)
+ tempData()->unsavedLocalBLOBs.remove(widget);
+ else
+ tempData()->unsavedLocalBLOBs.insert(widget, id);
+}
+
+/*
+todo
+void KexiFormView::updateActions(bool activated)
+{
+ if (viewMode()==Kexi::DesignViewMode) {
+ if (form()->selectedWidget()) {
+ if (form()->widget() == form()->selectedWidget())
+ KFormDesigner::FormManager::self()->emitFormWidgetSelected( form() );
+ else
+ KFormDesigner::FormManager::self()->emitWidgetSelected( form(), false );
+ }
+ else if (form()->selectedWidgets()) {
+ KFormDesigner::FormManager::self()->emitWidgetSelected( form(), true );
+ }
+ }
+ KexiDataAwareView::updateActions(activated);
+}*/
+
+/*
+void KexiFormView::parentDialogDetached()
+{
+ m_dbform->updateTabStopsOrder(form());
+}
+
+void KexiFormView::parentDialogAttached(KMdiChildFrm *)
+{
+ m_dbform->updateTabStopsOrder(form());
+}*/
+
+#include "kexiformview.moc"
+
diff --git a/kexi/plugins/forms/kexiformview.h b/kexi/plugins/forms/kexiformview.h
new file mode 100644
index 00000000..0a774556
--- /dev/null
+++ b/kexi/plugins/forms/kexiformview.h
@@ -0,0 +1,231 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIFORMVIEW_H
+#define KEXIFORMVIEW_H
+
+#include <qtimer.h>
+
+#include <kexiviewbase.h>
+#include <widget/kexidataawareview.h>
+
+#include "kexiformpart.h"
+#include <core/kexiblobbuffer.h>
+
+class KexiFormPart;
+class KexiMainWindow;
+class KexiDBForm;
+class KexiTableItem;
+class KexiTableViewData;
+class KexiFormScrollView;
+namespace KexiDB { class Cursor; }
+namespace KFormDesigner
+{
+ class Container;
+}
+
+//! The KexiFormView lass provides a data-driven (record-based) form view .
+/*! The KexiFormView can display data provided "by hand"
+ or from KexiDB-compatible database source.
+
+ This class provides a single view used inside KexiDialogBase.
+ It takes care of saving/loading form, of enabling actions when needed.
+ One KexiFormView object is instantiated for data view mode
+ and a second KexiFormView object is instantiated for design view mode.
+
+ @see KexiDataTable
+*/
+class KEXIFORMUTILS_EXPORT KexiFormView : public KexiDataAwareView
+{
+ Q_OBJECT
+
+ public:
+ enum ResizeMode {
+ ResizeAuto = 0,
+ ResizeDefault = ResizeAuto,
+ ResizeFixed = 1,
+ NoResize = 2 /*! @todo */
+ };
+
+// KexiFormView(KexiMainWindow *win, QWidget *parent, const char *name, KexiDB::Connection *conn);
+ KexiFormView(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0,
+ bool dbAware = true);
+ virtual ~KexiFormView();
+
+// KexiDB::Connection* connection() { return m_conn; }
+
+ virtual QSize preferredSizeHint(const QSize& otherSize);
+
+ int resizeMode() const { return m_resizeMode; }
+
+ KFormDesigner::Form* form() const;
+
+ /*! Assigns \a id local (static) BLOB's identifier for \a widget widget.
+ Previously assigned BLOB will be usassigned.
+ If \a id is 0, BLOB is unassigned and no new is assigned.
+
+ This method is called when a widget supporting BLOB data
+ (currently, images from KexiDBImageBox, within KexiDBFactory) has BLOB assigned by identifier \a id.
+ BLOB identifiers are defined by KexiBLOBBuffer (KexiBLOBBuffer::self() instance).
+
+ The data collected by this method is used on form's design saving (in design mode).
+ Local BLOBs are retrieved KexiBLOBBuffer::self() and stored in "kexi__blobs" 'system' table.
+ Note that db-aware BLOBs (non local) are not handled this way.
+ */
+ void setUnsavedLocalBLOB(QWidget *widget, KexiBLOBBuffer::Id_t id);
+
+ public slots:
+ /*! Reimplemented to update resize policy. */
+ virtual void show();
+
+ /*! Inserts autofields onto the form at \a pos position.
+ \a sourceMimeType can be "kexi/table" or "kexi/query",
+ \a sourceName is a name of a table or query, \a fields is a list of fields to insert (one or more)
+ Fields are inserted using standard KFormDesigner::InsertWidgetCommand framework,
+ so undo/redo is available for this operation.
+
+ If multiple fields are provided, they will be aligned vertically.
+ If \a pos is QPoint(-1,-1) (the default), position is computed automatically
+ based on a position last inserted field using this method.
+ If this method has not been called yet, position of QPoint(40, 40) will be set.
+
+ Called by:
+ - slotHandleDropEvent() when field(s) are dropped from the data source pane onto the form
+ - KexiFormManager is a used clicked "Insert fields" button on the data source pane. */
+ void insertAutoFields(const QString& sourceMimeType, const QString& sourceName,
+ const QStringList& fields, KFormDesigner::Container* targetContainerWidget,
+ const QPoint& pos = QPoint(-1,-1));
+
+ protected slots:
+ void slotPropertySetSwitched(KoProperty::Set *b, bool forceReload = false,
+ const QCString& propertyToSelect = QCString());
+ void slotDirty(KFormDesigner::Form *f, bool isDirty);
+ void slotFocus(bool in);
+ void slotHandleDragMoveEvent(QDragMoveEvent* e);
+
+ //! Handles field(s) dropping from the data source pane onto the form
+ //! @see insertAutoFields()
+ void slotHandleDropEvent(QDropEvent* e);
+
+//moved to formmanager void slotWidgetSelected(KFormDesigner::Form *form, bool multiple);
+//moved to formmanager void slotFormWidgetSelected(KFormDesigner::Form *form);
+//moved to formmanager void slotNoFormSelected();
+
+//moved to formmanager void setUndoEnabled(bool enabled);
+//moved to formmanager void setRedoEnabled(bool enabled);
+
+ protected:
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+ virtual tristate afterSwitchFrom(int mode);
+ virtual KoProperty::Set* propertySet() { return m_propertySet; }
+
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+ virtual tristate storeData(bool dontAsk = false);
+
+ KexiFormPart::TempData* tempData() const {
+ return dynamic_cast<KexiFormPart::TempData*>(parentDialog()->tempData()); }
+ KexiFormPart* formPart() const { return dynamic_cast<KexiFormPart*>(part()); }
+
+//moved to formmanager void disableWidgetActions();
+//moved to formmanager void enableFormActions();
+
+ void setForm(KFormDesigner::Form *f);
+
+ void initForm();
+
+ void loadForm();
+
+ //! Used in loadForm()
+ void updateAutoFieldsDataSource();
+
+ //! Used in loadForm()
+ void updateValuesForSubproperties();
+
+ virtual void resizeEvent ( QResizeEvent * );
+
+ void initDataSource();
+
+ virtual void setFocusInternal();
+
+/* // for navigator
+ virtual void moveToRecordRequested(uint r);
+ virtual void moveToLastRecordRequested();
+ virtual void moveToPreviousRecordRequested();
+ virtual void moveToNextRecordRequested();
+ virtual void moveToFirstRecordRequested();
+ virtual void addNewRecordRequested();*/
+
+ /*! Called after loading the form contents (before showing it).
+ Also called when the form window (KexiDialogBase) is detached
+ (in KMDI's Child Frame mode), because otherwise tabstop ordering can get broken. */
+ void updateTabStopsOrder();
+
+ /*! @internal */
+ void deleteQuery();
+
+ /*! @internal */
+ void updateDataSourcePage();
+
+ /*! Reimplemented after KexiViewBase.
+ Updates actions (e.g. availability). */
+// todo virtual void updateActions(bool activated);
+
+ KexiDBForm *m_dbform;
+ KexiFormScrollView *m_scrollView;
+ KoProperty::Set *m_propertySet;
+
+ /*! Database cursor used for data retrieving.
+ It is shared between subsequent Data view sessions (just reopened on switch),
+ but deleted and recreated from scratch when form's "dataSource" property changed
+ since last form viewing (m_previousDataSourceString is used for that). */
+ QString m_previousDataSourceString;
+
+ int m_resizeMode;
+
+ KexiDB::QuerySchema* m_query;
+
+ /*! True, if m_query is created as temporary object within this form.
+ If user selected an existing, predefined (stored) query, m_queryIsOwned will be false,
+ so the query object will not be destroyed. */
+ bool m_queryIsOwned;
+
+ KexiDB::Cursor *m_cursor;
+
+ /*! For new (empty) forms only:
+ Our form's area will be resized more than once.
+ We will resize form widget itself later (in resizeEvent()). */
+ int m_delayedFormContentsResizeOnShow;
+
+ //! Used in setFocusInternal()
+ QGuardedPtr<QWidget> m_setFocusInternalOnce;
+
+
+ /*! Stores geometry of widget recently inserted using insertAutoFields() method.
+ having this information, we'r eable to compute position for a newly
+ inserted widget in insertAutoFields() is such position has not been specified.
+ (the position is specified when a widget is inserted with mouse drag & dropping
+ but not with clicking of 'Insert fields' button from Data Source pane) */
+ QRect m_widgetGeometryForRecentInsertAutoFields;
+
+ //! Used in setUnsavedLocalBLOBs()
+// QMap<QWidget*, KexiBLOBBuffer::Id_t> m_unsavedLocalBLOBs;
+};
+
+#endif
diff --git a/kexi/plugins/forms/kformdesigner_kexidbfactory.desktop b/kexi/plugins/forms/kformdesigner_kexidbfactory.desktop
new file mode 100644
index 00000000..4e5bb719
--- /dev/null
+++ b/kexi/plugins/forms/kformdesigner_kexidbfactory.desktop
@@ -0,0 +1,55 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=KFormDesigner/WidgetFactory
+
+Name=Kexi DB Widgets
+Name[bg]=Графични обекти на Kexi за бази данни
+Name[ca]=Estris DB de Kexi
+Name[cy]=Celfigion Cronfa Ddata Kexi
+Name[da]=Kexi DB-kontroller
+Name[de]=Kexi Datenbank-Elemente
+Name[el]=Γραφικά συστατικά Kexi DB
+Name[eo]=Kexi DB-fenestraĵo
+Name[es]=Wigdet de BD de Kexi
+Name[et]=Kexi andmebaasividinad
+Name[eu]=Kexi-ren datu-baseko trepetak
+Name[fa]=عناصر Kexi DB
+Name[fi]=Kexi tietokantaelementit
+Name[fr]=Éléments graphiques de base de données Kexi
+Name[fy]=Kexi DB-widgets
+Name[gl]=Elementos de Base de Datos Kexi
+Name[he]=פריטי מסד נתונים של Kexi
+Name[hr]=Kexi DB widgeti
+Name[hu]=Kexi adatbázis-kezelési grafikus elemek
+Name[is]=Kexi gagnagrunns hlutir
+Name[it]=Oggetti per banche dati per Kexi
+Name[ja]=Kexi DB ウィジェット
+Name[km]=ធាតុ​ក្រាហ្វិក DB សម្រាប់ Kexi
+Name[lv]=Kexi DB logdaļas
+Name[ms]=Widget DB Kexi
+Name[nb]=DB-element for Kexi
+Name[nds]=Datenbank-Stüerelementen för Kexi
+Name[ne]=केक्सी DB विजेटहरू
+Name[nl]=Kexi DB-widgets
+Name[nn]=DB-element for Kexi
+Name[pl]=Kontrolki baz danych dla Kexi
+Name[pt]=Elementos de Base de Dados Kexi
+Name[pt_BR]=Widgets de BD do Kexi
+Name[ru]=Элементы управления для работы с базами данных Kexi
+Name[se]=Kexi-DV-áđat
+Name[sk]=Komponenty Kexi DB
+Name[sl]=Gradniki za zbirko podatkov za Kexi
+Name[sr]=Kexi-јеве DB контроле
+Name[sr@Latn]=Kexi-jeve DB kontrole
+Name[sv]=Kexi-databaskomponenter
+Name[ta]=கெக்சி டிபி சாளர உருக்கள்
+Name[tr]=Kexi DB Parçacıkları
+Name[uk]=Віджети Kexi DB
+Name[uz]=Kexi maʼlumot baza vidjetlari
+Name[uz@cyrillic]=Kexi маълумот база виджетлари
+Name[zh_CN]=Kexi 数据库部件
+Name[zh_TW]=Kexi DB 視窗元件
+
+X-KDE-Library=kformdesigner_kexidbwidgets
+X-KFormDesigner-FactoryGroup=kexi
+X-KFormDesigner-WidgetFactoryVersion=2
diff --git a/kexi/plugins/forms/widgets/Makefile.am b/kexi/plugins/forms/widgets/Makefile.am
new file mode 100644
index 00000000..5ca5cbd8
--- /dev/null
+++ b/kexi/plugins/forms/widgets/Makefile.am
@@ -0,0 +1,28 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+noinst_LTLIBRARIES = libkexiformutilswidgets.la
+
+libkexiformutilswidgets_la_SOURCES = \
+ kexidbutils.cpp \
+ kexidbautofield.cpp \
+ kexidbform.cpp \
+ kexidbsubform.cpp \
+ kexidblabel.cpp \
+ kexidbimagebox.cpp \
+ kexipushbutton.cpp \
+ kexiframe.cpp \
+ kexidblineedit.cpp \
+ kexidbcheckbox.cpp \
+ kexidbtextedit.cpp \
+ kexidbcombobox.cpp
+
+libkexiformutilswidgets_la_LDFLAGS = $(all_libraries) -Wnounresolved
+libkexiformutilswidgets_la_LIBADD =
+
+libkexiformutilswidgets_la_METASOURCES = AUTO
+
+SUBDIRS = .
+
+# set the include path for X, qt and KDE
+INCLUDES= -I$(top_srcdir)/kexi -I$(top_srcdir)/kexi/plugins/forms -I$(top_srcdir)/kexi/core $(all_includes)
+
diff --git a/kexi/plugins/forms/widgets/kexidbautofield.cpp b/kexi/plugins/forms/widgets/kexidbautofield.cpp
new file mode 100644
index 00000000..36fbdb1a
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbautofield.cpp
@@ -0,0 +1,846 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de>
+ Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbautofield.h"
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qmetaobject.h>
+#include <qapplication.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "kexidbcheckbox.h"
+#include "kexidbimagebox.h"
+#include "kexidblabel.h"
+#include "kexidblineedit.h"
+#include "kexidbtextedit.h"
+#include "kexidbcombobox.h"
+#include "kexipushbutton.h"
+#include "kexidbform.h"
+
+#include <kexidb/queryschema.h>
+#include <formeditor/utils.h>
+#include <kexiutils/utils.h>
+
+#define KexiDBAutoField_SPACING 10 //10 pixel for spacing between a label and an editor widget
+
+//! @internal
+class KexiDBAutoField::Private
+{
+ public:
+ Private()
+ {
+ }
+
+ WidgetType widgetType; //!< internal: equal to m_widgetType_property or equal to result
+ //!< of widgetTypeForFieldType() if widgetTypeForFieldType is Auto
+ WidgetType widgetType_property; //!< provides widget type or Auto
+ LabelPosition lblPosition;
+ QBoxLayout *layout;
+ QLabel *label;
+ QString caption;
+ KexiDB::Field::Type fieldTypeInternal;
+ QString fieldCaptionInternal;
+ QColor baseColor; //!< needed because for unbound mode editor==0
+ QColor textColor; //!< needed because for unbound mode editor==0
+ bool autoCaption : 1;
+ bool focusPolicyChanged : 1;
+ bool designMode : 1;
+};
+
+//-------------------------------------
+
+KexiDBAutoField::KexiDBAutoField(const QString &text, WidgetType type, LabelPosition pos,
+ QWidget *parent, const char *name, bool designMode)
+ : QWidget(parent, name)
+ , KexiFormDataItemInterface()
+ , KFormDesigner::DesignTimeDynamicChildWidgetHandler()
+ , d( new Private() )
+{
+ d->designMode = designMode;
+ init(text, type, pos);
+}
+
+KexiDBAutoField::KexiDBAutoField(QWidget *parent, const char *name, bool designMode, LabelPosition pos)
+ : QWidget(parent, name)
+ , KexiFormDataItemInterface()
+ , KFormDesigner::DesignTimeDynamicChildWidgetHandler()
+ , d( new Private() )
+{
+ d->designMode = designMode;
+ init(QString::null/*i18n("Auto Field")*/, Auto, pos);
+}
+
+KexiDBAutoField::~KexiDBAutoField()
+{
+ setUpdatesEnabled(false);
+ if (m_subwidget)
+ m_subwidget->setUpdatesEnabled(false);
+ delete d;
+}
+
+void
+KexiDBAutoField::init(const QString &text, WidgetType type, LabelPosition pos)
+{
+ d->fieldTypeInternal = KexiDB::Field::InvalidType;
+ d->layout = 0;
+ m_subwidget = 0;
+ d->label = new QLabel(text, this);
+ d->label->installEventFilter( this );
+ //QFontMetrics fm( font() );
+ //d->label->setFixedWidth( fm.width("This is a test string length") );
+ d->autoCaption = true;
+ d->focusPolicyChanged = false;
+ d->widgetType = Auto;
+ d->widgetType_property = (type==Auto ? Text : type); //to force "differ" to be true in setWidgetType()
+ setLabelPosition(pos);
+ setWidgetType(type);
+ d->baseColor = palette().active().base();
+ d->textColor = palette().active().text();
+}
+
+void
+KexiDBAutoField::setWidgetType(WidgetType type)
+{
+ const bool differ = (type != d->widgetType_property);
+ d->widgetType_property = type;
+ if(differ) {
+ if(type == Auto) {// try to guess type from data source type
+ if (visibleColumnInfo())
+ d->widgetType = KexiDBAutoField::widgetTypeForFieldType(visibleColumnInfo()->field->type());
+ else
+ d->widgetType = Auto;
+ }
+ else
+ d->widgetType = d->widgetType_property;
+ createEditor();
+ }
+}
+
+void
+KexiDBAutoField::createEditor()
+{
+ if(m_subwidget) {
+ delete (QWidget *)m_subwidget;
+ }
+
+ QWidget *newSubwidget;
+ switch( d->widgetType ) {
+ case Text:
+ case Double: //! @todo setup validator
+ case Integer: //! @todo setup validator
+ case Date:
+ case Time:
+ case DateTime:
+ newSubwidget = new KexiDBLineEdit( this, QCString("KexiDBAutoField_KexiDBLineEdit:")+name() );
+ break;
+ case MultiLineText:
+ newSubwidget = new KexiDBTextEdit( this, QCString("KexiDBAutoField_KexiDBTextEdit:")+name() );
+ break;
+ case Boolean:
+ newSubwidget = new KexiDBCheckBox(dataSource(), this, QCString("KexiDBAutoField_KexiDBCheckBox:")+name());
+ break;
+ case Image:
+ newSubwidget = new KexiDBImageBox(d->designMode, this, QCString("KexiDBAutoField_KexiDBImageBox:")+name());
+ break;
+ case ComboBox:
+ newSubwidget = new KexiDBComboBox(this, QCString("KexiDBAutoField_KexiDBComboBox:")+name(), d->designMode);
+ break;
+ default:
+ newSubwidget = 0;
+ changeText(d->caption);
+ //d->label->setText( d->dataSource.isEmpty() ? "<datasource>" : d->dataSource );
+ break;
+ }
+
+ setSubwidget( newSubwidget ); //this will also allow to declare subproperties, see KFormDesigner::WidgetWithSubpropertiesInterface
+ if(newSubwidget) {
+ newSubwidget->setName( QCString("KexiDBAutoField_") + newSubwidget->className() );
+ dynamic_cast<KexiDataItemInterface*>(newSubwidget)->setParentDataItemInterface(this);
+ dynamic_cast<KexiFormDataItemInterface*>(newSubwidget)
+ ->setColumnInfo(columnInfo()); //needed at least by KexiDBImageBox
+ dynamic_cast<KexiFormDataItemInterface*>(newSubwidget)
+ ->setVisibleColumnInfo(visibleColumnInfo()); //needed at least by KexiDBComboBox
+ newSubwidget->setProperty("dataSource", dataSource()); //needed at least by KexiDBImageBox
+ KFormDesigner::DesignTimeDynamicChildWidgetHandler::childWidgetAdded(this);
+ newSubwidget->show();
+ d->label->setBuddy(newSubwidget);
+ if (d->focusPolicyChanged) {//if focusPolicy is changed at top level, editor inherits it
+ newSubwidget->setFocusPolicy(focusPolicy());
+ }
+ else {//if focusPolicy is not changed at top level, inherit it from editor
+ QWidget::setFocusPolicy(newSubwidget->focusPolicy());
+ }
+ setFocusProxy(newSubwidget); //ok?
+ if (parentWidget())
+ newSubwidget->setPalette( qApp->palette() );
+ copyPropertiesToEditor();
+// KFormDesigner::installRecursiveEventFilter(newSubwidget, this);
+ }
+
+ setLabelPosition(labelPosition());
+}
+
+void KexiDBAutoField::copyPropertiesToEditor()
+{
+ if (m_subwidget) {
+// kdDebug() << "KexiDBAutoField::copyPropertiesToEditor(): base col: " << d->baseColor.name() <<
+// "; text col: " << d->textColor.name() << endl;
+ QPalette p( m_subwidget->palette() );
+ p.setColor( QPalette::Active, QColorGroup::Base, d->baseColor );
+ if(d->widgetType == Boolean)
+ p.setColor( QPalette::Active, QColorGroup::Foreground, d->textColor );
+ else
+ p.setColor( QPalette::Active, QColorGroup::Text, d->textColor );
+ m_subwidget->setPalette(p);
+ //m_subwidget->setPaletteBackgroundColor( d->baseColor );
+ }
+}
+
+void
+KexiDBAutoField::setLabelPosition(LabelPosition position)
+{
+ d->lblPosition = position;
+ if(d->layout) {
+ QBoxLayout *lyr = d->layout;
+ d->layout = 0;
+ delete lyr;
+ }
+
+ if(m_subwidget)
+ m_subwidget->show();
+ //! \todo support right-to-left layout where positions are inverted
+ if (position==Top || position==Left) {
+ int align = d->label->alignment();
+ if(position == Top) {
+ d->layout = (QBoxLayout*) new QVBoxLayout(this);
+ align |= AlignVertical_Mask;
+ align ^= AlignVertical_Mask;
+ align |= AlignTop;
+ }
+ else {
+ d->layout = (QBoxLayout*) new QHBoxLayout(this);
+ align |= AlignVertical_Mask;
+ align ^= AlignVertical_Mask;
+ align |= AlignVCenter;
+ }
+ d->label->setAlignment(align);
+ if(d->widgetType == Boolean
+ || (d->widgetType == Auto && fieldTypeInternal() == KexiDB::Field::InvalidType && !d->designMode))
+ {
+ d->label->hide();
+ }
+ else {
+ d->label->show();
+ }
+ d->layout->addWidget(d->label, 0, position == Top ? int(Qt::AlignLeft) : 0);
+ if(position == Left && d->widgetType != Boolean)
+ d->layout->addSpacing(KexiDBAutoField_SPACING);
+ d->layout->addWidget(m_subwidget, 1);
+ KexiSubwidgetInterface *subwidgetInterface = dynamic_cast<KexiSubwidgetInterface*>((QWidget*)m_subwidget);
+ if (subwidgetInterface) {
+ if (subwidgetInterface->appendStretchRequired(this))
+ d->layout->addStretch(0);
+ if (subwidgetInterface->subwidgetStretchRequired(this)) {
+ QSizePolicy sizePolicy( m_subwidget->sizePolicy() );
+ if(position == Left) {
+ sizePolicy.setHorData( QSizePolicy::Minimum );
+ d->label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ }
+ else {
+ sizePolicy.setVerData( QSizePolicy::Minimum );
+ d->label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ }
+ m_subwidget->setSizePolicy(sizePolicy);
+ }
+ }
+// if(m_subwidget)
+ // m_subwidget->setSizePolicy(...);
+ }
+ else {
+ d->layout = (QBoxLayout*) new QHBoxLayout(this);
+ d->label->hide();
+ d->layout->addWidget(m_subwidget);
+ }
+ //a hack to force layout to be refreshed (any better idea for this?)
+ resize(size()+QSize(1,0));
+ resize(size()-QSize(1,0));
+ if (dynamic_cast<KexiDBAutoField*>((QWidget*)m_subwidget)) {
+ //needed for KexiDBComboBox
+ dynamic_cast<KexiDBAutoField*>((QWidget*)m_subwidget)->setLabelPosition(position);
+ }
+}
+
+void
+KexiDBAutoField::setInvalidState( const QString &text )
+{
+ // Widget with an invalid dataSource is just a QLabel
+ if (d->designMode)
+ return;
+ d->widgetType = Auto;
+ createEditor();
+ setFocusPolicy(QWidget::NoFocus);
+ if (m_subwidget)
+ m_subwidget->setFocusPolicy(QWidget::NoFocus);
+//! @todo or set this to editor's text?
+ d->label->setText( text );
+}
+
+bool
+KexiDBAutoField::isReadOnly() const
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ return iface->isReadOnly();
+ else
+ return false;
+}
+
+void
+KexiDBAutoField::setReadOnly( bool readOnly )
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ iface->setReadOnly(readOnly);
+}
+
+void
+KexiDBAutoField::setValueInternal(const QVariant& add, bool removeOld)
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ iface->setValue(m_origValue, add, removeOld);
+// iface->setValueInternal(add, removeOld);
+}
+
+QVariant
+KexiDBAutoField::value()
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ return iface->value();
+ return QVariant();
+}
+
+bool
+KexiDBAutoField::valueIsNull()
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ return iface->valueIsNull();
+ return true;
+}
+
+bool
+KexiDBAutoField::valueIsEmpty()
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ return iface->valueIsEmpty();
+ return true;
+}
+
+bool
+KexiDBAutoField::valueIsValid()
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ return iface->valueIsValid();
+ return true;
+}
+
+bool
+KexiDBAutoField::valueChanged()
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ kexipluginsdbg << m_origValue << endl;
+ if(iface)
+ return iface->valueChanged();
+ return false;
+}
+
+void
+KexiDBAutoField::installListener(KexiDataItemChangesListener* listener)
+{
+ KexiFormDataItemInterface::installListener(listener);
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ iface->installListener(listener);
+}
+
+KexiDBAutoField::WidgetType KexiDBAutoField::widgetType() const
+{
+ return d->widgetType_property;
+}
+
+KexiDBAutoField::LabelPosition KexiDBAutoField::labelPosition() const
+{
+ return d->lblPosition;
+}
+
+QString KexiDBAutoField::caption() const
+{
+ return d->caption;
+}
+
+bool KexiDBAutoField::hasAutoCaption() const
+{
+ return d->autoCaption;
+}
+
+QWidget* KexiDBAutoField::editor() const
+{
+ return m_subwidget;
+}
+
+QLabel* KexiDBAutoField::label() const
+{
+ return d->label;
+}
+
+int KexiDBAutoField::fieldTypeInternal() const
+{
+ return d->fieldTypeInternal;
+}
+
+QString KexiDBAutoField::fieldCaptionInternal() const
+{
+ return d->fieldCaptionInternal;
+}
+
+bool
+KexiDBAutoField::cursorAtStart()
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ return iface->cursorAtStart();
+ return false;
+}
+
+bool
+KexiDBAutoField::cursorAtEnd()
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ return iface->cursorAtEnd();
+ return false;
+}
+
+void
+KexiDBAutoField::clear()
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ iface->clear();
+}
+
+void
+KexiDBAutoField::setFieldTypeInternal(int kexiDBFieldType)
+{
+ d->fieldTypeInternal = (KexiDB::Field::Type)kexiDBFieldType;
+ KexiDB::Field::Type fieldType;
+ //find real fied type to use
+ if (d->fieldTypeInternal==KexiDB::Field::InvalidType) {
+ if (visibleColumnInfo())
+ fieldType = KexiDB::Field::Text;
+ else
+ fieldType = KexiDB::Field::InvalidType;
+ }
+ else
+ fieldType = d->fieldTypeInternal;
+
+ const WidgetType newWidgetType = KexiDBAutoField::widgetTypeForFieldType( fieldType );
+
+ if(d->widgetType != newWidgetType) {
+ d->widgetType = newWidgetType;
+ createEditor();
+ }
+ setFieldCaptionInternal(d->fieldCaptionInternal);
+}
+
+void
+KexiDBAutoField::setFieldCaptionInternal(const QString& text)
+{
+ d->fieldCaptionInternal = text;
+ //change text only if autocaption is set and no columnInfo is available
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if((!iface || !iface->columnInfo()) && d->autoCaption) {
+ changeText(d->fieldCaptionInternal);
+ }
+}
+
+void
+KexiDBAutoField::setColumnInfo(KexiDB::QueryColumnInfo* cinfo)
+{
+ KexiFormDataItemInterface::setColumnInfo(cinfo);
+ setColumnInfoInternal(cinfo, cinfo);
+}
+
+void
+KexiDBAutoField::setColumnInfoInternal(KexiDB::QueryColumnInfo* cinfo, KexiDB::QueryColumnInfo* visibleColumnInfo)
+{
+ // change widget type depending on field type
+ if(d->widgetType_property == Auto) {
+ WidgetType newWidgetType = Auto;
+ KexiDB::Field::Type fieldType;
+ if (cinfo)
+ fieldType = visibleColumnInfo->field->type();
+ else if (dataSource().isEmpty())
+ fieldType = KexiDB::Field::InvalidType;
+ else
+ fieldType = KexiDB::Field::Text;
+
+ if (fieldType != KexiDB::Field::InvalidType) {
+ newWidgetType = KexiDBAutoField::widgetTypeForFieldType( fieldType );
+ }
+ if(d->widgetType != newWidgetType || newWidgetType==Auto) {
+ d->widgetType = newWidgetType;
+ createEditor();
+ }
+ }
+ // update label's text
+ changeText((cinfo && d->autoCaption) ? cinfo->captionOrAliasOrName() : d->caption);
+
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ iface->setColumnInfo(visibleColumnInfo);
+}
+
+//static
+KexiDBAutoField::WidgetType
+KexiDBAutoField::widgetTypeForFieldType(KexiDB::Field::Type type)
+{
+ switch(type) {
+ case KexiDB::Field::Integer:
+ case KexiDB::Field::ShortInteger:
+ case KexiDB::Field::BigInteger:
+ return Integer;
+ case KexiDB::Field::Boolean:
+ return Boolean;
+ case KexiDB::Field::Float:
+ case KexiDB::Field::Double:
+ return Double;
+ case KexiDB::Field::Date:
+ return Date;
+ case KexiDB::Field::DateTime:
+ return DateTime;
+ case KexiDB::Field::Time:
+ return Time;
+ case KexiDB::Field::Text:
+ return Text;
+ case KexiDB::Field::LongText:
+ return MultiLineText;
+ case KexiDB::Field::Enum:
+ return ComboBox;
+ case KexiDB::Field::InvalidType:
+ return Auto;
+ case KexiDB::Field::BLOB:
+ return Image;
+ default:
+ break;
+ }
+ return Text;
+}
+
+void
+KexiDBAutoField::changeText(const QString &text, bool beautify)
+{
+ QString realText;
+ bool unbound = false;
+ if (d->autoCaption && (d->widgetType==Auto || dataSource().isEmpty())) {
+ if (d->designMode)
+ realText = QString::fromLatin1(name())+" "+i18n("Unbound Auto Field", "(unbound)");
+ else
+ realText = QString::null;
+ unbound = true;
+ }
+ else {
+ if (beautify) {
+ /*! @todo look at appendColonToAutoLabels setting [bool]
+ @todo look at makeFirstCharacterUpperCaseInCaptions setting [bool]
+ (see doc/dev/settings.txt) */
+ if (!text.isEmpty()) {
+ realText = text[0].upper() + text.mid(1);
+ if (d->widgetType!=Boolean) {
+//! @todo ":" suffix looks weird for checkbox; remove this condition when [x] is displayed _after_ label
+//! @todo support right-to-left layout where position of ":" is inverted
+ realText += ": ";
+ }
+ }
+ }
+ else
+ realText = text;
+ }
+
+ if (unbound)
+ d->label->setAlignment( Qt::AlignCenter | Qt::WordBreak );
+ else
+ d->label->setAlignment( Qt::AlignCenter );
+// QWidget* widgetToAlterForegroundColor;
+ if(d->widgetType == Boolean) {
+ static_cast<QCheckBox*>((QWidget*)m_subwidget)->setText(realText);
+// widgetToAlterForegroundColor = m_subwidget;
+ }
+ else {
+ d->label->setText(realText);
+// widgetToAlterForegroundColor = d->label;
+ }
+/*
+ if (unbound)
+ widgetToAlterForegroundColor->setPaletteForegroundColor(
+ KexiUtils::blendedColors(
+ widgetToAlterForegroundColor->paletteForegroundColor(),
+ widgetToAlterForegroundColor->paletteBackgroundColor(), 2, 1));
+ else
+ widgetToAlterForegroundColor->setPaletteForegroundColor( paletteForegroundColor() );*/
+}
+
+void
+KexiDBAutoField::setCaption(const QString &caption)
+{
+ d->caption = caption;
+ if(!d->autoCaption && !caption.isEmpty())
+ changeText(d->caption);
+}
+
+void
+KexiDBAutoField::setAutoCaption(bool autoCaption)
+{
+ d->autoCaption = autoCaption;
+ if(d->autoCaption) {
+ //d->caption = QString::null;
+ if(columnInfo()) {
+ changeText(columnInfo()->captionOrAliasOrName());
+ }
+ else {
+ changeText(d->fieldCaptionInternal);
+ }
+ }
+ else
+ changeText(d->caption);
+}
+
+void
+KexiDBAutoField::setDataSource( const QString &ds ) {
+ KexiFormDataItemInterface::setDataSource(ds);
+ if (ds.isEmpty()) {
+ setColumnInfo(0);
+ }
+}
+
+QSize
+KexiDBAutoField::sizeHint() const
+{
+ if (d->lblPosition == NoLabel)
+ return m_subwidget ? m_subwidget->sizeHint() : QWidget::sizeHint();
+
+ QSize s1(0,0);
+ if (m_subwidget)
+ s1 = m_subwidget->sizeHint();
+ QSize s2(d->label->sizeHint());
+ if (d->lblPosition == Top)
+ return QSize(QMAX(s1.width(), s2.width()), s1.height()+KexiDBAutoField_SPACING+s2.height());
+
+ //left
+ return QSize(s1.width()+KexiDBAutoField_SPACING+s2.width(), QMAX(s1.height(), s2.height()));
+}
+
+void
+KexiDBAutoField::setFocusPolicy( FocusPolicy policy )
+{
+ d->focusPolicyChanged = true;
+ QWidget::setFocusPolicy(policy);
+ d->label->setFocusPolicy(policy);
+ if (m_subwidget)
+ m_subwidget->setFocusPolicy(policy);
+}
+
+void
+KexiDBAutoField::updateInformationAboutUnboundField()
+{
+ if ( (d->autoCaption && (dataSource().isEmpty() || dataSourceMimeType().isEmpty()))
+ || (!d->autoCaption && d->caption.isEmpty()) )
+ {
+ d->label->setText( QString::fromLatin1(name())+" "+i18n("Unbound Auto Field", " (unbound)") );
+ }
+// else
+// d->label->setText( QString::fromLatin1(name())+" "+i18n(" (unbound)") );
+}
+
+/*void
+KexiDBAutoField::paintEvent( QPaintEvent* pe )
+{
+ QWidget::paintEvent( pe );
+
+ if ( (d->autoCaption && (dataSource().isEmpty() || dataSourceMimeType().isEmpty()))
+ || (!d->autoCaption && d->caption.isEmpty()) )
+ {
+ QPainter p(this);
+ p.setPen( d->label->paletteForegroundColor() );
+ p.setClipRect(pe->rect());
+ p.setFont(d->label->font());
+ p.drawText(rect(), Qt::AlignLeft | Qt::WordBreak,
+ QString::fromLatin1(name())+" "+i18n(" (unbound)"));
+ }
+}*/
+
+void
+KexiDBAutoField::paletteChange( const QPalette& oldPal )
+{
+ Q_UNUSED(oldPal);
+ d->label->setPalette( palette() );
+}
+
+void KexiDBAutoField::unsetPalette()
+{
+ QWidget::unsetPalette();
+
+}
+
+// ===== methods below are just proxies for the internal editor or label =====
+
+const QColor & KexiDBAutoField::paletteForegroundColor() const
+{
+ return d->textColor;
+}
+
+void KexiDBAutoField::setPaletteForegroundColor( const QColor & color )
+{
+ d->textColor = color;
+ copyPropertiesToEditor();
+}
+
+const QColor & KexiDBAutoField::paletteBackgroundColor() const
+{
+ return d->baseColor;
+}
+
+void KexiDBAutoField::setPaletteBackgroundColor( const QColor & color )
+{
+ d->baseColor = color;
+ copyPropertiesToEditor();
+}
+
+const QColor & KexiDBAutoField::foregroundLabelColor() const
+{
+ if(d->widgetType == Boolean)
+ return paletteForegroundColor();
+
+ return d->label->paletteForegroundColor();
+}
+
+void KexiDBAutoField::setForegroundLabelColor( const QColor & color )
+{
+ if(d->widgetType == Boolean)
+ setPaletteForegroundColor(color);
+ else {
+ d->label->setPaletteForegroundColor(color);
+ QWidget::setPaletteForegroundColor(color);
+ }
+}
+
+const QColor & KexiDBAutoField::backgroundLabelColor() const
+{
+ if(d->widgetType == Boolean)
+ return paletteBackgroundColor();
+
+ return d->label->paletteBackgroundColor();
+}
+
+void KexiDBAutoField::setBackgroundLabelColor( const QColor & color )
+{
+ if(d->widgetType == Boolean)
+ setPaletteBackgroundColor(color);
+ else {
+ d->label->setPaletteBackgroundColor(color);
+ QWidget::setPaletteBackgroundColor(color);
+ }
+
+// if (m_subwidget)
+// m_subwidget->setPalette( qApp->palette() );
+}
+
+QVariant KexiDBAutoField::property( const char * name ) const
+{
+ bool ok;
+ QVariant val = KFormDesigner::WidgetWithSubpropertiesInterface::subproperty(name, ok);
+ if (ok)
+ return val;
+ return QWidget::property(name);
+}
+
+bool KexiDBAutoField::setProperty( const char * name, const QVariant & value )
+{
+ bool ok = KFormDesigner::WidgetWithSubpropertiesInterface::setSubproperty(name, value);
+ if (ok)
+ return true;
+ return QWidget::setProperty(name, value);
+}
+
+bool KexiDBAutoField::eventFilter( QObject *o, QEvent *e )
+{
+ if (o==d->label && d->label->buddy() && e->type()==QEvent::MouseButtonRelease) {
+ //focus label's buddy when user clicked the label
+ d->label->buddy()->setFocus();
+ }
+ return QWidget::eventFilter(o, e);
+}
+
+void KexiDBAutoField::setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue)
+{
+ KexiFormDataItemInterface::setDisplayDefaultValue(widget, displayDefaultValue);
+ if (dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget))
+ dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget)->setDisplayDefaultValue(m_subwidget, displayDefaultValue);
+}
+
+void KexiDBAutoField::moveCursorToEnd()
+{
+ KexiDataItemInterface *iface = dynamic_cast<KexiDataItemInterface*>((QWidget*)m_subwidget);
+ if (iface)
+ iface->moveCursorToEnd();
+}
+
+void KexiDBAutoField::moveCursorToStart()
+{
+ KexiDataItemInterface *iface = dynamic_cast<KexiDataItemInterface*>((QWidget*)m_subwidget);
+ if (iface)
+ iface->moveCursorToStart();
+}
+
+void KexiDBAutoField::selectAll()
+{
+ KexiDataItemInterface *iface = dynamic_cast<KexiDataItemInterface*>((QWidget*)m_subwidget);
+ if (iface)
+ iface->selectAll();
+}
+
+bool KexiDBAutoField::keyPressed(QKeyEvent *ke)
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if (iface && iface->keyPressed(ke))
+ return true;
+ return false;
+}
+
+#include "kexidbautofield.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbautofield.h b/kexi/plugins/forms/widgets/kexidbautofield.h
new file mode 100644
index 00000000..981a0519
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbautofield.h
@@ -0,0 +1,210 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de>
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIDBAUTOFIELD_H
+#define KEXIDBAUTOFIELD_H
+
+#include <qwidget.h>
+#include <kexidb/field.h>
+#include <formeditor/container.h>
+#include <formeditor/widgetwithsubpropertiesinterface.h>
+#include "kexiformdataiteminterface.h"
+
+class QBoxLayout;
+class QLabel;
+
+//! Universal "Auto Field" widget for Kexi forms
+/*! It acts as a container for most data-aware widgets. */
+class KEXIFORMUTILS_EXPORT KexiDBAutoField :
+ public QWidget,
+ public KexiFormDataItemInterface,
+ public KFormDesigner::DesignTimeDynamicChildWidgetHandler,
+ public KFormDesigner::WidgetWithSubpropertiesInterface
+{
+ Q_OBJECT
+//'caption' is uncovered now Q_PROPERTY(QString labelCaption READ caption WRITE setCaption DESIGNABLE true)
+ Q_OVERRIDE(QString caption READ caption WRITE setCaption DESIGNABLE true)
+ Q_OVERRIDE(QColor paletteForegroundColor READ paletteForegroundColor WRITE setPaletteForegroundColor DESIGNABLE true RESET unsetPalette)
+ Q_OVERRIDE(QColor paletteBackgroundColor READ paletteBackgroundColor WRITE setPaletteBackgroundColor DESIGNABLE true RESET unsetPalette)
+ Q_PROPERTY(QColor foregroundLabelColor READ foregroundLabelColor WRITE setForegroundLabelColor DESIGNABLE true RESET unsetPalette)
+ Q_PROPERTY(QColor backgroundLabelColor READ backgroundLabelColor WRITE setBackgroundLabelColor DESIGNABLE true RESET unsetPalette)
+ Q_PROPERTY(bool autoCaption READ hasAutoCaption WRITE setAutoCaption DESIGNABLE true)
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+ Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
+ Q_PROPERTY(LabelPosition labelPosition READ labelPosition WRITE setLabelPosition DESIGNABLE true)
+ Q_PROPERTY(WidgetType widgetType READ widgetType WRITE setWidgetType DESIGNABLE true)
+ /*internal, for design time only*/
+ Q_PROPERTY(int fieldTypeInternal READ fieldTypeInternal WRITE setFieldTypeInternal DESIGNABLE true STORED false)
+ Q_PROPERTY(QString fieldCaptionInternal READ fieldCaptionInternal WRITE setFieldCaptionInternal DESIGNABLE true STORED false)
+ Q_ENUMS( WidgetType LabelPosition )
+
+ public:
+ enum WidgetType { Auto = 100, Text, Integer, Double, Boolean, Date, Time, DateTime,
+ MultiLineText, ComboBox, Image };
+ enum LabelPosition { Left = 300, Top, NoLabel };
+
+ KexiDBAutoField(const QString &text, WidgetType type, LabelPosition pos,
+ QWidget *parent = 0, const char *name = 0, bool designMode = true);
+ KexiDBAutoField(QWidget *parent = 0, const char *name = 0, bool designMode = true,
+ LabelPosition pos = Left);
+
+ virtual ~KexiDBAutoField();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+ virtual void setDataSource( const QString &ds );
+ virtual void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+ virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo);
+
+ virtual void setInvalidState(const QString& text);
+ virtual bool isReadOnly() const;
+ virtual void setReadOnly( bool readOnly );
+
+ virtual QVariant value();
+ virtual bool valueIsNull();
+ virtual bool valueIsEmpty();
+ virtual bool valueIsValid();
+ virtual bool valueChanged();
+ virtual void clear();
+
+ //! Reimpelmented to also install \a listenter for internal editor
+ virtual void installListener(KexiDataItemChangesListener* listener);
+
+ WidgetType widgetType() const;
+ void setWidgetType(WidgetType type);
+
+ LabelPosition labelPosition() const;
+ virtual void setLabelPosition(LabelPosition position);
+
+ QString caption() const;
+ void setCaption(const QString &caption);
+
+ bool hasAutoCaption() const;
+ void setAutoCaption(bool autoCaption);
+
+ /*! If \a displayDefaultValue is true, the value set by KexiDataItemInterface::setValue()
+ is displayed in a special way. Used by KexiFormDataProvider::fillDataItems().
+ \a widget is equal to 'this'.
+ Reimplemented after KexiFormDataItemInterface. */
+ virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue);
+
+ QWidget* editor() const;
+ QLabel* label() const;
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+
+ static WidgetType widgetTypeForFieldType(KexiDB::Field::Type type);
+
+ /*! On design time it is not possible to pass a reference to KexiDB::Field object
+ so we're just providing field type. Only used when widget type is Auto.
+ @internal */
+ void setFieldTypeInternal(int kexiDBFieldType);
+
+ /*! On design time it is not possible to pass a reference to KexiDB::Field object
+ so we're just providing field caption. Only used when widget type is Auto.
+ @internal */
+ void setFieldCaptionInternal(const QString& text);
+
+ /*! @internal */
+ int fieldTypeInternal() const;
+
+ /*! @internal */
+ QString fieldCaptionInternal() const;
+
+ virtual QSize sizeHint() const;
+ virtual void setFocusPolicy ( FocusPolicy policy );
+
+ //! Reimplemented to return internal editor's color.
+ const QColor & paletteForegroundColor() const;
+
+ //! Reimplemented to set internal editor's color.
+ void setPaletteForegroundColor( const QColor & color );
+
+ //! Reimplemented to return internal editor's color.
+ const QColor & paletteBackgroundColor() const;
+
+ //! Reimplemented to set internal editor's color.
+ virtual void setPaletteBackgroundColor( const QColor & color );
+
+ //! \return label's foreground color
+ const QColor & foregroundLabelColor() const;
+
+ //! Sets label's foreground color
+ virtual void setForegroundLabelColor( const QColor & color );
+
+ //! \return label's background color
+ const QColor & backgroundLabelColor() const;
+
+ //! Sets label's background color
+ virtual void setBackgroundLabelColor( const QColor & color );
+
+ //! Reimplemented to accept subproperties. @see KFormDesigner::WidgetWithSubpropertiesInterface
+ virtual QVariant property( const char * name ) const;
+
+ //! Reimplemented to accept subproperties. @see KFormDesigner::WidgetWithSubpropertiesInterface
+ virtual bool setProperty( const char * name, const QVariant & value );
+
+ /*! Called by the top-level form on key press event to consume widget-specific shortcuts. */
+ virtual bool keyPressed(QKeyEvent *ke);
+
+ public slots:
+ virtual void unsetPalette();
+
+ protected slots:
+// void slotValueChanged();
+ virtual void paletteChange( const QPalette& oldPal );
+
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToEnd();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToStart();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void selectAll();
+
+ protected:
+ virtual void setValueInternal(const QVariant&add, bool removeOld);
+ void init(const QString &text, WidgetType type, LabelPosition pos);
+ virtual void createEditor();
+ void changeText(const QString &text, bool beautify = true);
+// virtual void paintEvent( QPaintEvent* pe );
+ void updateInformationAboutUnboundField();
+
+ //! internal editor can be created too late, so certain properties should be copied
+ void copyPropertiesToEditor();
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ //! Used by @ref setLabelPositionInternal(LabelPosition)
+ void setLabelPositionInternal(LabelPosition position, bool noLabel);
+
+ //! Used by KexiDBAutoField::setColumnInfo() and KexiDBComboBox::setColumnInfo()
+ void setColumnInfoInternal(KexiDB::QueryColumnInfo* cinfo, KexiDB::QueryColumnInfo* visibleColumnInfo);
+
+ private:
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbcheckbox.cpp b/kexi/plugins/forms/widgets/kexidbcheckbox.cpp
new file mode 100644
index 00000000..6b63851a
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbcheckbox.cpp
@@ -0,0 +1,175 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbcheckbox.h"
+
+#include <kexiutils/utils.h>
+#include <kexidb/queryschema.h>
+
+KexiDBCheckBox::KexiDBCheckBox(const QString &text, QWidget *parent, const char *name)
+ : QCheckBox(text, parent, name), KexiFormDataItemInterface()
+ , m_invalidState(false)
+ , m_tristateChanged(false)
+ , m_tristate(TristateDefault)
+{
+ setFocusPolicy(QWidget::StrongFocus);
+ updateTristate();
+ connect(this, SIGNAL(stateChanged(int)), this, SLOT(slotStateChanged(int)));
+}
+
+KexiDBCheckBox::~KexiDBCheckBox()
+{
+}
+
+void KexiDBCheckBox::setInvalidState( const QString& displayText )
+{
+ setEnabled(false);
+ setState(NoChange);
+ m_invalidState = true;
+//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ?
+ if (focusPolicy() & TabFocus)
+ setFocusPolicy(QWidget::ClickFocus);
+ setText(displayText);
+}
+
+void
+KexiDBCheckBox::setEnabled(bool enabled)
+{
+ if(enabled && m_invalidState)
+ return;
+ QCheckBox::setEnabled(enabled);
+}
+
+void
+KexiDBCheckBox::setReadOnly(bool readOnly)
+{
+ setEnabled(!readOnly);
+}
+
+void KexiDBCheckBox::setValueInternal(const QVariant &add, bool removeOld)
+{
+ Q_UNUSED(add);
+ Q_UNUSED(removeOld);
+ if (isTristateInternal())
+ setState( m_origValue.isNull() ? NoChange : (m_origValue.toBool() ? On : Off) );
+ else
+ setState( m_origValue.toBool() ? On : Off );
+}
+
+QVariant
+KexiDBCheckBox::value()
+{
+ if (state()==NoChange)
+ return QVariant();
+ return QVariant(state()==On, 1);
+}
+
+void KexiDBCheckBox::slotStateChanged(int )
+{
+ signalValueChanged();
+}
+
+bool KexiDBCheckBox::valueIsNull()
+{
+ return state() == NoChange;
+}
+
+bool KexiDBCheckBox::valueIsEmpty()
+{
+ return false;
+}
+
+bool KexiDBCheckBox::isReadOnly() const
+{
+ return !isEnabled();
+}
+
+QWidget*
+KexiDBCheckBox::widget()
+{
+ return this;
+}
+
+bool KexiDBCheckBox::cursorAtStart()
+{
+ return false; //! \todo ?
+}
+
+bool KexiDBCheckBox::cursorAtEnd()
+{
+ return false; //! \todo ?
+}
+
+void KexiDBCheckBox::clear()
+{
+ setState(NoChange);
+}
+
+void KexiDBCheckBox::setTristate(KexiDBCheckBox::Tristate tristate)
+{
+ m_tristateChanged = true;
+ m_tristate = tristate;
+ updateTristate();
+}
+
+KexiDBCheckBox::Tristate KexiDBCheckBox::isTristate() const
+{
+ return m_tristate;
+}
+
+bool KexiDBCheckBox::isTristateInternal() const
+{
+ if (m_tristate == TristateDefault)
+ return !dataSource().isEmpty();
+
+ return m_tristate == TristateOn;
+}
+
+void KexiDBCheckBox::updateTristate()
+{
+ if (m_tristate == TristateDefault) {
+//! @todo the data source may be defined as NOT NULL... thus disallowing NULL state
+ QCheckBox::setTristate( !dataSource().isEmpty() );
+ }
+ else {
+ QCheckBox::setTristate( m_tristate == TristateOn );
+ }
+}
+
+void KexiDBCheckBox::setDataSource(const QString &ds)
+{
+ KexiFormDataItemInterface::setDataSource(ds);
+ updateTristate();
+}
+
+void KexiDBCheckBox::setDisplayDefaultValue(QWidget *widget, bool displayDefaultValue)
+{
+ KexiFormDataItemInterface::setDisplayDefaultValue(widget, displayDefaultValue);
+ // initialize display parameters for default / entered value
+ KexiDisplayUtils::DisplayParameters * const params
+ = displayDefaultValue ? m_displayParametersForDefaultValue : m_displayParametersForEnteredValue;
+// setFont(params->font);
+ QPalette pal(palette());
+// pal.setColor(QPalette::Active, QColorGroup::Text, params->textColor);
+ pal.setColor(QPalette::Active, QColorGroup::Foreground, params->textColor);
+ setPalette(pal);
+}
+
+#include "kexidbcheckbox.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbcheckbox.h b/kexi/plugins/forms/widgets/kexidbcheckbox.h
new file mode 100644
index 00000000..d4a68bf3
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbcheckbox.h
@@ -0,0 +1,99 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBCheckBox_H
+#define KexiDBCheckBox_H
+
+#include "kexiformdataiteminterface.h"
+#include <qcheckbox.h>
+
+//! @short A db-aware check box
+class KEXIFORMUTILS_EXPORT KexiDBCheckBox : public QCheckBox, public KexiFormDataItemInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+ Q_OVERRIDE( Tristate tristate READ isTristate WRITE setTristate )
+ Q_ENUMS( Tristate )
+
+ public:
+ KexiDBCheckBox(const QString &text, QWidget *parent, const char *name=0);
+ virtual ~KexiDBCheckBox();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+ virtual QVariant value();
+ virtual void setInvalidState( const QString& displayText );
+
+ //! \return true if editor's value is null (not empty)
+ //! Used for checking if a given constraint within table of form is met.
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not necessary null).
+ //! Only few data types can accept "EMPTY" property
+ //! (use KexiDB::Field::hasEmptyProperty() to check this).
+ //! Used for checking if a given constraint within table or form is met.
+ virtual bool valueIsEmpty();
+
+ /*! \return 'readOnly' flag for this widget. */
+ virtual bool isReadOnly() const;
+
+ /*! \return the view widget of this item, e.g. line edit widget. */
+ virtual QWidget* widget();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+ virtual void clear();
+
+ virtual void setEnabled(bool enabled);
+
+ enum Tristate { TristateDefault, TristateOn, TristateOff };
+
+ void setTristate(Tristate tristate);
+ Tristate isTristate() const;
+
+ /*! Reimplemented after KexiFormDataItemInterface. */
+ virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue);
+
+ public slots:
+ void setDataSource(const QString &ds);
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+ void slotStateChanged(int state);
+
+ //! This implementation just disables read only widget
+ virtual void setReadOnly( bool readOnly );
+
+ protected:
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ //! \return true in isTristate() == TristateDefault and the widget has bound data source
+ //! or if isTristate() == TristateOn, else false is returned.
+ bool isTristateInternal() const;
+
+ //! Updates tristate in QCheckBox itself according to m_tristate.
+ void updateTristate();
+
+ private:
+ bool m_invalidState : 1;
+ bool m_tristateChanged : 1; //!< used in setTristate()
+ Tristate m_tristate; //!< used in isTristate() and setTristate()
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbcombobox.cpp b/kexi/plugins/forms/widgets/kexidbcombobox.cpp
new file mode 100644
index 00000000..19366a15
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbcombobox.cpp
@@ -0,0 +1,550 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbcombobox.h"
+#include "kexidblineedit.h"
+#include "../kexiformscrollview.h"
+
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kapplication.h>
+
+#include <qmetaobject.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qdrawutil.h>
+#include <qptrdict.h>
+#include <qcursor.h>
+
+#include <kexidb/queryschema.h>
+#include <widget/tableview/kexicomboboxpopup.h>
+#include <widget/tableview/kexicelleditorfactory.h>
+#include <kexiutils/utils.h>
+
+//! @internal
+class KexiDBComboBox::Private
+{
+ public:
+ Private()
+ : popup(0)
+ , visibleColumnInfo(0)
+ , subWidgetsWithDisabledEvents(0)
+ , isEditable(false)
+ , buttonPressed(false)
+ , mouseOver(false)
+ , dataEnteredByHand(true)
+ {
+ }
+ ~Private()
+ {
+ delete subWidgetsWithDisabledEvents;
+ subWidgetsWithDisabledEvents = 0;
+ }
+
+ KexiComboBoxPopup *popup;
+ KComboBox *paintedCombo; //!< fake combo used only to pass it as 'this' for QStyle (because styles use <static_cast>)
+ QSize sizeHint; //!< A cache for KexiDBComboBox::sizeHint(),
+ //!< rebuilt by KexiDBComboBox::fontChange() and KexiDBComboBox::styleChange()
+ KexiDB::QueryColumnInfo* visibleColumnInfo;
+ QPtrDict<char> *subWidgetsWithDisabledEvents; //! used to collect subwidget and its children (if isEditable is false)
+ bool isEditable : 1; //!< true is the combo box is editable
+ bool buttonPressed : 1;
+ bool mouseOver : 1;
+ bool dataEnteredByHand : 1;
+ bool designMode : 1;
+};
+
+//-------------------------------------
+
+KexiDBComboBox::KexiDBComboBox(QWidget *parent, const char *name, bool designMode)
+ : KexiDBAutoField(parent, name, designMode, NoLabel)
+ , KexiComboBoxBase()
+ , d(new Private())
+{
+ setMouseTracking(true);
+ setFocusPolicy(WheelFocus);
+ installEventFilter(this);
+ d->designMode = designMode;
+ d->paintedCombo = new KComboBox(this);
+ d->paintedCombo->hide();
+ d->paintedCombo->move(0,0);
+}
+
+KexiDBComboBox::~KexiDBComboBox()
+{
+ delete d;
+}
+
+KexiComboBoxPopup *KexiDBComboBox::popup() const
+{
+ return d->popup;
+}
+
+void KexiDBComboBox::setPopup(KexiComboBoxPopup *popup)
+{
+ d->popup = popup;
+}
+
+void KexiDBComboBox::setEditable(bool set)
+{
+ if (d->isEditable == set)
+ return;
+ d->isEditable = set;
+ d->paintedCombo->setEditable(set);
+ if (set)
+ createEditor();
+ else {
+ delete m_subwidget;
+ m_subwidget = 0;
+ }
+ update();
+}
+
+bool KexiDBComboBox::isEditable() const
+{
+ return d->isEditable;
+}
+
+void KexiDBComboBox::paintEvent( QPaintEvent * )
+{
+ QPainter p( this );
+ QColorGroup cg( palette().active() );
+// if ( hasFocus() )
+// cg.setColor(QColorGroup::Base, cg.highlight());
+// else
+ cg.setColor(QColorGroup::Base, paletteBackgroundColor()); //update base color using (reimplemented) bg color
+ p.setPen(cg.text());
+
+ QStyle::SFlags flags = QStyle::Style_Default;
+ if (isEnabled())
+ flags |= QStyle::Style_Enabled;
+ if (hasFocus())
+ flags |= QStyle::Style_HasFocus;
+ if (d->mouseOver)
+ flags |= QStyle::Style_MouseOver;
+
+ if ( width() < 5 || height() < 5 ) {
+ qDrawShadePanel( &p, rect(), cg, FALSE, 2, &cg.brush( QColorGroup::Button ) );
+ return;
+ }
+
+//! @todo support reverse layout
+//bool reverse = QApplication::reverseLayout();
+ style().drawComplexControl( QStyle::CC_ComboBox, &p, d->paintedCombo /*this*/, rect(), cg,
+ flags, (uint)QStyle::SC_All,
+ (d->buttonPressed ? QStyle::SC_ComboBoxArrow : QStyle::SC_None )
+ );
+
+ if (d->isEditable) {
+ //if editable, editor paints itself, nothing to do
+ }
+ else { //not editable: we need to paint the current item
+ QRect editorGeometry( this->editorGeometry() );
+ if ( hasFocus() ) {
+ if (0==qstrcmp(style().name(), "windows")) //a hack
+ p.fillRect( editorGeometry, cg.brush( QColorGroup::Highlight ) );
+ QRect r( QStyle::visualRect( style().subRect( QStyle::SR_ComboBoxFocusRect, d->paintedCombo ), this ) );
+ r = QRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2); //enlare by 1 pixel each side to avoid covering by the subwidget
+ style().drawPrimitive( QStyle::PE_FocusRect, &p,
+ r, cg, flags | QStyle::Style_FocusAtBorder, QStyleOption(cg.highlight()));
+ }
+ //todo
+ }
+}
+
+QRect KexiDBComboBox::editorGeometry() const
+{
+ QRect r( QStyle::visualRect(
+ style().querySubControlMetrics(QStyle::CC_ComboBox, d->paintedCombo,
+ QStyle::SC_ComboBoxEditField), d->paintedCombo ) );
+
+ //if ((height()-r.bottom())<6)
+ // r.setBottom(height()-6);
+ return r;
+}
+
+void KexiDBComboBox::createEditor()
+{
+ KexiDBAutoField::createEditor();
+ if (m_subwidget) {
+ m_subwidget->setGeometry( editorGeometry() );
+ if (!d->isEditable) {
+ m_subwidget->setCursor(QCursor(Qt::ArrowCursor)); // widgets like listedit have IbeamCursor, we don't want that
+//! @todo Qt4: set transparent background, for now we're setting button color
+ QPalette subwidgetPalette( m_subwidget->palette() );
+ subwidgetPalette.setColor(QPalette::Active, QColorGroup::Base,
+ subwidgetPalette.color(QPalette::Active, QColorGroup::Button));
+ m_subwidget->setPalette( subwidgetPalette );
+ if (d->subWidgetsWithDisabledEvents)
+ d->subWidgetsWithDisabledEvents->clear();
+ else
+ d->subWidgetsWithDisabledEvents = new QPtrDict<char>();
+ d->subWidgetsWithDisabledEvents->insert(m_subwidget, (char*)1);
+ m_subwidget->installEventFilter(this);
+ QObjectList *l = m_subwidget->queryList( "QWidget" );
+ for ( QObjectListIt it( *l ); it.current(); ++it ) {
+ d->subWidgetsWithDisabledEvents->insert(it.current(), (char*)1);
+ it.current()->installEventFilter(this);
+ }
+ delete l;
+ }
+ }
+ updateGeometry();
+}
+
+void KexiDBComboBox::setLabelPosition(LabelPosition position)
+{
+ if(m_subwidget) {
+ if (-1 != m_subwidget->metaObject()->findProperty("frameShape", true))
+ m_subwidget->setProperty("frameShape", QVariant((int)QFrame::NoFrame));
+ m_subwidget->setGeometry( editorGeometry() );
+ }
+// KexiSubwidgetInterface *subwidgetInterface = dynamic_cast<KexiSubwidgetInterface*>((QWidget*)m_subwidget);
+ // update size policy
+// if (subwidgetInterface && subwidgetInterface->subwidgetStretchRequired(this)) {
+ QSizePolicy sizePolicy( this->sizePolicy() );
+ if(position == Left)
+ sizePolicy.setHorData( QSizePolicy::Minimum );
+ else
+ sizePolicy.setVerData( QSizePolicy::Minimum );
+ //m_subwidget->setSizePolicy(sizePolicy);
+ setSizePolicy(sizePolicy);
+ //}
+// }
+}
+
+QRect KexiDBComboBox::buttonGeometry() const
+{
+ QRect arrowRect(
+ style().querySubControlMetrics( QStyle::CC_ComboBox, d->paintedCombo, QStyle::SC_ComboBoxArrow) );
+ arrowRect = QStyle::visualRect(arrowRect, d->paintedCombo);
+ arrowRect.setHeight( QMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); // a fix for Motif style
+ return arrowRect;
+}
+
+bool KexiDBComboBox::handleMousePressEvent(QMouseEvent *e)
+{
+ if ( e->button() != Qt::LeftButton || d->designMode )
+ return true;
+/*todo if ( m_discardNextMousePress ) {
+ d->discardNextMousePress = FALSE;
+ return;
+ }*/
+
+ if ( /*count() &&*/ ( !isEditable() || buttonGeometry().contains( e->pos() ) ) ) {
+ d->buttonPressed = false;
+
+/* if ( d->usingListBox() ) {
+ listBox()->blockSignals( TRUE );
+ qApp->sendEvent( listBox(), e ); // trigger the listbox's autoscroll
+ listBox()->setCurrentItem(d->current);
+ listBox()->blockSignals( FALSE );
+ popup();
+ if ( arrowRect.contains( e->pos() ) ) {
+ d->arrowPressed = TRUE;
+ d->arrowDown = TRUE;
+ repaint( FALSE );
+ }
+ } else {*/
+ showPopup();
+ return true;
+ }
+ return false;
+}
+
+bool KexiDBComboBox::handleKeyPressEvent(QKeyEvent *ke)
+{
+ const int k = ke->key();
+ const bool dropDown = (ke->state() == Qt::NoButton && ((k==Qt::Key_F2 && !d->isEditable) || k==Qt::Key_F4))
+ || (ke->state() == Qt::AltButton && k==Qt::Key_Down);
+ const bool escPressed = ke->state() == Qt::NoButton && k==Qt::Key_Escape;
+ const bool popupVisible = popup() && popup()->isVisible();
+ if ((dropDown || escPressed) && popupVisible) {
+ popup()->hide();
+ return true;
+ }
+ else if (dropDown && !popupVisible) {
+ d->buttonPressed = false;
+ showPopup();
+ return true;
+ }
+ else if (popupVisible) {
+ const bool enterPressed = k==Qt::Key_Enter || k==Qt::Key_Return;
+ if (enterPressed/* && m_internalEditorValueChanged*/) {
+ acceptPopupSelection();
+ return true;
+ }
+ return handleKeyPressForPopup( ke );
+ }
+
+ return false;
+}
+
+bool KexiDBComboBox::keyPressed(QKeyEvent *ke)
+{
+ if (KexiDBAutoField::keyPressed(ke))
+ return true;
+
+ const int k = ke->key();
+ const bool popupVisible = popup() && popup()->isVisible();
+ const bool escPressed = ke->state() == Qt::NoButton && k==Qt::Key_Escape;
+ if (escPressed && popupVisible) {
+ popup()->hide();
+ return true;
+ }
+ if (ke->state() == Qt::NoButton && (k==Qt::Key_PageDown || k==Qt::Key_PageUp) && popupVisible)
+ return true;
+ return false;
+}
+
+void KexiDBComboBox::mousePressEvent( QMouseEvent *e )
+{
+ if (handleMousePressEvent(e))
+ return;
+
+// QTimer::singleShot( 200, this, SLOT(internalClickTimeout()));
+// d->shortClick = TRUE;
+// }
+ KexiDBAutoField::mousePressEvent( e );
+}
+
+void KexiDBComboBox::mouseDoubleClickEvent( QMouseEvent *e )
+{
+ mousePressEvent( e );
+}
+
+bool KexiDBComboBox::eventFilter( QObject *o, QEvent *e )
+{
+ if (o==this) {
+ if (e->type()==QEvent::Resize) {
+ d->paintedCombo->resize(size());
+ if (m_subwidget)
+ m_subwidget->setGeometry( editorGeometry() );
+ }
+ else if (e->type()==QEvent::Enter) {
+ if (!d->isEditable
+ || /*over button if editable combo*/buttonGeometry().contains( static_cast<QMouseEvent*>(e)->pos() ))
+ {
+ d->mouseOver = true;
+ update();
+ }
+ }
+ else if (e->type()==QEvent::MouseMove) {
+ if (d->isEditable) {
+ const bool overButton = buttonGeometry().contains( static_cast<QMouseEvent*>(e)->pos() );
+ if (overButton != d->mouseOver) {
+ d->mouseOver = overButton;
+ update();
+ }
+ }
+ }
+ else if (e->type()==QEvent::Leave) {
+ d->mouseOver = false;
+ update();
+ }
+ else if (e->type()==QEvent::KeyPress) {
+ // handle F2/F4
+ if (handleKeyPressEvent(static_cast<QKeyEvent*>(e)))
+ return true;
+ }
+ else if (e->type()==QEvent::FocusOut) {
+ if (popup() && popup()->isVisible()) {
+ popup()->hide();
+ undoChanges();
+ }
+ }
+ }
+ else if (!d->isEditable && d->subWidgetsWithDisabledEvents && d->subWidgetsWithDisabledEvents->find(o)) {
+ if (e->type()==QEvent::MouseButtonPress) {
+ // clicking the subwidget should mean the same as clicking the combo box (i.e. show the popup)
+ if (handleMousePressEvent(static_cast<QMouseEvent*>(e)))
+ return true;
+ }
+ else if (e->type()==QEvent::KeyPress) {
+ if (handleKeyPressEvent(static_cast<QKeyEvent*>(e)))
+ return true;
+ }
+ return e->type()!=QEvent::Paint;
+ }
+ return KexiDBAutoField::eventFilter( o, e );
+}
+
+bool KexiDBComboBox::subwidgetStretchRequired(KexiDBAutoField* autoField) const
+{
+ Q_UNUSED(autoField);
+ return true;
+}
+
+void KexiDBComboBox::setPaletteBackgroundColor( const QColor & color )
+{
+ KexiDBAutoField::setPaletteBackgroundColor(color);
+ QPalette pal(palette());
+ QColorGroup cg(pal.active());
+ pal.setColor(QColorGroup::Base, red);
+ pal.setColor(QColorGroup::Background, red);
+ pal.setActive(cg);
+ QWidget::setPalette(pal);
+ update();
+}
+
+bool KexiDBComboBox::valueChanged()
+{
+ kdDebug() << "KexiDataItemInterface::valueChanged(): " << m_origValue.toString() << " ? " << value().toString() << endl;
+ return m_origValue != value();
+}
+
+void
+KexiDBComboBox::setColumnInfo(KexiDB::QueryColumnInfo* cinfo)
+{
+ KexiFormDataItemInterface::setColumnInfo(cinfo);
+}
+
+void KexiDBComboBox::setVisibleColumnInfo(KexiDB::QueryColumnInfo* cinfo)
+{
+ d->visibleColumnInfo = cinfo;
+ // we're assuming we already have columnInfo()
+ setColumnInfoInternal(columnInfo(), d->visibleColumnInfo);
+}
+
+KexiDB::QueryColumnInfo* KexiDBComboBox::visibleColumnInfo() const
+{
+ return d->visibleColumnInfo;
+}
+
+void KexiDBComboBox::moveCursorToEndInInternalEditor()
+{
+ if (d->isEditable && m_moveCursorToEndInInternalEditor_enabled)
+ moveCursorToEnd();
+}
+
+void KexiDBComboBox::selectAllInInternalEditor()
+{
+ if (d->isEditable && m_selectAllInInternalEditor_enabled)
+ selectAll();
+}
+
+void KexiDBComboBox::setValueInternal(const QVariant& add, bool removeOld)
+{
+ //// use KexiDBAutoField instead of KexiComboBoxBase::setValueInternal
+ //// expects existing popup(), but we want to have delayed creation
+ if (popup())
+ popup()->hide();
+ KexiComboBoxBase::setValueInternal(add, removeOld);
+}
+
+void KexiDBComboBox::setVisibleValueInternal(const QVariant& value)
+{
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ iface->setValue(value, QVariant(), false /*!removeOld*/);
+}
+
+QVariant KexiDBComboBox::visibleValue()
+{
+ return KexiComboBoxBase::visibleValue();
+}
+
+void KexiDBComboBox::setValueInInternalEditor(const QVariant& value)
+{
+ if (!m_setValueInInternalEditor_enabled)
+ return;
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if(iface)
+ iface->setValue(value, QVariant(), false/*!removeOld*/);
+}
+
+QVariant KexiDBComboBox::valueFromInternalEditor()
+{
+ return KexiDBAutoField::value();
+}
+
+QPoint KexiDBComboBox::mapFromParentToGlobal(const QPoint& pos) const
+{
+// const KexiFormScrollView* view = KexiUtils::findParentConst<const KexiFormScrollView>(this, "KexiFormScrollView");
+ if (!parentWidget())
+ return QPoint(-1,-1);
+ return parentWidget()->mapToGlobal(pos);
+// return view->viewport()->mapToGlobal(pos);
+}
+
+int KexiDBComboBox::popupWidthHint() const
+{
+ return width(); //popup() ? popup()->width() : 0;
+}
+
+void KexiDBComboBox::fontChange( const QFont & oldFont )
+{
+ d->sizeHint = QSize(); //force rebuild the cache
+ KexiDBAutoField::fontChange(oldFont);
+}
+
+void KexiDBComboBox::styleChange( QStyle& oldStyle )
+{
+ KexiDBAutoField::styleChange( oldStyle );
+ d->sizeHint = QSize(); //force rebuild the cache
+ if (m_subwidget)
+ m_subwidget->setGeometry( editorGeometry() );
+}
+
+QSize KexiDBComboBox::sizeHint() const
+{
+ if ( isVisible() && d->sizeHint.isValid() )
+ return d->sizeHint;
+
+ const int maxWidth = 7 * fontMetrics().width(QChar('x')) + 18;
+ const int maxHeight = QMAX( fontMetrics().lineSpacing(), 14 ) + 2;
+ d->sizeHint = (style().sizeFromContents(QStyle::CT_ComboBox, d->paintedCombo,
+ QSize(maxWidth, maxHeight)).expandedTo(QApplication::globalStrut()));
+
+ return d->sizeHint;
+}
+
+void KexiDBComboBox::editRequested()
+{
+}
+
+void KexiDBComboBox::acceptRequested()
+{
+ signalValueChanged();
+}
+
+void KexiDBComboBox::slotRowAccepted(KexiTableItem *item, int row)
+{
+ d->dataEnteredByHand = false;
+ KexiComboBoxBase::slotRowAccepted(item, row);
+ d->dataEnteredByHand = true;
+}
+
+void KexiDBComboBox::beforeSignalValueChanged()
+{
+ if (d->dataEnteredByHand) {
+ KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
+ if (iface) {
+ slotInternalEditorValueChanged( iface->value() );
+ }
+ }
+}
+
+void KexiDBComboBox::undoChanges()
+{
+ KexiDBAutoField::undoChanges();
+ KexiComboBoxBase::undoChanges();
+}
+
+#include "kexidbcombobox.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbcombobox.h b/kexi/plugins/forms/widgets/kexidbcombobox.h
new file mode 100644
index 00000000..5208d37d
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbcombobox.h
@@ -0,0 +1,181 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBComboBox_H
+#define KexiDBComboBox_H
+
+#include "kexidbutils.h"
+#include "kexidbautofield.h"
+#include <widget/tableview/kexicomboboxbase.h>
+
+//! @short Combo box widget for Kexi forms
+/*! This widget is implemented on top of KexiDBAutoField,
+ so as it uses KexiDBAutoField's ability of embedding subwidgets,
+ it can display not only a line edit but also text edit or image box
+ (more can be added in the future).
+ A drop-down button is added to mimic native combo box widget's functionality.
+*/
+class KEXIFORMUTILS_EXPORT KexiDBComboBox :
+ public KexiDBAutoField, public KexiComboBoxBase
+{
+ Q_OBJECT
+ Q_PROPERTY( bool editable READ isEditable WRITE setEditable )
+ //properties from KexiDBAutoField that should not be visible:
+ Q_OVERRIDE(QColor paletteBackgroundColor READ paletteBackgroundColor WRITE setPaletteBackgroundColor DESIGNABLE true RESET unsetPalette)
+ Q_OVERRIDE(QColor foregroundLabelColor DESIGNABLE false)
+ Q_OVERRIDE(QColor backgroundLabelColor DESIGNABLE false)
+ Q_OVERRIDE(bool autoCaption DESIGNABLE false)
+
+ public:
+ KexiDBComboBox(QWidget *parent, const char *name=0, bool designMode = true);
+ virtual ~KexiDBComboBox();
+
+ //! Implemented for KexiComboBoxBase: form has no 'related data' model (only the full database model)
+ virtual KexiTableViewColumn *column() const { return 0; }
+
+ //! Implemented for KexiComboBoxBase
+ virtual KexiDB::Field *field() const { return KexiDBAutoField::field(); }
+
+ //! Implemented for KexiComboBoxBase
+ virtual QVariant origValue() const { return m_origValue; }
+
+ void setEditable(bool set);
+ bool isEditable() const;
+
+ virtual void setLabelPosition(LabelPosition position);
+
+ virtual QVariant value() { return KexiComboBoxBase::value(); }
+
+ virtual QVariant visibleValue();
+
+ //! Reimpemented because to avoid taking value from the internal editor (index is taken from the popup instead)
+ virtual bool valueChanged();
+
+ virtual QSize sizeHint() const;
+
+ //! Reimplemented after KexiDBAutoField: jsut sets \a cinfo without initializing a subwidget.
+ //! Initialization is performed by \ref setVisibleColumnInfo().
+ virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo);
+
+ /*! Used internally to set visible database column information.
+ Reimplemented: performs initialization of the subwidget. */
+ virtual void setVisibleColumnInfo(KexiDB::QueryColumnInfo* cinfo);
+
+ /*! \return visible database column information for this item.
+ Reimplemented. */
+ virtual KexiDB::QueryColumnInfo* visibleColumnInfo() const;
+
+ const QColor & paletteBackgroundColor() const { return KexiDBAutoField::paletteBackgroundColor(); }
+
+ //! Reimplemented to also set 'this' widget's background color, not only subwidget's.
+ virtual void setPaletteBackgroundColor( const QColor & color );
+
+ /*! Undoes changes made to this item - just resets the widget to original value.
+ Reimplemented after KexiFormDataItemInterface to also revert the visible value
+ (i.e. text) to the original state. */
+ virtual void undoChanges();
+
+ public slots:
+ void slotRowAccepted(KexiTableItem *item, int row);
+ void slotItemSelected(KexiTableItem* item) { KexiComboBoxBase::slotItemSelected(item); }
+
+ protected slots:
+ void slotInternalEditorValueChanged(const QVariant& v)
+ { KexiComboBoxBase::slotInternalEditorValueChanged(v); }
+
+ protected:
+ QRect buttonGeometry() const;
+
+ virtual void paintEvent( QPaintEvent * );
+
+ virtual void mousePressEvent( QMouseEvent *e );
+
+ void mouseDoubleClickEvent( QMouseEvent *e );
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ //! \return internal editor's geometry
+ QRect editorGeometry() const;
+
+ //! Creates editor. Reimplemented, because if the combo box is not editable,
+ //! editor should not be created.
+ virtual void createEditor();
+
+ /*! Reimplemented */
+ virtual void styleChange( QStyle& oldStyle );
+
+ /*! Reimplemented */
+ virtual void fontChange( const QFont & oldFont );
+
+ virtual bool subwidgetStretchRequired(KexiDBAutoField* autoField) const;
+
+ //! Implemented for KexiComboBoxBase
+ virtual QWidget *internalEditor() const { return /*WidgetWithSubpropertiesInterface*/m_subwidget; }
+
+ //! Implemented for KexiComboBoxBase. Does nothing if the widget is not editable.
+ virtual void moveCursorToEndInInternalEditor();
+
+ //! Implemented for KexiComboBoxBase. Does nothing if the widget is not editable.
+ virtual void selectAllInInternalEditor();
+
+ //! Implemented for KexiComboBoxBase
+ virtual void setValueInInternalEditor(const QVariant& value);
+
+ //! Implemented for KexiComboBoxBase
+ virtual QVariant valueFromInternalEditor();
+
+ //! Implemented for KexiComboBoxBase
+ virtual void editRequested();
+
+ //! Implemented for KexiComboBoxBase
+ virtual void acceptRequested();
+
+ //! Implement this to return a position \a pos mapped from parent (e.g. viewport)
+ //! to global coordinates. QPoint(-1, -1) should be returned if this cannot be computed.
+ virtual QPoint mapFromParentToGlobal(const QPoint& pos) const;
+
+ //! Implement this to return a hint for popup width.
+ virtual int popupWidthHint() const;
+
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ //! Implemented to handle visible value instead of index
+ virtual void setVisibleValueInternal(const QVariant& value);
+
+ bool handleMousePressEvent(QMouseEvent *e);
+
+ bool handleKeyPressEvent(QKeyEvent *ke);
+
+ //! Implemented for KexiDataItemInterface
+ virtual void beforeSignalValueChanged();
+
+ virtual KexiComboBoxPopup *popup() const;
+ virtual void setPopup(KexiComboBoxPopup *popup);
+
+ /*! Called by top-level form on key press event.
+ Used for Key_Escape to if the popup is visible,
+ so the key press won't be consumed to perform "cancel editing".
+ Also used for grabbing page down/up keys. */
+ virtual bool keyPressed(QKeyEvent *ke);
+
+ class Private;
+ Private * const d;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbdateedit.cpp b/kexi/plugins/forms/widgets/kexidbdateedit.cpp
new file mode 100644
index 00000000..32584fce
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbdateedit.cpp
@@ -0,0 +1,230 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbdateedit.h"
+#include <qlayout.h>
+#include <qtoolbutton.h>
+#include <kpopupmenu.h>
+#include <kdatepicker.h>
+#include <kdatetbl.h>
+
+#include <kexiutils/utils.h>
+#include <kexidb/queryschema.h>
+
+KexiDBDateEdit::KexiDBDateEdit(const QDate &date, QWidget *parent, const char *name)
+ : QWidget(parent, name), KexiFormDataItemInterface()
+{
+ m_invalidState = false;
+ m_cleared = false;
+ m_readOnly = false;
+
+ m_edit = new QDateEdit(date, this);
+ m_edit->setAutoAdvance(true);
+ m_edit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
+ connect( m_edit, SIGNAL(valueChanged(const QDate&)), this, SLOT(slotValueChanged(const QDate&)) );
+ connect( m_edit, SIGNAL(valueChanged(const QDate&)), this, SIGNAL(dateChanged(const QDate&)) );
+
+ QToolButton* btn = new QToolButton(this);
+ btn->setText("...");
+ btn->setFixedWidth( QFontMetrics(btn->font()).width(" ... ") );
+ btn->setPopupDelay(1); //1 ms
+
+#ifdef QDateTimeEditor_HACK
+ m_dte_date = KexiUtils::findFirstChild<QDateTimeEditor>(m_edit, "QDateTimeEditor");
+#else
+ m_dte_date = 0;
+#endif
+
+ m_datePickerPopupMenu = new KPopupMenu(0, "date_popup");
+ connect(m_datePickerPopupMenu, SIGNAL(aboutToShow()), this, SLOT(slotShowDatePicker()));
+ m_datePicker = new KDatePicker(m_datePickerPopupMenu, QDate::currentDate(), 0);
+
+ KDateTable *dt = KexiUtils::findFirstChild<KDateTable>(m_datePicker, "KDateTable");
+ if (dt)
+ connect(dt, SIGNAL(tableClicked()), this, SLOT(acceptDate()));
+ m_datePicker->setCloseButton(true);
+ m_datePicker->installEventFilter(this);
+ m_datePickerPopupMenu->insertItem(m_datePicker);
+ btn->setPopup(m_datePickerPopupMenu);
+
+ QHBoxLayout* layout = new QHBoxLayout(this);
+ layout->addWidget(m_edit, 1);
+ layout->addWidget(btn, 0);
+
+ setFocusProxy(m_edit);
+}
+
+KexiDBDateEdit::~KexiDBDateEdit()
+{
+}
+
+void KexiDBDateEdit::setInvalidState( const QString& )
+{
+ setEnabled(false);
+ setReadOnly(true);
+ m_invalidState = true;
+//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ?
+ if (focusPolicy() & TabFocus)
+ setFocusPolicy(QWidget::ClickFocus);
+}
+
+void
+KexiDBDateEdit::setEnabled(bool enabled)
+{
+ // prevent the user from reenabling the widget when it is in invalid state
+ if(enabled && m_invalidState)
+ return;
+ QWidget::setEnabled(enabled);
+}
+
+void KexiDBDateEdit::setValueInternal(const QVariant &add, bool removeOld)
+{
+ int setNumberOnFocus = -1;
+ QDate d;
+ QString addString(add.toString());
+ if (removeOld) {
+ if (!addString.isEmpty() && addString[0].latin1()>='0' && addString[0].latin1() <='9') {
+ setNumberOnFocus = addString[0].latin1()-'0';
+ d = QDate(setNumberOnFocus*1000, 1, 1);
+ }
+ }
+ else
+ d = m_origValue.toDate();
+
+ m_edit->setDate(d);
+}
+
+QVariant
+KexiDBDateEdit::value()
+{
+ return QVariant(m_edit->date());
+}
+
+bool KexiDBDateEdit::valueIsNull()
+{
+ return !m_edit->date().isValid() || m_edit->date().isNull();
+}
+
+bool KexiDBDateEdit::valueIsEmpty()
+{
+ return m_cleared;
+}
+
+bool KexiDBDateEdit::isReadOnly() const
+{
+ //! @todo: data/time edit API has no readonly flag,
+ //! so use event filter to avoid changes made by keyboard or mouse when m_readOnly==true
+ return m_readOnly; //!isEnabled();
+}
+
+void KexiDBDateEdit::setReadOnly(bool set)
+{
+ m_readOnly = set;
+}
+
+QWidget*
+KexiDBDateEdit::widget()
+{
+ return this;
+}
+
+bool KexiDBDateEdit::cursorAtStart()
+{
+#ifdef QDateTimeEditor_HACK
+ return m_dte_date && m_edit->hasFocus() && m_dte_date->focusSection()==0;
+#else
+ return false;
+#endif
+}
+
+bool KexiDBDateEdit::cursorAtEnd()
+{
+#ifdef QDateTimeEditor_HACK
+ return m_dte_date && m_edit->hasFocus()
+ && m_dte_date->focusSection()==int(m_dte_date->sectionCount()-1);
+#else
+ return false;
+#endif
+}
+
+void KexiDBDateEdit::clear()
+{
+ m_edit->setDate(QDate());
+ m_cleared = true;
+}
+
+void
+KexiDBDateEdit::slotValueChanged(const QDate&)
+{
+ m_cleared = false;
+}
+
+void
+KexiDBDateEdit::slotShowDatePicker()
+{
+ QDate date = m_edit->date();
+
+ m_datePicker->setDate(date);
+ m_datePicker->setFocus();
+ m_datePicker->show();
+ m_datePicker->setFocus();
+}
+
+void
+KexiDBDateEdit::acceptDate()
+{
+ m_edit->setDate(m_datePicker->date());
+ m_datePickerPopupMenu->hide();
+}
+
+bool
+KexiDBDateEdit::eventFilter(QObject *o, QEvent *e)
+{
+ if (o != m_datePicker)
+ return false;
+
+ switch (e->type()) {
+ case QEvent::Hide:
+ m_datePickerPopupMenu->hide();
+ break;
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease: {
+ QKeyEvent *ke = (QKeyEvent *)e;
+ if (ke->key()==Qt::Key_Enter || ke->key()==Qt::Key_Return) {
+ //accepting picker
+ acceptDate();
+ return true;
+ }
+ else if (ke->key()==Qt::Key_Escape) {
+ //canceling picker
+ m_datePickerPopupMenu->hide();
+ return true;
+ }
+ else
+ m_datePickerPopupMenu->setFocus();
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+#include "kexidbdateedit.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbdateedit.h b/kexi/plugins/forms/widgets/kexidbdateedit.h
new file mode 100644
index 00000000..2ad693a8
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbdateedit.h
@@ -0,0 +1,118 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBDateEdit_H
+#define KexiDBDateEdit_H
+
+#include "kexiformdataiteminterface.h"
+#include <qdatetimeedit.h>
+
+class KPopupMenu;
+class KDatePicker;
+class QDateTimeEditor;
+
+//! @short A db-aware date editor
+class KEXIFORMUTILS_EXPORT KexiDBDateEdit : public QWidget, public KexiFormDataItemInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+ // properties copied from QDateEdit
+ Q_ENUMS( Order )
+ Q_PROPERTY( Order order READ order WRITE setOrder DESIGNABLE true)
+ Q_PROPERTY( QDate date READ date WRITE setDate DESIGNABLE true)
+ Q_PROPERTY( bool autoAdvance READ autoAdvance WRITE setAutoAdvance DESIGNABLE true)
+ Q_PROPERTY( QDate maxValue READ maxValue WRITE setMaxValue DESIGNABLE true)
+ Q_PROPERTY( QDate minValue READ minValue WRITE setMinValue DESIGNABLE true)
+ Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true )
+
+ public:
+ enum Order { DMY = QDateEdit::DMY, MDY = QDateEdit::MDY, YMD = QDateEdit::YMD, YDM = QDateEdit::YDM };
+
+ KexiDBDateEdit(const QDate &date, QWidget *parent, const char *name=0);
+ virtual ~KexiDBDateEdit();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+ virtual QVariant value();
+ virtual void setInvalidState( const QString& displayText );
+
+ //! \return true if editor's value is null (not empty)
+ //! Used for checking if a given constraint within table of form is met.
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not necessary null).
+ //! Only few data types can accept "EMPTY" property
+ //! (use KexiDB::Field::hasEmptyProperty() to check this).
+ //! Used for checking if a given constraint within table or form is met.
+ virtual bool valueIsEmpty();
+
+ /*! \return 'readOnly' flag for this widget. */
+ virtual bool isReadOnly() const;
+
+ /*! \return the view widget of this item, e.g. line edit widget. */
+ virtual QWidget* widget();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+ virtual void clear();
+
+ virtual void setEnabled(bool enabled);
+
+ // property functions
+ inline QDate date() const { return m_edit->date(); }
+ inline void setOrder(Order order) { m_edit->setOrder( (QDateEdit::Order) order); }
+ inline Order order() const { return (Order)m_edit->order(); }
+ inline void setAutoAdvance( bool advance ) { m_edit->setAutoAdvance(advance); }
+ inline bool autoAdvance() const { return m_edit->autoAdvance(); }
+ inline void setMinValue(const QDate& d) { m_edit->setMinValue(d); }
+ inline QDate minValue() const { return m_edit->minValue(); }
+ inline void setMaxValue(const QDate& d) { m_edit->setMaxValue(d); }
+ inline QDate maxValue() const { return m_edit->maxValue(); }
+
+ signals:
+ void dateChanged(const QDate &date);
+
+ public slots:
+ inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); }
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+ inline void setDate(const QDate& date) { m_edit->setDate(date); }
+ virtual void setReadOnly(bool set);
+
+ protected slots:
+ void slotValueChanged(const QDate&);
+ void slotShowDatePicker();
+ void acceptDate();
+
+ protected:
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+ virtual bool eventFilter(QObject *o, QEvent *e);
+
+ private:
+ KDatePicker *m_datePicker;
+ QDateEdit *m_edit;
+ KPopupMenu *m_datePickerPopupMenu;
+ QDateTimeEditor *m_dte_date;
+ bool m_invalidState : 1;
+ bool m_cleared : 1;
+ bool m_readOnly : 1;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbdatetimeedit.cpp b/kexi/plugins/forms/widgets/kexidbdatetimeedit.cpp
new file mode 100644
index 00000000..faaeca66
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbdatetimeedit.cpp
@@ -0,0 +1,243 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbdatetimeedit.h"
+
+#include <qtoolbutton.h>
+#include <qlayout.h>
+#include <kpopupmenu.h>
+#include <kdatepicker.h>
+#include <kdatetbl.h>
+#include <kexiutils/utils.h>
+
+KexiDBDateTimeEdit::KexiDBDateTimeEdit(const QDateTime &datetime, QWidget *parent, const char *name)
+ : QWidget(parent, name), KexiFormDataItemInterface()
+{
+ m_invalidState = false;
+ m_cleared = false;
+ m_readOnly = false;
+
+ m_dateEdit = new QDateEdit(datetime.date(), this);
+ m_dateEdit->setAutoAdvance(true);
+ m_dateEdit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
+// m_dateEdit->setFixedWidth( QFontMetrics(m_dateEdit->font()).width("8888-88-88___") );
+ connect(m_dateEdit, SIGNAL(valueChanged(const QDate&)), this, SLOT(slotValueChanged()));
+ connect(m_dateEdit, SIGNAL(valueChanged(const QDate&)), this, SIGNAL(dateTimeChanged()));
+
+ QToolButton* btn = new QToolButton(this);
+ btn->setText("...");
+ btn->setFixedWidth( QFontMetrics(btn->font()).width(" ... ") );
+ btn->setPopupDelay(1); //1 ms
+
+ m_timeEdit = new QTimeEdit(datetime.time(), this);;
+ m_timeEdit->setAutoAdvance(true);
+ m_timeEdit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
+ connect(m_timeEdit, SIGNAL(valueChanged(const QTime&)), this, SLOT(slotValueChanged()));
+ connect(m_timeEdit, SIGNAL(valueChanged(const QTime&)), this, SIGNAL(dateTimeChanged()));
+
+#ifdef QDateTimeEditor_HACK
+ m_dte_date = KexiUtils::findFirstChild<QDateTimeEditor>(m_dateEdit, "QDateTimeEditor");
+ m_dte_time = KexiUtils::findFirstChild<QDateTimeEditor>(m_timeEdit, "QDateTimeEditor");
+#else
+ m_dte_date = 0;
+#endif
+
+ m_datePickerPopupMenu = new KPopupMenu(0, "date_popup");
+ connect(m_datePickerPopupMenu, SIGNAL(aboutToShow()), this, SLOT(slotShowDatePicker()));
+ m_datePicker = new KDatePicker(m_datePickerPopupMenu, QDate::currentDate(), 0);
+
+ KDateTable *dt = KexiUtils::findFirstChild<KDateTable>(m_datePicker, "KDateTable");
+ if (dt)
+ connect(dt, SIGNAL(tableClicked()), this, SLOT(acceptDate()));
+ m_datePicker->setCloseButton(true);
+ m_datePicker->installEventFilter(this);
+ m_datePickerPopupMenu->insertItem(m_datePicker);
+ btn->setPopup(m_datePickerPopupMenu);
+
+ QHBoxLayout* layout = new QHBoxLayout(this);
+ layout->addWidget(m_dateEdit, 0);
+ layout->addWidget(btn, 0);
+ layout->addWidget(m_timeEdit, 0);
+ //layout->addStretch(1);
+
+ setFocusProxy(m_dateEdit);
+}
+
+KexiDBDateTimeEdit::~KexiDBDateTimeEdit()
+{
+}
+
+void KexiDBDateTimeEdit::setInvalidState(const QString & /*! @todo paint this text: text*/)
+{
+ setEnabled(false);
+ setReadOnly(true);
+ m_invalidState = true;
+//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ?
+ if (focusPolicy() & TabFocus)
+ setFocusPolicy(QWidget::ClickFocus);
+}
+
+void
+KexiDBDateTimeEdit::setEnabled(bool enabled)
+{
+ // prevent the user from reenabling the widget when it is in invalid state
+ if(enabled && m_invalidState)
+ return;
+ QWidget::setEnabled(enabled);
+}
+
+void KexiDBDateTimeEdit::setValueInternal(const QVariant &, bool )
+{
+ m_dateEdit->setDate(m_origValue.toDate());
+ m_timeEdit->setTime(m_origValue.toTime());
+}
+
+QVariant
+KexiDBDateTimeEdit::value()
+{
+ return QDateTime(m_dateEdit->date(), m_timeEdit->time());
+}
+
+bool KexiDBDateTimeEdit::valueIsNull()
+{
+ return !m_dateEdit->date().isValid() || m_dateEdit->date().isNull()
+ || !m_timeEdit->time().isValid() || m_timeEdit->time().isNull();
+}
+
+bool KexiDBDateTimeEdit::valueIsEmpty()
+{
+ return m_cleared;
+}
+
+bool KexiDBDateTimeEdit::isReadOnly() const
+{
+ //! @todo: data/time edit API has no readonly flag,
+ //! so use event filter to avoid changes made by keyboard or mouse when m_readOnly==true
+ return m_readOnly; //!isEnabled();
+}
+
+void KexiDBDateTimeEdit::setReadOnly(bool set)
+{
+ m_readOnly = set;
+}
+
+QWidget*
+KexiDBDateTimeEdit::widget()
+{
+ return m_dateEdit;
+}
+
+bool KexiDBDateTimeEdit::cursorAtStart()
+{
+#ifdef QDateTimeEditor_HACK
+ return m_dte_date && m_dateEdit->hasFocus() && m_dte_date->focusSection()==0;
+#else
+ return false;
+#endif
+}
+
+bool KexiDBDateTimeEdit::cursorAtEnd()
+{
+#ifdef QDateTimeEditor_HACK
+ return m_dte_time && m_timeEdit->hasFocus()
+ && m_dte_time->focusSection()==int(m_dte_time->sectionCount()-1);
+#else
+ return false;
+#endif
+}
+
+void KexiDBDateTimeEdit::clear()
+{
+ m_dateEdit->setDate(QDate());
+ m_timeEdit->setTime(QTime());
+ m_cleared = true;
+}
+
+void
+KexiDBDateTimeEdit::slotValueChanged()
+{
+ m_cleared = false;
+}
+
+void
+KexiDBDateTimeEdit::slotShowDatePicker()
+{
+ QDate date = m_dateEdit->date();
+
+ m_datePicker->setDate(date);
+ m_datePicker->setFocus();
+ m_datePicker->show();
+ m_datePicker->setFocus();
+}
+
+void
+KexiDBDateTimeEdit::acceptDate()
+{
+ m_dateEdit->setDate(m_datePicker->date());
+ m_datePickerPopupMenu->hide();
+}
+
+bool
+KexiDBDateTimeEdit::eventFilter(QObject *o, QEvent *e)
+{
+ if (o != m_datePicker)
+ return false;
+
+ switch (e->type()) {
+ case QEvent::Hide:
+ m_datePickerPopupMenu->hide();
+ break;
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease: {
+ QKeyEvent *ke = (QKeyEvent *)e;
+ if (ke->key()==Qt::Key_Enter || ke->key()==Qt::Key_Return) {
+ //accepting picker
+ acceptDate();
+ return true;
+ }
+ else if (ke->key()==Qt::Key_Escape) {
+ //canceling picker
+ m_datePickerPopupMenu->hide();
+ return true;
+ }
+ else
+ m_datePickerPopupMenu->setFocus();
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+QDateTime
+KexiDBDateTimeEdit::dateTime() const
+{
+ return QDateTime(m_dateEdit->date(), m_timeEdit->time());
+}
+
+void
+KexiDBDateTimeEdit::setDateTime(const QDateTime &dt)
+{
+ m_dateEdit->setDate(dt.date());
+ m_timeEdit->setTime(dt.time());
+}
+
+#include "kexidbdatetimeedit.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbdatetimeedit.h b/kexi/plugins/forms/widgets/kexidbdatetimeedit.h
new file mode 100644
index 00000000..1f185b16
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbdatetimeedit.h
@@ -0,0 +1,106 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBDateTimeEdit_H
+#define KexiDBDateTimeEdit_H
+
+#include "kexiformdataiteminterface.h"
+#include <qdatetimeedit.h>
+
+class KDatePicker;
+class QDateTimeEditor;
+class KPopupMenu;
+
+//! @short A db-aware datetime editor
+class KEXIFORMUTILS_EXPORT KexiDBDateTimeEdit : public QWidget, public KexiFormDataItemInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+ // properties copied from QDateTimeEdit
+ Q_PROPERTY( QDateTime dateTime READ dateTime WRITE setDateTime )
+ Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true )
+
+ public:
+ enum Order { DMY, MDY, YMD, YDM };
+
+ KexiDBDateTimeEdit(const QDateTime &datetime, QWidget *parent, const char *name=0);
+ virtual ~KexiDBDateTimeEdit();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+ virtual QVariant value();
+ virtual void setInvalidState( const QString& displayText );
+
+ //! \return true if editor's value is null (not empty)
+ //! Used for checking if a given constraint within table of form is met.
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not necessary null).
+ //! Only few data types can accept "EMPTY" property
+ //! (use KexiDB::Field::hasEmptyProperty() to check this).
+ //! Used for checking if a given constraint within table or form is met.
+ virtual bool valueIsEmpty();
+
+ /*! \return 'readOnly' flag for this widget. */
+ virtual bool isReadOnly() const;
+
+ /*! \return the view widget of this item, e.g. line edit widget. */
+ virtual QWidget* widget();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+ virtual void clear();
+
+ virtual void setEnabled(bool enabled);
+
+ // property functions
+ QDateTime dateTime() const;
+
+ signals:
+ void dateTimeChanged();
+
+ public slots:
+ inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); }
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+ void setDateTime(const QDateTime &dt);
+ virtual void setReadOnly(bool set);
+
+ protected:
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+ virtual bool eventFilter(QObject *o, QEvent *e);
+
+ protected slots:
+ void slotValueChanged();
+ void slotShowDatePicker();
+ void acceptDate();
+
+ private:
+ KDatePicker *m_datePicker;
+ QDateEdit* m_dateEdit;
+ QTimeEdit* m_timeEdit;
+ QDateTimeEditor *m_dte_date, *m_dte_time;
+ KPopupMenu *m_datePickerPopupMenu;
+ bool m_invalidState : 1;
+ bool m_cleared : 1;
+ bool m_readOnly : 1;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbdoublespinbox.cpp b/kexi/plugins/forms/widgets/kexidbdoublespinbox.cpp
new file mode 100644
index 00000000..67a2c1a6
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbdoublespinbox.cpp
@@ -0,0 +1,113 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbdoublespinbox.h"
+
+#include <qlineedit.h>
+
+KexiDBDoubleSpinBox::KexiDBDoubleSpinBox(QWidget *parent, const char *name)
+ : KDoubleSpinBox(parent, name) , KexiFormDataItemInterface()
+{
+ connect(this, SIGNAL(valueChanged(double)), this, SLOT(slotValueChanged()));
+}
+
+KexiDBDoubleSpinBox::~KexiDBDoubleSpinBox()
+{
+}
+
+void KexiDBDoubleSpinBox::setInvalidState( const QString& displayText )
+{
+ m_invalidState = true;
+ setEnabled(false);
+ setReadOnly(true);
+//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ?
+ if (focusPolicy() & TabFocus)
+ setFocusPolicy(QWidget::ClickFocus);
+ setSpecialValueText(displayText);
+ KDoubleSpinBox::setValue(minValue());
+}
+
+void
+KexiDBDoubleSpinBox::setEnabled(bool enabled)
+{
+ // prevent the user from reenabling the widget when it is in invalid state
+ if(enabled && m_invalidState)
+ return;
+ KDoubleSpinBox::setEnabled(enabled);
+}
+
+void KexiDBDoubleSpinBox::setValueInternal(const QVariant&, bool )
+{
+ KDoubleSpinBox::setValue(m_origValue.toDouble());
+}
+
+QVariant
+KexiDBDoubleSpinBox::value()
+{
+ return KDoubleSpinBox::value();
+}
+
+void KexiDBDoubleSpinBox::slotValueChanged()
+{
+ signalValueChanged();
+}
+
+bool KexiDBDoubleSpinBox::valueIsNull()
+{
+ return cleanText().isEmpty();
+}
+
+bool KexiDBDoubleSpinBox::valueIsEmpty()
+{
+ return false;
+}
+
+bool KexiDBDoubleSpinBox::isReadOnly() const
+{
+ return editor()->isReadOnly();
+}
+
+void KexiDBDoubleSpinBox::setReadOnly(bool set)
+{
+ editor()->setReadOnly(set);
+}
+
+QWidget*
+KexiDBDoubleSpinBox::widget()
+{
+ return this;
+}
+
+bool KexiDBDoubleSpinBox::cursorAtStart()
+{
+ return false; //! \todo ?
+}
+
+bool KexiDBDoubleSpinBox::cursorAtEnd()
+{
+ return false; //! \todo ?
+}
+
+void KexiDBDoubleSpinBox::clear()
+{
+ KDoubleSpinBox::setValue(minValue());
+}
+
+#include "kexidbdoublespinbox.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbdoublespinbox.h b/kexi/plugins/forms/widgets/kexidbdoublespinbox.h
new file mode 100644
index 00000000..c6bc627d
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbdoublespinbox.h
@@ -0,0 +1,79 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBDoubleSpinBox_H
+#define KexiDBDoubleSpinBox_H
+
+#include "kexiformdataiteminterface.h"
+#include <qwidget.h>
+#include <knuminput.h>
+
+//! @short A db-aware int spin box
+class KEXIFORMUTILS_EXPORT KexiDBDoubleSpinBox : public KDoubleSpinBox, public KexiFormDataItemInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+ Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true )
+
+ public:
+ KexiDBDoubleSpinBox(QWidget *parent, const char *name=0);
+ virtual ~KexiDBDoubleSpinBox();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+ virtual QVariant value();
+ virtual void setInvalidState( const QString& displayText );
+
+ //! \return true if editor's value is null (not empty)
+ //! Used for checking if a given constraint within table of form is met.
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not necessary null).
+ //! Only few data types can accept "EMPTY" property
+ //! (use KexiDB::Field::hasEmptyProperty() to check this).
+ //! Used for checking if a given constraint within table or form is met.
+ virtual bool valueIsEmpty();
+
+ /*! \return 'readOnly' flag for this widget. */
+ virtual bool isReadOnly() const;
+
+ /*! \return the view widget of this item, e.g. line edit widget. */
+ virtual QWidget* widget();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+ virtual void clear();
+
+ public slots:
+ virtual void setEnabled(bool enabled);
+ inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); }
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+ void slotValueChanged();
+ virtual void setReadOnly(bool set);
+
+ protected:
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ private:
+ bool m_invalidState : 1;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbform.cpp b/kexi/plugins/forms/widgets/kexidbform.cpp
new file mode 100644
index 00000000..cff12c7c
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbform.cpp
@@ -0,0 +1,714 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qobjectlist.h>
+#include <qpainter.h>
+#include <qcursor.h>
+#include <qapplication.h>
+#include <qfocusdata.h>
+
+#include <kdebug.h>
+
+#include "kexidbform.h"
+#include "kexiformpart.h"
+#include "kexiformscrollview.h"
+
+#include <formeditor/objecttree.h>
+#include <formeditor/formmanager.h>
+#include <formeditor/widgetlibrary.h>
+#include <widget/tableview/kexidataawareobjectiface.h>
+#include <widget/kexiscrollview.h>
+#include <kexiutils/utils.h>
+
+//! @internal
+class KexiDBForm::Private
+{
+ public:
+ Private()
+ : dataAwareObject(0)
+ , orderedFocusWidgetsIterator(orderedFocusWidgets)
+ , autoTabStops(false)
+ , popupFocused(false)
+ {
+ }
+
+ ~Private()
+ {
+ }
+
+ //! \return index of data-aware widget \a widget
+ int indexOfDataAwareWidget(QWidget *widget) const
+ {
+ if (!dynamic_cast<KexiDataItemInterface*>(widget))
+ return -1;
+ return indexOfDataItem( dynamic_cast<KexiDataItemInterface*>(widget) );
+ }
+
+ //! \return index of data item \a item, or -1 if not found
+ int indexOfDataItem( KexiDataItemInterface* item ) const
+ {
+ QMapConstIterator<KexiDataItemInterface*, uint> indicesForDataAwareWidgetsIt(
+ indicesForDataAwareWidgets.find(item));
+ if (indicesForDataAwareWidgetsIt == indicesForDataAwareWidgets.constEnd())
+ return -1;
+ kexipluginsdbg << "KexiDBForm: column # for item: "
+ << indicesForDataAwareWidgetsIt.data() << endl;
+ return indicesForDataAwareWidgetsIt.data();
+ }
+
+ //! Sets orderedFocusWidgetsIterator member to a position pointing to \a widget
+ void setOrderedFocusWidgetsIteratorTo( QWidget *widget )
+ {
+ if (orderedFocusWidgetsIterator.current() == widget)
+ return;
+ orderedFocusWidgetsIterator.toFirst();
+ while (orderedFocusWidgetsIterator.current() && orderedFocusWidgetsIterator.current()!=widget)
+ ++orderedFocusWidgetsIterator;
+ }
+
+ KexiDataAwareObjectInterface* dataAwareObject;
+ //! ordered list of focusable widgets (can be both data-widgets or buttons, etc.)
+ QPtrList<QWidget> orderedFocusWidgets;
+ //! ordered list of data-aware widgets
+ QPtrList<QWidget> orderedDataAwareWidgets;
+ QMap<KexiDataItemInterface*, uint> indicesForDataAwareWidgets; //!< a subset of orderedFocusWidgets mapped to indices
+ QPtrListIterator<QWidget> orderedFocusWidgetsIterator;
+ QPixmap buffer; //!< stores grabbed entire form's area for redraw
+ QRect prev_rect; //!< previously selected rectangle
+// QGuardedPtr<QWidget> widgetFocusedBeforePopup;
+ bool autoTabStops : 1;
+ bool popupFocused : 1; //!< used in KexiDBForm::eventFilter()
+};
+
+//========================
+
+KexiDBForm::KexiDBForm(QWidget *parent, KexiDataAwareObjectInterface* dataAwareObject,
+ const char *name/*, KexiDB::Connection *conn*/)
+ : KexiDBFormBase(parent, name)
+ , KexiFormDataItemInterface()
+ , d(new Private())
+{
+ installEventFilter(this);
+//test setDisplayMode( KexiGradientWidget::SimpleGradient );
+ editedItem = 0;
+ d->dataAwareObject = dataAwareObject;
+ m_hasFocusableWidget = false;
+
+ kexipluginsdbg << "KexiDBForm::KexiDBForm(): " << endl;
+ setCursor(QCursor(Qt::ArrowCursor)); //to avoid keeping Size cursor when moving from form's boundaries
+ setAcceptDrops( true );
+}
+
+KexiDBForm::~KexiDBForm()
+{
+ kexipluginsdbg << "KexiDBForm::~KexiDBForm(): close" << endl;
+ delete d;
+}
+
+KexiDataAwareObjectInterface* KexiDBForm::dataAwareObject() const { return d->dataAwareObject; }
+
+//repaint all children widgets
+static void repaintAll(QWidget *w)
+{
+ QObjectList *list = w->queryList("QWidget");
+ QObjectListIt it(*list);
+ for (QObject *obj; (obj=it.current()); ++it ) {
+ static_cast<QWidget*>(obj)->repaint();
+ }
+ delete list;
+}
+
+void
+KexiDBForm::drawRect(const QRect& r, int type)
+{
+ QValueList<QRect> l;
+ l.append(r);
+ drawRects(l, type);
+}
+
+void
+KexiDBForm::drawRects(const QValueList<QRect> &list, int type)
+{
+ QPainter p;
+ p.begin(this, true);
+ bool unclipped = testWFlags( WPaintUnclipped );
+ setWFlags( WPaintUnclipped );
+
+ if (d->prev_rect.isValid()) {
+ //redraw prev. selection's rectangle
+ p.drawPixmap( QPoint(d->prev_rect.x()-2, d->prev_rect.y()-2), d->buffer,
+ QRect(d->prev_rect.x()-2, d->prev_rect.y()-2, d->prev_rect.width()+4, d->prev_rect.height()+4));
+ }
+ p.setBrush(QBrush::NoBrush);
+ if(type == 1) // selection rect
+ p.setPen(QPen(white, 1, Qt::DotLine));
+ else if(type == 2) // insert rect
+ p.setPen(QPen(white, 2));
+ p.setRasterOp(XorROP);
+
+ d->prev_rect = QRect();
+ QValueList<QRect>::ConstIterator endIt = list.constEnd();
+ for(QValueList<QRect>::ConstIterator it = list.constBegin(); it != endIt; ++it) {
+ p.drawRect(*it);
+ if (d->prev_rect.isValid())
+ d->prev_rect = d->prev_rect.unite(*it);
+ else
+ d->prev_rect = *it;
+ }
+
+ if (!unclipped)
+ clearWFlags( WPaintUnclipped );
+ p.end();
+}
+
+void
+KexiDBForm::initBuffer()
+{
+ repaintAll(this);
+ d->buffer.resize( width(), height() );
+ d->buffer = QPixmap::grabWindow( winId() );
+ d->prev_rect = QRect();
+}
+
+void
+KexiDBForm::clearForm()
+{
+ QPainter p;
+ p.begin(this, true);
+ bool unclipped = testWFlags( WPaintUnclipped );
+ setWFlags( WPaintUnclipped );
+
+ //redraw entire form surface
+ p.drawPixmap( QPoint(0,0), d->buffer, QRect(0,0,d->buffer.width(), d->buffer.height()) );
+
+ if (!unclipped)
+ clearWFlags( WPaintUnclipped );
+ p.end();
+
+ repaintAll(this);
+}
+
+void
+KexiDBForm::highlightWidgets(QWidget *from, QWidget *to)//, const QPoint &point)
+{
+ QPoint fromPoint, toPoint;
+ if(from && from->parentWidget() && (from != this))
+ fromPoint = from->parentWidget()->mapTo(this, from->pos());
+ if(to && to->parentWidget() && (to != this))
+ toPoint = to->parentWidget()->mapTo(this, to->pos());
+
+ QPainter p;
+ p.begin(this, true);
+ bool unclipped = testWFlags( WPaintUnclipped );
+ setWFlags( WPaintUnclipped );
+
+ if (d->prev_rect.isValid()) {
+ //redraw prev. selection's rectangle
+ p.drawPixmap( QPoint(d->prev_rect.x(), d->prev_rect.y()), d->buffer,
+ QRect(d->prev_rect.x(), d->prev_rect.y(), d->prev_rect.width(), d->prev_rect.height()));
+ }
+
+ p.setPen( QPen(Qt::red, 2) );
+
+ if(to)
+ {
+ QPixmap pix1 = QPixmap::grabWidget(from);
+ QPixmap pix2 = QPixmap::grabWidget(to);
+
+ if((from != this) && (to != this))
+ p.drawLine( from->parentWidget()->mapTo(this, from->geometry().center()), to->parentWidget()->mapTo(this, to->geometry().center()) );
+
+ p.drawPixmap(fromPoint.x(), fromPoint.y(), pix1);
+ p.drawPixmap(toPoint.x(), toPoint.y(), pix2);
+
+ if(to == this)
+ p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4);
+ else
+ p.drawRoundRect(toPoint.x(), toPoint.y(), to->width(), to->height(), 5, 5);
+ }
+
+ if(from == this)
+ p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4);
+ else
+ p.drawRoundRect(fromPoint.x(), fromPoint.y(), from->width(), from->height(), 5, 5);
+
+ if((to == this) || (from == this))
+ d->prev_rect = QRect(0, 0, d->buffer.width(), d->buffer.height());
+ else if(to)
+ {
+ d->prev_rect.setX( (fromPoint.x() < toPoint.x()) ? (fromPoint.x() - 5) : (toPoint.x() - 5) );
+ d->prev_rect.setY( (fromPoint.y() < toPoint.y()) ? (fromPoint.y() - 5) : (toPoint.y() - 5) );
+ d->prev_rect.setRight( (fromPoint.x() < toPoint.x()) ? (toPoint.x() + to->width() + 10) : (fromPoint.x() + from->width() + 10) );
+ d->prev_rect.setBottom( (fromPoint.y() < toPoint.y()) ? (toPoint.y() + to->height() + 10) : (fromPoint.y() + from->height() + 10) ) ;
+ }
+ else
+ d->prev_rect = QRect(fromPoint.x()- 5, fromPoint.y() -5, from->width() + 10, from->height() + 10);
+
+ if (!unclipped)
+ clearWFlags( WPaintUnclipped );
+ p.end();
+}
+
+QSize
+KexiDBForm::sizeHint() const
+{
+ //todo: find better size (user configured?)
+ return QSize(400,300);
+}
+
+void KexiDBForm::setInvalidState( const QString& displayText )
+{
+ Q_UNUSED( displayText );
+
+ //! @todo draw "invalid data source" text on the surface?
+}
+
+bool KexiDBForm::autoTabStops() const
+{
+ return d->autoTabStops;
+}
+
+void KexiDBForm::setAutoTabStops(bool set)
+{
+ d->autoTabStops = set;
+}
+
+QPtrList<QWidget>* KexiDBForm::orderedFocusWidgets() const
+{
+ return &d->orderedFocusWidgets;
+}
+
+QPtrList<QWidget>* KexiDBForm::orderedDataAwareWidgets() const
+{
+ return &d->orderedDataAwareWidgets;
+}
+
+void KexiDBForm::updateTabStopsOrder(KFormDesigner::Form* form)
+{
+ QWidget *fromWidget = 0;
+ //QWidget *topLevelWidget = form->widget()->topLevelWidget();
+//js form->updateTabStopsOrder(); //certain widgets can have now updated focusPolicy properties, fix this
+ uint numberOfDataAwareWidgets = 0;
+// if (d->orderedFocusWidgets.isEmpty()) {
+ //generate a new list
+ for (KFormDesigner::ObjectTreeListIterator it(form->tabStopsIterator()); it.current(); ++it) {
+ if (it.current()->widget()->focusPolicy() & QWidget::TabFocus) {
+ //this widget has tab focus:
+ it.current()->widget()->installEventFilter(this);
+ //also filter events for data-aware children of this widget (i.e. KexiDBAutoField's editors)
+ QObjectList *children = it.current()->widget()->queryList("QWidget");
+ for (QObjectListIt childrenIt(*children); childrenIt.current(); ++childrenIt) {
+ // if (dynamic_cast<KexiFormDataItemInterface*>(childrenIt.current())) {
+ kexipluginsdbg << "KexiDBForm::updateTabStopsOrder(): also adding '"
+ << childrenIt.current()->className() << " " << childrenIt.current()->name()
+ << "' child to filtered widgets" << endl;
+ //it.current()->widget()->installEventFilter(static_cast<QWidget*>(childrenIt.current()));
+ childrenIt.current()->installEventFilter(this);
+ // }
+ }
+ delete children;
+ if (fromWidget) {
+ kexipluginsdbg << "KexiDBForm::updateTabStopsOrder() tab order: " << fromWidget->name()
+ << " -> " << it.current()->widget()->name() << endl;
+ // setTabOrder( fromWidget, it.current()->widget() );
+ }
+ fromWidget = it.current()->widget();
+ d->orderedFocusWidgets.append( it.current()->widget() );
+ }
+
+ KexiFormDataItemInterface* dataItem = dynamic_cast<KexiFormDataItemInterface*>( it.current()->widget() );
+ if (dataItem && !dataItem->dataSource().isEmpty()) {
+ kexipluginsdbg << "#" << numberOfDataAwareWidgets << ": "
+ << dataItem->dataSource() << " (" << it.current()->widget()->name() << ")" << endl;
+
+// /*! @todo d->indicesForDataAwareWidgets SHOULDNT BE UPDATED HERE BECAUSE
+// THERE CAN BE ALSO NON-TABSTOP DATA WIDGETS!
+// */
+ d->indicesForDataAwareWidgets.replace(
+ dataItem,
+ numberOfDataAwareWidgets );
+ numberOfDataAwareWidgets++;
+
+ d->orderedDataAwareWidgets.append( it.current()->widget() );
+ }
+ }//for
+// }
+/* else {
+ //restore ordering
+ for (QPtrListIterator<QWidget> it(d->orderedFocusWidgets); it.current(); ++it) {
+ if (fromWidget) {
+ kdDebug() << "KexiDBForm::updateTabStopsOrder() tab order: " << fromWidget->name()
+ << " -> " << it.current()->name() << endl;
+ setTabOrder( fromWidget, it.current() );
+ }
+ fromWidget = it.current();
+ }
+// SET_FOCUS_USING_REASON(focusWidget(), QFocusEvent::Tab);
+ }*/
+}
+
+void KexiDBForm::updateTabStopsOrder()
+{
+ for (QPtrListIterator<QWidget> it( d->orderedFocusWidgets ); it.current();) {
+ if (! (it.current()->focusPolicy() & QWidget::TabFocus))
+ d->orderedFocusWidgets.remove( it.current() );
+ else
+ ++it;
+ }
+}
+
+void KexiDBForm::updateReadOnlyFlags()
+{
+ for (QPtrListIterator<QWidget> it(d->orderedDataAwareWidgets); it.current(); ++it) {
+ KexiFormDataItemInterface* dataItem = dynamic_cast<KexiFormDataItemInterface*>( it.current() );
+ if (dataItem && !dataItem->dataSource().isEmpty()) {
+ if (dataAwareObject()->isReadOnly()) {
+ dataItem->setReadOnly( true );
+ }
+ }
+ }
+}
+
+bool KexiDBForm::eventFilter( QObject * watched, QEvent * e )
+{
+ //kexipluginsdbg << e->type() << endl;
+ if (e->type()==QEvent::Resize && watched == this)
+ kexipluginsdbg << "RESIZE" << endl;
+ if (e->type()==QEvent::KeyPress) {
+ if (preview()) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ const int key = ke->key();
+ bool tab = ke->state() == Qt::NoButton && key == Qt::Key_Tab;
+ bool backtab = ((ke->state() == Qt::NoButton || ke->state() == Qt::ShiftButton) && key == Qt::Key_Backtab)
+ || (ke->state() == Qt::ShiftButton && key == Qt::Key_Tab);
+ QObject *o = watched; //focusWidget();
+ QWidget* realWidget = dynamic_cast<QWidget*>(o); //will beused below (for tab/backtab handling)
+
+ if (!tab && !backtab) {
+ //for buttons, left/up and right/down keys act like tab/backtab (see qbutton.cpp)
+ if (realWidget->inherits("QButton")) {
+ if (ke->state() == Qt::NoButton && (key == Qt::Key_Right || key == Qt::Key_Down))
+ tab = true;
+ else if (ke->state() == Qt::NoButton && (key == Qt::Key_Left || key == Qt::Key_Up))
+ backtab = true;
+ }
+ }
+
+ if (!tab && !backtab) {
+ // allow the editor widget to grab the key press event
+ while (true) {
+ if (!o || o == dynamic_cast<QObject*>(d->dataAwareObject))
+ break;
+ if (dynamic_cast<KexiFormDataItemInterface*>(o)) {
+ realWidget = dynamic_cast<QWidget*>(o); //will be used below
+ if (realWidget == this) //we have encountered 'this' form surface, give up
+ return false;
+ KexiFormDataItemInterface* dataItemIface = dynamic_cast<KexiFormDataItemInterface*>(o);
+ while (dataItemIface) {
+ if (dataItemIface->keyPressed(ke))
+ return false;
+ dataItemIface = dynamic_cast<KexiFormDataItemInterface*>(dataItemIface->parentInterface()); //try in parent, e.g. in combobox
+ }
+ break;
+ }
+ o = o->parent();
+ }
+ // try to handle global shortcuts at the KexiDataAwareObjectInterface
+ // level (e.g. for "next record" action)
+ int curRow = d->dataAwareObject->currentRow();
+ int curCol = d->dataAwareObject->currentColumn();
+ bool moveToFirstField; //if true, we'll move focus to the first field (in tab order)
+ bool moveToLastField; //if true, we'll move focus to the first field (in tab order)
+ if (! (ke->state() == Qt::NoButton && (key == Qt::Key_Home
+ || key == Qt::Key_End || key == Qt::Key_Down || key == Qt::Key_Up))
+ /* ^^ home/end/down/up are already handled by widgets */
+ && d->dataAwareObject->handleKeyPress(
+ ke, curRow, curCol, false/*!fullRowSelection*/, &moveToFirstField, &moveToLastField))
+ {
+ if (ke->isAccepted())
+ return true;
+ QWidget* widgetToFocus;
+ if (moveToFirstField) {
+ widgetToFocus = d->orderedFocusWidgets.first(); //?
+ curCol = d->indexOfDataAwareWidget( widgetToFocus );
+ }
+ else if (moveToLastField) {
+ widgetToFocus = d->orderedFocusWidgets.last(); //?
+ curCol = d->indexOfDataAwareWidget( widgetToFocus );
+ }
+ else
+ widgetToFocus = d->orderedDataAwareWidgets.at( curCol ); //?
+
+ d->dataAwareObject->setCursorPosition( curRow, curCol );
+
+ if (widgetToFocus)
+ widgetToFocus->setFocus();
+ else
+ kexipluginswarn << "KexiDBForm::eventFilter(): widgetToFocus not found!" << endl;
+
+ ke->accept();
+ return true;
+ }
+ if (key == Qt::Key_Delete && ke->state()==Qt::ControlButton) {
+//! @todo remove hardcoded shortcuts: can be reconfigured...
+ d->dataAwareObject->deleteCurrentRow();
+ return true;
+ }
+ }
+ // handle Esc key
+ if (ke->state() == Qt::NoButton && key == Qt::Key_Escape) {
+ //cancel field editing/row editing if possible
+ if (d->dataAwareObject->cancelEditor())
+ return true;
+ else if (d->dataAwareObject->cancelRowEdit())
+ return true;
+ return false; // canceling not needed - pass the event to the active widget
+ }
+ // jstaniek: Fix for Qt bug (handling e.g. Alt+2, Ctrl+2 keys on every platform)
+ // It's important because we're using alt+2 short cut by default
+ // Damn! I've reported this to Trolltech in November 2004 - still not fixed.
+ if (ke->isAccepted() && (ke->state() & Qt::AltButton) && ke->text()>="0" && ke->text()<="9")
+ return true;
+
+ if (tab || backtab) {
+ //the watched widget can be a subwidget of a real widget, e.g. a drop down button of image box: find it
+ while (!KexiFormPart::library()->widgetInfoForClassName(realWidget->className()))
+ realWidget = realWidget->parentWidget();
+ if (!realWidget)
+ return true; //ignore
+ //the watched widget can be a subwidget of a real widget, e.g. autofield: find it
+ //QWidget* realWidget = static_cast<QWidget*>(watched);
+ while (dynamic_cast<KexiDataItemInterface*>(realWidget) && dynamic_cast<KexiDataItemInterface*>(realWidget)->parentInterface())
+ realWidget = dynamic_cast<QWidget*>( dynamic_cast<KexiDataItemInterface*>(realWidget)->parentInterface() );
+
+ d->setOrderedFocusWidgetsIteratorTo( realWidget );
+ kexipluginsdbg << realWidget->name() << endl;
+
+ // find next/prev widget to focus
+ QWidget *widgetToUnfocus = realWidget;
+ QWidget *widgetToFocus = 0;
+ bool wasAtFirstWidget = false; //used to protect against infinite loop
+ while (true) {
+ if (tab) {
+ if (d->orderedFocusWidgets.first() && realWidget == d->orderedFocusWidgets.last()) {
+ if (wasAtFirstWidget)
+ break;
+ d->orderedFocusWidgetsIterator.toFirst();
+ wasAtFirstWidget = true;
+ }
+ else if (realWidget == d->orderedFocusWidgetsIterator.current()) {
+ ++d->orderedFocusWidgetsIterator; //next
+ }
+ else
+ return true; //ignore
+ }
+ else {//backtab
+ if (d->orderedFocusWidgets.last() && realWidget == d->orderedFocusWidgets.first()) {
+ d->orderedFocusWidgetsIterator.toLast();
+ }
+ else if (realWidget == d->orderedFocusWidgetsIterator.current()) {
+ --d->orderedFocusWidgetsIterator; //prev
+ }
+ else
+ return true; //ignore
+ }
+
+ widgetToFocus = d->orderedFocusWidgetsIterator.current();
+
+ QObject *pageFor_widgetToFocus = 0;
+ KFormDesigner::TabWidget *tabWidgetFor_widgetToFocus
+ = KFormDesigner::findParent<KFormDesigner::TabWidget>(
+ widgetToFocus, "KFormDesigner::TabWidget", pageFor_widgetToFocus);
+ if (tabWidgetFor_widgetToFocus && tabWidgetFor_widgetToFocus->currentPage()!=pageFor_widgetToFocus) {
+ realWidget = widgetToFocus;
+ continue; //the new widget to focus is placed on invisible tab page: move to next widget
+ }
+ break;
+ }//while
+
+ //set focus, but don't use just setFocus() because certain widgets
+ //behaves differently (e.g. QLineEdit calls selectAll()) when
+ //focus event's reason is QFocusEvent::Tab
+ if (widgetToFocus->focusProxy())
+ widgetToFocus = widgetToFocus->focusProxy();
+ if (widgetToFocus && d->dataAwareObject->acceptEditor()) {
+ if (tab) {
+ //try to accept this will validate the current input (if any)
+ KexiUtils::unsetFocusWithReason(widgetToUnfocus, QFocusEvent::Tab);
+ KexiUtils::setFocusWithReason(widgetToFocus, QFocusEvent::Tab);
+ kexipluginsdbg << "focusing " << widgetToFocus->name() << endl;
+ }
+ else {//backtab
+ KexiUtils::unsetFocusWithReason(widgetToUnfocus, QFocusEvent::Backtab);
+ //set focus, see above note
+ KexiUtils::setFocusWithReason(d->orderedFocusWidgetsIterator.current(), QFocusEvent::Backtab);
+ kexipluginsdbg << "focusing " << d->orderedFocusWidgetsIterator.current()->name() << endl;
+ }
+ }
+ return true;
+ }
+ }
+ }
+ else if (e->type()==QEvent::FocusIn) {
+ bool focusDataWidget = preview();
+ if (static_cast<QFocusEvent*>(e)->reason()==QFocusEvent::Popup) {
+ kdDebug() << "->>> focus IN, popup" <<endl;
+ focusDataWidget = !d->popupFocused;
+ d->popupFocused = false;
+// if (d->widgetFocusedBeforePopup) {
+// watched = d->widgetFocusedBeforePopup;
+// d->widgetFocusedBeforePopup = 0;
+// }
+ }
+
+ if (focusDataWidget) {
+ kexipluginsdbg << "KexiDBForm: FocusIn: " << watched->className() << " " << watched->name() << endl;
+ if (d->dataAwareObject) {
+ QWidget *dataItem = dynamic_cast<QWidget*>(watched);
+ while (dataItem) {
+ while (dataItem && !dynamic_cast<KexiDataItemInterface*>(dataItem))
+ dataItem = dataItem->parentWidget();
+ if (!dataItem)
+ break;
+ kexipluginsdbg << "KexiDBForm: FocusIn: FOUND " << dataItem->className() << " " << dataItem->name() << endl;
+
+ const int index = d->indexOfDataAwareWidget(dataItem);
+ if (index>=0) {
+ kexipluginsdbg << "KexiDBForm: moving cursor to column #" << index << endl;
+ editedItem = 0;
+ if ((int)index!=d->dataAwareObject->currentColumn()) {
+ d->dataAwareObject->setCursorPosition( d->dataAwareObject->currentRow(), index /*column*/ );
+ }
+ break;
+ }
+ else
+ dataItem = dataItem->parentWidget();
+
+ dataItem->update();
+ }
+ }
+ }
+ }
+ else if (e->type()==QEvent::FocusOut) {
+ if (static_cast<QFocusEvent*>(e)->reason()==QFocusEvent::Popup) {
+ //d->widgetFocusedBeforePopup = (QWidget*)watched;
+ d->popupFocused = true;
+ }
+ else
+ d->popupFocused = false;
+// d->widgetFocusedBeforePopup = 0;
+// kdDebug() << "e->type()==QEvent::FocusOut " << watched->className() << " " <<watched->name() << endl;
+// UNSET_FOCUS_USING_REASON(watched, static_cast<QFocusEvent*>(e)->reason());
+ }
+ return KexiDBFormBase::eventFilter(watched, e);
+}
+
+bool KexiDBForm::valueIsNull()
+{
+ return true;
+}
+
+bool KexiDBForm::valueIsEmpty()
+{
+ return true;
+}
+
+bool KexiDBForm::isReadOnly() const
+{
+ if (d->dataAwareObject)
+ return d->dataAwareObject->isReadOnly();
+//! @todo ?
+ return false;
+}
+
+void KexiDBForm::setReadOnly( bool readOnly )
+{
+ if (d->dataAwareObject)
+ d->dataAwareObject->setReadOnly( readOnly ); //???
+}
+
+QWidget* KexiDBForm::widget()
+{
+ return this;
+}
+
+bool KexiDBForm::cursorAtStart()
+{
+ return false;
+}
+
+bool KexiDBForm::cursorAtEnd()
+{
+ return false;
+}
+
+void KexiDBForm::clear()
+{
+ //! @todo clear all fields?
+}
+
+bool KexiDBForm::preview() const {
+ return dynamic_cast<KexiScrollView*>(d->dataAwareObject)
+ ? dynamic_cast<KexiScrollView*>(d->dataAwareObject)->preview() : false;
+}
+
+void KexiDBForm::dragMoveEvent( QDragMoveEvent *e )
+{
+ KexiDBFormBase::dragMoveEvent( e );
+ emit handleDragMoveEvent(e);
+}
+
+void KexiDBForm::dropEvent( QDropEvent *e )
+{
+ KexiDBFormBase::dropEvent( e );
+ emit handleDropEvent(e);
+}
+
+void KexiDBForm::setCursor( const QCursor & cursor )
+{
+ //js: empty, to avoid fscking problems with random cursors!
+ //! @todo?
+
+ if (KFormDesigner::FormManager::self()->isInserting()) //exception
+ KexiDBFormBase::setCursor(cursor);
+}
+
+//! @todo: Qt4? XORed resize rectangles instead of black widgets
+/*
+void KexiDBForm::paintEvent( QPaintEvent *e )
+{
+ QPainter p;
+ p.begin(this, true);
+ bool unclipped = testWFlags( WPaintUnclipped );
+ setWFlags( WPaintUnclipped );
+
+ p.setPen(white);
+ p.setRasterOp(XorROP);
+ p.drawLine(e->rect().topLeft(), e->rect().bottomRight());
+
+ if (!unclipped)
+ clearWFlags( WPaintUnclipped );
+ p.end();
+ KexiDBFormBase::paintEvent(e);
+}
+*/
+
+#include "kexidbform.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbform.h b/kexi/plugins/forms/widgets/kexidbform.h
new file mode 100644
index 00000000..81a71bba
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbform.h
@@ -0,0 +1,139 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+#ifndef KEXIDBFORM_H
+#define KEXIDBFORM_H
+
+#include <qpixmap.h>
+
+#include <formeditor/form.h>
+#include "../kexiformdataiteminterface.h"
+
+#ifdef KEXI_USE_GRADIENT_WIDGET
+#include <kexigradientwidget.h>
+# define KexiDBFormBase KexiGradientWidget
+#else
+# define KexiDBFormBase QWidget
+#endif
+
+class KexiDataAwareObjectInterface;
+class KexiFormScrollView;
+
+//! @short A DB-aware form widget, acting as form's toplevel widget
+class KEXIFORMUTILS_EXPORT KexiDBForm :
+ public KexiDBFormBase,
+ public KFormDesigner::FormWidget,
+ public KexiFormDataItemInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+ Q_PROPERTY(bool autoTabStops READ autoTabStops WRITE setAutoTabStops DESIGNABLE true)
+ //original "size" property is not designable, so here's a custom (not storable) replacement
+ Q_PROPERTY( QSize sizeInternal READ sizeInternal WRITE resizeInternal DESIGNABLE true STORED false )
+ public:
+ KexiDBForm(QWidget *parent, KexiDataAwareObjectInterface* dataAwareObject, const char *name="kexi_dbform");
+ virtual ~KexiDBForm();
+
+ KexiDataAwareObjectInterface* dataAwareObject() const;
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+
+ //! no effect
+ QVariant value() { return QVariant(); }
+
+ virtual void setInvalidState( const QString& displayText );
+
+ virtual void drawRect(const QRect& r, int type);
+ virtual void drawRects(const QValueList<QRect> &list, int type);
+ virtual void initBuffer();
+ virtual void clearForm();
+ virtual void highlightWidgets(QWidget *from, QWidget *to/*, const QPoint &p*/);
+
+ virtual QSize sizeHint() const;
+
+ bool autoTabStops() const;
+
+ QPtrList<QWidget>* orderedFocusWidgets() const;
+
+ QPtrList<QWidget>* orderedDataAwareWidgets() const;
+
+ void updateTabStopsOrder(KFormDesigner::Form* form);
+
+ void updateTabStopsOrder();
+
+ virtual bool eventFilter ( QObject * watched, QEvent * e );
+
+ virtual bool valueIsNull();
+ virtual bool valueIsEmpty();
+ virtual bool isReadOnly() const;
+ virtual QWidget* widget();
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+ virtual void clear();
+
+ bool preview() const;
+
+ virtual void setCursor( const QCursor & cursor );
+
+ public slots:
+ void setAutoTabStops(bool set);
+ inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); }
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+
+ //! This implementation just disables read only widget
+ virtual void setReadOnly( bool readOnly );
+
+ //! @internal for sizeInternal property
+ QSize sizeInternal() const { return KexiDBFormBase::size(); }
+
+ //! @internal for sizeInternal property
+ void resizeInternal(const QSize& s) { KexiDBFormBase::resize(s); }
+
+ signals:
+ void handleDragMoveEvent(QDragMoveEvent *e);
+ void handleDropEvent(QDropEvent *e);
+
+ protected:
+ //! no effect
+ virtual void setValueInternal(const QVariant&, bool) {}
+
+ //! Used to emit handleDragMoveEvent() signal needed to control dragging over the container's surface
+ virtual void dragMoveEvent( QDragMoveEvent *e );
+
+ //! Used to emit handleDropEvent() signal needed to control dropping on the container's surface
+ virtual void dropEvent( QDropEvent *e );
+
+ //! called from KexiFormScrollView::initDataContents()
+ void updateReadOnlyFlags();
+// virtual void paintEvent( QPaintEvent * );
+
+ //! Points to a currently edited data item.
+ //! It is cleared when the focus is moved to other
+ KexiFormDataItemInterface *editedItem;
+
+ class Private;
+ Private *d;
+
+ friend class KexiFormScrollView;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbimagebox.cpp b/kexi/plugins/forms/widgets/kexidbimagebox.cpp
new file mode 100644
index 00000000..82e70086
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbimagebox.cpp
@@ -0,0 +1,870 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbimagebox.h"
+
+#include <qapplication.h>
+#include <qpixmap.h>
+#include <qstyle.h>
+#include <qclipboard.h>
+#include <qtooltip.h>
+#include <qimage.h>
+#include <qbuffer.h>
+#include <qfiledialog.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kfiledialog.h>
+#include <kimageio.h>
+#include <kstandarddirs.h>
+#include <kstaticdeleter.h>
+#include <kimageeffect.h>
+#include <kstdaccel.h>
+#include <kmessagebox.h>
+#include <kguiitem.h>
+
+#include <widget/utils/kexidropdownbutton.h>
+#include <widget/utils/kexicontextmenuutils.h>
+#include <kexiutils/utils.h>
+#include <kexidb/field.h>
+#include <kexidb/utils.h>
+#include <kexidb/queryschema.h>
+#include <formeditor/widgetlibrary.h>
+
+#ifdef Q_WS_WIN
+#include <win32_utils.h>
+#include <krecentdirs.h>
+#endif
+
+#include "kexidbutils.h"
+#include "../kexiformpart.h"
+
+static KStaticDeleter<QPixmap> KexiDBImageBox_pmDeleter;
+static QPixmap* KexiDBImageBox_pm = 0;
+static KStaticDeleter<QPixmap> KexiDBImageBox_pmSmallDeleter;
+static QPixmap* KexiDBImageBox_pmSmall = 0;
+
+KexiDBImageBox::KexiDBImageBox( bool designMode, QWidget *parent, const char *name )
+ : KexiFrame( parent, name, Qt::WNoAutoErase )
+ , KexiFormDataItemInterface()
+ , m_alignment(Qt::AlignAuto|Qt::AlignTop)
+ , m_designMode(designMode)
+ , m_readOnly(false)
+ , m_scaledContents(false)
+ , m_keepAspectRatio(true)
+ , m_insideSetData(false)
+ , m_setFocusOnButtonAfterClosingPopup(false)
+ , m_lineWidthChanged(false)
+ , m_paintEventEnabled(true)
+ , m_dropDownButtonVisible(true)
+ , m_insideSetPalette(false)
+{
+ installEventFilter(this);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+
+ //setup popup menu
+ m_popupMenu = new KexiImageContextMenu(this);
+ m_popupMenu->installEventFilter(this);
+
+ if (m_designMode) {
+ m_chooser = 0;
+ }
+ else {
+ m_chooser = new KexiDropDownButton(this);
+ m_chooser->setFocusPolicy(StrongFocus);
+ m_chooser->setPopup(m_popupMenu);
+ setFocusProxy(m_chooser);
+ m_chooser->installEventFilter(this);
+// m_chooser->setPalette(qApp->palette());
+// hlyr->addWidget(m_chooser);
+ }
+
+ setBackgroundMode(Qt::NoBackground);
+ setFrameShape(QFrame::Box);
+ setFrameShadow(QFrame::Plain);
+ setFrameColor(Qt::black);
+
+ m_paletteBackgroundColorChanged = false; //set this here, not before
+
+ connect(m_popupMenu, SIGNAL(updateActionsAvailabilityRequested(bool&, bool&)),
+ this, SLOT(slotUpdateActionsAvailabilityRequested(bool&, bool&)));
+ connect(m_popupMenu, SIGNAL(insertFromFileRequested(const KURL&)),
+ this, SLOT(handleInsertFromFileAction(const KURL&)));
+ connect(m_popupMenu, SIGNAL(saveAsRequested(const QString&)),
+ this, SLOT(handleSaveAsAction(const QString&)));
+ connect(m_popupMenu, SIGNAL(cutRequested()),
+ this, SLOT(handleCutAction()));
+ connect(m_popupMenu, SIGNAL(copyRequested()),
+ this, SLOT(handleCopyAction()));
+ connect(m_popupMenu, SIGNAL(pasteRequested()),
+ this, SLOT(handlePasteAction()));
+ connect(m_popupMenu, SIGNAL(clearRequested()),
+ this, SLOT(clear()));
+ connect(m_popupMenu, SIGNAL(showPropertiesRequested()),
+ this, SLOT(handleShowPropertiesAction()));
+
+// connect(m_popupMenu, SIGNAL(aboutToHide()), this, SLOT(slotAboutToHidePopupMenu()));
+// if (m_chooser) {
+ //we couldn't use m_chooser->setPopup() because of drawing problems
+// connect(m_chooser, SIGNAL(pressed()), this, SLOT(slotChooserPressed()));
+// connect(m_chooser, SIGNAL(released()), this, SLOT(slotChooserReleased()));
+// connect(m_chooser, SIGNAL(toggled(bool)), this, SLOT(slotToggled(bool)));
+// }
+
+ setDataSource( QString::null ); //to initialize popup menu and actions availability
+}
+
+KexiDBImageBox::~KexiDBImageBox()
+{
+}
+
+KexiImageContextMenu* KexiDBImageBox::contextMenu() const
+{
+ return m_popupMenu;
+}
+
+QVariant KexiDBImageBox::value()
+{
+ if (dataSource().isEmpty()) {
+ //not db-aware
+ return QVariant();
+ }
+ //db-aware mode
+ return m_value; //todo
+ //return QVariant(); //todo
+}
+
+void KexiDBImageBox::setValueInternal( const QVariant& add, bool removeOld, bool loadPixmap )
+{
+ if (isReadOnly())
+ return;
+ m_popupMenu->hide();
+ if (removeOld)
+ m_value = add.toByteArray();
+ else //do not add "m_origValue" to "add" as this is QByteArray
+ m_value = m_origValue.toByteArray();
+ bool ok = !m_value.isEmpty();
+ if (ok) {
+ ///unused (m_valueMimeType is not available unless the px is inserted) QString type( KImageIO::typeForMime(m_valueMimeType) );
+ ///ok = KImageIO::canRead( type );
+ ok = loadPixmap ? m_pixmap.loadFromData(m_value) : true; //, type.latin1());
+ if (!ok) {
+ //! @todo inform about error?
+ }
+ }
+ if (!ok) {
+ m_valueMimeType = QString::null;
+ m_pixmap = QPixmap();
+ }
+ repaint();
+}
+
+void KexiDBImageBox::setInvalidState( const QString& displayText )
+{
+ Q_UNUSED( displayText );
+
+// m_pixmapLabel->setPixmap(QPixmap());
+ if (!dataSource().isEmpty()) {
+ m_value = QByteArray();
+ }
+// m_pixmap = QPixmap();
+// m_originalFileName = QString::null;
+
+//! @todo m_pixmapLabel->setText( displayText );
+
+ if (m_chooser)
+ m_chooser->hide();
+ setReadOnly(true);
+}
+
+bool KexiDBImageBox::valueIsNull()
+{
+ return m_value.isEmpty();
+// return !m_pixmapLabel->pixmap() || m_pixmapLabel->pixmap()->isNull();
+}
+
+bool KexiDBImageBox::valueIsEmpty()
+{
+ return false;
+}
+
+bool KexiDBImageBox::isReadOnly() const
+{
+ return m_readOnly;
+}
+
+void KexiDBImageBox::setReadOnly(bool set)
+{
+ m_readOnly = set;
+}
+
+QPixmap KexiDBImageBox::pixmap() const
+{
+ if (dataSource().isEmpty()) {
+ //not db-aware
+ return m_data.pixmap();
+ }
+ //db-aware mode
+ return m_pixmap;
+}
+
+uint KexiDBImageBox::pixmapId() const
+{
+ if (dataSource().isEmpty()) {// && !m_data.stored()) {
+ //not db-aware
+ return m_data.id();
+ }
+ return 0;
+}
+
+void KexiDBImageBox::setPixmapId(uint id)
+{
+ if (m_insideSetData) //avoid recursion
+ return;
+ setData(KexiBLOBBuffer::self()->objectForId( id, /*unstored*/false ));
+ repaint();
+}
+
+uint KexiDBImageBox::storedPixmapId() const
+{
+ if (dataSource().isEmpty() && m_data.stored()) {
+ //not db-aware
+ return m_data.id();
+ }
+ return 0;
+}
+
+void KexiDBImageBox::setStoredPixmapId(uint id)
+{
+ setData(KexiBLOBBuffer::self()->objectForId( id, /*stored*/true ));
+ repaint();
+}
+
+bool KexiDBImageBox::hasScaledContents() const
+{
+ return m_scaledContents;
+// return m_pixmapLabel->hasScaledContents();
+}
+
+/*void KexiDBImageBox::setPixmap(const QByteArray& pixmap)
+{
+ setValueInternal(pixmap, true);
+// setBackgroundMode(pixmap.isNull() ? Qt::NoBackground : Qt::PaletteBackground);
+}*/
+
+void KexiDBImageBox::setScaledContents(bool set)
+{
+//todo m_pixmapLabel->setScaledContents(set);
+ m_scaledContents = set;
+ repaint();
+}
+
+void KexiDBImageBox::setKeepAspectRatio(bool set)
+{
+ m_keepAspectRatio = set;
+ if (m_scaledContents)
+ repaint();
+}
+
+QWidget* KexiDBImageBox::widget()
+{
+ //! @todo
+// return m_pixmapLabel;
+ return this;
+}
+
+bool KexiDBImageBox::cursorAtStart()
+{
+ return true;
+}
+
+bool KexiDBImageBox::cursorAtEnd()
+{
+ return true;
+}
+
+QByteArray KexiDBImageBox::data() const
+{
+ if (dataSource().isEmpty()) {
+ //static mode
+ return m_data.data();
+ }
+ else {
+ //db-aware mode
+ return m_value;
+ }
+}
+
+void KexiDBImageBox::insertFromFile()
+{
+ m_popupMenu->insertFromFile();
+}
+
+void KexiDBImageBox::handleInsertFromFileAction(const KURL& url)
+{
+ if (!dataSource().isEmpty() && isReadOnly())
+ return;
+
+ if (dataSource().isEmpty()) {
+ //static mode
+ KexiBLOBBuffer::Handle h = KexiBLOBBuffer::self()->insertPixmap( url );
+ if (!h)
+ return;
+ setData(h);
+ repaint();
+ }
+ else {
+ //db-aware
+ QString fileName( url.isLocalFile() ? url.path() : url.prettyURL() );
+
+ //! @todo download the file if remote, then set fileName properly
+ QFile f(fileName);
+ if (!f.open(IO_ReadOnly)) {
+ //! @todo err msg
+ return;
+ }
+ QByteArray ba = f.readAll();
+ if (f.status()!=IO_Ok) {
+ //! @todo err msg
+ f.close();
+ return;
+ }
+ m_valueMimeType = KImageIO::mimeType( fileName );
+ setValueInternal( ba, true );
+ }
+
+//! @todo emit signal for setting "dirty" flag within the design
+ if (!dataSource().isEmpty()) {
+ signalValueChanged();
+ }
+}
+
+void KexiDBImageBox::handleAboutToSaveAsAction(QString& origFilename, QString& fileExtension, bool& dataIsEmpty)
+{
+ if (data().isEmpty()) {
+ kdWarning() << "KexiDBImageBox::handleAboutToSaveAs(): no pixmap!" << endl;
+ dataIsEmpty = false;
+ return;
+ }
+ if (dataSource().isEmpty()) { //for static images filename and mimetype can be available
+ origFilename = m_data.originalFileName();
+ if (!origFilename.isEmpty())
+ origFilename = QString("/") + origFilename;
+ if (!m_data.mimeType().isEmpty())
+ fileExtension = KImageIO::typeForMime(m_data.mimeType()).lower();
+ }
+}
+
+void KexiDBImageBox::handleSaveAsAction(const QString& fileName)
+{
+ QFile f(fileName);
+ if (!f.open(IO_WriteOnly)) {
+ //! @todo err msg
+ return;
+ }
+ f.writeBlock( data() );
+ if (f.status()!=IO_Ok) {
+ //! @todo err msg
+ f.close();
+ return;
+ }
+ f.close();
+}
+
+void KexiDBImageBox::handleCutAction()
+{
+ if (!dataSource().isEmpty() && isReadOnly())
+ return;
+ handleCopyAction();
+ clear();
+}
+
+void KexiDBImageBox::handleCopyAction()
+{
+ qApp->clipboard()->setPixmap(pixmap(), QClipboard::Clipboard);
+}
+
+void KexiDBImageBox::handlePasteAction()
+{
+ if (isReadOnly() || (!m_designMode && !hasFocus()))
+ return;
+ QPixmap pm( qApp->clipboard()->pixmap(QClipboard::Clipboard) );
+// if (!pm.isNull())
+// setValueInternal(pm, true);
+ if (dataSource().isEmpty()) {
+ //static mode
+ setData(KexiBLOBBuffer::self()->insertPixmap( pm ));
+ }
+ else {
+ //db-aware mode
+ m_pixmap = pm;
+ QByteArray ba;
+ QBuffer buffer( ba );
+ buffer.open( IO_WriteOnly );
+ if (m_pixmap.save( &buffer, "PNG" )) {// write pixmap into ba in PNG format
+ setValueInternal( ba, true, false/* !loadPixmap */ );
+ }
+ else {
+ setValueInternal( QByteArray(), true );
+ }
+ }
+
+ repaint();
+ if (!dataSource().isEmpty()) {
+// emit pixmapChanged();
+ signalValueChanged();
+ }
+}
+
+void KexiDBImageBox::clear()
+{
+ if (dataSource().isEmpty()) {
+ //static mode
+ setData(KexiBLOBBuffer::Handle());
+ }
+ else {
+ if (isReadOnly())
+ return;
+ //db-aware mode
+ setValueInternal(QByteArray(), true);
+ //m_pixmap = QPixmap();
+ }
+
+// m_originalFileName = QString::null;
+
+ //! @todo emit signal for setting "dirty" flag within the design
+
+// m_pixmap = QPixmap(); //will be loaded on demand
+ repaint();
+ if (!dataSource().isEmpty()) {
+// emit pixmapChanged();//valueChanged(data());
+ signalValueChanged();
+ }
+}
+
+void KexiDBImageBox::handleShowPropertiesAction()
+{
+ //! @todo
+}
+
+void KexiDBImageBox::slotUpdateActionsAvailabilityRequested(bool& valueIsNull, bool& valueIsReadOnly)
+{
+ valueIsNull = !(
+ (dataSource().isEmpty() && !pixmap().isNull()) /*static pixmap available*/
+ || (!dataSource().isEmpty() && !this->valueIsNull()) /*db-aware pixmap available*/
+ );
+ // read-only if static pixmap or db-aware pixmap for read-only widget:
+ valueIsReadOnly = !m_designMode && dataSource().isEmpty() || !dataSource().isEmpty() && isReadOnly()
+ || m_designMode && !dataSource().isEmpty();
+}
+
+/*
+void KexiDBImageBox::slotAboutToHidePopupMenu()
+{
+// kexipluginsdbg << "##### slotAboutToHidePopupMenu() " << endl;
+ m_clickTimer.start(50, true);
+ if (m_chooser && m_chooser->isOn()) {
+ m_chooser->toggle();
+ if (m_setFocusOnButtonAfterClosingPopup) {
+ m_setFocusOnButtonAfterClosingPopup = false;
+ m_chooser->setFocus();
+ }
+ }
+}*/
+
+void KexiDBImageBox::contextMenuEvent( QContextMenuEvent * e )
+{
+ if (popupMenuAvailable())
+ m_popupMenu->exec( e->globalPos(), -1 );
+}
+
+/*void KexiDBImageBox::slotChooserPressed()
+{
+// if (!m_clickTimer.isActive())
+// return;
+// m_chooser->setDown( false );
+}
+
+void KexiDBImageBox::slotChooserReleased()
+{
+}
+
+void KexiDBImageBox::slotToggled(bool on)
+{
+ return;
+
+// kexipluginsdbg << "##### slotToggled() " << on << endl;
+ if (m_clickTimer.isActive() || !on) {
+ m_chooser->disableMousePress = true;
+ return;
+ }
+ m_chooser->disableMousePress = false;
+ QRect screen = qApp->desktop()->availableGeometry( m_chooser );
+ QPoint p;
+ if ( QApplication::reverseLayout() ) {
+ if ( (mapToGlobal( m_chooser->rect().bottomLeft() ).y() + m_popupMenu->sizeHint().height()) <= screen.height() )
+ p = m_chooser->mapToGlobal( m_chooser->rect().bottomRight() );
+ else
+ p = m_chooser->mapToGlobal( m_chooser->rect().topRight() - QPoint( 0, m_popupMenu->sizeHint().height() ) );
+ p.rx() -= m_popupMenu->sizeHint().width();
+ }
+ else {
+ if ( (m_chooser->mapToGlobal( m_chooser->rect().bottomLeft() ).y() + m_popupMenu->sizeHint().height()) <= screen.height() )
+ p = m_chooser->mapToGlobal( m_chooser->rect().bottomLeft() );
+ else
+ p = m_chooser->mapToGlobal( m_chooser->rect().topLeft() - QPoint( 0, m_popupMenu->sizeHint().height() ) );
+ }
+ if (!m_popupMenu->isVisible() && on) {
+ m_popupMenu->exec( p, -1 );
+ m_popupMenu->setFocus();
+ }
+ //m_chooser->setDown( false );
+}*/
+
+void KexiDBImageBox::updateActionStrings()
+{
+ if (!m_popupMenu)
+ return;
+ if (m_designMode) {
+/* QString titleString( i18n("Image Box") );
+ if (!dataSource().isEmpty())
+ titleString.prepend(dataSource() + " : ");
+ m_popupMenu->changeTitle(m_popupMenu->idAt(0), m_popupMenu->titlePixmap(m_popupMenu->idAt(0)), titleString);*/
+ }
+ else {
+ //update title in data view mode, based on the data source
+ if (columnInfo()) {
+ KexiImageContextMenu::updateTitle( m_popupMenu, columnInfo()->captionOrAliasOrName(),
+ KexiFormPart::library()->iconName(className()) );
+ }
+ }
+
+ if (m_chooser) {
+ if (popupMenuAvailable() && dataSource().isEmpty()) { //this may work in the future (see @todo below)
+ QToolTip::add(m_chooser, i18n("Click to show actions for this image box"));
+ } else {
+ QString beautifiedImageBoxName;
+ if (m_designMode) {
+ beautifiedImageBoxName = dataSource();
+ }
+ else {
+ beautifiedImageBoxName = columnInfo() ? columnInfo()->captionOrAliasOrName() : QString::null;
+ /*! @todo look at makeFirstCharacterUpperCaseInCaptions setting [bool]
+ (see doc/dev/settings.txt) */
+ beautifiedImageBoxName = beautifiedImageBoxName[0].upper() + beautifiedImageBoxName.mid(1);
+ }
+ QToolTip::add(m_chooser, i18n("Click to show actions for \"%1\" image box").arg(beautifiedImageBoxName));
+ }
+ }
+}
+
+bool KexiDBImageBox::popupMenuAvailable()
+{
+/*! @todo add kexi-global setting which anyway, allows to show this button
+ (read-only actions like copy/save as/print can be available) */
+ //chooser button can be only visible when data source is specified
+ return !dataSource().isEmpty();
+}
+
+void KexiDBImageBox::setDataSource( const QString &ds )
+{
+ KexiFormDataItemInterface::setDataSource( ds );
+ setData(KexiBLOBBuffer::Handle());
+ updateActionStrings();
+ KexiFrame::setFocusPolicy( focusPolicy() ); //set modified policy
+
+ if (m_chooser) {
+ m_chooser->setEnabled(popupMenuAvailable());
+ if (m_dropDownButtonVisible && popupMenuAvailable()) {
+ m_chooser->show();
+ }
+ else {
+ m_chooser->hide();
+ }
+ }
+
+ // update some properties s not changed by user
+//! @todo get default line width from global style settings
+ if (!m_lineWidthChanged) {
+ KexiFrame::setLineWidth( ds.isEmpty() ? 0 : 1 );
+ }
+ if (!m_paletteBackgroundColorChanged && parentWidget()) {
+ KexiFrame::setPaletteBackgroundColor(
+ dataSource().isEmpty() ? parentWidget()->paletteBackgroundColor() : palette().active().base() );
+ }
+}
+
+QSize KexiDBImageBox::sizeHint() const
+{
+ if (pixmap().isNull())
+ return QSize(80, 80);
+ return pixmap().size();
+}
+
+int KexiDBImageBox::realLineWidth() const
+{
+ if (frameShape()==QFrame::Box && (frameShadow()==QFrame::Sunken || frameShadow()==QFrame::Raised))
+ return 2 * lineWidth();
+ else
+ return lineWidth();
+}
+
+void KexiDBImageBox::paintEvent( QPaintEvent *pe )
+{
+ if (!m_paintEventEnabled)
+ return;
+ QPainter p(this);
+ p.setClipRect(pe->rect());
+ const int m = realLineWidth() + margin();
+ QColor bg(eraseColor());
+ if (m_designMode && pixmap().isNull()) {
+ QPixmap pm(size()-QSize(m, m));
+ QPainter p2;
+ p2.begin(&pm, this);
+ p2.fillRect(0,0,width(),height(), bg);
+
+ updatePixmap();
+ QPixmap *imagBoxPm;
+ const bool tooLarge = (height()-m-m) <= KexiDBImageBox_pm->height();
+ if (tooLarge || (width()-m-m) <= KexiDBImageBox_pm->width())
+ imagBoxPm = KexiDBImageBox_pmSmall;
+ else
+ imagBoxPm = KexiDBImageBox_pm;
+ QImage img(imagBoxPm->convertToImage());
+ img = KImageEffect::flatten(img, bg.dark(150),
+ qGray( bg.rgb() ) <= 20 ? QColor(Qt::gray).dark(150) : bg.light(105));
+
+ QPixmap converted;
+ converted.convertFromImage(img);
+// if (tooLarge)
+// p2.drawPixmap(2, 2, converted);
+// else
+ p2.drawPixmap(2, height()-m-m-imagBoxPm->height()-2, converted);
+ QFont f(qApp->font());
+ p2.setFont(f);
+ p2.setPen( KexiUtils::contrastColor( bg ) );
+ p2.drawText(pm.rect(), Qt::AlignCenter,
+ dataSource().isEmpty()
+ ? QString::fromLatin1(name())+"\n"+i18n("Unbound Image Box", "(unbound)") //i18n("No Image")
+ : dataSource());
+ p2.end();
+ bitBlt(this, m, m, &pm);
+ }
+ else {
+ QSize internalSize(size());
+ if (m_chooser && m_dropDownButtonVisible && !dataSource().isEmpty())
+ internalSize.setWidth( internalSize.width() - m_chooser->width() );
+
+ //clearing needed here because we may need to draw a pixmap with transparency
+ p.fillRect(0,0,width(),height(), bg);
+
+ KexiUtils::drawPixmap( p, m, QRect(QPoint(0,0), internalSize), pixmap(), m_alignment,
+ m_scaledContents, m_keepAspectRatio );
+ }
+ KexiFrame::drawFrame( &p );
+
+ // if the widget is focused, draw focus indicator rect _if_ there is no chooser button
+ if (!m_designMode && !dataSource().isEmpty() && hasFocus() && (!m_chooser || !m_chooser->isVisible())) {
+ style().drawPrimitive(
+ QStyle::PE_FocusRect, &p, style().subRect(QStyle::SR_PushButtonContents, this),
+ palette().active() );
+ }
+}
+
+/* virtual void KexiDBImageBox::paletteChange ( const QPalette & oldPalette )
+{
+ QFrame::paletteChange(oldPalette);
+ if (oldPalette.active().background()!=palette().active().background()) {
+ delete KexiDBImageBox_pm;
+ KexiDBImageBox_pm = 0;
+ repaint();
+ }
+}*/
+
+void KexiDBImageBox::updatePixmap()
+{
+ if (! (m_designMode && pixmap().isNull()) )
+ return;
+
+ if (!KexiDBImageBox_pm) {
+ QString fname( locate("data", QString("kexi/pics/imagebox.png")) );
+ KexiDBImageBox_pmDeleter.setObject( KexiDBImageBox_pm, new QPixmap(fname, "PNG") );
+ QImage img(KexiDBImageBox_pm->convertToImage());
+ KexiDBImageBox_pmSmallDeleter.setObject( KexiDBImageBox_pmSmall,
+ new QPixmap( img.smoothScale(img.width()/2, img.height()/2, QImage::ScaleMin) ) );
+ }
+}
+
+void KexiDBImageBox::setAlignment(int alignment)
+{
+ m_alignment = alignment;
+ if (!m_scaledContents || m_keepAspectRatio)
+ repaint();
+}
+
+void KexiDBImageBox::setData(const KexiBLOBBuffer::Handle& handle)
+{
+ if (m_insideSetData) //avoid recursion
+ return;
+ m_insideSetData = true;
+ m_data = handle;
+ emit idChanged(handle.id());
+ m_insideSetData = false;
+ update();
+}
+
+void KexiDBImageBox::resizeEvent( QResizeEvent * e )
+{
+ KexiFrame::resizeEvent(e);
+ if (m_chooser) {
+ QSize s( m_chooser->sizeHint() );
+ QSize margin( realLineWidth(), realLineWidth() );
+ s.setHeight( height() - 2*margin.height() );
+ s = s.boundedTo( size()-2*margin );
+ m_chooser->resize( s );
+ m_chooser->move( QRect(QPoint(0,0), e->size() - m_chooser->size() - margin + QSize(1,1)).bottomRight() );
+ }
+}
+
+/*
+bool KexiDBImageBox::setProperty( const char * name, const QVariant & value )
+{
+ const bool ret = QLabel::setProperty(name, value);
+ if (p_shadowEnabled) {
+ if (0==qstrcmp("indent", name) || 0==qstrcmp("font", name) || 0==qstrcmp("margin", name)
+ || 0==qstrcmp("frameShadow", name) || 0==qstrcmp("frameShape", name)
+ || 0==qstrcmp("frameStyle", name) || 0==qstrcmp("midLineWidth", name)
+ || 0==qstrcmp("lineWidth", name)) {
+ p_privateLabel->setProperty(name, value);
+ updatePixmap();
+ }
+ }
+ return ret;
+}
+*/
+
+void KexiDBImageBox::setColumnInfo(KexiDB::QueryColumnInfo* cinfo)
+{
+ KexiFormDataItemInterface::setColumnInfo(cinfo);
+ //updating strings and title is needed
+ updateActionStrings();
+}
+
+bool KexiDBImageBox::keyPressed(QKeyEvent *ke)
+{
+ // Esc key should close the popup
+ if (ke->state() == Qt::NoButton && ke->key() == Qt::Key_Escape) {
+ if (m_popupMenu->isVisible()) {
+ m_setFocusOnButtonAfterClosingPopup = true;
+ return true;
+ }
+ }
+// else if (ke->state() == Qt::ControlButton && KStdAccel::shortcut(KStdAccel::Copy).keyCodeQt() == (ke->key()|Qt::CTRL)) {
+// }
+ return false;
+}
+
+void KexiDBImageBox::setLineWidth( int width )
+{
+ m_lineWidthChanged = true;
+ KexiFrame::setLineWidth(width);
+}
+
+void KexiDBImageBox::setPalette( const QPalette &pal )
+{
+ KexiFrame::setPalette(pal);
+ if (m_insideSetPalette)
+ return;
+ m_insideSetPalette = true;
+ setPaletteBackgroundColor(pal.active().base());
+ setPaletteForegroundColor(pal.active().foreground());
+ m_insideSetPalette = false;
+}
+
+void KexiDBImageBox::setPaletteBackgroundColor( const QColor & color )
+{
+ kexipluginsdbg << "KexiDBImageBox::setPaletteBackgroundColor(): " << color.name() << endl;
+ m_paletteBackgroundColorChanged = true;
+ KexiFrame::setPaletteBackgroundColor(color);
+ if (m_chooser)
+ m_chooser->setPalette( qApp->palette() );
+}
+
+bool KexiDBImageBox::dropDownButtonVisible() const
+{
+ return m_dropDownButtonVisible;
+}
+
+void KexiDBImageBox::setDropDownButtonVisible( bool set )
+{
+//! @todo use global default setting for this property
+ if (m_dropDownButtonVisible == set)
+ return;
+ m_dropDownButtonVisible = set;
+ if (m_chooser) {
+ if (m_dropDownButtonVisible)
+ m_chooser->show();
+ else
+ m_chooser->hide();
+ }
+}
+
+bool KexiDBImageBox::subwidgetStretchRequired(KexiDBAutoField* autoField) const
+{
+ Q_UNUSED(autoField);
+ return true;
+}
+
+bool KexiDBImageBox::eventFilter( QObject * watched, QEvent * e )
+{
+ if (watched==this || watched==m_chooser) { //we're watching chooser as well because it's a focus proxy even if invisible
+ if (e->type()==QEvent::FocusIn || e->type()==QEvent::FocusOut || e->type()==QEvent::MouseButtonPress) {
+ update(); //to repaint focus rect
+ }
+ }
+ // hide popup menu as soon as it loses focus
+ if (watched==m_popupMenu && e->type()==QEvent::FocusOut) {
+ m_popupMenu->hide();
+ }
+ return KexiFrame::eventFilter(watched, e);
+}
+
+QWidget::FocusPolicy KexiDBImageBox::focusPolicy() const
+{
+ if (dataSource().isEmpty())
+ return NoFocus;
+ return m_focusPolicyInternal;
+}
+
+QWidget::FocusPolicy KexiDBImageBox::focusPolicyInternal() const
+{
+ return m_focusPolicyInternal;
+}
+
+void KexiDBImageBox::setFocusPolicy( FocusPolicy policy )
+{
+ m_focusPolicyInternal = policy;
+ KexiFrame::setFocusPolicy( focusPolicy() ); //set modified policy
+}
+
+#include "kexidbimagebox.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbimagebox.h b/kexi/plugins/forms/widgets/kexidbimagebox.h
new file mode 100644
index 00000000..3ad2f710
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbimagebox.h
@@ -0,0 +1,275 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBImageBox_H
+#define KexiDBImageBox_H
+
+#include "kexiformdataiteminterface.h"
+#include "kexiframe.h"
+#include "kexidbutils.h"
+#include <kexiblobbuffer.h>
+
+class KexiDropDownButton;
+class KexiImageContextMenu;
+
+//! @short A data-aware, editable image box.
+/*! Can also act as a normal static image box.
+*/
+class KEXIFORMUTILS_EXPORT KexiDBImageBox :
+ public KexiFrame,
+ public KexiFormDataItemInterface,
+ public KexiSubwidgetInterface
+{
+ Q_OBJECT
+ Q_PROPERTY( QString dataSource READ dataSource WRITE setDataSource )
+ Q_PROPERTY( QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType )
+ Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
+// Q_PROPERTY( QPixmap pixmap READ pixmap WRITE setPixmap )
+// Q_PROPERTY( QByteArray pixmapData READ pixmapData WRITE setPixmapData )
+ Q_PROPERTY( uint pixmapId READ pixmapId WRITE setPixmapId DESIGNABLE true STORED false )
+ Q_PROPERTY( uint storedPixmapId READ storedPixmapId WRITE setStoredPixmapId DESIGNABLE false STORED true )
+ Q_PROPERTY( bool scaledContents READ hasScaledContents WRITE setScaledContents )
+ Q_PROPERTY( bool keepAspectRatio READ keepAspectRatio WRITE setKeepAspectRatio )
+ Q_PROPERTY( Alignment alignment READ alignment WRITE setAlignment )
+// Q_PROPERTY( QString originalFileName READ originalFileName WRITE setOriginalFileName DESIGNABLE false )
+// Q_OVERRIDE( FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy )
+ Q_PROPERTY( bool dropDownButtonVisible READ dropDownButtonVisible WRITE setDropDownButtonVisible )
+ Q_OVERRIDE( int lineWidth READ lineWidth WRITE setLineWidth )
+ Q_OVERRIDE( FocusPolicy focusPolicy READ focusPolicyInternal WRITE setFocusPolicy )
+
+ public:
+ KexiDBImageBox( bool designMode, QWidget *parent, const char *name = 0 );
+ virtual ~KexiDBImageBox();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+
+ virtual QVariant value(); // { return m_value.data(); }
+
+// QByteArray pixmapData() const { return m_value.data(); }
+
+ QPixmap pixmap() const;
+
+ uint pixmapId() const;
+
+ uint storedPixmapId() const;
+//
+ virtual void setInvalidState( const QString& displayText );
+
+ virtual bool valueIsNull();
+
+ virtual bool valueIsEmpty();
+
+ virtual QWidget* widget();
+
+ //! always true
+ virtual bool cursorAtStart();
+
+ //! always true
+ virtual bool cursorAtEnd();
+
+// //! used to catch setIndent(), etc.
+// virtual bool setProperty ( const char * name, const QVariant & value );
+
+ virtual bool isReadOnly() const;
+
+ bool hasScaledContents() const;
+
+// bool designMode() const { return m_designMode; }
+
+ int alignment() const { return m_alignment; }
+
+ bool keepAspectRatio() const { return m_keepAspectRatio; }
+
+ virtual QSize sizeHint() const;
+
+ KexiImageContextMenu *contextMenu() const;
+
+ /*! \return original file name of image loaded from a file.
+ This can be later reused for displaying the image within a collection (to be implemented)
+ or on saving the image data back to file. */
+//todo QString originalFileName() const { return m_value.originalFileName(); }
+
+ //! Reimplemented to override behaviour of "lineWidth" property.
+ virtual void setLineWidth( int width );
+
+ //! Reimplemented to override behaviour of "paletteBackgroundColor"
+ //! and "paletteForegroundColor" properties.
+ virtual void setPalette( const QPalette &pal );
+
+ //! Reimplemented to override behaviour of "paletteBackgroundColor" property.
+ virtual void setPaletteBackgroundColor( const QColor & color );
+
+ //! \return true id drop down button should be visible (the default).
+ bool dropDownButtonVisible() const;
+
+ //! For overridden property
+ int lineWidth() const { return KexiFrame::lineWidth(); }
+
+ /*! Overriden to change the policy behaviour a bit:
+ NoFocus is returned regardless the real focus flag
+ if the data source is empty (see dataSource()). */
+ FocusPolicy focusPolicy() const;
+
+ //! \return the internal focus policy value, i.e. the one unrelated to data source presence.
+ FocusPolicy focusPolicyInternal() const;
+
+ /*! Sets the internal focus policy value.
+ "Internal" means that if there is no data source set, real policy becomes NoFocus. */
+ virtual void setFocusPolicy( FocusPolicy policy );
+
+ public slots:
+ void setPixmapId(uint id);
+
+ void setStoredPixmapId(uint id);
+
+ //! Sets the datasource to \a ds
+ virtual void setDataSource( const QString &ds );
+
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+
+ virtual void setReadOnly(bool set);
+
+ //! Sets \a pixmapData data for this widget. If the widget has data source set,
+ //! the pixmap will be also placed inside of the buffer and saved later.
+//todo void setPixmapData(const QByteArray& pixmapData) { m_value.setData(pixmapData); }
+
+ /*! Sets original file name of image loaded from a file.
+ @see originalFileName() */
+//todo void setOriginalFileName(const QString& name) { m_value.setOriginalFileName(name); }
+
+ void setScaledContents(bool set);
+
+ void setAlignment(int alignment);
+
+ void setKeepAspectRatio(bool set);
+
+// void updateActionsAvailability();
+
+ //! @internal
+// void slotToggled( bool on );
+
+ //! \return sets dropDownButtonVisible property. @see dropDownButtonVisible()
+ void setDropDownButtonVisible( bool set );
+
+ //! Forces execution of "insert from file" action
+ void insertFromFile();
+
+ signals:
+ //! Used for db-aware mode. Emitted when value has been changed.
+ //! Actual value can be obtained using value().
+// virtual void pixmapChanged();
+// virtual void valueChanged(const QByteArray& data);
+
+ void idChanged(long id);
+
+ protected slots:
+ void slotUpdateActionsAvailabilityRequested(bool& valueIsNull, bool& valueIsReadOnly);
+
+ void handleInsertFromFileAction(const KURL& url);
+ void handleAboutToSaveAsAction(QString& origFilename, QString& fileExtension, bool& dataIsEmpty);
+ void handleSaveAsAction(const QString& fileName);
+ void handleCutAction();
+ void handleCopyAction();
+ void handlePasteAction();
+ virtual void clear();
+ void handleShowPropertiesAction();
+
+ protected:
+ //! \return data depending on the current mode (db-aware or static)
+ QByteArray data() const;
+
+ virtual void contextMenuEvent ( QContextMenuEvent * e );
+// virtual void mousePressEvent( QMouseEvent *e );
+ virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo);
+ virtual void paintEvent( QPaintEvent* );
+ virtual void resizeEvent( QResizeEvent* e );
+ virtual bool eventFilter( QObject * watched, QEvent * e );
+
+ //! Sets value \a value for a widget.
+ virtual void setValueInternal( const QVariant& add, bool removeOld ) {
+ setValueInternal( add, removeOld, true /*loadPixmap*/ );
+ }
+
+ //! @internal, added \a loadPixmap option used by paste().
+ void setValueInternal( const QVariant& add, bool removeOld, bool loadPixmap );
+
+ //! Updates i18n'd action strings after datasource change
+ void updateActionStrings();
+ void updatePixmap();
+
+ //! @internal
+ void setData(const KexiBLOBBuffer::Handle& handle);
+
+ bool popupMenuAvailable();
+
+ /*! Called by top-level form on key press event.
+ Used for Key_Escape to if the popup is visible,
+ so the key press won't be consumed to perform "cancel editing". */
+ virtual bool keyPressed(QKeyEvent *ke);
+
+ //! \return real line width, i.e. for Boxed sunken or Boxed raised
+ //! frames returns doubled width value.
+ int realLineWidth() const;
+
+ //! Implemented for KexiSubwidgetInterface
+ virtual bool subwidgetStretchRequired(KexiDBAutoField* autoField) const;
+
+// virtual void drawContents ( QPainter *p );
+
+// virtual void fontChange( const QFont& font );
+// virtual void styleChange( QStyle& style );
+// virtual void enabledChange( bool enabled );
+
+// virtual void paletteChange( const QPalette& pal );
+// virtual void frameChanged();
+// virtual void showEvent( QShowEvent* e );
+
+// void updatePixmapLater();
+// class ImageLabel;
+// ImageLabel *m_pixmapLabel;
+ QPixmap m_pixmap;
+ QByteArray m_value; //!< for db-aware mode
+ QString m_valueMimeType; //!< for db-aware mode
+// PixmapData m_value;
+ KexiBLOBBuffer::Handle m_data;
+// QString m_originalFileName;
+ KexiDropDownButton *m_chooser;
+ KexiImageContextMenu *m_popupMenu;
+//moved KActionCollection m_actionCollection;
+//moved KAction *m_insertFromFileAction, *m_saveAsAction, *m_cutAction, *m_copyAction, *m_pasteAction,
+// *m_deleteAction, *m_propertiesAction;
+// QTimer m_clickTimer;
+ int m_alignment;
+ FocusPolicy m_focusPolicyInternal; //!< Used for focusPolicyInternal()
+ bool m_designMode : 1;
+ bool m_readOnly : 1;
+ bool m_scaledContents : 1;
+ bool m_keepAspectRatio : 1;
+ bool m_insideSetData : 1;
+ bool m_setFocusOnButtonAfterClosingPopup : 1;
+ bool m_lineWidthChanged : 1;
+ bool m_paletteBackgroundColorChanged : 1;
+ bool m_paintEventEnabled : 1; //!< used to disable paintEvent()
+ bool m_dropDownButtonVisible : 1;
+ bool m_insideSetPalette : 1;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbintspinbox.cpp b/kexi/plugins/forms/widgets/kexidbintspinbox.cpp
new file mode 100644
index 00000000..ac923347
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbintspinbox.cpp
@@ -0,0 +1,114 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbintspinbox.h"
+
+#include <qlineedit.h>
+#include <knumvalidator.h>
+
+KexiDBIntSpinBox::KexiDBIntSpinBox(QWidget *parent, const char *name)
+ : KIntSpinBox(parent, name) , KexiFormDataItemInterface()
+{
+ connect(this, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged()));
+}
+
+KexiDBIntSpinBox::~KexiDBIntSpinBox()
+{
+}
+
+void KexiDBIntSpinBox::setInvalidState( const QString& displayText )
+{
+ m_invalidState = true;
+ setEnabled(false);
+ setReadOnly(true);
+//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ?
+ if (focusPolicy() & TabFocus)
+ setFocusPolicy(QWidget::ClickFocus);
+ setSpecialValueText(displayText);
+ KIntSpinBox::setValue(minValue());
+}
+
+void
+KexiDBIntSpinBox::setEnabled(bool enabled)
+{
+ // prevent the user from reenabling the widget when it is in invalid state
+ if(enabled && m_invalidState)
+ return;
+ KIntSpinBox::setEnabled(enabled);
+}
+
+void KexiDBIntSpinBox::setValueInternal(const QVariant&, bool)
+{
+ KIntSpinBox::setValue(m_origValue.toInt());
+}
+
+QVariant
+KexiDBIntSpinBox::value()
+{
+ return KIntSpinBox::value();
+}
+
+void KexiDBIntSpinBox::slotValueChanged()
+{
+ signalValueChanged();
+}
+
+bool KexiDBIntSpinBox::valueIsNull()
+{
+ return cleanText().isEmpty();
+}
+
+bool KexiDBIntSpinBox::valueIsEmpty()
+{
+ return false;
+}
+
+bool KexiDBIntSpinBox::isReadOnly() const
+{
+ return editor()->isReadOnly();
+}
+
+void KexiDBIntSpinBox::setReadOnly(bool set)
+{
+ editor()->setReadOnly(set);
+}
+
+QWidget*
+KexiDBIntSpinBox::widget()
+{
+ return this;
+}
+
+bool KexiDBIntSpinBox::cursorAtStart()
+{
+ return false; //! \todo ?
+}
+
+bool KexiDBIntSpinBox::cursorAtEnd()
+{
+ return false; //! \todo ?
+}
+
+void KexiDBIntSpinBox::clear()
+{
+ KIntSpinBox::setValue(minValue()); //! \todo ?
+}
+
+#include "kexidbintspinbox.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbintspinbox.h b/kexi/plugins/forms/widgets/kexidbintspinbox.h
new file mode 100644
index 00000000..cddc614e
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbintspinbox.h
@@ -0,0 +1,80 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBIntSpinBox_H
+#define KexiDBIntSpinBox_H
+
+#include "kexiformdataiteminterface.h"
+#include <qwidget.h>
+#include <knuminput.h>
+
+//! @short A db-aware int spin box
+class KEXIFORMUTILS_EXPORT KexiDBIntSpinBox : public KIntSpinBox, public KexiFormDataItemInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+ Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true )
+
+ public:
+ KexiDBIntSpinBox(QWidget *parent, const char *name=0);
+ virtual ~KexiDBIntSpinBox();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+ virtual QVariant value();
+ virtual void setInvalidState( const QString& displayText );
+
+ //! \return true if editor's value is null (not empty)
+ //! Used for checking if a given constraint within table of form is met.
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not necessary null).
+ //! Only few data types can accept "EMPTY" property
+ //! (use KexiDB::Field::hasEmptyProperty() to check this).
+ //! Used for checking if a given constraint within table or form is met.
+ virtual bool valueIsEmpty();
+
+ /*! \return 'readOnly' flag for this widget. */
+ virtual bool isReadOnly() const;
+
+ /*! \return the view widget of this item, e.g. line edit widget. */
+ virtual QWidget* widget();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+ virtual void clear();
+
+ virtual void setEnabled(bool enabled);
+
+ public slots:
+ inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); }
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+ void slotValueChanged();
+ virtual void setReadOnly(bool set);
+
+ protected:
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ private:
+ bool m_invalidState : 1;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidblabel.cpp b/kexi/plugins/forms/widgets/kexidblabel.cpp
new file mode 100644
index 00000000..e30cc19e
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidblabel.cpp
@@ -0,0 +1,650 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de>
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidblabel.h"
+
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kimageeffect.h>
+
+#include <kexidb/field.h>
+#include <kexiutils/utils.h>
+
+#define SHADOW_OFFSET_X 3
+#define SHADOW_OFFSET_Y 3
+#define SHADOW_FACTOR 16.0
+#define SHADOW_OPACITY 50.0
+#define SHADOW_AXIS_FACTOR 2.0
+#define SHADOW_DIAGONAL_FACTOR 1.0
+#define SHADOW_THICKNESS 1
+
+//! @internal
+class KexiDBInternalLabel : public QLabel {
+ friend class KexiDBLabel;
+ public:
+ KexiDBInternalLabel( KexiDBLabel* );
+ virtual ~KexiDBInternalLabel();
+
+ protected:
+ void updateFrame();
+
+ QImage makeShadow( const QImage& textImage, const QColor &bgColor, const QRect& boundingRect );
+ QRect getBounding( const QImage &image, const QRect& startRect );
+// double defaultDecay( QImage& source, int i, int j );
+ KPixmap getShadowPixmap();
+
+ QRect m_shadowRect;
+ KexiDBLabel *m_parentLabel;
+};
+
+KexiDBInternalLabel::KexiDBInternalLabel( KexiDBLabel* parent )
+ : QLabel( parent )
+ , m_parentLabel(parent)
+{
+ int a = alignment() | Qt::WordBreak;
+ a &= (0xffffff ^ Qt::AlignVertical_Mask);
+ a |= Qt::AlignTop;
+ setAlignment( a );
+ updateFrame();
+}
+
+void KexiDBInternalLabel::updateFrame()
+{
+ setIndent(m_parentLabel->indent());
+ setMargin(m_parentLabel->margin());
+ setFont(m_parentLabel->font());
+
+ setFrameShadow(m_parentLabel->frameShadow());
+ setFrameShape(m_parentLabel->frameShape());
+ setFrameStyle(m_parentLabel->frameStyle());
+ setMidLineWidth(m_parentLabel->midLineWidth());
+ setLineWidth(m_parentLabel->lineWidth());
+}
+
+KexiDBInternalLabel::~KexiDBInternalLabel()
+{
+}
+
+/*!
+* This method is copied from kdebase/kdesktop/kshadowengine.cpp
+* Some modifactions were made.
+* --
+* Christian Nitschkowski
+*/
+QImage KexiDBInternalLabel::makeShadow( const QImage& textImage,
+ const QColor &bgColor, const QRect& boundingRect )
+{
+ QImage result;
+ QString origText( text() );
+
+ // create a new image for for the shaddow
+ const int w = textImage.width();
+ const int h = textImage.height();
+
+ // avoid calling these methods for every pixel
+ const int bgRed = bgColor.red();
+ const int bgGreen = bgColor.green();
+ const int bgBlue = bgColor.blue();
+
+ const int startX = boundingRect.x() + SHADOW_THICKNESS;
+ const int startY = boundingRect.y() + SHADOW_THICKNESS;
+ const int effectWidth = boundingRect.bottomRight().x() - SHADOW_THICKNESS;
+ const int effectHeight = boundingRect.bottomRight().y() - SHADOW_THICKNESS;
+// const int period = (effectWidth - startX) / 10;
+
+ double alphaShadow;
+
+ /*
+ * This is the source pixmap
+ */
+ QImage img = textImage.convertDepth( 32 );
+
+ /*
+ * Resize the image if necessary
+ */
+ if ( ( result.width() != w ) || ( result.height() != h ) ) {
+ result.create( w, h, 32 );
+ }
+
+// result.fill( 0 ); // all black
+ double realOpacity = SHADOW_OPACITY + QMIN(50.0/double(256.0-qGray(bgColor.rgb())), 50.0);
+ //int _h, _s, _v;
+ //.getHsv( &_h, &_s, &_v );
+ if (colorGroup().background()==Qt::red)//_s>=250 && _v>=250) //for colors like cyan or red, make the result more white
+ realOpacity += 50.0;
+ result.fill( (int)realOpacity );
+ result.setAlphaBuffer( true );
+
+ for ( int i = startX; i < effectWidth; i++ ) {
+ for ( int j = startY; j < effectHeight; j++ ) {
+ /*!
+ * This method is copied from kdebase/kdesktop/kshadowengine.cpp
+ * Some modifactions were made.
+ * --
+ * Christian Nitschkowski
+ */
+ if ( ( i < 1 ) || ( j < 1 ) || ( i > img.width() - 2 ) || ( j > img.height() - 2 ) )
+ continue;
+ else
+ alphaShadow = ( qGray( img.pixel( i - 1, j - 1 ) ) * SHADOW_DIAGONAL_FACTOR +
+ qGray( img.pixel( i - 1, j ) ) * SHADOW_AXIS_FACTOR +
+ qGray( img.pixel( i - 1, j + 1 ) ) * SHADOW_DIAGONAL_FACTOR +
+ qGray( img.pixel( i , j - 1 ) ) * SHADOW_AXIS_FACTOR +
+ 0 +
+ qGray( img.pixel( i , j + 1 ) ) * SHADOW_AXIS_FACTOR +
+ qGray( img.pixel( i + 1, j - 1 ) ) * SHADOW_DIAGONAL_FACTOR +
+ qGray( img.pixel( i + 1, j ) ) * SHADOW_AXIS_FACTOR +
+ qGray( img.pixel( i + 1, j + 1 ) ) * SHADOW_DIAGONAL_FACTOR ) / SHADOW_FACTOR;
+
+ // update the shadow's i,j pixel.
+ if (alphaShadow > 0)
+ result.setPixel( i, j, qRgba( bgRed, bgGreen , bgBlue,
+ ( int ) (( alphaShadow > realOpacity ) ? realOpacity : alphaShadow)
+ ) );
+ }
+/*caused too much redraw problems if (period && i % period) {
+ qApp->processEvents();
+ if (text() != origText) //text has been changed in the meantime: abort
+ return QImage();
+ }*/
+ }
+ return result;
+}
+
+KPixmap KexiDBInternalLabel::getShadowPixmap() {
+ /*!
+ * Backup the default color used to draw text.
+ */
+ const QColor textColor = colorGroup().foreground();
+
+ /*!
+ * Temporary storage for the generated shadow
+ */
+ KPixmap finalPixmap, tempPixmap;
+ QImage shadowImage, tempImage;
+ QPainter painter;
+
+ m_shadowRect = QRect();
+
+ tempPixmap.resize( size() );
+ tempPixmap.fill( Qt::black );
+ tempPixmap.setMask( tempPixmap.createHeuristicMask( true ) );
+
+ /*!
+ * The textcolor has to be white for creating shadows!
+ */
+ setPaletteForegroundColor( Qt::white );
+
+ /*!
+ Draw the label "as usual" in a pixmap
+ */
+ painter.begin( &tempPixmap );
+ painter.setFont( font() );
+ drawContents( &painter );
+ painter.end();
+ setPaletteForegroundColor( textColor );
+
+ /*!
+ * Calculate the first bounding rect.
+ * This will fit around the unmodified text.
+ */
+ shadowImage = tempPixmap;
+ tempPixmap.setMask( QBitmap() );
+
+ /*!
+ Get the first bounding rect.
+ This may speed up makeShadow later.
+ */
+ m_shadowRect = getBounding( shadowImage, m_shadowRect );
+
+ /*!
+ * Enlarge the bounding rect to make sure the shadow
+ * will fit in.
+ * The new rect has to fit in the pixmap.
+ * I have to admit this isn't really nice code...
+ */
+ m_shadowRect.setX( QMAX( m_shadowRect.x() - ( m_shadowRect.width() / 4 ), 0 ) );
+ m_shadowRect.setY( QMAX( m_shadowRect.y() - ( m_shadowRect.height() / 4 ), 0 ) );
+ m_shadowRect.setBottomRight( QPoint(
+ QMIN( m_shadowRect.x() + ( m_shadowRect.width() * 3 / 2 ), shadowImage.width() ),
+ QMIN( m_shadowRect.y() + ( m_shadowRect.height() * 3 / 2 ), shadowImage.height() ) ) );
+
+ shadowImage = makeShadow( shadowImage,
+ qGray( colorGroup().background().rgb() ) < 127 ? Qt::white : Qt::black,
+ m_shadowRect );
+ if (shadowImage.isNull())
+ return KPixmap();
+
+ /*!
+ Now get the final bounding rect.
+ */
+ m_shadowRect = getBounding( shadowImage, m_shadowRect );
+
+ /*!
+ Paint the labels background in a new pixmap.
+ */
+ finalPixmap.resize( size() );
+ painter.begin( &finalPixmap );
+ painter.fillRect( 0, 0, finalPixmap.width(), finalPixmap.height(),
+ palette().brush(
+ isEnabled() ? QPalette::Active : QPalette::Disabled,
+ QColorGroup::Background ) );
+ painter.end();
+
+ /*!
+ Copy the part of the background the shadow will be on
+ to another pixmap.
+ */
+ tempPixmap.resize( m_shadowRect.size() );
+ if (!finalPixmap.isNull()) {
+ bitBlt( &tempPixmap, 0, 0, &finalPixmap,
+ m_shadowRect.x() + SHADOW_OFFSET_X,
+ m_shadowRect.y() + SHADOW_OFFSET_Y,
+ m_shadowRect.width(),
+ m_shadowRect.height() );
+ }
+ /*!
+ Replace the big background pixmap with the
+ part we could out just before.
+ */
+ finalPixmap = tempPixmap;
+
+ /*!
+ Copy the "interesting" part of the shadow image
+ to a new image.
+ I tried to copy this to a pixmap directly,
+ but it didn't work correctly.
+ Maybe a Qt bug?
+ */
+ tempImage = shadowImage.copy( m_shadowRect );
+ tempPixmap.convertFromImage( tempImage );
+ /*!
+ Anyways, merge the shadow with the background.
+ */
+ if (!tempPixmap.isNull()) {
+ bitBlt( &finalPixmap, 0, 0, &tempPixmap );
+ }
+
+ /**
+ Now move the rect.
+ Don't do this before the shadow is copied from shadowImage!
+ */
+ m_shadowRect.moveBy( SHADOW_OFFSET_X, SHADOW_OFFSET_Y );
+
+ return finalPixmap;
+}
+
+QRect KexiDBInternalLabel::getBounding( const QImage &image, const QRect& startRect ) {
+ QPoint topLeft;
+ QPoint bottomRight;
+
+ const int startX = startRect.x();
+ const int startY = startRect.y();
+ /*!
+ * Ugly beast to get the correct width and height
+ */
+ const int width = QMIN( ( startRect.bottomRight().x() > 0
+ ? startRect.bottomRight().x() : QCOORD_MAX ),
+ image.width() );
+ const int height = QMIN( ( startRect.bottomRight().y() > 0
+ ? startRect.bottomRight().y() : QCOORD_MAX ),
+ image.height() );
+
+ /*!
+ Assume the first pixel has the color of the
+ background that has to be cut away.
+ Qt uses the four corner pixels to guess the
+ correct color, but in this case the topleft
+ pixel should be enough.
+ */
+ QRgb trans = image.pixel( 0, 0 );
+
+ for ( int y = startY; y < height; y++ ) {
+ for ( int x = startX; x < width; x++ ) {
+ if ( image.pixel( x, y ) != trans ) {
+ topLeft.setY( y );
+ y = height;
+ break;
+ }
+ }
+ }
+
+ for ( int x = startX; x < width; x++ ) {
+ for ( int y = startY; y < height; y++ ) {
+ if ( image.pixel( x, y ) != trans ) {
+ topLeft.setX( x );
+ x = width;
+ break;
+ }
+ }
+ }
+
+ for ( int y = height - 1; y > topLeft.y(); y-- ) {
+ for ( int x = width - 1; x > topLeft.x(); x-- ) {
+ if ( image.pixel( x, y ) != trans ) {
+ bottomRight.setY( y + 1 );
+ y = 0;
+ break;
+ }
+ }
+ }
+
+ for ( int x = width - 1; x > topLeft.x(); x-- ) {
+ for ( int y = height - 1; y > topLeft.y(); y-- ) {
+ if ( image.pixel( x, y ) != trans ) {
+ bottomRight.setX( x + 1 );
+ x = 0;
+ break;
+ }
+ }
+ }
+
+ return QRect(
+ topLeft.x(),
+ topLeft.y(),
+ bottomRight.x() - topLeft.x(),
+ bottomRight.y() - topLeft.y() );
+}
+
+//=========================================================
+
+//! @internal
+class KexiDBLabel::Private
+{
+ public:
+ Private()
+ : timer(0)
+// , autonumberDisplayParameters(0)
+ , pixmapDirty( true )
+ , shadowEnabled( false )
+ , resizeEvent( false )
+ {
+ }
+ ~Private() {}
+ KPixmap shadowPixmap;
+ QPoint shadowPosition;
+ KexiDBInternalLabel* internalLabel;
+ QTimer* timer;
+ QColor frameColor;
+ bool pixmapDirty : 1;
+ bool shadowEnabled : 1;
+ bool resizeEvent : 1;
+};
+
+//=========================================================
+
+KexiDBLabel::KexiDBLabel( QWidget *parent, const char *name, WFlags f )
+ : QLabel( parent, name, f )
+ , KexiDBTextWidgetInterface()
+ , KexiFormDataItemInterface()
+ , d( new Private() )
+{
+ init();
+}
+
+KexiDBLabel::KexiDBLabel( const QString& text, QWidget *parent, const char *name, WFlags f )
+ : QLabel( parent, name, f )
+ , KexiDBTextWidgetInterface()
+ , KexiFormDataItemInterface()
+ , d( new Private() )
+{
+ init();
+ setText( text );
+}
+
+KexiDBLabel::~KexiDBLabel()
+{
+ delete d;
+}
+
+void KexiDBLabel::init()
+{
+ m_hasFocusableWidget = false;
+ d->internalLabel = new KexiDBInternalLabel( this );
+ d->internalLabel->hide();
+ d->frameColor = palette().active().foreground();
+
+ setAlignment( d->internalLabel->alignment() );
+}
+
+void KexiDBLabel::updatePixmapLater() {
+ if (d->resizeEvent) {
+ if (!d->timer) {
+ d->timer = new QTimer(this, "KexiDBLabelTimer");
+ connect(d->timer, SIGNAL(timeout()), this, SLOT(updatePixmap()));
+ }
+ d->timer->start(100, true);
+ d->resizeEvent = false;
+ return;
+ }
+ if (d->timer && d->timer->isActive())
+ return;
+ updatePixmap();
+}
+
+void KexiDBLabel::updatePixmap() {
+ /*!
+ Whatever has changed in KexiDBLabel,
+ every parameter is set to our private-label.
+ Just in case...
+ */
+ d->internalLabel->setText( text() );
+ d->internalLabel->setFixedSize( size() );
+ d->internalLabel->setPalette( palette() );
+ d->internalLabel->setAlignment( alignment() );
+// d->shadowPixmap = KPixmap(); //parallel repaints won't hurt us cause incomplete pixmap
+ KPixmap shadowPixmap = d->internalLabel->getShadowPixmap();
+ if (shadowPixmap.isNull())
+ return;
+ d->shadowPixmap = shadowPixmap;
+ d->shadowPosition = d->internalLabel->m_shadowRect.topLeft();
+ d->pixmapDirty = false;
+ repaint();
+}
+
+void KexiDBLabel::paintEvent( QPaintEvent* e )
+{
+ QPainter p( this );
+ if ( d->shadowEnabled ) {
+ /*!
+ If required, update the pixmap-cache.
+ */
+ if ( d->pixmapDirty ) {
+ updatePixmapLater();
+ }
+
+ /*!
+ If the part that should be redrawn intersects with our shadow,
+ redraw the shadow where it intersects with e->rect().
+ Have to move the clipping rect around a bit because
+ the shadow has to be drawn using an offset relative to
+ the widgets border.
+ */
+ if ( !d->pixmapDirty && e->rect().contains( d->shadowPosition ) && !d->shadowPixmap.isNull()) {
+ QRect clipRect = QRect(
+ QMAX( e->rect().x() - d->shadowPosition.x(), 0 ),
+ QMAX( e->rect().y() - d->shadowPosition.y(), 0 ),
+ QMIN( e->rect().width() + d->shadowPosition.x(), d->shadowPixmap.width() ),
+ QMIN( e->rect().height() + d->shadowPosition.y(), d->shadowPixmap.height() ) );
+ p.drawPixmap( d->internalLabel->m_shadowRect.topLeft(), d->shadowPixmap, clipRect );
+ }
+ }
+ KexiDBTextWidgetInterface::paint( this, &p, text().isEmpty(), alignment(), false );
+ QLabel::paintEvent( e );
+}
+
+void KexiDBLabel::setValueInternal( const QVariant& add, bool removeOld ) {
+ if (removeOld)
+ setText(add.toString());
+ else
+ setText( m_origValue.toString() + add.toString() );
+}
+
+QVariant KexiDBLabel::value() {
+ return text();
+}
+
+void KexiDBLabel::setInvalidState( const QString& displayText )
+{
+ setText( displayText );
+}
+
+bool KexiDBLabel::valueIsNull()
+{
+ return text().isNull();
+}
+
+bool KexiDBLabel::valueIsEmpty()
+{
+ return text().isEmpty();
+}
+
+bool KexiDBLabel::isReadOnly() const
+{
+ return true;
+}
+
+void KexiDBLabel::setReadOnly( bool readOnly )
+{
+ Q_UNUSED(readOnly);
+}
+
+QWidget* KexiDBLabel::widget()
+{
+ return this;
+}
+
+bool KexiDBLabel::cursorAtStart()
+{
+ return false;
+}
+
+bool KexiDBLabel::cursorAtEnd()
+{
+ return false;
+}
+
+void KexiDBLabel::clear()
+{
+ setText(QString::null);
+}
+
+bool KexiDBLabel::setProperty( const char * name, const QVariant & value )
+{
+ const bool ret = QLabel::setProperty(name, value);
+ if (d->shadowEnabled) {
+ if (0==qstrcmp("indent", name) || 0==qstrcmp("font", name) || 0==qstrcmp("margin", name)
+ || 0==qstrcmp("frameShadow", name) || 0==qstrcmp("frameShape", name)
+ || 0==qstrcmp("frameStyle", name) || 0==qstrcmp("midLineWidth", name)
+ || 0==qstrcmp("lineWidth", name)) {
+ d->internalLabel->setProperty(name, value);
+ updatePixmap();
+ }
+ }
+ return ret;
+}
+
+void KexiDBLabel::setColumnInfo(KexiDB::QueryColumnInfo* cinfo)
+{
+ KexiFormDataItemInterface::setColumnInfo(cinfo);
+ KexiDBTextWidgetInterface::setColumnInfo(cinfo, this);
+}
+
+void KexiDBLabel::setShadowEnabled( bool state ) {
+ d->shadowEnabled = state;
+ d->pixmapDirty = true;
+ if (state)
+ d->internalLabel->updateFrame();
+ repaint();
+}
+
+void KexiDBLabel::resizeEvent( QResizeEvent* e ) {
+ if (isVisible())
+ d->resizeEvent = true;
+ d->pixmapDirty = true;
+ QLabel::resizeEvent( e );
+}
+
+void KexiDBLabel::fontChange( const QFont& font ) {
+ d->pixmapDirty = true;
+ d->internalLabel->setFont( font );
+ QLabel::fontChange( font );
+}
+
+void KexiDBLabel::styleChange( QStyle& style ) {
+ d->pixmapDirty = true;
+ QLabel::styleChange( style );
+}
+
+void KexiDBLabel::enabledChange( bool enabled ) {
+ d->pixmapDirty = true;
+ d->internalLabel->setEnabled( enabled );
+ QLabel::enabledChange( enabled );
+}
+
+void KexiDBLabel::paletteChange( const QPalette& oldPal ) {
+ Q_UNUSED(oldPal);
+ d->pixmapDirty = true;
+ d->internalLabel->setPalette( palette() );
+}
+
+/*const QColor & KexiDBLabel::paletteForegroundColor () const
+{
+ return d->foregroundColor;
+}
+
+void KexiDBLabel::setPaletteForegroundColor ( const QColor& color )
+{
+ d->foregroundColor = color;
+}*/
+
+void KexiDBLabel::frameChanged() {
+ d->pixmapDirty = true;
+ d->internalLabel->updateFrame();
+ QFrame::frameChanged();
+}
+
+void KexiDBLabel::showEvent( QShowEvent* e ) {
+ d->pixmapDirty = true;
+ QLabel::showEvent( e );
+}
+
+void KexiDBLabel::setText( const QString& text ) {
+ d->pixmapDirty = true;
+ QLabel::setText( text );
+ //This is necessary for KexiFormDataItemInterface
+ valueChanged();
+ repaint();
+}
+
+bool KexiDBLabel::shadowEnabled() const
+{
+ return d->shadowEnabled;
+}
+
+#define ClassName KexiDBLabel
+#define SuperClassName QLabel
+#include "kexiframeutils_p.cpp"
+#include "kexidblabel.moc"
diff --git a/kexi/plugins/forms/widgets/kexidblabel.h b/kexi/plugins/forms/widgets/kexidblabel.h
new file mode 100644
index 00000000..ec4e626a
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidblabel.h
@@ -0,0 +1,140 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de>
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIDBLABEL_H
+#define KEXIDBLABEL_H
+
+#include <qimage.h>
+#include <qlabel.h>
+
+#include <kpixmap.h>
+
+#include "../kexiformdataiteminterface.h"
+#include "../kexidbtextwidgetinterface.h"
+#include <widget/utils/kexidisplayutils.h>
+
+class QPainter;
+class QTimer;
+class KexiDBInternalLabel;
+
+//! @short An extended, data-aware, read-only text label.
+/*! It's text may have a drop-shadow.
+
+ @author Christian Nitschkowski, Jaroslaw Staniek
+*/
+class KEXIFORMUTILS_EXPORT KexiDBLabel : public QLabel, protected KexiDBTextWidgetInterface, public KexiFormDataItemInterface {
+ Q_OBJECT
+ Q_PROPERTY( QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true )
+ Q_PROPERTY( QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true )
+ Q_PROPERTY( bool shadowEnabled READ shadowEnabled WRITE setShadowEnabled DESIGNABLE true )
+ Q_OVERRIDE( QPixmap pixmap DESIGNABLE false )
+ Q_OVERRIDE( bool scaledContents DESIGNABLE false )
+// Q_OVERRIDE( QColor paletteForegroundColor READ paletteForegroundColor WRITE setPaletteForegroundColor DESIGNABLE true )
+ Q_PROPERTY( QColor frameColor READ frameColor WRITE setFrameColor DESIGNABLE true )
+
+ public:
+ KexiDBLabel( QWidget *parent, const char *name = 0, WFlags f = 0 );
+ KexiDBLabel( const QString& text, QWidget *parent, const char *name = 0, WFlags f = 0 );
+ virtual ~KexiDBLabel();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+
+ virtual QVariant value();
+
+ bool shadowEnabled() const;
+
+ virtual void setInvalidState( const QString& displayText );
+
+ virtual bool valueIsNull();
+
+ virtual bool valueIsEmpty();
+
+ //! always true
+ virtual bool isReadOnly() const;
+
+ virtual QWidget* widget();
+
+ //! always false
+ virtual bool cursorAtStart();
+
+ //! always false
+ virtual bool cursorAtEnd();
+
+ virtual void clear();
+
+ //! used to catch setIndent(), etc.
+ virtual bool setProperty ( const char * name, const QVariant & value );
+
+ virtual const QColor& frameColor() const;
+
+// const QColor & paletteForegroundColor() const;
+
+ public slots:
+ //! Sets the datasource to \a ds
+ inline void setDataSource( const QString &ds ) { KexiFormDataItemInterface::setDataSource( ds ); }
+
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+
+ virtual void setText( const QString& text );
+
+ /*! Enable/Disable the shadow effect.
+ KexiDBLabel acts just like a normal QLabel when shadow is disabled. */
+ void setShadowEnabled( bool state );
+
+ virtual void setPalette( const QPalette &pal );
+
+ virtual void setFrameColor(const QColor& color);
+
+// void setPaletteForegroundColor( const QColor& color );
+
+ protected slots:
+ //! empty
+ virtual void setReadOnly( bool readOnly );
+ void updatePixmap();
+
+ protected:
+ void init();
+ virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo);
+ virtual void paintEvent( QPaintEvent* );
+ virtual void resizeEvent( QResizeEvent* e );
+
+ //! Sets value \a value for a widget.
+ virtual void setValueInternal( const QVariant& add, bool removeOld );
+
+ virtual void fontChange( const QFont& font );
+ virtual void styleChange( QStyle& style );
+ virtual void enabledChange( bool enabled );
+
+ virtual void paletteChange( const QPalette& oldPal );
+ virtual void frameChanged();
+ virtual void showEvent( QShowEvent* e );
+
+ //! Reimplemented to paint using real frame color instead of froeground.
+ //! Also allows to paint more types of frame.
+ virtual void drawFrame( QPainter * );
+
+ void updatePixmapLater();
+
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidblineedit.cpp b/kexi/plugins/forms/widgets/kexidblineedit.cpp
new file mode 100644
index 00000000..3897a8cb
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidblineedit.cpp
@@ -0,0 +1,417 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidblineedit.h"
+#include "kexidbautofield.h"
+
+#include <kdebug.h>
+#include <knumvalidator.h>
+#include <kdatetbl.h>
+
+#include <qpopupmenu.h>
+#include <qpainter.h>
+
+#include <kexiutils/utils.h>
+#include <kexidb/queryschema.h>
+#include <kexidb/fieldvalidator.h>
+#include <kexiutils/utils.h>
+
+//! @todo reenable as an app aption
+//#define USE_KLineEdit_setReadOnly
+
+//! @internal A validator used for read only flag to disable editing
+class KexiDBLineEdit_ReadOnlyValidator : public QValidator
+{
+ public:
+ KexiDBLineEdit_ReadOnlyValidator( QObject * parent )
+ : QValidator(parent)
+ {
+ }
+ ~KexiDBLineEdit_ReadOnlyValidator() {}
+ virtual State validate( QString &, int & ) const { return Invalid; }
+};
+
+//-----
+
+KexiDBLineEdit::KexiDBLineEdit(QWidget *parent, const char *name)
+ : KLineEdit(parent, name)
+ , KexiDBTextWidgetInterface()
+ , KexiFormDataItemInterface()
+//moved , m_dateFormatter(0)
+//moved , m_timeFormatter(0)
+ , m_menuExtender(this, this)
+ , m_internalReadOnly(false)
+ , m_slotTextChanged_enabled(true)
+{
+#ifdef USE_KLineEdit_setReadOnly
+//! @todo reenable as an app aption
+ QPalette p(widget->palette());
+ p.setColor( lighterGrayBackgroundColor(palette()) );
+ widget->setPalette(p);
+#endif
+
+ connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged(const QString&)));
+}
+
+KexiDBLineEdit::~KexiDBLineEdit()
+{
+//moved delete m_dateFormatter;
+//moved delete m_timeFormatter;
+}
+
+void KexiDBLineEdit::setInvalidState( const QString& displayText )
+{
+ KLineEdit::setReadOnly(true);
+//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ?
+ if (focusPolicy() & TabFocus)
+ setFocusPolicy(QWidget::ClickFocus);
+ setText(displayText);
+}
+
+void KexiDBLineEdit::setValueInternal(const QVariant& add, bool removeOld)
+{
+#if 0 //moved to KexiTextFormatter
+ QVariant value;
+ if (removeOld)
+ value = add;
+ else {
+ if (add.toString().isEmpty())
+ value = m_origValue;
+ else
+ value = m_origValue.toString() + add.toString();
+ }
+
+ if (m_columnInfo) {
+ const KexiDB::Field::Type t = m_columnInfo->field->type();
+ if (t == KexiDB::Field::Boolean) {
+ //! @todo temporary solution for booleans!
+ setText( value.toBool() ? "1" : "0" );
+ return;
+ }
+ else if (t == KexiDB::Field::Date) {
+ setText( dateFormatter()->dateToString( value.toString().isEmpty() ? QDate() : value.toDate() ) );
+ setCursorPosition(0); //ok?
+ return;
+ }
+ else if (t == KexiDB::Field::Time) {
+ setText(
+ timeFormatter()->timeToString(
+ //hack to avoid converting null variant to valid QTime(0,0,0)
+ value.toString().isEmpty() ? value.toTime() : QTime(99,0,0)
+ )
+ );
+ setCursorPosition(0); //ok?
+ return;
+ }
+ else if (t == KexiDB::Field::DateTime) {
+ if (value.toString().isEmpty() ) {
+ setText( QString::null );
+ }
+ else {
+ setText(
+ dateFormatter()->dateToString( value.toDateTime().date() ) + " " +
+ timeFormatter()->timeToString( value.toDateTime().time() )
+ );
+ }
+ setCursorPosition(0); //ok?
+ return;
+ }
+ }
+#endif
+ m_slotTextChanged_enabled = false;
+ setText( m_textFormatter.valueToText(removeOld ? QVariant() : m_origValue, add.toString()) );
+// setText( value.toString() );
+ setCursorPosition(0); //ok?
+ m_slotTextChanged_enabled = true;
+}
+
+QVariant KexiDBLineEdit::value()
+{
+ return m_textFormatter.textToValue( text() );
+#if 0 // moved to KexiTextFormatter
+ if (! m_columnInfo)
+ return QVariant();
+ const KexiDB::Field::Type t = m_columnInfo->field->type();
+ switch (t) {
+ case KexiDB::Field::Text:
+ case KexiDB::Field::LongText:
+ return text();
+ case KexiDB::Field::Byte:
+ case KexiDB::Field::ShortInteger:
+ return text().toShort();
+//! @todo uint, etc?
+ case KexiDB::Field::Integer:
+ return text().toInt();
+ case KexiDB::Field::BigInteger:
+ return text().toLongLong();
+ case KexiDB::Field::Boolean:
+ //! @todo temporary solution for booleans!
+ return text() == "1" ? QVariant(true,1) : QVariant(false,0);
+ case KexiDB::Field::Date:
+ return dateFormatter()->stringToVariant( text() );
+ case KexiDB::Field::Time:
+ return timeFormatter()->stringToVariant( text() );
+ case KexiDB::Field::DateTime:
+ return stringToDateTime(*dateFormatter(), *timeFormatter(), text());
+ case KexiDB::Field::Float:
+ return text().toFloat();
+ case KexiDB::Field::Double:
+ return text().toDouble();
+ default:
+ return QVariant();
+ }
+//! @todo more data types!
+ return text();
+#endif
+}
+
+void KexiDBLineEdit::slotTextChanged(const QString&)
+{
+ if (!m_slotTextChanged_enabled)
+ return;
+ signalValueChanged();
+}
+
+bool KexiDBLineEdit::valueIsNull()
+{
+ return valueIsEmpty(); //ok??? text().isNull();
+}
+
+bool KexiDBLineEdit::valueIsEmpty()
+{
+ return m_textFormatter.valueIsEmpty( text() );
+#if 0 // moved to KexiTextFormatter
+ if (text().isEmpty())
+ return true;
+
+ if (m_columnInfo) {
+ const KexiDB::Field::Type t = m_columnInfo->field->type();
+ if (t == KexiDB::Field::Date || )
+ return dateFormatter()->isEmpty( text() );
+ else if (t == KexiDB::Field::Time)
+ return timeFormatter()->isEmpty( text() );
+ else if (t == KexiDB::Field::Time)
+ return dateTimeIsEmpty( *dateFormatter(), *timeFormatter(), text() );
+ }
+
+//! @todo
+ return text().isEmpty();
+#endif
+}
+
+bool KexiDBLineEdit::valueIsValid()
+{
+ return m_textFormatter.valueIsValid( text() );
+#if 0 // moved to KexiTextFormatter
+ if (!m_columnInfo)
+ return true;
+//! @todo fix for fields with "required" property = true
+ if (valueIsEmpty()/*ok?*/)
+ return true;
+
+ const KexiDB::Field::Type t = m_columnInfo->field->type();
+ if (t == KexiDB::Field::Date)
+ return dateFormatter()->stringToVariant( text() ).isValid();
+ else if (t == KexiDB::Field::Time)
+ return timeFormatter()->stringToVariant( text() ).isValid();
+ else if (t == KexiDB::Field::DateTime)
+ return dateTimeIsValid( *dateFormatter(), *timeFormatter(), text() );
+
+//! @todo
+ return true;
+#endif
+}
+
+bool KexiDBLineEdit::isReadOnly() const
+{
+ return m_internalReadOnly;
+}
+
+void KexiDBLineEdit::setReadOnly( bool readOnly )
+{
+#ifdef USE_KLineEdit_setReadOnly
+//! @todo reenable as an app aption
+ return KLineEdit::setReadOnly( readOnly );
+#else
+ m_internalReadOnly = readOnly;
+ if (m_internalReadOnly) {
+ m_readWriteValidator = validator();
+ if (!m_readOnlyValidator)
+ m_readOnlyValidator = new KexiDBLineEdit_ReadOnlyValidator(this);
+ setValidator( m_readOnlyValidator );
+ }
+ else {
+ //revert to r/w validator
+ setValidator( m_readWriteValidator );
+ }
+ m_menuExtender.updatePopupMenuActions();
+#endif
+}
+
+QPopupMenu * KexiDBLineEdit::createPopupMenu()
+{
+ QPopupMenu *contextMenu = KLineEdit::createPopupMenu();
+ m_menuExtender.createTitle(contextMenu);
+ return contextMenu;
+}
+
+
+QWidget* KexiDBLineEdit::widget()
+{
+ return this;
+}
+
+bool KexiDBLineEdit::cursorAtStart()
+{
+ return cursorPosition()==0;
+}
+
+bool KexiDBLineEdit::cursorAtEnd()
+{
+ return cursorPosition()==(int)text().length();
+}
+
+void KexiDBLineEdit::clear()
+{
+ if (!m_internalReadOnly)
+ KLineEdit::clear();
+}
+
+
+void KexiDBLineEdit::setColumnInfo(KexiDB::QueryColumnInfo* cinfo)
+{
+ KexiFormDataItemInterface::setColumnInfo(cinfo);
+ m_textFormatter.setField( cinfo ? cinfo->field : 0 );
+
+ if (!cinfo)
+ return;
+
+//! @todo handle input mask (via QLineEdit::setInputMask()) using a special KexiDB::FieldInputMask class
+ setValidator( new KexiDB::FieldValidator(*cinfo->field, this) );
+
+#if 0 // moved to KexiTextFormatter
+ if (t==KexiDB::Field::Date) {
+//! @todo use KDateWidget?
+ setInputMask( dateFormatter()->inputMask() );
+ }
+ else if (t==KexiDB::Field::Time) {
+//! @todo use KTimeWidget
+// setInputMask("00:00:00");
+ setInputMask( timeFormatter()->inputMask() );
+ }
+ else if (t==KexiDB::Field::DateTime) {
+ setInputMask(
+ dateTimeInputMask( *dateFormatter(), *timeFormatter() ) );
+ }
+#endif
+ const QString inputMask( m_textFormatter.inputMask() );
+ if (!inputMask.isEmpty())
+ setInputMask( inputMask );
+
+ KexiDBTextWidgetInterface::setColumnInfo(cinfo, this);
+}
+
+/*todo
+void KexiDBLineEdit::paint( QPainter *p )
+{
+ KexiDBTextWidgetInterface::paint( this, &p, text().isEmpty(), alignment(), hasFocus() );
+}*/
+
+void KexiDBLineEdit::paintEvent ( QPaintEvent *pe )
+{
+ KLineEdit::paintEvent( pe );
+ QPainter p(this);
+ KexiDBTextWidgetInterface::paint( this, &p, text().isEmpty(), alignment(), hasFocus() );
+}
+
+bool KexiDBLineEdit::event( QEvent * e )
+{
+ const bool ret = KLineEdit::event( e );
+ KexiDBTextWidgetInterface::event(e, this, text().isEmpty());
+ if (e->type()==QEvent::FocusOut) {
+ QFocusEvent *fe = static_cast<QFocusEvent *>(e);
+// if (fe->reason()!=QFocusEvent::ActiveWindow && fe->reason()!=QFocusEvent::Popup) {
+ if (fe->reason()==QFocusEvent::Tab || fe->reason()==QFocusEvent::Backtab) {
+ //display aligned to left after loosing the focus (only if this is tab/backtab event)
+//! @todo add option to set cursor at the beginning
+ setCursorPosition(0); //ok?
+ }
+ }
+ return ret;
+}
+
+bool KexiDBLineEdit::appendStretchRequired(KexiDBAutoField* autoField) const
+{
+ return KexiDBAutoField::Top == autoField->labelPosition();
+}
+
+void KexiDBLineEdit::handleAction(const QString& actionName)
+{
+ if (actionName=="edit_copy") {
+ copy();
+ }
+ else if (actionName=="edit_paste") {
+ paste();
+ }
+ else if (actionName=="edit_cut") {
+ cut();
+ }
+ //! @todo ?
+}
+
+void KexiDBLineEdit::setDisplayDefaultValue(QWidget *widget, bool displayDefaultValue)
+{
+ KexiFormDataItemInterface::setDisplayDefaultValue(widget, displayDefaultValue);
+ // initialize display parameters for default / entered value
+ KexiDisplayUtils::DisplayParameters * const params
+ = displayDefaultValue ? m_displayParametersForDefaultValue : m_displayParametersForEnteredValue;
+ setFont(params->font);
+ QPalette pal(palette());
+ pal.setColor(QPalette::Active, QColorGroup::Text, params->textColor);
+ setPalette(pal);
+}
+
+void KexiDBLineEdit::undo()
+{
+ cancelEditor();
+}
+
+void KexiDBLineEdit::moveCursorToEnd()
+{
+ KLineEdit::end(false/*!mark*/);
+}
+
+void KexiDBLineEdit::moveCursorToStart()
+{
+ KLineEdit::home(false/*!mark*/);
+}
+
+void KexiDBLineEdit::selectAll()
+{
+ KLineEdit::selectAll();
+}
+
+bool KexiDBLineEdit::keyPressed(QKeyEvent *ke)
+{
+ Q_UNUSED(ke);
+ return false;
+}
+
+#include "kexidblineedit.moc"
diff --git a/kexi/plugins/forms/widgets/kexidblineedit.h b/kexi/plugins/forms/widgets/kexidblineedit.h
new file mode 100644
index 00000000..5f0262b2
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidblineedit.h
@@ -0,0 +1,170 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBLineEdit_H
+#define KexiDBLineEdit_H
+
+#include <klineedit.h>
+#include <qvalidator.h>
+
+#include "kexiformdataiteminterface.h"
+#include "kexidbtextwidgetinterface.h"
+#include "kexidbutils.h"
+#include <widget/tableview/kexitextformatter.h>
+#include <widget/utils/kexidatetimeformatter.h>
+
+class KexiDBWidgetContextMenuExtender;
+
+/*! @internal Utility: alter background color to be a blended color
+ of the background and base (usually lighter gray). Used for read-only mode. */
+void setLighterGrayBackgroundColor(QWidget* widget);
+
+//! @short Line edit widget for Kexi forms
+/*! Handles many data types. User input is validated by using validators
+ and/or input masks.
+*/
+class KEXIFORMUTILS_EXPORT KexiDBLineEdit :
+ public KLineEdit,
+ protected KexiDBTextWidgetInterface,
+ public KexiFormDataItemInterface,
+ public KexiSubwidgetInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+ Q_OVERRIDE(bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true)
+
+ public:
+ KexiDBLineEdit(QWidget *parent, const char *name=0);
+ virtual ~KexiDBLineEdit();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+ virtual QVariant value();
+ virtual void setInvalidState( const QString& displayText );
+
+ //! \return true if editor's value is null (not empty)
+ //! Used for checking if a given constraint within table of form is met.
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not necessary null).
+ //! Only few data types can accept "EMPTY" property
+ //! (use KexiDB::Field::hasEmptyProperty() to check this).
+ //! Used for checking if a given constraint within table or form is met.
+ virtual bool valueIsEmpty();
+
+ /*! \return true if the value is valid */
+ virtual bool valueIsValid();
+
+ /*! \return 'readOnly' flag for this widget. */
+ virtual bool isReadOnly() const;
+
+ /*! If \a displayDefaultValue is true, the value set by KexiDataItemInterface::setValue()
+ is displayed in a special way. Used by KexiFormDataProvider::fillDataItems().
+ \a widget is equal to 'this'.
+ Reimplemented after KexiFormDataItemInterface. */
+ virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue);
+
+ /*! \return the view widget of this item, e.g. line edit widget. */
+ virtual QWidget* widget();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+ virtual void clear();
+
+ virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo);
+
+ /*! Handles action having standard name \a actionName.
+ Action could be: "edit_copy", "edit_paste", etc.
+ Reimplemented after KexiDataItemChangesListener. */
+ virtual void handleAction(const QString& actionName);
+
+ /*! Called by top-level form on key press event to consume widget-specific shortcuts. */
+ virtual bool keyPressed(QKeyEvent *ke);
+
+ public slots:
+ inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); }
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+ virtual void setReadOnly( bool readOnly );
+
+ //! Reimplemented, so "undo" means the same as "cancelEditor" action
+ virtual void undo();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToEnd();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToStart();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void selectAll();
+
+ protected slots:
+ void slotTextChanged(const QString&);
+
+ protected:
+ virtual void paintEvent ( QPaintEvent * );
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+ virtual bool event ( QEvent * );
+
+#if 0
+//moved to KexiTextFormatter
+ inline KexiDateFormatter* dateFormatter() {
+ return m_dateFormatter ? m_dateFormatter : m_dateFormatter = new KexiDateFormatter();
+ }
+
+ inline KexiTimeFormatter* timeFormatter() {
+ return m_timeFormatter ? m_timeFormatter : m_timeFormatter = new KexiTimeFormatter();
+ }
+#endif
+
+ virtual QPopupMenu * createPopupMenu();
+
+ //! Implemented for KexiSubwidgetInterface
+ virtual bool appendStretchRequired(KexiDBAutoField* autoField) const;
+
+#if 0
+//moved to KexiTextFormatter
+ //! Used for date and date/time types
+ KexiDateFormatter* m_dateFormatter;
+ //! Used for time and date/time types
+ KexiTimeFormatter* m_timeFormatter;
+#endif
+ //! Used to format text
+ KexiTextFormatter m_textFormatter;
+
+ //! Used for read only flag to disable editing
+ QGuardedPtr<const QValidator> m_readOnlyValidator;
+
+ //! Used to remember the previous validator used forf r/w mode, after setting the read only flag
+ QGuardedPtr<const QValidator> m_readWriteValidator;
+
+ //! Used for extending context menu
+ KexiDBWidgetContextMenuExtender m_menuExtender;
+
+ //! Used in isReadOnly, as sometimes we want to have the flag set tot true when KLineEdit::isReadOnly
+ //! is still false.
+ bool m_internalReadOnly : 1;
+
+ //! Used in slotTextChanged()
+ bool m_slotTextChanged_enabled : 1;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbsubform.cpp b/kexi/plugins/forms/widgets/kexidbsubform.cpp
new file mode 100644
index 00000000..8d1971a9
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbsubform.cpp
@@ -0,0 +1,131 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbsubform.h"
+
+#include "kexidbform.h"
+#include "../kexiformview.h"
+#include <kexidb/utils.h>
+#include <formeditor/formIO.h>
+#include <formeditor/objecttree.h>
+#include <formeditor/utils.h>
+#include <formeditor/container.h>
+#include <formeditor/formmanager.h>
+
+KexiDBSubForm::KexiDBSubForm(KFormDesigner::Form *parentForm, QWidget *parent, const char *name)
+: QScrollView(parent, name), m_parentForm(parentForm), m_form(0), m_widget(0)
+{
+ setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
+ viewport()->setPaletteBackgroundColor(colorGroup().mid());
+}
+/*
+void
+KexiDBSubForm::paintEvent(QPaintEvent *ev)
+{
+ QScrollView::paintEvent(ev);
+ QPainter p;
+
+ setWFlags(WPaintUnclipped);
+
+ QString txt("Subform");
+ QFont f = font();
+ f.setPointSize(f.pointSize() * 3);
+ QFontMetrics fm(f);
+ const int txtw = fm.width(txt), txth = fm.height();
+
+ p.begin(this, true);
+ p.setPen(black);
+ p.setFont(f);
+ p.drawText(width()/2, height()/2, txt, Qt::AlignCenter|Qt::AlignVCenter);
+ p.end();
+
+ clearWFlags( WPaintUnclipped );
+}
+*/
+void
+KexiDBSubForm::setFormName(const QString &name)
+{
+ if(m_formName==name)
+ return;
+
+ m_formName = name; //assign, even if the name points to nowhere
+
+ if(name.isEmpty()) {
+ delete m_widget;
+ m_widget = 0;
+ updateScrollBars();
+ return;
+ }
+
+ QWidget *pw = parentWidget();
+ KexiFormView *view = 0;
+ QStringList list;
+ while(pw) {
+ if(pw->isA("KexiDBSubForm")) {
+ if(list.contains(pw->name())) {
+//! @todo error message
+ return; // Be sure to don't run into a endless-loop cause of recursive subforms.
+ }
+ list.append(pw->name());
+ }
+ else if(! view && pw->isA("KexiFormView"))
+ view = static_cast<KexiFormView*>(pw); // we need a KexiFormView*
+ pw = pw->parentWidget();
+ }
+
+ if (!view || !view->parentDialog() || !view->parentDialog()->mainWin()
+ || !view->parentDialog()->mainWin()->project()->dbConnection())
+ return;
+
+ KexiDB::Connection *conn = view->parentDialog()->mainWin()->project()->dbConnection();
+
+ // we check if there is a form with this name
+ int id = KexiDB::idForObjectName(*conn, name, KexiPart::FormObjectType);
+ if((id == 0) || (id == view->parentDialog()->id())) // == our form
+ return; // because of recursion when loading
+
+ // we create the container widget
+ delete m_widget;
+ m_widget = new KexiDBFormBase(viewport(), "KexiDBSubForm_widget");
+ m_widget->show();
+ addChild(m_widget);
+ m_form = new KFormDesigner::Form(KexiFormPart::library(), this->name());
+ m_form->createToplevel(m_widget);
+
+ // and load the sub form
+ QString data;
+ tristate res = conn->loadDataBlock(id, data, QString::null);
+ if (res == true)
+ res = KFormDesigner::FormIO::loadFormFromString(m_form, m_widget, data);
+ if(res != true) {
+ delete m_widget;
+ m_widget = 0;
+ updateScrollBars();
+ m_formName = QString::null;
+ return;
+ }
+ m_form->setDesignMode(false);
+
+ // Install event filters on the whole newly created form
+ KFormDesigner::ObjectTreeItem *tree = m_parentForm->objectTree()->lookup(QObject::name());
+ KFormDesigner::installRecursiveEventFilter(this, tree->eventEater());
+}
+
+#include "kexidbsubform.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbsubform.h b/kexi/plugins/forms/widgets/kexidbsubform.h
new file mode 100644
index 00000000..5b73f860
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbsubform.h
@@ -0,0 +1,52 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBSubForm_H
+#define KexiDBSubForm_H
+
+#include <qscrollview.h>
+#include <formeditor/form.h>
+
+//! @short A form embedded as a widget inside other form
+class KEXIFORMUTILS_EXPORT KexiDBSubForm : public QScrollView
+{
+ Q_OBJECT
+ Q_PROPERTY(QString formName READ formName WRITE setFormName DESIGNABLE true)
+
+ public:
+ KexiDBSubForm(KFormDesigner::Form *parentForm, QWidget *parent, const char *name);
+ ~KexiDBSubForm() {}
+
+ //! \return the name of the subform to display inside this widget
+ QString formName() const { return m_formName; }
+
+ //! Sets the name of the subform to display inside this widget
+ void setFormName(const QString &name);
+
+ //void paintEvent(QPaintEvent *ev);
+
+ private:
+ KFormDesigner::Form *m_parentForm;
+ KFormDesigner::Form *m_form;
+ QWidget *m_widget;
+ QString m_formName;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbtextedit.cpp b/kexi/plugins/forms/widgets/kexidbtextedit.cpp
new file mode 100644
index 00000000..8541fc01
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbtextedit.cpp
@@ -0,0 +1,209 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbtextedit.h"
+#include "kexidblineedit.h"
+#include <kexidb/queryschema.h>
+
+#include <kapplication.h>
+#include <kstdaccel.h>
+#include <kdebug.h>
+
+#include <qpainter.h>
+
+KexiDBTextEdit::KexiDBTextEdit(QWidget *parent, const char *name)
+ : KTextEdit(parent, name)
+ , KexiDBTextWidgetInterface()
+ , KexiFormDataItemInterface()
+ , m_menuExtender(this, this)
+ , m_slotTextChanged_enabled(true)
+{
+ connect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
+ installEventFilter(this);
+}
+
+KexiDBTextEdit::~KexiDBTextEdit()
+{
+}
+
+void KexiDBTextEdit::setInvalidState( const QString& displayText )
+{
+ setReadOnly(true);
+//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ?
+ if (focusPolicy() & TabFocus)
+ setFocusPolicy(QWidget::ClickFocus);
+ KTextEdit::setText(displayText);
+}
+
+void KexiDBTextEdit::setValueInternal(const QVariant& add, bool removeOld)
+{
+ if (m_columnInfo && m_columnInfo->field->type()==KexiDB::Field::Boolean) {
+//! @todo temporary solution for booleans!
+ KTextEdit::setText( add.toBool() ? "1" : "0" );
+ }
+ else {
+ if (removeOld)
+ KTextEdit::setText( add.toString() );
+ else
+ KTextEdit::setText( m_origValue.toString() + add.toString() );
+ }
+}
+
+QVariant KexiDBTextEdit::value()
+{
+ return text();
+}
+
+void KexiDBTextEdit::slotTextChanged()
+{
+ if (!m_slotTextChanged_enabled)
+ return;
+ signalValueChanged();
+}
+
+bool KexiDBTextEdit::valueIsNull()
+{
+ return text().isNull();
+}
+
+bool KexiDBTextEdit::valueIsEmpty()
+{
+ return text().isEmpty();
+}
+
+bool KexiDBTextEdit::isReadOnly() const
+{
+ return KTextEdit::isReadOnly();
+}
+
+void KexiDBTextEdit::setReadOnly( bool readOnly )
+{
+ KTextEdit::setReadOnly( readOnly );
+ QPalette p = palette();
+ QColor c(readOnly ? lighterGrayBackgroundColor(kapp->palette()) : p.color(QPalette::Normal, QColorGroup::Base));
+ setPaper( c );
+ p.setColor(QColorGroup::Base, c);
+ p.setColor(QColorGroup::Background, c);
+ setPalette( p );
+}
+
+void KexiDBTextEdit::setText( const QString & text, const QString & context )
+{
+ KTextEdit::setText(text, context);
+}
+
+QWidget* KexiDBTextEdit::widget()
+{
+ return this;
+}
+
+bool KexiDBTextEdit::cursorAtStart()
+{
+ int para, index;
+ getCursorPosition ( &para, &index );
+ return para==0 && index==0;
+}
+
+bool KexiDBTextEdit::cursorAtEnd()
+{
+ int para, index;
+ getCursorPosition ( &para, &index );
+ return (paragraphs()-1)==para && (paragraphLength(paragraphs()-1)-1)==index;
+}
+
+void KexiDBTextEdit::clear()
+{
+ setText(QString::null, QString::null);
+}
+
+void KexiDBTextEdit::setColumnInfo(KexiDB::QueryColumnInfo* cinfo)
+{
+ KexiFormDataItemInterface::setColumnInfo(cinfo);
+ if (!cinfo)
+ return;
+ KexiDBTextWidgetInterface::setColumnInfo(m_columnInfo, this);
+}
+
+void KexiDBTextEdit::paintEvent ( QPaintEvent *pe )
+{
+ KTextEdit::paintEvent( pe );
+ QPainter p(this);
+ KexiDBTextWidgetInterface::paint( this, &p, text().isEmpty(), alignment(), hasFocus() );
+}
+
+QPopupMenu * KexiDBTextEdit::createPopupMenu(const QPoint & pos)
+{
+ QPopupMenu *contextMenu = KTextEdit::createPopupMenu(pos);
+ m_menuExtender.createTitle(contextMenu);
+ return contextMenu;
+}
+
+void KexiDBTextEdit::undo()
+{
+ cancelEditor();
+}
+
+void KexiDBTextEdit::setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue)
+{
+ KexiFormDataItemInterface::setDisplayDefaultValue(widget, displayDefaultValue);
+ // initialize display parameters for default / entered value
+ KexiDisplayUtils::DisplayParameters * const params
+ = displayDefaultValue ? m_displayParametersForDefaultValue : m_displayParametersForEnteredValue;
+ QPalette pal(palette());
+ pal.setColor(QPalette::Active, QColorGroup::Text, params->textColor);
+ setPalette(pal);
+ setFont(params->font);
+//! @todo support rich text...
+/* m_slotTextChanged_enabled = false;
+ //for rich text...
+ const QString origText( text() );
+ KTextEdit::setText(QString::null);
+ setCurrentFont(params->font);
+ setColor(params->textColor);
+ KTextEdit::setText(origText);
+ m_slotTextChanged_enabled = true;*/
+}
+
+void KexiDBTextEdit::moveCursorToEnd()
+{
+ KTextEdit::setCursorPosition(paragraphs()-1, paragraphLength( paragraphs()-1 ));
+}
+
+void KexiDBTextEdit::moveCursorToStart()
+{
+ KTextEdit::setCursorPosition(0 /*para*/, 0 /*index*/);
+}
+
+void KexiDBTextEdit::selectAll()
+{
+ KTextEdit::selectAll();
+}
+
+void KexiDBTextEdit::keyPressEvent( QKeyEvent *ke )
+{
+ // for instance, Windows uses Ctrl+Tab for moving between tabs, so do not steal this shortcut
+ if (KStdAccel::tabNext().contains( KKey(ke) ) || KStdAccel::tabPrev().contains( KKey(ke) )) {
+ ke->ignore();
+ return;
+ }
+ KTextEdit::keyPressEvent(ke);
+}
+
+#include "kexidbtextedit.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbtextedit.h b/kexi/plugins/forms/widgets/kexidbtextedit.h
new file mode 100644
index 00000000..a380b070
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbtextedit.h
@@ -0,0 +1,113 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBTextEdit_H
+#define KexiDBTextEdit_H
+
+#include "kexiformdataiteminterface.h"
+#include "kexidbtextwidgetinterface.h"
+#include "kexidbutils.h"
+#include <ktextedit.h>
+
+//! @short Multiline edit widget for Kexi forms
+class KEXIFORMUTILS_EXPORT KexiDBTextEdit :
+ public KTextEdit,
+ protected KexiDBTextWidgetInterface,
+ public KexiFormDataItemInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+
+ public:
+ KexiDBTextEdit(QWidget *parent, const char *name=0);
+ virtual ~KexiDBTextEdit();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+ virtual QVariant value();
+ virtual void setInvalidState( const QString& displayText );
+
+ //! \return true if editor's value is null (not empty)
+ //! Used for checking if a given constraint within table of form is met.
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not necessary null).
+ //! Only few data types can accept "EMPTY" property
+ //! (use KexiDB::Field::hasEmptyProperty() to check this).
+ //! Used for checking if a given constraint within table or form is met.
+ virtual bool valueIsEmpty();
+
+ /*! \return 'readOnly' flag for this widget. */
+ virtual bool isReadOnly() const;
+
+ /*! \return the view widget of this item, e.g. line edit widget. */
+ virtual QWidget* widget();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+ virtual void clear();
+
+ virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo);
+
+ /*! If \a displayDefaultValue is true, the value set by KexiDataItemInterface::setValue()
+ is displayed in a special way. Used by KexiFormDataProvider::fillDataItems().
+ \a widget is equal to 'this'.
+ Reimplemented after KexiFormDataItemInterface. */
+ virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue);
+
+ //! Windows uses Ctrl+Tab for moving between tabs, so do not steal this shortcut
+ virtual void keyPressEvent( QKeyEvent *ke );
+
+ public slots:
+ inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); }
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+ virtual void setReadOnly( bool readOnly );
+ virtual void setText( const QString & text, const QString & context );
+
+ //! Reimplemented, so "undo" means the same as "cancelEditor" action
+//! @todo enable "real" undo internally so user can use ctrl+z while editing
+ virtual void undo();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToEnd();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void moveCursorToStart();
+
+ //! Implemented for KexiDataItemInterface
+ virtual void selectAll();
+
+ protected slots:
+ void slotTextChanged();
+
+ protected:
+ virtual void paintEvent ( QPaintEvent * );
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+ QPopupMenu * createPopupMenu(const QPoint & pos);
+
+ //! Used for extending context menu
+ KexiDBWidgetContextMenuExtender m_menuExtender;
+
+ //! Used to disable slotTextChanged()
+ bool m_slotTextChanged_enabled : 1;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbtimeedit.cpp b/kexi/plugins/forms/widgets/kexidbtimeedit.cpp
new file mode 100644
index 00000000..82e61b83
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbtimeedit.cpp
@@ -0,0 +1,156 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbtimeedit.h"
+
+#include <qtoolbutton.h>
+#include <qlayout.h>
+#include <qpainter.h>
+
+#include <kpopupmenu.h>
+#include <kdatepicker.h>
+#include <kdatetbl.h>
+#include <kexiutils/utils.h>
+
+KexiDBTimeEdit::KexiDBTimeEdit(const QTime &time, QWidget *parent, const char *name)
+ : QTimeEdit(time, parent, name), KexiFormDataItemInterface()
+{
+ m_invalidState = false;
+ setAutoAdvance(true);
+ m_cleared = false;
+
+#ifdef QDateTimeEditor_HACK
+ m_dte_time = KexiUtils::findFirstChild<QDateTimeEditor>(this, "QDateTimeEditor");
+#else
+ m_dte_time = 0;
+#endif
+
+ connect(this, SIGNAL(valueChanged(const QTime&)), this, SLOT(slotValueChanged(const QTime&)));
+}
+
+KexiDBTimeEdit::~KexiDBTimeEdit()
+{
+}
+
+void KexiDBTimeEdit::setInvalidState( const QString&)
+{
+ setEnabled(false);
+ setReadOnly(true);
+ m_invalidState = true;
+//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ?
+ if (focusPolicy() & TabFocus)
+ setFocusPolicy(QWidget::ClickFocus);
+}
+
+void
+KexiDBTimeEdit::setEnabled(bool enabled)
+{
+ // prevent the user from reenabling the widget when it is in invalid state
+ if(enabled && m_invalidState)
+ return;
+ QTimeEdit::setEnabled(enabled);
+}
+
+void KexiDBTimeEdit::setValueInternal(const QVariant &add, bool removeOld)
+{
+ m_cleared = !m_origValue.isValid();
+
+ int setNumberOnFocus = -1;
+ QTime t;
+ QString addString(add.toString());
+ if (removeOld) {
+ if (!addString.isEmpty() && addString[0].latin1()>='0' && addString[0].latin1() <='9') {
+ setNumberOnFocus = addString[0].latin1()-'0';
+ t = QTime(setNumberOnFocus, 0, 0);
+ }
+ }
+ else
+ t = m_origValue.toTime();
+
+ setTime(t);
+}
+
+QVariant
+KexiDBTimeEdit::value()
+{
+ //QDateTime - a hack needed because QVariant(QTime) has broken isNull()
+ return QVariant(QDateTime( m_cleared ? QDate() : QDate(0,1,2)/*nevermind*/, time()));
+}
+
+bool KexiDBTimeEdit::valueIsNull()
+{
+ return !time().isValid() || time().isNull();
+}
+
+bool KexiDBTimeEdit::valueIsEmpty()
+{
+ return m_cleared;
+}
+
+bool KexiDBTimeEdit::isReadOnly() const
+{
+ //! @todo: data/time edit API has no readonly flag,
+ //! so use event filter to avoid changes made by keyboard or mouse when m_readOnly==true
+ return m_readOnly; //!isEnabled();
+}
+
+void KexiDBTimeEdit::setReadOnly(bool set)
+{
+ m_readOnly = set;
+}
+
+QWidget*
+KexiDBTimeEdit::widget()
+{
+ return this;
+}
+
+bool KexiDBTimeEdit::cursorAtStart()
+{
+#ifdef QDateTimeEditor_HACK
+ return m_dte_time && hasFocus() && m_dte_time->focusSection()==0;
+#else
+ return false;
+#endif
+}
+
+bool KexiDBTimeEdit::cursorAtEnd()
+{
+#ifdef QDateTimeEditor_HACK
+ return m_dte_time && hasFocus()
+ && m_dte_time->focusSection()==int(m_dte_time->sectionCount()-1);
+#else
+ return false;
+#endif
+}
+
+void KexiDBTimeEdit::clear()
+{
+ setTime(QTime());
+ m_cleared = true;
+}
+
+void
+KexiDBTimeEdit::slotValueChanged(const QTime&)
+{
+ m_cleared = false;
+}
+
+#include "kexidbtimeedit.moc"
diff --git a/kexi/plugins/forms/widgets/kexidbtimeedit.h b/kexi/plugins/forms/widgets/kexidbtimeedit.h
new file mode 100644
index 00000000..9665b1f9
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbtimeedit.h
@@ -0,0 +1,87 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiDBTimeEdit_H
+#define KexiDBTimeEdit_H
+
+#include "kexiformdataiteminterface.h"
+#include "kexidbtextwidgetinterface.h"
+#include <qdatetimeedit.h>
+
+class QDateTimeEditor;
+
+//! @short A db-aware time editor
+class KEXIFORMUTILS_EXPORT KexiDBTimeEdit : public QTimeEdit, public KexiFormDataItemInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true)
+ Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true)
+ Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true )
+
+ public:
+ KexiDBTimeEdit(const QTime &time, QWidget *parent, const char *name=0);
+ virtual ~KexiDBTimeEdit();
+
+ inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); }
+ inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); }
+ virtual QVariant value();
+ virtual void setInvalidState( const QString& displayText );
+
+ //! \return true if editor's value is null (not empty)
+ //! Used for checking if a given constraint within table of form is met.
+ virtual bool valueIsNull();
+
+ //! \return true if editor's value is empty (not necessary null).
+ //! Only few data types can accept "EMPTY" property
+ //! (use KexiDB::Field::hasEmptyProperty() to check this).
+ //! Used for checking if a given constraint within table or form is met.
+ virtual bool valueIsEmpty();
+
+ /*! \return 'readOnly' flag for this widget. */
+ virtual bool isReadOnly() const;
+
+ /*! \return the view widget of this item, e.g. line edit widget. */
+ virtual QWidget* widget();
+
+ virtual bool cursorAtStart();
+ virtual bool cursorAtEnd();
+ virtual void clear();
+
+ virtual void setEnabled(bool enabled);
+
+ public slots:
+ inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); }
+ inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); }
+ virtual void setReadOnly(bool set);
+
+ protected slots:
+ void slotValueChanged(const QTime&);
+
+ protected:
+ virtual void setValueInternal(const QVariant& add, bool removeOld);
+
+ private:
+ QDateTimeEditor* m_dte_time;
+ bool m_invalidState : 1;
+ bool m_cleared : 1;
+ bool m_readOnly : 1;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexidbutils.cpp b/kexi/plugins/forms/widgets/kexidbutils.cpp
new file mode 100644
index 00000000..0c08d64c
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbutils.cpp
@@ -0,0 +1,99 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexidbutils.h"
+
+#include <kpopupmenu.h>
+#include <kiconloader.h>
+
+#include <kexidb/queryschema.h>
+#include <kexidb/utils.h>
+#include <formeditor/widgetlibrary.h>
+#include <kexiutils/utils.h>
+#include "../kexiformpart.h"
+#include <widget/utils/kexicontextmenuutils.h>
+
+
+QColor lighterGrayBackgroundColor(const QPalette& palette)
+{
+ return KexiUtils::blendedColors(palette.active().background(), palette.active().base(), 1, 2);
+}
+
+//-------
+
+KexiDBWidgetContextMenuExtender::KexiDBWidgetContextMenuExtender( QObject* parent, KexiDataItemInterface* iface )
+ : QObject(parent)
+ , m_iface(iface)
+ , m_contextMenuHasTitle(false)
+{
+}
+
+KexiDBWidgetContextMenuExtender::~KexiDBWidgetContextMenuExtender()
+{
+}
+
+void KexiDBWidgetContextMenuExtender::createTitle(QPopupMenu *menu)
+{
+ if (!menu)
+ return;
+ m_contextMenu = menu;
+ KPopupTitle *titleItem = new KPopupTitle();
+ const int id = m_contextMenu->insertItem(titleItem, -1, 0);
+ m_contextMenu->setItemEnabled(id, false);
+ QString icon;
+ if (dynamic_cast<QWidget*>(m_iface))
+ icon = KexiFormPart::library()->iconName(dynamic_cast<QWidget*>(m_iface)->className());
+
+ m_contextMenuHasTitle = m_iface->columnInfo() ?
+ KexiContextMenuUtils::updateTitle(m_contextMenu,
+ m_iface->columnInfo()->captionOrAliasOrName(),
+ KexiDB::simplifiedTypeName(*m_iface->columnInfo()->field), icon)
+ : false;
+
+ if (!m_contextMenuHasTitle)
+ m_contextMenu->removeItem(id);
+ updatePopupMenuActions();
+}
+
+void KexiDBWidgetContextMenuExtender::updatePopupMenuActions()
+{
+ if (m_contextMenu) {
+ enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll }; //from qlineedit.h
+ const bool readOnly = m_iface->isReadOnly();
+ const int id = m_contextMenu->idAt(m_contextMenuHasTitle ? 1 : 0);
+
+//! @todo maybe redo will be enabled one day?
+ m_contextMenu->removeItem(id-(int)IdRedo);
+
+ // update cut/copy/paste
+ m_contextMenu->setItemEnabled(id-(int)IdCut, !readOnly);
+ m_contextMenu->setItemEnabled(id-(int)IdPaste, !readOnly);
+ m_contextMenu->setItemEnabled(id-(int)IdClear, !readOnly);
+ }
+}
+
+//------------------
+
+KexiSubwidgetInterface::KexiSubwidgetInterface()
+{
+}
+
+KexiSubwidgetInterface::~KexiSubwidgetInterface()
+{
+}
diff --git a/kexi/plugins/forms/widgets/kexidbutils.h b/kexi/plugins/forms/widgets/kexidbutils.h
new file mode 100644
index 00000000..386f1ee5
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexidbutils.h
@@ -0,0 +1,71 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KDBWIDGETS_UTILS_H
+#define KDBWIDGETS_UTILS_H
+
+#include <qpopupmenu.h>
+#include <kexidataiteminterface.h>
+
+QColor lighterGrayBackgroundColor(const QPalette& palette);
+
+//! @short Used for extending editor widgets' context menu.
+/*! @internal This is performed by adding a title and disabling editing
+ actions when "read only" flag is true. */
+class KexiDBWidgetContextMenuExtender : public QObject
+{
+ public:
+ KexiDBWidgetContextMenuExtender( QObject* parent, KexiDataItemInterface* iface );
+ ~KexiDBWidgetContextMenuExtender();
+
+ //! Creates title for context menu \a menu
+ void createTitle(QPopupMenu *menu);
+
+ //! Enables or disables context menu actions that can modify the value.
+ //! The menu has to be previously provided by createTitle().
+ void updatePopupMenuActions();
+
+ /*! Updates title for context menu based on data item \a iface caption or name
+ Used in createTitle(QPopupMenu *menu) and KexiDBImageBox.
+ \return true is the title has been added. */
+ static bool updateContextMenuTitleForDataItem(QPopupMenu *menu, KexiDataItemInterface* iface,
+ const QString& icon = QString::null);
+
+ protected:
+ KexiDataItemInterface* m_iface;
+ QGuardedPtr<QPopupMenu> m_contextMenu;
+ bool m_contextMenuHasTitle; //!< true if KPopupTitle has been added to the context menu.
+};
+
+class KexiDBAutoField;
+
+//! An interface allowing to define custom behaviour for subwidget of the KexiDBAutoField
+class KexiSubwidgetInterface
+{
+ public:
+ KexiSubwidgetInterface();
+ virtual ~KexiSubwidgetInterface();
+
+ virtual bool appendStretchRequired(KexiDBAutoField* autoField) const
+ { Q_UNUSED(autoField); return false; }
+ virtual bool subwidgetStretchRequired(KexiDBAutoField* autoField) const
+ { Q_UNUSED(autoField); return false; }
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexiframe.cpp b/kexi/plugins/forms/widgets/kexiframe.cpp
new file mode 100644
index 00000000..b49386da
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexiframe.cpp
@@ -0,0 +1,77 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiframe.h"
+
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include <kexiutils/utils.h>
+
+//! @internal
+class KexiFrame::Private
+{
+ public:
+ Private()
+ {
+ }
+ ~Private()
+ {
+ }
+ QColor frameColor;
+#if 0
+//todo
+ KexiFrame::Shape frameShape;
+ KexiFrame::Shadow frameShadow;
+#endif
+};
+
+//=========================================================
+
+KexiFrame::KexiFrame( QWidget * parent, const char * name, WFlags f )
+ : QFrame(parent, name, f)
+ , d( new Private() )
+{
+ //defaults
+ d->frameColor = palette().active().foreground();
+//! @todo obtain these defaults from current template's style...
+ setLineWidth(2);
+ setFrameStyle(QFrame::StyledPanel|QFrame::Raised);
+}
+
+KexiFrame::~KexiFrame()
+{
+ delete d;
+}
+
+void KexiFrame::dragMoveEvent( QDragMoveEvent *e )
+{
+ QFrame::dragMoveEvent(e);
+ emit handleDragMoveEvent(e);
+}
+
+void KexiFrame::dropEvent( QDropEvent *e )
+{
+ QFrame::dropEvent(e);
+ emit handleDropEvent(e);
+}
+
+#define ClassName KexiFrame
+#define SuperClassName QFrame
+#include "kexiframeutils_p.cpp"
+#include "kexiframe.moc"
diff --git a/kexi/plugins/forms/widgets/kexiframe.h b/kexi/plugins/forms/widgets/kexiframe.h
new file mode 100644
index 00000000..8d60d597
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexiframe.h
@@ -0,0 +1,84 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiFrame_H
+#define KexiFrame_H
+
+#include <qframe.h>
+
+//! @short Frame widget for Kexi forms
+class KEXIFORMUTILS_EXPORT KexiFrame : public QFrame
+{
+ Q_OBJECT
+//todo Q_ENUMS( Shape Shadow )
+ Q_PROPERTY( QColor frameColor READ frameColor WRITE setFrameColor DESIGNABLE true )
+//todo Q_OVERRIDE( Shape frameShape READ frameShape WRITE setFrameShape )
+//todo Q_OVERRIDE( Shadow frameShadow READ frameShadow WRITE setFrameShadow )
+
+ public:
+ KexiFrame( QWidget * parent, const char * name = 0, WFlags f = 0 );
+ virtual ~KexiFrame();
+
+ virtual const QColor& frameColor() const;
+
+#if 0
+//! @todo more options
+ enum Shadow {
+ NoShadow = QFrame::Plain,
+ Raised = QFrame::Raised,
+ Sunken = QFrame::Sunken
+ };
+//! @todo more options
+ enum Shape { NoFrame = QFrame::NoFrame, //!< no frame
+ Box = QFrame::Box, //!< rectangular box
+ Panel = QFrame::Panel, //!< rectangular panel
+ StyledPanel = QFrame::StyledPanel, //!< rectangular panel depending on the GUI style
+ GroupBoxPanel = QFrame::GroupBoxPanel //!< rectangular group-box-like panel depending on the GUI style
+ };
+ Shape frameShape() const;
+ void setFrameShape( KexiFrame::Shape shape );
+ Shadow frameShadow() const;
+ void setFrameShadow( KexiFrame::Shadow shadow );
+#endif
+
+ //! Used to emit handleDragMoveEvent() signal needed to control dragging over the container's surface
+ virtual void dragMoveEvent( QDragMoveEvent *e );
+
+ //! Used to emit handleDropEvent() signal needed to control dropping on the container's surface
+ virtual void dropEvent( QDropEvent *e );
+
+ public slots:
+ virtual void setPalette( const QPalette &pal );
+ virtual void setFrameColor(const QColor& color);
+
+ signals:
+ //! Needed to control dragging over the container's surface
+ void handleDragMoveEvent(QDragMoveEvent *e);
+
+ //! Needed to control dropping on the container's surface
+ void handleDropEvent(QDropEvent *e);
+
+ protected:
+ virtual void drawFrame( QPainter * );
+
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexiframeutils_p.cpp b/kexi/plugins/forms/widgets/kexiframeutils_p.cpp
new file mode 100644
index 00000000..11b8650a
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexiframeutils_p.cpp
@@ -0,0 +1,232 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+/* This file is included by KexiDBLabel and KexiFrame */
+
+//! @todo add more frame types
+void ClassName::drawFrame( QPainter *p )
+{
+ if (frameShape() == QFrame::Box) {
+ if ( frameShadow() == Plain )
+ qDrawPlainRect( p, frameRect(), d->frameColor, lineWidth() );
+ else
+ qDrawShadeRect( p, frameRect(), colorGroup(), frameShadow() == QFrame::Sunken,
+ lineWidth(), midLineWidth() );
+ }
+ else {
+ SuperClassName::drawFrame(p);
+ }
+}
+
+void ClassName::setPalette( const QPalette &pal )
+{
+ QPalette pal2(pal);
+ QColorGroup cg( pal2.active() );
+ cg.setColor(QColorGroup::Light, KexiUtils::bleachedColor( d->frameColor, 150 ));
+ cg.setColor(QColorGroup::Mid, d->frameColor);
+ cg.setColor(QColorGroup::Dark, d->frameColor.dark(150));
+ pal2.setActive(cg);
+ QColorGroup cg2( pal2.inactive() );
+ cg2.setColor(QColorGroup::Light, cg.light() );
+ cg2.setColor(QColorGroup::Mid, cg.mid());
+ cg2.setColor(QColorGroup::Dark, cg.dark());
+ pal2.setInactive(cg2);
+ SuperClassName::setPalette(pal2);
+}
+
+const QColor& ClassName::frameColor() const
+{
+ return d->frameColor;
+}
+
+void ClassName::setFrameColor(const QColor& color)
+{
+ d->frameColor = color;
+ //update light and dark colors
+ setPalette( palette() );
+}
+
+#if 0
+//todo
+ClassName::Shape ClassName::frameShape() const
+{
+ return d->frameShape;
+}
+
+void ClassName::setFrameShape( ClassName::Shape shape )
+{
+ d->frameShape = shape;
+ update();
+}
+
+ClassName::Shadow ClassName::frameShadow() const
+{
+ return d->frameShadow;
+}
+
+void ClassName::setFrameShadow( ClassName::Shadow shadow )
+{
+ d->frameShadow = shadow;
+ update();
+}
+#endif
+
+#if 0
+void QFrame::drawFrame( QPainter *p )
+{
+ QPoint p1, p2;
+ QRect r = frameRect();
+ int type = fstyle & MShape;
+ int cstyle = fstyle & MShadow;
+#ifdef QT_NO_DRAWUTIL
+ p->setPen( black ); // ####
+ p->drawRect( r ); //### a bit too simple
+#else
+ const QColorGroup & g = colorGroup();
+
+#ifndef QT_NO_STYLE
+ QStyleOption opt(lineWidth(),midLineWidth());
+
+ QStyle::SFlags flags = QStyle::Style_Default;
+ if (isEnabled())
+ flags |= QStyle::Style_Enabled;
+ if (cstyle == Sunken)
+ flags |= QStyle::Style_Sunken;
+ else if (cstyle == Raised)
+ flags |= QStyle::Style_Raised;
+ if (hasFocus())
+ flags |= QStyle::Style_HasFocus;
+ if (hasMouse())
+ flags |= QStyle::Style_MouseOver;
+#endif // QT_NO_STYLE
+
+ switch ( type ) {
+
+ case Box:
+ if ( cstyle == Plain )
+ qDrawPlainRect( p, r, g.foreground(), lwidth );
+ else
+ qDrawShadeRect( p, r, g, cstyle == Sunken, lwidth,
+ midLineWidth() );
+ break;
+
+ case LineEditPanel:
+ style().drawPrimitive( QStyle::PE_PanelLineEdit, p, r, g, flags, opt );
+ break;
+
+ case GroupBoxPanel:
+ style().drawPrimitive( QStyle::PE_PanelGroupBox, p, r, g, flags, opt );
+ break;
+
+ case TabWidgetPanel:
+ style().drawPrimitive( QStyle::PE_PanelTabWidget, p, r, g, flags, opt );
+ break;
+
+ case MenuBarPanel:
+#ifndef QT_NO_STYLE
+ style().drawPrimitive(QStyle::PE_PanelMenuBar, p, r, g, flags, opt);
+ break;
+#endif // fall through to Panel if QT_NO_STYLE
+
+ case ToolBarPanel:
+#ifndef QT_NO_STYLE
+ style().drawPrimitive( QStyle::PE_PanelDockWindow, p, rect(), g, flags, opt);
+ break;
+#endif // fall through to Panel if QT_NO_STYLE
+
+ case StyledPanel:
+#ifndef QT_NO_STYLE
+ if ( cstyle == Plain )
+ qDrawPlainRect( p, r, g.foreground(), lwidth );
+ else
+ style().drawPrimitive(QStyle::PE_Panel, p, r, g, flags, opt);
+ break;
+#endif // fall through to Panel if QT_NO_STYLE
+
+ case PopupPanel:
+#ifndef QT_NO_STYLE
+ {
+ int vextra = style().pixelMetric(QStyle::PM_PopupMenuFrameVerticalExtra, this),
+ hextra = style().pixelMetric(QStyle::PM_PopupMenuFrameHorizontalExtra, this);
+ if(vextra > 0 || hextra > 0) {
+ QRect fr = frameRect();
+ int fw = frameWidth();
+ if(vextra > 0) {
+ style().drawControl(QStyle::CE_PopupMenuVerticalExtra, p, this,
+ QRect(fr.x() + fw, fr.y() + fw, fr.width() - (fw*2), vextra),
+ g, flags, opt);
+ style().drawControl(QStyle::CE_PopupMenuVerticalExtra, p, this,
+ QRect(fr.x() + fw, fr.bottom() - fw - vextra, fr.width() - (fw*2), vextra),
+ g, flags, opt);
+ }
+ if(hextra > 0) {
+ style().drawControl(QStyle::CE_PopupMenuHorizontalExtra, p, this,
+ QRect(fr.x() + fw, fr.y() + fw + vextra, hextra, fr.height() - (fw*2) - vextra),
+ g, flags, opt);
+ style().drawControl(QStyle::CE_PopupMenuHorizontalExtra, p, this,
+ QRect(fr.right() - fw - hextra, fr.y() + fw + vextra, hextra, fr.height() - (fw*2) - vextra),
+ g, flags, opt);
+ }
+ }
+
+ if ( cstyle == Plain )
+ qDrawPlainRect( p, r, g.foreground(), lwidth );
+ else
+ style().drawPrimitive(QStyle::PE_PanelPopup, p, r, g, flags, opt);
+ break;
+ }
+#endif // fall through to Panel if QT_NO_STYLE
+
+ case Panel:
+ if ( cstyle == Plain )
+ qDrawPlainRect( p, r, g.foreground(), lwidth );
+ else
+ qDrawShadePanel( p, r, g, cstyle == Sunken, lwidth );
+ break;
+
+ case WinPanel:
+ if ( cstyle == Plain )
+ qDrawPlainRect( p, r, g.foreground(), wpwidth );
+ else
+ qDrawWinPanel( p, r, g, cstyle == Sunken );
+ break;
+ case HLine:
+ case VLine:
+ if ( type == HLine ) {
+ p1 = QPoint( r.x(), r.height()/2 );
+ p2 = QPoint( r.x()+r.width(), p1.y() );
+ }
+ else {
+ p1 = QPoint( r.x()+r.width()/2, 0 );
+ p2 = QPoint( p1.x(), r.height() );
+ }
+ if ( cstyle == Plain ) {
+ QPen oldPen = p->pen();
+ p->setPen( QPen(g.foreground(),lwidth) );
+ p->drawLine( p1, p2 );
+ p->setPen( oldPen );
+ }
+ else
+ qDrawShadeLine( p, p1, p2, g, cstyle == Sunken,
+ lwidth, midLineWidth() );
+ break;
+ }
+#endif // QT_NO_DRAWUTIL
+
+#endif
diff --git a/kexi/plugins/forms/widgets/kexipushbutton.cpp b/kexi/plugins/forms/widgets/kexipushbutton.cpp
new file mode 100644
index 00000000..acfda0a4
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexipushbutton.cpp
@@ -0,0 +1,32 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexipushbutton.h"
+
+KexiPushButton::KexiPushButton( const QString & text, QWidget * parent, const char * name )
+: KPushButton(text, parent, name)
+{
+}
+
+KexiPushButton::~KexiPushButton()
+{
+}
+
+#include "kexipushbutton.moc"
diff --git a/kexi/plugins/forms/widgets/kexipushbutton.h b/kexi/plugins/forms/widgets/kexipushbutton.h
new file mode 100644
index 00000000..12c01631
--- /dev/null
+++ b/kexi/plugins/forms/widgets/kexipushbutton.h
@@ -0,0 +1,55 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KexiPushButton_H
+#define KexiPushButton_H
+
+#include <kpushbutton.h>
+#include "../kexiformeventhandler.h"
+
+//! @short Push Button widget for Kexi forms
+class KEXIFORMUTILS_EXPORT KexiPushButton : public KPushButton
+{
+ Q_OBJECT
+ Q_PROPERTY(QString onClickAction READ onClickAction WRITE setOnClickAction DESIGNABLE true)
+ Q_PROPERTY(QString onClickActionOption READ onClickActionOption WRITE setOnClickActionOption DESIGNABLE true)
+
+ public:
+ KexiPushButton( const QString & text, QWidget * parent, const char * name = 0 );
+ ~KexiPushButton();
+
+ public slots:
+ //! action string for "on click" event
+ //! @see KexiFormPart::slotAssignAction()
+ //! @see KexiFormEventAction::ActionData
+ QString onClickAction() const { return m_onClickActionData.string; }
+ void setOnClickAction(const QString& actionString) { m_onClickActionData.string = actionString; }
+
+ //! action option allowing to select whether the object should be opened in data view mode or printed, etc.
+ //! @see KexiFormPart::slotAssignAction()
+ //! @see KexiFormEventAction::ActionData
+ QString onClickActionOption() const { return m_onClickActionData.option; }
+ void setOnClickActionOption(const QString& option) { m_onClickActionData.option = option; }
+
+ protected:
+ KexiFormEventAction::ActionData m_onClickActionData;
+};
+
+#endif
diff --git a/kexi/plugins/importexport/Makefile.am b/kexi/plugins/importexport/Makefile.am
new file mode 100644
index 00000000..02d8b733
--- /dev/null
+++ b/kexi/plugins/importexport/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = csv
diff --git a/kexi/plugins/importexport/csv/Makefile.am b/kexi/plugins/importexport/csv/Makefile.am
new file mode 100644
index 00000000..7ad16495
--- /dev/null
+++ b/kexi/plugins/importexport/csv/Makefile.am
@@ -0,0 +1,21 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_csv_importexport.la
+
+kexihandler_csv_importexport_la_SOURCES = kexicsv_importexportpart.cpp kexicsvimportdialog.cpp \
+ kexicsvimportoptionsdlg.cpp kexicsvwidgets.cpp kexicsvexport.cpp kexicsvexportwizard.cpp
+
+kexihandler_csv_importexport_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module
+kexihandler_csv_importexport_la_LIBADD = ../../../core/libkexicore.la \
+ ../../../migration/libkeximigrate.la
+
+INCLUDES= -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget -I$(top_srcdir)/kexi/migration \
+ -I$(top_srcdir)/kexi/kexiDB $(all_includes)
+
+METASOURCES = AUTO
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=kexicsv_importexporthandler.desktop
+
+include ../../Makefile.common
diff --git a/kexi/plugins/importexport/csv/kexicsv_importexporthandler.desktop b/kexi/plugins/importexport/csv/kexicsv_importexporthandler.desktop
new file mode 100644
index 00000000..1c2a383a
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsv_importexporthandler.desktop
@@ -0,0 +1,51 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+Name=Kexi CSV Data Import/Export Plugin
+Name[bg]=Приставка за импортиране/експортиране от CSV в Kexi
+Name[ca]=Connector d'importació/Exportació de dades CVS per a Kexi
+Name[da]=Kexi CSV data-import/eksport plugin
+Name[de]=Kexi CSV-Daten Import-/Export-Modul
+Name[el]=Πρόσθετο εισαγωγής/εξαγωγής CSV δεδομένων του Kexi
+Name[eo]=Kexi CSV-datuma import-eksport-kromaĵo
+Name[es]=Complemento de Kexi para importar y exportar datos CSV
+Name[et]=Kexi CSV-andmete impordi/ekspordifilter
+Name[fa]=وصلۀ واردات/صادرات دادۀ Kexi CSV
+Name[fr]=Module d'importation / exportation de données CSV de Kexi
+Name[fy]=Kexi ymport/eksport Plugin foar CSV-gegevens
+Name[gl]=Importación de Datos CSV de Kexi
+Name[he]=תוסף של Kexi ליבוא/יצוא של מידע מסוג CSV
+Name[hu]=Kexi CSV adatimportáló és -exportáló modul
+Name[is]=Kexi CSV gagna inn/útflutnings íforrit
+Name[it]=Importazione ed esportazione di dati CSV di Kexi
+Name[ja]=Kexi CSV データ インポート/エクスポートプラグイン
+Name[km]=កម្មវិធី​ជំនួយ​ក្នុង​ការ​នាំចេញ និង​នាំចូល​ទិន្នន័យ CSV សម្រាប់ Kexi
+Name[lv]=Kexi CSV datu importa/eksporta spraudnis
+Name[nb]=CSV-data import/eksportfilter for Kexi
+Name[nds]=CSV-Datenimport-/exportmoduul för Kexi
+Name[ne]=केक्सी CSV डेटा आयात/निर्यात प्लगइन
+Name[nl]=Kexi import/exportplugin voor CSV-gegevens
+Name[pl]=Wtyczka importu/eksportu danych CSV dla Kexi
+Name[pt]=Importação de Dados CSV do Kexi
+Name[pt_BR]=Plugin de Importação/Exportação de Dados CSV do Kexi
+Name[ru]=Модуль импорта/экспорта CSV (значения через запятую) для Kexi
+Name[se]=Kexi CSV-dáhta sisa-/olggosfievrridan lassemoduvla
+Name[sk]=Modul Kexi pre import a export CSV dát
+Name[sl]=Vstavek za uvoz/izvoz podatkov CVS za Kexi
+Name[sr]=Kexi-јев прикључак за увоз и извоз из CSV-а
+Name[sr@Latn]=Kexi-jev priključak za uvoz i izvoz iz CSV-a
+Name[sv]=Kexi insticksprogram för import/export av CSV-data
+Name[uk]=Втулок імпорту/експорту CSV-даних для Kexi
+Name[uz]=Kexi CSV maʼlumot import/eksport plagini
+Name[uz@cyrillic]=Kexi CSV маълумот импорт/экспорт плагини
+Name[zh_CN]=Kexi CSV 数据导入/导出插件
+Name[zh_TW]=Kexi CSV 資料匯入/匯出外掛程式
+
+X-KDE-Library=kexihandler_csv_importexport
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=csv_importexport
+X-Kexi-GroupIcon=csv_importexport
+X-Kexi-ItemIcon=csv_importexport
+X-Kexi-NoObject=true
diff --git a/kexi/plugins/importexport/csv/kexicsv_importexportpart.cpp b/kexi/plugins/importexport/csv/kexicsv_importexportpart.cpp
new file mode 100644
index 00000000..caa8640d
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsv_importexportpart.cpp
@@ -0,0 +1,87 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexicsv_importexportpart.h"
+#include "kexicsvimportdialog.h"
+#include "kexicsvexportwizard.h"
+#include <core/keximainwindow.h>
+#include <core/kexiproject.h>
+#include <kexiutils/utils.h>
+
+#include <kgenericfactory.h>
+
+KexiCSVImportExportPart::KexiCSVImportExportPart(QObject *parent, const char *name, const QStringList &args)
+ : KexiInternalPart(parent, name, args)
+{
+}
+
+KexiCSVImportExportPart::~KexiCSVImportExportPart()
+{
+}
+
+QWidget *KexiCSVImportExportPart::createWidget(const char* widgetClass, KexiMainWindow* mainWin,
+ QWidget *parent, const char *objName, QMap<QString,QString>* args )
+{
+ if (0==qstrcmp(widgetClass, "KexiCSVImportDialog")) {
+ KexiCSVImportDialog::Mode mode = (args && (*args)["sourceType"]=="file")
+ ? KexiCSVImportDialog::File : KexiCSVImportDialog::Clipboard;
+ KexiCSVImportDialog *dlg = new KexiCSVImportDialog( mode, mainWin, parent, objName );
+ m_cancelled = dlg->cancelled();
+ if (m_cancelled) {
+ delete dlg;
+ return 0;
+ }
+ return dlg;
+ }
+ else if (0==qstrcmp(widgetClass, "KexiCSVExportWizard")) {
+ if (!args)
+ return 0;
+ KexiCSVExport::Options options;
+ if (!options.assign( *args ))
+ return 0;
+ KexiCSVExportWizard *dlg = new KexiCSVExportWizard( options, mainWin, parent, objName);
+ m_cancelled = dlg->cancelled();
+ if (m_cancelled) {
+ delete dlg;
+ return 0;
+ }
+ return dlg;
+ }
+ return 0;
+}
+
+bool KexiCSVImportExportPart::executeCommand(KexiMainWindow* mainWin, const char* commandName,
+ QMap<QString,QString>* args)
+{
+ if (0==qstrcmp(commandName, "KexiCSVExport")) {
+ KexiCSVExport::Options options;
+ if (!options.assign( *args ))
+ return false;
+ KexiDB::TableOrQuerySchema tableOrQuery(
+ mainWin->project()->dbConnection(), options.itemId);
+ QTextStream *stream = 0;
+ if (args->contains("textStream"))
+ stream = KexiUtils::stringToPtr<QTextStream>( (*args)["textStream"] );
+ return KexiCSVExport::exportData(tableOrQuery, options, -1, stream);
+ }
+ return false;
+}
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_csv_importexport,
+ KGenericFactory<KexiCSVImportExportPart>("kexihandler_csv_importexport") )
diff --git a/kexi/plugins/importexport/csv/kexicsv_importexportpart.h b/kexi/plugins/importexport/csv/kexicsv_importexportpart.h
new file mode 100644
index 00000000..8ee8e8cd
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsv_importexportpart.h
@@ -0,0 +1,44 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXI_MIGRATION_PART_H
+#define KEXI_MIGRATION_PART_H
+
+#include <core/kexiinternalpart.h>
+#include "kexicsvexportwizard.h"
+
+/*! Internal part for CSV data import/export dialogs. */
+class KexiCSVImportExportPart : public KexiInternalPart
+{
+ public:
+ KexiCSVImportExportPart(QObject *parent, const char *name, const QStringList &args);
+ virtual ~KexiCSVImportExportPart();
+
+ /*! Reimplemented to return wizard object. */
+ virtual QWidget *createWidget(const char* widgetClass, KexiMainWindow* mainWin,
+ QWidget *parent, const char *objName = 0, QMap<QString,QString>* args = 0);
+
+ /*! Reimplemented to execute a command \a commandName (nonvisual). The result are put into the \a args. */
+ virtual bool executeCommand(KexiMainWindow* mainWin, const char* commandName,
+ QMap<QString,QString>* args = 0);
+
+ protected:
+};
+
+#endif
diff --git a/kexi/plugins/importexport/csv/kexicsvexport.cpp b/kexi/plugins/importexport/csv/kexicsvexport.cpp
new file mode 100644
index 00000000..b83f85a7
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvexport.cpp
@@ -0,0 +1,271 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005,2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexicsvexport.h"
+#include "kexicsvwidgets.h"
+#include <main/startup/KexiStartupFileDialog.h>
+#include <kexidb/cursor.h>
+#include <kexidb/utils.h>
+#include <core/keximainwindow.h>
+#include <core/kexiproject.h>
+#include <core/kexipartinfo.h>
+#include <core/kexipartmanager.h>
+#include <core/kexiguimsghandler.h>
+#include <kexiutils/utils.h>
+#include <widget/kexicharencodingcombobox.h>
+
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qclipboard.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kactivelabel.h>
+#include <kpushbutton.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <ksavefile.h>
+
+
+using namespace KexiCSVExport;
+
+Options::Options()
+ : mode(File), itemId(0), addColumnNames(true)
+{
+}
+
+bool Options::assign( QMap<QString,QString>& args )
+{
+ mode = (args["destinationType"]=="file")
+ ? KexiCSVExport::File : KexiCSVExport::Clipboard;
+
+ if (args.contains("delimiter"))
+ delimiter = args["delimiter"];
+ else
+ delimiter = (mode==File) ? KEXICSV_DEFAULT_FILE_DELIMITER : KEXICSV_DEFAULT_CLIPBOARD_DELIMITER;
+
+ if (args.contains("textQuote"))
+ textQuote = args["textQuote"];
+ else
+ textQuote = (mode==File) ? KEXICSV_DEFAULT_FILE_TEXT_QUOTE : KEXICSV_DEFAULT_CLIPBOARD_TEXT_QUOTE;
+
+ bool ok;
+ itemId = args["itemId"].toInt(&ok);
+ if (!ok || itemId<=0)
+ return false;
+ if (args.contains("forceDelimiter"))
+ forceDelimiter = args["forceDelimiter"];
+ if (args.contains("addColumnNames"))
+ addColumnNames = (args["addColumnNames"]=="1");
+ return true;
+}
+
+//------------------------------------
+
+bool KexiCSVExport::exportData(KexiDB::TableOrQuerySchema& tableOrQuery,
+ const Options& options, int rowCount, QTextStream *predefinedTextStream)
+{
+ KexiDB::Connection* conn = tableOrQuery.connection();
+ if (!conn)
+ return false;
+
+ if (rowCount == -1)
+ rowCount = KexiDB::rowCount(tableOrQuery);
+ if (rowCount == -1)
+ return false;
+
+//! @todo move this to non-GUI location so it can be also used via command line
+//! @todo add a "finish" page with a progressbar.
+//! @todo look at rowCount whether the data is really large;
+//! if so: avoid copying to clipboard (or ask user) because of system memory
+
+//! @todo OPTIMIZATION: use fieldsExpanded(true /*UNIQUE*/)
+//! @todo OPTIMIZATION? (avoid multiple data retrieving) look for already fetched data within KexiProject..
+
+ KexiDB::QuerySchema* query = tableOrQuery.query();
+ if (!query)
+ query = tableOrQuery.table()->query();
+
+ KexiDB::QueryColumnInfo::Vector fields( query->fieldsExpanded( KexiDB::QuerySchema::WithInternalFields ) );
+ QString buffer;
+
+ KSaveFile *kSaveFile = 0;
+ QTextStream *stream = 0;
+
+ const bool copyToClipboard = options.mode==Clipboard;
+ if (copyToClipboard) {
+//! @todo (during exporting): enlarge bufSize by factor of 2 when it became too small
+ uint bufSize = QMIN((rowCount<0 ? 10 : rowCount) * fields.count() * 20, 128000);
+ buffer.reserve( bufSize );
+ if (buffer.capacity() < bufSize) {
+ kdWarning() << "KexiCSVExportWizard::exportData() cannot allocate memory for " << bufSize
+ << " characters" << endl;
+ return false;
+ }
+ }
+ else {
+ if (predefinedTextStream) {
+ stream = predefinedTextStream;
+ }
+ else {
+ if (options.fileName.isEmpty()) {//sanity
+ kdWarning() << "KexiCSVExportWizard::exportData(): fname is empty" << endl;
+ return false;
+ }
+ kSaveFile = new KSaveFile(options.fileName);
+ if (0 == kSaveFile->status())
+ stream = kSaveFile->textStream();
+ if (0 != kSaveFile->status() || !stream) {//sanity
+ kdWarning() << "KexiCSVExportWizard::exportData(): status != 0 or stream == 0" << endl;
+ delete kSaveFile;
+ return false;
+ }
+ }
+ }
+
+//! @todo escape strings
+
+#define _ERR \
+ delete [] isText; \
+ if (kSaveFile) { kSaveFile->abort(); delete kSaveFile; } \
+ return false
+
+#define APPEND(what) \
+ if (copyToClipboard) buffer.append(what); else (*stream) << (what)
+
+// line endings should be as in RFC 4180
+#define CSV_EOLN "\r\n"
+
+ // 0. Cache information
+ const uint fieldsCount = query->fieldsExpanded().count(); //real fields count without internals
+ const QCString delimiter( options.delimiter.left(1).latin1() );
+ const bool hasTextQuote = !options.textQuote.isEmpty();
+ const QString textQuote( options.textQuote.left(1) );
+ const QCString escapedTextQuote( (textQuote + textQuote).latin1() ); //ok?
+ //cache for faster checks
+ bool *isText = new bool[fieldsCount];
+ bool *isDateTime = new bool[fieldsCount];
+ bool *isTime = new bool[fieldsCount];
+ bool *isBLOB = new bool[fieldsCount];
+ uint *visibleFieldIndex = new uint[fieldsCount];
+// bool isInteger[fieldsCount]; //cache for faster checks
+// bool isFloatingPoint[fieldsCount]; //cache for faster checks
+ for (uint i=0; i<fieldsCount; i++) {
+ KexiDB::QueryColumnInfo* ci;
+ const int indexForVisibleLookupValue = fields[i]->indexForVisibleLookupValue();
+ if (-1 != indexForVisibleLookupValue) {
+ ci = query->expandedOrInternalField( indexForVisibleLookupValue );
+ visibleFieldIndex[i] = indexForVisibleLookupValue;
+ }
+ else {
+ ci = fields[i];
+ visibleFieldIndex[i] = i;
+ }
+
+ isText[i] = ci->field->isTextType();
+ isDateTime[i] = ci->field->type()==KexiDB::Field::DateTime;
+ isTime[i] = ci->field->type()==KexiDB::Field::Time;
+ isBLOB[i] = ci->field->type()==KexiDB::Field::BLOB;
+// isInteger[i] = fields[i]->field->isIntegerType()
+// || fields[i]->field->type()==KexiDB::Field::Boolean;
+// isFloatingPoint[i] = fields[i]->field->isFPNumericType();
+ }
+
+ // 1. Output column names
+ if (options.addColumnNames) {
+ for (uint i=0; i<fieldsCount; i++) {
+ if (i>0)
+ APPEND( delimiter );
+ if (hasTextQuote){
+ APPEND( textQuote + fields[i]->captionOrAliasOrName().replace(textQuote, escapedTextQuote) + textQuote );
+ }
+ else {
+ APPEND( fields[i]->captionOrAliasOrName() );
+ }
+ }
+ APPEND(CSV_EOLN);
+ }
+
+ KexiGUIMessageHandler handler;
+ KexiDB::Cursor *cursor = conn->executeQuery(*query);
+ if (!cursor) {
+ handler.showErrorMessage(conn);
+ _ERR;
+ }
+ for (cursor->moveFirst(); !cursor->eof() && !cursor->error(); cursor->moveNext()) {
+ const uint realFieldCount = QMIN(cursor->fieldCount(), fieldsCount);
+ for (uint i=0; i<realFieldCount; i++) {
+ const uint real_i = visibleFieldIndex[i];
+ if (i>0)
+ APPEND( delimiter );
+ if (cursor->value(real_i).isNull())
+ continue;
+ if (isText[real_i]) {
+ if (hasTextQuote)
+ APPEND( textQuote + QString(cursor->value(real_i).toString()).replace(textQuote, escapedTextQuote) + textQuote );
+ else
+ APPEND( cursor->value(real_i).toString() );
+ }
+ else if (isDateTime[real_i]) { //avoid "T" in ISO DateTime
+ APPEND( cursor->value(real_i).toDateTime().date().toString(Qt::ISODate)+" "
+ + cursor->value(real_i).toDateTime().time().toString(Qt::ISODate) );
+ }
+ else if (isTime[real_i]) { //time is temporarily stored as null date + time...
+ APPEND( cursor->value(real_i).toTime().toString(Qt::ISODate) );
+ }
+ else if (isBLOB[real_i]) { //BLOB is escaped in a special way
+ if (hasTextQuote)
+//! @todo add options to suppport other types from KexiDB::BLOBEscapingType enum...
+ APPEND( textQuote + KexiDB::escapeBLOB(cursor->value(real_i).toByteArray(), KexiDB::BLOBEscapeHex) + textQuote );
+ else
+ APPEND( KexiDB::escapeBLOB(cursor->value(real_i).toByteArray(), KexiDB::BLOBEscapeHex) );
+ }
+ else {//other types
+ APPEND( cursor->value(real_i).toString() );
+ }
+ }
+ APPEND(CSV_EOLN);
+ }
+
+ if (copyToClipboard)
+ buffer.squeeze();
+
+ if (!conn->deleteCursor(cursor)) {
+ handler.showErrorMessage(conn);
+ _ERR;
+ }
+
+ if (copyToClipboard)
+ kapp->clipboard()->setText(buffer, QClipboard::Clipboard);
+
+ delete [] isText;
+ delete [] isDateTime;
+ delete [] isTime;
+ delete [] isBLOB;
+ delete [] visibleFieldIndex;
+
+ if (kSaveFile) {
+ if (!kSaveFile->close()) {
+ kdWarning() << "KexiCSVExportWizard::exportData(): error close(); status == "
+ << kSaveFile->status() << endl;
+ }
+ delete kSaveFile;
+ }
+ return true;
+}
diff --git a/kexi/plugins/importexport/csv/kexicsvexport.h b/kexi/plugins/importexport/csv/kexicsvexport.h
new file mode 100644
index 00000000..7c8138fe
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvexport.h
@@ -0,0 +1,58 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005,2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXI_CSVEXPORT_H
+#define KEXI_CSVEXPORT_H
+
+#include <kexidb/utils.h>
+
+namespace KexiCSVExport
+{
+
+//! Exporting mode: a file or clipboard
+enum Mode { Clipboard, File };
+
+//! Options used in KexiCSVExportWizard contructor.
+class Options {
+ public:
+ Options();
+
+ //! Assigns \a args. \return false on failure.
+ bool assign( QMap<QString,QString>& args );
+
+ Mode mode;
+ int itemId; //!< Table or query ID
+ QString fileName;
+ QString delimiter;
+ QString forceDelimiter; //!< Used for "clipboard" mode
+ QString textQuote;
+ bool addColumnNames : 1;
+};
+
+/*! Exports data. \return false on failure.
+ @param options options for the export
+ @param rowCount row count of the input data or -1 if the row cound has not yet been computed
+ @param predefinedTextStream text stream that should be used instead of writing to a file
+*/
+bool exportData(KexiDB::TableOrQuerySchema& tableOrQuery, const Options& options, int rowCount = -1,
+ QTextStream *predefinedTextStream = 0);
+
+}
+
+#endif
diff --git a/kexi/plugins/importexport/csv/kexicsvexportwizard.cpp b/kexi/plugins/importexport/csv/kexicsvexportwizard.cpp
new file mode 100644
index 00000000..11c0cff0
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvexportwizard.cpp
@@ -0,0 +1,431 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005,2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexicsvexportwizard.h"
+#include "kexicsvwidgets.h"
+#include <main/startup/KexiStartupFileDialog.h>
+#include <kexidb/cursor.h>
+#include <kexidb/utils.h>
+#include <core/keximainwindow.h>
+#include <core/kexiproject.h>
+#include <core/kexipartinfo.h>
+#include <core/kexipartmanager.h>
+#include <core/kexiguimsghandler.h>
+#include <kexiutils/utils.h>
+#include <widget/kexicharencodingcombobox.h>
+
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qclipboard.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kactivelabel.h>
+#include <kpushbutton.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <ksavefile.h>
+
+
+KexiCSVExportWizard::KexiCSVExportWizard( const KexiCSVExport::Options& options,
+ KexiMainWindow* mainWin, QWidget * parent, const char * name )
+ : KWizard(parent, name)
+ , m_options(options)
+// , m_mode(mode)
+// , m_itemId(itemId)
+ , m_mainWin(mainWin)
+ , m_fileSavePage(0)
+ , m_defaultsBtn(0)
+ , m_rowCount(-1)
+ , m_rowCountDetermined(false)
+ , m_cancelled(false)
+{
+ if (m_options.mode==KexiCSVExport::Clipboard) {
+ finishButton()->setText(i18n("Copy"));
+ backButton()->hide();
+ }
+ else {
+ finishButton()->setText(i18n("Export"));
+ }
+ helpButton()->hide();
+
+ QString infoLblFromText;
+ KexiGUIMessageHandler msgh(this);
+ m_tableOrQuery = new KexiDB::TableOrQuerySchema(
+ m_mainWin->project()->dbConnection(), m_options.itemId);
+ if (m_tableOrQuery->table()) {
+ if (m_options.mode==KexiCSVExport::Clipboard) {
+ setCaption(i18n("Copy Data From Table to Clipboard"));
+ infoLblFromText = i18n("Copying data from table:");
+ }
+ else {
+ setCaption(i18n("Export Data From Table to CSV File"));
+ infoLblFromText = i18n("Exporting data from table:");
+ }
+ }
+ else if (m_tableOrQuery->query()) {
+ if (m_options.mode==KexiCSVExport::Clipboard) {
+ setCaption(i18n("Copy Data From Query to Clipboard"));
+ infoLblFromText = i18n("Copying data from table:");
+ }
+ else {
+ setCaption(i18n("Export Data From Query to CSV File"));
+ infoLblFromText = i18n("Exporting data from query:");
+ }
+ }
+ else {
+ msgh.showErrorMessage(m_mainWin->project()->dbConnection(),
+ i18n("Could not open data for exporting."));
+ m_cancelled = true;
+ return;
+ }
+ // OK, source data found.
+
+ // Setup pages
+
+ // 1. File Save Page
+ if (m_options.mode==KexiCSVExport::File) {
+ m_fileSavePage = new KexiStartupFileDialog(
+ ":CSVImportExport", //startDir
+ KexiStartupFileDialog::Custom | KexiStartupFileDialog::SavingFileBasedDB,
+ this, "m_fileSavePage");
+ m_fileSavePage->setMinimumHeight(kapp->desktop()->height()/2);
+ m_fileSavePage->setAdditionalFilters( csvMimeTypes() );
+ m_fileSavePage->setDefaultExtension("csv");
+ m_fileSavePage->setLocationText( KexiUtils::stringToFileName(m_tableOrQuery->captionOrName()) );
+ connect(m_fileSavePage, SIGNAL(rejected()), this, SLOT(reject()));
+ addPage(m_fileSavePage, i18n("Enter Name of File You Want to Save Data To"));
+ }
+
+ // 2. Export options
+ m_exportOptionsPage = new QWidget(this, "m_exportOptionsPage");
+ QGridLayout *exportOptionsLyr = new QGridLayout( m_exportOptionsPage, 6, 3,
+ KDialogBase::marginHint(), KDialogBase::spacingHint(), "exportOptionsLyr");
+ m_infoLblFrom = new KexiCSVInfoLabel( infoLblFromText, m_exportOptionsPage );
+ KexiPart::Info *partInfo = Kexi::partManager().infoForMimeType(
+ m_tableOrQuery->table() ? "kexi/table" : "kexi/query");
+ if (partInfo)
+ m_infoLblFrom->setIcon(partInfo->itemIcon());
+ m_infoLblFrom->separator()->hide();
+ exportOptionsLyr->addMultiCellWidget(m_infoLblFrom, 0, 0, 0, 2);
+
+ m_infoLblTo = new KexiCSVInfoLabel(
+ (m_options.mode==KexiCSVExport::File) ? i18n("To CSV file:") : i18n("To clipboard:"),
+ m_exportOptionsPage
+ );
+ if (m_options.mode==KexiCSVExport::Clipboard)
+ m_infoLblTo->setIcon("editpaste");
+ exportOptionsLyr->addMultiCellWidget(m_infoLblTo, 1, 1, 0, 2);
+
+ m_showOptionsButton = new KPushButton(KGuiItem(i18n("Show Options >>"), "configure"),
+ m_exportOptionsPage);
+ connect(m_showOptionsButton, SIGNAL(clicked()), this, SLOT(slotShowOptionsButtonClicked()));
+ exportOptionsLyr->addMultiCellWidget(m_showOptionsButton, 2, 2, 0, 0);
+ m_showOptionsButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+
+ // -<options section>
+ m_exportOptionsSection = new QGroupBox(1, Vertical, i18n("Options"), m_exportOptionsPage,
+ "m_exportOptionsSection");
+ m_exportOptionsSection->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ exportOptionsLyr->addMultiCellWidget(m_exportOptionsSection, 3, 3, 0, 1);
+ QWidget *exportOptionsSectionWidget
+ = new QWidget(m_exportOptionsSection, "exportOptionsSectionWidget");
+ QGridLayout *exportOptionsSectionLyr = new QGridLayout( exportOptionsSectionWidget, 5, 2,
+ 0, KDialogBase::spacingHint(), "exportOptionsLyr");
+
+ // -delimiter
+ m_delimiterWidget = new KexiCSVDelimiterWidget(false, //!lineEditOnBottom
+ exportOptionsSectionWidget);
+ m_delimiterWidget->setDelimiter(defaultDelimiter());
+ exportOptionsSectionLyr->addWidget( m_delimiterWidget, 0, 1 );
+ QLabel *delimiterLabel = new QLabel(m_delimiterWidget, i18n("Delimiter:"), exportOptionsSectionWidget);
+ exportOptionsSectionLyr->addWidget( delimiterLabel, 0, 0 );
+
+ // -text quote
+ QWidget *textQuoteWidget = new QWidget(exportOptionsSectionWidget);
+ QHBoxLayout *textQuoteLyr = new QHBoxLayout(textQuoteWidget);
+ exportOptionsSectionLyr->addWidget(textQuoteWidget, 1, 1);
+ m_textQuote = new KexiCSVTextQuoteComboBox( textQuoteWidget );
+ m_textQuote->setTextQuote(defaultTextQuote());
+ textQuoteLyr->addWidget( m_textQuote );
+ textQuoteLyr->addStretch(0);
+ QLabel *textQuoteLabel = new QLabel(m_textQuote, i18n("Text quote:"), exportOptionsSectionWidget);
+ exportOptionsSectionLyr->addWidget( textQuoteLabel, 1, 0 );
+
+ // - character encoding
+ m_characterEncodingCombo = new KexiCharacterEncodingComboBox( exportOptionsSectionWidget );
+ m_characterEncodingCombo->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ exportOptionsSectionLyr->addWidget( m_characterEncodingCombo, 2, 1 );
+ QLabel *characterEncodingLabel = new QLabel(m_characterEncodingCombo, i18n("Text encoding:"),
+ exportOptionsSectionWidget);
+ exportOptionsSectionLyr->addWidget( characterEncodingLabel, 2, 0 );
+
+ // - checkboxes
+ m_addColumnNamesCheckBox = new QCheckBox(i18n("Add column names as the first row"),
+ exportOptionsSectionWidget);
+ m_addColumnNamesCheckBox->setChecked(true);
+ exportOptionsSectionLyr->addWidget( m_addColumnNamesCheckBox, 3, 1 );
+//! @todo 1.1: for copying use "Always use above options for copying" string
+ m_alwaysUseCheckBox = new QCheckBox(i18n("Always use above options for exporting"),
+ m_exportOptionsPage);
+ exportOptionsLyr->addMultiCellWidget(m_alwaysUseCheckBox, 4, 4, 0, 1);
+// exportOptionsSectionLyr->addWidget( m_alwaysUseCheckBox, 4, 1 );
+ m_exportOptionsSection->hide();
+ m_alwaysUseCheckBox->hide();
+ // -</options section>
+
+// exportOptionsLyr->setColStretch(3, 1);
+ exportOptionsLyr->addMultiCell(
+ new QSpacerItem( 0, 0, QSizePolicy::Preferred, QSizePolicy::MinimumExpanding), 5, 5, 0, 1 );
+
+// addPage(m_exportOptionsPage, i18n("Set Export Options"));
+ addPage(m_exportOptionsPage, m_options.mode==KexiCSVExport::Clipboard ? i18n("Copying") : i18n("Exporting"));
+ setFinishEnabled(m_exportOptionsPage, true);
+
+ // load settings
+ kapp->config()->setGroup("ImportExport");
+ if (m_options.mode!=KexiCSVExport::Clipboard && readBoolEntry("ShowOptionsInCSVExportDialog", false)) {
+ show();
+ slotShowOptionsButtonClicked();
+ }
+ if (readBoolEntry("StoreOptionsForCSVExportDialog", false)) {
+ // load defaults:
+ m_alwaysUseCheckBox->setChecked(true);
+ QString s = readEntry("DefaultDelimiterForExportingCSVFiles", defaultDelimiter());
+ if (!s.isEmpty())
+ m_delimiterWidget->setDelimiter(s);
+ s = readEntry("DefaultTextQuoteForExportingCSVFiles", defaultTextQuote());
+ m_textQuote->setTextQuote(s); //will be invaliudated here, so not a problem
+ s = readEntry("DefaultEncodingForExportingCSVFiles");
+ if (!s.isEmpty())
+ m_characterEncodingCombo->setSelectedEncoding(s);
+ m_addColumnNamesCheckBox->setChecked(
+ readBoolEntry("AddColumnNamesForExportingCSVFiles", true) );
+ }
+
+ updateGeometry();
+
+ // -keep widths equal on page #2:
+ int width = QMAX( m_infoLblFrom->leftLabel()->sizeHint().width(),
+ m_infoLblTo->leftLabel()->sizeHint().width());
+ m_infoLblFrom->leftLabel()->setFixedWidth(width);
+ m_infoLblTo->leftLabel()->setFixedWidth(width);
+}
+
+KexiCSVExportWizard::~KexiCSVExportWizard()
+{
+ delete m_tableOrQuery;
+}
+
+bool KexiCSVExportWizard::cancelled() const
+{
+ return m_cancelled;
+}
+
+void KexiCSVExportWizard::showPage ( QWidget * page )
+{
+ if (page == m_fileSavePage) {
+ m_fileSavePage->setFocus();
+ }
+ else if (page==m_exportOptionsPage) {
+ if (m_options.mode==KexiCSVExport::File)
+ m_infoLblTo->setFileName( m_fileSavePage->currentFileName() );
+ QString text = m_tableOrQuery->captionOrName();
+ if (!m_rowCountDetermined) {
+ //do this costly operation only once
+ m_rowCount = KexiDB::rowCount(*m_tableOrQuery);
+ m_rowCountDetermined = true;
+ }
+ int columns = KexiDB::fieldCount(*m_tableOrQuery);
+ text += "\n";
+ if (m_rowCount>0)
+ text += i18n("(rows: %1, columns: %2)").arg(m_rowCount).arg(columns);
+ else
+ text += i18n("(columns: %1)").arg(columns);
+ m_infoLblFrom->setLabelText(text);
+ QFontMetrics fm(m_infoLblFrom->fileNameLabel()->font());
+ m_infoLblFrom->fileNameLabel()->setFixedHeight( fm.height() * 2 + fm.lineSpacing() );
+ if (m_defaultsBtn)
+ m_defaultsBtn->show();
+ }
+
+ if (page!=m_exportOptionsPage) {
+ if (m_defaultsBtn)
+ m_defaultsBtn->hide();
+ }
+
+ KWizard::showPage(page);
+}
+
+void KexiCSVExportWizard::next()
+{
+ if (currentPage() == m_fileSavePage) {
+ if (!m_fileSavePage->checkFileName())
+ return;
+ KWizard::next();
+ finishButton()->setFocus();
+ return;
+ }
+ KWizard::next();
+}
+
+void KexiCSVExportWizard::done(int result)
+{
+ if (QDialog::Accepted == result) {
+ if (m_fileSavePage)
+ m_options.fileName = m_fileSavePage->currentFileName();
+ m_options.delimiter = m_delimiterWidget->delimiter();
+ m_options.textQuote = m_textQuote->textQuote();
+ m_options.addColumnNames = m_addColumnNamesCheckBox->isChecked();
+ if (!KexiCSVExport::exportData(*m_tableOrQuery, m_options))
+ return;
+ }
+ else if (QDialog::Rejected == result) {
+ //nothing to do
+ }
+
+ //store options
+ kapp->config()->setGroup("ImportExport");
+ if (m_options.mode!=KexiCSVExport::Clipboard)
+ writeEntry("ShowOptionsInCSVExportDialog", m_exportOptionsSection->isVisible());
+ const bool store = m_alwaysUseCheckBox->isChecked();
+ writeEntry("StoreOptionsForCSVExportDialog", store);
+ // only save if an option differs from default
+
+ if (store && m_delimiterWidget->delimiter()!=defaultDelimiter())
+ writeEntry("DefaultDelimiterForExportingCSVFiles", m_delimiterWidget->delimiter());
+ else
+ deleteEntry("DefaultDelimiterForExportingCSVFiles");
+ if (store && m_textQuote->textQuote()!=defaultTextQuote())
+ writeEntry("DefaultTextQuoteForExportingCSVFiles", m_textQuote->textQuote());
+ else
+ deleteEntry("DefaultTextQuoteForExportingCSVFiles");
+ if (store && !m_characterEncodingCombo->defaultEncodingSelected())
+ writeEntry("DefaultEncodingForExportingCSVFiles", m_characterEncodingCombo->selectedEncoding());
+ else
+ deleteEntry("DefaultEncodingForExportingCSVFiles");
+ if (store && !m_addColumnNamesCheckBox->isChecked())
+ writeEntry("AddColumnNamesForExportingCSVFiles", m_addColumnNamesCheckBox->isChecked());
+ else
+ deleteEntry("AddColumnNamesForExportingCSVFiles");
+
+ KWizard::done(result);
+}
+
+void KexiCSVExportWizard::slotShowOptionsButtonClicked()
+{
+ if (m_exportOptionsSection->isVisible()) {
+ m_showOptionsButton->setText(i18n("Show Options >>"));
+ m_exportOptionsSection->hide();
+ m_alwaysUseCheckBox->hide();
+ if (m_defaultsBtn)
+ m_defaultsBtn->hide();
+ }
+ else {
+ m_showOptionsButton->setText(i18n("Hide Options <<"));
+ m_exportOptionsSection->show();
+ m_alwaysUseCheckBox->show();
+ if (m_defaultsBtn)
+ m_defaultsBtn->show();
+ }
+}
+
+void KexiCSVExportWizard::layOutButtonRow( QHBoxLayout * layout )
+{
+ QWizard::layOutButtonRow( layout );
+
+ //find the last sublayout
+ QLayout *l = 0;
+ for (QLayoutIterator lit( layout->iterator() ); lit.current(); ++lit)
+ l = lit.current()->layout();
+ if (dynamic_cast<QBoxLayout*>(l)) {
+ if (!m_defaultsBtn) {
+ m_defaultsBtn = new KPushButton(i18n("Defaults"), this);
+ QWidget::setTabOrder(backButton(), m_defaultsBtn);
+ connect(m_defaultsBtn, SIGNAL(clicked()), this, SLOT(slotDefaultsButtonClicked()));
+ }
+ if (!m_exportOptionsSection->isVisible())
+ m_defaultsBtn->hide();
+ dynamic_cast<QBoxLayout*>(l)->insertWidget(0, m_defaultsBtn);
+ }
+}
+
+void KexiCSVExportWizard::slotDefaultsButtonClicked()
+{
+ m_delimiterWidget->setDelimiter(defaultDelimiter());
+ m_textQuote->setTextQuote(defaultTextQuote());
+ m_addColumnNamesCheckBox->setChecked(true);
+ m_characterEncodingCombo->selectDefaultEncoding();
+}
+
+static QString convertKey(const char *key, KexiCSVExport::Mode mode)
+{
+ QString _key(QString::fromLatin1(key));
+ if (mode == KexiCSVExport::Clipboard) {
+ _key.replace("Exporting", "Copying");
+ _key.replace("Export", "Copy");
+ _key.replace("CSVFiles", "CSVToClipboard");
+ }
+ return _key;
+}
+
+bool KexiCSVExportWizard::readBoolEntry(const char *key, bool defaultValue)
+{
+ return kapp->config()->readBoolEntry(convertKey(key, m_options.mode), defaultValue);
+}
+
+QString KexiCSVExportWizard::readEntry(const char *key, const QString& defaultValue)
+{
+ return kapp->config()->readEntry(convertKey(key, m_options.mode), defaultValue);
+}
+
+void KexiCSVExportWizard::writeEntry(const char *key, const QString& value)
+{
+ kapp->config()->writeEntry(convertKey(key, m_options.mode), value);
+}
+
+void KexiCSVExportWizard::writeEntry(const char *key, bool value)
+{
+ kapp->config()->writeEntry(convertKey(key, m_options.mode), value);
+}
+
+void KexiCSVExportWizard::deleteEntry(const char *key)
+{
+ kapp->config()->deleteEntry(convertKey(key, m_options.mode));
+}
+
+QString KexiCSVExportWizard::defaultDelimiter() const
+{
+ if (m_options.mode==KexiCSVExport::Clipboard) {
+ if (!m_options.forceDelimiter.isEmpty())
+ return m_options.forceDelimiter;
+ else
+ return KEXICSV_DEFAULT_CLIPBOARD_DELIMITER;
+ }
+ return KEXICSV_DEFAULT_FILE_DELIMITER;
+}
+
+QString KexiCSVExportWizard::defaultTextQuote() const
+{
+ if (m_options.mode==KexiCSVExport::Clipboard)
+ return KEXICSV_DEFAULT_CLIPBOARD_TEXT_QUOTE;
+ return KEXICSV_DEFAULT_FILE_TEXT_QUOTE;
+}
+
+#include "kexicsvexportwizard.moc"
diff --git a/kexi/plugins/importexport/csv/kexicsvexportwizard.h b/kexi/plugins/importexport/csv/kexicsvexportwizard.h
new file mode 100644
index 00000000..8fdaecd0
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvexportwizard.h
@@ -0,0 +1,113 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005,2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXI_CSVEXPORTWIZARD_H
+#define KEXI_CSVEXPORTWIZARD_H
+
+#include <kwizard.h>
+
+#include "kexicsvexport.h"
+
+class QCheckBox;
+class QGroupBox;
+class KPushButton;
+class KexiMainWindow;
+class KexiStartupFileDialog;
+class KexiCSVDelimiterWidget;
+class KexiCSVTextQuoteComboBox;
+class KexiCSVInfoLabel;
+class KexiCharacterEncodingComboBox;
+namespace KexiDB {
+ class TableOrQuerySchema;
+}
+
+/*! @short Kexi CSV export wizard
+ Supports exporting to a file and to a clipboard. */
+class KexiCSVExportWizard : public KWizard
+{
+ Q_OBJECT
+
+ public:
+ KexiCSVExportWizard( const KexiCSVExport::Options& options, KexiMainWindow* mainWin,
+ QWidget * parent = 0, const char * name = 0 );
+
+ virtual ~KexiCSVExportWizard();
+
+ bool cancelled() const;
+
+ virtual void showPage ( QWidget * page );
+
+ protected slots:
+ virtual void next();
+ virtual void done(int result);
+ void slotShowOptionsButtonClicked();
+ void slotDefaultsButtonClicked();
+
+ protected:
+ //! reimplemented to add "Defaults" button on the left hand
+ virtual void layOutButtonRow( QHBoxLayout * layout );
+
+ //! \return default delimiter depending on mode.
+ QString defaultDelimiter() const;
+
+ //! \return default text quote depending on mode.
+ QString defaultTextQuote() const;
+
+ //! Helper, works like kapp->config()->readBoolEntry(const char*, bool) but if mode is Clipboard,
+ //! "Exporting" is replaced with "Copying" and "Export" is replaced with "Copy"
+ //! and "CSVFiles" is replaced with "CSVToClipboard"
+ //! in \a key, to keep the setting separate.
+ bool readBoolEntry(const char *key, bool defaultValue);
+
+ //! Helper like \ref readBoolEntry(const char *, bool), but for QString values.
+ QString readEntry(const char *key, const QString& defaultValue = QString::null);
+
+ //! Helper, works like kapp->config()->writeEntry(const char*,bool) but if mode is Clipboard,
+ //! "Exporting" is replaced with "Copying" and "Export" is replaced with "Copy"
+ //! and "CSVFiles" is replaced with "CSVToClipboard"
+ //! in \a key, to keep the setting separate.
+ void writeEntry(const char *key, bool value);
+
+ //! Helper like \ref writeEntry(const char *, bool), but for QString values.
+ void writeEntry(const char *key, const QString& value);
+
+ //! Helper like \ref writeEntry(const char *, bool), but for deleting config entry.
+ void deleteEntry(const char *key);
+
+ KexiCSVExport::Options m_options;
+// Mode m_mode;
+// int m_itemId;
+ KexiMainWindow* m_mainWin;
+ KexiStartupFileDialog* m_fileSavePage;
+ QWidget* m_exportOptionsPage;
+ KPushButton *m_showOptionsButton;
+ KPushButton *m_defaultsBtn;
+ QGroupBox* m_exportOptionsSection;
+ KexiCSVInfoLabel *m_infoLblFrom, *m_infoLblTo;
+ KexiCSVDelimiterWidget* m_delimiterWidget;
+ KexiCSVTextQuoteComboBox* m_textQuote;
+ KexiCharacterEncodingComboBox *m_characterEncodingCombo;
+ QCheckBox* m_addColumnNamesCheckBox, *m_alwaysUseCheckBox;
+ KexiDB::TableOrQuerySchema* m_tableOrQuery;
+ int m_rowCount; //!< Cached row count for a table/query.
+ bool m_rowCountDetermined : 1;
+ bool m_cancelled : 1;
+};
+
+#endif
diff --git a/kexi/plugins/importexport/csv/kexicsvimportdialog.cpp b/kexi/plugins/importexport/csv/kexicsvimportdialog.cpp
new file mode 100644
index 00000000..16a9d416
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvimportdialog.cpp
@@ -0,0 +1,1662 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This work is based on kspread/dialogs/kspread_dlg_csv.cc
+ and will be merged back with KOffice libraries.
+
+ Copyright (C) 2002-2003 Norbert Andres <nandres@web.de>
+ Copyright (C) 2002-2003 Ariya Hidayat <ariya@kde.org>
+ Copyright (C) 2002 Laurent Montel <montel@kde.org>
+ Copyright (C) 1999 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qclipboard.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qmime.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qtable.h>
+#include <qlayout.h>
+#include <qfiledialog.h>
+#include <qpainter.h>
+#include <qtextcodec.h>
+#include <qtimer.h>
+#include <qfontmetrics.h>
+#include <qtooltip.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kglobalsettings.h>
+#include <kiconloader.h>
+#include <kcharsets.h>
+#include <knuminput.h>
+#include <kprogress.h>
+#include <kactivelabel.h>
+
+#include <kexiutils/identifier.h>
+#include <kexiutils/utils.h>
+#include <core/kexi.h>
+#include <core/kexiproject.h>
+#include <core/kexipart.h>
+#include <core/kexipartinfo.h>
+#include <core/keximainwindow.h>
+#include <core/kexiguimsghandler.h>
+#include <kexidb/connection.h>
+#include <kexidb/tableschema.h>
+#include <kexidb/transaction.h>
+#include <widget/kexicharencodingcombobox.h>
+
+#include "kexicsvimportdialog.h"
+#include "kexicsvwidgets.h"
+
+#ifdef Q_WS_WIN
+#include <krecentdirs.h>
+#include <windows.h>
+#endif
+
+#if 0
+#include <kspread_cell.h>
+#include <kspread_doc.h>
+#include <kspread_sheet.h>
+#include <kspread_undo.h>
+#include <kspread_view.h>
+#endif
+
+#define _IMPORT_ICON "table" /*todo: change to "file_import" or so*/
+#define _TEXT_TYPE 0
+#define _NUMBER_TYPE 1
+#define _DATE_TYPE 2
+#define _TIME_TYPE 3
+#define _DATETIME_TYPE 4
+#define _PK_FLAG 5
+
+//extra:
+#define _NO_TYPE_YET -1 //allows to accept a number of empty cells, before something non-empty
+#define _FP_NUMBER_TYPE 255 //_NUMBER_TYPE variant
+#define MAX_ROWS_TO_PREVIEW 100 //max 100 rows is reasonable
+#define MAX_BYTES_TO_PREVIEW 10240 //max 10KB is reasonable
+#define MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER 4096
+
+class KexiCSVImportDialogTable : public QTable
+{
+public:
+ KexiCSVImportDialogTable( QWidget * parent = 0, const char * name = 0 )
+ : QTable(parent, name) {
+ f = font();
+ f.setBold(true);
+ }
+ virtual void paintCell( QPainter * p, int row, int col, const QRect & cr, bool selected, const QColorGroup & cg ) {
+ if (row==0)
+ p->setFont(f);
+ else
+ p->setFont(font());
+ QTable::paintCell(p, row, col, cr, selected, cg);
+ }
+ virtual void setColumnWidth( int col, int w ) {
+ //make columns a bit wider
+ QTable::setColumnWidth( col, w + 16 );
+ }
+ QFont f;
+};
+
+//! Helper used to temporary disable keyboard and mouse events
+void installRecursiveEventFilter(QObject *filter, QObject *object)
+{
+ object->installEventFilter(filter);
+
+ if (!object->children())
+ return;
+
+ QObjectList list = *object->children();
+ for(QObject *obj = list.first(); obj; obj = list.next())
+ installRecursiveEventFilter(filter, obj);
+}
+
+KexiCSVImportDialog::KexiCSVImportDialog( Mode mode, KexiMainWindow* mainWin,
+ QWidget * parent, const char * name
+)
+ : KDialogBase(
+ KDialogBase::Plain,
+ i18n( "Import CSV Data File" )
+//! @todo use "Paste CSV Data From Clipboard" caption for mode==Clipboard
+ ,
+ (mode==File ? User1 : (ButtonCode)0) |Ok|Cancel,
+ Ok,
+ parent,
+ name ? name : "KexiCSVImportDialog",
+ true,
+ false,
+ KGuiItem( i18n("&Options"))
+ ),
+ m_mainWin(mainWin),
+ m_cancelled( false ),
+ m_adjustRows( true ),
+ m_startline( 0 ),
+ m_textquote( QString(KEXICSV_DEFAULT_FILE_TEXT_QUOTE)[0] ),
+ m_mode(mode),
+ m_prevSelectedCol(-1),
+ m_columnsAdjusted(false),
+ m_1stRowForFieldNamesDetected(false),
+ m_firstFillTableCall(true),
+ m_blockUserEvents(false),
+ m_primaryKeyColumn(-1),
+ m_dialogCancelled(false),
+ m_conn(0),
+ m_destinationTableSchema(0),
+ m_allRowsLoadedInPreview(false),
+ m_stoppedAt_MAX_BYTES_TO_PREVIEW(false)
+{
+ setWFlags(getWFlags() | Qt::WStyle_Maximize | Qt::WStyle_SysMenu);
+ hide();
+ setButtonOK(KGuiItem( i18n("&Import..."), _IMPORT_ICON));
+
+ m_typeNames.resize(5);
+ m_typeNames[0] = i18n("text");
+ m_typeNames[1] = i18n("number");
+ m_typeNames[2] = i18n("date");
+ m_typeNames[3] = i18n("time");
+ m_typeNames[4] = i18n("date/time");
+
+ kapp->config()->setGroup("ImportExport");
+ m_maximumRowsForPreview = kapp->config()->readNumEntry("MaximumRowsForPreviewInImportDialog", MAX_ROWS_TO_PREVIEW);
+ m_maximumBytesForPreview = kapp->config()->readNumEntry("MaximumBytesForPreviewInImportDialog", MAX_BYTES_TO_PREVIEW);
+
+ m_pkIcon = SmallIcon("key");
+
+ m_uniquenessTest.setAutoDelete(true);
+
+ setIcon(DesktopIcon(_IMPORT_ICON));
+ setSizeGripEnabled( TRUE );
+
+// m_encoding = QString::fromLatin1(KGlobal::locale()->encoding());
+// m_stripWhiteSpaceInTextValuesChecked = true;
+ m_file = 0;
+ m_inputStream = 0;
+
+ QVBoxLayout *lyr = new QVBoxLayout(plainPage(), 0, KDialogBase::spacingHint(), "lyr");
+
+ m_infoLbl = new KexiCSVInfoLabel(
+ m_mode==File ? i18n("Preview of data from file:")
+ : i18n("Preview of data from clipboard:"),
+ plainPage()
+ );
+ lyr->addWidget( m_infoLbl );
+
+ QWidget* page = new QFrame( plainPage(), "page" );
+ QGridLayout *glyr= new QGridLayout( page, 4, 5, 0, KDialogBase::spacingHint(), "glyr");
+ lyr->addWidget( page );
+
+ // Delimiter: comma, semicolon, tab, space, other
+ m_delimiterWidget = new KexiCSVDelimiterWidget(true /*lineEditOnBottom*/, page);
+ m_detectDelimiter = true;
+ glyr->addMultiCellWidget( m_delimiterWidget, 1, 2, 0, 0 );
+
+ QLabel *delimiterLabel = new QLabel(m_delimiterWidget, i18n("Delimiter:"), page);
+ delimiterLabel->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
+ glyr->addMultiCellWidget( delimiterLabel, 0, 0, 0, 0 );
+
+ // Format: number, text, currency,
+ m_formatComboText = i18n( "Format for column %1:" );
+ m_formatCombo = new KComboBox(page, "m_formatCombo");
+ m_formatCombo->insertItem(i18n("Text"));
+ m_formatCombo->insertItem(i18n("Number"));
+ m_formatCombo->insertItem(i18n("Date"));
+ m_formatCombo->insertItem(i18n("Time"));
+ m_formatCombo->insertItem(i18n("Date/Time"));
+ glyr->addMultiCellWidget( m_formatCombo, 1, 1, 1, 1 );
+
+ m_formatLabel = new QLabel(m_formatCombo, "", page);
+ m_formatLabel->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
+ glyr->addWidget( m_formatLabel, 0, 1 );
+
+ m_primaryKeyField = new QCheckBox( i18n( "Primary key" ), page, "m_primaryKeyField" );
+ glyr->addWidget( m_primaryKeyField, 2, 1 );
+ connect(m_primaryKeyField, SIGNAL(toggled(bool)), this, SLOT(slotPrimaryKeyFieldToggled(bool)));
+
+ m_comboQuote = new KexiCSVTextQuoteComboBox( page );
+ glyr->addWidget( m_comboQuote, 1, 2 );
+
+ TextLabel2 = new QLabel( m_comboQuote, i18n( "Text quote:" ), page, "TextLabel2" );
+ TextLabel2->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred );
+ TextLabel2->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
+ glyr->addWidget( TextLabel2, 0, 2 );
+
+ m_startAtLineSpinBox = new KIntSpinBox( page, "m_startAtLineSpinBox" );
+ m_startAtLineSpinBox->setMinValue(1);
+ m_startAtLineSpinBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
+ m_startAtLineSpinBox->setMinimumWidth(QFontMetrics(m_startAtLineSpinBox->font()).width("8888888"));
+ glyr->addWidget( m_startAtLineSpinBox, 1, 3 );
+
+ m_startAtLineLabel = new QLabel( m_startAtLineSpinBox, "",
+ page, "TextLabel3" );
+ m_startAtLineLabel->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred );
+ m_startAtLineLabel->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
+ glyr->addWidget( m_startAtLineLabel, 0, 3 );
+
+ QSpacerItem* spacer_2 = new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Preferred );
+ glyr->addItem( spacer_2, 0, 4 );
+
+ m_ignoreDuplicates = new QCheckBox( page, "m_ignoreDuplicates" );
+ m_ignoreDuplicates->setText( i18n( "Ignore duplicated delimiters" ) );
+ glyr->addMultiCellWidget( m_ignoreDuplicates, 2, 2, 2, 4 );
+
+ m_1stRowForFieldNames = new QCheckBox( page, "m_1stRowForFieldNames" );
+ m_1stRowForFieldNames->setText( i18n( "First row contains column names" ) );
+ glyr->addMultiCellWidget( m_1stRowForFieldNames, 3, 3, 2, 4 );
+
+ m_table = new KexiCSVImportDialogTable( plainPage(), "m_table" );
+ lyr->addWidget( m_table );
+
+ m_table->setSizePolicy( QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding, 1, 1) );
+ m_table->setNumRows( 0 );
+ m_table->setNumCols( 0 );
+
+/** @todo reuse Clipboard too! */
+/*
+if ( m_mode == Clipboard )
+ {
+ setCaption( i18n( "Inserting From Clipboard" ) );
+ QMimeSource * mime = QApplication::clipboard()->data();
+ if ( !mime )
+ {
+ KMessageBox::information( this, i18n("There is no data in the clipboard.") );
+ m_cancelled = true;
+ return;
+ }
+
+ if ( !mime->provides( "text/plain" ) )
+ {
+ KMessageBox::information( this, i18n("There is no usable data in the clipboard.") );
+ m_cancelled = true;
+ return;
+ }
+ m_fileArray = QByteArray(mime->encodedData( "text/plain" ) );
+ }
+ else if ( mode == File )
+ {*/
+ m_dateRegExp = QRegExp("(\\d{1,4})([/\\-\\.])(\\d{1,2})([/\\-\\.])(\\d{1,4})");
+ m_timeRegExp1 = QRegExp("(\\d{1,2}):(\\d{1,2}):(\\d{1,2})");
+ m_timeRegExp2 = QRegExp("(\\d{1,2}):(\\d{1,2})");
+ m_fpNumberRegExp = QRegExp("[\\-]{0,1}\\d*[,\\.]\\d+");
+ QString caption( i18n("Open CSV Data File") );
+
+ if (m_mode == File) {
+ QStringList mimetypes( csvMimeTypes() );
+#ifdef Q_WS_WIN
+ //! @todo remove
+ QString recentDir = KGlobalSettings::documentPath();
+ m_fname = QFileDialog::getOpenFileName(
+ KFileDialog::getStartURL(":CSVImportExport", recentDir).path(),
+ KexiUtils::fileDialogFilterStrings(mimetypes, false),
+ page, "KexiCSVImportDialog", caption);
+ if ( !m_fname.isEmpty() ) {
+ //save last visited path
+ KURL url;
+ url.setPath( m_fname );
+ if (url.isLocalFile())
+ KRecentDirs::add(":CSVImportExport", url.directory());
+ }
+#else
+ m_fname = KFileDialog::getOpenFileName(":CSVImportExport", mimetypes.join(" "),
+ this, caption);
+#endif
+ //cancel action !
+ if ( m_fname.isEmpty() )
+ {
+ actionButton( Ok )->setEnabled( false );
+ m_cancelled = true;
+ if (parentWidget())
+ parentWidget()->raise();
+ return;
+ }
+ }
+ else if (m_mode == Clipboard) {
+ QCString subtype("plain");
+ m_clipboardData = QApplication::clipboard()->text(subtype, QClipboard::Clipboard);
+/* debug
+ for (int i=0;QApplication::clipboard()->data(QClipboard::Clipboard)->format(i);i++)
+ kdDebug() << i << ": "
+ << QApplication::clipboard()->data(QClipboard::Clipboard)->format(i) << endl;
+*/
+ }
+ else {
+ return;
+ }
+
+ m_loadingProgressDlg = 0;
+ m_importingProgressDlg = 0;
+ if (m_mode == File) {
+ m_loadingProgressDlg = new KProgressDialog(
+ this, "m_loadingProgressDlg", i18n("Loading CSV Data"), i18n("Loading CSV Data from \"%1\"...")
+ .arg(QDir::convertSeparators(m_fname)), true);
+ m_loadingProgressDlg->progressBar()->setTotalSteps( m_maximumRowsForPreview+1 );
+ m_loadingProgressDlg->show();
+ }
+
+ if (m_mode==Clipboard) {
+ m_infoLbl->setIcon("editpaste");
+ }
+ //updateRowCountInfo();
+
+ m_table->setSelectionMode(QTable::NoSelection);
+
+ connect(m_formatCombo, SIGNAL(activated(int)),
+ this, SLOT(formatChanged(int)));
+ connect(m_delimiterWidget, SIGNAL(delimiterChanged(const QString&)),
+ this, SLOT(delimiterChanged(const QString&)));
+ connect(m_startAtLineSpinBox, SIGNAL(valueChanged ( int )),
+ this, SLOT(startlineSelected(int)));
+ connect(m_comboQuote, SIGNAL(activated(int)),
+ this, SLOT(textquoteSelected(int)));
+ connect(m_table, SIGNAL(currentChanged(int, int)),
+ this, SLOT(currentCellChanged(int, int)));
+ connect(m_table, SIGNAL(valueChanged(int,int)),
+ this, SLOT(cellValueChanged(int,int)));
+ connect(m_ignoreDuplicates, SIGNAL(stateChanged(int)),
+ this, SLOT(ignoreDuplicatesChanged(int)));
+ connect(m_1stRowForFieldNames, SIGNAL(stateChanged(int)),
+ this, SLOT(slot1stRowForFieldNamesChanged(int)));
+
+ connect(this, SIGNAL(user1Clicked()), this, SLOT(optionsButtonClicked()));
+
+ installRecursiveEventFilter(this, this);
+
+ initLater();
+}
+
+KexiCSVImportDialog::~KexiCSVImportDialog()
+{
+ delete m_file;
+}
+
+void KexiCSVImportDialog::initLater()
+{
+ if (!openData())
+ return;
+
+// delimiterChanged(detectedDelimiter); // this will cause fillTable()
+ m_columnsAdjusted = false;
+ fillTable();
+ delete m_loadingProgressDlg;
+ m_loadingProgressDlg = 0;
+ if (m_dialogCancelled) {
+// m_loadingProgressDlg->hide();
+ // m_loadingProgressDlg->close();
+ QTimer::singleShot(0, this, SLOT(reject()));
+ return;
+ }
+
+ currentCellChanged(0, 0);
+
+// updateGeometry();
+ adjustSize();
+ KDialog::centerOnScreen( this );
+
+ if (m_loadingProgressDlg)
+ m_loadingProgressDlg->hide();
+ show();
+ m_table->setFocus();
+}
+
+bool KexiCSVImportDialog::openData()
+{
+ if (m_mode!=File) //data already loaded, no encoding stuff needed
+ return true;
+
+ delete m_inputStream;
+ m_inputStream = 0;
+ if (m_file) {
+ m_file->close();
+ delete m_file;
+ }
+ m_file = new QFile(m_fname);
+ if (!m_file->open(IO_ReadOnly))
+ {
+ m_file->close();
+ delete m_file;
+ m_file = 0;
+ KMessageBox::sorry( this, i18n("Cannot open input file <nobr>\"%1\"</nobr>.")
+ .arg(QDir::convertSeparators(m_fname)) );
+ actionButton( Ok )->setEnabled( false );
+ m_cancelled = true;
+ if (parentWidget())
+ parentWidget()->raise();
+ return false;
+ }
+ return true;
+}
+
+bool KexiCSVImportDialog::cancelled() const
+{
+ return m_cancelled;
+}
+
+void KexiCSVImportDialog::fillTable()
+{
+ KexiUtils::WaitCursor wc(true);
+ repaint();
+ m_blockUserEvents = true;
+ QPushButton *pb = actionButton(KDialogBase::Cancel);
+ if (pb)
+ pb->setEnabled(true); //allow to cancel
+ KexiUtils::WaitCursor wait;
+
+ if (m_table->numRows()>0) //to accept editor
+ m_table->setCurrentCell(0,0);
+
+ int row, column, maxColumn;
+ QString field = QString::null;
+
+ for (row = 0; row < m_table->numRows(); ++row)
+ for (column = 0; column < m_table->numCols(); ++column)
+ m_table->clearCell(row, column);
+
+ m_detectedTypes.clear();
+ m_detectedTypes.resize(1024, _NO_TYPE_YET);//_TEXT_TYPE);
+ m_uniquenessTest.clear();
+ m_uniquenessTest.resize(1024);
+ m_1stRowForFieldNamesDetected = true;
+
+ if (true != loadRows(field, row, column, maxColumn, true))
+ return;
+
+ m_1stRowForFieldNamesDetected = false;
+
+ // file with only one line without '\n'
+ if (field.length() > 0)
+ {
+ setText(row - m_startline, column, field, true);
+ ++row;
+ field = QString::null;
+ }
+
+ adjustRows( row - m_startline - (m_1stRowForFieldNames->isChecked()?1:0) );
+
+ maxColumn = QMAX( maxColumn, column );
+ m_table->setNumCols(maxColumn);
+
+ for (column = 0; column < m_table->numCols(); ++column)
+ {
+// QString header = m_table->horizontalHeader()->label(column);
+// if (header != i18n("Text") && header != i18n("Number") &&
+// header != i18n("Date") && header != i18n("Currency"))
+// const int detectedType = m_detectedTypes[column+1];
+// m_table->horizontalHeader()->setLabel(column, m_typeNames[ detectedType ]); //i18n("Text"));
+ updateColumnText(column);
+ if (!m_columnsAdjusted)
+ m_table->adjustColumn(column);
+ }
+ m_columnsAdjusted = true;
+
+ if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
+ if (_NUMBER_TYPE != m_detectedTypes[ m_primaryKeyColumn ]) {
+ m_primaryKeyColumn = -1;
+ }
+ }
+
+ m_prevSelectedCol = -1;
+ m_table->setCurrentCell(0,0);
+ currentCellChanged(0, 0);
+ if (m_primaryKeyColumn != -1)
+ m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
+
+ const int count = QMAX(0, m_table->numRows()-1+m_startline);
+ m_allRowsLoadedInPreview = count < m_maximumRowsForPreview && !m_stoppedAt_MAX_BYTES_TO_PREVIEW;
+ if (m_allRowsLoadedInPreview) {
+ m_startAtLineSpinBox->setMaxValue(count);
+ m_startAtLineSpinBox->setValue(m_startline+1);
+ }
+ m_startAtLineLabel->setText(i18n( "Start at line%1:").arg(
+ m_allRowsLoadedInPreview ? QString(" (1-%1)").arg(count)
+ : QString::null //we do not know what's real count
+ ));
+ updateRowCountInfo();
+
+ m_blockUserEvents = false;
+ repaint();
+ m_table->verticalScrollBar()->repaint();//avoid missing repaint
+ m_table->horizontalScrollBar()->repaint();//avoid missing repaint
+}
+
+QString KexiCSVImportDialog::detectDelimiterByLookingAtFirstBytesOfFile(QTextStream& inputStream)
+{
+ m_file->at(0);
+
+ // try to detect delimiter
+ // \t has priority, then ; then ,
+ const QIODevice::Offset origOffset = inputStream.device()->at();
+ QChar c, prevChar=0;
+ int detectedDelimiter = 0;
+ bool insideQuote = false;
+
+ //characters by priority
+ const int CH_TAB_AFTER_QUOTE = 500;
+ const int CH_SEMICOLON_AFTER_QUOTE = 499;
+ const int CH_COMMA_AFTER_QUOTE = 498;
+ const int CH_TAB = 200; // \t
+ const int CH_SEMICOLON = 199; // ;
+ const int CH_COMMA = 198; // ,
+
+ QValueList<int> tabsPerLine, semicolonsPerLine, commasPerLine;
+ int tabs = 0, semicolons = 0, commas = 0;
+ int line = 0;
+ for (uint i=0; !inputStream.atEnd() && i < MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER; i++) {
+ (*m_inputStream) >> c; // read one char
+ if (prevChar=='"') {
+ if (c!='"') //real quote (not double "")
+ insideQuote = !insideQuote;
+ }
+ if (insideQuote) {
+ prevChar = c;
+ continue;
+ }
+ if (c==' ')
+ continue;
+ if (c=='\n') {//end of line
+ //remember # of tabs/semicolons/commas in this line
+ tabsPerLine += tabs;
+ tabs = 0;
+ semicolonsPerLine += semicolons;
+ semicolons = 0;
+ commasPerLine += commas;
+ commas = 0;
+ line++;
+ }
+ else if (c=='\t') {
+ tabs++;
+ detectedDelimiter = QMAX( prevChar=='"' ? CH_TAB_AFTER_QUOTE : CH_TAB, detectedDelimiter );
+ }
+ else if (c==';') {
+ semicolons++;
+ detectedDelimiter = QMAX( prevChar=='"' ? CH_SEMICOLON_AFTER_QUOTE : CH_SEMICOLON, detectedDelimiter );
+ }
+ else if (c==',') {
+ commas++;
+ detectedDelimiter = QMAX( prevChar=='"' ? CH_COMMA_AFTER_QUOTE : CH_COMMA, detectedDelimiter );
+ }
+ prevChar = c;
+ }
+
+ inputStream.device()->at(origOffset); //restore orig. offset
+
+ //now, try to find a delimiter character that exists the same number of times in all the checked lines
+ //this detection method has priority over others
+ QValueList<int>::ConstIterator it;
+ if (tabsPerLine.count()>1) {
+ tabs = tabsPerLine.isEmpty() ? 0 : tabsPerLine.first();
+ for (it=tabsPerLine.constBegin(); it!=tabsPerLine.constEnd(); ++it) {
+ if (tabs != *it)
+ break;
+ }
+ if (tabs>0 && it==tabsPerLine.constEnd())
+ return "\t";
+ }
+ if (semicolonsPerLine.count()>1) {
+ semicolons = semicolonsPerLine.isEmpty() ? 0 : semicolonsPerLine.first();
+ for (it=semicolonsPerLine.constBegin(); it!=semicolonsPerLine.constEnd(); ++it) {
+ if (semicolons != *it)
+ break;
+ }
+ if (semicolons > 0 && it==semicolonsPerLine.constEnd())
+ return ";";
+ }
+ if (commasPerLine.count()>1) {
+ commas = commasPerLine.first();
+ for (it=commasPerLine.constBegin(); it!=commasPerLine.constEnd(); ++it) {
+ if (commas != *it)
+ break;
+ }
+ if (commas > 0 && it==commasPerLine.constEnd())
+ return ",";
+ }
+ //now return the winning character by looking at CH_* symbol
+ if (detectedDelimiter == CH_TAB_AFTER_QUOTE || detectedDelimiter == CH_TAB)
+ return "\t";
+ if (detectedDelimiter == CH_SEMICOLON_AFTER_QUOTE || detectedDelimiter == CH_SEMICOLON)
+ return ";";
+ if (detectedDelimiter == CH_COMMA_AFTER_QUOTE || detectedDelimiter == CH_COMMA)
+ return ",";
+
+ return KEXICSV_DEFAULT_FILE_DELIMITER; //<-- default
+}
+
+tristate KexiCSVImportDialog::loadRows(QString &field, int &row, int &column, int &maxColumn,
+ bool inGUI)
+{
+ enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
+ S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
+ field = QString::null;
+ const bool ignoreDups = m_ignoreDuplicates->isChecked();
+ bool lastCharDelimiter = false;
+ bool nextRow = false;
+ row = column = 1;
+ maxColumn = 0;
+ QChar x;
+ const bool hadInputStream = m_inputStream!=0;
+ delete m_inputStream;
+ if ( m_mode == Clipboard ) {
+ m_inputStream = new QTextStream(m_clipboardData, IO_ReadOnly);
+ if (!hadInputStream)
+ m_delimiterWidget->setDelimiter(KEXICSV_DEFAULT_CLIPBOARD_DELIMITER);
+ }
+ else {
+ m_file->at(0); //always seek at 0 because loadRows() is called many times
+ m_inputStream = new QTextStream(m_file);
+ if (m_options.defaultEncodingExplicitySet) {
+ QTextCodec *codec = KGlobal::charsets()->codecForName(m_options.encoding);
+ if (codec)
+ m_inputStream->setCodec(codec); //QTextCodec::codecForName("CP1250"));
+ }
+ if (m_detectDelimiter) {
+ const QString delimiter( detectDelimiterByLookingAtFirstBytesOfFile(*m_inputStream) );
+ if (m_delimiterWidget->delimiter() != delimiter)
+ m_delimiterWidget->setDelimiter( delimiter );
+ }
+ }
+ const QChar delimiter(m_delimiterWidget->delimiter()[0]);
+ m_stoppedAt_MAX_BYTES_TO_PREVIEW = false;
+ int progressStep = 0;
+ if (m_importingProgressDlg)
+ progressStep = QMAX( 1, m_importingProgressDlg->progressBar()->totalSteps()/200 );
+ int offset = 0;
+ for (;!m_inputStream->atEnd(); offset++)
+ {
+//disabled: this breaks wide spreadsheets
+// if (column >= m_maximumRowsForPreview)
+// return true;
+
+ if (m_importingProgressDlg && ((offset % progressStep) < 5)) {
+ //update progr. bar dlg on final exporting
+ m_importingProgressDlg->progressBar()->setValue(offset);
+ qApp->processEvents();
+ if (m_importingProgressDlg->wasCancelled()) {
+ delete m_importingProgressDlg;
+ m_importingProgressDlg = 0;
+ return ::cancelled;
+ }
+ }
+
+ (*m_inputStream) >> x; // read one char
+
+ if (x == '\r') {
+ continue; // eat '\r', to handle RFC-compliant files
+ }
+ if (offset==0 && x.unicode()==0xfeff) {
+ // Ignore BOM, the "Byte Order Mark"
+ // (http://en.wikipedia.org/wiki/Byte_Order_Mark, // http://www.unicode.org/charts/PDF/UFFF0.pdf)
+ // Probably fixed in Qt4.
+ continue;
+ }
+
+ switch (state)
+ {
+ case S_START :
+ if (x == m_textquote)
+ {
+ state = S_QUOTED_FIELD;
+ }
+ else if (x == delimiter)
+ {
+ setText(row - m_startline, column, field, inGUI);
+ field = QString::null;
+ if ((ignoreDups == false) || (lastCharDelimiter == false))
+ ++column;
+ lastCharDelimiter = true;
+ }
+ else if (x == '\n')
+ {
+ if (!inGUI) {
+ //fill remaining empty fields (database wants them explicitly)
+ for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
+ setText(row - m_startline, additionalColumn, QString::null, inGUI);
+ }
+ }
+ nextRow = true;
+ maxColumn = QMAX( maxColumn, column );
+ column = 1;
+ }
+ else
+ {
+ field += x;
+ state = S_MAYBE_NORMAL_FIELD;
+ }
+ break;
+ case S_QUOTED_FIELD :
+ if (x == m_textquote)
+ {
+ state = S_MAYBE_END_OF_QUOTED_FIELD;
+ }
+/*allow \n inside quoted fields
+ else if (x == '\n')
+ {
+ setText(row - m_startline, column, field, inGUI);
+ field = "";
+ if (x == '\n')
+ {
+ nextRow = true;
+ maxColumn = QMAX( maxColumn, column );
+ column = 1;
+ }
+ else
+ {
+ if ((ignoreDups == false) || (lastCharDelimiter == false))
+ ++column;
+ lastCharDelimiter = true;
+ }
+ state = S_START;
+ }*/
+ else
+ {
+ field += x;
+ }
+ break;
+ case S_MAYBE_END_OF_QUOTED_FIELD :
+ if (x == m_textquote)
+ {
+ field += x; //no, this was just escaped quote character
+ state = S_QUOTED_FIELD;
+ }
+ else if (x == delimiter || x == '\n')
+ {
+ setText(row - m_startline, column, field, inGUI);
+ field = QString::null;
+ if (x == '\n')
+ {
+ nextRow = true;
+ maxColumn = QMAX( maxColumn, column );
+ column = 1;
+ }
+ else
+ {
+ if ((ignoreDups == false) || (lastCharDelimiter == false))
+ ++column;
+ lastCharDelimiter = true;
+ }
+ state = S_START;
+ }
+ else
+ {
+ state = S_END_OF_QUOTED_FIELD;
+ }
+ break;
+ case S_END_OF_QUOTED_FIELD :
+ if (x == delimiter || x == '\n')
+ {
+ setText(row - m_startline, column, field, inGUI);
+ field = QString::null;
+ if (x == '\n')
+ {
+ nextRow = true;
+ maxColumn = QMAX( maxColumn, column );
+ column = 1;
+ }
+ else
+ {
+ if ((ignoreDups == false) || (lastCharDelimiter == false))
+ ++column;
+ lastCharDelimiter = true;
+ }
+ state = S_START;
+ }
+ else
+ {
+ state = S_END_OF_QUOTED_FIELD;
+ }
+ break;
+ case S_MAYBE_NORMAL_FIELD :
+ if (x == m_textquote)
+ {
+ field = QString::null;
+ state = S_QUOTED_FIELD;
+ break;
+ }
+ case S_NORMAL_FIELD :
+ if (x == delimiter || x == '\n')
+ {
+ setText(row - m_startline, column, field, inGUI);
+ field = QString::null;
+ if (x == '\n')
+ {
+ nextRow = true;
+ maxColumn = QMAX( maxColumn, column );
+ column = 1;
+ }
+ else
+ {
+ if ((ignoreDups == false) || (lastCharDelimiter == false))
+ ++column;
+ lastCharDelimiter = true;
+ }
+ state = S_START;
+ }
+ else
+ {
+ field += x;
+ }
+ }
+ if (x != delimiter)
+ lastCharDelimiter = false;
+
+ if (nextRow) {
+ if (!inGUI && row==1 && m_1stRowForFieldNames->isChecked()) {
+ // do not save to the database 1st row if it contains column names
+ m_importingStatement->clearArguments();
+ }
+ else if (!saveRow(inGUI))
+ return false;
+
+ ++row;
+ }
+
+ if (m_firstFillTableCall && row==2
+ && !m_1stRowForFieldNames->isChecked() && m_1stRowForFieldNamesDetected)
+ {
+ //'1st row for field name' flag detected: reload table
+ m_1stRowForFieldNamesDetected = false;
+ m_table->setNumRows( 0 );
+ m_firstFillTableCall = false; //this trick is allowed only once, on startup
+ m_1stRowForFieldNames->setChecked(true); //this will reload table
+ //slot1stRowForFieldNamesChanged(1);
+ m_blockUserEvents = false;
+ repaint();
+ return false;
+ }
+
+ if (!m_importingProgressDlg && row % 20 == 0) {
+ qApp->processEvents();
+ //only for GUI mode:
+ if (!m_firstFillTableCall && m_loadingProgressDlg && m_loadingProgressDlg->wasCancelled()) {
+ delete m_loadingProgressDlg;
+ m_loadingProgressDlg = 0;
+ m_dialogCancelled = true;
+ reject();
+ return false;
+ }
+ }
+
+ if (!m_firstFillTableCall && m_loadingProgressDlg) {
+ m_loadingProgressDlg->progressBar()->setValue(QMIN(m_maximumRowsForPreview, row));
+ }
+
+ if ( inGUI && row > (m_maximumRowsForPreview + (m_1stRowForFieldNamesDetected?1:0)) ) {
+ kexipluginsdbg << "KexiCSVImportDialog::fillTable() loading stopped at row #"
+ << m_maximumRowsForPreview << endl;
+ break;
+ }
+ if (nextRow) {
+ nextRow = false;
+ //additional speedup: stop processing now if too many bytes were loaded for preview
+ kexipluginsdbg << offset << endl;
+ if (inGUI && offset >= m_maximumBytesForPreview && row >= 2) {
+ m_stoppedAt_MAX_BYTES_TO_PREVIEW = true;
+ return true;
+ }
+ }
+ }
+ return true;
+}
+
+void KexiCSVImportDialog::updateColumnText(int col)
+{
+ QString colName;
+ if (col<(int)m_columnNames.count() && (m_1stRowForFieldNames->isChecked() || m_changedColumnNames[col]))
+ colName = m_columnNames[ col ];
+ if (colName.isEmpty()) {
+ colName = i18n("Column %1").arg(col+1); //will be changed to a valid identifier on import
+ m_changedColumnNames[ col ] = false;
+ }
+ int detectedType = m_detectedTypes[col];
+ if (detectedType==_FP_NUMBER_TYPE)
+ detectedType=_NUMBER_TYPE; //we're simplifying that for now
+ else if (detectedType==_NO_TYPE_YET) {
+ m_detectedTypes[col]=_TEXT_TYPE; //entirely empty column
+ detectedType=_TEXT_TYPE;
+ }
+ m_table->horizontalHeader()->setLabel(col,
+ i18n("Column %1").arg(col+1) + " \n(" + m_typeNames[ detectedType ] + ") ");
+ m_table->setText(0, col, colName);
+ m_table->horizontalHeader()->adjustHeaderSize();
+
+ //check uniqueness
+ QValueList<int> *list = m_uniquenessTest[col];
+ if (m_primaryKeyColumn==-1 && list && !list->isEmpty()) {
+ qHeapSort(*list);
+ QValueList<int>::ConstIterator it=list->constBegin();
+ int prevValue = *it;
+ ++it;
+ for(; it!=list->constEnd() && prevValue!=(*it); ++it)
+ prevValue=(*it);
+ if (it!=list->constEnd()) {
+ //duplicates:
+ list->clear();
+ }
+ else {
+ //a candidate for PK (autodetected)!
+ if (-1==m_primaryKeyColumn) {
+ m_primaryKeyColumn=col;
+ }
+ }
+ }
+ if (list) //not needed now: conserve memory
+ list->clear();
+}
+
+void KexiCSVImportDialog::detectTypeAndUniqueness(int row, int col, const QString& text)
+{
+ int intValue;
+ const int type = m_detectedTypes[col];
+ if (row==1 || type!=_TEXT_TYPE) {
+ bool found = false;
+ if (text.isEmpty() && type==_NO_TYPE_YET)
+ found = true; //real type should be found later
+ //detect type because it's 1st row or all prev. rows were not text
+ //-FP number? (trying before "number" type is a must)
+ if (!found && (row==1 || type==_NUMBER_TYPE || type==_FP_NUMBER_TYPE || type==_NO_TYPE_YET)) {
+ bool ok = text.isEmpty() || m_fpNumberRegExp.exactMatch(text);
+ //if (!ok)
+ // text.toDouble(&ok);
+ if (ok && (row==1 || type==_NUMBER_TYPE || type==_FP_NUMBER_TYPE || type==_NO_TYPE_YET)) {
+ m_detectedTypes[col]=_FP_NUMBER_TYPE;
+ found = true; //yes
+ }
+ }
+ //-number?
+ if (!found && (row==1 || type==_NUMBER_TYPE || type==_NO_TYPE_YET)) {
+ bool ok = text.isEmpty();//empty values allowed
+ if (!ok)
+ intValue = text.toInt(&ok);
+ if (ok && (row==1 || type==_NO_TYPE_YET)) {
+ m_detectedTypes[col]=_NUMBER_TYPE;
+ found = true; //yes
+ }
+ }
+ //-date?
+ if (!found && (row==1 || type==_DATE_TYPE || type==_NO_TYPE_YET)) {
+ if ((row==1 || type==_NO_TYPE_YET)
+ && (text.isEmpty() || m_dateRegExp.exactMatch(text)))
+ {
+ m_detectedTypes[col]=_DATE_TYPE;
+ found = true; //yes
+ }
+ }
+ //-time?
+ if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
+ if ((row==1 || type==_NO_TYPE_YET)
+ && (text.isEmpty() || m_timeRegExp1.exactMatch(text) || m_timeRegExp2.exactMatch(text)))
+ {
+ m_detectedTypes[col]=_TIME_TYPE;
+ found = true; //yes
+ }
+ }
+ //-date/time?
+ if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
+ if (row==1 || type==_NO_TYPE_YET) {
+ bool detected = text.isEmpty();
+ if (!detected) {
+ const QStringList dateTimeList( QStringList::split(" ", text) );
+ bool ok = dateTimeList.count()>=2;
+//! @todo also support ISODateTime's "T" separator?
+//! @todo also support timezones?
+ if (ok) {
+ //try all combinations
+ QString datePart( dateTimeList[0].stripWhiteSpace() );
+ QString timePart( dateTimeList[1].stripWhiteSpace() );
+ ok = m_dateRegExp.exactMatch(datePart)
+ && (m_timeRegExp1.exactMatch(timePart) || m_timeRegExp2.exactMatch(timePart));
+ }
+ detected = ok;
+ }
+ if (detected) {
+ m_detectedTypes[col]=_DATETIME_TYPE;
+ found = true; //yes
+ }
+ }
+ }
+ if (!found && type==_NO_TYPE_YET && !text.isEmpty()) {
+ //eventually, a non-emptytext after a while
+ m_detectedTypes[col]=_TEXT_TYPE;
+ found = true; //yes
+ }
+ //default: text type (already set)
+ }
+ //check uniqueness for this value
+ QValueList<int> *list = m_uniquenessTest[col];
+ if (row==1 && (!list || !list->isEmpty()) && !text.isEmpty() && _NUMBER_TYPE == m_detectedTypes[col]) {
+ if (!list) {
+ list = new QValueList<int>();
+ m_uniquenessTest.insert(col, list);
+ }
+ list->append( intValue );
+ }
+ else {
+ //the value is empty or uniqueness test failed in the past
+ if (list && !list->isEmpty())
+ list->clear(); //indicate that uniqueness test failed
+ }
+}
+
+bool KexiCSVImportDialog::parseDate(const QString& text, QDate& date)
+{
+ if (!m_dateRegExp.exactMatch(text))
+ return false;
+ //dddd - dd - dddd
+ //1 2 3 4 5 <- pos
+ const int d1 = m_dateRegExp.cap(1).toInt(), d3 = m_dateRegExp.cap(3).toInt(), d5 = m_dateRegExp.cap(5).toInt();
+ if (m_dateRegExp.cap(2)=="/") //probably separator for american format mm/dd/yyyy
+ date = QDate(d5, d1, d3);
+ else {
+ if (d5 > 31) //d5 == year
+ date = QDate(d5, d3, d1);
+ else //d1 == year
+ date = QDate(d1, d3, d5);
+ }
+ return date.isValid();
+}
+
+bool KexiCSVImportDialog::parseTime(const QString& text, QTime& time)
+{
+ time = QTime::fromString(text, Qt::ISODate); //same as m_timeRegExp1
+ if (time.isValid())
+ return true;
+ if (m_timeRegExp2.exactMatch(text)) { //hh:mm:ss
+ time = QTime(m_timeRegExp2.cap(1).toInt(), m_timeRegExp2.cap(3).toInt(), m_timeRegExp2.cap(5).toInt());
+ return true;
+ }
+ return false;
+}
+
+void KexiCSVImportDialog::setText(int row, int col, const QString& text, bool inGUI)
+{
+ if (!inGUI) {
+ //save text directly to database buffer
+ if (col==1) { //1st col
+ m_importingStatement->clearArguments();
+ if (m_implicitPrimaryKeyAdded)
+ *m_importingStatement << QVariant(); //id will be autogenerated here
+ }
+ const int detectedType = m_detectedTypes[col-1];
+ if (detectedType==_NUMBER_TYPE) {
+ *m_importingStatement << ( text.isEmpty() ? QVariant() : text.toInt() );
+//! @todo what about time and float/double types and different integer subtypes?
+ }
+ else if (detectedType==_FP_NUMBER_TYPE) {
+ //replace ',' with '.'
+ QCString t(text.latin1());
+ const int textLen = t.length();
+ for (int i=0; i<textLen; i++) {
+ if (t.at(i)==',') {
+ t.at(i) = '.';
+ break;
+ }
+ }
+ *m_importingStatement << ( t.isEmpty() ? QVariant() : t.toDouble() );
+ }
+ else if (detectedType==_DATE_TYPE) {
+ QDate date;
+ if (parseDate(text, date))
+ *m_importingStatement << date;
+ }
+ else if (detectedType==_TIME_TYPE) {
+ QTime time;
+ if (parseTime(text, time))
+ *m_importingStatement << time;
+ }
+ else if (detectedType==_DATETIME_TYPE) {
+ QStringList dateTimeList( QStringList::split(" ", text) );
+ if (dateTimeList.count()<2)
+ dateTimeList = QStringList::split("T", text); //also support ISODateTime's "T" separator
+//! @todo also support timezones?
+ if (dateTimeList.count()>=2) {
+ QString datePart( dateTimeList[0].stripWhiteSpace() );
+ QDate date;
+ if (parseDate(datePart, date)) {
+ QString timePart( dateTimeList[1].stripWhiteSpace() );
+ QTime time;
+ if (parseTime(timePart, time))
+ *m_importingStatement << QDateTime(date, time);
+ }
+ }
+ }
+ else //_TEXT_TYPE and the rest
+ *m_importingStatement << (m_options.stripWhiteSpaceInTextValuesChecked ? text.stripWhiteSpace() : text);
+ return;
+ }
+ //save text to GUI (table view)
+ if (m_table->numCols() < col) {
+ m_table->setNumCols(col);
+ if ((int)m_columnNames.size() < m_table->numCols()) {
+ m_columnNames.resize(m_table->numCols()+10);
+ m_changedColumnNames.resize(m_table->numCols()+10);
+ }
+ }
+
+ if (m_1stRowForFieldNames->isChecked()) {
+ if ((row+m_startline)==1) {//this is for column name
+ if ((col-1) < (int)m_changedColumnNames.size() && false==m_changedColumnNames[col-1]) {
+ //this column has no custom name entered by a user
+ //-get the name from the data cell
+ QString colName(text.simplifyWhiteSpace());
+ if (!colName.isEmpty()) {
+ if (colName.left(1)>="0" && colName.left(1)<="9")
+ colName.prepend(i18n("Column")+" ");
+ m_columnNames[ col-1 ] = colName;
+ }
+ }
+ return;
+ }
+ }
+ else {
+ if ((row+m_startline)==1) {//this row is for column name
+ if (m_1stRowForFieldNamesDetected && !m_1stRowForFieldNames->isChecked()) {
+ QString f( text.simplifyWhiteSpace() );
+ if (f.isEmpty() || !f[0].isLetter())
+ m_1stRowForFieldNamesDetected = false; //this couldn't be a column name
+ }
+ }
+ row++; //1st row was for column names
+ }
+
+ if (row < 2) // skipped by the user
+ return;
+
+ if (m_table->numRows() < row) {
+// if (m_maximumRowsForPreview >= row+100)
+ m_table->setNumRows(row+100); /* We add more rows at a time to limit recalculations */
+ //else
+// m_table->setNumRows(m_maximumRowsForPreview);
+ m_table->verticalHeader()->setLabel(0, i18n("Column name")+" ");
+ m_adjustRows=true;
+ }
+
+ m_table->setText(row - 1, col - 1, (m_options.stripWhiteSpaceInTextValuesChecked ? text.stripWhiteSpace() : text));
+ m_table->verticalHeader()->setLabel(row-1, QString::number(row-1));
+
+ detectTypeAndUniqueness(row-1, col-1, text);
+}
+
+bool KexiCSVImportDialog::saveRow(bool inGUI)
+{
+ if (inGUI) {
+ //nothing to do
+ return true;
+ }
+ //save db buffer
+ bool res = m_importingStatement->execute();
+//todo: move
+ m_importingStatement->clearArguments();
+ return res;
+// return m_conn->insertRecord(*m_destinationTableSchema, m_dbRowBuffer);
+}
+
+void KexiCSVImportDialog::adjustRows(int iRows)
+{
+ if (m_adjustRows)
+ {
+ m_table->setNumRows( iRows );
+ m_adjustRows=false;
+ for (int i = 0; i<iRows; i++)
+ m_table->adjustRow(i);
+ }
+}
+
+void KexiCSVImportDialog::formatChanged(int id)
+{
+ if (id==_PK_FLAG) {
+ if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
+ m_table->setPixmap(0, m_primaryKeyColumn, QPixmap());
+ }
+ if (m_primaryKeyField->isChecked()) {
+ m_primaryKeyColumn = m_table->currentColumn();
+ m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
+ }
+ else
+ m_primaryKeyColumn = -1;
+ return;
+ }
+ else {
+ m_detectedTypes[m_table->currentColumn()]=id;
+ m_primaryKeyField->setEnabled( _NUMBER_TYPE == id );
+ m_primaryKeyField->setChecked( m_primaryKeyColumn == m_table->currentColumn() && m_primaryKeyField->isEnabled() );
+ }
+ updateColumnText(m_table->currentColumn());
+}
+
+void KexiCSVImportDialog::delimiterChanged(const QString& delimiter)
+{
+ Q_UNUSED(delimiter);
+ m_columnsAdjusted = false;
+ m_detectDelimiter = false; //selected by hand: do not detect in the future
+ //delayed, otherwise combobox won't be repainted
+ fillTableLater();
+}
+
+void KexiCSVImportDialog::textquoteSelected(int)
+{
+ const QString tq(m_comboQuote->textQuote());
+ if (tq.isEmpty())
+ m_textquote = 0;
+ else
+ m_textquote = tq[0];
+
+ kexipluginsdbg << "KexiCSVImportDialog::textquoteSelected(): " << m_textquote << endl;
+
+ //delayed, otherwise combobox won't be repainted
+ fillTableLater();
+}
+
+void KexiCSVImportDialog::fillTableLater()
+{
+ m_table->setNumRows( 0 );
+ QTimer::singleShot(10, this, SLOT(fillTable()));
+}
+
+void KexiCSVImportDialog::startlineSelected(int startline)
+{
+// const int startline = line.toInt() - 1;
+ if (m_startline == (startline-1))
+ return;
+ m_startline = startline-1;
+ m_adjustRows=true;
+ fillTable();
+ m_table->setFocus();
+}
+
+void KexiCSVImportDialog::currentCellChanged(int, int col)
+{
+ if (m_prevSelectedCol==col)
+ return;
+ m_prevSelectedCol = col;
+ int type = m_detectedTypes[col];
+ if (type==_FP_NUMBER_TYPE)
+ type=_NUMBER_TYPE; //we're simplifying that for now
+
+ m_formatCombo->setCurrentItem( type );
+ m_formatLabel->setText( m_formatComboText.arg(col+1) );
+ m_primaryKeyField->setEnabled( _NUMBER_TYPE == m_detectedTypes[col]);
+ m_primaryKeyField->blockSignals(true); //block to disable executing slotPrimaryKeyFieldToggled()
+ m_primaryKeyField->setChecked( m_primaryKeyColumn == col );
+ m_primaryKeyField->blockSignals(false);
+}
+
+void KexiCSVImportDialog::cellValueChanged(int row,int col)
+{
+ if (row==0) {//column name has changed
+ m_columnNames[ col ] = m_table->text(row, col);
+ m_changedColumnNames.setBit( col );
+ }
+}
+
+void KexiCSVImportDialog::accept()
+{
+//! @todo MOVE MOST OF THIS TO CORE/ (KexiProject?) after KexiDialogBase code is moved to non-gui place
+
+ KexiGUIMessageHandler msg; //! @todo make it better integrated with main window
+
+ const uint numRows( m_table->numRows() );
+ if (numRows == 0)
+ return; //impossible
+
+ if (numRows == 1) {
+ if (KMessageBox::No == KMessageBox::questionYesNo(this,
+ i18n("Data set contains no rows. Do you want to import empty table?")))
+ return;
+ }
+
+ KexiProject* project = m_mainWin->project();
+ if (!project) {
+ msg.showErrorMessage(i18n("No project available."));
+ return;
+ }
+ m_conn = project->dbConnection(); //cache this pointer
+ if (!m_conn) {
+ msg.showErrorMessage(i18n("No database connection available."));
+ return;
+ }
+ KexiPart::Part *part = Kexi::partManager().partForMimeType("kexi/table");
+ if (!part) {
+ msg.showErrorMessage(&Kexi::partManager());
+ return;
+ }
+
+ //get suggested name based on the file name
+ QString suggestedName;
+ if (m_mode==File) {
+ suggestedName = KURL::fromPathOrURL(m_fname).fileName();
+ //remove extension
+ if (!suggestedName.isEmpty()) {
+ const int idx = suggestedName.findRev(".");
+ if (idx!=-1)
+ suggestedName = suggestedName.mid(0, idx ).simplifyWhiteSpace();
+ }
+ }
+
+ //-new part item
+ KexiPart::Item* partItemForSavedTable = project->createPartItem(part->info(), suggestedName);
+ if (!partItemForSavedTable) {
+ // msg.showErrorMessage(project);
+ return;
+ }
+
+#define _ERR \
+ { project->deleteUnstoredItem(partItemForSavedTable); \
+ m_conn = 0; \
+ delete m_destinationTableSchema; \
+ m_destinationTableSchema = 0; \
+ return; }
+
+ //-ask for table name/title
+ // (THIS IS FROM KexiMainWindowImpl::saveObject())
+ bool allowOverwriting = true;
+ tristate res = m_mainWin->getNewObjectInfo( partItemForSavedTable, part, allowOverwriting );
+ if (~res || !res) {
+ //! @todo: err
+ _ERR;
+ }
+ //(allowOverwriting is now set to true, if user accepts overwriting,
+ // and overwriting will be needed)
+
+// KexiDB::SchemaData sdata(part->info()->projectPartID());
+// sdata.setName( partItem->name() );
+
+ //-create table schema (and thus schema object)
+ //-assign information (THIS IS FROM KexiDialogBase::storeNewData())
+ m_destinationTableSchema = new KexiDB::TableSchema(partItemForSavedTable->name());
+ m_destinationTableSchema->setCaption( partItemForSavedTable->caption() );
+ m_destinationTableSchema->setDescription( partItemForSavedTable->description() );
+ const uint numCols( m_table->numCols() );
+
+ m_implicitPrimaryKeyAdded = false;
+ //add PK if user wanted it
+ int msgboxResult;
+ if (m_primaryKeyColumn==-1
+ && KMessageBox::No != (msgboxResult = KMessageBox::questionYesNoCancel(this,
+ i18n("No Primary Key (autonumber) has been defined.\n"
+ "Should it be automatically defined on import (recommended)?\n\n"
+ "Note: An imported table without a Primary Key may not be editable (depending on database type)."),
+ QString::null, KGuiItem(i18n("Add Database Primary Key to a Table", "Add Primary Key"), "key"),
+ KGuiItem(i18n("Do Not Add Database Primary Key to a Table", "Do Not Add")))))
+ {
+ if (msgboxResult == KMessageBox::Cancel)
+ _ERR; //cancel accepting
+
+ //add implicit PK field
+//! @todo make this field hidden (what about e.g. pgsql?)
+ m_implicitPrimaryKeyAdded = true;
+
+ QString fieldName("id");
+ QString fieldCaption("Id");
+
+ QStringList colnames;
+ for (uint col = 0; col < numCols; col++)
+ colnames.append( m_table->text(0, col).lower().simplifyWhiteSpace() );
+
+ if (colnames.find(fieldName)!=colnames.end()) {
+ int num = 1;
+ while (colnames.find(fieldName+QString::number(num))!=colnames.end())
+ num++;
+ fieldName += QString::number(num);
+ fieldCaption += QString::number(num);
+ }
+ KexiDB::Field *field = new KexiDB::Field(
+ fieldName,
+ KexiDB::Field::Integer,
+ KexiDB::Field::NoConstraints,
+ KexiDB::Field::NoOptions,
+ 0,0, //uint length=0, uint precision=0,
+ QVariant(), //QVariant defaultValue=QVariant(),
+ fieldCaption
+ ); //no description and width for now
+ field->setPrimaryKey(true);
+ field->setAutoIncrement(true);
+ m_destinationTableSchema->addField( field );
+ }
+
+ for (uint col = 0; col < numCols; col++) {
+ QString fieldCaption( m_table->text(0, col).simplifyWhiteSpace() );
+ QString fieldName( KexiUtils::string2Identifier( fieldCaption ) );
+ if (m_destinationTableSchema->field(fieldName)) {
+ QString fixedFieldName;
+ uint i = 2; //"apple 2, apple 3, etc. if there're many "apple" names
+ do {
+ fixedFieldName = fieldName + "_" + QString::number(i);
+ if (!m_destinationTableSchema->field(fixedFieldName))
+ break;
+ i++;
+ } while (true);
+ fieldName = fixedFieldName;
+ fieldCaption += (" " + QString::number(i));
+ }
+ const int detectedType = m_detectedTypes[col];
+ KexiDB::Field::Type fieldType;
+ if (detectedType==_DATE_TYPE)
+ fieldType = KexiDB::Field::Date;
+ if (detectedType==_TIME_TYPE)
+ fieldType = KexiDB::Field::Time;
+ if (detectedType==_DATETIME_TYPE)
+ fieldType = KexiDB::Field::DateTime;
+ else if (detectedType==_NUMBER_TYPE)
+ fieldType = KexiDB::Field::Integer;
+ else if (detectedType==_FP_NUMBER_TYPE)
+ fieldType = KexiDB::Field::Double;
+//! @todo what about time and float/double types and different integer subtypes?
+ else //_TEXT_TYPE and the rest
+ fieldType = KexiDB::Field::Text;
+//! @todo what about long text?
+
+ KexiDB::Field *field = new KexiDB::Field(
+ fieldName,
+ fieldType,
+ KexiDB::Field::NoConstraints,
+ KexiDB::Field::NoOptions,
+ 0,0, //uint length=0, uint precision=0,
+ QVariant(), //QVariant defaultValue=QVariant(),
+ fieldCaption
+ ); //no description and width for now
+
+ if ((int)col == m_primaryKeyColumn) {
+ field->setPrimaryKey(true);
+ field->setAutoIncrement(true);
+ }
+ m_destinationTableSchema->addField( field );
+ }
+
+ KexiDB::Transaction transaction = m_conn->beginTransaction();
+ if (transaction.isNull()) {
+ msg.showErrorMessage(m_conn);
+ _ERR;
+ }
+ KexiDB::TransactionGuard tg(transaction);
+
+ //-create physical table
+ if (!m_conn->createTable(m_destinationTableSchema, allowOverwriting)) {
+ msg.showErrorMessage(m_conn);
+ _ERR;
+ }
+
+#define _DROP_DEST_TABLE_AND_RETURN \
+ { \
+ if (m_importingProgressDlg) \
+ m_importingProgressDlg->hide(); \
+ project->deleteUnstoredItem(partItemForSavedTable); \
+ m_conn->dropTable(m_destinationTableSchema); /*alsoRemoveSchema*/ \
+ m_destinationTableSchema = 0; \
+ m_conn = 0; \
+ return; \
+ }
+
+ m_importingStatement = m_conn->prepareStatement(
+ KexiDB::PreparedStatement::InsertStatement, *m_destinationTableSchema);
+ if (!m_importingStatement) {
+ msg.showErrorMessage(m_conn);
+ _DROP_DEST_TABLE_AND_RETURN;
+ }
+
+ if (m_file) {
+ if (!m_importingProgressDlg) {
+ m_importingProgressDlg = new KProgressDialog( this, "m_importingProgressDlg",
+ i18n("Importing CSV Data"), QString::null, true );
+ }
+ m_importingProgressDlg->setLabel(
+ i18n("Importing CSV Data from <nobr>\"%1\"</nobr> into \"%2\" table...")
+ .arg(QDir::convertSeparators(m_fname)).arg(m_destinationTableSchema->name()) );
+ m_importingProgressDlg->progressBar()->setTotalSteps( QFileInfo(*m_file).size() );
+ m_importingProgressDlg->show();
+ }
+
+ int row, column, maxColumn;
+ QString field = QString::null;
+
+ // main job
+ res = loadRows(field, row, column, maxColumn, false /*!gui*/ );
+
+ delete m_importingProgressDlg;
+ m_importingProgressDlg = 0;
+ if (true != res) {
+ //importing cancelled or failed
+ if (!res) //do not display err msg when res == cancelled
+ msg.showErrorMessage(m_conn);
+ _DROP_DEST_TABLE_AND_RETURN;
+ }
+
+ // file with only one line without '\n'
+ if (field.length() > 0)
+ {
+ setText(row - m_startline, column, field, false /*!gui*/);
+ //fill remaining empty fields (database wants them explicitly)
+ for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
+ setText(row - m_startline, additionalColumn, QString::null, false /*!gui*/);
+ }
+ if (!saveRow(false /*!gui*/)) {
+ msg.showErrorMessage(m_conn);
+ _DROP_DEST_TABLE_AND_RETURN;
+ }
+ ++row;
+ field = QString::null;
+ }
+
+ if (!tg.commit()) {
+ msg.showErrorMessage(m_conn);
+ _DROP_DEST_TABLE_AND_RETURN;
+ }
+
+ //-now we can store the item
+ partItemForSavedTable->setIdentifier( m_destinationTableSchema->id() );
+ project->addStoredItem( part->info(), partItemForSavedTable );
+
+ QDialog::accept();
+ KMessageBox::information(this, i18n("Data has been successfully imported to table \"%1\".")
+ .arg(m_destinationTableSchema->name()));
+ parentWidget()->raise();
+ m_conn = 0;
+}
+
+int KexiCSVImportDialog::getHeader(int col)
+{
+ QString header = m_table->horizontalHeader()->label(col);
+
+ if (header == i18n("Text type for column", "Text"))
+ return TEXT;
+ else if (header == i18n("Numeric type for column", "Number"))
+ return NUMBER;
+ else if (header == i18n("Currency type for column", "Currency"))
+ return CURRENCY;
+ else
+ return DATE;
+}
+
+QString KexiCSVImportDialog::getText(int row, int col)
+{
+ return m_table->text(row, col);
+}
+
+void KexiCSVImportDialog::ignoreDuplicatesChanged(int)
+{
+ fillTable();
+}
+
+void KexiCSVImportDialog::slot1stRowForFieldNamesChanged(int)
+{
+ m_adjustRows=true;
+ if (m_1stRowForFieldNames->isChecked() && m_startline>0 && m_startline>=(m_startAtLineSpinBox->maxValue()-1))
+ m_startline--;
+ fillTable();
+}
+
+void KexiCSVImportDialog::optionsButtonClicked()
+{
+ KexiCSVImportOptionsDialog dlg(m_options, this);
+ if (QDialog::Accepted != dlg.exec())
+ return;
+
+ KexiCSVImportOptions newOptions( dlg.options() );
+ if (m_options != newOptions) {
+ m_options = newOptions;
+ if (!openData())
+ return;
+ fillTable();
+ }
+}
+
+bool KexiCSVImportDialog::eventFilter ( QObject * watched, QEvent * e )
+{
+ QEvent::Type t = e->type();
+ // temporary disable keyboard and mouse events for time-consuming tasks
+ if (m_blockUserEvents && (t==QEvent::KeyPress || t==QEvent::KeyRelease
+ || t==QEvent::MouseButtonPress || t==QEvent::MouseButtonDblClick
+ || t==QEvent::Paint ))
+ return true;
+
+ if (watched == m_startAtLineSpinBox && t==QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ if (ke->key()==Qt::Key_Enter || ke->key()==Qt::Key_Return) {
+ m_table->setFocus();
+ return true;
+ }
+ }
+ return QDialog::eventFilter( watched, e );
+}
+
+void KexiCSVImportDialog::slotPrimaryKeyFieldToggled(bool on)
+{
+ Q_UNUSED(on);
+ formatChanged(_PK_FLAG);
+}
+
+void KexiCSVImportDialog::updateRowCountInfo()
+{
+ m_infoLbl->setFileName( m_fname );
+ if (m_allRowsLoadedInPreview) {
+ m_infoLbl->setCommentText(
+ i18n("row count", "(rows: %1)").arg( m_table->numRows()-1+m_startline ) );
+ QToolTip::remove( m_infoLbl );
+ }
+ else {
+ m_infoLbl->setCommentText(
+ i18n("row count", "(rows: more than %1)").arg( m_table->numRows()-1+m_startline ) );
+ QToolTip::add( m_infoLbl->commentLabel(), i18n("Not all rows are visible on this preview") );
+ }
+}
+
+#include "kexicsvimportdialog.moc"
diff --git a/kexi/plugins/importexport/csv/kexicsvimportdialog.h b/kexi/plugins/importexport/csv/kexicsvimportdialog.h
new file mode 100644
index 00000000..1f7b159e
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvimportdialog.h
@@ -0,0 +1,231 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This work is based on kspread/dialogs/kspread_dlg_csv.cc
+ and will be merged back with KOffice libraries.
+
+ Copyright (C) 2002-2003 Norbert Andres <nandres@web.de>
+ Copyright (C) 2002-2003 Ariya Hidayat <ariya@kde.org>
+ Copyright (C) 2002 Laurent Montel <montel@kde.org>
+ Copyright (C) 1999 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXI_CSVDIALOG_H
+#define KEXI_CSVDIALOG_H
+
+#include <qvaluevector.h>
+#include <qvaluelist.h>
+#include <qptrvector.h>
+#include <qregexp.h>
+#include <qbitarray.h>
+
+#include <kdialogbase.h>
+
+#include <kexiutils/tristate.h>
+#include <kexidb/connection.h>
+
+#include "kexicsvimportoptionsdlg.h"
+
+class QVBoxLayout;
+class QHBoxLayout;
+class QGridLayout;
+class QButtonGroup;
+class QCheckBox;
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class QRadioButton;
+class QTable;
+class QFile;
+class KComboBox;
+class KIntSpinBox;
+class KProgressDialog;
+
+class KexiMainWindow;
+class KexiCSVDelimiterWidget;
+class KexiCSVTextQuoteComboBox;
+class KexiCSVInfoLabel;
+
+/**
+ * @short Kexi CSV import dialog
+ *
+ * This is temporary solution for Kexi CSV import,
+ * based on kspread/dialogs/kspread_dlg_csv.h, cc.
+ *
+ * Provides dialog for managing CSV (comma separated value) data.
+ *
+ * Currently KexiCSVImportDialog is used for converting text into columns,
+ * inserting text file and pasting text from clipboard, where conversion
+ * from CSV (comma separated value) data is is all required.
+ * The different purposed mentioned above is determined
+ * using mode, which can be Column, File, or Clipboard respectively.
+*/
+class KexiCSVImportDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ enum Mode { Clipboard, File /*, Column*/ };
+ enum Header { TEXT, NUMBER, DATE, CURRENCY };
+
+ //! @todo what about making it kexidb-independent?
+ KexiCSVImportDialog( Mode mode, KexiMainWindow* mainWin, QWidget * parent,
+ const char * name = 0/*, QRect const & rect*/);
+
+ virtual ~KexiCSVImportDialog();
+
+ bool cancelled() const;
+ virtual bool eventFilter ( QObject * watched, QEvent * e );
+
+ protected:
+ bool openData();
+ virtual void accept();
+
+ private:
+ QGridLayout* MyDialogLayout;
+ QHBoxLayout* Layout1;
+ QTable* m_table;
+ KexiCSVDelimiterWidget* m_delimiterWidget;
+ bool m_detectDelimiter; //!< true if delimiter should be detected
+ //!< (true by default, set to false if user sets delimiter)
+ QString m_formatComboText;
+ QLabel* m_formatLabel;
+ KComboBox* m_formatCombo;
+ KIntSpinBox *m_startAtLineSpinBox;
+ KexiCSVTextQuoteComboBox* m_comboQuote;
+ QLabel* m_startAtLineLabel;
+ QLabel* TextLabel2;
+ QCheckBox* m_ignoreDuplicates;
+ QCheckBox* m_1stRowForFieldNames;
+ QCheckBox* m_primaryKeyField;
+
+ KexiMainWindow* m_mainWin;
+
+ void detectTypeAndUniqueness(int row, int col, const QString& text);
+ void setText(int row, int col, const QString& text, bool inGUI);
+
+ /*! Parses date from \a text and stores into \a date.
+ m_dateRegExp is used for clever detection;
+ if '/' separated is found, it's assumed the format is american mm/dd/yyyy.
+ This function supports omitted zeros, so 1/2/2006 is parsed properly too.
+ \return true on success. */
+ bool parseDate(const QString& text, QDate& date);
+
+ /*! Parses time from \a text and stores into \a date.
+ m_timeRegExp1 and m_timeRegExp2 are used for clever detection;
+ both hh:mm:ss and hh:mm are supported.
+ This function supports omitted zeros, so 1:2:3 is parsed properly too.
+ \return true on success. */
+ bool parseTime(const QString& text, QTime& time);
+
+ /*! Called after the first fillTable() when number of rows is unknown. */
+ void adjustRows(int iRows);
+
+ int getHeader(int col);
+ QString getText(int row, int col);
+ void updateColumnText(int col);
+ void updateRowCountInfo();
+ tristate loadRows(QString &field, int &row, int &columnm, int &maxColumn, bool inGUI);
+
+ /*! Detects delimiter by looking at first 4K bytes of the data. Used by loadRows().
+ The used algorithm:
+ 1. Look byte by byte and locate special characters that can be delimiters.
+ Special fact is taken into account: if there are '"' quotes used for text values,
+ delimiters that follow directly the closing quote has higher priority than the one
+ that follows other character. We do not assume that every text value is quoted.
+ Summing up, there is following hierarchy (from highest to lowest):
+ quote+tab, quote+semicolon, quote+comma, tab, semicolon, comma.
+ Space characters are skipped. Text inside quotes is skipped, as well as double
+ (escaped) quotes.
+ 2. While scanning the data, for every row following number of tabs, semicolons and commas
+ (only these outside of the quotes) are computed. On every line the values are appended
+ to a separate list (QValueList<int>).
+ 3. After scanning, all the values are checked on the QValueList<int> of tabs.
+ If the list has more one element (so there was more than one row) and all the values
+ (numbers of tabs) are equal, it's very probable the tab is a delimiter.
+ So, this character is returned as a delimiter.
+ 3a. The same algorithm as in 3. is performed for semicolon character.
+ 3b. The same algorithm as in 3. is performed for comma character.
+ 4. If the step 3. did not return a delimiter, a character found in step 1. with
+ the highest priority is retured as delimiter. */
+ QString detectDelimiterByLookingAtFirstBytesOfFile(QTextStream& inputStream);
+
+ /*! Callback, called whenever row is loaded in loadRows(). When inGUI is true,
+ nothing is performed, else database buffer is written back to the database. */
+ bool saveRow(bool inGUI);
+
+ bool m_cancelled;
+ bool m_adjustRows;
+ int m_startline;
+ QChar m_textquote;
+ QString m_clipboardData;
+ QByteArray m_fileArray;
+ Mode m_mode;
+ int m_prevSelectedCol;
+
+ //! vector of detected types, 0==text (the default), 1==number, 2==date
+//! @todo more types
+ QValueVector<int> m_detectedTypes;
+
+ //! m_detectedUniqueColumns[i]==true means that i-th column has unique values
+ //! (only for numeric type)
+ QPtrVector< QValueList<int> > m_uniquenessTest;
+
+ QRegExp m_dateRegExp, m_timeRegExp1, m_timeRegExp2, m_fpNumberRegExp;
+ QValueVector<QString> m_typeNames, m_columnNames;
+ QBitArray m_changedColumnNames;
+ bool m_columnsAdjusted : 1; //!< to call adjustColumn() only once
+ bool m_1stRowForFieldNamesDetected : 1; //!< used to force rerun fillTable() after 1st row
+ bool m_firstFillTableCall : 1; //!< used to know whether it's 1st fillTable() call
+ bool m_blockUserEvents : 1;
+ int m_primaryKeyColumn; //!< index of column with PK assigned (-1 if none)
+ int m_maximumRowsForPreview;
+ int m_maximumBytesForPreview;
+ QPixmap m_pkIcon;
+ QString m_fname;
+ QFile* m_file;
+ QTextStream *m_inputStream; //!< used in loadData()
+ KexiCSVImportOptions m_options;
+ KProgressDialog *m_loadingProgressDlg, *m_importingProgressDlg;
+ bool m_dialogCancelled;
+ KexiCSVInfoLabel *m_infoLbl;
+ KexiDB::Connection *m_conn; //!< (temp) database connection used for importing
+ KexiDB::TableSchema *m_destinationTableSchema; //!< (temp) dest. table schema used for importing
+ KexiDB::PreparedStatement::Ptr m_importingStatement;
+ QValueList<QVariant> m_dbRowBuffer; //!< (temp) used for importing
+ bool m_implicitPrimaryKeyAdded; //!< (temp) used for importing
+ bool m_allRowsLoadedInPreview; //!< we need to know whether all rows were loaded or it's just a partial data preview
+ bool m_stoppedAt_MAX_BYTES_TO_PREVIEW; //!< used to compute m_allRowsLoadedInPreview
+
+ private slots:
+ void fillTable();
+ void fillTableLater();
+ void initLater();
+ void formatChanged(int id);
+ void delimiterChanged(const QString& delimiter);
+ void startlineSelected(int line);
+ void textquoteSelected(int);
+ void currentCellChanged(int, int col);
+ void ignoreDuplicatesChanged(int);
+ void slot1stRowForFieldNamesChanged(int);
+ void cellValueChanged(int row,int col);
+ void optionsButtonClicked();
+ void slotPrimaryKeyFieldToggled(bool on);
+};
+
+#endif
diff --git a/kexi/plugins/importexport/csv/kexicsvimportoptionsdlg.cpp b/kexi/plugins/importexport/csv/kexicsvimportoptionsdlg.cpp
new file mode 100644
index 00000000..b381dde3
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvimportoptionsdlg.cpp
@@ -0,0 +1,140 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "kexicsvimportoptionsdlg.h"
+#include <widget/kexicharencodingcombobox.h>
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtextcodec.h>
+#include <qcheckbox.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kcombobox.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+
+KexiCSVImportOptions::KexiCSVImportOptions()
+{
+ kapp->config()->setGroup("ImportExport");
+ encoding = kapp->config()->readEntry("DefaultEncodingForImportingCSVFiles");
+ if (encoding.isEmpty()) {
+ encoding = QString::fromLatin1(KGlobal::locale()->encoding());
+ defaultEncodingExplicitySet = false;
+ }
+ else
+ defaultEncodingExplicitySet = true;
+
+ stripWhiteSpaceInTextValuesChecked
+ = kapp->config()->readBoolEntry("StripBlanksOffOfTextValuesWhenImportingCSVFiles", true);
+}
+
+KexiCSVImportOptions::~KexiCSVImportOptions()
+{
+}
+
+bool KexiCSVImportOptions::operator== ( const KexiCSVImportOptions & opt ) const
+{
+ return defaultEncodingExplicitySet==opt.defaultEncodingExplicitySet
+ && stripWhiteSpaceInTextValuesChecked==opt.stripWhiteSpaceInTextValuesChecked
+ && encoding==opt.encoding;
+}
+
+bool KexiCSVImportOptions::operator!= ( const KexiCSVImportOptions & opt ) const
+{
+ return !( *this==opt );
+}
+
+//----------------------------------
+
+KexiCSVImportOptionsDialog::KexiCSVImportOptionsDialog(
+ const KexiCSVImportOptions& options, QWidget* parent )
+ : KDialogBase(
+ KDialogBase::Plain,
+ i18n( "CSV Import Options" ),
+ Ok|Cancel,
+ Ok,
+ parent,
+ "KexiCSVImportOptionsDialog",
+ true,
+ false
+ )
+{
+ QGridLayout *lyr = new QGridLayout( plainPage(), 5, 3,
+ KDialogBase::marginHint(), KDialogBase::spacingHint());
+
+ m_encodingComboBox = new KexiCharacterEncodingComboBox(plainPage(), options.encoding);
+ lyr->addWidget( m_encodingComboBox, 0, 1 );
+
+ QLabel* lbl = new QLabel( m_encodingComboBox, i18n("Text encoding:"), plainPage());
+ lyr->addWidget( lbl, 0, 0 );
+
+ lyr->addItem( new QSpacerItem( 20, KDialogBase::spacingHint(), QSizePolicy::Fixed, QSizePolicy::Fixed ), 2, 1 );
+ lyr->addItem( new QSpacerItem( 121, KDialogBase::spacingHint(), QSizePolicy::Expanding, QSizePolicy::Minimum ), 0, 2 );
+
+ m_chkAlwaysUseThisEncoding = new QCheckBox(
+ i18n("Always use this encoding when importing CSV data files"), plainPage());
+ lyr->addWidget( m_chkAlwaysUseThisEncoding, 1, 1 );
+
+ m_chkStripWhiteSpaceInTextValues = new QCheckBox(
+ i18n("Strip leading and trailing blanks off of text values"), plainPage());
+ lyr->addWidget( m_chkStripWhiteSpaceInTextValues, 3, 1 );
+ lyr->addItem( new QSpacerItem( 20, KDialogBase::spacingHint(), QSizePolicy::Minimum, QSizePolicy::Expanding ), 4, 1 );
+
+ //update widgets
+ if (options.defaultEncodingExplicitySet) {
+ m_encodingComboBox->setSelectedEncoding(options.encoding);
+ m_chkAlwaysUseThisEncoding->setChecked(true);
+ }
+ m_chkStripWhiteSpaceInTextValues->setChecked(options.stripWhiteSpaceInTextValuesChecked);
+
+ adjustSize();
+ m_encodingComboBox->setFocus();
+}
+
+KexiCSVImportOptionsDialog::~KexiCSVImportOptionsDialog()
+{
+}
+
+KexiCSVImportOptions KexiCSVImportOptionsDialog::options() const
+{
+ KexiCSVImportOptions opt;
+ opt.encoding = m_encodingComboBox->selectedEncoding();
+ opt.stripWhiteSpaceInTextValuesChecked = m_chkStripWhiteSpaceInTextValues->isChecked();
+ return opt;
+}
+
+void KexiCSVImportOptionsDialog::accept()
+{
+ kapp->config()->setGroup("ImportExport");
+ if (m_chkAlwaysUseThisEncoding->isChecked())
+ kapp->config()->writeEntry("DefaultEncodingForImportingCSVFiles",
+ m_encodingComboBox->selectedEncoding());
+ else
+ kapp->config()->deleteEntry("DefaultEncodingForImportingCSVFiles");
+
+ kapp->config()->writeEntry("StripBlanksOffOfTextValuesWhenImportingCSVFiles",
+ m_chkStripWhiteSpaceInTextValues->isChecked());
+
+ KDialogBase::accept();
+}
+
+#include "kexicsvimportoptionsdlg.moc"
diff --git a/kexi/plugins/importexport/csv/kexicsvimportoptionsdlg.h b/kexi/plugins/importexport/csv/kexicsvimportoptionsdlg.h
new file mode 100644
index 00000000..e0567c9c
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvimportoptionsdlg.h
@@ -0,0 +1,62 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KEXICSVOPTIONSDIALOG_H
+#define KEXICSVOPTIONSDIALOG_H
+
+#include <kdialogbase.h>
+#include <qcheckbox.h>
+
+class KexiCharacterEncodingComboBox;
+
+//! @short CSV Options
+class KexiCSVImportOptions
+{
+ public:
+ KexiCSVImportOptions();
+ ~KexiCSVImportOptions();
+
+ bool operator== ( const KexiCSVImportOptions & opt ) const;
+ bool operator!= ( const KexiCSVImportOptions & opt ) const;
+
+ QString encoding;
+ bool defaultEncodingExplicitySet;
+ bool stripWhiteSpaceInTextValuesChecked;
+};
+
+//! @short CSV Options dialog
+class KexiCSVImportOptionsDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ KexiCSVImportOptionsDialog( const KexiCSVImportOptions& options, QWidget* parent = 0 );
+ virtual ~KexiCSVImportOptionsDialog();
+
+ KexiCSVImportOptions options() const;
+
+ protected slots:
+ virtual void accept();
+
+ protected:
+ KexiCharacterEncodingComboBox *m_encodingComboBox;
+ QCheckBox *m_chkAlwaysUseThisEncoding;
+ QCheckBox *m_chkStripWhiteSpaceInTextValues;
+};
+
+#endif
diff --git a/kexi/plugins/importexport/csv/kexicsvwidgets.cpp b/kexi/plugins/importexport/csv/kexicsvwidgets.cpp
new file mode 100644
index 00000000..8e3cf4c2
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvwidgets.cpp
@@ -0,0 +1,233 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexicsvwidgets.h"
+
+#include <qdir.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <klineedit.h>
+#include <kdialogbase.h>
+#include <kactivelabel.h>
+#include <kiconloader.h>
+#include <kmimetype.h>
+
+#define KEXICSV_OTHER_DELIMITER_INDEX 4
+
+KexiCSVDelimiterWidget::KexiCSVDelimiterWidget( bool lineEditOnBottom, QWidget * parent )
+ : QWidget(parent, "KexiCSVDelimiterWidget")
+ , m_availableDelimiters(KEXICSV_OTHER_DELIMITER_INDEX)
+
+{
+ QBoxLayout *lyr =
+ lineEditOnBottom ?
+ (QBoxLayout *)new QVBoxLayout( this, 0, KDialogBase::spacingHint() )
+ : (QBoxLayout *)new QHBoxLayout( this, 0, KDialogBase::spacingHint() );
+
+ m_availableDelimiters[0]=KEXICSV_DEFAULT_FILE_DELIMITER;
+ m_availableDelimiters[1]=";";
+ m_availableDelimiters[2]="\t";
+ m_availableDelimiters[3]=" ";
+
+ m_combo = new KComboBox(this, "KexiCSVDelimiterComboBox");
+ m_combo->insertItem( i18n("Comma \",\"") ); //<-- KEXICSV_DEFAULT_FILE_DELIMITER
+ m_combo->insertItem( i18n( "Semicolon \";\"" ) );
+ m_combo->insertItem( i18n( "Tabulator" ) );
+ m_combo->insertItem( i18n( "Space \" \"" ) );
+ m_combo->insertItem( i18n( "Other" ) );
+ lyr->addWidget(m_combo);
+ setFocusProxy(m_combo);
+
+ m_delimiterEdit = new KLineEdit( this, "m_delimiterEdit" );
+// m_delimiterEdit->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)0, (QSizePolicy::SizeType)0, 0, 0, m_delimiterEdit->sizePolicy().hasHeightForWidth() ) );
+ m_delimiterEdit->setMaximumSize( QSize( 30, 32767 ) );
+ m_delimiterEdit->setMaxLength(1);
+ lyr->addWidget( m_delimiterEdit );
+ if (!lineEditOnBottom)
+ lyr->addStretch(2);
+
+ slotDelimiterChangedInternal(KEXICSV_DEFAULT_FILE_DELIMITER_INDEX); //this will init m_delimiter
+ connect(m_combo, SIGNAL(activated(int)),
+ this, SLOT(slotDelimiterChanged(int)));
+ connect(m_delimiterEdit, SIGNAL(returnPressed()),
+ this, SLOT(slotDelimiterLineEditReturnPressed()));
+ connect(m_delimiterEdit, SIGNAL(textChanged( const QString & )),
+ this, SLOT(slotDelimiterLineEditTextChanged( const QString & ) ));
+}
+
+void KexiCSVDelimiterWidget::slotDelimiterChanged(int index)
+{
+ slotDelimiterChangedInternal(index);
+ if (index==KEXICSV_OTHER_DELIMITER_INDEX)
+ m_delimiterEdit->setFocus();
+}
+
+void KexiCSVDelimiterWidget::slotDelimiterChangedInternal(int index)
+{
+ bool changed = false;
+ if (index > KEXICSV_OTHER_DELIMITER_INDEX)
+ return;
+ else if (index == KEXICSV_OTHER_DELIMITER_INDEX) {
+ changed = m_delimiter != m_delimiterEdit->text();
+ m_delimiter = m_delimiterEdit->text();
+ }
+ else {
+ changed = m_delimiter != m_availableDelimiters[index];
+ m_delimiter = m_availableDelimiters[index];
+ }
+ m_delimiterEdit->setEnabled(index == KEXICSV_OTHER_DELIMITER_INDEX);
+ if (changed)
+ emit delimiterChanged(m_delimiter);
+}
+
+void KexiCSVDelimiterWidget::slotDelimiterLineEditReturnPressed()
+{
+ if (m_combo->currentItem() != KEXICSV_OTHER_DELIMITER_INDEX)
+ return;
+ slotDelimiterChangedInternal(KEXICSV_OTHER_DELIMITER_INDEX);
+}
+
+void KexiCSVDelimiterWidget::slotDelimiterLineEditTextChanged( const QString & )
+{
+ slotDelimiterChangedInternal(KEXICSV_OTHER_DELIMITER_INDEX);
+}
+
+void KexiCSVDelimiterWidget::setDelimiter(const QString& delimiter)
+{
+ QValueVector<QString>::ConstIterator it = m_availableDelimiters.constBegin();
+ int index = 0;
+ for (; it != m_availableDelimiters.constEnd(); ++it, index++) {
+ if (*it == delimiter) {
+ m_combo->setCurrentItem(index);
+ slotDelimiterChangedInternal(index);
+ return;
+ }
+ }
+ //else: set other (custom) delimiter
+ m_delimiterEdit->setText(delimiter);
+ m_combo->setCurrentItem(KEXICSV_OTHER_DELIMITER_INDEX);
+ slotDelimiterChangedInternal(KEXICSV_OTHER_DELIMITER_INDEX);
+}
+
+//----------------------------------------------------
+
+KexiCSVTextQuoteComboBox::KexiCSVTextQuoteComboBox( QWidget * parent )
+ : KComboBox(parent, "KexiCSVTextQuoteComboBox")
+{
+ insertItem( "\"" );
+ insertItem( "'" );
+ insertItem( i18n( "None" ) );
+}
+
+QString KexiCSVTextQuoteComboBox::textQuote() const
+{
+ if (currentItem()==2)
+ return QString::null;
+ return currentText();
+}
+
+void KexiCSVTextQuoteComboBox::setTextQuote(const QString& textQuote)
+{
+ if (textQuote=="\"" || textQuote=="'")
+ setCurrentText(textQuote);
+ else if (textQuote.isEmpty())
+ setCurrentText(i18n( "None" ));
+}
+
+//----------------------------------------------------
+
+KexiCSVInfoLabel::KexiCSVInfoLabel( const QString& labelText, QWidget* parent )
+ : QWidget(parent, "KexiCSVInfoLabel")
+{
+ QVBoxLayout *vbox = new QVBoxLayout( this, 0, KDialogBase::spacingHint() );
+ QHBoxLayout *hbox = new QHBoxLayout( this );
+ vbox->addLayout(hbox);
+ m_leftLabel = new QLabel(labelText, this);
+ m_leftLabel->setMinimumWidth(130);
+ m_leftLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ m_leftLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft | Qt::WordBreak);
+ hbox->addWidget(m_leftLabel);
+ m_iconLbl = new QLabel(this);
+ m_iconLbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ m_iconLbl->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
+ m_fnameLbl = new KActiveLabel(this);
+ m_fnameLbl->setFocusPolicy(NoFocus);
+ m_fnameLbl->setTextFormat(Qt::PlainText);
+ m_fnameLbl->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding,1,0));
+ m_fnameLbl->setLineWidth(1);
+ m_fnameLbl->setFrameStyle(QFrame::Box);
+ m_fnameLbl->setAlignment(Qt::AlignVCenter | Qt::AlignLeft | Qt::WordBreak);
+ hbox->addSpacing(5);
+ hbox->addWidget(m_iconLbl);
+ hbox->addWidget(m_fnameLbl, 1, Qt::AlignVCenter | Qt::AlignLeft | Qt::WordBreak);
+ hbox->addSpacing(10);
+ m_commentLbl = new KActiveLabel(this);
+ m_commentLbl->setFocusPolicy(NoFocus);
+ m_commentLbl->setTextFormat(Qt::PlainText);
+ m_commentLbl->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ m_commentLbl->setLineWidth(1);
+ m_commentLbl->setFrameStyle(QFrame::Box);
+ m_commentLbl->setAlignment(Qt::AlignVCenter | Qt::AlignLeft | Qt::WordBreak);
+ hbox->addWidget(m_commentLbl, 0, Qt::AlignVCenter | Qt::AlignRight | Qt::WordBreak);
+
+ m_separator = new QFrame(this);
+ m_separator->setFrameShape(QFrame::HLine);
+ m_separator->setFrameShadow(QFrame::Sunken);
+ vbox->addWidget(m_separator);
+}
+
+void KexiCSVInfoLabel::setFileName( const QString& fileName )
+{
+ m_fnameLbl->setText( QDir::convertSeparators(fileName) );
+ if (!fileName.isEmpty()) {
+ m_iconLbl->setPixmap(
+ KMimeType::pixmapForURL(KURL::fromPathOrURL(fileName), 0, KIcon::Desktop) );
+ }
+}
+
+void KexiCSVInfoLabel::setLabelText( const QString& text )
+{
+ m_fnameLbl->setText( text );
+// int lines = m_fnameLbl->lines();
+// m_fnameLbl->setFixedHeight(
+// QFontMetrics(m_fnameLbl->currentFont()).height() * lines );
+}
+
+void KexiCSVInfoLabel::setIcon(const QString& iconName)
+{
+ m_iconLbl->setPixmap( DesktopIcon(iconName) );
+}
+
+void KexiCSVInfoLabel::setCommentText( const QString& text )
+{
+ m_commentLbl->setText(text);
+}
+
+//----------------------------------------------------
+
+QStringList csvMimeTypes()
+{
+ QStringList mimetypes;
+ mimetypes << "text/x-csv" << "text/plain" << "all/allfiles";
+ return mimetypes;
+}
+
+#include "kexicsvwidgets.moc"
diff --git a/kexi/plugins/importexport/csv/kexicsvwidgets.h b/kexi/plugins/importexport/csv/kexicsvwidgets.h
new file mode 100644
index 00000000..f128b658
--- /dev/null
+++ b/kexi/plugins/importexport/csv/kexicsvwidgets.h
@@ -0,0 +1,116 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXI_CSVWIDGETS_H
+#define KEXI_CSVWIDGETS_H
+
+#include <qvaluevector.h>
+#include <kcombobox.h>
+
+class KLineEdit;
+class KActiveLabel;
+class QLabel;
+
+#define KEXICSV_DEFAULT_FILE_TEXT_QUOTE "\""
+#define KEXICSV_DEFAULT_CLIPBOARD_TEXT_QUOTE ""
+#define KEXICSV_DEFAULT_FILE_DELIMITER ","
+#define KEXICSV_DEFAULT_CLIPBOARD_DELIMITER "\t"
+#define KEXICSV_DEFAULT_FILE_DELIMITER_INDEX 0
+
+//! \return a list of mimetypes usable for handling CSV format handling
+QStringList csvMimeTypes();
+
+/*! @short A helper widget showing a short text information with an icon.
+ See ctor for details.
+ Used by CSV import and export dialogs. */
+class KexiCSVInfoLabel : public QWidget
+{
+ public:
+ /* Sets up a new info label \a labelText label with text like "Preview of data from file:".
+ setFileName() can be used to display filename and setCommentAfterFileName() to display
+ additional comment.
+
+ The widget's layout can look like this:
+
+ \pre [icon] [labeltext] [filename] [comment]
+ */
+ KexiCSVInfoLabel( const QString& labelText, QWidget* parent );
+
+ void setFileName( const QString& fileName );
+ void setLabelText( const QString& text );
+ void setCommentText( const QString& text );
+// void setIconForFileName();
+
+ //! sets icon pixmap to \a iconName. Used wher setIconForFilename was false in ctor.
+ void setIcon(const QString& iconName);
+
+ QLabel* leftLabel() const { return m_leftLabel; }
+ KActiveLabel* fileNameLabel() const { return m_fnameLbl; }
+ KActiveLabel* commentLabel() const { return m_commentLbl; }
+ QFrame* separator() const { return m_separator; }
+
+ protected:
+ QLabel *m_leftLabel, *m_iconLbl;
+ KActiveLabel *m_fnameLbl, *m_commentLbl;
+ QFrame* m_separator;
+};
+
+//! @short A combo box widget providing a list of possible delimiters
+//! Used by CSV import and export dialogs
+class KexiCSVDelimiterWidget : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KexiCSVDelimiterWidget( bool lineEditOnBottom, QWidget * parent = 0 );
+
+ QString delimiter() const { return m_delimiter; }
+ void setDelimiter(const QString& delimiter);
+
+ signals:
+ void delimiterChanged(const QString& delimiter);
+
+ protected slots:
+ //! only called when a delimiter was set by user directly
+ void slotDelimiterChanged(int idx);
+ void slotDelimiterChangedInternal(int idx);
+ void slotDelimiterLineEditTextChanged( const QString & );
+ void slotDelimiterLineEditReturnPressed();
+
+ protected:
+ QString m_delimiter;
+ QValueVector<QString> m_availableDelimiters;
+ KComboBox* m_combo;
+ KLineEdit* m_delimiterEdit;
+};
+
+//! @short A combo box widget providing a list of possible quote characters
+//! Used by CSV import and export dialogs
+class KexiCSVTextQuoteComboBox : public KComboBox
+{
+ public:
+ KexiCSVTextQuoteComboBox( QWidget * parent = 0 );
+
+ QString textQuote() const;
+
+ //! Sets text quote. Only available are: ", ', and empty string.
+ void setTextQuote(const QString& textQuote);
+};
+
+#endif
diff --git a/kexi/plugins/macros/Makefile.am b/kexi/plugins/macros/Makefile.am
new file mode 100644
index 00000000..cf5fb0d4
--- /dev/null
+++ b/kexi/plugins/macros/Makefile.am
@@ -0,0 +1,9 @@
+if include_kunittest
+
+ # Unittest is disabled per default.
+ # TESTSDIR = tests
+
+endif
+
+METASOURCES = AUTO
+SUBDIRS = lib kexiactions kexipart
diff --git a/kexi/plugins/macros/configure.in.in b/kexi/plugins/macros/configure.in.in
new file mode 100644
index 00000000..52fb9f5a
--- /dev/null
+++ b/kexi/plugins/macros/configure.in.in
@@ -0,0 +1,13 @@
+# Check for kunittest
+AC_MSG_CHECKING([for kunittest])
+
+# First we check if the console unittester could be compiled
+have_kunittest_header="no"
+KDE_CHECK_HEADER(kunittest/tester.h, have_kunittest_header="yes", , )
+AM_CONDITIONAL(include_kunittest, test "$have_kunittest_header" = "yes")
+
+# Second we check if the GUI-unittester could be compiled
+have_kunittestgui_header="no"
+KDE_CHECK_HEADER(kunittest/runnergui.h, have_kunittestgui_header="yes", , )
+AM_CONDITIONAL(include_kunittestgui, test "$have_kunittestgui_header" = "yes")
+
diff --git a/kexi/plugins/macros/kexiactions/Makefile.am b/kexi/plugins/macros/kexiactions/Makefile.am
new file mode 100644
index 00000000..4f42e5e9
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/Makefile.am
@@ -0,0 +1,27 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+noinst_LTLIBRARIES = libkeximacroactions.la
+
+libkeximacroactions_la_SOURCES = \
+ kexiaction.cpp \
+ openaction.cpp \
+ executeaction.cpp \
+ navigateaction.cpp \
+ messageaction.cpp \
+ datatableaction.cpp
+
+libkeximacroactions_la_CXXFLAGS = $(USE_EXCEPTIONS)
+
+libkeximacroactions_la_LDFLAGS = $(all_libraries)
+libkeximacroactions_la_LIBADD = \
+ $(top_builddir)/kexi/plugins/macros/lib/libkomacro.la \
+ $(top_builddir)/kexi/core/libkexicore.la \
+ $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI)
+
+libkeximacroactions_la_METASOURCES = AUTO
+SUBDIRS = .
+
+INCLUDES = \
+ -I$(top_srcdir)/kexi/core \
+ -I$(top_srcdir)/kexi \
+ $(all_includes)
diff --git a/kexi/plugins/macros/kexiactions/datatableaction.cpp b/kexi/plugins/macros/kexiactions/datatableaction.cpp
new file mode 100644
index 00000000..90b13e4f
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/datatableaction.cpp
@@ -0,0 +1,185 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "datatableaction.h"
+//#include "objectvariable.h"
+
+#include <core/kexi.h>
+#include <core/kexiproject.h>
+#include <core/kexipartmanager.h>
+#include <core/kexipartinfo.h>
+#include <core/kexipart.h>
+#include <core/keximainwindow.h>
+#include <core/kexiinternalpart.h>
+
+#include <klocale.h>
+
+using namespace KexiMacro;
+
+namespace KexiMacro {
+
+ //static const QString OBJECT = "method";
+ //static const QString OBJECT = "type";
+ //static const QString OBJECT = "partitem";
+
+ template<class ACTIONIMPL>
+ class MethodVariable : public KexiVariable<ACTIONIMPL>
+ {
+ public:
+ MethodVariable(ACTIONIMPL* actionimpl)
+ : KexiVariable<ACTIONIMPL>(actionimpl, "method", i18n("Method"))
+ {
+ QStringList list;
+ list << "import" << "export";
+ this->appendChild( KSharedPtr<KoMacro::Variable>( new KoMacro::Variable(list, "@list") ) );
+
+ this->setVariant( list[0] );
+ }
+ };
+
+ template<class ACTIONIMPL>
+ class TypeVariable : public KexiVariable<ACTIONIMPL>
+ {
+ public:
+ TypeVariable(ACTIONIMPL* actionimpl)
+ : KexiVariable<ACTIONIMPL>(actionimpl, "type", i18n("Type"))
+ {
+ QStringList list;
+ list << "file" << "clipboard";
+ this->appendChild( KSharedPtr<KoMacro::Variable>( new KoMacro::Variable(list, "@list") ) );
+
+ this->setVariant( list[0] );
+ }
+ };
+
+ template<class ACTIONIMPL>
+ class PartItemVariable : public KexiVariable<ACTIONIMPL>
+ {
+ public:
+ PartItemVariable(ACTIONIMPL* actionimpl, const QString& partitem = QString::null)
+ : KexiVariable<ACTIONIMPL>(actionimpl, "partitem", i18n("Item"))
+ {
+ QStringList namelist;
+ if(actionimpl->mainWin()->project()) {
+ KexiPart::PartInfoList* parts = Kexi::partManager().partInfoList();
+ for(KexiPart::PartInfoListIterator it(*parts); it.current(); ++it) {
+ KexiPart::Info* info = it.current();
+ if(! info->isDataExportSupported())
+ continue;
+ KexiPart::ItemDict* items = actionimpl->mainWin()->project()->items(info);
+ if(items)
+ for(KexiPart::ItemDictIterator item_it = *items; item_it.current(); ++item_it)
+ namelist << info->objectName() + "." + item_it.current()->name();
+ }
+ for(QStringList::Iterator it = namelist.begin(); it != namelist.end(); ++it)
+ this->appendChild( KSharedPtr<KoMacro::Variable>(new KoMacro::Variable(*it)) );
+
+ //const QString name = info->objectName(); //info->groupName();
+ //this->appendChild( KSharedPtr<KoMacro::Variable>(new KoMacro::Variable(name)) );
+ }
+ const QString n =
+ namelist.contains(partitem)
+ ? partitem
+ : namelist.count() > 0 ? namelist[0] : "";
+ this->setVariant(n);
+ kdDebug()<<"##################### KexiActions::ObjectVariable() variant="<<this->variant()<<endl;
+ }
+ };
+
+}
+
+DataTableAction::DataTableAction()
+ : KexiAction("datatable", i18n("Data Table"))
+{
+ setVariable(KSharedPtr<KoMacro::Variable>( new MethodVariable<DataTableAction>(this) ));
+ setVariable(KSharedPtr<KoMacro::Variable>( new TypeVariable<DataTableAction>(this) ));
+ setVariable(KSharedPtr<KoMacro::Variable>( new PartItemVariable<DataTableAction>(this) ));
+}
+
+DataTableAction::~DataTableAction()
+{
+}
+
+bool DataTableAction::notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name)
+{
+ kdDebug()<<"DataTableAction::notifyUpdated() name="<<name<<" macroitem.action="<<(macroitem->action() ? macroitem->action()->name() : "NOACTION")<<endl;
+ /*
+ KSharedPtr<KoMacro::Variable> variable = macroitem->variable(name, false);
+ if(! variable) {
+ kdWarning()<<"DataTableAction::notifyUpdated() No such variable="<<name<<" in macroitem."<<endl;
+ return false;
+ }
+ variable->clearChildren();
+ if(name == "method") {
+ const int partitem = macroitem->variant(OBJECT, true).toString();
+ macroitem->variable(OBJECT, true)->setChildren(
+ KoMacro::Variable::List() << KSharedPtr<KoMacro::Variable>(new ObjectVariable<ExecuteAction>(this, partitem)) );
+ }
+ */
+ return true;
+}
+
+void DataTableAction::activate(KSharedPtr<KoMacro::Context> context)
+{
+ if(! mainWin()->project()) {
+ kdWarning() << "ExecuteAction::activate(KSharedPtr<KoMacro::Context>) Invalid project" << endl;
+ return;
+ }
+
+ const QString method = context->variable("method")->variant().toString();
+ const QString type = context->variable("type")->variant().toString();
+
+ const QString partitem = context->variable("partitem")->variant().toString();
+ QString identifier;
+ if(! partitem.isEmpty()) {
+ QStringList parts = QStringList::split(".", partitem);
+ KexiPart::Part* part = Kexi::partManager().partForMimeType( QString("kexi/%1").arg(parts[0]) );
+ KexiPart::Item* item = part ? mainWin()->project()->item(part->info(), parts[1]) : 0;
+ if(! item)
+ throw KoMacro::Exception(i18n("No such item \"%1\"").arg(partitem));
+ identifier = QString::number(item->identifier());
+ }
+
+ QMap<QString,QString> args;
+ if(! identifier.isNull())
+ args.insert("itemId", identifier);
+
+ if(method == "import") {
+ args.insert("sourceType", type);
+ QDialog *dlg = KexiInternalPart::createModalDialogInstance(
+ "csv_importexport", "KexiCSVImportDialog", 0, mainWin(), 0, &args);
+ if (!dlg)
+ return; //error msg has been shown by KexiInternalPart
+ dlg->exec();
+ delete dlg;
+ }
+ else if(method == "export") {
+ args.insert("destinationType", type);
+ QDialog *dlg = KexiInternalPart::createModalDialogInstance(
+ "csv_importexport", "KexiCSVExportWizard", 0, mainWin(), 0, &args);
+ if (!dlg)
+ return; //error msg has been shown by KexiInternalPart
+ dlg->exec();
+ delete dlg;
+ }
+ else {
+ throw KoMacro::Exception(i18n("No such method \"%1\"").arg(method));
+ }
+}
+
+//#include "executeaction.moc"
diff --git a/kexi/plugins/macros/kexiactions/datatableaction.h b/kexi/plugins/macros/kexiactions/datatableaction.h
new file mode 100644
index 00000000..3b5b32c0
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/datatableaction.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACRO_DATATABLEACTION_H
+#define KEXIMACRO_DATATABLEACTION_H
+
+#include "kexiaction.h"
+
+class KexiMainWindow;
+
+namespace KoMacro {
+ class Context;
+}
+
+namespace KexiMacro {
+
+ /**
+ * The DataTableAction class implements a @a KoMacro::Action
+ * to provide functionality to import or export a datatable.
+ * The datatable is used to deal with comma separated values.
+ */
+ class DataTableAction : public KexiAction
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ DataTableAction();
+
+ /**
+ * Destructor.
+ */
+ virtual ~DataTableAction();
+
+ /**
+ * This function is called, when the @a KoMacro::Variable
+ * with name @p name used within the @a KoMacro::MacroItem
+ * @p macroitem got changed.
+ *
+ * @param macroitem The @a KoMacro::MacroItem instance where
+ * the variable defined with @p name is located in.
+ * @param name The name the @a KoMacro::Variable has.
+ * @return true if the update was successfully else false
+ * is returned.
+ */
+ virtual bool notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name);
+
+ public slots:
+
+ /**
+ * Called if the @a Action should be executed within the
+ * defined @p context .
+ */
+ virtual void activate(KSharedPtr<KoMacro::Context> context);
+
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/kexiactions/executeaction.cpp b/kexi/plugins/macros/kexiactions/executeaction.cpp
new file mode 100644
index 00000000..1e7f24a2
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/executeaction.cpp
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "executeaction.h"
+
+#include <core/kexi.h>
+#include <core/kexiproject.h>
+#include <core/kexipartmanager.h>
+#include <core/kexipartinfo.h>
+#include <core/kexipart.h>
+#include <core/keximainwindow.h>
+
+#include <klocale.h>
+
+using namespace KexiMacro;
+
+namespace KexiMacro {
+ static const QString OBJECT = "object";
+ static const QString NAME = "name";
+}
+
+ExecuteAction::ExecuteAction()
+ : KexiAction("execute", i18n("Execute"))
+{
+ int conditions = ObjectVariable<ExecuteAction>::VisibleInNav | ObjectVariable<ExecuteAction>::Executable;
+ KSharedPtr<KoMacro::Variable> objvar = new ObjectVariable<ExecuteAction>(this, conditions);
+ setVariable(objvar);
+
+ setVariable(KSharedPtr<KoMacro::Variable>( new ObjectNameVariable<ExecuteAction>(this, objvar->variant().toString()) ));
+}
+
+ExecuteAction::~ExecuteAction()
+{
+}
+
+bool ExecuteAction::notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name)
+{
+ kdDebug()<<"ExecuteAction::notifyUpdated() name="<<name<<" macroitem.action="<<(macroitem->action() ? macroitem->action()->name() : "NOACTION")<<endl;
+ KSharedPtr<KoMacro::Variable> variable = macroitem->variable(name, false);
+ if(! variable) {
+ kdWarning()<<"ExecuteAction::notifyUpdated() No such variable="<<name<<" in macroitem."<<endl;
+ return false;
+ }
+
+ variable->clearChildren();
+ if(name == OBJECT) {
+ const QString objectvalue = macroitem->variant(OBJECT, true).toString(); // e.g. "macro" or "script"
+ const QString objectname = macroitem->variant(NAME, true).toString(); // e.g. "macro1" or "macro2" if objectvalue above is "macro"
+ macroitem->variable(NAME, true)->setChildren(
+ KoMacro::Variable::List() << KSharedPtr<KoMacro::Variable>(new ObjectNameVariable<ExecuteAction>(this, objectvalue, objectname)) );
+ }
+
+ return true;
+}
+
+void ExecuteAction::activate(KSharedPtr<KoMacro::Context> context)
+{
+ if(! mainWin()->project()) {
+ kdWarning() << "ExecuteAction::activate(KSharedPtr<KoMacro::Context>) Invalid project" << endl;
+ return;
+ }
+
+ const QString mimetype = QString("kexi/%1").arg( context->variable("object")->variant().toString() );
+ const QString name = context->variable("name")->variant().toString();
+
+ KexiPart::Part* part = Kexi::partManager().partForMimeType(mimetype);
+ if(! part) {
+ throw KoMacro::Exception(i18n("No such mimetype \"%1\"").arg(mimetype));
+ }
+
+ KexiPart::Item* item = mainWin()->project()->item(part->info(), name);
+ if(! item) {
+ throw KoMacro::Exception(i18n("Failed to open part \"%1\" for mimetype \"%2\"").arg(name).arg(mimetype));
+ }
+
+ part->execute(item);
+}
+
+//#include "executeaction.moc"
diff --git a/kexi/plugins/macros/kexiactions/executeaction.h b/kexi/plugins/macros/kexiactions/executeaction.h
new file mode 100644
index 00000000..17a8ca88
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/executeaction.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACRO_EXECUTEACTION_H
+#define KEXIMACRO_EXECUTEACTION_H
+
+#include "kexiaction.h"
+
+class KexiMainWindow;
+
+namespace KoMacro {
+ class Context;
+}
+
+namespace KexiMacro {
+
+ /**
+ * The ExecuteAction class implements a @a KoMacro::Action
+ * to provide functionality to execute an object like
+ * e.g. a script or a macro.
+ */
+ class ExecuteAction : public KexiAction
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ ExecuteAction();
+
+ /**
+ * Destructor.
+ */
+ virtual ~ExecuteAction();
+
+ /**
+ * This function is called, when the @a KoMacro::Variable
+ * with name @p name used within the @a KoMacro::MacroItem
+ * @p macroitem got changed.
+ *
+ * @param macroitem The @a KoMacro::MacroItem instance where
+ * the variable defined with @p name is located in.
+ * @param name The name the @a KoMacro::Variable has.
+ * @return true if the update was successfully else false
+ * is returned.
+ */
+ virtual bool notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name);
+
+ public slots:
+
+ /**
+ * Called if the @a Action should be executed within the
+ * defined @p context .
+ */
+ virtual void activate(KSharedPtr<KoMacro::Context> context);
+
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/kexiactions/kexiaction.cpp b/kexi/plugins/macros/kexiactions/kexiaction.cpp
new file mode 100644
index 00000000..521f8cfc
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/kexiaction.cpp
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexiaction.h"
+#include "../lib/exception.h"
+
+#include <ksharedptr.h>
+
+using namespace KexiMacro;
+
+KexiAction::KexiAction(const QString& name, const QString& text)
+ : KoMacro::Action(name)
+{
+ m_mainwin = dynamic_cast< KexiMainWindow* >( KoMacro::Manager::self()->guiClient() );
+
+ if(! m_mainwin) {
+ throw KoMacro::Exception("Invalid KexiMainWindow instance.");
+ }
+
+ // Set the caption this action has.
+ setText(text);
+}
+
+KexiAction::~KexiAction()
+{
+}
+
+KexiMainWindow* KexiAction::mainWin() const
+{
+ return m_mainwin;
+}
diff --git a/kexi/plugins/macros/kexiactions/kexiaction.h b/kexi/plugins/macros/kexiactions/kexiaction.h
new file mode 100644
index 00000000..a61e2bc1
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/kexiaction.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACRO_KEXIACTION_H
+#define KEXIMACRO_KEXIACTION_H
+
+#include "../lib/action.h"
+#include "../lib/variable.h"
+#include "../lib/macroitem.h"
+#include "../lib/context.h"
+
+#include "objectvariable.h"
+#include "objectnamevariable.h"
+
+#include <core/keximainwindow.h>
+
+namespace KexiMacro {
+
+ /**
+ * Template class to offer common functionality needed by all
+ * @a KoMacro::Action implementations Kexi provides.
+ *
+ * All the actions Kexi provides are inherited from this
+ * template class.
+ */
+ class KexiAction : public KoMacro::Action
+ {
+ public:
+
+ /**
+ * Constructor.
+ *
+ * @param name The unique name the @a KoMacro::Action has. This
+ * name will be used to identify the action.
+ * @param text The i18n-caption text used for display purposes.
+ */
+ KexiAction(const QString& name, const QString& text);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiAction();
+
+ /**
+ * @return the @a KexiMainWindow instance we are
+ * running in.
+ */
+ KexiMainWindow* mainWin() const;
+
+ private:
+
+ /// The @a KexiMainWindow instance.
+ KexiMainWindow* m_mainwin;
+
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/kexiactions/kexivariable.h b/kexi/plugins/macros/kexiactions/kexivariable.h
new file mode 100644
index 00000000..27dcc0ef
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/kexivariable.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACRO_KEXIVARIABLE_H
+#define KEXIMACRO_KEXIVARIABLE_H
+
+#include "../lib/manager.h"
+#include "../lib/exception.h"
+#include "../lib/action.h"
+#include "../lib/variable.h"
+
+#include <ksharedptr.h>
+
+class KexiMainWindow;
+
+namespace KoMacro {
+ class Context;
+}
+
+namespace KexiMacro {
+
+ /**
+ * Template class to offer common functionality needed by all
+ * @a KoMacro::Variable implementations Kexi provides.
+ */
+ template<class ACTIONIMPL>
+ class KexiVariable : public KoMacro::Variable
+ {
+ public:
+
+ /**
+ * Constructor.
+ */
+ KexiVariable(ACTIONIMPL* actionimpl, const QString& name, const QString& caption)
+ : KoMacro::Variable()
+ , m_actionimpl(actionimpl)
+ {
+ setName(name);
+ setText(caption);
+ }
+
+ protected:
+
+ /**
+ * @return the @a KexiAction implementation this @a KexiVariable
+ * is a child of.
+ */
+ ACTIONIMPL* actionImpl() const
+ {
+ return m_actionimpl;
+ }
+
+ private:
+ /// The parent @a KexiAction implementation.
+ ACTIONIMPL* m_actionimpl;
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/kexiactions/messageaction.cpp b/kexi/plugins/macros/kexiactions/messageaction.cpp
new file mode 100644
index 00000000..1a4605cb
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/messageaction.cpp
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "messageaction.h"
+
+#include <core/keximainwindow.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+
+using namespace KexiMacro;
+
+MessageAction::MessageAction()
+ : KexiAction("message", i18n("Message"))
+{
+ setVariable("caption", i18n("Caption"), QString(""));
+ setVariable("message", i18n("Message"), QString(""));
+}
+
+MessageAction::~MessageAction()
+{
+}
+
+void MessageAction::activate(KSharedPtr<KoMacro::Context> context)
+{
+ kdDebug() << "MessageAction::activate(KSharedPtr<Context>)" << endl;
+ const QString caption = context->variable("caption")->variant().toString();
+ const QString message = context->variable("message")->variant().toString();
+ KMessageBox::information(mainWin(), message, caption);
+}
+
+//#include "messageaction.moc"
diff --git a/kexi/plugins/macros/kexiactions/messageaction.h b/kexi/plugins/macros/kexiactions/messageaction.h
new file mode 100644
index 00000000..543674bd
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/messageaction.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACRO_MESSAGEACTION_H
+#define KEXIMACRO_MESSAGEACTION_H
+
+
+#include "kexiaction.h"
+
+class KexiMainWindow;
+
+namespace KoMacro {
+ class Context;
+}
+
+namespace KexiMacro {
+
+ /**
+ * The ExecuteObject class implements a @a KoMacro::Action
+ * to provide functionality to execute an object like
+ * e.g. a script or a macro.
+ */
+ class MessageAction : public KexiAction
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ MessageAction();
+
+ /**
+ * Destructor.
+ */
+ virtual ~MessageAction();
+
+ public slots:
+
+ /**
+ * Called if the @a Action should be executed within the
+ * defined @param context .
+ */
+ virtual void activate(KSharedPtr<KoMacro::Context> context);
+
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/kexiactions/navigateaction.cpp b/kexi/plugins/macros/kexiactions/navigateaction.cpp
new file mode 100644
index 00000000..d3fe551c
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/navigateaction.cpp
@@ -0,0 +1,158 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "navigateaction.h"
+
+#include <core/kexi.h>
+#include <core/kexiproject.h>
+#include <core/kexipartmanager.h>
+#include <core/kexipartinfo.h>
+#include <core/kexipart.h>
+#include <core/keximainwindow.h>
+#include <core/kexidialogbase.h>
+
+#include <widget/kexidataawareview.h>
+#include <widget/tableview/kexidataawareobjectiface.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+using namespace KexiMacro;
+
+namespace KexiMacro {
+
+ template<class ACTIONIMPL>
+ class NavigateVariable : public KexiVariable<ACTIONIMPL>
+ {
+ public:
+ NavigateVariable(ACTIONIMPL* actionimpl)
+ : KexiVariable<ACTIONIMPL>(actionimpl, "record", i18n("Record"))
+ {
+ QStringList list;
+ list << "first" << "previous" << "next" << "last" << "goto";
+ this->appendChild( KSharedPtr<KoMacro::Variable>( new KoMacro::Variable(list, "@list") ) );
+
+ /*TODO should this actions belong to navigate? maybe it would be more wise to have
+ such kind of functionality in an own e.g. "Modify" action to outline, that
+ we are manipulating the database that way... */
+ //"add" << "save" << "delete" << "query" << "execute" << "cancel" << "reload"
+
+ this->setVariant( list[0] );
+ }
+ };
+
+}
+
+NavigateAction::NavigateAction()
+ : KexiAction("navigate", i18n("Navigate"))
+{
+ KoMacro::Variable* navvar = new NavigateVariable<NavigateAction>(this);
+ setVariable(KSharedPtr<KoMacro::Variable>( navvar ));
+
+ KoMacro::Variable* rowvar = new KexiVariable<NavigateAction>(this, "rownr", i18n("Row"));
+ rowvar->setVariant(0);
+ setVariable(KSharedPtr<KoMacro::Variable>(rowvar));
+
+ KoMacro::Variable* colvar = new KexiVariable<NavigateAction>(this, "colnr", i18n("Column"));
+ colvar->setVariant(0);
+ setVariable(KSharedPtr<KoMacro::Variable>(colvar));
+}
+
+NavigateAction::~NavigateAction()
+{
+}
+
+bool NavigateAction::notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name)
+{
+ kdDebug()<<"NavigateAction::notifyUpdated() name="<<name<<" macroitem.action="<<(macroitem->action() ? macroitem->action()->name() : "NOACTION")<<endl;
+ KSharedPtr<KoMacro::Variable> variable = macroitem->variable(name, false);
+ if(! variable) {
+ kdWarning()<<"NavigateAction::notifyUpdated() No such variable="<<name<<" in macroitem."<<endl;
+ return false;
+ }
+
+ variable->clearChildren();
+ if(name == "goto") {
+ const int rownr = macroitem->variant("rownr", true).toInt(); // e.g. "macro" or "script"
+ const int colnr = macroitem->variant("colnr", true).toInt(); // e.g. "macro1" or "macro2" if objectvalue above is "macro"
+
+ macroitem->variable("rownr", true)->setChildren(
+ KoMacro::Variable::List() << KSharedPtr<KoMacro::Variable>(new KoMacro::Variable(rownr)) );
+ macroitem->variable("colnr", true)->setChildren(
+ KoMacro::Variable::List() << KSharedPtr<KoMacro::Variable>(new KoMacro::Variable(colnr)) );
+ }
+
+ return true;
+}
+
+void NavigateAction::activate(KSharedPtr<KoMacro::Context> context)
+{
+ KexiDialogBase* dialog = dynamic_cast<KexiDialogBase*>( mainWin()->activeWindow() );
+ if(! dialog) {
+ throw KoMacro::Exception(i18n("No window active."));
+ }
+
+ KexiViewBase* view = dialog->selectedView();
+ if(! view) {
+ throw KoMacro::Exception(i18n("No view selected for \"%1\".").arg(dialog->caption()));
+ }
+
+ KexiDataAwareView* dbview = dynamic_cast<KexiDataAwareView*>( view );
+ KexiDataAwareObjectInterface* dbobj = dbview ? dbview->dataAwareObject() : 0;
+ if(! dbview) {
+ throw KoMacro::Exception(i18n("The view for \"%1\" could not handle data.").arg(dialog->caption()));
+ }
+
+ const QString record = context->variable("record")->variant().toString();
+ if(record == "previous") {
+ dbobj->selectPrevRow();
+ }
+ else if(record == "next") {
+ dbobj->selectNextRow();
+ }
+ else if(record == "first") {
+ dbobj->selectFirstRow();
+ }
+ else if(record == "last") {
+ dbobj->selectLastRow();
+ }
+ else if(record == "goto") {
+ int rownr = context->variable("rownr")->variant().toInt() - 1;
+ int colnr = context->variable("colnr")->variant().toInt() - 1;
+ dbobj->setCursorPosition(rownr >= 0 ? rownr : dbobj->currentRow(), colnr >= 0 ? colnr : dbobj->currentColumn());
+ }
+ else {
+ /*
+ virtual void selectNextPage(); //!< page down action
+ virtual void selectPrevPage(); //!< page up action
+ void deleteAllRows();
+ void deleteCurrentRow();
+ void deleteAndStartEditCurrentCell();
+ void startEditOrToggleValue();
+ bool acceptRowEdit();
+ void cancelRowEdit();
+ void sortAscending();
+ void sortDescending();
+ */
+ throw KoMacro::Exception(i18n("Unknown record \"%1\" in view for \"%2\".").arg(record).arg(dialog->caption()));
+ }
+}
+
+//#include "navigateaction.moc"
diff --git a/kexi/plugins/macros/kexiactions/navigateaction.h b/kexi/plugins/macros/kexiactions/navigateaction.h
new file mode 100644
index 00000000..f7f74f86
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/navigateaction.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACRO_NAVIGATEACTION_H
+#define KEXIMACRO_NAVIGATEACTION_H
+
+#include "kexiaction.h"
+
+class KexiMainWindow;
+
+namespace KoMacro {
+ class Context;
+}
+
+namespace KexiMacro {
+
+ /**
+ * The NavigateAction class implements a @a KoMacro::Action
+ * to provide functionality to execute an object like
+ * e.g. a script or a macro.
+ */
+ class NavigateAction : public KexiAction
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ NavigateAction();
+
+ /**
+ * Destructor.
+ */
+ virtual ~NavigateAction();
+
+ /**
+ * This function is called, when the @a KoMacro::Variable
+ * with name @p name used within the @a KoMacro::MacroItem
+ * @p macroitem got changed.
+ *
+ * @param macroitem The @a KoMacro::MacroItem instance where
+ * the variable defined with @p name is located in.
+ * @param name The name the @a KoMacro::Variable has.
+ * @return true if the update was successfully else false
+ * is returned.
+ */
+ virtual bool notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name);
+
+ public slots:
+
+ /**
+ * Called if the @a Action should be executed within the
+ * defined @p context .
+ */
+ virtual void activate(KSharedPtr<KoMacro::Context> context);
+
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/kexiactions/objectnamevariable.h b/kexi/plugins/macros/kexiactions/objectnamevariable.h
new file mode 100644
index 00000000..eeaabe04
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/objectnamevariable.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACRO_OBJECTNAMEVARIABLE_H
+#define KEXIMACRO_OBJECTNAMEVARIABLE_H
+
+#include "../lib/variable.h"
+
+#include "kexivariable.h"
+
+#include <core/kexi.h>
+#include <core/kexiproject.h>
+#include <core/kexipartmanager.h>
+#include <core/kexipartinfo.h>
+
+#include <klocale.h>
+
+namespace KexiMacro {
+
+ /**
+ * The ViewVariable class provide a list of KexiPart::PartItem's
+ * supported by a KexiPart::Part as @a KoMacro::Variable .
+ */
+ template<class ACTIONIMPL>
+ class ObjectNameVariable : public KexiVariable<ACTIONIMPL>
+ {
+ public:
+ ObjectNameVariable(ACTIONIMPL* actionimpl, const QString& objectname = QString::null, const QString& name = QString::null)
+ : KexiVariable<ACTIONIMPL>(actionimpl, "name", i18n("Name"))
+ {
+ if(! actionimpl->mainWin()->project())
+ return;
+
+ QStringList namelist;
+ KexiPart::Info* info = Kexi::partManager().infoForMimeType( QString("kexi/%1").arg(objectname) );
+ if(info) {
+ KexiPart::ItemDict* items = actionimpl->mainWin()->project()->items(info);
+ if(items)
+ for(KexiPart::ItemDictIterator item_it = *items; item_it.current(); ++item_it)
+ namelist << item_it.current()->name();
+ }
+
+ if(namelist.count() <= 0)
+ namelist << "";
+
+ for(QStringList::Iterator it = namelist.begin(); it != namelist.end(); ++it)
+ this->appendChild( KSharedPtr<KoMacro::Variable>(new KoMacro::Variable(*it)) );
+
+ this->setVariant( (name.isNull() || ! namelist.contains(name)) ? namelist[0] : name );
+
+ kdDebug()<<"##################### KexiActions::ObjectNameVariable() objectname="<<objectname<<" name="<<name<<" value="<<this->variant()<<" children="<<namelist<<endl;
+ }
+
+ virtual ~ObjectNameVariable() {}
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/kexiactions/objectvariable.h b/kexi/plugins/macros/kexiactions/objectvariable.h
new file mode 100644
index 00000000..b61f24e3
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/objectvariable.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACRO_OBJECTVARIABLE_H
+#define KEXIMACRO_OBJECTVARIABLE_H
+
+#include "../lib/action.h"
+#include "../lib/variable.h"
+
+#include "kexivariable.h"
+
+#include <core/kexi.h>
+#include <core/kexiproject.h>
+#include <core/kexipartmanager.h>
+#include <core/kexipartinfo.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+namespace KexiMacro {
+
+ /**
+ * The ObjectVariable class implements @a KoMacro::Variable to
+ * provide a variable list of Kexi-objects. Those Kexi-objects
+ * are KexiPart's like e.g. table, query, form or script.
+ */
+ template<class ACTIONIMPL>
+ class ObjectVariable : public KexiVariable<ACTIONIMPL>
+ {
+ public:
+
+ enum Conditions {
+ VisibleInNav = 1,
+ Executable = 2,
+ DataExport = 4
+ };
+
+ ObjectVariable(ACTIONIMPL* actionimpl, int conditions = VisibleInNav, const QString& objectname = QString::null)
+ : KexiVariable<ACTIONIMPL>(actionimpl, "object", i18n("Object"))
+ {
+ KexiPart::PartInfoList* parts = Kexi::partManager().partInfoList();
+ for(KexiPart::PartInfoListIterator it(*parts); it.current(); ++it) {
+ KexiPart::Info* info = it.current();
+
+ if(conditions & VisibleInNav && ! info->isVisibleInNavigator())
+ continue;
+ if(conditions & Executable && ! info->isExecuteSupported())
+ continue;
+ if(conditions & DataExport && ! info->isDataExportSupported())
+ continue;
+
+ const QString name = info->objectName(); //info->groupName();
+ this->appendChild( KSharedPtr<KoMacro::Variable>(new KoMacro::Variable(name)) );
+ }
+
+ if(! objectname.isNull())
+ this->setVariant( objectname );
+ else if(this->children().count() > 0)
+ this->setVariant( this->children()[0]->variant() );
+ else
+ this->setVariant( QString::null );
+
+ kdDebug()<<"##################### KexiActions::ObjectVariable() variant="<<this->variant()<<endl;
+ }
+
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/kexiactions/openaction.cpp b/kexi/plugins/macros/kexiactions/openaction.cpp
new file mode 100644
index 00000000..b67041bb
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/openaction.cpp
@@ -0,0 +1,154 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "openaction.h"
+
+#include <core/kexi.h>
+#include <core/kexiproject.h>
+#include <core/kexipartmanager.h>
+#include <core/kexipartinfo.h>
+#include <core/kexipart.h>
+#include <core/keximainwindow.h>
+
+#include <klocale.h>
+
+using namespace KexiMacro;
+
+namespace KexiMacro {
+
+ static const QString DATAVIEW = "data";
+ static const QString DESIGNVIEW = "design";
+ static const QString TEXTVIEW = "text";
+
+ static const QString OBJECT = "object";
+ static const QString NAME = "name";
+ static const QString VIEW = "view";
+
+ /**
+ * The ViewVariable class provide a list of viewmodes supported
+ * by a KexiPart::Part as @a KoMacro::Variable .
+ */
+ template<class ACTIONIMPL>
+ class ViewVariable : public KexiVariable<ACTIONIMPL>
+ {
+ public:
+ ViewVariable(ACTIONIMPL* actionimpl, const QString& objectname = QString::null, const QString& viewname = QString::null)
+ : KexiVariable<ACTIONIMPL>(actionimpl, VIEW, i18n("View"))
+ {
+ QStringList namelist;
+ KexiPart::Part* part = Kexi::partManager().partForMimeType( QString("kexi/%1").arg(objectname) );
+ if(part) {
+ int viewmodes = part->supportedViewModes();
+ if(viewmodes & Kexi::DataViewMode)
+ namelist << DATAVIEW;
+ if(viewmodes & Kexi::DesignViewMode)
+ namelist << DESIGNVIEW;
+ if(viewmodes & Kexi::TextViewMode)
+ namelist << TEXTVIEW;
+ for(QStringList::Iterator it = namelist.begin(); it != namelist.end(); ++it)
+ this->children().append( KSharedPtr<KoMacro::Variable>(new KoMacro::Variable(*it)) );
+ }
+ const QString n =
+ namelist.contains(viewname)
+ ? viewname
+ : namelist.count() > 0 ? namelist[0] : "";
+
+ this->setVariant(n);
+ }
+ };
+
+}
+
+OpenAction::OpenAction()
+ : KexiAction("open", i18n("Open"))
+{
+ const int conditions = ObjectVariable<OpenAction>::VisibleInNav;
+
+ KSharedPtr<KoMacro::Variable> objvar = new ObjectVariable<OpenAction>(this, conditions);
+ setVariable(objvar);
+
+ setVariable(KSharedPtr<KoMacro::Variable>( new ObjectNameVariable<OpenAction>(this, objvar->variant().toString()) ));
+ setVariable(KSharedPtr<KoMacro::Variable>( new ViewVariable<OpenAction>(this, objvar->variant().toString()) ));
+}
+
+OpenAction::~OpenAction()
+{
+}
+
+bool OpenAction::notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name)
+{
+ kdDebug()<<"OpenAction::notifyUpdated() name="<<name<<" macroitem.action="<<(macroitem->action() ? macroitem->action()->name() : "NOACTION")<<endl;
+ KSharedPtr<KoMacro::Variable> variable = macroitem->variable(name, false);
+ if(! variable) {
+ kdWarning()<<"OpenAction::notifyUpdated() No such variable="<<name<<" in macroitem."<<endl;
+ return false;
+ }
+
+ variable->clearChildren();
+ if(name == OBJECT) {
+ const QString objectvalue = macroitem->variant(OBJECT, true).toString(); // e.g. "table" or "query"
+ const QString objectname = macroitem->variant(NAME, true).toString(); // e.g. "table1" or "table2" if objectvalue above is "table"
+ const QString viewname = macroitem->variant(VIEW, true).toString(); // "data", "design" or "text"
+
+ macroitem->variable(NAME, true)->setChildren(
+ KoMacro::Variable::List() << KSharedPtr<KoMacro::Variable>(new ObjectNameVariable<OpenAction>(this, objectvalue, objectname)) );
+ macroitem->variable(VIEW, true)->setChildren(
+ KoMacro::Variable::List() << KSharedPtr<KoMacro::Variable>(new ViewVariable<OpenAction>(this, objectvalue, viewname)) );
+ }
+
+ return true;
+}
+
+void OpenAction::activate(KSharedPtr<KoMacro::Context> context)
+{
+ if(! mainWin()->project()) {
+ throw KoMacro::Exception(i18n("No project loaded."));
+ }
+
+ const QString objectname = context->variable(OBJECT)->variant().toString();
+ const QString name = context->variable(NAME)->variant().toString();
+ KexiPart::Item* item = mainWin()->project()->itemForMimeType( QString("kexi/%1").arg(objectname).latin1(), name );
+ if(! item) {
+ throw KoMacro::Exception(i18n("No such object \"%1.%2\".").arg(objectname).arg(name));
+ }
+
+ // Determinate the viewmode.
+ const QString view = context->variable(VIEW)->variant().toString();
+ int viewmode;
+ if(view == DATAVIEW)
+ viewmode = Kexi::DataViewMode;
+ else if(view == DESIGNVIEW)
+ viewmode = Kexi::DesignViewMode;
+ else if(view == TEXTVIEW)
+ viewmode = Kexi::TextViewMode;
+ else {
+ throw KoMacro::Exception(i18n("No such viewmode \"%1\" in object \"%2.%3\".").arg(view).arg(objectname).arg(name));
+ }
+
+ // Try to open the object now.
+ bool openingCancelled;
+ if(! mainWin()->openObject(item, viewmode, openingCancelled)) {
+ if(! openingCancelled) {
+ throw KoMacro::Exception(i18n("Failed to open object \"%1.%2\".").arg(objectname).arg(name));
+ }
+ }
+}
+
+//#include "openaction.moc"
diff --git a/kexi/plugins/macros/kexiactions/openaction.h b/kexi/plugins/macros/kexiactions/openaction.h
new file mode 100644
index 00000000..b49f1238
--- /dev/null
+++ b/kexi/plugins/macros/kexiactions/openaction.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACRO_OPENACTION_H
+#define KEXIMACRO_OPENACTION_H
+
+#include "kexiaction.h"
+
+class KexiMainWindow;
+
+namespace KoMacro {
+ class Context;
+}
+
+namespace KexiMacro {
+
+ /**
+ * The OpenAction class implements a @a KoMacro::Action
+ * to provide functionality to open any kind of Kexi
+ * object (e.g. table, query, form, script, ...).
+ */
+ class OpenAction : public KexiAction
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Constructor.
+ */
+ OpenAction();
+
+ /**
+ * Destructor.
+ */
+ virtual ~OpenAction();
+
+ /**
+ * This function is called, when the @a KoMacro::Variable
+ * with name @p name used within the @a KoMacro::MacroItem
+ * @p macroitem got changed.
+ *
+ * @param macroitem The @a KoMacro::MacroItem instance where
+ * the variable defined with @p name is located in.
+ * @param name The name the @a KoMacro::Variable has.
+ * @return true if the update was successfully else false
+ * is returned.
+ */
+ virtual bool notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name);
+
+ public slots:
+
+ /**
+ * Called if the @a Action should be executed within the
+ * defined @p context .
+ */
+ virtual void activate(KSharedPtr<KoMacro::Context> context);
+
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/kexipart/Makefile.am b/kexi/plugins/macros/kexipart/Makefile.am
new file mode 100644
index 00000000..51cff0ea
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/Makefile.am
@@ -0,0 +1,32 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_macro.la
+
+kexihandler_macro_la_SOURCES = \
+ keximacropart.cpp keximacroview.cpp keximacroproperty.cpp keximacrodesignview.cpp keximacrotextview.cpp keximacroerrorbase.ui keximacroerror.cpp
+
+kexihandler_macro_la_LDFLAGS = \
+ $(KDE_PLUGIN) -module -no-undefined -Wnounresolved $(all_libraries) $(VER_INFO)
+
+kexihandler_macro_la_LIBADD = \
+ ../kexiactions/libkeximacroactions.la \
+ $(top_builddir)/kexi/core/libkexicore.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/lib/koproperty/libkoproperty.la
+
+INCLUDES = \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/lib/kofficecore/ \
+ -I$(top_srcdir)/kexi/core \
+ -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget \
+ $(all_includes)
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=keximacrohandler.desktop
+
+SUBDIRS = .
+METASOURCES = AUTO
+
+noinst_HEADERS = \
+ keximacropart.h keximacroview.h keximacrodesignview.h keximacrotextview.h keximacroerror.h
diff --git a/kexi/plugins/macros/kexipart/keximacrodesignview.cpp b/kexi/plugins/macros/kexipart/keximacrodesignview.cpp
new file mode 100644
index 00000000..030be0cb
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacrodesignview.cpp
@@ -0,0 +1,497 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "keximacrodesignview.h"
+#include "keximacroproperty.h"
+
+#include <qtimer.h>
+#include <qdom.h>
+#include <kdebug.h>
+
+#include <kexidialogbase.h>
+#include <kexidb/connection.h>
+#include <kexidb/error.h>
+
+#include <core/kexi.h>
+#include <core/kexiproject.h>
+#include <core/kexipartmanager.h>
+#include <core/kexipartinfo.h>
+
+#include <widget/kexidatatable.h>
+#include <widget/tableview/kexitableview.h>
+#include <widget/tableview/kexitableviewdata.h>
+#include <widget/tableview/kexitableitem.h>
+#include <widget/tableview/kexidataawarepropertyset.h>
+
+#include <koproperty/set.h>
+#include <koproperty/property.h>
+
+#include "../lib/macro.h"
+#include "../lib/macroitem.h"
+#include "../lib/xmlhandler.h"
+
+/// constants used to name columns instead of hardcoding indices
+#define COLUMN_ID_ACTION 0
+#define COLUMN_ID_COMMENT 1
+
+/**
+* \internal d-pointer class to be more flexible on future extension of the
+* functionality without to much risk to break the binary compatibility.
+*/
+class KexiMacroDesignView::Private
+{
+ public:
+
+ /**
+ * The view used to display the actions
+ * a \a Macro has.
+ */
+ KexiDataTable* datatable;
+
+ /**
+ * For convenience. The table view ( datatable->tableView() ).
+ */
+ KexiTableView* tableview;
+
+ /**
+ * The \a KexiTableViewData data-model for the
+ * \a KexiTableView above.
+ */
+ KexiTableViewData* tabledata;
+
+ /**
+ * The \a KexiDataAwarePropertySet is used to display
+ * properties an action provides in the propertyview.
+ */
+ KexiDataAwarePropertySet* propertyset;
+
+ /// Boolean flag to avoid infinite recursion.
+ bool reloadsProperties;
+ /// Boolean flag to avoid infinite recursion.
+ bool updatesProperties;
+
+ /**
+ * Constructor.
+ *
+ * \param m The passed \a KoMacro::Manager instance our
+ * \a manager points to.
+ */
+ Private()
+ : propertyset(0)
+ , reloadsProperties(false)
+ , updatesProperties(false)
+ {
+ }
+
+ /**
+ * Destructor.
+ */
+ ~Private()
+ {
+ delete propertyset;
+ }
+
+};
+
+KexiMacroDesignView::KexiMacroDesignView(KexiMainWindow *mainwin, QWidget *parent, ::KoMacro::Macro* const macro)
+ : KexiMacroView(mainwin, parent, macro, "KexiMacroDesignView")
+ , d( new Private() )
+{
+ // The table's data-model.
+ d->tabledata = new KexiTableViewData();
+ d->tabledata->setSorting(-1); // disable sorting
+
+ // Add the "Action" column.
+ KexiTableViewColumn* actioncol = new KexiTableViewColumn(
+ "action", // name/identifier
+ KexiDB::Field::Enum, // fieldtype
+ KexiDB::Field::NoConstraints, // constraints
+ KexiDB::Field::NoOptions, // options
+ 0, // length
+ 0, // precision
+ QVariant(), // default value
+ i18n("Action"), // caption
+ QString::null, // description
+ 0 // width
+ );
+ d->tabledata->addColumn(actioncol);
+
+ QValueVector<QString> items;
+ items.append(""); // empty means no action
+
+ // Append the list of actions provided by Kexi.
+ QStringList actionnames = KoMacro::Manager::self()->actionNames();
+ QStringList::ConstIterator it, end( actionnames.constEnd() );
+ for( it = actionnames.constBegin(); it != end; ++it) {
+ KSharedPtr<KoMacro::Action> action = KoMacro::Manager::self()->action(*it);
+ items.append( action->text() );
+ }
+
+ actioncol->field()->setEnumHints(items);
+
+ // Add the "Comment" column.
+ d->tabledata->addColumn( new KexiTableViewColumn(
+ "comment", // name/identifier
+ KexiDB::Field::Text, // fieldtype
+ KexiDB::Field::NoConstraints, // constraints
+ KexiDB::Field::NoOptions, // options
+ 0, // length
+ 0, // precision
+ QVariant(), // default value
+ i18n("Comment"), // caption
+ QString::null, // description
+ 0 // width
+ ) );
+
+ // Create the tableview.
+ QHBoxLayout* layout = new QHBoxLayout(this);
+ d->datatable = new KexiDataTable(mainWin(), this, "Macro KexiDataTable", false /*not db aware*/);
+ layout->addWidget(d->datatable);
+ d->tableview = d->datatable->tableView();
+ d->tableview->setSpreadSheetMode();
+ d->tableview->setColumnStretchEnabled( true, COLUMN_ID_COMMENT ); //last column occupies the rest of the area
+
+ // We need to register our KexiMacroPropertyFactory to use our own
+ // KoProperty::Property implementation.
+ KexiMacroPropertyFactory::initFactory();
+
+ // Create the propertyset.
+ d->propertyset = new KexiDataAwarePropertySet(this, d->tableview);
+
+ // Connect signals the KexiDataTable provides to local slots.
+ connect(d->tabledata, SIGNAL(aboutToChangeCell(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)),
+ this, SLOT(beforeCellChanged(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)));
+ connect(d->tabledata, SIGNAL(rowUpdated(KexiTableItem*)),
+ this, SLOT(rowUpdated(KexiTableItem*)));
+ connect(d->tabledata, SIGNAL(rowInserted(KexiTableItem*,uint,bool)),
+ this, SLOT(rowInserted(KexiTableItem*,uint,bool)));
+ connect(d->tabledata, SIGNAL(rowDeleted()),
+ this, SLOT(rowDeleted()));
+
+ // Everything is ready. So, update the data now.
+ updateData();
+ setDirty(false);
+}
+
+KexiMacroDesignView::~KexiMacroDesignView()
+{
+ delete d;
+}
+
+void KexiMacroDesignView::updateData()
+{
+ kdDebug() << "KexiMacroDesignView::updateData()" << endl;
+
+ // Remove previous content of tabledata.
+ d->tabledata->deleteAllRows();
+ // Remove old property sets.
+ d->propertyset->clear();
+
+ // Add some empty rows
+ for (int i=0; i<50; i++) {
+ d->tabledata->append( d->tabledata->createItem() );
+ }
+
+ // Set the MacroItem's
+ QStringList actionnames = KoMacro::Manager::self()->actionNames();
+ KoMacro::MacroItem::List macroitems = macro()->items();
+ KoMacro::MacroItem::List::ConstIterator it(macroitems.constBegin()), end(macroitems.constEnd());
+ for(uint idx = 0; it != end; ++it, idx++) {
+ KexiTableItem* tableitem = d->tabledata->at(idx);
+ if(! tableitem) {
+ // If there exists no such item, add it.
+ tableitem = d->tabledata->createItem();
+ d->tabledata->append(tableitem);
+ }
+ // Set the action-column.
+ KSharedPtr<KoMacro::Action> action = (*it)->action();
+ if(action.data()) {
+ int i = actionnames.findIndex( action->name() );
+ if(i >= 0) {
+ tableitem->at(COLUMN_ID_ACTION) = i + 1;
+ //setAction(tableitem, action->name());
+ }
+ }
+ // Set the comment-column.
+ tableitem->at(COLUMN_ID_COMMENT) = (*it)->comment();
+ }
+
+ // set data for our spreadsheet: this will clear our sets
+ d->tableview->setData(d->tabledata);
+
+ // Add the property sets.
+ it = macroitems.constBegin();
+ for(uint idx = 0; it != end; ++it, idx++) {
+ updateProperties(idx, 0, *it);
+ }
+
+ // work around a bug in the KexiTableView where we lose the stretch-setting...
+ d->tableview->setColumnStretchEnabled( true, COLUMN_ID_COMMENT ); //last column occupies the rest of the area
+
+ propertySetReloaded(true);
+}
+
+bool KexiMacroDesignView::loadData()
+{
+ if(! KexiMacroView::loadData()) {
+ return false;
+ }
+ updateData(); // update the tableview's data.
+ return true;
+}
+
+KoProperty::Set* KexiMacroDesignView::propertySet()
+{
+ return d->propertyset->currentPropertySet();
+}
+
+void KexiMacroDesignView::beforeCellChanged(KexiTableItem* item, int colnum, QVariant& newvalue, KexiDB::ResultInfo* result)
+{
+ Q_UNUSED(result);
+ kdDebug() << "KexiMacroDesignView::beforeCellChanged() colnum=" << colnum << " newvalue=" << newvalue.toString() << endl;
+
+ int rowindex = d->tabledata->findRef(item);
+ if(rowindex < 0) {
+ kdWarning() << "KexiMacroDesignView::beforeCellChanged() No such item" << endl;
+ return;
+ }
+
+ // If the rowindex doesn't exists yet, we need to append new
+ // items till we are able to access the item we like to use.
+ for(int i = macro()->items().count(); i <= rowindex; i++) {
+ macro()->addItem( KSharedPtr<KoMacro::MacroItem>( new KoMacro::MacroItem() ) );
+ }
+
+ // Get the matching MacroItem.
+ KSharedPtr<KoMacro::MacroItem> macroitem = macro()->items()[rowindex];
+ if(! macroitem.data()) {
+ kdWarning() << "KexiMacroDesignView::beforeCellChanged() Invalid item for rowindex=" << rowindex << endl;
+ return;
+ }
+
+ // Handle the column that should be changed
+ switch(colnum) {
+ case COLUMN_ID_ACTION: { // The "Action" column
+ QString actionname;
+ bool ok;
+ int selectedindex = newvalue.toInt(&ok);
+ if(ok && selectedindex > 0) {
+ QStringList actionnames = KoMacro::Manager::self()->actionNames();
+ actionname = actionnames[ selectedindex - 1 ]; // first item is empty
+ }
+ KSharedPtr<KoMacro::Action> action = KoMacro::Manager::self()->action(actionname);
+ macroitem->setAction(action);
+ updateProperties(d->propertyset->currentRow(), d->propertyset->currentPropertySet(), macroitem);
+ propertySetReloaded(true);
+ } break;
+ case COLUMN_ID_COMMENT: { // The "Comment" column
+ macroitem->setComment( newvalue.toString() );
+ } break;
+ default:
+ kdWarning() << "KexiMacroDesignView::beforeCellChanged() No such column number " << colnum << endl;
+ return;
+ }
+
+ setDirty();
+}
+
+void KexiMacroDesignView::rowUpdated(KexiTableItem* item)
+{
+ int rowindex = d->tabledata->findRef(item);
+ kdDebug() << "KexiMacroDesignView::rowUpdated() rowindex=" << rowindex << endl;
+ //propertySetSwitched();
+ //propertySetReloaded(true);
+ //setDirty();
+}
+
+void KexiMacroDesignView::rowInserted(KexiTableItem*, uint row, bool)
+{
+ kdDebug() << "KexiMacroDesignView::rowInserted() rowindex=" << row << endl;
+ KoMacro::MacroItem::List& macroitems = macro()->items();
+
+ if(row < macroitems.count()) {
+ // If a new item was inserted, we need to insert a new item to our
+ // list of MacroItems too. If the new item was appended, we don't
+ // need to do anything yet cause the new item will be handled on
+ // beforeCellChanged() anyway.
+ kdDebug() << "KexiMacroDesignView::rowInserted() Inserting new MacroItem" << endl;
+ KSharedPtr<KoMacro::MacroItem> macroitem = KSharedPtr<KoMacro::MacroItem>( new KoMacro::MacroItem() );
+ KoMacro::MacroItem::List::Iterator it = macroitems.at(row);
+ macroitems.insert(it, macroitem);
+ }
+}
+
+void KexiMacroDesignView::rowDeleted()
+{
+ int rowindex = d->propertyset->currentRow();
+ if(rowindex < 0) {
+ kdWarning() << "KexiMacroDesignView::rowDeleted() No such item" << endl;
+ return;
+ }
+ kdDebug() << "KexiMacroDesignView::rowDeleted() rowindex=" << rowindex << endl;
+ KoMacro::MacroItem::List& macroitems = macro()->items();
+ macroitems.remove( macroitems.at(rowindex) );
+}
+
+bool KexiMacroDesignView::updateSet(KoProperty::Set* set, KSharedPtr<KoMacro::MacroItem> macroitem, const QString& variablename)
+{
+ kdDebug() << "KexiMacroDesignView::updateSet() variablename=" << variablename << endl;
+ KoProperty::Property* property = KexiMacroProperty::createProperty(macroitem, variablename);
+ if(! property)
+ return false;
+ set->addProperty(property);
+ return true;
+}
+
+void KexiMacroDesignView::updateProperties(int row, KoProperty::Set* set, KSharedPtr<KoMacro::MacroItem> macroitem)
+{
+ kdDebug() << "KexiMacroDesignView::updateProperties() row=" << row << endl;
+
+ if(row < 0 || d->updatesProperties) {
+ return; // ignore invalid rows and avoid infinite recursion.
+ }
+
+ KSharedPtr<KoMacro::Action> action = macroitem->action();
+ if(! action.data()) {
+ // don't display a propertyset if there is no action defined.
+ d->propertyset->remove(row);
+ return; // job done.
+ }
+
+ d->updatesProperties = true;
+
+ if(set) {
+ // we need to clear old data before adding the new content.
+ set->clear();
+ }
+ else {
+ // if there exists no such propertyset yet, create one.
+ set = new KoProperty::Set(d->propertyset, action->name());
+ d->propertyset->insert(row, set, true);
+ connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
+ this, SLOT(propertyChanged(KoProperty::Set&, KoProperty::Property&)));
+ }
+
+ // The caption.
+ KoProperty::Property* prop = new KoProperty::Property("this:classString", action->text());
+ prop->setVisible(false);
+ set->addProperty(prop);
+
+ // Display the list of variables.
+ QStringList varnames = action->variableNames();
+ for(QStringList::Iterator it = varnames.begin(); it != varnames.end(); ++it) {
+ if(updateSet(set, macroitem, *it)) {
+ KSharedPtr<KoMacro::Variable> variable = macroitem->variable(*it, true);
+ kdDebug()<<"KexiMacroDesignView::updateProperties() name=" << *it << " variable=" << variable->variant().toString() << endl;
+#if 0
+ macroitem->setVariable(*it, variable);
+#endif
+ }
+ }
+
+ d->updatesProperties = false;
+}
+
+void KexiMacroDesignView::propertyChanged(KoProperty::Set& set, KoProperty::Property& property)
+{
+ Q_UNUSED(set);
+ kdDebug() << "!!!!! KexiMacroDesignView::propertyChanged() propertyname=" << property.name() << endl;
+ setDirty();
+
+ /*
+ if(d->reloadsProperties) // be sure to don't update properties if we are still on reloading.
+ return;
+ d->reloadsProperties = true;
+
+ const int row = d->propertyset->currentRow();
+ const QCString name = property.name();
+ kdDebug() << "KexiMacroDesignView::propertyChanged() name=" << name << " row=" << row << endl;
+
+ //TODO reload is only needed if something changed!
+ bool dirty = true; bool reload = true;//dirtyvarnames.count()>0;
+
+ if(dirty || reload) { // Only reload properties if it's really needed.
+ setDirty();
+ if(reload) {
+ // The MacroItem which should be changed.
+ KSharedPtr<KoMacro::MacroItem> macroitem = macro()->items()[row];
+ // Update the properties.
+ updateProperties(row, &set, macroitem);
+ }
+ // It's needed to call the reload delayed cause in KoProperty::Editor
+ // QTimer::singleShot(10, this, SLOT(selectItemLater())); may lead
+ // to crashes if we are to fast.
+ QTimer::singleShot(50, this, SLOT(reloadPropertyLater()));
+ }
+
+ d->reloadsProperties = false;
+ */
+
+ /*
+ QStringList dirtyvarnames = macroitem->setVariable(name, KSharedPtr<KoMacro::Variable>(pv));
+ bool dirty = false;
+ bool reload = false;
+ for(QStringList::Iterator it = dirtyvarnames.begin(); it != dirtyvarnames.end(); ++it) {
+ KSharedPtr<KoMacro::Variable> variable = macroitem->variable(*it);
+ if(! variable.data()) {
+ kdDebug() << "KexiMacroDesignView::propertyChanged() name=" << name << " it=" << *it << " skipped cause such a variable is not known." << endl;
+ continue;
+ }
+
+ if(! set.contains( (*it).latin1() )) {
+ // If there exist no such property yet, we need to add it.
+ if(updateSet(&set, macroitem, *it))
+ reload = true; // we like to reload the whole set
+ continue;
+ }
+
+ kdDebug() << "KexiMacroDesignView::propertyChanged() set existing property=" << *it << endl;
+ KoProperty::Property& p = set.property((*it).latin1());
+ KoMacro::Variable::List children = variable->children();
+ if(children.count() > 0) {
+ QStringList keys, names;
+ KoMacro::Variable::List::Iterator childit(children.begin()), childend(children.end());
+ for(; childit != childend; ++childit) {
+ const QString s = (*childit)->variant().toString();
+ keys << s;
+ names << s;
+ }
+ p.setListData( new KoProperty::Property::ListData(keys, names) );
+ }
+ p.setValue(variable->variant());
+ dirty = true;
+ }
+
+ // If there are expired aka not any longer needed properties around, we
+ // need to reload the whole set.
+ for(KoProperty::Set::Iterator setit = set; setit.current(); ++setit) {
+ if(setit.currentKey() == name) continue; // don't remove ourself
+ if(setit.currentKey().left(5) == QCString("this:")) continue; // don't remove internal properties
+ if(setit.currentKey() == QCString("newrow")) continue; // also an internal used property
+ if(action.data() && action->hasVariable(setit.currentKey())) continue; // the property is still valid
+ reload = true; // we like to reload the whole set
+ }
+ */
+}
+
+void KexiMacroDesignView::reloadPropertyLater()
+{
+ propertySetReloaded(true);
+}
+
+#include "keximacrodesignview.moc"
+
diff --git a/kexi/plugins/macros/kexipart/keximacrodesignview.h b/kexi/plugins/macros/kexipart/keximacrodesignview.h
new file mode 100644
index 00000000..c3eca2d2
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacrodesignview.h
@@ -0,0 +1,129 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIMACRODESIGNVIEW_H
+#define KEXIMACRODESIGNVIEW_H
+
+#include "keximacroview.h"
+
+// Forward declarations.
+namespace KoMacro {
+ class Action;
+ class Macro;
+ class MacroItem;
+}
+namespace KoProperty {
+ class Property;
+}
+namespace KexiDB {
+ class ResultInfo;
+}
+class KexiTableItem;
+
+/**
+ * The KexiScriptDesignView implements \a KexiMacroView to provide
+ * a GUI-Editor to edit a Macro.
+ */
+class KexiMacroDesignView : public KexiMacroView
+{
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ *
+ * \param mainwin The \a KexiMainWindow instance this \a KexiViewBase
+ * belongs to.
+ * \param parent The parent widget this widget should be displayed in.
+ * \param macro The \a KoMacro::Macro instance this view is for.
+ */
+ KexiMacroDesignView(KexiMainWindow *mainwin, QWidget *parent, ::KoMacro::Macro* const macro);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiMacroDesignView();
+
+ /**
+ * Load the data from XML source and fill the internally
+ * used \a KoMacro::Macro instance.
+ */
+ virtual bool loadData();
+
+ /**
+ * \return the \a KoProperty::Set properties this view provides.
+ */
+ virtual KoProperty::Set* propertySet();
+
+ private slots:
+
+ /**
+ * Called before a cell changed in the internaly used
+ * \a KexiTableView .
+ */
+ void beforeCellChanged(KexiTableItem*, int, QVariant&, KexiDB::ResultInfo*);
+
+ /**
+ * Called if the passed \p item got updated.
+ */
+ void rowUpdated(KexiTableItem* item);
+
+ /**
+ * Called if a row got deleted.
+ */
+ void rowDeleted();
+
+ /**
+ * Called if a row got inserted.
+ */
+ void rowInserted(KexiTableItem* item, uint row, bool repaint);
+
+ /**
+ * Called if a property in the \a KoProperty got changed.
+ */
+ void propertyChanged(KoProperty::Set&, KoProperty::Property&);
+
+ /**
+ * Reloads the propertyset delayed.
+ */
+ void reloadPropertyLater();
+
+ private:
+ /// \internal d-pointer class.
+ class Private;
+ /// \internal d-pointer instance.
+ Private* const d;
+
+ /**
+ * Update the table's data.
+ */
+ void updateData();
+
+ /**
+ * Update the \a KoProperty::Set set with the passed \a KoMacro::MacroItem
+ * \p item and the variablename \p variablename .
+ */
+ bool updateSet(KoProperty::Set* set, KSharedPtr<KoMacro::MacroItem> item, const QString& variablename);
+
+ /**
+ * Update the properties of the \a KoProperty::Set \p set at
+ * row-number \p row with the \a KoMacro::MacroItem \p macroitem .
+ */
+ void updateProperties(int row, KoProperty::Set* set, KSharedPtr<KoMacro::MacroItem> macroitem);
+};
+
+#endif
diff --git a/kexi/plugins/macros/kexipart/keximacroerror.cpp b/kexi/plugins/macros/kexipart/keximacroerror.cpp
new file mode 100644
index 00000000..15f4df3f
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacroerror.cpp
@@ -0,0 +1,130 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Tobi Krebs (tobi.krebs@gmail.com)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "keximacroerror.h"
+
+#include <core/kexiproject.h>
+#include <core/keximainwindow.h>
+
+#include <qtimer.h>
+
+/**
+* \internal d-pointer class to be more flexible on future extension of the
+* functionality without to much risk to break the binary compatibility.
+*/
+class KexiMacroError::Private
+{
+ public:
+ KexiMainWindow* const mainwin;
+ KSharedPtr<KoMacro::Context> context;
+
+ Private(KexiMainWindow* const m, KoMacro::Context* const c)
+ : mainwin(m)
+ , context(c)
+ {
+ }
+};
+
+KexiMacroError::KexiMacroError(KexiMainWindow* mainwin, KSharedPtr<KoMacro::Context> context)
+ : KexiMacroErrorBase(mainwin, "KexiMacroError" , /*WFlags*/ Qt::WDestructiveClose)
+ , d(new Private(mainwin, context))
+{
+ //setText(i18n("Execution failed")); //caption
+ //errortext, errorlist, continuebtn,cancelbtn, designerbtn
+
+ KoMacro::Exception* exception = context->exception();
+
+ iconlbl->setPixmap(KGlobal::instance()->iconLoader()->loadIcon("messagebox_critical", KIcon::Small, 32));
+ errorlbl->setText(i18n("<qt>Failed to execute the macro \"%1\".<br>%2</qt>").arg( context->macro()->name() ).arg( exception->errorMessage() ));
+
+ int i = 1;
+ KoMacro::MacroItem::List items = context->macro()->items();
+ for (KoMacro::MacroItem::List::ConstIterator mit = items.begin(); mit != items.end(); mit++)
+ {
+ KListViewItem* listviewitem = new KListViewItem(errorlist);
+ listviewitem->setText(0,QString("%1").arg(i++));
+ listviewitem->setText(1,i18n("Action"));
+ KSharedPtr<KoMacro::MacroItem> macroitem = *mit;
+
+ if (macroitem != 0 && macroitem->action() != 0)
+ {
+ listviewitem->setText(2,macroitem->action()->name());
+ }
+
+ if(macroitem == context->macroItem())
+ {
+ listviewitem->setOpen(true);
+ listviewitem->setSelected(true);
+ errorlist->setSelected(listviewitem, true);
+ errorlist->ensureItemVisible(listviewitem);
+ }
+
+ KoMacro::Variable::Map variables = macroitem->variables();
+ KoMacro::Variable::Map::ConstIterator vit;
+ for ( vit = variables.begin(); vit != variables.end(); ++vit ) {
+ KListViewItem* child = new KListViewItem(listviewitem);
+ child->setText(1,vit.key());
+ child->setText(2,vit.data()->toString());
+ }
+ }
+
+ connect(designerbtn, SIGNAL(clicked()), this, SLOT(designbtnClicked()));
+ connect(continuebtn, SIGNAL(clicked()), this, SLOT(continuebtnClicked()));
+}
+
+KexiMacroError::~KexiMacroError()
+{
+ delete d;
+}
+
+void KexiMacroError::designbtnClicked()
+{
+ if(! d->mainwin->project()) {
+ kdWarning() << QString("KexiMacroError::designbtnClicked(): No project open.") << endl;
+ return;
+ }
+
+ // We need to determinate the KexiPart::Item which should be opened.
+ KSharedPtr<KoMacro::Macro> macro = d->context->macro();
+ const QString name = macro->name();
+ KexiPart::Item* item = d->mainwin->project()->itemForMimeType("kexi/macro", name);
+ if(! item) {
+ kdWarning() << QString("KexiMacroError::designbtnClicked(): No such macro \"%1\"").arg(name) << endl;
+ return;
+ }
+
+ // Try to open the KexiPart::Item now.
+ bool openingCancelled;
+ if(! d->mainwin->openObject(item, Kexi::DesignViewMode, openingCancelled)) {
+ if(! openingCancelled) {
+ kdWarning() << QString("KexiMacroError::designbtnClicked(): Open macro \"%1\" in designview failed.").arg(name) << endl;
+ return;
+ }
+ }
+
+ close();
+}
+
+void KexiMacroError::continuebtnClicked()
+{
+ QTimer::singleShot(200, d->context, SLOT(activateNext()));
+ close();
+}
diff --git a/kexi/plugins/macros/kexipart/keximacroerror.h b/kexi/plugins/macros/kexipart/keximacroerror.h
new file mode 100644
index 00000000..641859b7
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacroerror.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Tobi Krebs (tobi.krebs@gmail.com)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KEXIMACROERROR_H
+#define KEXIMACROERROR_H
+
+#include <qwidget.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+
+#include <klistview.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kglobal.h>
+#include <kdebug.h>
+
+#include "../lib/context.h"
+#include "../lib/exception.h"
+#include "../lib/macro.h"
+#include "../lib/macroitem.h"
+
+#include "keximacroerrorbase.h"
+
+// Forward-declarations.
+class KexiMainWindow;
+
+/**
+* An error dialog used to displayed more detailed informations about
+* a @a KoMacro::Exception that got thrown within a @a KoMacro::Context
+* during execution.
+*/
+class KexiMacroError : public KexiMacroErrorBase
+{
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * @param mainwin The parent @a KexiMainWindow instance.
+ * @param context The @a KoMacro::Context where the error happened.
+ */
+ KexiMacroError(KexiMainWindow* mainwin, KSharedPtr<KoMacro::Context> context);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiMacroError();
+
+ private slots:
+
+ /**
+ * Called if the "Open Macrodesigner"-Button is clicked.
+ */
+ void designbtnClicked();
+
+ /**
+ * Called if the "continue"-Button is clicked.
+ */
+ void continuebtnClicked();
+
+ private:
+ /// \internal d-pointer class.
+ class Private;
+ /// \internal d-pointer instance.
+ Private* const d;
+
+};
+
+#endif
diff --git a/kexi/plugins/macros/kexipart/keximacroerrorbase.ui b/kexi/plugins/macros/kexipart/keximacroerrorbase.ui
new file mode 100644
index 00000000..74e47cfc
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacroerrorbase.ui
@@ -0,0 +1,213 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KexiMacroErrorBase</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>KexiMacroErrorBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>492</width>
+ <height>424</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Error</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>iconlbl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>48</width>
+ <height>48</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>errorlbl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>No</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Value</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>errorlist</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsMovable">
+ <bool>false</bool>
+ </property>
+ <property name="autoOpen">
+ <bool>false</bool>
+ </property>
+ <property name="tooltipColumn">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>designerbtn</cstring>
+ </property>
+ <property name="text">
+ <string>Open in design view</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>continuebtn</cstring>
+ </property>
+ <property name="text">
+ <string>Continue</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>cancelbtn</cstring>
+ </property>
+ <property name="text">
+ <string>Abort</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>cancelbtn</sender>
+ <signal>clicked()</signal>
+ <receiver>KexiMacroErrorBase</receiver>
+ <slot>close()</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kexi/plugins/macros/kexipart/keximacrohandler.desktop b/kexi/plugins/macros/kexipart/keximacrohandler.desktop
new file mode 100644
index 00000000..c08a7b2a
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacrohandler.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Macros
+GenericName[bg]=Макроси
+GenericName[br]=Makroù
+GenericName[da]=Makroer
+GenericName[de]=Makros
+GenericName[el]=Μακροεντολές
+GenericName[eo]=Makrooj
+GenericName[et]=Makrod
+GenericName[fa]=کلان‌دستورها
+GenericName[fy]=Macro's
+GenericName[ga]=Macraí
+GenericName[hr]=Makroi
+GenericName[hu]=Makrók
+GenericName[it]=Macro
+GenericName[ja]=マクロ
+GenericName[km]=ម៉ាក្រូ​
+GenericName[lv]=Makross
+GenericName[nb]=Makroer
+GenericName[nds]=Makros
+GenericName[ne]=म्याक्रोस
+GenericName[nl]=Macro's
+GenericName[pl]=Makra
+GenericName[ru]=Макросы
+GenericName[se]=Makroat
+GenericName[sk]=Makrá
+GenericName[sl]=Makri
+GenericName[sr]=Макрои
+GenericName[sr@Latn]=Makroi
+GenericName[sv]=Makron
+GenericName[uk]=Макроси
+GenericName[uz]=Makros
+GenericName[uz@cyrillic]=Макрос
+GenericName[zh_TW]=巨集
+
+Name=Macros
+Name[bg]=Макроси
+Name[br]=Makroù
+Name[da]=Makroer
+Name[de]=Makros
+Name[el]=Μακροεντολές
+Name[eo]=Makrooj
+Name[et]=Makrod
+Name[fa]=کلان‌دستورها
+Name[fy]=Macro's
+Name[ga]=Macraí
+Name[hr]=Makroi
+Name[hu]=Makrók
+Name[it]=Macro
+Name[ja]=マクロ
+Name[km]=ម៉ាក្រូ​
+Name[lv]=Makross
+Name[nb]=Makroer
+Name[nds]=Makros
+Name[ne]=म्याक्रोस
+Name[nl]=Macro's
+Name[pl]=Makra
+Name[ru]=Макросы
+Name[se]=Makroat
+Name[sk]=Makrá
+Name[sl]=Makri
+Name[sr]=Макрои
+Name[sr@Latn]=Makroi
+Name[sv]=Makron
+Name[uk]=Макроси
+Name[uz]=Makros
+Name[uz@cyrillic]=Макрос
+Name[zh_TW]=巨集
+
+X-KDE-Library=kexihandler_macro
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=macro
+X-Kexi-TypeMime=kexi/macro
+X-Kexi-ItemIcon=macro
+X-Kexi-SupportsDataExport=false
+X-Kexi-SupportsPrinting=false
+X-Kexi-SupportsExecution=true
diff --git a/kexi/plugins/macros/kexipart/keximacropart.cpp b/kexi/plugins/macros/kexipart/keximacropart.cpp
new file mode 100644
index 00000000..c4f020e4
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacropart.cpp
@@ -0,0 +1,172 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "keximacropart.h"
+
+#include "keximacroview.h"
+#include "keximacrodesignview.h"
+#include "keximacrotextview.h"
+
+//#include "kexiviewbase.h"
+//#include "keximainwindow.h"
+//#include "kexiproject.h"
+
+#include <qdom.h>
+#include <qstringlist.h>
+#include <kgenericfactory.h>
+#include <kexipartitem.h>
+//#include <kxmlguiclient.h>
+//#include <kexidialogbase.h>
+//#include <kconfig.h>
+//#include <kdebug.h>
+
+#include "../lib/manager.h"
+#include "../lib/macro.h"
+#include "../lib/macroitem.h"
+#include "../lib/action.h"
+
+#include "../kexiactions/openaction.h"
+#include "../kexiactions/executeaction.h"
+#include "../kexiactions/navigateaction.h"
+#include "../kexiactions/messageaction.h"
+#include "../kexiactions/datatableaction.h"
+
+/**
+* \internal d-pointer class to be more flexible on future extension of the
+* functionality without to much risk to break the binary compatibility.
+*/
+class KexiMacroPart::Private
+{
+ public:
+};
+
+KexiMacroPart::KexiMacroPart(QObject *parent, const char *name, const QStringList &l)
+ : KexiPart::Part(parent, name, l)
+ , d( new Private() )
+{
+ //kdDebug() << "KexiMacroPart::KexiMacroPart() Ctor" << endl;
+
+ //registered ID
+ m_registeredPartID = (int)KexiPart::MacroObjectType;
+
+ //name of the instance.
+ m_names["instanceName"]
+ = i18n("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). "
+ "Use '_' character instead of spaces. First character should be a..z character. "
+ "If you cannot use latin characters in your language, use english word.",
+ "macro");
+
+ //describing caption
+ m_names["instanceCaption"] = i18n("Macro");
+
+ //supported viewmodes
+ m_supportedViewModes = Kexi::DesignViewMode | Kexi::TextViewMode;
+}
+
+KexiMacroPart::~KexiMacroPart()
+{
+ //kdDebug() << "KexiMacroPart::~KexiMacroPart() Dtor" << endl;
+ delete d;
+}
+
+bool KexiMacroPart::execute(KexiPart::Item* item, QObject* sender)
+{
+ KexiDialogBase* dialog = new KexiDialogBase(m_mainWin);
+ dialog->setId( item->identifier() );
+ KexiMacroView* view = dynamic_cast<KexiMacroView*>( createView(dialog, dialog, *item, Kexi::DataViewMode) );
+ if(! view) {
+ kdWarning() << "KexiMacroPart::execute() Failed to create a view." << endl;
+ return false;
+ }
+
+ if(! view->macro().data()) {
+ kdWarning() << "KexiMacroPart::execute() No such item " << item->name() << endl;
+ return false;
+ }
+
+ kdDebug() << "KexiMacroPart::execute() itemname=" << item->name() << endl;
+ view->loadData();
+ view->execute(sender);
+ view->deleteLater();
+ return true;
+}
+
+void KexiMacroPart::initPartActions()
+{
+ //kdDebug() << "KexiMacroPart::initPartActions()" << endl;
+
+ KoMacro::Manager::init(m_mainWin);
+ new KexiMacro::OpenAction;
+ new KexiMacro::ExecuteAction;
+ new KexiMacro::DataTableAction;
+ new KexiMacro::NavigateAction;
+ new KexiMacro::MessageAction;
+}
+
+void KexiMacroPart::initInstanceActions()
+{
+ //kdDebug() << "KexiMacroPart::initInstanceActions()" << endl;
+ //createSharedAction(Kexi::DesignViewMode, i18n("Execute Macro"), "exec", 0, "data_execute");
+}
+
+KexiViewBase* KexiMacroPart::createView(QWidget* parent, KexiDialogBase* dialog, KexiPart::Item& item, int viewMode, QMap<QString,QString>*)
+{
+ const QString itemname = item.name();
+ //kdDebug() << "KexiMacroPart::createView() itemname=" << itemname << endl;
+
+ if(! itemname.isNull()) {
+ KSharedPtr<KoMacro::Macro> macro = ::KoMacro::Manager::self()->getMacro(itemname);
+ if(! macro) {
+ // If we don't have a macro with that name yet, create one.
+ macro = ::KoMacro::Manager::self()->createMacro(itemname);
+ // and remember the new macro for later usage.
+ ::KoMacro::Manager::self()->addMacro(itemname, macro);
+ }
+
+ KexiMainWindow *win = dialog->mainWin();
+ if(win && win->project() && win->project()->dbConnection()) {
+ if(viewMode == Kexi::DesignViewMode) {
+ return new KexiMacroDesignView(win, parent, macro);
+ }
+ if(viewMode == Kexi::TextViewMode) {
+ return new KexiMacroTextView(win, parent, macro);
+ }
+ if(viewMode == Kexi::DataViewMode) {
+ // Called if the macro should be executed.
+ return new KexiMacroView(win, parent, macro);
+ }
+ }
+ }
+
+ //kdDebug() << "KexiMacroPart::createView() No view available." << endl;
+ return 0;
+}
+
+QString KexiMacroPart::i18nMessage(const QCString& englishMessage) const
+{
+ if(englishMessage=="Design of object \"%1\" has been modified.") {
+ return i18n("Design of macro \"%1\" has been modified.");
+ }
+ if(englishMessage=="Object \"%1\" already exists.") {
+ return i18n("Macro \"%1\" already exists.");
+ }
+ return englishMessage;
+}
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_macro, KGenericFactory<KexiMacroPart>("kexihandler_macro") )
+
+#include "keximacropart.moc"
diff --git a/kexi/plugins/macros/kexipart/keximacropart.h b/kexi/plugins/macros/kexipart/keximacropart.h
new file mode 100644
index 00000000..8d2d7af2
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacropart.h
@@ -0,0 +1,95 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIMACROPART_H
+#define KEXIMACROPART_H
+
+//#include <qcstring.h>
+
+#include <kexi.h>
+#include <kexipart.h>
+#include <kexidialogbase.h>
+
+/**
+ * Kexi Macro Plugin.
+ */
+class KexiMacroPart : public KexiPart::Part
+{
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * \param parent The parent QObject this part is child of.
+ * \param name The name this part has.
+ * \param args Optional list of arguments passed to this part.
+ */
+ KexiMacroPart(QObject *parent, const char *name, const QStringList& args);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiMacroPart();
+
+ /**
+ * Implementation of the KexiPart::Part::action method used to
+ * provide scripts as KAction's to the outside world.
+ */
+ virtual bool execute(KexiPart::Item* item, QObject* sender = 0);
+
+ /**
+ * \return the i18n message for the passed \p englishMessage string.
+ */
+ virtual QString i18nMessage(const QCString& englishMessage) const;
+
+ protected:
+
+ /**
+ * Create a new view.
+ *
+ * \param parent The parent QWidget the new view is displayed in.
+ * \param dialog The \a KexiDialogBase the view is child of.
+ * \param item The \a KexiPart::Item this view is for.
+ * \param viewMode The viewmode we like to have a view for.
+ */
+ virtual KexiViewBase* createView(QWidget *parent,
+ KexiDialogBase* dialog,
+ KexiPart::Item& item,
+ int viewMode = Kexi::DesignViewMode,
+ QMap<QString,QString>* staticObjectArgs = 0);
+
+ /**
+ * Initialize the part's actions.
+ */
+ virtual void initPartActions();
+
+ /**
+ * Initialize the instance actions.
+ */
+ virtual void initInstanceActions();
+
+ private:
+ /// \internal d-pointer class.
+ class Private;
+ /// \internal d-pointer instance.
+ Private* const d;
+};
+
+#endif
+
diff --git a/kexi/plugins/macros/kexipart/keximacroproperty.cpp b/kexi/plugins/macros/kexipart/keximacroproperty.cpp
new file mode 100644
index 00000000..2fdafd28
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacroproperty.cpp
@@ -0,0 +1,626 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "keximacroproperty.h"
+
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qlistbox.h>
+#include <qpainter.h>
+
+#include <kcombobox.h>
+#include <kpushbutton.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+
+#include "../lib/variable.h"
+#include "../lib/macroitem.h"
+
+#define KEXIMACRO_PROPERTYEDITORTYPE 5682
+
+/*************************************************************
+ * KexiMacroProperty
+ */
+
+/**
+* @internal d-pointer class to be more flexible on future extension of the
+* functionality without to much risk to break the binary compatibility.
+*/
+class KexiMacroProperty::Private
+{
+ public:
+ /** The @a KoMacro::MacroItem the custom property uses
+ internal. Together with the name we are able to identify
+ the used variable at runtime. */
+ KSharedPtr<KoMacro::MacroItem> macroitem;
+ /** The name the variable @a KoMacro::Variable is known
+ as in the @a KoMacro::MacroItem defined above. */
+ QString name;
+};
+
+KexiMacroProperty::KexiMacroProperty(KoProperty::Property* parent, KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name)
+ : KoProperty::CustomProperty(parent)
+ , d( new Private() )
+{
+ d->macroitem = macroitem;
+ d->name = name;
+ init();
+}
+
+KexiMacroProperty::~KexiMacroProperty()
+{
+ delete d;
+}
+
+void KexiMacroProperty::init()
+{
+ Q_ASSERT( d->macroitem != 0 );
+ //kdDebug() << "--------- KexiMacroProperty::set() macroitem=" << d->macroitem->name() << " name=" << d->name << endl;
+
+ KSharedPtr<KoMacro::Action> action = d->macroitem->action();
+ KSharedPtr<KoMacro::Variable> actionvariable = action->variable(d->name);
+ if(! actionvariable.data()) {
+ kdDebug() << "KexiMacroProperty::createProperty() Skipped cause there exists no such action=" << d->name << endl;
+ return;
+ }
+
+ KSharedPtr<KoMacro::Variable> variable = d->macroitem->variable(d->name, true/*checkaction*/);
+ if(! variable.data()) {
+ kdDebug() << "KexiMacroProperty::createProperty() Skipped cause there exists no such variable=" << d->name << endl;
+ return;
+ }
+
+ //TESTCASE!!!!!!!!!!!!!!!!!!!!!!
+ //if(! variable->isEnabled()) qFatal( QString("############## VARIABLE=%1").arg(variable->name()).latin1() );
+
+ Q_ASSERT(! d->name.isNull());
+ m_property->setName( d->name.latin1() );
+ m_property->setCaption( actionvariable->text() );
+ m_property->setDescription( action->comment() );
+ m_property->setValue( variable->variant(), true );
+ m_property->setType( KEXIMACRO_PROPERTYEDITORTYPE ); // use our own propertytype
+}
+
+KoProperty::Property* KexiMacroProperty::parentProperty() const
+{
+ return m_property;
+}
+
+void KexiMacroProperty::setValue(const QVariant &value, bool rememberOldValue)
+{
+ Q_UNUSED(rememberOldValue);
+ kdDebug()<<"KexiMacroProperty::setValue name="<<d->name<<" value="<<value<<" rememberOldValue="<<rememberOldValue<<endl;
+ if(! d->macroitem->setVariant(d->name, value)) { // takes care of the type-conversation
+ kdDebug()<<"KexiMacroProperty::setValue Update failed !!!"<<endl;
+ return;
+ }
+
+ // m_property->setValue() does check if the value changed by using
+ // this-value() and cause we already set it above, m_property->setValue()
+ // will be aborted. Well, we don't touch the properties value and handle
+ // it all via our CustomProperty class anyway. So, just ignore the property.
+ //m_property->setValue(this->value(), rememberOldValue, false/*useCustomProperty*/);
+
+ emit valueChanged();
+}
+
+QVariant KexiMacroProperty::value() const
+{
+ KSharedPtr<KoMacro::Variable> variable = d->macroitem->variable(d->name, true);
+ Q_ASSERT( variable.data() != 0 );
+ return variable.data() ? variable->variant() : QVariant();
+}
+
+bool KexiMacroProperty::handleValue() const
+{
+ return true; // we handle getting and setting of values and don't need KoProperty::Property for it.
+}
+
+KSharedPtr<KoMacro::MacroItem> KexiMacroProperty::macroItem() const
+{
+ return d->macroitem;
+}
+
+QString KexiMacroProperty::name() const
+{
+ return d->name;
+}
+
+KSharedPtr<KoMacro::Variable> KexiMacroProperty::variable() const
+{
+ return d->macroitem->variable(d->name, true/*checkaction*/);
+}
+
+KoProperty::Property* KexiMacroProperty::createProperty(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name)
+{
+ KoProperty::Property* property = new KoProperty::Property();
+ KexiMacroProperty* customproperty = new KexiMacroProperty(property, macroitem, name);
+ if(! customproperty->variable().data()) {
+ kdWarning() << "KexiMacroProperty::createProperty() No such variable" << endl;
+ delete customproperty; customproperty = 0;
+ delete property; property = 0;
+ return 0;
+ }
+ property->setCustomProperty(customproperty);
+ return property;
+}
+
+/*************************************************************
+ * KexiMacroPropertyFactory
+ */
+
+KexiMacroPropertyFactory::KexiMacroPropertyFactory(QObject* parent)
+ : KoProperty::CustomPropertyFactory(parent)
+{
+}
+
+KexiMacroPropertyFactory::~KexiMacroPropertyFactory()
+{
+}
+
+KoProperty::CustomProperty* KexiMacroPropertyFactory::createCustomProperty(KoProperty::Property* parent)
+{
+ kdDebug()<<"KexiMacroPropertyFactory::createCustomProperty parent="<<parent->name()<<endl;
+
+ KoProperty::CustomProperty* customproperty = parent->customProperty();
+ KexiMacroProperty* parentcustomproperty = dynamic_cast<KexiMacroProperty*>(customproperty);
+ if(! parentcustomproperty) {
+ kdWarning() << "KexiMacroPropertyFactory::createCustomProperty() parent=" << parent->name() << " has an invalid customproperty." << endl;
+ return 0;
+ }
+
+ KSharedPtr<KoMacro::MacroItem> macroitem = parentcustomproperty->macroItem();
+ Q_ASSERT( macroitem.data() != 0 );
+ const QString name = parentcustomproperty->name();
+ Q_ASSERT(! name.isEmpty());
+
+ KexiMacroProperty* macroproperty = new KexiMacroProperty(parent, macroitem, name);
+ if(! macroproperty->variable().data()) {
+ delete macroproperty; macroproperty = 0;
+ return 0;
+ }
+
+ return macroproperty;
+}
+
+KoProperty::Widget* KexiMacroPropertyFactory::createCustomWidget(KoProperty::Property* property)
+{
+ kdDebug()<<"KexiMacroPropertyFactory::createCustomWidget property="<<property->name()<<endl;
+ return new KexiMacroPropertyWidget(property);
+}
+
+void KexiMacroPropertyFactory::initFactory()
+{
+ CustomPropertyFactory* factory = KoProperty::FactoryManager::self()->factoryForEditorType(KEXIMACRO_PROPERTYEDITORTYPE);
+ if(! factory) {
+ factory = new KexiMacroPropertyFactory( KoProperty::FactoryManager::self() );
+ KoProperty::FactoryManager::self()->registerFactoryForEditor(KEXIMACRO_PROPERTYEDITORTYPE, factory);
+ }
+}
+
+/*************************************************************
+ * KexiMacroPropertyWidget
+ */
+
+/**
+* @internal implementation of a QListBoxItem to display the items of the
+* combobox used within @a KexiMacroPropertyWidget to handle variables
+* within a @a ListBox instance.
+*/
+class ListBoxItem : public QListBoxText
+{
+ public:
+ ListBoxItem(QListBox* listbox)
+ : QListBoxText(listbox), m_enabled(true) {}
+ ListBoxItem(QListBox* listbox, const QString& text, QListBoxItem* after)
+ : QListBoxText(listbox, text, after), m_enabled(true) {}
+ virtual ~ListBoxItem() {}
+ void setEnabled(bool enabled) { m_enabled = enabled; }
+ virtual int width(const QListBox* lb) const {
+ Q_ASSERT( dynamic_cast<KComboBox*>( lb->parent() ) );
+ return static_cast<KComboBox*>( lb->parent() )->lineEdit()->width() + 2;
+ }
+ virtual int height(const QListBox* lb) const {
+ Q_ASSERT( dynamic_cast<KComboBox*>( lb->parent() ) );
+ return m_enabled ? static_cast<KComboBox*>( lb->parent() )->height() + 2 : 0;
+ }
+ private:
+ bool m_enabled;
+};
+
+/**
+* @internal implementation of a @a ListBoxItem to provide an editable
+* @a KoProperty::Widget as QListBoxItem in a @a ListBox instance.
+*/
+class EditListBoxItem : public ListBoxItem
+{
+ public:
+
+ EditListBoxItem(QListBox* listbox, KexiMacroProperty* macroproperty)
+ : ListBoxItem(listbox)
+ , m_macroproperty(macroproperty)
+ , m_prop(0)
+ , m_widget(0)
+ {
+ init();
+ }
+
+ virtual ~EditListBoxItem() {
+ delete m_widget;
+ delete m_prop;
+ }
+
+ virtual QString text() const {
+ KSharedPtr<KoMacro::Variable> variable = m_macroproperty->variable();
+ Q_ASSERT( variable.data() );
+ //kdDebug()<<"EditListBoxItem::text() text="<<variable->toString()<<endl;
+ Q_ASSERT( variable->toString() != QString::null );
+ return variable->toString();
+ }
+
+ KoProperty::Widget* widget() const { return m_widget; }
+ KSharedPtr<KoMacro::MacroItem> macroItem() const { return m_macroproperty->macroItem(); }
+ KSharedPtr<KoMacro::Variable> variable() const { return m_macroproperty->variable(); }
+ KSharedPtr<KoMacro::Action> action() const { return m_macroproperty->macroItem()->action(); }
+
+ protected:
+ virtual void paint(QPainter* p) {
+ if(! m_widget) return;
+ Q_ASSERT( dynamic_cast<KComboBox*>( listBox()->parent() ) );
+ const int w = width(listBox());
+ const int h = height(listBox());
+ m_widget->setFixedSize(w - 2, h - 2);
+ p->drawPixmap(0, 0, QPixmap::grabWidget(m_widget), 1, 1, w - 1, h - 1);
+ }
+
+ private:
+ void init() {
+ KSharedPtr<KoMacro::MacroItem> macroitem = m_macroproperty->macroItem();
+ Q_ASSERT( macroitem.data() );
+ KSharedPtr<KoMacro::Action> action = m_macroproperty->macroItem()->action();
+ if(! action.data()) {
+ kdWarning() << "EditListBoxItem::EditListBoxItem() Skipped cause there exists no action for macroproperty=" << m_macroproperty->name() << endl;
+ return;
+ }
+ KoProperty::Property* parentproperty = m_macroproperty->parentProperty();
+ if(! parentproperty) {
+ kdWarning() << "EditListBoxItem::EditListBoxItem() No parentproperty defined" << endl;
+ return;
+ }
+ KSharedPtr<KoMacro::Variable> variable = m_macroproperty->variable();
+ if(! variable.data()) {
+ kdWarning() << "EditListBoxItem::EditListBoxItem() No variable defined for property=" << parentproperty->name() << endl;
+ return;
+ }
+
+ QVariant variant = variable->variant();
+
+ KSharedPtr<KoMacro::Variable> actionvariable = action->variable(m_macroproperty->name());
+ if(actionvariable.data()) {
+ QVariant actionvariant = actionvariable->variant();
+ Q_ASSERT( ! actionvariant.isNull() );
+ Q_ASSERT( variant.canCast(actionvariant.type()) );
+ variant.cast( actionvariant.type() ); //preserve type.
+ }
+
+ int type = KoProperty::Auto;
+ switch(variant.type()) {
+ case QVariant::UInt:
+ case QVariant::Int: {
+ type = KoProperty::Integer;
+ } break;
+ case QVariant::CString:
+ case QVariant::String: {
+ type = KoProperty::String;
+ } break;
+ default: {
+ kdWarning() << "EditListBoxItem::EditListBoxItem() name=" << variable->name() << " type=" << QVariant::typeToName(variant.type()) << endl;
+ } break;
+ }
+
+ QString name = variable->name();
+ Q_ASSERT(! name.isNull());
+ //if(name.isNull()) name = "aaaaaaaaaaaaaaaaa";//TESTCASE
+ m_prop = new KoProperty::Property(
+ name.latin1(), // name
+ variant, // value
+ variable->text(), // caption
+ QString::null, // description
+ type, // type
+ 0 //parentproperty // parent
+ );
+
+ m_widget = KoProperty::FactoryManager::self()->createWidgetForProperty(m_prop);
+ Q_ASSERT( m_widget != 0 );
+ //m_widget->reparent(listBox()->viewport(), 0, QPoint(0,0));
+ m_widget->reparent(listBox(), 0, QPoint(1,1));
+ //layout->addWidget(m_widget, 1);
+ m_widget->setMinimumHeight(5);
+ m_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ }
+
+ private:
+ KexiMacroProperty* m_macroproperty;
+ KoProperty::Property* m_prop;
+ KoProperty::Widget* m_widget;
+};
+
+/**
+* @internal implementation of a @a QListBox for the combobox used within
+* @a KexiMacroPropertyWidget to handle different variable-states.
+*/
+class ListBox : public QListBox
+{
+ public:
+ ListBox(KComboBox* parent, KexiMacroProperty* macroproperty)
+ : QListBox(parent)
+ , m_macroproperty(macroproperty)
+ , m_edititem(0)
+ {
+ viewport()->setBackgroundMode(PaletteBackground);
+ setVariableHeight(true);
+ update();
+ }
+
+ virtual ~ListBox() {}
+
+ void update() {
+ m_items.clear();
+ delete m_edititem;
+ m_edititem = 0;
+ clear();
+
+ m_edititem = new EditListBoxItem(this, m_macroproperty);
+ Q_ASSERT( m_edititem->widget() != 0 );
+
+ const QString name = m_macroproperty->name();
+ KoMacro::Variable::List children;
+ {
+ KoMacro::Variable::List actionchildren;
+
+ KSharedPtr<KoMacro::Variable> itemvar = m_macroproperty->macroItem()->variable(name,false);
+ //kdDebug() << "KexiMacroProperty::ListBox::update() itemvar="<<(itemvar.data() ? "name:"+itemvar->name()+" value:"+itemvar->toString() : "NULL")<<endl;
+ if(itemvar.data())
+ actionchildren = itemvar->children();
+
+ KSharedPtr<KoMacro::Action> action = m_edititem->action();
+ KSharedPtr<KoMacro::Variable> actionvar = action.data() ? action->variable(name) : KSharedPtr<KoMacro::Variable>();
+ //kdDebug() << "KexiMacroProperty::ListBox::update() actionvar="<<(actionvar.data() ? "name:"+actionvar->name()+" value:"+actionvar->toString() : "NULL")<<endl;
+ if(actionvar.data())
+ actionchildren += actionvar->children();
+
+ KoMacro::Variable::List::ConstIterator it(actionchildren.constBegin()), end(actionchildren.constEnd());
+ for(; it != end; ++it) {
+ if(name == (*it)->name()) {
+ KoMacro::Variable::List list = (*it)->children();
+ KoMacro::Variable::List::ConstIterator listit(list.constBegin()), listend(list.constEnd());
+ for(; listit != listend; ++listit)
+ children.append( *listit );
+ }
+ }
+
+ if(children.count() <= 0)
+ children = actionchildren;
+ }
+
+ /*
+ kdDebug() << "KexiMacroProperty::ListBox::update() name="<<name<<" childcount="<<children.count()<<endl;
+ KoMacro::Variable::List::ConstIterator listit(children.constBegin()), listend(children.constEnd());
+ for(; listit != listend; ++listit) {
+ kdDebug()<<" child name="<<(*listit)->name()<<" value="<<(*listit)->toString()<<" childcount="<<(*listit)->children().count()<<endl;
+ }
+ */
+
+ if(children.count() > 0) {
+ KoMacro::Variable::List::Iterator childit(children.begin()), childend(children.end());
+ for(; childit != childend; ++childit) {
+ const QString n = (*childit)->name();
+ //if(! n.startsWith("@")) continue;
+ const QVariant v = (*childit)->variant();
+
+ //kdDebug() << " child name=" << n << " value=" << v << endl;
+ switch( v.type() ) {
+ /* case QVariant::Map: {
+ const QMap<QString,QVariant> map = v.toMap();
+ for(QMap<QString,QVariant>::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it)
+ m_items.append(it.key());
+ } break; */
+ case QVariant::List: {
+ const QValueList<QVariant> list = v.toList();
+ for(QValueList<QVariant>::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
+ const QString s = (*it).toString().stripWhiteSpace();
+ if(! s.isEmpty())
+ m_items.append(s);
+ }
+ } break;
+ case QVariant::StringList: {
+ const QStringList list = v.toStringList();
+ for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
+ if(! (*it).isEmpty())
+ m_items.append(*it);
+ } break;
+ default: {
+ const QString s = v.toString().stripWhiteSpace();
+ if(! s.isEmpty())
+ m_items.append(s);
+ } break;
+ }
+ }
+ }
+
+ QListBoxItem* item = m_edititem;
+ const uint count = m_items.count();
+ for(uint i = 0; i < count; i++)
+ item = new ListBoxItem(this, m_items[i], item);
+ }
+
+ EditListBoxItem* editItem() const { return m_edititem; }
+ QStringList items() const { return m_items; }
+
+ virtual void hide () {
+ QListBox::hide();
+ for(uint i = 0; i < count(); i++)
+ static_cast<ListBoxItem*>( item(i) )->setEnabled(false);
+ }
+ virtual void show() {
+ update();
+ adjustSize();
+ QListBox::show();
+ }
+
+ private:
+ KexiMacroProperty* m_macroproperty;
+ EditListBoxItem* m_edititem;
+ QStringList m_items;
+};
+
+/**
+* @internal d-pointer class to be more flexible on future extension of the
+* functionality without to much risk to break the binary compatibility.
+*/
+class KexiMacroPropertyWidget::Private
+{
+ public:
+ KexiMacroProperty* macroproperty;
+ KComboBox* combobox;
+ ListBox* listbox;
+};
+
+KexiMacroPropertyWidget::KexiMacroPropertyWidget(KoProperty::Property* property, QWidget* parent)
+ : KoProperty::Widget(property, parent)
+ , d( new Private() )
+{
+ kdDebug() << "KexiMacroPropertyWidget::KexiMacroPropertyWidget() Ctor" << endl;
+
+ QHBoxLayout* layout = new QHBoxLayout(this, 0, 0);
+
+ d->macroproperty = dynamic_cast<KexiMacroProperty*>( property->customProperty() );
+ if(! d->macroproperty) {
+ kdWarning() << "KexiMacroPropertyWidget::KexiMacroPropertyWidget() Missing macroproperty for property=" << property->name() << endl;
+ return;
+ }
+
+ d->combobox = new KComboBox(this);
+ layout->addWidget(d->combobox);
+ d->listbox = new ListBox(d->combobox, d->macroproperty);
+ d->combobox->setEditable(true);
+ d->combobox->setListBox(d->listbox);
+ d->combobox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ d->combobox->setMinimumHeight(5);
+ d->combobox->setInsertionPolicy(QComboBox::NoInsertion);
+ d->combobox->setMinimumSize(10, 0); // to allow the combo to be resized to a small size
+ d->combobox->setAutoCompletion(false);
+ d->combobox->setContextMenuEnabled(false);
+
+ QVariant value = d->macroproperty->value();
+ int index = d->listbox->items().findIndex( value.toString() );
+ if(index >= 0) {
+ d->combobox->setCurrentItem(index + 1);
+ d->listbox->setCurrentItem(index + 1);
+ }
+ else {
+ Q_ASSERT( d->listbox->editItem()->widget() != 0 );
+ d->listbox->editItem()->widget()->setValue( d->macroproperty->value(), true );
+ //d->combobox->setCurrentItem(0);
+ }
+ kdDebug() << ">>> KexiMacroPropertyWidget::KexiMacroPropertyWidget() CurrentItem=" << d->combobox->currentItem() << endl;
+
+ d->combobox->setFocusProxy( d->listbox->editItem()->widget() );
+ setFocusWidget( d->combobox->lineEdit() );
+
+ connect(d->combobox, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotComboBoxChanged()));
+ connect(d->combobox, SIGNAL(activated(int)),
+ this, SLOT(slotComboBoxActivated()));
+ connect(d->listbox->editItem()->widget(), SIGNAL(valueChanged(Widget*)),
+ this, SLOT(slotWidgetValueChanged()));
+ connect(d->macroproperty, SIGNAL(valueChanged()),
+ this, SLOT(slotPropertyValueChanged()));
+}
+
+KexiMacroPropertyWidget::~KexiMacroPropertyWidget()
+{
+ kdDebug() << "KexiMacroPropertyWidget::~KexiMacroPropertyWidget() Dtor" << endl;
+ delete d;
+}
+
+QVariant KexiMacroPropertyWidget::value() const
+{
+ kdDebug()<<"KexiMacroPropertyWidget::value() value="<<d->macroproperty->value()<<endl;
+ return d->macroproperty->value();
+ /* QVariant value = d->combobox->currentText();
+ value.cast( d->macroproperty->value().type() );
+ return value; */
+}
+
+void KexiMacroPropertyWidget::setValue(const QVariant& value, bool emitChange)
+{
+ kdDebug()<<"KexiMacroPropertyWidget::setValue() value="<<value<<" emitChange="<<emitChange<<endl;
+
+ if(! emitChange)
+ d->combobox->blockSignals(true);
+
+ const QString s = value.toString();
+ d->combobox->setCurrentText( s.isNull() ? "" : s );
+
+ if(emitChange)
+ emit valueChanged(this);
+ else
+ d->combobox->blockSignals(false);
+}
+
+void KexiMacroPropertyWidget::setReadOnlyInternal(bool readOnly)
+{
+ Q_UNUSED(readOnly);
+ //kdDebug()<<"KexiMacroPropertyWidget::setReadOnlyInternal() readOnly="<<readOnly<<endl;
+}
+
+void KexiMacroPropertyWidget::slotComboBoxChanged()
+{
+ kdDebug()<<"KexiMacroPropertyWidget::slotComboBoxChanged()"<<endl;
+ const QVariant v = d->combobox->currentText();
+ d->macroproperty->setValue(v, true);
+ //emit valueChanged(this);
+}
+
+void KexiMacroPropertyWidget::slotComboBoxActivated()
+{
+ Q_ASSERT( d->listbox->editItem()->widget() );
+ const int index = d->combobox->currentItem();
+ QString text = (index == 0)
+ ? d->listbox->editItem()->widget()->value().toString()
+ : d->combobox->text(index);
+ kdDebug()<<"KexiMacroPropertyWidget::slotComboBoxActivated() index="<<index<<" text="<<text<<endl;
+ d->combobox->setCurrentText(text);
+ slotWidgetValueChanged();
+}
+
+void KexiMacroPropertyWidget::slotWidgetValueChanged()
+{
+ d->macroproperty->emitPropertyChanged();
+}
+
+void KexiMacroPropertyWidget::slotPropertyValueChanged()
+{
+ Q_ASSERT( d->listbox->editItem()->widget() );
+ const QVariant v = d->macroproperty->value();
+ kdDebug()<<"KexiMacroPropertyWidget::slotPropertyValueChanged() value="<<v<<endl;
+ d->listbox->editItem()->widget()->setValue(v, true);
+}
+
+#include "keximacroproperty.moc"
diff --git a/kexi/plugins/macros/kexipart/keximacroproperty.h b/kexi/plugins/macros/kexipart/keximacroproperty.h
new file mode 100644
index 00000000..19f7b7ac
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacroproperty.h
@@ -0,0 +1,186 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIMACROPROPERTY_H
+#define KEXIMACROPROPERTY_H
+
+#include <ksharedptr.h>
+#include <koproperty/property.h>
+#include <koproperty/factory.h>
+#include <koproperty/customproperty.h>
+#include <koproperty/widget.h>
+
+namespace KoMacro {
+ class Variable;
+ class MacroItem;
+}
+
+class KexiMacroPropertyWidget;
+
+/**
+* Implementation of a @a KoProperty::CustomProperty to have
+* more control about the handling of our macro-properties.
+*/
+class KexiMacroProperty
+ : public QObject
+ , public KoProperty::CustomProperty
+{
+ Q_OBJECT
+
+ friend class KexiMacroPropertyWidget;
+
+ public:
+
+ /** Constructor. */
+ explicit KexiMacroProperty(KoProperty::Property* parent, KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name);
+ /** Destructor. */
+ virtual ~KexiMacroProperty();
+
+ /** @return the parent @a KoProperty::Property instance. */
+ KoProperty::Property* parentProperty() const;
+
+ /** This function is called by @ref KoProperty::Property::setValue()
+ when a custom property is set.
+ You don't have to modify the property value, it is done by Property class.
+ You just have to update child or parent properties value (m_property->parent()->setValue()).
+ Note that, when calling Property::setValue, you <b>need</b> to set
+ useCustomProperty (3rd parameter) to false, or there will be infinite recursion. */
+ virtual void setValue(const QVariant &value, bool rememberOldValue);
+
+ /** This function is called by @ref KoProperty::Property::value()
+ when a custom property is set and @ref handleValue() is true.
+ You should return property's value, taken from parent's value.*/
+ virtual QVariant value() const;
+
+ /** Tells whether CustomProperty should be used to get the property's value.
+ You should return true for child properties, and false for others. */
+ virtual bool handleValue() const;
+
+ /** \return the \a KoMacro::MacroItem this custom property has or
+ NULL if there was no item provided. */
+ KSharedPtr<KoMacro::MacroItem> macroItem() const;
+
+ /** \return the name the property has in the \a KoMacro::MacroItem
+ above. Is QString::null if there was no item provided. */
+ QString name() const;
+
+ /** \return the \a KoMacro::Variable which has the name @a name()
+ in the item @a macroItem() . If such a variable doesn't exists NULL
+ is returned. */
+ KSharedPtr<KoMacro::Variable> variable() const;
+
+ /** Factory function to create a new @a KoProperty::Property instance
+ that will use a @a KexiMacroProperty as container. */
+ static KoProperty::Property* createProperty(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name);
+
+ signals:
+
+ /** Emitted if @a setValue was called and the value changed. */
+ void valueChanged();
+
+ private:
+ /** \internal d-pointer class. */
+ class Private;
+ /** \internal d-pointer instance. */
+ Private* const d;
+ /** \internal initializer method. */
+ inline void init();
+};
+
+/**
+* Implementation of a @a KoProperty::CustomPropertyFactory to handle
+* creation of @a KexiMacroProperty and @a KexiMacroPropertyWidget
+* instances for our macro-properties.
+*/
+class KexiMacroPropertyFactory : public KoProperty::CustomPropertyFactory
+{
+ public:
+ /** Constructor. */
+ explicit KexiMacroPropertyFactory(QObject* parent);
+ /** Destructor. */
+ virtual ~KexiMacroPropertyFactory();
+
+ /** @return a new instance of custom property for @p parent.
+ Implement this for property types you want to support.
+ Use parent->type() to get type of the property. */
+ virtual KoProperty::CustomProperty* createCustomProperty(KoProperty::Property* parent);
+
+ /** @return a new instance of custom property for @p property.
+ Implement this for property editor types you want to support.
+ Use parent->type() to get type of the property. */
+ virtual KoProperty::Widget* createCustomWidget(KoProperty::Property* property);
+
+ /** Initializes this factory. The factory may register itself at
+ the @a KoProperty::FactoryManager if not alreadydone before. This
+ function should be called from within the @a KexiMacroDesignView
+ before the functionality provided with @a KexiMacroProperty and
+ @a KexiMacroPropertyWidget got used. */
+ static void initFactory();
+};
+
+/**
+ * Implementation of a @a KoProperty::Widget used to display and
+ * edit a @a KexiMacroProperty .
+ */
+class KexiMacroPropertyWidget : public KoProperty::Widget
+{
+ Q_OBJECT
+
+ public:
+ /** Constructor. */
+ explicit KexiMacroPropertyWidget(KoProperty::Property* property, QWidget* parent = 0);
+ /** Destructor. */
+ virtual ~KexiMacroPropertyWidget();
+
+ /** @return the value this widget has. */
+ virtual QVariant value() const;
+
+ /** Set the value @p value this widget has. If @p emitChange is true,
+ the @p KoProperty::Widget::valueChanged signal will be emitted. */
+ virtual void setValue(const QVariant& value, bool emitChange=true);
+
+ //virtual void drawViewer(QPainter *p, const QColorGroup &cg, const QRect &r, const QVariant &value);
+
+ protected:
+
+ /** Called if the value should be read only. */
+ virtual void setReadOnlyInternal(bool readOnly);
+
+ private slots:
+
+ /** Called if the text in the KComboBox changed. */
+ void slotComboBoxChanged();
+
+ /** Called if an item in the QListBox of the KComboBox got activated. */
+ void slotComboBoxActivated();
+
+ /** Called if the @a KoProperty::Widget of the EditListBoxItem got changed. */
+ void slotWidgetValueChanged();
+
+ /** Called if the value of a @a KexiMacroProperty changed to update
+ the widget and the displayed content. */
+ void slotPropertyValueChanged();
+
+ private:
+ /** \internal d-pointer class. */
+ class Private;
+ /** \internal d-pointer instance. */
+ Private* const d;
+};
+
+#endif
+
diff --git a/kexi/plugins/macros/kexipart/keximacrotextview.cpp b/kexi/plugins/macros/kexipart/keximacrotextview.cpp
new file mode 100644
index 00000000..95c94a47
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacrotextview.cpp
@@ -0,0 +1,90 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "keximacrotextview.h"
+
+#include <ktextedit.h>
+#include <kdebug.h>
+
+#include <kexidialogbase.h>
+#include <kexidb/connection.h>
+
+#include "../lib/macro.h"
+#include "../lib/xmlhandler.h"
+
+/**
+* \internal d-pointer class to be more flexible on future extension of the
+* functionality without to much risk to break the binary compatibility.
+*/
+class KexiMacroTextView::Private
+{
+ public:
+
+ /**
+ * The Editor used to display and edit the XML text.
+ */
+ KTextEdit* editor;
+
+};
+
+KexiMacroTextView::KexiMacroTextView(KexiMainWindow *mainwin, QWidget *parent, ::KoMacro::Macro* const macro)
+ : KexiMacroView(mainwin, parent, macro, "KexiMacroTextView")
+ , d( new Private() )
+{
+ QHBoxLayout* layout = new QHBoxLayout(this);
+ d->editor = new KTextEdit(this);
+ d->editor->setTextFormat(Qt::PlainText);
+ d->editor->setWordWrap(QTextEdit::NoWrap);
+ layout->addWidget(d->editor);
+
+ connect(d->editor, SIGNAL(textChanged()), this, SLOT(editorChanged()));
+}
+
+KexiMacroTextView::~KexiMacroTextView()
+{
+ delete d;
+}
+
+void KexiMacroTextView::editorChanged()
+{
+ setDirty(true);
+}
+
+bool KexiMacroTextView::loadData()
+{
+ QString data;
+ if(! loadDataBlock(data)) {
+ kexipluginsdbg << "KexiMacroTextView::loadData(): no DataBlock" << endl;
+ return false;
+ }
+
+ kdDebug() << QString("KexiMacroTextView::loadData()\n%1").arg(data) << endl;
+ //d->editor->blockSignals(true);
+ d->editor->setText(data);
+ //d->editor->blockSignals(false);
+ setDirty(false);
+ return true;
+}
+
+tristate KexiMacroTextView::storeData(bool /*dontAsk*/)
+{
+ kexipluginsdbg << QString("KexiMacroTextView::storeData() %1 [%2]\n%3").arg(parentDialog()->partItem()->name()).arg(parentDialog()->id()).arg(d->editor->text()) << endl;
+ return storeDataBlock( d->editor->text() );
+}
+
+#include "keximacrotextview.moc"
+
diff --git a/kexi/plugins/macros/kexipart/keximacrotextview.h b/kexi/plugins/macros/kexipart/keximacrotextview.h
new file mode 100644
index 00000000..66a2229c
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacrotextview.h
@@ -0,0 +1,77 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIMACROTEXTVIEW_H
+#define KEXIMACROTEXTVIEW_H
+
+#include "keximacroview.h"
+
+// Forward declaration.
+namespace KoMacro {
+ class Macro;
+}
+
+/**
+ * The KexiMacroTextView implements \a KexiMacroView to provide
+ * a simple texteditor to edit the XML document of a Macro.
+ */
+class KexiMacroTextView : public KexiMacroView
+{
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ *
+ * \param mainwin The \a KexiMainWindow instance this \a KexiViewBase
+ * belongs to.
+ * \param parent The parent widget this widget should be displayed in.
+ * \param macro The \a KoMacro::Macro instance this view is for.
+ */
+ KexiMacroTextView(KexiMainWindow *mainwin, QWidget *parent, ::KoMacro::Macro* const macro);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiMacroTextView();
+
+ /**
+ * Load the data and display it in the editor.
+ */
+ virtual bool loadData();
+
+ /**
+ * Try to store the modified data in the already opened and
+ * currently used \a KexiDB::SchemaData instance.
+ */
+ virtual tristate storeData(bool dontAsk = false);
+
+ private slots:
+
+ /**
+ * This slot got called if the text of the editor changed.
+ */
+ void editorChanged();
+
+ private:
+ /// \internal d-pointer class.
+ class Private;
+ /// \internal d-pointer instance.
+ Private* const d;
+};
+
+#endif
diff --git a/kexi/plugins/macros/kexipart/keximacroview.cpp b/kexi/plugins/macros/kexipart/keximacroview.cpp
new file mode 100644
index 00000000..35200829
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacroview.cpp
@@ -0,0 +1,175 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "keximacroview.h"
+
+#include <qdom.h>
+#include <kdebug.h>
+
+#include <kexidialogbase.h>
+#include <kexidb/connection.h>
+#include <kexidb/error.h>
+
+#include <core/kexi.h>
+#include <core/kexiproject.h>
+#include <core/kexipartmanager.h>
+#include <core/kexipartinfo.h>
+
+#include "../lib/macro.h"
+#include "../lib/xmlhandler.h"
+#include "../lib/exception.h"
+
+#include "keximacroerror.h"
+
+/**
+* \internal d-pointer class to be more flexible on future extension of the
+* functionality without to much risk to break the binary compatibility.
+*/
+class KexiMacroView::Private
+{
+ public:
+
+ /**
+ * The \a KoMacro::Manager instance used to access the
+ * Macro Framework.
+ */
+ KSharedPtr<KoMacro::Macro> macro;
+
+ /**
+ * Constructor.
+ *
+ * \param m The passed \a KoMacro::Manager instance our
+ * \a manager points to.
+ */
+ Private(KoMacro::Macro* const m)
+ : macro(m)
+ {
+ }
+
+};
+
+KexiMacroView::KexiMacroView(KexiMainWindow *mainwin, QWidget *parent, KoMacro::Macro* const macro, const char* name)
+ : KexiViewBase(mainwin, parent, (name ? name : "KexiMacroView"))
+ , d( new Private(macro) )
+{
+ //kdDebug() << "KexiMacroView::KexiMacroView() Ctor" << endl;
+ plugSharedAction( "data_execute", this, SLOT(execute()) );
+}
+
+KexiMacroView::~KexiMacroView()
+{
+ //kdDebug() << "KexiMacroView::~KexiMacroView() Dtor" << endl;
+ delete d;
+}
+
+KSharedPtr<KoMacro::Macro> KexiMacroView::macro() const
+{
+ return d->macro;
+}
+
+tristate KexiMacroView::beforeSwitchTo(int mode, bool& dontstore)
+{
+ kexipluginsdbg << "KexiMacroView::beforeSwitchTo mode=" << mode << " dontstore=" << dontstore << endl;
+ return true;
+}
+
+tristate KexiMacroView::afterSwitchFrom(int mode)
+{
+ kexipluginsdbg << "KexiMacroView::afterSwitchFrom mode=" << mode << endl;
+ loadData(); // reload the data
+ return true;
+}
+
+bool KexiMacroView::loadData()
+{
+ d->macro->clearItems();
+
+ QString data;
+ if(! loadDataBlock(data)) {
+ kexipluginsdbg << "KexiMacroView::loadData(): no DataBlock" << endl;
+ return false;
+ }
+
+ QString errmsg;
+ int errline, errcol;
+
+ QDomDocument domdoc;
+ bool parsed = domdoc.setContent(data, false, &errmsg, &errline, &errcol);
+
+ if(! parsed) {
+ kexipluginsdbg << "KexiMacroView::loadData() XML parsing error line: " << errline << " col: " << errcol << " message: " << errmsg << endl;
+ return false;
+ }
+
+ kexipluginsdbg << QString("KexiMacroView::loadData()\n%1").arg(domdoc.toString()) << endl;
+ QDomElement macroelem = domdoc.namedItem("macro").toElement();
+ if(macroelem.isNull()) {
+ kexipluginsdbg << "KexiMacroView::loadData() Macro domelement is null" << endl;
+ return false;
+ }
+
+ //kexipluginsdbg << "KexiMacroView::loadData()" << endl;
+ return d->macro->parseXML(macroelem);
+}
+
+KexiDB::SchemaData* KexiMacroView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ KexiDB::SchemaData *schema = KexiViewBase::storeNewData(sdata, cancel);
+ kexipluginsdbg << "KexiMacroView::storeNewData() new id:" << schema->id() << endl;
+
+ if(!schema || cancel) {
+ delete schema;
+ return 0;
+ }
+
+ if(! storeData()) {
+ kexipluginsdbg << "KexiMacroView::storeNewData() Failed to store the data." << endl;
+ //failure: remove object's schema data to avoid garbage
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ conn->removeObject( schema->id() );
+ delete schema;
+ return 0;
+ }
+
+ return schema;
+}
+
+tristate KexiMacroView::storeData(bool /*dontAsk*/)
+{
+ QDomDocument domdoc("macros");
+ QDomElement macroelem = d->macro->toXML();
+ domdoc.appendChild(macroelem);
+ const QString xml = domdoc.toString(2);
+ const QString name = QString("%1 [%2]").arg(parentDialog()->partItem()->name()).arg(parentDialog()->id());
+ kexipluginsdbg << QString("KexiMacroView::storeData %1\n%2").arg(name).arg(xml) << endl;
+ return storeDataBlock(xml);
+}
+
+void KexiMacroView::execute(QObject* sender)
+{
+ KSharedPtr<KoMacro::Context> context = d->macro->execute(sender);
+ if(context->hadException()) {
+ KexiMacroError* error = new KexiMacroError(
+ mainWin(), // The parent KexiMainWindow
+ context // The KoMacro::Context where the error occured.
+ );
+ error->exec();
+ }
+}
+
+#include "keximacroview.moc"
+
diff --git a/kexi/plugins/macros/kexipart/keximacroview.h b/kexi/plugins/macros/kexipart/keximacroview.h
new file mode 100644
index 00000000..beed842e
--- /dev/null
+++ b/kexi/plugins/macros/kexipart/keximacroview.h
@@ -0,0 +1,140 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIMACROVIEW_H
+#define KEXIMACROVIEW_H
+
+#include <kexiviewbase.h>
+
+// Forward declarations.
+namespace KoMacro {
+ class Macro;
+}
+namespace KoProperty {
+ class Property;
+}
+namespace KexiDB {
+ class ResultInfo;
+}
+class KexiTableItem;
+
+/**
+ * The KexiMacroView implements \a KexiViewBase to provide
+ * a base KexiView instance for Macros.
+ *
+ * The \a KexiMacroDesignView and the \a KexiMacroTextView
+ * are inherited from this class.
+ */
+class KexiMacroView : public KexiViewBase
+{
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ *
+ * \param mainwin The \a KexiMainWindow instance this \a KexiViewBase
+ * belongs to.
+ * \param parent The parent widget this widget should be displayed in.
+ * \param macro The \a KoMacro::Macro instance this view is for.
+ */
+ KexiMacroView(KexiMainWindow *mainwin, QWidget *parent, ::KoMacro::Macro* const macro, const char* name = 0);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiMacroView();
+
+ /**
+ * \return the Macro instance.
+ */
+ KSharedPtr<KoMacro::Macro> macro() const;
+
+ /**
+ * Load the data from XML source and fill the internally
+ * used \a KoMacro::Macro instance.
+ */
+ virtual bool loadData();
+
+ /**
+ * Try to call \a storeData with new data we like to store. On
+ * success the matching \a KexiDB::SchemaData is returned.
+ *
+ * \param sdata The source \a KexiDB::SchemaData instance.
+ * \param cancel Cancel on failure and don't try to clean
+ * possible temporary created data up.
+ * \return The matching \a KexiDB::SchemaData instance or NULL
+ * if storing failed.
+ */
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+
+ /**
+ * Try to store the modified data in the already opened and
+ * currently used \a KexiDB::SchemaData instance.
+ */
+ virtual tristate storeData(bool dontAsk = false);
+
+ public slots:
+
+ /**
+ * This slot will be invoked if Kexi's menuitem Data=>Execute
+ * got activated and will execute the Macro.
+ */
+ void execute(QObject* sender = 0);
+
+ protected:
+
+ /**
+ * Called by \a KexiDialogBase::switchToViewMode() right before dialog
+ * is switched to new mode.
+ *
+ * \param mode The viewmode to which should be switched. This
+ * could be either Kexi::DataViewMode, Kexi::DesignViewMode
+ * or Kexi::TextViewMode.
+ * \param donstore This call-by-reference boolean value defines
+ * if \a storeData should be called for the old but still
+ * selected viewmode. Set \a dontstore to true (it's false
+ * by default) if you want to avoid data storing.
+ * \return true if you accept or false if a error occupied and view
+ * shouldn't change. If there is no error but switching
+ * should be just cancelled (probably after showing some
+ * info messages), you need to return cancelled.
+ */
+ virtual tristate beforeSwitchTo(int mode, bool& dontstore);
+
+ /**
+ * Called by \a KexiDialogBase::switchToViewMode() right after dialog
+ * is switched to new mode.
+ *
+ * \param mode The viewmode to which we switched. This could
+ * be either Kexi::DataViewMode, Kexi::DesignViewMode
+ * or Kexi::TextViewMode.
+ * \return true if you accept or false if a error occupied and view
+ * shouldn't change. If there is no error but switching
+ * should be just cancelled (probably after showing
+ * some info messages), you need to return cancelled.
+ */
+ virtual tristate afterSwitchFrom(int mode);
+
+ private:
+ /// \internal d-pointer class.
+ class Private;
+ /// \internal d-pointer instance.
+ Private* const d;
+};
+
+#endif
diff --git a/kexi/plugins/macros/lib/Makefile.am b/kexi/plugins/macros/lib/Makefile.am
new file mode 100644
index 00000000..fc7867b8
--- /dev/null
+++ b/kexi/plugins/macros/lib/Makefile.am
@@ -0,0 +1,23 @@
+noinst_LTLIBRARIES = libkomacro.la
+
+libkomacro_la_SOURCES = \
+ exception.cpp \
+ variable.cpp \
+ metaparameter.cpp \
+ metamethod.cpp \
+ metaobject.cpp \
+ action.cpp \
+ macroitem.cpp \
+ macro.cpp \
+ context.cpp \
+ xmlhandler.cpp \
+ manager.cpp
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+
+libkomacro_la_LDFLAGS = $(all_libraries) -Wnounresolved
+libkomacro_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI)
+
+libkomacro_la_METASOURCES = AUTO
+SUBDIRS = .
+INCLUDES = $(all_includes)
diff --git a/kexi/plugins/macros/lib/action.cpp b/kexi/plugins/macros/lib/action.cpp
new file mode 100644
index 00000000..e2dc0b64
--- /dev/null
+++ b/kexi/plugins/macros/lib/action.cpp
@@ -0,0 +1,170 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "action.h"
+
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class Action::Private
+ {
+ public:
+
+ /**
+ * The name this @a Action has.
+ */
+ QString name;
+
+ /**
+ * The i18n-caption text this @a Action has.
+ */
+ QString text;
+
+ /**
+ * The comment the user is able to define for each action.
+ */
+ QString comment;
+
+ /**
+ * A map of @a Variable instances this @a Action
+ * provides accessible by there QString name.
+ */
+ Variable::Map varmap;
+
+ /**
+ * List of variablenames. This list provides a
+ * sorted order for the @a Variable instances
+ * defined in the map above.
+ */
+ QStringList varnames;
+
+ };
+
+}
+
+Action::Action(const QString& name, const QString& text)
+ : QObject()
+ , KShared()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+ kdDebug() << "Action::Action() name=" << name << endl;
+ d->name = name;
+ setText(text);
+
+ // Publish this action.
+ KoMacro::Manager::self()->publishAction( KSharedPtr<Action>(this) );
+}
+
+Action::~Action()
+{
+ //kdDebug() << QString("Action::~Action() name=\"%1\"").arg(name()) << endl;
+
+ // destroy the private d-pointer instance.
+ delete d;
+}
+
+const QString Action::toString() const
+{
+ return QString("Action:%1").arg(name());
+}
+
+const QString Action::name() const
+{
+ return d->name;
+}
+
+void Action::setName(const QString& name)
+{
+ d->name = name;
+}
+
+const QString Action::text() const
+{
+ return d->text;
+}
+
+void Action::setText(const QString& text)
+{
+ d->text = text;
+}
+
+const QString Action::comment() const
+{
+ return d->comment;
+}
+
+void Action::setComment(const QString& comment)
+{
+ d->comment = comment;
+}
+
+bool Action::hasVariable(const QString& name) const
+{
+ return d->varmap.contains(name);
+}
+
+KSharedPtr<Variable> Action::variable(const QString& name) const
+{
+ return d->varmap.contains(name) ? d->varmap[name] : KSharedPtr<Variable>(0);
+}
+
+Variable::Map Action::variables() const
+{
+ return d->varmap;
+}
+
+QStringList Action::variableNames() const
+{
+ return d->varnames;
+}
+
+void Action::setVariable(KSharedPtr<Variable> variable)
+{
+ const QString name = variable->name();
+ if(! d->varmap.contains(name)) {
+ d->varnames.append(name);
+ }
+ d->varmap.replace(name, variable);
+}
+
+void Action::setVariable(const QString& name, const QString& text, const QVariant& variant)
+{
+ Variable* variable = new Variable(variant);
+ variable->setName(name);
+ variable->setText(text);
+ setVariable( KSharedPtr<Variable>(variable) );
+}
+
+void Action::removeVariable(const QString& name)
+{
+ if(d->varmap.contains(name)) {
+ d->varmap.remove(name);
+ d->varnames.remove(name);
+ }
+}
+
+#include "action.moc"
diff --git a/kexi/plugins/macros/lib/action.h b/kexi/plugins/macros/lib/action.h
new file mode 100644
index 00000000..5200c1a4
--- /dev/null
+++ b/kexi/plugins/macros/lib/action.h
@@ -0,0 +1,187 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_ACTION_H
+#define KOMACRO_ACTION_H
+
+#include "manager.h"
+#include "context.h"
+#include "variable.h"
+
+#include <qobject.h>
+#include <ksharedptr.h>
+#include <qstringlist.h>
+
+namespace KoMacro {
+
+ /**
+ * The Action class extendes KAction to implement some additional
+ * functionality KAction doesn't provide.
+ */
+ class KOMACRO_EXPORT Action
+ : public QObject // Qt functionality like signals and slots
+ , public KShared // shared reference-counting
+ {
+ Q_OBJECT
+
+ /// Property to get/set the name.
+ Q_PROPERTY(QString name READ name WRITE setName)
+
+ /// Property to get/set the text.
+ Q_PROPERTY(QString text READ text WRITE setText)
+
+ /// Property to get/set the comment.
+ Q_PROPERTY(QString comment READ comment WRITE setComment)
+
+ public:
+
+ /**
+ * Shared pointer to implement reference-counting.
+ */
+ typedef QMap<QString, KSharedPtr<Action> > Map;
+
+ /**
+ * Constructor.
+ *
+ * @param name The unique name this @a Action has.
+ * @param text The i18n-caption text this @a Action has.
+ */
+ explicit Action(const QString& name, const QString& text = QString::null);
+
+ /**
+ * Destructor.
+ */
+ virtual ~Action();
+
+ /**
+ * @return a string representation of the functionality
+ * this action provides.
+ */
+ virtual const QString toString() const;
+
+ /**
+ * The name this @a Action has.
+ */
+ const QString name() const;
+
+ /**
+ * Set the name of the @a Action to @p name .
+ */
+ void setName(const QString& name);
+
+ /**
+ * @return the i18n-caption text this @a Action has.
+ */
+ const QString text() const;
+
+ /**
+ * Set the i18n-caption text this @a Action has.
+ */
+ void setText(const QString& text);
+
+ /**
+ * @return the comment associated with this action.
+ */
+ const QString comment() const;
+
+ /**
+ * Set the @p comment associated with this action.
+ */
+ void setComment(const QString& comment);
+
+ /**
+ * @return true if there exists a variable with the
+ * name @p name else false is returned.
+ */
+ bool hasVariable(const QString& name) const;
+
+ /**
+ * @return the variable @a Variable defined for the
+ * name @p name . If there exists no @a Variable with
+ * such a name, NULL is returned.
+ */
+ KSharedPtr<Variable> variable(const QString& name) const;
+
+ /**
+ * @return the map of variables this @a Action provides.
+ */
+ Variable::Map variables() const;
+
+ /**
+ * @return a list of variablenames this @a Action provides.s
+ */
+ QStringList variableNames() const;
+
+ /**
+ * Append the @a Variable @p variable to list of variables
+ * this @a Action provides.
+ */
+ void setVariable(KSharedPtr<Variable> variable);
+
+ /**
+ * Set the variable.
+ *
+ * @param name The name the variable should have.
+ * @param text The i18n-caption used for display.
+ * @param variant The QVariant value.
+ */
+ void setVariable(const QString& name, const QString& text, const QVariant& variant);
+
+ /**
+ * Remove the variable defined with @p name . If there exists
+ * no such variable, nothing is done.
+ */
+ void removeVariable(const QString& name);
+
+ /**
+ * This function is called, when the @a KoMacro::Variable
+ * with name @p name used within the @a KoMacro::MacroItem
+ * @p macroitem got changed.
+ *
+ * @param macroitem The @a KoMacro::MacroItem instance where
+ * the variable defined with @p name is located in.
+ * @param name The name the @a KoMacro::Variable has.
+ * @return true if the update was successfully else false
+ * is returned.
+ */
+ virtual bool notifyUpdated(const KSharedPtr<MacroItem> &macroitem, const QString& name) {
+ Q_UNUSED(macroitem);
+ Q_UNUSED(name);
+ return true; // The default implementation does nothing.
+ }
+
+ public slots:
+
+ /**
+ * Called if the @a Action should be executed within the
+ * defined @p context .
+ */
+ virtual void activate(KSharedPtr<Context> context) = 0;
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/context.cpp b/kexi/plugins/macros/lib/context.cpp
new file mode 100644
index 00000000..135c10c9
--- /dev/null
+++ b/kexi/plugins/macros/lib/context.cpp
@@ -0,0 +1,261 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "context.h"
+#include "action.h"
+#include "macro.h"
+#include "macroitem.h"
+#include "exception.h"
+
+//#include <qtimer.h>
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class Context::Private
+ {
+ public:
+
+ /**
+ * The @a Macro instance that owns this @a Context .
+ */
+ KSharedPtr<Macro> macro;
+
+ /**
+ * List of @a Action instances that are children of the
+ * macro.
+ */
+ QValueList<KSharedPtr<MacroItem > > items;
+
+ /**
+ * The currently selected @a MacroItem or NULL if there
+ * is now @a MacroItem selected yet.
+ */
+ KSharedPtr<MacroItem> macroitem;
+
+ /**
+ * Map of all @a Variable instance that are defined within
+ * this context.
+ */
+ QMap<QString, KSharedPtr<Variable > > variables;
+
+ /**
+ * The @a Exception instance thrown at the last @a activate()
+ * call or NULL if there was no exception thrown yet.
+ */
+ Exception* exception;
+
+ /// Constructor.
+ explicit Private(KSharedPtr<Macro> m)
+ : macro(m) // remember the macro
+ , items(m->items()) // set d-pointer children to macro children
+ , exception(0) // no exception yet.
+ {
+ }
+
+ /// Destructor.
+ ~Private()
+ {
+ delete exception;
+ }
+ };
+
+}
+//Constructor with initialization of our Private.object (d-pointer)
+Context::Context(KSharedPtr<Macro> macro)
+ : QObject()
+ , d( new Private(macro) ) // create the private d-pointer instance.
+{
+}
+
+//Destructor.
+Context::~Context()
+{
+ delete d;
+}
+
+//return if we have (d-pointer) variables
+bool Context::hasVariable(const QString& name) const
+{
+ //Use QMap?s contains to check if a variable with name exists
+ return d->variables.contains(name);
+}
+
+//return variable with name or throw an exception if none is found in variables
+KSharedPtr<Variable> Context::variable(const QString& name) const
+{
+ //Use QMap?s contains to check if a variable with name exists in context
+ if (d->variables.contains(name)) {
+ //return it
+ return d->variables[name];
+ }
+ //if there is a macroitem try to get variable from it
+ if(d->macroitem.data()) {
+ KSharedPtr<Variable> v = d->macroitem->variable(name, true);
+ if(v.data()) {
+ return v;
+ }
+ }
+ //none found throw exception
+ throw Exception(QString("Variable name='%1' does not exist.").arg(name));
+}
+
+//return a map of our (d-pointer) variables
+Variable::Map Context::variables() const
+{
+ return d->variables;
+}
+
+//set a variable
+void Context::setVariable(const QString& name, KSharedPtr<Variable> variable)
+{
+ //debuging infos
+ kdDebug() << QString("KoMacro::Context::setVariable name='%1' variable='%2'").arg(name).arg(variable->toString()) << endl;
+ //Use QMap?s replace to set/replace the variable named name
+ d->variables.replace(name, variable);
+}
+
+//return the associated Macro
+KSharedPtr<Macro> Context::macro() const
+{
+ return d->macro;
+}
+
+//return the currently selected MacroItem
+KSharedPtr<MacroItem> Context::macroItem() const
+{
+ return d->macroitem;
+}
+
+//return if this context had an exception
+bool Context::hadException() const
+{
+ return d->exception != 0;
+}
+
+//return the (d-pointer) exception
+Exception* Context::exception() const
+{
+ return d->exception;
+}
+
+//try to activate all action?s in this context
+void Context::activate(QValueList<KSharedPtr<MacroItem > >::ConstIterator it)
+{
+ //debuging infos
+ kdDebug() << "Context::activate()" << endl;
+ //Q_ASSIGN(d->macro);
+
+ //set end to constEnd
+ QValueList<KSharedPtr<MacroItem > >::ConstIterator end(d->items.constEnd());
+ //loop through actions
+ for(;it != end; ++it) {
+ // fetch the MacroItem we are currently pointing to.
+ d->macroitem = KSharedPtr<MacroItem>(*it);
+ //skip empty macroitems
+ if(! d->macroitem.data()) {
+ kdDebug() << "Context::activate() Skipping empty MacroItem" << endl;
+ continue;
+ }
+
+ // fetch the Action, the MacroItem points to.
+ KSharedPtr<Action> action = d->macroitem->action();
+ //skip macroitems without an action
+ if(! action.data()) {
+ kdDebug() << "Context::activate() Skipping MacroItem with no action" << endl;
+ continue;
+ }
+
+ try {
+ // activate the action
+ action->activate(this);
+ }
+ //catch exceptions
+ catch(Exception& e) {
+ //create a new exception from caugth one and set internal exception
+ d->exception = new Exception(e);
+ //add new tracemessages
+ //the macro name
+ d->exception->addTraceMessage( QString("macro=%1").arg(d->macro->name()) );
+ //the action name
+ d->exception->addTraceMessage( QString("action=%1").arg(action->name()) );
+ //and all variables wich belong to the action/macro
+ QStringList variables = action->variableNames();
+ for(QStringList::Iterator vit = variables.begin(); vit != variables.end(); ++vit) {
+ KSharedPtr<Variable> v = d->macroitem->variable(*vit, true);
+ d->exception->addTraceMessage( QString("%1=%2").arg(*vit).arg(v->toString()) );
+ }
+ return; // abort execution
+ }
+ }
+
+ // The run is done. So, let's remove the currently selected item to
+ // outline, that we did the job and there stays no dangling item.
+ d->macroitem = KSharedPtr<MacroItem>(0);
+}
+
+//try to activated an context
+void Context::activate(KSharedPtr<Context> context)
+{
+ //setup context
+ delete d->exception; d->exception = 0;
+
+ if(context->hadException()) {
+ // if the context in which this context should run in already had an exception,
+ // we adopt this exception and abort the execution.
+ d->exception = new Exception( *context->exception() );
+ return;
+ }
+
+ // Merge the passed context into this context
+ Variable::Map variables = context->variables();
+ //copy variables
+ Variable::Map::ConstIterator it, end( variables.constEnd() );
+ for( it = variables.constBegin(); it != end; ++it)
+ setVariable(it.key(), it.data());
+
+ //activate copied context.
+ activate(d->items.constBegin());
+}
+
+//try to continue activation of a context
+void Context::activateNext()
+{
+ //setup/clear context,
+ //allows us to continue activation even when an exception happend before
+ delete d->exception; d->exception = 0;
+
+ if(! d->macroitem) { // if no MacroItem is defined, we don't need to try to continue execution
+ return;
+ }
+
+ //find the macroitem from which to continue
+ QValueList<KSharedPtr<MacroItem > >::ConstIterator it = d->items.find(d->macroitem);
+ if (it != d->items.constEnd()) {
+ activate(++it); // try to continue the execution.
+ }
+}
+
+#include "context.moc"
diff --git a/kexi/plugins/macros/lib/context.h b/kexi/plugins/macros/lib/context.h
new file mode 100644
index 00000000..dd467dad
--- /dev/null
+++ b/kexi/plugins/macros/lib/context.h
@@ -0,0 +1,141 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_CONTEXT_H
+#define KOMACRO_CONTEXT_H
+
+#include <qobject.h>
+#include <ksharedptr.h>
+
+#include "variable.h"
+
+namespace KoMacro {
+
+ // Forward declaration.
+ class Macro;
+ class MacroItem;
+ class Action;
+ class Exception;
+
+ /**
+ * The context of an execution. If a @a Macro got executed it creates
+ * an instance of this class and passes it around all it's children
+ * as local execution context.
+ */
+ class KOMACRO_EXPORT Context
+ : public QObject
+ , public KShared
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ *
+ * @param macro The @a Macro this @a Context belongs to.
+ */
+ explicit Context(KSharedPtr<Macro> macro);
+
+ /**
+ * Destructor.
+ */
+ ~Context();
+
+ /**
+ * @return true if there exists a variable with name @p name
+ * else false got returned.
+ */
+ bool hasVariable(const QString& name) const;
+
+ /**
+ * @return the @a Variable defined with name @p name or
+ * NULL if there exists no such variable.
+ */
+ KSharedPtr<Variable> variable(const QString& name) const;
+
+ /**
+ * @return a map of all @a Variable instance that are defined
+ * within this context.
+ */
+ Variable::Map variables() const;
+
+ /**
+ * Set the variable @p variable defined with name @p name . If
+ * there exists already a variable with that name replace it.
+ */
+ void setVariable(const QString& name, KSharedPtr<Variable> variable);
+
+ /**
+ * @return the associated macro
+ */
+ KSharedPtr<Macro> macro() const;
+
+ /**
+ * @return the currently selected @a MacroItem instance
+ * or NULL if there is no @a MacroItem selected yet.
+ */
+ KSharedPtr<MacroItem> macroItem() const;
+
+ /**
+ * @return true if the last @a activate() stopped with an
+ * exception else false is returned.
+ */
+ bool hadException() const;
+
+ /**
+ * @return the @a Exception instance that was thrown on
+ * the last call of @a activate() . If there was no
+ * exception NULL is returned.
+ */
+ Exception* exception() const;
+
+ private slots:
+
+ /**
+ * A @a Context does take care of an execution-chain which
+ * should be activated one after another. The @a Context
+ * remembers what @a Action should be executed next and
+ * calling this slot just activates those @a Action .
+ */
+ virtual void activate(QValueList<KSharedPtr <MacroItem> >::ConstIterator it);
+
+ public slots:
+
+ /**
+ * This slot extends the slot above with the passed
+ * @a Context @p context which will be used as
+ * parent context for this context.
+ */
+ virtual void activate(KSharedPtr<Context> context);
+
+ /**
+ * This slot continues execution.
+ */
+ virtual void activateNext();
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/exception.cpp b/kexi/plugins/macros/lib/exception.cpp
new file mode 100644
index 00000000..7cfc7d71
--- /dev/null
+++ b/kexi/plugins/macros/lib/exception.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "exception.h"
+
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class Exception::Private
+ {
+ public:
+
+ /// A describing errormessage.
+ const QString errormessage;
+
+ /// A more detailed list of tracemessages.
+ QString tracemessages;
+
+ /**
+ * Constructor.
+ */
+ Private(const QString& errormessage)
+ : errormessage(errormessage)
+ {
+ }
+
+ };
+
+}
+
+//constructor
+Exception::Exception(const QString& errormessage)
+ : d( new Private(errormessage) ) // create the private d-pointer instance.
+{
+ //debuging infos
+ kdDebug() << QString("Exception errormessage=\"%1\"").arg(errormessage) << endl;
+}
+
+//copy constructor
+Exception::Exception (const Exception& e)
+ : d( new Private( e.errorMessage() ) )
+{
+ d->tracemessages = e.traceMessages();
+}
+
+//deconstructor
+Exception::~Exception()
+{
+ delete d;
+}
+
+//get d-pointer errormessage
+const QString Exception::errorMessage() const
+{
+ return d->errormessage;
+}
+
+//get d-pointer tracemessages
+const QString Exception::traceMessages() const
+{
+ return d->tracemessages;
+}
+
+//add a Qstring to d-pointer tracemessages
+void Exception::addTraceMessage(const QString& tracemessage)
+{
+ //no tracemessages till now
+ if(d->tracemessages.isEmpty())
+ d->tracemessages = tracemessage;
+ //append to existing ones
+ else
+ d->tracemessages += "\n" + tracemessage;
+}
+
diff --git a/kexi/plugins/macros/lib/exception.h b/kexi/plugins/macros/lib/exception.h
new file mode 100644
index 00000000..73504de0
--- /dev/null
+++ b/kexi/plugins/macros/lib/exception.h
@@ -0,0 +1,84 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_EXCEPTION_H
+#define KOMACRO_EXCEPTION_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include "komacro_export.h"
+
+namespace KoMacro {
+
+ /**
+ * Base Exception class. All exceptions we like to use within KoMacro
+ * need to inheritate from this exception.
+ */
+ class KOMACRO_EXPORT Exception
+ {
+ public:
+
+ /**
+ * Constructor.
+ *
+ * @param errormessage A describing errormessage why the
+ * exception got thrown.
+ */
+ explicit Exception(const QString& errormessage);
+
+ /**
+ * Copy-constructor.
+ */
+ Exception(const Exception&);
+
+ /**
+ * Destructor.
+ */
+ virtual ~Exception();
+
+ /**
+ * @return a describing errormessage.
+ */
+ const QString errorMessage() const;
+
+ /**
+ * @return a stringlist of traces. This are normaly just
+ * simple strings to show the way the exception was gone
+ * from bottom-up where the error was thrown till where
+ * we finally catched the error to display it to the
+ * user.
+ */
+ const QString traceMessages() const;
+
+ /**
+ * Add the message @p tracemessage to the list of traces.
+ */
+ void addTraceMessage(const QString& tracemessage);
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/komacro_export.h b/kexi/plugins/macros/lib/komacro_export.h
new file mode 100644
index 00000000..cc4b41a8
--- /dev/null
+++ b/kexi/plugins/macros/lib/komacro_export.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_EXPORT_H_
+#define KOMACRO_EXPORT_H_
+
+#ifdef __cplusplus
+# include <kdeversion.h> /* this will also include <kdelibs_export.h>, if available */
+#endif
+
+/* KDE_EXPORT will be defined multiple times without this on kdelibs 3.3 (tested on 3.3.1) */
+#include <kdemacros.h>
+
+/* workaround for KDElibs < 3.2 on !win32 */
+#ifndef KDE_EXPORT
+# define KDE_EXPORT
+#endif
+
+#ifndef KOMACRO_EXPORT
+# define KOMACRO_EXPORT KDE_EXPORT
+#endif
+
+#endif
diff --git a/kexi/plugins/macros/lib/macro.cpp b/kexi/plugins/macros/lib/macro.cpp
new file mode 100644
index 00000000..688cc7b0
--- /dev/null
+++ b/kexi/plugins/macros/lib/macro.cpp
@@ -0,0 +1,126 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "macro.h"
+#include "macroitem.h"
+#include "manager.h"
+#include "context.h"
+#include "variable.h"
+
+#include <qdom.h>
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class Macro::Private
+ {
+ public:
+
+ /**
+ * A list of @a MacroItem instances.
+ */
+ QValueList<KSharedPtr<MacroItem > > itemlist;
+
+ /**
+ * The name the @a Macro has.
+ */
+ QString name;
+
+ };
+
+}
+
+//constructor, initalize internal (d-pointer) name
+Macro::Macro(const QString& name)
+ : QObject()
+ , KShared()
+ , XMLHandler(this)
+ , d( new Private() ) // create the private d-pointer instance.
+{
+ d->name = name;
+}
+
+//destructor
+Macro::~Macro()
+{
+ // destroy the private d-pointer instance.
+ delete d;
+}
+
+//get internal (d-pointer) name
+const QString Macro::name() const
+{
+ return d->name;
+}
+
+//set internal (d-pointer) name
+void Macro::setName(const QString& name)
+{
+ d->name = name;
+}
+
+//get an "extended" name
+const QString Macro::toString() const
+{
+ return QString("Macro:%1").arg(name());
+}
+
+//get (d-pointer) itemlist
+QValueList<KSharedPtr<MacroItem > >& Macro::items() const
+{
+ return d->itemlist;
+}
+
+//add a macroitem to internal (d-pointer) itemlist
+void Macro::addItem(KSharedPtr<MacroItem> item)
+{
+ d->itemlist.append(item);
+}
+//clear internal (d-pointer) itemlist
+void Macro::clearItems()
+{
+ d->itemlist.clear();
+}
+
+//run our macro
+KSharedPtr<Context> Macro::execute(QObject* sender)
+{
+ kdDebug() << "Macro::execute(KSharedPtr<Context>)" << endl;
+
+ //create context in which macro can/should run
+ KSharedPtr<Context> c = KSharedPtr<Context>( new Context(this) );
+ if(sender) {
+ // set the sender-variable if we got a sender QObject.
+ c->setVariable("[sender]", KSharedPtr<Variable>( new Variable(sender) ));
+ }
+ //connect(context, SIGNAL(activated()), this, SIGNAL(activated()));
+
+ //call activate in the context of the macro
+ c->activate( c );
+
+ return c;
+}
+
+#include "macro.moc"
diff --git a/kexi/plugins/macros/lib/macro.h b/kexi/plugins/macros/lib/macro.h
new file mode 100644
index 00000000..da38e05b
--- /dev/null
+++ b/kexi/plugins/macros/lib/macro.h
@@ -0,0 +1,130 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_MACRO_H
+#define KOMACRO_MACRO_H
+
+#include <qobject.h>
+#include <ksharedptr.h>
+
+#include "action.h"
+#include "xmlhandler.h"
+
+namespace KoMacro {
+
+ // Forward declarations.
+ class Manager;
+ class MacroItem;
+ class Context;
+
+ /**
+ * The Macro class implements all the action-handling. Internaly the
+ * Macro provides a collection of @a MacroItem instances which each
+ * of them points to an @a Action instance.
+ */
+ class KOMACRO_EXPORT Macro
+ : public QObject // Qt functionality like signals and slots
+ , public KShared // shared reference-counting
+ , public XMLHandler // to (un-)serialize from/to XML
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * A QMap of @a Macro instances accessible by there unique name. Each
+ * class should use this typemap rather then the QMap direct. That
+ * way we are more flexible on future changes.
+ */
+ typedef QMap<QString, KSharedPtr<Macro > > Map;
+
+ /**
+ * Constructor.
+ *
+ * @param name The internal name this @a Macro has. This
+ * name will be used as unique identifier.
+ */
+ explicit Macro(const QString& name);
+
+ /**
+ * Destructor.
+ */
+ virtual ~Macro();
+
+ /**
+ * @return the name this @a Macro instance has.
+ */
+ const QString name() const;
+
+ /**
+ * Set the @p name this @a Macro instance has.
+ */
+ void setName(const QString& name);
+
+ /**
+ * @return a string-representation of the macro.
+ */
+ virtual const QString toString() const;
+
+ /**
+ * @return a list of @a MacroItem instances which
+ * are children of this @a Macro .
+ */
+ QValueList< KSharedPtr<MacroItem> >& items() const;
+
+ /**
+ * Add the @a MacroItem @p item to the list of items
+ * this @a Macro has.
+ */
+ void addItem(KSharedPtr<MacroItem> item);
+
+ /**
+ * Removes all @a MacroItem instances this @a Macro has.
+ */
+ void clearItems();
+
+ /**
+ * Connect the Qt signal @p signal of the QObject @p sender
+ * with this @a Macro . If the signal got emitted this
+ * @a Macro instance will be activated and the in the
+ * signal passed arguments are transfered into the
+ * activation @a Context .
+ */
+ //void connectSignal(const QObject* sender, const char* signal);
+
+ public slots:
+
+ /**
+ * Called if the @a Macro should be executed.
+ *
+ * @param context The @a Context this @a Macro should
+ * be executed in.
+ */
+ virtual KSharedPtr<Context> execute(QObject* sender);
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/macroitem.cpp b/kexi/plugins/macros/lib/macroitem.cpp
new file mode 100644
index 00000000..4027f2cc
--- /dev/null
+++ b/kexi/plugins/macros/lib/macroitem.cpp
@@ -0,0 +1,217 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "macroitem.h"
+
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class MacroItem::Private
+ {
+ public:
+ /**
+ * The @a Action this @a MacroItem has.
+ */
+ KSharedPtr<Action> action;
+
+ /**
+ * The comment this @a MacroItem has.
+ */
+ QString comment;
+
+ /**
+ * The @a QMap of @a Variable this @a MacroItem has.
+ */
+ Variable::Map variables;
+
+ /**
+ * define a @a QVariant -cast as inline for better performance
+ * @return the casted @a QVariant by passing a @param variant and its
+ * expected QVariant::Type @param type.
+ */
+ inline const QVariant cast(const QVariant& variant, QVariant::Type type) const
+ {
+ // If ok is true the QVariant v holds our new and to the correct type
+ // casted variant value. If ok is false the as argument passed variant
+ // QVariant contains the (maybe uncasted string to prevent data-loosing
+ // what would happen if we e.g. would expect an integer and cast it to
+ // an incompatible non-int string) value.
+ bool ok = false;
+ QVariant v;
+
+ // Try to cast the passed variant to the expected variant-type.
+ switch(type) {
+ case QVariant::Bool: {
+ const QString s = variant.toString();
+ ok = (s == "true" || s == "false" || s == "0" || s == "1" || s == "-1");
+ v = QVariant( variant.toBool(), 0 );
+ } break;
+ case QVariant::Int: {
+ v = variant.toInt(&ok);
+ // Check if the cast is correct.
+ Q_ASSERT(!ok || v.toString() == variant.toString());
+ } break;
+ case QVariant::UInt: {
+ v = variant.toUInt(&ok);
+ Q_ASSERT(!ok || v.toString() == variant.toString());
+ } break;
+ case QVariant::LongLong: {
+ v = variant.toLongLong(&ok);
+ Q_ASSERT(!ok || v.toString() == variant.toString());
+ } break;
+ case QVariant::ULongLong: {
+ v = variant.toULongLong(&ok);
+ Q_ASSERT(!ok || v.toString() == variant.toString());
+ } break;
+ case QVariant::Double: {
+ v = variant.toDouble(&ok);
+ Q_ASSERT(!ok || v.toString() == variant.toString());
+ } break;
+ case QVariant::String: {
+ ok = true; // cast will always be successfully
+ v = variant.toString();
+ } break;
+ default: {
+ // If we got another type we try to let Qt handle it...
+ ok = v.cast(type);
+ kdWarning()<<"MacroItem::Private::cast() Unhandled ok="<<ok<<" type="<<type<<" value="<<v<<endl;
+ } break;
+ }
+
+ return ok ? v : variant;
+ }
+
+ };
+
+}
+
+MacroItem::MacroItem()
+ : KShared()
+ , d( new Private() )
+{
+}
+
+MacroItem::~MacroItem()
+{
+ delete d;
+}
+
+QString MacroItem::comment() const
+{
+ return d->comment;
+}
+
+void MacroItem::setComment(const QString& comment)
+{
+ d->comment = comment;
+}
+
+KSharedPtr<Action> MacroItem::action() const
+{
+ return d->action;
+}
+
+void MacroItem::setAction(KSharedPtr<Action> action)
+{
+ d->action = action;
+}
+
+QVariant MacroItem::variant(const QString& name, bool checkaction) const
+{
+ KSharedPtr<Variable> v = variable(name, checkaction);
+ return v.data() ? v->variant() : QVariant();
+}
+
+KSharedPtr<Variable> MacroItem::variable(const QString& name, bool checkaction) const
+{
+ if(d->variables.contains(name))
+ return d->variables[name];
+ if(checkaction && d->action.data())
+ return d->action->variable(name);
+ return KSharedPtr<Variable>(0);
+}
+
+Variable::Map MacroItem::variables() const
+{
+ return d->variables;
+}
+
+bool MacroItem::setVariant(const QString& name, const QVariant& variant)
+{
+ // Let's look if there is an action defined for the variable. If that's
+ // the case, we try to use that action to preserve the type of the variant.
+ KSharedPtr<Variable> actionvariable = d->action ? d->action->variable(name) : KSharedPtr<Variable>(0);
+
+ // If we know the expected type, we try to cast the variant to the expected
+ // type else the variant stays untouched (so, it will stay a string).
+ const QVariant v = actionvariable.data()
+ ? d->cast(variant, actionvariable->variant().type()) // try to cast the variant
+ : variant; // don't cast anything, just leave the string-type...
+
+ // Now let's try to determinate the variable which should be changed.
+ KSharedPtr<Variable> variable = d->variables[name];
+ if(! variable.data()) {
+ // if there exists no such variable yet, create one.
+ kdDebug() << "MacroItem::setVariable() Creating new variable name=" << name << endl;
+
+ variable = KSharedPtr<Variable>( new Variable() );
+ variable->setName(name);
+ d->variables.replace(name, variable);
+ }
+
+ // Remember the previous value for the case we like to restore it.
+ const QVariant oldvar = variable->variant();
+
+ // Set the variable.
+ variable->setVariant(v);
+
+ // Now we inform the referenced action that a variable changed. If
+ // notifyUpdated() returns false, the action rejects the new variable
+ // and we need to restore the previous value.
+ if(d->action && ! d->action->notifyUpdated(this, name)) {
+ kdWarning() << "MacroItem::setVariable() Notify failed for variable name=" << name << endl;
+ variable->setVariant(oldvar);
+ return false; // the action rejected the changed variable whyever...
+ }
+
+ // Job done successfully. The variable is changed to the new value.
+ return true;
+}
+
+KSharedPtr<Variable> MacroItem::addVariable(const QString& name, const QVariant& variant)
+{
+ Q_ASSERT(! d->variables.contains(name) );
+ // Create a new Variable.
+ KSharedPtr<Variable> variable = KSharedPtr<Variable>( new Variable() );
+ variable->setName(name);
+
+ // Put it into the Variable-map.
+ d->variables.replace(name, variable);
+
+ // Set the variant of the Variable.
+ this->setVariant(name, variant);
+ return variable;
+}
diff --git a/kexi/plugins/macros/lib/macroitem.h b/kexi/plugins/macros/lib/macroitem.h
new file mode 100644
index 00000000..8f3e1502
--- /dev/null
+++ b/kexi/plugins/macros/lib/macroitem.h
@@ -0,0 +1,142 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_MACROITEM_H
+#define KOMACRO_MACROITEM_H
+
+#include <qobject.h>
+
+#include <ksharedptr.h>
+
+// Forward declarations.
+class QDomElement;
+
+#include "action.h"
+#include "context.h"
+
+namespace KoMacro {
+
+ // Forward-declarations.
+ //class Action;
+
+ /**
+ * The MacroItem class is an item in a @a Macro and represents one
+ * single execution step. Each MacroItem points to 0..1 @a Action
+ * instances which implement the execution. So, the MacroItem provides
+ * a simple state-pattern on the one hand (depending on the for this
+ * MacroItem choosen @a Action implementation) and holds the by the
+ * user defined modifications like e.g. the comment on the other hand.
+ */
+ class KOMACRO_EXPORT MacroItem : public KShared
+ {
+
+ public:
+
+ /**
+ * A list of \a MacroItem instances.
+ */
+ typedef QValueList<KSharedPtr<MacroItem > > List;
+
+ /**
+ * Constructor.
+ */
+ explicit MacroItem();
+
+ /**
+ * Destructor.
+ */
+ ~MacroItem();
+
+ /**
+ * @return the comment defined by the user for
+ * this @a MacroItem .
+ */
+ QString comment() const;
+
+ /**
+ * Set the comment @param comment defined by the user for this
+ * @a MacroItem .
+ */
+ void setComment(const QString& comment);
+
+ /**
+ * @return the @a Action this @a MacroItem points
+ * to. This method will return NULL if there is
+ * no @a Action defined yet else the returned
+ * @a Action will be used to implement the execution.
+ */
+ KSharedPtr<Action> action() const;
+
+ /**
+ * Set the @a Action @param action this @a MacroItem points to.
+ */
+ void setAction(KSharedPtr<Action> action);
+
+ /**
+ * @return @a Variant from the @a Variable identified with
+ * the name @param name . If this @a MacroItem doesn't
+ * have a @a Variable with that name NULL is
+ * returned.
+ * If the boolean value @param checkaction is true, we
+ * also look if our @a Action may know about
+ * such a @param name in the case this @a MacroItem
+ * doesn't have such a name.
+ */
+ QVariant variant(const QString& name, bool checkaction = false) const;
+
+ /**
+ * @return the @a Variable instance identified with
+ * the name @param name . If this @a MacroItem doesn't
+ * have a @a Variable with that name NULL is
+ * returned.
+ * If the boolean value @param checkaction is true, we
+ * also look if our @a Action may know about
+ * such a @param name in the case this @a MacroItem
+ * doesn't have such a name.
+ */
+ KSharedPtr<Variable> variable(const QString& name, bool checkaction = false) const;
+
+ /**
+ * @return a map of @a Variable instances.
+ */
+ QMap<QString, KSharedPtr<Variable> > variables() const;
+
+ /**
+ * Set the @a QVariant @param variant as variable with the variablename
+ * @param name .
+ * @return a bool for successfull setting.
+ */
+ bool setVariant(const QString& name, const QVariant& variant);
+
+ /**
+ * Add a new variable with the vaiablename @param name and the given
+ * @a QVariant @param variant to our @a MacroItem instance.
+ */
+ KSharedPtr<Variable> addVariable(const QString& name, const QVariant& variant);
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/manager.cpp b/kexi/plugins/macros/lib/manager.cpp
new file mode 100644
index 00000000..77ad98b1
--- /dev/null
+++ b/kexi/plugins/macros/lib/manager.cpp
@@ -0,0 +1,170 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "manager.h"
+#include "action.h"
+#include "function.h"
+#include "macro.h"
+#include "exception.h"
+
+#include <qobject.h>
+#include <qwidget.h>
+#include <qdom.h>
+#include <kxmlguibuilder.h>
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class Manager::Private
+ {
+ public:
+ KXMLGUIClient* const xmlguiclient;
+ QMap<QString, KSharedPtr<Macro > > macros;
+
+ QStringList actionnames;
+ QMap<QString, KSharedPtr<Action> > actions;
+
+ QMap<QString, QGuardedPtr<QObject> > objects;
+
+ Private(KXMLGUIClient* const xmlguiclient)
+ : xmlguiclient(xmlguiclient)
+ {
+ }
+ };
+
+ /// Pointer to our static singleton.
+ static ::KoMacro::Manager* _self = 0;
+
+ /// Automatically deletes our singleton on termination.
+ static KStaticDeleter< ::KoMacro::Manager > _manager;
+
+}
+
+void Manager::init(KXMLGUIClient* xmlguiclient)
+{
+ if(! _self) {
+ ::KoMacro::Manager* manager = new ::KoMacro::Manager(xmlguiclient);
+ _manager.setObject(_self, manager);
+ }
+ else {
+ throw Exception("Already initialized.");
+ }
+}
+
+Manager* Manager::self()
+{
+ //Q_ASSERT(_self);
+ return _self;
+}
+
+Manager::Manager(KXMLGUIClient* const xmlguiclient)
+ : d( new Private(xmlguiclient) ) // create the private d-pointer instance.
+{
+ kdDebug() << "Manager::Manager() Ctor" << endl;
+ QObject* obj = dynamic_cast<QObject*>(xmlguiclient);
+ if(obj) {
+ d->objects.replace(obj->name(), obj);
+ }
+
+ //TESTCASE
+ d->objects.replace("TestCase", new QWidget());
+}
+
+Manager::~Manager()
+{
+ // destroy the private d-pointer instance.
+ delete d;
+}
+
+KXMLGUIClient* Manager::guiClient() const
+{
+ return d->xmlguiclient;
+}
+
+bool Manager::hasMacro(const QString& macroname)
+{
+ return d->macros.contains(macroname);
+}
+
+KSharedPtr<Macro> Manager::getMacro(const QString& macroname)
+{
+ return d->macros[macroname];
+}
+
+void Manager::addMacro(const QString& macroname, KSharedPtr<Macro> macro)
+{
+ d->macros.replace(macroname, macro);
+}
+
+void Manager::removeMacro(const QString& macroname)
+{
+ d->macros.remove(macroname);
+}
+
+KSharedPtr<Macro> Manager::createMacro(const QString& macroname)
+{
+ KSharedPtr<Macro> macro = KSharedPtr<Macro>( new Macro(macroname) );
+ return macro;
+}
+
+KSharedPtr<Action> Manager::action(const QString& name) const
+{
+ return d->actions[name];
+}
+
+Action::Map Manager::actions() const
+{
+ return d->actions;
+}
+
+QStringList Manager::actionNames() const
+{
+ return d->actionnames;
+}
+
+void Manager::publishAction(KSharedPtr<Action> action)
+{
+ const QString name = action->name();
+ if(! d->actions.contains(name)) {
+ d->actionnames.append(name);
+ }
+ d->actions.replace(name, action);
+}
+
+void Manager::publishObject(const QString& name, QObject* object)
+{
+ Q_ASSERT(! d->objects.contains(name));
+ d->objects.replace(name, object);
+}
+
+QGuardedPtr<QObject> Manager::object(const QString& name) const
+{
+ return d->objects[name];
+}
+
+QMap<QString, QGuardedPtr<QObject> > Manager::objects() const
+{
+ return d->objects;
+}
diff --git a/kexi/plugins/macros/lib/manager.h b/kexi/plugins/macros/lib/manager.h
new file mode 100644
index 00000000..964f9d7c
--- /dev/null
+++ b/kexi/plugins/macros/lib/manager.h
@@ -0,0 +1,219 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_MANAGER_H
+#define KOMACRO_MANAGER_H
+
+#include <qmap.h>
+#include <qguardedptr.h>
+#include <ksharedptr.h>
+#include <kxmlguiclient.h>
+#include <kstaticdeleter.h>
+
+#include "komacro_export.h"
+
+class QObject;
+class QDomElement;
+
+namespace KoMacro {
+
+ // Forward declarations.
+ class Action;
+ class Macro;
+
+ /**
+ * The Manager class acts as window-wide manager for macros.
+ *
+ * Example how KoMacro could be used.
+ * @code
+ * // We have a class that inheritates from QObject and
+ * // implements some public signals and slots that will
+ * // be accessible by Macros once a class-instance
+ * // got published.
+ * class PublishedObject : public QObject {};
+ *
+ * // Somewhere we have our KMainWindow.
+ * KMainWindow* mainwindow = new KMainWindow();
+ *
+ * // Create a new KoMacro::Manager instance to access the
+ * // Macro-framework.
+ * KoMacro::Manager* manager = new KoMacro::Manager( mainwindow );
+ *
+ * // Now we like to publish a QObject
+ * PublishedObject* publishedobject = new PublishedObject();
+ * manager->publishObject(publishedobject);
+ *
+ * // ... here we are able to use manager->createAction() to
+ * // create Action instances on the fly and work with them.
+ *
+ * // Finally free the publishedobject instance we created. We
+ * // need to free it manualy cause PublishedObject doesn't
+ * // got a QObject parent as argument.
+ * delete publishedobject;
+ *
+ * // Finally free the manager-instance. It's always needed
+ * // to free the instance by yourself!
+ * delete manager;
+ * @endcode
+ */
+ class KOMACRO_EXPORT Manager
+ {
+ friend class KStaticDeleter< ::KoMacro::Manager >;
+ private:
+
+ /**
+ * Constructor.
+ *
+ * @param xmlguiclient The KXMLGUIClient instance this
+ * @a Manager is associated with.
+ */
+ explicit Manager(KXMLGUIClient* const xmlguiclient);
+
+ /**
+ * Destructor.
+ */
+ virtual ~Manager();
+
+ public:
+
+ /**
+ * Initialize this \a Manager singleton. This function
+ * needs to be called exactly once to initialize the
+ * \a Manager singleton before \a self() got used.
+ */
+ static void init(KXMLGUIClient* xmlguiclient);
+
+ /**
+ * @return a pointer to a Manager singleton-instance. The
+ * static method \a init() needs to be called exactly once
+ * before calling this method else we may return NULL .
+ */
+ static Manager* self();
+
+ /**
+ * @return the KXMLGUIClient instance this @a Manager is
+ * associated with.
+ */
+ KXMLGUIClient* guiClient() const;
+
+ /**
+ * \return true if we carry a \a Macro with the
+ * defined \p macroname .
+ */
+ bool hasMacro(const QString& macroname);
+
+ /**
+ * \return the \a Macro defined with \p macroname
+ * or NULL if we don't have such a \a Macro.
+ */
+ KSharedPtr<Macro> getMacro(const QString& macroname);
+
+ /**
+ * Add a new \a Macro to the list of known macros. If
+ * there exists already a \a Macro instance with the
+ * defined \p macroname then the already existing one
+ * will be replace.
+ *
+ * \param macroname The name the \a Macro will be
+ * accessible as.
+ * \param macro The \a Macro instance.
+ */
+ void addMacro(const QString& macroname, KSharedPtr<Macro> macro);
+
+ /**
+ * Remove the \a Macro defined with \p macroname . If
+ * we don't know about a \a Macro with that \p macroname
+ * nothing happens.
+ */
+ void removeMacro(const QString& macroname);
+
+ /**
+ * Factory function to create a new \a Macro instances.
+ * The returned new \a Macro instance will not be added
+ * to the list of known macros. Use \a addMacro if you
+ * like to attach the returned new \a Macro to this
+ * \a Manager instance.
+ */
+ KSharedPtr<Macro> createMacro(const QString& macroname);
+
+#if 0
+ /**
+ * Factory method to create @a Action instances from the
+ * defined @p element .
+ *
+ * @param element The serialized QDomElement that should
+ * be used to create the @a Action instance.
+ * @return A new @a Action instance or NULL if the
+ * defined @p element is not valid.
+ *
+ * @deprecated Moved to common XMLReader/XMLWriter classes. Use Macro::xmlHandler() !
+ */
+ KSharedPtr<Action> createAction(const QDomElement& element);
+#endif
+
+ /**
+ * @return the @a Action which was published under the
+ * name @p name or returns an empty @a KSharedPtr<Action> object
+ * if there was no such @a Action published.
+ */
+ KSharedPtr<Action> action(const QString& name) const;
+
+ /**
+ * @return a map of all published actions.
+ */
+ QMap<QString, KSharedPtr<Action> > actions() const;
+
+ /**
+ * @return a list of all published actions.
+ */
+ QStringList actionNames() const;
+
+ /**
+ * Publish the @a Action @p action . The published @a Action
+ * will be accessible via it's unique name.
+ */
+ void publishAction(KSharedPtr<Action> action);
+
+ /**
+ * Publish the passed QObject @p object. Those object will
+ * provide it's slots as callable functions.
+ */
+ void publishObject(const QString& name, QObject* object);
+
+ /**
+ * @return the publish QObject defined with name @p name
+ * or NULL if there exists no such object.
+ */
+ QGuardedPtr<QObject> object(const QString& name) const;
+
+ /**
+ * @return a map of the published QObject instances.
+ */
+ QMap<QString, QGuardedPtr<QObject> > objects() const;
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/metamethod.cpp b/kexi/plugins/macros/lib/metamethod.cpp
new file mode 100644
index 00000000..8aa4dc54
--- /dev/null
+++ b/kexi/plugins/macros/lib/metamethod.cpp
@@ -0,0 +1,344 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "metamethod.h"
+#include "metaobject.h"
+#include "metaparameter.h"
+#include "variable.h"
+#include "exception.h"
+
+#include <qobject.h>
+#include <qmetaobject.h>
+
+// to access the Qt3 QUObject API.
+#include <private/qucom_p.h>
+#include <private/qucomextra_p.h>
+
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class MetaMethod::Private
+ {
+ public:
+
+ /**
+ * The signature this @a MetaMethod has.
+ */
+ QString signature;
+
+ /**
+ * The signature tagname this @a MetaMethod has.
+ */
+ QString signaturetag;
+
+ /**
+ * The signature arguments this @a MetaMethod has.
+ */
+ QString signaturearguments;
+
+ /**
+ * Cached signature arguments parsed into a list
+ * of @a MetaParameter instances.
+ */
+ MetaParameter::List arguments;
+
+ /**
+ * The @a MetaObject this @a MetaMethod belongs to or is NULL
+ * if this @a MetaMethod doesn't belong to any @a MetaObject
+ * yet.
+ */
+ KSharedPtr<MetaObject> object;
+
+ /**
+ * The @a MetaMethod::Type this method provides access
+ * to.
+ */
+ MetaMethod::Type type;
+ };
+
+}
+
+MetaMethod::MetaMethod(const QString& signature, Type type, KSharedPtr<MetaObject> object)
+ : KShared()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+ d->signature = signature;
+ d->object = object;
+ d->type = type;
+
+ int startpos = d->signature.find("(");
+ int endpos = d->signature.findRev(")");
+ if(startpos < 0 || startpos > endpos) {
+ throw Exception(QString("Invalid signature \"%1\"").arg(d->signature));
+ }
+
+ d->signaturetag = d->signature.left(startpos).stripWhiteSpace();
+ if(d->signaturetag.isEmpty()) {
+ throw Exception(QString("Invalid tagname in signature \"%1\"").arg(d->signature));
+ }
+
+ d->signaturearguments = d->signature.mid(startpos + 1, endpos - startpos - 1).stripWhiteSpace();
+
+ do {
+ int commapos = d->signaturearguments.find(",");
+ int starttemplatepos = d->signaturearguments.find("<");
+ if(starttemplatepos >= 0 && (commapos < 0 || starttemplatepos < commapos)) {
+ int endtemplatepos = d->signaturearguments.find(">", starttemplatepos);
+ if(endtemplatepos <= 0) {
+ throw Exception(QString("No closing template-definiton in signature \"%1\"").arg(d->signature));
+ }
+ commapos = d->signaturearguments.find(",", endtemplatepos);
+ }
+
+ if(commapos > 0) {
+ QString s = d->signaturearguments.left(commapos).stripWhiteSpace();
+ if(! s.isEmpty()) {
+ d->arguments.append( new MetaParameter(s) );
+ }
+ d->signaturearguments = d->signaturearguments.right(d->signaturearguments.length() - commapos - 1);
+ }
+ else {
+ QString s = d->signaturearguments.stripWhiteSpace();
+ if(! s.isEmpty()) {
+ d->arguments.append( new MetaParameter(s) );
+ }
+ break;
+ }
+ } while(true);
+}
+
+MetaMethod::~MetaMethod()
+{
+ delete d;
+}
+
+KSharedPtr<MetaObject> const MetaMethod::object() const
+{
+ return d->object;
+}
+
+const QString MetaMethod::signature() const
+{
+ return d->signature;
+}
+
+const QString MetaMethod::signatureTag() const
+{
+ return d->signaturetag;
+}
+
+const QString MetaMethod::signatureArguments() const
+{
+ return d->signaturearguments;
+}
+
+MetaMethod::Type MetaMethod::type() const
+{
+ return d->type;
+}
+
+MetaParameter::List MetaMethod::arguments() const
+{
+ return d->arguments;
+}
+
+QUObject* MetaMethod::toQUObject(Variable::List arguments)
+{
+ uint argsize = d->arguments.size();
+
+ if(arguments.size() <= argsize) {
+ throw Exception(QString("To less arguments for slot with siganture \"%1\"").arg(d->signature));
+ }
+
+ // The first item in the QUObject-array is for the returnvalue
+ // while everything >=1 are the passed parameters.
+ QUObject* uo = new QUObject[ argsize + 1 ];
+
+ uo[0] = QUObject(); // empty placeholder for the returnvalue.
+
+ for(uint i = 0; i < argsize; i++) {
+ KSharedPtr<MetaParameter> metaargument = d->arguments[i];
+ KSharedPtr<Variable> variable = arguments[i + 1];
+
+ if ( !variable ) {
+ throw Exception(QString("Variable is undefined !"));
+ }
+
+ if(metaargument->type() != variable->type()) {
+ throw Exception(QString("Wrong variable type in method \"%1\". Expected \"%2\" but got \"%3\"").arg(d->signature).arg(metaargument->type()).arg(variable->type()));
+ }
+
+ switch(metaargument->type()) {
+
+ case Variable::TypeNone: {
+ kdDebug() << "Variable::TypeNone" << endl;
+ uo[i + 1] = QUObject();
+ } break;
+
+ case Variable::TypeVariant: {
+ kdDebug() << "Variable::TypeVariant" << endl;
+
+ const QVariant variant = variable->variant();
+ switch(metaargument->variantType()) {
+ case QVariant::String: {
+ const QString s = variant.toString();
+ static_QUType_QString.set( &(uo[i + 1]), s );
+ } break;
+ case QVariant::Int: {
+ const int j = variant.toInt();
+ static_QUType_int.set( &(uo[i + 1]), j );
+ } break;
+ case QVariant::Bool: {
+ const bool b = variant.toBool();
+ static_QUType_bool.set( &(uo[i + 1]), b );
+ } break;
+ case QVariant::Double: {
+ const double d = variant.toDouble();
+ static_QUType_double.set( &(uo[i + 1]), d );
+ } break;
+ case QVariant::Invalid: {
+ static_QUType_QVariant.set( &(uo[i + 1]), variant );
+ }
+
+ /*FIXME
+ static_QUType_charstar
+ static_QUType_ptr.get(uo); QObject *qobj = (QObject *)(ptr);
+ */
+
+ default: {
+ throw Exception(QString("Invalid parameter !!!!!!!!!!!!!!!!!!!!!!!"));
+ } break;
+ }
+ } break;
+
+ case Variable::TypeObject: {
+ kdDebug() << "Variable::TypeObject" << endl;
+
+ const QObject* obj = arguments[i + 1]->object();
+ if(! obj) { //FIXME: move check to MetaParameter?!
+ throw Exception(QString("No QObject !"));
+ }
+ static_QUType_ptr.set( &(uo[i + 1]), obj );
+ } break;
+
+ default: {
+ throw Exception(QString("Invalid variable type"));
+ } break;
+ }
+
+ }
+
+ return uo;
+}
+
+KSharedPtr<Variable> MetaMethod::toVariable(QUObject* uo)
+{
+ const QString desc( uo->type->desc() );
+
+ if(desc == "null") {
+ return new Variable();
+ }
+
+ if(desc == "QString") {
+ const QString s = static_QUType_QString.get(uo);
+ return new Variable(s);
+ }
+
+ if(desc == "int") {
+ const int j = static_QUType_int.get(uo);
+ return new Variable(j);
+ }
+
+ if(desc == "bool") {
+ const bool b = static_QUType_bool.get(uo);
+ return new Variable(b);
+ }
+
+ if(desc == "double") {
+ const double d = static_QUType_double.get(uo);
+ return new Variable(d);
+ }
+
+ if(desc == "QVariant") {
+ QVariant v = static_QUType_QVariant.get(uo);
+ return new Variable(v);
+ }
+
+ throw Exception(QString("Invalid parameter '%1'").arg(desc));
+}
+
+Variable::List MetaMethod::toVariableList(QUObject* uo)
+{
+ Variable::List list;
+
+ MetaParameter::List::ConstIterator it, end( d->arguments.constEnd() );
+ for( it = d->arguments.constBegin(); it != end; ++it) {
+ list.append( toVariable(uo) );
+ uo++;
+ }
+
+ return list;
+}
+
+KSharedPtr<Variable> MetaMethod::invoke(Variable::List arguments)
+{
+ kdDebug() << "KSharedPtr<Variable> MetaMethod::invoke(Variable::List arguments)" << endl;
+
+ if(! d->object) {
+ throw Exception("MetaObject is undefined.");
+ }
+
+ QObject* obj = d->object->object();
+ KSharedPtr<Variable> returnvalue;
+ QUObject* qu = 0;
+
+ try {
+ qu = toQUObject(arguments);
+
+ switch( d->type ) {
+ case Signal: {
+ int index = d->object->indexOfSignal( d->signature.latin1() );
+ obj->qt_emit(index, qu);
+ } break;
+ case Slot: {
+ int index = d->object->indexOfSlot( d->signature.latin1() );
+ obj->qt_invoke(index, qu);
+ } break;
+ default: {
+ throw Exception("Unknown type.");
+ } break;
+ }
+ returnvalue = toVariable( &qu[0] );
+ }
+ catch(Exception& e) {
+ delete [] qu; // free the QUObject array and
+ kdDebug() << "EXCEPTION in KoMacro::MetaMethod::invoke(Variable::List)" << endl;
+ throw Exception(e); // re-throw exception
+ }
+
+ delete [] qu;
+ return returnvalue;
+}
diff --git a/kexi/plugins/macros/lib/metamethod.h b/kexi/plugins/macros/lib/metamethod.h
new file mode 100644
index 00000000..df53ac60
--- /dev/null
+++ b/kexi/plugins/macros/lib/metamethod.h
@@ -0,0 +1,150 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_METAMETHOD_H
+#define KOMACRO_METAMETHOD_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <ksharedptr.h>
+
+#include "komacro_export.h"
+
+struct QUObject;
+
+namespace KoMacro {
+
+ // forward declarations.
+ class Variable;
+ class MetaObject;
+ class MetaParameter;
+ class MetaProxy;
+
+ /**
+ * Class to provide abstract methods for the undocumented
+ * Qt3 QUObject-API functionality.
+ *
+ * The design tried to limit future porting to Qt4 by providing a
+ * somewhat similar API to the Qt4 QMeta* stuff.
+ */
+ class KOMACRO_EXPORT MetaMethod : public KShared
+ {
+ public:
+
+ /**
+ * The type of method this @a MetaMethod provides
+ * access to.
+ */
+ enum Type {
+ Signal, /// The @a MetaMethod points to a Qt signal.
+ Slot, /// The @a MetaMethod points to a Qt slot.
+ Unknown /// The @a MetaMethod is not known.
+ };
+
+ /**
+ * Constructor.
+ *
+ * @param signature The signature this @a MetaMethod has. This
+ * includes the tagname and the arguments and could look like
+ * "myslot(const QString&, int)".
+ * @param type The @a MetaMethod::Type the @a MethodMethod
+ * has.
+ * @param object The @a MetaObject this @a MethodMethod
+ * belongs to. Each @a MethodMethod is associated with
+ * exactly one @a MetaObject .
+ */
+ explicit MetaMethod(const QString& signature, Type type = Unknown, KSharedPtr<MetaObject> object = 0);
+
+ /**
+ * Destructor.
+ */
+ ~MetaMethod();
+
+ /**
+ * @return the @a MetaObject instance this @a MethodMethod
+ * belongs to.
+ */
+ KSharedPtr<MetaObject> const object() const;
+
+ /**
+ * @return the signature this @a MetaMethod has. It could
+ * be something like "mySlot(const QString&,int)".
+ */
+ const QString signature() const;
+
+ /**
+ * @return the signatures tagname this @a MetaMethod has.
+ * At the signature "mySlot(const QString&,int)" the
+ * tagname would be "mySlot".
+ */
+ const QString signatureTag() const;
+
+ /**
+ * @return the signatures arguments this @a MetaMethod has.
+ * At the signature "mySlot(const QString&,int)" the
+ * arguments are "const QString&,int".
+ */
+ const QString signatureArguments() const;
+
+ /**
+ * @return the @a Type of method this @a MetaMethod provides
+ * access to.
+ */
+ Type type() const;
+
+ /**
+ * @return the signature arguments as parsed list of
+ * @a MetaParameter instances.
+ */
+ QValueList< KSharedPtr<MetaParameter> > arguments() const;
+
+ /**
+ * Translate the passed @p arguments list of @a Variable instances
+ * into a Qt3 QUObject* array.
+ */
+ QUObject* toQUObject(QValueList< KSharedPtr<Variable> > arguments);
+
+ /**
+ * Translate the passed @p uo QUObject reference into an internal used
+ * @a Variable instances.
+ */
+ KSharedPtr<Variable> toVariable(QUObject* uo);
+
+ /**
+ * Translate the passed @p uo QUObject array into an internal used
+ * list of @a Variable instances.
+ */
+ QValueList< KSharedPtr<Variable> > toVariableList(QUObject* uo);
+
+ /**
+ * Invoke the @a MetaMethod with the optional arguments
+ * @p arguments and return a variable.
+ */
+ KSharedPtr<Variable> invoke(QValueList< KSharedPtr<Variable> > arguments);
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/metaobject.cpp b/kexi/plugins/macros/lib/metaobject.cpp
new file mode 100644
index 00000000..000f4181
--- /dev/null
+++ b/kexi/plugins/macros/lib/metaobject.cpp
@@ -0,0 +1,151 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "metaobject.h"
+#include "metamethod.h"
+#include "variable.h"
+#include "exception.h"
+
+#include <qguardedptr.h>
+#include <qmetaobject.h>
+
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class MetaObject::Private
+ {
+ public:
+
+ /**
+ * The QObject instance this @a MetaObject belongs to.
+ */
+ QGuardedPtr<QObject> const object;
+
+ /**
+ * Constructor.
+ */
+ Private(QObject* const object)
+ : object(object)
+ {
+ }
+ };
+
+}
+
+MetaObject::MetaObject(QObject* const object)
+ : KShared()
+ , d( new Private(object) ) // create the private d-pointer instance.
+{
+}
+
+MetaObject::~MetaObject()
+{
+ delete d;
+}
+
+QObject* const MetaObject::object() const
+{
+ if(! d->object) {
+ throw Exception(QString("Object is undefined."));
+ }
+ return d->object;
+}
+
+/*
+QStrList MetaObject::signalNames() const
+{
+ return object()->metaObject()->signalNames();
+}
+
+QStrList MetaObject::slotNames() const
+{
+ return object()->metaObject()->slotNames();
+}
+*/
+
+int MetaObject::indexOfSignal(const char* signal) const
+{
+ QMetaObject* metaobject = object()->metaObject();
+ int signalid = metaobject->findSignal(signal, false);
+ if(signalid < 0) {
+ throw Exception(QString("Invalid signal \"%1\"").arg(signal));
+ }
+ return signalid;
+}
+
+int MetaObject::indexOfSlot(const char* slot) const
+{
+ QMetaObject* metaobject = object()->metaObject();
+ int slotid = metaobject->findSlot(slot, false);
+ if(slotid < 0) {
+ throw Exception(QString("Invalid slot \"%1\"").arg(slot));
+ }
+ return slotid;
+}
+
+KSharedPtr<MetaMethod> MetaObject::method(int index)
+{
+ QObject* obj = object();
+ MetaMethod::Type type = MetaMethod::Slot;
+ QMetaObject* metaobject = obj->metaObject();
+
+ const QMetaData* metadata = metaobject->slot(index, true);
+ if(! metadata) {
+ // Try to get a signal with that index iff we failed to determinate
+ // a matching slot.
+
+ metadata = metaobject->signal(index, true);
+ if(! metadata) {
+ throw Exception(QString("Invalid method index \"%1\" in object \"%2\"").arg(index).arg(obj->name()));
+ }
+ type = MetaMethod::Signal;
+ }
+
+ if(metadata->access != QMetaData::Public) {
+ throw Exception(QString("Not allowed to access method \"%1\" in object \"%2\"").arg(metadata->name).arg(obj->name()));
+ }
+
+ return new MetaMethod(metadata->name, type, this);
+}
+
+KSharedPtr<MetaMethod> MetaObject::signal(const char* signal)
+{
+ return method( indexOfSignal(signal) );
+}
+
+KSharedPtr<MetaMethod> MetaObject::slot(const char* slot)
+{
+ return method( indexOfSlot(slot) );
+}
+
+KSharedPtr<Variable> MetaObject::invokeMethod(int index, Variable::List arguments)
+{
+ // kdDebug() << "MetaObject::invokeMethod(int index, Variable::List arguments)" << endl;
+ KSharedPtr<MetaMethod> m = method(index);
+ // kdDebug() << "MetaObject::invokeMethod(int index, Variable::List arguments) return" << endl;
+ return m->invoke(arguments);
+}
+
diff --git a/kexi/plugins/macros/lib/metaobject.h b/kexi/plugins/macros/lib/metaobject.h
new file mode 100644
index 00000000..8b611574
--- /dev/null
+++ b/kexi/plugins/macros/lib/metaobject.h
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_METAOBJECT_H
+#define KOMACRO_METAOBJECT_H
+
+#include <qobject.h>
+#include <ksharedptr.h>
+
+#include "komacro_export.h"
+
+namespace KoMacro {
+
+ // forward declarations.
+ class Variable;
+ class MetaMethod;
+
+ /**
+ * Class to provide abstract access to extended QObject functionality
+ * like the undocumented QUObject-API in Qt3.
+ *
+ * The design tried to limit future porting to Qt4 by providing a
+ * somewhat similar API to the Qt4 QMeta* stuff.
+ */
+ class KOMACRO_EXPORT MetaObject : public KShared
+ {
+ public:
+
+ /**
+ * Constructor.
+ *
+ * @param object The QObject instance this @a MetaObject provides
+ * abstract access to.
+ */
+ explicit MetaObject(QObject* const object);
+
+ /**
+ * Destructor.
+ */
+ ~MetaObject();
+
+ /**
+ * @return the QObject this @a MetaObject provides abstract
+ * access to.
+ */
+ QObject* const object() const;
+
+ //QStrList signalNames() const;
+ //QStrList slotNames() const;
+
+ /**
+ * @return the index of the signal @p signal .
+ */
+ int indexOfSignal(const char* signal) const;
+
+ /**
+ * @return the index of the slot @p slot .
+ */
+ int indexOfSlot(const char* slot) const;
+
+ /**
+ * @return the @a MetaMethod that matches to the
+ * index @p index .
+ */
+ KSharedPtr<MetaMethod> method(int index);
+
+ /**
+ * @return a @a MetaMethod for the signal @p signal .
+ */
+ KSharedPtr<MetaMethod> signal(const char* signal);
+
+ /**
+ * @return a @a MetaMethod for the slot @p slot .
+ */
+ KSharedPtr<MetaMethod> slot(const char* slot);
+
+//KSharedPtr<MetaMethod> addSlot(const char* slot);
+//void connectSignal(QObject* obj, const char* signal);
+
+ /**
+ * Invoke the @a MetaMethod that has the index @p index .
+ *
+ * @param index The index the signal or slot has. Use
+ * @a indexOfSignal() and @a indexOfSlot() to determinate
+ * those index.
+ * @param arguments The optional arguments passed to the
+ * method.
+ * @return The returnvalue the method provides and that got
+ * returned if the execution is done.
+ */
+ KSharedPtr<Variable> invokeMethod(int index, QValueList< KSharedPtr<Variable> > arguments);
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/metaparameter.cpp b/kexi/plugins/macros/lib/metaparameter.cpp
new file mode 100644
index 00000000..7f072b2b
--- /dev/null
+++ b/kexi/plugins/macros/lib/metaparameter.cpp
@@ -0,0 +1,146 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "metaparameter.h"
+#include "exception.h"
+#include "variable.h"
+
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class MetaParameter::Private
+ {
+ public:
+
+ /**
+ * The signatures argument that represents this MetaParameter.
+ * This could be something like "const QString&", "int" or
+ * "QMap &lt; QString, QVariant &gt; ".
+ */
+ QString signatureargument;
+
+ /**
+ * The type of the @a Variable .
+ */
+ MetaParameter::Type type;
+
+ /**
+ * If the @a MetaParameter::Type is a Variant this QVariant::Type
+ * is used to defined what kind of Variant it is.
+ */
+ QVariant::Type varianttype;
+
+ };
+
+}
+
+MetaParameter::MetaParameter(const QString& signatureargument)
+ : KShared()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+ d->type = TypeNone;
+
+ if(! signatureargument.isNull()) {
+ setSignatureArgument( signatureargument );
+ }
+}
+
+MetaParameter::~MetaParameter()
+{
+ delete d;
+}
+
+MetaParameter::Type MetaParameter::type() const
+{
+ return d->type;
+}
+
+const QString MetaParameter::typeName() const
+{
+ switch( d->type ) {
+ case TypeNone:
+ return "None";
+ case TypeVariant:
+ return "Variant";
+ case TypeObject:
+ return "Object";
+ }
+ return QString::null;
+}
+
+void MetaParameter::setType(MetaParameter::Type type)
+{
+ d->type = type;
+ d->varianttype = QVariant::Invalid;
+}
+
+QVariant::Type MetaParameter::variantType() const
+{
+ return d->varianttype;
+}
+
+void MetaParameter::setVariantType(QVariant::Type varianttype)
+{
+ d->type = TypeVariant;
+ d->varianttype = varianttype;
+}
+
+void MetaParameter::setSignatureArgument(const QString& signatureargument)
+{
+ d->signatureargument = signatureargument;
+
+ QString argument = signatureargument;
+ if(argument.startsWith("const")) {
+ argument = argument.mid(5).stripWhiteSpace();
+ }
+
+ if(argument.endsWith("&")) {
+ argument = argument.left( argument.length() - 1 ).stripWhiteSpace();
+ }
+
+ if(argument.isEmpty()) {
+ throw Exception(QString("Empty signature argument passed."));
+ }
+ if(argument == "QVariant") {
+ setVariantType( QVariant::Invalid );
+ }
+
+ QVariant::Type type = argument.isNull() ? QVariant::Invalid : QVariant::nameToType(argument.latin1());
+ if (type != QVariant::Invalid) {
+ setVariantType( type );
+ }
+ else {
+ setType( TypeObject );
+ }
+}
+
+bool MetaParameter::validVariable(KSharedPtr<Variable> variable) const
+{
+ if( type() != variable->type() ) {
+ return false;
+ }
+ return true;
+}
diff --git a/kexi/plugins/macros/lib/metaparameter.h b/kexi/plugins/macros/lib/metaparameter.h
new file mode 100644
index 00000000..ab2a4004
--- /dev/null
+++ b/kexi/plugins/macros/lib/metaparameter.h
@@ -0,0 +1,136 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_METAPARAMETER_H
+#define KOMACRO_METAPARAMETER_H
+
+#include <qstring.h>
+#include <qvariant.h>
+#include <qobject.h>
+#include <ksharedptr.h>
+
+#include "komacro_export.h"
+
+namespace KoMacro {
+
+ // Forward declarations.
+ class Variable;
+
+ /**
+ * Class to provide abstract methods for the undocumented
+ * Qt3 QUObject-API functionality.
+ *
+ * The design tried to limit future porting to Qt4 by providing a
+ * somewhat similar API to the Qt4 QMeta* stuff.
+ */
+ class KOMACRO_EXPORT MetaParameter : public KShared
+ {
+
+ /**
+ * Property to get the type of the variable.
+ */
+ Q_PROPERTY(Type type READ type)
+
+ /**
+ * Property to get the type of the variable as string.
+ */
+ Q_PROPERTY(QString typeName READ typeName)
+
+ public:
+
+ /**
+ * List of @a MetaParameter instances.
+ */
+ typedef QValueList<KSharedPtr <MetaParameter > > List;
+
+ /**
+ * Constructor.
+ *
+ * @param signatureargument The signatures argument
+ * that will be used to determinate the arguments
+ * type. This could be something like "const QString&",
+ * "int" or "QMap &lt; QString, QVariant &gt; ".
+ */
+ explicit MetaParameter(const QString& signatureargument = QString::null);
+
+ /**
+ * Destructor.
+ */
+ ~MetaParameter();
+
+ /**
+ * Possible types the @a MetaParameter could provide.
+ */
+ enum Type {
+ TypeNone = 0, /// None type, the @a MetaParameter is empty.
+ TypeVariant, /// The @a MetaParameter is a QVariant.
+ TypeObject /// The @a MetaParameter is a QObject.
+ };
+
+ /**
+ * @return the @a MetaParameter::Type this variable has.
+ */
+ Type type() const;
+
+ /**
+ * @return the @a MetaParameter::Type as string. The typename
+ * could be "None", "Variant" or "Object".
+ */
+ const QString typeName() const;
+
+ /**
+ * Set the @a MetaParameter::Type this variable is.
+ */
+ void setType(Type type);
+
+ /**
+ * @return the @a MetaParameter::Type this variable is.
+ */
+ QVariant::Type variantType() const;
+
+ /**
+ * Set the @a MetaParameter::Type this variable is.
+ */
+ void setVariantType(QVariant::Type varianttype);
+
+ /**
+ * @return true if the passed @a Variable @p variable is
+ * valid for this @a MetaParameter . Valid means, that
+ * the variable has a castable type.
+ */
+ bool validVariable(KSharedPtr<Variable> variable) const;
+
+ protected:
+
+ /**
+ * @internal used method to set the signature argument. Those
+ * argument will be used to determinate the arguments type.
+ */
+ void setSignatureArgument(const QString& signatureargument);
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/variable.cpp b/kexi/plugins/macros/lib/variable.cpp
new file mode 100644
index 00000000..598b8b46
--- /dev/null
+++ b/kexi/plugins/macros/lib/variable.cpp
@@ -0,0 +1,246 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "variable.h"
+#include "exception.h"
+
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class Variable::Private
+ {
+ public:
+
+ /**
+ * The name this @a Variable has.
+ */
+ QString name;
+
+ /**
+ * The i18n-caption used for display purposes only
+ * this @a Variable has.
+ */
+ QString text;
+
+ /**
+ * If @a Variable::Type is @a Variable::TypeVariant this QVariant
+ * holds the value else it's invalid.
+ */
+ QVariant variant;
+
+ /**
+ * If @a Variable::Type is @a Variable::TypeObject this QObject is
+ * the value else it's NULL.
+ */
+ const QObject* object;
+
+ /**
+ * Optional list of children this @a Variable has.
+ */
+ // TODO Dow we use this or is it for the future??
+ Variable::List children;
+
+ /**
+ * Defines if the variable is enabled or disabled.
+ */
+ bool enabled;
+
+ explicit Private()
+ : enabled(true)
+ {
+ }
+ };
+
+}
+
+Variable::Variable()
+ : MetaParameter()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+ setType(TypeNone);
+ d->object = 0;
+}
+
+Variable::Variable(const QVariant& variant, const QString& name, const QString& text)
+ : MetaParameter()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+ setVariantType(variant.type());
+ d->variant = variant;
+ d->object = 0;
+ d->name = name;
+ d->text = text;
+}
+
+Variable::Variable(const QObject* object)
+ : MetaParameter()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+ setType(TypeObject);
+ d->object = object;
+}
+
+Variable::Variable(const QDomElement& element)
+ : MetaParameter()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+
+ QString typesignature = element.attribute("type", "const QString&");
+ QString value = element.text();
+
+ setSignatureArgument( typesignature );
+
+ switch( type() ) {
+ case KoMacro::MetaParameter::TypeVariant: {
+ //kdDebug() << QString("KoMacro::Variable(QDomElement) KoMacro::MetaParameter::TypeVariant") << endl;
+ // Set the variant without overwritting the previously detected varianttype.
+ setVariant( QVariant(value), false );
+ } break;
+ case KoMacro::MetaParameter::TypeObject: {
+ //kdDebug() << QString("KoMacro::Variable(QDomElement) KoMacro::MetaParameter::TypeObject") << endl;
+ //TODO setObject();
+ } break;
+ default: {
+ kdWarning() << QString("KoMacro::Variable(QDomElement) KoMacro::MetaParameter::TypeNone") << endl;
+ } break;
+ }
+}
+
+Variable::~Variable()
+{
+ delete d;
+}
+
+QString Variable::name() const
+{
+ return d->name;
+}
+
+void Variable::setName(const QString& name)
+{
+ d->name = name;
+}
+
+QString Variable::text() const
+{
+ return d->text;
+}
+
+void Variable::setText(const QString& text)
+{
+ d->text = text;
+}
+
+const QVariant Variable::variant() const
+{
+ //Q_ASSERT( type() == MetaParameter::TypeVariant );
+ //Q_ASSERT( variantType() != QVariant::Invalid );
+ //if(variantType() == QVariant::Invalid) return QVariant();
+ return d->variant;
+}
+
+void Variable::setVariant(const QVariant& variant, bool detecttype)
+{
+ if(detecttype) {
+ setVariantType( variant.type() );
+ }
+ d->variant = variant;
+}
+
+const QObject* Variable::object() const
+{
+ Q_ASSERT( ! d->object );
+ return d->object;
+}
+
+void Variable::setObject(const QObject* object)
+{
+ setType(TypeObject);
+ d->object = object;
+}
+
+Variable::operator QVariant () const
+{
+ return variant();
+}
+
+Variable::operator const QObject* () const
+{
+ return object();
+}
+
+const QString Variable::toString() const
+{
+ switch( type() ) {
+ case KoMacro::MetaParameter::TypeVariant: {
+ return variant().toString();
+ } break;
+ case KoMacro::MetaParameter::TypeObject: {
+ return QString("[%1]").arg( object()->name() );
+ } break;
+ default: {
+ throw Exception("Type is undefined.");
+ } break;
+ }
+ return QString::null;
+}
+
+int Variable::toInt() const
+{
+ return variant().toInt();
+}
+
+Variable::List Variable::children() const
+{
+ return d->children;
+}
+
+void Variable::appendChild(KSharedPtr<Variable> variable)
+{
+ d->children.append(variable);
+}
+
+void Variable::clearChildren()
+{
+ d->children.clear();
+}
+
+void Variable::setChildren(const Variable::List& children)
+{
+ d->children = children;
+}
+
+/*
+bool Variable::isEnabled() const
+{
+ return d->enabled;
+}
+
+void Variable::setEnabled(const bool enabled)
+{
+ d->enabled = enabled;
+}
+*/
diff --git a/kexi/plugins/macros/lib/variable.h b/kexi/plugins/macros/lib/variable.h
new file mode 100644
index 00000000..26e9619e
--- /dev/null
+++ b/kexi/plugins/macros/lib/variable.h
@@ -0,0 +1,222 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_VARIABLE_H
+#define KOMACRO_VARIABLE_H
+
+#include <qobject.h>
+#include <qdom.h>
+#include <qvariant.h>
+#include <ksharedptr.h>
+
+#include "metaparameter.h"
+
+namespace KoMacro {
+
+ /**
+ * A variable value used to provide abstract access to variables. The
+ * class handles QVariant and QObject and provides access to them.
+ * Variable inherits KShared and implements reference couting. So, it's
+ * not needed to take care of memory-managment.
+ */
+ class KOMACRO_EXPORT Variable : public MetaParameter
+ {
+
+ /**
+ * Property to get and set a QVariant as variable.
+ */
+ Q_PROPERTY(QVariant variant READ variant WRITE setVariant)
+
+ /**
+ * Property to get and set a QObject as variable.
+ */
+ Q_PROPERTY(QObject* object READ object WRITE setObject)
+
+ /**
+ * Property to get a string-representation of the variable.
+ */
+ Q_PROPERTY(QString string READ toString)
+
+ public:
+
+ /**
+ * A list of variables.
+ */
+ typedef QValueList<KSharedPtr<Variable > > List;
+
+ /**
+ * A map of variables.
+ */
+ typedef QMap<QString, KSharedPtr<Variable > > Map;
+
+ /**
+ * Default constructor.
+ */
+ explicit Variable();
+
+ /**
+ * Constructor from the QVariant @p variant .
+ *
+ * @param variant The value this variable has.
+ * @param name The unique @a name() this variable has.
+ * @param text The describing @a text() this variable has.
+ */
+ Variable(const QVariant& variant, const QString& name = QString::null, const QString& text = QString::null);
+
+ /**
+ * Constructor from the QObject @p object .
+ *
+ * @param object The value this variable has.
+ */
+ Variable(const QObject* object);
+
+ /**
+ * Constructor from the QDomElement @p element .
+ * @deprecated replaced with methods of @a XMLHandler.
+ * @param element The QDomElement that may optional contains the
+ * variable content or other additional informations.
+ */
+ Variable(const QDomElement& element);
+
+ /**
+ * Destructor.
+ */
+ virtual ~Variable();
+
+ /**
+ * @return the name this @a Variable has.
+ */
+ QString name() const;
+
+ /**
+ * Set the name @param name this @a Variable has.
+ */
+ void setName(const QString& name);
+
+ /**
+ * @return the caption this @a Variable has.
+ */
+ QString text() const;
+
+ /**
+ * Set the caption @param text this @a Variable has.
+ */
+ void setText(const QString& text);
+
+ /**
+ * Set the QObject @param object this variable has. A
+ * previously remembered value will be overwritten and
+ * the new type is a @a TypeObject .
+ */
+ void setObject(const QObject* object);
+
+ /**
+ * @return the QVariant this variable has. If this
+ * variable isn't a @a TypeVariant an invalid QVariant
+ * got returned.
+ */
+ const QVariant variant() const;
+
+ /**
+ * Set the QVariant @param variant this variable has. A
+ * previously remembered value will be overwritten and
+ * the new type is a @a TypeVariant . If @param detecttype is
+ * true the method tries to set the @a variantType according
+ * to the passed QVariant. If false the variantType won't
+ * be changed.
+ */
+ void setVariant(const QVariant& variant, bool detecttype = true);
+
+ /**
+ * @return the QObject this variable has. If this
+ * variable isn't a @a TypeObject NULL got returned.
+ */
+ const QObject* object() const;
+
+ /**
+ * Implicit conversion to QVariant operator. This method
+ * calls @a variant() internaly.
+ */
+ operator QVariant () const;
+
+ /**
+ * Implicit conversion to QObject operator. This method
+ * calls @a object() internaly.
+ */
+ operator const QObject* () const;
+
+ /**
+ * @return a string-represenation of the variable.
+ */
+ const QString toString() const;
+
+ /**
+ * @return a integer-represenation of the variable.
+ */
+ int toInt() const;
+
+ /**
+ * @return the optional list of @a Variable instances
+ * that are children of this @a Variable .
+ *
+ * @note that the list is returned call-by-reference. The
+ * list is accessed as getter/setter (read/write). So,
+ * don't set this method to const!
+ */
+ List children() const;
+
+ /**
+ * Append a @a Variable to the list of children this
+ * @a Variable has.
+ */
+ void appendChild(KSharedPtr<Variable> variable);
+
+ /**
+ * Clear the list of children this @a Variable has.
+ */
+ void clearChildren();
+
+ /**
+ * Set the children this @a Variable has.
+ */
+ void setChildren(const List& children);
+
+#if 0
+ /**
+ * @return true if this @a Variable is enabled else
+ * false is returned.
+ */
+ bool isEnabled() const;
+
+ /**
+ * Set this @a Variable to be enabled if @param enabled is
+ * true else the variable is disabled.
+ */
+ void setEnabled(const bool enabled);
+#endif
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/lib/xmlhandler.cpp b/kexi/plugins/macros/lib/xmlhandler.cpp
new file mode 100644
index 00000000..b35759e1
--- /dev/null
+++ b/kexi/plugins/macros/lib/xmlhandler.cpp
@@ -0,0 +1,226 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "xmlhandler.h"
+#include "macro.h"
+#include "macroitem.h"
+#include "action.h"
+
+#include <qdom.h>
+#include <kdebug.h>
+
+using namespace KoMacro;
+
+namespace KoMacro {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class XMLHandler::Private
+ {
+ public:
+
+ /**
+ * The @a Macro instance this @a XMLHandler
+ * manages.
+ */
+ Macro* const macro;
+
+ /**
+ * Constructor.
+ *
+ * @param macro The @a Macro instance this
+ * @a XMLHandler manages.
+ */
+ Private(Macro* const macro)
+ : macro(macro)
+ {
+ }
+ };
+
+}
+
+XMLHandler::XMLHandler(Macro* const macro)
+ : d( new Private(macro) )
+{
+}
+
+XMLHandler::~XMLHandler()
+{
+ delete d;
+}
+
+bool XMLHandler::parseXML(const QDomElement& element)
+{
+ // Remove old items. We should clear first.
+ d->macro->clearItems();
+
+ // We expect a <macro> element. Do we really need to be such strict or
+ // would it be more wise to trust the application in that case?
+ if(element.tagName() != "macro") {
+ kdDebug() << QString("XMLHandler::parseXML() Invalid tagname \"%1\"").arg(element.tagName()) << endl;
+ return false;
+ }
+
+ // To be flexible with the xml-scheme, we need a version-number for xml.
+ // If there is more than one version, parsing should update old macro-data, so that it
+ // could write out in the newer version in toXML().
+ if( element.attribute("xmlversion") != "1"){
+ kdDebug() << QString("XMLHandler::parseXML() Invalid xml-version \"%1\"").arg(element.attribute("xmlversion")) << endl;
+ return false;
+ }
+
+ // Do we need to load the macro's name?
+ // d->macro->setName(element.attribute("name"));
+
+ // Iterate through the child nodes the passed QDomElement has and
+ // build the MacroItem elements.
+ for(QDomNode itemnode = element.firstChild(); ! itemnode.isNull(); itemnode = itemnode.nextSibling()) {
+ // The tagname should be "item"
+ if(itemnode.nodeName() == "item") {
+ // The node is an element.
+ const QDomElement itemelem = itemnode.toElement();
+
+ // Create a new MacroItem
+ KSharedPtr<MacroItem> item = new MacroItem();
+
+ // Add the new item to our Macro.
+ d->macro->addItem( item );
+
+ // Each MacroItem may point to an Action instance. We
+ // try to determinate this action now and if it's defined
+ // and available, we set it.
+ KSharedPtr<Action> action = Manager::self()->action( itemelem.attribute("action") );
+ if(action.data()) {
+ item->setAction(action);
+ }
+
+ // Set the comment
+ item->setComment( itemelem.attribute("comment") );
+
+ // Iterate through the children this item has and try
+ // to fill the list of variables our new MacroItem has.
+ for(QDomNode childnode = itemnode.firstChild(); ! childnode.isNull(); childnode = childnode.nextSibling()) {
+ // The tagname should be "variable"
+ if(childnode.nodeName() == "variable") {
+ // The node is an element.
+ const QDomElement childelem = childnode.toElement();
+
+ // The name the variable has.
+ const QString name = childelem.attribute("name");
+ // The value the variable has.
+ const QString value = childelem.text();
+
+ // Store the new variable in our macroitem.
+ item->addVariable(name, value);
+ }
+ }
+ }
+ }
+
+ // Job was done successfully.
+ return true;
+}
+
+QDomElement XMLHandler::toXML()
+{
+ // The QDomDocument provides us the functionality to create new QDomElement instances.
+ QDomDocument document;
+
+ // Create the Macro-QDomElement. This element will be returned.
+ QDomElement macroelem = document.createElement("macro");
+
+ // Set the Macro-XML-Version, it should be the newest Version.
+ macroelem.setAttribute("xmlversion","1");
+
+ // Do we need to store the macro's name? Normaly the application
+ // could/should take care of it cause we don't know how the app
+ // may store the XML and cause we don't like to introduce
+ // redundancy at this point.
+ //macroelem.setAttribute("name",d->macro->name());
+
+ // The list of MacroItem-children a Macro provides.
+ QValueList<KSharedPtr<MacroItem > > items = d->macro->items();
+
+ // Create an iterator...
+ QValueList<KSharedPtr<MacroItem > >::ConstIterator it(items.constBegin()), end(items.constEnd());
+ // ...and iterate over the list of children the Macro provides.
+ for(;it != end; ++it) {
+ // We are iterating over MacroItem instances.
+ KSharedPtr<MacroItem> item = *it;
+
+ // Flag to determinate if we really need to remember this item what
+ // is only the case if comment or action is defined.
+ bool append = false;
+
+ // Each MacroItem will have an own node.
+ QDomElement itemelem = document.createElement("item");
+
+ // Each MacroItem could point to an Action provided by the Manager.
+ const KSharedPtr<Action> action = item->action();
+ if( action ) {
+ append = true;
+
+ // Remember the name of the action.
+ itemelem.setAttribute("action", action->name());
+
+ // Each MacroItem could have a list of variables. We
+ // iterate through that list and build a element
+ // for each single variable.
+ QMap<QString, KSharedPtr<Variable > > varmap = item->variables();
+
+ for(QMap<QString, KSharedPtr<Variable > >::ConstIterator vit = varmap.constBegin(); vit != varmap.constEnd(); ++vit) {
+ const KSharedPtr<Variable> v = vit.data();
+ if(! v.data()) {
+ // skip if the variable is NULL.
+ continue;
+ }
+ // Create an own element for the variable. The tagname will be
+ // the name of the variable.
+ QDomElement varelement = document.createElement("variable");
+
+ // Remember the name the value has.
+ varelement.setAttribute("name", vit.key());
+
+ // Remember the value as textnode.
+ varelement.appendChild(document.createTextNode(v->toString()));
+
+ // Add the new variable-element to our MacroItem.
+ itemelem.appendChild(varelement);
+ }
+ }
+
+ // Each MacroItem could have an optional comment.
+ const QString comment = item->comment();
+ if(! comment.isEmpty()) {
+ append = true;
+ itemelem.setAttribute("comment", item->comment());
+ }
+
+ // Check if we really need to remember the item.
+ if(append) {
+ macroelem.appendChild(itemelem);
+ }
+
+ }
+
+ // Job done. Return the macro's element.
+ return macroelem;
+}
diff --git a/kexi/plugins/macros/lib/xmlhandler.h b/kexi/plugins/macros/lib/xmlhandler.h
new file mode 100644
index 00000000..b6978d0f
--- /dev/null
+++ b/kexi/plugins/macros/lib/xmlhandler.h
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACRO_XMLHANDLER_H
+#define KOMACRO_XMLHANDLER_H
+
+#include "komacro_export.h"
+
+class QObject;
+class QDomElement;
+
+namespace KoMacro {
+
+ // Forward declarations.
+ class Macro;
+
+ /**
+ * The XMLHandler class manages the (un-)serialization of
+ * a @a Macro instance to/from XML.
+ */
+ class KOMACRO_EXPORT XMLHandler
+ {
+ public:
+
+ /**
+ * Constructor to init a @a XMLHandler .
+ * @param macro The @a Macro instance which will
+ * be managed.
+ */
+ XMLHandler(Macro* const macro);
+
+ /**
+ * Destructor to @a XMLHandler .
+ */
+ ~XMLHandler();
+
+ /**
+ * Reads a given @a QDomElement, extracts given
+ * Actions into the managed Macro-Instance.
+ * @param element The @a QDomElement within
+ * the @a Macro.
+ * @return Return true when parsing is successfull.
+ */
+ bool parseXML(const QDomElement& element);
+
+ /**
+ * Converts the macro to a @a QDomElement.
+ * @return The resulten @a QDomElement from
+ * the @a Macro.
+ */
+ QDomElement toXML();
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/tests/Makefile.am b/kexi/plugins/macros/tests/Makefile.am
new file mode 100644
index 00000000..36dbd76f
--- /dev/null
+++ b/kexi/plugins/macros/tests/Makefile.am
@@ -0,0 +1,28 @@
+if include_kunittestgui
+ GUIBINPROGRAM = komacrotestgui
+else
+ GUIBINPROGRAM =
+endif
+
+bin_PROGRAMS = komacrotest $(GUIBINPROGRAM)
+
+komacrotest_SOURCES = komacrotest.cpp testobject.cpp testaction.cpp actiontests.cpp macrotests.cpp macroitemtests.cpp variabletests.cpp xmlhandlertests.cpp xmlhandlertests2.cpp
+komacrotest_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+komacrotest_LDADD = -lkunittest ../lib/libkomacro.la $(LIB_KDEUI) $(LIB_KPARTS)
+
+if include_kunittestgui
+ komacrotestgui_SOURCES = komacrotestgui.cpp testobject.cpp testaction.cpp actiontests.cpp macrotests.cpp macroitemtests.cpp variabletests.cpp xmlhandlertests.cpp xmlhandlertests2.cpp
+ komacrotestgui_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+ komacrotestgui_LDADD = -lkunittestgui ../lib/libkomacro.la $(LIB_KDEUI) $(LIB_KPARTS)
+endif
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+INCLUDES = -I$(srcdir)/tests -I$(srcdir)../ $(all_includes)
+METASOURCES = AUTO
+
+guicheck: komacrotestgui
+ kunittest ./komacrotestgui
+
+check: komacrotest
+ echo $(srcdir)
+ kunittest ./komacrotest
diff --git a/kexi/plugins/macros/tests/actiontests.cpp b/kexi/plugins/macros/tests/actiontests.cpp
new file mode 100644
index 00000000..0150ecfd
--- /dev/null
+++ b/kexi/plugins/macros/tests/actiontests.cpp
@@ -0,0 +1,211 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "actiontests.h"
+#include "testobject.h"
+#include "testaction.h"
+#include "komacrotestbase.h"
+
+#include "../lib/action.h"
+#include "../lib/function.h"
+#include "../lib/manager.h"
+#include "../lib/macro.h"
+#include "../lib/variable.h"
+#include "../lib/metaobject.h"
+#include "../lib/metamethod.h"
+#include "../lib/metaparameter.h"
+#include "../lib/exception.h"
+#include "../lib/macroitem.h"
+
+#include <ostream>
+
+#include <qstringlist.h>
+#include <qdom.h>
+
+#include <kdebug.h>
+#include <kunittest/runner.h>
+#include <kxmlguiclient.h>
+
+using namespace KUnitTest;
+using namespace KoMacroTest;
+
+namespace KoMacroTest {
+
+ /**
+ * Register KoMacroTest::CommonTests as TestSuite.
+ */
+
+ KUNITTEST_SUITE("KoMacroTestSuite");
+ KUNITTEST_REGISTER_TESTER(ActionTests);
+
+
+ class ActionTests::Private
+ {
+ public:
+ /**
+ * An KXMLGUIClient instance created on @a setUp() and
+ * passed to the @a KoMacro::Manager to bridge to the
+ * app-functionality.
+ */
+ KXMLGUIClient* xmlguiclient;
+
+ /**
+ * An @a TestObject instance used internaly to test
+ * handling and communication with from QObject
+ * inheritated instances.
+ */
+ TestAction* testaction;
+
+ QDomDocument* doomdocument;
+
+ KSharedPtr<KoMacro::Macro> macro;
+
+ QValueList< KSharedPtr<KoMacro::MacroItem> > items;
+
+ KSharedPtr<KoMacro::Action> actionptr;
+
+ Private()
+ : xmlguiclient(0)
+ , testaction(0)
+ , doomdocument(0)
+ , macro(0)
+ , actionptr(0)
+ {
+ }
+ };
+}
+
+typedef QValueList< KSharedPtr<KoMacro::MacroItem> >::size_type sizetype;
+
+
+ActionTests::ActionTests()
+ : KUnitTest::SlotTester()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+}
+
+ActionTests::~ActionTests()
+{
+ delete d->xmlguiclient;
+ delete d;
+}
+
+
+void ActionTests::setUp()
+{
+ d->xmlguiclient = new KXMLGUIClient();
+
+ if (::KoMacro::Manager::self() == 0) {
+ ::KoMacro::Manager::init( d->xmlguiclient );
+ }
+
+ d->testaction = new TestAction();
+ ::KoMacro::Manager::self()->publishAction(d->testaction);
+
+ d->doomdocument = new QDomDocument();
+
+ QString const xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\" >"
+ "<item action=\"testaction\" >"
+ "</item>"
+ "</macro>");
+
+ d->doomdocument->setContent(xml);
+ d->macro = KoMacro::Manager::self()->createMacro("testMacro");
+ d->macro->parseXML(d->doomdocument->documentElement());
+ d->macro->execute(this);
+ d->items = d->macro->items();
+ d->actionptr = d->items[0]->action();
+}
+
+void ActionTests::tearDown()
+{
+ delete d->actionptr;
+ delete d->macro;
+ delete d->doomdocument;
+ delete d->xmlguiclient;
+}
+
+
+void ActionTests::testMacro()
+{
+ kdDebug()<<"===================== testMacro() ======================" << endl;
+
+ //fetch Items and ..
+ //QValueList< KSharedPtr<KoMacro::MacroItem> >& items = d->macro->items();
+
+ //... check that there is one
+ KOMACROTEST_XASSERT( d->items.count(), sizetype(0) );
+}
+
+void ActionTests::testAction()
+{
+ kdDebug()<<"===================== testAction() ======================" << endl;
+
+ //get it
+ //KSharedPtr<KoMacro::Action> actionptr = d->items[0]->action();
+ //-> check that it is not null
+ KOMACROTEST_XASSERT(sizetype(d->actionptr.data()), sizetype(0));
+}
+
+void ActionTests::testText()
+{
+ kdDebug()<<"===================== testText() ======================" << endl;
+
+ //KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+
+ const QString leetSpeech = "']['3 $']['";
+
+ //check i18n text
+ KOMACROTEST_ASSERT(d->actionptr->text(),QString("Test"));
+ //change it
+ d->actionptr->setText(leetSpeech);
+ //retest it
+ KOMACROTEST_ASSERT(d->actionptr->text(),QString(leetSpeech));
+}
+
+
+void ActionTests::testName()
+{
+ kdDebug()<<"===================== testName() ======================" << endl;
+
+ //KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+
+ //check name
+ KOMACROTEST_ASSERT(d->actionptr->name(),QString("testaction"));
+ //change it
+ d->actionptr->setName("ActionJackson");
+ //retest it
+ KOMACROTEST_ASSERT(d->actionptr->name(),QString("ActionJackson"));
+}
+
+void ActionTests::testComment()
+{
+ kdDebug()<<"===================== testComment() ======================" << endl;
+
+ //KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+
+ //check comment
+ KOMACROTEST_XASSERT(d->actionptr->comment(),QString("No Comment!"));
+ //set comment
+ d->actionptr->setComment("Stringtest");
+ //check comment again
+ KOMACROTEST_ASSERT(d->actionptr->comment(),QString("Stringtest"));
+}
+
+#include "actiontests.moc"
diff --git a/kexi/plugins/macros/tests/actiontests.h b/kexi/plugins/macros/tests/actiontests.h
new file mode 100644
index 00000000..48b5a252
--- /dev/null
+++ b/kexi/plugins/macros/tests/actiontests.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACROTEST_ACTIONTESTS_H
+#define KOMACROTEST_ACTIONTESTS_H
+
+#include <kunittest/tester.h>
+
+namespace KoMacroTest {
+
+ /**
+ * The common testsuite used to test common @a KoMacro
+ * functionality.
+ */
+ class ActionTests : public KUnitTest::SlotTester
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ ActionTests();
+
+ /**
+ * Destructor.
+ */
+ virtual ~ActionTests();
+
+ public slots:
+
+ /**
+ * This slot got called by KUnitTest before testing
+ * starts.
+ */
+ void setUp();
+
+ /**
+ * This slot got called by KUnitTest after all tests
+ * are done.
+ */
+ void tearDown();
+
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testMacro();
+
+ /**
+ * Test the @a KoMacro::Action functionality.
+ */
+ void testAction();
+
+ void testText();
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testName();
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testComment();
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/tests/commontests.cpp b/kexi/plugins/macros/tests/commontests.cpp
new file mode 100644
index 00000000..84c596aa
--- /dev/null
+++ b/kexi/plugins/macros/tests/commontests.cpp
@@ -0,0 +1,907 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "commontests.h"
+#include "testobject.h"
+#include "testaction.h"
+#include "komacrotestbase.h"
+
+#include "../lib/action.h"
+#include "../lib/function.h"
+#include "../lib/manager.h"
+#include "../lib/macro.h"
+#include "../lib/variable.h"
+#include "../lib/metaobject.h"
+#include "../lib/metamethod.h"
+#include "../lib/metaparameter.h"
+#include "../lib/exception.h"
+#include "../lib/macroitem.h"
+
+#include <ostream>
+#include <climits>
+
+#include <qstringlist.h>
+#include <qdom.h>
+
+#include <kdebug.h>
+#include <kunittest/runner.h>
+#include <kxmlguiclient.h>
+
+using namespace KUnitTest;
+using namespace KoMacroTest;
+
+namespace KoMacroTest {
+
+ /**
+ * Register KoMacroTest::CommonTests as TestSuite.
+ */
+ KUNITTEST_SUITE("CommonTestsSuite")
+ KUNITTEST_REGISTER_TESTER(CommonTests);
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class CommonTests::Private
+ {
+ public:
+
+ /**
+ * An KXMLGUIClient instance created on @a setUp() and
+ * passed to the @a KoMacro::Manager to bridge to the
+ * app-functionality.
+ */
+ KXMLGUIClient* xmlguiclient;
+
+ /**
+ * An @a TestObject instance used internaly to test
+ * handling and communication with from QObject
+ * inheritated instances.
+ */
+ TestObject* testobject;
+
+ TestAction* testaction;
+
+ QDomDocument* doomdocument;
+
+ /**
+ * Constructor.
+ */
+ Private()
+ : xmlguiclient(0)
+ , testobject(0)
+ , testaction(0)
+ , doomdocument(0)
+ {
+ }
+ };
+
+}
+
+typedef QValueList< KSharedPtr<KoMacro::MacroItem> >::size_type sizetype;
+
+CommonTests::CommonTests()
+ : KUnitTest::SlotTester()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+}
+
+CommonTests::~CommonTests()
+{
+ delete d->xmlguiclient;
+ delete d;
+}
+
+void CommonTests::setUp()
+{
+ d->xmlguiclient = new KXMLGUIClient();
+ ::KoMacro::Manager::init( d->xmlguiclient );
+
+ d->testobject = new TestObject( this );
+ ::KoMacro::Manager::self()->publishObject("TestObject", d->testobject);
+
+ d->testaction = new TestAction();
+ ::KoMacro::Manager::self()->publishAction(d->testaction);
+
+ d->doomdocument = new QDomDocument();
+
+ QString const xml = QString("<!DOCTYPE macros>"
+
+ "<macro xmlversion=\"1\">"
+
+ "<item action=\"testaction\" >"
+ "</item>"
+ "</macro>");
+
+ d->doomdocument->setContent(xml);
+}
+
+void CommonTests::tearDown()
+{
+ delete d->doomdocument;
+ delete d->testobject;
+ delete d->xmlguiclient;
+}
+
+void CommonTests::testManager()
+{
+ kdDebug()<<"===================== testManager() ======================" << endl;
+
+ //check if manager-guiClient equals xmlguiclient
+ KOMACROTEST_ASSERT( ::KoMacro::Manager::self()->guiClient(), d->xmlguiclient );
+ //check if manger-object equals testobject
+ KOMACROTEST_ASSERT( dynamic_cast<TestObject*>( (QObject*)::KoMacro::Manager::self()->object("TestObject") ), d->testobject );
+}
+/*
+void CommonTests::testAction()
+{
+ const QString testString = "teststring";
+ const QString testInt = "testint";
+ const QString testBool = "testbool";
+
+ //TODO: CLEANUP!!!!!!
+ //TODO: test manipulation of action and macroitem and context.
+
+ kdDebug()<<"===================== testAction() ======================" << endl;
+
+ //Publish TestAction for the first time.
+
+ QDomElement const domelement = d->doomdocument->documentElement();
+
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+
+ //Is our XML parseable ?
+ KOMACROTEST_ASSERT(macro->parseXML(domelement),true);
+
+ //??
+ macro->execute(this);
+
+ //create list of KSharedPtr from the childs of the macro
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = macro->items();
+
+
+ //check that there is one
+ KOMACROTEST_ASSERT( items.count(), sizetype(1) );
+ //fetch the first one
+ KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+ //How do we know that an action exist ?
+ //-> check that it is not null
+ KOMACROTEST_XASSERT(sizetype(actionptr.data()), sizetype(0));
+ //fetch the "teststring"-variable
+ KSharedPtr<KoMacro::Variable> variableptr = actionptr->variable("teststring");
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ //check that it is " "
+ KOMACROTEST_ASSERT(variableptr->variant().toString(),QString("testString"));
+
+ //fetch the "testint"-variable
+ variableptr = actionptr->variable("testint");
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ //check that it is " "
+ KOMACROTEST_ASSERT(variableptr->variant().toInt(),int(0));
+
+ //fetch the "testbool"-variable
+ variableptr = actionptr->variable("testbool");
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ //check that it is " "
+ KOMACROTEST_ASSERT(variableptr->variant().toBool(),true);
+
+ actionptr->setVariable("teststring", "STRINGTEST", "TestString");
+ variableptr = actionptr->variable("teststring");
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ //check that it is " "
+ KOMACROTEST_ASSERT(variableptr->variant().toString(),QString("TestString"));
+
+ actionptr->setVariable("testint","INTTEST",INT_MAX);
+ variableptr = actionptr->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(INT_MAX));
+
+ actionptr->setVariable("testbool","BOOLTEST", "false");
+ variableptr = actionptr->variable("testbool");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(variableptr->variant().toBool(),false);
+
+ //create new macroitem for testing
+ KoMacro::MacroItem* macroitem = new KoMacro::MacroItem();
+ //set the action
+ macroitem->setAction(d->testaction);
+ //append the macroitem to testitems
+ items.append(macroitem);
+ //increased ??
+ KOMACROTEST_ASSERT( items.count(), sizetype(2) );
+
+ //Manipulate the macroitem
+ macroitem->setVariable("teststring", "TeStString");
+ variableptr = macroitem->variable("teststring");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(variableptr->variant().toString(),QString("TeStString"));
+
+ macroitem->setVariable("testint",INT_MIN);
+ variableptr = macroitem->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(INT_MIN));
+
+ macroitem->setVariable("testint",-1);
+ variableptr = macroitem->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(-1));
+
+
+ //commontests.cpp: In member function 'void KoMacroTest::CommonTests::testAction()':
+ //commontests.cpp:249: error: call of overloaded 'setVariable(const char [8], int)' is ambiguous
+ //../lib/macroitem.h:131: note: candidates are: QStringList KoMacro::MacroItem::setVariable(const QString&, KSharedPtr<KoMacro::Variable>)
+ //../lib/macroitem.h:137: note: QStringList KoMacro::MacroItem::setVariable(const QString&, const QVariant&)
+
+ macroitem->setVariable("testint",(int) 0);
+ variableptr = macroitem->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(0));
+
+
+ macroitem->setVariable("testint",1);
+ variableptr = macroitem->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(1));
+
+ macroitem->setVariable("testint",INT_MAX);
+ variableptr = macroitem->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(INT_MAX));
+
+ macroitem->setVariable("testbool","false");
+ variableptr = macroitem->variable("testbool");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(variableptr->variant().toBool(),false);
+
+ //secondway for appending an macroitem
+ //add the manipulated macroitem
+ macro->addItem(macroitem);
+ //increased ??
+ KOMACROTEST_ASSERT( items.count(), sizetype(3));
+} */
+
+void CommonTests::testXmlhandler()
+{
+ kdDebug()<<"===================== testXmlhandler() ======================" << endl;
+
+ // Local Init
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomElement domelement;
+
+ // Save old doomdocument
+ QString xmlOld = d->doomdocument->toString();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >testString</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testbla\" >somethingwrong</variable>" // TODO Is here a kdDebug-msg enough?
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</macro>");
+ // Set the XML-document with the above string.
+ d->doomdocument->setContent(xml);
+ domelement = d->doomdocument->documentElement();
+ //Is our XML parseable ?
+ KOMACROTEST_ASSERT(macro->parseXML(domelement),true);
+
+ // Test-XML-document with bad root element.
+ xml = QString("<!DOCTYPE macros>"
+ "<maro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >testString</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</maro>");
+ d->doomdocument->setContent(xml);
+ domelement = d->doomdocument->documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(domelement),false);
+
+ // Test-XML-document with wrong macro-xmlversion.
+ xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"2\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >testString</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</macro>");
+ d->doomdocument->setContent(xml);
+ domelement = d->doomdocument->documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(domelement),false);
+
+ // TODO Test-XML-document if it has a wrong structure like wrong parathesis
+ // or missing end tag (is this critical??).
+ /*xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >testString</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "</item>"
+ "</macro>");
+ d->doomdocument->setContent(xml);
+ domelement = d->doomdocument->documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(domelement),false);*/
+
+ // Test-XML-document with wrong item- and variable-tags.
+ // TODO Could this happen??
+ xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<iem action=\"testaction\" >"
+ "<vle name=\"teststring\" >testString</variable>"
+ "<v name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</macro>");
+ d->doomdocument->setContent(xml);
+ domelement = d->doomdocument->documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(domelement),true); //should be false?
+
+ // TODO Test-XML-document with maximum field-size.
+ xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >testString</variable>"
+ "<variable name=\"testint\" > 0 </variable>" // the value should be INT_MAX
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>" // DBL_MAX
+ "</item>"
+ "</macro>");
+ d->doomdocument->setContent(xml);
+ domelement = d->doomdocument->documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(domelement),true);
+
+ // TODO Test-XML-document with minimum field-size.
+ xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >testString</variable>"
+ "<variable name=\"testint\" >0</variable>" // INT_MIN
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>" // DBL_MIN
+ "</item>"
+ "</macro>");
+ d->doomdocument->setContent(xml);
+ domelement = d->doomdocument->documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(domelement),true);
+
+ // TODO Part 2: Read the parsen macro and make a comparison to the XML-document.
+
+ // TODO Part 3: From a Macro to XML.
+
+ // RODO Part 4: Compare the transformed XML with the given macro.
+
+ // Set back xml-string for other tests.
+ d->doomdocument->setContent(xmlOld);
+}
+
+void CommonTests::testFunction()
+{
+//TODO: CLEANUP!!!!!!
+/*
+ kdDebug()<<"===================== testFunction() ======================" << endl;
+
+ //create a QDomDocument
+ QDomDocument domdocument = QDomDocument();
+ //create some data
+ QString const comment = "Describe what the function does";
+ QString const name = "myfunc";
+ QString const text = "My Function";
+ QString const receiver = "TestObject";
+ QString const argument1 = "Some string";
+ int const argument2 = 12345;
+
+ //set "Function"-content in QDocument
+ domdocument.setContent(QString(
+ "<function name=\"" + name + "\" text=\"" + text + "\" comment=\"" + comment + "\" receiver=\"" + receiver + "\" slot=\"myslot(const QString &amp; , int)\">"
+ "<argument>" + argument1 + "</argument>"
+ "<argument>" + QString("%1").arg(argument2) + "</argument>"
+ "</function>"
+ ));
+
+ //create an KomacroFunction with our data, and put it into a KSharedPtr
+ KSharedPtr<KoMacro::Action> functionptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //cast KSharedPtr to KoMacro-"Function"
+ KoMacro::Function* func = dynamic_cast<KoMacro::Function*>( functionptr.data() );
+ //check that function is not null
+ KOMACROTEST_XASSERT((int) func, 0);
+
+ //check domElement
+ KOMACROTEST_ASSERT( func->domElement(), domdocument.documentElement() );
+ //check name
+ KOMACROTEST_ASSERT( QString(func->name()), name );
+ //check text
+ KOMACROTEST_ASSERT( func->text(), text );
+ //check comment
+ KOMACROTEST_ASSERT( func->comment(), comment );
+ //check receiver
+ KOMACROTEST_ASSERT( func->receiver(), receiver );
+ //check slot (arguments)
+ KOMACROTEST_ASSERT( QString(func->slot()), QString("myslot(const QString&,int)") );
+
+ //create KoMacro-MetaObject from receiverObject
+ KSharedPtr<KoMacro::MetaObject> receivermetaobject = func->receiverObject();
+ //check that receivermetaobject.data is not null
+ KOMACROTEST_XASSERT((int) receivermetaobject.data(), 0);
+
+ //create KoMacro-MetaMethod from receiverObject
+ KSharedPtr<KoMacro::MetaMethod> receivermetamethod = receivermetaobject->slot( func->slot().latin1() );
+ //check that receivermetamethod.data is not null
+ KOMACROTEST_XASSERT((int) receivermetamethod.data(), 0);
+
+ //create list of variables from func
+ KoMacro::Variable::List funcvariables = func->variables();
+ //counter for hardcoded tests see below ...
+ uint i = 0;
+ KoMacro::Variable::List::ConstIterator it, end( funcvariables.constEnd() );
+ for( it = funcvariables.constBegin(); it != end; ++it) {
+ kdDebug() << "VARIABLE => " << (*it ? (*it)->toString() : "<NULL>") << endl;
+ //hardcoded test:
+ // firstrun we have a QString, secondrun we have an int
+ switch(i) {
+ case 0: { // returnvalue
+ KOMACROTEST_ASSERT(*it, KSharedPtr<KoMacro::Variable>(NULL));
+ } break;
+ case 1: { // first parameter
+ //check first variable of func is the same as argument1
+ //QString const argument1 = "Some string";
+ KOMACROTEST_ASSERT((*it)->toString(), argument1);
+ } break;
+ case 2: { // second parameter
+ //check second variable of func is the same as argument2
+ //int const argument2 = 12345;
+ KOMACROTEST_ASSERT((*it)->toInt(), argument2);
+ } break;
+ default: {
+ } break;
+ }
+ i++;
+ }
+
+ //check that we have two arguments + one returnvalue in func
+ KOMACROTEST_ASSERT( funcvariables.count(), uint(3) );
+
+ // check that the first argument (the returnvalue) is empty
+ KOMACROTEST_ASSERT( funcvariables[0], KSharedPtr<KoMacro::Variable>(NULL) );
+
+ //create a KoMacro-Variable-Ptr from first func argument
+ KSharedPtr<KoMacro::Variable> stringvar = funcvariables[1];
+ //check that it is not null
+ KOMACROTEST_XASSERT((int) stringvar.data(),0);
+ //check via QVariant type that stringvar is from Type Variant
+ KOMACROTEST_ASSERT( stringvar->type(), KoMacro::MetaParameter::TypeVariant );
+ //check via metaparameter that variant is from type string
+ KOMACROTEST_ASSERT( stringvar->variantType(), QVariant::String );
+ //chech that stringvar equals argument1
+ KOMACROTEST_ASSERT( stringvar->toString(), argument1 );
+
+ //create a KoMacro-Variable-Ptr from second func argument
+ KSharedPtr<KoMacro::Variable> intvar = funcvariables[2];
+ //check that it is not null
+ KOMACROTEST_XASSERT((int) intvar.data(), 0);
+ //check via QVariant type that stringvar is from Type Variant
+ KOMACROTEST_ASSERT( intvar->type(), KoMacro::MetaParameter::TypeVariant );
+ //check that intvar is An String -> we create an string from int because of xml
+ KOMACROTEST_ASSERT( intvar->variantType(), QVariant::String );
+ //check that intvar equals argument2
+ KOMACROTEST_ASSERT( intvar->toInt(), argument2 );
+
+ //returnvalue see testobject ....
+ KSharedPtr<KoMacro::Variable> funcreturnvalue = receivermetamethod->invoke( funcvariables );
+ kdDebug() << "CommonTests::testFunction() RETURNVALUE =====> " << funcreturnvalue->toString() << endl;
+ KOMACROTEST_ASSERT( funcreturnvalue->toInt(), argument2 );
+
+ //check returnvalue
+ //func->setReturnValue(new KoMacro::Variable("54321"));
+ //KOMACROTEST_ASSERT( func->returnValue()->toString(), QString("54321") );
+*/
+}
+
+void CommonTests::testIntFunction()
+{
+//TODO: CLEANUP!!!!!!
+/*
+ kdDebug()<<"===================== testIntFunction() ======================" << endl;
+
+ //create a QDomDocument
+ QDomDocument domdocument = QDomDocument();
+
+ //set "Function"-content in QDocument
+ domdocument.setContent(QString(
+ "<function name=\"myfunc\" text=\"My Function\" comment=\"comment\" receiver=\"TestObject\" slot=\"myslot(const QString &amp; , int)\">"
+ "<argument>Some string</argument>"
+ "<argument>12345</argument>"
+ "</function>"
+ ));
+
+ //create an KomacroFunction with our data, and put it into a KSharedPtr
+ KSharedPtr<KoMacro::Action> functionptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //Cast data to function
+ KoMacro::Function* func = dynamic_cast<KoMacro::Function*>( functionptr.data() );
+ //check that it is not null
+ KOMACROTEST_XASSERT((int) func, 0);
+ //execute the function
+ func->activate();
+ //Check returnvalue is same value we entered
+ //KOMACROTEST_ASSERT(func->returnValue()->toString(),QString("12345"));
+*/
+}
+
+void CommonTests::testDoubleFunction()
+{
+//TODO: CLEANUP!!!!!!
+/*
+ kdDebug()<<"===================== testDoubleFunction() ======================" << endl;
+
+ //create a QDomDocument
+ QDomDocument domdocument = QDomDocument();
+
+ //set "Function"-content in QDocument
+ domdocument.setContent(QString(
+ "<function name=\"myfunc\" text=\"My Function\" comment=\"comment\" receiver=\"TestObject\" slot=\"myslot(const QString &amp; , double)\">"
+ "<argument>Some string</argument>"
+ "<argument>12.56</argument>"
+ "</function>"
+ ));
+
+ //create an KomacroFunction with our data, and put it into a KSharedPtr
+ KSharedPtr<KoMacro::Action> functionptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //Cast data to function
+ KoMacro::Function* func = dynamic_cast<KoMacro::Function*>( functionptr.data() );
+ //check that it is not null
+ KOMACROTEST_XASSERT((int) func, 0);
+ //execute the function
+ func->activate();
+ //Check returnvalue is same value we entered
+ //KOMACROTEST_ASSERT(func->returnValue()->toString(),QString("12.56"));
+*/
+}
+
+void CommonTests::testQStringFunction()
+{
+//TODO: CLEANUP!!!!!!
+/*
+ kdDebug()<<"===================== testQStringFunction() ======================" << endl;
+
+ //create a QDomDocument
+ QDomDocument domdocument = QDomDocument();
+
+ //set "Function"-content in QDocument
+ domdocument.setContent(QString(
+ "<function name=\"myfunc\" text=\"My Function\" comment=\"comment\" receiver=\"TestObject\" slot=\"myslot(const QString &amp;)\">"
+ "<argument>Some string</argument>"
+ "</function>"
+ ));
+
+ //create an KomacroFunction with our data, and put it into a KSharedPtr
+ KSharedPtr<KoMacro::Action> functionptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //Cast data to function
+ KoMacro::Function* func = dynamic_cast<KoMacro::Function*>( functionptr.data() );
+ //check that it is not null
+ KOMACROTEST_XASSERT((int) func, 0);
+ //execute the function func->activate();
+ //Check returnvalue is same value we entered
+ //KOMACROTEST_ASSERT(func->returnValue()->toString(),QString("Some string"));
+*/
+}
+
+void CommonTests::testMacro()
+{
+//TODO: CLEANUP!!!!!!
+ kdDebug()<<"===================== testMacro() ======================" << endl;
+
+ QDomElement const domelement = d->doomdocument->documentElement();
+
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ //Is our XML parseable ?
+ KOMACROTEST_ASSERT(macro->parseXML(domelement),true);
+
+// //create a QDomDocument
+// QDomDocument domdocument = QDomDocument();
+//
+// //Fully fleged content this time with macro,function and action
+// domdocument.setContent(QString(
+// "<macro name=\"mymacro\" icon=\"myicon\" text=\"My Macro\" comment=\"Some comment to describe the Macro.\">"
+// "<action name=\"myaction\" text=\"My Action\" comment=\"Just some comment\" />"
+// "<function name=\"myfunc\" text=\"My Function\" comment=\"Describe what the function does\" receiver=\"TestObject\" slot=\"myslot(const QString &amp;)\">"
+// "<argument>The myfunc argument string</argument>"
+// "</function>"
+// "</macro>"
+// ));
+//
+// //create Macro
+// // KSharedPtr<KoMacro::Action> macroptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+// //cast data to Macro
+// KoMacro::Macro* macro = dynamic_cast<KoMacro::Macro*>( macroptr.data() );
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(macro.data()), sizetype(0));
+ //check that domeElement given to manager is the sam as in the macro
+// KOMACROTEST_ASSERT( macro->toXML(), d->doomdocument->documentElement() );
+ //check the name
+ KOMACROTEST_ASSERT( QString(macro->name()), QString("testMacro") );
+
+ /**
+ @deprecated values no longer exist
+
+ //check the text
+ KOMACROTEST_ASSERT( macro->text(), QString("My Macro") );
+ //check iconname
+ KOMACROTEST_ASSERT( QString(macro->icon()), QString("myicon") );
+ //check comment
+ KOMACROTEST_ASSERT( macro->comment(), QString("Some comment to describe the Macro.") );
+ */
+
+ //create list of KsharedPtr from the childs of the macro
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = macro->items();
+ //check that there is one
+ KOMACROTEST_ASSERT( items.count(), sizetype(1) );
+ //fetch the first one
+ KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+ //How do we know that an action exist ?
+ //-> check that it is not null
+ KOMACROTEST_XASSERT(sizetype(actionptr.data()), sizetype(0));
+ //check that it has the right name
+ KOMACROTEST_ASSERT( QString(actionptr->name()), QString("testaction") );
+ //check that it has the right text
+ KOMACROTEST_ASSERT( actionptr->text(), QString("Test") );
+ //check that it has the right comment
+// KOMACROTEST_ASSERT( actionptr->comment(), QString("") );
+/*
+ //fetch the second one
+ KSharedPtr<KoMacro::Action> myfuncptr = children[1];
+ //cast it to function
+
+ KoMacro::Function* myfunc = dynamic_cast<KoMacro::Function*>( myfuncptr.data() );
+ //check that it isn?t null
+ KOMACROTEST_XASSERT((int) myfunc, 0);
+
+ //check it?s name
+ KOMACROTEST_ASSERT( QString(myfunc->name()), QString("myfunc"));
+
+ //check it?s text
+ KOMACROTEST_ASSERT( myfunc->text(), QString("My Function") );
+ //check it?s comment
+ KOMACROTEST_ASSERT( myfunc->comment(), QString("Describe what the function does") );
+ //check it?s receiver object
+ KOMACROTEST_ASSERT( myfunc->receiver(), QString("TestObject") );
+ //check it?s slot
+ KOMACROTEST_ASSERT( myfunc->slot(), QString("myslot(const QString&)") );
+
+ //exceute it
+ myfunc->activate();
+*/
+ //create another macro
+ KSharedPtr<KoMacro::Macro> yanMacro = KoMacro::Manager::self()->createMacro("testMacro2");
+
+ KOMACROTEST_ASSERT(yanMacro->parseXML(domelement),true);
+ //create two more macros
+ //KSharedPtr<KoMacro::Action> yanActionptr2 = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //KSharedPtr<KoMacro::Action> yanActionptr3 = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+
+ //check that they aren?t null
+ KOMACROTEST_XASSERT(sizetype(yanMacro.data()), sizetype(0));
+ //KOMACROTEST_XASSERT((int) yanActionptr2.data(), 0);
+ //KOMACROTEST_XASSERT((int) yanActionptr3.data(), 0);
+
+ //create a list of the children from yanMacro
+ //QValueList< KSharedPtr<KoMacro::Action> > yanChildren = yanMacro->children();
+ //check that there are two
+ //KOMACROTEST_ASSERT(yanChildren.count(), uint(2));
+/*
+ {
+ //keep oldsize
+ const int oldsize = yanChildren.count();
+ //add a new child to the macro
+ yanMacro->addChild(yanActionptr2);
+ //get the children
+ yanChildren = yanMacro->children();
+ //get count of children
+ const int size = yanChildren.count();
+ //check count has changed
+ KOMACROTEST_XASSERT(size, oldsize);
+ }
+
+ {
+ //keep oldsize
+ const int oldsize = yanChildren.count();
+ //add a new child to the macro
+ yanMacro->addChild(yanActionptr3);
+ //get the children
+ yanChildren = yanMacro->children();
+ //get count of children
+ const int size = yanChildren.count();
+ //check count has changed
+ KOMACROTEST_XASSERT(size, oldsize);
+ //check the hasChildren function
+ KOMACROTEST_ASSERT(yanMacro->hasChildren(), true);
+ }
+*/
+
+}
+
+void CommonTests::testDom() {
+//TODO: CLEANUP!!!!!!
+ kdDebug()<<"===================== testDom() ======================" << endl;
+/*
+ //create a QDomDocument
+ QDomDocument domdocument = QDomDocument();
+ //create data for various documents
+ QString const comment = "Describe what the function does";
+ QString const name = "myfunc";
+ QString const text = "My Function";
+ QString const receiver1 = "TestObject";
+ QString const receiver2 = "GibtsNich";
+
+ //create wrong Argument tag
+ domdocument.setContent(QString(
+ "<function name=\"" + name + "\" text=\"" + text + "\" comment=\"" + comment + "\" receiver=\"" + receiver1 + "\" slot=\"myslot(const QString &amp; , int)\">"
+ "<Arg0ment>Some string</Arg0ment>"
+ "<Arg0ment>12345</Arg0ment>"
+ "</function>"
+ ));
+ //create functiom
+ KSharedPtr<KoMacro::Action> macroptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //try to execute function and catch exception
+ KOMACROTEST_ASSERTEXCEPTION(KoMacro::Exception&, macroptr->activate());
+
+ //create wrong receiver
+ domdocument.setContent(QString(
+ "<function name=\"" + name + "\" text=\"" + text + "\" comment=\"" + comment + "\" receiver=\"" + receiver2 + "\" slot=\"myslot(const QString &amp; , int)\">"
+ "<argument>Some string</argument>"
+ "<argument>12345</argument>"
+ "</function>"
+ ));
+ //create function
+ macroptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //try to execute function and catch exception
+ KOMACROTEST_ASSERTEXCEPTION(KoMacro::Exception&, macroptr->activate());
+
+ //create "wrong" number of parameters
+ domdocument.setContent(QString(
+ "<function name=\"" + name + "\" text=\"" + text + "\" comment=\"" + comment + "\" receiver=\"" + receiver1 + "\" slot=\"myslot(const QString &amp; , int, double)\">"
+ "<argument>Some string</argument>"
+ "<argument>12345</argument>"
+ "<argument>12345.25</argument>"
+ "</function>"
+ ));
+ //create function
+ macroptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //try to execute function and catch exception
+ KOMACROTEST_ASSERTEXCEPTION(KoMacro::Exception&, macroptr->activate());
+
+ //create wrong function tag
+ domdocument.setContent(QString(
+ "<NoFunction name=\"" + name + "\" text=\"" + text + "\" comment=\"" + comment + "\" receiver=\"" + receiver1 + "\" slot=\"myslot(const QString &amp; , int, double)\">"
+ "<argument>Some string</argument>"
+ "<argument>12345</argument>"
+ "<argument>12345.25</argument>"
+ "</NoFunction>"
+ ));
+ //try to create function and catch exception
+ KOMACROTEST_ASSERTEXCEPTION(KoMacro::Exception&, macroptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() ));
+
+ //create empty function
+ domdocument.setContent(QString(
+ "<function name=\"\" text=\"\" comment=\"\" receiver=\"\" slot=\"\">"
+ "<argument> </argument>"
+ "<argument> </argument>"
+ "<argument> </argument>"
+ "</function>"
+ ));
+ //create function
+ macroptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //try to execute function and catch exception
+ KOMACROTEST_ASSERTEXCEPTION(KoMacro::Exception&, macroptr->activate());
+
+
+ //create empty function
+ domdocument.setContent(QString(
+ "<function>"
+ "</function>"
+ ));
+ //create function
+ macroptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //try to execute function and catch exception
+ KOMACROTEST_ASSERTEXCEPTION(KoMacro::Exception&, macroptr->activate());
+*/
+}
+
+void CommonTests::testVariables()
+{
+//TODO: CLEANUP!!!!!!
+ kdDebug()<<"===================== testVariables() ======================" << endl;
+/*
+ //create a QDomDocument
+ QDomDocument domdocument = QDomDocument();
+ //create data
+ domdocument.setContent(QString(
+ "<macro name=\"mymacro123\" text=\"My Macro 123\">"
+ "<function name=\"func1\" text=\"Function1\" receiver=\"TestObject\" slot=\"myslot(const QString &amp;)\" >"
+ "<argument>$MyArgumentVariable</argument>"
+ "<return>$MyReturnVariable</return>"
+ "</function>"
+ "</macro>"
+ ));
+
+ //create an macro
+ KSharedPtr<KoMacro::Action> macroptr = ::KoMacro::Manager::self()->createAction( domdocument.documentElement() );
+ //cast data to macro
+ KoMacro::Macro* macro = dynamic_cast<KoMacro::Macro*>( macroptr.data() );
+ //check that it is not null
+ KOMACROTEST_XASSERT((int) macro, 0);
+
+ //create a list of its children
+ QValueList< KSharedPtr<KoMacro::Action> > children = macro->children();
+ //Check that there are two children. The first child is always the returnvalue.
+ KOMACROTEST_ASSERT( children.count(), uint(2) );
+ //fetch the children
+ KSharedPtr<KoMacro::Action> func1ptr = children[1];
+
+ //create new context
+ KSharedPtr<KoMacro::Context> context = new KoMacro::Context(macroptr);
+
+ {
+ //try to execute function with non-functional variable
+ KOMACROTEST_ASSERTEXCEPTION(KoMacro::Exception&, func1ptr->activate(context));
+
+ KOMACROTEST_ASSERTEXCEPTION(KoMacro::Exception&, context->variable("$MyReturnVariable333"));
+ }
+
+ {
+ //set variable to be a QString
+ context->setVariable("$MyArgumentVariable", new KoMacro::Variable("Some string"));
+ //execute function
+ func1ptr->activate(context);
+ //fetch return value
+ KSharedPtr<KoMacro::Variable> returnvariable = context->variable("$MyReturnVariable");
+ //check that it is not null
+ KOMACROTEST_XASSERT( (int) returnvariable.data(), 0);
+ //check that it is "Some String"
+ KOMACROTEST_ASSERT(returnvariable->toString(),QString("Some string"));
+ }
+
+ {
+ //set variable to be an Int
+ context->setVariable("$MyArgumentVariable", new KoMacro::Variable( 12345 ));
+ //execute function
+ func1ptr->activate(context);
+ //fetch return value
+ KSharedPtr<KoMacro::Variable> returnvariable = context->variable("$MyReturnVariable");
+ //check that it is not null
+ KOMACROTEST_XASSERT( (int) returnvariable.data(), 0);
+ //check that it is 12345
+ KOMACROTEST_ASSERT(returnvariable->toInt(),12345);
+ }
+*/
+}
+
+#include "commontests.moc"
diff --git a/kexi/plugins/macros/tests/commontests.h b/kexi/plugins/macros/tests/commontests.h
new file mode 100644
index 00000000..a3199ce2
--- /dev/null
+++ b/kexi/plugins/macros/tests/commontests.h
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACROTEST_COMMONTESTS_H
+#define KOMACROTEST_COMMONTESTS_H
+
+#include <kunittest/tester.h>
+
+namespace KoMacroTest {
+
+ /**
+ * The common testsuite used to test common @a KoMacro
+ * functionality.
+ */
+ class CommonTests : public KUnitTest::SlotTester
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ CommonTests();
+
+ /**
+ * Destructor.
+ */
+ virtual ~CommonTests();
+
+ public slots:
+
+ /**
+ * This slot got called by KUnitTest before testing
+ * starts.
+ */
+ void setUp();
+
+ /**
+ * This slot got called by KUnitTest after all tests
+ * are done.
+ */
+ void tearDown();
+
+ /**
+ * Test the @a KoMacro::Manager functionality.
+ */
+ void testManager();
+
+ /**
+ * Test the @a KoMacro::Action functionality.
+ */
+ //void testAction();
+
+ /**
+ * Test the @a KoMacro::XmlHandler functionality.
+ */
+ void testXmlhandler();
+
+ /**
+ * Test the @a KoMacro::Function functionality.
+ */
+ void testFunction();
+
+ /**
+ * Test the @a KoMacro::Function functionality with an int.
+ */
+ void testIntFunction();
+
+ /**
+ * Test the @a KoMacro::Function functionality with a double.
+ */
+ void testDoubleFunction();
+
+ /**
+ * Test the @a KoMacro::Function functionality with a QString.
+ */
+ void testQStringFunction();
+
+ /**
+ * Test the @a KoMacro::Macro functionality.
+ */
+ void testMacro();
+
+ /**
+ * Test the @a KoMacro::Dom functionality.
+ */
+ void testDom();
+ /**
+ * Test the @a KoMacro::Variable functionality.
+ */
+ void testVariables();
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/tests/komacrotest.cpp b/kexi/plugins/macros/tests/komacrotest.cpp
new file mode 100644
index 00000000..55d017a9
--- /dev/null
+++ b/kexi/plugins/macros/tests/komacrotest.cpp
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kunittest/runner.h>
+
+static const char description[] =
+ I18N_NOOP("KoMacroTester");
+static const char version[] = "0.1";
+static KCmdLineOptions options[] =
+{
+ KCmdLineLastOption
+};
+
+int main( int argc, char** argv )
+{
+ try {
+ KAboutData about("KoMacroTester",
+ I18N_NOOP("KoMacroTester"),
+ version,
+ description,
+ KAboutData::License_LGPL,
+ "(C) 2005 Sebastian Sauer", 0, 0, "mail@dipe.org");
+
+ KCmdLineArgs::init(argc, argv, &about);
+ KCmdLineArgs::addCmdLineOptions( options );
+ KApplication app;
+
+ //create an new "Console"-runner
+ KUnitTest::Runner * runner = KUnitTest::Runner::self();
+ //start our Testsuite
+ runner->runTests();
+ //done
+ return 0;
+ }
+ // mmh seems we forgot to catch an exception...
+ catch(...) {
+ qFatal("Unhandled Exception!");
+ }
+}
diff --git a/kexi/plugins/macros/tests/komacrotestbase.h b/kexi/plugins/macros/tests/komacrotestbase.h
new file mode 100644
index 00000000..d423e086
--- /dev/null
+++ b/kexi/plugins/macros/tests/komacrotestbase.h
@@ -0,0 +1,90 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+#ifndef KOMACROTEST_BASE_H
+#define KOMACROTEST_BASE_H
+
+//Our own extended Macros from KUnittest
+/**
+* Macro to perform an equality check and exits the method if the check failed.
+*
+* @param actual The actual value.
+* @param expected The expected value.
+*/
+#define KOMACROTEST_ASSERT(actual, expected) \
+ { \
+ std::cout << QString("Testing: %1 == %2").arg(#actual).arg(#expected).latin1() << std::endl; \
+ check( __FILE__, __LINE__, #actual, actual, expected, false ); \
+ if(actual != expected) \
+ { \
+ kdWarning() << QString("==============> FAILED") << endl; \
+ return; \
+ } \
+ }
+
+/**
+* Macro to perform a check that is expected to fail and that exits the method if the check failed.
+*
+* @param actual The actual value.
+* @param notexpected The not expected value.
+*/
+#define KOMACROTEST_XASSERT(actual, notexpected) \
+ { \
+ std::cout << QString("Testing: %1 != %2").arg(#actual).arg(#notexpected).latin1() << std::endl; \
+ check( __FILE__, __LINE__, #actual, actual, notexpected, true ); \
+ if(actual == notexpected) \
+ { \
+ kdWarning() << QString("==============> FAILED") << endl; \
+ return; \
+ } \
+ }
+
+/**
+* Macro to test that @p expression throws an exception that is catched with the
+* @p exceptionCatch exception.
+*
+* @param exceptionCatch The exception that is expected to be thrown.
+* @param expression The expression that is executed within a try-catch block to
+* check for the @p exceptionCatch .
+*/
+#define KOMACROTEST_ASSERTEXCEPTION(exceptionCatch, expression) \
+ { \
+ try { \
+ expression; \
+ } \
+ catch(exceptionCatch) { \
+ setExceptionRaised(true); \
+ } \
+ if(exceptionRaised()) { \
+ success(QString(__FILE__) + "[" + QString::number(__LINE__) + "]: passed " + #expression); \
+ setExceptionRaised(false); \
+ } \
+ else { \
+ failure(QString(__FILE__) + "[" + QString::number(__LINE__) + QString("]: failed to throw an exception on: ") + #expression); \
+ return; \
+ } \
+ }
+
+#endif
+
+//Used more tha once at various places
+//names of variables from testaction
+namespace KoMacroTest {
+ static const QString TESTSTRING = "teststring";
+ static const QString TESTINT = "testint";
+ static const QString TESTBOOL = "testbool";
+}
diff --git a/kexi/plugins/macros/tests/komacrotestgui.cpp b/kexi/plugins/macros/tests/komacrotestgui.cpp
new file mode 100644
index 00000000..abf4459d
--- /dev/null
+++ b/kexi/plugins/macros/tests/komacrotestgui.cpp
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+
+#include "kunittest/runnergui.h"
+
+static const char description[] =
+ I18N_NOOP("KoMacroTestgui.");
+
+static const char version[] = "0.1";
+
+static const KCmdLineOptions options[] =
+{
+ KCmdLineLastOption
+};
+
+int main( int argc, char** argv )
+{
+ try {
+ KAboutData const about("KomacroTests", I18N_NOOP("KomacroTests"), version, description,
+ KAboutData::License_LGPL, "(C) 2005 Tobi Krebs", 0, 0,
+ "Tobi.Krebs@gmail.com");
+
+ KCmdLineArgs::init(argc, argv, &about);
+ KCmdLineArgs::addCmdLineOptions( options );
+ //create new kapplication
+ KApplication app;
+ //create new kunitrunnergui
+ KUnitTest::RunnerGUI runner(0);
+ //show the ui
+ runner.show();
+ //set ui mainwidget
+ app.setMainWidget(&runner);
+ //return exitcode of ui
+ return app.exec();
+ }
+ // mmh seems we forgot to catch an exception...
+ catch(...) {
+ qFatal("Unhandled Exception!");
+ }
+}
diff --git a/kexi/plugins/macros/tests/macroitemtests.cpp b/kexi/plugins/macros/tests/macroitemtests.cpp
new file mode 100644
index 00000000..366318e1
--- /dev/null
+++ b/kexi/plugins/macros/tests/macroitemtests.cpp
@@ -0,0 +1,243 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "macroitemtests.h"
+#include "testaction.h"
+#include "komacrotestbase.h"
+
+#include "../lib/action.h"
+#include "../lib/manager.h"
+#include "../lib/macro.h"
+#include "../lib/variable.h"
+#include "../lib/metaobject.h"
+#include "../lib/metamethod.h"
+#include "../lib/metaparameter.h"
+#include "../lib/exception.h"
+#include "../lib/macroitem.h"
+
+#include <ostream>
+
+#include <qstringlist.h>
+#include <qdom.h>
+
+#include <kdebug.h>
+#include <kunittest/runner.h>
+#include <kxmlguiclient.h>
+
+using namespace KUnitTest;
+using namespace KoMacroTest;
+
+namespace KoMacroTest {
+
+ /**
+ * Register KoMacroTest::CommonTests as TestSuite.
+ */
+
+ KUNITTEST_SUITE("KoMacroTestSuite");
+ KUNITTEST_REGISTER_TESTER(MacroitemTests);
+
+
+ class MacroitemTests::Private
+ {
+ public:
+ /**
+ * An KXMLGUIClient instance created on @a setUp() and
+ * passed to the @a KoMacro::Manager to bridge to the
+ * app-functionality.
+ */
+ KXMLGUIClient* xmlguiclient;
+
+ /**
+ * An @a TestObject instance used internaly to test
+ * handling and communication with from QObject
+ * inheritated instances.
+ */
+ TestAction* testaction;
+
+ QDomDocument* doomdocument;
+
+ KSharedPtr<KoMacro::Macro> macro;
+
+ Private()
+ : xmlguiclient(0)
+ , testaction(0)
+ , doomdocument(0)
+ , macro(0)
+ {
+ }
+ };
+}
+
+typedef QValueList< KSharedPtr<KoMacro::MacroItem> >::size_type sizetype;
+
+MacroitemTests::MacroitemTests()
+ : KUnitTest::SlotTester()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+}
+
+MacroitemTests::~MacroitemTests()
+{
+ delete d->xmlguiclient;
+ delete d;
+}
+
+void MacroitemTests::setUp()
+{
+ d->xmlguiclient = new KXMLGUIClient();
+
+ if (::KoMacro::Manager::self() == 0) {
+ ::KoMacro::Manager::init( d->xmlguiclient );
+ }
+
+ d->testaction = new TestAction();
+ ::KoMacro::Manager::self()->publishAction(d->testaction);
+
+ d->doomdocument = new QDomDocument();
+
+ QString const xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\" >"
+ "<item action=\"testaction\" >"
+ "</item>"
+ "</macro>");
+
+ d->doomdocument->setContent(xml);
+ d->macro = KoMacro::Manager::self()->createMacro("testMacro");
+ d->macro->parseXML(d->doomdocument->documentElement());
+ d->macro->execute(this);
+}
+
+void MacroitemTests::tearDown()
+{
+ delete d->macro;
+ delete d->doomdocument;
+ delete d->xmlguiclient;
+}
+
+
+void MacroitemTests::testMacro()
+{
+ kdDebug()<<"===================== testMacroitem() ======================" << endl;
+ kdDebug()<<"===================== testMacro() ======================" << endl;
+
+ //fetch Items and ..
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = d->macro->items();
+
+ //... check that there is one
+ KOMACROTEST_XASSERT( items.count(), sizetype(0) );
+}
+
+void MacroitemTests::testMacroItemString()
+{
+
+
+ kdDebug()<<"===================== testMacroItemString() ======================" << endl;
+
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = d->macro->items();
+ KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+ KSharedPtr<KoMacro::Variable> variableptr = actionptr->variable(TESTSTRING);
+
+ //create new macroitem for testing
+ KoMacro::MacroItem* macroitem = new KoMacro::MacroItem();
+ //set the action
+ macroitem->setAction(d->testaction);
+
+ //append the macroitem to testitems
+ items.append(macroitem);
+
+ //increased ??
+ KOMACROTEST_ASSERT( items.count(), sizetype(2) );
+
+ //Manipulate the macroitem
+ macroitem->setVariable(TESTSTRING, "TeStString");
+ variableptr = macroitem->variable(TESTSTRING);
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(variableptr->variant().toString(),QString("TeStString"));
+
+
+ //secondway for appending an macroitem
+ //add the manipulated macroitem
+ d->macro->addItem(macroitem);
+
+ //increased ??
+ KOMACROTEST_ASSERT( items.count(), sizetype(3));
+
+}
+
+void MacroitemTests::testMacroItemInt()
+{
+
+
+ kdDebug()<<"===================== testMacroItemInt() ======================" << endl;
+
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = d->macro->items();
+ KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+
+ //create new macroitem for testing
+ KoMacro::MacroItem* macroitem = new KoMacro::MacroItem();
+ //set the action
+ macroitem->setAction(d->testaction);
+ items.append(macroitem);
+
+ macroitem->setVariable(TESTINT,INT_MIN);
+ KSharedPtr<KoMacro::Variable> variableptr = macroitem->variable(TESTINT);
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(INT_MIN));
+
+ macroitem->setVariable(TESTINT,-1);
+ variableptr = macroitem->variable(TESTINT);
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(-1));
+
+ macroitem->setVariable(TESTINT,QVariant(0));
+ variableptr = macroitem->variable(TESTINT);
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(0));
+
+ macroitem->setVariable(TESTINT,1);
+ variableptr = macroitem->variable(TESTINT);
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(1));
+
+ macroitem->setVariable(TESTINT,INT_MAX);
+ variableptr = macroitem->variable(TESTINT);
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(INT_MAX));
+}
+
+void MacroitemTests::testMacroItemBool()
+{
+
+
+ kdDebug()<<"===================== testMacroItemBool() ======================" << endl;
+
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = d->macro->items();
+ KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+
+ //create new macroitem for testing
+ KoMacro::MacroItem* macroitem = new KoMacro::MacroItem();
+ //set the action
+ macroitem->setAction(d->testaction);
+ items.append(macroitem);
+
+ macroitem->setVariable(TESTBOOL,"false");
+ KSharedPtr<KoMacro::Variable> variableptr = macroitem->variable(TESTBOOL);
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(variableptr->variant().toBool(),false);
+}
+#include "macroitemtests.moc"
diff --git a/kexi/plugins/macros/tests/macroitemtests.h b/kexi/plugins/macros/tests/macroitemtests.h
new file mode 100644
index 00000000..3e44eebd
--- /dev/null
+++ b/kexi/plugins/macros/tests/macroitemtests.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACROTEST_ACTIONTESTS_H
+#define KOMACROTEST_ACTIONTESTS_H
+
+#include <kunittest/tester.h>
+
+namespace KoMacroTest {
+
+ /**
+ * The common testsuite used to test common @a KoMacro
+ * functionality.
+ */
+ class MacroitemTests : public KUnitTest::SlotTester
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ MacroitemTests();
+
+ /**
+ * Destructor.
+ */
+ virtual ~MacroitemTests();
+
+ public slots:
+
+ /**
+ * This slot got called by KUnitTest before testing
+ * starts.
+ */
+ void setUp();
+
+ /**
+ * This slot got called by KUnitTest after all tests
+ * are done.
+ */
+ void tearDown();
+
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testMacro();
+
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testMacroItemString();
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testMacroItemInt();
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testMacroItemBool();
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/tests/macrotests.cpp b/kexi/plugins/macros/tests/macrotests.cpp
new file mode 100644
index 00000000..ed222df2
--- /dev/null
+++ b/kexi/plugins/macros/tests/macrotests.cpp
@@ -0,0 +1,192 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "macrotests.h"
+#include "testobject.h"
+#include "testaction.h"
+#include "komacrotestbase.h"
+
+#include "../lib/action.h"
+#include "../lib/function.h"
+#include "../lib/manager.h"
+#include "../lib/macro.h"
+#include "../lib/variable.h"
+#include "../lib/metaobject.h"
+#include "../lib/metamethod.h"
+#include "../lib/metaparameter.h"
+#include "../lib/exception.h"
+#include "../lib/macroitem.h"
+
+#include <ostream>
+
+#include <qstringlist.h>
+#include <qdom.h>
+
+#include <kdebug.h>
+#include <kunittest/runner.h>
+#include <kxmlguiclient.h>
+
+using namespace KUnitTest;
+using namespace KoMacroTest;
+
+namespace KoMacroTest {
+
+ /**
+ * Register KoMacroTest::CommonTests as TestSuite.
+ */
+ KUNITTEST_SUITE("KoMacroTestSuite")
+ KUNITTEST_REGISTER_TESTER(MacroTests);
+
+ class MacroTests::Private
+ {
+ public:
+ /**
+ * An KXMLGUIClient instance created on @a setUp() and
+ * passed to the @a KoMacro::Manager to bridge to the
+ * app-functionality.
+ */
+ KXMLGUIClient* xmlguiclient;
+
+ /**
+ * An @a TestObject instance used internaly to test
+ * handling and communication with from QObject
+ * inheritated instances.
+ */
+ TestObject* testobject;
+
+ TestAction* testaction;
+
+ QDomDocument* doomdocument;
+
+ Private()
+ : xmlguiclient(0)
+ , testobject(0)
+ , testaction(0)
+ , doomdocument(0)
+ {
+ }
+ };
+}
+
+typedef QValueList< KSharedPtr<KoMacro::MacroItem> >::size_type sizetype;
+
+
+MacroTests::MacroTests()
+ : KUnitTest::SlotTester()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+}
+
+MacroTests::~MacroTests()
+{
+ delete d->xmlguiclient;
+ delete d;
+}
+
+
+void MacroTests::setUp()
+{
+ d->xmlguiclient = new KXMLGUIClient();
+ //::KoMacro::Manager::init( d->xmlguiclient );
+ if (::KoMacro::Manager::self() == 0) {
+ ::KoMacro::Manager::init( d->xmlguiclient );
+ }
+ d->testobject = new TestObject( this );
+ ::KoMacro::Manager::self()->publishObject("TestObject", d->testobject);
+
+ d->testaction = new TestAction();
+ ::KoMacro::Manager::self()->publishAction(d->testaction);
+
+ d->doomdocument = new QDomDocument();
+
+ QString const xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\" >"
+ "<item action=\"testaction\" >"
+ "</item>"
+ "</macro>");
+
+ d->doomdocument->setContent(xml);
+}
+
+void MacroTests::tearDown()
+{
+ delete d->doomdocument;
+ delete d->testobject;
+ delete d->xmlguiclient;
+}
+
+void MacroTests::testMacro()
+{
+ kdDebug()<<"===================== testMacro() ======================" << endl;
+
+ QDomElement const domelement = d->doomdocument->documentElement();
+
+ KSharedPtr<KoMacro::Macro> macro1 = KoMacro::Manager::self()->createMacro("testMacro");
+ KSharedPtr<KoMacro::Macro> macro2 = KoMacro::Manager::self()->createMacro("testMacro");
+ //Is our XML parseable ?
+ KOMACROTEST_ASSERT(macro1->parseXML(domelement),true);
+ KOMACROTEST_ASSERT(macro2->parseXML(domelement),true);
+
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(macro1.data()), sizetype(0));
+ KOMACROTEST_XASSERT(sizetype(macro2.data()), sizetype(0));
+
+ //check macro1 == macro2
+ KOMACROTEST_ASSERT(macro1->name(), macro2->name() );
+
+ //create list of KsharedPtr from the childs of the macro
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items1 = macro1->items();
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items2 = macro2->items();
+
+ //check that there is one
+ KOMACROTEST_XASSERT( items1.count(), sizetype(0) );
+ KOMACROTEST_XASSERT( items2.count(), sizetype(0) );
+
+ //check macro1 == macro2
+ KOMACROTEST_ASSERT( items1.count(), items2.count() );
+
+ //check the name
+ KOMACROTEST_ASSERT( QString(macro1->name()), QString("testMacro") );
+
+ {
+ const QString tmp1 = QString("test");
+ macro1->setName(tmp1);
+
+ //check the name changed
+ KOMACROTEST_XASSERT( QString(macro1->name()), QString("testMacro") );
+ //check the name
+ KOMACROTEST_ASSERT( QString(macro1->name()), QString("test") );
+ }
+
+ //fetch the first one
+ KSharedPtr<KoMacro::Action> actionptr = items1[0]->action();
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(actionptr.data()), sizetype(0));
+ //check that it has the right name
+ KOMACROTEST_ASSERT( QString(actionptr->name()), QString("testaction") );
+ //check that it has the right text
+ KOMACROTEST_ASSERT( actionptr->text(), QString("Test") );
+
+ //try to clear items
+ macro1->clearItems();
+ //get items
+ items1 = macro1->items();
+ //check that they are deleted
+ KOMACROTEST_ASSERT( items1.count(), sizetype(0) );
+}
+#include "macrotests.moc"
diff --git a/kexi/plugins/macros/tests/macrotests.h b/kexi/plugins/macros/tests/macrotests.h
new file mode 100644
index 00000000..ed8d0f21
--- /dev/null
+++ b/kexi/plugins/macros/tests/macrotests.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACROTEST_MACROTESTS_H
+#define KOMACROTEST_MACROTESTS_H
+
+#include <kunittest/tester.h>
+
+namespace KoMacroTest {
+
+ /**
+ * The common testsuite used to test common @a KoMacro
+ * functionality.
+ */
+ class MacroTests : public KUnitTest::SlotTester
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ MacroTests();
+
+ /**
+ * Destructor.
+ */
+ virtual ~MacroTests();
+
+ public slots:
+
+ /**
+ * This slot got called by KUnitTest before testing
+ * starts.
+ */
+ void setUp();
+
+ /**
+ * This slot got called by KUnitTest after all tests
+ * are done.
+ */
+ void tearDown();
+
+ /**
+ * Test the @a KoMacro::Action functionality.
+ */
+ void testMacro();
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/tests/testaction.cpp b/kexi/plugins/macros/tests/testaction.cpp
new file mode 100644
index 00000000..4063aa1b
--- /dev/null
+++ b/kexi/plugins/macros/tests/testaction.cpp
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "testaction.h"
+
+#include "../lib/action.h"
+#include "../lib/context.h"
+#include "../lib/macroitem.h"
+#include "../lib/variable.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+using namespace KoMacroTest;
+
+TestAction::TestAction()
+ : KoMacro::Action("testaction", "Test")
+{
+ setVariable("teststring", "Stringtest", QString("testString"));
+ setVariable("testint", "Inttest", int(0));
+ setVariable("testdouble", "Doubletest", double(0.5));
+ setVariable("testbool", "Booltest", QVariant(true,0));
+}
+
+TestAction::~TestAction()
+{
+}
+
+bool TestAction::notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name)
+{
+ Q_UNUSED(macroitem);
+ Q_UNUSED(name);
+ return true;
+}
+
+void TestAction::activate(KSharedPtr<KoMacro::Context> context)
+{
+ kdDebug() << "TestAction::activate(KSharedPtr<Context>)" << endl;
+ const QString teststring = context->variable("teststring")->variant().toString();
+ const int testint = context->variable("testint")->variant().toInt();
+ const bool testbool = context->variable("testbool")->variant().toBool();
+}
+
+#include "testaction.moc"
diff --git a/kexi/plugins/macros/tests/testaction.h b/kexi/plugins/macros/tests/testaction.h
new file mode 100644
index 00000000..9eebff3c
--- /dev/null
+++ b/kexi/plugins/macros/tests/testaction.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ * copyright (C) 2006 by Sascha Kupper (kusato@kfnv.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACROTEST_TESTACTION_H
+#define KOMACROTEST_TESTACTION_H
+
+#include <ksharedptr.h>
+
+#include "../lib/action.h"
+
+namespace KoMacro {
+ class Context;
+ class MacroItem;
+}
+
+namespace KoMacroTest {
+
+ /**
+ * This TestAction implements a @a KoMacro::Action to
+ * test the functionality provided by this class.
+ */
+ class TestAction : public KoMacro::Action
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ TestAction();
+
+ /**
+ * Destructor.
+ */
+ virtual ~TestAction();
+
+ /**
+ * This function is called, when the @a KoMacro::Variable
+ * with name @p name used within the @a KoMacro::MacroItem
+ * @p macroitem got changed.
+ *
+ * @param macroitem The @a KoMacro::MacroItem instance where
+ * the variable defined with @p name is located in.
+ * @param name The name the @a KoMacro::Variable has.
+ * @return true if the update was successfully else false
+ * is returned.
+ */
+ virtual bool notifyUpdated(KSharedPtr<KoMacro::MacroItem> macroitem, const QString& name);
+
+ public slots:
+
+ /**
+ * Called if the @a Action should be executed within the
+ * defined @p context .
+ */
+ virtual void activate(KSharedPtr<KoMacro::Context> context);
+
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/tests/testobject.cpp b/kexi/plugins/macros/tests/testobject.cpp
new file mode 100644
index 00000000..39cadb7a
--- /dev/null
+++ b/kexi/plugins/macros/tests/testobject.cpp
@@ -0,0 +1,117 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "testobject.h"
+
+//#include "../lib/manager.h"
+//#include "../lib/action.h"
+//#include "../lib/function.h"
+//#include "../lib/macro.h"
+//#include "../lib/metaobject.h"
+
+//#include <qstringlist.h>
+//#include <qdom.h>
+
+#include <kdebug.h>
+//#include <kxmlguiclient.h>
+
+using namespace KoMacroTest;
+
+namespace KoMacroTest {
+
+ /**
+ * @internal d-pointer class to be more flexible on future extension of the
+ * functionality without to much risk to break the binary compatibility.
+ */
+ class TestObject::Private
+ {
+ public:
+
+ /**
+ * The @a KUnitTest::Tester instance that likes to test
+ * our TestObject instance.
+ */
+ KUnitTest::Tester* const tester;
+ Private(KUnitTest::Tester* const tester)
+ : tester(tester)
+ {
+ }
+ };
+
+}
+
+TestObject::TestObject(KUnitTest::Tester* const tester)
+ : QObject()
+ , d( new Private(tester) ) // create the private d-pointer instance.
+{
+ setName("TestObject");
+}
+
+TestObject::~TestObject()
+{
+ delete d;
+}
+
+//testObject without arguments
+void TestObject::myslot()
+{
+ QString s = "CALLED => TestObject::myslot()";
+ //be loud
+ kdDebug() << s << endl;
+ //add some extra Debuginfos to TestResults see tester.h
+ d->tester->results()->addDebugInfo(s);
+}
+
+//testobject with QString and int argument
+//int is returnvalue
+int TestObject::myslot(const QString&, int i)
+{
+ QString s = "CALLED => TestObject::myslot(const QString&, int)";
+ //be loud
+ kdDebug() << s << endl;
+ //add some extra debuginfos to TestResults
+ d->tester->results()->addDebugInfo(s);
+ return i;
+}
+
+//testobject with QString argument
+//QString is returnvalue
+QString TestObject::myslot(const QString& s)
+{
+ QString t = QString("CALLED => TestObject::myslot(const QString& s) s=%1").arg(s);
+ //be loud
+ kdDebug() << t << endl;
+ //add some extra Debuginfos to TestResults
+ d->tester->results()->addDebugInfo(t);
+ return s;
+}
+
+//testobject with QString and double argument
+//double is returnvalue
+double TestObject::myslot(const QString&, double d)
+{
+ QString s = "CALLED => TestObject::myslot(const QString&, double)";
+ //be loud
+ kdDebug() << s << endl;
+ //add some extra Debuginfos to TestResults
+ this->d->tester->results()->addDebugInfo(s);
+ return d;
+}
+
+#include "testobject.moc"
diff --git a/kexi/plugins/macros/tests/testobject.h b/kexi/plugins/macros/tests/testobject.h
new file mode 100644
index 00000000..da5e8ae2
--- /dev/null
+++ b/kexi/plugins/macros/tests/testobject.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACROTEST_TESTOBJECT_H
+#define KOMACROTEST_TESTOBJECT_H
+
+#include <qobject.h>
+#include <kunittest/tester.h>
+
+namespace KoMacroTest {
+
+ /**
+ * The TestObject class is used to test handling and communication
+ * of external from QObject inheritated classes.
+ */
+ class TestObject : public QObject
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ *
+ * @param tester The @a KUnitTest::Tester instance
+ * that likes to test our TestObject instance.
+ */
+ TestObject(KUnitTest::Tester* const tester);
+
+ /**
+ * Destructor.
+ */
+ virtual ~TestObject();
+
+ public slots:
+
+ /**
+ * This slot got published to the KoMacro-framework
+ * and will be called to test the functionality.
+ */
+ void myslot();
+
+ /**
+ * This slot got published to the KoMacro-framework
+ * and will be called to test the functionality.
+ */
+ int myslot(const QString&, int);
+
+ /**
+ * This slot got published to the KoMacro-framework
+ * and will be called to test the functionality.
+ */
+ QString myslot(const QString&);
+
+ /**
+ * This slot got published to the KoMacro-framework
+ * and will be called to test the functionality.
+ * @return is @param d
+ */
+ double myslot(const QString&, double d);
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/tests/variabletests.cpp b/kexi/plugins/macros/tests/variabletests.cpp
new file mode 100644
index 00000000..8bc8d9c7
--- /dev/null
+++ b/kexi/plugins/macros/tests/variabletests.cpp
@@ -0,0 +1,236 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "variabletests.h"
+#include "testobject.h"
+#include "testaction.h"
+#include "komacrotestbase.h"
+
+#include "../lib/action.h"
+#include "../lib/function.h"
+#include "../lib/manager.h"
+#include "../lib/macro.h"
+#include "../lib/variable.h"
+#include "../lib/metaobject.h"
+#include "../lib/metamethod.h"
+#include "../lib/metaparameter.h"
+#include "../lib/exception.h"
+#include "../lib/macroitem.h"
+
+#include <ostream>
+
+#include <qstringlist.h>
+#include <qdom.h>
+
+#include <kdebug.h>
+#include <kunittest/runner.h>
+#include <kxmlguiclient.h>
+
+using namespace KUnitTest;
+using namespace KoMacroTest;
+
+namespace KoMacroTest {
+
+ /**
+ * Register KoMacroTest::CommonTests as TestSuite.
+ */
+
+ KUNITTEST_SUITE("KoMacroTestSuite");
+ KUNITTEST_REGISTER_TESTER(VariableTests);
+
+
+ class VariableTests::Private
+ {
+ public:
+ /**
+ * An KXMLGUIClient instance created on @a setUp() and
+ * passed to the @a KoMacro::Manager to bridge to the
+ * app-functionality.
+ */
+ KXMLGUIClient* xmlguiclient;
+
+ /**
+ * An @a TestObject instance used internaly to test
+ * handling and communication with from QObject
+ * inheritated instances.
+ */
+ TestAction* testaction;
+
+ QDomDocument* doomdocument;
+
+ KSharedPtr<KoMacro::Macro> macro;
+
+ Private()
+ : xmlguiclient(0)
+ , testaction(0)
+ , doomdocument(0)
+ , macro(0)
+ {
+ }
+ };
+}
+
+typedef QValueList< KSharedPtr<KoMacro::MacroItem> >::size_type sizetype;
+
+/******************************************************************************
+* This is an xtra big TODO:
+* - get rid of all double declarations
+* - create xtra-class for Variable/Macroitem tests
+* - add comments
+******************************************************************************/
+VariableTests::VariableTests()
+ : KUnitTest::SlotTester()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+}
+
+VariableTests::~VariableTests()
+{
+ delete d->xmlguiclient;
+ delete d;
+}
+
+
+void VariableTests::setUp()
+{
+ d->xmlguiclient = new KXMLGUIClient();
+
+ if (::KoMacro::Manager::self() == 0) {
+ ::KoMacro::Manager::init( d->xmlguiclient );
+ }
+
+ d->testaction = new TestAction();
+ ::KoMacro::Manager::self()->publishAction(d->testaction);
+
+ d->doomdocument = new QDomDocument();
+
+ QString const xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\" >"
+ "<item action=\"testaction\" >"
+ "</item>"
+ "</macro>");
+
+ d->doomdocument->setContent(xml);
+ d->macro = KoMacro::Manager::self()->createMacro("testMacro");
+ d->macro->parseXML(d->doomdocument->documentElement());
+ d->macro->execute(this);
+}
+
+void VariableTests::tearDown()
+{
+ delete d->macro;
+ delete d->doomdocument;
+ delete d->xmlguiclient;
+}
+
+void VariableTests::testMacro()
+{
+ kdDebug()<<"===================== testVariable() ===================" << endl;
+ kdDebug()<<"===================== testMacro() ======================" << endl;
+
+ //fetch Items and ..
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = d->macro->items();
+
+ //... check that there is one
+ KOMACROTEST_XASSERT( items.count(), sizetype(0) );
+}
+
+void VariableTests::testVariableString() {
+ kdDebug()<<"===================== testVariableString() ======================" << endl;
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = d->macro->items();
+ KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+
+ //fetch the "teststring"-variable
+ KSharedPtr<KoMacro::Variable> variableptr = actionptr->variable(TESTSTRING);
+ //So there is a variable, does hasVariable() work ?
+ KOMACROTEST_ASSERT(actionptr->hasVariable(TESTSTRING),true);
+ //check count of variables
+ KOMACROTEST_ASSERT(sizetype(actionptr->variableNames().count()),sizetype(4));
+ //remove one
+ actionptr->removeVariable(TESTSTRING);
+ //Decreased ??
+ KOMACROTEST_ASSERT(sizetype(actionptr->variableNames().count()),sizetype(3));
+ //add one
+ actionptr->setVariable(variableptr);
+ //increased ??
+ KOMACROTEST_ASSERT(sizetype(actionptr->variableNames().count()),sizetype(4));
+
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ //check that it is "testString"
+ KOMACROTEST_ASSERT(variableptr->variant().toString(),QString("testString"));
+
+ actionptr->setVariable("teststring", "STRINGTEST", "TestString");
+ variableptr = actionptr->variable("teststring");
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ //check that it is " "
+ KOMACROTEST_ASSERT(variableptr->variant().toString(),QString("TestString"));
+}
+
+void VariableTests::testVariableInt() {
+ kdDebug()<<"===================== testVariableInt() ======================" << endl;
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = d->macro->items();
+ KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+
+ //fetch the "testint"-variable
+ KSharedPtr<KoMacro::Variable> variableptr = actionptr->variable(TESTINT);
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ //check that it is 0
+ KOMACROTEST_ASSERT(variableptr->variant().toInt(),int(0));
+
+ actionptr->setVariable(TESTINT,"INTTEST",INT_MAX);
+ variableptr = actionptr->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(INT_MAX));
+
+ actionptr->setVariable(TESTINT,"INTTEST",INT_MAX+1);
+ variableptr = actionptr->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(INT_MAX+1));
+
+ actionptr->setVariable(TESTINT,"INTTEST",INT_MIN);
+ variableptr = actionptr->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(INT_MIN));
+
+ actionptr->setVariable(TESTINT,"INTTEST",INT_MIN-1);
+ variableptr = actionptr->variable("testint");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(sizetype(variableptr->variant().toInt()),sizetype(INT_MIN-1));
+}
+
+void VariableTests::testVariableBool() {
+ kdDebug()<<"===================== testVariableBool() ======================" << endl;
+ QValueList< KSharedPtr<KoMacro::MacroItem> >& items = d->macro->items();
+ KSharedPtr<KoMacro::Action> actionptr = items[0]->action();
+
+ //fetch the "testbool"-variable
+ KSharedPtr<KoMacro::Variable> variableptr = actionptr->variable(TESTBOOL);
+ //check that it is not null
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ //check that it is " "
+ KOMACROTEST_ASSERT(variableptr->variant().toBool(),true);
+
+ actionptr->setVariable("testbool","BOOLTEST", "false");
+ variableptr = actionptr->variable("testbool");
+ KOMACROTEST_XASSERT(sizetype(variableptr.data()), sizetype(0));
+ KOMACROTEST_ASSERT(variableptr->variant().toBool(),false);
+}
+#include "variabletests.moc"
diff --git a/kexi/plugins/macros/tests/variabletests.h b/kexi/plugins/macros/tests/variabletests.h
new file mode 100644
index 00000000..5bc7f144
--- /dev/null
+++ b/kexi/plugins/macros/tests/variabletests.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2005 by Sebastian Sauer (mail@dipe.org)
+ * copyright (C) 2005 by Tobi Krebs (tobi.krebs@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACROTEST_VARIABLETESTS_H
+#define KOMACROTEST_VARIABLETESTS_H
+
+#include <kunittest/tester.h>
+
+namespace KoMacroTest {
+
+ /**
+ * The common testsuite used to test common @a KoMacro
+ * functionality.
+ */
+ class VariableTests : public KUnitTest::SlotTester
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ VariableTests();
+
+ /**
+ * Destructor.
+ */
+ virtual ~VariableTests();
+
+ public slots:
+
+ /**
+ * This slot got called by KUnitTest before testing
+ * starts.
+ */
+ void setUp();
+
+ /**
+ * This slot got called by KUnitTest after all tests
+ * are done.
+ */
+ void tearDown();
+
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testMacro();
+
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testVariableString();
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testVariableInt();
+ /**
+ * Subtest for the @a KoMacro::Action functionality.
+ */
+ void testVariableBool();
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+ };
+
+}
+
+#endif
diff --git a/kexi/plugins/macros/tests/xmlhandlertests.cpp b/kexi/plugins/macros/tests/xmlhandlertests.cpp
new file mode 100644
index 00000000..9a0ebcb1
--- /dev/null
+++ b/kexi/plugins/macros/tests/xmlhandlertests.cpp
@@ -0,0 +1,619 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "xmlhandlertests.h"
+#include "testaction.h"
+#include "komacrotestbase.h"
+
+#include "../lib/action.h"
+#include "../lib/manager.h"
+#include "../lib/macro.h"
+#include "../lib/variable.h"
+#include "../lib/macroitem.h"
+
+#include <ostream>
+#include <cfloat>
+
+#include <qdom.h>
+
+#include <kdebug.h>
+#include <kunittest/runner.h>
+#include <kxmlguiclient.h>
+
+using namespace KUnitTest;
+using namespace KoMacroTest;
+
+namespace KoMacroTest {
+
+ /**
+ * Register KoMacroTest::CommonTests as TestSuite.
+ */
+ KUNITTEST_SUITE("KoMacroTestSuite")
+ KUNITTEST_REGISTER_TESTER(XMLHandlerTests);
+
+ class XMLHandlerTests::Private
+ {
+ public:
+ /**
+ * An KXMLGUIClient instance created on @a setUp() and
+ * passed to the @a KoMacro::Manager to bridge to the
+ * app-functionality.
+ */
+ KXMLGUIClient* xmlguiclient;
+
+ /**
+ * An @a TestObject instance used internaly to test
+ * handling and communication with from QObject
+ * inheritated instances.
+ */
+ KSharedPtr<KoMacro::Action> testaction;
+
+ Private()
+ : xmlguiclient(0)
+ , testaction(0)
+ {
+ }
+ };
+}
+
+XMLHandlerTests::XMLHandlerTests()
+ : KUnitTest::SlotTester()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+}
+
+XMLHandlerTests::~XMLHandlerTests()
+{
+ delete d->xmlguiclient;
+ delete d;
+}
+
+
+void XMLHandlerTests::setUp()
+{
+ d->xmlguiclient = new KXMLGUIClient();
+
+ //Singelton more or less ...
+ if (::KoMacro::Manager::self() == 0) {
+ ::KoMacro::Manager::init( d->xmlguiclient );
+ }
+
+ d->testaction = new TestAction();
+ ::KoMacro::Manager::self()->publishAction(d->testaction);
+}
+
+void XMLHandlerTests::tearDown()
+{
+ delete d->xmlguiclient;
+}
+
+/**
+* Test the @a KoMacro::XMLHandler parseXML() and toXML()-function.
+*/
+void XMLHandlerTests::testParseAndToXML()
+{
+ kdDebug()<<"===================== testParseAndToXML() ======================" << endl;
+
+ // 1.Test - Correct DomElement.
+ testCorrectDomElement();
+ // 2.Test - XML-document with bad root element.
+ testBadRoot();
+ // 3.Test - XML-document with a missing Variable.
+ testMissingVariable();
+ // 4.Test - One more Variable in XML-Document.
+ testMoreVariables();
+ // 5.Test - XML-document with wrong macro-xmlversion.
+ testWrongVersion();
+ // 6.Test - XML-document if it has a wrong structure like wrong parathesis
+ // or missing end tag.
+ testWrongXMLStruct();
+ // 7.Test-XML-document with maximum field-size.
+ testMaxNum();
+ // 8.Test-XML-document with maximum+1 field-size.
+ testMaxNum2();
+ // 9.Test-XML-document with minimum field-size.
+ testMinNum();
+ // 10.Test-XML-document with minimum-1 field-size.
+ testMinNum2();
+ // 11.Test - With a to big number.
+ testBigNumber();
+ // 12.Test - With two MacroItems.
+ testTwoMacroItems();
+}
+
+/***************************************************************************
+* Begin of Sub-methos of testParseXML().
+***************************************************************************/
+// 1.Test - Correct DomElement.
+void XMLHandlerTests::testCorrectDomElement()
+{
+ // Local Init
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</macro>");
+ // Set the XML-document with the above string.
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ // Is our XML parseable by calling parseXML()?
+ KOMACROTEST_ASSERT(macro->parseXML(elem),true);
+
+ // Is the parsen content in the Macro correct ?
+ QMap<QString,bool> isvariableok;
+ isvariableok["teststring"] = true;
+ isvariableok["testint"] = true;
+ isvariableok["testbool"] = true;
+ isvariableok["testdouble"] = true;
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+
+ // Transform back by calling toXML().
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,false,true,isvariableok);
+
+ // Test the Compare-method when a Variable will change, it must fail.
+ macro->items().first()->variable("teststring")->setVariant("bla");
+ isvariableok.replace("teststring",false);
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+}
+
+// 2.Test - XML-document with bad root element.
+void XMLHandlerTests::testBadRoot()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<maro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</maro>");
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_XASSERT(macro->parseXML(elem),true);
+
+ //no assertMacroContentEqToXML(), because parsing failed.
+ assertMacroContentEqToXML(macro,elem,true,false,QMap<QString,bool>());
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,true,false,QMap<QString,bool>());
+}
+
+// 3.Test - XML-document with a missing Variable.
+void XMLHandlerTests::testMissingVariable()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</macro>");
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(elem),true);
+
+ QMap<QString,bool> isvariableok;
+ isvariableok["teststring"] = true;
+ isvariableok["testint"] = true;
+ isvariableok["testdouble"] = true;
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,false,true,isvariableok);
+}
+
+// 4.Test - One more Variable in XML-Document.
+void XMLHandlerTests::testMoreVariables()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "<variable name=\"testbla\" >somethingwrong</variable>"
+ "</item>"
+ "</macro>");
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(elem),true);
+
+ QMap<QString,bool> isvariableok;
+ isvariableok["teststring"] = true;
+ isvariableok["testint"] = true;
+ isvariableok["testbool"] = true;
+ isvariableok["testdouble"] = true;
+ isvariableok["testbla"] = true;
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,false,true,isvariableok);
+}
+
+// 5.Test - XML-document with wrong macro-xmlversion.
+void XMLHandlerTests::testWrongVersion()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"2\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</macro>");
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_XASSERT(macro->parseXML(elem),true);
+
+ //no assertMacroContentEqToXML(), because parsing failed.
+ assertMacroContentEqToXML(macro,elem,true,false,QMap<QString,bool>());
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,true,false,QMap<QString,bool>());
+}
+
+// 6.Test - XML-document if it has a wrong structure like wrong parathesis
+// or missing end tag.
+void XMLHandlerTests::testWrongXMLStruct()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "macro xmlversion=\"1\">>"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "</item>"
+ "</macro>");
+ KOMACROTEST_XASSERT(doomdocument.setContent(xml),true);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_XASSERT(macro->parseXML(elem),true);
+
+ //no assertMacroContentEqToXML(), because parsing failed.
+ assertMacroContentEqToXML(macro,elem,true,false,QMap<QString,bool>());
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,true,false,QMap<QString,bool>());
+}
+
+// 7.Test-XML-document with maximum field-size.
+void XMLHandlerTests::testMaxNum()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" > %1 </variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" > %2 </variable>"
+ "</item>"
+ "</macro>").arg(INT_MAX).arg(DBL_MAX);
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(elem),true);
+
+ QMap<QString,bool> isvariableok;
+ isvariableok["teststring"] = true;
+ isvariableok["testint"] = true;
+ isvariableok["testbool"] = true;
+ isvariableok["testdouble"] = true;
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+
+ QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,false,true,isvariableok);
+}
+
+// 8.Test-XML-document with maximum+1 field-size.
+void XMLHandlerTests::testMaxNum2()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" > %1 </variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" > %2 </variable>"
+ "</item>"
+ "</macro>").arg(INT_MAX+1).arg(DBL_MAX+1);
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(elem),true);
+
+ QMap<QString,bool> isvariableok;
+ isvariableok["teststring"] = true;
+ isvariableok["testint"] = true;
+ isvariableok["testbool"] = true;
+ isvariableok["testdouble"] = true;
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,false,true,isvariableok);
+}
+
+// 9.Test-XML-document with minimum field-size.
+void XMLHandlerTests::testMinNum()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" > %1 </variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" > %2 </variable>"
+ "</item>"
+ "</macro>").arg(INT_MIN).arg(DBL_MIN);
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(elem),true);
+
+ QMap<QString,bool> isvariableok;
+ isvariableok["teststring"] = true;
+ isvariableok["testint"] = true;
+ isvariableok["testbool"] = true;
+ isvariableok["testdouble"] = true;
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,false,true,isvariableok);
+}
+
+// 10.Test-XML-document with minimum+1 field-size.
+void XMLHandlerTests::testMinNum2()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" > %1 </variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" > %2 </variable>"
+ "</item>"
+ "</macro>").arg(INT_MIN-1).arg(DBL_MIN-1);
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(elem),true);
+
+ QMap<QString,bool> isvariableok;
+ isvariableok["teststring"] = true;
+ isvariableok["testint"] = true;
+ isvariableok["testbool"] = true;
+ isvariableok["testdouble"] = true;
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,false,true,isvariableok);
+}
+
+// 11.Test - With a to big number.
+void XMLHandlerTests::testBigNumber()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" > 0123456789012345678901234567890123456789 </variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" > %1 </variable>"
+ "</item>"
+ "</macro>").arg(DBL_MAX+1);
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(elem),true);
+
+ QMap<QString,bool> isvariableok;
+ isvariableok["teststring"] = true;
+ isvariableok["testint"] = true;
+ isvariableok["testbool"] = true;
+ isvariableok["testdouble"] = true;
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,false,true,isvariableok);
+}
+
+// 12.Test - With two MacroItems.
+void XMLHandlerTests::testTwoMacroItems()
+{
+ KSharedPtr<KoMacro::Macro> macro = KoMacro::Manager::self()->createMacro("testMacro");
+ QDomDocument doomdocument;
+
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "<variable name=\"testbla\" >somethingwrong</variable>"
+ "</item>"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >testBBstring2</variable>"
+ "<variable name=\"testint\" >4</variable>"
+ "<variable name=\"testbool\" >false</variable>"
+ "<variable name=\"testdouble\" >0.7</variable>"
+ "<variable name=\"testbla\" >somethingwrong2</variable>"
+ "</item>"
+ "</macro>");
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+ KOMACROTEST_ASSERT(macro->parseXML(elem),true);
+
+ QMap<QString,bool> isvariableok;
+ isvariableok["teststring"] = true;
+ isvariableok["testint"] = true;
+ isvariableok["testbool"] = true;
+ isvariableok["testdouble"] = true;
+ assertMacroContentEqToXML(macro,elem,false,true,isvariableok);
+
+ const QDomElement elem2 = macro->toXML();
+ assertMacroContentEqToXML(macro,elem2,false,true,isvariableok);
+}
+/***************************************************************************
+* End of Sub-methos of testParseAndToXML().
+***************************************************************************/
+
+/**
+* Compares a XML-Element with a Macro. Call sub-asserts.
+* @p macro The parsen @a Macro.
+* @p elem The given @a QDomElement which is parsen.
+* @p isitemsempty Bool for expectation of an empty @a MacroItem -List.
+* @p isactionset Bool for expectation that the @a Action -names are equal.
+* @p isvariableok QMap of Bools for comparing each @a Variable .
+*/
+void XMLHandlerTests::assertMacroContentEqToXML(const KSharedPtr<KoMacro::Macro> macro,
+ const QDomElement& elem,
+ const bool isitemsempty,
+ const bool isactionset,
+ const QMap<QString, bool> isvariableok)
+{
+ // Make an Iterator over the MacroItems of the Macro.
+ const QValueList<KSharedPtr<KoMacro::MacroItem > > macroitems = macro->items();
+ QValueList<KSharedPtr<KoMacro::MacroItem > >::ConstIterator
+ mit(macroitems.constBegin()), end(macroitems.constEnd());
+
+ //1.comparison - Is the MacroItem-list empty?
+ {
+ if( isitemsempty ) {
+ KOMACROTEST_XASSERT(macroitems.empty(),false);
+ kdDebug() << "There is no correct MacroItem parsen." << endl;
+ return;
+ }
+ else {
+ KOMACROTEST_ASSERT(macroitems.empty(),false);
+ }
+ }
+
+ // Got to the first item-elements of the elem (there is only one in the tests).
+ QDomNode itemnode = elem.firstChild();
+
+ // Iterate over the MacroItems and item-elements.
+ while(mit != end && ! itemnode.isNull()) {
+ const KSharedPtr<KoMacro::MacroItem> macroitem = *mit;
+ const QDomElement itemelem = itemnode.toElement();
+
+ //2.comparison - Is the Action-name equal?
+ {
+ if( ! isactionset) {
+ KOMACROTEST_XASSERT(macroitem->action()->name() == itemelem.attribute("action"),true);
+ kdDebug() << "Action-name not equal: "
+ << macroitem->action()->name()
+ << " != " << itemelem.attribute("action") << endl;
+ return;
+ }
+ else {
+ KOMACROTEST_ASSERT(macroitem->action()->name() == itemelem.attribute("action"),true);
+ }
+ }
+
+ // Go down to MacroItem->Variable and item->variable and compare them.
+ QMap<QString, KSharedPtr<KoMacro::Variable > > mvariables = macroitem->variables();
+ QDomNode varnode = itemelem.firstChild();
+
+ while ( ! varnode.isNull()) {
+ const QDomElement varelem = varnode.toElement();
+ const KSharedPtr<KoMacro::Variable> varitem = mvariables.find(varelem.attribute("name")).data();
+
+ //3.comparison - Is the content of the Variable
+ // in the MacroItem and and item equal?
+ {
+ const bool var = *isvariableok.find(varelem.attribute("name"));
+ if( ! var ) {
+ KOMACROTEST_XASSERT(varitem->variant() == QVariant(varelem.text()), !var);
+ kdDebug() << "The content of the Variable: " << varitem->name()
+ << " is not equal." << varitem->variant()
+ << "!=" << varelem.text() << endl;
+ }
+ else {
+ KOMACROTEST_ASSERT(varitem->variant() == QVariant(varelem.text()), var);
+ }
+
+ }
+
+ // Erase the MacroItem from the map, because it is parsen correctly.
+ mvariables.erase(varitem->name());
+ // Go to next Variable in node-tree.
+ varnode = varnode.nextSibling();
+ }
+
+ //4.comparison - Is every MacroItem parsen?
+ {
+ KOMACROTEST_ASSERT(mvariables.empty(),true);
+ kdDebug() << "There are non-filled variable in the MacroItem: " << mvariables.count() <<endl;
+ }
+
+ // Go to next MacroItem and next item-element.
+ mit++;
+ itemnode = itemnode.nextSibling();
+ }
+}
+
+// Prints a QMap of Variables to kdDebug().
+void XMLHandlerTests::printMvariables(const QMap<QString, KSharedPtr<KoMacro::Variable > > mvariables, const QString s)
+{
+ //QValueList<QString>::ConstIterator kit (keys.constBegin()), end(keys.constEnd());
+ QMap<QString, KSharedPtr<KoMacro::Variable > >::ConstIterator mvit (mvariables.constBegin()), end(mvariables.constEnd());
+ while(mvit != end){
+ const KoMacro::Variable * v = *mvit;
+ kdDebug() << s << ": " << v->name() << endl;
+ mvit++;
+ }
+}
+
+#include "xmlhandlertests.moc"
diff --git a/kexi/plugins/macros/tests/xmlhandlertests.h b/kexi/plugins/macros/tests/xmlhandlertests.h
new file mode 100644
index 00000000..c78a8c79
--- /dev/null
+++ b/kexi/plugins/macros/tests/xmlhandlertests.h
@@ -0,0 +1,122 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACROTEST_XMLHandlerTests_H
+#define KOMACROTEST_XMLHandlerTests_H
+
+#include <kunittest/tester.h>
+#include "../lib/macro.h"
+
+namespace KoMacroTest {
+
+ /**
+ * The common testsuite used to test common @a KoMacro
+ * functionality.
+ */
+ class XMLHandlerTests : public KUnitTest::SlotTester
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ XMLHandlerTests();
+
+ /**
+ * Destructor.
+ */
+ virtual ~XMLHandlerTests();
+
+ public slots:
+
+ /**
+ * This slot got called by KUnitTest before testing
+ * starts.
+ */
+ void setUp();
+
+ /**
+ * This slot got called by KUnitTest after all tests
+ * are done.
+ */
+ void tearDown();
+
+ /**
+ * Test the @a KoMacro::XMLHandler parseXML()-function.
+ */
+ void testParseAndToXML();
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+
+ /**
+ * Compares a XML-Element with a Macro. Call sub-asserts.
+ * @p macro The parsen @a Macro.
+ * @p domelement The given @a QDomElement which is parsen.
+ * @p isitemsempty Bool for expectation of an empty @a MacroItem -List.
+ * @p isactionset Bool for expectation that the @a Action -names are equal.
+ * @p isvariableok QMap of Bools for comparing each @a Variable .
+ */
+ void assertMacroContentEqToXML(const KSharedPtr<KoMacro::Macro> macro,
+ const QDomElement& elem,
+ const bool isitemsempty,
+ const bool isactionset,
+ const QMap<QString, bool> isvariableok);
+
+ // Prints a QMap of Variables to kdDebug().
+ void printMvariables(const QMap<QString, KSharedPtr<KoMacro::Variable > > mvariables, const QString s);
+
+ /**
+ * Sub-methods of testParseXML() and testToXML().
+ * Test the correct parsing of a @a QDomElement into a @a Macro
+ * respectively expected failure of parsing. Then transform it
+ * back and compare it.
+ */
+ // 1.Test - Correct DomElement.
+ void testCorrectDomElement();
+ // 2.Test - XML-document with bad root element.
+ void testBadRoot();
+ // 3.Test - XML-document with a missing Variable.
+ void testMissingVariable();
+ // 4.Test - One more Variable in XML-Document.
+ void testMoreVariables();
+ // 5.Test - XML-document with wrong macro-xmlversion.
+ void testWrongVersion();
+ // 6.Test - XML-document if it has a wrong structure like
+ // wrong parathesis or missing end tag.
+ void testWrongXMLStruct();
+ // 7.Test-XML-document with maximum field-size.
+ void testMaxNum();
+ // 8.Test-XML-document with maximum+1 field-size.
+ void testMaxNum2();
+ // 9.Test-XML-document with minimum field-size.
+ void testMinNum();
+ // 10.Test-XML-document with minimum-1 field-size.
+ void testMinNum2();
+ // 11.Test - With a to big number.
+ void testBigNumber();
+ // 12.Test - With two MacroItems.
+ void testTwoMacroItems();
+ };
+}
+
+#endif
diff --git a/kexi/plugins/macros/tests/xmlhandlertests2.cpp b/kexi/plugins/macros/tests/xmlhandlertests2.cpp
new file mode 100644
index 00000000..2234eaae
--- /dev/null
+++ b/kexi/plugins/macros/tests/xmlhandlertests2.cpp
@@ -0,0 +1,1161 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "xmlhandlertests2.h"
+#include "testaction.h"
+#include "komacrotestbase.h"
+
+#include "../lib/action.h"
+#include "../lib/manager.h"
+#include "../lib/macro.h"
+#include "../lib/variable.h"
+#include "../lib/macroitem.h"
+
+#include <ostream>
+#include <cfloat>
+
+#include <qdom.h>
+
+#include <kdebug.h>
+#include <kunittest/runner.h>
+#include <kxmlguiclient.h>
+
+using namespace KUnitTest;
+using namespace KoMacroTest;
+
+namespace KoMacroTest {
+
+ /**
+ * Register KoMacroTest::CommonTests as TestSuite.
+ */
+ KUNITTEST_SUITE("KoMacroTestSuite")
+ KUNITTEST_REGISTER_TESTER(XMLHandlerTests2);
+
+ class XMLHandlerTests2::Private
+ {
+ public:
+ /**
+ * An KXMLGUIClient instance created on @a setUp() and
+ * passed to the @a KoMacro::Manager to bridge to the
+ * app-functionality.
+ */
+ KXMLGUIClient* xmlguiclient;
+
+ /**
+ * @a Macro instance as a container for the macroitems;
+ */
+ KSharedPtr<KoMacro::Macro> macro; // container for manually created items
+ KSharedPtr<KoMacro::Macro> macro2; // container for parsen items
+ KSharedPtr<KoMacro::Macro> macro3; // container for parsen items after back-converting by toXML() and again parseXML()
+
+ /**
+ * An @a TestObject instance used internaly to test
+ * handling and communication with from QObject
+ * inheritated instances.
+ */
+ KSharedPtr<KoMacro::Action> testaction;
+ KSharedPtr<KoMacro::Action> action2; // action of the parsen macro2
+ KSharedPtr<KoMacro::Action> action3; // action of the parsen macro3
+ KSharedPtr<KoMacro::Action> testaction_2; // for test12
+ KSharedPtr<KoMacro::Action> action2_2; // action of the parsen macro2, for test12
+ KSharedPtr<KoMacro::Action> action3_2; // action of the parsen macro3, for test12
+
+ /**
+ * Represents a @a QValuList of @a MacroItem which are parsen in the
+ * correspondig @a Macro .
+ */
+ QValueList<KSharedPtr<KoMacro::MacroItem > > macroitems2; // items of macro2
+ QValueList<KSharedPtr<KoMacro::MacroItem > > macroitems3; // items of macro3
+
+ /**
+ * @a MacroItem instances which ist fillen manually from the given XML
+ * and parsen by the @a XMLHandler over the XML.
+ */
+ KSharedPtr<KoMacro::MacroItem> macroitem; // created manually from XML
+ KSharedPtr<KoMacro::MacroItem> macroitem2; // parsen from XML in macro2
+ KSharedPtr<KoMacro::MacroItem> macroitem3; // parsen from XML in macro3
+ KSharedPtr<KoMacro::MacroItem> macroitem_2; // created manually from XML, for test12
+ KSharedPtr<KoMacro::MacroItem> macroitem2_2;// parsen from XML in macro2, for test12
+ KSharedPtr<KoMacro::MacroItem> macroitem3_2;// parsen from XML in macro3, for test12
+
+ Private()
+ : xmlguiclient(0)
+ , testaction(0)
+ {
+ }
+ };
+}
+
+XMLHandlerTests2::XMLHandlerTests2()
+ : KUnitTest::SlotTester()
+ , d( new Private() ) // create the private d-pointer instance.
+{
+}
+
+XMLHandlerTests2::~XMLHandlerTests2()
+{
+ delete d->xmlguiclient;
+ delete d;
+}
+
+
+void XMLHandlerTests2::setUp()
+{
+ d->xmlguiclient = new KXMLGUIClient();
+
+ //Singelton more or less ...
+ if (::KoMacro::Manager::self() == 0) {
+ ::KoMacro::Manager::init( d->xmlguiclient );
+ }
+
+ d->macro = KoMacro::Manager::self()->createMacro("testMacro");
+ d->macro2 = KoMacro::Manager::self()->createMacro("testMacro");
+ d->macro3 = KoMacro::Manager::self()->createMacro("testMacro");
+
+ d->testaction = new TestAction();
+ d->testaction_2 = new TestAction();
+ ::KoMacro::Manager::self()->publishAction(d->testaction);
+ ::KoMacro::Manager::self()->publishAction(d->testaction_2);
+}
+
+void XMLHandlerTests2::tearDown()
+{
+ delete d->xmlguiclient;
+}
+
+/**
+* Test the @a KoMacro::XMLHandler parseXML() and toXML()-function.
+*/
+void XMLHandlerTests2::testParseAndToXML()
+{
+ kdDebug()<<"===================== testParseAndToXML2() ======================" << endl;
+
+ // 1.Test - Correct DomElement.
+ testCorrectDomElement();
+ // 2.Test - XML-document with bad root element.
+ testBadRoot();
+ // 3.Test - XML-document with a missing Variable.
+ testMissingVariable();
+ // 4.Test - One more Variable in XML-Document.
+ testMoreVariables();
+ // 5.Test - XML-document with wrong macro-xmlversion.
+ testWrongVersion();
+ // 6.Test - XML-document if it has a wrong structure like wrong parathesis
+ // or missing end tag.
+ testWrongXMLStruct();
+ // 7.Test-XML-document with maximum field-size.
+ testMaxNum();
+ // 8.Test-XML-document with maximum+1 field-size.
+ testMaxNum2();
+ // 9.Test-XML-document with minimum field-size.
+ testMinNum();
+ // 10.Test-XML-document with minimum-1 field-size.
+ testMinNum2();
+ // 11.Test - With a to big number.
+ testBigNumber();
+ // 12.Test - With two MacroItems.
+ testTwoMacroItems();
+}
+
+
+/***************************************************************************
+* Begin of Sub-methos of testParseXML().
+***************************************************************************/
+// 1.Test - Correct DomElement.
+void XMLHandlerTests2::testCorrectDomElement()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</macro>");
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(0));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(0.6));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_ASSERT(d->macro2->parseXML(elem),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems2 = d->macro2->items();
+ // 1a.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems2.size(),(sizetypelist)1);
+
+ {
+ // 2a.comparison - Test if the Action is correct?
+ d->macroitem2 = *d->macroitems2.constBegin();
+ d->action2 = d->macroitem2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action2),true);
+
+ // 3a.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem2->variables().size(),(sizetypemap)4);
+ {
+ // 4a.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem2->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem2->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem2->variable("testdouble")), true);
+ }
+ }
+ // Change varint and the belonging Variable in the parsen macro2test
+ // and test it in the macro3 below
+ varint->setVariant(117);
+ d->macroitem2->variable("testint")->setVariant(117);
+
+ // Now convert the parsen macro2 back to a QDomElement and again into macro3 for a better comparison.
+ const QDomElement elem2 = d->macro2->toXML();
+ KOMACROTEST_ASSERT(d->macro3->parseXML(elem2),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems3 = d->macro3->items();
+ // 1b.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems3.size(),(sizetypelist)1);
+
+ {
+ // 2b.comparison - Test if the Action is correct?
+ d->macroitem3 = *d->macroitems3.constBegin();
+ d->action3 = d->macroitem3->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3),true);
+
+ // 3b.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem3->variables().size(),(sizetypemap)4);
+ {
+ // 4b.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem3->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem3->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem3->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem3->variable("testdouble")), true);
+ }
+ }
+}
+
+
+// 2.Test - XML-document with bad root element.
+void XMLHandlerTests2::testBadRoot()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<maro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</maro>");
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(0));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(0.6));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_XASSERT(d->macro2->parseXML(elem),true);
+}
+
+// 3.Test - XML-document with a missing Variable.
+void XMLHandlerTests2::testMissingVariable()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</macro>");
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(0));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(0.6));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_ASSERT(d->macro2->parseXML(elem),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems2 = d->macro2->items();
+ // 1a.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems2.size(),(sizetypelist)1);
+
+ {
+ // 2a.comparison - Test if the Action is correct?
+ d->macroitem2 = *d->macroitems2.constBegin();
+ d->action2 = d->macroitem2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action2),true);
+
+ // 3a.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem2->variables().size(),(sizetypemap)3);
+ {
+ // 4a.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem2->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem2->variable("testdouble")), true);
+ }
+ }
+
+ // Now convert the parsen macro2 back to a QDomElement and again into macro3 for a better comparison.
+ const QDomElement elem2 = d->macro2->toXML();
+ KOMACROTEST_ASSERT(d->macro3->parseXML(elem2),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems3 = d->macro3->items();
+ // 1b.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems3.size(),(sizetypelist)1);
+
+ {
+ // 2b.comparison - Test if the Action is correct?
+ d->macroitem3 = *d->macroitems3.constBegin();
+ d->action3 = d->macroitem3->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3),true);
+
+ // 3b.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem3->variables().size(),(sizetypemap)3);
+ {
+ // 4b.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem3->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem3->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem3->variable("testdouble")), true);
+ }
+ }
+}
+
+// 4.Test - One more Variable in XML-Document.
+void XMLHandlerTests2::testMoreVariables()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "<variable name=\"testbla\" >somethingwrong</variable>"
+ "</item>"
+ "</macro>");
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(0));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(0.6));
+ KSharedPtr<KoMacro::Variable> varbla = d->macroitem->addVariable("testbla","somethingwrong");
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_ASSERT(d->macro2->parseXML(elem),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems2 = d->macro2->items();
+ // 1a.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems2.size(),(sizetypelist)1);
+
+ {
+ // 2a.comparison - Test if the Action is correct?
+ d->macroitem2 = *d->macroitems2.constBegin();
+ d->action2 = d->macroitem2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action2),true);
+
+ // 3a.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem2->variables().size(),(sizetypemap)5);
+ {
+ // 4a.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem2->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem2->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem2->variable("testdouble")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbla, d->macroitem2->variable("testbla")), true);
+ }
+ }
+
+ // Now convert the parsen macro2 back to a QDomElement and again into macro3 for a better comparison.
+ const QDomElement elem2 = d->macro2->toXML();
+ KOMACROTEST_ASSERT(d->macro3->parseXML(elem2),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems3 = d->macro3->items();
+ // 1b.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems3.size(),(sizetypelist)1);
+
+ {
+ // 2b.comparison - Test if the Action is correct?
+ d->macroitem3 = *d->macroitems3.constBegin();
+ d->action3 = d->macroitem3->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3),true);
+
+ // 3b.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem3->variables().size(),(sizetypemap)5);
+ {
+ // 4b.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem3->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem3->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem3->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem3->variable("testdouble")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbla, d->macroitem3->variable("testbla")), true);
+ }
+ }
+}
+
+
+// 5.Test - XML-document with wrong macro-xmlversion.
+void XMLHandlerTests2::testWrongVersion()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<maro xmlversion=\"2\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</maro>");
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(0));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(0.6));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_XASSERT(d->macro2->parseXML(elem),true);
+}
+
+
+// 6.Test - XML-document if it has a wrong structure like wrong parathesis
+// or missing end tag.
+void XMLHandlerTests2::testWrongXMLStruct()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "maro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</maro>");
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(0));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(0.6));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_XASSERT(d->macro2->parseXML(elem),true);
+}
+
+// 7.Test-XML-document with maximum field-size.
+void XMLHandlerTests2::testMaxNum()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" > %1 </variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" > %2 </variable>"
+ "</item>"
+ "</macro>").arg(INT_MAX).arg(DBL_MAX);
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(INT_MAX));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(DBL_MAX));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_ASSERT(d->macro2->parseXML(elem),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems2 = d->macro2->items();
+ // 1a.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems2.size(),(sizetypelist)1);
+
+ {
+ // 2a.comparison - Test if the Action is correct?
+ d->macroitem2 = *d->macroitems2.constBegin();
+ d->action2 = d->macroitem2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action2),true);
+
+ // 3a.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem2->variables().size(),(sizetypemap)4);
+ {
+ // 4a.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem2->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem2->variable("testbool")), true);
+// KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem2->variable("testdouble")), true);
+ }
+ }
+
+ // Now convert the parsen macro2 back to a QDomElement and again into macro3 for a better comparison.
+ const QDomElement elem2 = d->macro2->toXML();
+ KOMACROTEST_ASSERT(d->macro3->parseXML(elem2),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems3 = d->macro3->items();
+ // 1b.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems3.size(),(sizetypelist)1);
+
+ {
+ // 2b.comparison - Test if the Action is correct?
+ d->macroitem3 = *d->macroitems3.constBegin();
+ d->action3 = d->macroitem3->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3),true);
+
+ // 3b.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem3->variables().size(),(sizetypemap)4);
+ {
+ // 4b.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem3->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem3->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem3->variable("testbool")), true);
+// KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem3->variable("testdouble")),true);
+ }
+ }
+}
+
+// 8.Test-XML-document with maximum+1 field-size.
+void XMLHandlerTests2::testMaxNum2()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" > %1 </variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" > %2 </variable>"
+ "</item>"
+ "</macro>").arg(INT_MAX+1).arg(DBL_MAX+1);
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(INT_MAX+1));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(DBL_MAX+1));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_ASSERT(d->macro2->parseXML(elem),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems2 = d->macro2->items();
+ // 1a.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems2.size(),(sizetypelist)1);
+
+ {
+ // 2a.comparison - Test if the Action is correct?
+ d->macroitem2 = *d->macroitems2.constBegin();
+ d->action2 = d->macroitem2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action2),true);
+
+ // 3a.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem2->variables().size(),(sizetypemap)4);
+ {
+ // 4a.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem2->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem2->variable("testbool")), true);
+// KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem2->variable("testdouble")),true);
+ }
+ }
+
+ // Now convert the parsen macro2 back to a QDomElement and again into macro3 for a better comparison.
+ const QDomElement elem2 = d->macro2->toXML();
+ KOMACROTEST_ASSERT(d->macro3->parseXML(elem2),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems3 = d->macro3->items();
+ // 1b.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems3.size(),(sizetypelist)1);
+
+ {
+ // 2b.comparison - Test if the Action is correct?
+ d->macroitem3 = *d->macroitems3.constBegin();
+ d->action3 = d->macroitem3->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3),true);
+
+ // 3b.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem3->variables().size(),(sizetypemap)4);
+ {
+ // 4b.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem3->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem3->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem3->variable("testbool")), true);
+// KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem3->variable("testdouble")),true);
+ }
+ }
+}
+
+// 9.Test-XML-document with minimum field-size.
+void XMLHandlerTests2::testMinNum()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" > %1 </variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" > %2 </variable>"
+ "</item>"
+ "</macro>").arg(INT_MIN).arg(DBL_MIN);
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(INT_MIN));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(DBL_MIN));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_ASSERT(d->macro2->parseXML(elem),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems2 = d->macro2->items();
+ // 1a.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems2.size(),(sizetypelist)1);
+
+ {
+ // 2a.comparison - Test if the Action is correct?
+ d->macroitem2 = *d->macroitems2.constBegin();
+ d->action2 = d->macroitem2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action2),true);
+
+ // 3a.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem2->variables().size(),(sizetypemap)4);
+ {
+ // 4a.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem2->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem2->variable("testbool")), true);
+// KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem2->variable("testdouble")),true);
+ }
+ }
+
+ // Now convert the parsen macro2 back to a QDomElement and again into macro3 for a better comparison.
+ const QDomElement elem2 = d->macro2->toXML();
+ KOMACROTEST_ASSERT(d->macro3->parseXML(elem2),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems3 = d->macro3->items();
+ // 1b.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems3.size(),(sizetypelist)1);
+
+ {
+ // 2b.comparison - Test if the Action is correct?
+ d->macroitem3 = *d->macroitems3.constBegin();
+ d->action3 = d->macroitem3->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3),true);
+
+ // 3b.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem3->variables().size(),(sizetypemap)4);
+ {
+ // 4b.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem3->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem3->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem3->variable("testbool")), true);
+// KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem3->variable("testdouble")),true);
+ }
+ }
+}
+
+// 10.Test-XML-document with minimum+1 field-size.
+void XMLHandlerTests2::testMinNum2()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" > %1 </variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" > %2 </variable>"
+ "</item>"
+ "</macro>").arg(INT_MIN-1).arg(DBL_MIN-1);
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(INT_MIN-1));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(DBL_MIN-1));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_ASSERT(d->macro2->parseXML(elem),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems2 = d->macro2->items();
+ // 1a.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems2.size(),(sizetypelist)1);
+
+ {
+ // 2a.comparison - Test if the Action is correct?
+ d->macroitem2 = *d->macroitems2.constBegin();
+ d->action2 = d->macroitem2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action2),true);
+
+ // 3a.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem2->variables().size(),(sizetypemap)4);
+ {
+ // 4a.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem2->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem2->variable("testbool")), true);
+// KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem2->variable("testdouble")),true);
+ }
+ }
+
+ // Now convert the parsen macro2 back to a QDomElement and again into macro3 for a better comparison.
+ const QDomElement elem2 = d->macro2->toXML();
+ KOMACROTEST_ASSERT(d->macro3->parseXML(elem2),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems3 = d->macro3->items();
+ // 1b.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems3.size(),(sizetypelist)1);
+
+ {
+ // 2b.comparison - Test if the Action is correct?
+ d->macroitem3 = *d->macroitems3.constBegin();
+ d->action3 = d->macroitem3->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3),true);
+
+ // 3b.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem3->variables().size(),(sizetypemap)4);
+ {
+ // 4b.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem3->variable("teststring")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem3->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem3->variable("testbool")), true);
+// KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem3->variable("testdouble")),true);
+ }
+ }
+}
+
+// 11.Test - With a to big number.
+void XMLHandlerTests2::testBigNumber()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0123456789012345678901234567890123456789</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "</item>"
+ "</macro>");
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+
+ d->macroitem->setAction(d->testaction);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ //TODO //KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(0123456789012345678901234567890123456789));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(0.6));
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_ASSERT(d->macro2->parseXML(elem),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems2 = d->macro2->items();
+ // 1a.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems2.size(),(sizetypelist)1);
+
+ {
+ // 2a.comparison - Test if the Action is correct?
+ d->macroitem2 = *d->macroitems2.constBegin();
+ d->action2 = d->macroitem2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action2),true);
+
+ // 3a.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem2->variables().size(),(sizetypemap)4);
+ {
+ // 4a.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem2->variable("teststring")), true);
+ //KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem2->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem2->variable("testdouble")), true);
+ }
+ }
+
+ // Now convert the parsen macro2 back to a QDomElement and again into macro3 for a better comparison.
+ const QDomElement elem2 = d->macro2->toXML();
+ KOMACROTEST_ASSERT(d->macro3->parseXML(elem2),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems3 = d->macro3->items();
+ // 1b.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems3.size(),(sizetypelist)1);
+
+ {
+ // 2b.comparison - Test if the Action is correct?
+ d->macroitem3 = *d->macroitems3.constBegin();
+ d->action3 = d->macroitem3->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3),true);
+
+ // 3b.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem3->variables().size(),(sizetypemap)4);
+ {
+ // 4b.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem3->variable("teststring")), true);
+ //KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem3->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem3->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem3->variable("testdouble")), true);
+ }
+ }
+}
+
+// 12.Test - With two MacroItems.
+void XMLHandlerTests2::testTwoMacroItems()
+{
+ // Clear macroitems in the macros.
+ d->macro->clearItems();
+ d->macro2->clearItems();
+ d->macro3->clearItems();
+
+ // Part 1: From XML to a Macro.
+ // Test-XML-document with normal allocated variables.
+ const QString xml = QString("<!DOCTYPE macros>"
+ "<macro xmlversion=\"1\">"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >test_string</variable>"
+ "<variable name=\"testint\" >0</variable>"
+ "<variable name=\"testbool\" >true</variable>"
+ "<variable name=\"testdouble\" >0.6</variable>"
+ "<variable name=\"testbla\" >somethingwrong</variable>"
+ "</item>"
+ "<item action=\"testaction\" >"
+ "<variable name=\"teststring\" >testString2</variable>"
+ "<variable name=\"testint\" >4</variable>"
+ "<variable name=\"testbool\" >false</variable>"
+ "<variable name=\"testdouble\" >0.7</variable>"
+ "<variable name=\"testbla\" >somethingwrong2</variable>"
+ "</item>"
+ "</macro>");
+ // Set the XML-document with the above string.
+ QDomDocument doomdocument;
+ doomdocument.setContent(xml);
+ const QDomElement elem = doomdocument.documentElement();
+
+ // Create a MacroItem with the TestAction for macro2 and add it to macro.
+ d->macroitem = new KoMacro::MacroItem();
+ d->macroitem_2 = new KoMacro::MacroItem();
+ d->macro->addItem(d->macroitem);
+ d->macro->addItem(d->macroitem_2);
+
+ d->macroitem->setAction(d->testaction);
+ d->macroitem_2->setAction(d->testaction_2);
+
+ // Push the Variables into the macroitem.
+ KSharedPtr<KoMacro::Variable> varstring = d->macroitem->addVariable("teststring",QVariant("test_string"));
+ KSharedPtr<KoMacro::Variable> varint = d->macroitem->addVariable("testint",QVariant(0));
+ KSharedPtr<KoMacro::Variable> varbool = d->macroitem->addVariable("testbool",QVariant(true));
+ KSharedPtr<KoMacro::Variable> vardouble = d->macroitem->addVariable("testdouble",QVariant(0.6));
+ KSharedPtr<KoMacro::Variable> varbla = d->macroitem->addVariable("testbla","somethingwrong");
+
+ // Push the Variables into the macroitem4.
+ KSharedPtr<KoMacro::Variable> varstring_2 = d->macroitem_2->addVariable("teststring",QVariant("testString2"));
+ KSharedPtr<KoMacro::Variable> varint_2 = d->macroitem_2->addVariable("testint",QVariant(4));
+ KSharedPtr<KoMacro::Variable> varbool_2 = d->macroitem_2->addVariable("testbool",QVariant(false));
+ KSharedPtr<KoMacro::Variable> vardouble_2 = d->macroitem_2->addVariable("testdouble",QVariant(0.7));
+ KSharedPtr<KoMacro::Variable> varbla_2 = d->macroitem_2->addVariable("testbla","somethingwrong2");
+
+ // Is our XML parseable into a 2. Macro by calling parseXML()?
+ KOMACROTEST_ASSERT(d->macro2->parseXML(elem),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems2 = d->macro2->items();
+ // 1a.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems2.size(),(sizetypelist)2);
+
+ {
+ QValueList<KSharedPtr<KoMacro::MacroItem > >::ConstIterator mit2(d->macroitems2.constBegin());
+ // 2a.comparison - Test if the Action is correct?
+ d->macroitem2 = *mit2;
+ mit2++;
+ d->macroitem2_2 = *mit2;
+ d->action2 = d->macroitem2->action();
+ d->action2_2 = d->macroitem2_2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action2),true);
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction_2,d->action2_2),true);
+
+ // 3a.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem2->variables().size(),(sizetypemap)5);
+ KOMACROTEST_ASSERT(d->macroitem2_2->variables().size(),(sizetypemap)5);
+ {
+ // 4a.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem2->variable("teststring")),true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem2->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem2->variable("testdouble")),true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbla, d->macroitem2->variable("testbla")),true);
+
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring_2,d->macroitem2_2->variable("teststring")),true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint_2, d->macroitem2_2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool_2, d->macroitem2_2->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble_2,d->macroitem2_2->variable("testdouble")),true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbla_2, d->macroitem2_2->variable("testbla")),true);
+ }
+ }
+
+ // Now convert the parsen macro2 back to a QDomElement and again into macro3 for a better comparison.
+ const QDomElement elem2 = d->macro2->toXML();
+ KOMACROTEST_ASSERT(d->macro3->parseXML(elem2),true);
+
+ // Go down to the MacroItem of macro2.
+ d->macroitems3 = d->macro3->items();
+ // 1b.comparison - Test if the MacroItems have the correct number?
+ KOMACROTEST_ASSERT(d->macroitems3.size(),(sizetypelist)2);
+
+ {
+ QValueList<KSharedPtr<KoMacro::MacroItem > >::ConstIterator mit3(d->macroitems3.constBegin());
+ // 2b.comparison - Test if the Action is correct?
+ d->macroitem3 = *mit3;
+ mit3++;
+ d->macroitem3_2 = *mit3;
+ d->action3 = d->macroitem3->action();
+ d->action3_2 = d->macroitem3_2->action();
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3),true);
+ KOMACROTEST_ASSERT(assertActionsEqual(d->testaction,d->action3_2),true);
+
+ // 3b.comparison - Test if the Variables have the correct number?
+ KOMACROTEST_ASSERT(d->macroitem3->variables().size(),(sizetypemap)5);
+ KOMACROTEST_ASSERT(d->macroitem3_2->variables().size(),(sizetypemap)5);
+ {
+ // 4b.comparison - Test if the Variables are equal.
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring, d->macroitem3->variable("teststring")),true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint, d->macroitem3->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool, d->macroitem3->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble, d->macroitem3->variable("testdouble")),true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbla, d->macroitem3->variable("testbla")),true);
+
+ KOMACROTEST_ASSERT(assertVariablesEqual(varstring_2,d->macroitem3_2->variable("teststring")),true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varint_2, d->macroitem3_2->variable("testint")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbool_2, d->macroitem3_2->variable("testbool")), true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(vardouble_2,d->macroitem3_2->variable("testdouble")),true);
+ KOMACROTEST_ASSERT(assertVariablesEqual(varbla_2, d->macroitem3_2->variable("testbla")),true);
+ }
+ }
+}
+
+/***************************************************************************
+* End of Sub-methos of testParseAndToXML2().
+***************************************************************************/
+
+bool XMLHandlerTests2::assertActionsEqual(KSharedPtr<KoMacro::Action> action,
+ KSharedPtr<KoMacro::Action> action2)
+{
+ return action->name() == action2->name();
+}
+
+bool XMLHandlerTests2::assertVariablesEqual(KSharedPtr<KoMacro::Variable> var,
+ KSharedPtr<KoMacro::Variable> var2)
+{
+ if ( var->variant() != var2->variant() ) kdDebug() << "Variable1: " << var->variant() << " and Variable2: " << var2->variant() << endl;
+ return var->variant() == var2->variant();
+}
+
+#include "xmlhandlertests2.moc"
diff --git a/kexi/plugins/macros/tests/xmlhandlertests2.h b/kexi/plugins/macros/tests/xmlhandlertests2.h
new file mode 100644
index 00000000..0a3fee3a
--- /dev/null
+++ b/kexi/plugins/macros/tests/xmlhandlertests2.h
@@ -0,0 +1,132 @@
+/***************************************************************************
+ * This file is part of the KDE project
+ * copyright (C) 2006 by Bernd Steindorff (bernd@itii.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KOMACROTEST_XMLHandlerTests2_H
+#define KOMACROTEST_XMLHandlerTests2_H
+
+#include <kunittest/tester.h>
+#include "../lib/macro.h"
+
+namespace KoMacroTest {
+
+ /**
+ * The common testsuite used to test common @a KoMacro
+ * functionality.
+ */
+ class XMLHandlerTests2 : public KUnitTest::SlotTester
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Constructor.
+ */
+ XMLHandlerTests2();
+
+ /**
+ * Destructor.
+ */
+ virtual ~XMLHandlerTests2();
+
+ public slots:
+
+ /**
+ * This slot got called by KUnitTest before testing
+ * starts.
+ */
+ void setUp();
+
+ /**
+ * This slot got called by KUnitTest after all tests
+ * are done.
+ */
+ void tearDown();
+
+ /**
+ * Test the @a KoMacro::XMLHandler parseXML()-function.
+ */
+ void testParseAndToXML();
+
+ private:
+ /// @internal d-pointer class.
+ class Private;
+ /// @internal d-pointer instance.
+ Private* const d;
+
+ typedef QMap<QString,KoMacro::Variable>::size_type sizetypemap;
+ typedef QValueList<KSharedPtr<KoMacro::MacroItem > >::size_type sizetypelist;
+
+ /**
+ * Compares a XML-Element with a Macro. Call sub-asserts.
+ * @p macro The parsen @a Macro.
+ * @p domelement The given @a QDomElement which is parsen.
+ * @p isitemsempty Bool for expectation of an empty @a MacroItem -List.
+ * @p isactionset Bool for expectation that the @a Action -names are equal.
+ * @p isvariableok QMap of Bools for comparing each @a Variable .
+ */
+/* void assertMacroContentEqToXML(const KSharedPtr<KoMacro::Macro> macro,
+ const QDomElement& elem,
+ const bool isitemsempty,
+ const bool isactionset,
+ const QMap<QString, bool> isvariableok);
+
+ // Prints a QMap of Variables to kdDebug().
+ void printMvariables(const QMap<QString, KSharedPtr<KoMacro::Variable > > mvariables, const QString s);
+*/
+ /**
+ * Sub-methods of testParseXML() and testToXML().
+ * Test the correct parsing of a @a QDomElement into a @a Macro
+ * respectively expected failure of parsing. Then transform it
+ * back and compare it.
+ */
+ // 1.Test - Correct DomElement.
+ void testCorrectDomElement();
+ // 2.Test - XML-document with bad root element.
+ void testBadRoot();
+ // 3.Test - XML-document with a missing Variable.
+ void testMissingVariable();
+ // 4.Test - One more Variable in XML-Document.
+ void testMoreVariables();
+ // 5.Test - XML-document with wrong macro-xmlversion.
+ void testWrongVersion();
+ // 6.Test - XML-document if it has a wrong structure like
+ // wrong parathesis or missing end tag.
+ void testWrongXMLStruct();
+ // 7.Test-XML-document with maximum field-size.
+ void testMaxNum();
+ // 8.Test-XML-document with maximum+1 field-size.
+ void testMaxNum2();
+ // 9.Test-XML-document with minimum field-size.
+ void testMinNum();
+ // 10.Test-XML-document with minimum-1 field-size.
+ void testMinNum2();
+ // 11.Test - With a to big number.
+ void testBigNumber();
+ // 12.Test - With two MacroItems.
+ void testTwoMacroItems();
+
+
+ bool assertActionsEqual(KSharedPtr<KoMacro::Action> action,
+ KSharedPtr<KoMacro::Action> action2);
+
+ bool assertVariablesEqual(KSharedPtr<KoMacro::Variable> var,
+ KSharedPtr<KoMacro::Variable> var2);
+ };
+}
+
+#endif
diff --git a/kexi/plugins/migration/Makefile.am b/kexi/plugins/migration/Makefile.am
new file mode 100644
index 00000000..e496773e
--- /dev/null
+++ b/kexi/plugins/migration/Makefile.am
@@ -0,0 +1,20 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_migration.la
+
+kexihandler_migration_la_SOURCES = keximigrationpart.cpp
+
+kexihandler_migration_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module
+kexihandler_migration_la_LIBADD = ../../core/libkexicore.la \
+ ../../migration/libkeximigrate.la
+
+INCLUDES= -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget -I$(top_srcdir)/kexi/migration \
+ -I$(top_srcdir)/kexi/kexiDB $(all_includes)
+
+METASOURCES = AUTO
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=keximigrationhandler.desktop
+
+include ../Makefile.common
diff --git a/kexi/plugins/migration/keximigrationhandler.desktop b/kexi/plugins/migration/keximigrationhandler.desktop
new file mode 100644
index 00000000..4ca99b05
--- /dev/null
+++ b/kexi/plugins/migration/keximigrationhandler.desktop
@@ -0,0 +1,102 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Migration Plugin
+GenericName[bg]=Приставка за мигриране
+GenericName[ca]=Connector de migració
+GenericName[cy]=Ategyn Mudo
+GenericName[da]=Migrationsplugin
+GenericName[de]=Migrations-Modul
+GenericName[el]=Πρόσθετο μεταφοράς
+GenericName[eo]=Migradkromaĵo
+GenericName[es]=Complemento para migración
+GenericName[et]=Migreerumisplugin
+GenericName[eu]=Migraziorako plugina
+GenericName[fa]=وصلۀ جابه‌جایی
+GenericName[fi]=Yhdistämisliitännäinen
+GenericName[fr]=Module de migration
+GenericName[fy]=Migraasjeplugin
+GenericName[ga]=Breiseán Migration
+GenericName[gl]=Plugin de Migración
+GenericName[he]=תוסף Migration
+GenericName[hr]=Migracijski dodatak
+GenericName[hu]=Migrálási modul
+GenericName[is]=Gagnaflutnings íforrit
+GenericName[it]=Plugin di migrazione
+GenericName[ja]=データ移行プラグイン
+GenericName[km]=កម្មវិធី​ជំនួយ​ផ្លាស់ប្ដូរ​កន្លែង
+GenericName[lv]=Migrācijas spraudnis
+GenericName[ms]=Plugin Migrasi
+GenericName[nb]=Programtillegg for migrering
+GenericName[nds]=Datenutlagern-Moduul
+GenericName[ne]=माइग्रेसन प्लगइन
+GenericName[nl]=Migratieplugin
+GenericName[nn]=Programtillegg for migrering
+GenericName[pl]=Wtyczka migracji
+GenericName[pt]='Plugin' de Migração
+GenericName[pt_BR]=Plugin de Migração
+GenericName[ru]=Миграция
+GenericName[sk]=Modul pre migráciu
+GenericName[sl]=Vstavek za prehod
+GenericName[sr]=Миграциони прикључак
+GenericName[sr@Latn]=Migracioni priključak
+GenericName[sv]=Övergångsinsticksprogram
+GenericName[uk]=Втулок міграції
+GenericName[uz]=Migratsiya plagini
+GenericName[uz@cyrillic]=Миграция плагини
+GenericName[zh_CN]=升迁插件
+GenericName[zh_TW]=轉移外掛程式
+Name=Migration Plugin
+Name[bg]=Приставка за мигриране
+Name[ca]=Connector de migració
+Name[cy]=Ategyn Mudo
+Name[da]=Migrationsplugin
+Name[de]=Migrations-Modul
+Name[el]=Πρόσθετο μεταφοράς
+Name[eo]=Migradkromaĵo
+Name[es]=Complemento para migración
+Name[et]=Migreerumisplugin
+Name[eu]=Migraziorako plugina
+Name[fa]=وصلۀ جابه‌جایی
+Name[fi]=Yhdistämisliitännäinen
+Name[fr]=Module de migration
+Name[fy]=Migrationplugin
+Name[ga]=Breiseán Migration
+Name[gl]=Plugin de Migración
+Name[he]=תוסף Migration
+Name[hi]=माइग्रेशन प्लगइन
+Name[hr]=Migracijski dodatak
+Name[hu]=Migrálási modul
+Name[is]=Gagnaflutnings íforrit
+Name[it]=Plugin di migrazione
+Name[ja]=データ移行プラグイン
+Name[km]=កម្មវិធី​ជំនួយ​សម្រាប់​ផ្លាស់ប្ដូរ
+Name[lv]=Migrācijas spraudnis
+Name[ms]=Plugin Migrasi
+Name[nb]=Programtillegg for migrering
+Name[nds]=Datenutlagern-Moduul
+Name[ne]=माइग्रेसन प्लगइन
+Name[nl]=Migratieplugin
+Name[nn]=Programtillegg for migrering
+Name[pl]=Wtyczka migracji
+Name[pt]='Plugin' de Migração
+Name[pt_BR]=Plugin de Migração
+Name[ru]=Модуль миграции
+Name[sk]=Modul pre migráciu
+Name[sl]=Vstavek za prehod
+Name[sr]=Миграциони прикључак
+Name[sr@Latn]=Migracioni priključak
+Name[sv]=Övergångsinsticksprogram
+Name[uk]=Втулок міграції
+Name[uz]=Migratsiya plagini
+Name[uz@cyrillic]=Миграция плагини
+Name[zh_CN]=升迁插件
+Name[zh_TW]=轉移外掛程式
+X-KDE-Library=kexihandler_migration
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=migration
+X-Kexi-GroupIcon=migration
+X-Kexi-ItemIcon=migration
+X-Kexi-NoObject=true
diff --git a/kexi/plugins/migration/keximigrationpart.cpp b/kexi/plugins/migration/keximigrationpart.cpp
new file mode 100644
index 00000000..0f6c408b
--- /dev/null
+++ b/kexi/plugins/migration/keximigrationpart.cpp
@@ -0,0 +1,46 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "keximigrationpart.h"
+
+#include <migration/importwizard.h>
+
+#include <kgenericfactory.h>
+
+KexiMigrationPart::KexiMigrationPart(QObject *parent, const char *name, const QStringList &args)
+ : KexiInternalPart(parent, name, args)
+{
+}
+
+KexiMigrationPart::~KexiMigrationPart()
+{
+}
+
+QWidget *KexiMigrationPart::createWidget(const char* /*widgetClass*/, KexiMainWindow* mainWin,
+ QWidget *parent, const char *objName, QMap<QString,QString>* args )
+{
+ Q_UNUSED( mainWin );
+
+ KexiMigration::ImportWizard *w = new KexiMigration::ImportWizard(parent, args);
+ w->setName(objName);
+ return w;
+}
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_migration,
+ KGenericFactory<KexiMigrationPart>("kexihandler_migration") )
diff --git a/kexi/plugins/migration/keximigrationpart.h b/kexi/plugins/migration/keximigrationpart.h
new file mode 100644
index 00000000..528aac82
--- /dev/null
+++ b/kexi/plugins/migration/keximigrationpart.h
@@ -0,0 +1,38 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXI_MIGRATION_PART_H
+#define KEXI_MIGRATION_PART_H
+
+#include <core/kexiinternalpart.h>
+
+/*! @short Internal part for data/project migration wizard. */
+class KexiMigrationPart : public KexiInternalPart
+{
+ public:
+ KexiMigrationPart(QObject *parent, const char *name, const QStringList &args);
+ virtual ~KexiMigrationPart();
+
+ /*! Reimplement this if your internal part has to return widgets
+ or QDialog objects. */
+ virtual QWidget *createWidget(const char* /*widgetClass*/, KexiMainWindow* mainWin,
+ QWidget *parent, const char *objName = 0, QMap<QString,QString>* args = 0);
+};
+
+#endif
diff --git a/kexi/plugins/queries/Makefile.am b/kexi/plugins/queries/Makefile.am
new file mode 100644
index 00000000..c0b620d4
--- /dev/null
+++ b/kexi/plugins/queries/Makefile.am
@@ -0,0 +1,29 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_query.la
+
+kexihandler_query_la_SOURCES = kexiquerypart.cpp kexiquerydesignersql.cpp \
+ kexiquerydesignersqlhistory.cpp kexiquerydesignerguieditor.cpp \
+ kexiqueryview.cpp
+kexihandler_query_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module -no-undefined
+kexihandler_query_la_LIBADD = ../../core/libkexicore.la \
+ $(top_builddir)/kexi/kexidb/libkexidb.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/kexi/widget/tableview/libkexidatatable.la \
+ $(top_builddir)/kexi/widget/relations/libkexirelationsview.la \
+ $(top_builddir)/lib/koproperty/libkoproperty.la
+
+INCLUDES= -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget -I$(top_srcdir)/kexi/widget/tableview \
+ -I$(top_srcdir)/lib -I$(top_srcdir)/lib/kofficecore \
+ -I$(top_srcdir)/kexi/kexidb $(all_includes)
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=kexiqueryhandler.desktop
+
+rcdir = $(kde_datadir)/kexi
+rc_DATA = kexiquerypartui.rc kexiquerypartinstui.rc
+
+METASOURCES = AUTO
+
+include ../Makefile.common
diff --git a/kexi/plugins/queries/kexiaddparamdialog.cpp b/kexi/plugins/queries/kexiaddparamdialog.cpp
new file mode 100644
index 00000000..fb40f9a2
--- /dev/null
+++ b/kexi/plugins/queries/kexiaddparamdialog.cpp
@@ -0,0 +1,47 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <klocale.h>
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <qvbox.h>
+#include <kexidataprovider.h>
+#include "kexiaddparamdialog.h"
+#include "kexiaddparamdialog.moc"
+#include "kexiaddparamwidget.h"
+
+KexiAddParamDialog::KexiAddParamDialog(QWidget *parent)
+ : KDialogBase(parent, "kexiaddparamdialog", true, i18n("Add Parameter"), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true)
+{
+ m_wid=new KexiAddParamWidget(makeVBoxMainWidget());
+ for (int i=1;i<=KexiDataProvider::Parameter::maxType;i++)
+ m_wid->typecombo->insertItem(KexiDataProvider::Parameter::typeDescription[i]);
+}
+
+KexiAddParamDialog::~KexiAddParamDialog()
+{
+}
+
+QString KexiAddParamDialog::parameterName() {
+ return m_wid->paramname->text();
+}
+
+int KexiAddParamDialog::parameterType() {
+ return m_wid->typecombo->currentItem()+1;
+}
diff --git a/kexi/plugins/queries/kexiaddparamdialog.h b/kexi/plugins/queries/kexiaddparamdialog.h
new file mode 100644
index 00000000..79558a7c
--- /dev/null
+++ b/kexi/plugins/queries/kexiaddparamdialog.h
@@ -0,0 +1,40 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KEXIADDPARAMDIALOG_H
+#define KEXIADDPARAMDIALOG_H
+
+#include <kdialogbase.h>
+
+class KexiAddParamWidget;
+
+class KexiAddParamDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ KexiAddParamDialog(QWidget *parent);
+ virtual ~KexiAddParamDialog();
+ QString parameterName();
+ int parameterType();
+ private:
+ KexiAddParamWidget *m_wid;
+};
+
+#endif
diff --git a/kexi/plugins/queries/kexiaddparamwidget.ui b/kexi/plugins/queries/kexiaddparamwidget.ui
new file mode 100644
index 00000000..43ec25f1
--- /dev/null
+++ b/kexi/plugins/queries/kexiaddparamwidget.ui
@@ -0,0 +1,135 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>KexiAddParamWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KexiAddParamWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>496</width>
+ <height>205</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Parameter</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>kexi_</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>paramname</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>Message:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>message</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Type:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>typecombo</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kexi/plugins/queries/kexidynamicqueryparameterdialog.cpp b/kexi/plugins/queries/kexidynamicqueryparameterdialog.cpp
new file mode 100644
index 00000000..4a77f37c
--- /dev/null
+++ b/kexi/plugins/queries/kexidynamicqueryparameterdialog.cpp
@@ -0,0 +1,63 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#include "kexidynamicqueryparameterdialog.h"
+#include "kexidynamicqueryparameterdialog.moc"
+
+#include <qvbox.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <qlineedit.h>
+#include <qobjectlist.h>
+
+KexiDynamicQueryParameterDialog::KexiDynamicQueryParameterDialog(QWidget *parent,
+ KexiDataProvider::Parameters *values, const KexiDataProvider::ParameterList &list):
+ KDialogBase(parent, "paramddialog", true, i18n("Query Parameters"),
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true )
+{
+ m_values=values;
+ int y;
+ m_mainView=new QVBox(this);
+
+ for (KexiDataProvider::ParameterList::const_iterator it=list.begin();
+ it!=list.end();++it) {
+ QLineEdit *le=new QLineEdit(m_mainView,(*it).name.utf8());
+ le->setText((*values)[(*it).name]);
+ }
+
+ setMainWidget(m_mainView);
+}
+
+KexiDynamicQueryParameterDialog::~KexiDynamicQueryParameterDialog() {}
+
+void KexiDynamicQueryParameterDialog::slotOk() {
+ QObjectList *l=queryList(0,"kexi_.*",true,true);
+ QObjectListIt it(*l);
+ QObject *obj;
+ kdDebug()<<"KexiDynamicQueryParameterDialog::slotOk()"<<endl;
+ while ((obj=it.current())!=0) {
+ kdDebug()<<"KexiDynamicQueryParameterDialog::slotOk()::loop"<<endl;
+ (*m_values)[QString().fromUtf8(obj->name())]=
+ (dynamic_cast<QLineEdit*>(obj))->text();
+ ++it;
+ }
+ delete l;
+ KDialogBase::slotOk();
+}
diff --git a/kexi/plugins/queries/kexidynamicqueryparameterdialog.h b/kexi/plugins/queries/kexidynamicqueryparameterdialog.h
new file mode 100644
index 00000000..b315e4f9
--- /dev/null
+++ b/kexi/plugins/queries/kexidynamicqueryparameterdialog.h
@@ -0,0 +1,45 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _KEXI_DYNAMIC_QUERY_PARAMETER_DIALOG_H_
+#define _KEXI_DYNAMIC_QUERY_PARAMETER_DIALOG_H_
+
+
+#include <kdialogbase.h>
+#include <kexidataprovider.h>
+
+class QVBox;
+
+class KexiDynamicQueryParameterDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KexiDynamicQueryParameterDialog(QWidget *parent,KexiDataProvider::Parameters *, const KexiDataProvider::ParameterList &);
+ virtual ~KexiDynamicQueryParameterDialog();
+
+protected:
+ virtual void slotOk();
+private:
+//temporary only. Later a different widget will be used
+ QVBox *m_mainView;
+ KexiDataProvider::Parameters *m_values;
+
+};
+
+#endif
diff --git a/kexi/plugins/queries/kexiparameterlisteditor.ui b/kexi/plugins/queries/kexiparameterlisteditor.ui
new file mode 100644
index 00000000..ac4a3230
--- /dev/null
+++ b/kexi/plugins/queries/kexiparameterlisteditor.ui
@@ -0,0 +1,88 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>KexiParameterListEditor</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KexiParameterListEditor</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>190</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Parameters:</string>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Type</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>list</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>addParameter</cstring>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>deleteParameter</cstring>
+ </property>
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kexi/plugins/queries/kexiquerydesignerguieditor.cpp b/kexi/plugins/queries/kexiquerydesignerguieditor.cpp
new file mode 100644
index 00000000..d67573e8
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignerguieditor.cpp
@@ -0,0 +1,1803 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiquerydesignerguieditor.h"
+
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qdom.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <kexidb/field.h>
+#include <kexidb/queryschema.h>
+#include <kexidb/connection.h>
+#include <kexidb/parser/parser.h>
+#include <kexidb/parser/sqlparser.h>
+#include <kexidb/utils.h>
+#include <kexidb/roweditbuffer.h>
+#include <kexiutils/identifier.h>
+#include <kexiproject.h>
+#include <keximainwindow.h>
+#include <kexiinternalpart.h>
+#include <kexitableview.h>
+#include <kexitableitem.h>
+#include <kexitableviewdata.h>
+#include <kexidragobjects.h>
+#include <kexidialogbase.h>
+#include <kexidatatable.h>
+#include <kexi.h>
+#include <kexisectionheader.h>
+#include <widget/tableview/kexidataawarepropertyset.h>
+#include <widget/relations/kexirelationwidget.h>
+#include <widget/relations/kexirelationviewtable.h>
+#include <koproperty/property.h>
+#include <koproperty/set.h>
+
+#include "kexiquerypart.h"
+
+//! @todo remove KEXI_NO_QUERY_TOTALS later
+#define KEXI_NO_QUERY_TOTALS
+
+//! indices for table columns
+#define COLUMN_ID_COLUMN 0
+#define COLUMN_ID_TABLE 1
+#define COLUMN_ID_VISIBLE 2
+#ifdef KEXI_NO_QUERY_TOTALS
+# define COLUMN_ID_SORTING 3
+# define COLUMN_ID_CRITERIA 4
+#else
+# define COLUMN_ID_TOTALS 3
+# define COLUMN_ID_SORTING 4
+# define COLUMN_ID_CRITERIA 5
+#endif
+
+/*! @internal */
+class KexiQueryDesignerGuiEditor::Private
+{
+public:
+ Private()
+ : fieldColumnIdentifiers(101, false/*case insens.*/)
+ {
+ droppedNewItem = 0;
+ slotTableAdded_enabled = true;
+ }
+
+ bool changeSingleCellValue(KexiTableItem &item, int columnNumber,
+ const QVariant& value, KexiDB::ResultInfo* result)
+ {
+ data->clearRowEditBuffer();
+ if (!data->updateRowEditBuffer(&item, columnNumber, value)
+ || !data->saveRowChanges(item, true))
+ {
+ if (result)
+ *result = *data->result();
+ return false;
+ }
+ return true;
+ }
+
+ KexiTableViewData *data;
+ KexiDataTable *dataTable;
+ QGuardedPtr<KexiDB::Connection> conn;
+
+ KexiRelationWidget *relations;
+ KexiSectionHeader *head;
+ QSplitter *spl;
+
+ /*! Used to remember in slotDroppedAtRow() what data was dropped,
+ so we can create appropriate prop. set in slotRowInserted()
+ This information is cached and entirely refreshed on updateColumnsData(). */
+ KexiTableViewData *fieldColumnData, *tablesColumnData;
+
+ /*! Collects identifiers selected in 1st (field) column,
+ so we're able to distinguish between table identifiers selected from
+ the dropdown list, and strings (e.g. expressions) entered by hand.
+ This information is cached and entirely refreshed on updateColumnsData().
+ The dict is filled with (char*)1 values (doesn't matter what it is);
+ */
+ QDict<char> fieldColumnIdentifiers;
+
+ KexiDataAwarePropertySet* sets;
+ KexiTableItem *droppedNewItem;
+
+ QString droppedNewTable, droppedNewField;
+
+ bool slotTableAdded_enabled : 1;
+};
+
+static bool isAsterisk(const QString& tableName, const QString& fieldName)
+{
+ return tableName=="*" || fieldName.endsWith("*");
+}
+
+//! @internal \return true if sorting is allowed for \a fieldName and \a tableName
+static bool sortingAllowed(const QString& fieldName, const QString& tableName) {
+ return ! (fieldName=="*" || (fieldName.isEmpty() && tableName=="*"));
+}
+
+//=========================================================
+
+KexiQueryDesignerGuiEditor::KexiQueryDesignerGuiEditor(
+ KexiMainWindow *mainWin, QWidget *parent, const char *name)
+ : KexiViewBase(mainWin, parent, name)
+ , d( new Private() )
+{
+ d->conn = mainWin->project()->dbConnection();
+
+ d->spl = new QSplitter(Vertical, this);
+ d->spl->setChildrenCollapsible(false);
+ d->relations = new KexiRelationWidget(mainWin, d->spl, "relations");
+ connect(d->relations, SIGNAL(tableAdded(KexiDB::TableSchema&)),
+ this, SLOT(slotTableAdded(KexiDB::TableSchema&)));
+ connect(d->relations, SIGNAL(tableHidden(KexiDB::TableSchema&)),
+ this, SLOT(slotTableHidden(KexiDB::TableSchema&)));
+ connect(d->relations, SIGNAL(tableFieldDoubleClicked(KexiDB::TableSchema*,const QString&)),
+ this, SLOT(slotTableFieldDoubleClicked(KexiDB::TableSchema*,const QString&)));
+
+ d->head = new KexiSectionHeader(i18n("Query Columns"), Vertical, d->spl);
+ d->dataTable = new KexiDataTable(mainWin, d->head, "guieditor_dataTable", false);
+ d->dataTable->dataAwareObject()->setSpreadSheetMode();
+
+ d->data = new KexiTableViewData(); //just empty data
+ d->sets = new KexiDataAwarePropertySet( this, d->dataTable->dataAwareObject() );
+ initTableColumns();
+ initTableRows();
+
+ QValueList<int> c;
+ c << COLUMN_ID_COLUMN << COLUMN_ID_TABLE << COLUMN_ID_CRITERIA;
+ if (d->dataTable->tableView()/*sanity*/) {
+ d->dataTable->tableView()->adjustColumnWidthToContents(COLUMN_ID_VISIBLE);
+ d->dataTable->tableView()->adjustColumnWidthToContents(COLUMN_ID_SORTING);
+ d->dataTable->tableView()->maximizeColumnsWidth( c );
+ d->dataTable->tableView()->setDropsAtRowEnabled(true);
+ connect(d->dataTable->tableView(), SIGNAL(dragOverRow(KexiTableItem*,int,QDragMoveEvent*)),
+ this, SLOT(slotDragOverTableRow(KexiTableItem*,int,QDragMoveEvent*)));
+ connect(d->dataTable->tableView(), SIGNAL(droppedAtRow(KexiTableItem*,int,QDropEvent*,KexiTableItem*&)),
+ this, SLOT(slotDroppedAtRow(KexiTableItem*,int,QDropEvent*,KexiTableItem*&)));
+ connect(d->dataTable->tableView(), SIGNAL(newItemAppendedForAfterDeletingInSpreadSheetMode()),
+ this, SLOT(slotNewItemAppendedForAfterDeletingInSpreadSheetMode()));
+ }
+ connect(d->data, SIGNAL(aboutToChangeCell(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)),
+ this, SLOT(slotBeforeCellChanged(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)));
+ connect(d->data, SIGNAL(rowInserted(KexiTableItem*,uint,bool)),
+ this, SLOT(slotRowInserted(KexiTableItem*,uint,bool)));
+ connect(d->relations, SIGNAL(tablePositionChanged(KexiRelationViewTableContainer*)),
+ this, SLOT(slotTablePositionChanged(KexiRelationViewTableContainer*)));
+ connect(d->relations, SIGNAL(aboutConnectionRemove(KexiRelationViewConnection*)),
+ this, SLOT(slotAboutConnectionRemove(KexiRelationViewConnection*)));
+
+ QVBoxLayout *l = new QVBoxLayout(this);
+ l->addWidget(d->spl);
+
+ addChildView(d->relations);
+ addChildView(d->dataTable);
+ setViewWidget(d->dataTable, true);
+ d->relations->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
+ d->head->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
+ updateGeometry();
+ d->spl->setSizes(QValueList<int>()<< 800<<400);
+}
+
+KexiQueryDesignerGuiEditor::~KexiQueryDesignerGuiEditor()
+{
+}
+
+void
+KexiQueryDesignerGuiEditor::initTableColumns()
+{
+ KexiTableViewColumn *col1 = new KexiTableViewColumn("column", KexiDB::Field::Enum, i18n("Column"),
+ i18n("Describes field name or expression for the designed query."));
+ col1->setRelatedDataEditable(true);
+
+ d->fieldColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text);
+ col1->setRelatedData( d->fieldColumnData );
+ d->data->addColumn(col1);
+
+ KexiTableViewColumn *col2 = new KexiTableViewColumn("table", KexiDB::Field::Enum, i18n("Table"),
+ i18n("Describes table for a given field. Can be empty."));
+ d->tablesColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text);
+ col2->setRelatedData( d->tablesColumnData );
+ d->data->addColumn(col2);
+
+ KexiTableViewColumn *col3 = new KexiTableViewColumn("visible", KexiDB::Field::Boolean, i18n("Visible"),
+ i18n("Describes visibility for a given field or expression."));
+ col3->field()->setDefaultValue( QVariant(false, 0) );
+ col3->field()->setNotNull( true );
+ d->data->addColumn(col3);
+
+#ifndef KEXI_NO_QUERY_TOTALS
+ KexiTableViewColumn *col4 = new KexiTableViewColumn("totals", KexiDB::Field::Enum, i18n("Totals"),
+ i18n("Describes a way of computing totals for a given field or expression."));
+ QValueVector<QString> totalsTypes;
+ totalsTypes.append( i18n("Group by") );
+ totalsTypes.append( i18n("Sum") );
+ totalsTypes.append( i18n("Average") );
+ totalsTypes.append( i18n("Min") );
+ totalsTypes.append( i18n("Max") );
+ //todo: more like this
+ col4->field()->setEnumHints(totalsTypes);
+ d->data->addColumn(col4);
+#endif
+
+ KexiTableViewColumn *col5 = new KexiTableViewColumn("sort", KexiDB::Field::Enum, i18n("Sorting"),
+ i18n("Describes a way of sorting for a given field."));
+ QValueVector<QString> sortTypes;
+ sortTypes.append( "" );
+ sortTypes.append( i18n("Ascending") );
+ sortTypes.append( i18n("Descending") );
+ col5->field()->setEnumHints(sortTypes);
+ d->data->addColumn(col5);
+
+ KexiTableViewColumn *col6 = new KexiTableViewColumn("criteria", KexiDB::Field::Text, i18n("Criteria"),
+ i18n("Describes the criteria for a given field or expression."));
+ d->data->addColumn(col6);
+
+// KexiTableViewColumn *col7 = new KexiTableViewColumn(i18n("Or"), KexiDB::Field::Text);
+// d->data->addColumn(col7);
+}
+
+void KexiQueryDesignerGuiEditor::initTableRows()
+{
+ d->data->deleteAllRows();
+ //const int columns = d->data->columnsCount();
+ for (int i=0; i<(int)d->sets->size(); i++) {
+ KexiTableItem* item;
+ d->data->append(item = d->data->createItem());
+ item->at(COLUMN_ID_VISIBLE) = QVariant(false, 0);
+ }
+ d->dataTable->dataAwareObject()->setData(d->data);
+
+ updateColumnsData();
+}
+
+void KexiQueryDesignerGuiEditor::updateColumnsData()
+{
+ d->dataTable->dataAwareObject()->acceptRowEdit();
+
+ QStringList sortedTableNames;
+ for (TablesDictIterator it(*d->relations->tables());it.current();++it)
+ sortedTableNames += it.current()->schema()->name();
+ qHeapSort( sortedTableNames );
+
+ //several tables can be hidden now, so remove rows for these tables
+ QValueList<int> rowsToDelete;
+ for (int r = 0; r<(int)d->sets->size(); r++) {
+ KoProperty::Set *set = d->sets->at(r);
+ if (set) {
+ QString tableName = (*set)["table"].value().toString();
+ QString fieldName = (*set)["field"].value().toString();
+ const bool allTablesAsterisk = tableName=="*" && d->relations->tables()->isEmpty();
+ const bool fieldNotFound = tableName!="*"
+ && !(*set)["isExpression"].value().toBool()
+ && sortedTableNames.end() == qFind( sortedTableNames.begin(), sortedTableNames.end(), tableName );
+
+ if (allTablesAsterisk || fieldNotFound) {
+ //table not found: mark this line for later removal
+ rowsToDelete += r;
+ }
+ }
+ }
+ d->data->deleteRows( rowsToDelete );
+
+ //update 'table' and 'field' columns
+ d->tablesColumnData->deleteAllRows();
+ d->fieldColumnData->deleteAllRows();
+ d->fieldColumnIdentifiers.clear();
+
+ KexiTableItem *item = d->fieldColumnData->createItem(); //new KexiTableItem(2);
+ (*item)[COLUMN_ID_COLUMN]="*";
+ (*item)[COLUMN_ID_TABLE]="*";
+ d->fieldColumnData->append( item );
+ d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1); //cache
+
+// tempData()->clearQuery();
+ tempData()->unregisterForTablesSchemaChanges();
+ for (QStringList::const_iterator it = sortedTableNames.constBegin();
+ it!=sortedTableNames.constEnd(); ++it)
+ {
+ //table
+/*! @todo what about query? */
+ KexiDB::TableSchema *table = d->relations->tables()->find(*it)->schema()->table();
+ d->conn->registerForTableSchemaChanges(*tempData(), *table); //this table will be used
+ item = d->tablesColumnData->createItem(); //new KexiTableItem(2);
+ (*item)[COLUMN_ID_COLUMN]=table->name();
+ (*item)[COLUMN_ID_TABLE]=(*item)[COLUMN_ID_COLUMN];
+ d->tablesColumnData->append( item );
+ //fields
+ item = d->fieldColumnData->createItem(); //new KexiTableItem(2);
+ (*item)[COLUMN_ID_COLUMN]=table->name()+".*";
+ (*item)[COLUMN_ID_TABLE]=(*item)[COLUMN_ID_COLUMN];
+ d->fieldColumnData->append( item );
+ d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1); //cache
+ for (KexiDB::Field::ListIterator t_it = table->fieldsIterator();t_it.current();++t_it) {
+ item = d->fieldColumnData->createItem(); // new KexiTableItem(2);
+ (*item)[COLUMN_ID_COLUMN]=table->name()+"."+t_it.current()->name();
+ (*item)[COLUMN_ID_TABLE]=QString(" ") + t_it.current()->name();
+ d->fieldColumnData->append( item );
+ d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1); //cache
+ }
+ }
+//TODO
+}
+
+KexiRelationWidget *KexiQueryDesignerGuiEditor::relationView() const
+{
+ return d->relations;
+}
+
+KexiQueryPart::TempData *
+KexiQueryDesignerGuiEditor::tempData() const
+{
+ return static_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
+}
+
+static QString msgCannotSwitch_EmptyDesign() {
+ return i18n("Cannot switch to data view, because query design is empty.\n"
+ "First, please create your design.");
+}
+
+bool
+KexiQueryDesignerGuiEditor::buildSchema(QString *errMsg)
+{
+ //build query schema
+ KexiQueryPart::TempData * temp = tempData();
+ if (temp->query()) {
+ temp->clearQuery();
+ } else {
+ temp->setQuery( new KexiDB::QuerySchema() );
+ }
+
+ //add tables
+ for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
+/*! @todo what about query? */
+ temp->query()->addTable( it.current()->schema()->table() );
+ }
+
+ //add fields, also build:
+ // -WHERE expression
+ // -ORDER BY list
+ KexiDB::BaseExpr *whereExpr = 0;
+ const uint count = QMIN(d->data->count(), d->sets->size());
+ bool fieldsFound = false;
+ KexiTableViewData::Iterator it(d->data->iterator());
+ for (uint i=0; i<count && it.current(); ++it, i++) {
+ if (!it.current()->at(COLUMN_ID_TABLE).isNull() && it.current()->at(COLUMN_ID_COLUMN).isNull()) {
+ //show message about missing field name, and set focus to that cell
+ kexipluginsdbg << "no field provided!" << endl;
+ d->dataTable->dataAwareObject()->setCursorPosition(i,0);
+ if (errMsg)
+ *errMsg = i18n("Select column for table \"%1\"")
+ .arg(it.current()->at(COLUMN_ID_TABLE).toString());
+ return false;
+ }
+
+ KoProperty::Set *set = d->sets->at(i);
+ if (set) {
+ QString tableName = (*set)["table"].value().toString().stripWhiteSpace();
+ QString fieldName = (*set)["field"].value().toString();
+ QString fieldAndTableName = fieldName;
+ KexiDB::Field *currentField = 0; // will be set if this column is a single field
+ KexiDB::QueryColumnInfo* currentColumn = 0;
+ if (!tableName.isEmpty())
+ fieldAndTableName.prepend(tableName+".");
+ const bool fieldVisible = (*set)["visible"].value().toBool();
+ QString criteriaStr = (*set)["criteria"].value().toString();
+ QCString alias = (*set)["alias"].value().toCString();
+ if (!criteriaStr.isEmpty()) {
+ int token;
+ KexiDB::BaseExpr *criteriaExpr = parseExpressionString(criteriaStr, token,
+ true/*allowRelationalOperator*/);
+ if (!criteriaExpr) {//for sanity
+ if (errMsg)
+ *errMsg = i18n("Invalid criteria \"%1\"").arg(criteriaStr);
+ delete whereExpr;
+ return false;
+ }
+ //build relational expression for column variable
+ KexiDB::VariableExpr *varExpr = new KexiDB::VariableExpr(fieldAndTableName);
+ criteriaExpr = new KexiDB::BinaryExpr(KexiDBExpr_Relational, varExpr, token, criteriaExpr);
+ //critera ok: add it to WHERE section
+ if (whereExpr)
+ whereExpr = new KexiDB::BinaryExpr(KexiDBExpr_Logical, whereExpr, AND, criteriaExpr);
+ else //first expr.
+ whereExpr = criteriaExpr;
+ }
+ if (tableName.isEmpty()) {
+ if ((*set)["isExpression"].value().toBool()==true) {
+ //add expression column
+ int dummyToken;
+ KexiDB::BaseExpr *columnExpr = parseExpressionString(fieldName, dummyToken,
+ false/*!allowRelationalOperator*/);
+ if (!columnExpr) {
+ if (errMsg)
+ *errMsg = i18n("Invalid expression \"%1\"").arg(fieldName);
+ return false;
+ }
+ temp->query()->addExpression(columnExpr, fieldVisible);
+ if (fieldVisible)
+ fieldsFound = true;
+ if (!alias.isEmpty())
+ temp->query()->setColumnAlias( temp->query()->fieldCount()-1, alias );
+ }
+ //TODO
+ }
+ else if (tableName=="*") {
+ //all tables asterisk
+ temp->query()->addAsterisk( new KexiDB::QueryAsterisk( temp->query(), 0 ), fieldVisible );
+ if (fieldVisible)
+ fieldsFound = true;
+ continue;
+ }
+ else {
+ KexiDB::TableSchema *t = d->conn->tableSchema(tableName);
+ if (fieldName=="*") {
+ //single-table asterisk: <tablename> + ".*" + number
+ temp->query()->addAsterisk( new KexiDB::QueryAsterisk( temp->query(), t ), fieldVisible );
+ if (fieldVisible)
+ fieldsFound = true;
+ } else {
+ if (!t) {
+ kexipluginswarn << "query designer: NO TABLE '"
+ << (*set)["table"].value().toString() << "'" << endl;
+ continue;
+ }
+ currentField = t->field( fieldName );
+ if (!currentField) {
+ kexipluginswarn << "query designer: NO FIELD '" << fieldName << "'" << endl;
+ continue;
+ }
+ if (!fieldVisible && criteriaStr.isEmpty() && (*set)["isExpression"]
+ && (*set)["sorting"].value().toString()!="nosorting")
+ {
+ kexipluginsdbg << "invisible field with sorting: do not add it to the fields list" << endl;
+ continue;
+ }
+ temp->query()->addField(currentField, fieldVisible);
+ currentColumn = temp->query()->expandedOrInternalField(
+ temp->query()->fieldsExpanded().count() - 1 );
+ if (fieldVisible)
+ fieldsFound = true;
+ if (!alias.isEmpty())
+ temp->query()->setColumnAlias( temp->query()->fieldCount()-1, alias );
+ }
+ }
+ }
+ else {//!set
+ kexipluginsdbg << it.current()->at(COLUMN_ID_TABLE).toString() << endl;
+ }
+ }
+ if (!fieldsFound) {
+ if (errMsg)
+ *errMsg = msgCannotSwitch_EmptyDesign();
+ return false;
+ }
+ if (whereExpr)
+ kexipluginsdbg << "KexiQueryDesignerGuiEditor::buildSchema(): setting CRITERIA: "
+ << whereExpr->debugString() << endl;
+
+ //set always, because if whereExpr==NULL,
+ //this will clear prev. expr
+ temp->query()->setWhereExpression( whereExpr );
+
+ //add relations (looking for connections)
+ for (ConnectionListIterator it(*d->relations->connections()); it.current(); ++it) {
+ KexiRelationViewTableContainer *masterTable = it.current()->masterTable();
+ KexiRelationViewTableContainer *detailsTable = it.current()->detailsTable();
+
+/*! @todo what about query? */
+ temp->query()->addRelationship(
+ masterTable->schema()->table()->field(it.current()->masterField()),
+ detailsTable->schema()->table()->field(it.current()->detailsField()) );
+ }
+
+ // Add sorting information (ORDER BY) - we can do that only now
+ // after all QueryColumnInfo items are instantiated
+ KexiDB::OrderByColumnList orderByColumns;
+ it = d->data->iterator();
+ int fieldNumber = -1; //field number (empty rows are omitted)
+ for (uint i=0/*row number*/; i<count && it.current(); ++it, i++) {
+ KoProperty::Set *set = d->sets->at(i);
+ if (!set)
+ continue;
+ fieldNumber++;
+ KexiDB::Field *currentField = 0;
+ KexiDB::QueryColumnInfo *currentColumn = 0;
+ QString sortingString( (*set)["sorting"].value().toString() );
+ if (sortingString!="ascending" && sortingString!="descending")
+ continue;
+ if (!(*set)["visible"].value().toBool()) {
+ // this row defines invisible field but contains sorting information,
+ // what means KexiDB::Field should be used as a reference for this sorting
+ // Note1: alias is not supported here.
+
+ // Try to find a field (not mentioned after SELECT):
+ currentField = temp->query()->findTableField( (*set)["field"].value().toString() );
+ if (!currentField) {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::buildSchema(): NO FIELD '"
+ << (*set)["field"].value().toString()
+ << " available for sorting" << endl;
+ continue;
+ }
+ orderByColumns.appendField(*currentField, sortingString=="ascending");
+ continue;
+ }
+ currentField = temp->query()->field( (uint)fieldNumber );
+ if (!currentField || currentField->isExpression() || currentField->isQueryAsterisk())
+//! @todo support expressions here
+ continue;
+//! @todo ok, but not for expressions
+ QString aliasString( (*set)["alias"].value().toString() );
+ currentColumn = temp->query()->columnInfo(
+ (*set)["table"].value().toString() + "."
+ + (aliasString.isEmpty() ? currentField->name() : aliasString) );
+ if (currentField && currentColumn) {
+ if (currentColumn->visible)
+ orderByColumns.appendColumn(*currentColumn, sortingString=="ascending");
+ else if (currentColumn->field)
+ orderByColumns.appendField(*currentColumn->field, sortingString=="ascending");
+ }
+ }
+ temp->query()->setOrderByColumnList( orderByColumns );
+
+ temp->query()->debug();
+ temp->registerTableSchemaChanges(temp->query());
+ //TODO?
+ return true;
+}
+
+tristate
+KexiQueryDesignerGuiEditor::beforeSwitchTo(int mode, bool &dontStore)
+{
+ kexipluginsdbg << "KexiQueryDesignerGuiEditor::beforeSwitch()" << mode << endl;
+
+ if (!d->dataTable->dataAwareObject()->acceptRowEdit())
+ return cancelled;
+
+ if (mode==Kexi::DesignViewMode) {
+ return true;
+ }
+ else if (mode==Kexi::DataViewMode) {
+// if (!d->dataTable->dataAwareObject()->acceptRowEdit())
+ // return cancelled;
+
+ if (!dirty() && parentDialog()->neverSaved()) {
+ KMessageBox::information(this, msgCannotSwitch_EmptyDesign());
+ return cancelled;
+ }
+ if (dirty() || !tempData()->query()) {
+ //remember current design in a temporary structure
+ dontStore=true;
+ QString errMsg;
+ //build schema; problems are not allowed
+ if (!buildSchema(&errMsg)) {
+ KMessageBox::sorry(this, errMsg);
+ return cancelled;
+ }
+ }
+ //TODO
+ return true;
+ }
+ else if (mode==Kexi::TextViewMode) {
+ dontStore=true;
+ //build schema; ignore problems
+ buildSchema();
+/* if (tempData()->query && tempData()->query->fieldCount()==0) {
+ //no fields selected: let's add "*" (all-tables asterisk),
+ // otherwise SQL statement will be invalid
+ tempData()->query->addAsterisk( new KexiDB::QueryAsterisk( tempData()->query ) );
+ }*/
+ //todo
+ return true;
+ }
+
+ return false;
+}
+
+tristate
+KexiQueryDesignerGuiEditor::afterSwitchFrom(int mode)
+{
+ const bool was_dirty = dirty();
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ if (mode==Kexi::NoViewMode || (mode==Kexi::DataViewMode && !tempData()->query())) {
+ //this is not a SWITCH but a fresh opening in this view mode
+ if (!m_dialog->neverSaved()) {
+ if (!loadLayout()) {
+ //err msg
+ parentDialog()->setStatus(conn,
+ i18n("Query definition loading failed."),
+ i18n("Query design may be corrupted so it could not be opened even in text view.\n"
+ "You can delete the query and create it again."));
+ return false;
+ }
+ // Invalid queries case:
+ // KexiDialogBase::switchToViewMode() first opens DesignViewMode,
+ // and then KexiQueryPart::loadSchemaData() doesn't allocate QuerySchema object
+ // do we're carefully looking at parentDialog()->schemaData()
+ KexiDB::QuerySchema * q = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
+ if (q) {
+ KexiDB::ResultInfo result;
+ showFieldsForQuery( q, result );
+ if (!result.success) {
+ parentDialog()->setStatus(&result, i18n("Query definition loading failed."));
+ tempData()->proposeOpeningInTextViewModeBecauseOfProblems = true;
+ return false;
+ }
+ }
+//! @todo load global query properties
+ }
+ }
+ else if (mode==Kexi::TextViewMode || mode==Kexi::DataViewMode) {
+ // Switch from text or data view. In the second case, the design could be changed as well
+ // because there could be changes made in the text view before switching to the data view.
+ if (tempData()->queryChangedInPreviousView) {
+ //previous view changed query data
+ //-clear and regenerate GUI items
+ initTableRows();
+ //todo
+ if (tempData()->query()) {
+ //there is a query schema to show
+ showTablesForQuery( tempData()->query() );
+ //-show fields
+ KexiDB::ResultInfo result;
+ showFieldsAndRelationsForQuery( tempData()->query(), result );
+ if (!result.success) {
+ parentDialog()->setStatus(&result, i18n("Query definition loading failed."));
+ return false;
+ }
+ }
+ else {
+ d->relations->clear();
+ }
+ }
+//! @todo load global query properties
+ }
+
+ if (mode==Kexi::DataViewMode) {
+ //this is just a SWITCH from data view
+ //set cursor if needed:
+ if (d->dataTable->dataAwareObject()->currentRow()<0
+ || d->dataTable->dataAwareObject()->currentColumn()<0)
+ {
+ d->dataTable->dataAwareObject()->ensureCellVisible(0,0);
+ d->dataTable->dataAwareObject()->setCursorPosition(0,0);
+ }
+ }
+ tempData()->queryChangedInPreviousView = false;
+ setFocus(); //to allow shared actions proper update
+ if (!was_dirty)
+ setDirty(false);
+ return true;
+}
+
+
+KexiDB::SchemaData*
+KexiQueryDesignerGuiEditor::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ if (!d->dataTable->dataAwareObject()->acceptRowEdit()) {
+ cancel = true;
+ return 0;
+ }
+ QString errMsg;
+ KexiQueryPart::TempData * temp = tempData();
+ if (!temp->query() || !(viewMode()==Kexi::DesignViewMode && !temp->queryChangedInPreviousView)) {
+ //only rebuild schema if it has not been rebuilt previously
+ if (!buildSchema(&errMsg)) {
+ KMessageBox::sorry(this, errMsg);
+ cancel = true;
+ return 0;
+ }
+ }
+ (KexiDB::SchemaData&)*temp->query() = sdata; //copy main attributes
+
+ bool ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *temp->query(), true /*newObject*/ );
+ m_dialog->setId( temp->query()->id() );
+
+ if (ok)
+ ok = storeLayout();
+
+// temp->query = 0; //will be returned, so: don't keep it
+ if (!ok) {
+ temp->setQuery( 0 );
+// delete query;
+ return 0;
+ }
+ return temp->takeQuery(); //will be returned, so: don't keep it in temp
+}
+
+tristate KexiQueryDesignerGuiEditor::storeData(bool dontAsk)
+{
+ if (!d->dataTable->dataAwareObject()->acceptRowEdit())
+ return cancelled;
+
+ const bool was_dirty = dirty();
+ tristate res = KexiViewBase::storeData(dontAsk); //this clears dirty flag
+ if (true == res)
+ res = buildSchema();
+ if (true == res)
+ res = storeLayout();
+ if (true != res) {
+ if (was_dirty)
+ setDirty(true);
+ }
+ return res;
+}
+
+void KexiQueryDesignerGuiEditor::showTablesForQuery(KexiDB::QuerySchema *query)
+{
+//replaced by code below that preserves geometries d->relations->clear();
+
+ // instead of hiding all tables and showing some tables,
+ // show only these new and hide these unncecessary; the same for connections)
+ d->slotTableAdded_enabled = false; //speedup
+ d->relations->removeAllConnections(); //connections will be recreated
+ d->relations->hideAllTablesExcept( query->tables() );
+ for (KexiDB::TableSchema::ListIterator it(*query->tables()); it.current(); ++it) {
+ d->relations->addTable( it.current() );
+ }
+
+ d->slotTableAdded_enabled = true;
+ updateColumnsData();
+}
+
+void KexiQueryDesignerGuiEditor::addConnection(
+ KexiDB::Field *masterField, KexiDB::Field *detailsField)
+{
+ SourceConnection conn;
+ conn.masterTable = masterField->table()->name(); //<<<TODO
+ conn.masterField = masterField->name();
+ conn.detailsTable = detailsField->table()->name();
+ conn.detailsField = detailsField->name();
+ d->relations->addConnection( conn );
+}
+
+void KexiQueryDesignerGuiEditor::showFieldsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result)
+{
+ showFieldsOrRelationsForQueryInternal(query, true, false, result);
+}
+
+void KexiQueryDesignerGuiEditor::showRelationsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result)
+{
+ showFieldsOrRelationsForQueryInternal(query, false, true, result);
+}
+
+void KexiQueryDesignerGuiEditor::showFieldsAndRelationsForQuery(KexiDB::QuerySchema *query,
+ KexiDB::ResultInfo& result)
+{
+ showFieldsOrRelationsForQueryInternal(query, true, true, result);
+}
+
+void KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(
+ KexiDB::QuerySchema *query, bool showFields, bool showRelations, KexiDB::ResultInfo& result)
+{
+ result.clear();
+ const bool was_dirty = dirty();
+
+ //1. Show explicitly declared relations:
+ if (showRelations) {
+ KexiDB::Relationship *rel;
+ for (KexiDB::Relationship::ListIterator it(*query->relationships());
+ (rel=it.current()); ++it)
+ {
+//! @todo: now only sigle-field relationships are implemented!
+ KexiDB::Field *masterField = rel->masterIndex()->fields()->first();
+ KexiDB::Field *detailsField = rel->detailsIndex()->fields()->first();
+ addConnection(masterField, detailsField);
+ }
+ }
+
+ //2. Collect information about criterias
+ // --this must be top level chain of AND's
+ // --this will also show joins as: [table1.]field1 = [table2.]field2
+ QDict<KexiDB::BaseExpr> criterias(101, false);
+ KexiDB::BaseExpr* e = query->whereExpression();
+ KexiDB::BaseExpr* eItem = 0;
+ while (e) {
+ //eat parentheses because the expression can be (....) AND (... AND ... )
+ while (e && e->toUnary() && e->token()=='(')
+ e = e->toUnary()->arg();
+
+ if (e->toBinary() && e->token()==AND) {
+ eItem = e->toBinary()->left();
+ e = e->toBinary()->right();
+ }
+ else {
+ eItem = e;
+ e = 0;
+ }
+
+ //eat parentheses
+ while (eItem && eItem->toUnary() && eItem->token()=='(')
+ eItem = eItem->toUnary()->arg();
+
+ if (!eItem)
+ continue;
+
+ kexidbg << eItem->toString() << endl;
+ KexiDB::BinaryExpr* binary = eItem->toBinary();
+ if (binary && eItem->exprClass()==KexiDBExpr_Relational) {
+ KexiDB::Field *leftField = 0, *rightField = 0;
+ if (eItem->token()=='='
+ && binary->left()->toVariable()
+ && binary->right()->toVariable()
+ && (leftField = query->findTableField( binary->left()->toString() ))
+ && (rightField = query->findTableField( binary->right()->toString() )))
+ {
+//! @todo move this check to parser on QuerySchema creation
+//! or to QuerySchema creation (WHERE expression should be then simplified
+//! by removing joins
+
+ //this is relationship defined as following JOIN: [table1.]field1 = [table2.]field2
+ if (showRelations) {
+//! @todo testing primary key here is too simplified; maybe look ar isForeignKey() or indices..
+//! @todo what about multifield joins?
+ if (leftField->isPrimaryKey())
+ addConnection(leftField /*master*/, rightField /*details*/);
+ else
+ addConnection(rightField /*master*/, leftField /*details*/);
+//! @todo addConnection() should have "bool oneToOne" arg, for 1-to-1 relations
+ }
+ }
+ else if (binary->left()->toVariable()) {
+ //this is: variable , op , argument
+ //store variable -> argument:
+ criterias.insert(binary->left()->toVariable()->name, binary->right());
+ }
+ else if (binary->right()->toVariable()) {
+ //this is: argument , op , variable
+ //store variable -> argument:
+ criterias.insert(binary->right()->toVariable()->name, binary->left());
+ }
+ }
+ } //while
+
+ if (!showFields)
+ return;
+
+ //3. show fields (including * and table.*)
+ uint row_num = 0;
+ KexiDB::Field *field;
+ QPtrDict<char> usedCriterias(101); // <-- used criterias will be saved here
+ // so in step 4. we will be able to add
+ // remaining invisible columns with criterias
+ for (KexiDB::Field::ListIterator it(*query->fields());
+ (field = it.current()); ++it, row_num++)
+ {
+ //append a new row
+ QString tableName, fieldName, columnAlias, criteriaString;
+ KexiDB::BinaryExpr *criteriaExpr = 0;
+ KexiDB::BaseExpr *criteriaArgument = 0;
+ if (field->isQueryAsterisk()) {
+ if (field->table()) {//single-table asterisk
+ tableName = field->table()->name();
+ fieldName = "*";
+ }
+ else {//all-tables asterisk
+ tableName = "*";
+ fieldName = "";
+ }
+ }
+ else {
+ columnAlias = query->columnAlias(row_num);
+ if (field->isExpression()) {
+// if (columnAlias.isEmpty()) {
+// columnAlias = i18n("expression", "expr%1").arg(row_num); //TODO
+// }
+// if (columnAlias.isEmpty())
+//TODO: ok? perhaps do not allow to omit aliases?
+ fieldName = field->expression()->toString();
+// else
+// fieldName = columnAlias + ": " + field->expression()->toString();
+ }
+ else {
+ tableName = field->table()->name();
+ fieldName = field->name();
+ criteriaArgument = criterias[fieldName];
+ if (!criteriaArgument) {//try table.field
+ criteriaArgument = criterias[tableName+"."+fieldName];
+ }
+ if (criteriaArgument) {//criteria expression is just a parent of argument
+ criteriaExpr = criteriaArgument->parent()->toBinary();
+ usedCriterias.insert(criteriaArgument, (char*)1); //save info. about used criteria
+ }
+ }
+ }
+ //create new row data
+ KexiTableItem *newItem = createNewRow(tableName, fieldName, true /* visible*/);
+ if (criteriaExpr) {
+//! @todo fix for !INFIX operators
+ if (criteriaExpr->token()=='=')
+ criteriaString = criteriaArgument->toString();
+ else
+ criteriaString = criteriaExpr->tokenToString() + " " + criteriaArgument->toString();
+ (*newItem)[COLUMN_ID_CRITERIA] = criteriaString;
+ }
+ d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
+ //OK, row inserted: create a new set for it
+ KoProperty::Set &set = *createPropertySet( row_num, tableName, fieldName, true/*new one*/ );
+ if (!columnAlias.isEmpty())
+ set["alias"].setValue(columnAlias, false);
+ if (!criteriaString.isEmpty())
+ set["criteria"].setValue( criteriaString, false );
+ if (field->isExpression()) {
+// (*newItem)[COLUMN_ID_COLUMN] = ;
+ if (!d->changeSingleCellValue(*newItem, COLUMN_ID_COLUMN,
+ QVariant(columnAlias + ": " + field->expression()->toString()), &result))
+ return; //problems with setting column expression
+ }
+ }
+
+ //4. show ORDER BY information
+ d->data->clearRowEditBuffer();
+ KexiDB::OrderByColumnList &orderByColumns = query->orderByColumnList();
+ QMap<KexiDB::QueryColumnInfo*,int> columnsOrder(
+ query->columnsOrder(KexiDB::QuerySchema::UnexpandedListWithoutAsterisks) );
+ for (KexiDB::OrderByColumn::ListConstIterator orderByColumnsIt( orderByColumns.constBegin() );
+ orderByColumnsIt!=orderByColumns.constEnd(); ++orderByColumnsIt)
+ {
+ KexiDB::QueryColumnInfo *column = (*orderByColumnsIt).column();
+ KexiTableItem *rowItem = 0;
+ KoProperty::Set *rowPropertySet = 0;
+ if (column) {
+ //sorting for visible column
+ if (column->visible) {
+ if (columnsOrder.contains(column)) {
+ const int columnPosition = columnsOrder[ column ];
+ rowItem = d->data->at( columnPosition );
+ rowPropertySet = d->sets->at( columnPosition );
+ kexipluginsdbg << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal():\n\t"
+ "Setting \"" << (*orderByColumnsIt).debugString() << "\" sorting for row #"
+ << columnPosition << endl;
+ }
+ }
+ }
+ else if ((*orderByColumnsIt).field()) {
+ //this will be presented as invisible field: create new row
+ field = (*orderByColumnsIt).field();
+ QString tableName( field->table() ? field->table()->name() : QString::null );
+ rowItem = createNewRow( tableName, field->name(), false /* !visible*/);
+ d->dataTable->dataAwareObject()->insertItem(rowItem, row_num);
+ rowPropertySet = createPropertySet( row_num, tableName, field->name(), true /*newOne*/ );
+ propertySetSwitched();
+ kexipluginsdbg << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal():\n\t"
+ "Setting \"" << (*orderByColumnsIt).debugString() << "\" sorting for invisible field "
+ << field->name() << ", table " << tableName << " -row #" << row_num << endl;
+ row_num++;
+ }
+ //alter sorting for either existing or new row
+ if (rowItem && rowPropertySet) {
+ d->data->updateRowEditBuffer(rowItem, COLUMN_ID_SORTING,
+ (*orderByColumnsIt).ascending() ? 1 : 2); // this will automatically update "sorting" property
+ // in slotBeforeCellChanged()
+ d->data->saveRowChanges(*rowItem, true);
+ (*rowPropertySet)["sorting"].clearModifiedFlag(); // this property should look "fresh"
+ if (!rowItem->at(COLUMN_ID_VISIBLE).toBool()) //update
+ (*rowPropertySet)["visible"].setValue(QVariant(false,0), false/*rememberOldValue*/);
+ }
+ }
+
+ //5. Show fields for unused criterias (with "Visible" column set to false)
+ KexiDB::BaseExpr *criteriaArgument; // <-- contains field or table.field
+ for (QDictIterator<KexiDB::BaseExpr> it(criterias); (criteriaArgument = it.current()); ++it) {
+ if (usedCriterias[it.current()])
+ continue;
+ //unused: append a new row
+ KexiDB::BinaryExpr *criteriaExpr = criteriaArgument->parent()->toBinary();
+ if (!criteriaExpr) {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
+ "criteriaExpr is not a binary expr" << endl;
+ continue;
+ }
+ KexiDB::VariableExpr *columnNameArgument = criteriaExpr->left()->toVariable(); //left or right
+ if (!columnNameArgument) {
+ columnNameArgument = criteriaExpr->right()->toVariable();
+ if (!columnNameArgument) {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
+ "columnNameArgument is not a variable (table or table.field) expr" << endl;
+ continue;
+ }
+ }
+ KexiDB::Field* field = 0;
+ if (-1 == columnNameArgument->name.find('.') && query->tables()->count()==1) {
+ //extreme case: only field name provided for one-table query:
+ field = query->tables()->first()->field(columnNameArgument->name);
+ }
+ else {
+ field = query->findTableField(columnNameArgument->name);
+ }
+
+ if (!field) {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
+ "no columnInfo found in the query for name \"" << columnNameArgument->name << endl;
+ continue;
+ }
+ QString tableName, fieldName, columnAlias, criteriaString;
+//! @todo what about ALIAS?
+ tableName = field->table()->name();
+ fieldName = field->name();
+ //create new row data
+ KexiTableItem *newItem = createNewRow(tableName, fieldName, false /* !visible*/);
+ if (criteriaExpr) {
+//! @todo fix for !INFIX operators
+ if (criteriaExpr->token()=='=')
+ criteriaString = criteriaArgument->toString();
+ else
+ criteriaString = criteriaExpr->tokenToString() + " " + criteriaArgument->toString();
+ (*newItem)[COLUMN_ID_CRITERIA] = criteriaString;
+ }
+ d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
+ //OK, row inserted: create a new set for it
+ KoProperty::Set &set = *createPropertySet( row_num++, tableName, fieldName, true/*new one*/ );
+//! @todo if (!columnAlias.isEmpty())
+//! @todo set["alias"].setValue(columnAlias, false);
+//// if (!criteriaString.isEmpty())
+ set["criteria"].setValue( criteriaString, false );
+ set["visible"].setValue( QVariant(false,1), false );
+ }
+
+ //current property set has most probably changed
+ propertySetSwitched();
+
+ if (!was_dirty)
+ setDirty(false);
+ //move to 1st column, 1st row
+ d->dataTable->dataAwareObject()->ensureCellVisible(0,0);
+// tempData()->registerTableSchemaChanges(query);
+}
+
+bool KexiQueryDesignerGuiEditor::loadLayout()
+{
+ QString xml;
+// if (!loadDataBlock( xml, "query_layout" )) {
+ loadDataBlock( xml, "query_layout" );
+ //TODO errmsg
+// return false;
+// }
+ if (xml.isEmpty()) {
+ //in a case when query layout was not saved, build layout by hand
+ // -- dynamic cast because of a need for handling invalid queries
+ // (as in KexiQueryDesignerGuiEditor::afterSwitchFrom()):
+ KexiDB::QuerySchema * q = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
+ if (q) {
+ showTablesForQuery( q );
+ KexiDB::ResultInfo result;
+ showRelationsForQuery( q, result );
+ if (!result.success) {
+ parentDialog()->setStatus(&result, i18n("Query definition loading failed."));
+ return false;
+ }
+ }
+ return true;
+ }
+
+ QDomDocument doc;
+ doc.setContent(xml);
+ QDomElement doc_el = doc.documentElement(), el;
+ if (doc_el.tagName()!="query_layout") {
+ //TODO errmsg
+ return false;
+ }
+
+ const bool was_dirty = dirty();
+
+ //add tables and relations to the relation view
+ for (el = doc_el.firstChild().toElement(); !el.isNull(); el=el.nextSibling().toElement()) {
+ if (el.tagName()=="table") {
+ KexiDB::TableSchema *t = d->conn->tableSchema(el.attribute("name"));
+ int x = el.attribute("x","-1").toInt();
+ int y = el.attribute("y","-1").toInt();
+ int width = el.attribute("width","-1").toInt();
+ int height = el.attribute("height","-1").toInt();
+ QRect rect;
+ if (x!=-1 || y!=-1 || width!=-1 || height!=-1)
+ rect = QRect(x,y,width,height);
+ d->relations->addTable( t, rect );
+ }
+ else if (el.tagName()=="conn") {
+ SourceConnection src_conn;
+ src_conn.masterTable = el.attribute("mtable");
+ src_conn.masterField = el.attribute("mfield");
+ src_conn.detailsTable = el.attribute("dtable");
+ src_conn.detailsField = el.attribute("dfield");
+ d->relations->addConnection(src_conn);
+ }
+ }
+
+ if (!was_dirty)
+ setDirty(false);
+ return true;
+}
+
+bool KexiQueryDesignerGuiEditor::storeLayout()
+{
+ KexiQueryPart::TempData * temp = tempData();
+
+ // Save SQL without driver-escaped keywords.
+ KexiDB::Connection* dbConn = mainWin()->project()->dbConnection();
+ if (m_dialog->schemaData()) //set this instance as obsolete (only if it's stored)
+ dbConn->setQuerySchemaObsolete( m_dialog->schemaData()->name() );
+
+ KexiDB::Connection::SelectStatementOptions options;
+ options.identifierEscaping = KexiDB::Driver::EscapeKexi|KexiDB::Driver::EscapeAsNecessary;
+ options.addVisibleLookupColumns = false;
+ QString sqlText = dbConn->selectStatement( *temp->query(), options );
+ if (!storeDataBlock( sqlText, "sql" )) {
+ return false;
+ }
+
+ //serialize detailed XML query definition
+ QString xml = "<query_layout>", tmp;
+ for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
+ KexiRelationViewTableContainer *table_cont = it.current();
+/*! @todo what about query? */
+ tmp = QString("<table name=\"")+QString(table_cont->schema()->name())+"\" x=\""
+ +QString::number(table_cont->x())
+ +"\" y=\""+QString::number(table_cont->y())
+ +"\" width=\""+QString::number(table_cont->width())
+ +"\" height=\""+QString::number(table_cont->height())
+ +"\"/>";
+ xml += tmp;
+ }
+
+ KexiRelationViewConnection *con;
+ for (ConnectionListIterator it(*d->relations->connections()); (con = it.current()); ++it) {
+ tmp = QString("<conn mtable=\"") + QString(con->masterTable()->schema()->name())
+ + "\" mfield=\"" + con->masterField() + "\" dtable=\""
+ + QString(con->detailsTable()->schema()->name())
+ + "\" dfield=\"" + con->detailsField() + "\"/>";
+ xml += tmp;
+ }
+ xml += "</query_layout>";
+ if (!storeDataBlock( xml, "query_layout" )) {
+ return false;
+ }
+
+// mainWin()->project()->reloadPartItem( m_dialog );
+
+ return true;
+}
+
+QSize KexiQueryDesignerGuiEditor::sizeHint() const
+{
+ QSize s1 = d->relations->sizeHint();
+ QSize s2 = d->head->sizeHint();
+ return QSize(QMAX(s1.width(),s2.width()), s1.height()+s2.height());
+}
+
+KexiTableItem*
+KexiQueryDesignerGuiEditor::createNewRow(const QString& tableName, const QString& fieldName,
+ bool visible) const
+{
+ KexiTableItem *newItem = d->data->createItem();
+ QString key;
+ if (tableName=="*")
+ key="*";
+ else {
+ if (!tableName.isEmpty())
+ key = (tableName+".");
+ key += fieldName;
+ }
+ (*newItem)[COLUMN_ID_COLUMN]=key;
+ (*newItem)[COLUMN_ID_TABLE]=tableName;
+ (*newItem)[COLUMN_ID_VISIBLE]=QVariant(visible, 1);
+#ifndef KEXI_NO_QUERY_TOTALS
+ (*newItem)[COLUMN_ID_TOTALS]=QVariant(0);
+#endif
+ return newItem;
+}
+
+void KexiQueryDesignerGuiEditor::slotDragOverTableRow(
+ KexiTableItem * /*item*/, int /*row*/, QDragMoveEvent* e)
+{
+ if (e->provides("kexi/field")) {
+ e->acceptAction(true);
+ }
+}
+
+void
+KexiQueryDesignerGuiEditor::slotDroppedAtRow(KexiTableItem * /*item*/, int /*row*/,
+ QDropEvent *ev, KexiTableItem*& newItem)
+{
+ QString sourceMimeType;
+ QString srcTable;
+ QString srcField;
+
+ if (!KexiFieldDrag::decodeSingle(ev,sourceMimeType,srcTable,srcField))
+ return;
+ //insert new row at specific place
+ newItem = createNewRow(srcTable, srcField, true /* visible*/);
+ d->droppedNewItem = newItem;
+ d->droppedNewTable = srcTable;
+ d->droppedNewField = srcField;
+ //TODO
+}
+
+void KexiQueryDesignerGuiEditor::slotNewItemAppendedForAfterDeletingInSpreadSheetMode()
+{
+ KexiTableItem *item = d->data->last();
+ if (item)
+ item->at(COLUMN_ID_VISIBLE) = QVariant(false, 0); //the same init as in initTableRows()
+}
+
+void KexiQueryDesignerGuiEditor::slotRowInserted(KexiTableItem* item, uint row, bool /*repaint*/)
+{
+ if (d->droppedNewItem && d->droppedNewItem==item) {
+ createPropertySet( row, d->droppedNewTable, d->droppedNewField, true );
+ propertySetSwitched();
+ d->droppedNewItem=0;
+ }
+}
+
+void KexiQueryDesignerGuiEditor::slotTableAdded(KexiDB::TableSchema & /*t*/)
+{
+ if (!d->slotTableAdded_enabled)
+ return;
+ updateColumnsData();
+ setDirty();
+ d->dataTable->setFocus();
+}
+
+void KexiQueryDesignerGuiEditor::slotTableHidden(KexiDB::TableSchema & /*t*/)
+{
+ updateColumnsData();
+ setDirty();
+}
+
+/*! @internal generates smallest unique alias */
+QCString KexiQueryDesignerGuiEditor::generateUniqueAlias() const
+{
+//TODO: add option for using non-i18n'd "expr" prefix?
+ const QCString expStr
+ = i18n("short for 'expression' word (only latin letters, please)", "expr").latin1();
+//TODO: optimization: cache it?
+ QAsciiDict<char> aliases(101);
+ for (int r = 0; r<(int)d->sets->size(); r++) {
+ KoProperty::Set *set = d->sets->at(r);
+ if (set) {
+ const QCString a = (*set)["alias"].value().toCString().lower();
+ if (!a.isEmpty())
+ aliases.insert(a,(char*)1);
+ }
+ }
+ int aliasNr=1;
+ for (;;aliasNr++) {
+ if (!aliases[expStr+QString::number(aliasNr).latin1()])
+ break;
+ }
+ return expStr+QString::number(aliasNr).latin1();
+}
+
+//! @todo this is primitive, temporary: reuse SQL parser
+KexiDB::BaseExpr*
+KexiQueryDesignerGuiEditor::parseExpressionString(const QString& fullString, int& token,
+ bool allowRelationalOperator)
+{
+ QString str = fullString.stripWhiteSpace();
+ int len = 0;
+ //KexiDB::BaseExpr *expr = 0;
+ //1. get token
+ token = 0;
+ //2-char-long tokens
+ if (str.startsWith(">="))
+ token = GREATER_OR_EQUAL;
+ else if (str.startsWith("<="))
+ token = LESS_OR_EQUAL;
+ else if (str.startsWith("<>"))
+ token = NOT_EQUAL;
+ else if (str.startsWith("!="))
+ token = NOT_EQUAL2;
+ else if (str.startsWith("=="))
+ token = '=';
+
+ if (token!=0)
+ len = 2;
+ else if (str.startsWith("=") //1-char-long tokens
+ || str.startsWith("<")
+ || str.startsWith(">"))
+ {
+ token = str[0].latin1();
+ len = 1;
+ }
+ else {
+ if (allowRelationalOperator)
+ token = '=';
+ }
+
+ if (!allowRelationalOperator && token!=0)
+ return 0;
+
+ //1. get expression after token
+ if (len>0)
+ str = str.mid(len).stripWhiteSpace();
+ if (str.isEmpty())
+ return 0;
+
+ KexiDB::BaseExpr *valueExpr = 0;
+ QRegExp re;
+ if (str.length()>=2 &&
+ (
+ (str.startsWith("\"") && str.endsWith("\""))
+ || (str.startsWith("'") && str.endsWith("'")))
+ )
+ {
+ valueExpr = new KexiDB::ConstExpr(CHARACTER_STRING_LITERAL, str.mid(1,str.length()-2));
+ }
+ else if (str.startsWith("[") && str.endsWith("]")) {
+ valueExpr = new KexiDB::QueryParameterExpr(str.mid(1,str.length()-2));
+ }
+ else if ((re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})")).exactMatch( str ))
+ {
+ valueExpr = new KexiDB::ConstExpr(DATE_CONST, QDate::fromString(
+ re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
+ +"-"+re.cap(3).rightJustify(2, '0'), Qt::ISODate));
+ }
+ else if ((re = QRegExp("(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
+ || (re = QRegExp("(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
+ {
+ QString res = re.cap(1).rightJustify(2, '0')+":"+re.cap(2).rightJustify(2, '0')
+ +":"+re.cap(3).rightJustify(2, '0');
+// kexipluginsdbg << res << endl;
+ valueExpr = new KexiDB::ConstExpr(TIME_CONST, QTime::fromString(res, Qt::ISODate));
+ }
+ else if ((re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
+ || (re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
+ {
+ QString res = re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
+ +"-"+re.cap(3).rightJustify(2, '0')
+ +"T"+re.cap(4).rightJustify(2, '0')+":"+re.cap(5).rightJustify(2, '0')
+ +":"+re.cap(6).rightJustify(2, '0');
+// kexipluginsdbg << res << endl;
+ valueExpr = new KexiDB::ConstExpr(DATETIME_CONST,
+ QDateTime::fromString(res, Qt::ISODate));
+ }
+ else if (str[0]>='0' && str[0]<='9' || str[0]=='-' || str[0]=='+') {
+ //number
+ QString decimalSym = KGlobal::locale()->decimalSymbol();
+ bool ok;
+ int pos = str.find('.');
+ if (pos==-1) {//second chance: local decimal symbol
+ pos = str.find(decimalSym);
+ }
+ if (pos>=0) {//real const number
+ const int left = str.left(pos).toInt(&ok);
+ if (!ok)
+ return 0;
+ const int right = str.mid(pos+1).toInt(&ok);
+ if (!ok)
+ return 0;
+ valueExpr = new KexiDB::ConstExpr(REAL_CONST, QPoint(left,right)); //decoded to QPoint
+ }
+ else {
+ //integer const
+ const Q_LLONG val = str.toLongLong(&ok);
+ if (!ok)
+ return 0;
+ valueExpr = new KexiDB::ConstExpr(INTEGER_CONST, val);
+ }
+ }
+ else if (str.lower()=="null") {
+ valueExpr = new KexiDB::ConstExpr(SQL_NULL, QVariant());
+ }
+ else {//identfier
+ if (!KexiUtils::isIdentifier(str))
+ return 0;
+ valueExpr = new KexiDB::VariableExpr(str);
+ //find first matching field for name 'str':
+ for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
+/*! @todo what about query? */
+ if (it.current()->schema()->table() && it.current()->schema()->table()->field(str)) {
+ valueExpr->toVariable()->field = it.current()->schema()->table()->field(str);
+ break;
+ }
+ }
+ }
+ return valueExpr;
+}
+
+void KexiQueryDesignerGuiEditor::slotBeforeCellChanged(KexiTableItem *item, int colnum,
+ QVariant& newValue, KexiDB::ResultInfo* result)
+{
+ if (colnum == COLUMN_ID_COLUMN) {
+ if (newValue.isNull()) {
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, QVariant(), false/*!allowSignals*/);
+ d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(false,1));//invisible
+ d->data->updateRowEditBuffer(item, COLUMN_ID_SORTING, QVariant());
+#ifndef KEXI_NO_QUERY_TOTALS
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant());//remove totals
+#endif
+ d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, QVariant());//remove crit.
+ d->sets->removeCurrentPropertySet();
+ }
+ else {
+ //auto fill 'table' column
+ QString fieldId( newValue.toString().stripWhiteSpace() ); //tmp, can look like "table.field"
+ QString fieldName; //"field" part of "table.field" or expression string
+ QString tableName; //empty for expressions
+ QCString alias;
+ QString columnValueForExpr; //for setting pretty printed "alias: expr" in 1st column
+ const bool isExpression = !d->fieldColumnIdentifiers[fieldId];
+ if (isExpression) {
+ //this value is entered by hand and doesn't match
+ //any value in the combo box -- we're assuming this is an expression
+ //-table remains null
+ //-find "alias" in something like "alias : expr"
+ const int id = fieldId.find(':');
+ if (id>0) {
+ alias = fieldId.left(id).stripWhiteSpace().latin1();
+ if (!KexiUtils::isIdentifier(alias)) {
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ result->msg = i18n("Entered column alias \"%1\" is not a valid identifier.")
+ .arg(alias);
+ result->desc = i18n("Identifiers should start with a letter or '_' character");
+ return;
+ }
+ }
+ fieldName = fieldId.mid(id+1).stripWhiteSpace();
+ //check expr.
+ KexiDB::BaseExpr *e;
+ int dummyToken;
+ if ((e = parseExpressionString(fieldName, dummyToken, false/*allowRelationalOperator*/)))
+ {
+ fieldName = e->toString(); //print it prettier
+ //this is just checking: destroy expr. object
+ delete e;
+ }
+ else {
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ result->msg = i18n("Invalid expression \"%1\"").arg(fieldName);
+ return;
+ }
+ }
+ else {//not expr.
+ //this value is properly selected from combo box list
+ if (fieldId=="*") {
+ tableName = "*";
+ }
+ else {
+ if (!KexiDB::splitToTableAndFieldParts(
+ fieldId, tableName, fieldName, KexiDB::SetFieldNameIfNoTableName))
+ {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::slotBeforeCellChanged(): no 'field' or 'table.field'" << endl;
+ return;
+ }
+ }
+ }
+ bool saveOldValue = true;
+ KoProperty::Set *set = d->sets->findPropertySetForItem(*item); //*propertyBuffer();
+ if (!set) {
+ saveOldValue = false; // no old val.
+ const int row = d->data->findRef(item);
+ if (row<0) {
+ result->success = false;
+ return;
+ }
+ set = createPropertySet( row, tableName, fieldName, true );
+ propertySetSwitched();
+ }
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, QVariant(tableName), false/*!allowSignals*/);
+ d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(true,1));
+#ifndef KEXI_NO_QUERY_TOTALS
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant(0));
+#endif
+ if (!sortingAllowed(fieldName, tableName)) {
+ // sorting is not available for "*" or "table.*" rows
+//! @todo what about expressions?
+ d->data->updateRowEditBuffer(item, COLUMN_ID_SORTING, QVariant());
+ }
+ //update properties
+ (*set)["field"].setValue(fieldName, saveOldValue);
+ if (isExpression) {
+ //-no alias but it's needed:
+ if (alias.isEmpty()) //-try oto get old alias
+ alias = (*set)["alias"].value().toCString();
+ if (alias.isEmpty()) //-generate smallest unique alias
+ alias = generateUniqueAlias();
+ }
+ (*set)["isExpression"].setValue(QVariant(isExpression,1), saveOldValue);
+ if (!alias.isEmpty()) {
+ (*set)["alias"].setValue(alias, saveOldValue);
+ //pretty printed "alias: expr"
+ newValue = QString(alias) + ": " + fieldName;
+ }
+ (*set)["caption"].setValue(QString::null, saveOldValue);
+ (*set)["table"].setValue(tableName, saveOldValue);
+ updatePropertiesVisibility(*set);
+ }
+ }
+ else if (colnum==COLUMN_ID_TABLE) {
+ if (newValue.isNull()) {
+ if (!item->at(COLUMN_ID_COLUMN).toString().isEmpty())
+ d->data->updateRowEditBuffer(item, COLUMN_ID_COLUMN, QVariant(), false/*!allowSignals*/);
+ d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(false,1));//invisible
+#ifndef KEXI_NO_QUERY_TOTALS
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant());//remove totals
+#endif
+ d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, QVariant());//remove crit.
+ d->sets->removeCurrentPropertySet();
+ }
+ //update property
+ KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
+ if (set) {
+ if ((*set)["isExpression"].value().toBool()==false) {
+ (*set)["table"] = newValue;
+ (*set)["caption"] = QString::null;
+ }
+ else {
+ //do not set table for expr. columns
+ newValue = QVariant();
+ }
+// KoProperty::Set &set = *propertyBuffer();
+ updatePropertiesVisibility(*set);
+ }
+ }
+ else if (colnum==COLUMN_ID_VISIBLE) {
+ bool saveOldValue = true;
+ if (!propertySet()) {
+ saveOldValue = false;
+ createPropertySet( d->dataTable->dataAwareObject()->currentRow(),
+ item->at(COLUMN_ID_TABLE).toString(), item->at(COLUMN_ID_COLUMN).toString(), true );
+#ifndef KEXI_NO_QUERY_TOTALS
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant(0));//totals
+#endif
+ propertySetSwitched();
+ }
+ KoProperty::Set &set = *propertySet();
+ set["visible"].setValue(newValue, saveOldValue);
+ }
+#ifndef KEXI_NO_QUERY_TOTALS
+ else if (colnum==COLUMN_ID_TOTALS) {
+ //TODO:
+ //unused yet
+ setDirty(true);
+ }
+#endif
+ else if (colnum==COLUMN_ID_SORTING) {
+ KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
+ QString table( set->property("table").value().toString() );
+ QString field( set->property("field").value().toString() );
+ if (newValue.toInt()==0 || sortingAllowed(field, table)) {
+ KoProperty::Property &property = set->property("sorting");
+ QString key( property.listData()->keysAsStringList()[ newValue.toInt() ] );
+ kexipluginsdbg << "new key=" << key << endl;
+ property.setValue(key, true);
+ }
+ else { //show msg: sorting is not available
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ result->msg = i18n("Could not set sorting for multiple columns (%1)")
+ .arg(table=="*" ? table : (table+".*"));
+ }
+ }
+ else if (colnum==COLUMN_ID_CRITERIA) {
+//! @todo this is primitive, temporary: reuse SQL parser
+ QString operatorStr, argStr;
+ KexiDB::BaseExpr* e = 0;
+ const QString str = newValue.toString().stripWhiteSpace();
+ int token;
+ QString field, table;
+ KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
+ if (set) {
+ field = (*set)["field"].value().toString();
+ table = (*set)["table"].value().toString();
+ }
+ if (!str.isEmpty() && (!set || table=="*" || field.find("*")!=-1)) {
+ //asterisk found! criteria not allowed
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ if (propertySet())
+ result->msg = i18n("Could not set criteria for \"%1\"")
+ .arg(table=="*" ? table : field);
+ else
+ result->msg = i18n("Could not set criteria for empty row");
+ //moved to result->allowToDiscardChanges handler //d->dataTable->dataAwareObject()->cancelEditor(); //prevents further editing of this cell
+ }
+ else if (str.isEmpty() || (e = parseExpressionString(str, token, true/*allowRelationalOperator*/)))
+ {
+ if (e) {
+ QString tokenStr;
+ if (token!='=') {
+ KexiDB::BinaryExpr be(KexiDBExpr_Relational, 0, token, 0);
+ tokenStr = be.tokenToString() + " ";
+ }
+ (*set)["criteria"] = tokenStr + e->toString(); //print it prettier
+ //this is just checking: destroy expr. object
+ delete e;
+ }
+ else if (str.isEmpty()) {
+ (*set)["criteria"] = QVariant(); //clear it
+ }
+ setDirty(true);
+ }
+ else {
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ result->msg = i18n("Invalid criteria \"%1\"").arg(newValue.toString());
+ }
+ }
+}
+
+void KexiQueryDesignerGuiEditor::slotTablePositionChanged(KexiRelationViewTableContainer*)
+{
+ setDirty(true);
+}
+
+void KexiQueryDesignerGuiEditor::slotAboutConnectionRemove(KexiRelationViewConnection*)
+{
+ setDirty(true);
+}
+
+void KexiQueryDesignerGuiEditor::slotTableFieldDoubleClicked(
+ KexiDB::TableSchema* table, const QString& fieldName )
+{
+ if (!table || (!table->field(fieldName) && fieldName!="*"))
+ return;
+ int row_num;
+ //find last filled row in the GUI table
+ for (row_num=d->sets->size()-1; row_num>=0 && !d->sets->at(row_num); row_num--)
+ ;
+ row_num++; //after
+ //add row
+ KexiTableItem *newItem = createNewRow(table->name(), fieldName, true /* visible*/);
+ d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
+ d->dataTable->dataAwareObject()->setCursorPosition(row_num, 0);
+ //create buffer
+ createPropertySet( row_num, table->name(), fieldName, true/*new one*/ );
+ propertySetSwitched();
+ d->dataTable->setFocus();
+}
+
+KoProperty::Set *KexiQueryDesignerGuiEditor::propertySet()
+{
+ return d->sets->currentPropertySet();
+}
+
+void KexiQueryDesignerGuiEditor::updatePropertiesVisibility(KoProperty::Set& set)
+{
+ const bool asterisk = isAsterisk(
+ set["table"].value().toString(), set["field"].value().toString()
+ );
+#ifndef KEXI_NO_UNFINISHED
+ set["caption"].setVisible( !asterisk );
+#endif
+ set["alias"].setVisible( !asterisk );
+/*always invisible #ifndef KEXI_NO_UNFINISHED
+ set["sorting"].setVisible( !asterisk );
+#endif*/
+ propertySetReloaded(true);
+}
+
+KoProperty::Set*
+KexiQueryDesignerGuiEditor::createPropertySet( int row,
+ const QString& tableName, const QString& fieldName, bool newOne )
+{
+ //const bool asterisk = isAsterisk(tableName, fieldName);
+ QString typeName = "KexiQueryDesignerGuiEditor::Column";
+ KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
+ KoProperty::Property *prop;
+
+ //meta-info for property editor
+ set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Query column")) );
+ prop->setVisible(false);
+//! \todo add table_field icon (add buff->addProperty(prop = new KexiProperty("this:iconName", "table_field") );
+// prop->setVisible(false);
+
+ set->addProperty(prop = new KoProperty::Property("table", QVariant(tableName)) );
+ prop->setVisible(false);//always hidden
+
+ set->addProperty(prop = new KoProperty::Property("field", QVariant(fieldName)) );
+ prop->setVisible(false);//always hidden
+
+ set->addProperty(prop = new KoProperty::Property("caption", QVariant(QString::null), i18n("Caption") ) );
+#ifdef KEXI_NO_UNFINISHED
+ prop->setVisible(false);
+#endif
+
+ set->addProperty(prop = new KoProperty::Property("alias", QVariant(QString::null), i18n("Alias")) );
+
+ set->addProperty(prop = new KoProperty::Property("visible", QVariant(true, 4)) );
+ prop->setVisible(false);
+
+/*TODO:
+ set->addProperty(prop = new KexiProperty("totals", QVariant(QString::null)) );
+ prop->setVisible(false);*/
+
+ //sorting
+ QStringList slist, nlist;
+ slist << "nosorting" << "ascending" << "descending";
+ nlist << i18n("None") << i18n("Ascending") << i18n("Descending");
+ set->addProperty(prop = new KoProperty::Property("sorting",
+ slist, nlist, *slist.at(0), i18n("Sorting")));
+ prop->setVisible(false);
+
+ set->addProperty(prop = new KoProperty::Property("criteria", QVariant(QString::null)) );
+ prop->setVisible(false);
+
+ set->addProperty(prop = new KoProperty::Property("isExpression", QVariant(false, 1)) );
+ prop->setVisible(false);
+
+ connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
+ this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
+
+ d->sets->insert(row, set, newOne);
+
+ updatePropertiesVisibility(*set);
+ return set;
+}
+
+void KexiQueryDesignerGuiEditor::setFocus()
+{
+ d->dataTable->setFocus();
+}
+
+void KexiQueryDesignerGuiEditor::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
+{
+ const QCString& pname = property.name();
+/*
+ * TODO (js) use KexiProperty::setValidator(QString) when implemented as described in TODO #60
+ */
+ if (pname=="alias" || pname=="name") {
+ const QVariant& v = property.value();
+ if (!v.toString().stripWhiteSpace().isEmpty() && !KexiUtils::isIdentifier( v.toString() )) {
+ KMessageBox::sorry(this,
+ KexiUtils::identifierExpectedMessage(property.caption(), v.toString()));
+ property.resetValue();
+ }
+ if (pname=="alias") {
+ if (set["isExpression"].value().toBool()==true) {
+ //update value in column #1
+ d->dataTable->dataAwareObject()->acceptEditor();
+// d->dataTable->dataAwareObject()->setCursorPosition(d->dataTable->dataAwareObject()->currentRow(),0);
+ //d->dataTable->dataAwareObject()->startEditCurrentCell();
+ d->data->updateRowEditBuffer(d->dataTable->dataAwareObject()->selectedItem(),
+ 0, QVariant(set["alias"].value().toString() + ": " + set["field"].value().toString()));
+ d->data->saveRowChanges(*d->dataTable->dataAwareObject()->selectedItem(), true);
+// d->dataTable->dataAwareObject()->acceptRowEdit();
+ }
+ }
+ }
+}
+
+void KexiQueryDesignerGuiEditor::slotNewItemStored(KexiPart::Item& item)
+{
+ d->relations->objectCreated(item.mimeType(), item.name().latin1());
+}
+
+void KexiQueryDesignerGuiEditor::slotItemRemoved(const KexiPart::Item& item)
+{
+ d->relations->objectDeleted(item.mimeType(), item.name().latin1());
+}
+
+void KexiQueryDesignerGuiEditor::slotItemRenamed(const KexiPart::Item& item, const QCString& oldName)
+{
+ d->relations->objectRenamed(item.mimeType(), oldName, item.name().latin1());
+}
+
+#include "kexiquerydesignerguieditor.moc"
+
diff --git a/kexi/plugins/queries/kexiquerydesignerguieditor.h b/kexi/plugins/queries/kexiquerydesignerguieditor.h
new file mode 100644
index 00000000..03acb7f6
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignerguieditor.h
@@ -0,0 +1,170 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYDESIGNERGUIEDITOR_H
+#define KEXIQUERYDESIGNERGUIEDITOR_H
+
+#include <qguardedptr.h>
+#include <qsplitter.h>
+
+#include <kexiviewbase.h>
+#include "kexiquerypart.h"
+
+class KexiMainWindow;
+class KexiTableViewData;
+class KexiDataTable;
+class KexiTableItem;
+class KexiRelationWidget;
+class KexiSectionHeader;
+class KexiDataAwarePropertySet;
+class KexiRelationViewTableContainer;
+class KexiRelationViewConnection;
+
+namespace KexiPart
+{
+ class Item;
+}
+
+namespace KoProperty {
+ class Property;
+ class Set;
+}
+
+namespace KexiDB
+{
+ class Connection;
+ class QuerySchema;
+ class TableSchema;
+ class ResultInfo;
+}
+
+//! Design view of the Query Designer
+class KexiQueryDesignerGuiEditor : public KexiViewBase
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryDesignerGuiEditor(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0);
+ virtual ~KexiQueryDesignerGuiEditor();
+
+// KexiDB::QuerySchema *schema();
+
+ KexiRelationWidget *relationView() const;
+
+ virtual QSize sizeHint() const;
+
+ public slots:
+ virtual void setFocus();
+
+ protected:
+ void initTableColumns(); //!< Called just once.
+ void initTableRows(); //!< Called to have all rows empty.
+//unused void addRow(const QString &tbl, const QString &field);
+// void restore();
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+ virtual tristate afterSwitchFrom(int mode);
+
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+ virtual tristate storeData(bool dontAsk = false);
+
+ /*! Updates data in columns depending on tables that are currently inserted.
+ Tabular Data in combo box popups is updated as well. */
+ void updateColumnsData();
+
+ /*! \return property buffer associated with currently selected row (i.e. field)
+ or 0 if current row is empty. */
+ virtual KoProperty::Set *propertySet();
+
+ KoProperty::Set* createPropertySet( int row,
+ const QString& tableName, const QString& fieldName, bool newOne = false );
+
+ /*! Builds query schema out of information provided by gui.
+ The schema is stored in temp->query member.
+ \a errMsg is optional error message returned.
+ \return true on proper schema creation. */
+ bool buildSchema(QString *errMsg = 0);
+
+ KexiQueryPart::TempData * tempData() const;
+
+ /*! Helper: allocates and initializes new table view's row. Doesn't insert it, just returns.
+ \a tableName and \a fieldName shoudl be provided.
+ \a visible flag sets value for "Visible" column. */
+ KexiTableItem* createNewRow(const QString& tableName, const QString& fieldName,
+ bool visible) const;
+
+ KexiDB::BaseExpr* parseExpressionString(const QString& fullString, int& token,
+ bool allowRelationalOperator);
+
+ QCString generateUniqueAlias() const;
+ void updatePropertiesVisibility(KoProperty::Set& buf);
+
+ protected slots:
+ void slotDragOverTableRow(KexiTableItem *item, int row, QDragMoveEvent* e);
+ void slotDroppedAtRow(KexiTableItem *item, int row,
+ QDropEvent *ev, KexiTableItem*& newItem);
+ //! Reaction on appending a new item after deleting one
+ void slotNewItemAppendedForAfterDeletingInSpreadSheetMode();
+ void slotTableAdded(KexiDB::TableSchema &t);
+ void slotTableHidden(KexiDB::TableSchema &t);
+
+ //! Called before cell change in tableview.
+ void slotBeforeCellChanged(KexiTableItem *item, int colnum,
+ QVariant& newValue, KexiDB::ResultInfo* result);
+
+ void slotRowInserted(KexiTableItem* item, uint row, bool repaint);
+ void slotTablePositionChanged(KexiRelationViewTableContainer*);
+ void slotAboutConnectionRemove(KexiRelationViewConnection*);
+ void slotTableFieldDoubleClicked( KexiDB::TableSchema* table, const QString& fieldName );
+
+ /*! Loads layout of relation GUI diagram. */
+ bool loadLayout();
+
+ /*! Stores layout of relation GUI diagram. */
+ bool storeLayout();
+
+ void showTablesForQuery(KexiDB::QuerySchema *query);
+ //! @internal
+ void showFieldsOrRelationsForQueryInternal(
+ KexiDB::QuerySchema *query, bool showFields, bool showRelations, KexiDB::ResultInfo& result);
+ //! convenience method equal to showFieldsOrRelationsForQueryInternal(query, true, true)
+ void showFieldsAndRelationsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result);
+ //! convenience method equal to showFieldsOrRelationsForQueryInternal(query, true, false)
+ void showFieldsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result);
+ //! convenience method equal to showFieldsOrRelationsForQueryInternal(query, false, true)
+ void showRelationsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result);
+
+ void addConnection(KexiDB::Field *masterField, KexiDB::Field *detailsField);
+
+ void slotPropertyChanged(KoProperty::Set& list, KoProperty::Property& property);
+
+// void slotObjectCreated(const QCString &mime, const QCString& name);
+ void slotNewItemStored(KexiPart::Item&);
+ void slotItemRemoved(const KexiPart::Item& item);
+ void slotItemRenamed(const KexiPart::Item& item, const QCString& oldName);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class KexiQueryView; // for storeNewData() and storeData() only
+};
+
+#endif
+
diff --git a/kexi/plugins/queries/kexiquerydesignersql.cpp b/kexi/plugins/queries/kexiquerydesignersql.cpp
new file mode 100644
index 00000000..469d551c
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignersql.cpp
@@ -0,0 +1,542 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qsplitter.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qtimer.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+
+#include <kexiutils/utils.h>
+#include <kexidb/driver.h>
+#include <kexidb/connection.h>
+#include <kexidb/parser/parser.h>
+
+#include <kexiproject.h>
+#include <keximainwindow.h>
+
+#include "kexiquerydesignersqleditor.h"
+#include "kexiquerydesignersqlhistory.h"
+#include "kexiquerydesignersql.h"
+#include "kexiquerypart.h"
+
+#include "kexisectionheader.h"
+
+
+static bool compareSQL(const QString& sql1, const QString& sql2)
+{
+ //TODO: use reformatting functions here
+ return sql1.stripWhiteSpace()==sql2.stripWhiteSpace();
+}
+
+//===================
+
+//! @internal
+class KexiQueryDesignerSQLView::Private
+{
+ public:
+ Private() :
+ history(0)
+ , historyHead(0)
+ , statusPixmapOk( DesktopIcon("button_ok") )
+ , statusPixmapErr( DesktopIcon("button_cancel") )
+ , statusPixmapInfo( DesktopIcon("messagebox_info") )
+ , parsedQuery(0)
+ , heightForStatusMode(-1)
+ , heightForHistoryMode(-1)
+ , eventFilterForSplitterEnabled(true)
+ , justSwitchedFromNoViewMode(false)
+ , slotTextChangedEnabled(true)
+ {
+ }
+ KexiQueryDesignerSQLEditor *editor;
+ KexiQueryDesignerSQLHistory *history;
+ QLabel *pixmapStatus, *lblStatus;
+ QHBox *status_hbox;
+ QVBox *history_section;
+ KexiSectionHeader *head, *historyHead;
+ QPixmap statusPixmapOk, statusPixmapErr, statusPixmapInfo;
+ QSplitter *splitter;
+ KToggleAction *action_toggle_history;
+ //! For internal use, this pointer is usually copied to TempData structure,
+ //! when switching out of this view (then it's cleared).
+ KexiDB::QuerySchema *parsedQuery;
+ //! For internal use, statement passed in switching to this view
+ QString origStatement;
+ //! needed to remember height for both modes, between switching
+ int heightForStatusMode, heightForHistoryMode;
+ //! helper for slotUpdateMode()
+ bool action_toggle_history_was_checked : 1;
+ //! helper for eventFilter()
+ bool eventFilterForSplitterEnabled : 1;
+ //! helper for beforeSwitchTo()
+ bool justSwitchedFromNoViewMode : 1;
+ //! helper for slotTextChanged()
+ bool slotTextChangedEnabled : 1;
+};
+
+//===================
+
+KexiQueryDesignerSQLView::KexiQueryDesignerSQLView(KexiMainWindow *mainWin, QWidget *parent, const char *name)
+ : KexiViewBase(mainWin, parent, name)
+ , d( new Private() )
+{
+ d->splitter = new QSplitter(this);
+ d->splitter->setOrientation(Vertical);
+ d->head = new KexiSectionHeader(i18n("SQL Query Text"), Vertical, d->splitter);
+ d->editor = new KexiQueryDesignerSQLEditor(mainWin, d->head, "sqle");
+// d->editor->installEventFilter(this);//for keys
+ connect(d->editor, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
+ addChildView(d->editor);
+ setViewWidget(d->editor);
+ d->splitter->setFocusProxy(d->editor);
+ setFocusProxy(d->editor);
+
+ d->history_section = new QVBox(d->splitter);
+
+ d->status_hbox = new QHBox(d->history_section);
+ d->status_hbox->installEventFilter(this);
+ d->splitter->setResizeMode(d->history_section, QSplitter::KeepSize);
+ d->status_hbox->setSpacing(0);
+ d->pixmapStatus = new QLabel(d->status_hbox);
+ d->pixmapStatus->setFixedWidth(d->statusPixmapOk.width()*3/2);
+ d->pixmapStatus->setAlignment(AlignHCenter | AlignTop);
+ d->pixmapStatus->setMargin(d->statusPixmapOk.width()/4);
+ d->pixmapStatus->setPaletteBackgroundColor( palette().active().color(QColorGroup::Base) );
+
+ d->lblStatus = new QLabel(d->status_hbox);
+ d->lblStatus->setAlignment(AlignLeft | AlignTop | WordBreak);
+ d->lblStatus->setMargin(d->statusPixmapOk.width()/4);
+ d->lblStatus->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
+ d->lblStatus->resize(d->lblStatus->width(),d->statusPixmapOk.width()*3);
+ d->lblStatus->setPaletteBackgroundColor( palette().active().color(QColorGroup::Base) );
+
+ QHBoxLayout *b = new QHBoxLayout(this);
+ b->addWidget(d->splitter);
+
+ plugSharedAction("querypart_check_query", this, SLOT(slotCheckQuery()));
+ plugSharedAction("querypart_view_toggle_history", this, SLOT(slotUpdateMode()));
+ d->action_toggle_history = static_cast<KToggleAction*>( sharedAction( "querypart_view_toggle_history" ) );
+
+ d->historyHead = new KexiSectionHeader(i18n("SQL Query History"), Vertical, d->history_section);
+ d->historyHead->installEventFilter(this);
+ d->history = new KexiQueryDesignerSQLHistory(d->historyHead, "sql_history");
+
+ static const QString msg_back = i18n("Back to Selected Query");
+ static const QString msg_clear = i18n("Clear History");
+ d->historyHead->addButton("select_item", msg_back, this, SLOT(slotSelectQuery()));
+ d->historyHead->addButton("editclear", msg_clear, d->history, SLOT(clear()));
+ d->history->popupMenu()->insertItem(SmallIcon("select_item"), msg_back, this, SLOT(slotSelectQuery()));
+ d->history->popupMenu()->insertItem(SmallIcon("editclear"), msg_clear, d->history, SLOT(clear()));
+ connect(d->history, SIGNAL(currentItemDoubleClicked()), this, SLOT(slotSelectQuery()));
+
+ d->heightForHistoryMode = -1; //height() / 2;
+ //d->historyHead->hide();
+ d->action_toggle_history_was_checked = !d->action_toggle_history->isChecked(); //to force update
+ slotUpdateMode();
+ slotCheckQuery();
+}
+
+KexiQueryDesignerSQLView::~KexiQueryDesignerSQLView()
+{
+ delete d;
+}
+
+KexiQueryDesignerSQLEditor *KexiQueryDesignerSQLView::editor() const
+{
+ return d->editor;
+}
+
+void KexiQueryDesignerSQLView::setStatusOk()
+{
+ d->pixmapStatus->setPixmap(d->statusPixmapOk);
+ setStatusText("<h2>"+i18n("The query is correct")+"</h2>");
+ d->history->addEvent(d->editor->text().stripWhiteSpace(), true, QString::null);
+}
+
+void KexiQueryDesignerSQLView::setStatusError(const QString& msg)
+{
+ d->pixmapStatus->setPixmap(d->statusPixmapErr);
+ setStatusText("<h2>"+i18n("The query is incorrect")+"</h2><p>"+msg+"</p>");
+ d->history->addEvent(d->editor->text().stripWhiteSpace(), false, msg);
+}
+
+void KexiQueryDesignerSQLView::setStatusEmpty()
+{
+ d->pixmapStatus->setPixmap(d->statusPixmapInfo);
+ setStatusText(i18n("Please enter your query and execute \"Check query\" function to verify it."));
+}
+
+void KexiQueryDesignerSQLView::setStatusText(const QString& text)
+{
+ if (!d->action_toggle_history->isChecked()) {
+ QSimpleRichText rt(text, d->lblStatus->font());
+ rt.setWidth(d->lblStatus->width());
+ QValueList<int> sz = d->splitter->sizes();
+ const int newHeight = rt.height()+d->lblStatus->margin()*2;
+ if (sz[1]<newHeight) {
+ sz[1] = newHeight;
+ d->splitter->setSizes(sz);
+ }
+ d->lblStatus->setText(text);
+ }
+}
+
+tristate
+KexiQueryDesignerSQLView::beforeSwitchTo(int mode, bool &dontStore)
+{
+//TODO
+ dontStore = true;
+ if (mode==Kexi::DesignViewMode || mode==Kexi::DataViewMode) {
+ QString sqlText = d->editor->text().stripWhiteSpace();
+ KexiQueryPart::TempData * temp = tempData();
+ if (sqlText.isEmpty()) {
+ //special case: empty SQL text
+ if (temp->query()) {
+ temp->queryChangedInPreviousView = true; //query changed
+ temp->setQuery(0);
+// delete temp->query; //safe?
+// temp->query = 0;
+ }
+ }
+ else {
+ const bool designViewWasVisible = parentDialog()->viewForMode(mode)!=0;
+ //should we check SQL text?
+ if (designViewWasVisible
+ && !d->justSwitchedFromNoViewMode //unchanged, but we should check SQL text
+ && compareSQL(d->origStatement, d->editor->text())) {
+ //statement unchanged! - nothing to do
+ temp->queryChangedInPreviousView = false;
+ }
+ else {
+ //yes: parse SQL text
+ if (!slotCheckQuery()) {
+ if (KMessageBox::No==KMessageBox::warningYesNo(this, "<p>"+i18n("The query you entered is incorrect.")
+ +"</p><p>"+i18n("Do you want to cancel any changes made to this SQL text?")+"</p>"
+ +"</p><p>"+i18n("Answering \"No\" allows you to make corrections.")+"</p>"))
+ {
+ return cancelled;
+ }
+ //do not change original query - it's invalid
+ temp->queryChangedInPreviousView = false;
+ //this view is no longer _just_ switched from "NoViewMode"
+ d->justSwitchedFromNoViewMode = false;
+ return true;
+ }
+ //this view is no longer _just_ switched from "NoViewMode"
+ d->justSwitchedFromNoViewMode = false;
+ //replace old query schema with new one
+ temp->setQuery( d->parsedQuery ); //this will also delete temp->query()
+// delete temp->query; //safe?
+// temp->query = d->parsedQuery;
+ d->parsedQuery = 0;
+ temp->queryChangedInPreviousView = true;
+ }
+ }
+ }
+
+ //TODO
+ /*
+ if (d->doc) {
+ KexiDB::Parser *parser = new KexiDB::Parser(mainWin()->project()->dbConnection());
+ parser->parse(getQuery());
+ d->doc->setSchema(parser->select());
+
+ if(parser->operation() == KexiDB::Parser::OP_Error)
+ {
+ d->history->addEvent(getQuery(), false, parser->error().error());
+ kdDebug() << "KexiQueryDesignerSQLView::beforeSwitchTo(): syntax error!" << endl;
+ return false;
+ }
+ delete parser;
+ }
+
+ setDirty(true);*/
+// if (parentDialog()->hasFocus())
+ d->editor->setFocus();
+ return true;
+}
+
+tristate
+KexiQueryDesignerSQLView::afterSwitchFrom(int mode)
+{
+ kdDebug() << "KexiQueryDesignerSQLView::afterSwitchFrom()" << endl;
+// if (mode==Kexi::DesignViewMode || mode==Kexi::DataViewMode) {
+ if (mode==Kexi::NoViewMode) {
+ //User opened text view _directly_.
+ //This flag is set to indicate for beforeSwitchTo() that even if text has not been changed,
+ //SQL text should be invalidated.
+ d->justSwitchedFromNoViewMode = true;
+ }
+ KexiQueryPart::TempData * temp = tempData();
+ KexiDB::QuerySchema *query = temp->query();
+ if (!query) {//try to just get saved schema, instead of temporary one
+ query = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
+ }
+
+ if (mode!=0/*failure only if it is switching from prev. view*/ && !query) {
+ //TODO msg
+ return false;
+ }
+
+ if (!query) {
+ //no valid query schema delivered: just load sql text, no matter if it's valid
+ if (!loadDataBlock( d->origStatement, "sql", true /*canBeEmpty*/ ))
+ return false;
+ }
+ else {
+ // Use query with Kexi keywords (but not driver-specific keywords) escaped.
+ temp->setQuery( query );
+// temp->query = query;
+ KexiDB::Connection* conn = mainWin()->project()->dbConnection();
+ KexiDB::Connection::SelectStatementOptions options;
+ options.identifierEscaping = KexiDB::Driver::EscapeKexi;
+ options.addVisibleLookupColumns = false;
+ d->origStatement = conn->selectStatement(*query, options).stripWhiteSpace();
+ }
+
+ d->slotTextChangedEnabled = false;
+ d->editor->setText( d->origStatement );
+ d->slotTextChangedEnabled = true;
+ QTimer::singleShot(100, d->editor, SLOT(setFocus()));
+ return true;
+}
+
+QString
+KexiQueryDesignerSQLView::sqlText() const
+{
+ return d->editor->text();
+}
+
+bool KexiQueryDesignerSQLView::slotCheckQuery()
+{
+ QString sqlText( d->editor->text().stripWhiteSpace() );
+ if (sqlText.isEmpty()) {
+ delete d->parsedQuery;
+ d->parsedQuery = 0;
+ setStatusEmpty();
+ return true;
+ }
+
+ kdDebug() << "KexiQueryDesignerSQLView::slotCheckQuery()" << endl;
+ //KexiQueryPart::TempData * temp = tempData();
+ KexiDB::Parser *parser = mainWin()->project()->sqlParser();
+ const bool ok = parser->parse( sqlText );
+ delete d->parsedQuery;
+ d->parsedQuery = parser->query();
+ if (!d->parsedQuery || !ok || !parser->error().type().isEmpty()) {
+ KexiDB::ParserError err = parser->error();
+ setStatusError(err.error());
+ d->editor->jump(err.at());
+ delete d->parsedQuery;
+ d->parsedQuery = 0;
+ return false;
+ }
+
+ setStatusOk();
+ return true;
+}
+
+void KexiQueryDesignerSQLView::slotUpdateMode()
+{
+ if (d->action_toggle_history->isChecked() == d->action_toggle_history_was_checked)
+ return;
+
+ d->eventFilterForSplitterEnabled = false;
+
+ QValueList<int> sz = d->splitter->sizes();
+ d->action_toggle_history_was_checked = d->action_toggle_history->isChecked();
+ int heightToSet = -1;
+ if (d->action_toggle_history->isChecked()) {
+ d->status_hbox->hide();
+ d->historyHead->show();
+ d->history->show();
+ if (d->heightForHistoryMode==-1)
+ d->heightForHistoryMode = m_dialog->height() / 2;
+ heightToSet = d->heightForHistoryMode;
+ d->heightForStatusMode = sz[1]; //remember
+ }
+ else {
+ if (d->historyHead)
+ d->historyHead->hide();
+ d->status_hbox->show();
+ if (d->heightForStatusMode>=0) {
+ heightToSet = d->heightForStatusMode;
+ } else {
+ d->heightForStatusMode = d->status_hbox->height();
+ }
+ if (d->heightForHistoryMode>=0)
+ d->heightForHistoryMode = sz[1];
+ }
+
+ if (heightToSet>=0) {
+ sz[1] = heightToSet;
+ d->splitter->setSizes(sz);
+ }
+ d->eventFilterForSplitterEnabled = true;
+ slotCheckQuery();
+}
+
+void KexiQueryDesignerSQLView::slotTextChanged()
+{
+ if (!d->slotTextChangedEnabled)
+ return;
+ setDirty(true);
+ setStatusEmpty();
+}
+
+bool KexiQueryDesignerSQLView::eventFilter( QObject *o, QEvent *e )
+{
+ if (d->eventFilterForSplitterEnabled) {
+ if (e->type()==QEvent::Resize && o && o==d->historyHead && d->historyHead->isVisible()) {
+ d->heightForHistoryMode = d->historyHead->height();
+ }
+ else if (e->type()==QEvent::Resize && o && o==d->status_hbox && d->status_hbox->isVisible()) {
+ d->heightForStatusMode = d->status_hbox->height();
+ }
+ }
+ return KexiViewBase::eventFilter(o, e);
+}
+
+void KexiQueryDesignerSQLView::updateActions(bool activated)
+{
+ if (activated) {
+ slotUpdateMode();
+ }
+ setAvailable("querypart_check_query", true);
+ setAvailable("querypart_view_toggle_history", true);
+ KexiViewBase::updateActions(activated);
+}
+
+void KexiQueryDesignerSQLView::slotSelectQuery()
+{
+ QString sql = d->history->selectedStatement();
+ if (!sql.isEmpty()) {
+ d->editor->setText( sql );
+ }
+}
+
+KexiQueryPart::TempData *
+KexiQueryDesignerSQLView::tempData() const
+{
+ return dynamic_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
+}
+
+KexiDB::SchemaData*
+KexiQueryDesignerSQLView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ Q_UNUSED( cancel );
+
+ //here: we won't store query layout: it will be recreated 'by hand' in GUI Query Editor
+ bool queryOK = slotCheckQuery();
+ bool ok = true;
+ KexiDB::SchemaData* query = 0;
+ if (queryOK) {
+ //query is ok
+ if (d->parsedQuery) {
+ query = d->parsedQuery; //will be returned, so: don't keep it
+ d->parsedQuery = 0;
+ }
+ else {//empty query
+ query = new KexiDB::SchemaData(); //just empty
+ }
+
+ (KexiDB::SchemaData&)*query = sdata; //copy main attributes
+ ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *query, true /*newObject*/ );
+ if (ok) {
+ m_dialog->setId( query->id() );
+ ok = storeDataBlock( d->editor->text(), "sql" );
+ }
+ }
+ else {
+ //query is not ok
+//#if 0
+ //TODO: allow saving invalid queries
+ //TODO: just ask this question:
+ query = new KexiDB::SchemaData(); //just empty
+
+ ok = (KMessageBox::questionYesNo(this, i18n("Do you want to save invalid query?"),
+ 0, KStdGuiItem::yes(), KStdGuiItem::no(), "askBeforeSavingInvalidQueries"/*config entry*/)==KMessageBox::Yes);
+ if (ok) {
+ (KexiDB::SchemaData&)*query = sdata; //copy main attributes
+ ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *query, true /*newObject*/ );
+ }
+ if (ok) {
+ m_dialog->setId( query->id() );
+ ok = storeDataBlock( d->editor->text(), "sql" );
+ }
+//#else
+ //ok = false;
+//#endif
+ }
+ if (!ok) {
+ delete query;
+ query = 0;
+ }
+ return query;
+}
+
+tristate KexiQueryDesignerSQLView::storeData(bool dontAsk)
+{
+ tristate res = KexiViewBase::storeData(dontAsk);
+ if (~res)
+ return res;
+ if (res == true) {
+ res = storeDataBlock( d->editor->text(), "sql" );
+#if 0
+ bool queryOK = slotCheckQuery();
+ if (queryOK) {
+ res = storeDataBlock( d->editor->text(), "sql" );
+ }
+ else {
+ //query is not ok
+ //TODO: allow saving invalid queries
+ //TODO: just ask this question:
+ res = false;
+ }
+#endif
+ }
+ if (res == true) {
+ QString empty_xml;
+ res = storeDataBlock( empty_xml, "query_layout" ); //clear
+ }
+ if (!res)
+ setDirty(true);
+ return res;
+}
+
+
+/*void KexiQueryDesignerSQLView::slotHistoryHeaderButtonClicked(const QString& buttonIdentifier)
+{
+ if (buttonIdentifier=="select_query") {
+ slotSelectQuery();
+ }
+ else if (buttonIdentifier=="clear_history") {
+ d->history->clear();
+ }
+}*/
+
+#include "kexiquerydesignersql.moc"
+
diff --git a/kexi/plugins/queries/kexiquerydesignersql.h b/kexi/plugins/queries/kexiquerydesignersql.h
new file mode 100644
index 00000000..f31c838f
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignersql.h
@@ -0,0 +1,82 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYDESIGNERSQL_H
+#define KEXIQUERYDESIGNERSQL_H
+
+#include <kexiviewbase.h>
+#include "kexiquerypart.h"
+
+class KexiQueryDesignerSQLEditor;
+class KexiQueryDesignerSQLViewPrivate;
+
+//! The KexiQueryDesignerSQLView class for editing Queries in text mode.
+/*! It is a view containing SQL text editor
+ and SQL history/status widget splitted vertically.
+ Depending on user's will, the widget can be in "sql history"
+ mode or in "sql status" mode. */
+class KexiQueryDesignerSQLView : public KexiViewBase
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryDesignerSQLView(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0);
+ virtual ~KexiQueryDesignerSQLView();
+
+ QString sqlText() const;
+ KexiQueryDesignerSQLEditor *editor() const;
+
+ virtual bool eventFilter ( QObject *o, QEvent *e );
+
+ protected:
+ KexiQueryPart::TempData * tempData() const;
+
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+ virtual tristate afterSwitchFrom(int mode);
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+ virtual tristate storeData(bool dontAsk = false);
+
+ void setStatusOk();
+ void setStatusError(const QString& msg);
+ void setStatusEmpty();
+ void setStatusText(const QString& text);
+
+ virtual void updateActions(bool activated);
+
+ protected slots:
+ /*! Performs query checking (by text parsing). \return true and sets d->parsedQuery
+ to the new query schema object on success. */
+ bool slotCheckQuery();
+ void slotUpdateMode();
+ void slotTextChanged();
+// void slotHistoryHeaderButtonClicked(const QString& buttonIdentifier);
+ void slotSelectQuery();
+
+ signals:
+ void queryShortcut();
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class KexiQueryView; // for storeNewData() and storeData() only
+};
+
+#endif
diff --git a/kexi/plugins/queries/kexiquerydesignersqlhistory.cpp b/kexi/plugins/queries/kexiquerydesignersqlhistory.cpp
new file mode 100644
index 00000000..d86caf83
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignersqlhistory.cpp
@@ -0,0 +1,373 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qpainter.h>
+#include <qclipboard.h>
+#include <qregexp.h>
+
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+#include <kapplication.h>
+
+#include "kexiquerydesignersqlhistory.h"
+
+KexiQueryDesignerSQLHistory::KexiQueryDesignerSQLHistory(QWidget *parent, const char *name)
+ : QScrollView(parent, name)
+{
+ viewport()->setPaletteBackgroundColor(white);
+
+ m_selected = 0;
+ m_history = new History();
+ m_history->setAutoDelete(true);
+
+ m_popup = new KPopupMenu(this);
+ m_popup->insertItem(SmallIcon("editcopy"), i18n("Copy to Clipboard"), this, SLOT(slotToClipboard()));
+}
+
+KexiQueryDesignerSQLHistory::~KexiQueryDesignerSQLHistory()
+{
+}
+
+void
+KexiQueryDesignerSQLHistory::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ QRect clipping(cx, cy, cw, ch);
+
+ int y = 0;
+ for(HistoryEntry *it = m_history->first(); it; it = m_history->next())
+ {
+// it->drawItem(p, visibleWidth());
+ if(clipping.intersects(it->geometry(y, visibleWidth(), fontMetrics())))
+ {
+ p->saveWorldMatrix();
+ p->translate(0, y);
+ it->drawItem(p, visibleWidth(), colorGroup());
+ p->restoreWorldMatrix();
+ }
+ y += it->geometry(y, visibleWidth(), fontMetrics()).height() + 5;
+ }
+}
+
+void
+KexiQueryDesignerSQLHistory::contentsMousePressEvent(QMouseEvent * e)
+{
+ int y = 0;
+ HistoryEntry *popupHistory = 0;
+ int pos;
+ for(QPtrListIterator<HistoryEntry> it(*m_history); it.current(); ++it)
+ {
+ if(it.current()->isSelected())
+ {
+ //clear
+ it.current()->setSelected(false, colorGroup());
+ updateContents(it.current()->geometry(y, visibleWidth(), fontMetrics()));
+ }
+
+ if(it.current()->geometry(y, visibleWidth(), fontMetrics()).contains(e->pos()))
+ {
+ popupHistory = it.current();
+ pos = y;
+ }
+ y += it.current()->geometry(y, visibleWidth(), fontMetrics()).height() + 5;
+ }
+
+ //now do update
+ if (popupHistory) {
+ if (m_selected && m_selected != popupHistory) {
+ m_selected->setSelected(false, colorGroup());
+ updateContents(m_selected->geometry(pos, visibleWidth(), fontMetrics()));
+ }
+ m_selected = popupHistory;
+ m_selected->setSelected(true, colorGroup());
+ updateContents(m_selected->geometry(pos, visibleWidth(), fontMetrics()));
+ if(e->button() == RightButton) {
+ m_popup->exec(e->globalPos());
+ }
+ }
+}
+
+void
+KexiQueryDesignerSQLHistory::contentsMouseDoubleClickEvent(QMouseEvent * e)
+{
+ contentsMousePressEvent(e);
+ if (m_selected)
+ emit currentItemDoubleClicked();
+}
+
+void
+KexiQueryDesignerSQLHistory::addEvent(const QString& q, bool s, const QString &error)
+{
+ HistoryEntry *he=m_history->last();
+ if (he) {
+ if (he->statement()==q) {
+ he->updateTime(QTime::currentTime());
+ repaint();
+ return;
+ }
+ }
+ addEntry(new HistoryEntry(s, QTime::currentTime(), q, error));
+}
+
+void
+KexiQueryDesignerSQLHistory::addEntry(HistoryEntry *e)
+{
+ m_history->append(e);
+// m_history->prepend(e);
+
+ int y = 0;
+ for(HistoryEntry *it = m_history->first(); it; it = m_history->next())
+ {
+ y += it->geometry(y, visibleWidth(), fontMetrics()).height() + 5;
+ }
+
+ resizeContents(visibleWidth() - 1, y);
+ if (m_selected) {
+ m_selected->setSelected(false, colorGroup());
+ }
+ m_selected = e;
+ m_selected->setSelected(true, colorGroup());
+ ensureVisible(0,y+5);
+ updateContents();
+/* ensureVisible(0, 0);
+ if (m_selected) {
+ m_selected->setSelected(false, colorGroup());
+ }
+ m_selected = e;
+ m_selected->setSelected(true, colorGroup());
+// updateContents();
+ updateContents(m_selected->geometry(0, visibleWidth(), fontMetrics()));*/
+}
+
+/*void
+KexiQueryDesignerSQLHistory::contextMenu(const QPoint &pos, HistoryEntry *)
+{
+ KPopupMenu p(this);
+ p.insertItem(SmallIcon("editcopy"), i18n("Copy to Clipboard"), this, SLOT(slotToClipboard()));
+
+
+#ifndef KEXI_NO_UNFINISHED
+ p.insertSeparator();
+ p.insertItem(SmallIcon("edit"), i18n("Edit"), this, SLOT(slotEdit()));
+ p.insertItem(SmallIcon("reload"), i18n("Requery"));
+#endif
+
+ p.exec(pos);
+}*/
+
+void
+KexiQueryDesignerSQLHistory::slotToClipboard()
+{
+ if(!m_selected)
+ return;
+
+ QApplication::clipboard()->setText(m_selected->statement(), QClipboard::Clipboard);
+}
+
+void
+KexiQueryDesignerSQLHistory::slotEdit()
+{
+ emit editRequested(m_selected->statement());
+}
+
+QString
+KexiQueryDesignerSQLHistory::selectedStatement() const
+{
+ return m_selected ? m_selected->statement() : QString::null;
+}
+
+void
+KexiQueryDesignerSQLHistory::setHistory(History *h)
+{
+ m_history = h;
+ update();
+}
+
+void KexiQueryDesignerSQLHistory::clear()
+{
+ m_selected = 0;
+ m_history->clear();
+ updateContents();
+}
+
+KPopupMenu* KexiQueryDesignerSQLHistory::popupMenu() const
+{
+ return m_popup;
+}
+
+//==================================
+
+HistoryEntry::HistoryEntry(bool succeed, const QTime &execTime, const QString &statement, /*int ,*/ const QString &err)
+{
+ m_succeed = succeed;
+ m_execTime = execTime;
+ m_statement = statement;
+ m_error = err;
+ m_selected = false;
+ highlight(QColorGroup());
+}
+
+void
+HistoryEntry::drawItem(QPainter *p, int width, const QColorGroup &cg)
+{
+ p->setPen(QColor(200, 200, 200));
+ p->setBrush(QColor(200, 200, 200));
+ p->drawRect(2, 2, 200, 20);
+ p->setPen(QColor(0, 0, 0));
+
+ if(m_succeed)
+ p->drawPixmap(4, 4, SmallIcon("button_ok"));
+ else
+ p->drawPixmap(4, 4, SmallIcon("button_cancel"));
+
+ p->drawText(22, 2, 180, 20, Qt::AlignLeft | Qt::AlignVCenter, m_execTime.toString());
+ p->setPen(QColor(200, 200, 200));
+ p->setBrush(QColor(255, 255, 255));
+ m_formated->setWidth(width - 2);
+ QRect content(2, 21, width - 2, m_formated->height());
+// QRect content = p->fontMetrics().boundingRect(2, 21, width - 2, 0, Qt::WordBreak | Qt::AlignLeft | Qt::AlignVCenter, m_statement);
+// QRect content(2, 21, width - 2, p->fontMetrics().height() + 4);
+// content = QRect(2, 21, width - 2, m_for.height());
+
+ if(m_selected)
+ p->setBrush(cg.highlight());
+
+ p->drawRect(content);
+
+ if(!m_selected)
+ p->setPen(cg.text());
+ else
+ p->setPen(cg.highlightedText());
+
+ content.setX(content.x() + 2);
+ content.setWidth(content.width() - 2);
+// p->drawText(content, Qt::WordBreak | Qt::AlignLeft | Qt::AlignVCenter, m_statement);
+ m_formated->draw(p, content.x(), content.y(), content, cg);
+}
+
+void
+HistoryEntry::highlight(const QColorGroup &cg)
+{
+ QString statement;
+ QString text;
+ bool quote = false;
+ bool dblquote = false;
+
+ statement = m_statement;
+ statement.replace("<", "&lt;");
+ statement.replace(">", "&gt;");
+ statement.replace("\r\n", "<br>"); //(js) first win32 specific pair
+ statement.replace("\n", "<br>"); // now single \n
+ statement.replace(" ", "&nbsp;");
+ statement.replace("\t", "&nbsp;&nbsp;&nbsp;");
+
+ // getting quoting...
+ if(!m_selected)
+ {
+ for(int i=0; i < (int)statement.length(); i++)
+ {
+ QString beginTag;
+ QString endTag;
+ QChar curr = QChar(statement[i]);
+
+ if(curr == "'" && !dblquote && QChar(statement[i-1]) != "\\")
+ {
+ if(!quote)
+ {
+ quote = true;
+ beginTag += "<font color=\"#ff0000\">";
+ }
+ else
+ {
+ quote = false;
+ endTag += "</font>";
+ }
+ }
+ if(curr == "\"" && !quote && QChar(statement[i-1]) != "\\")
+ {
+ if(!dblquote)
+ {
+ dblquote = true;
+ beginTag += "<font color=\"#ff0000\">";
+ }
+ else
+ {
+ dblquote = false;
+ endTag += "</font>";
+ }
+ }
+ if(QRegExp("[0-9]").exactMatch(QString(curr)) && !quote && !dblquote)
+ {
+ beginTag += "<font color=\"#0000ff\">";
+ endTag += "</font>";
+ }
+
+ text += beginTag + curr + endTag;
+ }
+ }
+ else
+ {
+ text = QString("<font color=\"%1\">%2").arg(cg.highlightedText().name()).arg(statement);
+ }
+
+ QRegExp keywords("\\b(SELECT|UPDATE|INSERT|DELETE|DROP|FROM|WHERE|AND|OR|NOT|NULL|JOIN|LEFT|RIGHT|ON|INTO|TABLE)\\b");
+ keywords.setCaseSensitive(false);
+ text = text.replace(keywords, "<b>\\1</b>");
+
+ if(!m_error.isEmpty())
+// text += ("<br>"+i18n("Error: %1").arg(m_error));
+// text += QString("<br><font face=\"") + KGlobalSettings::generalFont().family() + QString("\" size=\"-1\">") + i18n("Error: %1").arg(m_error) + "</font>";
+ text += QString("<br><font face=\"") + KGlobalSettings::generalFont().family() + QString("\">") + i18n("Error: %1").arg(m_error) + "</font>";
+
+ kdDebug() << "HistoryEntry::highlight() text:" << text << endl;
+// m_formated = new QSimpleRichText(text, QFont("courier", 8));
+ m_formated = new QSimpleRichText(text, KGlobalSettings::fixedFont());
+
+}
+
+void
+HistoryEntry::setSelected(bool selected, const QColorGroup &cg)
+{
+ m_selected = selected;
+ highlight(cg);
+}
+
+QRect
+HistoryEntry::geometry(int y, int width, QFontMetrics f)
+{
+ Q_UNUSED( f );
+
+// int h = 21 + f.boundingRect(2, 21, width - 2, 0, Qt::WordBreak | Qt::AlignLeft | Qt::AlignVCenter, m_statement).height();
+// return QRect(0, y, width, h);
+ m_formated->setWidth(width - 2);
+ return QRect(0, y, width, m_formated->height() + 21);
+}
+
+void HistoryEntry::updateTime(const QTime &execTime) {
+ m_execTime=execTime;
+}
+
+HistoryEntry::~HistoryEntry()
+{
+}
+
+#include "kexiquerydesignersqlhistory.moc"
diff --git a/kexi/plugins/queries/kexiquerydesignersqlhistory.h b/kexi/plugins/queries/kexiquerydesignersqlhistory.h
new file mode 100644
index 00000000..a8d0c2e0
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignersqlhistory.h
@@ -0,0 +1,104 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYDESIGNERSQLHISTORY_H
+#define KEXIQUERYDESIGNERSQLHISTORY_H
+
+#include <qscrollview.h>
+#include <qdatetime.h>
+#include <qptrlist.h>
+#include <qmap.h>
+#include <qsimplerichtext.h>
+
+class QSimpleRichText;
+class KPopupMenu;
+
+class HistoryEntry
+{
+ public:
+ HistoryEntry(bool success, const QTime &time, const QString &statement, /*int y,*/ const QString &error = QString::null);
+ ~HistoryEntry();
+
+ QRect geometry(int y, int width, QFontMetrics f);
+ void drawItem(QPainter *p, int width, const QColorGroup &cg);
+
+ void setSelected(bool selected, const QColorGroup &cg);
+ bool isSelected() const { return m_selected; }
+ void highlight(const QColorGroup &selected);
+
+ QString statement() { return m_statement; }
+ void updateTime(const QTime &execTime);
+
+ private:
+ bool m_succeed;
+ QTime m_execTime;
+ QString m_statement;
+ QString m_error;
+ QSimpleRichText *m_formated;
+
+ int m_y;
+ bool m_selected;
+};
+
+typedef QPtrList<HistoryEntry> History;
+
+class KexiQueryDesignerSQLHistory : public QScrollView
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryDesignerSQLHistory(QWidget *parent, const char *name=0);
+ virtual ~KexiQueryDesignerSQLHistory();
+
+ KPopupMenu* popupMenu() const;
+
+// void contextMenu(const QPoint &pos, HistoryEntry *e);
+
+ void setHistory(History *h);
+
+ QString selectedStatement() const;
+
+ public slots:
+ void addEvent(const QString& q, bool s, const QString &error);
+
+ void slotToClipboard();
+ void slotEdit();
+
+ void clear();
+
+// HistoryItem itemAt(int y);
+
+ protected:
+ void addEntry(HistoryEntry *e);
+ virtual void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+ virtual void contentsMousePressEvent(QMouseEvent * e);
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent * e);
+
+ signals:
+ void editRequested(const QString &text);
+ void currentItemDoubleClicked();
+
+ private:
+ History *m_history;
+ HistoryEntry *m_selected;
+ KPopupMenu *m_popup;
+};
+
+#endif
diff --git a/kexi/plugins/queries/kexiqueryhandler.desktop b/kexi/plugins/queries/kexiqueryhandler.desktop
new file mode 100644
index 00000000..4a4f478e
--- /dev/null
+++ b/kexi/plugins/queries/kexiqueryhandler.desktop
@@ -0,0 +1,111 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Queries
+GenericName[bg]=Заявки
+GenericName[ca]=Consultes
+GenericName[cs]=Dotazy
+GenericName[cy]=Ymholiadau
+GenericName[da]=Forespørgsler
+GenericName[de]=Abfragen
+GenericName[el]=Ερωτήματα
+GenericName[eo]=Serĉmendoj
+GenericName[es]=Consultas
+GenericName[et]=Päringud
+GenericName[eu]=Kontsultak
+GenericName[fa]=پرس‌و‌جوها
+GenericName[fi]=Kyselyt
+GenericName[fr]=Requêtes
+GenericName[ga]=Iarratais
+GenericName[gl]=Pesquisas
+GenericName[he]=שאילתות
+GenericName[hi]=क्वैरीज़
+GenericName[hr]=Upiti
+GenericName[hu]=Lekérdezések
+GenericName[is]=Fyrirspurnir
+GenericName[it]=Interrogazioni
+GenericName[ja]=クエリ
+GenericName[km]=សំណួរ​
+GenericName[lt]=Užklausos
+GenericName[lv]=Vaicājumi
+GenericName[ms]=Pertanyaan
+GenericName[nb]=Spørringer
+GenericName[nds]=Affragen
+GenericName[ne]=क्वेरीहरू
+GenericName[nn]=Spørjingar
+GenericName[pl]=Zapytania
+GenericName[pt]=Pesquisas
+GenericName[pt_BR]=Consultas
+GenericName[ru]=Запросы
+GenericName[se]=Jearahusat
+GenericName[sk]=Otázky
+GenericName[sl]=Poizvedbe
+GenericName[sr]=Упити
+GenericName[sr@Latn]=Upiti
+GenericName[sv]=Förfrågningar
+GenericName[ta]=கேள்விகள்
+GenericName[tr]=Sorgular
+GenericName[uk]=Запити
+GenericName[uz]=Soʻrovlar
+GenericName[uz@cyrillic]=Сўровлар
+GenericName[zh_CN]=查询
+GenericName[zh_TW]=查詢
+Name=Queries
+Name[bg]=Заявки
+Name[ca]=Consultes
+Name[cs]=Dotazy
+Name[cy]=Ymholiadau
+Name[da]=Forespørgsler
+Name[de]=Abfragen
+Name[el]=Ερωτήματα
+Name[eo]=Serĉmendoj
+Name[es]=Consultas
+Name[et]=Päringud
+Name[eu]=Kontsultak
+Name[fa]=پرس‌و‌جوها
+Name[fi]=Kyselyt
+Name[fr]=Requêtes
+Name[ga]=Iarratais
+Name[gl]=Pesquisas
+Name[he]=שאילתות
+Name[hi]=क्वैरीज़
+Name[hr]=Upiti
+Name[hu]=Lekérdezések
+Name[is]=Fyrirspurnir
+Name[it]=Interrogazioni
+Name[ja]=クエリ
+Name[km]=សំណួរ​
+Name[lt]=Užklausos
+Name[lv]=Vaicājumi
+Name[ms]=Pertanyaan
+Name[nb]=Spørringer
+Name[nds]=Affragen
+Name[ne]=क्वेरीहरू
+Name[nn]=Spørjingar
+Name[pl]=Zapytania
+Name[pt]=Procuras
+Name[pt_BR]=Consultas
+Name[ru]=Запросы
+Name[se]=Jearahusat
+Name[sk]=Otázky
+Name[sl]=Poizvedbe
+Name[sr]=Упити
+Name[sr@Latn]=Upiti
+Name[sv]=Förfrågningar
+Name[ta]=கேள்விகள்
+Name[tg]=Талаботҳо
+Name[tr]=Sorgular
+Name[uk]=Запити
+Name[uz]=Talablar
+Name[uz@cyrillic]=Талаблар
+Name[zh_CN]=查询
+Name[zh_TW]=查詢
+X-KDE-Library=kexihandler_query
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=query
+X-Kexi-TypeMime=kexi/query
+X-Kexi-ItemIcon=query
+X-Kexi-SupportsDataExport=true
+X-Kexi-SupportsPrinting=true
diff --git a/kexi/plugins/queries/kexiquerypart.cpp b/kexi/plugins/queries/kexiquerypart.cpp
new file mode 100644
index 00000000..6cecfcf1
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerypart.cpp
@@ -0,0 +1,310 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004,2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiquerypart.h"
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include <keximainwindow.h>
+#include <kexidialogbase.h>
+#include <kexiproject.h>
+#include <kexipartinfo.h>
+
+#include <kexidb/cursor.h>
+#include <kexidb/parser/parser.h>
+
+#include "kexiqueryview.h"
+#include "kexiquerydesignerguieditor.h"
+#include "kexiquerydesignersql.h"
+
+//------------------------------------------------
+
+KexiQueryPart::KexiQueryPart(QObject *parent, const char *name, const QStringList &l)
+ : KexiPart::Part(parent, name, l)
+{
+ // REGISTERED ID:
+ m_registeredPartID = (int)KexiPart::QueryObjectType;
+
+ m_names["instanceName"]
+ = i18n("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). "
+ "Use '_' character instead of spaces. First character should be a..z character. "
+ "If you cannot use latin characters in your language, use english word.",
+ "query");
+ m_names["instanceCaption"] = i18n("Query");
+ m_supportedViewModes = Kexi::DataViewMode | Kexi::DesignViewMode | Kexi::TextViewMode;
+}
+
+KexiQueryPart::~KexiQueryPart()
+{
+}
+
+KexiDialogTempData*
+KexiQueryPart::createTempData(KexiDialogBase* dialog)
+{
+ KexiQueryPart::TempData *data = new KexiQueryPart::TempData(dialog, dialog->mainWin()->project()->dbConnection());
+ data->listenerInfoString = dialog->part()->instanceCaption() + " \"" + dialog->partItem()->name() + "\"";
+ return data;
+}
+
+KexiViewBase*
+KexiQueryPart::createView(QWidget *parent, KexiDialogBase* dialog, KexiPart::Item &item, int viewMode, QMap<QString,QString>*)
+{
+ Q_UNUSED( item );
+
+ kdDebug() << "KexiQueryPart::createView()" << endl;
+
+ if (viewMode == Kexi::DataViewMode) {
+ return new KexiQueryView(dialog->mainWin(), parent, "dataview");
+ }
+ else if (viewMode == Kexi::DesignViewMode) {
+ KexiQueryDesignerGuiEditor* view = new KexiQueryDesignerGuiEditor(
+ dialog->mainWin(), parent, "guieditor");
+ //needed for updating tables combo box:
+ KexiProject *prj = dialog->mainWin()->project();
+ connect(prj, SIGNAL(newItemStored(KexiPart::Item&)),
+ view, SLOT(slotNewItemStored(KexiPart::Item&)));
+ connect(prj, SIGNAL(itemRemoved(const KexiPart::Item&)),
+ view, SLOT(slotItemRemoved(const KexiPart::Item&)));
+ connect(prj, SIGNAL(itemRenamed(const KexiPart::Item&, const QCString&)),
+ view, SLOT(slotItemRenamed(const KexiPart::Item&, const QCString&)));
+
+// connect(dialog->mainWin()->project(), SIGNAL(tableCreated(KexiDB::TableSchema&)),
+// view, SLOT(slotTableCreated(KexiDB::TableSchema&)));
+ return view;
+ }
+ else if (viewMode == Kexi::TextViewMode) {
+ return new KexiQueryDesignerSQLView(dialog->mainWin(), parent, "sqldesigner");
+ }
+
+ return 0;
+}
+
+bool
+KexiQueryPart::remove(KexiMainWindow *win, KexiPart::Item &item)
+{
+ if (!win || !win->project() || !win->project()->dbConnection())
+ return false;
+ KexiDB::Connection *conn = win->project()->dbConnection();
+ KexiDB::QuerySchema *sch = conn->querySchema(item.identifier());
+ if (sch)
+ return conn->dropQuery( sch );
+ //last chance: just remove item
+ return conn->removeObject( item.identifier() );
+}
+
+#if 0
+KexiPart::DataSource *
+KexiQueryPart::dataSource()
+{
+ return new KexiQueryDataSource(this);
+}
+
+void KexiQueryPart::initPartActions( KActionCollection *col )
+{
+}
+
+void KexiQueryPart::initInstanceActions( int mode, KActionCollection *col )
+{
+ if (mode==Kexi::DataViewMode) {
+ }
+ else if (mode==Kexi::DesignViewMode) {
+ }
+ else if (mode==Kexi::TextViewMode) {
+// new KAction(i18n("Check Query"), "test_it", 0, this, SLOT(slotCheckQuery()), col, "querypart_check_query");
+
+//TODO new KAction(i18n("Execute Query"), "?????", 0, this, SLOT(checkQuery()), col, "querypart_execute_query");
+ }
+}
+#endif
+
+void KexiQueryPart::initPartActions()
+{
+}
+
+void KexiQueryPart::initInstanceActions()
+{
+// new KAction(i18n("Check Query"), "test_it", 0, this, SLOT(slotCheckQuery()),
+// m_instanceGuiClients[Kexi::DesignViewMode]->actionCollection(), "querypart_check_query");
+
+ KAction *a = createSharedAction(Kexi::TextViewMode, i18n("Check Query"), "test_it",
+ Key_F9, "querypart_check_query");
+ a->setToolTip(i18n("Check Query"));
+ a->setWhatsThis(i18n("Checks query for validity."));
+
+ a = createSharedToggleAction(
+ Kexi::TextViewMode, i18n("Show SQL History"), "view_top_bottom"/*TODO other icon*/,
+ 0, "querypart_view_toggle_history");
+ a->setWhatsThis(i18n("Shows or hides SQL editor's history."));
+
+// setActionAvailable("querypart_check_query", true);
+}
+
+KexiDB::SchemaData*
+KexiQueryPart::loadSchemaData(KexiDialogBase *dlg, const KexiDB::SchemaData& sdata, int viewMode)
+{
+ KexiQueryPart::TempData * temp = static_cast<KexiQueryPart::TempData*>(dlg->tempData());
+ QString sqlText;
+ if (!loadDataBlock( dlg, sqlText, "sql" )) {
+ return 0;
+ }
+ KexiDB::Parser *parser = dlg->mainWin()->project()->sqlParser();
+ parser->parse( sqlText );
+ KexiDB::QuerySchema *query = parser->query();
+ //error?
+ if (!query) {
+ if (viewMode==Kexi::TextViewMode) {
+ //for SQL view, no parsing is initially needed:
+ //-just make a copy:
+ return KexiPart::Part::loadSchemaData(dlg, sdata, viewMode);
+ }
+ /* Set this to true on data loading loadSchemaData() to indicate that TextView mode
+ could be used instead of DataView or DesignView, because there are problems
+ with opening object. */
+ temp->proposeOpeningInTextViewModeBecauseOfProblems = true;
+ //todo
+ return 0;
+ }
+ query->debug();
+ (KexiDB::SchemaData&)*query = sdata; //copy main attributes
+
+ temp->registerTableSchemaChanges(query);
+
+ query->debug();
+ return query;
+}
+
+QString KexiQueryPart::i18nMessage(const QCString& englishMessage, KexiDialogBase* dlg) const
+{
+ Q_UNUSED(dlg);
+ if (englishMessage=="Design of object \"%1\" has been modified.")
+ return i18n("Design of query \"%1\" has been modified.");
+ if (englishMessage=="Object \"%1\" already exists.")
+ return i18n("Query \"%1\" already exists.");
+
+ return englishMessage;
+}
+
+tristate KexiQueryPart::rename(KexiMainWindow *win, KexiPart::Item &item, const QString& newName)
+{
+ Q_UNUSED(newName);
+ if (!win->project()->dbConnection())
+ return false;
+ win->project()->dbConnection()->setQuerySchemaObsolete( item.name() );
+ return true;
+}
+
+//----------------
+
+KexiQueryPart::TempData::TempData(KexiDialogBase* parent, KexiDB::Connection *conn)
+ : KexiDialogTempData(parent)
+ , KexiDB::Connection::TableSchemaChangeListenerInterface()
+ , queryChangedInPreviousView(false)
+ , m_query(0)
+{
+ this->conn = conn;
+}
+
+KexiQueryPart::TempData::~TempData()
+{
+ conn->unregisterForTablesSchemaChanges(*this);
+}
+
+void KexiQueryPart::TempData::clearQuery()
+{
+ if (!m_query)
+ return;
+ unregisterForTablesSchemaChanges();
+ m_query->clear();
+}
+
+void KexiQueryPart::TempData::unregisterForTablesSchemaChanges()
+{
+ conn->unregisterForTablesSchemaChanges(*this);
+}
+
+void KexiQueryPart::TempData::registerTableSchemaChanges(KexiDB::QuerySchema *q)
+{
+ if (!q)
+ return;
+ for (KexiDB::TableSchema::ListIterator it(*q->tables());
+ it.current(); ++it)
+ {
+ conn->registerForTableSchemaChanges(*this, *it.current());
+ }
+}
+
+tristate KexiQueryPart::TempData::closeListener()
+{
+ KexiDialogBase* dlg = static_cast<KexiDialogBase*>(parent());
+ return dlg->mainWin()->closeDialog(dlg);
+}
+
+KexiDB::QuerySchema *KexiQueryPart::TempData::takeQuery()
+{
+ KexiDB::QuerySchema *query = m_query;
+ m_query = 0;
+ return query;
+}
+
+void KexiQueryPart::TempData::setQuery(KexiDB::QuerySchema *query)
+{
+ if (m_query && m_query == query)
+ return;
+ if (m_query
+ /* query not owned by dialog */
+ && (static_cast<KexiDialogBase*>(parent())->schemaData() != static_cast<KexiDB::SchemaData*>( m_query )))
+ {
+ delete m_query;
+ }
+ m_query = query;
+}
+
+//----------------
+
+#if 0
+KexiQueryDataSource::KexiQueryDataSource(KexiPart::Part *part)
+ : KexiPart::DataSource(part)
+{
+}
+
+KexiQueryDataSource::~KexiQueryDataSource()
+{
+}
+
+KexiDB::FieldList *
+KexiQueryDataSource::fields(KexiProject *, const KexiPart::Item &)
+{
+ return 0;
+}
+
+KexiDB::Cursor *
+KexiQueryDataSource::cursor(KexiProject *, const KexiPart::Item &, bool)
+{
+ return 0;
+}
+#endif
+
+//----------------
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_query, KGenericFactory<KexiQueryPart>("kexihandler_query") )
+
+#include "kexiquerypart.moc"
+
diff --git a/kexi/plugins/queries/kexiquerypart.h b/kexi/plugins/queries/kexiquerypart.h
new file mode 100644
index 00000000..6b16f28d
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerypart.h
@@ -0,0 +1,118 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004,2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYPART_H
+#define KEXIQUERYPART_H
+
+#include <qmap.h>
+
+#include <kexidialogbase.h>
+#include <kexipart.h>
+#include <kexipartitem.h>
+//#include <kexipartdatasource.h>
+
+#include <kexidb/queryschema.h>
+#include <kexidb/connection.h>
+
+class KexiMainWin;
+namespace KexiDB
+{
+ class QuerySchema;
+ class Connection;
+}
+
+class KexiProject;
+
+//! @short Kexi Query Designer Plugin.
+class KexiQueryPart : public KexiPart::Part
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryPart(QObject *parent, const char *name, const QStringList &);
+ virtual ~KexiQueryPart();
+
+ virtual bool remove(KexiMainWindow *win, KexiPart::Item &item);
+
+ //! @short Temporary data kept in memory while switching between Query Dialog's views
+ class TempData : public KexiDialogTempData,
+ public KexiDB::Connection::TableSchemaChangeListenerInterface
+ {
+ public:
+ TempData(KexiDialogBase* parent, KexiDB::Connection *conn);
+ virtual ~TempData();
+ virtual tristate closeListener();
+ void clearQuery();
+ void unregisterForTablesSchemaChanges();
+ void registerTableSchemaChanges(KexiDB::QuerySchema *q);
+
+ /*! Assigns query \a query for this data.
+ Existing query (available using query()) is deleted but only
+ if it is not owned by parent dialog (i.e. != KexiDialogBase::schemaData()).
+ \a query can be 0.
+ If \a query is equal to existing query, nothing is performed.
+ */
+ void setQuery(KexiDB::QuerySchema *query);
+
+ //! \return query associated with this data
+ KexiDB::QuerySchema *query() const { return m_query; }
+
+ //! Takes query associated with this data (without deleting) and returns it.
+ //! After this call query() == 0
+ KexiDB::QuerySchema *takeQuery();
+
+ //! Connection used for retrieving definition of the query
+ KexiDB::Connection *conn;
+
+ /*! true, if \a query member has changed in previous view.
+ Used on view switching. We're checking this flag to see if we should
+ rebuild internal structure for DesignViewMode of regenerated sql text
+ in TextViewMode after switch from other view. */
+ bool queryChangedInPreviousView : 1;
+
+ protected:
+ KexiDB::QuerySchema *m_query;
+ };
+
+ virtual QString i18nMessage(const QCString& englishMessage,
+ KexiDialogBase* dlg) const;
+
+ /*! Renames stored data pointed by \a item to \a newName.
+ Reimplemented to mark the query obsolete by using KexiDB::Connection::setQuerySchemaObsolete(). */
+ virtual tristate rename(KexiMainWindow * win, KexiPart::Item & item, const QString& newName);
+
+ protected:
+ virtual KexiDialogTempData* createTempData(KexiDialogBase* dialog);
+
+ virtual KexiViewBase* createView(QWidget *parent, KexiDialogBase* dialog,
+ KexiPart::Item &item, int viewMode = Kexi::DataViewMode, QMap<QString,QString>* staticObjectArgs = 0);
+
+// virtual void initPartActions( KActionCollection *col );
+// virtual void initInstanceActions( int mode, KActionCollection *col );
+
+ virtual void initPartActions();
+ virtual void initInstanceActions();
+
+ virtual KexiDB::SchemaData* loadSchemaData(KexiDialogBase *dlg,
+ const KexiDB::SchemaData& sdata, int viewMode);
+};
+
+#endif
+
diff --git a/kexi/plugins/queries/kexiquerypartinstui.rc b/kexi/plugins/queries/kexiquerypartinstui.rc
new file mode 100644
index 00000000..405c4377
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerypartinstui.rc
@@ -0,0 +1,24 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexiquerypartinst" version="4">
+
+<MenuBar>
+ <Menu name="view" noMerge="1">
+ <text>&amp;View</text>
+ <Action name="querypart_view_toggle_history"/>
+ </Menu>
+ <Menu name="data">
+ <text>&amp;Data</text>
+ <Action name="querypart_check_query"/>
+ <Action name="querypart_execute_query"/>
+ <Merge/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="designToolBar" fullWidth="false" noMerge="0">
+ <text>Design</text>
+ <Action name="querypart_check_query"/>
+ <Action name="querypart_execute_query"/>
+ <Action name="querypart_view_toggle_history"/>
+</ToolBar>
+
+</kpartgui>
diff --git a/kexi/plugins/queries/kexiquerypartui.rc b/kexi/plugins/queries/kexiquerypartui.rc
new file mode 100644
index 00000000..5b384aea
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerypartui.rc
@@ -0,0 +1,11 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexiquerypart" version="2">
+
+<MenuBar>
+ <!-- (not needed - it is autogenerated!) <Menu name="widgets">
+ <text>&amp;Create</text>
+ <Action name="querypart_create"/>
+ </Menu -->
+</MenuBar>
+
+</kpartgui>
diff --git a/kexi/plugins/queries/kexiqueryview.cpp b/kexi/plugins/queries/kexiqueryview.cpp
new file mode 100644
index 00000000..cf3fee96
--- /dev/null
+++ b/kexi/plugins/queries/kexiqueryview.cpp
@@ -0,0 +1,154 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004, 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <kexiproject.h>
+#include <kexidb/connection.h>
+#include <kexidb/parser/parser.h>
+#include <kexidb/cursor.h>
+#include <keximainwindow.h>
+#include <kexiutils/utils.h>
+
+#include "kexiqueryview.h"
+#include "kexiquerydesignersql.h"
+#include "kexiquerydesignerguieditor.h"
+#include "kexiquerypart.h"
+#include <widget/tableview/kexitableview.h>
+#include <widget/kexiqueryparameters.h>
+
+//! @internal
+class KexiQueryView::Private
+{
+ public:
+ Private()
+ : cursor(0)
+// , queryHasBeenChangedInViewMode( Kexi::NoViewMode )
+ {}
+ ~Private() {}
+ KexiDB::Cursor *cursor;
+ /*! Used in storeNewData(), storeData() to decide whether
+ we should ask other view to save changes.
+ Stores information about view mode. */
+// int queryHasBeenChangedInViewMode;
+};
+
+//---------------------------------------------------------------------------------
+
+KexiQueryView::KexiQueryView(KexiMainWindow *win, QWidget *parent, const char *name)
+ : KexiDataTable(win, parent, name)
+ , d( new Private() )
+{
+ tableView()->setInsertingEnabled(false); //default
+}
+
+KexiQueryView::~KexiQueryView()
+{
+ if (d->cursor)
+ d->cursor->connection()->deleteCursor(d->cursor);
+ delete d;
+}
+
+tristate KexiQueryView::executeQuery(KexiDB::QuerySchema *query)
+{
+ if (!query)
+ return false;
+ KexiUtils::WaitCursor wait;
+ KexiDB::Cursor *oldCursor = d->cursor;
+ KexiDB::debug( query->parameters() );
+ bool ok;
+ QValueList<QVariant> params;
+ {
+ KexiUtils::WaitCursorRemover remover;
+ params = KexiQueryParameters::getParameters(this,
+ *mainWin()->project()->dbConnection()->driver(), *query, ok);
+ }
+ if (!ok) {//input cancelled
+ return cancelled;
+ }
+ d->cursor = mainWin()->project()->dbConnection()->executeQuery(*query, params);
+ if (!d->cursor) {
+ parentDialog()->setStatus(parentDialog()->mainWin()->project()->dbConnection(),
+ i18n("Query executing failed."));
+ //todo: also provide server result and sql statement
+ return false;
+ }
+ setData(d->cursor);
+
+//! @todo remove close() when dynamic cursors arrive
+ d->cursor->close();
+
+ if (oldCursor)
+ oldCursor->connection()->deleteCursor(oldCursor);
+
+//! @todo maybe allow writing and inserting for single-table relations?
+ tableView()->setReadOnly( true );
+//! @todo maybe allow writing and inserting for single-table relations?
+ //set data model itself read-only too
+ tableView()->data()->setReadOnly( true );
+ tableView()->setInsertingEnabled( false );
+ return true;
+}
+
+tristate KexiQueryView::afterSwitchFrom(int mode)
+{
+ if (mode==Kexi::NoViewMode) {
+ KexiDB::QuerySchema *querySchema = static_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
+ const tristate result = executeQuery(querySchema);
+ if (true != result)
+ return result;
+ }
+ else if (mode==Kexi::DesignViewMode || Kexi::TextViewMode) {
+ KexiQueryPart::TempData * temp = static_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
+
+ //remember what view we should use to store data changes, if needed
+// if (temp->queryChangedInPreviousView)
+// d->queryHasBeenChangedInViewMode = mode;
+// else
+// d->queryHasBeenChangedInViewMode = Kexi::NoViewMode;
+
+ const tristate result = executeQuery(temp->query());
+ if (true != result)
+ return result;
+ }
+ return true;
+}
+
+KexiDB::SchemaData* KexiQueryView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ KexiViewBase * view = parentDialog()->viewThatRecentlySetDirtyFlag();
+ if (dynamic_cast<KexiQueryDesignerGuiEditor*>(view))
+ return dynamic_cast<KexiQueryDesignerGuiEditor*>(view)->storeNewData(sdata, cancel);
+ if (dynamic_cast<KexiQueryDesignerSQLView*>(view))
+ return dynamic_cast<KexiQueryDesignerSQLView*>(view)->storeNewData(sdata, cancel);
+ return 0;
+}
+
+tristate KexiQueryView::storeData(bool dontAsk)
+{
+ KexiViewBase * view = parentDialog()->viewThatRecentlySetDirtyFlag();
+ if (dynamic_cast<KexiQueryDesignerGuiEditor*>(view))
+ return dynamic_cast<KexiQueryDesignerGuiEditor*>(view)->storeData(dontAsk);
+ if (dynamic_cast<KexiQueryDesignerSQLView*>(view))
+ return dynamic_cast<KexiQueryDesignerSQLView*>(view)->storeData(dontAsk);
+ return false;
+}
+
+
+#include "kexiqueryview.moc"
+
diff --git a/kexi/plugins/queries/kexiqueryview.h b/kexi/plugins/queries/kexiqueryview.h
new file mode 100644
index 00000000..f0083738
--- /dev/null
+++ b/kexi/plugins/queries/kexiqueryview.h
@@ -0,0 +1,58 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004, 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYVIEW_H
+#define KEXIQUERYVIEW_H
+
+#include <kexidatatable.h>
+
+namespace KexiDB
+{
+ class QuerySchema;
+}
+class KexiMainWindow;
+
+class KexiQueryView : public KexiDataTable
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryView(KexiMainWindow *win, QWidget *parent, const char *name=0);
+ ~KexiQueryView();
+
+ protected:
+ virtual tristate afterSwitchFrom(int mode);
+
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+
+ virtual tristate storeData(bool dontAsk = false);
+
+ /*! Executes query \a query, filling the table view with query results.
+ \return true on success, false on failure and cancelled when user has
+ cancelled execution (for example when she pressed the Cancel button
+ of the "Enter Query Parameter" input dialog. */
+ tristate executeQuery(KexiDB::QuerySchema *query);
+
+ class Private;
+ Private *d;
+};
+
+#endif
+
diff --git a/kexi/plugins/relations/Makefile.am b/kexi/plugins/relations/Makefile.am
new file mode 100644
index 00000000..6e35ab3a
--- /dev/null
+++ b/kexi/plugins/relations/Makefile.am
@@ -0,0 +1,28 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_relation.la
+
+#kexihandler_relation_la_SOURCES = kexirelationhandler.cpp kexirelationhandlerproxy.cpp kexirelationview.cpp \
+# kexirelationviewtable.cpp kexirelationdialog.cpp \
+# kexirelationviewconnection.cpp
+kexihandler_relation_la_SOURCES = kexirelationpartimpl.cpp \
+ kexirelationmaindlg.cpp
+
+kexihandler_relation_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module
+kexihandler_relation_la_LIBADD = ../../core/libkexicore.la \
+ ../../widget/relations/libkexirelationsview.la
+
+INCLUDES= -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget -I$(top_srcdir)/kexi/widget/relations \
+ -I$(top_srcdir)/kexi/tableview \
+ -I$(top_srcdir)/kexi/kexidb $(all_includes)
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=kexirelationhandler.desktop
+
+rcdir = $(kde_datadir)/kexi
+rc_DATA = kexirelationpartui.rc kexirelationpartinstui.rc
+
+METASOURCES = AUTO
+
+include ../Makefile.common
diff --git a/kexi/plugins/relations/kexirelationhandler.desktop b/kexi/plugins/relations/kexirelationhandler.desktop
new file mode 100644
index 00000000..7232d316
--- /dev/null
+++ b/kexi/plugins/relations/kexirelationhandler.desktop
@@ -0,0 +1,110 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Relationships
+GenericName[bg]=Релации
+GenericName[ca]=Relacions
+GenericName[cs]=Vztahy
+GenericName[cy]=Perthnasau
+GenericName[de]=Beziehungen
+GenericName[el]=Συσχετίσεις
+GenericName[eo]=Rilatoj
+GenericName[es]=Relaciones
+GenericName[et]=Sõltuvused
+GenericName[eu]=Erlazioak
+GenericName[fa]=روابط
+GenericName[fi]=Yhteyssuhteet
+GenericName[fr]=Relations
+GenericName[fy]=Relaasjes
+GenericName[ga]=Gaolta
+GenericName[gl]=Relacións
+GenericName[he]=יחסים
+GenericName[hi]=रिलेशनशिप
+GenericName[hr]=Poveznice
+GenericName[hu]=Kapcsolatok
+GenericName[is]=Tengsl
+GenericName[it]=Relazioni
+GenericName[ja]=リレーションシップ
+GenericName[km]=ទំនាក់ទំនង​
+GenericName[lv]=Relācijas
+GenericName[ms]=Hubungan
+GenericName[nb]=Relasjoner
+GenericName[nds]=Betöög
+GenericName[ne]=सम्बन्धहरू
+GenericName[nl]=Relaties
+GenericName[nn]=Relasjonar
+GenericName[pl]=Relacje
+GenericName[pt]=Relações
+GenericName[pt_BR]=Relacionamentos
+GenericName[ru]=Связи
+GenericName[se]=Relašuvnnat
+GenericName[sk]=Vzťahy
+GenericName[sl]=Razmerja
+GenericName[sr]=Односи
+GenericName[sr@Latn]=Odnosi
+GenericName[sv]=Förhållanden
+GenericName[ta]=உறவுமுறைகள்
+GenericName[tr]=İlişkiler
+GenericName[uk]=Взаємозвязки
+GenericName[uz]=Aloqalar
+GenericName[uz@cyrillic]=Алоқалар
+GenericName[zh_CN]=关系
+GenericName[zh_TW]=關係
+Name=Relationships
+Name[bg]=Релации
+Name[ca]=Relacions
+Name[cs]=Vztahy
+Name[cy]=Perthnasau
+Name[de]=Beziehungen
+Name[el]=Συσχετίσεις
+Name[eo]=Rilatoj
+Name[es]=Relaciones
+Name[et]=Sõltuvused
+Name[eu]=Erlazioak
+Name[fa]=روابط
+Name[fi]=Yhteydet
+Name[fr]=Relations
+Name[fy]=Relaasjes
+Name[ga]=Gaolta
+Name[gl]=Relacións
+Name[he]=יחסים
+Name[hi]=रिलेशनशिप
+Name[hr]=Poveznice
+Name[hu]=Kapcsolatok
+Name[is]=Tengsl
+Name[it]=Relazioni
+Name[ja]=リレーションシップ
+Name[km]=ទំនាក់ទំនង​
+Name[lv]=Relācijas
+Name[ms]=Hubungan
+Name[nb]=Relasjoner
+Name[nds]=Betöög
+Name[ne]=सम्बन्धहरू
+Name[nl]=Relaties
+Name[nn]=Relasjonar
+Name[pl]=Relacje
+Name[pt]=Relações
+Name[pt_BR]=Relações
+Name[ru]=Взаимосвязи
+Name[se]=Relašuvnnat
+Name[sk]=Vzťahy
+Name[sl]=Razmerja
+Name[sr]=Односи
+Name[sr@Latn]=Odnosi
+Name[sv]=Förhållanden
+Name[ta]=உறவுகள்
+Name[tg]=Муносибатҳо
+Name[tr]=İlişkiler
+Name[uk]=Взаємозвязки
+Name[uz]=Aloqalar
+Name[uz@cyrillic]=Алоқалар
+Name[zh_CN]=关系
+Name[zh_TW]=關係
+X-KDE-Library=kexihandler_relation
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=relation
+X-Kexi-TypeMime=kexi/relation
+X-Kexi-ItemIcon=relations
+X-Kexi-NoObject=true
diff --git a/kexi/plugins/relations/kexirelationmaindlg.cpp b/kexi/plugins/relations/kexirelationmaindlg.cpp
new file mode 100644
index 00000000..6b14fffa
--- /dev/null
+++ b/kexi/plugins/relations/kexirelationmaindlg.cpp
@@ -0,0 +1,81 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexirelationmaindlg.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+
+#include <qlayout.h>
+
+#include <kexidb/connection.h>
+
+#include "keximainwindow.h"
+#include "kexiproject.h"
+#include "kexirelationwidget.h"
+#include "kexirelationview.h"
+
+KexiRelationMainDlg::KexiRelationMainDlg(KexiMainWindow *mainWin, QWidget *parent, const char *name)
+ : KexiViewBase(mainWin, parent, name)
+{
+ kdDebug() << "KexiRelationMainDlg()" << endl;
+// setIcon(SmallIcon("relation"));
+ m_defaultIconName = "relation";
+ setCaption( i18n("Relationships") );
+// setDocID( win->generatePrivateDocID() );
+
+ m_rel = new KexiRelationWidget(mainWin, this);
+ //the view can receive some our actions
+ addActionProxyChild( m_rel );
+// addActionProxyChild( m_view->relationView() );
+
+ QVBoxLayout *g = new QVBoxLayout(this);
+ g->addWidget(m_rel);
+
+ //show all tables
+ KexiDB::Connection *conn = mainWin->project()->dbConnection();
+ QStringList tables = conn->tableNames();
+ for (QStringList::ConstIterator it = tables.constBegin(); it!=tables.constEnd(); ++it) {
+ m_rel->addTable( *it );
+ }
+}
+
+KexiRelationMainDlg::~KexiRelationMainDlg()
+{
+}
+
+QSize KexiRelationMainDlg::sizeHint() const
+{
+ return QSize(600,300);
+}
+
+QWidget*
+KexiRelationMainDlg::mainWidget()
+{
+ return m_rel;
+}
+
+QString KexiRelationMainDlg::itemIcon()
+{
+ return "relation";
+}
+
+#include "kexirelationmaindlg.moc"
+
diff --git a/kexi/plugins/relations/kexirelationmaindlg.h b/kexi/plugins/relations/kexirelationmaindlg.h
new file mode 100644
index 00000000..791d6544
--- /dev/null
+++ b/kexi/plugins/relations/kexirelationmaindlg.h
@@ -0,0 +1,47 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIRELATIONMAINDLG_H
+#define KEXIRELATIONMAINDLG_H
+
+#include <kexiviewbase.h>
+
+class KexiMainWindow;
+class KexiRelationWidget;
+
+class KexiRelationMainDlg : public KexiViewBase
+{
+ Q_OBJECT
+
+ public:
+ KexiRelationMainDlg(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0);
+ ~KexiRelationMainDlg();
+
+ virtual QSize sizeHint() const;
+
+ virtual QWidget* mainWidget();
+
+ virtual QString itemIcon();
+
+ private:
+ KexiRelationWidget *m_rel;
+};
+
+#endif
+
diff --git a/kexi/plugins/relations/kexirelationpartimpl.cpp b/kexi/plugins/relations/kexirelationpartimpl.cpp
new file mode 100644
index 00000000..a2a7c213
--- /dev/null
+++ b/kexi/plugins/relations/kexirelationpartimpl.cpp
@@ -0,0 +1,85 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexirelationmaindlg.h"
+#include "kexirelationpartimpl.h"
+
+#include <kexirelationwidget.h>
+#include <kexidialogbase.h>
+#include <keximainwindow.h>
+
+#include <kgenericfactory.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+
+KexiRelationPartImpl::KexiRelationPartImpl(QObject *parent, const char *name, const QStringList &args)
+ : KexiInternalPart(parent, name, args)
+{
+ kdDebug() << "KexiRelationPartImpl()" << endl;
+}
+
+KexiRelationPartImpl::~KexiRelationPartImpl()
+{
+}
+
+/*QWidget *
+KexiRelationPartImpl::createWidget(const char* , KexiMainWindow* mainWin,
+ QWidget *parent, const char *objName)
+{
+ return new KexiRelationWidget(mainWin, parent, objName);
+}*/
+
+/*KexiDialogBase *
+KexiRelationPartImpl::createDialog(KexiMainWindow* mainWin, const char *)
+{
+ kdDebug() << "KexiRelationPartImpl::createDialog()" << endl;
+ KexiDialogBase * dlg = new KexiDialogBase(mainWin, i18n("Relations"));
+ dlg->setIcon(SmallIcon("relation"));
+ dlg->setDocID( mainWin->generatePrivateDocID() );
+
+ KexiRelationMainDlg *view = new KexiRelationMainDlg(mainWin, 0, "relations");
+ dlg->addView(view);
+// dlg->show();
+// dlg->registerDialog();
+
+ return dlg;
+}*/
+
+KexiViewBase *
+KexiRelationPartImpl::createView(KexiMainWindow* mainWin, QWidget *parent, const char *)
+{
+// kdDebug() << "KexiRelationPartImpl::createDialog()" << endl;
+// KexiDialogBase * dlg = new KexiDialogBase(mainWin, i18n("Relations"));
+// dlg->setIcon(SmallIcon("relation"));
+// dlg->setDocID( mainWin->generatePrivateDocID() );
+
+ KexiRelationMainDlg *view = new KexiRelationMainDlg(mainWin, parent, "relations");
+// dlg->addView(view);
+// dlg->show();
+// dlg->registerDialog();
+
+ return view;
+}
+
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_relation,
+ KGenericFactory<KexiRelationPartImpl>("kexihandler_relation") )
+
+#include "kexirelationpartimpl.moc"
+
diff --git a/kexi/plugins/relations/kexirelationpartimpl.h b/kexi/plugins/relations/kexirelationpartimpl.h
new file mode 100644
index 00000000..b5b5438e
--- /dev/null
+++ b/kexi/plugins/relations/kexirelationpartimpl.h
@@ -0,0 +1,46 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIRELATIONPARTIMPL_H
+#define KEXIRELATIONPARTIMPL_H
+
+#include <kexiinternalpart.h>
+
+class KexiRelationPartImpl : public KexiInternalPart
+{
+ Q_OBJECT
+
+ public:
+ KexiRelationPartImpl(QObject *parent, const char *name, const QStringList &args);
+ virtual ~KexiRelationPartImpl();
+
+ protected:
+// virtual QWidget *createWidget(const char* widgetClass, KexiMainWindow* mainWin,
+// QWidget *parent, const char *objName=0);
+
+ virtual KexiViewBase *createView(KexiMainWindow* mainWin, QWidget *parent,
+ const char *objName=0);
+
+ //virtual KexiDialogBase *createWindow(KexiMainWindow *parent);
+ //virtual QWidget *createWidget(QWidget *parent, KexiMainWindow *win);
+};
+
+#endif
+
+
diff --git a/kexi/plugins/relations/kexirelationpartinstui.rc b/kexi/plugins/relations/kexirelationpartinstui.rc
new file mode 100644
index 00000000..cad23d56
--- /dev/null
+++ b/kexi/plugins/relations/kexirelationpartinstui.rc
@@ -0,0 +1,6 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexirelationpartinst" version="1">
+
+<!-- TODO -->
+
+</kpartgui>
diff --git a/kexi/plugins/relations/kexirelationpartui.rc b/kexi/plugins/relations/kexirelationpartui.rc
new file mode 100644
index 00000000..67002e71
--- /dev/null
+++ b/kexi/plugins/relations/kexirelationpartui.rc
@@ -0,0 +1,14 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexirelationpart" version="2">
+<MenuBar>
+ <Menu name="project">
+ <text>&amp;Project</text>
+ <Action name="relations"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="projectToolBar" fullWidth="false">
+ <text>Project</text>
+ <Action name="relations"/>
+</ToolBar>
+</kpartgui>
diff --git a/kexi/plugins/reports/Makefile.am b/kexi/plugins/reports/Makefile.am
new file mode 100644
index 00000000..4f8e54c4
--- /dev/null
+++ b/kexi/plugins/reports/Makefile.am
@@ -0,0 +1,51 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_report.la kexireportwidgets.la
+
+kexihandler_report_la_SOURCES = kexireports.cpp
+kexihandler_report_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module -no-undefined
+kexihandler_report_la_LIBADD = ../../core/libkexicore.la \
+ $(top_builddir)/kexi/widget/utils/libkexiguiutils.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/kexi/formeditor/libkformdesigner.la \
+ $(top_builddir)/kexi/plugins/forms/libkexiformutils.la \
+ ./libkexireportutils.la
+
+kexireportwidgets_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module -no-undefined
+kexireportwidgets_la_SOURCES = reportwidgets.cpp kexireportfactory.cpp
+kexireportwidgets_la_LIBADD = $(top_builddir)/kexi/formeditor/libkformdesigner.la \
+ $(top_builddir)/kexi/plugins/forms/libkexiformutils.la \
+ $(top_builddir)/kexi/plugins/forms/widgets/libkexiformutilswidgets.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ ./libkexireportutils.la
+
+lib_LTLIBRARIES = libkexireportutils.la
+libkexireportutils_la_SOURCES = \
+ kexireportpart.cpp kexireportview.cpp kexireportform.cpp
+libkexireportutils_la_LDFLAGS = $(all_libraries) $(VER_INFO) -no-undefined
+libkexireportutils_la_LIBADD = $(top_builddir)/kexi/core/libkexicore.la \
+ $(top_builddir)/kexi/formeditor/libkformdesigner.la \
+ $(top_builddir)/kexi/plugins/forms/widgets/libkexiformutilswidgets.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/kexi/plugins/forms/libkexiformutils.la
+
+kformdesignerservicesdir=$(kde_servicesdir)/kformdesigner
+kformdesignerservices_DATA = kformdesigner_kexireportfactory.desktop
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=kexireporthandler.desktop
+
+rcdir = $(kde_datadir)/kexi
+rc_DATA = kexireportpartui.rc kexireportpartinstui.rc
+
+SUBDIRS = .
+
+INCLUDES= -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/lib -I$(top_srcdir)/lib/kofficecore \
+ -I$(top_srcdir)/kexi/widget/utils \
+ -I$(top_srcdir)/kexi/widget \
+ -I$(top_srcdir)/kexi/formeditor -I$(top_srcdir)/kexi/plugins/forms $(all_includes)
+
+METASOURCES = AUTO
+
+include ../Makefile.common
diff --git a/kexi/plugins/reports/kexireportfactory.cpp b/kexi/plugins/reports/kexireportfactory.cpp
new file mode 100644
index 00000000..0ac782c4
--- /dev/null
+++ b/kexi/plugins/reports/kexireportfactory.cpp
@@ -0,0 +1,227 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+#include <qpopupmenu.h>
+#include <qvaluevector.h>
+
+#include <kgenericfactory.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <klineedit.h>
+
+#include <container.h>
+#include <form.h>
+#include <formmanager.h>
+#include <widgetlibrary.h>
+
+#include "reportwidgets.h"
+#include "kexireportfactory.h"
+
+KexiReportFactory::KexiReportFactory(QObject *parent, const char *name, const QStringList &)
+ : KFormDesigner::WidgetFactory(parent, name)
+{
+ KFormDesigner::WidgetInfo *wView = new KFormDesigner::WidgetInfo(this);
+ wView->setPixmap("report");
+ wView->setClassName("KexiReportForm");
+ wView->setName(i18n("Report"));
+ wView->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "report"));
+ wView->setDescription(i18n("A report"));
+ addClass(wView);
+
+ KFormDesigner::WidgetInfo *wLabel = new KFormDesigner::WidgetInfo(this);
+ wLabel->setPixmap("label");
+ wLabel->setClassName("Label");
+ wLabel->setName(i18n("Label"));
+ wLabel->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "label"));
+ wLabel->setDescription(i18n("A label to display text"));
+ addClass(wLabel);
+
+ KFormDesigner::WidgetInfo *wPicLabel = new KFormDesigner::WidgetInfo(this);
+ wPicLabel->setPixmap("pixmaplabel");
+ wPicLabel->setClassName("PicLabel");
+ wPicLabel->setName(i18n("Picture Label"));
+ wPicLabel->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "picture"));
+ wPicLabel->setDescription(i18n("A label to display images or icons"));
+ addClass(wPicLabel);
+
+ KFormDesigner::WidgetInfo *wLine = new KFormDesigner::WidgetInfo(this);
+ wLine->setPixmap("line");
+ wLine->setClassName("ReportLine");
+ wLine->setName(i18n("Line"));
+ wLine->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "line"));
+ wLine->setDescription(i18n("A simple line"));
+ addClass(wLine);
+
+ KFormDesigner::WidgetInfo *wSubReport = new KFormDesigner::WidgetInfo(this);
+ wSubReport->setPixmap("report");
+ wSubReport->setClassName("KexiSubReport");
+ wSubReport->setName(i18n("Sub Report"));
+ wSubReport->setNamePrefix(
+ i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "subReport"));
+ wSubReport->setDescription(i18n("A report embedded in another report"));
+ addClass(wSubReport);
+}
+
+KexiReportFactory::~KexiReportFactory()
+{
+}
+
+QString
+KexiReportFactory::name()
+{
+ return "kexireportwidgets";
+}
+
+QWidget*
+KexiReportFactory::createWidget(const QCString &c, QWidget *p, const char *n,
+ KFormDesigner::Container *container, int options)
+{
+ Q_UNUSED(options);
+ kexipluginsdbg << "KexiReportFactory::create() " << this << endl;
+
+ QString text( container->form()->library()->textForWidgetName(n, c) );
+
+ if(c == "Label")
+ return new Label(text, p, n);
+ else if(c == "PicLabel")
+ return new PicLabel(DesktopIcon("image"), p, n);
+ else if(c == "ReportLine")
+ return new ReportLine(p, n);
+ else if(c == "KexiSubReport")
+ return new KexiSubReport(p, n);
+
+ return 0;
+}
+
+bool
+KexiReportFactory::createMenuActions(const QCString &classname, QWidget *w,
+ QPopupMenu *menu, KFormDesigner::Container *container)
+{
+ Q_UNUSED(w);
+ Q_UNUSED(container);
+ if(classname == "Label") {
+ /*! @todo use KAction */
+ menu->insertItem(SmallIconSet("edit"), i18n("Edit Rich Text"), this, SLOT(editText()));
+ return true;
+ }
+ return false;
+}
+
+bool
+KexiReportFactory::startEditing(const QCString &c, QWidget *w, KFormDesigner::Container *container)
+{
+ m_container = container;
+
+ if(c == "Label") {
+ QLabel *label = static_cast<QLabel*>(w);
+ if(label->textFormat() == RichText) {
+ m_widget = w;
+ editText();
+ }
+ else
+ createEditor(c, label->text(), label, container, label->geometry(), label->alignment());
+ return true;
+ }
+ return false;
+}
+
+bool
+KexiReportFactory::isPropertyVisibleInternal(const QCString &classname, QWidget *w, const QCString &property, bool isTopLevel)
+{
+ if(classname == "Label") {
+ if(property == "pixmap")
+ return false;
+ }
+ else if(classname == "PicLabel") {
+ if((property == "text") || (property == "indent") || (property == "textFormat") || (property == "font") || (property == "alignment"))
+ return false;
+ }
+
+ return WidgetFactory::isPropertyVisibleInternal(classname, w, property, isTopLevel);
+}
+
+QValueList<QCString>
+KexiReportFactory::autoSaveProperties(const QCString &classname)
+{
+ QValueList<QCString> l;
+
+ if(classname == "Label")
+ l << "text";
+ else if(classname == "PicLabel")
+ l << "pixmap";
+
+ return l;
+}
+
+/*
+void
+KexiReportFactory::changeText(const QString &text)
+{
+ QWidget *w = WidgetFactory::m_widget;
+ changeProperty("text", text, m_container);
+
+ int width = w->sizeHint().width();
+
+ if(w->width() < width)
+ w->resize(width, w->height() );
+}
+
+void
+KexiReportFactory::resizeEditor(QWidget *widget, const QCString &)
+{
+ QSize s = widget->size();
+ QPoint p = widget->pos();
+ QRect r;
+
+ m_editor->resize(s);
+ m_editor->move(p);
+}*/
+
+void
+KexiReportFactory::editText()
+{
+ QCString classname = m_widget->className();
+ QString text;
+
+ if(classname == "Label")
+ text = ((QLabel*)m_widget)->text();
+
+ if(editRichText(m_widget, text)) {
+ changeProperty("textFormat", "RichText", m_container->form());
+ changeProperty("text", text, m_container->form());
+ }
+
+ if(classname == "Label")
+ m_widget->resize(m_widget->sizeHint());
+}
+
+bool
+KexiReportFactory::previewWidget(const QCString &, QWidget *, KFormDesigner::Container *)
+{
+ return false;
+}
+
+KFORMDESIGNER_WIDGET_FACTORY(KexiReportFactory, kexireportwidgets)
+
+#include "kexireportfactory.moc"
+
diff --git a/kexi/plugins/reports/kexireportfactory.h b/kexi/plugins/reports/kexireportfactory.h
new file mode 100644
index 00000000..c6b91702
--- /dev/null
+++ b/kexi/plugins/reports/kexireportfactory.h
@@ -0,0 +1,62 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIREPORTFACTORY_H
+#define KEXIREPORTFACTORY_H
+
+#include <widgetfactory.h>
+
+//! Kexi Factory (DB widgets + subform)
+class KexiReportFactory : public KFormDesigner::WidgetFactory
+{
+ Q_OBJECT
+
+ public:
+ KexiReportFactory(QObject *parent, const char *name, const QStringList &args);
+ virtual ~KexiReportFactory();
+
+ virtual QString name();
+ virtual QWidget *createWidget(const QCString &classname, QWidget *parent, const char *name, KFormDesigner::Container *container,
+ int options = DefaultOptions);
+
+ virtual bool createMenuActions(const QCString &classname, QWidget *w, QPopupMenu *menu,
+ KFormDesigner::Container *container);
+ virtual bool startEditing(const QCString &classname, QWidget *w, KFormDesigner::Container *container);
+ virtual bool previewWidget(const QCString &, QWidget *, KFormDesigner::Container *);
+
+ //virtual void saveSpecialProperty(const QString &classname, const QString &name, const QVariant &value, QWidget *w,
+ //QDomElement &parentNode, QDomDocument &parent) {}
+ //virtual void readSpecialProperty(const QCString &classname, QDomElement &node, QWidget *w, KFormDesigner::ObjectTreeItem *item) {}
+ virtual QValueList<QCString> autoSaveProperties(const QCString &classname);
+
+ public slots:
+ void editText();
+
+ protected:
+ virtual bool isPropertyVisibleInternal(const QCString &, QWidget *, const QCString &, bool isTopLevel);
+// virtual void changeText(const QString &newText);
+// virtual void resizeEditor(QWidget *widget, const QCString &classname);
+
+ private:
+ QWidget *m_widget;
+ KFormDesigner::Container *m_container;
+};
+
+#endif
+
diff --git a/kexi/plugins/reports/kexireportform.cpp b/kexi/plugins/reports/kexireportform.cpp
new file mode 100644
index 00000000..d5dd6f55
--- /dev/null
+++ b/kexi/plugins/reports/kexireportform.cpp
@@ -0,0 +1,188 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qobjectlist.h>
+#include <qpainter.h>
+#include <qcursor.h>
+
+#include <kdebug.h>
+
+#include "kexireportform.h"
+
+KexiReportForm::KexiReportForm(QWidget *parent, const char *name/*, KexiDB::Connection *conn*/)
+ : QWidget(parent, name)
+{
+ //m_conn = conn;
+ kexipluginsdbg << "KexiReportForm::KexiReportForm(): " << endl;
+ setCursor(QCursor(Qt::ArrowCursor)); //to avoid keeping Size cursor when moving from form's boundaries
+ setBackgroundColor(white);
+}
+
+KexiReportForm::~KexiReportForm()
+{
+ kexipluginsdbg << "KexiReportForm::~KexiReportForm(): close" << endl;
+}
+
+//repaint all children widgets
+static void repaintAll(QWidget *w)
+{
+ QObjectList *list = w->queryList("QWidget");
+ QObjectListIt it(*list);
+ for (QObject *obj; (obj=it.current()); ++it ) {
+ static_cast<QWidget*>(obj)->repaint();
+ }
+ delete list;
+}
+
+void
+KexiReportForm::drawRect(const QRect& r, int type)
+{
+ QValueList<QRect> l;
+ l.append(r);
+ drawRects(l, type);
+}
+
+void
+KexiReportForm::drawRects(const QValueList<QRect> &list, int type)
+{
+ QPainter p;
+ p.begin(this, true);
+ bool unclipped = testWFlags( WPaintUnclipped );
+ setWFlags( WPaintUnclipped );
+
+ if (prev_rect.isValid()) {
+ //redraw prev. selection's rectangle
+ p.drawPixmap( QPoint(prev_rect.x()-2, prev_rect.y()-2), buffer, QRect(prev_rect.x()-2, prev_rect.y()-2, prev_rect.width()+4, prev_rect.height()+4));
+ }
+ p.setBrush(QBrush::NoBrush);
+ if(type == 1) // selection rect
+ p.setPen(QPen(white, 1, Qt::DotLine));
+ else if(type == 2) // insert rect
+ p.setPen(QPen(white, 2));
+ p.setRasterOp(XorROP);
+
+ prev_rect = QRect();
+ QValueList<QRect>::ConstIterator endIt = list.constEnd();
+ for(QValueList<QRect>::ConstIterator it = list.constBegin(); it != endIt; ++it) {
+ p.drawRect(*it);
+ prev_rect = prev_rect.unite(*it);
+ }
+
+ if (!unclipped)
+ clearWFlags( WPaintUnclipped );
+ p.end();
+}
+
+void
+KexiReportForm::initBuffer()
+{
+ repaintAll(this);
+ buffer.resize( width(), height() );
+ buffer = QPixmap::grabWindow( winId() );
+ prev_rect = QRect();
+}
+
+void
+KexiReportForm::clearForm()
+{
+ QPainter p;
+ p.begin(this, true);
+ bool unclipped = testWFlags( WPaintUnclipped );
+ setWFlags( WPaintUnclipped );
+
+ //redraw entire form surface
+ p.drawPixmap( QPoint(0,0), buffer, QRect(0,0,buffer.width(), buffer.height()) );
+
+ if (!unclipped)
+ clearWFlags( WPaintUnclipped );
+ p.end();
+
+ repaintAll(this);
+}
+
+void
+KexiReportForm::highlightWidgets(QWidget *from, QWidget *to)//, const QPoint &point)
+{
+ QPoint fromPoint, toPoint;
+ if(from && from->parentWidget() && (from != this))
+ fromPoint = from->parentWidget()->mapTo(this, from->pos());
+ if(to && to->parentWidget() && (to != this))
+ toPoint = to->parentWidget()->mapTo(this, to->pos());
+
+ QPainter p;
+ p.begin(this, true);
+ bool unclipped = testWFlags( WPaintUnclipped );
+ setWFlags( WPaintUnclipped );
+
+ if (prev_rect.isValid()) {
+ //redraw prev. selection's rectangle
+ p.drawPixmap( QPoint(prev_rect.x(), prev_rect.y()), buffer, QRect(prev_rect.x(), prev_rect.y(), prev_rect.width(), prev_rect.height()));
+ }
+
+ p.setPen( QPen(Qt::red, 2) );
+
+ if(to)
+ {
+ QPixmap pix1 = QPixmap::grabWidget(from);
+ QPixmap pix2 = QPixmap::grabWidget(to);
+
+ if((from != this) && (to != this))
+ p.drawLine( from->parentWidget()->mapTo(this, from->geometry().center()), to->parentWidget()->mapTo(this, to->geometry().center()) );
+
+ p.drawPixmap(fromPoint.x(), fromPoint.y(), pix1);
+ p.drawPixmap(toPoint.x(), toPoint.y(), pix2);
+
+ if(to == this)
+ p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4);
+ else
+ p.drawRoundRect(toPoint.x(), toPoint.y(), to->width(), to->height(), 5, 5);
+ }
+
+ if(from == this)
+ p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4);
+ else
+ p.drawRoundRect(fromPoint.x(), fromPoint.y(), from->width(), from->height(), 5, 5);
+
+ if((to == this) || (from == this))
+ prev_rect = QRect(0, 0, buffer.width(), buffer.height());
+ else if(to)
+ {
+ prev_rect.setX( (fromPoint.x() < toPoint.x()) ? (fromPoint.x() - 5) : (toPoint.x() - 5) );
+ prev_rect.setY( (fromPoint.y() < toPoint.y()) ? (fromPoint.y() - 5) : (toPoint.y() - 5) );
+ prev_rect.setRight( (fromPoint.x() < toPoint.x()) ? (toPoint.x() + to->width() + 10) : (fromPoint.x() + from->width() + 10) );
+ prev_rect.setBottom( (fromPoint.y() < toPoint.y()) ? (toPoint.y() + to->height() + 10) : (fromPoint.y() + from->height() + 10) ) ;
+ }
+ else
+ prev_rect = QRect(fromPoint.x()- 5, fromPoint.y() -5, from->width() + 10, from->height() + 10);
+
+ if (!unclipped)
+ clearWFlags( WPaintUnclipped );
+ p.end();
+}
+
+QSize
+KexiReportForm::sizeHint() const
+{
+ //todo: find better size (user configured?)
+ return QSize(400,300);
+}
+
+#include "kexireportform.moc"
+
diff --git a/kexi/plugins/reports/kexireportform.h b/kexi/plugins/reports/kexireportform.h
new file mode 100644
index 00000000..8b03c2ec
--- /dev/null
+++ b/kexi/plugins/reports/kexireportform.h
@@ -0,0 +1,60 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIREPORTFORM_H
+#define KEXIREPORTFORM_H
+
+#include <qwidget.h>
+#include <qpixmap.h>
+
+#include <formeditor/form.h>
+
+//! The report top widget
+class KEXIREPORTUTILS_EXPORT KexiReportForm : public QWidget, public KFormDesigner::FormWidget
+{
+ Q_OBJECT
+
+ public:
+ KexiReportForm(QWidget *parent, const char *name="kexi_dbform");
+ virtual ~KexiReportForm();
+
+ /*QString datasource() const { return m_ds; }
+ bool navigatorShown() const { return m_nav; }
+ void setDatasource(const QString &s) { m_ds = s; }
+ void showRecordNavigator(bool s) { m_nav = s; }*/
+
+ virtual void drawRect(const QRect& r, int type);
+ virtual void drawRects(const QValueList<QRect> &list, int type);
+ virtual void initBuffer();
+ virtual void clearForm();
+ virtual void highlightWidgets(QWidget *from, QWidget *to/*, const QPoint &p*/);
+
+ virtual QSize sizeHint() const;
+
+ private:
+ /*QString m_ds;
+ bool m_nav;
+ KexiDB::Connection *m_conn;*/
+
+ QPixmap buffer; //!< stores grabbed entire form's area for redraw
+ QRect prev_rect; //!< previously selected rectangle
+};
+
+#endif
diff --git a/kexi/plugins/reports/kexireporthandler.desktop b/kexi/plugins/reports/kexireporthandler.desktop
new file mode 100644
index 00000000..e9606dbf
--- /dev/null
+++ b/kexi/plugins/reports/kexireporthandler.desktop
@@ -0,0 +1,108 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Reports
+GenericName[bg]=Отчети
+GenericName[ca]=Informes
+GenericName[cs]=Sestavy
+GenericName[cy]=Adroddiadau
+GenericName[da]=Rapporter
+GenericName[de]=Berichte
+GenericName[el]=Αναφορές
+GenericName[eo]=Raportoj
+GenericName[es]=Informes
+GenericName[et]=Aruanded
+GenericName[eu]=Txostenak
+GenericName[fa]=گزارشها
+GenericName[fi]=Raportit
+GenericName[fr]=Rapports
+GenericName[fy]=Rapporten
+GenericName[ga]=Tuairiscí
+GenericName[gl]=Informes
+GenericName[he]=דו"חות
+GenericName[hr]=Izvještaji
+GenericName[hu]=Jelentések
+GenericName[is]=Skýrslur
+GenericName[it]=Rapporti
+GenericName[ja]=レポート
+GenericName[km]=របាយការណ៍
+GenericName[lt]=Ataskaitos
+GenericName[lv]=Atskaites
+GenericName[ms]=Laporan
+GenericName[nb]=Rapporter
+GenericName[nds]=Berichten
+GenericName[ne]=प्रतिवेदनहरू
+GenericName[nl]=Rapporten
+GenericName[nn]=Rapportar
+GenericName[pl]=Raporty
+GenericName[pt]=Relatórios
+GenericName[pt_BR]=Relatórios
+GenericName[ru]=Отчёты
+GenericName[se]=Raporttat
+GenericName[sk]=Správy
+GenericName[sl]=Poročila
+GenericName[sr]=Извештаји
+GenericName[sr@Latn]=Izveštaji
+GenericName[sv]=Rapporter
+GenericName[uk]=Звіти
+GenericName[uz]=Hisobotlar
+GenericName[uz@cyrillic]=Ҳисоботлар
+GenericName[zh_CN]=报表
+GenericName[zh_TW]=報告
+Name=Reports
+Name[bg]=Отчети
+Name[ca]=Informes
+Name[cs]=Sestavy
+Name[cy]=Adroddiadau
+Name[da]=Rapporter
+Name[de]=Berichte
+Name[el]=Αναφορές
+Name[eo]=Raportoj
+Name[es]=Informes
+Name[et]=Aruanded
+Name[eu]=Txostenak
+Name[fa]=گزارشها
+Name[fi]=Raportit
+Name[fr]=Rapports
+Name[fy]=Rapporten
+Name[ga]=Tuairiscí
+Name[gl]=Informes
+Name[he]=דו"חות
+Name[hr]=Izvještaji
+Name[hu]=Jelentések
+Name[is]=Skýrslur
+Name[it]=Rapporti
+Name[ja]=レポート
+Name[km]=របាយការណ៍
+Name[lt]=Ataskaitos
+Name[lv]=Atskaites
+Name[ms]=Laporan
+Name[nb]=Rapporter
+Name[nds]=Berichten
+Name[ne]=प्रतिवेदनहरू
+Name[nl]=Rapporten
+Name[nn]=Rapportar
+Name[pl]=Raporty
+Name[pt]=Relatórios
+Name[pt_BR]=Relatórios
+Name[ru]=Отчёты
+Name[se]=Raporttat
+Name[sk]=Správy
+Name[sl]=Poročila
+Name[sr]=Извештаји
+Name[sr@Latn]=Izveštaji
+Name[sv]=Rapporter
+Name[uk]=Звіти
+Name[uz]=Hisobotlar
+Name[uz@cyrillic]=Ҳисоботлар
+Name[zh_CN]=报表
+Name[zh_TW]=報告
+X-KDE-Library=kexihandler_report
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=report
+X-Kexi-TypeMime=kexi/report
+X-Kexi-ItemIcon=report
+X-Kexi-SupportsDataExport=false
+X-Kexi-SupportsPrinting=false
diff --git a/kexi/plugins/reports/kexireportpart.cpp b/kexi/plugins/reports/kexireportpart.cpp
new file mode 100644
index 00000000..ad83cbf4
--- /dev/null
+++ b/kexi/plugins/reports/kexireportpart.cpp
@@ -0,0 +1,141 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include "kexiviewbase.h"
+#include "keximainwindow.h"
+#include "kexiproject.h"
+#include <kexipartitem.h>
+#include <kexidialogbase.h>
+
+#include <kexidb/connection.h>
+#include <kexidb/fieldlist.h>
+#include <kexidb/field.h>
+
+#include <form.h>
+#include <formIO.h>
+#include <widgetlibrary.h>
+
+#include <kexiformmanager.h>
+#include <kexiformpart.h>
+
+#include "kexireportview.h"
+#include "kexireportpart.h"
+
+KFormDesigner::WidgetLibrary* KexiReportPart::static_reportsLibrary = 0L;
+
+KexiReportPart::KexiReportPart(QObject *parent, const char *name, const QStringList &l)
+ : KexiPart::Part(parent, name, l)
+{
+ // REGISTERED ID:
+ m_registeredPartID = (int)KexiPart::ReportObjectType;
+
+ kexipluginsdbg << "KexiReportPart::KexiReportPart()" << endl;
+ m_names["instanceName"]
+ = i18n("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). "
+ "Use '_' character instead of spaces. First character should be a..z character. "
+ "If you cannot use latin characters in your language, use english word.",
+ "report");
+ m_names["instanceCaption"] = i18n("Report");
+ m_supportedViewModes = Kexi::DataViewMode | Kexi::DesignViewMode;
+
+ // Only create form manager if it's not yet created.
+ // KexiFormPart could have created is already.
+ KFormDesigner::FormManager *formManager = KFormDesigner::FormManager::self();
+ if (!formManager)
+ formManager = new KexiFormManager(this, "kexi_form_and_report_manager");
+
+ // Create and store a handle to report' library. Forms will have their own library too.
+/* @todo add configuration for supported factory groups */
+ QStringList supportedFactoryGroups;
+ supportedFactoryGroups += "kexi-report";
+ static_reportsLibrary = KFormDesigner::FormManager::createWidgetLibrary(
+ formManager, supportedFactoryGroups);
+ static_reportsLibrary->setAdvancedPropertiesVisible(false);
+}
+
+KexiReportPart::~KexiReportPart()
+{
+}
+
+KFormDesigner::WidgetLibrary* KexiReportPart::library()
+{
+ return static_reportsLibrary;
+}
+
+void
+KexiReportPart::initPartActions()
+{
+}
+
+void
+KexiReportPart::initInstanceActions()
+{
+ KFormDesigner::FormManager::self()->createActions(
+ library(), actionCollectionForMode(Kexi::DesignViewMode), guiClient());
+}
+
+KexiDialogTempData*
+KexiReportPart::createTempData(KexiDialogBase* dialog)
+{
+ return new KexiReportPart::TempData(dialog);
+}
+
+KexiViewBase*
+KexiReportPart::createView(QWidget *parent, KexiDialogBase* dialog,
+ KexiPart::Item &item, int, QMap<QString,QString>*)
+{
+ kexipluginsdbg << "KexiReportPart::createView()" << endl;
+ KexiMainWindow *win = dialog->mainWin();
+ if (!win || !win->project() || !win->project()->dbConnection())
+ return 0;
+
+ KexiReportView *view = new KexiReportView(win, parent, item.name().latin1(),
+ win->project()->dbConnection() );
+
+ return view;
+}
+
+QString
+KexiReportPart::i18nMessage(const QCString& englishMessage, KexiDialogBase* dlg) const
+{
+ Q_UNUSED(dlg);
+ if (englishMessage=="Design of object \"%1\" has been modified.")
+ return i18n("Design of report \"%1\" has been modified.");
+ if (englishMessage=="Object \"%1\" already exists.")
+ return i18n("Report \"%1\" already exists.");
+ return englishMessage;
+}
+
+//---------------
+
+KexiReportPart::TempData::TempData(QObject* parent)
+ : KexiDialogTempData(parent)
+{
+}
+
+KexiReportPart::TempData::~TempData()
+{
+}
+
+#include "kexireportpart.moc"
+
diff --git a/kexi/plugins/reports/kexireportpart.h b/kexi/plugins/reports/kexireportpart.h
new file mode 100644
index 00000000..19731e57
--- /dev/null
+++ b/kexi/plugins/reports/kexireportpart.h
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIREPORTPART_H
+#define KEXIREPORTPART_H
+
+#include <kexi.h>
+#include <kexipart.h>
+#include <kexidialogbase.h>
+
+namespace KFormDesigner
+{
+ class FormManager;
+ class WidgetLibrary;
+ class Form;
+}
+
+namespace KexiDB
+{
+ class FieldList;
+}
+
+/*! @short Kexi Report Plugin
+ It just creates a \ref KexiReportView. See there for most of code. */
+class KEXIREPORTUTILS_EXPORT KexiReportPart : public KexiPart::Part
+{
+ Q_OBJECT
+
+ public:
+ KexiReportPart(QObject *parent, const char *name, const QStringList &);
+ virtual ~KexiReportPart();
+
+ //! \return a pointer to Reports Widget Library.
+ static KFormDesigner::WidgetLibrary* library();
+
+// KFormDesigner::FormManager *manager() { return m_manager; }
+
+ void generateForm(KexiDB::FieldList *list, QDomDocument &domDoc);
+
+ class TempData : public KexiDialogTempData
+ {
+ public:
+ TempData(QObject* parent);
+ ~TempData();
+ QGuardedPtr<KFormDesigner::Form> form;
+ QGuardedPtr<KFormDesigner::Form> previewForm;
+ QString tempForm;
+ QPoint scrollViewContentsPos; //!< to preserve contents pos after switching to other view
+ int resizeMode; //!< form's window's resize mode -one of KexiFormView::ResizeMode items
+ };
+
+ virtual QString i18nMessage(const QCString& englishMessage,
+ KexiDialogBase* dlg) const;
+
+ protected:
+ virtual KexiDialogTempData* createTempData(KexiDialogBase* dialog);
+
+ virtual KexiViewBase* createView(QWidget *parent, KexiDialogBase* dialog,
+ KexiPart::Item &item, int viewMode = Kexi::DataViewMode, QMap<QString,QString>* staticObjectArgs = 0);
+
+ virtual void initPartActions();
+ virtual void initInstanceActions();
+
+ static KFormDesigner::WidgetLibrary* static_reportsLibrary;
+
+ private:
+// QGuardedPtr<KFormDesigner::FormManager> m_manager;
+};
+
+#endif
+
diff --git a/kexi/plugins/reports/kexireportpartinstui.rc b/kexi/plugins/reports/kexireportpartinstui.rc
new file mode 100644
index 00000000..9bfc8fe3
--- /dev/null
+++ b/kexi/plugins/reports/kexireportpartinstui.rc
@@ -0,0 +1,37 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexireportpartinst" version="1">
+
+<MenuBar>
+ <Menu name="edit">
+ <Action name="fompart_clear_contents"/>
+ </Menu>
+ <Menu name="format" noMerge="1">
+ <text>&amp;Format</text>
+ <Action name="snap_to_grid"/>
+ <Separator/>
+ <Action name="reportpart_align_menu"/>
+ <Action name="reportpart_adjust_size_menu"/>
+ <Separator/>
+ <Action name="reportpart_format_raise"/>
+ <Action name="reportpart_format_lower"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="widgets" fullWidth="false">
+ <text>Widgets</text>
+ <Action name="pointer"/>
+ <Separator/>
+ <Action name="library_widget_KexiSubReport"/>
+ <Separator/>
+ <Action name="library_widget_Label"/>
+ <Action name="library_widget_PicLabel"/>
+ <Action name="library_widget_ReportLine"/>
+</ToolBar>
+<ToolBar name="format" fullWidth="false" noMerge="1">
+<text>Format Toolbar</text>
+ <Action name="reportpart_align_menu"/>
+ <Action name="reportpart_adjust_size_menu"/>
+</ToolBar>
+
+</kpartgui>
+
diff --git a/kexi/plugins/reports/kexireportpartui.rc b/kexi/plugins/reports/kexireportpartui.rc
new file mode 100644
index 00000000..a81b09b1
--- /dev/null
+++ b/kexi/plugins/reports/kexireportpartui.rc
@@ -0,0 +1,6 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexireportpart" version="1">
+
+
+</kpartgui>
+
diff --git a/kexi/plugins/reports/kexireports.cpp b/kexi/plugins/reports/kexireports.cpp
new file mode 100644
index 00000000..51b03054
--- /dev/null
+++ b/kexi/plugins/reports/kexireports.cpp
@@ -0,0 +1,24 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <kgenericfactory.h>
+
+#include "kexireportpart.h"
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_report, KGenericFactory<KexiReportPart>("kexihandler_report") )
diff --git a/kexi/plugins/reports/kexireportview.cpp b/kexi/plugins/reports/kexireportview.cpp
new file mode 100644
index 00000000..6b7f46b5
--- /dev/null
+++ b/kexi/plugins/reports/kexireportview.cpp
@@ -0,0 +1,477 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexireportview.h"
+
+#include <kdebug.h>
+
+#include <form.h>
+#include <formIO.h>
+#include <formmanager.h>
+#include <objecttree.h>
+#include <widgetpropertyset.h>
+#include <container.h>
+
+#include <kexidialogbase.h>
+//#include <kexidatasourcewizard.h>
+#include <kexidb/fieldlist.h>
+#include <kexidb/connection.h>
+
+#include "kexireportform.h"
+#include <utils/kexirecordnavigator.h>
+
+#define NO_DSWIZARD
+
+KexiReportScrollView::KexiReportScrollView(QWidget *parent, bool preview)
+ : KexiScrollView(parent, preview)
+{
+ if(preview) {
+ setRecordNavigatorVisible(true);
+ recordNavigator()->setLabelText(i18n("Page:"));
+ recordNavigator()->setInsertingButtonVisible(false);
+ }
+ connect(this, SIGNAL(resizingStarted()), this, SLOT(slotResizingStarted()));
+}
+
+KexiReportScrollView::~KexiReportScrollView()
+{
+}
+
+void
+KexiReportScrollView::show()
+{
+ KexiScrollView::show();
+
+ //now get resize mode settings for entire form
+ if (m_preview) {
+ KexiReportView* fv = dynamic_cast<KexiReportView*>(parent());
+ int resizeMode = fv ? fv->resizeMode() : KexiReportView::ResizeAuto;
+ if (resizeMode == KexiReportView::ResizeAuto)
+ setResizePolicy(AutoOneFit);
+ }
+}
+
+void
+KexiReportScrollView::slotResizingStarted()
+{
+ if(m_form && KFormDesigner::FormManager::self())
+ setSnapToGrid(KFormDesigner::FormManager::self()->snapWidgetsToGrid(), m_form->gridSize());
+ else
+ setSnapToGrid(false);
+}
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+KexiReportView::KexiReportView(KexiMainWindow *win, QWidget *parent, const char *name,
+ KexiDB::Connection *conn)
+ : KexiViewBase(win, parent, name), m_propertySet(0), m_conn(conn)
+ , m_resizeMode(KexiReportView::ResizeDefault)
+{
+ QHBoxLayout *l = new QHBoxLayout(this);
+ l->setAutoAdd(true);
+
+ m_scrollView = new KexiReportScrollView(this, viewMode()==Kexi::DataViewMode);
+ setViewWidget(m_scrollView);
+// m_scrollView->show();
+
+ m_reportform = new KexiReportForm(m_scrollView->viewport(), name/*, conn*/);
+// m_reportform->resize(QSize(400, 300));
+ m_scrollView->setWidget(m_reportform);
+ m_scrollView->setResizingEnabled(viewMode()!=Kexi::DataViewMode);
+
+// initForm();
+
+ if (viewMode()==Kexi::DataViewMode) {
+ m_scrollView->viewport()->setPaletteBackgroundColor(m_reportform->palette().active().background());
+#if 0
+ connect(reportPart()->manager(), SIGNAL(noFormSelected()), SLOT(slotNoFormSelected()));
+#endif
+ }
+ else {
+ connect(KFormDesigner::FormManager::self(), SIGNAL(propertySetSwitched(KoProperty::Set *, bool)),
+ this, SLOT(slotPropertySetSwitched(KoProperty::Set *, bool)));
+ connect(KFormDesigner::FormManager::self(), SIGNAL(dirty(KFormDesigner::Form *, bool)),
+ this, SLOT(slotDirty(KFormDesigner::Form *, bool)));
+
+ // action stuff
+ /*connect(reportPart()->manager(), SIGNAL(widgetSelected(KFormDesigner::Form*, bool)), SLOT(slotWidgetSelected(KFormDesigner::Form*, bool)));
+ connect(reportPart()->manager(), SIGNAL(formWidgetSelected(KFormDesigner::Form*)), SLOT(slotFormWidgetSelected(KFormDesigner::Form*)));
+ connect(reportPart()->manager(), SIGNAL(undoEnabled(bool, const QString&)), this, SLOT(setUndoEnabled(bool)));
+ connect(reportPart()->manager(), SIGNAL(redoEnabled(bool, const QString&)), this, SLOT(setRedoEnabled(bool)));*/
+
+ plugSharedAction("edit_copy", KFormDesigner::FormManager::self(), SLOT(copyWidget()));
+ plugSharedAction("edit_cut", KFormDesigner::FormManager::self(), SLOT(cutWidget()));
+ plugSharedAction("edit_paste", KFormDesigner::FormManager::self(), SLOT(pasteWidget()));
+ plugSharedAction("edit_delete", KFormDesigner::FormManager::self(), SLOT(deleteWidget()));
+ plugSharedAction("edit_select_all", KFormDesigner::FormManager::self(), SLOT(selectAll()));
+ plugSharedAction("reportpart_clear_contents", KFormDesigner::FormManager::self(), SLOT(clearWidgetContent()));
+ plugSharedAction("edit_undo", KFormDesigner::FormManager::self(), SLOT(undo()));
+ plugSharedAction("edit_redo", KFormDesigner::FormManager::self(), SLOT(redo()));
+
+ plugSharedAction("reportpart_format_raise", KFormDesigner::FormManager::self(), SLOT(bringWidgetToFront()) );
+ plugSharedAction("reportpart_format_lower", KFormDesigner::FormManager::self(), SLOT(sendWidgetToBack()) );
+
+ plugSharedAction("reportpart_align_menu", KFormDesigner::FormManager::self(), 0 );
+ plugSharedAction("reportpart_align_to_left", KFormDesigner::FormManager::self(),SLOT(alignWidgetsToLeft()) );
+ plugSharedAction("reportpart_align_to_right", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToRight()) );
+ plugSharedAction("reportpart_align_to_top", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToTop()) );
+ plugSharedAction("reportpart_align_to_bottom", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToBottom()) );
+ plugSharedAction("reportpart_align_to_grid", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToGrid()) );
+
+ plugSharedAction("reportpart_adjust_size_menu", KFormDesigner::FormManager::self(), 0 );
+ plugSharedAction("reportpart_adjust_to_fit", KFormDesigner::FormManager::self(), SLOT(adjustWidgetSize()) );
+ plugSharedAction("reportpart_adjust_size_grid", KFormDesigner::FormManager::self(), SLOT(adjustSizeToGrid()) );
+ plugSharedAction("reportpart_adjust_height_small", KFormDesigner::FormManager::self(), SLOT(adjustHeightToSmall()) );
+ plugSharedAction("reportpart_adjust_height_big", KFormDesigner::FormManager::self(), SLOT(adjustHeightToBig()) );
+ plugSharedAction("reportpart_adjust_width_small", KFormDesigner::FormManager::self(), SLOT(adjustWidthToSmall()) );
+ plugSharedAction("reportpart_adjust_width_big", KFormDesigner::FormManager::self(), SLOT(adjustWidthToBig()) );
+ }
+
+ initForm();
+
+ connect(this, SIGNAL(focus(bool)), this, SLOT(slotFocus(bool)));
+ /// @todo skip this if ther're no borders
+// m_reportform->resize( m_reportform->size()+QSize(m_scrollView->verticalScrollBar()->width(), m_scrollView->horizontalScrollBar()->height()) );
+}
+
+KexiReportView::~KexiReportView()
+{
+ // Important: form window is closed.
+ // Set property set to 0 because there is *only one* instance of a property set class
+ // in Kexi, so the main window wouldn't know the set in fact has been changed.
+ m_propertySet = 0;
+ propertySetSwitched();
+}
+
+KFormDesigner::Form*
+KexiReportView::form() const
+{
+ if(viewMode()==Kexi::DataViewMode)
+ return tempData()->previewForm;
+ else
+ return tempData()->form;
+}
+
+void
+KexiReportView::setForm(KFormDesigner::Form *f)
+{
+ if(viewMode()==Kexi::DataViewMode)
+ tempData()->previewForm = f;
+ else
+ tempData()->form = f;
+}
+
+void
+KexiReportView::initForm()
+{
+ setForm( new KFormDesigner::Form(KexiReportPart::library()) );
+ form()->createToplevel(m_reportform, m_reportform);
+
+ // Show the form wizard if this is a new Form
+// KexiDB::FieldList *fields = 0;
+ if(parentDialog()->id() < 0)
+ {
+#ifndef NO_DSWIZARD
+ KexiDataSourceWizard *w = new KexiDataSourceWizard(mainWin(), (QWidget*)mainWin(), "datasource_wizard");
+ if(!w->exec())
+ fields = 0;
+ else
+ fields = w->fields();
+ delete w;
+#endif
+ }
+
+/* if(fields)
+ {
+ @todo generate a report from a table or a query
+ QDomDocument dom;
+ reportPart()->generateForm(fields, dom);
+ KFormDesigner::FormIO::loadFormFromDom(form(), m_reportform, dom);
+ }
+ else*/
+ loadForm();
+
+ KFormDesigner::FormManager::self()->importForm(form(), viewMode()==Kexi::DataViewMode);
+ m_scrollView->setForm(form());
+ m_scrollView->refreshContentsSize();
+}
+
+void
+KexiReportView::loadForm()
+{
+
+//@todo also load m_resizeMode !
+
+ kexipluginsdbg << "KexiReportForm::loadForm() Loading the form with id : " << parentDialog()->id() << endl;
+ // If we are previewing the Form, use the tempData instead of the form stored in the db
+ if(viewMode()==Kexi::DataViewMode && !tempData()->tempForm.isNull() ) {
+ KFormDesigner::FormIO::loadFormFromString(form(), m_reportform, tempData()->tempForm);
+ return;
+ }
+
+ // normal load
+ QString data;
+ loadDataBlock(data);
+ KFormDesigner::FormIO::loadFormFromString(form(), m_reportform, data);
+}
+
+void
+KexiReportView::slotPropertySetSwitched(KoProperty::Set *set, bool forceReload)
+{
+ m_propertySet = set;
+ if (forceReload)
+ propertySetReloaded(true/*preservePrevSelection*/);
+ else
+ propertySetSwitched();
+}
+
+tristate
+KexiReportView::beforeSwitchTo(int mode, bool &dontStore)
+{
+ if (mode!=viewMode() && viewMode()!=Kexi::DataViewMode) {
+ //remember our pos
+ tempData()->scrollViewContentsPos
+ = QPoint(m_scrollView->contentsX(), m_scrollView->contentsY());
+ }
+
+ // we don't store on db, but in our TempData
+ dontStore = true;
+ if(dirty() && (mode == Kexi::DataViewMode) && form()->objectTree())
+ KFormDesigner::FormIO::saveFormToString(form(), tempData()->tempForm);
+
+ return true;
+}
+
+tristate
+KexiReportView::afterSwitchFrom(int mode)
+{
+ if (mode != 0 && mode != Kexi::DesignViewMode) {
+ //preserve contents pos after switching to other view
+ m_scrollView->setContentsPos(tempData()->scrollViewContentsPos.x(),
+ tempData()->scrollViewContentsPos.y());
+ }
+// if (mode == Kexi::DesignViewMode) {
+ //m_scrollView->move(0,0);
+ //m_scrollView->setContentsPos(0,0);
+ //m_scrollView->moveChild(m_reportform, 0, 0);
+// }
+
+ if((mode == Kexi::DesignViewMode) && viewMode()==Kexi::DataViewMode) {
+ // The form may have been modified, so we must recreate the preview
+ delete m_reportform; // also deletes form()
+ m_reportform = new KexiReportForm(m_scrollView->viewport());
+ m_scrollView->setWidget(m_reportform);
+
+ initForm();
+#if 0
+ slotNoFormSelected();
+#endif
+
+ //reset position
+ m_scrollView->setContentsPos(0,0);
+ m_reportform->move(0,0);
+ }
+ return true;
+}
+
+void
+KexiReportView::slotDirty(KFormDesigner::Form *dirtyForm, bool isDirty)
+{
+ if(dirtyForm == form())
+ KexiViewBase::setDirty(isDirty);
+}
+
+KexiDB::SchemaData*
+KexiReportView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ KexiDB::SchemaData *s = KexiViewBase::storeNewData(sdata, cancel);
+ kexipluginsdbg << "KexiReportForm::storeNewData(): new id:" << s->id() << endl;
+
+ if (!s || cancel) {
+ delete s;
+ return 0;
+ }
+ if (!storeData()) {
+ //failure: remove object's schema data to avoid garbage
+ m_conn->removeObject( s->id() );
+ delete s;
+ return 0;
+ }
+ return s;
+}
+
+tristate
+KexiReportView::storeData(bool dontAsk)
+{
+ Q_UNUSED(dontAsk)
+ kexipluginsdbg << "KexiReportForm::storeData(): " << parentDialog()->partItem()->name() << " [" << parentDialog()->id() << "]" << endl;
+ QString data;
+ KFormDesigner::FormIO::saveFormToString(tempData()->form, data);
+ if (!storeDataBlock(data))
+ return false;
+ tempData()->tempForm = QString();
+
+ return true;
+}
+
+#if 0
+/// Action stuff /////////////////
+void
+KexiReportView::slotWidgetSelected(KFormDesigner::Form *f, bool multiple)
+{
+ if(f != form())
+ return;
+
+ enableFormActions();
+ // Enable edit actions
+ setAvailable("edit_copy", true);
+ setAvailable("edit_cut", true);
+ setAvailable("edit_clear", true);
+
+ // 'Align Widgets' menu
+ setAvailable("reportpart_align_menu", multiple);
+ setAvailable("reportpart_align_to_left", multiple);
+ setAvailable("reportpart_align_to_right", multiple);
+ setAvailable("reportpart_align_to_top", multiple);
+ setAvailable("reportpart_align_to_bottom", multiple);
+
+ setAvailable("reportpart_adjust_size_menu", true);
+ setAvailable("reportpart_adjust_width_small", multiple);
+ setAvailable("reportpart_adjust_width_big", multiple);
+ setAvailable("reportpart_adjust_height_small", multiple);
+ setAvailable("reportpart_adjust_height_big", multiple);
+
+ setAvailable("reportpart_format_raise", true);
+ setAvailable("reportpart_format_lower", true);
+}
+
+void
+KexiReportView::slotFormWidgetSelected(KFormDesigner::Form *f)
+{
+ if(f != form())
+ return;
+
+ disableWidgetActions();
+ enableFormActions();
+}
+
+void
+KexiReportView::slotNoFormSelected() // == form in preview mode
+{
+ disableWidgetActions();
+
+ // Disable paste action
+ setAvailable("edit_paste", false);
+ setAvailable("edit_undo", false);
+ setAvailable("edit_redo", false);
+}
+
+void
+KexiReportView::enableFormActions()
+{
+ setAvailable("edit_paste", KFormDesigner::FormManager::self()->isPasteEnabled());
+}
+
+void
+KexiReportView::disableWidgetActions()
+{
+ // Disable edit actions
+ setAvailable("edit_copy", false);
+ setAvailable("edit_cut", false);
+ setAvailable("edit_clear", false);
+
+ // Disable format functions
+ setAvailable("reportpart_align_menu", false);
+ setAvailable("reportpart_align_to_left", false);
+ setAvailable("reportpart_align_to_right", false);
+ setAvailable("reportpart_align_to_top", false);
+ setAvailable("reportpart_align_to_bottom", false);
+
+ setAvailable("reportpart_adjust_size_menu", false);
+ setAvailable("reportpart_adjust_width_small", false);
+ setAvailable("reportpart_adjust_width_big", false);
+ setAvailable("reportpart_adjust_height_small", false);
+ setAvailable("reportpart_adjust_height_big", false);
+
+ setAvailable("reportpart_format_raise", false);
+ setAvailable("reportpart_format_lower", false);
+}
+
+void
+KexiReportView::setUndoEnabled(bool enabled)
+{
+ setAvailable("edit_undo", enabled);
+}
+
+void
+KexiReportView::setRedoEnabled(bool enabled)
+{
+ setAvailable("edit_redo", enabled);
+}
+#endif
+
+QSize
+KexiReportView::preferredSizeHint(const QSize& otherSize)
+{
+ return (m_reportform->size()
+ +QSize(m_scrollView->verticalScrollBar()->isVisible() ? m_scrollView->verticalScrollBar()->width()*3/2 : 10,
+ m_scrollView->horizontalScrollBar()->isVisible() ? m_scrollView->horizontalScrollBar()->height()*3/2 : 10))
+ .expandedTo( KexiViewBase::preferredSizeHint(otherSize) );
+}
+
+void
+KexiReportView::resizeEvent( QResizeEvent *e )
+{
+ if (viewMode()==Kexi::DataViewMode) {
+ m_scrollView->refreshContentsSizeLater(
+ e->size().width()!=e->oldSize().width(),
+ e->size().height()!=e->oldSize().height()
+ );
+ }
+ KexiViewBase::resizeEvent(e);
+ m_scrollView->updateNavPanelGeometry();
+}
+
+void
+KexiReportView::show()
+{
+ KexiViewBase::show();
+
+//moved from KexiFormScrollView::show():
+
+ //now get resize mode settings for entire form
+ // if (resizeMode() == KexiFormView::ResizeAuto)
+ if (viewMode()==Kexi::DataViewMode) {
+ if (resizeMode() == ResizeAuto)
+ m_scrollView->setResizePolicy(QScrollView::AutoOneFit);
+ }
+}
+
+void
+KexiReportView::slotFocus(bool in)
+{
+ if(in && form() && KFormDesigner::FormManager::self() && KFormDesigner::FormManager::self()->activeForm() != form())
+ KFormDesigner::FormManager::self()->windowChanged(form()->widget());//m_dbform);
+}
+
+
+#include "kexireportview.moc"
+
diff --git a/kexi/plugins/reports/kexireportview.h b/kexi/plugins/reports/kexireportview.h
new file mode 100644
index 00000000..b600c06d
--- /dev/null
+++ b/kexi/plugins/reports/kexireportview.h
@@ -0,0 +1,130 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIREPORTVIEW_H
+#define KEXIREPORTVIEW_H
+
+#include <qscrollview.h>
+#include <qtimer.h>
+
+#include <kexiviewbase.h>
+
+#include "kexiscrollview.h"
+#include "kexireportpart.h"
+
+class KexiReportForm;
+
+class KEXIREPORTUTILS_EXPORT KexiReportScrollView : public KexiScrollView
+{
+ Q_OBJECT
+
+ public:
+ KexiReportScrollView(QWidget *parent, bool preview);
+ virtual ~KexiReportScrollView();
+
+ void setForm(KFormDesigner::Form *form) { m_form = form; }
+
+ public slots:
+ /*! Reimplemented to update resize policy. */
+ virtual void show();
+
+ protected slots:
+ void slotResizingStarted();
+
+ private:
+ KFormDesigner::Form *m_form;
+};
+
+
+//! The FormPart's view
+/*! This class presents a single view used inside KexiDialogBase.
+ It takes care of saving/loading report, of enabling actions when needed.
+ One KexiReportView object is instantiated for data view mode (preview == true in constructor),
+ and second KexiReportView object is instantiated for design view mode
+ (preview == false in constructor). */
+class KEXIREPORTUTILS_EXPORT KexiReportView : public KexiViewBase
+{
+ Q_OBJECT
+
+ public:
+ enum ResizeMode {
+ ResizeAuto = 0,
+ ResizeDefault = ResizeAuto,
+ ResizeFixed = 1,
+ NoResize = 2 /*! @todo */
+ };
+
+ KexiReportView(KexiMainWindow *win, QWidget *parent, const char *name, KexiDB::Connection *conn);
+ virtual ~KexiReportView();
+
+ KexiDB::Connection* connection() { return m_conn; }
+
+ virtual QSize preferredSizeHint(const QSize& otherSize);
+
+ int resizeMode() const { return m_resizeMode; }
+
+ public slots:
+ /*! Reimplemented to update resize policy. */
+ virtual void show();
+
+ protected slots:
+ void slotPropertySetSwitched(KoProperty::Set *set, bool forceReload = false);
+ void slotDirty(KFormDesigner::Form *f, bool isDirty);
+ void slotFocus(bool in);
+
+ /*void slotWidgetSelected(KFormDesigner::Form *form, bool multiple);
+ void slotFormWidgetSelected(KFormDesigner::Form *form);
+ void slotNoFormSelected();
+
+ void setUndoEnabled(bool enabled);
+ void setRedoEnabled(bool enabled); */
+
+ protected:
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+ virtual tristate afterSwitchFrom(int mode);
+ virtual KoProperty::Set* propertySet() { return m_propertySet; }
+
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+ virtual tristate storeData(bool dontAsk = false);
+
+ KexiReportPart::TempData* tempData() const {
+ return static_cast<KexiReportPart::TempData*>(parentDialog()->tempData()); }
+ KexiReportPart* reportPart() const { return static_cast<KexiReportPart*>(part()); }
+
+ void disableWidgetActions();
+ void enableFormActions();
+
+ KFormDesigner::Form* form() const;
+ void setForm(KFormDesigner::Form *f);
+
+ void initForm();
+ void loadForm();
+
+ virtual void resizeEvent ( QResizeEvent * );
+
+ private:
+ KexiReportForm *m_reportform;
+ KexiReportScrollView *m_scrollView;
+ KoProperty::Set *m_propertySet;
+ KexiDB::Connection *m_conn;
+ int m_resizeMode;
+};
+
+#endif
diff --git a/kexi/plugins/reports/kformdesigner_kexireportfactory.desktop b/kexi/plugins/reports/kformdesigner_kexireportfactory.desktop
new file mode 100644
index 00000000..46ccbd61
--- /dev/null
+++ b/kexi/plugins/reports/kformdesigner_kexireportfactory.desktop
@@ -0,0 +1,53 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=KFormDesigner/WidgetFactory
+
+Name=Kexi Report Widgets
+Name[bg]=Графични елементи за отчети на Kexi
+Name[ca]=Estris d'informe de Kexi
+Name[cy]=Celfigion Adroddiad i Kexi
+Name[da]=Kexi Rapportkontroller
+Name[de]=Kexi Bericht-Elemente
+Name[el]=Γραφικά συστατικά αναφορών Kexi
+Name[eo]=Kexi-raportfenestraĵo
+Name[es]=Widgets para informes de Kexi
+Name[et]=Kexi aruandevidinad
+Name[eu]=Kexi-ren txosten-trepetak
+Name[fa]=عناصر گزارش Kexi
+Name[fi]=Kexi Raporttielementit
+Name[fr]=Éléments de rapport Kexi
+Name[fy]=Kexi Rapport-widgets
+Name[gl]=Elementos de Informe de Kexi
+Name[he]=פריטי דו"חות של Kexi
+Name[hr]=Kexi widgeti izvještaja
+Name[hu]=Kexi jelentéskezelő grafikus elemek
+Name[is]=Kexi skýrslu hlutar
+Name[it]=Oggetti dei rapporti per Kexi
+Name[ja]=Kexi レポートウィジェット
+Name[km]=ធាតុ​ក្រាហ្វិក​របាយការណ៍​​សម្រាប់ Kexi
+Name[lv]=Kexi atskaišu logdaļas
+Name[ms]=Widget Laporan Kexi
+Name[nb]=Skjermelement for Kexi-rapport
+Name[nds]=Bericht-Elementen för Kexi
+Name[ne]=केक्सी प्रतिवेदन विजेटहरू
+Name[nl]=Kexi Rapportwidgets
+Name[nn]=Skjermelement for Kexi-rapport
+Name[pl]=Kontrolki raportów dla Kexi
+Name[pt]=Elementos de Relatório do Kexi
+Name[pt_BR]=Widgets de Relatório do Kexi
+Name[ru]=Элементы управления для отчётов Kexi
+Name[se]=Kexi-raportaáđat
+Name[sk]=Moduly správ Kexi
+Name[sl]=Gradniki za poročila za Kexi
+Name[sr]=Kexi-јеве контроле за извештаје
+Name[sr@Latn]=Kexi-jeve kontrole za izveštaje
+Name[sv]=Kexi-rapportkomponenter
+Name[uk]=Віджети звітів Kexi
+Name[uz]=Kexi hisobot vidjetlari
+Name[uz@cyrillic]=Kexi ҳисобот виджетлари
+Name[zh_CN]=Kexi 报表部件
+Name[zh_TW]=Kexi 報告視窗元件
+
+X-KDE-Library=kformdesigner_kexireportwidgets
+X-KFormDesigner-FactoryGroup=kexi-report
+X-KFormDesigner-WidgetFactoryVersion=2
diff --git a/kexi/plugins/reports/reportwidgets.cpp b/kexi/plugins/reports/reportwidgets.cpp
new file mode 100644
index 00000000..5437325a
--- /dev/null
+++ b/kexi/plugins/reports/reportwidgets.cpp
@@ -0,0 +1,181 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+#include <qpainter.h>
+
+#include <form.h>
+#include <formIO.h>
+#include <formmanager.h>
+#include <kexidb/utils.h>
+#include <kexidb/connection.h>
+#include <kexipart.h>
+
+#include "kexireportview.h"
+#include "reportwidgets.h"
+
+Label::Label(const QString &text, QWidget *parent, const char *name)
+: QLabel(text, parent, name)
+{
+ setPaletteBackgroundColor(white);
+}
+
+////////////////////////////////////////////////////////////////////
+
+ReportLine::ReportLine(QWidget *parent, const char *name)
+: QWidget(parent, name)
+{
+ m_lineStyle = (ReportLineStyle)Qt::SolidLine;
+ m_lineWidth = 1;
+ m_capStyle = (CapStyle)Qt::FlatCap;
+ m_color = paletteForegroundColor();
+ setPaletteBackgroundColor(white);
+}
+
+ReportLine::ReportLineStyle
+ReportLine::lineStyle() const
+{
+ return m_lineStyle;
+}
+
+void
+ReportLine::setLineStyle(ReportLineStyle style)
+{
+ m_lineStyle = style;
+ update();
+}
+
+int
+ReportLine::lineWidth() const
+{
+ return m_lineWidth;
+}
+
+void
+ReportLine::setLineWidth(int width)
+{
+ m_lineWidth = width;
+ update();
+}
+
+QColor
+ReportLine::color() const
+{
+ return m_color;
+}
+
+void
+ReportLine::setColor(const QColor &color)
+{
+ m_color = color;
+ update();
+}
+
+ReportLine::CapStyle
+ReportLine::capStyle() const
+{
+ return m_capStyle;
+}
+
+void
+ReportLine::setCapStyle(CapStyle capStyle)
+{
+ m_capStyle = capStyle;
+ update();
+}
+
+void
+ReportLine::paintEvent (QPaintEvent *ev)
+{
+ QPainter p(this);
+ if(!ev->erased())
+ p.eraseRect(0, 0, width(), height());
+ QPen pen(m_color, m_lineWidth, (Qt::PenStyle)m_lineStyle);
+ pen.setCapStyle((Qt::PenCapStyle)m_capStyle);
+ p.setPen(pen);
+ p.drawLine(0, 0, width() -1, height() - 1);
+}
+
+////////////////////////////////////////////////////////////////////
+
+
+PicLabel::PicLabel(const QPixmap &pix, QWidget *parent, const char *name)
+ : QLabel(parent, name)
+{
+ setPixmap(pix);
+ setScaledContents(false);
+ setPaletteBackgroundColor(white);
+}
+
+bool
+PicLabel::setProperty(const char *name, const QVariant &value)
+{
+ if(QString(name) == "pixmap")
+ resize(value.toPixmap().height(), value.toPixmap().width());
+ return QLabel::setProperty(name, value);
+}
+
+////////////////////////////////////////////////////////////////////
+
+KexiSubReport::KexiSubReport(QWidget *parent, const char *name)
+: QScrollView(parent, name), m_form(0), m_widget(0)
+{
+ setFrameStyle(QFrame::Plain | QFrame::Box);
+ viewport()->setPaletteBackgroundColor(white);
+}
+
+void
+KexiSubReport::setReportName(const QString &name)
+{
+ if(name.isEmpty())
+ return;
+
+ // we need a KexiReportView*
+ QWidget *w = parentWidget();
+ while(w && !w->isA("KexiReportView"))
+ w = w->parentWidget();
+ KexiReportView *view = (KexiReportView*)w;
+ if(!view)
+ return;
+
+ // we check if there is a form with this name
+ int id = KexiDB::idForObjectName(*(view->connection()), name, KexiPart::ReportObjectType);
+ if((id == 0) || (id == view->parentDialog()->id())) // == our form
+ return; // because of recursion when loading
+
+ // we create the container widget
+ delete m_widget;
+ m_widget = new QWidget(viewport(), "kexisubreport_widget");
+ m_widget->show();
+ addChild(m_widget);
+ m_form = new Form(KexiReportPart::library(), this->name());
+ m_form->createToplevel(m_widget);
+
+ // and load the sub form
+ QString data;
+ tristate res = view->connection()->loadDataBlock(id, data , QString::null);
+ if(res != true)
+ return;
+
+ KFormDesigner::FormIO::loadFormFromString(m_form, m_widget, data);
+ m_form->setDesignMode(false);
+
+ m_reportName = name;
+}
+
+#include "reportwidgets.moc"
+
diff --git a/kexi/plugins/reports/reportwidgets.h b/kexi/plugins/reports/reportwidgets.h
new file mode 100644
index 00000000..f8140b0c
--- /dev/null
+++ b/kexi/plugins/reports/reportwidgets.h
@@ -0,0 +1,117 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIREPORTWIDGETS_H
+#define KEXIREPORTWIDGETS_H
+
+#include <qlabel.h>
+#include <qscrollview.h>
+
+namespace KFormDesigner {
+ class Form;
+ class FormManager;
+}
+
+using KFormDesigner::Form;
+
+//! A form embedded as a widget inside other form
+class KexiSubReport : public QScrollView
+{
+ Q_OBJECT
+ Q_PROPERTY(QString reportName READ reportName WRITE setReportName DESIGNABLE true);
+
+ public:
+ KexiSubReport(QWidget *parent, const char *name);
+ ~KexiSubReport() {}
+
+ //! \return the name of the subreport inside the db
+ QString reportName() const { return m_reportName; }
+ void setReportName(const QString &name);
+
+ private:
+// KFormDesigner::FormManager *m_manager;
+ Form *m_form;
+ QWidget *m_widget;
+ QString m_reportName;
+};
+
+//! A simple label inside a report
+class Label : public QLabel
+{
+ Q_OBJECT
+
+ public:
+ Label(const QString &text, QWidget *parent, const char *name);
+ ~Label() {}
+};
+
+//! A simple picture label inside a report
+class PicLabel : public QLabel
+{
+ Q_OBJECT
+
+ public:
+ PicLabel(const QPixmap &pix, QWidget *parent, const char *name);
+ ~PicLabel() {}
+
+ virtual bool setProperty(const char *name, const QVariant &value);
+};
+
+//! A line
+class ReportLine : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(ReportLineStyle lineStyle READ lineStyle WRITE setLineStyle)
+ Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth)
+ Q_PROPERTY(QColor color READ color WRITE setColor)
+ Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle)
+
+ public:
+ enum ReportLineStyle { NoLine = Qt::NoPen, Solid = Qt::SolidLine, Dash = Qt::DashLine, Dot = Qt::DotLine,
+ DashDot = Qt::DashDotLine, DashDotDot = Qt::DashDotDotLine };
+ enum CapStyle { Flat = Qt::FlatCap, Square = Qt::SquareCap, Round = Qt::RoundCap };
+
+ ReportLine(QWidget *parent, const char *name);
+ ~ReportLine(){;}
+
+ ReportLineStyle lineStyle() const;
+ void setLineStyle(ReportLineStyle style);
+
+ int lineWidth() const;
+ void setLineWidth(int width);
+
+ QColor color() const;
+ void setColor(const QColor &color);
+
+ CapStyle capStyle() const;
+ void setCapStyle(CapStyle capStyle);
+
+ protected:
+ virtual void paintEvent (QPaintEvent *ev);
+
+ private:
+ ReportLineStyle m_lineStyle;
+ int m_lineWidth;
+ CapStyle m_capStyle;
+ QColor m_color;
+};
+
+
+#endif
+
diff --git a/kexi/plugins/scripting/Makefile.am b/kexi/plugins/scripting/Makefile.am
new file mode 100644
index 00000000..ccd1bd64
--- /dev/null
+++ b/kexi/plugins/scripting/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = kexiscripting kexiapp kexidb scripts
diff --git a/kexi/plugins/scripting/README b/kexi/plugins/scripting/README
new file mode 100644
index 00000000..e754b9cd
--- /dev/null
+++ b/kexi/plugins/scripting/README
@@ -0,0 +1,28 @@
+Kexi Scripting README
+---------------------
+
+The code in this directory implements a scripting plugin for
+Kexi. The Kross Scripting Framework located at koffice/libs/kross
+is used to embed scripting interpreters and access Kexi
+functionality from within those interpreters.
+
+See also http://www.kexi-project.org/wiki/wikiview/index.php?Scripting
+
+/kexiscripting/
+The scripting-plugin which will be loaded by Kexi at startup to
+embed Kross into Kexi.
+
+/kexiapp/
+Access to a running Kexi application. Kexi itself takes care of
+publishing it's KexiMainWindowImpl instance and the kexiapp-plugin
+provides access to some of the applications functionality at runtime.
+
+/kexidb/
+Kross-plugin to provide nearly the whole KexiDB-framework to scripting
+interpreters. That way we are able to read/write from/to all by KexiDB
+supported databases.
+
+/scripts/
+Kexi-dependend scripts. This directory holds our in python or ruby
+written scripting extensions. Those extensions are just plugins for
+Kexi to extend it's functionality.
diff --git a/kexi/plugins/scripting/kexiapp/Makefile.am b/kexi/plugins/scripting/kexiapp/Makefile.am
new file mode 100644
index 00000000..a2702a26
--- /dev/null
+++ b/kexi/plugins/scripting/kexiapp/Makefile.am
@@ -0,0 +1,21 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+INCLUDES = -I$(top_srcdir)/kexi $(KROSS_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = krosskexiapp.la
+
+krosskexiapp_la_SOURCES = \
+ kexiapppart.cpp \
+ kexiappmainwindow.cpp \
+ kexiappmodule.cpp
+
+krosskexiapp_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module
+krosskexiapp_la_LIBADD = \
+ $(LIB_QT) \
+ $(LIB_KDECORE) \
+ $(LIB_KROSS_API) \
+ $(LIB_KROSS_MAIN) \
+ $(top_builddir)/kexi/core/libkexicore.la
+
+METASOURCES = AUTO
+SUBDIRS = .
diff --git a/kexi/plugins/scripting/kexiapp/kexiappmainwindow.cpp b/kexi/plugins/scripting/kexiapp/kexiappmainwindow.cpp
new file mode 100644
index 00000000..4d82bc5d
--- /dev/null
+++ b/kexi/plugins/scripting/kexiapp/kexiappmainwindow.cpp
@@ -0,0 +1,106 @@
+/***************************************************************************
+ * kexiappmainwindow.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexiappmainwindow.h"
+#include "kexiapppart.h"
+
+#include "core/keximainwindow.h"
+#include "core/kexiproject.h"
+#include "core/kexi.h"
+#include "kexidb/connection.h"
+
+#include "main/manager.h"
+
+//#include <kdebug.h>
+
+namespace Kross { namespace KexiApp {
+
+ /// \internal
+ class KexiAppMainWindowPrivate
+ {
+ public:
+ KexiMainWindow* mainwindow;
+
+ KexiProject* project() {
+ KexiProject* project = mainwindow->project();
+ if(! project)
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception("No project loaded.") );
+ return project;
+ }
+ };
+
+}}
+
+using namespace Kross::KexiApp;
+
+KexiAppMainWindow::KexiAppMainWindow(KexiMainWindow* mainwindow)
+ : Kross::Api::Class<KexiAppMainWindow>("KexiAppMainWindow")
+ , d(new KexiAppMainWindowPrivate())
+{
+ d->mainwindow = mainwindow;
+
+ this->addFunction0<Kross::Api::Variant>("isConnected", this, &KexiAppMainWindow::isConnected);
+ this->addFunction0<Kross::Api::Object>("getConnection", this, &KexiAppMainWindow::getConnection);
+
+ this->addFunction1<Kross::Api::List, Kross::Api::Variant>("getPartItems", this, &KexiAppMainWindow::getPartItems);
+ this->addFunction1<Kross::Api::Variant, KexiAppPartItem>("openPartItem", this, &KexiAppMainWindow::openPartItem);
+}
+
+KexiAppMainWindow::~KexiAppMainWindow()
+{
+ delete d;
+}
+
+const QString KexiAppMainWindow::getClassName() const
+{
+ return "Kross::KexiApp::KexiAppMainWindow";
+}
+
+bool KexiAppMainWindow::isConnected()
+{
+ return d->project()->isConnected();
+}
+
+Kross::Api::Object::Ptr KexiAppMainWindow::getConnection()
+{
+ ::KexiDB::Connection* connection = d->project()->dbConnection();
+ if(! connection)
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception("No connection established.") );
+ Kross::Api::Module::Ptr module = Kross::Api::Manager::scriptManager()->loadModule("krosskexidb");
+ if(! module)
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception("Could not load \"krosskexidb\" module.") );
+ return module->get("KexiDBConnection", connection);
+}
+
+Kross::Api::List* KexiAppMainWindow::getPartItems(const QString& mimetype)
+{
+ if(mimetype.isNull()) return 0; // just to be sure...
+ KexiPart::ItemDict* items = d->project()->itemsForMimeType( mimetype.latin1() );
+ if(! items) return 0;
+ return new Kross::Api::ListT<KexiAppPartItem>( *items );
+}
+
+bool KexiAppMainWindow::openPartItem(KexiAppPartItem* partitem)
+{
+ bool openingCancelled;
+ KexiDialogBase* dialog = partitem
+ ? d->mainwindow->openObject(partitem->item(), Kexi::DataViewMode, openingCancelled)
+ : 0;
+ return (dialog != 0 && ! openingCancelled);
+}
diff --git a/kexi/plugins/scripting/kexiapp/kexiappmainwindow.h b/kexi/plugins/scripting/kexiapp/kexiappmainwindow.h
new file mode 100644
index 00000000..fd02c193
--- /dev/null
+++ b/kexi/plugins/scripting/kexiapp/kexiappmainwindow.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * kexiappmainwindow.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIAPP_KEXIAPPMAINWINDOW_H
+#define KROSS_KEXIAPP_KEXIAPPMAINWINDOW_H
+
+#include <qstring.h>
+#include <qvariant.h>
+
+#include <api/object.h>
+#include <api/variant.h>
+#include <api/list.h>
+#include <api/class.h>
+
+// Forward declarations.
+class KexiMainWindow;
+
+namespace Kross { namespace KexiApp {
+
+ // Forward declarations.
+ class KexiAppPartItem;
+ class KexiAppMainWindowPrivate;
+
+ /**
+ * Class to handle Kexi's mainwindow instance.
+ */
+ class KexiAppMainWindow : public Kross::Api::Class<KexiAppMainWindow>
+ {
+ public:
+
+ /**
+ * Constructor.
+ *
+ * \param mainwindow The \a KexiMainWindow instance
+ * this class provides access to.
+ */
+ KexiAppMainWindow(KexiMainWindow* mainwindow);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiAppMainWindow();
+
+ /// \see Kross::Api::Object::getClassName
+ virtual const QString getClassName() const;
+
+ /** \return true if Kexi is connected with a project else
+ false is returned. */
+ bool isConnected();
+
+ /** \return the \a Kross::KexiDB::KexiDBConnection object that
+ belongs to the opened project or throw an exception if there
+ was no project opened (no connection established). Cause the
+ KexiApp-module doesn't know anything about the KexiDB-module
+ we have to use the base-class \a Kross::Api::Object to pass
+ the \a Kross::KexiDB::KexiDBConnection object around. */
+ Kross::Api::Object::Ptr getConnection();
+
+ /** \return a list of \a KexiAppPartItem objects for the defined
+ \p mimetype string. */
+ Kross::Api::List* getPartItems(const QString& mimetype);
+
+ /** Try to open the defined \a KexiAppPartItem and \return true
+ on success else false. */
+ bool openPartItem(KexiAppPartItem* partitem);
+
+ private:
+ /// Private d-pointer class.
+ KexiAppMainWindowPrivate* d;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexiapp/kexiappmodule.cpp b/kexi/plugins/scripting/kexiapp/kexiappmodule.cpp
new file mode 100644
index 00000000..cb664496
--- /dev/null
+++ b/kexi/plugins/scripting/kexiapp/kexiappmodule.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * kexiappmodule.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexiappmodule.h"
+#include "kexiappmainwindow.h"
+
+#include "core/keximainwindow.h"
+
+#include <api/object.h>
+#include <api/qtobject.h>
+#include <main/manager.h>
+
+#include <kdebug.h>
+
+// The as version() published versionnumber of this kross-module.
+#define KROSS_KEXIAPP_VERSION 1
+
+extern "C"
+{
+ /**
+ * Exported an loadable function as entry point to use
+ * the \a KexiAppModule.
+ */
+ Kross::Api::Object* KDE_EXPORT init_module(Kross::Api::Manager* manager)
+ {
+ return new Kross::KexiApp::KexiAppModule(manager);
+ }
+}
+
+namespace Kross { namespace KexiApp {
+
+ /// \internal
+ class KexiAppModulePrivate
+ {
+ public:
+ Kross::Api::Manager* manager;
+ };
+
+}}
+
+using namespace Kross::KexiApp;
+
+KexiAppModule::KexiAppModule(Kross::Api::Manager* manager)
+ : Kross::Api::Module("KexiApp")
+ , d(new KexiAppModulePrivate())
+{
+ kdDebug() << "Kross::KexiApp::KexiAppModule Ctor" << endl;
+
+ d->manager = manager;
+
+ Kross::Api::Object::Ptr mainwinobject = manager->getChild("KexiMainWindow");
+ if(mainwinobject) {
+ Kross::Api::QtObject* mainwinqtobject = dynamic_cast< Kross::Api::QtObject* >( mainwinobject.data() );
+ if(mainwinqtobject) {
+ ::KexiMainWindow* mainwin = dynamic_cast< ::KexiMainWindow* >( mainwinqtobject->getObject() );
+ if(mainwin) {
+ addChild( "version", new Kross::Api::Variant(KROSS_KEXIAPP_VERSION) );
+ addChild( new KexiAppMainWindow(mainwin) );
+ return;
+ }
+ else kdDebug()<<"Kross::KexiApp::KexiAppModule: Failed to determinate KexiMainWindow instance"<<endl;
+ }
+ else kdDebug()<<"Kross::KexiApp::KexiAppModule: Failed to cast 'KexiMainWindow' to a QtObject"<<endl;
+ }
+ else kdDebug()<<"Kross::KexiApp::KexiAppModule: No such object 'KexiMainWindow'"<<endl;
+
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception("There was no 'KexiMainWindow' published.") );
+}
+
+KexiAppModule::~KexiAppModule()
+{
+ kdDebug() << "Kross::KexiApp::KexiAppModule Dtor" << endl;
+ delete d;
+}
+
+
+const QString KexiAppModule::getClassName() const
+{
+ return "Kross::KexiApp::KexiAppModule";
+}
+
diff --git a/kexi/plugins/scripting/kexiapp/kexiappmodule.h b/kexi/plugins/scripting/kexiapp/kexiappmodule.h
new file mode 100644
index 00000000..08ed71f0
--- /dev/null
+++ b/kexi/plugins/scripting/kexiapp/kexiappmodule.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * kexiappmodule.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIAPP_KEXIAPPMODULE_H
+#define KROSS_KEXIAPP_KEXIAPPMODULE_H
+
+#include <qstring.h>
+#include <qvariant.h>
+
+#include <api/module.h>
+
+namespace Kross { namespace Api {
+ class Manager;
+}}
+
+namespace Kross {
+
+/**
+ * Wrapper around the Kexi-application to access runtime
+ * information a running Kexi-application likes to
+ * provide.
+ */
+namespace KexiApp {
+
+ class KexiAppModulePrivate;
+
+ /**
+ * The Kexi-application module which provides us the
+ * main entrypoint to communicate with a running
+ * Kexi-application.
+ */
+ class KexiAppModule : public Kross::Api::Module
+ {
+ public:
+
+ /**
+ * Constructor.
+ *
+ * \param manager The \a Kross::Api::Manager singleton
+ * instance used to access this module.
+ */
+ KexiAppModule(Kross::Api::Manager* manager);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiAppModule();
+
+ /// \see Kross::Api::Object::getClassName
+ virtual const QString getClassName() const;
+
+ private:
+ /// Private d-pointer class.
+ KexiAppModulePrivate* d;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexiapp/kexiapppart.cpp b/kexi/plugins/scripting/kexiapp/kexiapppart.cpp
new file mode 100644
index 00000000..23d6c2f5
--- /dev/null
+++ b/kexi/plugins/scripting/kexiapp/kexiapppart.cpp
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * kexiapppart.cpp
+ * This file is part of the KDE project
+ * copyright (C)2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexiapppart.h"
+
+#include "core/kexipart.h"
+#include "core/kexipartitem.h"
+//#include "core/kexiproject.h"
+
+using namespace Kross::KexiApp;
+
+KexiAppPartItem::KexiAppPartItem(KexiPart::Item* item)
+ : Kross::Api::Class<KexiAppPartItem>("KexiAppPartItem")
+{
+ this->addFunction0<Kross::Api::Variant>("identifier", item, &::KexiPart::Item::identifier );
+
+ this->addFunction1<void, Kross::Api::Variant>("setIdentifier", item, &::KexiPart::Item::setIdentifier );
+
+ this->addFunction0<Kross::Api::Variant>("mimeType", item, &::KexiPart::Item::mimeType );
+ this->addFunction1<void, Kross::Api::Variant>("setMimeType", item, &::KexiPart::Item::setMimeType );
+
+ this->addFunction0<Kross::Api::Variant>("name", item, &::KexiPart::Item::name );
+ this->addFunction1<void, Kross::Api::Variant>("setName", item, &::KexiPart::Item::setName );
+
+ this->addFunction0<Kross::Api::Variant>("caption", item, &::KexiPart::Item::caption );
+ this->addFunction1<void, Kross::Api::Variant>("setCaption", item, &::KexiPart::Item::setCaption );
+
+ this->addFunction0<Kross::Api::Variant>("description", item, &::KexiPart::Item::description );
+ this->addFunction1<void, Kross::Api::Variant>("setDescription", item, &::KexiPart::Item::setDescription );
+}
diff --git a/kexi/plugins/scripting/kexiapp/kexiapppart.h b/kexi/plugins/scripting/kexiapp/kexiapppart.h
new file mode 100644
index 00000000..5f55d6bf
--- /dev/null
+++ b/kexi/plugins/scripting/kexiapp/kexiapppart.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * kexiapppart.h
+ * This file is part of the KDE project
+ * copyright (C)2006 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIAPP_KEXIAPPPART_H
+#define KROSS_KEXIAPP_KEXIAPPPART_H
+
+#include <qstring.h>
+#include <qvariant.h>
+
+#include <api/object.h>
+#include <api/variant.h>
+#include <api/class.h>
+
+// Forward declarations.
+namespace KexiPart {
+ class Item;
+ class Part;
+}
+
+namespace Kross { namespace KexiApp {
+
+ /**
+ * Class to handle Kexi Part::Item instance.
+ */
+ class KexiAppPartItem : public Kross::Api::Class<KexiAppPartItem>
+ {
+ public:
+ KexiAppPartItem(KexiPart::Item*);
+ virtual ~KexiAppPartItem() {}
+ virtual const QString getClassName() const { return "Kross::KexiApp::KexiAppPartItem"; }
+
+ KexiPart::Item* item() { return m_item; }
+ private:
+ KexiPart::Item* m_item;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb.doxyfile b/kexi/plugins/scripting/kexidb.doxyfile
new file mode 100644
index 00000000..e40a378e
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb.doxyfile
@@ -0,0 +1,324 @@
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+PROJECT_NAME = KrossKexiDB
+PROJECT_NUMBER = 1.0.
+OUTPUT_DIRECTORY = kexidbdocs
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+USE_WINDOWS_ENCODING = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+INLINE_INHERITED_MEMB = NO
+##INLINE_INHERITED_MEMB = YES
+
+FULL_PATH_NAMES = NO
+#STRIP_FROM_PATH = /home/snoopy/
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = YES
+JAVADOC_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+DETAILS_AT_TOP = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+INHERIT_DOCS = YES
+
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+SUBGROUPING = NO
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = NO
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = YES
+HIDE_UNDOC_MEMBERS = YES
+HIDE_UNDOC_CLASSES = YES
+HIDE_FRIEND_COMPOUNDS = YES
+HIDE_IN_BODY_DOCS = YES
+INTERNAL_DOCS = NO
+
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = YES
+SHOW_INCLUDE_FILES = NO
+INLINE_INFO = NO
+SORT_MEMBER_DOCS = NO
+SORT_BRIEF_DOCS = NO
+SORT_BY_SCOPE_NAME = NO
+GENERATE_TODOLIST = NO
+GENERATE_TESTLIST = NO
+GENERATE_BUGLIST = NO
+GENERATE_DEPRECATEDLIST= NO
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = NO
+SHOW_DIRECTORIES = NO
+FILE_VERSION_FILTER =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = NO
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+INPUT = ../kexidb/
+IMAGE_PATH =
+FILE_PATTERNS = *.cpp *.h *.dox
+RECURSIVE = YES
+
+EXCLUDE =
+EXCLUDE_SYMLINKS = YES
+EXCLUDE_PATTERNS = config.h *.moc.cpp
+
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS = *
+EXAMPLE_RECURSIVE = NO
+
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+STRIP_CODE_COMMENTS = YES
+
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION = NO
+VERBATIM_HEADERS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+ALPHABETICAL_INDEX = NO
+COLS_IN_ALPHA_INDEX = 4
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+GENERATE_LATEX = YES
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+COMPACT_LATEX = YES
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+##PDF_HYPERLINKS = NO
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+##LATEX_BATCHMODE = NO
+LATEX_BATCHMODE = YES
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+GENERATE_XML = NO
+XML_OUTPUT = xml
+XML_SCHEMA =
+XML_DTD =
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = NO
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+TAGFILES =
+GENERATE_TAGFILE = krosskexidb.tag
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = NO
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+HAVE_DOT = NO
+
+CLASS_DIAGRAMS = NO
+HIDE_UNDOC_RELATIONS = NO
+CLASS_GRAPH = NO
+COLLABORATION_GRAPH = NO
+GROUP_GRAPHS = NO
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similiar to the OMG's Unified Modeling
+# Language.
+UML_LOOK = NO
+
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = NO
+INCLUDED_BY_GRAPH = NO
+##CALL_GRAPH = YES
+GRAPHICAL_HIERARCHY = NO
+DIRECTORY_GRAPH = NO
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+MAX_DOT_GRAPH_DEPTH = 1000
+DOT_TRANSPARENT = NO
+DOT_MULTI_TARGETS = NO
+GENERATE_LEGEND = NO
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+SEARCHENGINE = NO
diff --git a/kexi/plugins/scripting/kexidb/Makefile.am b/kexi/plugins/scripting/kexidb/Makefile.am
new file mode 100644
index 00000000..12f82501
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/Makefile.am
@@ -0,0 +1,30 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+INCLUDES = -I$(top_srcdir)/kexi $(KROSS_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = krosskexidb.la
+
+krosskexidb_la_SOURCES = \
+ kexidbfield.cpp \
+ kexidbfieldlist.cpp \
+ kexidbschema.cpp \
+ kexidbparser.cpp \
+ kexidbcursor.cpp \
+ kexidbtransaction.cpp \
+ kexidbconnectiondata.cpp \
+ kexidbconnection.cpp \
+ kexidbdriver.cpp \
+ kexidbdrivermanager.cpp \
+ kexidbmodule.cpp
+
+krosskexidb_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module
+krosskexidb_la_LIBADD = \
+ $(LIB_QT) \
+ $(LIB_KDECORE) \
+ $(LIB_KROSS_API) \
+ $(LIB_KROSS_MAIN) \
+ $(top_builddir)/kexi/kexidb/libkexidb.la \
+ $(top_builddir)/kexi/kexidb/parser/libkexidbparser.la
+
+METASOURCES = AUTO
+SUBDIRS = .
diff --git a/kexi/plugins/scripting/kexidb/kexidbconnection.cpp b/kexi/plugins/scripting/kexidb/kexidbconnection.cpp
new file mode 100644
index 00000000..d3b7cc76
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbconnection.cpp
@@ -0,0 +1,221 @@
+/***************************************************************************
+ * kexidbconnection.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexidbconnection.h"
+#include "kexidbconnectiondata.h"
+#include "kexidbdrivermanager.h"
+#include "kexidbdriver.h"
+#include "kexidbcursor.h"
+#include "kexidbfieldlist.h"
+#include "kexidbschema.h"
+#include "kexidbtransaction.h"
+#include "kexidbparser.h"
+
+#include <api/exception.h>
+
+#include <kdebug.h>
+
+#include <kexidb/transaction.h>
+
+using namespace Kross::KexiDB;
+
+KexiDBConnection::KexiDBConnection(::KexiDB::Connection* connection, KexiDBDriver* driver, KexiDBConnectionData* connectiondata)
+ : Kross::Api::Class<KexiDBConnection>("KexiDBConnection")
+ , m_connection(connection)
+ , m_connectiondata(connectiondata ? connectiondata : new KexiDBConnectionData(connection->data()))
+ , m_driver(driver ? driver : new KexiDBDriver(connection->driver()))
+{
+ this->addFunction0< Kross::Api::Variant >("hadError", this, &KexiDBConnection::hadError);
+ this->addFunction0< Kross::Api::Variant >("lastError", this, &KexiDBConnection::lastError);
+
+ this->addFunction0< KexiDBConnectionData >("data", this, &KexiDBConnection::data);
+ this->addFunction0< KexiDBDriver >("driver", this, &KexiDBConnection::driver);
+
+ this->addFunction0< Kross::Api::Variant >("connect", this, &KexiDBConnection::connect);
+ this->addFunction0< Kross::Api::Variant >("isConnected", this, &KexiDBConnection::isConnected);
+ this->addFunction0< Kross::Api::Variant >("disconnect", this, &KexiDBConnection::disconnect);
+
+ this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("databaseExists", this, &KexiDBConnection::databaseExists);
+ this->addFunction0< Kross::Api::Variant >("currentDatabase", this, &KexiDBConnection::currentDatabase);
+ this->addFunction0< Kross::Api::Variant >("databaseNames", this, &KexiDBConnection::databaseNames);
+ this->addFunction0< Kross::Api::Variant >("isDatabaseUsed", this, &KexiDBConnection::isDatabaseUsed);
+ this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("useDatabase", this, &KexiDBConnection::useDatabase);
+ this->addFunction0< Kross::Api::Variant >("closeDatabase", this, &KexiDBConnection::closeDatabase);
+
+ this->addFunction0< Kross::Api::Variant >("tableNames", this, &KexiDBConnection::tableNames);
+ this->addFunction0< Kross::Api::Variant >("queryNames", this, &KexiDBConnection::queryNames);
+
+ this->addFunction1< KexiDBCursor, Kross::Api::Variant >("executeQueryString", this, &KexiDBConnection::executeQueryString);
+ this->addFunction1< KexiDBCursor, KexiDBQuerySchema >("executeQuerySchema", this, &KexiDBConnection::executeQuerySchema);
+
+ addFunction("insertRecord", &KexiDBConnection::insertRecord);
+
+ this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("createDatabase", this, &KexiDBConnection::createDatabase);
+ this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("dropDatabase", this, &KexiDBConnection::dropDatabase);
+
+ this->addFunction1< Kross::Api::Variant, KexiDBTableSchema >("createTable", this, &KexiDBConnection::createTable);
+ this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("dropTable", this, &KexiDBConnection::dropTable);
+ this->addFunction2< Kross::Api::Variant, KexiDBTableSchema, KexiDBTableSchema >("alterTable", this, &KexiDBConnection::alterTable);
+ this->addFunction2< Kross::Api::Variant, KexiDBTableSchema, Kross::Api::Variant >("alterTableName", this, &KexiDBConnection::alterTableName);
+
+ this->addFunction1< KexiDBTableSchema, Kross::Api::Variant >("tableSchema", this, &KexiDBConnection::tableSchema);
+ this->addFunction1< Kross::Api::Variant, KexiDBTableSchema >("isEmptyTable", this, &KexiDBConnection::isEmptyTable);
+ this->addFunction1< KexiDBQuerySchema, Kross::Api::Variant >("querySchema", this, &KexiDBConnection::querySchema);
+
+ this->addFunction0< Kross::Api::Variant >("autoCommit", this, &KexiDBConnection::autoCommit);
+ this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("setAutoCommit", this, &KexiDBConnection::setAutoCommit);
+
+ this->addFunction0< KexiDBTransaction >("beginTransaction", this, &KexiDBConnection::beginTransaction);
+ this->addFunction1< Kross::Api::Variant, KexiDBTransaction >("commitTransaction", this, &KexiDBConnection::commitTransaction);
+ this->addFunction1< Kross::Api::Variant, KexiDBTransaction >("rollbackTransaction", this, &KexiDBConnection::rollbackTransaction);
+ this->addFunction0< KexiDBTransaction >("defaultTransaction", this, &KexiDBConnection::defaultTransaction);
+ this->addFunction1< void, KexiDBTransaction >("setDefaultTransaction", this, &KexiDBConnection::setDefaultTransaction);
+ this->addFunction0<Kross::Api::List>("transactions", this, &KexiDBConnection::transactions);
+
+ this->addFunction0< KexiDBParser >("parser", this, &KexiDBConnection::parser);
+}
+
+KexiDBConnection::~KexiDBConnection() {
+}
+
+const QString KexiDBConnection::getClassName() const {
+ return "Kross::KexiDB::KexiDBConnection";
+}
+
+::KexiDB::Connection* KexiDBConnection::connection() const {
+ if(! m_connection)
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("KexiDB::Connection is NULL.")) );
+ //if(m_connection->error())
+ // throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("KexiDB::Connection error: %1").arg(m_connection->errorMsg())) );
+ return m_connection;
+}
+
+bool KexiDBConnection::hadError() const { return connection()->error(); }
+const QString KexiDBConnection::lastError() const { return connection()->errorMsg(); }
+
+KexiDBConnectionData* KexiDBConnection::data() { return m_connectiondata.data(); }
+KexiDBDriver* KexiDBConnection::driver() { return m_driver.data(); }
+
+bool KexiDBConnection::connect() { return connection()->connect(); }
+bool KexiDBConnection::isConnected() { return connection()->isConnected(); }
+bool KexiDBConnection::disconnect() { return connection()->disconnect(); }
+
+bool KexiDBConnection::isReadOnly() const { return connection()->isReadOnly(); }
+
+bool KexiDBConnection::databaseExists(const QString& dbname) { return connection()->databaseExists(dbname); }
+const QString KexiDBConnection::currentDatabase() const { return connection()->currentDatabase(); }
+const QStringList KexiDBConnection::databaseNames() const { return connection()->databaseNames(); }
+bool KexiDBConnection::isDatabaseUsed() const { return connection()->isDatabaseUsed(); }
+bool KexiDBConnection::useDatabase(const QString& dbname) { return connection()->databaseExists(dbname) && m_connection->useDatabase(dbname); }
+bool KexiDBConnection::closeDatabase() { return connection()->closeDatabase(); }
+
+const QStringList KexiDBConnection::allTableNames() const { return connection()->tableNames(true); }
+const QStringList KexiDBConnection::tableNames() const { return connection()->tableNames(false); }
+
+const QStringList KexiDBConnection::queryNames() const {
+ bool ok = true;
+ QStringList queries = connection()->objectNames(::KexiDB::QueryObjectType, &ok);
+ if(! ok) throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to determinate querynames.")) );
+ return queries;
+}
+
+KexiDBCursor* KexiDBConnection::executeQueryString(const QString& sqlquery) {
+ // The ::KexiDB::Connection::executeQuery() method does not check if we pass a valid SELECT-statement
+ // or e.g. a DROP TABLE operation. So, let's check for such dangerous operations right now.
+ ::KexiDB::Parser parser( connection() );
+ if(! parser.parse(sqlquery))
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to parse query: %1 %2").arg(parser.error().type()).arg(parser.error().error())) );
+ if( parser.query() == 0 || parser.operation() != ::KexiDB::Parser::OP_Select )
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Invalid query operation \"%1\"").arg(parser.operationString()) ) );
+ ::KexiDB::Cursor* cursor = connection()->executeQuery(sqlquery);
+ return cursor ? new KexiDBCursor(cursor) : 0;
+}
+
+KexiDBCursor* KexiDBConnection::executeQuerySchema(KexiDBQuerySchema* queryschema) {
+ ::KexiDB::Cursor* cursor = connection()->executeQuery( *queryschema->queryschema() );
+ return cursor ? new KexiDBCursor(cursor) : 0;
+}
+
+/*TODO
+bool KexiDBConnection::insertRecordIntoFieldlist(KexiDBFieldList* fieldlist, QValueList<QVariant> values) {
+ return connection()->insertRecord(*fieldlist->fieldlist(), values);
+}
+
+bool KexiDBConnection::insertRecordIntoTable(KexiDBTableSchema* tableschema, QValueList<QVariant> values) {
+ return connection()->insertRecord(*tableschema->tableschema(), values);
+}
+*/
+Kross::Api::Object::Ptr KexiDBConnection::insertRecord(Kross::Api::List::Ptr args) {
+ QValueList<QVariant> values = Kross::Api::Variant::toList(args->item(1));
+ Kross::Api::Object::Ptr obj = args->item(0);
+ if(obj->getClassName() == "Kross::KexiDB::KexiDBFieldList")
+ return new Kross::Api::Variant(
+ QVariant(connection()->insertRecord(
+ *Kross::Api::Object::fromObject<KexiDBFieldList>(obj)->fieldlist(),
+ values
+ ), 0));
+ return new Kross::Api::Variant(
+ QVariant(connection()->insertRecord(
+ *Kross::Api::Object::fromObject<KexiDBTableSchema>(obj)->tableschema(),
+ values
+ ), 0));
+}
+
+bool KexiDBConnection::createDatabase(const QString& dbname) { return connection()->createDatabase(dbname); }
+bool KexiDBConnection::dropDatabase(const QString& dbname) { return connection()->dropDatabase(dbname); }
+
+bool KexiDBConnection::createTable(KexiDBTableSchema* tableschema) { return connection()->createTable(tableschema->tableschema(), false); }
+bool KexiDBConnection::dropTable(const QString& tablename) { return true == connection()->dropTable(tablename); }
+bool KexiDBConnection::alterTable(KexiDBTableSchema* fromschema, KexiDBTableSchema* toschema) { return true == connection()->alterTable(*fromschema->tableschema(), *toschema->tableschema()); }
+bool KexiDBConnection::alterTableName(KexiDBTableSchema* tableschema, const QString& newtablename) { return connection()->alterTableName(*tableschema->tableschema(), newtablename); }
+
+KexiDBTableSchema* KexiDBConnection::tableSchema(const QString& tablename) const {
+ ::KexiDB::TableSchema* tableschema = connection()->tableSchema(tablename);
+ return tableschema ? new KexiDBTableSchema(tableschema) : 0;
+}
+
+bool KexiDBConnection::isEmptyTable(KexiDBTableSchema* tableschema) const {
+ bool success;
+ bool notempty = connection()->isEmpty(*tableschema->tableschema(), success);
+ return (! (success && notempty));
+}
+
+KexiDBQuerySchema* KexiDBConnection::querySchema(const QString& queryname) const {
+ ::KexiDB::QuerySchema* queryschema = connection()->querySchema(queryname);
+ return queryschema ? new KexiDBQuerySchema(queryschema) : 0;
+}
+
+bool KexiDBConnection::autoCommit() const { return connection()->autoCommit(); }
+bool KexiDBConnection::setAutoCommit(bool enabled) { return connection()->setAutoCommit(enabled); }
+
+KexiDBTransaction* KexiDBConnection::beginTransaction() {
+ ::KexiDB::Transaction t = connection()->beginTransaction();
+ return new KexiDBTransaction(t);
+}
+
+bool KexiDBConnection::commitTransaction(KexiDBTransaction* transaction) { return connection()->commitTransaction( transaction->transaction() ); }
+bool KexiDBConnection::rollbackTransaction(KexiDBTransaction* transaction) { return connection()->rollbackTransaction( transaction->transaction() ); }
+KexiDBTransaction* KexiDBConnection::defaultTransaction() { return new KexiDBTransaction( connection()->defaultTransaction() ); }
+void KexiDBConnection::setDefaultTransaction(KexiDBTransaction* transaction) { connection()->setDefaultTransaction( transaction->transaction() ); }
+
+Kross::Api::List* KexiDBConnection::transactions() {
+ return new Kross::Api::ListT<KexiDBTransaction>( connection()->transactions() );
+}
+
+KexiDBParser* KexiDBConnection::parser() { return new KexiDBParser(this, new ::KexiDB::Parser(connection())); }
diff --git a/kexi/plugins/scripting/kexidb/kexidbconnection.h b/kexi/plugins/scripting/kexidb/kexidbconnection.h
new file mode 100644
index 00000000..7e1a7d3a
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbconnection.h
@@ -0,0 +1,194 @@
+/***************************************************************************
+ * kexidbconnection.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBCONNECTION_H
+#define KROSS_KEXIDB_KEXIDBCONNECTION_H
+
+#include <qstring.h>
+#include <ksharedptr.h>
+
+#include <api/object.h>
+#include <api/variant.h>
+#include <api/list.h>
+#include <api/class.h>
+
+//#include <kexidb/driver.h>
+#include <kexidb/connection.h>
+
+namespace Kross { namespace KexiDB {
+
+ // Forward declarations.
+ class KexiDBDriver;
+ class KexiDBConnectionData;
+ class KexiDBCursor;
+ class KexiDBTableSchema;
+ class KexiDBQuerySchema;
+ class KexiDBTransaction;
+ class KexiDBParser;
+
+ /**
+ * A connection to a database.
+ *
+ * Example (in Python) ;
+ * @code
+ * # Import the kexidb module.
+ * import krosskexidb
+ * # Get the drivermanager.
+ * drivermanager = krosskexidb.DriverManager()
+ * # We need a connectiondata object.
+ * connectiondata = drivermanager.createConnectionData()
+ * # Fill the new connectiondata object with what we need to connect.
+ * connectiondata.setFileName("/home/user/kexisqlite3file.kexi")
+ * # Create the database-driver to access the SQLite3 backend.
+ * driver = drivermanager.driver("SQLite3")
+ * # Create the connection now.
+ * connection = driver.createConnection(connectiondata)
+ * # Establish the connection.
+ * if not connection.connect(): raise("Failed to connect with db")
+ * # Open database for usage. The filebased driver uses the filename as databasename.
+ * if not connection.useDatabase("/home/user/kexisqlite3file.kexi"): raise("Failed to use db")
+ * @endcode
+ */
+ class KexiDBConnection : public Kross::Api::Class<KexiDBConnection>
+ {
+ public:
+ KexiDBConnection(::KexiDB::Connection* connection, KexiDBDriver* driver = 0, KexiDBConnectionData* connectiondata = 0);
+ virtual ~KexiDBConnection();
+ virtual const QString getClassName() const;
+
+ private:
+
+ /** Return true if there was an error during last operation on the database. */
+ bool hadError() const;
+ /** Return the last errormessage. */
+ const QString lastError() const;
+
+ /** Return the KexiDBConnectionData object used to create this connection. */
+ KexiDBConnectionData* data();
+ /** Return the KexiDBDriver object this connection belongs too. */
+ KexiDBDriver* driver();
+
+ /** Try to connect and return true if we are successfully connected now. */
+ bool connect();
+ /** Return true if we are connected. */
+ bool isConnected();
+ /** Disconnect and return true if we are successfully disconnected now. */
+ bool disconnect();
+
+ /** Returns true if the connection is read-only. */
+ bool isReadOnly() const;
+
+ /** Return true if the as argument passed databasename exists. */
+ bool databaseExists(const QString& dbname);
+ /** Return the name of currently used database for this connection or empty
+ string if there is no used database. */
+ const QString currentDatabase() const;
+ /** Return list of database names for opened connection. */
+ const QStringList databaseNames() const;
+ /** Return true if connection is properly established. */
+ bool isDatabaseUsed() const;
+ /** Opens an existing database specified by the as argument passed databasename
+ and returns true if the database is used now. */
+ bool useDatabase(const QString& dbname);
+ /** Closes currently used database for this connection. */
+ bool closeDatabase();
+
+ /** Return names of all table schemas stored in currently used database include the
+ internal KexiDB system table names (kexi__*) */
+ const QStringList allTableNames() const;
+ /** Return names of all table schemas without the internal KexiDB system table names (kexi__*) */
+ const QStringList tableNames() const;
+ /** Return names of all query schemas stored in currently used database. */
+ const QStringList queryNames() const;
+
+ /** Executes query described by the as argument passed sqlstatement-string. Returns the
+ opened cursor created for results of this query. */
+ KexiDBCursor* executeQueryString(const QString& sqlquery);
+ /** Executes query described by the as argument passed KexiDBQuerySchema object. Returns
+ the opened cursor created for results of this query. */
+ KexiDBCursor* executeQuerySchema(KexiDBQuerySchema* queryschema);
+
+//TODO replace following method with a proxymethod.
+ /** Inserts the as argument passed KexiDBField object. */
+ Kross::Api::Object::Ptr insertRecord(Kross::Api::List::Ptr);
+
+ /** Creates new database with the as argument passed databasename. */
+ bool createDatabase(const QString& dbname);
+ /** Drops the as argument passed databasename. */
+ bool dropDatabase(const QString& dbname);
+
+ /** Creates table defined by the as argument passed KexiTableSchema object. */
+ bool createTable(KexiDBTableSchema* tableschema);
+ /** Drops table defined by the as argument passed KexiDBTableSchema object. */
+ bool dropTable(const QString& tablename);
+ /** Alters the as first argument passed KexiDBTableSchema object using the as
+ second argument passed KexiDBTableSchema. */
+ bool alterTable(KexiDBTableSchema* fromschema, KexiDBTableSchema* toschema);
+ /** Alters the tablename of the as first argument passed KexiDBTableSchema into
+ the as second argument passed new tablename. */
+ bool alterTableName(KexiDBTableSchema* tableschema, const QString& newtablename);
+
+ /** Returns the KexiDBTableSchema object of the table matching to the as argument
+ passed tablename. */
+ KexiDBTableSchema* tableSchema(const QString& tablename) const;
+ /** Returns true if there is at least one valid record in the as argument passed tablename. */
+ bool isEmptyTable(KexiDBTableSchema* tableschema) const;
+ /** Returns the KexiDBQuerySchema object of the query matching to the as argument passed queryname. */
+ KexiDBQuerySchema* querySchema(const QString& queryname) const;
+
+ /** Return true if the \"auto commit\" option is on. */
+ bool autoCommit() const;
+ /** Set the auto commit option. This does not affect currently started transactions and can
+ be changed even when connection is not established. */
+ bool setAutoCommit(bool enabled);
+
+ /** Creates new transaction handle and starts a new transaction. */
+ KexiDBTransaction* beginTransaction();
+ /** Commits the as rgument passed KexiDBTransaction object. */
+ bool commitTransaction(KexiDBTransaction* transaction);
+ /** Rollback the as rgument passed KexiDBTransaction object. */
+ bool rollbackTransaction(KexiDBTransaction* transaction);
+ /** Return the KEXIDBTransaction object for default transaction for this connection. */
+ KexiDBTransaction* defaultTransaction();
+ /** Sets default transaction that will be used as context for operations on data in opened
+ database for this connection. */
+ void setDefaultTransaction(KexiDBTransaction* transaction);
+
+ /** Return list of currently active KexiDBTransaction objects. */
+ Kross::Api::List* transactions();
+
+ /** Return a KexiDBParser object. */
+ KexiDBParser* parser();
+
+ private:
+ ::KexiDB::Connection* connection() const;
+ ::KexiDB::Connection* m_connection;
+
+ KSharedPtr<KexiDBConnectionData> m_connectiondata;
+ KSharedPtr<KexiDBDriver> m_driver;
+
+ /// Initialize the class instance.
+ void initialize();
+
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbconnectiondata.cpp b/kexi/plugins/scripting/kexidb/kexidbconnectiondata.cpp
new file mode 100644
index 00000000..61b81d3e
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbconnectiondata.cpp
@@ -0,0 +1,112 @@
+/***************************************************************************
+ * kexidbconnectiondata.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexidbconnectiondata.h"
+
+#include <qvariant.h>
+
+using namespace Kross::KexiDB;
+
+KexiDBConnectionData::KexiDBConnectionData(::KexiDB::ConnectionData* data)
+ : Kross::Api::Class<KexiDBConnectionData>("KexiDBConnectionData")
+ , m_data(data)
+{
+ this->addFunction0< Kross::Api::Variant >("caption", this, &KexiDBConnectionData::caption);
+ this->addFunction1< void, Kross::Api::Variant >("setCaption", this, &KexiDBConnectionData::setCaption);
+
+ this->addFunction0< Kross::Api::Variant >("description", this, &KexiDBConnectionData::description);
+ this->addFunction1< void, Kross::Api::Variant >("setDescription", this, &KexiDBConnectionData::setDescription);
+
+ this->addFunction0< Kross::Api::Variant >("driverName", this, &KexiDBConnectionData::driverName);
+ this->addFunction1< void, Kross::Api::Variant >("setDriverName", this, &KexiDBConnectionData::setDriverName);
+
+ this->addFunction0< Kross::Api::Variant >("localSocketFileUsed", this, &KexiDBConnectionData::localSocketFileUsed);
+ this->addFunction1< void, Kross::Api::Variant >("setLocalSocketFileUsed", this, &KexiDBConnectionData::setLocalSocketFileUsed);
+
+ this->addFunction0< Kross::Api::Variant >("localSocketFileName", this, &KexiDBConnectionData::localSocketFileName);
+ this->addFunction1< void, Kross::Api::Variant >("setLocalSocketFileName", this, &KexiDBConnectionData::setLocalSocketFileName);
+
+ this->addFunction0< Kross::Api::Variant >("databaseName", this, &KexiDBConnectionData::databaseName);
+ this->addFunction1< void, Kross::Api::Variant >("setDatabaseName", this, &KexiDBConnectionData::setDatabaseName);
+
+ this->addFunction0< Kross::Api::Variant >("hostName", this, &KexiDBConnectionData::hostName);
+ this->addFunction1< void, Kross::Api::Variant >("setHostName", this, &KexiDBConnectionData::setHostName);
+
+ this->addFunction0< Kross::Api::Variant >("port", this, &KexiDBConnectionData::port);
+ this->addFunction1< void, Kross::Api::Variant >("setPort", this, &KexiDBConnectionData::setPort);
+
+ this->addFunction0< Kross::Api::Variant >("password", this, &KexiDBConnectionData::password);
+ this->addFunction1< void, Kross::Api::Variant >("setPassword", this, &KexiDBConnectionData::setPassword);
+
+ this->addFunction0< Kross::Api::Variant >("userName", this, &KexiDBConnectionData::userName);
+ this->addFunction1< void, Kross::Api::Variant >("setUserName", this, &KexiDBConnectionData::setUserName);
+
+ this->addFunction0< Kross::Api::Variant >("fileName", this, &KexiDBConnectionData::fileName);
+ this->addFunction1< void, Kross::Api::Variant >("setFileName", this, &KexiDBConnectionData::setFileName);
+
+ this->addFunction0< Kross::Api::Variant >("dbPath", this, &KexiDBConnectionData::dbPath);
+ this->addFunction0< Kross::Api::Variant >("dbFileName", this, &KexiDBConnectionData::dbFileName);
+ this->addFunction0< Kross::Api::Variant >("serverInfoString", this, &KexiDBConnectionData::serverInfoString);
+}
+
+KexiDBConnectionData::~KexiDBConnectionData()
+{
+ //delete m_data;
+}
+
+const QString KexiDBConnectionData::getClassName() const
+{
+ return "Kross::KexiDB::KexiDBConnectionData";
+}
+
+const QString KexiDBConnectionData::caption() const { return m_data->caption; }
+void KexiDBConnectionData::setCaption(const QString& name) { m_data->caption = name; }
+
+const QString KexiDBConnectionData::description() const { return m_data->description; }
+void KexiDBConnectionData::setDescription(const QString& desc) { m_data->description = desc; }
+
+const QString KexiDBConnectionData::driverName() const { return m_data->driverName; }
+void KexiDBConnectionData::setDriverName(const QString& driver) { m_data->driverName = driver; }
+
+bool KexiDBConnectionData::localSocketFileUsed() const { return m_data->useLocalSocketFile; }
+void KexiDBConnectionData::setLocalSocketFileUsed(bool used) { m_data->useLocalSocketFile = used; }
+const QString KexiDBConnectionData::localSocketFileName() const { return m_data->localSocketFileName; }
+void KexiDBConnectionData::setLocalSocketFileName(const QString& socketfilename) { m_data->localSocketFileName = socketfilename; }
+
+const QString KexiDBConnectionData::databaseName() const { return m_dbname; }
+void KexiDBConnectionData::setDatabaseName(const QString& dbname) { m_dbname = dbname; }
+
+const QString KexiDBConnectionData::hostName() const { return m_data->hostName; }
+void KexiDBConnectionData::setHostName(const QString& hostname) { m_data->hostName = hostname; }
+
+int KexiDBConnectionData::port() const { return m_data->port; }
+void KexiDBConnectionData::setPort(int p) { m_data->port = p; }
+
+const QString KexiDBConnectionData::password() const { return m_data->password; }
+void KexiDBConnectionData::setPassword(const QString& passwd) { m_data->password = passwd; }
+
+const QString KexiDBConnectionData::userName() const { return m_data->userName; }
+void KexiDBConnectionData::setUserName(const QString& username) { m_data->userName = username; }
+
+const QString KexiDBConnectionData::fileName() const { return m_data->fileName(); }
+void KexiDBConnectionData::setFileName(const QString& filename) { m_data->setFileName(filename); }
+
+const QString KexiDBConnectionData::dbPath() const { return m_data->dbPath(); }
+const QString KexiDBConnectionData::dbFileName() const { return m_data->dbFileName(); }
+const QString KexiDBConnectionData::serverInfoString() const { return m_data->serverInfoString(true); }
diff --git a/kexi/plugins/scripting/kexidb/kexidbconnectiondata.h b/kexi/plugins/scripting/kexidb/kexidbconnectiondata.h
new file mode 100644
index 00000000..aaddffbd
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbconnectiondata.h
@@ -0,0 +1,126 @@
+/***************************************************************************
+ * kexidbconnectiondata.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBCONNECTIONDATA_H
+#define KROSS_KEXIDB_KEXIDBCONNECTIONDATA_H
+
+#include <qstring.h>
+
+#include <api/object.h>
+#include <api/variant.h>
+#include <api/list.h>
+#include <api/class.h>
+
+#include <kexidb/connection.h>
+#include <kexidb/connectiondata.h>
+
+namespace Kross { namespace KexiDB {
+
+ /**
+ * A KexiDBConnectionData is used to store the details needed for
+ * a connection with a database.
+ */
+ class KexiDBConnectionData : public Kross::Api::Class<KexiDBConnectionData>
+ {
+ friend class KexiDBDriverManager;
+ public:
+ KexiDBConnectionData(::KexiDB::ConnectionData* data);
+ virtual ~KexiDBConnectionData();
+ operator ::KexiDB::ConnectionData& () { return *m_data; }
+ operator ::KexiDB::ConnectionData* () { return m_data; }
+ virtual const QString getClassName() const;
+ ::KexiDB::ConnectionData* data() { return m_data; }
+
+ private:
+
+ /** Return the connection name. */
+ const QString caption() const;
+ /** Set the connection name. */
+ void setCaption(const QString& name);
+
+ /** Return the description. */
+ const QString description() const;
+ /** Set the description. */
+ void setDescription(const QString& desc);
+
+ /** Return drivername. */
+ const QString driverName() const;
+ /** Set the drivername. */
+ void setDriverName(const QString& driver);
+
+ /** Return true if a local socket file is used else false. */
+ bool localSocketFileUsed() const;
+ /** Set if the local socket file should be used. */
+ void setLocalSocketFileUsed(bool used);
+ /** Return the local socket filename. */
+ const QString localSocketFileName() const;
+ /** Set the local socket filename. */
+ void setLocalSocketFileName(const QString& socketfilename);
+
+ // For serverbased drivers
+
+ /** Return the database name. */
+ const QString databaseName() const;
+ /** Set the database name. */
+ void setDatabaseName(const QString& dbname);
+
+ /** Return the hostname. */
+ const QString hostName() const;
+ /** Set the hostname. */
+ void setHostName(const QString& hostname);
+
+ /** Return the port number. */
+ int port() const;
+ /** Set the port number. */
+ void setPort(int p);
+
+ /** Return the password. */
+ const QString password() const;
+ /** Set the password. */
+ void setPassword(const QString& passwd);
+
+ /** Return the username. */
+ const QString userName() const;
+ /** Set the username. */
+ void setUserName(const QString& username);
+
+ // For filebased drivers
+
+ /** Return the filename. */
+ const QString fileName() const;
+ /** Set the filename. */
+ void setFileName(const QString& filename);
+
+ /** Return the database path. */
+ const QString dbPath() const;
+ /** Return the database filename. */
+ const QString dbFileName() const;
+
+ /** Return a user-friendly string representation. */
+ const QString serverInfoString() const;
+
+ private:
+ ::KexiDB::ConnectionData* m_data;
+ QString m_dbname;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbcursor.cpp b/kexi/plugins/scripting/kexidb/kexidbcursor.cpp
new file mode 100644
index 00000000..3bc1763d
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbcursor.cpp
@@ -0,0 +1,139 @@
+/***************************************************************************
+ * kexidbcursor.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexidbcursor.h"
+#include "kexidbconnection.h"
+
+#include <kexidb/tableschema.h>
+#include <kexidb/queryschema.h>
+
+#include <kdebug.h>
+
+using namespace Kross::KexiDB;
+
+KexiDBCursor::KexiDBCursor(::KexiDB::Cursor* cursor)
+ : Kross::Api::Class<KexiDBCursor>("KexiDBCursor")
+ , m_cursor(cursor)
+{
+ this->addFunction0<Kross::Api::Variant>("open", this, &KexiDBCursor::open );
+ this->addFunction0<Kross::Api::Variant>("isOpened", this, &KexiDBCursor::isOpened );
+ this->addFunction0<Kross::Api::Variant>("reopen", this, &KexiDBCursor::reopen );
+ this->addFunction0<Kross::Api::Variant>("close", this, &KexiDBCursor::close );
+ this->addFunction0<Kross::Api::Variant>("moveFirst", this, &KexiDBCursor::moveFirst );
+ this->addFunction0<Kross::Api::Variant>("moveLast", this, &KexiDBCursor::moveLast );
+ this->addFunction0<Kross::Api::Variant>("movePrev", this, &KexiDBCursor::movePrev );
+ this->addFunction0<Kross::Api::Variant>("moveNext", this, &KexiDBCursor::moveNext );
+ this->addFunction0<Kross::Api::Variant>("bof", this, &KexiDBCursor::bof );
+ this->addFunction0<Kross::Api::Variant>("eof", this, &KexiDBCursor::eof );
+ this->addFunction0<Kross::Api::Variant>("at", this, &KexiDBCursor::at );
+ this->addFunction0<Kross::Api::Variant>("fieldCount", this, &KexiDBCursor::fieldCount );
+ this->addFunction1<Kross::Api::Variant, Kross::Api::Variant>("value", this, &KexiDBCursor::value );
+ this->addFunction2<Kross::Api::Variant, Kross::Api::Variant, Kross::Api::Variant>("setValue", this, &KexiDBCursor::setValue );
+ this->addFunction0<Kross::Api::Variant>("save", this, &KexiDBCursor::save );
+}
+
+KexiDBCursor::~KexiDBCursor()
+{
+ ///@todo check ownership
+ //delete m_cursor;
+
+ clearBuffers();
+}
+
+void KexiDBCursor::clearBuffers()
+{
+ QMap<Q_LLONG, Record*>::ConstIterator
+ it( m_modifiedrecords.constBegin() ), end( m_modifiedrecords.constEnd() );
+ for( ; it != end; ++it)
+ delete it.data();
+ m_modifiedrecords.clear();
+}
+
+const QString KexiDBCursor::getClassName() const
+{
+ return "Kross::KexiDB::KexiDBCursor";
+}
+
+bool KexiDBCursor::open() { return m_cursor->open(); }
+bool KexiDBCursor::isOpened() { return m_cursor->isOpened(); }
+bool KexiDBCursor::reopen() { return m_cursor->reopen(); }
+bool KexiDBCursor::close() { return m_cursor->close(); }
+
+bool KexiDBCursor::moveFirst() { return m_cursor->moveFirst(); }
+bool KexiDBCursor::moveLast() { return m_cursor->moveLast(); }
+bool KexiDBCursor::movePrev() { return m_cursor->movePrev(); }
+bool KexiDBCursor::moveNext() { return m_cursor->moveNext(); }
+
+bool KexiDBCursor::bof() { return m_cursor->bof(); }
+bool KexiDBCursor::eof() { return m_cursor->eof(); }
+
+Q_LLONG KexiDBCursor::at() { return m_cursor->at(); }
+uint KexiDBCursor::fieldCount() { return m_cursor->fieldCount(); }
+
+QVariant KexiDBCursor::value(uint index)
+{
+ return m_cursor->value(index);
+}
+
+bool KexiDBCursor::setValue(uint index, QVariant value)
+{
+ ::KexiDB::QuerySchema* query = m_cursor->query();
+ if(! query) {
+ kdDebug() << "Invalid query in KexiDBCursor::setValue index=" << index << " value=" << value << endl;
+ return false;
+ }
+
+ ::KexiDB::QueryColumnInfo* column = query->fieldsExpanded().at(index);
+ if(! column) {
+ kdDebug() << "Invalid column in KexiDBCursor::setValue index=" << index << " value=" << value << endl;
+ return false;
+ }
+
+ const Q_LLONG position = m_cursor->at();
+ if(! m_modifiedrecords.contains(position))
+ m_modifiedrecords.replace(position, new Record(m_cursor));
+ m_modifiedrecords[position]->buffer->insert(*column, value);
+ return true;
+}
+
+bool KexiDBCursor::save()
+{
+ if(m_modifiedrecords.count() < 1)
+ return true;
+
+ //It is needed to close the cursor before we are able to update the rows
+ //since else the database could be locked (e.g. at the case of SQLite a
+ //KexiDB: Object ERROR: 6: SQLITE_LOCKED would prevent updating).
+ //Maybe it works fine with other drivers like MySQL or Postqre?
+ m_cursor->close();
+
+ bool ok = true;
+ QMap<Q_LLONG, Record*>::ConstIterator
+ it( m_modifiedrecords.constBegin() ), end( m_modifiedrecords.constEnd() );
+ for( ; it != end; ++it) {
+ bool b = m_cursor->updateRow(it.data()->rowdata, * it.data()->buffer, m_cursor->isBuffered());
+ if(ok) {
+ ok = b;
+ //break;
+ }
+ }
+ //m_cursor->close();
+ clearBuffers();
+ return ok;
+}
diff --git a/kexi/plugins/scripting/kexidb/kexidbcursor.h b/kexi/plugins/scripting/kexidb/kexidbcursor.h
new file mode 100644
index 00000000..6e92a38e
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbcursor.h
@@ -0,0 +1,159 @@
+/***************************************************************************
+ * kexidbcursor.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBCURSOR_H
+#define KROSS_KEXIDB_KEXIDBCURSOR_H
+
+#include <qstring.h>
+
+#include <api/object.h>
+#include <api/variant.h>
+#include <api/list.h>
+#include <api/class.h>
+
+#include <kexidb/cursor.h>
+#include <kexidb/roweditbuffer.h>
+
+namespace Kross { namespace KexiDB {
+
+ // Forward declaration.
+ class KexiDBConnection;
+
+ /**
+ * The cursor provides a control structure for the successive traversal
+ * of records in a result set as returned e.g. by a query.
+ *
+ * Example (in Python) that shows how to iterate over the result of a query;
+ * @code
+ * # Once we have a KexiDBConnection object we are able to execute a query string and get a cursor as result.
+ * cursor = connection.executeQueryString("SELECT * from emp")
+ * # Let's check if the query was successfully.
+ * if not cursor: raise("Query failed")
+ * # Walk through all items in the table.
+ * while(not cursor.eof()):
+ * # Iterate over the fields the record has.
+ * for i in range( cursor.fieldCount() ):
+ * # Print some information.
+ * print "%s %s %s" % (cursor.at(), i, cursor.value(i))
+ * # and move on to the next record.
+ * cursor.moveNext()
+ * @endcode
+ *
+ * Example (in Python) that shows how to use a cursor to strip
+ * all whitespaces at the beginning and the end from the values
+ * in a table;
+ * @code
+ * import krosskexidb
+ * drivermanager = krosskexidb.DriverManager()
+ * connectiondata = drivermanager.createConnectionDataByFile("/home/me/kexiprojectfile.kexi")
+ * driver = drivermanager.driver( connectiondata.driverName() )
+ * connection = driver.createConnection(connectiondata)
+ * if not connection.connect(): raise "Failed to connect"
+ * if not connection.useDatabase( connectiondata.databaseName() ):
+ * if not connection.useDatabase( connectiondata.fileName() ):
+ * raise "Failed to use database"
+ *
+ * table = connection.tableSchema("emp")
+ * query = table.query()
+ * cursor = connection.executeQuerySchema(query)
+ * if not cursor: raise("Query failed")
+ * while(not cursor.eof()):
+ * for i in range( cursor.fieldCount() ):
+ * v = str( cursor.value(i) )
+ * if v.startswith(' ') or v.endswith(' '):
+ * cursor.setValue(i, v.strip())
+ * cursor.moveNext()
+ * if not cursor.save(): raise "Failed to save changes"
+ * @endcode
+ */
+ class KexiDBCursor : public Kross::Api::Class<KexiDBCursor>
+ {
+ public:
+ KexiDBCursor(::KexiDB::Cursor* cursor);
+ virtual ~KexiDBCursor();
+ virtual const QString getClassName() const;
+
+ private:
+
+ /** Opens the cursor. */
+ bool open();
+ /** Returns true if the cursor is opened else false. */
+ bool isOpened();
+ /** Closes and then opens again the same cursor. */
+ bool reopen();
+ /** Closes previously opened cursor. */
+ bool close();
+
+ /** Moves current position to the first record and retrieves it. */
+ bool moveFirst();
+ /** Moves current position to the last record and retrieves it. */
+ bool moveLast();
+ /** Moves current position to the previous record and retrieves it. */
+ bool movePrev();
+ /** Moves current position to the next record and retrieves it. */
+ bool moveNext();
+
+ /** Returns true if current position is before first record. */
+ bool bof();
+ /** Returns true if current position is after last record. */
+ bool eof();
+
+ /** Returns current internal position of the cursor's query. Records
+ are numbered from 0; the value -1 means that the cursor does not
+ point to a valid record. */
+ Q_LLONG at();
+ /** Returns the number of fields available for this cursor. */
+ uint fieldCount();
+ /** Returns the value stored in the passed column number (counting from 0). */
+ QVariant value(uint index);
+ /** Set the value for the field defined with index. The new value is buffered
+ and does not got written as long as save() is not called. */
+ bool setValue(uint index, QVariant value);
+
+ /** Save any changes done with setValue(). You should call this only once at
+ the end of all value/setValue iterations cause the cursor is closed once
+ the changes got saved successfully. */
+ bool save();
+
+ private:
+ ::KexiDB::Cursor* m_cursor;
+
+ class Record {
+ public:
+ ::KexiDB::RowData rowdata;
+ ::KexiDB::RowEditBuffer* buffer;
+ Record(::KexiDB::Cursor* cursor)
+ : buffer( new ::KexiDB::RowEditBuffer(true) )
+ {
+ cursor->storeCurrentRow(rowdata);
+ }
+ ~Record()
+ {
+ delete buffer;
+ }
+ };
+ QMap<Q_LLONG, Record*> m_modifiedrecords;
+
+ void clearBuffers();
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbdriver.cpp b/kexi/plugins/scripting/kexidb/kexidbdriver.cpp
new file mode 100644
index 00000000..f019b237
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbdriver.cpp
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * kexidbdriver.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexidbdriver.h"
+#include "kexidbdrivermanager.h"
+
+#include <qvaluelist.h>
+#include <qptrlist.h>
+#include <kdebug.h>
+
+#include <kexidb/connection.h>
+
+using namespace Kross::KexiDB;
+
+KexiDBDriver::KexiDBDriver(::KexiDB::Driver* driver)
+ : Kross::Api::Class<KexiDBDriver>("KexiDBDriver")
+ , m_driver(driver)
+{
+ this->addFunction0<Kross::Api::Variant>("isValid", this, &KexiDBDriver::isValid );
+ this->addFunction0<Kross::Api::Variant>("versionMajor", this, &KexiDBDriver::versionMajor );
+ this->addFunction0<Kross::Api::Variant>("versionMinor", this, &KexiDBDriver::versionMinor );
+ this->addFunction1<Kross::Api::Variant, Kross::Api::Variant>("escapeString", this, &KexiDBDriver::escapeString);
+ this->addFunction0<Kross::Api::Variant>("isFileDriver", this, &KexiDBDriver::isFileDriver );
+ this->addFunction0<Kross::Api::Variant>("fileDBDriverMimeType", this, &KexiDBDriver::fileDBDriverMimeType );
+ this->addFunction1<Kross::Api::Variant, Kross::Api::Variant>("isSystemObjectName", this, &KexiDBDriver::isSystemObjectName );
+ this->addFunction1<Kross::Api::Variant, Kross::Api::Variant>("isSystemDatabaseName", this, &KexiDBDriver::isSystemDatabaseName );
+ this->addFunction1<Kross::Api::Variant, Kross::Api::Variant>("isSystemFieldName", this, &KexiDBDriver::isSystemFieldName );
+ this->addFunction2<Kross::Api::Variant, Kross::Api::Variant, Kross::Api::Variant> ("valueToSQL", this, &KexiDBDriver::valueToSQL );
+
+ this->addFunction1<KexiDBConnection, KexiDBConnectionData>("createConnection", this, &KexiDBDriver::createConnection);
+ this->addFunction0< Kross::Api::ListT< KexiDBConnection > >("connectionsList", this, &KexiDBDriver::connectionsList);
+}
+
+KexiDBDriver::~KexiDBDriver()
+{
+}
+
+const QString KexiDBDriver::getClassName() const
+{
+ return "Kross::KexiDB::KexiDBDriver";
+}
+
+bool KexiDBDriver::isValid() { return m_driver->isValid(); }
+int KexiDBDriver::versionMajor() { return m_driver->version().major; }
+int KexiDBDriver::versionMinor() { return m_driver->version().minor; }
+QString KexiDBDriver::escapeString(const QString& s) { return m_driver->escapeString(s); }
+bool KexiDBDriver::isFileDriver() { return m_driver->isFileDriver(); }
+QString KexiDBDriver::fileDBDriverMimeType() { return m_driver->fileDBDriverMimeType(); }
+bool KexiDBDriver::isSystemObjectName(const QString& name) { return m_driver->isSystemObjectName(name); }
+bool KexiDBDriver::isSystemDatabaseName(const QString& name) { return m_driver->isSystemDatabaseName(name); }
+bool KexiDBDriver::isSystemFieldName(const QString& name) { return m_driver->isSystemFieldName(name); }
+QString KexiDBDriver::valueToSQL(const QString& fieldtype, const QVariant& value) { return m_driver->valueToSQL(fieldtype, value); }
+KexiDBConnection* KexiDBDriver::createConnection(KexiDBConnectionData* data) { return new KexiDBConnection( m_driver->createConnection(*data) ); }
+QPtrList< ::KexiDB::Connection > KexiDBDriver::connectionsList() { return m_driver->connectionsList(); }
diff --git a/kexi/plugins/scripting/kexidb/kexidbdriver.h b/kexi/plugins/scripting/kexidb/kexidbdriver.h
new file mode 100644
index 00000000..edf7283c
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbdriver.h
@@ -0,0 +1,114 @@
+/***************************************************************************
+ * kexidbdriver.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBDRIVER_H
+#define KROSS_KEXIDB_KEXIDBDRIVER_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+//#include <qguardedptr.h>
+
+#include <api/object.h>
+#include <api/variant.h>
+#include <api/list.h>
+#include <api/class.h>
+
+#include <kexidb/driver.h>
+
+#include "kexidbconnection.h"
+#include "kexidbconnectiondata.h"
+
+namespace Kross { namespace KexiDB {
+
+ /**
+ * Drivers are the implementations Kexi uses to access the
+ * driver-backends.
+ *
+ * Example (in Python) ;
+ * @code
+ * # Import the kexidb module.
+ * import krosskexidb
+ * # Get the drivermanager.
+ * drivermanager = krosskexidb.DriverManager()
+ * # Create the driver now.
+ * driver = drivermanager.driver("SQLite3")
+ * # Check if the driver is valid.
+ * if not driver.isValid(): raise "Invalid driver"
+ * # Create a connectiondata object.
+ * connectiondata = drivermanager.createConnectionData()
+ * # Fill the new connectiondata object with what we need to connect.
+ * connectiondata.setFileName("/home/user/kexisqlite3file.kexi")
+ * # Print the list of connections before.
+ * print driver.connectionsList()
+ * # Create the connection now.
+ * connection = driver.createConnection(connectiondata)
+ * # Print the list of connections again. This includes our just created connection now.
+ * print driver.connectionsList()
+ * @endcode
+ */
+ class KexiDBDriver : public Kross::Api::Class<KexiDBDriver>
+ {
+ public:
+ KexiDBDriver(::KexiDB::Driver* driver);
+ virtual ~KexiDBDriver();
+ virtual const QString getClassName() const;
+
+ private:
+
+ /** Return true if this driver is valid else false. */
+ bool isValid();
+ /** The drivers major versionnumber. */
+ int versionMajor();
+ /** The drivers minor versionnumber. */
+ int versionMinor();
+ /** Driver-specific SQL string escaping. For example the " or ' char may
+ need to be escaped for values used within SQL-statements. */
+ QString escapeString(const QString& s);
+ /** Returns true if this driver is file-based. */
+ bool isFileDriver();
+ /** Return a name of MIME type of files handled by this driver if it is a
+ file-based database's driver otherwise returns null string. */
+ QString fileDBDriverMimeType();
+ /** Returns true if the passed string is a system object's name, eg. name
+ of build-in system table that cannot be used or created by a user. */
+ bool isSystemObjectName(const QString& name);
+ /** Returns true if the passed string is a system database's name, eg. name
+ of build-in, system database that cannot be used or created by a user. */
+ bool isSystemDatabaseName(const QString& name);
+ /** Returns true if the passed string is a system field's name, build-in
+ system field that cannot be used or created by a user. */
+ bool isSystemFieldName(const QString& name);
+ /** The as second argument passed string got escaped to be usable within
+ a SQL-statement and those escaped string got returned by the method.
+ The first argument defines the fieldtype to what we should escape the
+ second argument to. */
+ QString valueToSQL(const QString& fieldtype, const QVariant& value);
+ /** Create a new KexiDBConnection object and return it. */
+ KexiDBConnection* createConnection(KexiDBConnectionData* data);
+ /** Return a list of KexiDBConnection objects. */
+ QPtrList< ::KexiDB::Connection > connectionsList();
+
+ private:
+ ::KexiDB::Driver* m_driver;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbdrivermanager.cpp b/kexi/plugins/scripting/kexidb/kexidbdrivermanager.cpp
new file mode 100644
index 00000000..66a0df26
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbdrivermanager.cpp
@@ -0,0 +1,178 @@
+/***************************************************************************
+ * kexidbdrivermanager.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexidbdrivermanager.h"
+#include "kexidbdriver.h"
+#include "kexidbconnectiondata.h"
+#include "kexidbfield.h"
+#include "kexidbschema.h"
+
+#include <api/exception.h>
+
+#include <qguardedptr.h>
+#include <kdebug.h>
+#include <kmimetype.h>
+
+#include <kexidb/driver.h>
+#include <kexidb/connectiondata.h>
+#include <kexidb/field.h>
+#include <kexidb/tableschema.h>
+#include <kexidb/queryschema.h>
+
+using namespace Kross::KexiDB;
+
+KexiDBDriverManager::KexiDBDriverManager()
+ : Kross::Api::Class<KexiDBDriverManager>("DriverManager")
+{
+ //krossdebug( QString("Kross::KexiDB::KexiDBDriverManager::KexiDBDriverManager()") );
+
+ this->addFunction0< Kross::Api::Variant >("driverNames", this, &KexiDBDriverManager::driverNames);
+
+ this->addFunction1< KexiDBDriver, Kross::Api::Variant >("driver", this, &KexiDBDriverManager::driver);
+ this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("lookupByMime", this, &KexiDBDriverManager::lookupByMime);
+ this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("mimeForFile", this, &KexiDBDriverManager::mimeForFile);
+
+ this->addFunction0< KexiDBConnectionData >("createConnectionData", this, &KexiDBDriverManager::createConnectionData);
+ this->addFunction1< KexiDBConnectionData, Kross::Api::Variant >("createConnectionDataByFile", this, &KexiDBDriverManager::createConnectionDataByFile);
+ this->addFunction0< KexiDBField >("field", this, &KexiDBDriverManager::field);
+ this->addFunction1< KexiDBTableSchema, Kross::Api::Variant >("tableSchema", this, &KexiDBDriverManager::tableSchema);
+ this->addFunction0< KexiDBQuerySchema>("querySchema", this, &KexiDBDriverManager::querySchema);
+}
+
+KexiDBDriverManager::~KexiDBDriverManager() {
+ //krossdebug( QString("Kross::KexiDB::KexiDBDriverManager::~KexiDBDriverManager()") );
+}
+
+const QString KexiDBDriverManager::getClassName() const {
+ return "Kross::KexiDB::KexiDBDriverManager";
+}
+
+KexiDB::DriverManager& KexiDBDriverManager::driverManager()
+{
+ if(m_drivermanager.error())
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("KexiDB::DriverManager error: %1").arg(m_drivermanager.errorMsg())) );
+ return m_drivermanager;
+}
+
+const QStringList KexiDBDriverManager::driverNames() {
+ return driverManager().driverNames();
+}
+
+KexiDBDriver* KexiDBDriverManager::driver(const QString& drivername) {
+ QGuardedPtr< ::KexiDB::Driver > driver = driverManager().driver(drivername); // caching is done by the DriverManager
+ if(! driver) return 0;
+ if(driver->error()) throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("KexiDB::Driver error for drivername '%1': %2").arg(drivername).arg(driver->errorMsg())) );
+ return new KexiDBDriver(driver);
+}
+
+const QString KexiDBDriverManager::lookupByMime(const QString& mimetype) {
+ return driverManager().lookupByMime(mimetype);
+}
+
+const QString KexiDBDriverManager::mimeForFile(const QString& filename) {
+ QString mimename = KMimeType::findByFileContent( filename )->name();
+ if(mimename.isEmpty() || mimename=="application/octet-stream" || mimename=="text/plain")
+ mimename = KMimeType::findByURL(filename)->name();
+ return mimename;
+}
+
+KexiDBConnectionData* KexiDBDriverManager::createConnectionData() {
+ return new KexiDBConnectionData( new ::KexiDB::ConnectionData() );
+}
+
+KexiDBConnectionData* KexiDBDriverManager::createConnectionDataByFile(const QString& filename) {
+ //! @todo reuse the original code!
+
+ QString mimename = KMimeType::findByFileContent(filename)->name();
+ if(mimename.isEmpty() || mimename=="application/octet-stream" || mimename=="text/plain")
+ mimename = KMimeType::findByURL(filename)->name();
+
+ if(mimename == "application/x-kexiproject-shortcut" || mimename == "application/x-kexi-connectiondata") {
+ KConfig config(filename, true, false);
+ QString groupkey;
+ QStringList groups(config.groupList());
+ QStringList::ConstIterator it, end( groups.constEnd() );
+ for( it = groups.constBegin(); it != end; ++it) {
+ if((*it).lower()!="file information") {
+ groupkey = *it;
+ break;
+ }
+ }
+ if(groupkey.isNull()) {
+ kdDebug() << "No groupkey in KexiDBDriverManager::createConnectionDataByFile filename=" << filename << endl;
+ return 0;
+ }
+
+ config.setGroup(groupkey);
+ //QString type( config.readEntry("type", "database").lower() );
+ //bool isDatabaseShortcut = (type == "database");
+
+ ::KexiDB::ConnectionData* data = new ::KexiDB::ConnectionData();
+ int version = config.readNumEntry("version", 2); //KexiDBShortcutFile_version
+ data->setFileName(QString::null);
+ data->caption = config.readEntry("caption");
+ data->description = config.readEntry("comment");
+ QString dbname = config.readEntry("name");
+ data->driverName = config.readEntry("engine");
+ data->hostName = config.readEntry("server");
+ data->port = config.readNumEntry("port", 0);
+ data->useLocalSocketFile = config.readBoolEntry("useLocalSocketFile", false);
+ data->localSocketFileName = config.readEntry("localSocketFile");
+
+ if(version >= 2 && config.hasKey("encryptedPassword")) {
+ data->password = config.readEntry("encryptedPassword");
+ uint len = data->password.length();
+ for (uint i=0; i<len; i++)
+ data->password[i] = QChar( data->password[i].unicode() - 47 - i );
+ }
+ if(data->password.isEmpty())
+ data->password = config.readEntry("password");
+
+ data->savePassword = ! data->password.isEmpty();
+ data->userName = config.readEntry("user");
+
+ KexiDBConnectionData* c = new KexiDBConnectionData(data);
+ c->setDatabaseName(dbname);
+ return c;
+ }
+
+ QString const drivername = driverManager().lookupByMime(mimename);
+ if(! drivername) {
+ kdDebug() << "No driver in KexiDBDriverManager::createConnectionDataByFile filename=" << filename << " mimename=" << mimename << endl;
+ return 0;
+ }
+
+ ::KexiDB::ConnectionData* data = new ::KexiDB::ConnectionData();
+ data->setFileName(filename);
+ data->driverName = drivername;
+ return new KexiDBConnectionData(data);
+}
+
+KexiDBField* KexiDBDriverManager::field() {
+ return new KexiDBField( new ::KexiDB::Field() );
+}
+
+KexiDBTableSchema* KexiDBDriverManager::tableSchema(const QString& tablename) {
+ return new KexiDBTableSchema( new ::KexiDB::TableSchema(tablename) );
+}
+
+KexiDBQuerySchema* KexiDBDriverManager::querySchema() {
+ return new KexiDBQuerySchema( new ::KexiDB::QuerySchema() );
+}
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbdrivermanager.h b/kexi/plugins/scripting/kexidb/kexidbdrivermanager.h
new file mode 100644
index 00000000..b6e31108
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbdrivermanager.h
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * kexidbdrivermanager.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBDRIVERMANAGER_H
+#define KROSS_KEXIDB_KEXIDBDRIVERMANAGER_H
+
+#include <qstring.h>
+
+#include <api/object.h>
+#include <api/variant.h>
+#include <api/list.h>
+#include <api/class.h>
+
+#include <kexidb/drivermanager.h>
+
+namespace Kross { namespace KexiDB {
+
+ // Forward declarations.
+ class KexiDBDriver;
+ class KexiDBConnectionData;
+ class KexiDBField;
+ class KexiDBTableSchema;
+ class KexiDBQuerySchema;
+
+ /**
+ * The drivermanager is the base class to access KexiDBDriver objects and provides
+ * common functionality to deal with the KexiDB module.
+ *
+ * Example (in Python) ;
+ * @code
+ * # Import the kexidb module.
+ * import krosskexidb
+ * # Get the drivermanager.
+ * drivermanager = krosskexidb.DriverManager()
+ * # Let's determinate the mimetype (e.g. "application/x-sqlite3").
+ * mimetype = drivermanager.mimeForFile("/home/user/mykexidbfile.kexi")
+ * # Now we use that mimetype to get the name of the driver to handle that file (e.g. "SQLite3")
+ * drivername = drivermanager.lookupByMime(mimetype)
+ * # We are able to create the driver now.
+ * driver = drivermanager.driver(drivername)
+ * @endcode
+ */
+ class KexiDBDriverManager : public Kross::Api::Class<KexiDBDriverManager>
+ {
+ public:
+ KexiDBDriverManager();
+ virtual ~KexiDBDriverManager();
+ virtual const QString getClassName() const;
+
+ private:
+
+ /** Returns a list with avaible drivernames. */
+ const QStringList driverNames();
+
+ /** Return the to the defined drivername matching KexiDBDriver object. */
+ KexiDBDriver* driver(const QString& drivername);
+
+ /** Return the to the defined mimetype-string matching drivername. */
+ const QString lookupByMime(const QString& mimetype);
+
+ /** Return the matching mimetype for the defined file. */
+ const QString mimeForFile(const QString& filename);
+
+ /** Return a new KexiDBConnectionData object. */
+ KexiDBConnectionData* createConnectionData();
+
+ /** Create and return a KexiDBConnectionData object. Fill the content of the
+ KexiDBConnectionData object with the defined file as. The file could be e.g.
+ a *.kexi file or a *.kexis file. */
+ KexiDBConnectionData* createConnectionDataByFile(const QString& filename);
+
+ /** Return a new KexiDBField object. */
+ KexiDBField* field();
+
+ /** Return a new KexiDBTableSchema object. */
+ KexiDBTableSchema* tableSchema(const QString& tablename);
+
+ /** Return a new KexiDBQuerySchema object. */
+ KexiDBQuerySchema* querySchema();
+
+ private:
+ inline ::KexiDB::DriverManager& driverManager();
+ ::KexiDB::DriverManager m_drivermanager;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbfield.cpp b/kexi/plugins/scripting/kexidb/kexidbfield.cpp
new file mode 100644
index 00000000..949b5e1a
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbfield.cpp
@@ -0,0 +1,147 @@
+/***************************************************************************
+ * kexidbfield.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+
+#include "kexidbfield.h"
+
+#include <api/variant.h>
+
+using namespace Kross::KexiDB;
+
+KexiDBField::KexiDBField(::KexiDB::Field* field)
+ : Kross::Api::Class<KexiDBField>("KexiDBField")
+ , m_field(field)
+{
+ this->addFunction0< Kross::Api::Variant >("type", this, &KexiDBField::type);
+ this->addFunction1< void, Kross::Api::Variant >("setType", this, &KexiDBField::setType);
+
+ this->addFunction0< Kross::Api::Variant >("subType", this, &KexiDBField::subType);
+ this->addFunction1< void, Kross::Api::Variant >("setSubType", this, &KexiDBField::setSubType);
+
+ this->addFunction0< Kross::Api::Variant >("variantType", this, &KexiDBField::variantType);
+ this->addFunction0< Kross::Api::Variant >("typeGroup", this, &KexiDBField::typeGroup);
+
+ this->addFunction0< Kross::Api::Variant >("isAutoInc", this, &KexiDBField::isAutoInc);
+ this->addFunction1< void, Kross::Api::Variant >("setAutoInc", this, &KexiDBField::setAutoInc);
+
+ this->addFunction0< Kross::Api::Variant >("isUniqueKey", this, &KexiDBField::isUniqueKey);
+ this->addFunction1< void, Kross::Api::Variant >("setUniqueKey", this, &KexiDBField::setUniqueKey);
+
+ this->addFunction0< Kross::Api::Variant >("isPrimaryKey", this, &KexiDBField::isPrimaryKey);
+ this->addFunction1< void, Kross::Api::Variant >("setPrimaryKey", this, &KexiDBField::setPrimaryKey);
+
+ this->addFunction0< Kross::Api::Variant >("isForeignKey", this, &KexiDBField::isForeignKey);
+ this->addFunction1< void, Kross::Api::Variant >("setForeignKey", this, &KexiDBField::setForeignKey);
+
+ this->addFunction0< Kross::Api::Variant >("isNotNull", this, &KexiDBField::isNotNull);
+ this->addFunction1< void, Kross::Api::Variant >("setNotNull", this, &KexiDBField::setNotNull);
+
+ this->addFunction0< Kross::Api::Variant >("isNotEmpty", this, &KexiDBField::isNotEmpty);
+ this->addFunction1< void, Kross::Api::Variant >("setNotEmpty", this, &KexiDBField::setNotEmpty);
+
+ this->addFunction0< Kross::Api::Variant >("isIndexed", this, &KexiDBField::isIndexed);
+ this->addFunction1< void, Kross::Api::Variant >("setIndexed", this, &KexiDBField::setIndexed);
+
+ this->addFunction0< Kross::Api::Variant >("isUnsigned", this, &KexiDBField::isUnsigned);
+ this->addFunction1< void, Kross::Api::Variant >("setUnsigned", this, &KexiDBField::setUnsigned);
+
+ this->addFunction0< Kross::Api::Variant >("name", this, &KexiDBField::name);
+ this->addFunction1< void, Kross::Api::Variant >("setName", this, &KexiDBField::setName);
+
+ this->addFunction0< Kross::Api::Variant >("caption", this, &KexiDBField::caption);
+ this->addFunction1< void, Kross::Api::Variant >("setCaption", this, &KexiDBField::setCaption);
+
+ this->addFunction0< Kross::Api::Variant >("description", this, &KexiDBField::description);
+ this->addFunction1< void, Kross::Api::Variant >("setDescription", this, &KexiDBField::setDescription);
+
+ this->addFunction0< Kross::Api::Variant >("length", this, &KexiDBField::length);
+ this->addFunction1< void, Kross::Api::Variant >("setLength", this, &KexiDBField::setLength);
+
+ this->addFunction0< Kross::Api::Variant >("precision", this, &KexiDBField::precision);
+ this->addFunction1< void, Kross::Api::Variant >("setPrecision", this, &KexiDBField::setPrecision);
+
+ this->addFunction0< Kross::Api::Variant >("width", this, &KexiDBField::width);
+ this->addFunction1< void, Kross::Api::Variant >("setWidth", this, &KexiDBField::setWidth);
+
+ this->addFunction0< Kross::Api::Variant >("defaultValue", this, &KexiDBField::defaultValue);
+ this->addFunction1< void, Kross::Api::Variant >("setDefaultValue", this, &KexiDBField::setDefaultValue);
+}
+
+KexiDBField::~KexiDBField()
+{
+}
+
+const QString KexiDBField::getClassName() const
+{
+ return "Kross::KexiDB::KexiDBField";
+}
+
+const QString KexiDBField::type() { return m_field->typeString(); }
+void KexiDBField::setType(const QString type) { m_field->setType( ::KexiDB::Field::typeForString(type) ); }
+
+const QString KexiDBField::subType() { return m_field->subType(); }
+void KexiDBField::setSubType(const QString& subtype) { m_field->setSubType(subtype); }
+
+const QString KexiDBField::variantType() { return QVariant::typeToName( m_field->variantType() ); }
+const QString KexiDBField::typeGroup() { return m_field->typeGroupString(); }
+
+bool KexiDBField::isAutoInc() { return m_field->isAutoIncrement(); }
+void KexiDBField::setAutoInc(bool autoinc) { m_field->setAutoIncrement(autoinc); }
+
+bool KexiDBField::isUniqueKey() { return m_field->isUniqueKey(); }
+void KexiDBField::setUniqueKey(bool unique) { m_field->setUniqueKey(unique); }
+
+bool KexiDBField::isPrimaryKey() { return m_field->isPrimaryKey(); }
+void KexiDBField::setPrimaryKey(bool primary) { m_field->setPrimaryKey(primary); }
+
+bool KexiDBField::isForeignKey() { return m_field->isForeignKey(); }
+void KexiDBField::setForeignKey(bool foreign) { m_field->setForeignKey(foreign); }
+
+bool KexiDBField::isNotNull() { return m_field->isNotNull(); }
+void KexiDBField::setNotNull(bool notnull) { m_field->setNotNull(notnull); }
+
+bool KexiDBField::isNotEmpty() { return m_field->isNotEmpty(); }
+void KexiDBField::setNotEmpty(bool notempty) { m_field->setNotEmpty(notempty); }
+
+bool KexiDBField::isIndexed() { return m_field->isIndexed(); }
+void KexiDBField::setIndexed(bool indexed) { m_field->setIndexed(indexed); }
+
+bool KexiDBField::isUnsigned() { return m_field->isUnsigned(); }
+void KexiDBField::setUnsigned(bool isunsigned) { m_field->setUnsigned(isunsigned); }
+
+const QString KexiDBField::name() { return m_field->name(); }
+void KexiDBField::setName(const QString& name) { m_field->setName(name); }
+
+const QString KexiDBField::caption() { return m_field->caption(); }
+void KexiDBField::setCaption(const QString& caption) { m_field->setCaption(caption); }
+
+const QString KexiDBField::description() { return m_field->description(); }
+void KexiDBField::setDescription(const QString& desc) { m_field->setDescription(desc); }
+
+uint KexiDBField::length() { return m_field->length(); }
+void KexiDBField::setLength(uint length) { m_field->setLength(length); }
+
+uint KexiDBField::precision() { return m_field->precision(); }
+void KexiDBField::setPrecision(uint precision) { m_field->setPrecision(precision); }
+
+uint KexiDBField::width() { return m_field->width(); }
+void KexiDBField::setWidth(uint width) { m_field->setWidth(width); }
+
+QVariant KexiDBField::defaultValue() { return m_field->defaultValue(); }
+void KexiDBField::setDefaultValue(const QVariant& defaultvalue) { m_field->setDefaultValue(defaultvalue); }
diff --git a/kexi/plugins/scripting/kexidb/kexidbfield.h b/kexi/plugins/scripting/kexidb/kexidbfield.h
new file mode 100644
index 00000000..a4c2ef23
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbfield.h
@@ -0,0 +1,148 @@
+/***************************************************************************
+ * kexidbfield.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBFIELD_H
+#define KROSS_KEXIDB_KEXIDBFIELD_H
+
+#include <qstring.h>
+
+#include <api/object.h>
+#include <api/list.h>
+#include <api/class.h>
+
+#include <kexidb/drivermanager.h>
+#include <kexidb/field.h>
+
+namespace Kross { namespace KexiDB {
+
+ /**
+ * A field in a record.
+ */
+ class KexiDBField : public Kross::Api::Class<KexiDBField>
+ {
+ public:
+ KexiDBField(::KexiDB::Field* field);
+ virtual ~KexiDBField();
+ virtual const QString getClassName() const;
+ ::KexiDB::Field* field() { return m_field; }
+
+ private:
+
+ /** Returns the type string for this field, e.g. "Integer" for Integer type. */
+ const QString type();
+ /** Sets the type string for this field, e.g. "Integer" for Integer type. */
+ void setType(const QString type);
+
+ /** Returns the optional subtype for this field. Subtype is a string providing
+ additional hint for field's type. E.g. for BLOB type, it can be a MIME type or
+ certain QVariant type name, for example: "QPixmap", "QColor" or "QFont". */
+ const QString subType();
+ /** Sets the optional subtype for this field. */
+ void setSubType(const QString& subtype);
+
+ /** Returns the QVariant::typeName which is equivalent to the type this field has. */
+ const QString variantType();
+ /** Returns type group string for this field, e.g. "IntegerGroup" for IntegerGroup type. */
+ const QString typeGroup();
+
+ /** Returns true if the field is autoincrement (e.g. integer/numeric). */
+ bool isAutoInc();
+ /** Sets auto increment flag. */
+ void setAutoInc(bool autoinc);
+
+ /** Returns true if the field is member of single-field unique key. */
+ bool isUniqueKey();
+ /** Specifies whether the field has single-field unique constraint or not. */
+ void setUniqueKey(bool unique);
+
+ /** Returns true if the field is member of single-field primary key. */
+ bool isPrimaryKey();
+ /** Specifies whether the field is single-field primary key or not. */
+ void setPrimaryKey(bool primary);
+
+ /** Returns true if the field is member of single-field foreign key. */
+ bool isForeignKey();
+ /** Sets whether the field has to be declared with single-field foreign key. */
+ void setForeignKey(bool foreign);
+
+ /** Returns true if the field is not allowed to be null. */
+ bool isNotNull();
+ /** Specifies whether the field has single-field unique constraint or not. */
+ void setNotNull(bool notnull);
+
+ /** Returns true if the field is not allowed to be empty. */
+ bool isNotEmpty();
+ /** Specifies whether the field has single-field unique constraint or not. */
+ void setNotEmpty(bool notempty);
+
+ /** Returns true if the field is indexed using single-field database index. */
+ bool isIndexed();
+ /** Specifies whether the field is indexed or not. */
+ void setIndexed(bool indexed);
+
+ /** Returns true if the field is an unsigned integer. */
+ bool isUnsigned();
+ /** Specifies whether the field is an unsigned integer or not. */
+ void setUnsigned(bool isunsigned);
+
+ /** Returns the name of this field. */
+ const QString name();
+ /** Sets the name of this field. */
+ void setName(const QString& name);
+
+ /** Returns the caption of this field. */
+ const QString caption();
+ /** Sets the caption of this field. */
+ void setCaption(const QString& caption);
+
+ /** Returns the descriptive text for this field. */
+ const QString description();
+ /** Set the description for this field. */
+ void setDescription(const QString& desc);
+
+ /** Returns the length of text if the field type is text. */
+ uint length();
+ /** Sets the length for this field. Only works for Text Type (not including LongText). */
+ void setLength(uint length);
+
+ /** Returns precision for numeric and other fields that have both length and
+ precision (floating point types). */
+ uint precision();
+ /** Sets the precision for numeric and other fields. */
+ void setPrecision(uint precision);
+
+ /** Returns the width of this field (usually in pixels or points).
+ 0 (the default) means there is no hint for the width. */
+ uint width();
+ /** Sets the width of this field. */
+ void setWidth(uint width);
+
+ /** Returns the default value this field has. */
+ QVariant defaultValue();
+ /** Sets the default value this field has. */
+ void setDefaultValue(const QVariant& defaultvalue);
+
+ private:
+ ::KexiDB::Field* m_field;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbfieldlist.cpp b/kexi/plugins/scripting/kexidb/kexidbfieldlist.cpp
new file mode 100644
index 00000000..f36bf0b0
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbfieldlist.cpp
@@ -0,0 +1,100 @@
+/***************************************************************************
+ * kexidbfieldlist.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexidbfieldlist.h"
+#include "kexidbfield.h"
+
+#include <api/variant.h>
+#include <api/exception.h>
+
+#include <kdebug.h>
+
+using namespace Kross::KexiDB;
+
+KexiDBFieldList::KexiDBFieldList(::KexiDB::FieldList* fieldlist)
+ : Kross::Api::Class<KexiDBFieldList>("KexiDBFieldList")
+ , m_fieldlist(fieldlist)
+{
+ this->addFunction0< Kross::Api::Variant >("fieldCount", this, &KexiDBFieldList::fieldCount);
+ this->addFunction1< KexiDBField, Kross::Api::Variant >("field", this, &KexiDBFieldList::field);
+ this->addFunction1< KexiDBField, Kross::Api::Variant >("fieldByName", this, &KexiDBFieldList::fieldByName);
+
+ this->addFunction0< Kross::Api::List >("fields", this, &KexiDBFieldList::fields);
+
+ this->addFunction1< Kross::Api::Variant, KexiDBField >("hasField", this, &KexiDBFieldList::hasField);
+ this->addFunction0< Kross::Api::Variant >("names", this, &KexiDBFieldList::names);
+
+ this->addFunction1< void, KexiDBField >("addField", this, &KexiDBFieldList::addField);
+ this->addFunction2< void, Kross::Api::Variant, KexiDBField >("insertField", this, &KexiDBFieldList::insertField);
+ this->addFunction1< void, KexiDBField >("removeField", this, &KexiDBFieldList::removeField);
+ this->addFunction0< void >("clear", this, &KexiDBFieldList::clear);
+ this->addFunction1< void, KexiDBFieldList >("setFields", this, &KexiDBFieldList::setFields);
+
+ this->addFunction1< KexiDBFieldList, Kross::Api::Variant >("subList", this, &KexiDBFieldList::subList);
+}
+
+KexiDBFieldList::~KexiDBFieldList()
+{
+}
+
+const QString KexiDBFieldList::getClassName() const
+{
+ return "Kross::KexiDB::KexiDBFieldList";
+}
+
+uint KexiDBFieldList::fieldCount() {
+ return m_fieldlist->fieldCount();
+}
+
+KexiDBField* KexiDBFieldList::field(uint index) {
+ ::KexiDB::Field* field = m_fieldlist->field(index);
+ return field ? new KexiDBField(field) : 0;
+}
+
+KexiDBField* KexiDBFieldList::fieldByName(const QString& name) {
+ ::KexiDB::Field* field = m_fieldlist->field(name);
+ return field ? new KexiDBField(field) : 0;
+}
+
+Kross::Api::List* KexiDBFieldList::fields() {
+ return new Kross::Api::ListT<KexiDBField>( *m_fieldlist->fields() );
+}
+
+bool KexiDBFieldList::hasField(KexiDBField* field) { return m_fieldlist->hasField( field->field() ); }
+const QStringList KexiDBFieldList::names() const { return m_fieldlist->names(); }
+void KexiDBFieldList::addField(KexiDBField* field) { m_fieldlist->addField( field->field() ); }
+void KexiDBFieldList::insertField(uint index, KexiDBField* field) { m_fieldlist->insertField(index, field->field()); }
+void KexiDBFieldList::removeField(KexiDBField* field) { m_fieldlist->removeField( field->field() ); }
+void KexiDBFieldList::clear() { m_fieldlist->clear(); }
+
+void KexiDBFieldList::setFields(KexiDBFieldList* fieldlist) {
+ m_fieldlist->clear();
+ ::KexiDB::FieldList* fl = fieldlist->fieldlist();
+ for(::KexiDB::Field::ListIterator it = *fl->fields(); it.current(); ++it)
+ m_fieldlist->addField( it.current() );
+}
+
+KexiDBFieldList* KexiDBFieldList::subList(QValueList<QVariant> list) {
+ QValueList<QVariant>::ConstIterator it( list.constBegin() ), end( list.constEnd() );
+ QStringList sl;
+ for(; it != end; ++it) sl.append( (*it).toString() );
+ ::KexiDB::FieldList* fl = m_fieldlist->subList(sl);
+ return fl ? new Kross::KexiDB::KexiDBFieldList(fl) : 0;
+}
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbfieldlist.h b/kexi/plugins/scripting/kexidb/kexidbfieldlist.h
new file mode 100644
index 00000000..ee990eb3
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbfieldlist.h
@@ -0,0 +1,104 @@
+/***************************************************************************
+ * kexidbfieldlist.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBFIELDLIST_H
+#define KROSS_KEXIDB_KEXIDBFIELDLIST_H
+
+#include <qstring.h>
+
+#include <api/object.h>
+#include <api/list.h>
+#include <api/class.h>
+
+#include <kexidb/drivermanager.h>
+#include <kexidb/fieldlist.h>
+
+namespace Kross { namespace KexiDB {
+
+ // Forward declarations.
+ class KexiDBField;
+ class KexiDBFieldList;
+
+ /**
+ * A list of fields. The KexiDBFieldList can be used to handle KexiDBField objects
+ * in a backend-independent way.
+ *
+ * Example (in Python) ;
+ * @code
+ * # Get the tableschema for the "dept" table.
+ * table = connection.tableSchema("dept")
+ * # Create a KexiDBFieldList based on the table and filled with the selected fields.
+ * subfields = ["deptno","name","loc"]
+ * fieldlist = table.fieldlist().subList(subfields)
+ * # Create the "SELECT * from dept;" queryschema.
+ * query = table.query()
+ * # We change the queryschema to "SELECT deptno,name,loc FROM dept;" now.
+ * query.fieldlist().setFields(fieldlist)
+ * # and change the query to "SELECT deptno,name,loc FROM dept WHERE deptno=5;"
+ * query.setWhereExpression("deptno=5")
+ * # Execute the query and get a KexiDBCursor object as result which could be used to iterate through the result.
+ * cursor = connection.executeQuerySchema(query)
+ * @endcode
+ */
+ class KexiDBFieldList : public Kross::Api::Class<KexiDBFieldList>
+ {
+ public:
+ KexiDBFieldList(::KexiDB::FieldList* fieldlist);
+ virtual ~KexiDBFieldList();
+ virtual const QString getClassName() const;
+ ::KexiDB::FieldList* fieldlist() { return m_fieldlist; }
+
+ private:
+
+ /** Returns the number of fields. */
+ uint fieldCount();
+ /** Return the field specified by the index-number passed as an argument. */
+ KexiDBField* field(uint index);
+ /** Return the field specified by the as an argument passed fieldname. */
+ KexiDBField* fieldByName(const QString& name);
+
+ /** Returns a list of all fields. */
+ Kross::Api::List* fields();
+ /** Returns true if the KexiDBField object passed as an argument is in the field list. */
+ bool hasField(KexiDBField* field);
+ /** Return a list of field names. */
+ const QStringList names() const;
+
+ /** Adds the KexiDBField object passed as an argument to the field list. */
+ void addField(KexiDBField* field);
+ /** Inserts the KexiDBField object passed as the second argument
+ into the field list at the position defined by the first argument. */
+ void insertField(uint index, KexiDBField* field);
+ /** Removes the KexiDBField object passed as an argument from the field list. */
+ void removeField(KexiDBField* field);
+ /** Removes all KexiDBField objects from the fieldlist. */
+ void clear();
+ /** Set the fieldlist to the as argument passed list of fields. */
+ void setFields(KexiDBFieldList* fieldlist);
+ /** Creates and returns list that contain fields selected by name. */
+ KexiDBFieldList* subList(QValueList<QVariant> list);
+
+ private:
+ ::KexiDB::FieldList* m_fieldlist;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbmodule.cpp b/kexi/plugins/scripting/kexidb/kexidbmodule.cpp
new file mode 100644
index 00000000..36f7b71f
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbmodule.cpp
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * kexidbmodule.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#include "kexidbmodule.h"
+#include "kexidbdrivermanager.h"
+#include "kexidbconnection.h"
+
+//#include <api/object.h>
+//#include <api/variant.h>
+#include <main/manager.h>
+
+#include <kdebug.h>
+
+// The as version() published versionnumber of this kross-module.
+#define KROSS_KEXIDB_VERSION 1
+
+extern "C"
+{
+ /**
+ * Exported an loadable function as entry point to use
+ * the \a KexiDBModule.
+ */
+ Kross::Api::Object* KDE_EXPORT init_module(Kross::Api::Manager* manager)
+ {
+ return new Kross::KexiDB::KexiDBModule(manager);
+ }
+}
+
+using namespace Kross::KexiDB;
+
+KexiDBModule::KexiDBModule(Kross::Api::Manager* /*manager*/)
+ : Kross::Api::Module("KexiDB")
+ //, m_manager(manager)
+{
+ //kdDebug() << "Kross::KexiDB::KexiDBModule Ctor" << endl;
+ addChild( "version", new Kross::Api::Variant(KROSS_KEXIDB_VERSION) );
+ addChild( new KexiDBDriverManager() );
+}
+
+KexiDBModule::~KexiDBModule()
+{
+ //kdDebug() << "Kross::KexiDB::KexiDBModule Dtor" << endl;
+}
+
+const QString KexiDBModule::getClassName() const
+{
+ return "Kross::KexiDB::KexiDBModule";
+}
+
+Kross::Api::Object::Ptr KexiDBModule::get(const QString& name, void* p)
+{
+ if(name == "KexiDBConnection") {
+ ::KexiDB::Connection* connection = (::KexiDB::Connection*)p;
+ if(connection)
+ return new KexiDBConnection(connection);
+ }
+ return 0;
+}
diff --git a/kexi/plugins/scripting/kexidb/kexidbmodule.h b/kexi/plugins/scripting/kexidb/kexidbmodule.h
new file mode 100644
index 00000000..b91b6047
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbmodule.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * kexidbmodule.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBMODULE_H
+#define KROSS_KEXIDB_KEXIDBMODULE_H
+
+#include <qstring.h>
+#include <qvariant.h>
+
+#include <api/module.h>
+
+namespace Kross { namespace Api {
+ class Manager;
+}}
+
+namespace Kross {
+
+/**
+ * KrossKexiDB provides access to the KexiDB database functionality.
+ */
+namespace KexiDB {
+
+ /**
+ * \internal
+ * The KexiDBModule is the implementation of a kross-module.
+ */
+ class KexiDBModule : public Kross::Api::Module
+ {
+ public:
+ KexiDBModule(Kross::Api::Manager* manager);
+ virtual ~KexiDBModule();
+ virtual const QString getClassName() const;
+
+ /**
+ * \internal
+ * Variable module-method use to call transparent some functionality
+ * the module provides.
+ *
+ * \param name A name passed to the method. This name is used internaly
+ * to determinate what the caller likes to do. Each implemented
+ * module have to implement what should be done.
+ * \param p A variable pointer passed to the method. It depends on
+ * the module and the name what this pointer is.
+ * \return a \a Kross::Api::Object or NULL.
+ */
+ virtual Kross::Api::Object::Ptr get(const QString& name, void* p = 0);
+
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbparser.cpp b/kexi/plugins/scripting/kexidb/kexidbparser.cpp
new file mode 100644
index 00000000..b022570d
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbparser.cpp
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * kexidbparser.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+
+#include "kexidbparser.h"
+#include "kexidbschema.h"
+#include "kexidbconnection.h"
+
+#include <api/variant.h>
+
+using namespace Kross::KexiDB;
+
+KexiDBParser::KexiDBParser(KexiDBConnection* connection, ::KexiDB::Parser* parser)
+ : Kross::Api::Class<KexiDBParser>("KexiDBParser")
+ , m_connection(connection)
+ , m_parser(parser)
+{
+ this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("parse", this, &KexiDBParser::parse);
+ this->addFunction0< void >("clear", this, &KexiDBParser::clear);
+
+ this->addFunction0< Kross::Api::Variant >("operation", this, &KexiDBParser::operation);
+
+ this->addFunction0< KexiDBTableSchema >("table", this, &KexiDBParser::table);
+ this->addFunction0< KexiDBQuerySchema >("query", this, &KexiDBParser::query);
+ this->addFunction0< KexiDBConnection >("connection", this, &KexiDBParser::connection);
+ this->addFunction0< Kross::Api::Variant >("statement", this, &KexiDBParser::statement);
+
+ this->addFunction0< Kross::Api::Variant >("errorType", this, &KexiDBParser::errorType);
+ this->addFunction0< Kross::Api::Variant >("errorMsg", this, &KexiDBParser::errorMsg);
+ this->addFunction0< Kross::Api::Variant >("errorAt", this, &KexiDBParser::errorAt);
+}
+
+KexiDBParser::~KexiDBParser()
+{
+}
+
+const QString KexiDBParser::getClassName() const
+{
+ return "Kross::KexiDB::KexiDBParser";
+}
+
+bool KexiDBParser::parse(const QString& sql) { return m_parser->parse(sql); }
+void KexiDBParser::clear() { m_parser->clear(); }
+const QString KexiDBParser::operation() { return m_parser->operationString(); }
+
+KexiDBTableSchema* KexiDBParser::table() {
+ ::KexiDB::TableSchema* t = m_parser->table();
+ return t ? new KexiDBTableSchema(t) : 0;
+}
+
+KexiDBQuerySchema* KexiDBParser::query() {
+ ::KexiDB::QuerySchema* q = m_parser->query();
+ return q ? new KexiDBQuerySchema(q) : 0;
+}
+
+KexiDBConnection* KexiDBParser::connection() { return m_connection; }
+const QString KexiDBParser::statement() { return m_parser->statement(); }
+
+const QString KexiDBParser::errorType() { return m_parser->error().type(); }
+const QString KexiDBParser::errorMsg() { return m_parser->error().error(); }
+int KexiDBParser::errorAt() { return m_parser->error().at(); }
diff --git a/kexi/plugins/scripting/kexidb/kexidbparser.h b/kexi/plugins/scripting/kexidb/kexidbparser.h
new file mode 100644
index 00000000..09ac22da
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbparser.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+ * kexidbparser.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBPARSER_H
+#define KROSS_KEXIDB_KEXIDBPARSER_H
+
+#include <qstring.h>
+
+#include <api/object.h>
+#include <api/list.h>
+#include <api/class.h>
+
+#include <kexidb/drivermanager.h>
+#include <kexidb/parser/parser.h>
+
+namespace Kross { namespace KexiDB {
+
+ // Forward declaration.
+ class KexiDBConnection;
+ class KexiDBTableSchema;
+ class KexiDBQuerySchema;
+
+ /**
+ * The KexiDBParser could be used to parse SQL-statements.
+ *
+ * Example (in Python) ;
+ * @code
+ * # First we need a parser object.
+ * parser = connection.parser()
+ * # Parse a SQL-statement.
+ * parser.parse("SELECT * from table1")
+ * # The operation could be e.g. SELECT or INSERT.
+ * if parser.operation() == 'Error':
+ * raise parser.errorMsg()
+ * # Print some feedback.
+ * print "Successfully parsed the SQL-statement %s" % parser.statement()
+ * @endcode
+ */
+ class KexiDBParser : public Kross::Api::Class<KexiDBParser>
+ {
+ public:
+ KexiDBParser(KexiDBConnection* connection, ::KexiDB::Parser* parser);
+ virtual ~KexiDBParser();
+ virtual const QString getClassName() const;
+
+ private:
+
+ /** Clears previous results and runs the parser on the SQL statement passed as an argument. */
+ bool parse(const QString& sql);
+ /** Clears parsing results. */
+ void clear();
+ /** Returns the resulting operation. */
+ const QString operation();
+
+ /** Returns the KexiDBTableSchema object on a CREATE TABLE operation. */
+ KexiDBTableSchema* table();
+ /** Returns the KexiDBQuerySchema object on a SELECT operation. */
+ KexiDBQuerySchema* query();
+ /** Returns the KexiDBConnection object pointing to the used database connection. */
+ KexiDBConnection* connection();
+ /** Returns the SQL query statement. */
+ const QString statement();
+
+ /** Returns the type string of the last error. */
+ const QString errorType();
+ /** Returns the message of the last error. */
+ const QString errorMsg();
+ /** Returns the position where the last error occurred. */
+ int errorAt();
+
+ private:
+ KexiDBConnection* m_connection;
+ ::KexiDB::Parser* m_parser;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbschema.cpp b/kexi/plugins/scripting/kexidb/kexidbschema.cpp
new file mode 100644
index 00000000..e07917f3
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbschema.cpp
@@ -0,0 +1,197 @@
+/***************************************************************************
+ * kexidbschema.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+
+#include "kexidbschema.h"
+#include "kexidbfieldlist.h"
+
+#include <qregexp.h>
+#include <kdebug.h>
+
+#include <api/variant.h>
+
+using namespace Kross::KexiDB;
+
+/***************************************************************************
+ *KexiDBSchema
+ */
+
+template<class T>
+KexiDBSchema<T>::KexiDBSchema(const QString& name, ::KexiDB::SchemaData* schema, ::KexiDB::FieldList* fieldlist)
+ : Kross::Api::Class<T>(name)
+ , m_schema(schema)
+ , m_fieldlist(fieldlist)
+{
+ this->template addFunction0<Kross::Api::Variant>("name", this, &KexiDBSchema<T>::name);
+ this->template addFunction1<void, Kross::Api::Variant>("setName", this, &KexiDBSchema<T>::setName);
+
+ this->template addFunction0<Kross::Api::Variant>("caption", this, &KexiDBSchema<T>::caption);
+ this->template addFunction1<void, Kross::Api::Variant>("setCaption", this, &KexiDBSchema<T>::setCaption);
+
+ this->template addFunction0<Kross::Api::Variant>("description", this, &KexiDBSchema<T>::description);
+ this->template addFunction1<void, Kross::Api::Variant>("setDescription", this, &KexiDBSchema<T>::setDescription);
+
+ this->template addFunction0<KexiDBFieldList>("fieldlist", this, &KexiDBSchema<T>::fieldlist);
+}
+
+template<class T>
+KexiDBSchema<T>::~KexiDBSchema<T>() {
+}
+
+template<class T>
+const QString KexiDBSchema<T>::name() const {
+ return m_schema->name();
+}
+
+template<class T>
+void KexiDBSchema<T>::setName(const QString& name) {
+ m_schema->setName(name);
+}
+
+template<class T>
+const QString KexiDBSchema<T>::caption() const {
+ return m_schema->caption();
+}
+
+template<class T>
+void KexiDBSchema<T>::setCaption(const QString& caption) {
+ m_schema->setCaption(caption);
+}
+
+template<class T>
+const QString KexiDBSchema<T>::description() const {
+ return m_schema->description();
+}
+
+template<class T>
+void KexiDBSchema<T>::setDescription(const QString& description) {
+ m_schema->setDescription(description);
+}
+
+template<class T>
+KexiDBFieldList* KexiDBSchema<T>::fieldlist() const {
+ return new KexiDBFieldList(m_fieldlist);
+}
+
+/***************************************************************************
+ * KexiDBTableSchema
+ */
+
+KexiDBTableSchema::KexiDBTableSchema(::KexiDB::TableSchema* tableschema)
+ : KexiDBSchema<KexiDBTableSchema>("KexiDBTableSchema", tableschema, tableschema)
+{
+ this->addFunction0<KexiDBQuerySchema>("query", this, &KexiDBTableSchema::query);
+}
+
+KexiDBTableSchema::~KexiDBTableSchema() {
+}
+
+const QString KexiDBTableSchema::getClassName() const {
+ return "Kross::KexiDB::KexiDBTableSchema";
+}
+
+::KexiDB::TableSchema* KexiDBTableSchema::tableschema() {
+ return static_cast< ::KexiDB::TableSchema* >(m_schema);
+}
+
+KexiDBQuerySchema* KexiDBTableSchema::query() {
+ return new KexiDBQuerySchema( tableschema()->query() );
+}
+
+/***************************************************************************
+ * KexiDBQuerySchema
+ */
+
+KexiDBQuerySchema::KexiDBQuerySchema(::KexiDB::QuerySchema* queryschema)
+ : KexiDBSchema<KexiDBQuerySchema>("KexiDBQuerySchema", queryschema, queryschema)
+{
+ this->addFunction0<Kross::Api::Variant>("statement", this, &KexiDBQuerySchema::statement);
+ this->addFunction1<void, Kross::Api::Variant>("setStatement", this, &KexiDBQuerySchema::setStatement);
+ this->addFunction1<Kross::Api::Variant, Kross::Api::Variant>("setWhereExpression", this, &KexiDBQuerySchema::setWhereExpression);
+}
+
+KexiDBQuerySchema::~KexiDBQuerySchema() {
+}
+
+const QString KexiDBQuerySchema::getClassName() const {
+ return "Kross::KexiDB::KexiDBQuerySchema";
+}
+
+::KexiDB::QuerySchema* KexiDBQuerySchema::queryschema() {
+ return static_cast< ::KexiDB::QuerySchema* >(m_schema);
+}
+
+const QString KexiDBQuerySchema::statement() const {
+ return static_cast< ::KexiDB::QuerySchema* >(m_schema)->statement();
+}
+
+void KexiDBQuerySchema::setStatement(const QString& statement) {
+ static_cast< ::KexiDB::QuerySchema* >(m_schema)->setStatement(statement);
+}
+
+bool KexiDBQuerySchema::setWhereExpression(const QString& whereexpression) {
+ ::KexiDB::BaseExpr* oldexpr = static_cast< ::KexiDB::QuerySchema* >(m_schema)->whereExpression();
+
+ ///@todo use ::KexiDB::Parser for such kind of parser-functionality.
+ QString s = whereexpression;
+ try {
+ QRegExp re("[\"',]{1,1}");
+ while(true) {
+ s.remove(QRegExp("^[\\s,]+"));
+ int pos = s.find('=');
+ if(pos < 0) break;
+ QString key = s.left(pos).stripWhiteSpace();
+ s = s.mid(pos + 1).stripWhiteSpace();
+
+ QString value;
+ int sp = s.find(re);
+ if(sp >= 0) {
+ if(re.cap(0) == ",") {
+ value = s.left(sp).stripWhiteSpace();
+ s = s.mid(sp+1).stripWhiteSpace();
+ }
+ else {
+ int ep = s.find(re.cap(0),sp+1);
+ value = s.mid(sp+1,ep-1);
+ s = s.mid(ep + 1);
+ }
+ }
+ else {
+ value = s;
+ s = QString::null;
+ }
+
+ ::KexiDB::Field* field = static_cast< ::KexiDB::QuerySchema* >(m_schema)->field(key);
+ if(! field)
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Invalid WHERE-expression: Field \"%1\" does not exists in tableschema \"%2\".").arg(key).arg(m_schema->name())) );
+
+ QVariant v(value);
+ if(! v.cast(field->variantType()))
+ throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Invalid WHERE-expression: The for Field \"%1\" defined value is of type \"%2\" rather then the expected type \"%3\"").arg(key).arg(v.typeName()).arg(field->variantType())) );
+
+ static_cast< ::KexiDB::QuerySchema* >(m_schema)->addToWhereExpression(field,v);
+ }
+ }
+ catch(Kross::Api::Exception::Ptr e) {
+ Kross::krosswarning("Exception in Kross::KexiDB::KexiDBQuerySchema::setWhereExpression: ");
+ static_cast< ::KexiDB::QuerySchema* >(m_schema)->setWhereExpression(oldexpr); // fallback
+ return false;
+ }
+ return true;
+}
diff --git a/kexi/plugins/scripting/kexidb/kexidbschema.h b/kexi/plugins/scripting/kexidb/kexidbschema.h
new file mode 100644
index 00000000..61b6bc88
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbschema.h
@@ -0,0 +1,134 @@
+/***************************************************************************
+ * kexidbschema.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBSCHEMA_H
+#define KROSS_KEXIDB_KEXIDBSCHEMA_H
+
+#include <qstring.h>
+
+#include <api/object.h>
+#include <api/class.h>
+
+#include <kexidb/drivermanager.h>
+#include <kexidb/schemadata.h>
+#include <kexidb/tableschema.h>
+#include <kexidb/queryschema.h>
+
+namespace Kross { namespace KexiDB {
+
+ // Forward-declarations.
+ class KexiDBFieldList;
+ class KexiDBQuerySchema;
+
+ /**
+ * The KexiDBSchema object provides common functionality for schemas
+ * like KexiDBTableSchema or KexiDBQuerySchema.
+ *
+ * Example (in Python) ;
+ * @code
+ * # Get the tableschema from a KexiDBConnection object.
+ * tableschema = connection.tableSchema("dept")
+ * # Print some information.
+ * print "table=%s description=%s" % (tableschema.name(), tableschema.description())
+ * # Get the "SELECT * FROM dept;" queryschema for the table.
+ * queryschema = tableschema.query()
+ * # Walk through the fields/columns the queryschema has and print the fieldnames.
+ * for field in queryschema.fieldlist().fields():
+ * print "fieldname=%s" % field.name()
+ * # Execute the query. The returned KexiDBCursor object could be used then to iterate through the result.
+ * cursor = connection.executeQuerySchema(queryschema)
+ * @endcode
+ */
+ template<class T>
+ class KexiDBSchema : public Kross::Api::Class<T>
+ {
+ public:
+ KexiDBSchema(const QString& name, ::KexiDB::SchemaData* schema, ::KexiDB::FieldList* fieldlist);
+ virtual ~KexiDBSchema();
+
+ private:
+
+ /** Returns the name of the schema. */
+ const QString name() const;
+ /** Set the name of the schema. */
+ void setName(const QString& name);
+
+ /** Returns the caption of the schema. */
+ const QString caption() const;
+ /** Set the caption of the schema. */
+ void setCaption(const QString& caption);
+
+ /** Returns a description of the schema. */
+ const QString description() const;
+ /** Set a description of the schema. */
+ void setDescription(const QString& description);
+
+ /** Returns the KexiDBFieldList object this schema has. */
+ KexiDBFieldList* fieldlist() const;
+
+ protected:
+ ::KexiDB::SchemaData* m_schema;
+ ::KexiDB::FieldList* m_fieldlist;
+ };
+
+ /**
+ * The KexiDBTableSchema object implements a KexiDBSchema for tables.
+ */
+ class KexiDBTableSchema : public KexiDBSchema<KexiDBTableSchema>
+ {
+ public:
+ KexiDBTableSchema(::KexiDB::TableSchema* tableschema);
+ virtual ~KexiDBTableSchema();
+ virtual const QString getClassName() const;
+ ::KexiDB::TableSchema* tableschema();
+
+ private:
+
+ /** Return the KexiDBQuerySchema object that represents a
+ "SELECT * FROM this_KexiDBTableSchema_object" SQL-statement. */
+ KexiDBQuerySchema* query();
+
+ };
+
+ /**
+ * The KexiDBTableSchema object implements a KexiDBSchema for queries.
+ */
+ class KexiDBQuerySchema : public KexiDBSchema<KexiDBQuerySchema>
+ {
+ public:
+ KexiDBQuerySchema(::KexiDB::QuerySchema* queryschema);
+ virtual ~KexiDBQuerySchema();
+ virtual const QString getClassName() const;
+ ::KexiDB::QuerySchema* queryschema();
+
+ private:
+
+ /** Returns the SQL-statement of this query schema. */
+ const QString statement() const;
+ /** Set the SQL-statement of this query schema. */
+ void setStatement(const QString& statement);
+ /** Set the where-expression. */
+ bool setWhereExpression(const QString& whereexpression);
+
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/kexidbtransaction.cpp b/kexi/plugins/scripting/kexidb/kexidbtransaction.cpp
new file mode 100644
index 00000000..d4cdff24
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbtransaction.cpp
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * kexidbtransaction.cpp
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+
+#include "kexidbtransaction.h"
+#include "kexidbconnection.h"
+#include <api/variant.h>
+
+//#include <kdebug.h>
+
+using namespace Kross::KexiDB;
+
+KexiDBTransaction::KexiDBTransaction(::KexiDB::Transaction& transaction)
+ : Kross::Api::Class<KexiDBTransaction>("KexiDBTransaction")
+ , m_transaction(transaction)
+{
+ this->addFunction0< Kross::Api::Variant >("isActive", this, &KexiDBTransaction::isActive);
+ this->addFunction0< Kross::Api::Variant >("isNull", this, &KexiDBTransaction::isNull);
+}
+
+KexiDBTransaction::~KexiDBTransaction()
+{
+}
+
+const QString KexiDBTransaction::getClassName() const
+{
+ return "Kross::KexiDB::KexiDBTransaction";
+}
+
+::KexiDB::Transaction& KexiDBTransaction::transaction()
+{
+ return m_transaction;
+}
+
+bool KexiDBTransaction::isActive() const { return m_transaction.active(); }
+bool KexiDBTransaction::isNull() const { return m_transaction.isNull(); }
diff --git a/kexi/plugins/scripting/kexidb/kexidbtransaction.h b/kexi/plugins/scripting/kexidb/kexidbtransaction.h
new file mode 100644
index 00000000..6a6b5785
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/kexidbtransaction.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * kexidbtransaction.h
+ * This file is part of the KDE project
+ * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ ***************************************************************************/
+
+#ifndef KROSS_KEXIDB_KEXIDBTRANSACTION_H
+#define KROSS_KEXIDB_KEXIDBTRANSACTION_H
+
+#include <qstring.h>
+
+#include <api/class.h>
+
+#include <kexidb/drivermanager.h>
+#include <kexidb/transaction.h>
+
+namespace Kross { namespace KexiDB {
+
+ // Forward declaration.
+ class KexiDBConnection;
+
+ /**
+ * Transactions are used to ensure that integrity of a database is
+ * maintained.
+ */
+ class KexiDBTransaction : public Kross::Api::Class<KexiDBTransaction>
+ {
+ public:
+ KexiDBTransaction(::KexiDB::Transaction& transaction);
+ virtual ~KexiDBTransaction();
+ virtual const QString getClassName() const;
+ ::KexiDB::Transaction& transaction();
+
+ private:
+
+ /** Return true if the transaction is active (ie. started). */
+ bool isActive() const;
+
+ /** Return true if the transaction is uninitialized (null). */
+ bool isNull() const;
+
+ private:
+ ::KexiDB::Transaction& m_transaction;
+ };
+
+}}
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexidb/readme.dox b/kexi/plugins/scripting/kexidb/readme.dox
new file mode 100644
index 00000000..c4e33a5b
--- /dev/null
+++ b/kexi/plugins/scripting/kexidb/readme.dox
@@ -0,0 +1,32 @@
+/** @mainpage KrossKexiDB
+ *
+ * The Kross KexiDB module provides a scripting bridge to the
+ * KexiDB library. KexiDB is the database abstraction layer used
+ * within Kexi to deal with all supported database-backends like
+ * SQLite, MySQL and Postqre.
+ *
+ * The @a KexiDBDriverManager is the manager module which provides
+ * the entry point to access the KexiDB functionality from
+ * scripting languages like Python and Ruby.
+ *
+ * @see http://www.kexi-project.org/scripting/
+ * @see http://kross.dipe.org
+ * @see http://www.kexi-project.org/wiki/wikiview/index.php?Scripting
+ *
+ * @section Legal
+ *
+ * @li copyright (C) 2004-2006 by Sebastian Sauer (mail AT dipe DOT org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
diff --git a/kexi/plugins/scripting/kexiscripting/Makefile.am b/kexi/plugins/scripting/kexiscripting/Makefile.am
new file mode 100644
index 00000000..ed3e2264
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/Makefile.am
@@ -0,0 +1,37 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_script.la
+
+kexihandler_script_la_SOURCES = \
+ kexiscriptpart.cpp kexiscripteditor.cpp kexiscriptdesignview.cpp
+
+kexihandler_script_la_LDFLAGS = \
+ $(KDE_PLUGIN) -module -no-undefined -Wnounresolved $(all_libraries) $(VER_INFO)
+
+kexihandler_script_la_LIBADD = \
+ $(top_builddir)/lib/kross/main/libkrossmain.la \
+ $(top_builddir)/kexi/core/libkexicore.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/lib/koproperty/libkoproperty.la
+
+INCLUDES = \
+ $(KOFFICE_INCLUDES) \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/kexi/core \
+ -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget \
+ $(all_includes)
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=kexiscripthandler.desktop
+
+rcdir = $(kde_datadir)/kexi
+rc_DATA = kexiscriptpartui.rc kexiscriptpartinstui.rc
+
+METASOURCES = AUTO
+
+SUBDIRS = .
+
+include ../../Makefile.common
+
+noinst_HEADERS = kexiscriptpart.h kexiscripteditor.h kexiscriptdesignview.h
diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.cpp b/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.cpp
new file mode 100644
index 00000000..ff2f93d0
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.cpp
@@ -0,0 +1,337 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Sebastian Sauer <mail@dipe.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiscriptdesignview.h"
+#include "kexiscripteditor.h"
+
+#include <kross/main/manager.h>
+#include <kross/main/scriptcontainer.h>
+#include <kross/main/scriptaction.h>
+#include <kross/api/interpreter.h>
+
+#include <qlayout.h>
+#include <qsplitter.h>
+#include <qtimer.h>
+#include <qdatetime.h>
+#include <qdom.h>
+#include <qstylesheet.h>
+#include <ktextbrowser.h>
+#include <kdebug.h>
+
+#include <kexidialogbase.h>
+#include <kexidb/connection.h>
+
+/// @internal
+class KexiScriptDesignViewPrivate
+{
+ public:
+
+ /**
+ * The \a Kross::Api::ScriptAction instance which provides
+ * us access to the scripting framework Kross.
+ */
+ Kross::Api::ScriptAction* scriptaction;
+
+ /// The \a KexiScriptEditor to edit the scripting code.
+ KexiScriptEditor* editor;
+
+ /// The \a KoProperty::Set used in the propertyeditor.
+ KoProperty::Set* properties;
+
+ /// Boolean flag to avoid infinite recursion.
+ bool updatesProperties;
+
+ /// Used to display statusmessages.
+ KTextBrowser* statusbrowser;
+};
+
+KexiScriptDesignView::KexiScriptDesignView(KexiMainWindow *mainWin, QWidget *parent, Kross::Api::ScriptAction* scriptaction)
+ : KexiViewBase(mainWin, parent, "KexiScriptDesignView")
+ , d( new KexiScriptDesignViewPrivate() )
+{
+ d->scriptaction = scriptaction;
+ d->updatesProperties = false;
+
+ QSplitter* splitter = new QSplitter(this);
+ splitter->setOrientation(Vertical);
+ QHBoxLayout* layout = new QHBoxLayout(this);
+ layout->addWidget(splitter);
+
+ d->editor = new KexiScriptEditor(mainWin, splitter, "ScriptEditor");
+ splitter->setFocusProxy(d->editor);
+ addChildView(d->editor);
+ setViewWidget(d->editor);
+
+ d->statusbrowser = new KTextBrowser(splitter, "ScriptStatusBrowser");
+ d->statusbrowser->setReadOnly(true);
+ d->statusbrowser->setTextFormat(QTextBrowser::RichText);
+ //d->browser->setWordWrap(QTextEdit::WidgetWidth);
+ d->statusbrowser->installEventFilter(this);
+ splitter->setResizeMode(d->statusbrowser, QSplitter::KeepSize);
+
+ plugSharedAction( "data_execute", this, SLOT(execute()) );
+ if(KexiEditor::isAdvancedEditor()) // the configeditor is only in advanced mode avaiable.
+ plugSharedAction( "script_config_editor", d->editor, SLOT(slotConfigureEditor()) );
+
+ loadData();
+
+ d->properties = new KoProperty::Set(this, "KexiScripting");
+ connect(d->properties, SIGNAL( propertyChanged(KoProperty::Set&, KoProperty::Property&) ),
+ this, SLOT( slotPropertyChanged(KoProperty::Set&, KoProperty::Property&) ));
+
+ // To schedule the initialize fixes a crasher in Kate.
+ QTimer::singleShot(50, this, SLOT( initialize() ));
+}
+
+KexiScriptDesignView::~KexiScriptDesignView()
+{
+ delete d->properties;
+ delete d;
+}
+
+Kross::Api::ScriptAction* KexiScriptDesignView::scriptAction() const
+{
+ return d->scriptaction;
+}
+
+void KexiScriptDesignView::initialize()
+{
+ updateProperties();
+ d->editor->initialize( d->scriptaction );
+}
+
+void KexiScriptDesignView::updateProperties()
+{
+ if(d->updatesProperties)
+ return;
+ d->updatesProperties = true;
+
+ Kross::Api::Manager* manager = Kross::Api::Manager::scriptManager();
+
+ QString interpretername = d->scriptaction->getInterpreterName();
+ Kross::Api::InterpreterInfo* info = interpretername.isEmpty() ? 0 : manager->getInterpreterInfo(interpretername);
+
+ {
+ // if interpreter isn't defined or invalid, try to fallback.
+ QStringList list;
+ list << "python" << "ruby";
+ QStringList::ConstIterator it( list.constBegin() ), end( list.constEnd() );
+ while( (! info) && (it != end) ) {
+ interpretername = (*it);
+ info = manager->getInterpreterInfo(interpretername);
+ if(info)
+ d->scriptaction->setInterpreterName(interpretername);
+ ++it;
+ }
+ }
+
+ if(info) {
+ d->properties->clear();
+
+ QStringList interpreters = manager->getInterpreters();
+ KoProperty::Property::ListData* proplist = new KoProperty::Property::ListData(interpreters, interpreters);
+ KoProperty::Property* prop = new KoProperty::Property(
+ "language", // name
+ proplist, // ListData
+ d->scriptaction->getInterpreterName(), // value
+ i18n("Interpreter"), // caption
+ i18n("The used scripting interpreter."), // description
+ KoProperty::List // type
+ );
+ d->properties->addProperty(prop);
+
+ Kross::Api::InterpreterInfo::Option::Map options = info->getOptions();
+ Kross::Api::InterpreterInfo::Option::Map::ConstIterator it, end( options.constEnd() );
+ for( it = options.constBegin(); it != end; ++it) {
+ Kross::Api::InterpreterInfo::Option* option = it.data();
+ KoProperty::Property* prop = new KoProperty::Property(
+ it.key().latin1(), // name
+ d->scriptaction->getOption(it.key(), option->value), // value
+ option->name, // caption
+ option->comment, // description
+ KoProperty::Auto // type
+ );
+ d->properties->addProperty(prop);
+ }
+ }
+
+ //propertySetSwitched();
+ propertySetReloaded(true);
+ d->updatesProperties = false;
+}
+
+KoProperty::Set* KexiScriptDesignView::propertySet()
+{
+ return d->properties;
+}
+
+void KexiScriptDesignView::slotPropertyChanged(KoProperty::Set& /*set*/, KoProperty::Property& property)
+{
+ if(property.isNull())
+ return;
+
+ if(property.name() == "language") {
+ QString language = property.value().toString();
+ kdDebug() << QString("KexiScriptDesignView::slotPropertyChanged() language=%1").arg(language) << endl;
+ d->scriptaction->setInterpreterName( language );
+ // We assume Kross and the HighlightingInterface are using same
+ // names for the support languages...
+ d->editor->setHighlightMode( language );
+ updateProperties();
+ }
+ else {
+ bool ok = d->scriptaction->setOption( property.name(), property.value() );
+ if(! ok) {
+ kdWarning() << QString("KexiScriptDesignView::slotPropertyChanged() unknown property '%1'.").arg(property.name()) << endl;
+ return;
+ }
+ }
+
+ setDirty(true);
+}
+
+void KexiScriptDesignView::execute()
+{
+ d->statusbrowser->clear();
+ QTime time;
+ time.start();
+ d->statusbrowser->append( i18n("Execution of the script \"%1\" started.").arg(d->scriptaction->name()) );
+
+ d->scriptaction->activate();
+ if( d->scriptaction->hadException() ) {
+ QString errormessage = d->scriptaction->getException()->getError();
+ d->statusbrowser->append(QString("<b>%2</b><br>").arg(QStyleSheet::escape(errormessage)) );
+
+ QString tracedetails = d->scriptaction->getException()->getTrace();
+ d->statusbrowser->append( QStyleSheet::escape(tracedetails) );
+
+ long lineno = d->scriptaction->getException()->getLineNo();
+ if(lineno >= 0)
+ d->editor->setLineNo(lineno);
+ }
+ else {
+ d->statusbrowser->append( i18n("Successfully executed. Time elapsed: %1ms").arg(time.elapsed()) );
+ }
+}
+
+bool KexiScriptDesignView::loadData()
+{
+ QString data;
+ if(! loadDataBlock(data)) {
+ kexipluginsdbg << "KexiScriptDesignView::loadData(): no DataBlock" << endl;
+ return false;
+ }
+
+ QString errMsg;
+ int errLine;
+ int errCol;
+
+ QDomDocument domdoc;
+ bool parsed = domdoc.setContent(data, false, &errMsg, &errLine, &errCol);
+
+ if(! parsed) {
+ kexipluginsdbg << "KexiScriptDesignView::loadData() XML parsing error line: " << errLine << " col: " << errCol << " message: " << errMsg << endl;
+ return false;
+ }
+
+ QDomElement scriptelem = domdoc.namedItem("script").toElement();
+ if(scriptelem.isNull()) {
+ kexipluginsdbg << "KexiScriptDesignView::loadData(): script domelement is null" << endl;
+ return false;
+ }
+
+ QString interpretername = scriptelem.attribute("language");
+ Kross::Api::Manager* manager = Kross::Api::Manager::scriptManager();
+ Kross::Api::InterpreterInfo* info = interpretername.isEmpty() ? 0 : manager->getInterpreterInfo(interpretername);
+ if(info) {
+ d->scriptaction->setInterpreterName(interpretername);
+
+ Kross::Api::InterpreterInfo::Option::Map options = info->getOptions();
+ Kross::Api::InterpreterInfo::Option::Map::ConstIterator it, end = options.constEnd();
+ for( it = options.constBegin(); it != end; ++it) {
+ QString value = scriptelem.attribute( it.data()->name );
+ if(! value.isNull()) {
+ QVariant v(value);
+ if( v.cast( it.data()->value.type() ) ) // preserve the QVariant's type
+ d->scriptaction->setOption(it.data()->name, v);
+ }
+ }
+ }
+
+ d->scriptaction->setCode( scriptelem.text() );
+
+ return true;
+}
+
+KexiDB::SchemaData* KexiScriptDesignView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ KexiDB::SchemaData *s = KexiViewBase::storeNewData(sdata, cancel);
+ kexipluginsdbg << "KexiScriptDesignView::storeNewData(): new id:" << s->id() << endl;
+
+ if(!s || cancel) {
+ delete s;
+ return 0;
+ }
+
+ if(! storeData()) {
+ kdWarning() << "KexiScriptDesignView::storeNewData Failed to store the data." << endl;
+ //failure: remove object's schema data to avoid garbage
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ conn->removeObject( s->id() );
+ delete s;
+ return 0;
+ }
+
+ return s;
+}
+
+tristate KexiScriptDesignView::storeData(bool /*dontAsk*/)
+{
+ kexipluginsdbg << "KexiScriptDesignView::storeData(): " << parentDialog()->partItem()->name() << " [" << parentDialog()->id() << "]" << endl;
+
+ QDomDocument domdoc("script");
+ QDomElement scriptelem = domdoc.createElement("script");
+ domdoc.appendChild(scriptelem);
+
+ QString language = d->scriptaction->getInterpreterName();
+ scriptelem.setAttribute("language", language);
+
+ Kross::Api::InterpreterInfo* info = Kross::Api::Manager::scriptManager()->getInterpreterInfo(language);
+ if(info) {
+ Kross::Api::InterpreterInfo::Option::Map defoptions = info->getOptions();
+ QMap<QString, QVariant>& options = d->scriptaction->getOptions();
+ QMap<QString, QVariant>::ConstIterator it, end( options.constEnd() );
+ for( it = options.constBegin(); it != end; ++it) {
+ if( defoptions.contains(it.key()) ) { // only remember options which the InterpreterInfo knows about...
+ scriptelem.setAttribute(it.key(), it.data().toString());
+ }
+ }
+ }
+
+ QDomText scriptcode = domdoc.createTextNode(d->scriptaction->getCode());
+ scriptelem.appendChild(scriptcode);
+
+ return storeDataBlock( domdoc.toString() );
+}
+
+#include "kexiscriptdesignview.moc"
+
diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.h b/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.h
new file mode 100644
index 00000000..cee1ed76
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.h
@@ -0,0 +1,124 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Sebastian Sauer <mail@dipe.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXISCRIPTDESIGNVIEW_H
+#define KEXISCRIPTDESIGNVIEW_H
+
+#include <kexiviewbase.h>
+
+#include <koproperty/set.h>
+#include <koproperty/property.h>
+
+// Forward declarations.
+class KexiScriptContainer;
+class KexiScriptEditor;
+class KexiScriptDesignViewPrivate;
+
+namespace Kross { namespace Api {
+ class ScriptAction;
+}}
+
+/**
+ * The KexiScriptDesignView class provides the \a KexiViewBase to
+ * manage script modules in the design-view. The design-view
+ * is used to be able to view and edit the scripting code via
+ * a \a KexiScriptEditor instance.
+ */
+class KexiScriptDesignView : public KexiViewBase
+{
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Constructor.
+ */
+ KexiScriptDesignView(KexiMainWindow *mainWin, QWidget *parent, Kross::Api::ScriptAction* scriptaction);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiScriptDesignView();
+
+ /**
+ * \return the \a Kross::Api::ScriptAction this \a KexiScriptDesignView
+ * is responsible for.
+ */
+ Kross::Api::ScriptAction* scriptAction() const;
+
+ /**
+ * \return a property set for this view.
+ */
+ virtual KoProperty::Set* propertySet();
+
+ /**
+ * Try to call \a storeData with new data we like to store. On
+ * success the matching \a KexiDB::SchemaData is returned.
+ *
+ * \param sdata The source \a KexiDB::SchemaData instance.
+ * \param cancel Cancel on failure and don't try to clean
+ * possible temporary created data up.
+ * \return The matching \a KexiDB::SchemaData instance or NULL
+ * if storing failed.
+ */
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+
+ /**
+ * Try to store the modified data in the already opened and
+ * currently used \a KexiDB::SchemaData instance.
+ */
+ virtual tristate storeData(bool dontAsk = false);
+
+ private slots:
+
+ /**
+ * Deferred initialization.
+ */
+ void initialize();
+
+ /**
+ * Handle changes in the property editor.
+ */
+ void slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property);
+
+ /**
+ * Update the \a KoProperty::Property::Dict propertymap of the
+ * interpreter-dependent options.
+ */
+ void updateProperties();
+
+ /**
+ * Execute the scripting code.
+ */
+ void execute();
+
+ private:
+ KexiScriptDesignViewPrivate* d;
+
+ /**
+ * Load the data from XML source and fill the internally
+ * used \a Kross::Api::ScriptContainer instance.
+ */
+ bool loadData();
+};
+
+#endif
diff --git a/kexi/plugins/scripting/kexiscripting/kexiscripteditor.cpp b/kexi/plugins/scripting/kexiscripting/kexiscripteditor.cpp
new file mode 100644
index 00000000..a638af36
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/kexiscripteditor.cpp
@@ -0,0 +1,104 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Sebastian Sauer <mail@dipe.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiscripteditor.h"
+
+#include <kross/main/scriptaction.h>
+
+#include <kdebug.h>
+//#include <kparts/factory.h>
+//#include <klibloader.h>
+//#include <kmdimainfrm.h>
+//#include <kmainwindow.h>
+#include <kpopupmenu.h>
+
+#include <kexidialogbase.h>
+
+/// \internal d-pointer class
+class KexiScriptEditor::Private
+{
+ public:
+ Kross::Api::ScriptAction* scriptaction;
+ Private() : scriptaction(0) {}
+};
+
+KexiScriptEditor::KexiScriptEditor(KexiMainWindow *mainWin, QWidget *parent, const char *name)
+ : KexiEditor(mainWin, parent, name)
+ , d( new Private() )
+{
+}
+
+KexiScriptEditor::~KexiScriptEditor()
+{
+ delete d;
+}
+
+bool KexiScriptEditor::isInitialized() const
+{
+ return d->scriptaction != 0;
+}
+
+void KexiScriptEditor::initialize(Kross::Api::ScriptAction* scriptaction)
+{
+ d->scriptaction = scriptaction;
+ Q_ASSERT(d->scriptaction);
+
+ disconnect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
+
+ QString code = d->scriptaction->getCode();
+ if(code.isNull()) {
+ // If there is no code we just add some information.
+///@todo remove after release
+ code = "# " + QStringList::split("\n", i18n(
+ "This note will appear for a user in the script's source code "
+ "as a comment. Keep every row not longer than 60 characters and use '\n.'",
+
+ "This is Technology Preview (BETA) version of scripting\n"
+ "support in Kexi. The scripting API may change in details\n"
+ "in the next Kexi version.\n"
+ "For more information and documentation see\n%1"
+ ).arg("http://www.kexi-project.org/scripting/"), true).join("\n# ") + "\n";
+ }
+ KexiEditor::setText(code);
+ // We assume Kross and the HighlightingInterface are using same
+ // names for the support languages...
+ setHighlightMode(d->scriptaction->getInterpreterName());
+
+ clearUndoRedo();
+ KexiEditor::setDirty(false);
+ connect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
+}
+
+void KexiScriptEditor::slotTextChanged()
+{
+ KexiScriptEditor::setDirty(true);
+ if(d->scriptaction)
+ d->scriptaction->setCode( KexiEditor::text() );
+}
+
+void KexiScriptEditor::setLineNo(long lineno)
+{
+ setCursorPosition(lineno, 0);
+}
+
+#include "kexiscripteditor.moc"
+
diff --git a/kexi/plugins/scripting/kexiscripting/kexiscripteditor.h b/kexi/plugins/scripting/kexiscripting/kexiscripteditor.h
new file mode 100644
index 00000000..1ef02ff9
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/kexiscripteditor.h
@@ -0,0 +1,77 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
+ Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl>
+ Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Sebastian Sauer <mail@dipe.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXISCRIPTEDITOR_H
+#define KEXISCRIPTEDITOR_H
+
+#include <kexieditor.h>
+
+namespace Kross { namespace Api {
+ class ScriptAction;
+}}
+
+/**
+ * The KexiEditor class embeds text editor
+ * for editing scripting code.
+ */
+class KexiScriptEditor : public KexiEditor
+{
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Constructor.
+ */
+ KexiScriptEditor(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiScriptEditor();
+
+ /**
+ * \returns true if this editor is already initialized (\a initialize was
+ * called) else false is returned.
+ */
+ bool isInitialized() const;
+
+ /**
+ * Initializes the editor. Call this if you like to start
+ * with a clear editor instance. Thinks like the language
+ * highlighter will be reset, undo/redo are cleared and
+ * setDirty(false) is set.
+ */
+ void initialize(Kross::Api::ScriptAction* scriptaction);
+
+ public slots:
+ void slotTextChanged();
+ void setLineNo(long);
+
+ private:
+ /// \internal d-pointer class.
+ class Private;
+ /// \internal d-pointer instance.
+ Private* const d;
+};
+
+#endif
diff --git a/kexi/plugins/scripting/kexiscripting/kexiscripthandler.desktop b/kexi/plugins/scripting/kexiscripting/kexiscripthandler.desktop
new file mode 100644
index 00000000..66e5f9fb
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/kexiscripthandler.desktop
@@ -0,0 +1,105 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Scripts
+GenericName[bg]=Скриптове
+GenericName[br]=Urzhiaouegoù
+GenericName[ca]=Seqüències
+GenericName[cs]=Skripty
+GenericName[cy]=Sgriptiau
+GenericName[da]=Scripter
+GenericName[de]=Skripte
+GenericName[el]=Σενάρια
+GenericName[eo]=Skriptoj
+GenericName[es]=Guiones
+GenericName[et]=Skriptid
+GenericName[eu]=Script-ak
+GenericName[fa]=دست‌نوشته‌ها
+GenericName[fi]=Skriptit
+GenericName[fy]=Skripts
+GenericName[ga]=Scripteanna
+GenericName[gl]=Programas
+GenericName[he]=תסריטים
+GenericName[hr]=Skripte
+GenericName[hu]=Szkriptek
+GenericName[is]=Skriftur
+GenericName[it]=Script
+GenericName[ja]=スクリプト
+GenericName[km]=ស្គ្រីប​
+GenericName[lt]=Scenarijai
+GenericName[lv]=Skripti
+GenericName[ms]=Skrip
+GenericName[nb]=Skript
+GenericName[nds]=Skripten
+GenericName[ne]=स्क्रिप्टहरू
+GenericName[nn]=Skript
+GenericName[pl]=Skrypty
+GenericName[pt]=Programas
+GenericName[ru]=Сценарии
+GenericName[se]=Skriptat
+GenericName[sk]=Skripty
+GenericName[sl]=Skripti
+GenericName[sr]=Скрипте
+GenericName[sr@Latn]=Skripte
+GenericName[sv]=Skript
+GenericName[uk]=Скрипти
+GenericName[uz]=Skriptlar
+GenericName[uz@cyrillic]=Скриптлар
+GenericName[zh_CN]=脚本
+GenericName[zh_TW]=命令稿
+Name=Scripts
+Name[bg]=Скриптове
+Name[br]=Urzhiaouegoù
+Name[ca]=Seqüències
+Name[cs]=Skripty
+Name[cy]=Sgriptiau
+Name[da]=Scripter
+Name[de]=Skripte
+Name[el]=Σενάρια
+Name[eo]=Skriptoj
+Name[es]=Guiones
+Name[et]=Skriptid
+Name[eu]=Script-ak
+Name[fa]=دست‌نوشته‌ها
+Name[fi]=Skriptit
+Name[fy]=Skripts
+Name[ga]=Scripteanna
+Name[gl]=Programas
+Name[he]=תסריטים
+Name[hr]=Skripte
+Name[hu]=Szkriptek
+Name[is]=Skriftur
+Name[it]=Script
+Name[ja]=スクリプト
+Name[km]=ស្គ្រីប​
+Name[lt]=Scenarijai
+Name[lv]=Skripti
+Name[ms]=Skrip
+Name[nb]=Skript
+Name[nds]=Skripten
+Name[ne]=स्क्रिप्टहरू
+Name[nn]=Skript
+Name[pl]=Skrypty
+Name[pt]=Programas
+Name[ru]=Сценарии
+Name[se]=Skriptat
+Name[sk]=Skripty
+Name[sl]=Skripti
+Name[sr]=Скрипте
+Name[sr@Latn]=Skripte
+Name[sv]=Skript
+Name[uk]=Скрипти
+Name[uz]=Skriptlar
+Name[uz@cyrillic]=Скриптлар
+Name[zh_CN]=脚本
+Name[zh_TW]=命令稿
+X-KDE-Library=kexihandler_script
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=script
+X-Kexi-TypeMime=kexi/script
+X-Kexi-ItemIcon=script
+X-Kexi-SupportsExecution=true
+X-Kexi-SupportsDataExport=false
+X-Kexi-SupportsPrinting=false
diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptpart.cpp b/kexi/plugins/scripting/kexiscripting/kexiscriptpart.cpp
new file mode 100644
index 00000000..d650e958
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/kexiscriptpart.cpp
@@ -0,0 +1,201 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiscriptpart.h"
+#include "kexiscriptdesignview.h"
+
+#include "kexiviewbase.h"
+#include "keximainwindow.h"
+#include "kexiproject.h"
+
+#include <kross/main/manager.h>
+#include <kross/main/scriptaction.h>
+#include <kross/main/scriptguiclient.h>
+
+#include <kgenericfactory.h>
+#include <kexipartitem.h>
+#include <kxmlguiclient.h>
+#include <kexidialogbase.h>
+#include <kconfig.h>
+#include <kdebug.h>
+
+/// \internal
+class KexiScriptPart::Private
+{
+ public:
+ Kross::Api::ScriptGUIClient* scriptguiclient;
+};
+
+KexiScriptPart::KexiScriptPart(QObject *parent, const char *name, const QStringList &l)
+ : KexiPart::Part(parent, name, l)
+ , d( new Private() )
+{
+ d->scriptguiclient = 0;
+
+ // REGISTERED ID:
+ m_registeredPartID = (int)KexiPart::ScriptObjectType;
+
+ m_names["instanceName"]
+ = i18n("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). "
+ "Use '_' character instead of spaces. First character should be a..z character. "
+ "If you cannot use latin characters in your language, use english word.",
+ "script");
+ m_names["instanceCaption"] = i18n("Script");
+ m_supportedViewModes = Kexi::DesignViewMode;
+}
+
+KexiScriptPart::~KexiScriptPart()
+{
+ delete d->scriptguiclient;
+ delete d;
+}
+
+bool KexiScriptPart::execute(KexiPart::Item* item, QObject* sender)
+{
+ Q_UNUSED(sender);
+
+ if(! item) {
+ kdWarning() << "KexiScriptPart::execute: Invalid item." << endl;
+ return false;
+ }
+
+ KexiDialogBase* dialog = new KexiDialogBase(m_mainWin);
+ dialog->setId( item->identifier() );
+ KexiScriptDesignView* view = dynamic_cast<KexiScriptDesignView*>( createView(dialog, dialog, *item, Kexi::DesignViewMode) );
+ if(! view) {
+ kdWarning() << "KexiScriptPart::execute: Failed to create a view." << endl;
+ return false;
+ }
+
+ Kross::Api::ScriptAction* scriptaction = view->scriptAction();
+ if(scriptaction) {
+
+ const QString dontAskAgainName = "askExecuteScript";
+ KConfig* config = KGlobal::config();
+ QString dontask = config->readEntry(dontAskAgainName).lower();
+
+ bool exec = (dontask == "yes");
+ if( !exec && dontask != "no" ) {
+ exec = KMessageBox::warningContinueCancel(0,
+ i18n("Do you want to execute the script \"%1\"?\n\nScripts obtained from unknown sources can contain dangerous code.").arg(scriptaction->text()),
+ i18n("Execute Script?"), KGuiItem(i18n("Execute"), "exec"),
+ dontAskAgainName, KMessageBox::Notify | KMessageBox::Dangerous
+ ) == KMessageBox::Continue;
+ }
+
+ if(exec) {
+ //QTimer::singleShot(10, scriptaction, SLOT(activate()));
+ d->scriptguiclient->executeScriptAction( scriptaction );
+ }
+ }
+
+ view->deleteLater(); // not needed any longer.
+ return true;
+}
+
+void KexiScriptPart::initPartActions()
+{
+ if(m_mainWin) {
+ // At this stage the KexiPart::Part::m_mainWin should be defined, so
+ // that we are able to use it's KXMLGUIClient.
+
+ // Initialize the ScriptGUIClient.
+ d->scriptguiclient = new Kross::Api::ScriptGUIClient( m_mainWin );
+
+ // Publish the KexiMainWindow singelton instance. At least the KexiApp
+ // scripting-plugin depends on this instance and loading the plugin will
+ // fail if it's not avaiable.
+ if(! Kross::Api::Manager::scriptManager()->hasChild("KexiMainWindow")) {
+ Kross::Api::Manager::scriptManager()->addQObject(m_mainWin, "KexiMainWindow");
+
+ // Add the KAction's provided by the ScriptGUIClient to the
+ // KexiMainWindow.
+ //FIXME: fix+use createSharedPartAction() whyever it doesn't work as expected right now...
+ QPopupMenu* popup = m_mainWin->findPopupMenu("tools");
+ if(popup) {
+ KAction* execscriptaction = d->scriptguiclient->action("executescriptfile");
+ if(execscriptaction)
+ execscriptaction->plug( popup );
+ KAction* configscriptaction = d->scriptguiclient->action("configurescripts");
+ if(configscriptaction)
+ configscriptaction->plug( popup );
+ KAction* scriptmenuaction = d->scriptguiclient->action("installedscripts");
+ if(scriptmenuaction)
+ scriptmenuaction->plug( popup );
+ /*
+ KAction* execscriptmenuaction = d->scriptguiclient->action("executedscripts");
+ if(execscriptmenuaction)
+ execscriptmenuaction->plug( popup );
+ KAction* loadedscriptmenuaction = d->scriptguiclient->action("loadedscripts");
+ if(loadedscriptmenuaction)
+ loadedscriptmenuaction->plug( popup );
+ */
+ }
+ }
+ }
+}
+
+void KexiScriptPart::initInstanceActions()
+{
+ //createSharedAction(Kexi::DesignViewMode, i18n("Execute Script"), "player_play", 0, "data_execute");
+ createSharedAction(Kexi::DesignViewMode, i18n("Configure Editor..."), "configure", 0, "script_config_editor");
+}
+
+KexiViewBase* KexiScriptPart::createView(QWidget *parent, KexiDialogBase* dialog, KexiPart::Item& item, int viewMode, QMap<QString,QString>*)
+{
+ QString partname = item.name();
+ if( ! partname.isNull() ) {
+ KexiMainWindow *win = dialog->mainWin();
+ if(!win || !win->project() || !win->project()->dbConnection())
+ return 0;
+
+ Kross::Api::ScriptActionCollection* collection = d->scriptguiclient->getActionCollection("projectscripts");
+ if(! collection) {
+ collection = new Kross::Api::ScriptActionCollection( i18n("Scripts"), d->scriptguiclient->actionCollection(), "projectscripts" );
+ d->scriptguiclient->addActionCollection("projectscripts", collection);
+ }
+
+ const char* name = partname.latin1();
+ Kross::Api::ScriptAction::Ptr scriptaction = collection->action(name);
+ if(! scriptaction) {
+ scriptaction = new Kross::Api::ScriptAction(partname);
+ collection->attach(scriptaction); //TODO remove again on unload!
+ }
+
+ if(viewMode == Kexi::DesignViewMode) {
+ return new KexiScriptDesignView(win, parent, scriptaction);
+ }
+ }
+ return 0;
+}
+
+QString KexiScriptPart::i18nMessage(const QCString& englishMessage) const
+{
+ if (englishMessage=="Design of object \"%1\" has been modified.")
+ return i18n("Design of script \"%1\" has been modified.");
+ if (englishMessage=="Object \"%1\" already exists.")
+ return i18n("Script \"%1\" already exists.");
+ return englishMessage;
+}
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_script, KGenericFactory<KexiScriptPart>("kexihandler_script") )
+
+#include "kexiscriptpart.moc"
diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptpart.h b/kexi/plugins/scripting/kexiscripting/kexiscriptpart.h
new file mode 100644
index 00000000..ddba0d72
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/kexiscriptpart.h
@@ -0,0 +1,100 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
+ Copyright (C) 2005 Sebastian Sauer <mail@dipe.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXISCRIPTPART_H
+#define KEXISCRIPTPART_H
+
+#include <qdom.h>
+#include <qcstring.h>
+
+#include <kexi.h>
+#include <kexipart.h>
+#include <kexidialogbase.h>
+
+/**
+ * Kexi Scripting Plugin.
+ */
+class KexiScriptPart : public KexiPart::Part
+{
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * \param parent The parent QObject this part is child of.
+ * \param name The name this part has.
+ * \param args Optional list of arguments passed to this part.
+ */
+ KexiScriptPart(QObject *parent, const char *name, const QStringList& args);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KexiScriptPart();
+
+ /**
+ * Implementation of the \a KexiPart::Part::execute method used to
+ * execute the passed \p item instance.
+ */
+ virtual bool execute(KexiPart::Item* item, QObject* sender = 0);
+
+ /**
+ * \return the i18n message for the passed \p englishMessage string.
+ */
+ virtual QString i18nMessage(const QCString& englishMessage) const;
+
+ protected:
+
+ /**
+ * Create a new view.
+ *
+ * \param parent The parent QWidget the new view is displayed in.
+ * \param dialog The \a KexiDialogBase the view is child of.
+ * \param item The \a KexiPart::Item this view is for.
+ * \param viewMode The viewmode we like to have a view for.
+ */
+ virtual KexiViewBase* createView(QWidget *parent,
+ KexiDialogBase* dialog,
+ KexiPart::Item& item,
+ int viewMode = Kexi::DesignViewMode,
+ QMap<QString,QString>* staticObjectArgs = 0);
+
+ /**
+ * Initialize the part's actions.
+ */
+ virtual void initPartActions();
+
+ /**
+ * Initialize the instance actions.
+ */
+ virtual void initInstanceActions();
+
+ private:
+ /// \internal d-pointer class.
+ class Private;
+ /// \internal d-pointer instance.
+ Private* const d;
+};
+
+#endif
+
diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptpartinstui.rc b/kexi/plugins/scripting/kexiscripting/kexiscriptpartinstui.rc
new file mode 100644
index 00000000..16124c34
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/kexiscriptpartinstui.rc
@@ -0,0 +1,10 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexiscriptpartinst" version="14">
+
+ <MenuBar>
+ <Menu name="settings" noMerge="1">
+ <Action name="script_config_editor"/>
+ </Menu>
+ </MenuBar>
+
+</kpartgui>
diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptpartui.rc b/kexi/plugins/scripting/kexiscripting/kexiscriptpartui.rc
new file mode 100644
index 00000000..171d643f
--- /dev/null
+++ b/kexi/plugins/scripting/kexiscripting/kexiscriptpartui.rc
@@ -0,0 +1,10 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexiscriptpart" version="10">
+
+ <MenuBar>
+ <Menu name="settings" noMerge="1">
+ <Action name="script_config_editor"/>
+ </Menu>
+ </MenuBar>
+
+</kpartgui>
diff --git a/kexi/plugins/scripting/scripts/Makefile.am b/kexi/plugins/scripting/scripts/Makefile.am
new file mode 100644
index 00000000..b63eedee
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = exportxhtml importxhtml projectdocumentor copycenter python
diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenter.py b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.py
new file mode 100644
index 00000000..3718512f
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.py
@@ -0,0 +1,644 @@
+"""
+Copy Center
+
+Description:
+Python script to copy data between different datastores.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+class CopyCenter:
+
+ class Plugin:
+ def __init__(self, plugin):
+ self.plugin = plugin
+ self.name = plugin.name
+ self.source = self.load("Source")
+ self.destination = self.load("Destination")
+
+ def load(self, plugintype):
+ instance = None
+ try:
+ if hasattr(self.plugin, plugintype):
+ return getattr(self.plugin, plugintype)(self.plugin)
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ return None
+
+ def __init__(self, scriptpath):
+ self.scriptpath = scriptpath
+ self.homepath = self.getHomePath()
+ self.plugins = {}
+
+ import os
+ import sys
+ if not os.path.exists(scriptpath):
+ print "The Path %s does not exist" % scriptpath
+ else:
+ import re
+ regexp = re.compile('^CopyCenterPlugin(.*)\\.py$')
+ for f in os.listdir(scriptpath):
+ file = os.path.join(scriptpath, f)
+ if not os.path.isfile(file): continue
+ m = regexp.match(f)
+ if not m: continue
+ print "Plugin name=%s file=%s" % (m.group(1),file)
+ mylocals = {}
+ try:
+ execfile(file, globals(), mylocals)
+ if mylocals.has_key("CopyCenterPlugin"):
+ plugin = mylocals.get("CopyCenterPlugin")(self)
+ self.plugins[plugin.name] = self.Plugin(plugin)
+ except:
+ print "Failed to import file=%s" % file
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+
+ def getHomePath(self):
+ """ Return the homedirectory. """
+ import os
+ try:
+ home = os.getenv("HOME")
+ if not home:
+ import pwd
+ user = os.getenv("USER") or os.getenv("LOGNAME")
+ if not user:
+ pwent = pwd.getpwuid(os.getuid())
+ else:
+ pwent = pwd.getpwnam(user)
+ home = pwent[6]
+ return home
+ except (KeyError, ImportError):
+ return os.curdir
+
+class Copierer:
+ def __init__(self): pass
+ def appendProgressMessage(self,messagetext): pass
+ def writeSuccess(self,record,rowcount): pass
+ def writeFailed(self,record): pass
+
+def runGuiApp(copycenter, name):
+ import qt
+ import sys
+
+ #--------------------------------------------------------------------
+
+ class ListViewDialog(qt.QDialog):
+ def __init__(self, parent, caption):
+ qt.QDialog.__init__(self, parent, "ProgressDialog", 1)
+ self.parent = parent
+ self.setCaption(caption)
+ layout = qt.QVBoxLayout(self)
+ box = qt.QVBox(self)
+ box.setMargin(2)
+ layout.addWidget(box)
+ self.listview = qt.QListView(box)
+ self.listview.setAllColumnsShowFocus(True)
+ self.listview.header().setStretchEnabled(True,0)
+ btnbox = qt.QHBox(box)
+ btnbox.setMargin(6)
+ btnbox.setSpacing(6)
+ self.okbtn = qt.QPushButton(btnbox)
+ self.okbtn.setText("Ok")
+ #qt.QObject.connect(okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ self.cancelbtn = qt.QPushButton(btnbox)
+ self.cancelbtn.setText("Cancel")
+ qt.QObject.connect(self.cancelbtn, qt.SIGNAL("clicked()"), self.close)
+ box.setMinimumSize(qt.QSize(460,380))
+ def addItem(self,valuelist,afteritem = None):
+ if afteritem == None:
+ item = qt.QListViewItem(self.listview)
+ else:
+ item = qt.QListViewItem(self.listview,afteritem)
+ i = 0
+ for value in valuelist:
+ item.setText(i,value)
+ i += 1
+ return item
+
+ #--------------------------------------------------------------------
+
+ class CopyJobWidget(qt.QVBox):
+ def __init__(self,dialog,parent):
+ self.dialog = dialog
+ qt.QVBox.__init__(self,parent)
+ self.setSpacing(6)
+ typebox = qt.QHBox(self)
+ typebox.setSpacing(6)
+ label = qt.QLabel("Job File:",typebox)
+ self.jobfilecombobox = qt.QComboBox(typebox)
+ typebox.setStretchFactor(self.jobfilecombobox,1)
+ self.jobfilecombobox.setEditable(True)
+ self.jobfilecombobox.insertItem("")
+ label.setBuddy(self.jobfilecombobox)
+ qt.QObject.connect(self.jobfilecombobox, qt.SIGNAL("textChanged(const QString&)"), self.jobfilecomboboxChanged)
+
+ import os
+ import re
+ for f in os.listdir(self.dialog.copycenter.homepath):
+ file = os.path.join(self.dialog.copycenter.homepath,f)
+ if os.path.isfile(file) and re.search(".+\\.copycenterjob.xml$",f):
+ self.jobfilecombobox.insertItem(file)
+
+ loadbtn = qt.QPushButton(typebox)
+ loadbtn.setText("Open...")
+ qt.QObject.connect(loadbtn, qt.SIGNAL("clicked()"), self.openClicked)
+ savebtn = qt.QPushButton(typebox)
+ savebtn.setText("Save...")
+ qt.QObject.connect(savebtn, qt.SIGNAL("clicked()"), self.saveClicked)
+
+ self.listview = qt.QListView(self)
+ self.listview.setAllColumnsShowFocus(True)
+ self.listview.setSorting(-1)
+ self.listview.setDefaultRenameAction(qt.QListView.Reject)
+ self.listview.header().setClickEnabled(False)
+ self.listview.addColumn("Name")
+ self.listview.addColumn("Value")
+ qt.QObject.connect(self.listview, qt.SIGNAL("doubleClicked(QListViewItem*, const QPoint&, int)"), self.doubleClicked)
+ #qt.QObject.connect(self.listview, qt.SIGNAL("itemRenamed(QListViewItem*, int, const QString&)"), self.itemRenamed)
+
+ def doubleClicked(self, **args):
+ print "CopyJobWidget.doubleClicked"
+ item = self.listview.selectedItem()
+ if item and item.parent(): item.startRename(1)
+
+ def readOptions(self,domnode,plugininst):
+ print "CopyJobWidget.readOptions plugintype=\"%s\"" % plugininst.plugintype
+ for node in domnode.childNodes:
+ if node.nodeType == node.ELEMENT_NODE:
+ v = node.getAttribute("value")
+ plugininst.options[node.nodeName] = v
+ print "Option \"%s\" has value \"%s\" now." % (node.nodeName, v)
+
+ def jobfilecomboboxChanged(self, **args):
+ print "CopyJobWidget.jobfilecomboboxChanged"
+ import os
+ import xml.dom.minidom
+ filename = str(self.jobfilecombobox.currentText())
+ if not os.path.isfile(filename): return
+ domdoc = xml.dom.minidom.parse(filename)
+ try:
+ elements = domdoc.getElementsByTagName("CopyCenterJob")[0]
+ sourcenode = elements.getElementsByTagName("Source")[0]
+ destinationnode = elements.getElementsByTagName("Destination")[0]
+ except:
+ raise "The XML-file \"%s\" does not contain a valid copy-job." % filename
+
+ sourcepluginname = str(sourcenode.getAttribute('plugin'))
+ if not self.dialog.sourcedata.combobox.listBox().findItem(sourcepluginname,qt.Qt.ExactMatch):
+ raise "There exists no plugin with the name \"%s\"." % sourcepluginname
+ self.dialog.sourcedata.combobox.setCurrentText(sourcepluginname)
+
+ destinationpluginname = str(destinationnode.getAttribute('plugin'))
+ if not self.dialog.destinationdata.combobox.listBox().findItem(destinationpluginname,qt.Qt.ExactMatch):
+ raise "There exists no plugin with the name \"%s\"." % destinationpluginname
+ self.dialog.destinationdata.combobox.setCurrentText(destinationpluginname)
+
+ self.readOptions(sourcenode,self.dialog.getSourcePluginImpl())
+ self.readOptions(destinationnode,self.dialog.getDestinationPluginImpl())
+ self.maybeUpdate()
+
+ def openClicked(self):
+ text = str(self.jobfilecombobox.currentText())
+ if text == "": text = self.dialog.copycenter.homepath
+ filename = str(qt.QFileDialog.getOpenFileName(text,"*.copycenterjob.xml;;*",self.dialog))
+ if filename != "": self.jobfilecombobox.setCurrentText(filename)
+
+ def escape(self,s):
+ return s.replace("&", "&amp;").replace("'", "&apos;").replace("<", "&lt;").replace(">", "&gt;").replace('"', "&quot;")
+
+ def writeOptions(self,writer,pluginname,plugininst):
+ print "CopyJobWidget.writeOptions"
+ writer.write("<%s plugin=\"%s\">\n" % (plugininst.plugintype, pluginname))
+ for optionname in plugininst.options:
+ value = self.escape( unicode(plugininst.options[optionname]).encode("utf-8") )
+ writer.write("\t<%s value=\"%s\" />\n" % (optionname,value))
+ writer.write("</%s>\n" % plugininst.plugintype)
+
+ def saveClicked(self):
+ text = str(self.jobfilecombobox.currentText())
+ if text == "":
+ import os
+ text = os.path.join(self.dialog.copycenter.homepath,"default.copycenterjob.xml")
+ filename = str(qt.QFileDialog.getSaveFileName(text,"*.copycenterjob.xml;;*",self.dialog))
+ if str(filename) == "": return
+ f = open(filename, "w")
+ f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ f.write("<CopyCenterJob>\n")
+ sourcepluginname = self.dialog.sourcedata.combobox.currentText()
+ self.writeOptions(f, sourcepluginname, self.dialog.getSourcePluginImpl())
+ destinationpluginname = self.dialog.destinationdata.combobox.currentText()
+ self.writeOptions(f, destinationpluginname, self.dialog.getDestinationPluginImpl())
+ f.write("</CopyCenterJob>\n")
+ f.close()
+ print "File \%s\" successfully written." % filename
+
+ def addItem(self, pluginimpl, afteritem = None, parentitem = None):
+ #print "CopyJobWidget.addItem"
+ class ListViewItem(qt.QListViewItem):
+ def __init__(self, pluginimpl, listview, parentitem = None, afteritem = None):
+ self.pluginimpl = pluginimpl
+ if parentitem == None:
+ qt.QListViewItem.__init__(self,listview)
+ self.setOpen(True)
+ else:
+ if afteritem == None:
+ qt.QListViewItem.__init__(self,parentitem)
+ else:
+ qt.QListViewItem.__init__(self,parentitem,afteritem)
+ self.setRenameEnabled(1,True)
+ def startRename(self, columnindex):
+ qt.QListViewItem.startRename(self,columnindex)
+ #lineedit = self.listView().viewport().child("qt_renamebox")
+ #if lineedit:
+ # regexp = qt.QRegExp("^[_A-Z]+[_A-Z0-9]*$", False)
+ # v = qt.QRegExpValidator(regexp, self.listView());
+ # lineedit.setValidator(v)
+ def okRename(self, columnindex):
+ if columnindex == 1:
+ n = str(self.text(0))
+ if not self.pluginimpl.options.has_key(n):
+ raise "No such option \"%s\"" % n
+ qt.QListViewItem.okRename(self,columnindex)
+ v = str(qt.QListViewItem.text(self,1))
+ print "Option \"%s\" has value \"%s\" now." % (n,v)
+ self.pluginimpl.options[n] = v
+
+ def text(self, columnindex):
+ if columnindex == 1:
+ if qt.QListViewItem.text(self,0).contains("password"):
+ return "*" * len(str(qt.QListViewItem.text(self,1)))
+ return qt.QListViewItem.text(self,columnindex)
+ return ListViewItem(pluginimpl, self.listview, parentitem, afteritem)
+
+ def updateItem(self,pluginname,pluginimpl):
+ #print "CopyJobWidget.updateItem"
+ if pluginimpl == None: return
+ #plugin = self.dialog.plugins[pluginname]
+ item = self.addItem(pluginimpl)
+ item.setText(0,"%s: %s" % (pluginimpl.plugintype, pluginname))
+ afteritem = None
+ for i in pluginimpl.options:
+ afteritem = self.addItem(pluginimpl, afteritem, item)
+ afteritem.setText(0,str(i))
+ afteritem.setText(1,str(pluginimpl.options[i]))
+ print "CopyJobWidget.updateItem Added item with name \"%s\" and value \"%s\"" % (str(i),str(pluginimpl.options[i]))
+ pass
+
+ def maybeUpdate(self):
+ print "CopyJobWidget.maybeUpdate"
+ self.listview.clear()
+ try:
+ self.updateItem(self.dialog.getDestinationPluginName(), self.dialog.getDestinationPluginImpl())
+ self.updateItem(self.dialog.getSourcePluginName(), self.dialog.getSourcePluginImpl())
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ self.listview.clear()
+
+ #--------------------------------------------------------------------
+
+ class ProgressDialog(qt.QDialog):
+ def __init__(self, dialog):
+ self.dialog = dialog
+ self.starttime = None
+ qt.QDialog.__init__(self, dialog, "ProgressDialog", 1)
+ self.setCaption("Copying...")
+ layout = qt.QVBoxLayout(self)
+ box = qt.QVBox(self)
+ box.setSpacing(6)
+ box.setMargin(6)
+ layout.addWidget(box)
+ self.textbrowser = qt.QTextBrowser(box)
+ self.textbrowser.setWordWrap(qt.QTextEdit.WidgetWidth)
+ self.textbrowser.setTextFormat(qt.Qt.RichText)
+ statusbox = qt.QFrame(box)
+ layout = qt.QGridLayout(statusbox,4,2,0,2)
+ layout.addWidget(qt.QLabel("Number of records done:",statusbox),0,0)
+ self.donecounter = 0
+ self.donelabel = qt.QLabel("-",statusbox)
+ layout.addWidget(self.donelabel,0,1)
+ layout.addWidget(qt.QLabel("Successfully copied records:",statusbox),1,0)
+ self.successcounter = 0
+ self.successlabel = qt.QLabel("-",statusbox)
+ layout.addWidget(self.successlabel,1,1)
+ layout.addWidget(qt.QLabel("Failed to copy records:",statusbox),2,0)
+ self.failedcounter = 0
+ self.failedlabel = qt.QLabel("-",statusbox)
+ layout.addWidget(self.failedlabel,2,1)
+ layout.addWidget(qt.QLabel("Elapsed time in seconds:",statusbox),3,0)
+ self.elapsedlabel = qt.QLabel("-",statusbox)
+ layout.addWidget(self.elapsedlabel,3,1)
+ btnbox = qt.QHBox(box)
+ btnbox.setSpacing(6)
+ self.donebtn = qt.QPushButton(btnbox)
+ self.donebtn.setText("Done")
+ self.donebtn.setEnabled(False)
+ qt.QObject.connect(self.donebtn,qt.SIGNAL("clicked()"),self.close)
+ self.cancelbtn = qt.QPushButton(btnbox)
+ self.cancelbtn.setText("Cancel")
+ qt.QObject.connect(self.cancelbtn,qt.SIGNAL("clicked()"),self.close)
+ box.setMinimumSize( qt.QSize(500,380) )
+
+ def updateStates(self):
+ if self.starttime != None:
+ self.donelabel.setText(str(self.donecounter))
+ self.failedlabel.setText(str(self.failedcounter))
+ self.successlabel.setText(str(self.successcounter))
+ self.elapsedlabel.setText( str(self.starttime.elapsed() / 1000) )
+ self.donelabel.update()
+ self.failedlabel.update()
+ self.successlabel.update()
+ self.elapsedlabel.update()
+
+ def writeSuccess(self, record, rowcount):
+ self.donecounter += rowcount
+ self.successcounter += rowcount
+ qt.qApp.processEvents()
+ def writeFailed(self, record):
+ self.donecounter += 1
+ self.failedcounter += 1
+ qt.qApp.processEvents()
+
+ def startCopy(self):
+ try:
+ global Copierer
+ copierer = Copierer()
+ copierer.appendProgressMessage = self.textbrowser.append
+ copierer.writeSuccess = self.writeSuccess
+ copierer.writeFailed = self.writeFailed
+
+ self.starttime = qt.QTime()
+ self.updatetimer = qt.QTimer(self)
+ qt.QObject.connect(self.updatetimer,qt.SIGNAL("timeout()"),self.updateStates)
+
+ # Initialize the source
+ sourcename = self.dialog.getSourcePluginName()
+ sourceimpl = self.dialog.getSourcePluginImpl()
+ self.textbrowser.append("Source: %s" % sourcename)
+ if sourceimpl == None:
+ raise "No such source."
+ try:
+ sourceimpl.init(copierer)
+
+ # Initialize the destination
+ destinationname = self.dialog.getDestinationPluginName()
+ destinationimpl = self.dialog.getDestinationPluginImpl()
+ self.textbrowser.append("<hr>Destination: %s" % destinationname)
+ if destinationimpl == None:
+ raise "No such destination."
+ try:
+ destinationimpl.init(copierer)
+
+ self.starttime.start()
+ self.updatetimer.start(500)
+ qt.qApp.processEvents()
+
+ # Copy the records
+ self.textbrowser.append("<hr><i>Copy the records...</i>")
+ while True:
+ record = sourceimpl.read()
+ if record == None: break
+ destinationimpl.write(record)
+
+ self.updateStates()
+ finally:
+ destinationimpl.finish()
+ finally:
+ sourceimpl.finish()
+
+ self.setCaption("Copy done")
+ self.textbrowser.append("<hr><b>Copy done.</b>")
+ except:
+ self.setCaption("Copy failed")
+ self.textbrowser.append("<b>Error: %s</b>" % sys.exc_info()[0])
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ #self.progressbar.setEnabled(False)
+ self.donebtn.setEnabled(True)
+ self.cancelbtn.setEnabled(False)
+ self.updatetimer.stop()
+ self.starttime = None
+
+ def show(self):
+ qt.QDialog.show(self)
+ qt.QTimer.singleShot(10,self.startCopy)
+ qt.qApp.processEvents()
+
+ def closeEvent(self, closeevent):
+ if not self.dialog.getSourcePluginImpl().isFinished():
+ if qt.QMessageBox.warning(self,"Abort?","Abort the copy?",qt.QMessageBox.Yes,qt.QMessageBox.No) != qt.QMessageBox.Yes:
+ closeevent.ignore()
+ return
+ self.dialog.getSourcePluginImpl().finish()
+ self.dialog.getDestinationPluginImpl().finish()
+ closeevent.accept()
+
+ #--------------------------------------------------------------------
+
+ class DataSelector(qt.QVGroupBox):
+ def __init__(self, plugintype, title, caption, parent, dialog, items):
+ self.plugintype = plugintype
+ self.pluginimpl = None
+ self.dialog = dialog
+ self.mainbox = None
+
+ qt.QVGroupBox.__init__(self,title,parent)
+ self.setInsideMargin(6)
+ self.setInsideSpacing(0)
+
+ typebox = qt.QHBox(self)
+ label = qt.QLabel(caption,typebox)
+ self.combobox = qt.QComboBox(typebox)
+ for item in items:
+ self.combobox.insertItem(str(item))
+ label.setBuddy(self.combobox)
+ typebox.setStretchFactor(self.combobox,1)
+
+ self.scrollview = qt.QScrollView(self)
+ try:
+ self.scrollview.setResizePolicy(qt.QScrollView.AutoOne)
+ self.scrollview.setFrameStyle(qt.QFrame.NoFrame);
+ self.scrollview.setResizePolicy(qt.QScrollView.AutoOneFit);
+ self.scrollview.viewport().setPaletteBackgroundColor(self.paletteBackgroundColor())
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ qt.QObject.connect(self.combobox, qt.SIGNAL("activated(int)"), self.activated)
+
+ def updatePlugin(self):
+ print "DataSelector.updatePlugin"
+ self.pluginimpl = None
+ text = str(self.combobox.currentText())
+ plugin = self.dialog.copycenter.plugins[text]
+ self.pluginimpl = getattr(plugin, self.plugintype)
+
+ def removeMainBox(self):
+ if self.mainbox == None: return
+ try:
+ self.scrollview.removeChild(self.mainbox)
+ self.mainbox.destroy()
+ except:
+ pass
+ self.mainbox = None
+
+ def updateMainBox(self):
+ print "DataSelector.updateMainBox"
+ self.removeMainBox()
+ self.mainbox = qt.QVBox( self.scrollview.viewport() )
+ self.mainbox.setSpacing(2)
+ if self.pluginimpl != None:
+ try:
+ self.pluginimpl.createWidget(self.dialog, self.mainbox)
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ self.mainbox.setStretchFactor(qt.QWidget(self.mainbox), 1)
+ self.mainbox.show()
+ self.scrollview.addChild(self.mainbox)
+
+ def activated(self, **args):
+ self.updatePlugin()
+ self.updateMainBox()
+
+ def maybeUpdate(self):
+ print "DataSelector.maybeUpdate"
+ self.removeMainBox()
+ qt.QTimer.singleShot(50, self.activated)
+
+ def maybeDone(self):
+ print "DataSelector.maybeDone"
+ if self.pluginimpl.widget == None: return
+ for optionname in self.pluginimpl.options:
+ self.pluginimpl.options[optionname] = self.pluginimpl.widget.getOptionValue(optionname)
+
+ #--------------------------------------------------------------------
+
+ class Dialog(qt.QDialog):
+ def __init__(self, copycenter, parent):
+ self.copycenter = copycenter
+
+ import qt
+ import os
+ import sys
+
+ self.ListViewDialog = ListViewDialog
+ qt.QDialog.__init__(self, parent, "Dialog", 1, qt.Qt.WDestructiveClose)
+ self.setCaption("Copy Center")
+ layout = qt.QVBoxLayout(self)
+ box = qt.QVBox(self)
+ box.setMargin(6)
+ box.setSpacing(6)
+ layout.addWidget(box)
+ self.tab = qt.QTabWidget(box)
+ self.tab.setMargin(6)
+ box.setStretchFactor(self.tab,1)
+
+ self.jobsbox = CopyJobWidget(self,self.tab)
+ self.tab.addTab(self.jobsbox,"Jobs")
+
+ self.splitter = qt.QSplitter(self.tab)
+
+ sourceplugins = []
+ destinationplugins = []
+ for pluginname in self.copycenter.plugins:
+ if self.copycenter.plugins[pluginname].source != None:
+ sourceplugins.append(pluginname)
+ if self.copycenter.plugins[pluginname].destination != None:
+ destinationplugins.append(pluginname)
+ sourceplugins.sort()
+ destinationplugins.sort()
+
+ self.sourcedata = DataSelector(
+ "source", # id
+ "Read Data From", # title
+ "Source:", # caption
+ self.splitter, self, sourceplugins)
+ self.destinationdata = DataSelector(
+ "destination", # id
+ "Write Data to", # title
+ "Destination:", # caption
+ self.splitter, self, destinationplugins)
+
+ btnbox = qt.QHBox(box)
+ btnbox.setSpacing(6)
+ okbtn = qt.QPushButton(btnbox)
+ okbtn.setText("Start Copy")
+ okbtn.setDefault(True)
+ qt.QObject.connect(okbtn,qt.SIGNAL("clicked()"),self.startCopy)
+ cancelbtn = qt.QPushButton(btnbox)
+ cancelbtn.setText("Cancel")
+ qt.QObject.connect(cancelbtn,qt.SIGNAL("clicked()"),self.close)
+
+ self.tab.addTab(self.splitter,"Copy")
+ self.tab.setCurrentPage(1)
+
+ self.helpbrowser = qt.QTextBrowser(self.tab)
+ self.helpbrowser.setLinkUnderline(False)
+ self.helpbrowser.setUndoRedoEnabled(False)
+ self.tab.addTab(self.helpbrowser,"Help")
+ qt.QObject.connect(self.tab,qt.SIGNAL("currentChanged(QWidget*)"),self.currentTabChanged)
+
+ box.setMinimumSize( qt.QSize(760,500) )
+
+ defaultfile = os.path.join(self.copycenter.homepath,"default.copycenterjob.xml")
+ if os.path.isfile(defaultfile):
+ print "Reading default copy job file: %s" % defaultfile
+ self.jobsbox.jobfilecombobox.setCurrentText(defaultfile)
+
+ def getSourcePluginName(self):
+ return str(self.sourcedata.combobox.currentText())
+ def getSourcePluginImpl(self):
+ return self.copycenter.plugins[self.getSourcePluginName()].source
+ def getDestinationPluginName(self):
+ return str(self.destinationdata.combobox.currentText())
+ def getDestinationPluginImpl(self):
+ return self.copycenter.plugins[self.getDestinationPluginName()].destination
+
+ def currentTabChanged(self,widget):
+ if self.tab.currentPage() == self.jobsbox:
+ # The "Copy" page is done
+ self.sourcedata.maybeDone()
+ self.destinationdata.maybeDone()
+ # Update the "Jobs" page
+ self.jobsbox.maybeUpdate()
+ elif self.tab.currentPage() == self.splitter:
+ # Update the "Copy" page
+ self.sourcedata.maybeUpdate()
+ self.destinationdata.maybeUpdate()
+ elif self.tab.currentPage() == self.helpbrowser and self.helpbrowser.lines() <= 1:
+ # Update the "Help" page
+ import os
+ file = os.path.join(self.copycenter.scriptpath, "readme.html")
+ if not os.path.isfile(file): return
+ fh = open(file,'r')
+ self.helpbrowser.setText( fh.read() )
+ fh.close()
+
+ def startCopy(self):
+ dlg = ProgressDialog(self)
+ dlg.show()
+
+ #--------------------------------------------------------------------
+
+ if name == "__main__":
+ qtapp = qt.QApplication(sys.argv)
+ else:
+ qtapp = qt.qApp
+ dialog = Dialog(copycenter, qtapp.mainWidget())
+ dialog.exec_loop()
+
+import os
+
+if __name__ == "__main__":
+ scriptpath = os.getcwd()
+else:
+ scriptpath = os.path.dirname(__name__)
+
+copycenter = CopyCenter(scriptpath)
+runGuiApp(copycenter, __name__)
diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc
new file mode 100644
index 00000000..e3e758b4
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc
@@ -0,0 +1,10 @@
+<KrossScripting>
+ <ScriptAction
+ name="CopyCenter"
+ version="1"
+ text="Copy Center"
+ description="Copy data from one source to another."
+ icon="editcopy"
+ interpreter="python"
+ file="CopyCenter.py" />
+</KrossScripting>
diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py
new file mode 100644
index 00000000..e8241405
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py
@@ -0,0 +1,646 @@
+"""
+CopyCenterPlugin to provide 'KexiDB'.
+
+Description:
+This python-script is a plugin for the CopyCenter.py.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+GPL v2 or higher.
+"""
+
+class CopyCenterPlugin:
+ """ The CopyCenterPlugin to provide abstract access to the 'KexiDB'
+ framework to CopyCenter.py """
+
+ name = "Kexi Database"
+ """ The name this plugin has. The name should be unique and
+ will be used for displaying a caption. """
+
+ class Plugin:
+ """ The implementation of a plugin which is published to the
+ CopyCenter.py script. While there exists only one instance of
+ the CopyCenterPlugin class, there will be n instances of this
+ Plugin class (one for 'source' aka read-data-from and one for
+ 'destination' aka write-data-to) created from within the
+ CopyCenter.py. The Source and Destination classes are extending
+ this Plugin class with specialized functionality. """
+ def __init__(self,copycenterplugin):
+ """ Constructor. """
+ self.copycenterplugin = copycenterplugin
+ self.widget = None
+ self.options = {
+ 'autoconnect' : False,
+ 'project' : '', #'~/test.kexi',
+ 'driver' : '', #'MySQL', #'SQLite3','MySQL',...
+ 'file' : '', #'/path/to/mysqlite3dbfile.kexi'
+ 'hostname' : '127.0.0.1',
+ 'port' : '',
+ 'usesocketfile' : False,
+ 'socketfile' : '',
+ 'username' : '',
+ 'password' : '',
+ 'database' : '',
+ 'table' : '',
+ 'fields' : '',
+ 'where' : '',
+ }
+ self.connection = copycenterplugin.Connection(self)
+ def isFinished(self):
+ return self.connection.isFinished()
+ def finish(self):
+ """ Called if reading is finished."""
+ self.connection.finish()
+ def createWidget(self, dialog, parent):
+ """ Create and return a widget to modify the plugin settings. """
+ return self.copycenterplugin.createWidget(dialog, self, parent)
+
+ class Source(Plugin):
+ """ Specialization of the Plugin class to implement the
+ 'source' aka read-data-from functionality. """
+ plugintype = "Source"
+ def init(self,copierer):
+ """ Called if reading should be initialize. """
+ self.connection.init(copierer)
+ self.connection.initRead()
+ # Called if a record should be readed.
+ self.read = self.connection.readRecord
+
+ class Destination(Plugin):
+ """ Specialization of the Plugin class to implement the
+ 'destination' aka write-data-to functionality. """
+ plugintype = "Destination"
+ def init(self,copierer):
+ """ Called if writing should be initialize. """
+ self.connection.init(copierer)
+ self.connection.initWrite()
+ # Called if a record should be written.
+ self.write = self.connection.writeRecord
+
+ class Connection:
+ """ Abstract access to work with KexiDB. """
+ def __init__(self,plugin):
+ self.plugin = plugin
+ self.copierer = None
+ self.kexidbconnection = None
+ self.kexidbcursor = None
+ self.tableschema = None
+ self.fieldlist = None
+ def lastError(self): return self.kexidbconnection.lastError()
+ def connect(self): return self.kexidbconnection.connect()
+ def disconnect(self):
+ if self.kexidbconnection == None or self.kexidbconnection.disconnect():
+ self.kexidbconnection = None
+ return True
+ return False
+ def isConnected(self): return self.kexidbconnection != None and self.kexidbconnection.isConnected()
+ def tableNames(self): return self.kexidbconnection.tableNames()
+ def hasTableName(self,tablename): return tablename in self.kexidbconnection.tableNames()
+ def tableSchema(self,tablename): return self.kexidbconnection.tableSchema(tablename)
+
+ def init(self,copierer):
+ self.copierer = copierer
+ if self.kexidbconnection == None:
+ if self.plugin.widget == None:
+ raise "No connection established."
+ self.copierer.appendProgressMessage("<i>Trying to connect...</i>")
+ if self.plugin.widget.driverbox.driver == None:
+ raise "Invalid driver."
+ if not self.plugin.widget.connectClicked():
+ raise "Failed to connect."
+ connectiondata = self.kexidbconnection.data()
+ self.copierer.appendProgressMessage("Connected: %s %s" % (connectiondata.driverName(),connectiondata.serverInfoString()))
+
+ tablename = str(self.plugin.widget.tablebox.tableedit.text())
+ if tablename == "":
+ raise "No table defined"
+ fields = [ f.strip() for f in str(self.plugin.widget.fieldbox.fieldsedit.text()).split(",") if len(f) > 0 ]
+ if len(fields) < 1:
+ raise "No fields defined"
+
+ self.tableschema = self.kexidbconnection.tableSchema(tablename)
+ if not self.tableschema: raise "No such tableschema \"%s\"" % tablename
+ self.copierer.appendProgressMessage("Table: %s" % self.tableschema.name())
+
+ if len(fields) == 1 and fields[0] == "*":
+ self.fieldlist = self.tableschema.fieldlist()
+ else:
+ self.fieldlist = self.tableschema.fieldlist().subList(fields)
+ if not self.fieldlist: raise "No such fields \"%s\"" % fields
+ fieldlistnames = self.fieldlist.names()
+ if len(fieldlistnames) < 1: raise "No valid fields defined for \"%s\"" % fields
+ self.copierer.appendProgressMessage("Fields: %s" % fieldlistnames)
+ def finish(self):
+ if self.plugin.widget == None:
+ self.disconnect()
+ else:
+ self.plugin.widget.disconnectClicked()
+ self.kexidbcursor = None
+ self.kexidbconnection = None
+ self.tableschema = None
+ self.fieldlist = None
+ self.copierer = None
+ def isFinished(self):
+ return self.copierer == None
+
+ def initRead(self):
+ print "Initialize read"
+ #queryschema = self.plugin.copycenterplugin.drivermanager.querySchema()
+ queryschema = self.tableschema.query()
+ queryschema.fieldlist().setFields(self.fieldlist)
+ print "QuerySchema: %s" % queryschema.fieldlist().names()
+
+ whereexpression = str(self.plugin.widget.whereedit.text())
+ if whereexpression != "":
+ print "WHERE-expression: %s" % whereexpression
+ if not queryschema.setWhereExpression(whereexpression):
+ raise "Invalid WHERE-expression."
+
+ #print "QuerySchema statement=%s" % queryschema.statement()
+ self.kexidbcursor = self.kexidbconnection.executeQuerySchema(queryschema)
+ if not self.kexidbcursor:
+ raise "Failed to create cursor."
+ if not self.kexidbcursor.moveFirst():
+ raise "The cursor has no records to read from."
+
+ def readRecord(self):
+ if self.kexidbcursor == None or self.kexidbcursor.eof():
+ return None
+ record = []
+ for i in range( self.kexidbcursor.fieldCount() ):
+ record.append( self.kexidbcursor.value(i) )
+ self.kexidbcursor.moveNext()
+ #print "read record: %s" % record
+ return record
+
+ def initWrite(self):
+ print "Initialize write"
+
+ def writeRecord(self,record):
+ print "write record: %s" % record
+ if self.kexidbconnection.insertRecord(self.fieldlist,record):
+ print "=> insert successfully"
+ self.copierer.writeSuccess(record, 1)
+ else:
+ print "=> insert failed: %s" % self.kexidbconnection.lastError()
+ self.copierer.writeFailed(record)
+ #import time
+ #time.sleep(1)
+ return True
+
+ def __init__(self, copycenter):
+ """ Constructor. """
+ import krosskexidb
+ self.drivermanager = krosskexidb.DriverManager()
+ self.copycenter = copycenter
+
+ def createWidget(self, dialog, plugin, parent):
+ """ Each plugin may provide a qt.QWidget back to the
+ CopyCenter.py. The widget will be used to configure our
+ plugin settings. """
+
+ import qt
+ import os
+ import re
+
+ self.dialog = dialog
+ self.mainbox = None
+ class ProjectBox(qt.QHBox):
+ def __init__(self,main,copycenterplugin,plugin,parent):
+ self.main = main
+ self.copycenterplugin = copycenterplugin
+ self.plugin = plugin
+
+ qt.QHBox.__init__(self,parent)
+ prjlabel = qt.QLabel("Project File:",self)
+ self.prjcombo = qt.QComboBox(self)
+ self.prjcombo.setEditable(True)
+ self.prjcombo.insertItem("")
+
+ path = copycenterplugin.copycenter.homepath
+ for f in os.listdir(path):
+ file = os.path.join(path,f)
+ if os.path.isfile(file) and re.search(".+\\.(kexi|kexis|kexic)$",f):
+ self.prjcombo.insertItem(os.path.join("~",f))
+
+ prjlabel.setBuddy(self.prjcombo)
+ prjsavebtn = qt.QPushButton("...",self)
+ qt.QObject.connect(prjsavebtn, qt.SIGNAL("clicked()"),self.buttonClicked)
+ qt.QObject.connect(self.prjcombo, qt.SIGNAL("textChanged(const QString&)"), self.main.projectChanged)
+ self.setStretchFactor(self.prjcombo,1)
+ def buttonClicked(self):
+ text = str(self.prjcombo.currentText())
+ if text == "":
+ text = self.copycenterplugin.copycenter.homepath
+ elif re.search("^\\~(\\/|\\\\)",text):
+ import os
+ text = os.path.join(self.copycenterplugin.copycenter.homepath,text[2:])
+ if self.plugin.plugintype == "Source":
+ filename = qt.QFileDialog.getOpenFileName(text,"*.kexi *.kexis *.kexic;;*",self.copycenterplugin.dialog)
+ else: # "Destination":
+ filename = qt.QFileDialog.getSaveFileName(text,"*.kexi *.kexis *.kexic;;*",self.copycenterplugin.dialog)
+ if str(filename) != "": self.prjcombo.setCurrentText(str(filename))
+
+ class DriverBox(qt.QVBox):
+ def __init__(self,main,parent):
+ qt.QVBox.__init__(self,parent)
+ self.main = main
+ self.copycenterplugin = main.copycenterplugin
+ self.plugin = main.plugin
+ self.driver = None
+
+ driverbox = qt.QHBox(self)
+ driverlabel = qt.QLabel("Driver:",driverbox)
+ self.drivercombo = qt.QComboBox(driverbox)
+ self.drivercombo.insertItem("")
+ for driver in self.copycenterplugin.drivermanager.driverNames():
+ self.drivercombo.insertItem(driver)
+
+ qt.QObject.connect(self.drivercombo, qt.SIGNAL("activated(int)"), self.activated)
+ driverlabel.setBuddy(self.drivercombo)
+ driverbox.setStretchFactor(self.drivercombo,1)
+
+ self.box = qt.QVBox(self)
+ self.mainbox = None
+
+ def activated(self,index):
+ drivertext = str(self.drivercombo.currentText())
+
+ self.box.hide()
+ if self.mainbox:
+ self.mainbox.hide()
+ self.mainbox.destroy()
+ self.mainbox = None
+ if index == 0 or drivertext == "":
+ self.driver = None
+ self.box.show()
+ self.main.updateConnectButtons()
+ return False
+
+ self.driver = self.copycenterplugin.drivermanager.driver(drivertext)
+
+ mainbox = qt.QVBox(self.box)
+ mainbox.setSpacing(2)
+
+ if self.driver.isFileDriver():
+ filebox = qt.QHBox(mainbox)
+ filelabel = qt.QLabel("File:",filebox)
+ self.fileedit = qt.QLineEdit(self.plugin.options['file'],filebox)
+ filelabel.setBuddy(self.fileedit)
+ filebox.setStretchFactor(self.fileedit,1)
+ filebtn = qt.QPushButton("...",filebox)
+ qt.QObject.connect(filebtn, qt.SIGNAL("clicked()"), self.fileClicked)
+ else:
+ hostbox = qt.QHBox(mainbox)
+ hostlabel = qt.QLabel("Hostname:",hostbox)
+ self.hostedit = qt.QLineEdit(self.plugin.options['hostname'],hostbox)
+ hostlabel.setBuddy(self.hostedit)
+ hostbox.setStretchFactor(self.hostedit,1)
+
+ portbox = qt.QHBox(mainbox)
+ portlabel = qt.QLabel("Port:",portbox)
+ self.portedit = qt.QLineEdit(self.plugin.options['port'],portbox)
+ portlabel.setBuddy(self.portedit)
+ portbox.setStretchFactor(self.portedit,1)
+
+ sockbox = qt.QHBox(mainbox)
+ self.sockfilecheckbox = qt.QCheckBox("Socket File:",sockbox)
+ qt.QObject.connect(self.sockfilecheckbox, qt.SIGNAL("toggled(bool)"), self.sockfilecheckboxClicked)
+ self.sockfilebox = qt.QHBox(sockbox)
+ self.sockfileedit = qt.QLineEdit(self.plugin.options['socketfile'],self.sockfilebox)
+ self.sockfilebox.setEnabled(False)
+ sockfilebtn = qt.QPushButton("...",self.sockfilebox)
+ self.sockfilecheckbox.setChecked( str(self.plugin.options['usesocketfile']) == str(True) )
+ qt.QObject.connect(sockfilebtn, qt.SIGNAL("clicked()"), self.sockfileClicked)
+ self.sockfilebox.setStretchFactor(self.sockfileedit,1)
+ sockbox.setStretchFactor(self.sockfilebox,1)
+
+ userbox = qt.QHBox(mainbox)
+ userlabel = qt.QLabel("Username:",userbox)
+ self.useredit = qt.QLineEdit(self.plugin.options['username'],userbox)
+ userlabel.setBuddy(self.useredit)
+ userbox.setStretchFactor(self.useredit,1)
+
+ passbox = qt.QHBox(mainbox)
+ passlabel = qt.QLabel("Password:",passbox)
+ self.passedit = qt.QLineEdit(self.plugin.options['password'],passbox)
+ self.passedit.setEchoMode(qt.QLineEdit.Password)
+ passlabel.setBuddy(self.passedit)
+ passbox.setStretchFactor(self.passedit,1)
+
+ dbbox = qt.QHBox(mainbox)
+ dblabel = qt.QLabel("Database:",dbbox)
+ self.dbedit = qt.QLineEdit(self.plugin.options['database'],dbbox)
+ dblabel.setBuddy(self.dbedit)
+ dbbox.setStretchFactor(self.dbedit,1)
+ #self.tablecombo.setText("")
+
+ self.mainbox = mainbox
+ self.mainbox.show()
+ self.box.show()
+ self.main.updateConnectButtons()
+ return True
+
+ def fileClicked(self):
+ text = str(self.fileedit.text())
+ if text == "": text = self.copycenterplugin.copycenter.homepath
+ if self.plugin.plugintype == "Source":
+ filename = qt.QFileDialog.getOpenFileName(text,"*",self.copycenterplugin.dialog)
+ else: # "Destination":
+ filename = qt.QFileDialog.getSaveFileName(text,"*",self.copycenterplugin.dialog)
+ if str(filename) != "": self.fileedit.setText(str(filename))
+ def sockfilecheckboxClicked(self,checked):
+ self.sockfilebox.setEnabled(checked)
+
+ def sockfileClicked(self):
+ text = str(self.sockfileedit.text())
+ if text == "": text = self.copycenterplugin.copycenter.homepath
+ if self.plugin.plugintype == "Source":
+ filename = qt.QFileDialog.getOpenFileName(text,"*",self.copycenterplugin.dialog)
+ else: # "Destination":
+ filename = qt.QFileDialog.getSaveFileName(text,"*",self.copycenterplugin.dialog)
+ if str(filename) != "": self.sockfileedit.setText(str(filename))
+
+ class TableBox(qt.QHBox):
+ def __init__(self,copycenterplugin,plugin,parent):
+ qt.QHBox.__init__(self,parent)
+ self.copycenterplugin = copycenterplugin
+ self.plugin = plugin
+ tablelabel = qt.QLabel("Table:",self)
+ self.tableedit = qt.QLineEdit(self.plugin.options['table'],self)
+ self.tablebtn = qt.QPushButton("...",self)
+ self.tablebtn.setEnabled(False)
+ qt.QObject.connect(self.tablebtn, qt.SIGNAL("clicked()"), self.buttonClicked)
+ tablelabel.setBuddy(self.tableedit)
+ self.setStretchFactor(self.tableedit,1)
+ def buttonClicked(self):
+ ListViewDialog = self.copycenterplugin.dialog.ListViewDialog
+ class TableDialog(ListViewDialog):
+ def __init__(self,tablebox):
+ ListViewDialog.__init__(self,tablebox,"Tables")
+ self.mainwidget = tablebox
+ self.listview.addColumn("Name")
+ text = str(self.mainwidget.tableedit.text())
+ item = None
+ for table in self.mainwidget.plugin.connection.tableNames():
+ if item == None:
+ item = qt.QListViewItem(self.listview,table)
+ else:
+ item = qt.QListViewItem(self.listview,item,table)
+ if table == text:
+ self.listview.setSelected(item,True)
+ self.listview.ensureItemVisible(item)
+ qt.QObject.connect(self.listview, qt.SIGNAL("doubleClicked(QListViewItem*, const QPoint&, int)"), self.okClicked)
+ qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ def okClicked(self):
+ item = self.listview.selectedItem()
+ if item == None:
+ self.mainwidget.tableedit.setText("")
+ else:
+ self.mainwidget.tableedit.setText(item.text(0))
+ self.close()
+ dialog = TableDialog(self)
+ dialog.show()
+
+ class FieldBox(qt.QHBox):
+ def __init__(self,copycenterplugin,plugin,parent):
+ qt.QHBox.__init__(self,parent)
+ self.copycenterplugin = copycenterplugin
+ self.plugin = plugin
+ self.tablename = ""
+ fieldslabel = qt.QLabel("Fields:",self)
+ self.fieldsedit = qt.QLineEdit(self.plugin.options['fields'],self)
+ self.setStretchFactor(self.fieldsedit,1)
+ fieldslabel.setBuddy(self.fieldsedit)
+ self.fieldsbtn = qt.QPushButton("...",self)
+ self.fieldsbtn.setEnabled(False)
+ qt.QObject.connect(self.fieldsbtn, qt.SIGNAL("clicked()"), self.fieldsClicked)
+ def fieldsClicked(self):
+ ListViewDialog = self.copycenterplugin.dialog.ListViewDialog
+ class FieldsDialog(ListViewDialog):
+ def __init__(self, fieldbox):
+ ListViewDialog.__init__(self,fieldbox,"Fields")
+ self.fieldbox = fieldbox
+ self.listview.setSelectionMode(qt.QListView.Multi)
+ self.listview.setSorting(-1)
+ self.listview.header().setClickEnabled(False)
+ self.listview.addColumn("Name")
+ self.listview.addColumn("Type")
+ self.listview.addColumn("Options")
+ fieldslist = str(self.fieldbox.fieldsedit.text()).split(",")
+ allfields = ("*" in fieldslist)
+ tableschema = self.fieldbox.plugin.connection.tableSchema(self.fieldbox.tablename)
+ item = None
+ for field in tableschema.fieldlist().fields():
+ opts = []
+ for opt in ("isAutoInc","isNotNull","isNotEmpty"):
+ if getattr(field,opt)():
+ opts.append(opt[2:])
+ item = self.addItem(( field.name(),field.type(),",".join(opts) ),item)
+ if allfields or field.name() in fieldslist:
+ self.listview.setSelected(item,True)
+ qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ def okClicked(self):
+ selitems = []
+ item = self.listview.firstChild()
+ while item:
+ if item.isSelected():
+ selitems.append(str(item.text(0)))
+ item = item.nextSibling()
+ self.fieldbox.fieldsedit.setText(",".join(selitems))
+ self.close()
+ dialog = FieldsDialog(self)
+ dialog.show()
+ def tableChanged(self, text):
+ self.tablename = str(text)
+ if self.plugin.connection.isConnected():
+ if self.plugin.connection.hasTableName(self.tablename):
+ self.fieldsbtn.setEnabled(True)
+ return
+ self.fieldsbtn.setEnabled(False)
+
+ class MainBox(qt.QHBox):
+ def __init__(self,copycenterplugin,plugin,parent):
+ qt.QHBox.__init__(self,parent)
+ self.copycenterplugin = copycenterplugin
+ self.plugin = plugin
+
+ self.prjbox = ProjectBox(self,copycenterplugin,plugin,parent)
+ self.driverbox = DriverBox(self,parent)
+
+ statusbar = qt.QHBox(parent)
+ statusbar.setSpacing(2)
+ #self.statuslabel = qt.QLabel("Disconnected",statusbar)
+ #statusbar.setStretchFactor(self.statuslabel,1)
+ statusbar.setStretchFactor(qt.QWidget(statusbar),1)
+ self.connectbtn = qt.QPushButton("Connect",statusbar)
+ self.connectbtn.setEnabled(False)
+ qt.QObject.connect(self.connectbtn, qt.SIGNAL("clicked()"),self.connectClicked)
+ self.disconnectbtn = qt.QPushButton("Disconnect",statusbar)
+ self.disconnectbtn.setEnabled(False)
+ qt.QObject.connect(self.disconnectbtn, qt.SIGNAL("clicked()"),self.disconnectClicked)
+
+ #self.connectionbox = ConnectionBox(copycenterplugin,plugin,parent)
+ self.tablebox = TableBox(copycenterplugin,plugin,parent)
+ self.fieldbox = FieldBox(copycenterplugin,plugin,parent)
+ qt.QObject.connect(self.tablebox.tableedit, qt.SIGNAL("textChanged(const QString&)"), self.fieldbox.tableChanged)
+
+ if self.plugin.options['project'] != '':
+ self.prjbox.prjcombo.setCurrentText(self.plugin.options['project'])
+
+ if self.plugin.options['driver'] != '':
+ try:
+ item = str(self.driverbox.drivercombo.listBox().findItem(self.plugin.options['driver'],qt.Qt.ExactMatch).text())
+ self.driverbox.drivercombo.setCurrentText(item)
+ self.driverbox.activated(item)
+ except:
+ pass
+
+ if self.plugin.plugintype == "Destination":
+ #typebox = qt.QHBox(parent)
+ #label = qt.QLabel("Operation:",typebox)
+ #combobox = qt.QComboBox(typebox)
+ #combobox.insertItem("Append")
+ #combobox.insertItem("Replace")
+ #combobox.insertItem("Update")
+ #combobox.insertItem("Update/Insert")
+ #combobox.insertItem("Insert new")
+ #label.setBuddy(combobox)
+ #typebox.setStretchFactor(combobox,1)
+ pass
+ elif self.plugin.plugintype == "Source":
+ wherebox = qt.QHBox(parent)
+ wherelabel = qt.QLabel("Where:",wherebox)
+ self.whereedit = qt.QLineEdit(self.plugin.options['where'],wherebox)
+
+ #orderbox = qt.QHBox(parent)
+ #orderlabel = qt.QLabel("Order By:",orderbox)
+ #orderedit = qt.QLineEdit("",orderbox)
+
+ #errbox = qt.QHBox(parent)
+ #errlabel = qt.QLabel("On Error:",errbox)
+ #errcombo = qt.QComboBox(errbox)
+ #errcombo.insertItem("Ask")
+ #errcombo.insertItem("Skip")
+ #errcombo.insertItem("Abort")
+ #errlabel.setBuddy(errcombo)
+ #errbox.setStretchFactor(errcombo,1)
+
+ if self.plugin.options['autoconnect']:
+ self.connectClicked()
+
+ def projectChanged(self, text):
+ #if self.driverbox.drivercombo.currentItem() != 0:
+ # self.driverbox.drivercombo.setCurrentItem(0)
+
+ file = str(text)
+ import os
+ if re.search("^\\~(\\/|\\\\)",file):
+ file = os.path.join(self.copycenterplugin.copycenter.homepath,file[2:])
+ if file == "" or not os.path.isfile(file):
+ self.driverbox.drivercombo.setCurrentItem(0)
+ self.driverbox.activated(0)
+ return
+
+ connectiondata = self.copycenterplugin.drivermanager.createConnectionDataByFile(file)
+ if connectiondata == None:
+ raise "Unsupported file."
+
+ drivername = connectiondata.driverName().lower()
+ print "driver: %s" % drivername
+ for i in range(1,self.driverbox.drivercombo.count()):
+ if drivername == self.driverbox.drivercombo.text(i).lower():
+ self.driverbox.drivercombo.setCurrentItem(i)
+ self.driverbox.activated(i)
+ break
+
+ if self.driverbox.driver != None:
+ if self.driverbox.driver.isFileDriver():
+ self.driverbox.fileedit.setText(connectiondata.fileName())
+ else: # server
+ self.driverbox.hostedit.setText(connectiondata.hostName())
+ self.driverbox.portedit.setText(str(connectiondata.port()))
+ self.driverbox.sockfilecheckbox.setChecked(connectiondata.localSocketFileUsed())
+ self.driverbox.sockfileedit.setText(connectiondata.localSocketFileName())
+ self.driverbox.useredit.setText(connectiondata.userName())
+ self.driverbox.passedit.setText(connectiondata.password())
+ self.driverbox.dbedit.setText(connectiondata.databaseName())
+
+ def connectClicked(self):
+ if self.driverbox.driver == None:
+ print "No driver selected."
+ return False
+ connectiondata = self.copycenterplugin.drivermanager.createConnectionData()
+ if self.driverbox.driver.isFileDriver():
+ file = str(self.driverbox.fileedit.text())
+ if file == "" or not os.path.isfile(file):
+ qt.QMessageBox.critical(self,"Failed to connect","There exists no such database file \"%s\"" % file)
+ return False
+ connectiondata.setFileName(file)
+ connectiondata.setDatabaseName(file)
+ else:
+ connectiondata.setHostName(str(self.driverbox.hostedit.text()))
+ connectiondata.setPort(str(self.driverbox.portedit.text()))
+ connectiondata.setLocalSocketFileUsed(self.driverbox.sockfilecheckbox.isChecked())
+ connectiondata.setLocalSocketFileName(str(self.driverbox.sockfileedit.text()))
+ connectiondata.setPassword(str(self.driverbox.passedit.text()))
+ connectiondata.setUserName(str(self.driverbox.useredit.text()))
+ connectiondata.setDatabaseName(str(self.driverbox.dbedit.text()))
+ print "Creating connection"
+ connection = self.driverbox.driver.createConnection(connectiondata)
+ print "Trying to connect"
+ if not connection.connect():
+ qt.QMessageBox.critical(self,"Failed to connect",connection.lastError())
+ return False
+ print "Use database \"%s\"" % connectiondata.databaseName()
+ if not connection.useDatabase( connectiondata.databaseName() ):
+ qt.QMessageBox.critical(self,"Failed to connect",connection.lastError())
+ return False
+ print "dbnames = %s" % connection.databaseNames()
+ print "tablenames = %s" % connection.tableNames()
+ #self.useDatabase(connection, filename)
+
+ self.plugin.connection.kexidbconnection = connection
+ self.updateConnectButtons()
+ return True
+
+ def disconnectClicked(self):
+ if not self.plugin.connection.disconnect():
+ qt.QMessageBox.critical(self,"Failed to disconnect",self.plugin.connection.lastError())
+ return
+ self.updateConnectButtons()
+
+ def updateConnectButtons(self):
+ connected = self.plugin.connection.isConnected()
+ self.prjbox.setEnabled(not connected)
+ self.driverbox.setEnabled(not connected)
+ self.connectbtn.setEnabled( (not connected) and (self.driverbox.driver != None) )
+ self.disconnectbtn.setEnabled(connected)
+ self.tablebox.tablebtn.setEnabled(connected)
+ self.fieldbox.tableChanged(self.tablebox.tableedit.text())
+
+ def getOptionValue(self,optionname):
+ try:
+ if optionname == 'project': return str(self.prjbox.prjcombo.currentText())
+ elif optionname == 'driver': return str(self.driverbox.drivercombo.currentText())
+ elif optionname == 'file': return str(self.driverbox.fileedit.text())
+ elif optionname == 'hostname': return str(self.driverbox.hostedit.text())
+ elif optionname == 'port': return str(self.driverbox.portedit.text())
+ elif optionname == 'usesocketfile': return str(self.driverbox.sockfilecheckbox.isChecked())
+ elif optionname == 'socketfile': return str(self.driverbox.sockfileedit.text())
+ elif optionname == 'username': return str(self.driverbox.useredit.text())
+ elif optionname == 'password': return str(self.driverbox.passedit.text())
+ elif optionname == 'database': return str(self.driverbox.dbedit.text())
+ elif optionname == 'table': return str(self.tablebox.tableedit.text())
+ elif optionname == 'fields': return str(self.fieldbox.fieldsedit.text())
+ elif optionname == 'where': return str(self.whereedit.text())
+ except:
+ pass
+ return ""
+
+ mainbox = MainBox(self,plugin,parent)
+ plugin.widget = mainbox
+ return mainbox
+
diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py
new file mode 100644
index 00000000..985d757d
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py
@@ -0,0 +1,495 @@
+"""
+CopyCenterPlugin to provide 'QtSQL'.
+
+Description:
+This python-script is a plugin for the CopyCenter.py.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+GPL v2 or higher.
+"""
+
+class CopyCenterPlugin:
+ """ The CopyCenterPlugin to provide 'QtSQL' to CopyCenter.py """
+
+ name = "QtSQL Database"
+ """ The name this plugin has. The name should be unique and
+ will be used for displaying a caption. """
+
+ class Plugin:
+ def _init_(self,copycenterplugin):
+ self.copycenterplugin = copycenterplugin
+ self.widget = None
+ self.database = None
+ self.cursor = None
+ self.isfinished = True
+ def _init(self,copierer):
+ self.copierer = copierer
+ if not self.widget.connectClicked():
+ raise "Failed to connect with database."
+ if self.database == None or not self.database.isOpen():
+ raise "Database is not initialized or not opened."
+ self.copierer.appendProgressMessage("Connected: %s %s@%s:%i %s" %
+ (str(self.database.driverName()),str(self.database.userName()),str(self.database.hostName()),self.database.port(),str(self.database.databaseName())) )
+ self.isfinished = False
+ def isFinished(self):
+ return self.isfinished
+ def finish(self):
+ self.isfinished = True
+ self.widget.disconnectClicked()
+ def createWidget(self,dialog,parent):
+ return self.copycenterplugin.widget(dialog, self, parent)
+
+ class Source(Plugin):
+ plugintype = "Source"
+ def __init__(self,copycenterplugin):
+ self._init_(copycenterplugin)
+ self.options = {
+ 'driver': 'QMYSQL3', #'QMYSQL3','QPSQL7','QODBC3',...
+ 'hostname': '127.0.0.1',
+ 'port': 3306,
+ 'username': 'root', #'MyUsername',
+ 'password': '', #'MySecretPassword',
+ 'database': '', #'MyQtSQLDatabase',
+ 'table': '', #'table1',
+ 'fields': '', #'f1,f2',
+ 'where': '',
+ }
+ def init(self,copierer):
+ self._init(copierer)
+ tablename = str(self.widget.tableedit.text())
+ wherestatement = str(self.widget.whereedit.text())
+ import qt
+ import qtsql
+ self.cursor = qtsql.QSqlCursor(tablename,True,self.database)
+ self.cursor.setFilter(wherestatement)
+ if not self.cursor.select():
+ raise "Select on cursor failed.<br>%s<br>%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) )
+ self.fieldlist = []
+ for fieldname in str(self.widget.fieldedit.text()).split(","):
+ fn = fieldname.strip()
+ if fn != "":
+ field = self.cursor.field(fn)
+ if not field:
+ raise "There exists no such field \"%s\" in the table \"%s\"." % (fn,tablename)
+ self.fieldlist.append(str(field.name()))
+ if len(self.fieldlist) < 1:
+ raise "No fields for table \"%s\" defined." % tablename
+ copierer.appendProgressMessage("SQL: %s" % str(self.cursor.executedQuery()))
+
+ def read(self):
+ if not self.cursor.next():
+ return None
+ record = []
+ for fieldname in self.fieldlist:
+ record.append( unicode(self.cursor.value(fieldname).toString()).encode("latin-1") )
+ #print "read record: %s" % record
+ return record
+
+ class Destination(Plugin):
+ plugintype = "Destination"
+ def __init__(self,copycenterplugin):
+ self._init_(copycenterplugin)
+ self.options = {
+ 'driver': 'QMYSQL3', #'QMYSQL3','QPSQL7','QODBC3',...
+ 'hostname': '127.0.0.1',
+ 'port': 3306,
+ 'username': 'root', #'MyUsername',
+ 'password': '', #'MySecretPassword',
+ 'database': '', #'MyQtSQLDatabase',
+ 'table': '', #'table2',
+ 'fields': '', #'field1,field2',
+ 'operation': 'Insert', #'Insert','Update'...
+ 'indexfield': '',
+ }
+ def init(self,copierer):
+ self._init(copierer)
+ import qt
+ import qtsql
+
+ self.fieldlist = []
+ for fieldname in str(self.widget.fieldedit.text()).split(","):
+ fn = fieldname.strip()
+ if fn != "": self.fieldlist.append(fn)
+
+ tablename = str(self.widget.tableedit.text())
+ self.cursor = qtsql.QSqlCursor(tablename,True,self.database)
+ {
+ 0: self.initInsert,
+ 1: self.initUpdate
+ }[ self.widget.operationedit.currentItem() ]()
+
+ def initInsert(self):
+ self.write = self.writeInsert
+ if not self.cursor.select():
+ raise "Select on cursor failed.<br>%s<br>%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) )
+ for fieldname in self.fieldlist: # check fieldlist
+ field = self.cursor.field(fieldname)
+ if not field: raise "There exists no such field \"%s\" in the table \"%s\"." % (fieldname, self.cursor.name())
+ self.copierer.appendProgressMessage("Insert SQL: %s" % str(self.cursor.executedQuery()))
+
+ def writeInsert(self, record):
+ print "insert record: %s" % record
+ import qt
+ cursorrecord = self.cursor.primeInsert()
+ count = len(record)
+ for i in range(len(self.fieldlist)):
+ if i == count: break
+ r = record[i]
+ if r == None:
+ v = qt.QVariant()
+ else:
+ v = qt.QVariant(r)
+ cursorrecord.setValue(self.fieldlist[i], v)
+ rowcount = self.cursor.insert()
+ if rowcount < 1:
+ drv = unicode(self.cursor.lastError().driverText()).encode("latin-1")
+ db = unicode(self.cursor.lastError().databaseText()).encode("latin-1")
+ print "failed: %s %s" % (drv,db)
+ self.copierer.writeFailed(record)
+ else:
+ self.copierer.writeSuccess(record,rowcount)
+ #import time
+ #time.sleep(1)
+ return True
+
+ def initUpdate(self):
+ self.write = self.writeUpdate
+ self.indexfieldname = str(self.widget.indexedit.text()).strip()
+ if self.indexfieldname == "": raise "No index-field defined."
+ pkindex = self.cursor.index(self.indexfieldname)
+ if not pkindex: raise "Invalid index-field defined."
+ self.cursor.setPrimaryIndex(pkindex)
+ #self.cursor.setMode( qtsql.QSqlCursor.Insert | qtsql.QSqlCursor.Update )
+ self.copierer.appendProgressMessage("Update SQL: %s" % str(self.cursor.executedQuery()))
+
+ def writeUpdate(self, record):
+ import qt
+ # determinate the primary-index
+ try:
+ idx = self.fieldlist.index(self.indexfieldname)
+ indexvalue = record[idx]
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ raise "Failed to determinate the value for the primary key."
+ # select cursor and go to matching record.
+ wherestatement = "%s = \"%s\"" % (self.indexfieldname, indexvalue)
+ if not self.cursor.select(wherestatement):
+ raise "Select on cursor failed.<br>%s<br>%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) )
+ if not self.cursor.next():
+ #print "No such record to update !"
+ return False
+ # Prepare updating the record.
+ cursorrecord = self.cursor.primeUpdate()
+ # Update the fields in the record.
+ count = len(record)
+ for i in range(len(self.fieldlist)):
+ if i == count: break
+ fieldname = self.fieldlist[i]
+ if self.indexfieldname != fieldname: # don't update the indexfield!
+ r = record[i]
+ if r == None:
+ v = qt.QVariant()
+ else:
+ v = qt.QVariant(r)
+ cursorrecord.setValue(fieldname, v)
+ # Write updated record.
+ rowcount = self.cursor.update()
+ if rowcount < 1:
+ self.copierer.writeFailed(record)
+ else:
+ self.copierer.writeSuccess(record,rowcount)
+ print "updated record (rowcount %s): %s" % (rowcount,record)
+ return True
+
+ def __init__(self, copycenter):
+ """ Constructor. """
+ pass
+
+ def widget(self,dialog,plugin,parent):
+ """ Each plugin may provide a qt.QWidget back to the
+ CopyCenter.py. The widget will be used to configure our
+ plugin settings. """
+
+ import qt
+ import os
+
+ self.dialog = dialog
+ ListViewDialog = self.dialog.ListViewDialog
+ class TableDialog(ListViewDialog):
+ def __init__(self, mainwidget):
+ ListViewDialog.__init__(self,mainwidget,"Tables")
+ self.mainwidget = mainwidget
+ self.listview.addColumn("Name")
+ text = str(self.mainwidget.tableedit.text())
+ item = None
+ for table in self.mainwidget.plugin.database.tables():
+ if item == None:
+ item = qt.QListViewItem(self.listview,table)
+ else:
+ item = qt.QListViewItem(self.listview,item,table)
+ if table == text:
+ self.listview.setSelected(item,True)
+ self.listview.ensureItemVisible(item)
+ qt.QObject.connect(self.listview, qt.SIGNAL("doubleClicked(QListViewItem*, const QPoint&, int)"), self.okClicked)
+ qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ def okClicked(self):
+ item = self.listview.selectedItem()
+ if item == None:
+ self.mainwidget.tableedit.setText("")
+ else:
+ self.mainwidget.tableedit.setText(item.text(0))
+ self.close()
+
+ class FieldsDialog(ListViewDialog):
+ def __init__(self, mainwidget):
+ ListViewDialog.__init__(self,parent,"Fields")
+ self.mainwidget = mainwidget
+ self.listview.setSelectionMode(qt.QListView.Multi)
+ self.listview.setSorting(-1)
+ self.listview.header().setClickEnabled(False)
+ self.listview.addColumn("Name")
+ self.listview.addColumn("Type")
+ self.listview.addColumn("Options")
+ tablename = str(self.mainwidget.tableedit.text())
+ recinfo = self.mainwidget.plugin.database.recordInfo(tablename)
+ if recinfo != None:
+ fieldslist = str(self.mainwidget.fieldedit.text()).split(",")
+ allfields = ("*" in fieldslist)
+ item = None
+ for fieldinfo in recinfo:
+ opts = ""
+ for s in ('Required','Calculated'): #,'Generated'):
+ if getattr(fieldinfo,"is%s" % s)(): opts += "%s " % s
+ item = self.addItem((fieldinfo.name(), qt.QVariant.typeToName(fieldinfo.type()), opts),item)
+ if allfields or fieldinfo.name() in fieldslist:
+ self.listview.setSelected(item,True)
+ qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked)
+ def okClicked(self):
+ selitems = []
+ item = self.listview.firstChild()
+ while item:
+ if item.isSelected():
+ selitems.append(str(item.text(0)))
+ item = item.nextSibling()
+ self.mainwidget.fieldedit.setText(",".join(selitems))
+ self.close()
+
+
+ class MainWidget(qt.QHBox):
+ def __init__(self,plugin,dialog,parent):
+ import qt
+ import qtsql
+ qt.QHBox.__init__(self,parent)
+ self.dialog = dialog
+ self.plugin = plugin
+
+ self.connectionbox = qt.QVBox(parent)
+ self.connectionbox.setSpacing(2)
+
+ driverbox = qt.QHBox(self.connectionbox)
+ driverlabel = qt.QLabel("Driver:",driverbox)
+ self.driveredit = qt.QComboBox(driverbox)
+ for driver in qtsql.QSqlDatabase.drivers():
+ self.driveredit.insertItem(driver)
+ if self.plugin.options['driver'] == driver:
+ self.driveredit.setCurrentItem(self.driveredit.count() - 1)
+ driverlabel.setBuddy(self.driveredit)
+ driverbox.setStretchFactor(self.driveredit,1)
+
+ hostbox = qt.QHBox(self.connectionbox)
+ hostlabel = qt.QLabel("Hostname:",hostbox)
+ self.hostedit = qt.QLineEdit(self.plugin.options['hostname'],hostbox)
+ hostlabel.setBuddy(self.hostedit)
+ hostbox.setStretchFactor(self.hostedit,1)
+
+ portbox = qt.QHBox(self.connectionbox)
+ portlabel = qt.QLabel("Port:",portbox)
+ self.portedit = qt.QLineEdit(str(self.plugin.options['port']),portbox)
+ portlabel.setBuddy(self.portedit)
+ portbox.setStretchFactor(self.portedit,1)
+
+ userbox = qt.QHBox(self.connectionbox)
+ userlabel = qt.QLabel("Username:",userbox)
+ self.useredit = qt.QLineEdit(self.plugin.options['username'],userbox)
+ userlabel.setBuddy(self.useredit)
+ userbox.setStretchFactor(self.useredit,1)
+
+ passbox = qt.QHBox(self.connectionbox)
+ passlabel = qt.QLabel("Password:",passbox)
+ self.passedit = qt.QLineEdit(self.plugin.options['password'],passbox)
+ self.passedit.setEchoMode(qt.QLineEdit.Password)
+ passlabel.setBuddy(self.passedit)
+ passbox.setStretchFactor(self.passedit,1)
+
+ dbbox = qt.QHBox(self.connectionbox)
+ dblabel = qt.QLabel("Database:",dbbox)
+ self.dbedit = qt.QLineEdit(self.plugin.options['database'],dbbox)
+ dblabel.setBuddy(self.dbedit)
+ dbbox.setStretchFactor(self.dbedit,1)
+
+ statusbar = qt.QHBox(parent)
+ statusbar.setSpacing(2)
+ statusbar.setStretchFactor(qt.QWidget(statusbar),1)
+ self.connectbtn = qt.QPushButton("Connect",statusbar)
+ qt.QObject.connect(self.connectbtn, qt.SIGNAL("clicked()"),self.connectClicked)
+ self.disconnectbtn = qt.QPushButton("Disconnect",statusbar)
+ self.disconnectbtn.setEnabled(False)
+ qt.QObject.connect(self.disconnectbtn, qt.SIGNAL("clicked()"),self.disconnectClicked)
+
+ tablebox = qt.QHBox(parent)
+ tablelabel = qt.QLabel("Table:",tablebox)
+ self.tableedit = qt.QLineEdit(self.plugin.options['table'],tablebox)
+ qt.QObject.connect(self.tableedit, qt.SIGNAL("textChanged(const QString&)"), self.tableEditChanged)
+ self.tablebtn = qt.QPushButton("...",tablebox)
+ self.tablebtn.setEnabled(False)
+ qt.QObject.connect(self.tablebtn, qt.SIGNAL("clicked()"), self.tableBtnClicked)
+ tablelabel.setBuddy(self.tableedit)
+ tablebox.setStretchFactor(self.tableedit,1)
+
+ fieldbox = qt.QHBox(parent)
+ fieldlabel = qt.QLabel("Fields:",fieldbox)
+ self.fieldedit = qt.QLineEdit(self.plugin.options['fields'],fieldbox)
+ self.fieldbtn = qt.QPushButton("...",fieldbox)
+ self.fieldbtn.setEnabled(False)
+ qt.QObject.connect(self.fieldbtn, qt.SIGNAL("clicked()"), self.fieldBtnClicked)
+ fieldlabel.setBuddy(self.fieldedit)
+ fieldbox.setStretchFactor(self.fieldedit,1)
+
+ if self.plugin.plugintype == "Source":
+ box = qt.QHBox(parent)
+ wherelabel = qt.QLabel("Where:",box)
+ self.whereedit = qt.QLineEdit(self.plugin.options['where'],box)
+ wherelabel.setBuddy(self.whereedit)
+ box.setStretchFactor(self.whereedit,1)
+ elif self.plugin.plugintype == "Destination":
+
+ class OperationBox(qt.QVBox):
+ def __init__(self, mainwidget, parent):
+ self.mainwidget = mainwidget
+ qt.QVBox.__init__(self, parent)
+ opbox = qt.QHBox(self)
+ operationlabel = qt.QLabel("Operation:",opbox)
+ self.mainwidget.operationedit = qt.QComboBox(opbox)
+ for op in ('Insert','Update'):
+ self.mainwidget.operationedit.insertItem(op)
+ if self.mainwidget.plugin.options['operation'] == op:
+ self.mainwidget.operationedit.setCurrentItem(self.mainwidget.operationedit.count() - 1)
+ operationlabel.setBuddy(self.mainwidget.operationedit)
+ opbox.setStretchFactor(self.mainwidget.operationedit,1)
+ self.box = None
+ qt.QObject.connect(self.mainwidget.operationedit, qt.SIGNAL("activated(int)"), self.operationActivated)
+ self.operationActivated()
+ def operationActivated(self, **args):
+ if self.box:
+ self.box.hide()
+ self.box.destroy()
+ self.box = None
+ def showInsert(self):
+ pass
+ def showUpdate(self):
+ self.box = qt.QHBox(self)
+ indexlabel = qt.QLabel("Indexfield:", self.box)
+ self.mainwidget.indexedit = qt.QLineEdit(self.mainwidget.plugin.options['indexfield'], self.box)
+ indexlabel.setBuddy(self.mainwidget.indexedit)
+ self.box.setStretchFactor(self.mainwidget.indexedit,1)
+ {
+ 0: showInsert,
+ 1: showUpdate,
+ }[ self.mainwidget.operationedit.currentItem() ](self)
+ if self.box != None: self.box.show()
+ OperationBox(self,parent)
+
+ def tableEditChanged(self,text):
+ if self.plugin.database != None and self.plugin.database.isOpen():
+ if str(text) in self.plugin.database.tables():
+ self.fieldbtn.setEnabled(True)
+ return
+ self.fieldbtn.setEnabled(False)
+
+ def tableBtnClicked(self):
+ dialog = TableDialog(self)
+ dialog.show()
+
+ def fieldBtnClicked(self):
+ dialog = FieldsDialog(self)
+ dialog.show()
+
+ def updateConnectState(self):
+ connected = self.plugin.database != None and self.plugin.database.isOpen()
+ self.connectionbox.setEnabled(not connected)
+ self.connectbtn.setEnabled(not connected)
+ self.disconnectbtn.setEnabled(connected)
+ self.tablebtn.setEnabled(connected)
+ self.tableEditChanged(self.tableedit.text())
+
+ def getOptionValue(self,optionname):
+ try:
+ if optionname == 'driver': return str(self.driveredit.currentText())
+ if optionname == 'hostname': return str(self.hostedit.text())
+ if optionname == 'port': return str(self.portedit.text())
+ if optionname == 'username': return str(self.useredit.text())
+ if optionname == 'password': return str(self.passedit.text())
+ if optionname == 'database': return str(self.dbedit.text())
+ if optionname == 'table': return str(self.tableedit.text())
+ if optionname == 'fields': return str(self.fieldedit.text())
+ if optionname == 'where': return str(self.whereedit.text())
+ if optionname == 'operation': return str(self.operationedit.currentText())
+ if optionname == 'indexfield': return str(self.indexedit.text())
+ except:
+ import traceback
+ print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) )
+ return ""
+
+ def connectClicked(self):
+ if self.plugin.database != None and self.plugin.database.isOpen():
+ print "already connected. not needed to reconnect..."
+ self.updateConnectState()
+ return True
+ print "trying to connect..."
+
+ import qtsql
+ drivername = str(self.driveredit.currentText())
+ print "drivername: %s" % drivername
+ connectionname = "CopyCenter%s" % self.plugin.plugintype
+ print "connectionname: %s" % connectionname
+ self.plugin.database = qtsql.QSqlDatabase.addDatabase(drivername,connectionname)
+ if not self.plugin.database:
+ qt.QMessageBox.critical(self,"Failed to connect","<qt>Failed to create database for driver \"%s\"</qt>" % drivername)
+ return False
+
+ hostname = str(self.hostedit.text())
+ self.plugin.database.setHostName(hostname)
+
+ portnumber = int(str(self.portedit.text()))
+ self.plugin.database.setPort(portnumber)
+
+ username = str(self.useredit.text())
+ self.plugin.database.setUserName(username)
+
+ password = str(self.passedit.text())
+ self.plugin.database.setPassword(password)
+
+ databasename = str(self.dbedit.text())
+ self.plugin.database.setDatabaseName(databasename)
+
+ if not self.plugin.database.open():
+ qt.QMessageBox.critical(self,"Failed to connect","<qt>%s<br><br>%s</qt>" % (self.plugin.database.lastError().driverText(),self.plugin.database.lastError().databaseText()))
+ return False
+ print "database is opened now!"
+ self.updateConnectState()
+ return True
+
+ def disconnectClicked(self):
+ print "trying to disconnect..."
+ if self.plugin.database:
+ self.plugin.database.close()
+ self.plugin.database = None
+ print "database is closed now!"
+ self.updateConnectState()
+
+ plugin.widget = MainWidget(plugin,self.dialog,parent)
+ return plugin.widget
diff --git a/kexi/plugins/scripting/scripts/copycenter/Makefile.am b/kexi/plugins/scripting/scripts/copycenter/Makefile.am
new file mode 100644
index 00000000..d46928ed
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+scriptsdir = $(kde_datadir)/kexi/scripts/copycenter
+scripts_SCRIPTS = CopyCenter.py CopyCenterPluginQtSQL.py CopyCenterPluginKexiDB.py CopyCenter.rc readme.html
diff --git a/kexi/plugins/scripting/scripts/copycenter/readme.html b/kexi/plugins/scripting/scripts/copycenter/readme.html
new file mode 100644
index 00000000..2aff6152
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/copycenter/readme.html
@@ -0,0 +1,20 @@
+<html><body>
+<h1>Copy Center</h1>
+
+<b>Version 1.2</b>
+
+<p>Python script to copy data between database backends. The flexible
+plugin-architecture allows transparent copies between different backends.</p>
+
+<ul>
+<li>Read+write Kexi Databases. This includes all database backends supported by Kexi (like SQLite, MySQL or PostgreSQL).</li>
+<li>Read+write QtSQL Databases. MySQL, PostgreSQL and UnixODBC are supported. There might even be more like Oracle in the commercial Qt version or 3rd party backends.</li>
+<li>Runs embedded in Kexi (from the tools=>scripts menu) as well as independent of Kexi (use "krossrunner ~/.kde/share/apps/kexi/scripts/copycenter/CopyCenter.py" or python direct).</li>
+<li>Depends only on PyQt. PyKDE is not used at all and Kross (included in KOffice 1.5) is optional.</li>
+</ul>
+
+Author: (C)2006 Sebastian Sauer (mail at dipe dot org)<br>
+Website: http://www.kde-files.org/content/show.php?content=35251<br>
+License: GPL v2 or higher<br>
+
+</body></html>
diff --git a/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py
new file mode 100644
index 00000000..cace0340
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py
@@ -0,0 +1,196 @@
+"""
+Export table or query data.
+
+Description:
+This script exports a KexiDB table or query to different fileformats.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+class Datasource:
+ def __init__(self):
+ import kexiapp
+ keximainwindow = kexiapp.get("KexiAppMainWindow")
+
+ try:
+ self.connection = keximainwindow.getConnection()
+ except:
+ raise "No connection established. Please open a project before."
+
+ self.schema = None
+
+ def getSources(self):
+ sources = []
+ for table in self.connection.tableNames():
+ sources.append("Tables/%s" % table)
+ for query in self.connection.queryNames():
+ sources.append("Queries/%s" % query)
+ sources.sort()
+ return sources
+
+ def setSource(self, source):
+ s = source.split("/",1)
+ if s[0] == "Tables":
+ self.schema = self.connection.tableSchema( s[1] )
+ self.queryschema = self.schema.query()
+ elif s[0] == "Queries":
+ self.schema = self.connection.querySchema( s[1] )
+ self.queryschema = self.schema
+ self.cursor = None
+ return self.schema != None
+
+ def name(self):
+ return self.schema.name()
+
+ def caption(self):
+ return self.schema.caption()
+
+ def description(self):
+ return self.schema.description()
+
+ def header(self):
+ h = []
+ for field in self.schema.fieldlist().fields():
+ s = field.caption()
+ if s == None or s == "":
+ s = field.name()
+ h.append(s)
+ return h
+
+ def getNext(self):
+ if not self.cursor:
+ self.cursor = self.connection.executeQuerySchema( self.queryschema )
+ if not self.cursor:
+ raise "Failed to execute queryschema."
+ if not self.cursor.moveFirst():
+ raise "Failed to move cursor to first record."
+ if self.cursor.eof():
+ self.cursor = None
+ return None
+ items = []
+ for i in range( self.cursor.fieldCount() ):
+ items.append( self.cursor.value(i) )
+ self.cursor.moveNext()
+ return items
+
+class HtmlExporter:
+ def __init__(self, datasource):
+ self.datasource = datasource
+
+ def htmlescape(self, text):
+ import string
+ return string.replace(string.replace(string.replace(str(text),'&','&amp;'),'<','&lt;'),'>','&gt;')
+
+ def write(self, output, style):
+ name = self.datasource.name()
+
+ output.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n")
+ output.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n")
+ output.write("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n")
+ output.write("<head><title>%s</title>\n" % name)
+ output.write("<style type=\"text/css\">\n<!--\n")
+ if style == "Paper":
+ output.write("html { background-color:#efefef; }")
+ output.write("body { background-color:#fafafa; color:#303030; margin:1em; padding:1em; border:#606060 1px solid; }")
+ elif style == "Blues":
+ output.write("html { background-color:#0000aa; }")
+ output.write("body { background-color:#000066; color:#efefff; margin:1em; padding:1em; border:#00f 1px solid; }")
+ output.write("h1 { color:#0000ff; }")
+ output.write("th { color:#0000aa; }")
+ else:
+ output.write("html { background-color:#ffffff; color:#000; }")
+ output.write("body { margin:1em; }")
+ output.write("\n//-->\n</style>\n")
+ output.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n")
+ output.write("</head><body><h1>%s</h1>\n" % name)
+
+ caption = self.datasource.caption()
+ if caption and caption != name:
+ output.write("caption: %s<br />\n" % caption)
+
+ description = self.datasource.description()
+ if description:
+ output.write("description: %s<br />\n" % description)
+
+ #import datetime
+ #output.write("date: %s<br />" % datetime.datetime.now())
+
+ output.write("<table border='1'>\n")
+
+ output.write("<tr>")
+ for h in self.datasource.header():
+ output.write("<th>%s</th>" % h)
+ output.write("</tr>")
+
+ while 1 == 1:
+ items = self.datasource.getNext()
+ if items == None: break
+ output.write("<tr>")
+ for item in items:
+ u = unicode(str(self.htmlescape(item)),"latin-1")
+ output.write("<td>%s</td>" % u.encode("utf-8"))
+ output.write("</tr>\n")
+ output.write("</table>\n")
+ output.write("</body></html>\n")
+
+class GuiApp:
+ def __init__(self, datasource):
+ self.datasource = datasource
+
+ try:
+ import gui
+ except:
+ raise "Import of the Kross GUI module failed."
+
+ self.dialog = gui.Dialog("Export XHTML")
+ self.dialog.addLabel(self.dialog, "Export a table- or query-datasource to a XHTML-file.")
+
+ datasourceitems = self.datasource.getSources()
+ self.datasourcelist = self.dialog.addList(self.dialog, "Datasource:", datasourceitems)
+
+ styleitems = ["Plain", "Paper", "Blues"]
+ self.stylelist = self.dialog.addList(self.dialog, "Style:", styleitems)
+
+ #queryframe = Tkinter.Frame(frame)
+ #queryframe.pack()
+ #Tkinter.Label(queryframe, text="Table or query to export:").pack(side=Tkinter.LEFT)
+ #self.querycontent = Tkinter.StringVar()
+ #self.query = apply(Tkinter.OptionMenu, (queryframe, self.querycontent) + tuple( self.datasource.getSources() ))
+ #self.query.pack(side=Tkinter.LEFT)
+
+ self.file = self.dialog.addFileChooser(self.dialog,
+ "File:",
+ gui.getHome() + "/kexidata.xhtml",
+ (('XHTML files', '*.xhtml'),('All files', '*')))
+
+ btnframe = self.dialog.addFrame(self.dialog)
+ self.dialog.addButton(btnframe, "Export", self.doExport)
+ self.dialog.addButton(btnframe, "Cancel", self.dialog.close)
+
+ self.dialog.show()
+
+ def doExport(self):
+ file = str( self.file.get() )
+ query = str( self.datasourcelist.get() )
+ print "Exporting '%s' to file '%s' ..." % (query,file)
+
+ if not self.datasource.setSource(query):
+ raise "Invalid datasource selected."
+ #return
+
+ style = str( self.stylelist.get() )
+
+ f = open(file, "w")
+ global HtmlExporter
+ exporter = HtmlExporter(self.datasource)
+ exporter.write(f, style)
+ f.close()
+
+ print "Successfully exported '%s' to file %s" % (query,file)
+ self.dialog.close()
+
+GuiApp( Datasource() )
diff --git a/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc
new file mode 100644
index 00000000..11c1dcdf
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc
@@ -0,0 +1,8 @@
+<KrossScripting>
+ <ScriptAction
+ name="exportxhtml"
+ text="Export Data to XHTML File"
+ icon="fileexport"
+ interpreter="python"
+ file="ExportXHTML.py" />
+</KrossScripting>
diff --git a/kexi/plugins/scripting/scripts/exportxhtml/Makefile.am b/kexi/plugins/scripting/scripts/exportxhtml/Makefile.am
new file mode 100644
index 00000000..1c7b9ca6
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/exportxhtml/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+scriptsdir = $(kde_datadir)/kexi/scripts/exportxhtml
+scripts_SCRIPTS = ExportXHTML.py ExportXHTML.rc
diff --git a/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py
new file mode 100755
index 00000000..200b3dee
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py
@@ -0,0 +1,434 @@
+"""
+Import data from a XHTML file to a KexiDB table.
+
+Description:
+This script implements import of data from a XHTML file to a KexiDB table. The
+table needs to be an already existing table the data should be added to.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+class SaxInput:
+ """ The inputsource we like to import the data from. This class
+ provides us abstract access to the SAX XML parser we use internaly
+ to import data from the XML-file. """
+
+ xmlfile = None
+ """ The XML file we should read the content from. """
+
+ def __init__(self):
+ """ Constructor. """
+
+ # try to import the xml.sax python module.
+ try:
+ import xml.sax.saxlib
+ import xml.sax.saxexts
+ except:
+ raise "Import of the python xml.sax.saxlib module failed. This module is needed by the ImportXHTML python script."
+
+ def read(self, outputwriter):
+ """ Start reading and parsing the XML-file. """
+
+ import xml.sax.saxlib
+ import xml.sax.saxexts
+
+ class SaxHandler(xml.sax.saxlib.HandlerBase):
+ """ The SaxHandler is our event-handler SAX calls on
+ parsing the XML-file. """
+
+ tablebase = ["html","body","table"]
+ """ The table-base defines where we will find our table-tag
+ that holds all the data we are interessted at. The default
+ is to look at <html><body><table></table></body></html>. """
+
+ def __init__(self, inputreader, outputwriter):
+ """ Constructor. """
+
+ # The to a SaxInput instance pointing inputreader.
+ self.inputreader = inputreader
+ # The to a KexiDBOutput instance pointing outputwriter.
+ self.outputwriter = outputwriter
+ # The hierachy-level in the DOM-tree we are in.
+ self.level = 0
+ # Defines if we are in the with tablebase defined DOM-element.
+ self.intable = False
+
+ # Points to a KexiDBOutput.Record instance if we are in a DOM-element that defines a record.
+ self.record = None
+ # Points to a KexiDBOutput.Field instance if we are in a record's field.
+ self.field = None
+
+ def startDocument(self):
+ sys.stdout.write('=> Starting parsing\n')
+
+ def endDocument(self):
+ sys.stdout.write('=> Fineshed parsing\n')
+
+ def startElement(self, name, attrs):
+ """ This method is called by SAX if a DOM-element starts. """
+
+ if self.level < len(self.tablebase):
+ if self.tablebase[self.level] != name:
+ self.intable = False
+ else:
+ self.intable = True
+ self.level += 1
+ if not self.intable:
+ return
+
+ # Print some debugging-output to stdout.
+ for idx in range(self.level): sys.stdout.write(' ')
+ sys.stdout.write('Element: %s' % name)
+ for attrName in attrs.keys():
+ sys.stdout.write(' %s="%s"' % (attrName,attrs.get(attrName)))
+ sys.stdout.write('\n')
+
+ # handle tr-, th- and td-tags inside the table.
+ if name == "tr" and (self.level == len(self.tablebase) + 1):
+ self.record = self.outputwriter.Record()
+ elif name == "td" and (self.level == len(self.tablebase) + 2):
+ self.field = self.outputwriter.Field()
+ elif name == "th" and (self.level == len(self.tablebase) + 2):
+ self.field = self.outputwriter.Field()
+
+ def endElement(self, name):
+ """ This method is called by SAX if a DOM-Element ends. """
+
+ self.level -= 1
+ #sys.stdout.write('EndElement:%s level:%s len(self.tablebase):%s\n' % (name,self.level,len(self.tablebase)))
+
+ if self.record != None:
+ # a record is defined. so, we are looking for the matching
+ # end-tags to close a record or a field.
+ if name == "tr" and (self.level == len(self.tablebase)):
+ self.outputwriter.write(self.record)
+ self.record = None
+ self.field = None
+ elif name == "td" and (self.level == len(self.tablebase) + 1):
+ #if self.field == None:
+ # raise "Unexpected closing </td>"
+ self.record.setField( self.field )
+ self.field = None
+ elif name == "th" and (self.level == len(self.tablebase) + 1):
+ #if self.field == None:
+ # raise "Unexpected closing </td>"
+ self.record.setHeader( self.field )
+ self.field = None
+
+ def characters(self, chars, offset, length):
+ """ This method is called by SAX if the text-content of a DOM-Element
+ was parsed. """
+
+ if self.field != None:
+ # the xml-data is unicode and we need to encode it
+ # to latin-1 cause KexiDB deals only with latin-1.
+ u = unicode(chars[offset:offset+length])
+ self.field.append(u.encode("latin-1"))
+
+ # start the job
+ outputwriter.begin()
+ # create saxhandler to handle parsing events.
+ handler = SaxHandler(self, outputwriter)
+ # we need a sax-parser and connect it with the handler.
+ parser = xml.sax.saxexts.make_parser()
+ parser.setDocumentHandler(handler)
+ # open the XML-file, parse the content and close the file again.
+ f = file(self.xmlfile, 'r')
+ parser.parseFile(f)
+ f.close()
+ # job is done
+ outputwriter.end()
+
+class KexiDBOutput:
+ """ The destination target we like to import the data to. This class
+ provides abstract access to the KexiDB module. """
+
+ class Result:
+ """ Holds some informations about the import-result. """
+ def __init__(self, outputwriter):
+ self.outputwriter = outputwriter
+ # number of records successfully imported.
+ self.successcount = 0
+ # number of records where import failed.
+ self.failedcount = 0
+
+ def addLog(self, record, state):
+ import datetime
+ date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M.%S")
+ self.outputwriter.logfile.write("%s (%s) %s\n" % (date,state,str(record)))
+
+ def success(self, record):
+ """ Called if a record was written successfully. """
+ print "SUCCESS: %s" % str(record)
+ self.successcount += 1
+ if hasattr(self.outputwriter,"logfile"):
+ self.addLog(record, "Success")
+
+ def failed(self, record):
+ """ Called if we failed to write a record. """
+ print "FAILED: %s" % str(record)
+ self.failedcount += 1
+ if hasattr(self.outputwriter,"logfile"):
+ self.addLog(record, "Failed")
+
+ class Record:
+ """ A Record in the dataset. """
+ def __init__(self):
+ self.fields = []
+ def setHeader(self, headerfield):
+ self.fields.append( headerfield )
+ self.isHeader = True
+ def setField(self, field):
+ self.fields.append( field )
+ def __str__(self):
+ s = "["
+ for f in self.fields:
+ s += "%s, " % str(f)
+ return s + "]"
+
+ class Field:
+ """ A field in a record. """
+ def __init__(self):
+ self.content = []
+ def append(self, content):
+ self.content.append( content )
+ def __str__(self):
+ return "".join(self.content)
+
+ def __init__(self):
+ """ Constructor. """
+ import kexiapp
+ keximainwindow = kexiapp.get("KexiAppMainWindow")
+
+ try:
+ self.connection = keximainwindow.getConnection()
+ except:
+ raise "No connection established. Please open a project before."
+
+ self.fieldlist = None
+ self.headerrecord = None
+ self.mapping = {}
+
+ def begin(self):
+ """ Called before parsing starts. """
+ print "START JOB"
+ if self.fieldlist == None:
+ raise "Invalid tableschema or fieldlist!"
+ global KexiDBOutput
+ self.result = KexiDBOutput.Result(self)
+ if hasattr(self,"logfilename") and self.logfilename != None and self.logfilename != "":
+ self.logfile = open(self.logfilename,'w')
+
+ def end(self):
+ """ Called if parsing is fineshed. """
+ print "END JOB"
+ self.logfile = None
+ self.mapping = {}
+ #self.headerrecord = None
+
+ def getTables(self):
+ """ return a list of avaiable tablenames. """
+ tables = self.connection.tableNames()
+ tables.sort()
+ return tables
+
+ def setTable(self, tablename):
+ """ Set the tablename we like to import the data to. """
+ tableschema = self.connection.tableSchema(tablename)
+ if tableschema == None:
+ raise "There exists no table with the name '%s'!" % tablename
+ self.fieldlist = tableschema.fieldlist()
+ fields = self.fieldlist.fields()
+ for field in fields:
+ print "KexiDBOutput.setTable(%s): %s(%s)" % (tablename,field.name(),field.type())
+ print "names=%s" % self.fieldlist.names()
+
+ def setMapping(self, mapping):
+ """ Set the tablefieldname=xmlcolnr dictonary we should map the data to. """
+ self.mapping = mapping
+
+ def setLogFile(self, logfilename):
+ """ Set the name of the logfile. """
+ self.logfilename = logfilename
+
+ def write(self, record):
+ """ Write the record to the KexiDB table. """
+
+ if hasattr(record, "isHeader"):
+ self.headerrecord = record
+ return
+
+ sys.stdout.write('KexiDBOutput.write:')
+ for f in record.fields:
+ sys.stdout.write(' "%s"' % f)
+ sys.stdout.write('\n')
+
+ if hasattr(self,"onWrite"):
+ if not self.onWrite(record):
+ raise RuntimeError()
+ delattr(self,"onWrite")
+ self.fieldlist = self.fieldlist.subList( list( self.mapping ) )
+
+ # Translate a KexiDBOutput.Record into a list of values.
+ values = []
+ for k in self.fieldlist.names():
+ values.append( str(record.fields[ int(self.mapping[k]) ]) )
+ print "Import values: %s" % values
+
+ try:
+ if self.connection.insertRecord(self.fieldlist, values):
+ self.result.success(record)
+ else:
+ self.result.failed(record)
+ except:
+ err = self.connection.lastError()
+ raise Exception( "Failed to insert the record:\n%s\n\n%s" % (values,err) )
+ #raise Exception( "Failed to insert into table \"%s\" the record:\n%s\n%s" % (self.tableschema.name(),values,self.connection.lastError()) )
+
+class GuiApp:
+ """ The GUI-dialog displayed to let the user define the source
+ XML-file and the destination KexiDB table. """
+
+ class InitialDialog:
+ def __init__(self, guiapp):
+ self.guiapp = guiapp
+ self.ok = False
+
+ import gui
+ self.dialog = gui.Dialog("Import XHTML")
+ self.dialog.addLabel(self.dialog, "Import data from a XHTML-file to a KexiDB table.\n"
+ "The destination table needs to be an existing table the data should be added to.")
+ self.importfile = self.dialog.addFileChooser(self.dialog,
+ "Source File:",
+ gui.getHome() + "/kexidata.xhtml",
+ (('XHTML files', '*.xhtml'),('All files', '*')))
+
+ self.desttable = self.dialog.addList(self.dialog, "Destination Table:", self.guiapp.outputwriter.getTables())
+
+ #self.operation = self.dialog.addList(self.dialog, "Operation:", ("Insert","Update","Insert/Update"))
+ #self.error = self.dialog.addList(self.dialog, "On error:", ("Ask","Skip","Abort"))
+
+ self.logfile = self.dialog.addFileChooser(self.dialog,
+ "Log File:",
+ "",
+ (('Logfiles', '*.log'),('All files', '*')))
+
+ btnframe = self.dialog.addFrame(self.dialog)
+ self.dialog.addButton(btnframe, "Next", self.doNext)
+ self.dialog.addButton(btnframe, "Cancel", self.doCancel)
+ self.dialog.show()
+
+ def doCancel(self):
+ """ Called if the Cancel-button was pressed. """
+ self.dialog.close()
+ self.dialog = None
+ #self.guiapp.InitialDialog
+
+ def doNext(self):
+ """ Start to import the XML-file into the KexiDB table. """
+
+ self.guiapp.inputreader.xmlfile = str(self.importfile.get())
+ self.guiapp.outputwriter.setTable( str(self.desttable.get()) )
+ self.guiapp.outputwriter.setLogFile( str(self.logfile.get()) )
+
+ try:
+ self.guiapp.inputreader.read( self.guiapp.outputwriter )
+
+ msgbox = self.dialog.showMessageBox("info","Import done",
+ "Successfully imported records: %s\nFailed to import records: %s" % (self.guiapp.outputwriter.result.successcount, self.guiapp.outputwriter.result.failedcount) )
+ msgbox.show()
+
+ self.doCancel()
+ except RuntimeError, e:
+ pass
+ #except Exception, e:
+ # import traceback
+ # traceback.print_exc()
+ # msgbox = self.dialog.showMessageBox("error", "Error", e)
+ # msgbox.show()
+
+ class MapperDialog:
+ """ The dialog that provides us a way to map
+ XHTML columns to the destination table. """
+
+ def __init__(self, outputwriter, record):
+ self.outputwriter = outputwriter
+ self.ok = False
+ fieldlist = outputwriter.fieldlist
+
+ import gui
+ self.dlg = gui.Dialog("Import XHTML")
+ self.dlg.addLabel(self.dlg, "Define how the destination table should be mapped to the data from the XHTML file.")
+ values = ["",]
+ for i in range(len(record.fields)):
+ try:
+ values.append( "%s: %s" % (i,str(outputwriter.headerrecord.fields[i])) )
+ except:
+ values.append( "%s: (%s)" % (i,str(record.fields[i])) )
+
+ self.items = []
+ i = 0
+ for field in fieldlist.fields():
+ f = self.dlg.addFrame(self.dlg)
+
+ l = self.dlg.addList(f, "%s:" % field.name(), values)
+ self.items.append( (field,l) )
+
+ details = "%s:" % str( field.type() )
+ if field.isAutoInc(): details += "autoinc,"
+ if field.isUniqueKey(): details += "unique,"
+ if field.isNotNull(): details += "notnull,"
+ if field.isNotEmpty(): details += "notempty,"
+ self.dlg.addLabel(f, "(%s)" % details[:-1])
+
+ try:
+ variable = str( record.fields[i] )
+ try:
+ int(variable)
+ i += 1
+ if not field.isAutoInc():
+ l.set(i)
+ except ValueError, e:
+ if not field.type() in ("Integer","BigInteger","ShortInteger","Float","Double"):
+ i += 1
+ l.set(i)
+ except:
+ pass
+
+ btnframe = self.dlg.addFrame(self.dlg)
+ self.dlg.addButton(btnframe, "Next", self.doNext)
+ self.dlg.addButton(btnframe, "Cancel", self.dlg.close)
+ self.dlg.show()
+
+ def doNext(self):
+ mapping = {}
+ for item in self.items:
+ (field,l) = item
+ fieldname = field.name()
+ colnr = str( l.get() ).split(":",1)[0]
+ if colnr.isdigit():
+ print "Table field '%s' is mapped to XML column '%s'" % (fieldname,colnr)
+ mapping[ fieldname ] = colnr
+ self.outputwriter.setMapping(mapping)
+ self.ok = True
+ self.dlg.close()
+
+ def __init__(self, inputreader, outputwriter):
+ """ Constructor. """
+
+ self.inputreader = inputreader
+ self.outputwriter = outputwriter
+ self.outputwriter.onWrite = self.onWrite
+
+ self.InitialDialog(self)
+
+ def onWrite(self, record):
+ """ This method got called after the first record got
+ readed and before we start to import. """
+ return self.MapperDialog(self.outputwriter, record).ok
+
+GuiApp( SaxInput(), KexiDBOutput() )
diff --git a/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc
new file mode 100644
index 00000000..0cfe7718
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc
@@ -0,0 +1,8 @@
+<KrossScripting>
+ <ScriptAction
+ name="importxhtml"
+ text="Import Data From XHTML File"
+ icon="fileimport"
+ interpreter="python"
+ file="ImportXHTML.py" />
+</KrossScripting>
diff --git a/kexi/plugins/scripting/scripts/importxhtml/Makefile.am b/kexi/plugins/scripting/scripts/importxhtml/Makefile.am
new file mode 100644
index 00000000..a0a424fa
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/importxhtml/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+scriptsdir = $(kde_datadir)/kexi/scripts/importxhtml
+scripts_SCRIPTS = ImportXHTML.py ImportXHTML.rc
diff --git a/kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am b/kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am
new file mode 100644
index 00000000..9d32e165
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+scriptsdir = $(kde_datadir)/kexi/scripts/projectdocumentor
+scripts_SCRIPTS = ProjectDocumentor.py ProjectDocumentor.rc
diff --git a/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py
new file mode 100755
index 00000000..89a60301
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py
@@ -0,0 +1,186 @@
+"""
+Project Documentor
+
+Description:
+This script collects various informations about a Kexi project
+and exports them to a HTML file.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+class DataProvider:
+ def __init__(self):
+ import kexiapp
+ keximainwindow = kexiapp.get("KexiAppMainWindow")
+
+ try:
+ self.connection = keximainwindow.getConnection()
+ except:
+ raise "No connection established. Please open the project to be documented first."
+
+ def printConnection(self):
+ condata = self.connection.data()
+ infos = []
+ for item in ("caption", "description", "driverName", "hostName", "port", "userName", "fileName", "dbPath", "localSocketFileName", "serverInfoString"):
+ result = getattr(condata, item)()
+ if result != None and result != "" and (item != "port" or result != 0):
+ infos.append( (item, result) )
+ return infos
+
+ def printDriver(self):
+ driver = self.connection.driver()
+ result = [ ("Version", "%s.%s" % (driver.versionMajor(),driver.versionMinor())) ]
+ conlist = driver.connectionsList()
+ if len(conlist) > 0:
+ result.append( ("Connections",str(conlist)) )
+ return result
+
+ def printDatabases(self):
+ result = [ ("Current database", self.connection.currentDatabase()) ]
+ dbnames = self.connection.databaseNames()
+ if len(dbnames) > 0:
+ result.append( ("Databases",str(dbnames)) )
+ return result
+
+ def printTables(self):
+ result = []
+ for t in self.connection.tableNames():
+ tableschema = self.connection.tableSchema(t)
+ ti = []
+ for i in ("name", "caption", "description"):
+ v = getattr(tableschema,i)()
+ if v != None and v != "":
+ ti.append( (i,v) )
+ tf = []
+ for field in tableschema.fieldlist().fields():
+ tfi = []
+ for n in ("caption","description","type","subType","typeGroup","length","defaultValue"):
+ v = getattr(field,n)()
+ if v != None and v != "":
+ tfi.append( (n,v) )
+ props = []
+ for n in ("PrimaryKey","ForeignKey","AutoInc","UniqueKey","NotNull", "NotEmpty","Indexed","Unsigned"):
+ v = getattr(field,"is%s" % n)()
+ if v != None and v != "" and v != False and v != 0:
+ props.append( "%s " % n )
+ if len(props) > 0:
+ tfi.append( ("properties",props) )
+
+ tf.append( (field.name(), tfi) )
+ ti.append( ("fields", tf) )
+ if len(ti) > 0:
+ result.append( (t, ti) )
+ return result
+
+ def printQueries(self):
+ result = []
+ for q in self.connection.queryNames():
+ queryschema = self.connection.querySchema(q)
+ qi = []
+ for i in ("name", "caption", "description", "statement"):
+ v = getattr(queryschema,i)()
+ if v != None and v != "":
+ qi.append( (i,v) )
+ if len(qi) > 0:
+ result.append( (q, qi) )
+ return result
+
+class GuiApp:
+ def __init__(self, dataprovider):
+ self.dataprovider = dataprovider
+
+ try:
+ import gui
+ except:
+ raise "Import of the Kross GUI module failed."
+
+ self.dialog = gui.Dialog("Project Documentor")
+
+ self.dialog.addLabel(self.dialog, "Save information about the project to an HTML file.")
+
+ self.file = self.dialog.addFileChooser(self.dialog,
+ "File:",
+ gui.getHome() + "/projectdoc.html",
+ (('HTML files', '*.html'),('All files', '*')))
+
+ self.printCheckBoxes = {}
+ for d in dir(self.dataprovider):
+ if d.startswith("print"):
+ self.printCheckBoxes[d] = self.dialog.addCheckBox(self.dialog, d[5:], True)
+
+ #value = getattr(self.dataprovider,d)()
+ #if value != None and len(value) > 0:
+ # f.write("<h2>%s</h2>" % d[5:])
+ # f.write( self.toHTML(value) )
+
+ #self.exportProjectdetails =
+ #self.exportTableschemas = self.dialog.addCheckBox(self.dialog, "Table schemas", True)
+ #self.exportQueryschemas = self.dialog.addCheckBox(self.dialog, "Query schemas", True)
+
+ btnframe = self.dialog.addFrame(self.dialog)
+ self.dialog.addButton(btnframe, "Save", self.doSave)
+ self.dialog.addButton(btnframe, "Cancel", self.dialog.close)
+
+ self.dialog.show()
+
+ def toHTML(self, value):
+ import types
+ result = ""
+ if isinstance(value, types.TupleType):
+ result += "<ul>"
+ if len(value) == 1:
+ result += "<li>%s</li>" % value
+ elif len(value) == 2:
+ result += "<li>%s: %s</li>" % (value[0], self.toHTML(value[1]))
+ elif len(value) > 2:
+ for item in value:
+ i = self.toHTML(item)
+ if i != "":
+ result += "<li>%s</li>" % i
+ result += "</ul>"
+ elif isinstance(value, types.ListType):
+ for item in value:
+ result += "%s" % self.toHTML(item)
+ else:
+ result += "%s" % value
+ return result
+
+ def doSave(self):
+ file = str( self.file.get() )
+ print "Attempting to save project documentation to file: %s" % file
+
+ f = open(file, "w")
+
+ f.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>")
+ f.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 4.01 Strict//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11-strict.dtd\">")
+ f.write("<html><head><title>Project information</title>")
+ f.write("<style type=\"text/css\">")
+ f.write(" html { background-color:#fafafa; }")
+ f.write(" body { background-color:#ffffff; margin:1em; padding:1em; border:#99a 1px solid; color:#003; }")
+ f.write("</style>")
+ f.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />")
+ f.write("</head><body><h1>Project information</h1>")
+
+ for d in dir(self.dataprovider):
+ if d.startswith("print"):
+ print "GuiApp.doSave() CHECK %s" % d
+ a = self.printCheckBoxes[d]
+ if a and a.isChecked():
+ print "GuiApp.doSave() BEGIN %s" % d
+ value = getattr(self.dataprovider,d)()
+ if value != None and len(value) > 0:
+ f.write("<h2>%s</h2>" % d[5:])
+ f.write( self.toHTML(value) )
+ print "GuiApp.doSave() END %s" % d
+
+ f.close()
+
+ print "Successfully saved project documentation to file: %s" % file
+ self.dialog.close()
+
+GuiApp( DataProvider() )
+
diff --git a/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc
new file mode 100644
index 00000000..bb0f6c69
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc
@@ -0,0 +1,8 @@
+<KrossScripting>
+ <ScriptAction
+ name="projectdocumentor"
+ text="Project Documentation Generator"
+ icon="contents"
+ interpreter="python"
+ file="ProjectDocumentor.py" />
+</KrossScripting>
diff --git a/kexi/plugins/scripting/scripts/python/Makefile.am b/kexi/plugins/scripting/scripts/python/Makefile.am
new file mode 100644
index 00000000..4b31c35a
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/python/Makefile.am
@@ -0,0 +1,2 @@
+include $(top_srcdir)/kexi/Makefile.global
+SUBDIRS = kexiapp
diff --git a/kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am b/kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am
new file mode 100644
index 00000000..f0f0492d
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am
@@ -0,0 +1,2 @@
+kexiapppythondir = $(kde_datadir)/kexi/kross/python/kexiapp
+kexiapppython_SCRIPTS = __init__.py
diff --git a/kexi/plugins/scripting/scripts/python/kexiapp/__init__.py b/kexi/plugins/scripting/scripts/python/kexiapp/__init__.py
new file mode 100755
index 00000000..b5224304
--- /dev/null
+++ b/kexi/plugins/scripting/scripts/python/kexiapp/__init__.py
@@ -0,0 +1,25 @@
+"""
+Initializer for the krosskexiapp-module.
+
+Description:
+This module provides the entry-point for python scripts
+to work with a running Kexi application instance.
+
+Author:
+Sebastian Sauer <mail@dipe.org>
+
+Copyright:
+Dual-licensed under LGPL v2+higher and the BSD license.
+"""
+
+try:
+ import krosskexiapp
+except ImportError, e:
+ raise "Import of the Kross KexiApp module failed.\n%s" % e
+
+def get(modulename):
+ return krosskexiapp.get(modulename)
+
+def currentConnection():
+ mainwindow = krosskexiapp.get("KexiAppMainWindow")
+ return mainwindow.getConnection()
diff --git a/kexi/plugins/tables/Makefile.am b/kexi/plugins/tables/Makefile.am
new file mode 100644
index 00000000..0971a64e
--- /dev/null
+++ b/kexi/plugins/tables/Makefile.am
@@ -0,0 +1,28 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_table.la
+
+kexihandler_table_la_SOURCES = kexitablepart.cpp kexitabledesignerview.cpp kexitabledesignerview_p.cpp \
+ kexitabledesigner_dataview.cpp kexitabledesignercommands.cpp kexilookupcolumnpage.cpp
+
+kexihandler_table_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module
+kexihandler_table_la_LIBADD = $(top_builddir)/kexi/core/libkexicore.la \
+ $(top_builddir)/kexi/kexidb/libkexidb.la \
+ $(top_builddir)/kexi/widget/tableview/libkexidatatable.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/lib/koproperty/libkoproperty.la
+
+INCLUDES= $(KOFFICE_INCLUDES) \
+ -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget -I$(top_srcdir)/kexi/widget/tableview \
+ -I$(top_srcdir)/kexi/kexidb -I$(top_srcdir)/lib $(all_includes)
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=kexitablehandler.desktop
+
+rcdir = $(kde_datadir)/kexi
+rc_DATA = kexitablepartui.rc kexitablepartinstui.rc
+
+METASOURCES = AUTO
+
+include ../Makefile.common
diff --git a/kexi/plugins/tables/kexilookupcolumnpage.cpp b/kexi/plugins/tables/kexilookupcolumnpage.cpp
new file mode 100644
index 00000000..9df92794
--- /dev/null
+++ b/kexi/plugins/tables/kexilookupcolumnpage.cpp
@@ -0,0 +1,419 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexilookupcolumnpage.h"
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qheader.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+#include <ktoolbarbutton.h>
+#include <kdebug.h>
+#include <kpopupmenu.h>
+
+#include <widget/kexipropertyeditorview.h>
+#include <widget/kexidatasourcecombobox.h>
+#include <widget/kexifieldlistview.h>
+#include <widget/kexifieldcombobox.h>
+#include <widget/kexismalltoolbutton.h>
+#include <kexidb/connection.h>
+#include <kexiproject.h>
+
+#include <koproperty/property.h>
+#include <koproperty/utils.h>
+
+QString mimeTypeToType(const QString& mimeType)
+{
+ if (mimeType=="kexi/table")
+ return "table";
+ else if (mimeType=="kexi/query")
+ return "query";
+//! @todo more types
+ return mimeType;
+}
+
+QString typeToMimeType(const QString& type)
+{
+ if (type=="table")
+ return "kexi/table";
+ else if (type=="query")
+ return "kexi/query";
+//! @todo more types
+ return type;
+}
+
+//----------------------------------------------
+
+//! @internal
+class KexiLookupColumnPage::Private
+{
+ public:
+ Private()
+ : currentFieldUid(-1)
+ , insideClearRowSourceSelection(false)
+ , propertySetEnabled(true)
+ {
+ }
+ ~Private()
+ {
+ }
+
+ bool hasPropertySet() const {
+ return propertySet;
+ }
+
+ void setPropertySet(KoProperty::Set* aPropertySet) {
+ propertySet = aPropertySet;
+ }
+
+ QVariant propertyValue(const QCString& propertyName) const {
+ return propertySet ? propertySet->property(propertyName).value() : QVariant();
+ }
+
+ void changeProperty(const QCString &property, const QVariant &value)
+ {
+ if (!propertySetEnabled)
+ return;
+ propertySet->changeProperty(property, value);
+ }
+
+ void updateInfoLabelForPropertySet(const QString& textToDisplayForNullSet) {
+ KexiPropertyEditorView::updateInfoLabelForPropertySet(
+ objectInfoLabel, propertySet, textToDisplayForNullSet);
+ }
+
+ KexiDataSourceComboBox *rowSourceCombo;
+ KexiFieldComboBox *boundColumnCombo, *visibleColumnCombo;
+ KexiObjectInfoLabel *objectInfoLabel;
+ QLabel *rowSourceLabel, *boundColumnLabel, *visibleColumnLabel;
+ QToolButton *clearRowSourceButton, *gotoRowSourceButton, *clearBoundColumnButton,
+ *clearVisibleColumnButton;
+ //! Used only in assignPropertySet() to check whether we already have the set assigned
+ int currentFieldUid;
+
+ bool insideClearRowSourceSelection : 1;
+ //! True is changeProperty() works. Used to block updating properties when within assignPropertySet().
+ bool propertySetEnabled : 1;
+
+ private:
+ //! A property set that is displayed on the page.
+ //! The set is also updated after any change in this page's data.
+ QGuardedPtr<KoProperty::Set> propertySet;
+};
+
+//----------------------------------------------
+
+KexiLookupColumnPage::KexiLookupColumnPage(QWidget *parent)
+ : QWidget(parent)
+ , d(new Private())
+{
+ setName("KexiLookupColumnPage");
+
+ QVBoxLayout *vlyr = new QVBoxLayout(this);
+ d->objectInfoLabel = new KexiObjectInfoLabel(this, "KexiObjectInfoLabel");
+ vlyr->addWidget(d->objectInfoLabel);
+
+//todo d->noDataSourceAvailableSingleText = i18n("No data source could be assigned for this widget.");
+//todo d->noDataSourceAvailableMultiText = i18n("No data source could be assigned for multiple widgets.");
+
+ //-Row Source
+ QWidget *contents = new QWidget(this);
+ vlyr->addWidget(contents);
+ QVBoxLayout *contentsVlyr = new QVBoxLayout(contents);
+
+ QHBoxLayout *hlyr = new QHBoxLayout(contentsVlyr);
+ d->rowSourceLabel = new QLabel(i18n("Row source:"), contents);
+ d->rowSourceLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ d->rowSourceLabel->setMargin(2);
+ d->rowSourceLabel->setMinimumHeight(IconSize(KIcon::Small)+4);
+ d->rowSourceLabel->setAlignment(Qt::AlignLeft|Qt::AlignBottom);
+ hlyr->addWidget(d->rowSourceLabel);
+
+ d->gotoRowSourceButton = new KexiSmallToolButton(contents, QString::null, "goto", "gotoRowSourceButton");
+ d->gotoRowSourceButton->setMinimumHeight(d->rowSourceLabel->minimumHeight());
+ QToolTip::add(d->gotoRowSourceButton, i18n("Go to selected row source"));
+ hlyr->addWidget(d->gotoRowSourceButton);
+ connect(d->gotoRowSourceButton, SIGNAL(clicked()), this, SLOT(slotGotoSelectedRowSource()));
+
+ d->clearRowSourceButton = new KexiSmallToolButton(contents, QString::null,
+ "clear_left", "clearRowSourceButton");
+ d->clearRowSourceButton->setMinimumHeight(d->rowSourceLabel->minimumHeight());
+ QToolTip::add(d->clearRowSourceButton, i18n("Clear row source"));
+ hlyr->addWidget(d->clearRowSourceButton);
+ connect(d->clearRowSourceButton, SIGNAL(clicked()), this, SLOT(clearRowSourceSelection()));
+
+ d->rowSourceCombo = new KexiDataSourceComboBox(contents, "rowSourceCombo");
+ d->rowSourceLabel->setBuddy(d->rowSourceCombo);
+ contentsVlyr->addWidget(d->rowSourceCombo);
+
+ contentsVlyr->addSpacing(8);
+
+ //- Bound Column
+ hlyr = new QHBoxLayout(contentsVlyr);
+ d->boundColumnLabel = new QLabel(i18n("Bound column:"), contents);
+ d->boundColumnLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ d->boundColumnLabel->setMargin(2);
+ d->boundColumnLabel->setMinimumHeight(IconSize(KIcon::Small)+4);
+ d->boundColumnLabel->setAlignment(Qt::AlignLeft|Qt::AlignBottom);
+ hlyr->addWidget(d->boundColumnLabel);
+
+ d->clearBoundColumnButton = new KexiSmallToolButton(contents, QString::null,
+ "clear_left", "clearBoundColumnButton");
+ d->clearBoundColumnButton->setMinimumHeight(d->boundColumnLabel->minimumHeight());
+ QToolTip::add(d->clearBoundColumnButton, i18n("Clear bound column"));
+ hlyr->addWidget(d->clearBoundColumnButton);
+ connect(d->clearBoundColumnButton, SIGNAL(clicked()), this, SLOT(clearBoundColumnSelection()));
+
+ d->boundColumnCombo = new KexiFieldComboBox(contents, "boundColumnCombo");
+ d->boundColumnLabel->setBuddy(d->boundColumnCombo);
+ contentsVlyr->addWidget(d->boundColumnCombo);
+
+ contentsVlyr->addSpacing(8);
+
+ //- Visible Column
+ hlyr = new QHBoxLayout(contentsVlyr);
+ d->visibleColumnLabel = new QLabel(i18n("Visible column:"), contents);
+ d->visibleColumnLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ d->visibleColumnLabel->setMargin(2);
+ d->visibleColumnLabel->setMinimumHeight(IconSize(KIcon::Small)+4);
+ d->visibleColumnLabel->setAlignment(Qt::AlignLeft|Qt::AlignBottom);
+ hlyr->addWidget(d->visibleColumnLabel);
+
+ d->clearVisibleColumnButton = new KexiSmallToolButton(contents, QString::null,
+ "clear_left", "clearVisibleColumnButton");
+ d->clearVisibleColumnButton->setMinimumHeight(d->visibleColumnLabel->minimumHeight());
+ QToolTip::add(d->clearVisibleColumnButton, i18n("Clear visible column"));
+ hlyr->addWidget(d->clearVisibleColumnButton);
+ connect(d->clearVisibleColumnButton, SIGNAL(clicked()), this, SLOT(clearVisibleColumnSelection()));
+
+ d->visibleColumnCombo = new KexiFieldComboBox(contents, "visibleColumnCombo");
+ d->visibleColumnLabel->setBuddy(d->visibleColumnCombo);
+ contentsVlyr->addWidget(d->visibleColumnCombo);
+
+ vlyr->addStretch(1);
+
+ connect(d->rowSourceCombo, SIGNAL(textChanged(const QString &)),
+ this, SLOT(slotRowSourceTextChanged(const QString &)));
+ connect(d->rowSourceCombo, SIGNAL(dataSourceChanged()), this, SLOT(slotRowSourceChanged()));
+ connect(d->boundColumnCombo, SIGNAL(selected()), this, SLOT(slotBoundColumnSelected()));
+ connect(d->visibleColumnCombo, SIGNAL(selected()), this, SLOT(slotVisibleColumnSelected()));
+
+ clearBoundColumnSelection();
+ clearVisibleColumnSelection();
+}
+
+KexiLookupColumnPage::~KexiLookupColumnPage()
+{
+ delete d;
+}
+
+void KexiLookupColumnPage::setProject(KexiProject *prj)
+{
+ d->rowSourceCombo->setProject(prj,
+ true/*showTables*/, true/*showQueries*/
+ );
+ d->boundColumnCombo->setProject(prj);
+ d->visibleColumnCombo->setProject(prj);
+}
+
+void KexiLookupColumnPage::assignPropertySet(KoProperty::Set* propertySet)
+{
+ if (!d->hasPropertySet() && !propertySet)
+ return;
+ if (propertySet && d->currentFieldUid == (*propertySet)["uid"].value().toInt())
+ return; //already assigned
+
+ d->propertySetEnabled = false;
+ d->setPropertySet( propertySet );
+ d->updateInfoLabelForPropertySet( i18n("No field selected") );
+
+ const bool hasRowSource = d->hasPropertySet() && !d->propertyValue("rowSourceType").isNull()
+ && !d->propertyValue("rowSource").isNull();
+
+ QString rowSource, rowSourceType;
+ if (hasRowSource) {
+ rowSourceType = typeToMimeType( d->propertyValue("rowSourceType").toString() );
+ rowSource = d->propertyValue("rowSource").toString();
+ }
+ d->rowSourceCombo->setDataSource( rowSourceType, rowSource );
+ d->rowSourceLabel->setEnabled( d->hasPropertySet() );
+ d->rowSourceCombo->setEnabled( d->hasPropertySet() );
+ if (!d->hasPropertySet())
+ d->clearRowSourceButton->setEnabled( false );
+
+ int boundColumn = -1, visibleColumn = -1;
+ if (d->rowSourceCombo->isSelectionValid()) {
+ boundColumn = d->propertyValue("boundColumn").toInt();
+ visibleColumn = d->propertyValue("visibleColumn").toInt();
+ }
+ d->boundColumnCombo->setFieldOrExpression(boundColumn);
+ d->visibleColumnCombo->setFieldOrExpression(visibleColumn);
+ updateBoundColumnWidgetsAvailability();
+ d->propertySetEnabled = true;
+}
+
+void KexiLookupColumnPage::clearBoundColumnSelection()
+{
+ d->boundColumnCombo->setCurrentText("");
+ d->boundColumnCombo->setFieldOrExpression(QString::null);
+ slotBoundColumnSelected();
+ d->clearBoundColumnButton->setEnabled(false);
+}
+
+void KexiLookupColumnPage::slotBoundColumnSelected()
+{
+// KexiDB::Field::Type dataType = KexiDB::Field::InvalidType;
+//! @todo this should also work for expressions
+/*disabled KexiDB::Field *field = d->fieldListView->schema()->field( d->boundColumnCombo->fieldOrExpression() );
+ if (field)
+ dataType = field->type();
+*/
+ d->clearBoundColumnButton->setEnabled( !d->boundColumnCombo->fieldOrExpression().isEmpty() );
+ if (!d->boundColumnCombo->fieldOrExpression().isEmpty()) {
+ kdDebug() << endl;
+ }
+
+ // update property set
+ if (d->hasPropertySet()) {
+ d->changeProperty("boundColumn", d->boundColumnCombo->indexOfField());
+ }
+/*
+ emit boundColumnChanged(
+ d->boundColumnCombo->fieldOrExpression(),
+ d->boundColumnCombo->fieldOrExpressionCaption(),
+ dataType
+ );*/
+}
+
+void KexiLookupColumnPage::clearVisibleColumnSelection()
+{
+ d->visibleColumnCombo->setCurrentText("");
+ d->visibleColumnCombo->setFieldOrExpression(QString::null);
+ slotVisibleColumnSelected();
+ d->clearVisibleColumnButton->setEnabled(false);
+}
+
+void KexiLookupColumnPage::slotVisibleColumnSelected()
+{
+// KexiDB::Field::Type dataType = KexiDB::Field::InvalidType;
+//! @todo this should also work for expressions
+ d->clearVisibleColumnButton->setEnabled( !d->visibleColumnCombo->fieldOrExpression().isEmpty() );
+
+ // update property set
+ if (d->hasPropertySet()) {
+//! @todo support expression in special "visibleExpression"
+ d->changeProperty("visibleColumn", d->visibleColumnCombo->indexOfField());
+ }
+}
+
+void KexiLookupColumnPage::slotRowSourceChanged()
+{
+ if (!d->rowSourceCombo->project())
+ return;
+ QString mime = d->rowSourceCombo->selectedMimeType();
+ bool rowSourceFound = false;
+ QString name = d->rowSourceCombo->selectedName();
+ if ((mime=="kexi/table" || mime=="kexi/query") && d->rowSourceCombo->isSelectionValid()) {
+ KexiDB::TableOrQuerySchema *tableOrQuery = new KexiDB::TableOrQuerySchema(
+ d->rowSourceCombo->project()->dbConnection(), name.latin1(), mime=="kexi/table");
+ if (tableOrQuery->table() || tableOrQuery->query()) {
+//disabled d->fieldListView->setSchema( tableOrQuery );
+/*tmp*/ delete tableOrQuery;
+ rowSourceFound = true;
+ d->boundColumnCombo->setTableOrQuery(name, mime=="kexi/table");
+ d->visibleColumnCombo->setTableOrQuery(name, mime=="kexi/table");
+ }
+ else {
+ delete tableOrQuery;
+ }
+ }
+ if (!rowSourceFound) {
+ d->boundColumnCombo->setTableOrQuery("", true);
+ d->visibleColumnCombo->setTableOrQuery("", true);
+ }
+ clearBoundColumnSelection();
+ clearVisibleColumnSelection();
+ d->clearRowSourceButton->setEnabled(rowSourceFound);
+ d->gotoRowSourceButton->setEnabled(rowSourceFound);
+/* disabled
+ if (dataSourceFound) {
+ slotFieldListViewSelectionChanged();
+ } else {
+ d->addField->setEnabled(false);
+ }*/
+ updateBoundColumnWidgetsAvailability();
+
+ //update property set
+ if (d->hasPropertySet()) {
+ d->changeProperty("rowSourceType", mimeTypeToType(mime));
+ d->changeProperty("rowSource", name);
+ }
+
+//disabled emit formDataSourceChanged(mime, name);
+//! @todo update d->propertySet ^^
+}
+
+void KexiLookupColumnPage::slotRowSourceTextChanged(const QString & string)
+{
+ Q_UNUSED(string);
+ const bool enable = d->rowSourceCombo->isSelectionValid();
+ if (enable) {
+ updateBoundColumnWidgetsAvailability();
+ }
+ else {
+ clearRowSourceSelection( d->rowSourceCombo->selectedName().isEmpty()/*alsoClearComboBox*/ );
+ }
+}
+
+void KexiLookupColumnPage::clearRowSourceSelection(bool alsoClearComboBox)
+{
+ if (d->insideClearRowSourceSelection)
+ return;
+ d->insideClearRowSourceSelection = true;
+ if (alsoClearComboBox && !d->rowSourceCombo->selectedName().isEmpty())
+ d->rowSourceCombo->setDataSource("", "");
+ d->clearRowSourceButton->setEnabled(false);
+ d->gotoRowSourceButton->setEnabled(false);
+ d->insideClearRowSourceSelection = false;
+}
+
+void KexiLookupColumnPage::slotGotoSelectedRowSource()
+{
+ QString mime = d->rowSourceCombo->selectedMimeType();
+ if (mime=="kexi/table" || mime=="kexi/query") {
+ if (d->rowSourceCombo->isSelectionValid())
+ emit jumpToObjectRequested(mime.latin1(), d->rowSourceCombo->selectedName().latin1());
+ }
+}
+
+void KexiLookupColumnPage::updateBoundColumnWidgetsAvailability()
+{
+ const bool hasRowSource = d->rowSourceCombo->isSelectionValid();
+ d->boundColumnCombo->setEnabled( hasRowSource );
+ d->boundColumnLabel->setEnabled( hasRowSource );
+ d->clearBoundColumnButton->setEnabled( hasRowSource && !d->boundColumnCombo->fieldOrExpression().isEmpty() );
+ d->visibleColumnCombo->setEnabled( hasRowSource );
+ d->visibleColumnLabel->setEnabled( hasRowSource );
+ d->clearVisibleColumnButton->setEnabled( hasRowSource && !d->visibleColumnCombo->fieldOrExpression().isEmpty() );
+}
+
+#include "kexilookupcolumnpage.moc"
diff --git a/kexi/plugins/tables/kexilookupcolumnpage.h b/kexi/plugins/tables/kexilookupcolumnpage.h
new file mode 100644
index 00000000..457b2e3d
--- /dev/null
+++ b/kexi/plugins/tables/kexilookupcolumnpage.h
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+#ifndef KEXILOOKUPCOLUMNPAGE_H
+#define KEXILOOKUPCOLUMNPAGE_H
+
+#include <qwidget.h>
+#include <kexidb/field.h>
+#include <kexidb/utils.h>
+#include <koproperty/set.h>
+
+class KCommand;
+class KexiObjectInfoLabel;
+class KexiDataSourceComboBox;
+class KexiFieldComboBox;
+class KexiFieldListView;
+class KexiProject;
+class KexiSmallToolButton;
+class QToolButton;
+class QLabel;
+class QFrame;
+
+//! @short A page within table designer's property pane, providing lookup column editor.
+/*! It's data model is basically KexiDB::LookupFieldSchema class, but the page does
+ not create it directly but instead updates a property set that defines
+ the field currently selected in the designer.
+
+ @todo not all features of KexiDB::LookupFieldSchema class are displayed on this page yet
+ */
+class KexiLookupColumnPage : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KexiLookupColumnPage(QWidget *parent);
+ virtual ~KexiLookupColumnPage();
+
+ public slots:
+ void setProject(KexiProject *prj);
+ void clearRowSourceSelection(bool alsoClearComboBox = true);
+ void clearBoundColumnSelection();
+ void clearVisibleColumnSelection();
+
+ //! Receives a pointer to a new property \a set (from KexiFormView::managerPropertyChanged())
+ void assignPropertySet(KoProperty::Set* propertySet);
+
+ signals:
+ //! Signal emitted when helper button 'Go to selected row sourcesource' is clicked.
+ void jumpToObjectRequested(const QCString& mime, const QCString& name);
+
+// /*! Signal emitted when current bound column has been changed. */
+// void boundColumnChanged(const QString& string, const QString& caption,
+ // KexiDB::Field::Type type);
+
+ protected slots:
+ void slotRowSourceTextChanged(const QString & string);
+ void slotRowSourceChanged();
+ void slotGotoSelectedRowSource();
+ void slotBoundColumnSelected();
+ void slotVisibleColumnSelected();
+
+ protected:
+ void updateBoundColumnWidgetsAvailability();
+
+ //! Used instead of m_propertySet->changeProperty() to honor m_propertySetEnabled
+ void changeProperty(const QCString &property, const QVariant &value);
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/kexi/plugins/tables/kexitabledesigner_dataview.cpp b/kexi/plugins/tables/kexitabledesigner_dataview.cpp
new file mode 100644
index 00000000..bea2d9f5
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesigner_dataview.cpp
@@ -0,0 +1,79 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexitabledesigner_dataview.h"
+
+#include <kexidb/connection.h>
+#include <kexidb/cursor.h>
+#include <kexiutils/utils.h>
+#include "kexitableview.h"
+#include "kexidatatableview.h"
+#include "keximainwindow.h"
+
+KexiTableDesigner_DataView::KexiTableDesigner_DataView(KexiMainWindow *win, QWidget *parent)
+ : KexiDataTable(win, parent, "KexiTableDesigner_DataView", true/*db-aware*/)
+{
+}
+
+KexiTableDesigner_DataView::~KexiTableDesigner_DataView()
+{
+ if (dynamic_cast<KexiDataTableView*>(tableView())
+ && dynamic_cast<KexiDataTableView*>(tableView())->cursor())
+ {
+ mainWin()->project()->dbConnection()->deleteCursor(
+ dynamic_cast<KexiDataTableView*>(tableView())->cursor() );
+ }
+}
+
+tristate KexiTableDesigner_DataView::beforeSwitchTo(int mode, bool &dontStore)
+{
+ Q_UNUSED( dontStore );
+
+ if (mode != Kexi::DataViewMode) {
+ //accept editing before switching
+// if (!m_view->acceptRowEdit()) {
+ if (!acceptRowEdit()) {
+ return cancelled;
+ }
+ }
+
+ return true;
+}
+
+tristate KexiTableDesigner_DataView::afterSwitchFrom(int mode)
+{
+ Q_UNUSED( mode );
+
+ if (tempData()->tableSchemaChangedInPreviousView) {
+ KexiUtils::WaitCursor wait;
+ KexiDB::Cursor *c = mainWin()->project()->dbConnection()->prepareQuery(*tempData()->table);
+ if (!c)
+ return false;
+ setData(c);
+ tempData()->tableSchemaChangedInPreviousView = false;
+ }
+ return true;
+}
+
+KexiTablePart::TempData* KexiTableDesigner_DataView::tempData() const
+{
+ return static_cast<KexiTablePart::TempData*>(parentDialog()->tempData());
+}
+
+#include "kexitabledesigner_dataview.moc"
diff --git a/kexi/plugins/tables/kexitabledesigner_dataview.h b/kexi/plugins/tables/kexitabledesigner_dataview.h
new file mode 100644
index 00000000..59e84ab1
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesigner_dataview.h
@@ -0,0 +1,49 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXITABLEDESIGNERDATAVIEW_H
+#define KEXITABLEDESIGNERDATAVIEW_H
+
+#include <kexidatatable.h>
+#include "kexitablepart.h"
+
+class KexiTableDesigner_DataView : public KexiDataTable
+{
+ Q_OBJECT
+
+ public:
+ KexiTableDesigner_DataView(KexiMainWindow *win, QWidget *parent);
+
+ virtual ~KexiTableDesigner_DataView();
+
+ KexiTablePart::TempData* tempData() const;
+
+ protected:
+// //! called just once from ctor
+// void init();
+// void initActions();
+// //! called whenever data should be reloaded (on switching to this view mode)
+// void initData();
+
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+ virtual tristate afterSwitchFrom(int mode);
+
+};
+
+#endif
diff --git a/kexi/plugins/tables/kexitabledesignercommands.cpp b/kexi/plugins/tables/kexitabledesignercommands.cpp
new file mode 100644
index 00000000..ccbb181a
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignercommands.cpp
@@ -0,0 +1,281 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+#include <qdom.h>
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qsplitter.h>
+#include <qmetaobject.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <kaccelmanager.h>
+
+#include <koproperty/property.h>
+
+#include "kexitabledesignercommands.h"
+
+using namespace KexiTableDesignerCommands;
+
+
+Command::Command(KexiTableDesignerView* view)
+ : KCommand()
+ , m_view(view)
+{
+}
+
+Command::~Command()
+{
+}
+
+//--------------------------------------------------------
+
+ChangeFieldPropertyCommand::ChangeFieldPropertyCommand( KexiTableDesignerView* view,
+ const KoProperty::Set& set, const QCString& propertyName, const QVariant& oldValue, const QVariant& newValue,
+ KoProperty::Property::ListData* const oldListData, KoProperty::Property::ListData* const newListData)
+ : Command(view)
+ , m_alterTableAction(
+ propertyName=="name" ? oldValue.toString() : set.property("name").value().toString(),
+ propertyName, newValue, set["uid"].value().toInt())
+ , m_oldValue(oldValue)
+// , m_fieldUID(set["uid"].value().toInt())
+ , m_oldListData( oldListData ? new KoProperty::Property::ListData(*oldListData) : 0 )
+ , m_listData( newListData ? new KoProperty::Property::ListData(*newListData) : 0 )
+{
+ kexipluginsdbg << "ChangeFieldPropertyCommand: " << debugString() << endl;
+}
+
+ChangeFieldPropertyCommand::~ChangeFieldPropertyCommand()
+{
+ delete m_oldListData;
+ delete m_listData;
+}
+
+QString ChangeFieldPropertyCommand::name() const
+{
+ return i18n("Change \"%1\" property for table field from \"%2\" to \"%3\"")
+ .arg(m_alterTableAction.propertyName()).arg(m_oldValue.toString())
+ .arg(m_alterTableAction.newValue().toString());
+}
+
+QString ChangeFieldPropertyCommand::debugString()
+{
+ QString s( name() );
+ if (m_oldListData || m_listData)
+ s += QString("\nAnd list data from [%1]\n to [%2]")
+ .arg( m_oldListData ?
+ QString("%1 -> %2")
+ .arg(m_oldListData->keysAsStringList().join(",")).arg(m_oldListData->names.join(","))
+ : QString("<NONE>"))
+ .arg( m_listData ?
+ QString("%1 -> %2")
+ .arg(m_listData->keysAsStringList().join(",")).arg(m_listData->names.join(","))
+ : QString("<NONE>"));
+ return s + QString(" (UID=%1)").arg(m_alterTableAction.uid());
+}
+
+void ChangeFieldPropertyCommand::execute()
+{
+ m_view->changeFieldProperty(
+ m_alterTableAction.uid(),
+ m_alterTableAction.propertyName().latin1(),
+ m_alterTableAction.newValue(), m_listData );
+}
+
+void ChangeFieldPropertyCommand::unexecute()
+{
+ m_view->changeFieldProperty(
+ m_alterTableAction.uid(),
+ m_alterTableAction.propertyName().latin1(),
+ m_oldValue, m_oldListData );
+}
+
+KexiDB::AlterTableHandler::ActionBase* ChangeFieldPropertyCommand::createAction()
+{
+ if (m_alterTableAction.propertyName()=="subType") {//skip these properties
+ return 0;
+ }
+ return new KexiDB::AlterTableHandler::ChangeFieldPropertyAction( m_alterTableAction );
+}
+
+//--------------------------------------------------------
+
+RemoveFieldCommand::RemoveFieldCommand( KexiTableDesignerView* view, int fieldIndex,
+ const KoProperty::Set* set)
+ : Command(view)
+ , m_alterTableAction( set ? (*set)["name"].value().toString() : QString::null,
+ set ? (*set)["uid"].value().toInt() : -1 )
+ , m_set( set ? new KoProperty::Set(*set /*deep copy*/) : 0 )
+ , m_fieldIndex(fieldIndex)
+{
+}
+
+RemoveFieldCommand::~RemoveFieldCommand()
+{
+ delete m_set;
+}
+
+QString RemoveFieldCommand::name() const
+{
+ if (m_set)
+ return i18n("Remove table field \"%1\"").arg(m_alterTableAction.fieldName());
+
+ return QString("Remove empty row at position %1").arg(m_fieldIndex);
+}
+
+void RemoveFieldCommand::execute()
+{
+// m_view->deleteField( m_fieldIndex );
+ m_view->deleteRow( m_fieldIndex );
+}
+
+void RemoveFieldCommand::unexecute()
+{
+ m_view->insertEmptyRow(m_fieldIndex);
+ if (m_set)
+ m_view->insertField( m_fieldIndex, *m_set );
+}
+
+QString RemoveFieldCommand::debugString()
+{
+ if (!m_set)
+ return name();
+
+ return name() + "\nAT ROW " + QString::number(m_fieldIndex)
+ + ", FIELD: " + (*m_set)["caption"].value().toString()
+ + QString(" (UID=%1)").arg(m_alterTableAction.uid());
+}
+
+KexiDB::AlterTableHandler::ActionBase* RemoveFieldCommand::createAction()
+{
+ return new KexiDB::AlterTableHandler::RemoveFieldAction( m_alterTableAction );
+}
+
+//--------------------------------------------------------
+
+InsertFieldCommand::InsertFieldCommand( KexiTableDesignerView* view,
+ int fieldIndex/*, const KexiDB::Field& field*/, const KoProperty::Set& set )
+ : Command(view)
+ , m_alterTableAction(0) //fieldIndex, new KexiDB::Field(field) /*deep copy*/)
+ , m_set( set ) //? new KoProperty::Set(*set) : 0 )
+{
+ KexiDB::Field *f = view->buildField( m_set );
+ if (f)
+ m_alterTableAction = new KexiDB::AlterTableHandler::InsertFieldAction(
+ fieldIndex, f, set["uid"].value().toInt());
+ else //null action
+ m_alterTableAction = new KexiDB::AlterTableHandler::InsertFieldAction(true);
+}
+
+InsertFieldCommand::~InsertFieldCommand()
+{
+ delete m_alterTableAction;
+}
+
+QString InsertFieldCommand::name() const
+{
+ return i18n("Insert table field \"%1\"").arg(m_set["caption"].value().toString());
+}
+
+void InsertFieldCommand::execute()
+{
+ m_view->insertField( m_alterTableAction->index(), /*m_alterTableAction.field(),*/ m_set );
+}
+
+void InsertFieldCommand::unexecute()
+{
+ m_view->clearRow( m_alterTableAction->index() );//m_alterTableAction.index() );
+}
+
+KexiDB::AlterTableHandler::ActionBase* InsertFieldCommand::createAction()
+{
+ return new KexiDB::AlterTableHandler::InsertFieldAction(*m_alterTableAction);
+}
+
+//--------------------------------------------------------
+
+ChangePropertyVisibilityCommand::ChangePropertyVisibilityCommand( KexiTableDesignerView* view,
+ const KoProperty::Set& set, const QCString& propertyName, bool visible)
+ : Command(view)
+ , m_alterTableAction(set.property("name").value().toString(), propertyName, visible, set["uid"].value().toInt())
+// , m_fieldUID(set["uid"].value().toInt())
+ , m_oldVisibility( set.property(propertyName).isVisible() )
+{
+ kexipluginsdbg << "ChangePropertyVisibilityCommand: " << debugString() << endl;
+}
+
+ChangePropertyVisibilityCommand::~ChangePropertyVisibilityCommand()
+{
+}
+
+QString ChangePropertyVisibilityCommand::name() const
+{
+ return QString("[internal] Change \"%1\" visibility from \"%2\" to \"%3\"")
+ .arg(m_alterTableAction.propertyName())
+ .arg(m_oldVisibility ? "true" : "false")
+ .arg(m_alterTableAction.newValue().toBool() ? "true" : "false");
+}
+
+void ChangePropertyVisibilityCommand::execute()
+{
+ m_view->changePropertyVisibility(
+ m_alterTableAction.uid(),
+ m_alterTableAction.propertyName().latin1(),
+ m_alterTableAction.newValue().toBool() );
+}
+
+void ChangePropertyVisibilityCommand::unexecute()
+{
+ m_view->changePropertyVisibility(
+ m_alterTableAction.uid(),
+ m_alterTableAction.propertyName().latin1(),
+ m_oldVisibility );
+}
+
+//--------------------------------------------------------
+
+InsertEmptyRowCommand::InsertEmptyRowCommand( KexiTableDesignerView* view, int row )
+ : Command(view)
+ , m_alterTableAction(true) //unused, null action
+ , m_row(row)
+{
+}
+
+InsertEmptyRowCommand::~InsertEmptyRowCommand()
+{
+}
+
+QString InsertEmptyRowCommand::name() const
+{
+ return QString("Insert empty row at position %1").arg(m_row);
+}
+
+void InsertEmptyRowCommand::execute()
+{
+ m_view->insertEmptyRow( m_row );
+}
+
+void InsertEmptyRowCommand::unexecute()
+{
+ // let's assume the row is empty...
+ m_view->deleteRow( m_row );
+}
+
diff --git a/kexi/plugins/tables/kexitabledesignercommands.h b/kexi/plugins/tables/kexitabledesignercommands.h
new file mode 100644
index 00000000..355aabe2
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignercommands.h
@@ -0,0 +1,188 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXITABLEDESIGNER_COMMANDS_H
+#define KEXITABLEDESIGNER_COMMANDS_H
+
+#include <qmap.h>
+#include <qdict.h>
+#include <qptrlist.h>
+#include <qptrdict.h>
+#include <qvariant.h>
+#include <qguardedptr.h>
+
+#include <kcommand.h>
+#include <kexidb/alter.h>
+#include <koproperty/set.h>
+
+#include "kexitabledesignerview.h"
+
+class QWidget;
+class QRect;
+class QPoint;
+class QStringList;
+class QCString;
+
+namespace KexiTableDesignerCommands {
+
+//! @short Base class for all Table Designer's commands
+class Command : public KCommand
+{
+ public:
+ Command(KexiTableDesignerView* view);
+ virtual ~Command();
+
+ //! Used to collect actions data for AlterTableHandler
+ //! Can return 0 if the action should not be passed to AlterTableHandler
+ virtual KexiDB::AlterTableHandler::ActionBase* createAction() { return 0; }
+
+ virtual QString debugString() { return name(); }
+
+ protected:
+ QGuardedPtr<KexiTableDesignerView> m_view;
+};
+
+//! @short Undo/redo command used for when changing a property for a table field
+class ChangeFieldPropertyCommand : public Command
+{
+ public:
+ /*! Creates the ChangeFieldPropertyCommand object.
+ Note: we use internal "uid" property of a field (set["uid"]) to avoid problems with looking
+ for field by name when more than one field exists with the same name
+ (it's invalid but allowed in design time).
+ \a oldlistData and and \a newListData can be specified so Property::setListData() will be called
+ on execute() and unexecute().
+ */
+ ChangeFieldPropertyCommand( KexiTableDesignerView* view,
+ const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& oldValue, const QVariant& newValue,
+ KoProperty::Property::ListData* const oldListData = 0, KoProperty::Property::ListData* const newListData = 0);
+
+ virtual ~ChangeFieldPropertyCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+ virtual KexiDB::AlterTableHandler::ActionBase* createAction();
+ virtual QString debugString();
+
+ protected:
+ KexiDB::AlterTableHandler::ChangeFieldPropertyAction m_alterTableAction;
+ QVariant m_oldValue;
+// int m_fieldUID;
+ KoProperty::Property::ListData* m_oldListData, *m_listData;
+};
+
+//! @short Undo/redo command used when a field is removed from a table
+class RemoveFieldCommand : public Command
+{
+ public:
+ /*! Constructs RemoveFieldCommand object.
+ If \a set is 0, the action only means removing empty row (internal). */
+ RemoveFieldCommand( KexiTableDesignerView* view, int fieldIndex,
+ const KoProperty::Set* set);
+
+ virtual ~RemoveFieldCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+ virtual KexiDB::AlterTableHandler::ActionBase* createAction();
+
+ virtual QString debugString();
+
+ protected:
+ KexiDB::AlterTableHandler::RemoveFieldAction m_alterTableAction;
+ KoProperty::Set* m_set;
+ int m_fieldIndex;
+};
+
+//! @short Undo/redo command used when a new field is inserted into a table
+class InsertFieldCommand : public Command
+{
+ public:
+ InsertFieldCommand( KexiTableDesignerView* view,
+ int fieldIndex/*, const KexiDB::Field& field*/, const KoProperty::Set& set );
+ virtual ~InsertFieldCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+ virtual KexiDB::AlterTableHandler::ActionBase* createAction();
+
+ virtual QString debugString() {
+ return name() + "\nAT ROW " + QString::number(m_alterTableAction->index()) //m_alterTableAction.index())
+ + ", FIELD: " + m_set["caption"].value().toString(); //m_alterTableAction.field().debugString();
+ }
+
+ protected:
+ KexiDB::AlterTableHandler::InsertFieldAction *m_alterTableAction;
+ KoProperty::Set m_set;
+};
+
+
+/* ---- Internal commands follow (not used for building performing ALTER TABLE ---- */
+
+//! @short Undo/redo command used when property visibility is changed
+/*! Internal, only used in addition to property change. */
+class ChangePropertyVisibilityCommand : public Command
+{
+ public:
+ /*! Creates the ChangePropertyVisibilityCommand object.
+ Note: we use internal "uid" property of a field (set["uid"]) to avoid problems with looking
+ for field by name when more than one field exists with the same name
+ (it's invalid but allowed in design time).
+ */
+ ChangePropertyVisibilityCommand( KexiTableDesignerView* view,
+ const KoProperty::Set& set, const QCString& propertyName,
+ bool visible);
+
+ virtual ~ChangePropertyVisibilityCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+
+ protected:
+ KexiDB::AlterTableHandler::ChangeFieldPropertyAction m_alterTableAction;
+// int m_fieldUID;
+ bool m_oldVisibility;
+};
+
+//! @short Undo/redo command used when property visibility is changed
+/*! Internal, only used in addition to property change. */
+class InsertEmptyRowCommand : public Command
+{
+ public:
+ /*! Creates the InsertEmptyRowCommand object. */
+ InsertEmptyRowCommand( KexiTableDesignerView* view, int row );
+ virtual ~InsertEmptyRowCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+
+ protected:
+ KexiDB::AlterTableHandler::ChangeFieldPropertyAction m_alterTableAction;
+ int m_row;
+};
+
+}
+
+#endif
diff --git a/kexi/plugins/tables/kexitabledesignerview.cpp b/kexi/plugins/tables/kexitabledesignerview.cpp
new file mode 100644
index 00000000..7e3478ed
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignerview.cpp
@@ -0,0 +1,1943 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexitabledesignerview.h"
+#include "kexitabledesignerview_p.h"
+#include "kexilookupcolumnpage.h"
+#include "kexitabledesignercommands.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qsplitter.h>
+
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <kiconeffect.h>
+
+#include <koproperty/set.h>
+#include <koproperty/utils.h>
+
+#include <kexidb/cursor.h>
+#include <kexidb/tableschema.h>
+#include <kexidb/connection.h>
+#include <kexidb/utils.h>
+#include <kexidb/roweditbuffer.h>
+#include <kexidb/error.h>
+#include <kexidb/lookupfieldschema.h>
+#include <kexiutils/identifier.h>
+#include <kexiproject.h>
+#include <keximainwindow.h>
+#include <widget/tableview/kexidataawarepropertyset.h>
+#include <widget/kexicustompropertyfactory.h>
+#include <kexiutils/utils.h>
+#include <kexidialogbase.h>
+#include <kexitableview.h>
+
+//#define MAX_FIELDS 101 //nice prime number
+
+//! used only for BLOBs
+#define DEFAULT_OBJECT_TYPE_VALUE "image"
+
+//#define KexiTableDesignerView_DEBUG
+
+//! @todo remove this when BLOBs are implemented
+//#define KEXI_NO_BLOB_FIELDS
+
+using namespace KexiTableDesignerCommands;
+
+//! @internal Used in tryCastQVariant() anf canCastQVariant()
+static bool isIntegerQVariant(QVariant::Type t)
+{
+ return t==QVariant::LongLong
+ || t==QVariant::ULongLong
+ || t==QVariant::Int
+ || t==QVariant::UInt;
+}
+
+//! @internal Used in tryCastQVariant()
+static bool canCastQVariant(QVariant::Type fromType, QVariant::Type toType)
+{
+ return (fromType==QVariant::Int && toType==QVariant::UInt)
+ || (fromType==QVariant::CString && toType==QVariant::String)
+ || (fromType==QVariant::LongLong && toType==QVariant::ULongLong)
+ || ((fromType==QVariant::String || fromType==QVariant::CString)
+ && (isIntegerQVariant(toType) || toType==QVariant::Double));
+}
+
+/*! @internal
+ \return a variant value converted from \a fromVal to \a toType type.
+ Null QVariant is returned if \a fromVal's type and \a toType type
+ are incompatible. */
+static QVariant tryCastQVariant( const QVariant& fromVal, QVariant::Type toType )
+{
+ const QVariant::Type fromType = fromVal.type();
+ if (fromType == toType)
+ return fromVal;
+ if (canCastQVariant(fromType, toType) || canCastQVariant(toType, fromType)
+ || (isIntegerQVariant(fromType) && toType==QVariant::Double))
+ {
+ QVariant res( fromVal );
+ if (res.cast(toType))
+ return res;
+ }
+ return QVariant();
+}
+
+
+KexiTableDesignerView::KexiTableDesignerView(KexiMainWindow *win, QWidget *parent)
+ : KexiDataTable(win, parent, "KexiTableDesignerView", false/*not db-aware*/)
+ , KexiTableDesignerInterface()
+ , d( new KexiTableDesignerViewPrivate(this) )
+{
+ //needed for custom "identifier" property editor widget
+ KexiCustomPropertyFactory::init();
+
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ d->view = dynamic_cast<KexiTableView*>(mainWidget());
+
+ d->data = new KexiTableViewData();
+ if (conn->isReadOnly())
+ d->data->setReadOnly(true);
+ d->data->setInsertingEnabled( false );
+
+ KexiTableViewColumn *col = new KexiTableViewColumn("pk", KexiDB::Field::Text, QString::null,
+ i18n("Additional information about the field"));
+ col->setIcon( KexiUtils::colorizeIconToTextColor( SmallIcon("info"), d->view->palette() ) );
+ col->setHeaderTextVisible(false);
+ col->field()->setSubType("KIcon");
+ col->setReadOnly(true);
+ d->data->addColumn( col );
+
+// col = new KexiTableViewColumn("name", KexiDB::Field::Text, i18n("Field Name"),
+ col = new KexiTableViewColumn("caption", KexiDB::Field::Text, i18n("Field Caption"),
+ i18n("Describes caption for the field"));
+// KexiUtils::Validator *vd = new KexiUtils::IdentifierValidator();
+// vd->setAcceptsEmptyValue(true);
+// col->setValidator( vd );
+ d->data->addColumn( col );
+
+ col = new KexiTableViewColumn("type", KexiDB::Field::Enum, i18n("Data Type"),
+ i18n("Describes data type for the field"));
+ d->data->addColumn( col );
+
+#ifdef KEXI_NO_BLOB_FIELDS
+//! @todo remove this later
+ QValueVector<QString> types(KexiDB::Field::LastTypeGroup-1); //don't show last type (BLOB)
+#else
+ QValueVector<QString> types(KexiDB::Field::LastTypeGroup);
+#endif
+ d->maxTypeNameTextWidth = 0;
+ QFontMetrics fm(font());
+ for (uint i=1; i<=types.count(); i++) {
+ types[i-1] = KexiDB::Field::typeGroupName(i);
+ d->maxTypeNameTextWidth = QMAX(d->maxTypeNameTextWidth, fm.width(types[i-1]));
+ }
+ col->field()->setEnumHints(types);
+
+ d->data->addColumn( col = new KexiTableViewColumn("comments", KexiDB::Field::Text, i18n("Comments"),
+ i18n("Describes additional comments for the field")) );
+
+ d->view->setSpreadSheetMode();
+
+ connect(d->data, SIGNAL(aboutToChangeCell(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)),
+ this, SLOT(slotBeforeCellChanged(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)));
+ connect(d->data, SIGNAL(rowUpdated(KexiTableItem*)),
+ this, SLOT(slotRowUpdated(KexiTableItem*)));
+ //connect(d->data, SIGNAL(aboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)),
+ // this, SLOT(slotAboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)));
+ connect(d->data, SIGNAL(aboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)),
+ this, SLOT(slotAboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)));
+
+ setMinimumSize(d->view->minimumSizeHint().width(), d->view->minimumSizeHint().height());
+ d->view->setFocus();
+
+ d->sets = new KexiDataAwarePropertySet( this, d->view );
+ connect(d->sets, SIGNAL(rowDeleted()), this, SLOT(updateActions()));
+ connect(d->sets, SIGNAL(rowInserted()), this, SLOT(slotRowInserted()));
+
+ d->contextMenuTitle = new KPopupTitle(d->view->contextMenu());
+ d->view->contextMenu()->insertItem(d->contextMenuTitle, -1, 0);
+ connect(d->view->contextMenu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowContextMenu()));
+
+ plugSharedAction("tablepart_toggle_pkey", this, SLOT(slotTogglePrimaryKey()));
+ d->action_toggle_pkey = static_cast<KToggleAction*>( sharedAction("tablepart_toggle_pkey") );
+ d->action_toggle_pkey->plug(d->view->contextMenu(), 1); //add at the beginning
+ d->view->contextMenu()->insertSeparator(2);
+ setAvailable("tablepart_toggle_pkey", !conn->isReadOnly());
+
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+ plugSharedAction("edit_undo", this, SLOT(slotUndo()));
+ plugSharedAction("edit_redo", this, SLOT(slotRedo()));
+ setAvailable("edit_undo", false);
+ setAvailable("edit_redo", false);
+ connect(d->history, SIGNAL(commandExecuted(KCommand*)), this, SLOT(slotCommandExecuted(KCommand*)));
+#endif
+
+#ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString::null); //to create the tab
+ KexiUtils::connectPushButtonActionForDebugWindow(
+ "simulateAlterTableExecution", this, SLOT(slotSimulateAlterTableExecution()));
+ KexiUtils::connectPushButtonActionForDebugWindow(
+ "executeRealAlterTable", this, SLOT(executeRealAlterTable()));
+#endif
+}
+
+KexiTableDesignerView::~KexiTableDesignerView()
+{
+// removeCurrentPropertySet();
+ delete d;
+}
+
+void KexiTableDesignerView::initData()
+{
+ //add column data
+// d->data->clear();
+ d->data->deleteAllRows();
+ int tableFieldCount = 0;
+ d->primaryKeyExists = false;
+
+ if (tempData()->table) {
+ tableFieldCount = tempData()->table->fieldCount();
+//not needed d->sets->clear(tableFieldCount);
+
+ //recreate table data rows
+ for(int i=0; i < tableFieldCount; i++) {
+ KexiDB::Field *field = tempData()->table->field(i);
+ KexiTableItem *item = d->data->createItem(); //new KexiTableItem(0);
+ if (field->isPrimaryKey()) {
+ (*item)[COLUMN_ID_ICON] = "key";
+ d->primaryKeyExists = true;
+ }
+ else {
+ KexiDB::LookupFieldSchema *lookupFieldSchema
+ = field->table() ? field->table()->lookupFieldSchema(*field) : 0;
+ if (lookupFieldSchema && lookupFieldSchema->rowSource().type()!=KexiDB::LookupFieldSchema::RowSource::NoType
+ && !lookupFieldSchema->rowSource().name().isEmpty())
+ {
+ (*item)[COLUMN_ID_ICON] = "combo";
+ }
+ }
+ (*item)[COLUMN_ID_CAPTION] = field->captionOrName();
+ (*item)[COLUMN_ID_TYPE] = field->typeGroup()-1; //-1 because type groups are counted from 1
+ (*item)[COLUMN_ID_DESC] = field->description();
+ d->data->append(item);
+
+//later! createPropertySet( i, field );
+ }
+ }
+// else {
+// d->sets->clear();//default size
+// }
+
+ //add empty space
+// const int columnsCount = d->data->columnsCount();
+ for (int i=tableFieldCount; i<(int)d->sets->size(); i++) {
+// KexiTableItem *item = new KexiTableItem(columnsCount);//3 empty fields
+ d->data->append(d->data->createItem());
+ }
+
+ //set data for our spreadsheet: this will clear our sets
+ d->view->setData(d->data);
+
+ //now recreate property sets
+ if (tempData()->table) {
+ for(int i=0; i < tableFieldCount; i++) {
+ KexiDB::Field *field = tempData()->table->field(i);
+ createPropertySet( i, *field );
+ }
+ }
+
+ //column widths
+ d->view->setColumnWidth(COLUMN_ID_ICON, IconSize( KIcon::Small ) + 10);
+ d->view->adjustColumnWidthToContents(COLUMN_ID_CAPTION); //adjust column width
+ d->view->setColumnWidth(COLUMN_ID_TYPE, d->maxTypeNameTextWidth + 2 * d->view->rowHeight());
+ d->view->setColumnStretchEnabled( true, COLUMN_ID_DESC ); //last column occupies the rest of the area
+ const int minCaptionColumnWidth = d->view->fontMetrics().width("wwwwwwwwwww");
+ if (minCaptionColumnWidth > d->view->columnWidth(COLUMN_ID_CAPTION))
+ d->view->setColumnWidth(COLUMN_ID_CAPTION, minCaptionColumnWidth);
+
+ setDirty(false);
+ d->view->setCursorPosition(0, COLUMN_ID_CAPTION); //set @ name column
+ propertySetSwitched();
+}
+
+//! Gets subtype strings and names for type \a fieldType
+void
+KexiTableDesignerView::getSubTypeListData(KexiDB::Field::TypeGroup fieldTypeGroup,
+ QStringList& stringsList, QStringList& namesList)
+{
+/* disabled - "mime" is moved from subType to "objectType" custom property
+ if (fieldTypeGroup==KexiDB::Field::BLOBGroup) {
+ // special case: BLOB type uses "mime-based" subtypes
+//! @todo hardcoded!
+ stringsList << "image";
+ namesList << i18n("Image object type", "Image");
+ }
+ else {*/
+ stringsList = KexiDB::typeStringsForGroup(fieldTypeGroup);
+ namesList = KexiDB::typeNamesForGroup(fieldTypeGroup);
+// }
+ kexipluginsdbg << "KexiTableDesignerView::getSubTypeListData(): subType strings: " <<
+ stringsList.join("|") << "\nnames: " << namesList.join("|") << endl;
+}
+
+KoProperty::Set *
+KexiTableDesignerView::createPropertySet( int row, const KexiDB::Field& field, bool newOne )
+{
+ QString typeName = "KexiDB::Field::" + field.typeGroupString();
+ KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
+ if (mainWin()->project()->dbConnection()->isReadOnly())
+ set->setReadOnly( true );
+// connect(buff,SIGNAL(propertyChanged(KexiPropertyBuffer&,KexiProperty&)),
+// this, SLOT(slotPropertyChanged(KexiPropertyBuffer&,KexiProperty&)));
+
+ KoProperty::Property *prop;
+
+ set->addProperty(prop = new KoProperty::Property("uid", d->generateUniqueId(), ""));
+ prop->setVisible(false);
+
+ //meta-info for property editor
+ set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Table field")) );
+ prop->setVisible(false);
+ set->addProperty(prop = new KoProperty::Property("this:iconName",
+//! \todo add table_field icon
+ "lineedit" //"table_field"
+ ));
+ prop->setVisible(false);
+ set->addProperty(prop = new KoProperty::Property("this:useCaptionAsObjectName",
+ QVariant(true, 1), QString::null)); //we want "caption" to be displayed in the header, not name
+ prop->setVisible(false);
+
+ //name
+ set->addProperty(prop
+ = new KoProperty::Property("name", QVariant(field.name()), i18n("Name"),
+ QString::null, KexiCustomPropertyFactory::Identifier) );
+
+ //type
+ set->addProperty( prop
+ = new KoProperty::Property("type", QVariant(field.type()), i18n("Type")) );
+#ifndef KexiTableDesignerView_DEBUG
+ prop->setVisible(false);//always hidden
+#endif
+
+ //subtype
+ QStringList typeStringList, typeNameList;
+ getSubTypeListData(field.typeGroup(), typeStringList, typeNameList);
+/* disabled - "mime" is moved from subType to "objectType" custom property
+ QString subTypeValue;
+ if (field.typeGroup()==KexiDB::Field::BLOBGroup) {
+// special case: BLOB type uses "mime-based" subtypes
+//! @todo this should be retrieved from KexiDB::Field when BLOB supports many different mimetypes
+ subTypeValue = slist.first();
+ }
+ else {*/
+ QString subTypeValue = field.typeString();
+ //}
+ set->addProperty(prop = new KoProperty::Property("subType",
+ typeStringList, typeNameList, subTypeValue, i18n("Subtype")));
+
+ // objectType
+ QStringList objectTypeStringList, objectTypeNameList;
+//! @todo this should be retrieved from KexiDB::Field when BLOB supports many different mimetypes
+ objectTypeStringList << "image";
+ objectTypeNameList << i18n("Image object type", "Image");
+ QString objectTypeValue( field.customProperty("objectType").toString() );
+ if (objectTypeValue.isEmpty())
+ objectTypeValue = DEFAULT_OBJECT_TYPE_VALUE;
+ set->addProperty(prop = new KoProperty::Property("objectType",
+ objectTypeStringList, objectTypeNameList, objectTypeValue, i18n("Subtype")/*todo other i18n string?*/));
+
+ set->addProperty( prop
+ = new KoProperty::Property("caption", QVariant(field.caption()), i18n("Caption") ) );
+ prop->setVisible(false);//always hidden
+
+ set->addProperty( prop
+ = new KoProperty::Property("description", QVariant(field.description())) );
+ prop->setVisible(false);//always hidden
+
+ set->addProperty(prop
+ = new KoProperty::Property("unsigned", QVariant(field.isUnsigned(), 4), i18n("Unsigned Number")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("length", (int)field.length()/*200?*/, i18n("Length")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("precision", (int)field.precision()/*200?*/, i18n("Precision")));
+#ifdef KEXI_NO_UNFINISHED
+ prop->setVisible(false);
+#endif
+ set->addProperty( prop
+ = new KoProperty::Property("visibleDecimalPlaces", field.visibleDecimalPlaces(), i18n("Visible Decimal Places")));
+ prop->setOption("min", -1);
+ prop->setOption("minValueText", i18n("Auto Decimal Places","Auto"));
+
+//! @todo set reasonable default for column width
+ set->addProperty( prop
+ = new KoProperty::Property("width", (int)field.width()/*200?*/, i18n("Column Width")));
+#ifdef KEXI_NO_UNFINISHED
+ prop->setVisible(false);
+#endif
+
+ set->addProperty( prop
+ = new KoProperty::Property("defaultValue", field.defaultValue(), i18n("Default Value"),
+ QString::null,
+//! @todo use "Variant" type here when supported by KoProperty
+ (KoProperty::PropertyType)field.variantType()) );
+ prop->setOption("3rdState", i18n("None"));
+// prop->setVisible(false);
+
+ set->addProperty( prop
+ = new KoProperty::Property("primaryKey", QVariant(field.isPrimaryKey(), 4), i18n("Primary Key")));
+ prop->setIcon("key");
+
+ set->addProperty( prop
+ = new KoProperty::Property("unique", QVariant(field.isUniqueKey(), 4), i18n("Unique")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("notNull", QVariant(field.isNotNull(), 4), i18n("Required")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("allowEmpty", QVariant(!field.isNotEmpty(), 4), i18n("Allow Zero\nSize")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("autoIncrement", QVariant(field.isAutoIncrement(), 4), i18n("Autonumber")));
+ prop->setIcon("autonumber");
+
+ set->addProperty( prop
+ = new KoProperty::Property("indexed", QVariant(field.isIndexed(), 4), i18n("Indexed")));
+
+ //- properties related to lookup columns (used and set by the "lookup column" tab in the property pane)
+ KexiDB::LookupFieldSchema *lookupFieldSchema = field.table() ? field.table()->lookupFieldSchema(field) : 0;
+ set->addProperty( prop = new KoProperty::Property("rowSource",
+ lookupFieldSchema ? lookupFieldSchema->rowSource().name() : QString::null, i18n("Row Source")));
+ prop->setVisible(false);
+
+ set->addProperty( prop = new KoProperty::Property("rowSourceType",
+ lookupFieldSchema ? lookupFieldSchema->rowSource().typeName() : QString::null, i18n("Row Source\nType")));
+ prop->setVisible(false);
+
+ set->addProperty( prop
+ = new KoProperty::Property("boundColumn",
+ lookupFieldSchema ? lookupFieldSchema->boundColumn() : -1, i18n("Bound Column")));
+ prop->setVisible(false);
+
+//! @todo this is backward-compatible code for "single visible column" implementation
+//! for multiple columns, only the first is displayed, so there is a data loss is GUI is used
+//! -- special koproperty editor needed
+ int visibleColumn = -1;
+ if (lookupFieldSchema && !lookupFieldSchema->visibleColumns().isEmpty())
+ visibleColumn = lookupFieldSchema->visibleColumns().first();
+ set->addProperty( prop
+ = new KoProperty::Property("visibleColumn", visibleColumn, i18n("Visible Column")));
+ prop->setVisible(false);
+
+//! @todo support columnWidths(), columnHeadersVisible(), maximumListRows(), limitToList(), displayWidget()
+
+ //----
+ d->updatePropertiesVisibility(field.type(), *set);
+
+ connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
+ this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
+
+ d->sets->insert(row, set, newOne);
+ return set;
+}
+
+void KexiTableDesignerView::updateActions(bool activated)
+{
+ Q_UNUSED(activated);
+/*! \todo check if we can set pkey for this column type (eg. BLOB?) */
+ setAvailable("tablepart_toggle_pkey", propertySet()!=0 && !mainWin()->project()->dbConnection()->isReadOnly());
+ if (!propertySet())
+ return;
+ KoProperty::Set &set = *propertySet();
+ d->slotTogglePrimaryKeyCalled = true;
+ d->action_toggle_pkey->setChecked(set["primaryKey"].value().toBool());
+ d->slotTogglePrimaryKeyCalled = false;
+}
+
+void KexiTableDesignerView::slotUpdateRowActions(int row)
+{
+ KexiDataTable::slotUpdateRowActions(row);
+ updateActions();
+}
+
+void KexiTableDesignerView::slotTogglePrimaryKey()
+{
+ if (d->slotTogglePrimaryKeyCalled)
+ return;
+ d->slotTogglePrimaryKeyCalled = true;
+ if (!propertySet())
+ return;
+ KoProperty::Set &set = *propertySet();
+ bool isSet = !set["primaryKey"].value().toBool();
+ set.changeProperty("primaryKey", QVariant(isSet,1)); //this will update all related properties as well
+/* CommandGroup *setPrimaryKeyCommand;
+ if (isSet) {
+ setPrimaryKeyCommand = new CommandGroup(i18n("Set primary key for field \"%1\"")
+ .arg(set["name"].value().toString()) );
+ }
+ else {
+ setPrimaryKeyCommand = new CommandGroup(i18n("Unset primary key for field \"%1\"")
+ .arg(set["name"].value().toString()) );
+ }
+ switchPrimaryKey(set, isSet, false, setPrimaryKeyCommand);*/
+ //addHistoryCommand( setPrimaryKeyCommand, false /* !execute */ );
+ d->slotTogglePrimaryKeyCalled = false;
+}
+
+void KexiTableDesignerView::switchPrimaryKey(KoProperty::Set &propertySet,
+ bool set, bool aWasPKey, CommandGroup* commandGroup)
+{
+ const bool was_pkey = aWasPKey || propertySet["primaryKey"].value().toBool();
+// propertySet["primaryKey"] = QVariant(set, 1);
+ d->setPropertyValueIfNeeded( propertySet, "primaryKey", QVariant(set,1), commandGroup );
+ if (&propertySet==this->propertySet()) {
+ //update action and icon @ column 0 (only if we're changing current property set)
+ d->action_toggle_pkey->setChecked(set);
+ if (d->view->selectedItem()) {
+ //show key in the table
+ d->view->data()->clearRowEditBuffer();
+ d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_ICON,
+ QVariant(set ? "key" : ""));
+ d->view->data()->saveRowChanges(*d->view->selectedItem(), true);
+ }
+ if (was_pkey || set) //change flag only if we're setting pk or really clearing it
+ d->primaryKeyExists = set;
+ }
+
+ if (set) {
+ //primary key is set, remove old pkey if exists
+ KoProperty::Set *s = 0;
+ int i;
+ const int count = (int)d->sets->size();
+ for (i=0; i<count; i++) {
+ s = d->sets->at(i);
+ if (s && s!=&propertySet && (*s)["primaryKey"].value().toBool() && i!=d->view->currentRow())
+ break;
+ }
+ if (i<count) {//remove
+ //(*s)["autoIncrement"] = QVariant(false, 0);
+ d->setPropertyValueIfNeeded( *s, "autoIncrement", QVariant(false,0), commandGroup );
+ //(*s)["primaryKey"] = QVariant(false, 0);
+ d->setPropertyValueIfNeeded( *s, "primaryKey", QVariant(false,0), commandGroup );
+ //remove key from table
+ d->view->data()->clearRowEditBuffer();
+ KexiTableItem *item = d->view->itemAt(i);
+ if (item) {
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_ICON, QVariant());
+ d->view->data()->saveRowChanges(*item, true);
+ }
+ }
+ //set unsigned big-integer type
+// d->view->data()->saveRowChanges(*d->view->selectedItem());
+ d->slotBeforeCellChanged_enabled = false;
+ d->view->data()->clearRowEditBuffer();
+ d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
+ QVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
+// QVariant(KexiDB::Field::typeGroupName(KexiDB::Field::IntegerGroup)));
+ d->view->data()->saveRowChanges(*d->view->selectedItem(), true);
+ //propertySet["subType"] = KexiDB::Field::typeString(KexiDB::Field::BigInteger);
+ d->setPropertyValueIfNeeded( propertySet, "subType", KexiDB::Field::typeString(KexiDB::Field::BigInteger),
+ commandGroup );
+ //propertySet["unsigned"] = QVariant(true,4);
+ d->setPropertyValueIfNeeded( propertySet, "unsigned", QVariant(true,4), commandGroup );
+/*todo*/
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ updateActions();
+}
+
+/*void KexiTableDesignerView::slotCellSelected(int, int row)
+{
+ kdDebug() << "KexiTableDesignerView::slotCellSelected()" << endl;
+ if(row == m_row)
+ return;
+ m_row = row;
+ propertyBufferSwitched();
+}*/
+
+tristate KexiTableDesignerView::beforeSwitchTo(int mode, bool &dontStore)
+{
+ if (!d->view->acceptRowEdit())
+ return false;
+/* if (mode==Kexi::DesignViewMode) {
+ initData();
+ return true;
+ }
+ else */
+ tristate res = true;
+ if (mode==Kexi::DataViewMode) {
+ if (!dirty() && parentDialog()->neverSaved()) {
+ KMessageBox::sorry(this, i18n("Cannot switch to data view, because table design is empty.\n"
+ "First, please create your design.") );
+ return cancelled;
+ }
+//<temporary>
+ else if (dirty() && !parentDialog()->neverSaved()) {
+// cancelled = (KMessageBox::No == KMessageBox::questionYesNo(this, i18n("Saving changes for existing table design is not yet supported.\nDo you want to discard your changes now?")));
+
+// KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ bool emptyTable;
+ int r = KMessageBox::warningYesNoCancel(this,
+ i18n("Saving changes for existing table design is now required.")
+ + "\n" + d->messageForSavingChanges(emptyTable, /* skip warning? */!isPhysicalAlteringNeeded()),
+ QString::null,
+ KStdGuiItem::save(), KStdGuiItem::discard(), QString::null,
+ KMessageBox::Notify|KMessageBox::Dangerous);
+ if (r == KMessageBox::Cancel)
+ res = cancelled;
+ else
+ res = true;
+ dontStore = (r!=KMessageBox::Yes);
+ if (!dontStore)
+ d->dontAskOnStoreData = true;
+// if (dontStore)
+// setDirty(false);
+ }
+//</temporary>
+ //todo
+ return res;
+ }
+ else if (mode==Kexi::TextViewMode) {
+ //todo
+ }
+ return res;
+}
+
+tristate KexiTableDesignerView::afterSwitchFrom(int mode)
+{
+ if (mode==Kexi::NoViewMode || mode==Kexi::DataViewMode) {
+ initData();
+ }
+ return true;
+}
+
+KoProperty::Set *KexiTableDesignerView::propertySet()
+{
+ return d->sets ? d->sets->currentPropertySet() : 0;
+}
+
+/*
+void KexiTableDesignerView::removeCurrentPropertySet()
+{
+ const int r = d->view->currentRow();
+ KoProperty::Set *buf = d->sets.at(r);
+ if (!buf)
+ return;
+ buf->debug();
+// m_currentBufferCleared = true;
+ d->sets.remove(r);
+ propertysetswitched();
+// delete buf;
+// m_currentBufferCleared = false;
+}
+*/
+
+void KexiTableDesignerView::slotBeforeCellChanged(
+ KexiTableItem *item, int colnum, QVariant& newValue, KexiDB::ResultInfo* /*result*/)
+{
+ if (!d->slotBeforeCellChanged_enabled)
+ return;
+// kdDebug() << d->view->selectedItem() << " " << item
+ //<< " " << d->sets->at( d->view->currentRow() ) << " " << propertySet() << endl;
+ if (colnum==COLUMN_ID_CAPTION) {//'caption'
+// if (!item->at(1).toString().isEmpty() && item->at(1).isNull()) {
+ //if 'type' is not filled yet
+ if (item->at(COLUMN_ID_TYPE).isNull()) {
+ //auto select 1st row of 'type' column
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, QVariant((int)0));
+ }
+
+ KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
+ if (propertySetForItem) {
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = false; //because we'll add the two changes as one KMacroCommand
+ QString oldName( propertySetForItem->property("name").value().toString() );
+ QString oldCaption( propertySetForItem->property("caption").value().toString() );
+
+ //we need to create the action now as set["name"] will be changed soon..
+ ChangeFieldPropertyCommand *changeCaptionCommand
+ = new ChangeFieldPropertyCommand( this, *propertySetForItem, "caption", oldCaption, newValue);
+
+ //update field caption and name
+ propertySetForItem->changeProperty("caption", newValue);
+ propertySetForItem->changeProperty("name", newValue); // "name" prop. is of custom type Identifier, so this assignment
+ // will automatically convert newValue to an valid identifier
+
+ //remember this action containing 2 subactions
+ CommandGroup *changeCaptionAndNameCommand = new CommandGroup(
+ i18n("Change \"%1\" field's name to \"%2\" and caption from \"%3\" to \"%4\"")
+ .arg(oldName).arg(propertySetForItem->property("name").value().toString())
+ .arg(oldCaption).arg(newValue.toString() ));
+ changeCaptionAndNameCommand->addCommand( changeCaptionCommand );
+// new ChangeFieldPropertyCommand( this, *propertySetForItem,
+ // "caption", oldCaption, newValue)
+ // );
+ changeCaptionAndNameCommand->addCommand(
+ new ChangeFieldPropertyCommand( this, *propertySetForItem,
+ "name", oldName, propertySetForItem->property("name").value().toString())
+ );
+ addHistoryCommand( changeCaptionAndNameCommand, false /* !execute */ );
+
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ }
+ }
+ else if (colnum==COLUMN_ID_TYPE) {//'type'
+ if (newValue.isNull()) {
+ //'type' col will be cleared: clear all other columns as well
+ d->slotBeforeCellChanged_enabled = false;
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_ICON, QVariant());
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, QVariant(QString::null));
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC, QVariant());
+ d->slotBeforeCellChanged_enabled = true;
+ return;
+ }
+
+ KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
+ if (!propertySetForItem)
+ return;
+
+ KoProperty::Set &set = *propertySetForItem; //propertySet();
+
+ //'type' col is changed (existed before)
+ //-get type group number
+ KexiDB::Field::TypeGroup fieldTypeGroup;
+ int i_fieldTypeGroup = newValue.toInt()+1/*counting from 1*/;
+ if (i_fieldTypeGroup < 1 || i_fieldTypeGroup >
+#ifdef KEXI_NO_BLOB_FIELDS
+//! @todo remove this later
+ (int)KexiDB::Field::LastTypeGroup-1) //don't show last (BLOB) type
+#else
+ (int)KexiDB::Field::LastTypeGroup)
+#endif
+ return;
+ fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(i_fieldTypeGroup);
+
+ //-get 1st type from this group, and update 'type' property
+ KexiDB::Field::Type fieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
+ if (fieldType==KexiDB::Field::InvalidType)
+ fieldType = KexiDB::Field::Text;
+//moved down set["type"] = (int)fieldType;
+// set["subType"] = KexiDB::Field::typeName(fieldType);
+
+ //-get subtypes for this type: keys (slist) and names (nlist)
+ QStringList slist, nlist;
+ getSubTypeListData(fieldTypeGroup, slist, nlist);
+
+ QString subTypeValue;
+/* disabled - "mime" is moved from subType to "objectType" custom property
+ if (fieldType==KexiDB::Field::BLOB) {
+ // special case: BLOB type uses "mime-based" subtypes
+ subTypeValue = slist.first();
+ }
+ else {*/
+ subTypeValue = KexiDB::Field::typeString(fieldType);
+ //}
+ KoProperty::Property *subTypeProperty = &set["subType"];
+ kexipluginsdbg << subTypeProperty->value() << endl;
+
+ // *** this action contains subactions ***
+ CommandGroup *changeDataTypeCommand = new CommandGroup(
+ i18n("Change data type for field \"%1\" to \"%2\"")
+ .arg(set["name"].value().toString()).arg( KexiDB::Field::typeName( fieldType ) ) );
+
+//kexipluginsdbg << "++++++++++" << slist << nlist << endl;
+
+ //update subtype list and value
+ const bool forcePropertySetReload
+ = KexiDB::Field::typeGroup( KexiDB::Field::typeForString(subTypeProperty->value().toString()) )
+ != fieldTypeGroup; //<-- ?????
+// const bool forcePropertySetReload = set["type"].value().toInt() != (int)fieldTypeGroup;
+ const bool useListData = slist.count() > 1; //disabled-> || fieldType==KexiDB::Field::BLOB;
+
+ if (!useListData) {
+ slist.clear(); //empty list will be passed
+ nlist.clear();
+ }
+ d->setPropertyValueIfNeeded( set, "type", (int)fieldType, changeDataTypeCommand,
+ false /*!forceAddCommand*/, true /*rememberOldValue*/);
+
+ // notNull and defaultValue=false is reasonable for boolean type
+ if (fieldType == KexiDB::Field::Boolean) {
+//! @todo maybe this is good for other data types as well?
+ d->setPropertyValueIfNeeded( set, "notNull", QVariant(true, 1), changeDataTypeCommand,
+ false /*!forceAddCommand*/, false /*!rememberOldValue*/);
+ d->setPropertyValueIfNeeded( set, "defaultValue", QVariant(false, 1), changeDataTypeCommand,
+ false /*!forceAddCommand*/, false /*!rememberOldValue*/);
+ }
+
+/* if (useListData) {
+ {
+ subTypeProperty->setListData( slist, nlist );
+ }
+ else {
+ subTypeProperty->setListData( 0 );
+ }*/
+ if (set["primaryKey"].value().toBool()==true) {
+ //primary keys require big int, so if selected type is not integer- remove PK
+ if (fieldTypeGroup != KexiDB::Field::IntegerGroup) {
+ /*not needed, line below will do the work
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_ICON, QVariant());
+ d->view->data()->saveRowChanges(*item); */
+ //set["primaryKey"] = QVariant(false, 1);
+ d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(false, 1), changeDataTypeCommand );
+//! @todo should we display (passive?) dialog informing about cleared pkey?
+ }
+ }
+// if (useListData)
+// subTypeProperty->setValue( subTypeValue, false/*!rememberOldValue*/ );
+ d->setPropertyValueIfNeeded( set, "subType", subTypeValue,
+ changeDataTypeCommand, false, false /*!rememberOldValue*/,
+ &slist, &nlist );
+
+ if (d->updatePropertiesVisibility(fieldType, set, changeDataTypeCommand) || forcePropertySetReload) {
+ //properties' visiblility changed: refresh prop. set
+ propertySetReloaded(true);
+ }
+
+ addHistoryCommand( changeDataTypeCommand, false /* !execute */ );
+ }
+ else if (colnum==COLUMN_ID_DESC) {//'description'
+ KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
+ if (!propertySetForItem)
+ return;
+ //update field desc.
+ QVariant oldValue((*propertySetForItem)["description"].value());
+ kexipluginsdbg << oldValue << endl;
+ propertySetForItem->changeProperty("description", newValue);
+ /*moved addHistoryCommand(
+ new ChangeFieldPropertyCommand( this, *propertySetForItem,
+ "description", oldValue, newValue ), false);*/
+ }
+}
+
+void KexiTableDesignerView::slotRowUpdated(KexiTableItem *item)
+{
+ const int row = d->view->data()->findRef(item);
+ if (row < 0)
+ return;
+
+ setDirty();
+
+ //-check if the row was empty before updating
+ //if yes: we want to add a property set for this new row (field)
+ QString fieldCaption( item->at(COLUMN_ID_CAPTION).toString() );
+ const bool prop_set_allowed = !item->at(COLUMN_ID_TYPE).isNull();
+
+ if (!prop_set_allowed && d->sets->at(row)/*propertySet()*/) {
+ //there is a property set, but it's not allowed - remove it:
+ d->sets->remove( row ); //d->sets->removeCurrentPropertySet();
+
+ //clear 'type' column:
+ d->view->data()->clearRowEditBuffer();
+// d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE, QVariant());
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, QVariant());
+ d->view->data()->saveRowChanges(*item);
+
+ } else if (prop_set_allowed && !d->sets->at(row)/*propertySet()*/) {
+ //-- create a new field:
+ KexiDB::Field::TypeGroup fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(
+ item->at(COLUMN_ID_TYPE).toInt()+1/*counting from 1*/ );
+ int intFieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
+ if (intFieldType==0)
+ return;
+
+ QString description( item->at(COLUMN_ID_DESC).toString() );
+
+//todo: check uniqueness:
+ QString fieldName( KexiUtils::string2Identifier(fieldCaption) );
+
+ KexiDB::Field::Type fieldType = KexiDB::intToFieldType( intFieldType );
+ KexiDB::Field field( //tmp
+ fieldName,
+ fieldType,
+ KexiDB::Field::NoConstraints,
+ KexiDB::Field::NoOptions,
+ /*length*/0,
+ /*precision*/0,
+ /*defaultValue*/QVariant(),
+ fieldCaption,
+ description,
+ /*width*/0);
+// m_newTable->addField( field );
+
+ // reasonable case for boolean type: set notNull flag and "false" as default value
+ if (fieldType == KexiDB::Field::Boolean) {
+ field.setNotNull( true );
+ field.setDefaultValue( QVariant(false, 0) );
+ }
+
+ kexipluginsdbg << "KexiTableDesignerView::slotRowUpdated(): " << field.debugString() << endl;
+
+ //create a new property set:
+ KoProperty::Set *newSet = createPropertySet( row, field, true );
+//moved
+ //add a special property indicating that this is brand new buffer,
+ //not just changed
+// KoProperty::Property* prop = new KoProperty::Property("newrow", QVariant());
+// prop->setVisible(false);
+// newbuff->addProperty( prop );
+
+ //refresh property editor:
+ propertySetSwitched();
+
+ if (row>=0) {
+ if (d->addHistoryCommand_in_slotRowUpdated_enabled) {
+ addHistoryCommand( new InsertFieldCommand( this, row, *newSet /*propertySet()*/ ), //, field /*will be copied*/
+ false /* !execute */ );
+ }
+ }
+ else {
+ kexipluginswarn << "KexiTableDesignerView::slotRowUpdated() row # not found !" << endl;
+ }
+ }
+}
+
+void KexiTableDesignerView::updateActions()
+{
+ updateActions(false);
+}
+
+void KexiTableDesignerView::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
+{
+// if (!d->slotPropertyChanged_enabled)
+// return;
+ const QCString pname = property.name();
+ kexipluginsdbg << "KexiTableDesignerView::slotPropertyChanged(): " << pname << " = " << property.value()
+ << " (oldvalue = " << property.oldValue() << ")" << endl;
+
+ // true is PK should be altered
+ bool changePrimaryKey = false;
+ // true is PK should be set to true, otherwise unset
+ bool setPrimaryKey = false;
+
+ if (pname=="primaryKey" && d->slotPropertyChanged_primaryKey_enabled) {
+ changePrimaryKey = true;
+ setPrimaryKey = property.value().toBool();
+ }
+
+ // update "lookup column" icon
+ if (pname=="rowSource" || pname=="rowSourceType") {
+//! @todo indicate invalid definitions of lookup columns as well using a special icon
+//! (e.g. due to missing data source)
+ const int row = d->sets->findRowForPropertyValue("uid", set["uid"].value().toInt());
+ KexiTableItem *item = d->view->itemAt(row);
+ if (item)
+ d->updateIconForItem(*item, set);
+ }
+
+ //setting autonumber requires setting PK as well
+ CommandGroup *setAutonumberCommand = 0;
+ CommandGroup *toplevelCommand = 0;
+ if (pname=="autoIncrement" && property.value().toBool()==true) {
+ if (set["primaryKey"].value().toBool()==false) {//we need PKEY here!
+ QString msg = QString("<p>")
+ +i18n("Setting autonumber requires primary key to be set for current field.")+"</p>";
+ if (d->primaryKeyExists)
+ msg += (QString("<p>")+ i18n("Previous primary key will be removed.")+"</p>");
+ msg += (QString("<p>")
+ +i18n("Do you want to create primary key for current field? "
+ "Click \"Cancel\" to cancel setting autonumber.")+"</p>");
+
+ if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
+ i18n("Setting Autonumber Field"),
+ KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
+ {
+ changePrimaryKey = true;
+ setPrimaryKey = true;
+ //switchPrimaryKey(set, true);
+ // this will be toplevel command
+ setAutonumberCommand = new CommandGroup(
+ i18n("Assign autonumber for field \"%1\"").arg(set["name"].value().toString()) );
+ toplevelCommand = setAutonumberCommand;
+ d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(true,1), setAutonumberCommand );
+ }
+ else {
+ setAutonumberCommand = new CommandGroup(
+ i18n("Remove autonumber from field \"%1\"").arg(set["name"].value().toString()) );
+ //d->slotPropertyChanged_enabled = false;
+// set["autoIncrement"].setValue( QVariant(false,1), false/*don't save old*/);
+// d->slotPropertyChanged_enabled = true;
+ d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(false,1), setAutonumberCommand,
+ true /*forceAddCommand*/, false/*rememberOldValue*/ );
+ addHistoryCommand( setAutonumberCommand, false /* !execute */ );
+ return;
+ }
+ }
+ }
+
+ //clear PK when these properties were set to false:
+ if ((pname=="indexed" || pname=="unique" || pname=="notNull")
+ && set["primaryKey"].value().toBool() && property.value().toBool()==false)
+ {
+//! @todo perhaps show a hint in help panel telling what happens?
+ changePrimaryKey = true;
+ setPrimaryKey = false;
+ // this will be toplevel command
+ CommandGroup *unsetIndexedOrUniquOrNotNullCommand = new CommandGroup(
+ i18n("Set \"%1\" property for field \"%2\"").arg(property.caption()).arg(set["name"].value().toString()) );
+ toplevelCommand = unsetIndexedOrUniquOrNotNullCommand;
+ d->setPropertyValueIfNeeded( set, pname, QVariant(false,1), unsetIndexedOrUniquOrNotNullCommand );
+ if (pname=="notNull") {
+//? d->setPropertyValueIfNeeded( set, "notNull", QVariant(true,1), unsetIndexedOrUniquOrNotNullCommand );
+ d->setPropertyValueIfNeeded( set, "unique", QVariant(false,1), unsetIndexedOrUniquOrNotNullCommand );
+ }
+ }
+
+ if (pname=="defaultValue") {
+ KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
+ set["defaultValue"].setType((KoProperty::PropertyType)KexiDB::Field::variantType(type));
+ }
+
+ if (pname=="subType" && d->slotPropertyChanged_subType_enabled) {
+ d->slotPropertyChanged_subType_enabled = false;
+ if (set["primaryKey"].value().toBool()==true
+ && property.value().toString()!=KexiDB::Field::typeString(KexiDB::Field::BigInteger))
+ {
+ kexipluginsdbg << "INVALID " << property.value().toString() << endl;
+// if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
+// i18n("This field has promary key assigned. Setting autonumber field"),
+// KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
+
+ }
+ KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
+ QString typeName;
+/* disabled - "mime" is moved from subType to "objectType" custom property
+ if (type==KexiDB::Field::BLOB) { //special case
+ //find i18n'd text
+ QStringList stringsList, namesList;
+ getSubTypeListData(KexiDB::Field::BLOBGroup, stringsList, namesList);
+ const int stringIndex = stringsList.findIndex( property.value().toString() );
+ if (-1 == stringIndex || stringIndex>=(int)namesList.count())
+ typeName = property.value().toString(); //for sanity
+ else
+ typeName = namesList[stringIndex];
+ }
+ else {*/
+ typeName = KexiDB::Field::typeName( KexiDB::Field::typeForString(property.value().toString()) );
+// }
+// kdDebug() << property.value().toString() << endl;
+// kdDebug() << set["type"].value() << endl;
+// if (KexiDB::Field::typeGroup( set["type"].value().toInt() ) == (int)KexiDB::Field::TextGroup) {
+ CommandGroup* changeFieldTypeCommand = new CommandGroup(
+ i18n("Change type for field \"%1\" to \"%2\"").arg(set["name"].value().toString())
+ .arg(typeName) );
+ d->setPropertyValueIfNeeded( set, "subType", property.value(), property.oldValue(),
+ changeFieldTypeCommand );
+
+ kexipluginsdbg << set["type"].value() << endl;
+ const KexiDB::Field::Type newType = KexiDB::Field::typeForString(property.value().toString());
+ set["type"].setValue( newType );
+
+ // cast "defaultValue" property value to a new type
+ QVariant oldDefVal( set["defaultValue"].value() );
+ QVariant newDefVal( tryCastQVariant(oldDefVal, KexiDB::Field::variantType(type)) );
+ if (oldDefVal.type()!=newDefVal.type())
+ set["defaultValue"].setType( newDefVal.type() );
+ d->setPropertyValueIfNeeded( set, "defaultValue", newDefVal, newDefVal,
+ changeFieldTypeCommand );
+
+ d->updatePropertiesVisibility(newType, set);
+ //properties' visiblility changed: refresh prop. set
+ propertySetReloaded(true);
+ d->slotPropertyChanged_subType_enabled = true;
+
+ addHistoryCommand( changeFieldTypeCommand, false /* !execute */ );
+ return;
+// }
+// d->slotPropertyChanged_subType_enabled = true;
+// return;
+ }
+
+ if (d->addHistoryCommand_in_slotPropertyChanged_enabled && !changePrimaryKey/*we'll add multiple commands for PK*/) {
+ addHistoryCommand( new ChangeFieldPropertyCommand(this, set,
+ property.name(), property.oldValue() /* ??? */, property.value()),
+ false /* !execute */ );
+ }
+
+ if (changePrimaryKey) {
+ d->slotPropertyChanged_primaryKey_enabled = false;
+ if (setPrimaryKey) {
+ //primary key implies some rules
+ //const bool prev_addHistoryCommand_in_slotPropertyChanged_enabled = d->addHistoryCommand_in_slotPropertyChanged_enabled;
+// d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+
+ //this action contains subactions
+ CommandGroup *setPrimaryKeyCommand = new CommandGroup(
+ i18n("Set primary key for field \"%1\"")
+ .arg(set["name"].value().toString()) );
+ if (toplevelCommand)
+ toplevelCommand->addCommand( setPrimaryKeyCommand );
+ else
+ toplevelCommand = setPrimaryKeyCommand;
+
+ d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(true,1), setPrimaryKeyCommand, true /*forceAddCommand*/ );
+ d->setPropertyValueIfNeeded( set, "unique", QVariant(true,1), setPrimaryKeyCommand );
+ d->setPropertyValueIfNeeded( set, "notNull", QVariant(true,1), setPrimaryKeyCommand );
+ d->setPropertyValueIfNeeded( set, "allowEmpty", QVariant(false,1), setPrimaryKeyCommand );
+ d->setPropertyValueIfNeeded( set, "indexed", QVariant(true,1), setPrimaryKeyCommand );
+//! \todo: add setting for this: "Integer PKeys have autonumber set by default"
+ d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(true,1), setPrimaryKeyCommand );
+
+/* set["unique"] = QVariant(true,1);
+ set["notNull"] = QVariant(true,1);
+ set["allowEmpty"] = QVariant(false,1);
+ set["indexed"] = QVariant(true,1);
+ set["autoIncrement"] = QVariant(true,1);*/
+// d->addHistoryCommand_in_slotPropertyChanged_enabled = prev_addHistoryCommand_in_slotPropertyChanged_enabled;
+//down addHistoryCommand( toplevelCommand, false /* !execute */ );
+ }
+ else {//! set PK to false
+ //remember this action containing 2 subactions
+ CommandGroup *setPrimaryKeyCommand = new CommandGroup(
+ i18n("Unset primary key for field \"%1\"")
+ .arg(set["name"].value().toString()) );
+ if (toplevelCommand)
+ toplevelCommand->addCommand( setPrimaryKeyCommand );
+ else
+ toplevelCommand = setPrimaryKeyCommand;
+
+ d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(false,1), setPrimaryKeyCommand, true /*forceAddCommand*/ );
+ d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(false,1), setPrimaryKeyCommand );
+// set["autoIncrement"] = QVariant(false,1);
+
+//down addHistoryCommand( toplevelCommand, false /* !execute */ );
+ }
+ switchPrimaryKey(set, setPrimaryKey, true/*wasPKey*/, toplevelCommand);
+ d->updatePropertiesVisibility(
+ KexiDB::Field::typeForString( set["subType"].value().toString() ), set, toplevelCommand);
+ addHistoryCommand( toplevelCommand, false /* !execute */ );
+ //properties' visiblility changed: refresh prop. set
+ propertySetReloaded(true/*preservePrevSelection*/);
+ d->slotPropertyChanged_primaryKey_enabled = true;
+ }
+}
+
+void KexiTableDesignerView::slotRowInserted()
+{
+ updateActions();
+
+ if (d->addHistoryCommand_in_slotRowInserted_enabled) {
+ const int row = d->view->currentRow();
+ if (row>=0) {
+ addHistoryCommand( new InsertEmptyRowCommand( this, row ), false /* !execute */ );
+ }
+ }
+ //TODO?
+}
+
+void KexiTableDesignerView::slotAboutToDeleteRow(
+ KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint)
+{
+ Q_UNUSED(result)
+ Q_UNUSED(repaint)
+ if (item[COLUMN_ID_ICON].toString()=="key")
+ d->primaryKeyExists = false;
+
+ if (d->addHistoryCommand_in_slotAboutToDeleteRow_enabled) {
+ const int row = d->view->data()->findRef(&item);
+ KoProperty::Set *set = row >=0 ? d->sets->at(row) : 0;
+ //set can be 0 here, what means "removing empty row"
+ addHistoryCommand(
+ new RemoveFieldCommand( this, row, set ),
+ false /* !execute */
+ );
+ }
+}
+
+KexiDB::Field * KexiTableDesignerView::buildField( const KoProperty::Set &set ) const
+{
+ //create a map of property values
+ kexipluginsdbg << set["type"].value() << endl;
+ QMap<QCString, QVariant> values = KoProperty::propertyValues(set);
+ //remove internal values, to avoid creating custom field's properties
+ QMap<QCString, QVariant>::Iterator it = values.begin();
+ KexiDB::Field *field = new KexiDB::Field();
+
+ while (it!=values.end()) {
+ const QString propName( it.key() );
+ if (d->internalPropertyNames.find(propName.latin1()) || propName.startsWith("this:")
+ || (/*sanity*/propName=="objectType" && KexiDB::Field::BLOB != KexiDB::intToFieldType( set["type"].value().toInt() )))
+ {
+ QMap<QCString, QVariant>::Iterator it_tmp = it;
+ ++it;
+ values.remove(it_tmp);
+ }
+ else
+ ++it;
+ }
+ //assign properties to the field
+ // (note that "objectType" property will be saved as custom property)
+ if (!KexiDB::setFieldProperties( *field, values )) {
+ delete field;
+ return 0;
+ }
+ return field;
+}
+
+tristate KexiTableDesignerView::buildSchema(KexiDB::TableSchema &schema, bool beSilent)
+{
+ if (!d->view->acceptRowEdit())
+ return cancelled;
+
+ tristate res = true;
+ //check for pkey; automatically add a pkey if user wanted
+ if (!d->primaryKeyExists) {
+ if (beSilent) {
+ kexipluginsdbg << "KexiTableDesignerView::buildSchema(): no primay key defined..." << endl;
+ }
+ else {
+ const int questionRes = KMessageBox::questionYesNoCancel(this,
+ i18n("<p>Table \"%1\" has no <b>primary key</b> defined.</p>"
+ "<p>Although a primary key is not required, it is needed "
+ "for creating relations between database tables. "
+ "Do you want to add primary key automatically now?</p>"
+ "<p>If you want to add a primary key by hand, press \"Cancel\" "
+ "to cancel saving table design.</p>").arg(schema.name()),
+ QString::null, KGuiItem(i18n("&Add Primary Key"), "key"), KStdGuiItem::no(),
+ "autogeneratePrimaryKeysOnTableDesignSaving");
+ if (questionRes==KMessageBox::Cancel) {
+ return cancelled;
+ }
+ else if (questionRes==KMessageBox::Yes) {
+ //-find unique name, starting with, "id", "id2", ....
+ int i=0;
+ int idIndex = 1; //means "id"
+ QString pkFieldName("id%1");
+ QString pkFieldCaption(i18n("Identifier%1", "Id%1"));
+ while (i<(int)d->sets->size()) {
+ KoProperty::Set *set = d->sets->at(i);
+ if (set) {
+ if ((*set)["name"].value().toString()
+ == pkFieldName.arg(idIndex==1?QString::null : QString::number(idIndex))
+ || (*set)["caption"].value().toString()
+ == pkFieldCaption.arg(idIndex==1?QString::null : QString::number(idIndex)))
+ {
+ //try next id index
+ i = 0;
+ idIndex++;
+ continue;
+ }
+ }
+ i++;
+ }
+ pkFieldName = pkFieldName.arg(idIndex==1?QString::null : QString::number(idIndex));
+ pkFieldCaption = pkFieldCaption.arg(idIndex==1?QString::null : QString::number(idIndex));
+ //ok, add PK with such unique name
+ d->view->insertEmptyRow(0);
+ d->view->setCursorPosition(0, COLUMN_ID_CAPTION);
+ d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_CAPTION,
+ QVariant(pkFieldCaption));
+ d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
+ QVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
+ if (!d->view->data()->saveRowChanges(*d->view->selectedItem(), true)) {
+ return cancelled;
+ }
+ slotTogglePrimaryKey();
+ }
+ }
+ }
+
+ //check for duplicates
+ KoProperty::Set *b = 0;
+ bool no_fields = true;
+ int i;
+ QDict<char> names(101, false);
+ char dummy;
+ for (i=0;i<(int)d->sets->size();i++) {
+ b = d->sets->at(i);
+ if (b) {
+ no_fields = false;
+ const QString name = (*b)["name"].value().toString();
+ if (name.isEmpty()) {
+ if (beSilent) {
+ kexipluginswarn <<
+ QString("KexiTableDesignerView::buildSchema(): no field caption entered at row %1...")
+ .arg(i+1) << endl;
+ }
+ else {
+ d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
+ d->view->startEditCurrentCell();
+ KMessageBox::information(this, i18n("You should enter field caption.") );
+ }
+ res = cancelled;
+ break;
+ }
+ if (names[name]) {
+ break;
+ }
+ names.insert( name, &dummy ); //remember
+ }
+ }
+ if (res == true && no_fields) {//no fields added
+ if (beSilent) {
+ kexipluginswarn <<
+ "KexiTableDesignerView::buildSchema(): no field defined..." << endl;
+ }
+ else {
+ KMessageBox::sorry(this,
+ i18n("You have added no fields.\nEvery table should have at least one field.") );
+ }
+ res = cancelled;
+ }
+ if (res == true && b && i<(int)d->sets->size()) {//found a duplicate
+ if (beSilent) {
+ kexipluginswarn <<
+ QString("KexiTableDesignerView::buildSchema(): duplicated field name '%1'")
+ .arg((*b)["name"].value().toString()) << endl;
+ }
+ else {
+ d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
+ d->view->startEditCurrentCell();
+//! @todo for "names hidden" mode we won't get this error because user is unable to change names
+ KMessageBox::sorry(this,
+ i18n("You have added \"%1\" field name twice.\nField names cannot be repeated. "
+ "Correct name of the field.")
+ .arg((*b)["name"].value().toString()) );
+ }
+ res = cancelled;
+ }
+ if (res == true) {
+ //for every field, create KexiDB::Field definition
+ for (i=0;i<(int)d->sets->size();i++) {
+ KoProperty::Set *s = d->sets->at(i);
+ if (!s)
+ continue;
+ KexiDB::Field * f = buildField( *s );
+ if (!f)
+ continue; //hmm?
+ schema.addField(f);
+ if (!(*s)["rowSource"].value().toString().isEmpty() && !(*s)["rowSourceType"].value().toString().isEmpty()) {
+ //add lookup column
+ KexiDB::LookupFieldSchema *lookupFieldSchema = new KexiDB::LookupFieldSchema();
+ lookupFieldSchema->rowSource().setTypeByName( (*s)["rowSourceType"].value().toString() );
+ lookupFieldSchema->rowSource().setName( (*s)["rowSource"].value().toString() );
+ lookupFieldSchema->setBoundColumn( (*s)["boundColumn"].value().toInt() );
+//! @todo this is backward-compatible code for "single visible column" implementation
+//! for multiple columns, only the first is displayed, so there is a data loss is GUI is used
+//! -- special koproperty editor needed
+ QValueList<uint> visibleColumns;
+ const int visibleColumn = (*s)["visibleColumn"].value().toInt();
+ if (visibleColumn >= 0)
+ visibleColumns.append( (uint)visibleColumn );
+ lookupFieldSchema->setVisibleColumns( visibleColumns );
+//! @todo support columnWidths(), columnHeadersVisible(), maximumListRows(), limitToList(), displayWidget()
+ if (!schema.setLookupFieldSchema(f->name(), lookupFieldSchema)) {
+ kexipluginswarn <<
+ "KexiTableDesignerView::buildSchema(): !schema.setLookupFieldSchema()" << endl;
+ delete lookupFieldSchema;
+ return false;
+ }
+ }
+ }
+ }
+ return res;
+}
+
+//! @internal
+//! A recursive function for copying alter table actions from undo/redo commands.
+static void copyAlterTableActions(KCommand* command, KexiDB::AlterTableHandler::ActionList &actions)
+{
+ CommandGroup* cmdGroup = dynamic_cast<CommandGroup*>( command );
+ if (cmdGroup) {//command group: flatten it
+ for (QPtrListIterator<KCommand> it(cmdGroup->commands()); it.current(); ++it)
+ copyAlterTableActions(it.current(), actions);
+ return;
+ }
+ Command* cmd = dynamic_cast<Command*>( command );
+ if (!cmd) {
+ kexipluginswarn << "KexiTableDesignerView::copyAlterTableActions(): cmd is not of type 'Command'!" << endl;
+ return;
+ }
+ KexiDB::AlterTableHandler::ActionBase* action = cmd->createAction();
+ //some commands can contain null actions, e.g. "set visibility" command
+ if (action)
+ actions.append( action );
+}
+
+tristate KexiTableDesignerView::buildAlterTableActions(KexiDB::AlterTableHandler::ActionList &actions)
+{
+ actions.clear();
+ kexipluginsdbg << "KexiTableDesignerView::buildAlterTableActions(): " << d->history->commands().count()
+ << " top-level command(s) to process..." << endl;
+ for (QPtrListIterator<KCommand> it(d->history->commands()); it.current(); ++it) {
+ copyAlterTableActions(it.current(), actions);
+ }
+ return true;
+}
+
+KexiDB::SchemaData* KexiTableDesignerView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ if (tempData()->table || m_dialog->schemaData()) //must not be
+ return 0;
+
+ //create table schema definition
+ tempData()->table = new KexiDB::TableSchema(sdata.name());
+ tempData()->table->setName( sdata.name() );
+ tempData()->table->setCaption( sdata.caption() );
+ tempData()->table->setDescription( sdata.description() );
+
+ tristate res = buildSchema(*tempData()->table);
+ cancel = ~res;
+
+ //FINALLY: create table:
+ if (res == true) {
+ //todo
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ res = conn->createTable(tempData()->table);
+ if (res!=true)
+ parentDialog()->setStatus(conn, "");
+ }
+
+ if (res == true) {
+ //we've current schema
+ tempData()->tableSchemaChangedInPreviousView = true;
+//not needed; KexiProject emits newItemStored signal //let project know the table is created
+// mainWin()->project()->emitTableCreated(*tempData()->table);
+ }
+ else {
+ delete tempData()->table;
+ tempData()->table = 0;
+ }
+ return tempData()->table;
+}
+
+tristate KexiTableDesignerView::storeData(bool dontAsk)
+{
+ if (!tempData()->table || !m_dialog->schemaData()) {
+ d->recentResultOfStoreData = false;
+ return false;
+ }
+
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ KexiDB::AlterTableHandler *alterTableHandler = 0;
+ KexiDB::TableSchema *newTable = 0;
+
+ //- create action list for the alter table handler
+ KexiDB::AlterTableHandler::ActionList actions;
+ tristate res = buildAlterTableActions( actions );
+ bool realAlterTableCanBeUsed = false; //!< @todo this is temporary flag before we switch entirely to real alter table
+ if (res == true) {
+ alterTableHandler = new KexiDB::AlterTableHandler( *conn );
+ alterTableHandler->setActions(actions);
+
+ if (!d->tempStoreDataUsingRealAlterTable) {
+ //only compute requirements
+ KexiDB::AlterTableHandler::ExecutionArguments args;
+ args.onlyComputeRequirements = true;
+ (void)alterTableHandler->execute(tempData()->table->name(), args);
+ res = args.result;
+ if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
+ realAlterTableCanBeUsed = true;
+ }
+ }
+
+ if (res == true) {
+ res = KexiTablePart::askForClosingObjectsUsingTableSchema(
+ this, *conn, *tempData()->table,
+ i18n("You are about to change the design of table \"%1\" "
+ "but following objects using this table are opened:")
+ .arg(tempData()->table->name()));
+ }
+
+ if (res == true) {
+ if (!d->tempStoreDataUsingRealAlterTable && !realAlterTableCanBeUsed) {
+//! @todo temp; remove this case:
+ delete alterTableHandler;
+ alterTableHandler = 0;
+ // - inform about removing the current table and ask for confirmation
+ if (!d->dontAskOnStoreData && !dontAsk) {
+ bool emptyTable;
+ const QString msg = d->messageForSavingChanges(emptyTable);
+ if (!emptyTable) {
+ if (KMessageBox::No == KMessageBox::questionYesNo(this, msg))
+ res = cancelled;
+ }
+ }
+ d->dontAskOnStoreData = false; //one-time use
+ if (~res) {
+ d->recentResultOfStoreData = res;
+ return res;
+ }
+ // keep old behaviour:
+ newTable = new KexiDB::TableSchema();
+ // copy the schema data
+ static_cast<KexiDB::SchemaData&>(*newTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
+ res = buildSchema(*newTable);
+ kexipluginsdbg << "KexiTableDesignerView::storeData() : BUILD SCHEMA:" << endl;
+ newTable->debug();
+
+ res = conn->alterTable(*tempData()->table, *newTable);
+ if (res != true)
+ parentDialog()->setStatus(conn, "");
+ }
+ else {
+ KexiDB::AlterTableHandler::ExecutionArguments args;
+ newTable = alterTableHandler->execute(tempData()->table->name(), args);
+ res = args.result;
+ kexipluginsdbg << "KexiTableDesignerView::storeData() : ALTER TABLE EXECUTE: "
+ << res.toString() << endl;
+ if (true != res) {
+ alterTableHandler->debugError();
+ parentDialog()->setStatus(alterTableHandler, "");
+ }
+ }
+ }
+ if (res == true) {
+ //change current schema
+ tempData()->table = newTable;
+ tempData()->tableSchemaChangedInPreviousView = true;
+ d->history->clear();
+ }
+ else {
+ delete newTable;
+ }
+ delete alterTableHandler;
+ d->recentResultOfStoreData = res;
+ return res;
+}
+
+tristate KexiTableDesignerView::simulateAlterTableExecution(QString *debugTarget)
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+# ifdef KEXI_DEBUG_GUI
+ if (mainWin()->activeWindow() != parentDialog()) //to avoid executing for multiple alter table views
+ return false;
+ if (!tempData()->table || !m_dialog->schemaData())
+ return false;
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ KexiDB::AlterTableHandler::ActionList actions;
+ tristate res = buildAlterTableActions( actions );
+//todo: result?
+ KexiDB::AlterTableHandler alterTableHandler( *conn );
+ alterTableHandler.setActions(actions);
+ KexiDB::AlterTableHandler::ExecutionArguments args;
+ if (debugTarget) {
+ args.debugString = debugTarget;
+ }
+ else {
+ args.simulate = true;
+ }
+ (void)alterTableHandler.execute(tempData()->table->name(), args);
+ return args.result;
+# else
+ return false;
+# endif
+#else
+ return false;
+#endif
+}
+
+void KexiTableDesignerView::slotSimulateAlterTableExecution()
+{
+ (void)simulateAlterTableExecution(0);
+}
+
+tristate KexiTableDesignerView::executeRealAlterTable()
+{
+ QSignal signal;
+ signal.connect( mainWin(), SLOT(slotProjectSave()) );
+ d->tempStoreDataUsingRealAlterTable = true;
+ d->recentResultOfStoreData = false;
+ signal.activate(); //will call KexiMainWindowImpl::slotProjectSaveAs() and thus storeData()
+ d->tempStoreDataUsingRealAlterTable = false;
+ return d->recentResultOfStoreData;
+}
+
+KexiTablePart::TempData* KexiTableDesignerView::tempData() const
+{
+ return static_cast<KexiTablePart::TempData*>(parentDialog()->tempData());
+}
+
+/*void KexiTableDesignerView::slotAboutToUpdateRow(
+ KexiTableItem* item, KexiDB::RowEditBuffer* buffer, KexiDB::ResultInfo* result)
+{
+ KexiDB::RowEditBuffer::SimpleMap map = buffer->simpleBuffer();
+ buffer->debug();
+
+ QVariant old_type = item->at(1);
+ QVariant *buf_type = buffer->at( d->view->field(1)->name() );
+
+ //check if there is a type specified
+// if ((old_type.isNull() && !buf_type) || (buf_type && buf_type->isNull())) {
+ //kdDebug() << "err" << endl;
+ //}
+// allow = true;
+// m_dirty = m_dirty | result->success;
+}*/
+
+#ifdef KEXI_DEBUG_GUI
+void KexiTableDesignerView::debugCommand( KCommand* command, int nestingLevel )
+{
+ if (dynamic_cast<Command*>(command))
+ KexiUtils::addAlterTableActionDebug(dynamic_cast<Command*>(command)->debugString(), nestingLevel);
+ else
+ KexiUtils::addAlterTableActionDebug(command->name(), nestingLevel);
+ //show subcommands
+ if (dynamic_cast<CommandGroup*>(command)) {
+ for (QPtrListIterator<KCommand> it(dynamic_cast<CommandGroup*>(command)->commands()); it.current(); ++it) {
+ debugCommand(it.current(), nestingLevel + 1);
+ }
+ }
+}
+#endif
+
+void KexiTableDesignerView::addHistoryCommand( KCommand* command, bool execute )
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+# ifdef KEXI_DEBUG_GUI
+ debugCommand( command, 0 );
+# endif
+ d->history->addCommand( command, execute );
+ updateUndoRedoActions();
+#endif
+}
+
+void KexiTableDesignerView::updateUndoRedoActions()
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+ setAvailable("edit_undo", d->historyActionCollection->action("edit_undo")->isEnabled());
+ setAvailable("edit_redo", d->historyActionCollection->action("edit_redo")->isEnabled());
+#endif
+}
+
+void KexiTableDesignerView::slotUndo()
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+# ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString("UNDO:"));
+# endif
+ d->history->undo();
+ updateUndoRedoActions();
+#endif
+}
+
+void KexiTableDesignerView::slotRedo()
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+# ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString("REDO:"));
+# endif
+ d->history->redo();
+ updateUndoRedoActions();
+#endif
+}
+
+void KexiTableDesignerView::slotCommandExecuted(KCommand *command)
+{
+#ifdef KEXI_DEBUG_GUI
+ debugCommand( command, 1 );
+#endif
+}
+
+void KexiTableDesignerView::slotAboutToShowContextMenu()
+{
+ //update title
+ if (propertySet()) {
+ const KoProperty::Set &set = *propertySet();
+ QString captionOrName(set["caption"].value().toString());
+ if (captionOrName.isEmpty())
+ captionOrName = set["name"].value().toString();
+//! @todo show "field" icon
+ d->contextMenuTitle->setTitle( i18n("Table field \"%1\"").arg(captionOrName) );
+ }
+ else {
+ d->contextMenuTitle->setTitle( i18n("Empty table row", "Empty Row") );
+ }
+}
+
+QString KexiTableDesignerView::debugStringForCurrentTableSchema(tristate& result)
+{
+ KexiDB::TableSchema tempTable;
+ //copy schema data
+ static_cast<KexiDB::SchemaData&>(tempTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
+ result = buildSchema(tempTable, true /*beSilent*/);
+ if (true!=result)
+ return QString::null;
+ return tempTable.debugString(false /*without name*/);
+}
+
+// -- low-level actions used by undo/redo framework
+
+void KexiTableDesignerView::clearRow(int row, bool addCommand)
+{
+ if (!d->view->acceptRowEdit())
+ return;
+ KexiTableItem *item = d->view->itemAt(row);
+ if (!item)
+ return;
+ //remove from prop. set
+ d->sets->remove( row );
+ //clear row in table view (just clear value in COLUMN_ID_TYPE column)
+// for (int i=0; i < (int)d->view->data()->columnsCount(); i++) {
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowUpdated_enabled = false;
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ d->slotBeforeCellChanged_enabled = false;
+ }
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, QVariant());
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowUpdated_enabled = true;
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ d->view->data()->saveRowChanges(*item, true);
+}
+
+void KexiTableDesignerView::insertField(int row, const QString& caption, bool addCommand)
+{
+ insertFieldInternal(row, 0, caption, addCommand);
+}
+
+void KexiTableDesignerView::insertField(int row, KoProperty::Set& set, bool addCommand)
+{
+ insertFieldInternal(row, &set, QString::null, addCommand);
+}
+
+void KexiTableDesignerView::insertFieldInternal(int row, KoProperty::Set* set, //const KexiDB::Field& field,
+ const QString& caption, bool addCommand)
+{
+ if (set && (!set->contains("type") || !set->contains("caption"))) {
+ kexipluginswarn << "KexiTableDesignerView::insertField(): no 'type' or 'caption' property in set!" << endl;
+ return;
+ }
+ if (!d->view->acceptRowEdit())
+ return;
+ KexiTableItem *item = d->view->itemAt(row);
+ if (!item)
+ return;
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowUpdated_enabled = false;
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ d->slotBeforeCellChanged_enabled = false;
+ }
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION,
+ set ? (*set)["caption"].value() : QVariant(caption));//field.caption());
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE,
+ set ? (int)KexiDB::Field::typeGroup( (*set)["type"].value().toInt() )-1/*counting from 0*/
+ : (((int)KexiDB::Field::TextGroup)-1)/*default type, counting from 0*/
+ );
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC,
+ set ? (*set)["description"].value() : QVariant());//field.description());
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ //this will create a new property set:
+ d->view->data()->saveRowChanges(*item);
+ if (set) {
+ KoProperty::Set *newSet = d->sets->at(row);
+ if (newSet) {
+ *newSet = *set; //deep copy
+ }
+ else {
+ kexipluginswarn << "KexiTableDesignerView::insertField() !newSet, row==" << row << endl;
+ }
+ }
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ d->addHistoryCommand_in_slotRowUpdated_enabled = true;
+ }
+ d->view->updateRow( row );
+ propertySetReloaded(true);
+}
+
+void KexiTableDesignerView::insertEmptyRow( int row, bool addCommand )
+{
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowInserted_enabled = false;
+ }
+ d->view->insertEmptyRow( row );
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowInserted_enabled = true;
+ }
+}
+
+/*void KexiTableDesignerView::deleteRow( int row )
+{
+ d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
+ d->view->deleteItem( d->view->data()->at(row) );
+ d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
+}*/
+
+void KexiTableDesignerView::deleteRow( int row, bool addCommand )
+{
+ KexiTableItem *item = d->view->itemAt( row );
+ if (!item)
+ return;
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
+ }
+ const bool res = d->view->deleteItem(item);
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
+ }
+ if (!res)
+ return;
+}
+
+void KexiTableDesignerView::changeFieldPropertyForRow( int row,
+ const QCString& propertyName, const QVariant& newValue,
+ KoProperty::Property::ListData* const listData, bool addCommand )
+{
+#ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString("** changeFieldProperty: \"")
+ + QString(propertyName) + "\" to \"" + newValue.toString() + "\"", 2/*nestingLevel*/);
+#endif
+ if (!d->view->acceptRowEdit())
+ return;
+
+ KoProperty::Set* set = d->sets->at( row );
+ if (!set || !set->contains(propertyName))
+ return;
+ KoProperty::Property &property = set->property(propertyName);
+ if (listData) {
+ if (listData->keys.isEmpty())
+ property.setListData( 0 );
+ else
+ property.setListData( new KoProperty::Property::ListData(*listData) );
+ }
+ if (propertyName != "type") //delayed type update (we need to have subtype set properly)
+ property.setValue(newValue);
+ KexiTableItem *item = d->view->itemAt(row);
+ Q_ASSERT(item);
+
+ if (propertyName == "type") {
+ // d->addHistoryCommand_in_slotRowUpdated_enabled = false;
+// d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ d->slotPropertyChanged_subType_enabled = false;
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE,
+ int( KexiDB::Field::typeGroup( newValue.toInt() ) )-1);
+ d->view->data()->saveRowChanges(*item);
+ d->addHistoryCommand_in_slotRowUpdated_enabled = true;
+// d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ // d->slotPropertyChanged_subType_enabled = true;
+ property.setValue(newValue); //delayed type update (we needed to have subtype set properly)
+ }
+
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowUpdated_enabled = false;
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ d->slotPropertyChanged_subType_enabled = false;
+ }
+ //special cases: properties displayed within the data grid:
+ if (propertyName == "caption") {
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = false;
+ }
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, newValue);
+ d->view->data()->saveRowChanges(*item);
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ }
+ else if (propertyName == "description") {
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = false;
+ }
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC, newValue);
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ d->view->data()->saveRowChanges(*item);
+ }
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ d->addHistoryCommand_in_slotRowUpdated_enabled = true;
+ d->slotPropertyChanged_subType_enabled = true;
+ }
+ d->view->updateRow( row );
+}
+
+void KexiTableDesignerView::changeFieldProperty( int fieldUID,
+ const QCString& propertyName, const QVariant& newValue,
+ KoProperty::Property::ListData* const listData, bool addCommand )
+{
+ //find a property by UID
+ const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
+ if (row<0) {
+ kexipluginswarn << "KexiTableDesignerView::changeFieldProperty(): field with uid="<<fieldUID<<" not found!"<<endl;
+ return;
+ }
+ changeFieldPropertyForRow(row, propertyName, newValue, listData, addCommand);
+}
+
+void KexiTableDesignerView::changePropertyVisibility(
+ int fieldUID, const QCString& propertyName, bool visible )
+{
+#ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString("** changePropertyVisibility: \"")
+ + QString(propertyName) + "\" to \"" + (visible ? "true" : "false") + "\"", 2/*nestingLevel*/);
+#endif
+ if (!d->view->acceptRowEdit())
+ return;
+
+ //find a property by name
+ const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
+ if (row<0)
+ return;
+ KoProperty::Set* set = d->sets->at( row );
+ if (!set || !set->contains(propertyName))
+ return;
+
+ KoProperty::Property &property = set->property(propertyName);
+ if (property.isVisible() != visible) {
+ property.setVisible(visible);
+ propertySetReloaded(true);
+ }
+}
+
+void KexiTableDesignerView::propertySetSwitched()
+{
+ KexiDataTable::propertySetSwitched();
+
+ //if (parentDialog()!=parentDialog()->mainWin()->currentDialog())
+ // return; //this is not the current dialog's view
+
+ static_cast<KexiTablePart*>(parentDialog()->part())->lookupColumnPage()
+ ->assignPropertySet(propertySet());
+}
+
+bool KexiTableDesignerView::isPhysicalAlteringNeeded()
+{
+ //- create action list for the alter table handler
+ KexiDB::AlterTableHandler::ActionList actions;
+ tristate res = buildAlterTableActions( actions );
+ if (res != true)
+ return true;
+
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ KexiDB::AlterTableHandler *alterTableHandler = new KexiDB::AlterTableHandler( *conn );
+ alterTableHandler->setActions(actions);
+
+ //only compute requirements
+ KexiDB::AlterTableHandler::ExecutionArguments args;
+ args.onlyComputeRequirements = true;
+ (void)alterTableHandler->execute(tempData()->table->name(), args);
+ res = args.result;
+ delete alterTableHandler;
+ if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
+ return false;
+ return true;
+}
+
+#include "kexitabledesignerview.moc"
diff --git a/kexi/plugins/tables/kexitabledesignerview.h b/kexi/plugins/tables/kexitabledesignerview.h
new file mode 100644
index 00000000..773163b6
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignerview.h
@@ -0,0 +1,258 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXITABLEDESIGNERINTERVIEW_H
+#define KEXITABLEDESIGNERINTERVIEW_H
+
+#include <koproperty/property.h>
+#include <kexidb/alter.h>
+#include <core/kexitabledesignerinterface.h>
+
+#include <kexidatatable.h>
+#include "kexitablepart.h"
+
+class KexiTableItem;
+class KexiTableDesignerViewPrivate;
+class KCommand;
+class CommandGroup;
+
+namespace KoProperty {
+ class Set;
+}
+
+//! Design view of the Table Designer
+/*! Contains a spreadsheet-like space for entering field definitions.
+ Property editor is provided for altering field definitions.
+
+ The view also supports Undo and Redo operations.
+ These are connected to a factility creating a list of actions used
+ by AlterTableHandler to perform required operation of altering the table.
+
+ Altering itself is performed upon design saving (storeData()).
+ Saving unstored designs just creates a new table.
+ Saving changes made to empty (not filled with data) table is performed
+ by physically deleting the previous table schema and recreating it
+ TODO: this will be not quite when we have db relationships supported.
+
+ Saving changes made to table containing data requires use of the AlterTableHandler
+ functionality.
+*/
+class KexiTableDesignerView : public KexiDataTable, public KexiTableDesignerInterface
+{
+ Q_OBJECT
+
+ public:
+ /*! Creates a new alter table dialog. */
+ KexiTableDesignerView(KexiMainWindow *win, QWidget *parent);
+
+ virtual ~KexiTableDesignerView();
+
+ KexiTablePart::TempData* tempData() const;
+
+ /*! Clears field information entered for row.
+ This is performed by removing values from caption and data type columns.
+ Used by InsertFieldCommand to undo inserting a new field. */
+ virtual void clearRow(int row, bool addCommand = false);
+
+ /*! Inserts a new field with \a caption for \a row.
+ Property set is also created. */
+ virtual void insertField(int row, const QString& caption, bool addCommand = false);
+
+ /*! Inserts a new \a field for \a row.
+ Property set is also created. \a set will be deeply-copied into the new set.
+ Used by InsertFieldCommand to insert a new field. */
+ virtual void insertField(int row, KoProperty::Set& set, bool addCommand = false);
+
+ /*! Inserts a new empty row at position \a row.
+ Used by RemoveFieldCommand as a part of undo inserting a new field;
+ also used by InsertEmptyRowCommand. */
+ virtual void insertEmptyRow( int row, bool addCommand = false );
+
+ /*! Deletes \a row from the table view. Property set is also deleted.
+ All the subsequent fields are moved up. Used for undoing InsertEmptyRowCommand
+ and by RemoveFieldCommand to remove a field. */
+ virtual void deleteRow( int row, bool addCommand = false );
+
+ /*! Deletes a field for \a row. Property set is also deleted.
+ Used by RemoveFieldCommand to remove a field. */
+// virtual void deleteField( int row );
+
+ /*! Changes property \a propertyName to \a newValue for a field at row \a row.
+ If \a listData is not NULL and not empty, a deep copy of it is passed to Property::setListData().
+ If \a listData \a nlist if not NULL but empty, Property::setListData(0) is called. */
+ virtual void changeFieldPropertyForRow( int row,
+ const QCString& propertyName, const QVariant& newValue,
+ KoProperty::Property::ListData* const listData, bool addCommand );
+
+ /*! Changes property \a propertyName to \a newValue.
+ Works exactly like changeFieldPropertyForRow() except the field is pointed by \a fieldUID.
+ Used by ChangeFieldPropertyCommand to change field's property. */
+ void changeFieldProperty( int fieldUID, const QCString& propertyName,
+ const QVariant& newValue, KoProperty::Property::ListData* const listData = 0,
+ bool addCommand = false );
+
+ /*! Changes visibility of property \a propertyName to \a visible for a field pointed by \a fieldUID.
+ Used by ChangePropertyVisibilityCommand. */
+ void changePropertyVisibility( int fieldUID, const QCString& propertyName, bool visible );
+
+ /*! Builds table field's schema by looking at the \a set. */
+ KexiDB::Field * buildField( const KoProperty::Set &set ) const;
+
+ /*! Creates temporary table for the current design and returns debug string for it. */
+ virtual QString debugStringForCurrentTableSchema(tristate& result);
+
+ /*! Simulates execution of alter table, and puts debug into \a debugTarget.
+ A case when debugTarget is not 0 is true for the alter table test suite. */
+ virtual tristate simulateAlterTableExecution(QString *debugTarget);
+
+ public slots:
+ /*! Real execution of the Alter Table. For debugging of the real alter table.
+ \return true on success, false on failure and cancelled if user has cancelled
+ execution. */
+ virtual tristate executeRealAlterTable();
+
+ protected slots:
+ /*! Equivalent to updateActions(false). Called on row insert/delete
+ in a KexiDataAwarePropertySet. */
+ void updateActions();
+
+ virtual void slotUpdateRowActions(int row);
+
+ void slotAboutToShowContextMenu();
+
+ //! Called before cell change in tableview.
+ void slotBeforeCellChanged(KexiTableItem *item, int colnum,
+ QVariant& newValue, KexiDB::ResultInfo* result);
+
+ //! Called on row change in a tableview.
+ void slotRowUpdated(KexiTableItem *item);
+
+ //! Called before row inserting in tableview.
+ void slotRowInserted();
+// void slotAboutToInsertRow(KexiTableItem* item, KexiDB::ResultInfo* result, bool repaint);
+
+ //! Called before row deleting in tableview.
+ void slotAboutToDeleteRow(KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint);
+
+ /*! Called after any property has been changed in the current property set,
+ to perform some actions (like updating other dependent properties) */
+ void slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property);
+
+ /*! Toggles primary key for currently selected field.
+ Does nothing for empty row. */
+ void slotTogglePrimaryKey();
+
+ /*! Undoes the recently performed action. */
+ void slotUndo();
+
+ /*! Redoes the recently undoed action. */
+ void slotRedo();
+
+ /*! Reaction on command execution from the command history */
+ void slotCommandExecuted(KCommand *command);
+
+ /*! Simulates real execution of the Alter Table. For debugging. */
+ void slotSimulateAlterTableExecution();
+
+ protected:
+ virtual void updateActions(bool activated);
+
+ //! called whenever data should be reloaded (on switching to this view mode)
+ void initData();
+
+ /*! Creates a new property set for \a field.
+ The property set will be asigned to \a row, and owned by this dialog.
+ If \a newOne is true, the property set will be marked as newly created.
+ \return newly created property set. */
+ KoProperty::Set* createPropertySet( int row, const KexiDB::Field& field, bool newOne = false );
+
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+
+ virtual tristate afterSwitchFrom(int mode);
+
+ /*! \return property set associated with currently selected row (i.e. field)
+ or 0 if current row is empty. */
+ virtual KoProperty::Set *propertySet();
+
+// void removeCurrentPropertySet();
+
+ /*! Reimplemented from KexiViewBase, because tables creation is more complex.
+ No table schema altering is required, so just buildSchema() is used to create a new schema.
+ */
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+
+ /*! Reimplemented from KexiViewBase, because table storage is more complex.
+ Table schema altering may be required, so just buildSchema() is used to create a new schema.
+ */
+ virtual tristate storeData(bool dontAsk = false);
+
+ /*! Builds table schema by looking at the current design. Used in storeNewData()
+ and storeData().
+ If \a beSilent is true, no message boxes are used to show questions or warnings.
+ This is used in the altertable test suite (kexi/tests/altertable).
+ \return true on successful schema creating, false on failure and cancelled when there
+ was a problem with user's design (and user has been informed about it). */
+ tristate buildSchema(KexiDB::TableSchema &schema, bool beSilent = false);
+
+ /*! Builds action list usable for KexiDB::AlterTableHandler by looking at undo buffer
+ of commands' history. Used in storeData() */
+ tristate buildAlterTableActions(KexiDB::AlterTableHandler::ActionList &actions);
+
+ /*! Helper, used for slotTogglePrimaryKey() and slotPropertyChanged().
+ Assigns primary key icon and value for property set \a propertySet,
+ and deselects it from previous pkey's row.
+ \a aWasPKey is internal.
+ If \a commandGroup is not 0, it is used as parent group for storing actions' history. */
+ void switchPrimaryKey(KoProperty::Set &propertySet, bool set, bool aWasPKey = false,
+ CommandGroup* commandGroup = 0);
+
+ //! Gets subtype strings and names for type \a fieldType.
+ void getSubTypeListData(KexiDB::Field::TypeGroup fieldTypeGroup,
+ QStringList& stringsList, QStringList& namesList);
+
+ /*! Adds history command \a command to the undo/redo buffer.
+ If \a execute is true, the command is executed afterwards. */
+ void addHistoryCommand( KCommand* command, bool execute );
+
+ //! Updates undo/redo shared actions availability by looking at command history's action
+ void updateUndoRedoActions();
+
+#ifdef KEXI_DEBUG_GUI
+ void debugCommand( KCommand* command, int nestingLevel );
+#endif
+
+ /*! Inserts a new \a field for \a row.
+ Property set is also created. If \a set is not 0 (the default),
+ it will be copied into the new set. Used by insertField(). */
+ void insertFieldInternal(int row, KoProperty::Set* set, const QString& caption, bool addCommand);
+
+ //! Reimplemented to pass the information also to the "Lookup" tab
+ virtual void propertySetSwitched();
+
+ /*! \return true if physical altering is needed for the current list of actions.
+ Used in KexiTableDesignerView::beforeSwitchTo() to avoid warning about removinf
+ table data if table recreating is not needed.
+ True is also returned if there is any trouble with getting the answer. */
+ bool isPhysicalAlteringNeeded();
+
+ private:
+ KexiTableDesignerViewPrivate *d;
+};
+
+#endif
diff --git a/kexi/plugins/tables/kexitabledesignerview_p.cpp b/kexi/plugins/tables/kexitabledesignerview_p.cpp
new file mode 100644
index 00000000..56ef997d
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignerview_p.cpp
@@ -0,0 +1,294 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexitabledesignerview_p.h"
+#include "kexitabledesignerview.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qsplitter.h>
+
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+
+#include <koproperty/set.h>
+
+#include <kexidb/cursor.h>
+#include <kexidb/tableschema.h>
+#include <kexidb/connection.h>
+#include <kexidb/utils.h>
+#include <kexidb/roweditbuffer.h>
+#include <kexidb/error.h>
+#include <kexiutils/identifier.h>
+#include <kexiproject.h>
+#include <keximainwindow.h>
+#include <widget/tableview/kexidataawarepropertyset.h>
+#include <widget/kexicustompropertyfactory.h>
+#include <kexiutils/utils.h>
+#include <kexidialogbase.h>
+#include <kexitableview.h>
+#include "kexitabledesignercommands.h"
+
+using namespace KexiTableDesignerCommands;
+
+//----------------------------------------------
+
+CommandHistory::CommandHistory(KActionCollection *actionCollection, bool withMenus)
+ : KCommandHistory(actionCollection, withMenus)
+{
+ // We need ALL the commands because we'll collect reuse their
+ // data before performing alter table, so set that to the maximum,
+ // as KCommandHistory has default = 50.
+ setUndoLimit(INT_MAX);
+ setRedoLimit(INT_MAX);
+}
+
+void CommandHistory::addCommand(KCommand *command, bool execute)
+{
+ KCommandHistory::addCommand(command, execute);
+ m_commandsToUndo.append(command);
+}
+
+void CommandHistory::undo()
+{
+ if (!m_commandsToUndo.isEmpty()) {
+ KCommand * cmd = m_commandsToUndo.take( m_commandsToUndo.count()-1 );
+ m_commandsToRedo.append( cmd );
+ }
+ KCommandHistory::undo();
+}
+
+void CommandHistory::redo()
+{
+ if (!m_commandsToRedo.isEmpty()) {
+ KCommand * cmd = m_commandsToRedo.take( m_commandsToRedo.count()-1 );
+ m_commandsToUndo.append( cmd );
+ }
+ KCommandHistory::redo();
+}
+
+void CommandHistory::clear() {
+ KCommandHistory::clear(); m_commandsToUndo.clear();
+}
+
+//----------------------------------------------
+
+KexiTableDesignerViewPrivate::KexiTableDesignerViewPrivate(KexiTableDesignerView* aDesignerView)
+ : designerView(aDesignerView)
+ , sets(0)
+ , uniqueIdCounter(0)
+ , dontAskOnStoreData(false)
+ , slotTogglePrimaryKeyCalled(false)
+ , primaryKeyExists(false)
+ , slotPropertyChanged_primaryKey_enabled(true)
+ , slotPropertyChanged_subType_enabled(true)
+ , addHistoryCommand_in_slotPropertyChanged_enabled(true)
+ , addHistoryCommand_in_slotRowUpdated_enabled(true)
+ , addHistoryCommand_in_slotAboutToDeleteRow_enabled(true)
+ , addHistoryCommand_in_slotRowInserted_enabled(true)
+ , slotBeforeCellChanged_enabled(true)
+ , tempStoreDataUsingRealAlterTable(false)
+{
+ historyActionCollection = new KActionCollection((QWidget*)0,"");
+ history = new CommandHistory(historyActionCollection, true);
+
+ internalPropertyNames.insert("subType",(char*)1);
+ internalPropertyNames.insert("uid",(char*)1);
+ internalPropertyNames.insert("newrow",(char*)1);
+ internalPropertyNames.insert("rowSource",(char*)1);
+ internalPropertyNames.insert("rowSourceType",(char*)1);
+ internalPropertyNames.insert("boundColumn",(char*)1);
+ internalPropertyNames.insert("visibleColumn",(char*)1);
+}
+
+KexiTableDesignerViewPrivate::~KexiTableDesignerViewPrivate() {
+ delete sets;
+ delete historyActionCollection;
+ delete history;
+}
+
+int KexiTableDesignerViewPrivate::generateUniqueId()
+{
+ return ++uniqueIdCounter;
+}
+
+void KexiTableDesignerViewPrivate::setPropertyValueIfNeeded(
+ const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& newValue, const QVariant& oldValue, CommandGroup* commandGroup,
+ bool forceAddCommand, bool rememberOldValue,
+ QStringList* const slist, QStringList* const nlist)
+{
+ KoProperty::Property& property = set[propertyName];
+
+ KoProperty::Property::ListData *oldListData = property.listData() ?
+ new KoProperty::Property::ListData(*property.listData()) : 0; //remember because we'll change list data soon
+ if (slist && nlist) {
+ if (slist->isEmpty() || nlist->isEmpty()) {
+ property.setListData(0);
+ }
+ else {
+ property.setListData(*slist, *nlist);
+ }
+ }
+ if (oldValue.type() == newValue.type()
+ && (oldValue == newValue || (!oldValue.isValid() && !newValue.isValid()))
+ && !forceAddCommand)
+ {
+ return;
+ }
+
+ const bool prev_addHistoryCommand_in_slotPropertyChanged_enabled
+ = addHistoryCommand_in_slotPropertyChanged_enabled; //remember
+ addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ if (property.value() != newValue)
+ property.setValue( newValue, rememberOldValue );
+ if (commandGroup) {
+ commandGroup->addCommand(
+ new ChangeFieldPropertyCommand( designerView, set, propertyName, oldValue, newValue,
+ oldListData, property.listData()) );
+ }
+ delete oldListData;
+ addHistoryCommand_in_slotPropertyChanged_enabled
+ = prev_addHistoryCommand_in_slotPropertyChanged_enabled; //restore
+}
+
+void KexiTableDesignerViewPrivate::setPropertyValueIfNeeded(
+ const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& newValue, CommandGroup* commandGroup,
+ bool forceAddCommand, bool rememberOldValue,
+ QStringList* const slist, QStringList* const nlist)
+{
+ KoProperty::Property& property = set[propertyName];
+ QVariant oldValue( property.value() );
+ setPropertyValueIfNeeded( set, propertyName, newValue, property.value(),
+ commandGroup, forceAddCommand, rememberOldValue, slist, nlist);
+}
+
+void KexiTableDesignerViewPrivate::setVisibilityIfNeeded( const KoProperty::Set& set, KoProperty::Property* prop,
+ bool visible, bool &changed, CommandGroup *commandGroup )
+{
+ if (prop->isVisible() != visible) {
+ if (commandGroup) {
+ commandGroup->addCommand(
+ new ChangePropertyVisibilityCommand( designerView, set, prop->name(), visible ) );
+ }
+ prop->setVisible( visible );
+ changed = true;
+ }
+}
+
+bool KexiTableDesignerViewPrivate::updatePropertiesVisibility(KexiDB::Field::Type fieldType, KoProperty::Set &set,
+ CommandGroup *commandGroup)
+{
+ bool changed = false;
+ KoProperty::Property *prop;
+ bool visible;
+
+ prop = &set["subType"];
+ kexipluginsdbg << "subType=" << prop->value().toInt() << " type=" << set["type"].value().toInt()<< endl;
+
+ //if there is no more than 1 subType name or it's a PK: hide the property
+ visible = (prop->listData() && prop->listData()->keys.count() > 1 /*disabled || isObjectTypeGroup*/)
+ && set["primaryKey"].value().toBool()==false;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["objectType"];
+ const bool isObjectTypeGroup = set["type"].value().toInt() == (int)KexiDB::Field::BLOB; // used only for BLOBs
+ visible = isObjectTypeGroup;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["unsigned"];
+ visible = KexiDB::Field::isNumericType(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["length"];
+ visible = (fieldType == KexiDB::Field::Text);
+ if (prop->isVisible()!=visible) {
+// prop->setVisible( visible );
+ //update the length when it makes sense
+ const int lengthToSet = visible ? KexiDB::Field::defaultTextLength() : 0;
+ setPropertyValueIfNeeded( set, "length", lengthToSet,
+ commandGroup, false, false /*!rememberOldValue*/ );
+// if (lengthToSet != prop->value().toInt())
+// prop->setValue( lengthToSet, false );
+// changed = true;
+ }
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+#ifndef KEXI_NO_UNFINISHED
+ prop = &set["precision"];
+ visible = KexiDB::Field::isFPNumericType(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+#endif
+ prop = &set["visibleDecimalPlaces"];
+ visible = KexiDB::supportsVisibleDecimalPlacesProperty(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["unique"];
+ visible = fieldType != KexiDB::Field::BLOB;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["indexed"];
+ visible = fieldType != KexiDB::Field::BLOB;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["allowEmpty"];
+ visible = KexiDB::Field::hasEmptyProperty(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["autoIncrement"];
+ visible = KexiDB::Field::isAutoIncrementAllowed(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+//! @todo remove this when BLOB supports default value
+#ifdef KEXI_NO_UNFINISHED
+ prop = &set["defaultValue"];
+ visible = !isObjectTypeGroup;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+#endif
+
+ return changed;
+}
+
+QString KexiTableDesignerViewPrivate::messageForSavingChanges(bool &emptyTable, bool skipWarning)
+{
+ KexiDB::Connection *conn = designerView->mainWin()->project()->dbConnection();
+ bool ok;
+ emptyTable = conn->isEmpty( *designerView->tempData()->table, ok ) && ok;
+ return i18n("Do you want to save the design now?")
+ + ( (emptyTable || skipWarning) ? QString::null :
+ (QString("\n\n") + designerView->part()->i18nMessage(":additional message before saving design",
+ designerView->parentDialog())) );
+}
+
+void KexiTableDesignerViewPrivate::updateIconForItem(KexiTableItem &item, KoProperty::Set& set)
+{
+ QVariant icon;
+ if (!set["rowSource"].value().toString().isEmpty() && !set["rowSourceType"].value().toString().isEmpty())
+ icon = "combo";
+ //show/hide icon in the table
+ view->data()->clearRowEditBuffer();
+ view->data()->updateRowEditBuffer(&item, COLUMN_ID_ICON, icon);
+ view->data()->saveRowChanges(item, true);
+}
+
+#include "kexitabledesignerview_p.moc"
diff --git a/kexi/plugins/tables/kexitabledesignerview_p.h b/kexi/plugins/tables/kexitabledesignerview_p.h
new file mode 100644
index 00000000..f5650e74
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignerview_p.h
@@ -0,0 +1,191 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIALTERTABLEDIALOG_P_H
+#define KEXIALTERTABLEDIALOG_P_H
+
+#include "kexitabledesignerview.h"
+#include <kcommand.h>
+
+class KexiDataAwarePropertySet;
+
+//! @internal indices for table columns
+#define COLUMN_ID_ICON 0
+#define COLUMN_ID_CAPTION 1
+#define COLUMN_ID_TYPE 2
+#define COLUMN_ID_DESC 3
+
+/*! @internal
+ Command group, reimplemented to get access to commands().
+ We need it to iterate through commands so we can perform a set of ALTER TABLE atomic actions. */
+class CommandGroup : public KMacroCommand
+{
+ public:
+ CommandGroup( const QString & name )
+ : KMacroCommand(name)
+ {}
+ virtual ~CommandGroup() {}
+ const QPtrList<KCommand>& commands() const { return m_commands; }
+};
+
+/*! @internal
+ Command history, reimplemented to get access to commands().
+ We need it to iterate through commands so we can perform a set of ALTER TABLE atomic actions. */
+class CommandHistory : public KCommandHistory
+{
+ Q_OBJECT
+ public:
+ CommandHistory(KActionCollection *actionCollection, bool withMenus = true);
+
+ const QPtrList<KCommand>& commands() const { return m_commandsToUndo; }
+
+ void addCommand(KCommand *command, bool execute = true);
+
+ void clear();
+
+ public slots:
+ virtual void undo();
+ virtual void redo();
+
+ protected:
+ QPtrList<KCommand> m_commandsToUndo, m_commandsToRedo;
+};
+
+//----------------------------------------------
+
+//! @internal
+class KexiTableDesignerViewPrivate
+{
+ public:
+ KexiTableDesignerViewPrivate(KexiTableDesignerView* aDesignerView);
+ ~KexiTableDesignerViewPrivate();
+
+ int generateUniqueId();
+
+ /*! @internal
+ Sets property \a propertyName in property set \a set to \a newValue.
+ If \a commandGroup is not 0, a new ChangeFieldPropertyCommand object is added there as well.
+ While setting the new value, addHistoryCommand_in_slotPropertyChanged_enabled is set to false,
+ so addHistoryCommand() wont be executed in slotPropertyChanged() as an answer to setting
+ the property.
+
+ If \a forceAddCommand is false (the default) and \a newValue does not differ from curent property value
+ (set[propertyName].value()), ChangeFieldPropertyCommand command is not added to the \a commandGroup.
+ Otherwise, command is always added.
+
+ \a rememberOldValue argument is passed to Property::setValue()
+
+ If \a slist and \a nlist if not NULL and not empty, these are passed to Property::setListData().
+ If \a slist and \a nlist if not NULL but empty, Property::setListData(0) is called.
+
+ addHistoryCommand_in_slotPropertyChanged_enabled is then set back to the original state.
+ */
+ void setPropertyValueIfNeeded( const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& newValue, CommandGroup* commandGroup,
+ bool forceAddCommand = false, bool rememberOldValue = true,
+ QStringList* const slist = 0, QStringList* const nlist = 0);
+
+ /*! Like above but allows to specify \a oldValue. */
+ void setPropertyValueIfNeeded(
+ const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& newValue, const QVariant& oldValue, CommandGroup* commandGroup,
+ bool forceAddCommand = false, bool rememberOldValue = true,
+ QStringList* const slist = 0, QStringList* const nlist = 0);
+
+ /*! @internal
+ Used in updatePropertiesVisibility().
+ Does nothing if visibility should not be changed, i.e. when prop->isVisible()==visible,
+ otherwise sets changed to true and sets visibility of property \a prop to \a visible.
+ */
+ void setVisibilityIfNeeded( const KoProperty::Set& set, KoProperty::Property* prop,
+ bool visible, bool &changed, CommandGroup *commandGroup );
+
+ bool updatePropertiesVisibility(KexiDB::Field::Type fieldType, KoProperty::Set &set,
+ CommandGroup *commandGroup = 0);
+
+ /*! \return message used to ask user for accepting saving the design.
+ \a emptyTable is set to true if the table designed contains no rows.
+ If \a skipWarning is true, no warning about data loss is appended (useful when
+ only non-physical altering actions will be performed). */
+ QString messageForSavingChanges(bool &emptyTable, bool skipWarning = false);
+
+ /*! Updates icon in the first column, depending on property set \a set.
+ For example, when "rowSource" and "rowSourceType" propertiesa are not empty,
+ "combo" icon appears. */
+ void updateIconForItem(KexiTableItem &item, KoProperty::Set& set);
+
+ KexiTableDesignerView* designerView;
+
+ KexiTableView *view; //!< helper
+
+ KexiTableViewData *data;
+
+ KexiDataAwarePropertySet *sets;
+
+ int row; //!< used to know if a new row is selected in slotCellSelected()
+
+ KToggleAction *action_toggle_pkey;
+
+ KPopupTitle *contextMenuTitle;
+
+ int uniqueIdCounter;
+
+ //! internal
+ int maxTypeNameTextWidth;
+ //! Set to true in beforeSwitchTo() to avoid asking again in storeData()
+ bool dontAskOnStoreData : 1;
+
+ bool slotTogglePrimaryKeyCalled : 1;
+
+ bool primaryKeyExists : 1;
+ //! Used in slotPropertyChanged() to avoid infinite recursion
+ bool slotPropertyChanged_primaryKey_enabled : 1;
+ //! Used in slotPropertyChanged() to avoid infinite recursion
+ bool slotPropertyChanged_subType_enabled : 1;
+ //! used in slotPropertyChanged() to disable addHistoryCommand()
+ bool addHistoryCommand_in_slotPropertyChanged_enabled : 1;
+ //! used in slotRowUpdated() to disable addHistoryCommand()
+ bool addHistoryCommand_in_slotRowUpdated_enabled : 1;
+ //! used in slotAboutToDeleteRow() to disable addHistoryCommand()
+ bool addHistoryCommand_in_slotAboutToDeleteRow_enabled : 1;
+ //! used in slotRowInserted() to disable addHistoryCommand()
+ bool addHistoryCommand_in_slotRowInserted_enabled : 1;
+
+ //! used to disable slotBeforeCellChanged()
+ bool slotBeforeCellChanged_enabled : 1;
+
+//! @tood temp; remove this:
+ //! Temporary flag, used for testingu the Alter Table machinery. Affects storeData()
+ //! Used in slotExecuteRealAlterTable() to switch on real alter table for a while.
+ bool tempStoreDataUsingRealAlterTable : 1;
+
+ /*! Set to a recent result of calling \ref tristate KexiTableDesignerView::storeData(bool dontAsk).
+ Then, it is used in \ref void KexiTableDesignerView::executeRealAlterTable()
+ to know what return value should be. */
+ tristate recentResultOfStoreData;
+
+ KActionCollection* historyActionCollection;
+ CommandHistory* history;
+
+ //! A cache used in KexiTableDesignerView::buildField() to quickly identify
+ //! properties internal to the designer
+ QAsciiDict<char> internalPropertyNames;
+};
+
+#endif
diff --git a/kexi/plugins/tables/kexitablehandler.desktop b/kexi/plugins/tables/kexitablehandler.desktop
new file mode 100644
index 00000000..8491b7a3
--- /dev/null
+++ b/kexi/plugins/tables/kexitablehandler.desktop
@@ -0,0 +1,118 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Tables
+GenericName[bg]=Таблици
+GenericName[br]=Taolennoù
+GenericName[ca]=Taules
+GenericName[cs]=Tabulky
+GenericName[cy]=Tablau
+GenericName[da]=Tabeller
+GenericName[de]=Tabellen
+GenericName[el]=Πίνακες
+GenericName[eo]=Tabeloj
+GenericName[es]=Tablas
+GenericName[et]=Tabelid
+GenericName[eu]=Taulak
+GenericName[fa]=جدولها
+GenericName[fi]=Taulukot
+GenericName[fr]=Tableaux
+GenericName[fy]=Tabellen
+GenericName[ga]=Táblaí
+GenericName[gl]=Táboas
+GenericName[he]=טבלאות
+GenericName[hi]=तालिका
+GenericName[hr]=Tablice
+GenericName[hu]=Táblák
+GenericName[is]=Töflur
+GenericName[it]=Tabelle
+GenericName[ja]=テーブル
+GenericName[km]=តារាង​
+GenericName[lt]=Lentelės
+GenericName[lv]=Tabulas
+GenericName[ms]=Jadual
+GenericName[nb]=Tabeller
+GenericName[nds]=Tabellen
+GenericName[ne]=तालिका
+GenericName[nl]=Tabellen
+GenericName[nn]=Tabellar
+GenericName[pl]=Tabele
+GenericName[pt]=Tabelas
+GenericName[pt_BR]=Tabelas
+GenericName[ru]=Таблицы
+GenericName[se]=Tabeallat
+GenericName[sk]=Tabuľky
+GenericName[sl]=Tabele
+GenericName[sr]=Табеле
+GenericName[sr@Latn]=Tabele
+GenericName[sv]=Tabeller
+GenericName[ta]=அட்டவணைகள்
+GenericName[tr]=Tablolar
+GenericName[uk]=Таблиці
+GenericName[uz]=Jadvallar
+GenericName[uz@cyrillic]=Жадваллар
+GenericName[zh_CN]=表
+GenericName[zh_TW]=表格
+Name=Tables
+Name[bg]=Таблици
+Name[br]=Taolennoù
+Name[ca]=Taules
+Name[cs]=Tabulky
+Name[cy]=Tablau
+Name[da]=Tabeller
+Name[de]=Tabellen
+Name[el]=Πίνακες
+Name[eo]=Tabeloj
+Name[es]=Tablas
+Name[et]=Tabelid
+Name[eu]=Taulak
+Name[fa]=جدولها
+Name[fi]=Taulukot
+Name[fr]=Tableaux
+Name[fy]=Tabellen
+Name[ga]=Táblaí
+Name[gl]=Táboas
+Name[he]=טבלאות
+Name[hi]=टेबल्स
+Name[hr]=Tablice
+Name[hu]=Táblák
+Name[is]=Töflur
+Name[it]=Tabelle
+Name[ja]=テーブル
+Name[km]=តារាង​
+Name[lt]=Lentelės
+Name[lv]=Tabulas
+Name[ms]=Jadual
+Name[nb]=Tabeller
+Name[nds]=Tabellen
+Name[ne]=तालिकाहरू
+Name[nl]=Tabellen
+Name[nn]=Tabellar
+Name[pl]=Tabele
+Name[pt]=Tabelas
+Name[pt_BR]=Tabelas
+Name[ru]=Таблицы
+Name[se]=Tabeallat
+Name[sk]=Tabuľky
+Name[sl]=Tabele
+Name[sr]=Табеле
+Name[sr@Latn]=Tabele
+Name[sv]=Tabeller
+Name[ta]=அட்டவணை
+Name[tg]=Ҷадвалҳо
+Name[tr]=Tablolar
+Name[uk]=Таблиці
+Name[uz]=Jadvallar
+Name[uz@cyrillic]=Жадваллар
+Name[wa]=Tåvleas
+Name[zh_CN]=表
+Name[zh_TW]=表格
+X-KDE-Library=kexihandler_table
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=table
+X-Kexi-TypeMime=kexi/table
+X-Kexi-ItemIcon=table
+X-Kexi-SupportsDataExport=true
+X-Kexi-SupportsPrinting=true
diff --git a/kexi/plugins/tables/kexitablepart.cpp b/kexi/plugins/tables/kexitablepart.cpp
new file mode 100644
index 00000000..3d09a81e
--- /dev/null
+++ b/kexi/plugins/tables/kexitablepart.cpp
@@ -0,0 +1,313 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2002, 2003 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexitablepart.h"
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kmessagebox.h>
+#include <ktabwidget.h>
+#include <kiconloader.h>
+
+#include "keximainwindow.h"
+#include "kexiproject.h"
+#include "kexipartinfo.h"
+#include "widget/kexidatatable.h"
+#include "widget/tableview/kexidatatableview.h"
+#include "kexitabledesignerview.h"
+#include "kexitabledesigner_dataview.h"
+#include "kexilookupcolumnpage.h"
+
+#include <kexidb/connection.h>
+#include <kexidb/cursor.h>
+#include <kexidialogbase.h>
+
+//! @internal
+class KexiTablePart::Private
+{
+ public:
+ Private()
+ {
+ }
+ ~Private()
+ {
+ delete static_cast<KexiLookupColumnPage*>(lookupColumnPage);
+ }
+ QGuardedPtr<KexiLookupColumnPage> lookupColumnPage;
+};
+
+KexiTablePart::KexiTablePart(QObject *parent, const char *name, const QStringList &l)
+ : KexiPart::Part(parent, name, l)
+ , d(new Private())
+{
+ // REGISTERED ID:
+ m_registeredPartID = (int)KexiPart::TableObjectType;
+
+ kdDebug() << "KexiTablePart::KexiTablePart()" << endl;
+ m_names["instanceName"]
+ = i18n("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). "
+ "Use '_' character instead of spaces. First character should be a..z character. "
+ "If you cannot use latin characters in your language, use english word.",
+ "table");
+ m_names["instanceCaption"] = i18n("Table");
+ m_supportedViewModes = Kexi::DataViewMode | Kexi::DesignViewMode;
+//js TODO: also add Kexi::TextViewMode when we'll have SQL ALTER TABLE EDITOR!!!
+}
+
+KexiTablePart::~KexiTablePart()
+{
+ delete d;
+}
+
+void KexiTablePart::initPartActions()
+{
+}
+
+void KexiTablePart::initInstanceActions()
+{
+//moved to main window createSharedAction(Kexi::DataViewMode, i18n("Filter"), "filter", 0, "tablepart_filter");
+
+ KAction *a = createSharedToggleAction(
+ Kexi::DesignViewMode, i18n("Primary Key"), "key", 0, "tablepart_toggle_pkey");
+// Kexi::DesignViewMode, i18n("Toggle Primary Key"), "key", 0, "tablepart_toggle_pkey");
+ a->setWhatsThis(i18n("Sets or removes primary key for currently selected field."));
+}
+
+KexiDialogTempData* KexiTablePart::createTempData(KexiDialogBase* dialog)
+{
+ return new KexiTablePart::TempData(dialog);
+}
+
+KexiViewBase* KexiTablePart::createView(QWidget *parent, KexiDialogBase* dialog,
+ KexiPart::Item &item, int viewMode, QMap<QString,QString>*)
+{
+ KexiMainWindow *win = dialog->mainWin();
+ if (!win || !win->project() || !win->project()->dbConnection())
+ return 0;
+
+
+ KexiTablePart::TempData *temp = static_cast<KexiTablePart::TempData*>(dialog->tempData());
+ if (!temp->table) {
+ temp->table = win->project()->dbConnection()->tableSchema(item.name());
+ kdDebug() << "KexiTablePart::execute(): schema is " << temp->table << endl;
+ }
+
+ if (viewMode == Kexi::DesignViewMode) {
+ KexiTableDesignerView *t = new KexiTableDesignerView(win, parent);
+ return t;
+ }
+ else if (viewMode == Kexi::DataViewMode) {
+ if(!temp->table)
+ return 0; //todo: message
+ //we're not setting table schema here -it will be forced to set
+ // in KexiTableDesigner_DataView::afterSwitchFrom()
+ KexiTableDesigner_DataView *t = new KexiTableDesigner_DataView(win, parent);
+ return t;
+ }
+ return 0;
+}
+
+bool KexiTablePart::remove(KexiMainWindow *win, KexiPart::Item &item)
+{
+ if (!win || !win->project() || !win->project()->dbConnection())
+ return false;
+
+ KexiDB::Connection *conn = win->project()->dbConnection();
+ KexiDB::TableSchema *sch = conn->tableSchema(item.identifier());
+
+ if (sch) {
+ tristate res = KexiTablePart::askForClosingObjectsUsingTableSchema(
+ win, *conn, *sch,
+ i18n("You are about to remove table \"%1\" but following objects using this table are opened:")
+ .arg(sch->name()));
+ return true == conn->dropTable( sch );
+ }
+ //last chance: just remove item
+ return conn->removeObject( item.identifier() );
+}
+
+tristate KexiTablePart::rename(KexiMainWindow *win, KexiPart::Item & item,
+ const QString& newName)
+{
+//TODO: what about objects (queries/forms) that use old name?
+ KexiDB::Connection *conn = win->project()->dbConnection();
+ KexiDB::TableSchema *sch = conn->tableSchema(item.identifier());
+ if (!sch)
+ return false;
+ return conn->alterTableName(*sch, newName);
+}
+
+KexiDB::SchemaData*
+KexiTablePart::loadSchemaData(KexiDialogBase *dlg, const KexiDB::SchemaData& sdata, int viewMode)
+{
+ Q_UNUSED( viewMode );
+
+ return dlg->mainWin()->project()->dbConnection()->tableSchema( sdata.name() );
+}
+
+#if 0
+KexiPart::DataSource *
+KexiTablePart::dataSource()
+{
+ return new KexiTableDataSource(this);
+}
+#endif
+
+tristate KexiTablePart::askForClosingObjectsUsingTableSchema(QWidget *parent, KexiDB::Connection& conn,
+ KexiDB::TableSchema& table, const QString& msg)
+{
+ QPtrList<KexiDB::Connection::TableSchemaChangeListenerInterface>* listeners
+ = conn.tableSchemaChangeListeners(table);
+ if (!listeners || listeners->isEmpty())
+ return true;
+
+ QString openedObjectsStr = "<ul>";
+ for (QPtrListIterator<KexiDB::Connection::TableSchemaChangeListenerInterface> it(*listeners);
+ it.current(); ++it) {
+ openedObjectsStr += QString("<li>%1</li>").arg(it.current()->listenerInfoString);
+ }
+ openedObjectsStr += "</ul>";
+ int r = KMessageBox::questionYesNo(parent,
+ "<p>"+msg+"</p><p>"+openedObjectsStr+"</p><p>"
+ +i18n("Do you want to close all windows for these objects?"),
+ QString::null, KGuiItem(i18n("Close windows"),"fileclose"), KStdGuiItem::cancel());
+ tristate res;
+ if (r == KMessageBox::Yes) {
+ //try to close every window
+ res = conn.closeAllTableSchemaChangeListeners(table);
+ if (res!=true) //do not expose closing errors twice; just cancel
+ res = cancelled;
+ }
+ else
+ res = cancelled;
+
+ return res;
+}
+
+QString
+KexiTablePart::i18nMessage(const QCString& englishMessage, KexiDialogBase* dlg) const
+{
+ if (englishMessage=="Design of object \"%1\" has been modified.")
+ return i18n("Design of table \"%1\" has been modified.");
+
+ if (englishMessage=="Object \"%1\" already exists.")
+ return i18n("Table \"%1\" already exists.");
+
+ if (dlg->currentViewMode()==Kexi::DesignViewMode && !dlg->neverSaved()
+ && englishMessage==":additional message before saving design")
+ return i18n("Warning! Any data in this table will be removed upon design's saving!");
+
+ return englishMessage;
+}
+
+void KexiTablePart::setupCustomPropertyPanelTabs(KTabWidget *tab, KexiMainWindow* mainWin)
+{
+ if (!d->lookupColumnPage) {
+ d->lookupColumnPage = new KexiLookupColumnPage(0);
+ connect(d->lookupColumnPage, SIGNAL(jumpToObjectRequested(const QCString&, const QCString&)),
+ mainWin, SLOT(highlightObject(const QCString&, const QCString&)));
+
+//! @todo add "Table" tab
+
+ /*
+ connect(d->dataSourcePage, SIGNAL(formDataSourceChanged(const QCString&, const QCString&)),
+ KFormDesigner::FormManager::self(), SLOT(setFormDataSource(const QCString&, const QCString&)));
+ connect(d->dataSourcePage, SIGNAL(dataSourceFieldOrExpressionChanged(const QString&, const QString&, KexiDB::Field::Type)),
+ KFormDesigner::FormManager::self(), SLOT(setDataSourceFieldOrExpression(const QString&, const QString&, KexiDB::Field::Type)));
+ connect(d->dataSourcePage, SIGNAL(insertAutoFields(const QString&, const QString&, const QStringList&)),
+ KFormDesigner::FormManager::self(), SLOT(insertAutoFields(const QString&, const QString&, const QStringList&)));*/
+ }
+
+ KexiProject *prj = mainWin->project();
+ d->lookupColumnPage->setProject(prj);
+
+//! @todo add lookup field icon
+ tab->addTab( d->lookupColumnPage, SmallIconSet("combo"), "");
+ tab->setTabToolTip( d->lookupColumnPage, i18n("Lookup column"));
+}
+
+KexiLookupColumnPage* KexiTablePart::lookupColumnPage() const
+{
+ return d->lookupColumnPage;
+}
+
+//----------------
+
+#if 0
+KexiTableDataSource::KexiTableDataSource(KexiPart::Part *part)
+ : KexiPart::DataSource(part)
+{
+}
+
+KexiTableDataSource::~KexiTableDataSource()
+{
+}
+
+KexiDB::FieldList *
+KexiTableDataSource::fields(KexiProject *project, const KexiPart::Item &it)
+{
+ kdDebug() << "KexiTableDataSource::fields(): " << it.name() << endl;
+ return project->dbConnection()->tableSchema(it.name());
+}
+
+KexiDB::Cursor *
+KexiTableDataSource::cursor(KexiProject * /*project*/,
+ const KexiPart::Item &/*it*/, bool /*buffer*/)
+{
+ return 0;
+}
+#endif
+
+//----------------
+
+KexiTablePart::TempData::TempData(QObject* parent)
+ : KexiDialogTempData(parent)
+ , table(0)
+ , tableSchemaChangedInPreviousView(true /*to force reloading on startup*/ )
+{
+}
+
+//----------------
+
+/**
+TODO
+*/
+/*
+AboutData( const char *programName,
+ const char *version,
+ const char *i18nShortDescription = 0,
+ int licenseType = License_Unknown,
+ const char *i18nCopyrightStatement = 0,
+ const char *i18nText = 0,
+ const char *homePageAddress = 0,
+ const char *bugsEmailAddress = "submit@bugs.kde.org"
+);
+
+#define KEXIPART_EXPORT_FACTORY( libname, partClass, aboutData ) \
+ static KexiPart::AboutData * libname ## updateAD(KexiPart::AboutData *ad) \
+ { ad->setAppName( #libname ); return ad; } \
+ K_EXPORT_COMPONENT_FACTORY( libname, KGenericFactory<partClass>(libname ## updateAD(#libname)) )
+*/
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_table, KGenericFactory<KexiTablePart>("kexihandler_table") )
+
+#include "kexitablepart.moc"
+
diff --git a/kexi/plugins/tables/kexitablepart.h b/kexi/plugins/tables/kexitablepart.h
new file mode 100644
index 00000000..e4b060ad
--- /dev/null
+++ b/kexi/plugins/tables/kexitablepart.h
@@ -0,0 +1,100 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2002, 2003 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXITABLEPART_H
+#define KEXITABLEPART_H
+
+#include <kexi.h>
+#include <kexipart.h>
+#include <kexidialogbase.h>
+//#include <kexipartdatasource.h>
+#include <kexipartitem.h>
+#include <kexidb/fieldlist.h>
+
+class KexiMainWin;
+class KexiLookupColumnPage;
+
+class KexiTablePart : public KexiPart::Part
+{
+ Q_OBJECT
+
+ public:
+ KexiTablePart(QObject *parent, const char *name, const QStringList &);
+ virtual ~KexiTablePart();
+
+ virtual bool remove(KexiMainWindow *win, KexiPart::Item &item);
+
+ virtual tristate rename(KexiMainWindow *win, KexiPart::Item &item,
+ const QString& newName);
+
+// virtual KexiPart::DataSource *dataSource();
+
+ class TempData : public KexiDialogTempData
+ {
+ public:
+ TempData(QObject* parent);
+ KexiDB::TableSchema *table;
+ /*! true, if \a table member has changed in previous view. Used on view switching.
+ We're checking this flag to see if we should refresh data for DataViewMode. */
+ bool tableSchemaChangedInPreviousView : 1;
+ };
+
+ static tristate askForClosingObjectsUsingTableSchema(
+ QWidget *parent, KexiDB::Connection& conn,
+ KexiDB::TableSchema& table, const QString& msg);
+
+ virtual QString i18nMessage(const QCString& englishMessage,
+ KexiDialogBase* dlg) const;
+
+ KexiLookupColumnPage* lookupColumnPage() const;
+
+ protected:
+ virtual KexiDialogTempData* createTempData(KexiDialogBase* dialog);
+
+ virtual KexiViewBase* createView(QWidget *parent, KexiDialogBase* dialog,
+ KexiPart::Item &item, int viewMode = Kexi::DataViewMode, QMap<QString,QString>* staticObjectArgs = 0);
+
+ virtual void initPartActions();
+ virtual void initInstanceActions();
+
+ virtual void setupCustomPropertyPanelTabs(KTabWidget *tab, KexiMainWindow* mainWin);
+
+ virtual KexiDB::SchemaData* loadSchemaData(KexiDialogBase *dlg, const KexiDB::SchemaData& sdata, int viewMode);
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#if 0
+class KexiTableDataSource : public KexiPart::DataSource
+{
+ public:
+ KexiTableDataSource(KexiPart::Part *part);
+ ~KexiTableDataSource();
+
+ virtual KexiDB::FieldList *fields(KexiProject *project, const KexiPart::Item &item);
+ virtual KexiDB::Cursor *cursor(KexiProject *project, const KexiPart::Item &item, bool buffer);
+};
+#endif
+
+#endif
+
diff --git a/kexi/plugins/tables/kexitablepartinstui.rc b/kexi/plugins/tables/kexitablepartinstui.rc
new file mode 100644
index 00000000..e96a5976
--- /dev/null
+++ b/kexi/plugins/tables/kexitablepartinstui.rc
@@ -0,0 +1,18 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexitablepartinst" version="6">
+
+<MenuBar>
+ <Menu name="edit" noMerge="0">
+ <text>&amp;Edit</text>
+ <Separator/>
+ <Action name="tablepart_toggle_pkey"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="designToolBar" fullWidth="false" noMerge="0">
+ <text>Design</text>
+ <!-- Design View -->
+ <!-- TODO: reenable after shared toggle actions fix: Action name="tablepart_toggle_pkey"/ -->
+</ToolBar>
+
+</kpartgui>
diff --git a/kexi/plugins/tables/kexitablepartui.rc b/kexi/plugins/tables/kexitablepartui.rc
new file mode 100644
index 00000000..c78b2587
--- /dev/null
+++ b/kexi/plugins/tables/kexitablepartui.rc
@@ -0,0 +1,7 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexitablepart" version="5">
+
+<MenuBar>
+</MenuBar>
+
+</kpartgui>