From d8753fcbe00f9cbbb02b389e1556ad01e47d2079 Mon Sep 17 00:00:00 2001 From: Juan Date: Sat, 12 Oct 2013 01:07:35 +0200 Subject: [PATCH] Added add project files to repository --- AUTHORS | 3 + COPYING | 674 +++ ChangeLog | 0 INSTALL | 370 ++ Makefile.am | 21 + Makefile.decl | 74 + NEWS | 0 README | 110 + autogen.sh | 5 + configure.ac | 133 + db/Makefile.am | 100 + db/db-calc.c | 368 ++ db/db-calc.h | 93 + db/db-conn.c | 1069 ++++ db/db-conn.h | 155 + db/db-file-loader.c | 756 +++ db/db-file-loader.h | 81 + db/db-iter.c | 72 + db/db-iter.h | 52 + db/db-iterator.c | 1159 +++++ db/db-iterator.h | 137 + db/db-model-holder.c | 72 + db/db-model-holder.h | 48 + db/db-model.c | 3444 +++++++++++++ db/db-model.h | 292 ++ db/db-param.c | 262 + db/db-param.h | 56 + db/db-plugin.c | 216 + db/db-plugin.h | 83 + db/db-request.c | 487 ++ db/db-request.h | 91 + db/db-result-set.c | 124 + db/db-result-set.h | 46 + db/db-result.c | 116 + db/db-result.h | 86 + db/db-row.c | 124 + db/db-row.h | 49 + db/db.h | 33 + db/db.pc.in | 11 + debian/changelog | 5 + debian/compat | 1 + debian/control | 73 + debian/copyright | 29 + debian/docs | 2 + debian/hedera.dirs | 8 + debian/hedera.install | 7 + debian/hedera.links | 1 + debian/hedera.manpages | 1 + debian/libhedera-dev.dirs | 15 + debian/libhedera-dev.install | 25 + debian/libhedera1.dirs | 6 + debian/libhedera1.install | 13 + debian/menu | 5 + debian/rules | 21 + debian/source/format | 1 + docs/Makefile.am | 3 + docs/reference/Makefile.am | 3 + docs/reference/hedera/Makefile.am | 99 + docs/reference/hedera/adding-modules.xml | 69 + docs/reference/hedera/configuring.xml | 87 + docs/reference/hedera/first-tutorial.xml | 27 + docs/reference/hedera/hedera-docs.sgml | 153 + docs/reference/hedera/image/vn-calendar.png | Bin 0 -> 15046 bytes docs/reference/hedera/introduction.xml | 50 + docs/reference/hedera/module-tutorial.xml | 76 + docs/reference/hedera/running.xml | 53 + glade/Makefile.am | 23 + glade/glade-vn-batch.c | 69 + glade/glade-vn.c | 23 + glade/glade-vn.h | 26 + glade/glade.sh | 7 + glade/vn.xml | 150 + gvn/Makefile.am | 63 + gvn/gvn-misc.c | 112 + gvn/gvn-misc.h | 32 + gvn/gvn-null.c | 33 + gvn/gvn-null.h | 41 + gvn/gvn-param-spec.c | 325 ++ gvn/gvn-param-spec.h | 81 + gvn/gvn-param.c | 504 ++ gvn/gvn-param.h | 109 + gvn/gvn-time.c | 97 + gvn/gvn-time.h | 56 + gvn/gvn-value.c | 575 +++ gvn/gvn-value.h | 38 + gvn/gvn.h | 30 + gvn/gvn.pc.in | 11 + image/icon.svg | 558 +++ image/logo.svg | 172 + main/Makefile.am | 45 + main/cacert.pem | 25 + main/hedera.1 | 50 + main/hedera.pc.in | 11 + main/hedera.sh.in | 10 + main/main.c | 38 + main/vn-hedera.desktop.in | 11 + module/Makefile.am | 5 + module/data/Makefile.am | 13 + module/data/consulter.glade | 304 ++ module/data/consulter.ui | 18 + module/data/customer.glade | 415 ++ module/data/example.ui | 11 + module/data/example.xml | 37 + module/data/signer.glade | 219 + module/data/users.glade | 908 ++++ module/src/Makefile.am | 21 + module/src/vn-consulter.c | 202 + module/src/vn-consulter.h | 50 + module/src/vn-customer.c | 39 + module/src/vn-customer.h | 42 + module/src/vn-users.c | 132 + module/src/vn-users.h | 49 + plugin/Makefile.am | 4 + plugin/mysql/Makefile.am | 20 + plugin/mysql/db-mysql.c | 427 ++ plugin/mysql/db-mysql.h | 54 + plugin/pg/Makefile.am | 41 + plugin/pg/db-pg.c | 880 ++++ plugin/pg/db-pg.h | 49 + po/POTFILES.in | 47 + po/POTFILES.skip | 13 + po/ca.po | 1075 ++++ po/es.po | 1107 +++++ po/nl.po | 1074 ++++ sql/Makefile.am | 119 + sql/parser/Makefile.am | 34 + sql/parser/gram.y | 650 +++ sql/parser/lemon.c | 4895 +++++++++++++++++++ sql/parser/lempar-tmpl.c | 851 ++++ sql/parser/scan.rl | 263 + sql/sql-delete.c | 110 + sql/sql-delete.h | 50 + sql/sql-dml.c | 117 + sql/sql-dml.h | 53 + sql/sql-expr.c | 35 + sql/sql-expr.h | 43 + sql/sql-field.c | 180 + sql/sql-field.h | 51 + sql/sql-function.c | 150 + sql/sql-function.h | 52 + sql/sql-holder.c | 127 + sql/sql-holder.h | 46 + sql/sql-insert.c | 169 + sql/sql-insert.h | 58 + sql/sql-join.c | 256 + sql/sql-join.h | 67 + sql/sql-list.c | 266 + sql/sql-list.h | 53 + sql/sql-multi-stmt.c | 103 + sql/sql-multi-stmt.h | 49 + sql/sql-object.c | 273 ++ sql/sql-object.h | 71 + sql/sql-operation.c | 188 + sql/sql-operation.h | 84 + sql/sql-param-list.c | 109 + sql/sql-param-list.h | 42 + sql/sql-param-object.c | 106 + sql/sql-param-object.h | 39 + sql/sql-parser.h | 35 + sql/sql-render.c | 442 ++ sql/sql-render.h | 89 + sql/sql-select-field.c | 124 + sql/sql-select-field.h | 47 + sql/sql-select-order.c | 139 + sql/sql-select-order.h | 63 + sql/sql-select.c | 308 ++ sql/sql-select.h | 81 + sql/sql-set.c | 108 + sql/sql-set.h | 45 + sql/sql-stmt.c | 36 + sql/sql-stmt.h | 45 + sql/sql-string.c | 218 + sql/sql-string.h | 55 + sql/sql-subquery.c | 119 + sql/sql-subquery.h | 48 + sql/sql-table.c | 128 + sql/sql-table.h | 46 + sql/sql-target.c | 100 + sql/sql-target.h | 45 + sql/sql-update-set.c | 120 + sql/sql-update-set.h | 48 + sql/sql-update.c | 117 + sql/sql-update.h | 52 + sql/sql-value.c | 239 + sql/sql-value.h | 51 + sql/sql.h | 48 + sql/sql.pc.in | 11 + template/lib-object.c | 101 + template/lib-object.h | 45 + vapi/Db-1.0.metadata | 10 + vapi/Gvn-1.0.metadata | 9 + vapi/Makefile.am | 17 + vapi/Sql-1.0.metadata | 1 + vapi/Vn-1.0.metadata | 8 + vapi/db.deps | 2 + vapi/gvn.deps | 1 + vapi/hedera.deps | 1 + vapi/hedera.vapi | 0 vapi/sql.deps | 2 + vapi/vn.deps | 2 + vn/Makefile.am | 137 + vn/column/Makefile.am | 23 + vn/column/column.h | 27 + vn/column/vn-column-check.c | 91 + vn/column/vn-column-check.h | 46 + vn/column/vn-column-combo.c | 302 ++ vn/column/vn-column-combo.h | 54 + vn/column/vn-column-entry.c | 124 + vn/column/vn-column-entry.h | 47 + vn/column/vn-column-image.c | 509 ++ vn/column/vn-column-image.h | 57 + vn/column/vn-column-spin.c | 39 + vn/column/vn-column-spin.h | 46 + vn/field/Makefile.am | 30 + vn/field/field.h | 31 + vn/field/vn-calendar.c | 156 + vn/field/vn-calendar.h | 49 + vn/field/vn-check.c | 142 + vn/field/vn-check.h | 48 + vn/field/vn-combo.c | 361 ++ vn/field/vn-combo.h | 60 + vn/field/vn-completion.c | 327 ++ vn/field/vn-completion.h | 56 + vn/field/vn-date-chooser.c | 317 ++ vn/field/vn-date-chooser.h | 54 + vn/field/vn-entry.c | 158 + vn/field/vn-entry.h | 50 + vn/field/vn-http-image.c | 231 + vn/field/vn-http-image.h | 50 + vn/field/vn-image.c | 200 + vn/field/vn-image.h | 48 + vn/field/vn-spin.c | 177 + vn/field/vn-spin.h | 48 + vn/gui/actions.glade | 82 + vn/gui/child-window.glade | 47 + vn/gui/login.glade | 403 ++ vn/gui/main.glade | 181 + vn/gui/menubar.ui | 28 + vn/image/hedera16x16.xpm | 48 + vn/image/hedera32x32.xpm | 107 + vn/image/icon.svg | 558 +++ vn/image/load.gif | Bin 0 -> 9427 bytes vn/image/logo.svg | 172 + vn/schema/hedera.gschema.xml | 23 + vn/schema/module.dtd | 22 + vn/vn-batch.c | 112 + vn/vn-batch.h | 53 + vn/vn-builder.c | 157 + vn/vn-builder.h | 54 + vn/vn-column.c | 359 ++ vn/vn-column.h | 72 + vn/vn-field.c | 572 +++ vn/vn-field.h | 73 + vn/vn-form.c | 253 + vn/vn-form.h | 67 + vn/vn-grid.c | 354 ++ vn/vn-grid.h | 60 + vn/vn-gui.c | 1543 ++++++ vn/vn-gui.h | 80 + vn/vn-handler.c | 551 +++ vn/vn-handler.h | 80 + vn/vn-login.c | 467 ++ vn/vn-login.h | 75 + vn/vn-mod.c | 195 + vn/vn-mod.h | 60 + vn/vn-model.c | 433 ++ vn/vn-model.h | 60 + vn/vn.h | 37 + vn/vn.pc.in | 11 + 269 files changed, 46995 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.decl create mode 100644 NEWS create mode 100644 README create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 db/Makefile.am create mode 100644 db/db-calc.c create mode 100644 db/db-calc.h create mode 100644 db/db-conn.c create mode 100644 db/db-conn.h create mode 100644 db/db-file-loader.c create mode 100644 db/db-file-loader.h create mode 100644 db/db-iter.c create mode 100644 db/db-iter.h create mode 100644 db/db-iterator.c create mode 100644 db/db-iterator.h create mode 100644 db/db-model-holder.c create mode 100644 db/db-model-holder.h create mode 100644 db/db-model.c create mode 100644 db/db-model.h create mode 100644 db/db-param.c create mode 100644 db/db-param.h create mode 100644 db/db-plugin.c create mode 100644 db/db-plugin.h create mode 100644 db/db-request.c create mode 100644 db/db-request.h create mode 100644 db/db-result-set.c create mode 100644 db/db-result-set.h create mode 100644 db/db-result.c create mode 100644 db/db-result.h create mode 100644 db/db-row.c create mode 100644 db/db-row.h create mode 100644 db/db.h create mode 100644 db/db.pc.in create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/hedera.dirs create mode 100644 debian/hedera.install create mode 100644 debian/hedera.links create mode 100644 debian/hedera.manpages create mode 100644 debian/libhedera-dev.dirs create mode 100644 debian/libhedera-dev.install create mode 100644 debian/libhedera1.dirs create mode 100644 debian/libhedera1.install create mode 100644 debian/menu create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 docs/Makefile.am create mode 100644 docs/reference/Makefile.am create mode 100644 docs/reference/hedera/Makefile.am create mode 100644 docs/reference/hedera/adding-modules.xml create mode 100644 docs/reference/hedera/configuring.xml create mode 100644 docs/reference/hedera/first-tutorial.xml create mode 100644 docs/reference/hedera/hedera-docs.sgml create mode 100644 docs/reference/hedera/image/vn-calendar.png create mode 100644 docs/reference/hedera/introduction.xml create mode 100644 docs/reference/hedera/module-tutorial.xml create mode 100644 docs/reference/hedera/running.xml create mode 100644 glade/Makefile.am create mode 100644 glade/glade-vn-batch.c create mode 100644 glade/glade-vn.c create mode 100644 glade/glade-vn.h create mode 100755 glade/glade.sh create mode 100644 glade/vn.xml create mode 100644 gvn/Makefile.am create mode 100644 gvn/gvn-misc.c create mode 100644 gvn/gvn-misc.h create mode 100644 gvn/gvn-null.c create mode 100644 gvn/gvn-null.h create mode 100644 gvn/gvn-param-spec.c create mode 100644 gvn/gvn-param-spec.h create mode 100644 gvn/gvn-param.c create mode 100644 gvn/gvn-param.h create mode 100644 gvn/gvn-time.c create mode 100644 gvn/gvn-time.h create mode 100644 gvn/gvn-value.c create mode 100644 gvn/gvn-value.h create mode 100644 gvn/gvn.h create mode 100644 gvn/gvn.pc.in create mode 100644 image/icon.svg create mode 100644 image/logo.svg create mode 100644 main/Makefile.am create mode 100644 main/cacert.pem create mode 100644 main/hedera.1 create mode 100644 main/hedera.pc.in create mode 100644 main/hedera.sh.in create mode 100644 main/main.c create mode 100644 main/vn-hedera.desktop.in create mode 100644 module/Makefile.am create mode 100644 module/data/Makefile.am create mode 100644 module/data/consulter.glade create mode 100644 module/data/consulter.ui create mode 100644 module/data/customer.glade create mode 100644 module/data/example.ui create mode 100644 module/data/example.xml create mode 100644 module/data/signer.glade create mode 100644 module/data/users.glade create mode 100644 module/src/Makefile.am create mode 100644 module/src/vn-consulter.c create mode 100644 module/src/vn-consulter.h create mode 100644 module/src/vn-customer.c create mode 100644 module/src/vn-customer.h create mode 100644 module/src/vn-users.c create mode 100644 module/src/vn-users.h create mode 100644 plugin/Makefile.am create mode 100644 plugin/mysql/Makefile.am create mode 100644 plugin/mysql/db-mysql.c create mode 100644 plugin/mysql/db-mysql.h create mode 100644 plugin/pg/Makefile.am create mode 100644 plugin/pg/db-pg.c create mode 100644 plugin/pg/db-pg.h create mode 100644 po/POTFILES.in create mode 100644 po/POTFILES.skip create mode 100644 po/ca.po create mode 100644 po/es.po create mode 100644 po/nl.po create mode 100644 sql/Makefile.am create mode 100644 sql/parser/Makefile.am create mode 100644 sql/parser/gram.y create mode 100644 sql/parser/lemon.c create mode 100644 sql/parser/lempar-tmpl.c create mode 100644 sql/parser/scan.rl create mode 100644 sql/sql-delete.c create mode 100644 sql/sql-delete.h create mode 100644 sql/sql-dml.c create mode 100644 sql/sql-dml.h create mode 100644 sql/sql-expr.c create mode 100644 sql/sql-expr.h create mode 100644 sql/sql-field.c create mode 100644 sql/sql-field.h create mode 100644 sql/sql-function.c create mode 100644 sql/sql-function.h create mode 100644 sql/sql-holder.c create mode 100644 sql/sql-holder.h create mode 100644 sql/sql-insert.c create mode 100644 sql/sql-insert.h create mode 100644 sql/sql-join.c create mode 100644 sql/sql-join.h create mode 100644 sql/sql-list.c create mode 100644 sql/sql-list.h create mode 100644 sql/sql-multi-stmt.c create mode 100644 sql/sql-multi-stmt.h create mode 100644 sql/sql-object.c create mode 100644 sql/sql-object.h create mode 100644 sql/sql-operation.c create mode 100644 sql/sql-operation.h create mode 100644 sql/sql-param-list.c create mode 100644 sql/sql-param-list.h create mode 100644 sql/sql-param-object.c create mode 100644 sql/sql-param-object.h create mode 100644 sql/sql-parser.h create mode 100644 sql/sql-render.c create mode 100644 sql/sql-render.h create mode 100644 sql/sql-select-field.c create mode 100644 sql/sql-select-field.h create mode 100644 sql/sql-select-order.c create mode 100644 sql/sql-select-order.h create mode 100644 sql/sql-select.c create mode 100644 sql/sql-select.h create mode 100644 sql/sql-set.c create mode 100644 sql/sql-set.h create mode 100644 sql/sql-stmt.c create mode 100644 sql/sql-stmt.h create mode 100644 sql/sql-string.c create mode 100644 sql/sql-string.h create mode 100644 sql/sql-subquery.c create mode 100644 sql/sql-subquery.h create mode 100644 sql/sql-table.c create mode 100644 sql/sql-table.h create mode 100644 sql/sql-target.c create mode 100644 sql/sql-target.h create mode 100644 sql/sql-update-set.c create mode 100644 sql/sql-update-set.h create mode 100644 sql/sql-update.c create mode 100644 sql/sql-update.h create mode 100644 sql/sql-value.c create mode 100644 sql/sql-value.h create mode 100644 sql/sql.h create mode 100644 sql/sql.pc.in create mode 100644 template/lib-object.c create mode 100644 template/lib-object.h create mode 100644 vapi/Db-1.0.metadata create mode 100644 vapi/Gvn-1.0.metadata create mode 100644 vapi/Makefile.am create mode 100644 vapi/Sql-1.0.metadata create mode 100644 vapi/Vn-1.0.metadata create mode 100644 vapi/db.deps create mode 100644 vapi/gvn.deps create mode 100644 vapi/hedera.deps create mode 100644 vapi/hedera.vapi create mode 100644 vapi/sql.deps create mode 100644 vapi/vn.deps create mode 100644 vn/Makefile.am create mode 100644 vn/column/Makefile.am create mode 100644 vn/column/column.h create mode 100644 vn/column/vn-column-check.c create mode 100644 vn/column/vn-column-check.h create mode 100644 vn/column/vn-column-combo.c create mode 100644 vn/column/vn-column-combo.h create mode 100644 vn/column/vn-column-entry.c create mode 100644 vn/column/vn-column-entry.h create mode 100644 vn/column/vn-column-image.c create mode 100644 vn/column/vn-column-image.h create mode 100644 vn/column/vn-column-spin.c create mode 100644 vn/column/vn-column-spin.h create mode 100644 vn/field/Makefile.am create mode 100644 vn/field/field.h create mode 100644 vn/field/vn-calendar.c create mode 100644 vn/field/vn-calendar.h create mode 100644 vn/field/vn-check.c create mode 100644 vn/field/vn-check.h create mode 100644 vn/field/vn-combo.c create mode 100644 vn/field/vn-combo.h create mode 100644 vn/field/vn-completion.c create mode 100644 vn/field/vn-completion.h create mode 100644 vn/field/vn-date-chooser.c create mode 100644 vn/field/vn-date-chooser.h create mode 100644 vn/field/vn-entry.c create mode 100644 vn/field/vn-entry.h create mode 100644 vn/field/vn-http-image.c create mode 100644 vn/field/vn-http-image.h create mode 100644 vn/field/vn-image.c create mode 100644 vn/field/vn-image.h create mode 100644 vn/field/vn-spin.c create mode 100644 vn/field/vn-spin.h create mode 100644 vn/gui/actions.glade create mode 100644 vn/gui/child-window.glade create mode 100644 vn/gui/login.glade create mode 100644 vn/gui/main.glade create mode 100644 vn/gui/menubar.ui create mode 100644 vn/image/hedera16x16.xpm create mode 100644 vn/image/hedera32x32.xpm create mode 100644 vn/image/icon.svg create mode 100644 vn/image/load.gif create mode 100644 vn/image/logo.svg create mode 100644 vn/schema/hedera.gschema.xml create mode 100644 vn/schema/module.dtd create mode 100644 vn/vn-batch.c create mode 100644 vn/vn-batch.h create mode 100644 vn/vn-builder.c create mode 100644 vn/vn-builder.h create mode 100644 vn/vn-column.c create mode 100644 vn/vn-column.h create mode 100644 vn/vn-field.c create mode 100644 vn/vn-field.h create mode 100644 vn/vn-form.c create mode 100644 vn/vn-form.h create mode 100644 vn/vn-grid.c create mode 100644 vn/vn-grid.h create mode 100644 vn/vn-gui.c create mode 100644 vn/vn-gui.h create mode 100644 vn/vn-handler.c create mode 100644 vn/vn-handler.h create mode 100644 vn/vn-login.c create mode 100644 vn/vn-login.h create mode 100644 vn/vn-mod.c create mode 100644 vn/vn-mod.h create mode 100644 vn/vn-model.c create mode 100644 vn/vn-model.h create mode 100644 vn/vn.h create mode 100644 vn/vn.pc.in diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..3374dca --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Alejandro Tomas Colombini Gomez +Juan Ferrer Toribio +Javier Gallego Ferris diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..007e939 --- /dev/null +++ b/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..e0c247c --- /dev/null +++ b/Makefile.am @@ -0,0 +1,21 @@ +include $(top_srcdir)/Makefile.decl + +ACLOCAL_AMFLAGS = -I build/m4 +DISTCHECK_CONFIGURE_FLAGS = --enable-vala --enable-gtk-doc + +SUBDIRS = \ + gvn \ + sql \ + db \ + plugin \ + vn \ + module \ + main \ + po \ + vapi \ + glade \ + docs + +EXTRA_DIST = \ + autogen.sh \ + Makefile.decl diff --git a/Makefile.decl b/Makefile.decl new file mode 100644 index 0000000..850631c --- /dev/null +++ b/Makefile.decl @@ -0,0 +1,74 @@ +# Definition of the install directories +# Libraries +if ENABLE_INSTALL + hedera_libdir = $(libdir)/$(PACKAGE) + hedera_datadir = $(datadir)/$(PACKAGE) +else + hedera_libdir = $(abs_top_builddir) + hedera_datadir = $(abs_top_srcdir) +endif + +gvn_libdir = $(hedera_libdir) +sql_libdir = $(hedera_libdir) +vn_libdir = $(hedera_libdir) +field_libdir = $(vn_libdir) +column_libdir = $(vn_libdir) +plugin_libdir = $(hedera_libdir)/plugin + +if ENABLE_INSTALL + db_libdir = $(hedera_libdir) + module_libdir = $(hedera_libdir)/module +else + db_libdir = $(hedera_libdir)/db + module_libdir = $(hedera_libdir)/module/src/.libs +endif + +mysql_libdir = $(plugin_libdir)/mysql +pg_libdir = $(plugin_libdir)/pg + +# Binaries (programs) +hedera_bindir = $(bindir) + +# Headers and development files +hedera_includedir = $(includedir)/$(PACKAGE) + +gvn_includedir = $(hedera_includedir)/gvn +sql_includedir = $(hedera_includedir)/sql +db_includedir = $(hedera_includedir)/db +vn_includedir = $(hedera_includedir)/vn +field_includedir = $(vn_includedir)/field +column_includedir = $(vn_includedir)/column + +pkgconfigdir = $(datadir)/pkgconfig + +if ENABLE_VALA + girdir = $(datadir)/gir-1.0 + typelibdir = $(libdir)/girepository-1.0 + vapidata = $(top_srcdir)/vapi + vapis = $(top_builddir)/vapi + vapidir = $(datadir)/vala-$(VALA_VERSION)/vapi +endif + +gladevn_libdir = $(libdir)/glade/modules +gladevn_datadir = $(datadir)/glade/catalogs + +# Data +vn_datadir = $(hedera_datadir)/vn +vn_imagedir = $(vn_datadir)/image +vn_guidir = $(vn_datadir)/gui + +if ENABLE_INSTALL + vn_xmldir = $(datadir)/xml/$(PACKAGE) + module_querydir = $(hedera_datadir)/module/sql + module_datadir = $(hedera_datadir)/module +else + vn_xmldir = $(vn_datadir)/schema + module_querydir = $(hedera_datadir)/module/sql + module_datadir = $(hedera_datadir)/module/data +endif + +desktopdir = $(datadir)/applications + +vapigen_v = $(vapigen_v_@AM_V@) +vapigen_v_ = $(vapigen_v_@AM_DEFAULT_V@) +vapigen_v_0 = @echo " VAPIGEN $(subst $(vapis)/,,$@)"; diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..673dc8d --- /dev/null +++ b/README @@ -0,0 +1,110 @@ +En sistemas Debian, para configurar la biblioteca para instalarla en una ruta +estándar y con bindings de Vala, partiendo del paquete sin modificar o la copia +del repositorio descargada por primera vez, se deben ejecutar los siguientes +comandos: + $ ./autogen.sh + $ cd build + $ ./configure --enable-vala --prefix='/usr' + $ make + $ make install + +En otras distribuciones o SOs es posible que el valor de la opcion --prefix +deba ser distinto (por defecto es /usr/local). En cualquier caso, para instalar +en la ruta por defecto del sistema se requieren permisos de administrador. + +A continuación se listan las opciones de configuración específicas del paquete, +su valor por defecto y una breve descripción de su efecto. Estas opciones se +emplearán del mismo modo que se ha utilizado la opcion '--enable-vala' en el +ejemplo de instalación anterior. + + --prefix(=/usr/local) + Configura la ruta de instalación. En Debian es recomendable configurar + la biblioteca usando el prefijo '/usr' para evitar el uso explícito de + variables de entorno. En caso de instalar la biblioteca en una ruta distinta + a '/usr', antes de compilar aplicaciones que usen la biblioteca, se deberá dar + el valor '*prefix*/share/pkgconfig' a la variable PKG_CONFIG_PATH y a la + variable LD_RUN_PATH el valor '*prefix*/lib'. Para más información + diríjase a la documentación de la biblioteca. + + --enable-install(=yes) + Desactivando esta opción se configura el paquete para que se ejecute + sin ser instalado. Si por el contrario se deja su valor por defecto, se + producirá un error al ejecutar las aplicaciones que usen de la biblioteca + sin haberla instalado, pero funcionará correctamente tras instalarla. + + --enable-vala(=no) + Con esta opción activa se generarán todos los archivos necesarios para + compilar código Vala que emplee la biblioteca Hedera, al hacer esto se + generan también datos intermedios de GObject Introspection, útiles para + ser usados por enlaces de la biblioteca a otros lenguajes. + + --enable-gtk-doc(=no) + Generar la documentación de la biblioteca al compilar. + + --enable-debug(=no) + Añadir información de debug a la compilación. + +Para mantenimiento, si se cambia la versión de la biblioteca, el cambio debe +reflejarse en los siguientes lugares: + configure.ac en la macro AC_INIT. + La llamada a la macro AC_SUBS que sigue a AC_INIT, indicando la versión con +un subrayado en lugar del punto. + Todos los vapi/*.metadata y vapi/*.deps (tanto en los nombres de fichero +como en el contenido de los .deps). + Los nombres de los ficheros *.pc.in. + +Si se ha obtenido la copia del proyecto desde el repositorio Subversion, también +se contará con el directorio ./debian, que contiene lo necesario para construir +los paquetes .deb para instalar y desinstalar limpiamente la biblioteca en +sistemas que los soporten. Para construir el paquete sin firmarlo (para pruebas +o instalaciones propias mediante este método), se deberá ejecutar +en el directorio raíz del proyecto: + $ debuild -uc -us + +Se debe tener en cuenta que los paquetes y otros ficheros generados se crean en +el directorio padre del raíz. +________________________________________________________________________________ +________________________________________________________________________________ + +To configure the library for install under a standard path on Debian systems +and generate the Vala bindings to it, from the fresh package or on the first +configuration of the repository copy, the next commands must be used: + $ ./autogen.sh + $ cd build + $ ./configure --enable-vala --prefix='/usr' + $ make + $ make install + +In other distributions or OSs the value for the --prefix option may be different +(it defaults to /usr/local). In any case, to install on this path you must have +root access. + +A list of the configuration options of the package is presented below, with +the default values and a brief description of each one. These options will be +used the same way it was done in the installation example above. + + --prefix(=/usr/local) + Configures the installation path. As said before, on Debian it's + recommended to configure the library using the prefix '/usr' to avoid the + additional setting of environment variables. In case you want to install + the library in another path, before compiling some application against the + library you'll have to set the variables PKG_CONFIG_PATH to + 'your_prefix/share/pkgconfig' and LD_RUN_PATH to 'your_prefix/lib'. For + more information on that look at the library's documentation. + + --enable-install(=yes) + Disabling this option, the package configures to be executed without + being installed. If the it's left to its default value, an error will be + prompted while trying to execute applications using the library without + installing, but it will work after installation. + + --enable-vala(=no) + With this option enabled all needed files to compile Vala code using the + Hedera library will be generated, doing so will also produce intermediate + GObject Introspection files useful to use for bindings to other languages. + + --enable-gtk-doc(=no) + Whether or not to generate the library's documentation on compile time. + + --enable-debug(=no) + Add debug information to the compilation. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..5d770a0 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,5 @@ +gtkdocize --copy +autoreconf -fi +glib-gettextize --force --copy +intltoolize --copy --force --automake +automake --add-missing --copy diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d3454e2 --- /dev/null +++ b/configure.ac @@ -0,0 +1,133 @@ + +AC_INIT([hedera], [1.0]) + +AC_CONFIG_AUX_DIR([build]) +AC_CONFIG_MACRO_DIR([build/m4]) +AC_CONFIG_HEADERS([build/config.h]) +LT_INIT([dlopen]) +AC_PREREQ([2.69]) +AM_SILENT_RULES([yes]) + +# Checks for programs +AC_PROG_CC + +PKG_CHECK_MODULES([glib], [glib-2.0]) +PKG_CHECK_MODULES([gdome], [gdome2]) +PKG_CHECK_MODULES([gtk], [gtk+-3.0]) +PKG_CHECK_MODULES([gladeui], gladeui-2.0) + +GTK_DOC_CHECK([1.14],[--flavour no-tmpl]) +IT_PROG_INTLTOOL([0.40.1], [no-xml]) + +# Checks for Ragel State Machine Compiler +AC_PATH_PROG(RAGEL, [ragel], [no]) +if test x"$RAGEL" = x"no" ; then + AC_MSG_ERROR([Ragel State Machine Compiler (ragel) not found.]) +fi + +# Checks if Vala bindings should be generated +dnl TODO: Use AC_ARG_WITH to pick the Vala version? +AC_MSG_CHECKING([wether to generate Vala bindings...]) +AC_ARG_ENABLE([vala], + [AS_HELP_STRING([--enable-vala], + [Enable vala bindings generation [default = no]])], + [ENABLE_VALA="$enableval"], + [ENABLE_VALA=no]) +AC_MSG_RESULT([$ENABLE_VALA]) + +AM_CONDITIONAL(HAVE_INTROSPECTION, [test FALSE]) + +if test x"$ENABLE_VALA" = x"yes" ; then + GOBJECT_INTROSPECTION_CHECK([1.30.0]) + AC_SUBST([GIR_SCANNER_ARGS], [--warn-all]) + if $HAVE_INTROSPECTION; then + AC_SUBST([VALA_VERSION], [0.20]) + AM_PROG_VALAC([$VALA_VERSION]) + AC_PATH_PROG(VAPIGEN, [vapigen], ["no"]) + fi +fi + +AM_CONDITIONAL(ENABLE_VALA, [test x"$ENABLE_VALA" = x"yes" -a x"$VAPIGEN" != x"no"]) + +# Internationalization +GETTEXT_PACKAGE=hedera +AC_SUBST([GETTEXT_PACKAGE]) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [GETTEXT package name]) +ALL_LINGUAS="es ca nl" +AM_GLIB_GNU_GETTEXT +AM_XGETTEXT_OPTION([-k_ -kQ_:1g -kN_ -kC_:1c,2 -kNC_:1c,2]) + +# GSettings configuration +GLIB_GSETTINGS + +CFLAGS=" -Wall" + +# Check for debug mode +AC_MSG_CHECKING([whether to build with debug information...]) +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], + [Enable debug data generation [default = no]])], + [ENABLE_DEBUG="$enableval"], + [ENABLE_DEBUG=no]) +AC_MSG_RESULT([$ENABLE_DEBUG]) + +if test x"$ENABLE_DEBUG" = x"yes"; then + CFLAGS+=" -ggdb" +fi + +# Check if it will be installed +AC_MSG_CHECKING([whether to configure to install...]) +AC_ARG_ENABLE([install], + [AS_HELP_STRING([--enable-install], + [Enable install configuration [default = yes]])], + [ENABLE_INSTALL="$enableval"], + [ENABLE_INSTALL=yes]) +AC_MSG_RESULT([$installit]) +AM_CONDITIONAL(ENABLE_INSTALL, [test x"$ENABLE_INSTALL" = x"yes"]) + +if test x"$ENABLE_INSTALL" = x"yes"; then + CFLAGS+=" -O3" +else + CFLAGS+=" -O0" +fi + +AC_SUBST([CPPFLAGS]) +AC_SUBST([CFLAGS]) +AC_SUBST([LDFLAGS]) + +# Portability check disabled for GTK-DOC (can be changed if needed) +AM_INIT_AUTOMAKE([-Wno-portability]) +AC_CONFIG_FILES([ + Makefile + gvn/gvn.pc + sql/sql.pc + db/db.pc + vn/vn.pc + main/hedera.pc + gvn/Makefile + sql/Makefile + sql/parser/Makefile + db/Makefile + plugin/Makefile + plugin/pg/Makefile + plugin/mysql/Makefile + vn/Makefile + vn/field/Makefile + vn/column/Makefile + module/Makefile + module/src/Makefile + module/data/Makefile + module/sql/Makefile + main/Makefile + main/vn-hedera.desktop + vapi/Makefile + glade/Makefile + docs/Makefile + docs/reference/Makefile + docs/reference/hedera/Makefile + po/Makefile.in +]) + +AC_SUBST([uVERSION], [${VERSION//./_}]) + +AC_OUTPUT diff --git a/db/Makefile.am b/db/Makefile.am new file mode 100644 index 0000000..480e18a --- /dev/null +++ b/db/Makefile.am @@ -0,0 +1,100 @@ +include $(top_srcdir)/Makefile.decl + +db_lib_LTLIBRARIES = libdb.la +db_include_HEADERS = \ + db.h \ + db-iter.h \ + db-calc.h \ + db-iterator.h \ + db-param.h \ + db-request.h \ + db-conn.h \ + db-row.h \ + db-result.h \ + db-result-set.h \ + db-model.h \ + db-model-holder.h \ + db-file-loader.h \ + db-plugin.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(DEFINES) \ + $(glib_CFLAGS) +libdb_la_LIBADD = \ + $(glib_LIBS) \ + $(top_builddir)/sql/libsql.la +libdb_la_SOURCES = \ + $(db_include_HEADERS) \ + db-iter.c \ + db-calc.c \ + db-iterator.c \ + db-param.c \ + db-request.c \ + db-conn.c \ + db-row.c \ + db-result.c \ + db-result-set.c \ + db-model.c \ + db-model-holder.c \ + db-file-loader.c \ + db-plugin.c + +if ENABLE_INSTALL +DEFINES = \ + -D_PLUGIN_DIR=\"$(plugin_libdir)/%s\" +else +DEFINES = \ + -D_PLUGIN_DIR=\"$(plugin_libdir)/%s/.libs\" +endif + +pkgconfig_DATA = db.pc + +EXTRA_DIST = db.pc.in + +DISTCLEANFILES = db.pc + +if ENABLE_VALA +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_SCANNER_ARGS = $(GIR_SCANNER_ARGS) + +INTROSPECTION_COMPILER_ARGS = \ + --includedir=$(top_builddir)/gvn \ + --includedir=$(top_builddir)/sql + +introspection_sources = $(filter-out db-row.*,$(libdb_la_SOURCES)) + +Db-$(VERSION).gir: $(db_lib_LTLIBRARIES) $(top_builddir)/sql/Sql-$(VERSION).gir + Db_@uVERSION@_gir_SCANNERFLAGS = \ + --include-uninstalled=$(top_builddir)/gvn/Gvn-$(VERSION).gir \ + --include-uninstalled=$(top_builddir)/sql/Sql-$(VERSION).gir + Db_@uVERSION@_gir_CFLAGS = -I$(top_srcdir) + Db_@uVERSION@_gir_LIBS = $(db_lib_LTLIBRARIES) + Db_@uVERSION@_gir_FILES = $(introspection_sources) + Db_@uVERSION@_gir_EXPORT_PACKAGES = db + INTROSPECTION_GIRS = Db-$(VERSION).gir + +gir_DATA = $(INTROSPECTION_GIRS) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) + +$(vapis)/db.vapi: $(INTROSPECTION_GIRS) $(vapidata)/Db-$(VERSION).metadata + $(vapigen_v)$(VAPIGEN) -q \ + --directory $(vapis) \ + --vapidir $(vapis) \ + --girdir $(top_builddir)/gvn \ + --girdir $(top_builddir)/sql \ + --metadatadir $(vapidata) \ + --library db \ + Db-$(VERSION).gir + +vapi_DATA = $(vapis)/db.vapi + +CLEANFILES += $(vapis)/$(vapi_DATA) + +endif +endif diff --git a/db/db-calc.c b/db/db-calc.c new file mode 100644 index 0000000..b402a8f --- /dev/null +++ b/db/db-calc.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-calc.h" +#include "db-row.h" + +/** + * SECTION: db-calc + * @short_description: the result of an operation made over a column or between + * columns + * @title: DbCalc + * + * A #GvnParam that holds the result of the user-given operation applied over + * a given column of a #DbModel. When any of these values is modified, #DbCalc + * updates its value according to the changes. + * + * #DbCalc has as built-in operations the sumatory and average that can be + * selected passing #DB_CALC_SUM OR #DB_CALC_AVG, respectively. + * + * While creating a #DbCalc it's possible to specify either a single column + * number, to calculate using only this column and no additional operations, or + * a #DbCalcFunc function, to use more than one column or do any operation + * needed with the #DbModel fields, in the latter case, custom data can also be + * passed. + **/ + +G_DEFINE_TYPE (DbCalc, db_calc, GVN_TYPE_PARAM); + +/** + * db_calc_new: + * @model: the #DbModel in which the operations will apply + * @type: the #DbCalcOperationType + * @col: the number of a column (starting with 0) + * + * Creates a new #DbCalc using @col to select the only column to be processed + * in each row of @model. + * + * Return value: a new #DbCalc + **/ +DbCalc * db_calc_new (DbModel * model, DbCalcOperationType type, gint col) +{ + g_return_val_if_fail (DB_IS_MODEL (model), NULL); + g_return_val_if_fail (0 <= col || col < db_model_get_ncols (model), NULL); + + return g_object_new + (DB_TYPE_CALC, "model", model, "type", type, "col", col, NULL); +} + +/** + * db_calc_new_with_func: + * @model: the #DbModel in which the operations will apply + * @type: the #DbCalcOperationType + * @func: (scope call): a #DbCalcFunc + * @data: additional data that will be passed to @func + * + * Creates a new #DbCalc using @func as the operation that will apply to each + * row of @model. + * + * Return value: a new #DbCalc + **/ +DbCalc * db_calc_new_with_func (DbModel * model, DbCalcOperationType type, DbCalcFunc func, gpointer data) +{ + g_return_val_if_fail (DB_IS_MODEL (model), NULL); + g_return_val_if_fail (func, NULL); + + return g_object_new + (DB_TYPE_CALC, "model", model, "type", type, "function", func, "data", data, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static gdouble db_calc_value_to_double (const GValue * v) +{ + switch (G_VALUE_TYPE (v)) + { + case G_TYPE_UINT: + return (gdouble) g_value_get_uint (v); + case G_TYPE_UINT64: + return (gdouble) g_value_get_uint64 (v); + case G_TYPE_ULONG: + return (gdouble) g_value_get_ulong (v); + case G_TYPE_INT: + return (gdouble) g_value_get_int (v); + case G_TYPE_INT64: + return (gdouble) g_value_get_int64 (v); + case G_TYPE_LONG: + return (gdouble) g_value_get_long (v); + case G_TYPE_FLOAT: + return (gdouble) g_value_get_float (v); + case G_TYPE_DOUBLE: + return g_value_get_double (v); + case G_TYPE_STRING: + return g_strtod (g_value_get_string (v), NULL); + default: + return 0; + } +} + +static void db_calc_internal_func + (DbCalc * obj, DbIter * iter, GValue * out, gpointer data) +{ + const GValue * value = db_model_get_value (obj->model, iter, obj->col, NULL); + g_value_copy (value, g_value_init (out, G_VALUE_TYPE (value))); +} + +static void db_calc_before_unset (DbModel * model, DbIter * iter, DbCalc * obj) +{ + GValue value = {0}; + + obj->func (obj, iter, &value, obj->data); + + if (!gvn_value_is_null (&value)) + { + gdouble current, old; + + current = db_calc_value_to_double (gvn_param_get_value (GVN_PARAM (obj))); + old = db_calc_value_to_double (&value); + + if (obj->type == DB_CALC_AVG) + { + if (obj->count != 1) + current = (current * (gdouble) obj->count - old) / (gdouble) (obj->count - 1); + else + current = 0; + } + else if (obj->type == DB_CALC_SUM) + current -= old; + + obj->count--; + g_value_unset (&value); + g_value_set_double (g_value_init (&value, G_TYPE_DOUBLE), current); + GVN_PARAM_GET_CLASS (obj)->put_value (GVN_PARAM (obj), &value); + } + + g_value_unset (&value); +} + +static void db_calc_before_delete (DbModel * model, gint path, DbCalc * obj) +{ + DbIter iter; + db_model_get_iter (model, &iter, path); + db_calc_before_unset (model, &iter, obj); +} + +static void db_calc_after_set (DbModel * model, DbIter * iter, DbCalc * obj) +{ + GValue value = {0}; + + obj->func (obj, iter, &value, obj->data); + + if (!gvn_value_is_null (&value)) + { + gdouble current, new; + + current = db_calc_value_to_double (gvn_param_get_value (GVN_PARAM (obj))); + new = db_calc_value_to_double (&value); + + if (obj->type == DB_CALC_AVG) + current = (current * (gdouble) obj->count + new) / (gdouble) (obj->count + 1); + else if (obj->type == DB_CALC_SUM) + current += new; + + obj->count++; + g_value_unset (&value); + g_value_set_double (g_value_init (&value, G_TYPE_DOUBLE), current); + GVN_PARAM_GET_CLASS (obj)->put_value (GVN_PARAM (obj), &value); + } + + g_value_unset (&value); +} + +static void db_calc_refresh (DbModel * model, DbModelStatus status, DbCalc * obj) +{ + DbIter iter; + gdouble current = 0; + GValue val = {0}; + + if (status != DB_MODEL_STATUS_READY) return; + if (!db_model_get_iter_first (model, &iter)) return; + + obj->count = 0; + + do { + obj->func (obj, &iter, &val, obj->data); + + if (!gvn_value_is_null (&val)) + { + current += db_calc_value_to_double (&val); + obj->count++; + } + + g_value_unset (&val); + } + while (db_model_iter_next (model, &iter)); + + if (obj->type == DB_CALC_AVG) + current = (obj->count > 0) ? current / (gdouble) obj->count : 0; + + g_value_set_double (g_value_init (&val, G_TYPE_DOUBLE), current); + GVN_PARAM_GET_CLASS (obj)->put_value (GVN_PARAM (obj), &val); + g_value_unset (&val); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +enum +{ + PROP_MODEL = 1 + ,PROP_TYPE + ,PROP_FUNC + ,PROP_DATA + ,PROP_COL +}; + +static void db_calc_set_property (DbCalc * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_MODEL: + obj->model = g_value_dup_object (value); + g_object_connect (obj->model + ,"signal::line-updated", db_calc_before_unset, obj + ,"signal-after::line-updated", db_calc_after_set, obj + ,"signal::line-deleted", db_calc_before_delete, obj + ,"signal::status-changed", db_calc_refresh, obj + , NULL + ); + break; + case PROP_TYPE: + obj->type = g_value_get_int (value); + break; + case PROP_FUNC: + if (g_value_get_pointer (value)) + obj->func = g_value_get_pointer (value); + break; + case PROP_DATA: + obj->data = g_value_get_pointer (value); + break; + case PROP_COL: + obj->col = g_value_get_int (value); + + if (!obj->func) + obj->func = db_calc_internal_func; + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_calc_get_property (DbCalc * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + case PROP_TYPE: + g_value_set_int (value, obj->type); + break; + case PROP_FUNC: + g_value_set_pointer (value, obj->func); + break; + case PROP_DATA: + g_value_set_pointer (value, obj->data); + break; + case PROP_COL: + g_value_set_int (value, obj->col); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_calc_init (DbCalc * obj) +{ + GvnParamSpec * spec; + + obj->model = NULL; + obj->type = 0; + obj->func = NULL; + obj->data = NULL; + obj->col = 0; + obj->count = 0; + + spec = gvn_param_spec_new_with_attrs (G_TYPE_DOUBLE, FALSE, TRUE, NULL); + GVN_PARAM_GET_CLASS (obj)->set_spec (GVN_PARAM (obj), spec); +} + +static void db_calc_finalize (DbCalc * obj) +{ + if (obj->model) + { + g_object_disconnect (obj->model + ,"any_signal::line-updated", db_calc_before_unset, obj + ,"any_signal::line-updated", db_calc_after_set, obj + ,"any_signal::line-deleted", db_calc_before_delete, obj + ,"any_signal::status-changed", db_calc_refresh, obj + ,NULL + ); + g_clear_object (&obj->model); + } +} + +static void db_calc_class_init (DbCalcClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) db_calc_finalize; + klass->set_property = (GObjectSetPropertyFunc) db_calc_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_calc_get_property; + + g_object_class_install_property (klass, PROP_MODEL + ,g_param_spec_object ("model" + ,_("Model") + ,_("The model where the operations will be applied") + ,DB_TYPE_MODEL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); + + g_object_class_install_property (klass, PROP_TYPE + ,g_param_spec_int ("type" + ,_("Operation type") + ,_("The type of the operation applied over the function") + ,0 + ,1 + ,0 + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); + + g_object_class_install_property (klass, PROP_FUNC + ,g_param_spec_pointer ("function" + ,_("Function") + ,_("The function to execute") + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); + + g_object_class_install_property (klass, PROP_DATA + ,g_param_spec_pointer ("data" + ,_("Data") + ,_("The user provided data for the function") + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); + + g_object_class_install_property (klass, PROP_COL + ,g_param_spec_int ("col" + ,_("Column") + ,_("A column to apply the operations over it") + ,0 + ,G_MAXINT32 + ,0 + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE) + ); +} diff --git a/db/db-calc.h b/db/db-calc.h new file mode 100644 index 0000000..60a503b --- /dev/null +++ b/db/db-calc.h @@ -0,0 +1,93 @@ +/* + Copyright (C) 2012 - Juan Ferrer Toribio + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. +*/ + +#ifndef DB_CALC_H +#define DB_CALC_H + +#include +#include +#include "db-model.h" + +#define DB_CALC_LOG_DOMAIN (g_quark_from_string ("DbCalc")) + +#define DB_TYPE_CALC (db_calc_get_type ()) +#define DB_CALC(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, DB_TYPE_CALC, DbCalc)) +#define DB_IS_CALC(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, DB_TYPE_CALC)) +#define DB_CALC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((obj), DB_TYPE_CALC, DbCalcClass)) +#define DB_IS_CALC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_CLASS)) +#define DB_CALC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_CALC, DbCalcClass)) + +typedef struct _DbCalc DbCalc; +typedef struct _DbCalcClass DbCalcClass; + +/** + * DbCalcFunc: + * @obj: a #DbCalc + * @iter: a #DbIter pointing to a row of the model + * @out: the return position for the resulting value + * @user_data: (closure): the data passed to the function + * + * The prototype of the functions used by the #DbCalc to perform operations + * on more than one column or to perform additional operations to a column, + * previously to add them to the calculations performed by the @DbCalc itself. + **/ +typedef void (*DbCalcFunc) (DbCalc * obj, DbIter * iter, GValue * out, gpointer user_data); + +/** + * DbCalcOperationType: + * @DB_CALC_SUM: sumatory of the values + * @DB_CALC_AVG: average of the values + * + * Defines which type of operation will be performed over the column values. + **/ +typedef enum +{ + DB_CALC_SUM, + DB_CALC_AVG +} +DbCalcOperationType; + +struct _DbCalc +{ + GvnParam parent; + + DbModel * model; + DbCalcOperationType type; + DbCalcFunc func; + gpointer data; + guint col; + guint count; +}; + +struct _DbCalcClass +{ + /* */ + GvnParamClass klass; +}; + +GType db_calc_get_type (); +DbCalc * db_calc_new (DbModel * model + ,DbCalcOperationType type + ,gint col); +DbCalc * db_calc_new_with_func (DbModel * model + ,DbCalcOperationType type + ,DbCalcFunc func + ,gpointer data); + +#endif diff --git a/db/db-conn.c b/db/db-conn.c new file mode 100644 index 0000000..cd9126e --- /dev/null +++ b/db/db-conn.c @@ -0,0 +1,1069 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-conn.h" +#include +#include + +#define IS_OPEN(obj) (obj->status & DB_CONN_OPEN) +#define IS_CLOSING(obj) (obj->status & DB_CONN_CLOSING) +#define IS_OPENING(obj) (obj->status & DB_CONN_OPENING) +#define IS_TRANSACTION(obj) (obj->status & DB_CONN_TRANSACTION) +#define IS_LOST(obj) (obj->status & DB_CONN_LOST) + +typedef struct +{ + DbConn * obj; + gpointer data; +} +IdleData; + +enum { + STATUS_CHANGED + ,ERROR + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +/** + * SECTION: db-conn + * @Short_description: connection managing and wrapping class. + * @Title: DbConn + * @See_also: #DbPg + * + * This class manages a connection to any kind of database (as long as it has + * a plugin to acces to it). It uses the plugin set on the configuration + * parameters (not yet implemented) to connect, query and disconnect. + * + * To do this use the methods db_conn_open(), db_conn_query() and + * db_conn_close() respectively. + * + * It is also possible to parse a SQL query and get an #SqlStmt object using the + * db_conn_parse() method. + * + * This class is thread safe. + **/ +G_DEFINE_TYPE (DbConn, db_conn, G_TYPE_OBJECT); + +/** + * db_conn_new: + * + * Creates a new connection. + * + * Return value: the #DbConn + **/ +DbConn * db_conn_new () +{ + return g_object_new (DB_TYPE_CONN, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +/* + * Frees the #IdleData struct. + */ +static void idle_data_free (IdleData * idle_data) +{ + g_object_unref (idle_data->obj); + g_free (idle_data); +} + +/* + * Notifies that an error happened. + */ +static gboolean db_conn_idle_error (IdleData * idle_data) +{ + GError * error = idle_data->data; + g_signal_emit (idle_data->obj, signals[ERROR], 0, error); + g_error_free (error); + return FALSE; +} + +/* + * Sets and error. + */ +static void db_conn_set_error (DbConn * obj, const GError * error) +{ + if (error) + { + IdleData * idle_data = g_new (IdleData, 1); + idle_data->obj = g_object_ref (obj); + idle_data->data = g_error_copy (error); + + g_idle_add_full (G_PRIORITY_DEFAULT, + (GSourceFunc) db_conn_idle_error, idle_data, (GDestroyNotify) idle_data_free); + } +} + +/* + * Notifies that connection status has changed. + */ +static gboolean db_conn_idle_status (IdleData * idle_data) +{ + DbConnStatus status = GPOINTER_TO_INT (idle_data->data); + g_signal_emit (idle_data->obj, signals[STATUS_CHANGED], 0, status); + return FALSE; +} + +/* + * Sets the status of the connection. + */ +static void db_conn_set_status (DbConn * obj, DbConnStatus status) +{ + IdleData * idle_data; + + if (obj->status != status) + { + idle_data = g_new (IdleData, 1); + idle_data->obj = g_object_ref (obj); + idle_data->data = GINT_TO_POINTER (status); + + g_idle_add_full (G_PRIORITY_DEFAULT, + (GSourceFunc) db_conn_idle_status, idle_data, (GDestroyNotify) idle_data_free); + + obj->status = status; + } +} + +/* + * Cancels all outstanding requests. + */ +static void db_conn_cancel_requests (DbConn * obj) +{ + DbRequest * request; + + while ((request = g_queue_pop_head (obj->requests))) + { + db_request_cancel (request); + db_request_complete (request); + } + + db_plugin_kill_query (obj->plugin); +} + +/* + * Executes asynchronous requests. + */ +static void db_conn_thread (DbConn * obj) +{ + gboolean exit = FALSE; + gboolean exec_success; + DbRequest * request; + + g_mutex_lock (obj->mutex); + + while (!exit) + { + if (IS_OPEN (obj) && !IS_TRANSACTION (obj)) + { + db_conn_set_status (obj, obj->status | DB_CONN_LOADING); + + while (TRUE) + { + if (!(request = g_queue_pop_head (obj->requests))) + break; + + g_mutex_unlock (obj->mutex); + exec_success = db_request_exec (request, NULL); + g_mutex_lock (obj->mutex); + + if (!exec_success && IS_LOST (obj)) + { + g_queue_push_head (obj->requests, request); + } + else + { + db_request_complete (request); + g_object_unref (request); + } + + if (IS_LOST (obj)) + break; + } + + db_conn_set_status (obj, obj->status & ~DB_CONN_LOADING); + } + + if (!IS_CLOSING (obj)) + g_cond_wait (obj->thread_cond, obj->mutex); + else + exit = TRUE; + } + + g_mutex_unlock (obj->mutex); +} + +/* + * Adds a request to the queue. + */ +static void db_conn_add_request (DbConn * obj, DbRequest * request) +{ + g_mutex_lock (obj->mutex); + + if ((IS_OPEN (obj) || IS_LOST (obj)) && !IS_CLOSING (obj)) + { + g_queue_push_tail (obj->requests, g_object_ref (request)); + + if (!IS_TRANSACTION (obj)) + g_cond_signal (obj->thread_cond); + } + else + { + db_request_cancel (request); + db_request_complete (request); + } + + g_mutex_unlock (obj->mutex); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * db_conn_load_plugin: + * @obj: a #DbConn + * @plugin: the plugin name + * @err: return address of a #GError or #NULL + * + * Tryes to load a plugin. + * + * Return vale: %TRUE if plugin was loaded successfully, %FALSE ortherwise + **/ +gboolean db_conn_load_plugin (DbConn * obj, const gchar * plugin, GError ** err) +{ + gchar * aux; + gchar * path; + gchar * plugin_name; + GModule * module; + DbPluginGetTypeFunc symbol; + gboolean ret = FALSE; + + g_return_val_if_fail (DB_IS_CONN (obj), FALSE); + g_return_val_if_fail (plugin, FALSE); + + g_mutex_lock (obj->mutex); + + if (!obj->plugin) + { + aux = g_strdup_printf (_PLUGIN_DIR, plugin); + plugin_name = g_strdup_printf ("db%s", plugin); + path = g_module_build_path (aux, plugin_name); + g_free (aux); + + if ((module = g_module_open (path, 0))) + g_module_make_resident (module); + + aux = g_strdup_printf ("db_%s_get_type", plugin); + + if (module && g_module_symbol (module, aux, (gpointer) &symbol)) + { + obj->plugin_name = g_strdup (plugin); + obj->plugin = g_object_new (symbol (), NULL); + ret = TRUE; + } + else if (err) + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_PLUGIN + ,_("Can't load DbPlugin '%s': %s") + ,plugin + ,g_module_error () + ); + else + g_warning (_("Can't load DbPlugin '%s': %s") + ,plugin + ,g_module_error () + ); + + g_free (plugin_name); + g_free (path); + g_free (aux); + } + else if (err) + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_PLUGIN + ,_("Plugin can't be loaded") + ); + + g_mutex_unlock (obj->mutex); + + return ret; +} + +/** + * db_conn_set_query_path: + * @obj: a #DbConn + * @path: the path + * + * Sets the query search path. + **/ +void db_conn_set_query_path (DbConn * obj, const gchar * path) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->settings_mutex); + + g_free (obj->query_path); + g_strfreev (obj->query_dirs); + obj->query_path = g_strdup (path); + obj->query_dirs = g_strsplit (obj->query_path, G_SEARCHPATH_SEPARATOR_S, 0); + + g_mutex_unlock (obj->settings_mutex); +} + +/** + * db_conn_get_query_path: + * @obj: a #DbConn + * + * Sets the query search path. + * + * Return value: the path, should be freed with g_free() + **/ +gchar * db_conn_get_query_path (DbConn * obj) +{ + gchar * path; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + g_mutex_lock (obj->settings_mutex); + path = g_strdup (obj->query_path); + g_mutex_unlock (obj->settings_mutex); + + return path; +} + +/** + * db_conn_open: + * @obj: a #DbConn + * @host: Host name + * @schema: Database name + * @user: User name + * @pass: Password + * + * Opens a new connection to the database and creates the thread that handles + * all requests made to it asincronously. + * + * Return value: %TRUE if the connection was successfully open, %FALSE otherwise + **/ +gboolean db_conn_open (DbConn * obj, const gchar * host, const gchar * schema, + const gchar * user, const gchar * pass, GError ** err) +{ + gboolean opened = FALSE; + + g_return_val_if_fail (DB_IS_CONN (obj), FALSE); + + g_mutex_lock (obj->mutex); + + if (!IS_OPEN (obj) && !IS_OPENING (obj) && !IS_CLOSING (obj)) + { + DbConnStatus last_status = obj->status; + + db_conn_set_status (obj, obj->status | DB_CONN_OPENING | DB_CONN_LOADING); + + g_mutex_unlock (obj->mutex); + opened = db_plugin_open (obj->plugin + ,host + ,schema + ,user + ,pass + ,err + ); + g_mutex_lock (obj->mutex); + + if (opened) + { + g_mutex_lock (obj->settings_mutex); + g_free (obj->host); + g_free (obj->schema); + g_free (obj->user); + g_free (obj->pass); + obj->host = g_strdup (host); + obj->schema = g_strdup (schema); + obj->user = g_strdup (user); + obj->pass = g_strdup (pass); + g_mutex_unlock (obj->settings_mutex); + + db_conn_set_status (obj, DB_CONN_OPEN); + + if (last_status & DB_CONN_LOST) + g_cond_signal (obj->thread_cond); + else + obj->thread = g_thread_new ("DbConn", + (GThreadFunc) db_conn_thread, obj); + } + else + db_conn_set_status (obj, last_status); + } + else + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_OPENING + ,_("Can't open a new connection, it's already open.") + ); + + g_mutex_unlock (obj->mutex); + return opened; +} + +void db_conn_set_ssl (DbConn * obj, const gchar * ca) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + db_plugin_set_ssl (obj->plugin, ca); +} + +/** + * db_conn_reconnect: + * @obj: a #DbConn + * + * Tries to reconnect to database. + **/ +gboolean db_conn_reconnect (DbConn * obj, GError ** err) +{ + gboolean opened; + gchar * host; + gchar * schema; + gchar * user; + gchar * pass; + + g_return_val_if_fail (DB_IS_CONN (obj), FALSE); + + g_mutex_lock (obj->settings_mutex); + host = g_strdup (obj->host); + schema = g_strdup (obj->schema); + user = g_strdup (obj->user); + pass = g_strdup (obj->pass); + g_mutex_unlock (obj->settings_mutex); + + opened = db_conn_open (obj, host, schema, user, pass, err); + + g_free (host); + g_free (schema); + g_free (user); + g_free (pass); + + return opened; +} + +/** + * db_conn_close: + * @obj: a #DbConn + * @wait: specifies whether to wait for pending requests before close the connection + * + * Closes the current connection to the database and the associated thread. + **/ +void db_conn_close (DbConn * obj, gboolean wait) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->mutex); + + if ((IS_OPEN (obj) || (IS_LOST (obj) && !IS_OPENING (obj))) && !IS_CLOSING (obj)) + { + db_conn_set_status (obj, obj->status | DB_CONN_CLOSING | DB_CONN_LOADING); + + if (!wait) + db_conn_cancel_requests (obj); + + g_cond_signal (obj->thread_cond); + g_mutex_unlock (obj->mutex); + + g_thread_join (obj->thread); + obj->thread = NULL; + + db_plugin_close (obj->plugin); + + g_mutex_lock (obj->mutex); + db_conn_set_status (obj, DB_CONN_CLOSED); + + g_mutex_lock (obj->settings_mutex); + g_free (obj->host); + g_free (obj->schema); + g_free (obj->user); + g_free (obj->pass); + obj->host = NULL; + obj->schema = NULL; + obj->user = NULL; + obj->pass = NULL; + g_mutex_unlock (obj->settings_mutex); + } + + g_mutex_unlock (obj->mutex); +} + +/** + * db_conn_exec: + * @obj: a #DbConn + * @sql: the SQL statement + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Sends a query to the database and waits for the resultset. The user is + * responsible for releasing the result with db_result_set_free(). + * + * Return value: (transfer full) (allow-none): the #DbResultSet on success, %NULL on failure + **/ +DbResultSet * db_conn_exec (DbConn * obj, const gchar * sql, GError ** err) +{ + GError * query_error = NULL; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + if (sql && g_strcmp0 (sql, "")) + { + DbResultSet * set; + + // XXX: Only for debug purposes. + g_message ("DbConn: Query sent: %s", sql); + + set = db_plugin_query (obj->plugin, sql, &query_error); + + if (!set && query_error) + { + if (query_error->code == DB_CONN_ERROR_LOST) + { + g_mutex_lock (obj->mutex); + + if (IS_OPEN (obj) && !IS_CLOSING (obj)) + db_conn_set_status (obj, DB_CONN_CLOSED | DB_CONN_LOST); + + g_mutex_unlock (obj->mutex); + } + + db_conn_set_error (obj, query_error); + g_propagate_error (err, query_error); + } + + return set; + } + else + { + query_error = g_error_new ( + DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_QUERY_EMPTY + ,_("The query is empty.") + ); + + db_conn_set_error (obj, query_error); + g_propagate_error (err, query_error); + } + + return NULL; +} + +/** + * db_conn_query: + * @obj: the #DbConn + * @sql: the SQL statement + * + * Sends a query to the database and waits for the result. + * + * Return value: (transfer full): the new #DbRequest + **/ +DbRequest * db_conn_query (DbConn * obj, const gchar * sql) +{ + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + request = db_request_new (obj, sql); + db_request_exec (request, NULL); + return request; +} + +/** + * db_conn_query_with_stmt: + * @obj: the #DbConn + * @stmt: the #SqlStmt + * + * Sends a stmt to the database and waits for the result. + * + * Return value: (transfer full): the new #DbRequest + **/ +DbRequest * db_conn_query_with_stmt (DbConn * obj, SqlStmt * stmt) +{ + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + request = db_request_new_with_stmt (obj, stmt); + db_request_exec (request, NULL); + return request; +} + +/** + * db_conn_query_async: + * @obj: the #DbConn + * @sql: the SQL statement + * @callback: + * @user_data: + * @notify: + * + * Return value: (transfer full): the new #DbRequest + **/ +DbRequest * db_conn_query_async (DbConn * obj, const gchar * sql, + DbRequestDoneCallback callback, gpointer user_data, GDestroyNotify notify) +{ + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + request = db_request_new (obj, sql); + db_request_set_callback (request, callback, user_data, notify); + db_conn_add_request (obj, request); + return request; +} + +/** + * db_conn_query_with_stmt_async: + * @obj: the #DbConn + * @stmt: the #SqlStmt + * @callback: + * @user_data: + * @notify: + * + * Return value: (transfer full): the new #DbRequest + **/ +DbRequest * db_conn_query_with_stmt_async (DbConn * obj, SqlStmt * stmt, + DbRequestDoneCallback callback, gpointer user_data, GDestroyNotify notify) +{ + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + request = db_request_new_with_stmt (obj, stmt); + db_request_set_callback (request, callback, user_data, notify); + db_conn_add_request (obj, request); + return request; +} + +/** + * db_conn_query_value: + * @obj: a #DbConn + * @sql: a SQL statement + * @value: (out): return location for a #GValue + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Sends a sql to the database and takes the first row value of the result. + * + * Return value: %TRUE on success, %FALSE on failure + **/ +gboolean db_conn_query_value (DbConn * obj, const gchar * sql, GValue * value, GError ** err) +{ + gboolean success; + DbRequest * request; + + g_return_val_if_fail (DB_IS_CONN (obj), FALSE); + + request = db_conn_query (obj, sql); + success = db_request_fetch_value (request, value, err); + g_object_unref (request); + + return success; +} + +/** + * db_conn_kill_query: + * @obj: a #DbConn + * + * Attempts to kill the query that is running, if any. + **/ +void db_conn_kill_query (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + db_plugin_kill_query (obj->plugin); +} + +/** + * db_conn_retry: + * @obj: a #DbConn + * + * Tries rerunning pending requests. + **/ +void db_conn_retry (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_cond_signal (obj->thread_cond); +} + +/** + * db_conn_parse: + * @obj: a #DbConn + * @sql: an SQL string + * + * Return value: (transfer full): a #SqlStmt + **/ +SqlStmt * db_conn_parse (DbConn * obj, gchar * sql) +{ + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + return db_plugin_parse (obj->plugin, sql); +} + +/** + * db_conn_render: + * @obj: a #DbConn. + * @object: the #GObject to render + * @err: (out) (allow-none): the return location for #GError + * + * Renders a #GObject object as a SQL string to send it in a database + * query. It takes the connection to know the codification in wich to escape + * the data. + * + * Return value: (transfer full): the rendered string, or %NULL if error. + **/ +gchar * db_conn_render (DbConn * obj, gpointer object, GError ** err) +{ + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + + return db_plugin_render (obj->plugin, object, err); +} + +/** + * db_conn_start_transaction: + * @obj: a #DbConn + * + * Enter the transaction state, all requests made are retained until you call + * the method db_conn_comit. + **/ +void db_conn_start_transaction (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->mutex); + + if (IS_OPEN (obj) && !IS_CLOSING (obj)) + { + obj->transaction++; + db_conn_set_status (obj, obj->status | DB_CONN_TRANSACTION); + } + + g_mutex_unlock (obj->mutex); +} + +/** + * db_conn_commit: + * @obj: a #DbConn + * + * Commits the current transaction. If transaction arrives to 0, all + * outstanding requests in the queue are sent. + **/ +void db_conn_commit (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->mutex); + + if (IS_TRANSACTION (obj)) + { + obj->transaction--; + + if (!obj->transaction) + { + db_conn_set_status (obj, obj->status & ~DB_CONN_TRANSACTION); + g_mutex_unlock (obj->mutex); + db_conn_retry (obj); + return; + } + } + + g_mutex_unlock (obj->mutex); +} + +/** + * db_conn_rollback: + * @obj: a #DbConn + * + * Deletes all pending requests from the input queue and rollbacks the current + * transactions, if any. + **/ +void db_conn_rollback (DbConn * obj) +{ + g_return_if_fail (DB_IS_CONN (obj)); + + g_mutex_lock (obj->mutex); + + if (IS_TRANSACTION (obj)) + { + db_conn_cancel_requests (obj); + obj->transaction = 0; + db_conn_set_status (obj, obj->status & ~DB_CONN_TRANSACTION); + } + + g_mutex_unlock (obj->mutex); +} + +/** + * db_conn_get_user: + * @obj: a #DbConn + * + * Gets the connection user. + * + * Return value: the user, should be freed using g_free() + **/ +gchar * db_conn_get_user (DbConn * obj) +{ + gchar * user; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + g_mutex_lock (obj->settings_mutex); + user = g_strdup (obj->user); + g_mutex_unlock (obj->settings_mutex); + + return user; +} + +/** + * db_conn_get_host: + * @obj: a #DbConn + * + * Gets the connection host. + * + * Return value: the host, should be freed using g_free() + **/ +gchar * db_conn_get_host (DbConn * obj) +{ + gchar * host; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + g_mutex_lock (obj->settings_mutex); + host = g_strdup (obj->host); + g_mutex_unlock (obj->settings_mutex); + + return host; +} + +/** + * db_conn_get_schema: + * @obj: a #DbConn + * + * Gets the connection schema. + * + * Return value: the schema, should be freed using g_free() + **/ +gchar * db_conn_get_schema (DbConn * obj) +{ + gchar * schema; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + + g_mutex_lock (obj->settings_mutex); + schema = g_strdup (obj->schema); + g_mutex_unlock (obj->settings_mutex); + + return schema; +} + +/** + * db_conn_create_stmt_from_file: + * @obj: a #DbConn + * @query_file: path to a file containing SQL statement + * + * Reads an SQL query from the specified query_file and creates a new + * #SqlString. The file path must be relative to any path in the "query-path" + * property. + * + * Return value: (transfer full): an #SqlString + **/ +SqlString * db_conn_create_stmt_from_file (DbConn * obj, const gchar * query_file) +{ + gint i; + gchar * file; + SqlString * stmt = NULL; + + g_return_val_if_fail (DB_IS_CONN (obj), NULL); + g_return_val_if_fail (query_file, NULL); + + file = g_strconcat (query_file, ".sql", NULL); + + for (i = 0; obj->query_dirs &&obj->query_dirs[i] && !stmt; i++) + { + gchar * buffer; + gchar * path = g_build_filename (obj->query_dirs[i], file, NULL); + + if (g_file_get_contents (path, &buffer, NULL, NULL)) + { + stmt = sql_string_new (buffer); + g_free (buffer); + } + + g_free (path); + } + + if (!stmt) + g_warning ("DbConn: Can't create statement from file: %s", query_file); + + g_free (file); + return stmt; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_PLUGIN = 1 + ,PROP_QUERY_PATH + ,PROP_HOST + ,PROP_USER + ,PROP_DB +}; + +static void db_conn_set_property (DbConn * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PLUGIN: + db_conn_load_plugin (obj, g_value_get_string (value), NULL); + break; + case PROP_QUERY_PATH: + db_conn_set_query_path (obj, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_conn_get_property (DbConn * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PLUGIN: + g_value_set_string (value, obj->plugin_name); + break; + case PROP_QUERY_PATH: + g_value_set_string (value, db_conn_get_query_path (obj)); + break; + case PROP_HOST: + g_value_take_string (value, db_conn_get_host (obj)); + break; + case PROP_USER: + g_value_take_string (value, db_conn_get_user (obj)); + break; + case PROP_DB: + g_value_take_string (value, db_conn_get_schema (obj)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_conn_init (DbConn * obj) +{ + obj->plugin = NULL; + obj->plugin_name = NULL; + obj->query_path = NULL; + obj->query_dirs = NULL; + obj->schema = NULL; + obj->host = NULL; + obj->user = NULL; + obj->pass = NULL; + obj->thread = NULL; + obj->thread_cond = g_new (GCond, 1); + obj->transaction = 0; + obj->status = DB_CONN_CLOSED; + obj->requests = g_queue_new (); + obj->mutex = g_new (GMutex, 1); + obj->settings_mutex = g_new (GMutex, 1); + g_mutex_init (obj->mutex); + g_mutex_init (obj->settings_mutex); + g_cond_init (obj->thread_cond); +} + +static void db_conn_finalize (DbConn * obj) +{ + db_conn_close (obj, FALSE); + + if (obj->plugin) + g_free (obj->plugin_name); + + g_mutex_clear (obj->settings_mutex); + g_mutex_clear (obj->mutex); + g_cond_clear (obj->thread_cond); + g_free (obj->query_path); + g_strfreev (obj->query_dirs); + g_free (obj->settings_mutex); + g_free (obj->mutex); + g_free (obj->thread_cond); + g_free (obj->host); + g_free (obj->user); + g_free (obj->pass); + g_free (obj->schema); + g_queue_free_full (obj->requests, (GDestroyNotify) g_object_unref); + G_OBJECT_CLASS (db_conn_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_conn_class_init (DbConnClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->set_property = (GObjectSetPropertyFunc) db_conn_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_conn_get_property; + klass->finalize = (GObjectFinalizeFunc) db_conn_finalize; + + signals[STATUS_CHANGED] = g_signal_new ("status-changed", + DB_TYPE_CONN, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT + ); + signals[ERROR] = g_signal_new ("error", + DB_TYPE_CONN, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER + ); + + g_object_class_install_property (klass, PROP_PLUGIN, + g_param_spec_string ("plugin" + ,_("Plugin") + ,_("The name of the plugin") + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_QUERY_PATH, + g_param_spec_string ("query-path" + ,_("Query path") + ,_("The path where query files are located") + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_HOST, + g_param_spec_string ("host" + ,_("Host") + ,_("The host name to connect to") + ,NULL + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_USER, + g_param_spec_string ("user" + ,_("User") + ,_("The user name") + ,NULL + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_DB, + g_param_spec_string ("db" + ,_("DB name") + ,_("The default schema") + ,NULL + ,G_PARAM_READABLE + )); +} diff --git a/db/db-conn.h b/db/db-conn.h new file mode 100644 index 0000000..bf34d4d --- /dev/null +++ b/db/db-conn.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_CONN_H +#define DB_CONN_H + +#include +#include "db-plugin.h" + +#define DB_CONN_LOG_DOMAIN (g_quark_from_string ("DbConn")) + +#define DB_TYPE_CONN (db_conn_get_type ()) +#define DB_CONN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_CONN, DbConn)) +#define DB_IS_CONN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_CONN)) +#define DB_CONN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_CONN, DbConnClass)) +#define DB_IS_CONN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_CONN)) +#define DB_CONN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_CONN, DbConnClass)) + +//#define PLUGIN_DIR "./plugin/%s/.libs" + +typedef struct _DbConn DbConn; +typedef struct _DbConnClass DbConnClass; + +#include "db-request.h" + +struct _DbConn +{ + GObject parent; + DbPlugin * plugin; + gchar * plugin_name; + gchar * query_path; + gchar ** query_dirs; + gchar * host; + gchar * user; + gchar * pass; + gchar * schema; + gint status; // DbConnStatus + GMutex * mutex; + GMutex * settings_mutex; + GThread * thread; + GCond * thread_cond; + GQueue * requests; + guint transaction; +}; + +struct _DbConnClass +{ + GObjectClass parent; +}; + +typedef enum +{ + DB_CONN_ERROR_LOST = 1 + ,DB_CONN_ERROR_PLUGIN + ,DB_CONN_ERROR_OPENING + ,DB_CONN_ERROR_BAD_LOGIN + ,DB_CONN_ERROR_BAD_RESPONSE + ,DB_CONN_ERROR_QUERY_EMPTY + ,DB_CONN_ERROR_QUERY_NONFATAL + ,DB_CONN_ERROR_QUERY_FATAL + ,DB_CONN_ERROR_QUERY_FAILED + ,DB_CONN_ERROR_UNKNOW +} +DbConnError; + +/** + * DbConnStatus: + * @DB_CONN_CLOSED: The connection is closed + * @DB_CONN_OPENING: The connection is opening + * @DB_CONN_IDLE: The connection is open but it's idle + * @DB_CONN_TRANSACTION: A transatcion has been started + * @DB_CONN_LOADING: The connection is making a query + * @DB_CONN_CLOSE_REQUESTED: Waiting for a query to close the connection + * @DB_CONN_CLOSING: The connection is closing + * @DB_CONN_LOST: The connection is closed because it has been lost + * + * Identifies the status of the connection. + **/ +typedef enum +{ + DB_CONN_CLOSED = 0 + ,DB_CONN_OPEN = 1 << 0 + ,DB_CONN_LOADING = 1 << 1 + ,DB_CONN_LOST = 1 << 2 + ,DB_CONN_TRANSACTION = 1 << 3 + ,DB_CONN_CLOSING = 1 << 4 + ,DB_CONN_OPENING = 1 << 5 +} +DbConnStatus; + +GType db_conn_get_type (); +DbConn * db_conn_new (); +gboolean db_conn_load_plugin (DbConn * obj, const gchar * plugin, GError ** err); +void db_conn_set_query_path (DbConn * obj, const gchar * path); +gchar * db_conn_get_query_path (DbConn * obj); +gboolean db_conn_open (DbConn * obj + ,const gchar * host + ,const gchar * schema + ,const gchar * user + ,const gchar * pass + ,GError ** err); +void db_conn_close (DbConn * obj, gboolean wait); +void db_conn_set_ssl (DbConn * obj, const gchar * ca); +gboolean db_conn_reconnect (DbConn * obj, GError ** err); + +DbResultSet * db_conn_exec (DbConn * obj, const gchar * sql, GError ** err); + +DbRequest * db_conn_query (DbConn * obj, const gchar * sql); +DbRequest * db_conn_query_async (DbConn * obj + ,const gchar * sql + ,DbRequestDoneCallback callback + ,gpointer user_data + ,GDestroyNotify notify); + +DbRequest * db_conn_query_with_stmt (DbConn * obj, SqlStmt * stmt); +DbRequest * db_conn_query_with_stmt_async (DbConn * obj + ,SqlStmt * stmt + ,DbRequestDoneCallback callback + ,gpointer user_data + ,GDestroyNotify notify); + +gboolean db_conn_query_value (DbConn * obj, const gchar * sql, GValue * value, GError ** err); + +void db_conn_retry (DbConn * obj); +void db_conn_kill_query (DbConn * obj); + +SqlStmt * db_conn_parse (DbConn * obj, gchar * sql); +gchar * db_conn_render (DbConn * obj, gpointer object, GError ** err); +guchar * db_conn_escape_binary (DbConn * obj, const guchar * from, gsize from_size, gsize * to_size); + +void db_conn_start_transaction (DbConn * obj); +void db_conn_commit (DbConn * obj); +void db_conn_rollback (DbConn * obj); + +gchar * db_conn_get_user (DbConn * obj); +gchar * db_conn_get_host (DbConn * obj); +gchar * db_conn_get_schema (DbConn * obj); + +SqlString * db_conn_create_stmt_from_file (DbConn * obj, const gchar * query_file); + +#endif \ No newline at end of file diff --git a/db/db-file-loader.c b/db/db-file-loader.c new file mode 100644 index 0000000..e2f62eb --- /dev/null +++ b/db/db-file-loader.c @@ -0,0 +1,756 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-file-loader.h" +#include "gio/gio.h" +#include +#include +#include + +#define CACHE_CLEAN_PERIOD 600 +#define CACHE_DEFAULT_SIZE 100000000 +#define CACHE_CLEAN_MARGIN (CACHE_DEFAULT_SIZE*20)/100 + +/** + * SECTION: db-file-loader + * @Short_description: utility to download/upload resources from an URL + * @Title: DbFileLoader + * + * #DbFileLoader can download and upload files from a remote or local location by + * its URL. Both operations will allways be made asynchronously, to retrieve the + * results you must pass a #DbFileLoaderCallbackFunc. + * + * By default, #DbFileLoader uses a local cache on disk to store the downloaded + * files. To avoid the use of this feature, create the #DbFileLoader using + * db_file_loader_new_simple(). The default cache directory is 'hedera', under + * g_get_user_cache_dir(). To use another directory as cache or set its size, + * use g_object_set(). + */ + +struct _DbFileLoaderPrivate +{ + gchar * dir; + gchar * cache; + goffset size; + gchar * host; + gchar * path; + GInetSocketAddress * addr; + GThreadPool * pool; + GMutex * mutex; + GMutex * cache_mutex; + GHashTable * downloading; + guint src; +}; + +G_DEFINE_TYPE (DbFileLoader, db_file_loader, G_TYPE_OBJECT) + +typedef struct +{ + DbFileLoader * obj; + gchar * name; + GBytes * data; + GError * error; + DbFileLoaderCallbackFunc func; + gpointer user_data; + GCancellable * cancel; +} +File; + +typedef struct +{ + guint64 atime; + goffset size; + gchar * path; +} +CacheFile; + +static const gchar * WDAY[] = {"Err", + "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + +static const gchar * MONTH[] = {"Err", + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +static const gchar * format_request = { + "GET /%s/%s HTTP/1.1\r\n" // Path to the file + "Host: %s\r\n" // Hostname + "%s" // If-modified-since: date\r\n + "\r\n" +}; + +/** + * db_file_loader_new: + * @host: the host where the files are stored + * @path: the base directory to find the loaded resources + * + * Creates a new #DbFileLoader pointing to @path in @host. The cache is set + * to default. + * + * Return value: a #DbFileLoader + **/ +DbFileLoader * db_file_loader_new (const gchar * host, const gchar * path) +{ + g_return_val_if_fail (host, NULL); + g_return_val_if_fail (path, NULL); + + return g_object_new + (DB_TYPE_FILE_LOADER, "host", host, "path", path, "cache", NULL, NULL); +} + +/** + * db_file_loader_new_simple: + * @host: the host where the files are stored + * @path: the base directory to find the loaded resources + * + * Creates a new #DbFileLoader pointing to @path in @host. To use a cache, + * call db_file_loader_new(). + * + * Return value: a #DbFileLoader + **/ +DbFileLoader * db_file_loader_new_simple (const gchar * host, const gchar * path) +{ + DbFileLoader * fl; + + g_return_val_if_fail (host, NULL); + g_return_val_if_fail (path, NULL); + + fl = g_object_new (DB_TYPE_FILE_LOADER, "host", host, "path", path, NULL); + + if (fl->priv->cache) + { + g_free (fl->priv->cache); + fl->priv->cache = NULL; + } + + return fl; +} + +/* + * db_file_loader_new_full: + * @host: the host where the files are stored + * @path: the base directory to find the loaded resources + * @cache: (allow-none): the base directory for the local cache or %NULL + * @size: maximal size for the cache + * + * Creates a new #DbFileLoader pointing to @path in @host. Note that the user + * needs permission to read and write in @cache. If @cache is %NULL, it will be + * set to default, if @max_cache is 0, the cache has no maximal size. + * + * Return value: a #DbFileLoader or %NULL if @cache is an invalid directory + **/ +DbFileLoader * db_file_loader_new_full + (const gchar * host, const gchar * path, const gchar * cache, goffset size) +{ + g_return_val_if_fail (host, NULL); + g_return_val_if_fail (path, NULL); + +// Comprobar que cache existe y se puede escribir, comprobar tamaño disponible + return g_object_new (DB_TYPE_FILE_LOADER, + "host", host, "path", path, + "cache", cache, "size", size, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +static File * file_new (DbFileLoader * obj, const gchar * path, const GBytes * data, + DbFileLoaderCallbackFunc func, gpointer user_data, gboolean async) +{ + File * file = g_new (File, 1); + + file->obj = g_object_ref (obj); + file->name = g_strdup (path); + file->data = data ? g_boxed_copy (G_TYPE_BYTES, data) : NULL; + file->error = NULL; + file->func = func; + file-> user_data = user_data; + file->cancel = async ? g_cancellable_new () : NULL; + + return file; +} + +static void file_free (File * file) +{ + if (file) + { + g_free (file->name); + + if (file->error) + g_error_free (file->error); + + if (file->data) + g_bytes_unref (file->data); + + g_object_unref (file->obj); + g_free (file); + } +} + +/* + * Connection management and usage + */ +static GFile * db_file_loader_get_cache_file (DbFileLoader * obj, const gchar * subpath) +{ + gchar * path = g_strconcat (obj->priv->cache, "/", subpath, NULL); + GFile * file = g_file_new_for_path (path); + g_free (path); + + return file; +} + +static gboolean db_file_loader_callback (File * file) +{ + if (file->cancel && !g_cancellable_is_cancelled (file->cancel)) + file->func (file->obj, file->data, file->error, file->user_data); + + g_hash_table_remove (file->obj->priv->downloading, file); + + return FALSE; +} + +static gboolean db_file_loader_resolve_host (DbFileLoader * obj, GCancellable * cancel, GError ** error) +{ + gboolean ret = FALSE; + GResolver * r = g_resolver_get_default (); + GList * ads = g_resolver_lookup_by_name (r, obj->priv->host, cancel, error); + + if (ads) + { + GInetAddress * a = g_list_nth_data (ads, 0);// TODO? Use the entire list +// TODO Add the option to set the connection port!! + obj->priv->addr = G_INET_SOCKET_ADDRESS (g_inet_socket_address_new (a, 80)); + g_resolver_free_addresses (ads); + ret = TRUE; + } + + g_object_unref (r); + return ret; +} + +static GIOStream * db_file_loader_connect (DbFileLoader * obj, GCancellable * cancel, GError ** error) +{ + GSocketClient * client = g_socket_client_new (); + GIOStream * connection = G_IO_STREAM (g_socket_client_connect + (client, G_SOCKET_CONNECTABLE (obj->priv->addr), cancel, error)); + + g_object_unref (client); + return connection; +} + +static gchar * db_file_loader_create_request (DbFileLoader * obj, const gchar * subpath) +{ + gchar * ifmod; + gchar * request; + gchar * date = NULL; + + if (obj->priv->cache) + { + GFileInfo * info; + GFile * file = db_file_loader_get_cache_file (obj, subpath); + + info = g_file_query_info + (file + ,G_FILE_ATTRIBUTE_TIME_CHANGED + ,G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS + ,NULL, NULL); + + g_object_unref (file); + + if (info) + { + guint64 date_attr = g_file_info_get_attribute_uint64 + (info, G_FILE_ATTRIBUTE_TIME_CHANGED); + GDateTime * dt = g_date_time_new_from_unix_utc (date_attr); + gchar * f_year_time = g_date_time_format (dt, "%Y %T GMT"); + gchar * f_month_day = g_date_time_format (dt, "%d"); + date = g_strdup_printf ("%s, %s %s %s" + ,WDAY[g_date_time_get_day_of_week (dt)] + ,f_month_day + ,MONTH[g_date_time_get_month (dt)] + ,f_year_time + ); + + g_free (f_month_day); + g_free (f_year_time); + g_date_time_unref (dt); + g_object_unref (info); + } + } + + ifmod = date ? g_strconcat ("If-modified-since: ", date, "\r\n", NULL) : ""; + request = g_strdup_printf (format_request, + obj->priv->path, subpath, obj->priv->host, ifmod); + + if (date) + { + g_free (date); + g_free (ifmod); + } + + return request; +} + +static gboolean db_file_loader_store (DbFileLoader * obj + ,const gchar * subpath + ,const gchar * data + ,gsize len + ,GError ** error) +{ + gsize dir_len; + gboolean ret = FALSE; + gchar * path = g_strconcat (obj->priv->cache, "/",subpath, NULL); + GFile * file = g_file_new_for_path (path); + gchar * name = g_file_get_basename (file); + + dir_len = strlen (path) - strlen (name); + gchar dir[dir_len]; + g_strlcpy (dir, path, dir_len); + g_free (name); + + g_mutex_lock (obj->priv->cache_mutex); + + if (g_mkdir_with_parents (dir, 00700) >= 0) + { + if (!g_file_query_exists (file, NULL) + && g_file_set_contents (path, data, len, error)) + ret = TRUE; + } + else + g_set_error (error, DB_FILE_LOADER_LOG_DOMAIN, + g_file_error_from_errno (errno), _("%s not cached"), subpath); + + g_mutex_unlock (obj->priv->cache_mutex); + + g_free (path); + g_object_unref (file); + + return ret; +} + +static gchar * db_file_loader_load_from_cache (DbFileLoader * obj + ,const gchar * subpath + ,gsize * len + ,GError ** error) +{ + gchar * data; + GFile * file = db_file_loader_get_cache_file (obj, subpath); + + g_file_load_contents (file, NULL, &data, len, NULL, error); + g_object_unref (file); + + return data; +} + +static void db_file_loader_job_download (File * file, DbFileLoader * obj) +{ + gsize len; + gchar ** split; + gchar * status_line = NULL; + gchar * request = NULL; + gchar * data = NULL; + GError * error = NULL; + GCancellable * cancel = file->cancel; + GIOStream * connection = NULL; + GOutputStream * send_stream; + GDataInputStream * receive_stream = NULL; + + g_mutex_lock (obj->priv->mutex); + + if (!obj->priv->addr && !db_file_loader_resolve_host (obj, cancel, &error)) + { + g_mutex_unlock (obj->priv->mutex); + goto final; + } + + g_mutex_unlock (obj->priv->mutex); + + if (!(connection = db_file_loader_connect (obj, cancel, &error))) + goto final; + + request = db_file_loader_create_request (obj, file->name); + send_stream = g_io_stream_get_output_stream (connection); + + if (0 > g_output_stream_write (send_stream, request, strlen (request), cancel, &error) + || !g_output_stream_close (send_stream, cancel, &error)) + goto final; + + receive_stream = g_data_input_stream_new (g_io_stream_get_input_stream (connection)); + g_data_input_stream_set_newline_type (receive_stream, G_DATA_STREAM_NEWLINE_TYPE_CR_LF); + status_line = g_data_input_stream_read_line (receive_stream, &len, cancel, &error); + + if (!status_line) + goto final; + + split = g_strsplit (status_line, " ", -1); + guint status = g_strv_length (split) >= 2 ? atoi (split[1]) : 0; + g_strfreev (split); + + switch (status) + { + gchar * line; + + case 200: + { + gboolean content_known; + guint i, nbytes = 0; + line = NULL; + + do + {// Read header fields + g_free (line); + line = g_data_input_stream_read_line + (receive_stream, &len, cancel, &error); + + if (g_str_has_prefix (line, "Content-Length: ")) + { + nbytes = atoi (line + 16); + content_known = TRUE; + } + } + while (line && len && !error); + + g_free (line); + + if (!error) + { + if (!content_known) + g_set_error (&error, DB_FILE_LOADER_LOG_DOMAIN, status, + _("Unknown content length of file %s"), file->name); + else + { + len = nbytes; + data = g_new (gchar, len); + + for (i = 0; !error && i < len; i++) + data[i] = g_data_input_stream_read_byte + (receive_stream, cancel, &error); + + if (!error && data) + db_file_loader_store (obj, file->name, data, len, &error); + } + } + + break; + } + case 304: + { + data = db_file_loader_load_from_cache (obj, file->name, &len, &error); + break; + } + default: + if (status_line && !error) + g_set_error (&error, DB_FILE_LOADER_LOG_DOMAIN, status, + "%s: '%s'", status_line, file->name); + } + + file->data = g_bytes_new_take (data, len); + +final: + g_free (request); + g_free (status_line); + + if (receive_stream) + { + g_input_stream_close (G_INPUT_STREAM (receive_stream), NULL, + error ? NULL : &error); + g_object_unref (receive_stream); + } + + if (connection) + { + g_io_stream_close (connection, NULL, error ? NULL : &error); + g_object_unref (connection); + } + + file->error = error; + + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + ,(GSourceFunc) db_file_loader_callback, file + ,(GDestroyNotify) file_free + ); +} + +/** + * db_file_loader_download: + * @obj: a #DbFileLoader + * @path: the path to the file from @obj's path + * @func: (scope async): the #DbFileLoaderCallbackFunc to call after downloading + * or in case of error + * @user_data: (closure): data to pass to @func + * + * Downloads a file from @file, which is a relative path to the file from the + * base URL of the #DbFileLoader. The result will be availble in @func. + **/ +void db_file_loader_download (DbFileLoader * obj + ,const gchar * path + ,DbFileLoaderCallbackFunc func + ,gpointer user_data) +{ + File * file; + + g_return_if_fail (DB_IS_FILE_LOADER (obj)); + g_return_if_fail (path); + + if (!obj->priv->pool) + obj->priv->pool = g_thread_pool_new ((GFunc) db_file_loader_job_download, + obj, -1, FALSE, NULL); + + file = file_new (obj, path, NULL, func, user_data, TRUE); + g_hash_table_add (obj->priv->downloading, file); + + g_thread_pool_push (obj->priv->pool, file, NULL); +} + +static void db_file_loader_job_upload (DbFileLoader * obj, File * file) +{ + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) db_file_loader_callback, file, + (GDestroyNotify) file_free); +} + +/** + * db_file_loader_upload: + * @obj: a #DbFileLoader + * @data: a #GBytes with the data to upload + * @path: a string with the path to the file + * @func: (scope async): the #DbFileLoaderCallbackFunc to call after uploading + * or in case of error + * @user_data: (closure): data to pass to @func + * + * Uploads @data to @file, which is a relative path to the file from the base + * URL of the #DbFileLoader. To retrieve the result, connect to the + * "downloaded" signal and to the "error" signal to manage errors. + **/ +void db_file_loader_upload (DbFileLoader * obj + ,GBytes * data + ,const gchar * path + ,DbFileLoaderCallbackFunc func + ,gpointer user_data) +{ + File * file; + + g_return_if_fail (DB_IS_FILE_LOADER (obj)); + g_return_if_fail (data); + g_return_if_fail (path); +//TODO? create a pool only for uploads + if (!obj->priv->pool) + obj->priv->pool = g_thread_pool_new ((GFunc) db_file_loader_job_upload, + obj, -1, TRUE, NULL); + + file = file_new (obj, path, data, func, user_data, TRUE); + + g_thread_pool_push (obj->priv->pool, file, NULL); +} + +static void db_file_loader_cancel (DbFileLoader * obj, const gchar * name) +{ + GHashTableIter iter; + File * file; + + g_return_if_fail (DB_IS_FILE_LOADER (obj)); + if (!obj->priv->downloading) return; + + g_hash_table_iter_init (&iter, obj->priv->downloading); + + while (g_hash_table_iter_next (&iter, (gpointer*) &file, NULL)) + if (!name || !g_strcmp0 (file->name, name)) + { + g_cancellable_cancel (file->cancel); + g_hash_table_iter_steal (&iter); + } +} + +/** + * db_file_loader_cancel_all: + * @obj: a #DbFileLoader + * + * Cancels all downloads pending or in progress. Note that after cancelling a + * task it may delay until it returns to the main loop. For the cause of this, + * see g_cancellable_cancel(). + **/ +void db_file_loader_cancel_all (DbFileLoader * obj) +{ + db_file_loader_cancel (obj, NULL); +} + +/** + * db_file_loader_cancel_by_name: + * @obj: a #DbFileLoader + * @name: an string with a filename + * + * Cancels the download of the file with name @name, that may be pending or in + * progress. See db_file_loader_cancel_all() for more information. + **/ +void db_file_loader_cancel_by_name (DbFileLoader * obj, const gchar * name) +{ + if (name) + db_file_loader_cancel (obj, name); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + HOST_PROP = 1 + ,PATH_PROP + ,CACHE_PROP + ,SIZE_PROP +}; + +static void db_file_loader_set_property (DbFileLoader * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case HOST_PROP: + { + g_free (obj->priv->host); + obj->priv->host = g_value_dup_string (value); + break; + } + case PATH_PROP: + { + g_free (obj->priv->path); + obj->priv->path = g_value_dup_string (value); + break; + } + case CACHE_PROP: + { + const gchar * cache = g_value_get_string (value); + g_free (obj->priv->cache); + obj->priv->cache = g_build_filename ( + (cache ? cache : g_get_user_cache_dir ()) + ,"hedera" + ,obj->priv->host + ,obj->priv->path + ,NULL); + break; + } + case SIZE_PROP: + { + obj->priv->size = g_value_get_int64 (value); + obj->priv->size = obj->priv->size <= 0 ? 0 : obj->priv->size; + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_file_loader_get_property (DbFileLoader * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case HOST_PROP: + g_value_set_string (value, obj->priv->host); + break; + case PATH_PROP: + g_value_set_string (value, obj->priv->path); + break; + case CACHE_PROP: + g_value_set_string (value, obj->priv->cache); + case SIZE_PROP: + g_value_set_int64 (value, obj->priv->size); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_file_loader_init (DbFileLoader * obj) +{ + obj->priv = G_TYPE_INSTANCE_GET_PRIVATE + (obj, DB_TYPE_FILE_LOADER, DbFileLoaderPrivate); + obj->priv->host = NULL; + obj->priv->path = NULL; + obj->priv->cache = NULL; + obj->priv->size = CACHE_DEFAULT_SIZE; + obj->priv->addr = NULL; + obj->priv->pool = NULL; + obj->priv->mutex = g_new (GMutex, 1); + g_mutex_init (obj->priv->mutex); + obj->priv->cache_mutex = g_new (GMutex, 1); + g_mutex_init (obj->priv->cache_mutex); + obj->priv->downloading = g_hash_table_new_full + ((GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal, NULL, NULL); +} + +static void db_file_loader_finalize (DbFileLoader * obj) +{ + G_OBJECT_CLASS (db_file_loader_parent_class)->finalize (G_OBJECT (obj)); + + g_free (obj->priv->host); + g_free (obj->priv->path); + g_free (obj->priv->cache); + + g_clear_object (&obj->priv->addr); + + if (obj->priv->pool) + g_thread_pool_free (obj->priv->pool, TRUE, TRUE); + + g_mutex_clear (obj->priv->cache_mutex); + g_free (obj->priv->cache_mutex); + g_mutex_clear (obj->priv->mutex); + g_free (obj->priv->mutex); + + g_hash_table_destroy (obj->priv->downloading); +} + +static void db_file_loader_class_init (DbFileLoaderClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) db_file_loader_finalize; + k->set_property = (GObjectSetPropertyFunc) db_file_loader_set_property; + k->get_property = (GObjectGetPropertyFunc) db_file_loader_get_property; + g_type_class_add_private (klass, sizeof (DbFileLoaderPrivate)); + + g_object_class_install_property (k, HOST_PROP, + g_param_spec_string ("host" + ,_("Host") + ,_("The host web server name to get the images") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); + + g_object_class_install_property (k, PATH_PROP, + g_param_spec_string ("path" + ,_("Path") + ,_("The path of the directory to interact with") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); + + g_object_class_install_property (k, CACHE_PROP, + g_param_spec_string ("cache" + ,_("Cache directory") + ,_("The local directory where the downloaded files will be stored. " + "The default cache directory is 'hedera', under g_get_user_cache_dir().") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); + + g_object_class_install_property (k, SIZE_PROP, + g_param_spec_int64 ("size" + ,_("Maximal cache size") + ,_("The maximal size for the contents of the cache directory") + ,0 + ,G_MAXOFFSET + ,CACHE_DEFAULT_SIZE + ,G_PARAM_READWRITE + )); +} diff --git a/db/db-file-loader.h b/db/db-file-loader.h new file mode 100644 index 0000000..0d0f70b --- /dev/null +++ b/db/db-file-loader.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_FILE_LOADER_H +#define DB_FILE_LOADER_H + +#include + +#define DB_FILE_LOADER_LOG_DOMAIN g_quark_from_string ("DbFileLoader") + +#define DB_TYPE_FILE_LOADER (db_file_loader_get_type ()) +#define DB_FILE_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, DB_TYPE_FILE_LOADER, DbFileLoader)) +#define DB_IS_FILE_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, DB_TYPE_FILE_LOADER)) +#define DB_FILE_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, DB_TYPE_FILE_LOADER, DbFileLoaderClass)) +#define DB_IS_FILE_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, DB_TYPE_FILE_LOADER)) +#define DB_FILE_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, DB_TYPE_FILE_LOADER, DbFileLoaderClass)) + +typedef struct _DbFileLoader DbFileLoader; +typedef struct _DbFileLoaderClass DbFileLoaderClass; +typedef struct _DbFileLoaderPrivate DbFileLoaderPrivate; + +/** + * DbFileLoaderCallbackFunc: + * @obj: a #DbFileLoader + * @data: the data of a file + * @error: a destination for a #GError + * @user_data: (closure): pointer to user data + * + * Function to call after any operation, successful or not. If the operation was + * cancelled, the error #G_IO_ERROR_CANCELLED will be returned. + **/ +typedef void (* DbFileLoaderCallbackFunc) (DbFileLoader * obj + ,GBytes * data + ,const GError * error + ,gpointer user_data); + +struct _DbFileLoader +{ + GObject parent; + DbFileLoaderPrivate * priv; +}; + +struct _DbFileLoaderClass +{ + /* */ + GObjectClass parent; +}; + +GType db_file_loader_get_type (); +DbFileLoader * db_file_loader_new (const gchar * host + ,const gchar * path); +DbFileLoader * db_file_loader_new_simple (const gchar * host + ,const gchar * path); +void db_file_loader_download (DbFileLoader * obj + ,const gchar * path + ,DbFileLoaderCallbackFunc func + ,gpointer user_data); +void db_file_loader_cancel_all (DbFileLoader * obj); +void db_file_loader_cancel_by_name (DbFileLoader * obj + ,const gchar * name); +void db_file_loader_upload (DbFileLoader * obj + ,GBytes * data + ,const gchar * path + ,DbFileLoaderCallbackFunc func + ,gpointer user_data); + +#endif \ No newline at end of file diff --git a/db/db-iter.c b/db/db-iter.c new file mode 100644 index 0000000..1db1fe8 --- /dev/null +++ b/db/db-iter.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-iter.h" + +/** + * SECTION: db-iter + * @Short_description: an iterator for the #DbModel + * @Title: DbIter + * @See_also: #DbModel + * + * The #DbIter is the structure used to navigate through a #DbModel. See #DbModel + * for the methods required to do it. + **/ +G_DEFINE_BOXED_TYPE (DbIter, db_iter, db_iter_copy, db_iter_free); + +/** + * db_model_iter_compare: + * @a: a #DbIter + * @b: a #DbIter + * + * Compares the two given iters for equality. The equality between two + * #DbIter is determined by its stamp and the row of a #DbModel they are + * pointing to. + * + * Return value: #TRUE if the two iters are the same + **/ +gboolean db_iter_compare (DbIter * a, DbIter * b) +{ + return (a->stamp == b->stamp && a->data == b->data); +} + +/** + * db_iter_copy: + * @obj: the #DbIter to copy + * + * Copies a #DbIter. Note that this function copies only the reference to the + * data in @obj. This is so due to the nature of #DbIter and it's use. + * + * Return value: a new #DbIter + **/ +DbIter * db_iter_copy (DbIter * obj) +{ + g_return_val_if_fail (obj, NULL); + + DbIter * dst = g_new (DbIter, 1); + dst->stamp = obj->stamp; + dst->data = obj->data; + dst->data2 = obj->data2; + dst->data3 = obj->data3; + return dst; +} + +void db_iter_free (DbIter * obj) +{ + g_return_if_fail (obj); + g_free (obj); +} diff --git a/db/db-iter.h b/db/db-iter.h new file mode 100644 index 0000000..f20b5d1 --- /dev/null +++ b/db/db-iter.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_ITER_H +#define DB_ITER_H + +#include + +#define DB_TYPE_ITER (db_iter_get_type()) + +typedef struct _DbIter DbIter; + +/** + * DbIter: + * @stamp: a unique stamp to catch invalid iterators + * @data: model-specific data + * @data2: model-specific data + * @data3: model-specific data + * + * The #DbIter is the primary structure for accessing a #DbModel. The #DbModel + * puts a unique integer in the @stamp member, and model-specific data in + * each of the three @data members. + **/ +struct _DbIter +{ + gint stamp; + gpointer data; + gpointer data2; + gpointer data3; +}; + +GType db_iter_get_type (); + +gboolean db_iter_compare (DbIter * a, DbIter * b); +DbIter * db_iter_copy (DbIter * obj); +void db_iter_free (DbIter * obj); + +#endif diff --git a/db/db-iterator.c b/db/db-iterator.c new file mode 100644 index 0000000..8585dbf --- /dev/null +++ b/db/db-iterator.c @@ -0,0 +1,1159 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-iterator.h" + +#define IS_READY(obj) (obj->model && db_model_get_status (obj->model) == DB_MODEL_STATUS_READY) + +/** + * SECTION:db-iterator + * @short_description: manages a iterator with its events + * @title: DbIterator + * + * The DbIterator manages a connection with a data base. + * There are different ways to create an DbIterator: + * + * + * + * db_iterator_new(): This constructor just needs a #DbModel object + * + * + * + * + * db_iterator_new_with_stmt(): This one needs a #DbConn with the connection and + * a #SqlStmt object + * + * + * + * + * db_iterator_new_with_sql(): This one needs a #DbConn with the connection and + * the sql string + * + * + * + */ +G_DEFINE_TYPE (DbIterator, db_iterator, G_TYPE_OBJECT); + +enum { + ITER_CHANGED + ,DATA_CHANGED + ,ROW_NUM_CHANGED + ,STATUS_CHANGED + ,OPERATIONS_DONE + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +/** + * db_iterator_new: + * @model: the #DbModel used by iterator. + * + * Creates a new #DbIterator. + * + * Return value: a #DbIterator. + **/ +DbIterator * db_iterator_new (DbModel * model) +{ + return g_object_new (DB_TYPE_ITERATOR, "model", model, NULL); +} + +/** + * db_iterator_new_with_stmt: + * @conn: the connection used to create the model. + * @stmt: the #SqlStmt statement used to create the model. + * + * Creates a new #DbIterator. #SqlStmt must be a #SqlSelect or a #SqlString + * object with a valid SELECT statement. + * + * Return value: a #DbIterator. + **/ +DbIterator * db_iterator_new_with_stmt (DbConn * conn, SqlStmt * stmt) +{ + DbIterator * obj; + DbModel * model; + + g_return_val_if_fail (DB_IS_CONN (conn) || !conn, NULL); + g_return_val_if_fail (SQL_IS_STMT (stmt) || !stmt, NULL); + + model = db_model_new (conn, stmt); + obj = g_object_new (DB_TYPE_ITERATOR, "model", model, NULL); + g_object_unref (model); + + return obj; +} + +/** + * db_iterator_new_with_sql: + * @conn: the connection used to create the model. + * @sql: the sql string used to create the model. + * + * Creates a new #DbIterator. sql must be a valid SELECT statement. + * + * Return value: a #DbIterator. + **/ +DbIterator * db_iterator_new_with_sql (DbConn * conn, const gchar * sql) +{ + g_return_val_if_fail (sql, NULL); + g_return_val_if_fail (DB_IS_CONN (conn) || !conn, NULL); + + return g_object_new (DB_TYPE_ITERATOR, "sql", sql, "conn", conn, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void db_iterator_unref_param (DbIterator * obj, DbParam * param) +{ + obj->params = g_list_remove (obj->params, param); +} + +/* + * #DbIterator instances call this function every time line in + * the model is changed. + */ +static void db_iterator_row_num_changed (DbIterator * obj) +{ + if (obj->row_selected) + obj->row = db_model_get_path (obj->model, &obj->iter); + else if (!obj->remember_selection) + obj->row = -1; + + g_signal_emit (obj, signals[ROW_NUM_CHANGED], 0); +} + +/* + * #DbIterator instances call this function every time the data of the params + * mustbe updated. + */ +static void db_iterator_iter_changed (DbIterator * obj) +{ + db_iterator_row_num_changed (obj); + g_signal_emit (obj, signals[ITER_CHANGED], 0); +} + +static void db_iterator_set_iter (DbIterator * obj, DbIter * iter) +{ + if (iter) + { + obj->row_selected = TRUE; + obj->iter = *iter; + db_iterator_iter_changed (obj); + } + else if (obj->row_selected) + { + obj->row_selected = FALSE; + db_iterator_iter_changed (obj); + } +} + +/* + * Function called when row is inserted on the model. + */ +static void db_iterator_on_model_line_inserted (DbModel * model, DbIter * iter, DbIterator * obj) +{ + g_signal_emit (obj, signals[DATA_CHANGED], 0); +} + +/* + * Function called when row is updated on the model. + */ +static void db_iterator_on_model_line_updated_after (DbModel * model, DbIter * iter, DbIterator * obj) +{ + if (obj->row_selected && db_iter_compare (iter, &obj->iter)) + db_iterator_iter_changed (obj); + + g_signal_emit (obj, signals[DATA_CHANGED], 0); +} + +/* + * Function called when row is deleted on the model. + */ +static void db_iterator_on_model_line_deleted (DbModel * model, gint row, DbIterator * obj) +{ + if (obj->row_selected && row == obj->row) + { + DbIter iter; + + if (db_model_get_iter (model, &iter, row + 1) + || db_model_get_iter (model, &iter, row - 1)) + db_iterator_set_iter (obj, &iter); + else + db_iterator_set_iter (obj, NULL); + } +} + +static void db_iterator_on_model_line_deleted_after (DbModel * model, gint row, DbIterator * obj) +{ + if (obj->row_selected) + db_iterator_row_num_changed (obj); + + g_signal_emit (obj, signals[DATA_CHANGED], 0); +} + +/* + * Function called when model rows are reordered. + */ +static void db_iterator_on_model_lines_reordered (DbModel * model, + gint column, gint * new_order, DbIterator * obj) +{ + if (obj->row_selected) + db_iterator_row_num_changed (obj); +} + +/* + * Function called when model status changes. + */ +static void db_iterator_on_model_status_changed (DbModel * model, DbModelStatus status, DbIterator * obj) +{ + if (status == DB_MODEL_STATUS_READY) + { + DbIter iter; + gint nrows = db_model_get_nrows (model); + + g_signal_emit (obj, signals[STATUS_CHANGED], 0, IS_READY(obj)); + + if (obj->row >= 0 && obj->row < nrows + && db_model_get_iter (model, &iter, obj->row)) + db_iterator_set_iter (obj, &iter); + else + db_iterator_set_iter (obj, NULL); + } + else + { + db_iterator_set_iter (obj, NULL); + g_signal_emit (obj, signals[STATUS_CHANGED], 0, IS_READY(obj)); + } +} + +/* + * Function called when model operations are done. + */ +static void db_iterator_on_model_operations_done (DbModel * model, DbIterator * obj) +{ + g_signal_emit (obj, signals[OPERATIONS_DONE], 0); +} + +static void db_iterator_set_model_mode (DbIterator * obj) +{ + if (obj->mode == DB_ITERATOR_MODE_ON_CHANGE) + db_model_set_mode (obj->model, DB_MODEL_MODE_ON_CHANGE); + else + db_model_set_mode (obj->model, DB_MODEL_MODE_ON_DEMAND); +} + +/* + * Sets the model used as a data source by iterator. + */ +static void db_iterator_set_model (DbIterator * obj, DbModel * model) +{ + if (!model) + return; + + if (!obj->model) + { + obj->model = g_object_ref (model); + g_object_connect (obj->model + ,"signal::lines-reordered", db_iterator_on_model_lines_reordered, obj + ,"signal::line-inserted", db_iterator_on_model_line_inserted, obj + ,"signal-after::line-updated", db_iterator_on_model_line_updated_after, obj + ,"signal::line-deleted", db_iterator_on_model_line_deleted, obj + ,"signal-after::line-deleted", db_iterator_on_model_line_deleted_after, obj + ,"signal::status-changed", db_iterator_on_model_status_changed, obj + ,"signal::operations-done", db_iterator_on_model_operations_done, obj + ,NULL + ); + db_iterator_set_model_mode (obj); + db_iterator_on_model_status_changed (model, + db_model_get_status (model), obj); + } + else + g_warning ("DbIterator: Can't reassign the 'model' property"); +} + +/* + * Check if the iterator has any selected row, otherwise issues a message. + */ +static gboolean db_iterator_check_row_selected (DbIterator * obj) +{ + if (obj->row_selected) + return TRUE; + + g_warning ("DbIterator: Row not selected"); + return FALSE; +} + +/* + * Attempts to create a model, if have enough information, and assigns it to + * the iterator. + */ +static void db_iterator_try_create_model (DbIterator * obj) +{ + DbModel * new_model = NULL; + + if (obj->conn) + { + if (obj->sql) + { + if (obj->use_file) + new_model = db_model_new_with_file (obj->conn, obj->sql); + else + new_model = db_model_new_with_sql (obj->conn, obj->sql); + } + if (obj->stmt) + new_model = db_model_new (obj->conn, obj->stmt); + } + + if (new_model) + db_iterator_set_model (obj, new_model); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * db_iterator_get_row: + * @obj: a #DbIterator + * + * Gets the selected row number. + * + * Return value: the row number + **/ +gint db_iterator_get_row (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), -1); + + if (obj->row_selected) + return obj->row; + else + return -1; +} + +/** + * db_iterator_get_model: + * @obj: a #DbIterator + * + * Gets the model used as a data source by iterator. + * + * Return value: (transfer none): the #DbModel + **/ +DbModel * db_iterator_get_model (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + + return obj->model; +} + +/** + * db_iterator_get_conn: + * @obj: a #DbIterator + * + * Gets connection used by the @obj model. + * + * Return value: (transfer none): the #DbConn + **/ +DbConn * db_iterator_get_conn (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + + if (obj->model) + db_model_get_conn (obj->model); + + return NULL; +} + +/** + * db_iterator_set_conn: + * @obj: a #DbIterator + * @conn: the #DbConn + * + * Sets the connection used by the @obj model. + **/ +void db_iterator_set_conn (DbIterator * obj, DbConn * conn) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (DB_IS_CONN (conn)); + g_return_if_fail (obj->model); + + db_model_set_conn (obj->model, conn); +} + +/** + * db_iterator_is_ready: + * @obj: a #DbIterator + * + * Gets if the iterator is ready. + * + * Return value: %TRUE if the iterator and its model are ready, %FALSE otherwise. + **/ +gboolean db_iterator_is_ready (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), FALSE); + + return IS_READY (obj); +} + +/** + * db_iterator_get_mode: + * @obj: a #DbIterator + * + * Gets the mode in which the iterator is working. + * + * Return value: the #DbIteratorMode + **/ +DbIteratorMode db_iterator_get_mode (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), 0); + + return obj->mode; +} + +/** + * db_iterator_set_mode: + * @obj: a #DbIterator + * @mode: the #DbIteratorMode mode + * + * Sets the mode in which the iterator should work. + **/ +void db_iterator_set_mode (DbIterator * obj, DbIteratorMode mode) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + obj->mode = mode; + + if (obj->model) + db_iterator_set_model_mode (obj); +} + +/** + * db_iterator_get_iter: + * @obj: a #DbIterator + * @iter: (allow-none): return location for selected row iter, or %NULL. + * + * Gets the currently selected iter. + * + * Return value: %TRUE if any row is selected, %FALSE otherwise. + **/ +gboolean db_iterator_get_iter (DbIterator * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), FALSE); + + if (!obj->row_selected) + return FALSE; + + *iter = obj->iter; + return TRUE; +} + +/** + * db_iterator_move_iter: + * @obj: a #DbIterator + * @iter: a #DbIter + * + * Moves the iterator cursor to the specified iter, if model is ready. + **/ +void db_iterator_move_iter (DbIterator * obj, DbIter * iter) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (IS_READY (obj)); + g_return_if_fail (obj->model); + + if (obj->row_selected && db_iter_compare (&obj->iter, iter)) + return; + + if (obj->mode != DB_ITERATOR_MODE_ON_DEMAND) + db_model_perform_operations (obj->model, FALSE); + + db_iterator_set_iter (obj, iter); +} + +/** + * db_iterator_move_first: + * @obj: a #DbIterator + * + * Moves the iterator cursor to the first row. + **/ +void db_iterator_move_first (DbIterator * obj) +{ + DbIter iter; + + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (obj->model); + + if (db_model_get_iter_first (obj->model, &iter)) + db_iterator_move_iter (obj, &iter); +} + +/** + * db_iterator_move_last: + * @obj: a #DbIterator + * + * Moves the iterator cursor to the last row. + **/ +void db_iterator_move_last (DbIterator * obj) +{ + DbIter iter; + + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (obj->model); + + if (db_model_get_last (obj->model, &iter)) + db_iterator_move_iter (obj, &iter); +} + +/** + * db_iterator_move_previous: + * @obj: a #DbIterator + * + * Moves the iterator cursor to the previous row, or displays a message if no + * exists. If no selected row, attempts to move the cursor to the first row. + **/ +void db_iterator_move_previous (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->row_selected) + { + DbIter iter = obj->iter; + + if (db_model_iter_prev (obj->model, &iter)) + db_iterator_move_iter (obj, &iter); + else + g_warning ( + "Can't move the cursor to the previous " + "row, because there are no more rows." + ); + } + else + db_iterator_move_first (obj); +} + +/** + * db_iterator_move_next: + * @obj: a #DbIterator + * + * Moves the iterator cursor to the next row, or displays a message if no exists. + * If no selected row, attempts to move the cursor to the first row. + **/ +void db_iterator_move_next (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->row_selected) + { + DbIter iter = obj->iter; + + if (db_model_iter_next (obj->model, &iter)) + db_iterator_move_iter (obj, &iter); + else + g_warning ( + "Can't move the cursor to the next " + "row, because there are no more rows." + ); + } + else + db_iterator_move_first (obj); +} + +/** + * db_iterator_move_to: + * @obj: a #DbIterator + * @move: a #DbIteratorMove + * + * Moves the iterator cursor to the predefined position. + **/ +void db_iterator_move_to (DbIterator * obj, DbIteratorMove move) +{ + switch (move) + { + case DB_ITERATOR_MOVE_FIRST: + db_iterator_move_first (obj); + break; + case DB_ITERATOR_MOVE_PREVIOUS: + db_iterator_move_previous (obj); + break; + case DB_ITERATOR_MOVE_NEXT: + db_iterator_move_next (obj); + break; + case DB_ITERATOR_MOVE_LAST: + db_iterator_move_last (obj); + break; + } +} + +/** + * db_iterator_insert: + * @obj: a #DbIterator + * + * Inserts a new row in the model and moves the iter to it, if inserted. + * successfully. + **/ +void db_iterator_insert (DbIterator * obj) +{ + DbIter iter; + + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (obj->model); + + if (obj->mode != DB_ITERATOR_MODE_ON_DEMAND) + db_model_perform_operations (obj->model, FALSE); + + if (db_model_insert (obj->model, &iter)) + db_iterator_set_iter (obj, &iter); +} + +/** + * db_iterator_delete: + * @obj: a #DbIterator + * + * Deletes the currently selected row from the model, if any. + **/ +void db_iterator_delete (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (db_iterator_check_row_selected (obj)) + { + db_model_delete (obj->model, &obj->iter); + + if (obj->mode != DB_ITERATOR_MODE_ON_DEMAND) + db_model_perform_operations (obj->model, FALSE); + } +} + +/** + * db_iterator_refresh: + * @obj: a #DbIterator + * + * Refresh the data of the model handled by iterator. + **/ +void db_iterator_refresh (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->model) + db_model_refresh (obj->model); +}; + +/** + * db_iterator_get_spec: + * @obj: a #DbIterator + * @column: the column index. + * + * Gets the spec from the model of the specified column index. + * + * Return value: (transfer none) (allow-none): a #GvnParamSpec or %NULL if + * can't get it because the model isn't ready. + **/ +const GvnParamSpec * db_iterator_get_spec (DbIterator * obj, gint column) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + + if (IS_READY (obj)) + return db_model_get_spec (obj->model, column); + else + return NULL; +} + +/** + * db_iterator_get_value: + * @obj: a #DbIterator + * @column: the column index. + * + * Gets the value of the specified column index. + * + * Return value: (transfer none) (allow-none): the value or %NULL if + * can't get it because the model isn't ready. + **/ +const GValue * db_iterator_get_value (DbIterator * obj, gint column) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + + if (obj->row_selected) + return db_model_get_value (obj->model, &obj->iter, column, NULL); + + return NULL; +} + +/** + * db_iterator_get_column_index: + * @obj: a #DbIterator + * @name: the name of a column of iterator. + * + * Retrieves the position in the query of the column called @name. + * + * Return value: the position of the column with name @name or -1 if there isn't + * a column called name or if name is %NULL. + **/ +gint db_iterator_get_column_index (DbIterator * obj, const gchar * name) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), -1); + g_return_val_if_fail (IS_READY (obj), -1); + + return db_model_get_column_index (obj->model, name); +} + +/** + * db_iterator_set_value: + * @obj: a #DbIterator + * @column: the column index. + * @value: a #GValue with the new value. + * @err: (out) (allow-none): the return location for a #GError or %NULL. + * + * Sets the value of the specified column index. + **/ +void db_iterator_set_value (DbIterator * obj, gint column, const GValue * value, GError ** err) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (db_iterator_check_row_selected (obj)) + db_model_set_value (obj->model, &obj->iter, column, value, err); +} + +/** + * db_iterator_add_param: + * @obj: a #DbIterator + * @param: the param to add. + * + * Adds a #DbParam to the list of iterator params. + **/ +void db_iterator_add_param (DbIterator * obj, DbParam * param) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (DB_IS_PARAM (param)); + + db_param_set_iterator (param, obj); + g_object_weak_ref (G_OBJECT (param), + (GWeakNotify) db_iterator_unref_param, obj); + obj->params = g_list_prepend (obj->params, param); +} + +/** + * db_iterator_get_param: + * @obj: a #DbIterator + * @column: the column name + * + * Creates a parameter for the specified column index and returns it. + * + * Return value: (transfer none): a #GvnParam + **/ +GvnParam * db_iterator_get_param (DbIterator * obj, const gchar * column) +{ + GList * n; + GvnParam * param; + + g_return_val_if_fail (DB_IS_ITERATOR (obj), NULL); + g_return_val_if_fail (column, NULL); + + for (n = obj->params; n; n = n->next) + if (!g_strcmp0 (db_param_get_column_name (n->data), column)) + break; + + if (!n) + { + param = db_param_new (column); + db_iterator_add_param (obj, DB_PARAM (param)); + } + else + param = n->data; + + return param; +} + +/** + * db_iterator_get_params: + * @obj: a #DbIterator + * + * Gets a list of params linked with the iterator. The returned list shoud be freed. + * + * Return value: (element-type Db.Param) (transfer container): the #GList + **/ +GList * db_iterator_get_params (DbIterator * obj) +{ + return g_list_copy (obj->params); +} + +/** + * db_iterator_bind_param: + * @obj: a #DbIterator + * @column: the column index. + * @param: the column index. + * + * Binds the param to the specified column index. If you want to link the same + * param several times you should use the db_iterator_get_param function. + **/ +void db_iterator_bind_param (DbIterator * obj, const gchar * column, GvnParam * param) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (GVN_IS_PARAM (obj)); + + gvn_param_set_master (param, + db_iterator_get_param (obj, column) + ); +} + +/** + * db_iterator_link: + * @obj: a #DbIterator + * @field: the field name in the iterator statement + * @src: the source #DbIterator + * @column: the column number of @src + * + * Links the iterator with another iterator parameter. + **/ +void db_iterator_link (DbIterator * obj, const gchar * field, DbIterator * src, const gchar * column) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (DB_IS_ITERATOR (src)); + g_return_if_fail (field); + + db_iterator_link_with_param (obj, field, + db_iterator_get_param (src, column)); +} + +/** + * db_iterator_link_with_param: + * @obj: a #DbIterator + * @field: the field name in the iterator statement + * @param: the #GvnParam + * + * Links the iterator with a parameter. + **/ +void db_iterator_link_with_param (DbIterator * obj, const gchar * field, GvnParam * param) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + g_return_if_fail (GVN_IS_PARAM (param)); + g_return_if_fail (obj->model); + g_return_if_fail (field); + + db_model_set_default_value_from_param (obj->model, field, param); +} + +/** + * db_iterator_get_nrows: + * @obj: a #DbIterator + * + * Gets the number of rows in the model pointed by the iterator. + * + * Return value: the number of rows + **/ +gint db_iterator_get_nrows (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), 0); + + if (obj->model) + return db_model_get_nrows (obj->model); + + return 0; +} + +/** + * db_iterator_get_update_flags: + * @obj: a #DbIterator + * + * Gets the flags that indicate how a model can be modified. + * + * Return value: the flags + **/ +DbModelUpdateFlags db_iterator_get_update_flags (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), 0); + + if (obj->model) + return db_model_get_update_flags (obj->model); + + return 0; +} + +/** + * db_iterator_perform_operations: + * @obj: a #DbIterator + * + * Performs all pending operations on the model. + **/ +void db_iterator_perform_operations (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->model) + db_model_perform_operations (obj->model, FALSE); +} + +/** + * db_iterator_reverse_operations: + * @obj: a #DbIterator + * + * Reverses all pending operations on the model. + **/ +void db_iterator_reverse_operations (DbIterator * obj) +{ + g_return_if_fail (DB_IS_ITERATOR (obj)); + + if (obj->model) + db_model_reverse_operations (obj->model); +} + +/** + * db_iterator_get_pending_operations: + * @obj: a #DbIterator + * + * Gets pending operations to perform in the current row. + * + * Return value: the pending operations + **/ +DbModelRowOp db_iterator_get_pending_operations (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), 0); + + if (obj->model && obj->row_selected) + return db_model_get_row_operations (obj->model, &obj->iter); + + return 0; +} + +/** + * db_iterator_has_pending_operations: + * @obj: a #DbIterator + * + * Checks for pending operations to perform. + * + * Return value: %TRUE if there are pending operations, %FALSE otherwise + **/ +gboolean db_iterator_has_pending_operations (DbIterator * obj) +{ + g_return_val_if_fail (DB_IS_ITERATOR (obj), FALSE); + + if (obj->model) + return db_model_has_pending_operations (obj->model); + + return FALSE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_MODEL = 1 + ,PROP_MODE + ,PROP_REMEMBER_SELECTION + ,PROP_CONN + ,PROP_STMT + ,PROP_SQL + ,PROP_USE_FILE +}; + +static void db_iterator_set_property (DbIterator * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_MODEL: + db_iterator_set_model (obj, g_value_get_object (value)); + break; + case PROP_MODE: + db_iterator_set_mode (obj, g_value_get_enum (value)); + break; + case PROP_REMEMBER_SELECTION: + obj->remember_selection = g_value_get_boolean (value); + break; + case PROP_CONN: + g_clear_object (&obj->conn); + obj->conn = g_value_dup_object (value); + db_iterator_try_create_model (obj); + break; + case PROP_STMT: + g_clear_object (&obj->stmt); + obj->stmt = g_value_dup_object (value); + db_iterator_try_create_model (obj); + break; + case PROP_SQL: + g_free (obj->sql); + obj->sql = g_value_dup_string (value); + db_iterator_try_create_model (obj); + break; + case PROP_USE_FILE: + obj->use_file = g_value_get_boolean (value); + db_iterator_try_create_model (obj); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void db_iterator_get_property (DbIterator * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + case PROP_MODE: + g_value_set_enum (value, obj->mode); + break; + case PROP_REMEMBER_SELECTION: + g_value_set_boolean (value, obj->remember_selection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_iterator_init (DbIterator * obj) +{ + obj->model = NULL; + obj->params = NULL; + obj->row = 0; + obj->row_selected = FALSE; + obj->conn = NULL; + obj->stmt = NULL; + obj->sql = NULL; + obj->use_file = FALSE; +} + +static void db_iterator_finalize (DbIterator * obj) +{ + GList * n; + + for (n = obj->params; n; n = n->next) + g_object_weak_unref (n->data, + (GWeakNotify) db_iterator_unref_param, obj); + + g_list_free (obj->params); + + if (obj->model) + { + g_object_disconnect (obj->model + ,"any_signal", db_iterator_on_model_line_inserted, obj + ,"any_signal", db_iterator_on_model_line_updated_after, obj + ,"any_signal", db_iterator_on_model_line_deleted, obj + ,"any_signal", db_iterator_on_model_line_deleted_after, obj + ,"any_signal", db_iterator_on_model_lines_reordered, obj + ,"any_signal", db_iterator_on_model_status_changed, obj + ,"any_signal", db_iterator_on_model_operations_done, obj + ,NULL + ); + g_object_unref (obj->model); + } + + g_clear_object (&obj->conn); + g_clear_object (&obj->stmt); + g_free (obj->sql); + + G_OBJECT_CLASS (db_iterator_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_iterator_class_init (DbIteratorClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->set_property = (GObjectSetPropertyFunc) db_iterator_set_property; + k->get_property = (GObjectGetPropertyFunc) db_iterator_get_property; + k->finalize = (GObjectFinalizeFunc) db_iterator_finalize; + + signals[ITER_CHANGED] = g_signal_new ("iter-changed", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[DATA_CHANGED] = g_signal_new ("data-changed", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[ROW_NUM_CHANGED] = g_signal_new ("row-num-changed", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[STATUS_CHANGED] = g_signal_new ("status-changed", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN + ); + signals[OPERATIONS_DONE] = g_signal_new ("operations-done", + DB_TYPE_ITERATOR, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + + g_object_class_install_property (k, PROP_MODEL, + g_param_spec_object ("model" + ,_("Model") + ,_("The DbModel handled by the iterator") + ,DB_TYPE_MODEL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MODE, + g_param_spec_enum ("mode" + ,_("Mode") + ,_("The mode in which the iterator is working") + ,DB_TYPE_ITERATOR_MODE + ,DB_ITERATOR_MODE_ON_CHANGE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_REMEMBER_SELECTION, + g_param_spec_boolean ("remember-selection" + ,_("Remember selection") + ,_("Wether to rememeber the selection when model is refreshed") + ,TRUE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the model") + ,DB_TYPE_CONN + ,G_PARAM_WRITABLE + )); + g_object_class_install_property (k, PROP_STMT, + g_param_spec_object ("stmt" + ,_("Statement") + ,_("The statement used to create the model") + ,SQL_TYPE_STMT + ,G_PARAM_WRITABLE + )); + g_object_class_install_property (k, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query used to create the model") + , NULL + ,G_PARAM_WRITABLE + )); + g_object_class_install_property (k, PROP_USE_FILE, + g_param_spec_boolean ("use-file" + ,_("Use file") + ,_("Wether to interpret the sql property as query file") + ,FALSE + ,G_PARAM_CONSTRUCT | G_PARAM_WRITABLE + )); +} + +GType db_iterator_mode_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GEnumValue values[] = + { + {DB_ITERATOR_MODE_ON_CHANGE, "DB_ITERATOR_MODE_ON_CHANGE", "on-change"} + ,{DB_ITERATOR_MODE_ON_ITER, "DB_ITERATOR_MODE_ON_ITER", "on-iter"} + ,{DB_ITERATOR_MODE_ON_DEMAND, "DB_ITERATOR_MODE_ON_DEMAND", "on-demand"} + ,{0, NULL, NULL} + }; + + type = g_enum_register_static + (g_intern_static_string ("DbIteratorMode"), values); + } + + return type; +} diff --git a/db/db-iterator.h b/db/db-iterator.h new file mode 100644 index 0000000..d17a0ce --- /dev/null +++ b/db/db-iterator.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_ITERATOR_H +#define DB_ITERATOR_H + +#include "db-model.h" + +#define DB_TYPE_ITERATOR (db_iterator_get_type ()) +#define DB_ITERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, DB_TYPE_ITERATOR, DbIterator)) +#define DB_IS_ITERATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, DB_TYPE_ITERATOR)) +#define DB_ITERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, DB_TYPE_ITERATOR, DbIteratorClass)) +#define DB_IS_ITERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, DB_TYPE_ITERATOR)) +#define DB_ITERATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, DB_TYPE_ITERATOR, DbIteratorClass)) + +#define DB_TYPE_ITERATOR_MODE (db_iterator_mode_get_type ()) + +typedef struct _DbIterator DbIterator; +typedef struct _DbIteratorClass DbIteratorClass; + +#include "db-param.h" + +/** + * DbIteratorMode: + * @DB_ITERATOR_MODE_ON_CHANGE: every change made in a cell will be sent to the + * database instantly. + * @DB_ITERATOR_MODE_ON_ITER: every change made in a row will be sent to the + * database when iter changes. + * @DB_ITERATOR_MODE_ON_DEMAND: nothing will be sent to the database since it's + * demanded. + * + * The working mode of a Iterator. + **/ +typedef enum +{ + DB_ITERATOR_MODE_ON_CHANGE + ,DB_ITERATOR_MODE_ON_ITER + ,DB_ITERATOR_MODE_ON_DEMAND +} +DbIteratorMode; + +/** + * DbIteratorMove: + * @DB_ITERATOR_MOVE_FIRST: Moves to the first position. + * @DB_ITERATOR_MOVE_PREVIOUS: Moves previous. + * @DB_ITERATOR_MOVE_NEXT: Moves next. + * @DB_ITERATOR_MOVE_LAST: Moves to the last position. + * + * Indicates the movement. + **/ +typedef enum +{ + DB_ITERATOR_MOVE_FIRST + ,DB_ITERATOR_MOVE_PREVIOUS + ,DB_ITERATOR_MOVE_NEXT + ,DB_ITERATOR_MOVE_LAST +} +DbIteratorMove; + +struct _DbIterator +{ + GObject parent; + DbModel * model; + GList * params; + DbIteratorMode mode; + DbIter iter; + gint row; + gboolean row_selected; + gboolean remember_selection; + + DbConn * conn; + SqlStmt * stmt; + gchar * sql; + gboolean use_file; +}; + +struct _DbIteratorClass +{ + /* */ + GObjectClass parent; +}; + +GType db_iterator_get_type (); +GType db_iterator_mode_get_type () G_GNUC_CONST; + +DbIterator * db_iterator_new (DbModel * model); +DbIterator * db_iterator_new_with_stmt (DbConn * conn, SqlStmt * stmt); +DbIterator * db_iterator_new_with_sql (DbConn * conn, const gchar * sql); +DbModel * db_iterator_get_model (DbIterator * obj); +DbConn * db_iterator_get_conn (DbIterator * obj); +void db_iterator_set_conn (DbIterator * obj, DbConn * conn); +gboolean db_iterator_is_ready (DbIterator * obj); +DbIteratorMode db_iterator_get_mode (DbIterator * obj); +void db_iterator_set_mode (DbIterator * obj, DbIteratorMode mode); +gint db_iterator_get_row (DbIterator * obj); +gboolean db_iterator_get_iter (DbIterator * obj, DbIter * iter); +void db_iterator_move_iter (DbIterator * obj, DbIter * iter); +void db_iterator_move_first (DbIterator * obj); +void db_iterator_move_last (DbIterator * obj); +void db_iterator_move_previous (DbIterator * obj); +void db_iterator_move_next (DbIterator * obj); +void db_iterator_move_to (DbIterator * obj, DbIteratorMove move); +void db_iterator_refresh (DbIterator * obj); +const GvnParamSpec * db_iterator_get_spec (DbIterator * obj, gint column); +const GValue * db_iterator_get_value (DbIterator * obj, gint column); +gint db_iterator_get_column_index (DbIterator * obj, const gchar * name); +void db_iterator_set_value (DbIterator * obj, gint column, const GValue * value, GError ** err); +void db_iterator_delete (DbIterator * obj); +void db_iterator_insert (DbIterator * obj); +void db_iterator_add_param (DbIterator * obj, DbParam * param); +GvnParam * db_iterator_get_param (DbIterator * obj, const gchar * column); +GList * db_iterator_get_params (DbIterator * obj); +void db_iterator_bind_param (DbIterator * obj, const gchar * column, GvnParam * param); +void db_iterator_link (DbIterator * obj, const gchar * field, DbIterator * src, const gchar * column); +void db_iterator_link_with_param (DbIterator * obj, const gchar * field, GvnParam * param); +gint db_iterator_get_nrows (DbIterator * obj); +DbModelUpdateFlags db_iterator_get_update_flags (DbIterator * obj); +void db_iterator_reverse_operations (DbIterator * obj); +void db_iterator_perform_operations (DbIterator * obj); +DbModelRowOp db_iterator_get_pending_operations (DbIterator * obj); +gboolean db_iterator_has_pending_operations (DbIterator * obj); + +#endif \ No newline at end of file diff --git a/db/db-model-holder.c b/db/db-model-holder.c new file mode 100644 index 0000000..ef7db6e --- /dev/null +++ b/db/db-model-holder.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-model-holder.h" + +/** + * SECTION: db-model-holder + * @Short_description: + * @Title: DbModelHolder + * @See_also: #DbModel + * + * This interface should be implemented from any class that uses a #DbModel as + * datasource. + **/ + +G_DEFINE_INTERFACE (DbModelHolder, db_model_holder, G_TYPE_INTERFACE); + +/** + * db_model_holder_get_model: + * @obj: a #DbModelHolder + * + * Gets the model used by holder. + * + * Return value: the #DbModel + **/ +DbModel * db_model_holder_get_model (DbModelHolder * obj) +{ + g_return_val_if_fail (DB_IS_MODEL_HOLDER (obj), NULL); + + return DB_MODEL_HOLDER_GET_INTERFACE (obj)->get_model (obj); +} + +/** + * db_model_holder_get_model: + * @obj: a #DbModelHolder + * @model: the #DbModel + * + * Sets the model used by holder. + **/ +void db_model_holder_set_model (DbModelHolder * obj, DbModel * model) +{ + g_return_if_fail (DB_IS_MODEL_HOLDER (obj)); + + DB_MODEL_HOLDER_GET_INTERFACE (obj)->set_model (obj, model); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_model_holder_default_init (DbModelHolderInterface * klass) +{ + g_object_interface_install_property (klass, + g_param_spec_object ("model" + ,"Model" + ,"The model used by the holder" + ,DB_TYPE_MODEL + ,G_PARAM_READWRITE + )); +} diff --git a/db/db-model-holder.h b/db/db-model-holder.h new file mode 100644 index 0000000..1a475ea --- /dev/null +++ b/db/db-model-holder.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_MODEL_HOLDER_H +#define DB_MODEL_HOLDER_H + +#include "db-model.h" + +#define DB_TYPE_MODEL_HOLDER (db_model_holder_get_type ()) +#define DB_MODEL_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_MODEL_HOLDER, DbModelHolder)) +#define DB_IS_MODEL_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_MODEL_HOLDER)) +#define DB_MODEL_HOLDER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), DB_TYPE_MODEL_HOLDER, DbModelHolderInterface)) + +typedef struct _DbModelHolder DbModelHolder; +typedef struct _DbModelHolderInterface DbModelHolderInterface; + +typedef DbModel * (* DbModelHolderGetModelFunc) (DbModelHolder * obj); +typedef void (* DbModelHolderSetModelFunc) (DbModelHolder * obj, DbModel * sql); + +struct _DbModelHolder {}; + +struct _DbModelHolderInterface +{ + /* */ + GTypeInterface parent; + DbModelHolderGetModelFunc get_model; + DbModelHolderSetModelFunc set_model; +}; + +GType db_model_holder_get_type (); +DbModel * db_model_holder_get_model (DbModelHolder * obj); +void db_model_holder_set_model (DbModelHolder * obj, DbModel * model); + +#endif \ No newline at end of file diff --git a/db/db-model.c b/db/db-model.c new file mode 100644 index 0000000..912eb4d --- /dev/null +++ b/db/db-model.c @@ -0,0 +1,3444 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This file is part of Hedera. + * + * Hedera is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "db-model.h" +#include "db-row.h" + +/** + * SECTION: db-model + * @Short_description: data vinculed to a SELECT query sent to the database + * @Title: DbModel + * @See_also: #DbIterator, #DbConn + * + * The #DbModel class gets an SQL query statement to retrieve the data from the + * database connected by a #DbConn. It is normally used undirectly, using instead + * #DbForm. + **/ + +/* + * DbModel: + * @data: (element-type Db.Row): + * @operation: (element-type Db.Operation): + * @row_ops: (element-type Db.Row Db.Operation): + **/ +struct _DbModelPrivate +{ + DbConn * conn; + SqlStmt * stmt; + gchar * sql; + gboolean use_file; + gchar * main_table; + DbModelUpdateFlags update_flags; + + GPtrArray * data; + DbColumn * column; + DbResult * result; + DbRequest * request; + DbModelStatus status; + DbModelMode mode; + gchar * user_main_table; + DbModelUpdateFlags user_update_flags; + guint result_pos; + GQueue * operation; + GHashTable * row_ops; + gint updated_col; + GValue * updated_value; + + GHashTable * column_index; + GSList * pending_request; + GSList * join; + GSList * column_default; + GSList * param_default; + + gint stamp; + + gboolean fresh; + gint sort_column_id; + gint old_sort_column_id; + DbSortType order; + DbSortType old_order; + DbIterCompareFunc default_sort_func; + gpointer default_sort_data; + GDestroyNotify default_sort_destroy; + DbIterCompareFunc sort_func; + gpointer sort_data; + GDestroyNotify sort_destroy; +}; + +G_DEFINE_TYPE (DbModel, db_model, G_TYPE_OBJECT) + +#define MODEL_NOT_READY(obj) (obj->priv->status != DB_MODEL_STATUS_READY) +#define VALID_ITER(iter, model) (iter->data && iter->stamp == model->priv->stamp) +#define DB_ROW_FIELD(row, field) (&((DbRow *) row)->value[field]) +#define DB_ROW_POSITION(row) (((DbRow *) row)->position) + +// Constructors + +/** + * db_model_new: + * @conn: a #DbConn + * @stmt: an #SqlStmt + * + * Returns the newly created #DbModel, filled with the data retrieved from the + * database with @stmt and through @conn. + * + * Return value: (transfer full): a new #DbModel + **/ +DbModel * db_model_new (DbConn * conn, SqlStmt * stmt) +{ + return g_object_new (DB_TYPE_MODEL, "conn", conn, "stmt", stmt, NULL); +} + +/** + * db_model_new_with_sql: + * @conn: a #DbConn + * @sql: a string containing an SQL statement + * + * Returns the newly created #DbModel, filled with the data retrieved from the + * database with @stmt and through @conn. + * + * Return value: (transfer full): a new #DbModel + **/ +DbModel * db_model_new_with_sql (DbConn * conn, const gchar * sql) +{ + return g_object_new (DB_TYPE_MODEL, + "conn", conn, "use-file", FALSE, "sql", sql, NULL); +} + +/** + * db_model_new_with_file: + * @conn: a #DbConn + * @file: the path to the file containing the SQL query to fill the model + * + * Returns a newly created #DbModel, filled with the data retrieved from @file. + * + * Return value: (transfer full): a new #DbModel + **/ +DbModel * db_model_new_with_file (DbConn * conn, const gchar * file) +{ + return g_object_new (DB_TYPE_MODEL, + "conn", conn, "use-file", TRUE, "sql", file, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +// Helper structures and methods + +// Structures + +/* + * DbUpdatedField: + * @column: the position of the field in the row + * @value: the old value of the updated field + * + * Previous value of an updated field. + **/ +typedef struct +{ + gint column; + GValue * value; +} +DbUpdatedField; + +/* + * DbOperation: + * @type: #DbModelRowOp flags + * @locked: %TRUE while the operation is being performed + * @row: the #DbRow over which the operation has been performed + * @updated: (element-type Db.UpdatedField): old values for the updated fields + * in @row + * @request: #DbRequest associated to the operation, once performed + * + * A structure explaining the operations performed over each #DbRow. + **/ +typedef struct +{ + DbModelRowOp type; + gboolean locked; + DbRow * row; + GSList * updated; +} +DbOperation; + +/* + * DbModelRequest: + * @request: a DbRequest being performed + * @operations: a GQueue of operations being performed + * @model: the DbModel over which the operations are being performed + * + * This struct holds the information of a request performed but not yet + * finalized. + **/ +typedef struct +{ + DbModel * obj; + GQueue * operations; +} +DbModelRequest; + +typedef struct +{ + DbModel * obj; + DbIter * iter; + gint col; +} +JoinData; + +typedef struct +{ + gchar * schema; + gchar * table; + GPtrArray * name; + gboolean main; +} +DbModelField; + +typedef struct +{ + DbModelField * left; + DbModelField * right; +} +DbJoin; + +typedef struct +{ + gchar * dst; + gint col; +} +DbColDef; + +typedef struct +{ + gchar * dst; + GvnParam * param; +} +DbParamDef; + +typedef struct +{ + gint count; + gint * index; +} +DbModelPKey; + +enum +{ + DB_MODEL_UNSORTED_SORT_COLUMN_ID = -2, + DB_MODEL_DEFAULT_SORT_COLUMN_ID +}; + +// Prototypes + +static void db_model_set_status (DbModel * obj + ,DbModelStatus status); +static void db_model_clear (DbModel * obj); +static void db_model_free_operation (DbModel * obj + ,DbOperation * op); +static void db_model_manage_join (DbModel * obj + ,DbIter * iter + ,gint col); +static void db_model_post_process_query (DbModel * obj); +static void db_model_finish_insert (DbModel * obj + ,DbRow * req_row, gint i + ,DbRow * row, gint j + ,DbIter * iter); +static gboolean db_model_table_row_all_null (DbModel * model + ,DbRow * row + ,gint col); + +// Signal Handlers + +enum +{ + STATUS_CHANGED + ,LINE_INSERTED + ,LINE_DELETED + ,LINE_TOGGLED + ,LINE_UPDATED + ,LINES_REORDERED + ,SORT_CHANGED + ,OPERATIONS_DONE + + ,LAST_SIGNAL +}; + +static guint db_model_signal[LAST_SIGNAL] = {0}; + +// Default signal handlers + +static void db_model_on_line_updated (DbModel * obj, DbIter * iter) +{ + GValue * updated_value = obj->priv->updated_value; + gint col; + + if (!updated_value || !G_IS_VALUE (updated_value)) return; + + col = obj->priv->updated_col; + gvn_value_ccopy (updated_value, DB_ROW_FIELD (iter->data, col)); + + db_model_manage_join (obj, iter, col); + + g_value_unset (updated_value); + g_free (updated_value); + obj->priv->updated_value = NULL; +} + +static void db_model_on_line_deleted (DbModel * obj, gint position) +{ + gint r_ind; + + for (r_ind = position + 1; r_ind < obj->priv->result->nrows; r_ind++) + DB_ROW_POSITION (g_ptr_array_index (obj->priv->data, (guint) r_ind))--; + + g_ptr_array_remove_index (obj->priv->data, (guint) position); + + obj->priv->result->nrows--; +} + +// External signal handlers + +static void db_model_calculate_update_flags (DbModel * obj) +{ + gint i; + gchar * main_table = NULL; + DbModelPrivate * priv = obj->priv; + + if (priv->result) + for (i = 0; i < priv->result->ncols && !main_table; i++) + if (priv->column[i].info & DB_COLUMN_PRI_KEY) + { + if (!priv->user_main_table) + main_table = priv->column[i].table; + else if (!g_strcmp0 (priv->user_main_table, priv->column[i].table)) + main_table = priv->user_main_table; + } + + g_free (priv->main_table); + + if (main_table) + { + priv->update_flags = DB_MODEL_ALL & priv->user_update_flags; + priv->main_table = g_strdup (main_table); + } + else + { + if (priv->user_main_table && priv->result) + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN), G_LOG_LEVEL_WARNING, + "The requested table can't be set as main table"); + + priv->update_flags = 0; + priv->main_table = NULL; + } +} + +static void db_model_on_data_ready (DbRequest * request, DbModel * obj) +{ + gint i; + DbResult * r; + DbModelPrivate * priv = obj->priv; + + if (priv->request != request) + { + g_object_unref (request); + return; + } + + for (i = 0; i < priv->result_pos; i++) + db_request_fetch_non_select (request, NULL); + + if ((r = db_request_fetch_result (request, NULL))) + { + gint i, j; + + priv->column = r->column; + priv->data = r->data; + + if (!r->data && !r->column) + { + db_result_free (r); + db_model_set_status (obj, DB_MODEL_STATUS_CLEAN); + } + else + { + GSList * t = NULL; + + priv->result = r; + + if (priv->fresh) + priv->column_index = g_hash_table_new_full (g_str_hash, + g_str_equal, (GDestroyNotify) g_free, NULL); + + for (i = 0; i < priv->result->ncols; i++) + { + // Set fields editable if *all* primary keys are selected FIXME + if (priv->column[i].info & DB_COLUMN_PRI_KEY + && !g_slist_find_custom (t, priv->column[i].table, (GCompareFunc) g_strcmp0)) + for (j = 0; j < priv->result->ncols; j++) + if (!g_strcmp0 (priv->column[i].table, priv->column[j].table)) + { + gvn_param_spec_set_editable (priv->column[j].spec, TRUE); + t = g_slist_prepend (t, priv->column[j].table); + } + + if (priv->fresh && priv->column_index) + g_hash_table_insert (priv->column_index, + g_strdup (priv->column[i].display), GINT_TO_POINTER (i)); + } + + g_slist_free (t); + + db_model_calculate_update_flags (obj); + + if (!priv->fresh) + db_model_set_sort_column_id (obj, + priv->old_sort_column_id, priv->old_order); + else + db_model_post_process_query (obj); + + db_model_set_status (obj, DB_MODEL_STATUS_READY); + } + } + else + db_model_set_status (obj, DB_MODEL_STATUS_ERROR); + + g_clear_object (&priv->request); +} + +static void db_model_on_join_query_done (DbRequest * request, JoinData * join_data) +{ + gint i, j; + DbResult * r; + DbIter * iter = join_data->iter; + GError * err = NULL; + DbModel * obj = join_data->obj; + DbModelPrivate * priv = obj->priv; + + while ((r = db_request_fetch_result (request, &err))) + { + for (i = 0; i < priv->result->ncols; i++) + for (j = 0; j < r->ncols; j++) + if (!g_strcmp0 (r->column[j].table, priv->column[i].table) + && !g_strcmp0 (r->column[j].name, priv->column[i].name)) + { + if (r->nrows > 0) + { + GValue * new_value = + DB_ROW_FIELD (g_ptr_array_index (r->data, 0), j); + + priv->updated_value = g_new0 (GValue, 1); + g_value_init (priv->updated_value, G_VALUE_TYPE (new_value)); + gvn_value_copy (new_value, priv->updated_value); + } + else + priv->updated_value = + g_value_init (g_new0 (GValue, 1), GVN_TYPE_NULL); + + priv->updated_col = i; + g_signal_emit (obj, db_model_signal[LINE_UPDATED], 0, iter); + } + + db_result_free (r); + } + + if (err) + { + g_message ("%s", err->message); + g_error_free (err); + } + + priv->pending_request = + g_slist_remove (obj->priv->pending_request, request); + g_object_unref (request); +} + +static void db_model_on_stmt_changed (SqlStmt * stmt, DbModel * obj) +{ + if (obj->priv->conn && stmt) + { + if (sql_object_is_ready (SQL_OBJECT (stmt))) + db_model_refresh (obj); + else + { + db_model_clear (obj); + db_model_set_status (obj, DB_MODEL_STATUS_CLEAN); + } + } +} + +static void db_model_on_operations_done (DbRequest * request, DbModelRequest * data) +{ + DbOperation * op; + GError * err = NULL; + DbModel * obj = data->obj; + DbModelPrivate * priv = obj->priv; + + obj->priv->pending_request = + g_slist_remove (priv->pending_request, request); + + while (db_request_fetch_non_select (request, &err) != -1) + { + gint i, j; + DbResult * result; + DbRow * req_row, * row; + + op = g_queue_pop_head (data->operations); + row = op->row; + + g_hash_table_remove (priv->row_ops, op->row); + + if (op->type & DB_MODEL_ROW_OP_DELETE) // DELETE + { + g_signal_emit + (obj, db_model_signal[LINE_DELETED], 0, DB_ROW_POSITION (row)); + } + else if (op->type & DB_MODEL_ROW_OP_INSERT) // INSERT + { // Catch the SELECT associated with each INSERT + + result = db_request_fetch_result (request, &err); + + if (result) + { + if (result->data && result->nrows > 0) + { + req_row = g_ptr_array_index (result->data, 0); + + for (i = 0; i < result->ncols; i++) + for (j = 0; j < priv->result->ncols; j++) + if (!g_strcmp0 (priv->column[j].name, result->column[i].name) + && !g_strcmp0 (priv->column[j].table, priv->main_table)) + { + DbIter iter; + db_model_finish_insert (obj, req_row, i, row, j, &iter); + } + } + + db_result_free (result); + } + } + else if (op->type & DB_MODEL_ROW_OP_UPDATE)// UPDATE + if (priv->join) + { + GSList * n; + DbUpdatedField * u; + DbIter iter; + iter.stamp = priv->stamp; + iter.data = row; + + for (n = op->updated; n; n = n->next) + { + u = n->data; + +/* if (r->next && r->next->data + && ((DbResult *) r->next->data)->column)*/ + if (priv->column_default + && gvn_value_is_null (u->value) + && g_strcmp0 (priv->column[u->column].table, priv->main_table) + && db_model_table_row_all_null (obj, row, u->column)) + { // INSERT + SELECT "update" + + result = db_request_fetch_result (request, &err); + + if (result) + { + if (result->data && result->nrows > 0) + { + req_row = g_ptr_array_index (result->data, 0); + + for (i = 0; i < result->ncols; i++) + for (j = 0; j < priv->result->ncols; j++) + if (!g_strcmp0 (priv->column[j].name, result->column[i].name) + && (!g_strcmp0 (priv->column[j].table, result->column[i].table) + || !result->column[i].table)) + db_model_finish_insert (obj, req_row, i, row, j, &iter); + } + + db_result_free (result); + } + } +/* + if (n->next && (u = ((DbUpdatedField *) n->next->data)) + && priv->column_default + && gvn_value_is_null (u->value) + && g_strcmp0 (priv->column[u->column].table, priv->main_table) + && db_model_table_row_all_null (obj, row, u->column)) + r = r->next; +*/ + } + } + + db_model_free_operation (obj, op); + } + + //XXX Iterate both the result list and the queue at the same time + // when the Request has its Results set on errors(future?). + // Currently, if something fails in the plugin's code, it returns NULL + + if (err) + { + while ((op = g_queue_pop_head (data->operations))) + { + op->locked = FALSE; + g_queue_push_tail (priv->operation, op); + } + } + else + g_signal_emit (obj, db_model_signal[OPERATIONS_DONE], 0); + + g_object_unref (request); +} + +// Private helper methods and functions + +static void join_data_free (JoinData * join_data) +{ + g_object_unref (join_data->obj); + db_iter_free (join_data->iter); + g_free (join_data); +} + +static void db_updated_field_free (DbUpdatedField * u) +{ + if (u && u->value) + { + g_value_unset (u->value); + g_free (u->value); + } + + g_free (u); + u = NULL; +} + +static void db_model_free_operation (DbModel * obj, DbOperation * op) +{ + if (op->updated) + g_slist_free_full (op->updated, (GDestroyNotify) db_updated_field_free); + + g_hash_table_remove (obj->priv->row_ops, op->row); + + g_free (op); +} + +static void db_model_clean_operations (DbModel * obj) +{ + DbOperation * op; + DbModelPrivate * priv = obj->priv; + + while ((op = g_queue_pop_head (priv->operation))) + db_model_free_operation (obj, op); +} + +static void db_model_request_free (DbModelRequest * req) +{ + if (req) + { + g_queue_free (req->operations); + g_object_unref (req->obj); + } + + g_free (req); +} + +static void db_operation_add_updated (DbOperation * op, gint col) +{ + GSList * n; + DbUpdatedField * u; + + for (n = op->updated; n; n = n->next) + if (((DbUpdatedField *) n->data)->column == col) + return; + + u = g_new (DbUpdatedField, 1); + gvn_value_copy (DB_ROW_FIELD (op->row, col) + ,g_value_init (u->value = g_new0 (GValue, 1) + ,G_VALUE_TYPE (DB_ROW_FIELD (op->row, col)))); + u->column = col; + op->type |= DB_MODEL_ROW_OP_UPDATE; + op->updated = g_slist_prepend (op->updated, u); + + return; +} + +static gboolean db_model_set_row_operation (DbModel * obj, + DbRow * row, DbModelRowOp type, gint col) +{ + DbOperation * op = g_hash_table_lookup (obj->priv->row_ops, row); + + if (!op) + { + DbOperation * new_op = g_new (DbOperation, 1); + new_op->locked = FALSE; + new_op->row = row; + new_op->updated = NULL; + new_op->type = type; + + if (type & DB_MODEL_ROW_OP_UPDATE) + db_operation_add_updated (new_op, col); +/* if (!db_operation_add_updated (new_op, col)) + { + g_free (new_op); + return FALSE; + } +*/ + g_hash_table_insert (obj->priv->row_ops, row, new_op); + g_queue_push_tail (obj->priv->operation, new_op); + } + else if (!op->locked) + { + if (type & DB_MODEL_ROW_OP_DELETE) + op->type ^= DB_MODEL_ROW_OP_DELETE; + + if (type & DB_MODEL_ROW_OP_UPDATE) + db_operation_add_updated (op, col); +// if (!db_operation_add_updated (op, col)) +// return FALSE; + } + else + return FALSE; + + return TRUE; +} + +void db_model_reverse_operations (DbModel * obj) +{ + DbModelPrivate * priv = obj->priv; + DbOperation * op; + + g_return_if_fail (DB_IS_MODEL (obj)); + + while ((op = g_queue_pop_tail (priv->operation))) + { + g_hash_table_remove (priv->row_ops, op->row); + + if (op->type & DB_MODEL_ROW_OP_DELETE) + { + if (op->type & DB_MODEL_ROW_OP_INSERT) + { + g_signal_emit (obj, + db_model_signal[LINE_DELETED], 0, DB_ROW_POSITION (op->row)); + } + else + { + DbIter iter; + iter.data = op->row; + iter.stamp = priv->stamp; + + g_signal_emit (obj, db_model_signal[LINE_TOGGLED], 0, &iter); + } + } + else if (op->type & DB_MODEL_ROW_OP_INSERT) + { + g_signal_emit (obj, + db_model_signal[LINE_DELETED], 0, DB_ROW_POSITION (op->row)); + } + else if (op->type & DB_MODEL_ROW_OP_UPDATE) + { + GSList * n; + DbUpdatedField * u; + + for (n = op->updated; n; n = n->next) + { + DbIter iter; + iter.stamp = priv->stamp; + iter.data = op->row; + u = n->data; + + priv->updated_value = g_new0 (GValue, 1); + g_value_init (priv->updated_value, G_VALUE_TYPE (u->value)); + gvn_value_copy (u->value, priv->updated_value); + priv->updated_col = u->column; + + g_signal_emit (obj, db_model_signal[LINE_UPDATED], 0, &iter); + } + } + + db_model_free_operation (obj, op); + } +} + +static DbModelField * db_model_field_new (const gchar * table, const gchar * schema) +{ + DbModelField * field = g_new (DbModelField, 1); + field->schema = g_strdup (schema); + field->table = g_strdup (table); + field->name = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + field->main = FALSE; + return field; +} + +static DbModelField * db_model_field_new_from_string (const gchar * str) +{ + DbModelField * field = NULL; + gchar * new_str = g_strdup (str), ** aux, ** f; + gint i, f_len; + g_strstrip (new_str); + f = g_strsplit (new_str, ".", G_MAXINT); + g_free (new_str); + f_len = g_strv_length (f); + + for (i = 0; i < f_len; i++) + { + aux = g_strsplit (f[i], "\"", G_MAXINT); + + if (g_strcmp0 (aux[0], "")) + { + g_strfreev (aux); + break; + } + + g_free (f[i]); + f[i] = g_strdup (aux[1]); + g_strfreev (aux); + } + + switch (f_len) + { + case 3: + { + field = db_model_field_new (f[1], f[0]); + g_ptr_array_add (field->name, g_strdup (f[2])); + break; + } + case 2: + { + field = db_model_field_new (f[0], NULL); + g_ptr_array_add (field->name, g_strdup (f[1])); + break; + } + case 1: + { + field = db_model_field_new (NULL, NULL); + g_ptr_array_add (field->name, g_strdup (f[0])); + } + } + + g_strfreev (f); + + return field; +} + +static void db_model_field_free (DbModelField * field) +{ + if (field) + { + if (field->schema) + g_free (field->schema); + if (field->table) + g_free (field->table); + if (field->name) + g_ptr_array_free (field->name, TRUE); + } + + g_free (field); +} + +static void db_join_free (DbJoin * join) +{ + if (join) + { + if (join->left) + db_model_field_free (join->left); + if (join->right) + db_model_field_free (join->right); + } + + g_free (join); +} + +static void db_model_calculate_col_def (DbModel * obj, SqlJoin * join, + SqlField * l_field, SqlField * r_field) +{ + gint i, col = -1; + gchar * dst = NULL; + DbModelPrivate * priv = obj->priv; + SqlField * f = NULL; + SqlTarget * l_table = join->target_left, * r_table = join->target_right; + + for (i = 0; i < priv->result->ncols; i++) + { + f = NULL; + + if (!g_strcmp0 (priv->column[i].name, l_field->name)) + { + f = l_field; + dst = (join->type == SQL_JOIN_TYPE_RIGHT) ? + l_field->name : r_field->name; + } + else if (!g_strcmp0 (priv->column[i].name, r_field->name)) + { + f = r_field; + dst = (join->type == SQL_JOIN_TYPE_LEFT) ? + l_field->name : r_field->name; + } + + if (f) + {//TODO add schema checks + if (f->target) + { + if (!g_strcmp0 (priv->column[i].table, f->target) + || (!g_strcmp0 (priv->column[i].table, SQL_TABLE (l_table)->name) + && !g_strcmp0 (f->target, l_table->alias))) + { + col = i; + break; + } + else if (!g_strcmp0 (priv->column[i].table, SQL_TABLE (r_table)->name) + && !g_strcmp0 (f->target, r_table->alias)) + { + col = i; + break; + } + } + else + { + col = i; + break; + } + } + } + + if (f) + db_model_set_default_value_from_column (obj, dst, col); +} + +static void db_model_set_join_fields (DbModel * obj, SqlJoin * join, + SqlField * lsql_field, SqlField * rsql_field, + DbModelField * lfield, DbModelField * rfield) +{ + gboolean check; + SqlTarget * ltarget = join->target_left, * rtarget = join->target_right; + + check = !g_strcmp0 (lfield->schema, lsql_field->schema) + || !g_strcmp0 (lfield->table, lsql_field->target) + || !g_strcmp0 (ltarget->alias, lsql_field->target) + || !g_strcmp0 (rfield->schema, rfield->schema) + || !g_strcmp0 (rfield->table, rsql_field->target) + || !g_strcmp0 (rtarget->alias, rsql_field->target); + + g_ptr_array_add (lfield->name, + g_strdup (check ? lsql_field->name : rsql_field->name)); + g_ptr_array_add (rfield->name, + g_strdup (check ? rsql_field->name : lsql_field->name)); +} + +static void db_model_post_process_query (DbModel * obj) +{ +// TODO When parser gets fully functional, these 3 lines won't be needed, +// because obj->stmt will be a parsed stmt: + gchar * rend = db_conn_render (obj->priv->conn, obj->priv->stmt, NULL); + SqlObject * stmt = sql_parser_parse (rend); +/*gchar * rend2 = NULL; +if (stmt && (rend2 = db_conn_render (obj->priv->conn, stmt, NULL))) +g_message ("SQL + Parser + Render =\n %s", rend2); +g_free (rend2);*/ + g_free (rend); + + if (stmt && SQL_IS_SELECT (stmt)) + { + DbModelField * lfield, * rfield; + SqlJoin * join; + GList * n; + SqlSelect * select = SQL_SELECT (stmt); + gboolean calculate_join = FALSE; + + for (n = sql_list_get_items (SQL_DML (select)->targets); n; n = n->next) + if ((join = n->data) + && SQL_IS_JOIN (join) + && SQL_IS_TABLE (join->target_left) + && SQL_IS_TABLE (join->target_right) + && SQL_IS_OPERATION (join->condition)) + { +// DbJoin and ColDef creation + GList * operators; + SqlOperation * op = SQL_OPERATION (join->condition); + SqlField * lsql_field = NULL, * rsql_field = NULL; + + lfield = db_model_field_new (SQL_TABLE (join->target_left)->name, NULL); + rfield = db_model_field_new (SQL_TABLE (join->target_right)->name, NULL); + + if (join->type == SQL_JOIN_TYPE_RIGHT) + rfield->main = TRUE; + else + lfield->main = TRUE; + + if (op->type == SQL_OPERATION_TYPE_AND) + { + GList * l; + + for (l = sql_list_get_items (op->operators); l; l = l->next) + { + SqlOperation * subop; + operators = sql_list_get_items (subop->operators); + + if (SQL_IS_OPERATION (subop = l->data) + && subop->type == SQL_OPERATION_TYPE_EQUAL + && operators->data // Left Field + && operators->next && operators->next->data) // Right Field + { + lsql_field = SQL_FIELD (operators->data); + rsql_field = SQL_FIELD (operators->next->data); + + db_model_set_join_fields (obj, join, + lsql_field, rsql_field, lfield, rfield); + + calculate_join = TRUE; + + if (join->type != SQL_JOIN_TYPE_INNER) + db_model_calculate_col_def + (obj, join, lsql_field, rsql_field); + } + else + { + calculate_join = FALSE; + break; + } + } + } + else + { + operators = sql_list_get_items (op->operators); + + if (op->type == SQL_OPERATION_TYPE_EQUAL && operators->data + && operators->next && operators->next->data) + { + lsql_field = SQL_FIELD (operators->data); + rsql_field = SQL_FIELD (operators->next->data); + + db_model_set_join_fields (obj, join, + lsql_field, rsql_field, lfield, rfield); + + calculate_join = TRUE; + + if (join->type != SQL_JOIN_TYPE_INNER) + db_model_calculate_col_def + (obj, join ,lsql_field, rsql_field); + } + } + + if (calculate_join) + { + DbJoin * join_res = g_new (DbJoin, 1); + join_res->left = lfield; + join_res->right = rfield; + + obj->priv->join = g_slist_prepend (obj->priv->join, join_res); + } + else + { + db_model_field_free (lfield); + db_model_field_free (rfield); + } + } + } + + if (G_IS_OBJECT (stmt)) + g_object_unref (stmt); +} + +static inline gboolean stored (const gint * v, const gint length, const gint target) +{ + gint i; + + for (i = 0; i < length; i++) + if (v[i] == target) + return TRUE; + return FALSE; +} + +static DbModelPKey * db_model_get_primary_key (DbModel *obj, gchar * table) +{ + gint i, j; + DbModelPKey * pkey = g_new (DbModelPKey, 1); + pkey->count = 0; +// Get number of pkeys + for (i = 0; i < obj->priv->result->ncols; i++) + if ((obj->priv->column[i].info & DB_COLUMN_PRI_KEY) + && !g_strcmp0 (obj->priv->column[i].table, table)) + pkey->count++; + + pkey->index = g_new (gint, pkey->count); +// Get pkey/s + for (i = 0; i < pkey->count; i++) + for (j = 0; j < obj->priv->result->ncols; j++) + if (!g_strcmp0 (obj->priv->column[j].table, table) + && (obj->priv->column[j].info & DB_COLUMN_PRI_KEY) + && !stored (pkey->index, i+1, j)) + { + pkey->index[i] = j; + break; + } + + return pkey; +} + +static void db_model_pkey_free (DbModelPKey * pkey) +{ + g_free (pkey->index); + g_free (pkey); +} + + +/* + * Comparison between values, using case-insensitive and UTF-8 strings + */ +static gint db_model_value_compare0 (const GValue * a, const GValue * b) +{ + GType a_type = G_VALUE_TYPE (a); + gboolean a_is_val = G_IS_VALUE (a); + gboolean b_is_val = G_IS_VALUE (b); + + if (!(a_is_val && b_is_val)) + { + if (a_is_val) + return 1; + if (b_is_val) + return -1; + } + else if (a_type == G_VALUE_TYPE (b)) + { + switch (a_type) + { + case G_TYPE_FLOAT: + { + gfloat aux = g_value_get_float (a) - g_value_get_float (b); + return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0; + } + case G_TYPE_DOUBLE: + { + gdouble aux = g_value_get_double (a) - g_value_get_double (b); + return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0; + } + case G_TYPE_INT: + return g_value_get_int (a) - g_value_get_int (b); + case G_TYPE_UINT: + return (gint) (g_value_get_uint (a) - g_value_get_uint (b)); + case G_TYPE_LONG: + return (gint) (g_value_get_long (a) - g_value_get_long (b)); + case G_TYPE_ULONG: + return (gint) (g_value_get_ulong (a) - g_value_get_ulong (b)); + case G_TYPE_BOOLEAN: + return (gint) (g_value_get_boolean (a) - g_value_get_boolean (b)); + case G_TYPE_CHAR: + return (gint) (g_value_get_schar (a) - g_value_get_schar (b)); + case G_TYPE_STRING: + { + gchar * a_str = g_utf8_casefold (g_value_get_string (a), -1); + gchar * b_str = g_utf8_casefold (g_value_get_string (b), -1); + return g_utf8_collate (a_str, b_str); + } + default: + if (a_type == G_TYPE_DATE) + return g_date_compare (g_value_get_boxed (a), g_value_get_boxed (b)); + if (a_type == G_TYPE_DATE_TIME) + return g_date_time_compare (g_value_get_boxed (a), g_value_get_boxed (b)); + else if (a_type == G_TYPE_BYTES) + return (gint) (g_value_get_boxed (a) - g_value_get_boxed (b)); + else if (a_type == GVN_TYPE_NULL) + return 0; + else + g_warning ("Attempting to compare invalid types: %s\n", + g_type_name (a_type)); + } + } + else if (gvn_value_is_null (a)) + return -1; + else if (gvn_value_is_null (b)) + return 1; + + return 1; +} + +static gint db_model_valcmp_asc (gpointer * a, gpointer * b, gpointer col) +{ + DbRow * first = *a; + DbRow * second = *b; + return db_model_value_compare0 (&first->value[GPOINTER_TO_INT (col)] + ,&second->value[GPOINTER_TO_INT (col)]); +} + +static gint db_model_valcmp_desc (gpointer * a, gpointer * b, gpointer col) +{ + DbRow * first = *a; + DbRow * second = *b; + return -db_model_value_compare0 (&(first->value)[GPOINTER_TO_INT (col)] + ,&(second->value)[GPOINTER_TO_INT (col)]); + +} + +static void db_model_set_status (DbModel * obj, DbModelStatus status) +{ + obj->priv->status = status; + g_signal_emit (obj, db_model_signal[STATUS_CHANGED], 0, status); +} + +static void db_model_cancel_pending_requests (DbModel * obj) +{ + GSList * n; + + for (n = obj->priv->pending_request; n; n = n->next) + db_request_cancel (n->data); +} + +static void db_model_add_pending_request (DbModel * obj, DbRequest * request) +{ + obj->priv->pending_request = g_slist_prepend (obj->priv->pending_request, + request); +} + +static void db_model_manage_join (DbModel * obj, DbIter * iter, gint col) +{ + DbModelPrivate * priv = obj->priv; + + if (priv->join + && gvn_param_spec_get_editable (priv->column[col].spec) + && !gvn_value_is_null (DB_ROW_FIELD (iter->data, col))) + { + gint i; + GSList * n; + gboolean send_request = FALSE, end = FALSE; + SqlList * stmts = g_object_ref_sink (sql_list_new (SQL_TYPE_MULTI_STMT)); + +/*FIXME + for (i = 0; i < obj->ncols; i++) + // Check for multi-field pkeys to be fully set + // need to know the total number of pkey fields + if (i != col && obj->column[i].info & DB_COLUMN_PRI_KEY + && gvn_value_is_null (DB_ROW_FIELD (iter->data, i)) + && !g_strcmp0 (obj->column[i].table, obj->column[col].table)) + { + end = TRUE; + break; + } +*/ + if (!end) + for (n = priv->join; n; n = n->next) + { + gint j; + SqlObject * where, * select; + DbModelField * main_field = NULL, * other_field = NULL; + DbJoin * join = n->data; + + if (join->left->main) + { + main_field = join->left; + other_field = join->right; + } + else if (join->right->main) + { + main_field = join->right; + other_field = join->left; + } + + for (i = 0; i < main_field->name->len; i++) + if (!g_strcmp0 (priv->column[col].table, main_field->table) + && !g_strcmp0 (priv->column[col].name, + g_ptr_array_index (main_field->name, i))) + { + send_request = TRUE; + break; + } + + if (!send_request) + continue; // Continue to the next DbJoin in the list + + select = sql_select_new (); + + sql_object_add_child (select, "targets", sql_table_new (other_field->table)); + + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + + for (i = 0; i < priv->result->ncols; i++) + if (!g_strcmp0 (priv->column[i].table, other_field->table)) + { + sql_object_add_child (select, "exprs", + sql_field_new (priv->column[i].name, other_field->table, NULL)); + } + else if (!g_strcmp0 (priv->column[i].table, main_field->table)) + { + for (j = 0; j < main_field->name->len; j++) + if (!g_strcmp0 (priv->column[i].name, + g_ptr_array_index (main_field->name, j))) + { + SqlObject * equal = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + + sql_object_add_child (equal, "operators", + sql_field_new (g_ptr_array_index (other_field->name, j) + ,other_field->table, NULL)); + + sql_object_add_child (equal, "operators", + sql_value_new_with_value (DB_ROW_FIELD (iter->data, i))); + + sql_object_add_child (where, "operators", equal); + } + } + + sql_object_set (select, "where", where); + sql_list_add (stmts, select); + } + + if (send_request) + { + JoinData * join_data; + DbRequest * request; + + join_data = g_new (JoinData, 1); + join_data->obj = g_object_ref (obj); + join_data->iter = db_iter_copy (iter); + join_data->col = col; + + request = db_conn_query_with_stmt_async (priv->conn + ,g_object_new (SQL_TYPE_MULTI_STMT, "stmts", stmts, NULL) + ,(DbRequestDoneCallback) db_model_on_join_query_done + ,join_data + ,(GDestroyNotify) join_data_free + ); + db_model_add_pending_request (obj, request); + } + + g_object_unref (stmts); + } +} + +static void db_model_finish_insert (DbModel * obj, + DbRow * req_row, gint i, DbRow * row, gint j, DbIter * iter) +{ + GValue * v; + gboolean emit = TRUE; + iter->stamp = obj->priv->stamp; + iter->data = row; + + obj->priv->updated_value = g_new0 (GValue, 1); + + if ((v = &req_row->value[i]) && G_IS_VALUE (v) + && !gvn_value_is_null (DB_ROW_FIELD (req_row, i)) + && gvn_value_is_null (DB_ROW_FIELD (row, j))) + { + g_value_init (obj->priv->updated_value, G_VALUE_TYPE (v)); + gvn_value_copy (v, obj->priv->updated_value); + } + else if (gvn_value_is_null (DB_ROW_FIELD (row, j))) + { + g_value_init (obj->priv->updated_value, GVN_TYPE_NULL); + } + else + { + emit = FALSE; + g_free (obj->priv->updated_value); + } + + if (emit) + { + obj->priv->updated_col = j; + g_signal_emit (obj, db_model_signal[LINE_UPDATED], 0, iter); + } +} + +static void db_model_clear (DbModel * obj) +{ + DbModelPrivate * priv = obj->priv; + + if (priv->request) + { + db_request_cancel (priv->request); + priv->request = NULL; + } + else if (priv->result) + { + db_model_clean_operations (obj); + db_model_cancel_pending_requests (obj); + + db_result_free (priv->result); + priv->result = NULL; + priv->column = NULL; + priv->data = NULL; + + g_free (priv->main_table); + priv->main_table = NULL; + priv->update_flags = 0; + + priv->fresh = FALSE; + priv->old_order = priv->order; + priv->old_sort_column_id = priv->sort_column_id; + priv->sort_column_id = DB_MODEL_UNSORTED_SORT_COLUMN_ID; + priv->stamp = g_random_int (); + } +} + +static gboolean db_model_table_row_all_null (DbModel * obj, DbRow * row, gint col) +{ + DbUpdatedField * u; + GSList * n; + gboolean updated; + gint i; + + for (i = 0; i < row->len; i++) + { + updated = FALSE; + + if (!g_strcmp0 (obj->priv->column[col].table, obj->priv->column[i].table) + && i != col) + { + DbOperation * operation = g_hash_table_lookup (obj->priv->row_ops, row); + + if (operation) + for (n = operation->updated; n; n = n->next) + if ((u = (DbUpdatedField *) n->data) && u->column == i) + { + updated = TRUE; + + if (!gvn_value_is_null (u->value)) + return FALSE; + } + + if (!updated && !gvn_value_is_null (DB_ROW_FIELD (row, i))) + return FALSE; + } + } + + return TRUE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +// Set&Get methods + +/** + * db_model_set_conn: + * @obj: a #DbModel + * @conn: (allow-none): a #DbConn + * + * Sets the connection through which the communication is established with + * the database. + **/ +void db_model_set_conn (DbModel * obj, DbConn * conn) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (DB_IS_CONN (conn) || !conn); + + if (conn) + { + if (!obj->priv->conn) + { + obj->priv->conn = g_object_ref (conn); + db_model_on_stmt_changed (obj->priv->stmt, obj); + } + else + g_warning ("DbModel: The connection can only be set once"); + } +} + +/** + * db_model_get_conn: + * @obj: a #DbModel + * + * Returns the connection through which the communication is established with + * the database. + * + * Return value: (transfer none): the #DbConn of the model + **/ +DbConn * db_model_get_conn (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + + return obj->priv->conn; +} + +/** + * db_model_get_spec: + * @obj: a #DbModel + * @col: the number of a column of @obj + * + * Returns the #GvnParamSpec of a field in the position @col of @obj. + * + * Return value: (transfer none): the #GvnParamSpec of the column with number @col + **/ +const GvnParamSpec * db_model_get_spec (DbModel * obj, gint col) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + + if ((obj->priv->result && 0 <= col + && col < obj->priv->result->ncols) + && obj->priv->column) + return obj->priv->column[col].spec; + return NULL; +} + +/** + * db_model_get_column_name: + * @obj: a #DbModel + * @col: the number of a column of @obj + * + * Retrieves the name of a field in the position @col of @obj. + * + * Return value: the name of the column with number @col + **/ +const gchar * db_model_get_column_name (DbModel * obj, gint col) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + + if ((obj->priv->result + && 0 <= col + && col < obj->priv->result->ncols) + && obj->priv->column) + return obj->priv->column[col].display; + return NULL; +} + +/** + * db_model_get_column_index: + * @obj: a #DbModel + * @name: the name of a column of @obj + * + * Retrieves the position in the query of the column called @name. + * + * Return value: the index of the column with name @name or -1 if there isn't + * a column called @name or if @name is NULL + **/ +gint db_model_get_column_index (DbModel * obj, const gchar * name) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), -1); + + if (!name || !obj->priv->column_index) + return -1; + + return GPOINTER_TO_INT (g_hash_table_lookup (obj->priv->column_index, name)); +} + +/** + * db_model_get_status: + * @obj: a #DbModel + * + * Returns the current #DbModelStatus of @obj. + * + * Return value: the status of @obj + **/ +DbModelStatus db_model_get_status (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), -1); + return obj->priv->status; +} + +/** + * db_model_get_mode: + * @obj: a #DbModel + * + * Retrieves the current working mode of @obj. See #DbModelMode. + * + * Return value: the current mode + **/ +DbModelMode db_model_get_mode (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), -1); + return (obj->priv->mode); +} + +/** + * db_model_set_mode: + * @obj: a #DbModel + * @mode: a #DbModelMode to set the model on + * + * Sets the working mode of @obj to @mode. See #DbModelMode. + **/ +void db_model_set_mode (DbModel * obj, DbModelMode mode) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + obj->priv->mode = mode; +} + +/** + * db_model_toggle_mode: + * @obj: a #DbModel + * + * Toogles the working mode of @obj. See #DbModelMode to see the two possible + * modes. + **/ +void db_model_toggle_mode (DbModel * obj) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + obj->priv->mode = (obj->priv->mode == DB_MODEL_MODE_ON_DEMAND)? + DB_MODEL_MODE_ON_CHANGE: + DB_MODEL_MODE_ON_DEMAND; +} + +/** + * db_model_set_result_pos: + * @obj: a #DbModel + * @pos: position of the query + * + * Sets the position where the query that will fill @model with data will be + * placed in a multi-query statement. + **/ +void db_model_set_result_pos (DbModel * obj, guint pos) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + obj->priv->result_pos = pos; +} + +/** + * db_model_get_update_flags: + * @obj: a #DbModel + * + * Retrieves the update flags of @obj. See #DbModelUpdateFlags for the details + * of those flags. See also db_model_request_update_flags(). + * + * Return value: the #DbModelUpdateFlags of @obj + **/ +DbModelUpdateFlags db_model_get_update_flags (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + return obj->priv->update_flags; +} + +/** + * db_model_request_update_flags: + * @obj: a #DbModel + * @flags: the set of #DbModelUpdateFlags to be set to @obj + * + * Requests the update flags of @obj. See #DbModelUpdateFlags for the details + * of those flags. If @obj does not allow an operation by itself, it will + * log a warning when trying to set this operation. If the flag set is + * #DB_MODEL_UPDATE it may not log until trying to update a non-updatable + * field, see db_model_set_value(). See also db_model_get_update_flags() and + * db_model_unset_update_flags(). + **/ +void db_model_request_update_flags (DbModel * obj, DbModelUpdateFlags flags) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + obj->priv->user_update_flags = flags; + db_model_calculate_update_flags (obj); +} + +/** + * db_model_unset_update_flags: + * @obj: a #DbModel + * @flags: the set of #DbModelUpdateFlags to be unset in @obj + * + * Unsets the update flags of @obj. See #DbModelUpdateFlags for the details + * of those flags. See also db_model_get_update_flags() and + * db_model_request_update_flags(). + **/ +void db_model_unset_update_flags (DbModel * obj, DbModelUpdateFlags flags) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + obj->priv->user_update_flags &= ~flags; + db_model_calculate_update_flags (obj); +} + +/** + * db_model_get_last: + * @obj: a @DbModel + * @iter: (out): an unitialized #DbIter + * + * Points @iter to the last row of @obj. + * + * Return value: %FALSE if @obj is a valid #DbModel, %TRUE otherwise + **/ +gboolean db_model_get_last (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + iter->data = g_ptr_array_index + (obj->priv->data, (guint) obj->priv->data->len - 1); + iter->stamp = obj->priv->stamp; + return TRUE; +} + +/** + * db_model_add_join_columns: + * @obj: a #DbModel + * @left: the field on the left of the join + * @right: the field on the right of the join + * + * Sets the binding betwen two joined fields. This will generate a SELECT query + * to the database to change the corresponding values in the model. Currently + * it's used by the internal parser. + **/ +void db_model_add_join_columns (DbModel * obj, const gchar * left, const gchar * right) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + DbJoin * join = g_new (DbJoin, 1); + join->left = db_model_field_new_from_string (left); + join->right = db_model_field_new_from_string (right); + + obj->priv->join = g_slist_prepend (obj->priv->join, join); +} + +/** + * db_model_set_default_value_from_column: + * @obj: a #DbModel + * @dst_field: the field to be set + * @src_column: field from wich the value is picked + * + * TODO: cambiar src_column a string y almacenarlo, cuando se vaya a usar, cambiarlo + por el entero correspondiente + **/ +void db_model_set_default_value_from_column (DbModel * obj, + const gchar * dst_field, gint src_column) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + if (!obj->priv->result || 0 > src_column || src_column >= obj->priv->result->ncols) + return; + + DbColDef * def = g_new (DbColDef, 1); + def->dst = g_strdup (dst_field); + def->col = src_column; + + obj->priv->column_default = g_slist_prepend (obj->priv->column_default, def); +} + +/** + * db_model_set_default_value_from_param: + * @obj: a #DbModel + * @dst_field: the field to be set + * @param: a #GvnParam + * @id: an identifier string for @param + * + * Get the default value for @dst_field from @param. + **/ +void db_model_set_default_value_from_param (DbModel * obj, + const gchar * dst_field, GvnParam * param, const gchar * id) +{ + SqlObject * op, * value; + + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (GVN_IS_PARAM (param)); + g_return_if_fail (dst_field); + + op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + sql_object_add_child (op, "operators", sql_field_new (dst_field, NULL, NULL)); + + value = sql_value_new (); + sql_value_set_param (SQL_VALUE (value), param); + sql_object_add_child (op, "operators", value); + + sql_object_add_held_object (SQL_OBJECT (obj->priv->stmt), id, op); + + DbParamDef * def = g_new (DbParamDef, 1); + def->dst = g_strdup (dst_field); + def->param = g_object_ref_sink (param); + + obj->priv->param_default = g_slist_prepend (obj->priv->param_default, def); +} + +/** + * db_model_add_param: + * @obj: a #DbModel + * @id: an identifier string for @param + * @param: the #GvnParam to add + * + * Links the statement in the model to @param. + **/ +void db_model_add_param (DbModel * obj, const gchar * id, GvnParam * param) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (GVN_IS_PARAM (param)); + + sql_string_add_param (SQL_STRING (obj->priv->stmt), id, param); +} + +/** + * db_model_request_main_table: + * @obj: a #DbModel + * @table: the name of the new main table of @model + * + * Requests the main table of @model. The main table is the only table on a + * #DbModel whose rows can be deleted or inserted. + **/ +void db_model_request_main_table (DbModel * obj, const gchar * table) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + + g_free (obj->priv->user_main_table); + obj->priv->user_main_table = g_strdup (table); + db_model_calculate_update_flags (obj); +} + +/** + * db_model_get_main_table: + * @obj: a #DbModel + * + * Returns the string with the name of the main table of @obj. The main table + * is the only table on a #DbModel whose rows can be deleted or inserted. By + * default the main table is set to the table the first column field belongs. + * To override this behaviour, use @db_model_request_main_table. + * + * Note that the requested main table may be different fron the actual main + * table used by the model + * + * Return value: the name of the main table of @obj + **/ +const gchar * db_model_get_main_table (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + return obj->priv->main_table; +} + +/** + * db_model_get_stmt: + * @obj: a #DbModel + * + * Returns the #SqlStmt which queries to the database about the data of @obj. + * + * Return value: (transfer none): the #SqlStmt property of @obj + **/ +const SqlStmt * db_model_get_stmt (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + + return obj->priv->stmt; +} + +/** + * db_model_set_stmt: + * @obj: a #DbModel + * @stmt: the #SqlStmt + * + * Sets the "stmt" property of the model. + **/ +void db_model_set_stmt (DbModel * obj, SqlStmt * stmt) +{ + if (!stmt) + return; + + g_return_if_fail (!obj->priv->stmt); + g_return_if_fail (SQL_IS_STRING (stmt) || SQL_IS_SELECT (stmt)); + + obj->priv->stmt = g_object_ref_sink (stmt); + g_signal_connect (stmt, "changed", G_CALLBACK (db_model_on_stmt_changed), obj); + db_model_on_stmt_changed (stmt, obj); +} + +/** + * db_model_set_sql: + * @obj: a #DbModel + * @sql: a value for the "sql property + * + * Sets the "sql" property to @sql. + **/ +void db_model_set_sql (DbModel * obj, const gchar * sql) +{ + SqlString * string; + + if (!sql) + return; + + g_return_if_fail (DB_IS_MODEL (obj)); + + g_free (obj->priv->sql); + obj->priv->sql = g_strdup (sql); + + if (obj->priv->use_file) + string = db_conn_create_stmt_from_file (obj->priv->conn, sql); + else + string = sql_string_new (sql); + + db_model_set_stmt (obj, SQL_STMT (string)); +} + +/** + * db_model_get: + * @obj: a #DbModel + * @iter: a #DbIter + * @...: (out callee-allocates): pairs of column number and value return + * locations, terminated by -1 + * + * Gets the values on the specified columns and sets the return locations + * pointing to these values. The column numbers must be integers, while the + * return locations must be of the same type of the value being returned. + * + * Returned values of type G_TYPE_OBJECT have to be unreferenced, values of + * type G_TYPE_STRING or G_TYPE_BOXED have to be freed. Other values are passed + * by value. + **/ +void db_model_get (DbModel * obj, DbIter * iter, ...) +{ + va_list va; + gint column; + GValue * val; + + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (obj->priv->result); + g_return_if_fail (iter != NULL); + + va_start (va, iter); + + while ((column = (va_arg (va, gint))) >= 0 && column < obj->priv->result->ncols) + { + val = DB_ROW_FIELD (iter->data, column); + + if (gvn_value_is_null (val)) + switch (gvn_param_spec_get_gtype (obj->priv->column[column].spec)) + { + case G_TYPE_CHAR: + *va_arg (va, gchar*) = 0; + break; + case G_TYPE_INT: + *va_arg (va, gint*) = 0; + break; + case G_TYPE_LONG: + *va_arg (va, glong*) = 0; + break; + case G_TYPE_FLOAT: + *va_arg (va, gfloat*) = 0; + break; + case G_TYPE_DOUBLE: + *va_arg (va, gdouble*) = 0; + break; + default: // Objects or chararrays. + *va_arg (va, gpointer*) = NULL; + } + else + gvn_value_get_valist (val, va); + } + + va_end (va); + g_return_if_fail (column == -1); +} + +/** + * db_model_set: + * @obj: a #DbModel + * @iter: a #DbIter + * @...: pairs of column number and value, terminated by -1 + * + * Sets the values on the specified columns. The column numbers must be + * integers, while the values must be of the same type of the value being set. + * + * The value will be referenced by the model if it is a G_TYPE_OBJECT, and it + * will be copied if it is a G_TYPE_STRING or G_TYPE_BOXED. + **/ +void db_model_set (DbModel * obj, DbIter * iter, ...) +{ + gint column; + gpointer content; + GValue val = {0}; + va_list va; + + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (obj->priv->result); + g_return_if_fail (iter != NULL); + + va_start (va, iter); + + while ((column = (va_arg (va, gint))) >= 0 && column < obj->priv->result->ncols) + { + content = va_arg (va, gpointer); + gvn_value_new_with_content (&val, + gvn_param_spec_get_gtype (obj->priv->column[column].spec), content); + gvn_value_copy (&val, DB_ROW_FIELD (iter->data, column)); + g_value_unset (&val); + } + + va_end (va); + g_return_if_fail (column == -1); +} + +/** + * db_model_get_value: + * @obj: a #DbModel + * @iter: a #DbIter pointing to a row of @obj + * @col: the number of the field to get the value + * @err: (out) (allow-none): a #GError or %NULL to ignore errors + * + * Gets a value from @obj pointed to by @iter and @col and puts it in @err. + * + * Return value: the value pointed to by @iter and @col or %NULL in case of error + **/ +const GValue * db_model_get_value (DbModel * obj, DbIter * iter, gint col, GError ** err) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), NULL); + g_return_val_if_fail (obj->priv->data && obj->priv->result, NULL); + g_return_val_if_fail (VALID_ITER (iter, obj), NULL); + + if (MODEL_NOT_READY (obj)) + { + g_set_error (err + ,DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_NOT_READY + ,"The model is not ready"); + return NULL; + } + + if (0 > col || col >= obj->priv->result->ncols) + { + g_set_error (err + ,DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_OUT_OF_RANGE + ,"Column out of range"); + return NULL; + } + + return DB_ROW_FIELD (iter->data, col); +} + +/** + * db_model_set_value: + * @obj: a #DbModel + * @iter: a #DbIter pointing to the row to be set + * @col: the column of the field to be set + * @value: new value for the field pointed to by @iter and @col + * @err: (out) (allow-none): a #GError or %NULL to ignore errors + * + * Sets the value of a single field to @value on @obj as well as on the database. + * If the database update fails, the model is not updated and err is set + * if it's not %NULL. + * If @iter is pointing a new row (inserted by db_model_insert() but not yet + * commited with db_model_perform_operations()) db_model_set_value() will only set + * the value on the model. + * + * Return value: %TRUE on success, %FALSE otherwise + **/ +gboolean db_model_set_value (DbModel * obj, DbIter * iter, gint col, const GValue * value, GError ** err) +{ + DbModelPrivate * priv = obj->priv; + gboolean ret = FALSE; + + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + g_return_val_if_fail (VALID_ITER (iter, obj), FALSE); + + if (MODEL_NOT_READY (obj) || !priv->result) + { + g_set_error (err, DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_NOT_READY, "Model not ready"); + } + else + { + g_return_val_if_fail (0 <= col && col < priv->result->ncols, FALSE); + + GValue new_value = {0}; + DbRow * row = iter->data; + DbOperation * operation = g_hash_table_lookup (priv->row_ops, row); + DbModelRowOp row_op = operation ? operation->type : 0; + GvnParamSpec * spec = priv->column[col].spec; + + if (!gvn_param_spec_validate (spec, value, err)) + { + return FALSE; + } + + if (!(priv->update_flags & DB_MODEL_UPDATE) + && !(row_op & DB_MODEL_ROW_OP_INSERT)) + { + g_set_error (err, DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_NOT_UPDATABLE, "Model not updatable"); + return FALSE; + } + + if (!gvn_value_is_null (value)) + { + g_value_init (&new_value, gvn_param_spec_get_gtype (spec)); + g_value_transform (value, &new_value); + } + else + g_value_init (&new_value, GVN_TYPE_NULL); + + if (gvn_value_compare (&new_value, DB_ROW_FIELD (iter->data, col))) + { + ret = TRUE; + } + else if (!db_model_set_row_operation (obj, row, DB_MODEL_ROW_OP_UPDATE, col)) + { + g_set_error (err, DB_MODEL_LOG_DOMAIN + ,DB_MODEL_ERROR_NOT_UPDATABLE, "Row locked. " + "There are one or more operations being applied over this row."); + } + else + { + priv->updated_col = col; + priv->updated_value = g_new0 (GValue, 1); + gvn_value_copy (&new_value + ,g_value_init (priv->updated_value, G_VALUE_TYPE (&new_value))); + + g_signal_emit (obj, db_model_signal[LINE_UPDATED], 0, iter); + + if (priv->mode == DB_MODEL_MODE_ON_CHANGE + && !(row_op & DB_MODEL_ROW_OP_INSERT)) + db_model_perform_operations (obj, FALSE); + + ret = TRUE; + } + + g_value_unset (&new_value); + } + + return ret; +} + +/** + * db_model_insert: + * @obj: a #DbModel + * @iter: (out): a #DbIter that will point to the new row + * + * Inserts an empty row at the end of @obj. The values for this row must be + * set one by one using db_model_set_value() or all at once with db_model_set(). + * And then committed with db_model_perform_operations(). + **/ +gboolean db_model_insert (DbModel * obj, DbIter * iter) +{ + gint i; + DbRow * row; + DbModelPrivate * priv; + + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + priv = obj->priv; + + if (MODEL_NOT_READY (obj) || !priv->result) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, "Model not ready"); + return FALSE; + } + + if (!(priv->update_flags & DB_MODEL_INSERT)) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, "Can't insert into this Model"); + return FALSE; + } + + row = db_row_new (priv->result->ncols, priv->result->nrows); + + for (i = 0; i < row->len; i++) + { + GSList * p; + const GValue * def = NULL; + + for (p = priv->param_default; p; p = p->next) + { + DbParamDef * param_def = p->data; + + if (!g_strcmp0 (param_def->dst, priv->column[i].name)) + { + def = gvn_param_get_value (param_def->param); + g_value_init (&row->value[i], G_VALUE_TYPE (def)); + gvn_value_copy (def, &row->value[i]); + } + } + + if (!def) + g_value_init (&row->value[i], GVN_TYPE_NULL); + } + + g_ptr_array_add (priv->data, row); + iter->data = g_ptr_array_index (priv->data, (guint) priv->data->len - 1); + iter->stamp = priv->stamp; + priv->result->nrows++; + + db_model_set_row_operation (obj, iter->data, DB_MODEL_ROW_OP_INSERT, 0); + + g_signal_emit (obj, db_model_signal[LINE_INSERTED], 0, iter); + + return TRUE; +} + +/** + * db_model_delete: + * @obj: a #DbModel + * @iter: a #DbIter pointing to the row to be deleted + * + * Deletes the row pointed to by @iter on @obj, as well as on the database (if + * it was already there, i.e. it's not a new row). + * If the deletion on the database fails, then the row on @obj is not deleted + * and a log message is emitted. + **/ +void db_model_delete (DbModel * obj, DbIter * iter) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + g_return_if_fail (VALID_ITER (iter, obj)); + + if (MODEL_NOT_READY (obj)) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, "Model not ready"); + return; + } + + if (!(obj->priv->update_flags & DB_MODEL_DELETE)) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, "Can't delete from this Model"); + return; + } + + if (!db_model_set_row_operation + (obj, g_ptr_array_index (obj->priv->data, DB_ROW_POSITION (iter->data)), + DB_MODEL_ROW_OP_DELETE, 0)) + return; + + g_signal_emit (obj, db_model_signal[LINE_TOGGLED], 0, iter); + + if (obj->priv->mode == DB_MODEL_MODE_ON_CHANGE) + db_model_perform_operations (obj, FALSE); +} + +/** + * db_model_order_by: + * @obj: a #DbModel + * @col: the number of the column that will be the sort criteria + * @order: the order to sort in + * + * Sorts @obj in the order indicated by @order and using the data + * on the field @col. + **/ +void db_model_order_by (DbModel * obj, gint col, DbSortType order) +{ + DbModelPrivate * priv; + g_return_if_fail (DB_IS_MODEL (obj)); + priv = obj->priv; + g_return_if_fail (priv->result && col >= -2 && col < priv->result->ncols); + g_return_if_fail (order == DB_SORT_ASCENDING || order == DB_SORT_DESCENDING); + + gint new_order[priv->result->nrows]; + gint old_col = priv->sort_column_id; + DbRow * row_iter; + gint r_ind, i = 0; + + if ((priv->order == order && priv->sort_column_id == col) + || !priv->data + || col == DB_MODEL_UNSORTED_SORT_COLUMN_ID) + return; + + if (col == DB_MODEL_DEFAULT_SORT_COLUMN_ID + && old_col != DB_MODEL_DEFAULT_SORT_COLUMN_ID) + col = old_col; + + priv->order = order; + priv->sort_column_id = col; + g_signal_emit (obj, db_model_signal[SORT_CHANGED], 0, NULL); + + if (order == DB_SORT_DESCENDING) + g_ptr_array_sort_with_data (priv->data + ,(GCompareDataFunc) db_model_valcmp_desc + ,GINT_TO_POINTER (col)); + else + g_ptr_array_sort_with_data (priv->data + ,(GCompareDataFunc) db_model_valcmp_asc + ,GINT_TO_POINTER (col)); + + for (r_ind = 0; r_ind < priv->result->nrows; r_ind++) + { + row_iter = g_ptr_array_index (priv->data, r_ind); + new_order[i] = DB_ROW_POSITION (row_iter); + DB_ROW_POSITION (row_iter) = i++; + } + + g_signal_emit (obj, db_model_signal[LINES_REORDERED], 0, col, new_order); +} + +/** + * db_model_search: + * @obj: a #DbModel + * @col: the field to search in + * @iter: a #DbIter that will point the first found element + * @content: the value looked for or %NULL + * + * Looks for a the passed value in the field specified by @col. + * + * Return value: Returns %TRUE if the value is found and %FALSE otherwise + **/ +gboolean db_model_search (DbModel * obj, gint col, DbIter * iter, gpointer content) +{ + gboolean ret; + GValue value = {0}; + + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + g_return_val_if_fail (obj->priv->result + && 0 <= obj->priv->result->ncols + && col < obj->priv->result->ncols , FALSE); + + gvn_value_new_with_content (&value, + gvn_param_spec_get_gtype (obj->priv->column[col].spec), content); + + ret = db_model_search_value (obj, col, iter, &value); + + g_value_unset (&value); + return ret; +} + +/** + * db_model_search_value: + * @obj: a #DbModel + * @col: the field to search in + * @iter: a #DbIter that will point the first found element + * @value: the value to search for + * + * Looks for a the value pointed to by @value in the field specified by @col. + * + * Return value: Returns %TRUE if the value is found and %FALSE otherwise + **/ +gboolean db_model_search_value + (DbModel * obj, gint col, DbIter * iter, const GValue * value) +{ + gint i; + GType type; + DbRow * row; + + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + g_return_val_if_fail (G_IS_VALUE (value), FALSE); + g_return_val_if_fail (obj->priv->result + && 0 <= col + && col < obj->priv->result->ncols, FALSE); + + type = gvn_param_spec_get_gtype (obj->priv->column[col].spec); + + if (gvn_value_is_null (value) || G_VALUE_TYPE (value) == type) + for (i = 0; i < obj->priv->result->nrows; i++) + { + row = g_ptr_array_index (obj->priv->data, i); + + if (gvn_value_compare (DB_ROW_FIELD (row, col), value)) + { + iter->stamp = obj->priv->stamp; + iter->data = row; + return TRUE; + } + } + + return FALSE; +} + +/** + * db_model_get_row_operations: + * @obj: a #DbModel + * @iter: a #DbIter + * + * Returns #DbModelRowOp flags indicating the operations being applied to the + * row pointed to by @iter. If no operations are being applied on the row, it + * returns 0. + * + * Return value: the #DbModelRowOp of the row or 0 + **/ +DbModelRowOp db_model_get_row_operations (DbModel * obj, DbIter * iter) +{ + DbOperation * op; + + g_return_val_if_fail (DB_IS_MODEL (obj), 0); + g_return_val_if_fail (VALID_ITER (iter, obj), 0); + + op = g_hash_table_lookup (obj->priv->row_ops, (DbRow *) iter->data); + return op ? op->type : 0; +} + +/** + * db_model_has_pending_operations: + * @obj: a #DbModel + * + * Returns whether there are pending operations in the model. + * + * Return value: #TRUE if the model has pending operations, #FALSE otherwise + **/ +gboolean db_model_has_pending_operations (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + return g_hash_table_size (obj->priv->row_ops) > 0; +} + +/** + * db_model_perform_operations: + * @obj: a #DbModel with a new row, not yet inserted + * @retry: whether to retry the failed operations (not implemented) + * + * Commits the changes made by db_model_set_value() to a new row, inserted + * by db_model_insert(). + * Note that if this method is not called after db_model_insert(), all the + * changes made on the new row will be lost. + * If the @obj is working in the #DB_MODEL_MODE_ON_DEMAND, non-interactive, + * mode, this method will perform every actions taken and not yet submitted. + **/ +void db_model_perform_operations (DbModel * obj, gboolean retry) +{ + DbModelPrivate * priv; + gboolean render_ops = FALSE, transaction = FALSE; + gint i; + DbRow * row; + DbOperation * op_elem; + GList * l = NULL; + GSList * sl = NULL; + SqlObject * multi = NULL, + * equal_op, * val = NULL, + * insert, * set, + * select, * where = NULL; + SqlList * stmts; + DbRequest * request; + GQueue * req_ops = NULL; + + g_return_if_fail (DB_IS_MODEL (obj)); + + if (MODEL_NOT_READY (obj)) + { + g_log (g_quark_to_string (DB_MODEL_LOG_DOMAIN), G_LOG_LEVEL_WARNING, + "Model not ready"); + return; + } + + priv = obj->priv; + + if (!priv->operation->length) + return; + + if (priv->operation->length > 1) + transaction = TRUE; + + stmts = sql_list_new (SQL_TYPE_STMT); + + while ((op_elem = g_queue_pop_head (priv->operation))) + { + const GValue * def; + + op_elem->locked = TRUE; + row = op_elem->row; + + if (op_elem->type & DB_MODEL_ROW_OP_DELETE) // DELETE + { + if (op_elem->type & DB_MODEL_ROW_OP_INSERT) + { + db_model_free_operation (obj, op_elem); + op_elem = NULL; + g_signal_emit (obj, + db_model_signal[LINE_DELETED], 0, DB_ROW_POSITION (row)); + + if (!transaction) + { + g_object_unref (stmts); + stmts = NULL; + } + } + else + { + SqlObject * delete = sql_delete_new (); + sql_object_add_child (delete, "targets", + sql_table_new (priv->main_table, NULL)); + + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + + DbModelPKey * pkey = + db_model_get_primary_key (obj, priv->main_table); + + for (i = 0; i < pkey->count; i++) + { + equal_op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + + sql_object_add_child (equal_op, "operators", + sql_field_new (priv->column[pkey->index[i]].name, NULL, NULL)); + sql_object_add_child (equal_op, "operators", + sql_value_new_with_value (&row->value[pkey->index[i]])); + + sql_object_add_child (where, "operators", equal_op); + } + + db_model_pkey_free (pkey); + + sql_object_set (delete, "where", where); + + sql_list_add (stmts, delete); + render_ops = TRUE; + } + } + else if (op_elem->type & DB_MODEL_ROW_OP_INSERT) // INSERT + { + SqlObject * target = sql_table_new (priv->main_table); + + insert = g_object_new (SQL_TYPE_INSERT, "table", target, NULL); + set = g_object_new (SQL_TYPE_SET, NULL); + sql_object_add_child (insert, "values", set); + + select = sql_select_new (); + sql_object_add_child (select, "targets", target); + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + + for (i = 0; i < row->len; i++) + if (!g_strcmp0 (priv->column[i].table, priv->main_table)) + { + gboolean cont = FALSE; + SqlObject * field = NULL; + + l = sql_list_get_items (SQL_INSERT (insert)->fields); + + for (; l; l = l->next) + if (!g_strcmp0 (((SqlField*) l->data)->name, priv->column[i].name) + && ((!((SqlField*) l->data)->target) + || !g_strcmp0 (((SqlField*) l->data)->target, priv->column[i].table))) + { + cont = TRUE; + break; + } + + if (cont) + continue; + + val = sql_value_new_with_value (&row->value[i]); + + sql_object_add_child (insert, "fields", + sql_field_new (priv->column[i].name, NULL, NULL)); + sql_object_add_child (set, + "exprs", gvn_value_is_null (&row->value[i]) ? NULL : val); + + sql_object_add_child (select, "exprs", + sql_field_new (priv->column[i].name, + priv->column[i].table, NULL)); + + // Set the filter conditions + if (priv->column[i].info & DB_COLUMN_PRI_KEY) + { + gboolean unset = FALSE; + GValue value = {0}; + SqlObject * eq_value; + + equal_op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + sql_object_add_child (equal_op, "operators", field); + + def = gvn_param_spec_get_default (priv->column[i].spec); + + if (!gvn_value_is_null (&row->value[i])) + { + eq_value = val; + } + else if (def && G_IS_VALUE (def)) + { + if (G_VALUE_TYPE (def) == SQL_TYPE_FUNCTION) + eq_value = g_value_get_object (def); + else + eq_value = sql_value_new_with_value (def); + } + else + { + gvn_value_set_null (&value); + eq_value = sql_value_new_with_value (&value); + unset = TRUE; + } + + sql_object_add_child (equal_op, "operators", eq_value); + + if (unset) + g_value_unset (&value); + + sql_object_add_child (where, "operators", equal_op); + } + } + + sql_object_set (select, "where", where); + + for (sl = priv->param_default; sl; sl = sl->next) + { + gboolean cont = FALSE; + DbParamDef * param_def = sl->data; + + l = sql_list_get_items (SQL_INSERT (insert)->fields); + + for (; l; l = l->next) + if (!g_strcmp0 (((SqlField*) l->data)->name, param_def->dst)) + { + cont = TRUE; + break; + } + + if (cont) continue; + + val = sql_value_new_with_value + (gvn_param_get_value (param_def->param)); + + sql_object_add_child (insert, "fields", + sql_field_new (param_def->dst, NULL, NULL)); + sql_object_add_child (set, "exprs", val); + } + + sql_list_add (stmts, insert); + sql_list_add (stmts, select); + render_ops = TRUE; + } + else if (op_elem->type & DB_MODEL_ROW_OP_UPDATE) // UPDATE + { + // Depending on the field, generate an UPDATE or INSERT+SELECT + GSList * m; + GValue * new_value; + SqlObject * update = NULL; + GSList * prev_tables = NULL; + GSList * prev_updates = NULL; + gboolean insert_set = FALSE; + select = NULL; + + for (m = op_elem->updated; m; m = m->next) + { + DbUpdatedField * u = m->data; + i = u->column; + + new_value = DB_ROW_FIELD (row, i); + + if (!priv->column_default + || !gvn_value_is_null (u->value) + || !g_strcmp0 (priv->column[i].table, priv->main_table) + || !db_model_table_row_all_null (obj, row, i)) + { + gboolean is_added = NULL != g_slist_find_custom (prev_tables, + priv->column[i].table, (GCompareFunc) g_strcmp0); + + if (!is_added) + { + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + update = g_object_new (SQL_TYPE_UPDATE, + "where", where, NULL); + } + else + { + GSList * l; + SqlTable * t; + + for (l = prev_updates; l; l = l->next) + if (SQL_IS_UPDATE (l->data) + && SQL_IS_TABLE (t = sql_list_get_items (SQL_DML (l->data)->targets)->data) + && !g_strcmp0 (SQL_TABLE (t)->name, priv->column[i].table)) + { + update = l->data; + break; + } + } + + sql_object_add_child (update, "sets", + g_object_new (SQL_TYPE_UPDATE_SET, + "field", sql_field_new (priv->column[i].name, NULL, NULL), + "expr", sql_value_new_with_value (new_value), + NULL)); + + if (!is_added) + { + guint j; + DbModelPKey * pkey = + db_model_get_primary_key (obj, priv->column[i].table); + + sql_object_add_child (update, "targets", + sql_table_new (priv->column[i].table)); + + for (j = 0; j < pkey->count; j++) + { + gint key_col = pkey->index[j]; + GValue * primary = NULL; + GSList * n; + + if (key_col == i) + primary = u->value; + else + { + for (n = op_elem->updated; n; n = n->next) + if (((DbUpdatedField *)n->data)->column == key_col) + break; + + if (n) + primary = ((DbUpdatedField *) n->data)->value; + else + primary = DB_ROW_FIELD (row, key_col); + } + + equal_op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + + sql_object_add_child (equal_op, "operators", + sql_field_new (priv->column[key_col].name, + priv->column[key_col].table, NULL)); + + val = sql_value_new_with_value (primary); + sql_object_add_child (equal_op, "operators", val); + + if (where) + sql_object_add_child (where, "operators", equal_op); + } + + sql_object_set (update, "where", where); + + sql_list_add (stmts, update); + db_model_pkey_free (pkey); + } + + prev_tables = g_slist_prepend (prev_tables, priv->column[i].table); + prev_updates = g_slist_prepend (prev_updates, update); + } + else + { + GSList * n; + + if (!insert) + { + insert = sql_insert_new (); + select = sql_select_new (); + } + + if (!SQL_INSERT (insert)->table) + { + SqlObject * table = sql_table_new (priv->column[i].table); + sql_object_set (insert, "table", table); + sql_object_add_child (select, "targets", table); + + set = g_object_new (SQL_TYPE_SET, NULL); + sql_object_add_child (insert, "values", set); + + insert_set = TRUE; + + for (n = priv->column_default; n; n = n->next) + { + gchar * dst = ((DbColDef *) n->data)->dst; + gint col_def = ((DbColDef *) n->data)->col; + + if (!g_strcmp0 (priv->column[i].table, + priv->column[col_def].table)) + continue; + + sql_object_add_child (insert, "fields", + sql_field_new (dst, NULL, NULL)); + + sql_object_add_child (set, "exprs" + ,sql_value_new_with_value (DB_ROW_FIELD (row, col_def))); + } + } + + if (insert_set) + { + val = sql_value_new_with_value (new_value); + + sql_object_add_child (insert, "fields", + sql_field_new (priv->column[i].name, NULL, NULL)); + sql_object_add_child (set, "exprs", val); + } + } + } + + g_slist_free (prev_tables); + g_slist_free (prev_updates); + + if (insert) + { + where = sql_operation_new (SQL_OPERATION_TYPE_AND); + + for (i = 0; i < row->len; i++) + if (!g_strcmp0 (SQL_TABLE (sql_list_get_items (SQL_DML (select)->targets)->data)->name + ,priv->column[i].table)) + { + SqlObject * field = SQL_OBJECT + (sql_field_new (priv->column[i].name, NULL, NULL)); + sql_object_add_child (select, "fields", field); + + if (priv->column[i].info & DB_COLUMN_PRI_KEY) + { + def = gvn_param_spec_get_default (priv->column[i].spec); + + if (!gvn_value_is_null (&row->value[i]) + || (def && G_VALUE_TYPE (def) == SQL_TYPE_FUNCTION)) + { + equal_op = sql_operation_new (SQL_OPERATION_TYPE_EQUAL); + + sql_object_add_child (equal_op, "operators", field); + + if (gvn_value_is_null (&row->value[i])) + { + if (def && G_VALUE_TYPE (def) == SQL_TYPE_FUNCTION) + sql_object_add_child (equal_op, "operators", + g_value_get_object (def)); + } + else + sql_object_add_child (equal_op, "operators", val); + + sql_object_add_child (where, "operators", equal_op); + } + } + } + + sql_object_set (select, "where", where); + + sql_list_add (stmts, insert); + sql_list_add (stmts, select); + } + + render_ops = TRUE; + } + + if (op_elem) + { + if (!req_ops) + req_ops = g_queue_new (); + + g_queue_push_tail (req_ops, op_elem); + } + } + + if (render_ops) + { + if (multi) + { + DbModelRequest * data = g_new (DbModelRequest, 1); + data->obj = g_object_ref (obj); + data->operations = req_ops; + multi = g_object_new (SQL_TYPE_MULTI_STMT, "stmts", stmts, NULL); + + request = db_conn_query_with_stmt_async (priv->conn + ,SQL_STMT (multi) + ,(DbRequestDoneCallback) db_model_on_operations_done + ,data + ,(GDestroyNotify) db_model_request_free + ); + db_model_add_pending_request (obj, request); + } + else + { + g_queue_free (req_ops); + db_model_clean_operations (obj); + } + } +} + +/** + * db_model_refresh: + * @obj: a #DbModel + * + * Executes the SELECT query and fills @obj with the data returned by it. + **/ +void db_model_refresh (DbModel * obj) +{ + DbModelPrivate * priv; + + g_return_if_fail (DB_IS_MODEL (obj)); + priv = obj->priv; + g_return_if_fail (priv->stmt); + + db_model_clear (obj); + db_model_set_status (obj, DB_MODEL_STATUS_LOADING); + + priv->request = db_conn_query_with_stmt_async (priv->conn + ,priv->stmt + ,(DbRequestDoneCallback) db_model_on_data_ready + ,g_object_ref (obj) + ,(GDestroyNotify) g_object_unref + ); +} + +/** + * db_model_get_nrows: + * @obj: a #DbModel + * + * Returns the current number of rows on #obj. + * + * Return value: the number of rows + **/ +gint db_model_get_nrows (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), 0); + + if (obj->priv->result) + return obj->priv->result->nrows; + else + return 0; +} + +/** + * db_model_iter_is_valid: + * @iter: a #DbIter + * @model: a #DbModel + * + * Checks if @iter is a valid #DbIter pointing inside @model. + **/ +gboolean db_model_iter_is_valid (DbIter * iter, DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + return (iter && iter->data && obj->priv->stamp == iter->stamp); +} + +// GtkTreeModel implementation methods. + +/** + * db_model_get_ncols: + * @obj: a #DbModel + * + * Returns the number of columns supported by @obj. + * + * Return value: the number of columns + **/ +gint db_model_get_ncols (DbModel * obj) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), -1); + + if (obj->priv->result) + return obj->priv->result->ncols; + else + return 0; +} + +/** + * db_model_get_column_type: + * @obj: a #DbModel + * @index: the number of the column + * + * Retrieves the type of the column specified by @index. + * + * Return value: the type of the column + **/ +GType db_model_get_column_type (DbModel * obj, gint index) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), G_TYPE_INVALID); + g_return_val_if_fail (obj->priv->result + && 0 <= index && index < obj->priv->result->ncols, G_TYPE_INVALID); + + if (obj->priv->column) + if (obj->priv->column[index].spec) + return gvn_param_spec_get_gtype (obj->priv->column[index].spec); + return G_TYPE_INVALID; +} + +/** + * db_model_get_path: + * @obj: a #DbModel + * @iter: a #DbIter + * + * Returns the number of the row pointed to by @iter. + * + * Return value: the number of the row pointed to by @iter + **/ +gint db_model_get_path (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), 0); + if (MODEL_NOT_READY (obj)) return 0; + g_return_val_if_fail (VALID_ITER (iter, obj), 0); + + return DB_ROW_POSITION (iter->data); +} + +/** + * db_model_get_iter: + * @obj: a #DbModel + * @iter: (out): an unitialized #DbIter + * @path: the number of the row being accessed + * + * Sets @iter pointing to the row of @obj specified by @path. + * + * Return value: %TRUE if the position is found, %FALSE otherwise + **/ +gboolean db_model_get_iter (DbModel * obj, DbIter * iter, gint path) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + if ((0 > path + || (obj->priv->result && path >= obj->priv->result->nrows)) + || MODEL_NOT_READY (obj)) + return FALSE; + + iter->stamp = obj->priv->stamp; + iter->data = g_ptr_array_index (obj->priv->data, (guint) path); + + if (iter->data) + return TRUE; + return FALSE; +} + +/** + * db_model_get_iter_first: + * @obj: a #DbModel + * @iter: (out): an unitialized #DbIter + * + * Sets @iter pointing to the first row of @obj. + * + * Return value: %TRUE if @iter is set right + **/ +gboolean db_model_get_iter_first (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + if (MODEL_NOT_READY(obj) + || !obj->priv->result || !obj->priv->result->nrows) + return FALSE; + + if (obj->priv->data) + { + iter->stamp = obj->priv->stamp; + iter->data = g_ptr_array_index (obj->priv->data, 0); + } + else + return FALSE; + + return TRUE; +} + +/** + * db_model_iter_prev: + * @obj: a #DbModel + * @iter: (inout): a valid #DbIter + * + * Sets @iter pointing to the previous row of @obj. + * + * Return value: %TRUE if the iter has been changed to the previous row + **/ +gboolean db_model_iter_prev (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + if (MODEL_NOT_READY(obj) + || !obj->priv->result || !obj->priv->result->nrows) + return FALSE; + + g_return_val_if_fail (VALID_ITER (iter, obj), FALSE); + + if ((iter->data = g_ptr_array_index + (obj->priv->data, (guint) DB_ROW_POSITION (iter->data) - 1))) + return TRUE; + return FALSE; +} + +/** + * db_model_iter_next: + * @obj: a #DbModel + * @iter: (inout): a valid #DbIter + * + * Sets @iter pointing to the next row of @obj. + * + * Return value: %TRUE if the iter has been changed to the next row + **/ +gboolean db_model_iter_next (DbModel * obj, DbIter * iter) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + if (MODEL_NOT_READY(obj) + || !obj->priv->result || !obj->priv->result->nrows) + return FALSE; + + g_return_val_if_fail (VALID_ITER (iter, obj), FALSE); + + gint pos = DB_ROW_POSITION (iter->data); + + if (pos < obj->priv->result->nrows-1) + { + iter->data = g_ptr_array_index (obj->priv->data, (guint) pos + 1); + return TRUE; + } + + iter->stamp = (iter->stamp)? 0 : 1; + iter->data = NULL; + return FALSE; +} + +// GtkTreeSortable implementation methods + +/** + * db_model_get_sort_column_id: + * @obj: a #DbModel + * @sort_column_id: (out): an integer + * @order: (out): a @DbSortType + * + * Fills in @sort_column_id and @order with the current sort + * column and the order. See #GtkTreeSortable. + * + * Return value: %TRUE if the sort column is not one of the special sort column + * ids + **/ +gboolean db_model_get_sort_column_id (DbModel * obj, gint * sort_column_id, + DbSortType * order) +{ + g_return_val_if_fail (DB_IS_MODEL (obj), FALSE); + + if (sort_column_id) + * sort_column_id = obj->priv->sort_column_id; + + if (order) + * order = obj->priv->order; + + if (obj->priv->sort_column_id == DB_MODEL_DEFAULT_SORT_COLUMN_ID + || obj->priv->sort_column_id == DB_MODEL_UNSORTED_SORT_COLUMN_ID) + return FALSE; + else + return TRUE; +} + +/** + * db_model_set_sort_column_id: + * @obj: a #DbModel + * @sort_column_id: the column to sort by + * @order: the order in which the sort will be done + * + * Sets @sort_column_id to be the current sort column. @obj will resort itself + * to reflect this change. See #GtkTreeSortable. + **/ +void db_model_set_sort_column_id (DbModel * obj, gint sort_column_id, + DbSortType order) +{ + g_return_if_fail (DB_IS_MODEL (obj)); + db_model_order_by (obj, sort_column_id, order); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +typedef enum +{ + PROP_CONN = 1 + ,PROP_STMT + ,PROP_SQL + ,PROP_USE_FILE + ,PROP_MAIN_TABLE + ,PROP_UPDATE_FLAGS + ,PROP_RESULT_POS +} +DbModelProp; + +static void db_model_set_property (DbModel * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + db_model_set_conn (obj, g_value_get_object (value)); + break; + case PROP_STMT: + db_model_set_stmt (obj, g_value_get_object (value)); + break; + case PROP_SQL: + db_model_set_sql (obj, g_value_get_string (value)); + break; + case PROP_USE_FILE: + obj->priv->use_file = g_value_get_boolean (value); + break; + case PROP_MAIN_TABLE: + db_model_request_main_table (obj, g_value_get_string (value)); + break; + case PROP_UPDATE_FLAGS: + db_model_request_update_flags (obj, g_value_get_flags (value)); + break; + case PROP_RESULT_POS: + obj->priv->result_pos = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void db_model_get_property (DbModel * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + g_value_set_object (value, obj->priv->conn); + break; + case PROP_STMT: + g_value_set_object (value, obj->priv->stmt); + break; + case PROP_SQL: + g_value_set_string (value, obj->priv->sql); + break; + case PROP_USE_FILE: + g_value_set_boolean (value, obj->priv->use_file); + break; + case PROP_MAIN_TABLE: + g_value_set_string (value, obj->priv->main_table); + break; + case PROP_UPDATE_FLAGS: + g_value_set_flags (value, obj->priv->update_flags); + break; + case PROP_RESULT_POS: + g_value_set_uint (value, obj->priv->result_pos); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void db_model_init (DbModel *obj) +{ + obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, DB_TYPE_MODEL, DbModelPrivate); + + obj->priv->conn = NULL; + obj->priv->stmt = NULL; + obj->priv->use_file = FALSE; + obj->priv->sql = NULL; + obj->priv->main_table = NULL; + obj->priv->update_flags = 0; + obj->priv->user_main_table = NULL; + obj->priv->user_update_flags = DB_MODEL_ALL; + obj->priv->request = NULL; + obj->priv->status = DB_MODEL_STATUS_CLEAN; + obj->priv->mode = DB_MODEL_MODE_ON_CHANGE; + obj->priv->result = NULL; + obj->priv->result_pos = 0; + obj->priv->data = NULL; + obj->priv->column = NULL; + obj->priv->operation = g_queue_new (); + obj->priv->row_ops = g_hash_table_new (g_direct_hash, g_direct_equal); + obj->priv->column_index = NULL; + obj->priv->join = NULL; + obj->priv->column_default = NULL; + obj->priv->param_default = NULL; + obj->priv->pending_request = NULL; + + obj->priv->stamp = g_random_int (); + + obj->priv->fresh = TRUE; + obj->priv->sort_column_id = DB_MODEL_UNSORTED_SORT_COLUMN_ID; + obj->priv->default_sort_data = NULL; + obj->priv->default_sort_func = NULL; + obj->priv->default_sort_destroy = NULL; +} + +static void db_model_finalize (DbModel * obj) +{ + GSList * n; + GObjectClass * parent; + parent = g_type_class_peek_parent (DB_MODEL_GET_CLASS (obj)); + + db_model_clear (obj); + + g_clear_object (&obj->priv->conn); + g_clear_object (&obj->priv->stmt); + + if (obj->priv->join) + g_slist_free_full (obj->priv->join, (GDestroyNotify) db_join_free); + + g_hash_table_destroy (obj->priv->row_ops); + g_queue_free (obj->priv->operation); + + g_free (obj->priv->sql); + g_free (obj->priv->main_table); + g_free (obj->priv->user_main_table); + + if (obj->priv->column_index) + g_hash_table_destroy (obj->priv->column_index); + + for (n = obj->priv->column_default; n; n = n->next) + { + g_free (((DbColDef *) n->data)->dst); + g_free (n->data); + } + + g_slist_free (obj->priv->column_default); + + for (n = obj->priv->param_default; n; n = n->next) + { + g_object_unref (((DbParamDef *) n->data)->param); + g_free (((DbParamDef *) n->data)->dst); + g_free (n->data); + } + + g_slist_free (obj->priv->param_default); + + parent->finalize (G_OBJECT (obj)); +} + +static void db_model_class_init (DbModelClass *k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->set_property = (GObjectSetPropertyFunc) db_model_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_model_get_property; + klass->finalize = (GObjectFinalizeFunc) db_model_finalize; + + g_type_class_add_private (klass, sizeof (DbModelPrivate)); + + /** + * DbModel::status-changed: + * @model: the object which received the signal + * @status: the current status of @model + * + * This signal is emitted every time the status of @model changes. + */ + db_model_signal[STATUS_CHANGED] = g_signal_new ("status-changed" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__INT + ,G_TYPE_NONE, 1, G_TYPE_INT + ); + + /** + * DbModel::line-inserted: + * @model: the object which received the signal + * @iter: a #DbIter + * + * This signal is emitted when a new row is inserted into @model. The inserted + * row is pointed to by @iter. + */ + db_model_signal[LINE_INSERTED] = g_signal_new ("line-inserted" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__BOXED + ,G_TYPE_NONE, 1, DB_TYPE_ITER + ); + + /** + * DbModel::line-deleted: + * @model: the object which received the signal + * @position: the position of the deleted line + * + * Every time a row of @model is deleted, this signal is emitted. + */ + db_model_signal[LINE_DELETED] = g_signal_new_class_handler ("line-deleted" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_LAST, (GCallback) db_model_on_line_deleted + ,NULL, NULL, g_cclosure_marshal_VOID__INT + ,G_TYPE_NONE, 1, G_TYPE_INT + ); + + /** + * DbModel::line-toggled: + * @model: the instance that received the signal + * @iter: a #DbIter + * + * Emitted to tell that the line is going to be deleted, + * but the operation is still. + */ + db_model_signal[LINE_TOGGLED] = g_signal_new ("line-toggled" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0 ,NULL, NULL + ,g_cclosure_marshal_VOID__BOXED + ,G_TYPE_NONE, 1, DB_TYPE_ITER + ); + + /** + * DbModel::line-updated: + * @model: the object which received the signal + * @iter: a #DbIter + * + * This signal is emitted when any value in a row of @model is set. + */ + db_model_signal[LINE_UPDATED] = g_signal_new_class_handler ("line-updated" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_LAST, (GCallback) db_model_on_line_updated + ,NULL, NULL, g_cclosure_marshal_VOID__BOXED + ,G_TYPE_NONE, 1, DB_TYPE_ITER + ); + + /** + * DbModel::lines-reordered: + * @model: the object which received the signal + * @col: the sort column + * @new_order: (array) (element-type gint): an array mapped with the new + * positions over the old ones + * + * This signal is emitted by @model every time its rows are reordered. + * + * The @new_order array is an array of which the positions indexed after the + * old order of the elements of @model, contain the new position of these + * elements in the reordered @model. + */ + db_model_signal[LINES_REORDERED] = g_signal_new ("lines-reordered" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__UINT_POINTER + ,G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER + ); + + /** + * DbModel::sort-changed: + * @model: the object which received the signal + * + * This signal is emitted when a new column is selected as sort criteria for + * @model. + */ + db_model_signal[SORT_CHANGED] = g_signal_new ("sort-changed" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__VOID + ,G_TYPE_NONE, 0 + ); + + /** + * DbModel::operations-done: + * @model: the object which received the signal + * @success: whether the operation has failed or succeded + * + * When an operation on the model is performed on the DB, this signal + * is emitted. + */ + db_model_signal[OPERATIONS_DONE] = g_signal_new ("operations-done" + ,DB_TYPE_MODEL, G_SIGNAL_RUN_FIRST, 0, NULL, NULL + ,g_cclosure_marshal_VOID__VOID + ,G_TYPE_NONE, 0 + ); + + g_object_class_install_property (klass, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The DbConn that manages the connection to the database") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_STMT, + g_param_spec_object ("stmt" + ,_("Statement") + ,_("The statement which retrieves the data") + ,SQL_TYPE_STMT + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("Depending on the \"use-file\" property this will " + "be the path to a file with queries for the " + "model or a SQL string") + ,NULL + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_USE_FILE, + g_param_spec_boolean ("use-file" + ,_("Use file") + ,_("If this is set to TRUE, the \"sql\" property will " + "hold the name of a file containing a query, if " + "set to FALSE, \"sql\" is used as an SQL string") + ,FALSE + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_MAIN_TABLE, + g_param_spec_string ("main-table" + ,_("Main Table") + ,_("The main table of the model") + ,NULL + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_UPDATE_FLAGS, + g_param_spec_flags ("update-flags" + ,_("Update flags") + ,_("The flags that indicate how a model can be modified") + ,DB_TYPE_MODEL_UPDATE_FLAGS + ,DB_MODEL_ALL + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (klass, PROP_RESULT_POS, + g_param_spec_uint ("result-pos" + ,_("Result position") + ,_("The position where the query that will fill the " + "model will be placed in a multi-query") + ,0 + ,G_MAXUINT32 + ,0 + ,G_PARAM_READWRITE + )); +} + +GType db_model_update_flags_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GFlagsValue values[] = + { + {DB_MODEL_INSERT, "DB_MODEL_INSERT", "insert"}, + {DB_MODEL_DELETE, "DB_MODEL_DELETE", "delete"}, + {DB_MODEL_UPDATE, "DB_MODEL_UPDATE", "update"}, + {0, NULL, NULL} + }; + + type = g_flags_register_static + (g_intern_static_string ("DbModelUpdateFlags"), values); + } + + return type; +} diff --git a/db/db-model.h b/db/db-model.h new file mode 100644 index 0000000..26385ee --- /dev/null +++ b/db/db-model.h @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This file is part of Hedera. + * + * Hedera is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_MODEL_H +#define DB_MODEL_H + +#include +#include "db-conn.h" +#include "db-iter.h" + +#define DB_MODEL_LOG_DOMAIN (g_quark_from_string ("DbModel")) + +#define DB_TYPE_MODEL (db_model_get_type()) +#define DB_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_MODEL, DbModel)) +#define DB_IS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_MODEL)) +#define DB_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_MODEL, DbModelClass)) +#define DB_IS_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_MODEL)) +#define DB_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_MODEL, DbModelClass)) + +#define DB_TYPE_MODEL_UPDATE_FLAGS (db_model_update_flags_get_type()) + +typedef struct _DbModel DbModel; +typedef struct _DbModelClass DbModelClass; +typedef struct _DbModelPrivate DbModelPrivate; + +/** + * DbModelRowOp: + * @DB_MODEL_ROW_OP_INSERT: an INSERT operation + * @DB_MODEL_ROW_OP_DELETE: a DELETE operation + * @DB_MODEL_ROW_OP_UPDATE: an UPDATE operation + * + * Flags stating the changes made over a row. + * + * If a DELETE is taking place on a row at the same time of another operation, + * the changes won't be performed and the row will be deleted. Every INSERT + * operation must have an UPDATE operation to add data, otherwise all data will + * get filled with the values set by default by the DB. Each UPDATE operation + * will merge with the previous ones and substitute the values already set by + * these. + **/ +typedef enum +{ + DB_MODEL_ROW_OP_INSERT = 1 << 0 + ,DB_MODEL_ROW_OP_DELETE = 1 << 1 + ,DB_MODEL_ROW_OP_UPDATE = 1 << 2 +} +DbModelRowOp; + +/** + * DbModelError: + * @DB_MODEL_ERROR_NOT_READY: the model data is not completely loaded yet + * @DB_MODEL_ERROR_NOT_UPDATABLE: a changed model column cannot be updated + * @DB_MODEL_ERROR_OUT_OF_RANGE: an index is out of the column range + * + * Error codes of a #DbModel. + **/ +typedef enum +{ + DB_MODEL_ERROR_NOT_READY + ,DB_MODEL_ERROR_NOT_UPDATABLE + ,DB_MODEL_ERROR_OUT_OF_RANGE +} +DbModelError; + +/** + * DbModelUpdateFlags: + * @DB_MODEL_INSERT: rows can be inserted into the model + * @DB_MODEL_DELETE: it's possible to delete rows from the model + * @DB_MODEL_UPDATE: the values of the model can be changed + * + * Indicates which operations are allowed for the user in a #DbModel. + **/ +typedef enum +{ + DB_MODEL_INSERT = 1 << 0 + ,DB_MODEL_DELETE = 1 << 1 + ,DB_MODEL_UPDATE = 1 << 2 + /* */ + ,DB_MODEL_ALL = DB_MODEL_INSERT | DB_MODEL_DELETE | DB_MODEL_UPDATE +} +DbModelUpdateFlags; + +/** + * DbModelStatus: + * @DB_MODEL_STATUS_ERROR: there has been an error while executing the main + * statement of a #DbModel + * @DB_MODEL_STATUS_LOADING: the statement is being executed + * @DB_MODEL_STATUS_READY: the statement execution is over and the data is + * available on the #DbModel + * @DB_MODEL_STATUS_CLEAN: the #DbModel is created but the statement has not + * been executed yet + * + * These constants indicate the status of the model. This status depends on the + * execution of the stamtement (@stmt property)of a #DbModel. They are returned + * by db_model_get_status(). + **/ +typedef enum +{ + DB_MODEL_STATUS_ERROR + ,DB_MODEL_STATUS_LOADING + ,DB_MODEL_STATUS_READY + ,DB_MODEL_STATUS_CLEAN +} +DbModelStatus; + +/** + * DbModelMode: + * @DB_MODEL_MODE_ON_CHANGE: every change made in the model will be inmediatelly + * sent to the database + * @DB_MODEL_MODE_ON_DEMAND: nothing will be sent to the database since demanded + * by calling db_model_perform_operations() + * + * The working mode of a #DbModel, depending on which the changes made on it + * will be sent to the database or will be made only locally. + * The same methods are used to modify the model in both modes + * (db_model_set_value(), db_model_insert() and db_model_delete()). + **/ +typedef enum +{ + DB_MODEL_MODE_ON_CHANGE + ,DB_MODEL_MODE_ON_DEMAND +} +DbModelMode; + +/** + * DbSortType: + * @DB_SORT_ASCENDING: Ascending sort order + * @DB_SORT_DESCENDING: Descending sort order + * + * Determines the direction of a sort. + **/ +typedef enum +{ + DB_SORT_ASCENDING + ,DB_SORT_DESCENDING +} +DbSortType; + +/** + * DbIterCompareFunc: + * @model: a #DbModel + * @a: a #DbIter + * @b: a #DbIter + * @user_data: (closure): user-provided data to use while comparing two #DbIter + * + * Prototype of a function used to search some in a model using + * #DbIter to iterate through it. + **/ +typedef gint (*DbIterCompareFunc) (DbModel * model + ,DbIter * a + ,DbIter * b + ,gpointer user_data); + +struct _DbModel +{ + GObject parent; + DbModelPrivate * priv; +}; + +struct _DbModelClass +{ + /* */ + GObjectClass parent; +}; + +GType db_model_get_type (); +GType db_model_update_flags_get_type () G_GNUC_CONST; + +DbModel * db_model_new (DbConn * conn, SqlStmt * stmt); +DbModel * db_model_new_with_sql (DbConn * conn, const gchar * sql); +DbModel * db_model_new_with_file (DbConn * conn, const gchar * file); +DbConn * db_model_get_conn (DbModel * obj); +void db_model_set_conn (DbModel * obj, DbConn * conn); +const GvnParamSpec * db_model_get_spec (DbModel * obj, gint col); +const gchar * db_model_get_column_name (DbModel * obj, gint col); +gint db_model_get_column_index (DbModel * obj, const gchar * name); +void db_model_refresh (DbModel * obj); +const SqlStmt * db_model_get_stmt (DbModel * obj); +void db_model_set_stmt (DbModel * obj, SqlStmt * stmt); +void db_model_add_pre_stmt (DbModel * obj, SqlStmt * stmt); +void db_model_add_post_stmt (DbModel * obj, SqlStmt * stmt); +DbModelStatus db_model_get_status (DbModel * obj); +const gchar * db_model_get_main_table (DbModel * obj); +void db_model_request_main_table (DbModel * obj, const gchar * table); +DbModelUpdateFlags db_model_get_update_flags (DbModel * obj); +void db_model_request_update_flags (DbModel * obj + ,DbModelUpdateFlags flags); +void db_model_unset_update_flags (DbModel * obj + ,DbModelUpdateFlags flags); +void db_model_set_mode (DbModel * obj, DbModelMode mode); +DbModelMode db_model_get_mode (DbModel * obj); +void db_model_toggle_mode (DbModel * obj); +gboolean db_model_get_last (DbModel * obj, DbIter * iter); +void db_model_get (DbModel * obj + ,DbIter * iter + ,...); +void db_model_set (DbModel * obj + ,DbIter * iter + ,...); +const GValue * db_model_get_value (DbModel * obj, + DbIter * iter, + gint col, + GError ** err); +gboolean db_model_set_value (DbModel * obj, + DbIter * iter, + gint col, + const GValue * value, + GError ** err); +gboolean db_model_insert (DbModel * obj, DbIter * iter); +void db_model_delete (DbModel * obj, DbIter * iter); +DbModelRowOp db_model_get_row_operations (DbModel * obj, DbIter * iter); +gboolean db_model_has_pending_operations (DbModel * obj); +void db_model_perform_operations (DbModel * obj, gboolean retry); +void db_model_reverse_operations (DbModel * obj); +void db_model_order_by (DbModel * obj, + gint col, + DbSortType order); +gboolean db_model_search (DbModel * obj, + gint col, + DbIter * iter, + gpointer content); +gboolean db_model_search_value (DbModel * obj, + gint col, + DbIter * iter, + const GValue * value); +gint db_model_get_nrows (DbModel * obj); +gboolean db_model_iter_is_valid (DbIter * iter + ,DbModel * model); +void db_model_add_join_columns (DbModel * obj + ,const gchar * left + ,const gchar * right); +void db_model_set_default_value_from_column (DbModel * obj + ,const gchar * dst_field + ,gint src_column); +void db_model_set_default_value_from_param (DbModel * obj + ,const gchar * dst_field + ,GvnParam * param + ,const gchar * id); +void db_model_add_param (DbModel * obj + ,const gchar * id + ,GvnParam * param); + +//GtkTreeModel-like methods + +gint db_model_get_ncols (DbModel * obj); +GType db_model_get_column_type (DbModel * obj, gint index); +gboolean db_model_get_iter_first (DbModel * obj, DbIter * iter); +gboolean db_model_get_iter (DbModel * obj, + DbIter * iter, + gint path); +gint db_model_get_path (DbModel * obj, DbIter * iter); +gboolean db_model_iter_next (DbModel * obj, DbIter * iter); +gboolean db_model_iter_prev (DbModel * obj, DbIter * iter); +void db_model_ref_node (DbModel * obj, DbIter * iter); +void db_model_unref_node (DbModel * obj, DbIter * iter); + +//GtkTreeSortable-like methods + +gboolean db_model_get_sort_column_id (DbModel * obj, + gint * sort_column_id, + DbSortType * order); +void db_model_set_sort_column_id (DbModel * obj, + gint sort_column_id, + DbSortType order); +void db_model_set_sort_func (DbModel * obj, + gint sort_column_id, + DbIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +void db_model_set_default_sort_func (DbModel * obj, + DbIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +gboolean db_model_has_default_sort_func (DbModel * obj); + +#endif diff --git a/db/db-param.c b/db/db-param.c new file mode 100644 index 0000000..4f2230a --- /dev/null +++ b/db/db-param.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-param.h" + +G_DEFINE_TYPE (DbParam, db_param, GVN_TYPE_PARAM); + +/** + * db_param_new: + * @column_name: col name of #DbModel associated to param. + * + * Creates a new #DbParam. + * + * Return value: the new #DbParam + **/ +GvnParam * db_param_new (const gchar * column_name) +{ + return g_object_new (DB_TYPE_PARAM, "column-name", column_name, NULL); +} + +/** + * db_param_new_with_index: + * @column_index: col index of #DbModel associated to param. + * + * Creates a new #DbParam. + * + * Return value: the new #DbParam + **/ +GvnParam * db_param_new_with_index (guint column_index) +{ + return g_object_new (DB_TYPE_PARAM, "column-index", column_index, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void db_param_on_value_changed (DbParam * obj, const GValue * value, gpointer data) +{ + db_iterator_set_value (obj->iterator, obj->column_index, value, NULL); +} + +static void db_param_on_iterator_status_changed (DbIterator * iterator, gboolean ready, DbParam * obj) +{ + GvnParamClass * klass = GVN_PARAM_GET_CLASS (obj); + + if (ready) + { + if (obj->column_name) + obj->column_index = db_iterator_get_column_index (obj->iterator, obj->column_name); + + klass->set_spec (GVN_PARAM (obj), + db_iterator_get_spec (obj->iterator, obj->column_index)); + } +} + +static void db_param_on_iterator_iter_changed (DbIterator * iterator, DbParam * obj) +{ + const GValue * value; + GvnParamClass * klass = GVN_PARAM_GET_CLASS (obj); + + if (db_iterator_get_row (obj->iterator) != -1 && obj->column_index != -1) + { + klass->set_status (GVN_PARAM (obj), GVN_PARAM_STATUS_OK); + value = db_iterator_get_value (obj->iterator, obj->column_index); + } + else + { + klass->set_status (GVN_PARAM (obj), GVN_PARAM_STATUS_BUSY); + value = NULL; + } + + g_signal_handlers_block_by_func (obj, db_param_on_value_changed, NULL); + + if (!value) + { + GValue tmp_value = {0}; + g_value_init (&tmp_value, GVN_TYPE_NULL); + klass->put_value (GVN_PARAM (obj), &tmp_value); + g_value_unset (&tmp_value); + } + else + klass->put_value (GVN_PARAM (obj), value); + + g_signal_handlers_unblock_by_func (obj, db_param_on_value_changed, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * db_param_get_column_index: + * @obj: a #DbParam + * + * Gets the iterator column referenced by the param. + * + * Return value: the column index. + **/ +guint db_param_get_column_index (DbParam * obj) +{ + g_return_val_if_fail (DB_IS_PARAM (obj), 0); + + return obj->column_index; +} + +/** + * db_param_get_column_name: + * @obj: a #DbParam + * + * Gets the iterator column referenced by the param. + * + * Return value: the column name. + **/ +const gchar * db_param_get_column_name (DbParam * obj) +{ + g_return_val_if_fail (DB_IS_PARAM (obj), 0); + + return obj->column_name; +} + +/** + * db_param_set_iterator: + * @obj: a #DbParam + * @iterator: the #DbIterator + * + * Sets the iterator that updates the param value, this property can be set if + * the parameter does not have a iterator associated yet. + **/ +void db_param_set_iterator (DbParam * obj, DbIterator * iterator) +{ + g_return_if_fail (DB_IS_PARAM (obj)); + g_return_if_fail (DB_IS_ITERATOR (iterator)); + g_return_if_fail (!obj->iterator); + + obj->iterator = g_object_ref (iterator); + g_object_connect (iterator + ,"signal::iter-changed", db_param_on_iterator_iter_changed, obj + ,"signal::status-changed", db_param_on_iterator_status_changed, obj + ,NULL + ); + db_param_on_iterator_status_changed (obj->iterator, + db_iterator_is_ready (iterator), obj); + db_param_on_iterator_iter_changed (obj->iterator, obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_ITERATOR = 1 + ,PROP_COLUMN_INDEX + ,PROP_COLUMN_NAME +}; + +static void db_param_set_property (DbParam * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_ITERATOR: + db_param_set_iterator (obj, g_value_get_object (value)); + break; + case PROP_COLUMN_INDEX: + obj->column_index = g_value_get_int (value); + break; + case PROP_COLUMN_NAME: + g_free (obj->column_name); + obj->column_name = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void db_param_get_property (DbParam * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_ITERATOR: + g_value_set_object (value, obj->iterator); + break; + case PROP_COLUMN_INDEX: + g_value_set_int (value, obj->column_index); + break; + case PROP_COLUMN_NAME: + g_value_set_string (value, obj->column_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_param_init (DbParam * obj) +{ + obj->column_index = -1; + obj->column_name = NULL; + obj->iterator = NULL; + g_signal_connect (obj, "value-changed", + G_CALLBACK (db_param_on_value_changed), NULL); +} + +static void db_param_finalize (DbParam * obj) +{ + if (obj->iterator) + { + g_object_disconnect (obj->iterator + ,"any_signal", db_param_on_iterator_iter_changed, obj + ,"any_signal", db_param_on_iterator_status_changed, obj + ,NULL + ); + g_clear_object (&obj->iterator); + } + + g_signal_handlers_disconnect_by_func (obj, + db_param_on_value_changed, NULL); + g_free (obj->column_name); + G_OBJECT_CLASS (db_param_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_param_class_init (DbParamClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) db_param_finalize; + klass->set_property = (GObjectSetPropertyFunc) db_param_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_param_get_property; + + g_object_class_install_property (klass, PROP_ITERATOR, + g_param_spec_object ("iterator" + ,_("Iterator") + ,_("The iterator owner of param") + ,DB_TYPE_ITERATOR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_COLUMN_INDEX, + g_param_spec_int ("column-index" + ,_("Column index") + ,_("The referenced column index") + ,-1 ,1024 ,-1 + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_COLUMN_NAME, + g_param_spec_string ("column-name" + ,_("Column name") + ,_("The referenced column name") + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} diff --git a/db/db-param.h b/db/db-param.h new file mode 100644 index 0000000..a3cb575 --- /dev/null +++ b/db/db-param.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_PARAM_H +#define DB_PARAM_H + +#include + +#define DB_TYPE_PARAM (db_param_get_type ()) +#define DB_PARAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_PARAM, DbParam)) +#define DB_IS_PARAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_PARAM)) +#define DB_PARAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_PARAM, DbParamClass)) +#define DB_IS_PARAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_PARAM)) +#define DB_PARAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_PARAM, DbParamClass)) + +typedef struct _DbParam DbParam; +typedef struct _DbParamClass DbParamClass; + +#include "db-iterator.h" + +struct _DbParam +{ + GvnParam parent; + DbIterator * iterator; + gint column_index; + gchar * column_name; +}; + +struct _DbParamClass +{ + /* */ + GvnParamClass parent; +}; + +GType db_param_get_type (); +GvnParam * db_param_new (const gchar * column_name); +GvnParam * db_param_new_with_index (guint column_index); +guint db_param_get_column_index (DbParam * obj); +const gchar * db_param_get_column_name (DbParam * obj); +void db_param_set_iterator (DbParam * obj, DbIterator * iterator); + +#endif \ No newline at end of file diff --git a/db/db-plugin.c b/db/db-plugin.c new file mode 100644 index 0000000..1666cb0 --- /dev/null +++ b/db/db-plugin.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-plugin.h" + +/** + * SECTION: db-plugin + * @Short_description: manages a connection to a database. + * @Title: DbPlugin + * @See_also: #DbConn + * + * This class manages a connection to a database internally. This + * is accessed through the #DbConn class to internally connect, query and + * disconnect the database. + **/ +G_DEFINE_ABSTRACT_TYPE (DbPlugin, db_plugin, G_TYPE_OBJECT); + +/** + * db_plugin_close: + * @obj: a #DbPlugin + * + * Closes de current connection to the database. + **/ +void db_plugin_close (DbPlugin * obj) +{ + g_return_if_fail (DB_IS_PLUGIN (obj)); + + g_mutex_lock (&obj->mutex); + g_mutex_lock (&obj->kill_mutex); + + DB_PLUGIN_GET_CLASS (obj)->close (obj); + + g_mutex_unlock (&obj->kill_mutex); + g_mutex_unlock (&obj->mutex); +} + +/** + * db_plugin_open: + * @obj: a #DbPlugin + * @host: the hostname + * @schema: the database schema + * @user: the user name + * @pass: the password + * @err: (out) (allow-none): destination of #GError, if you want to handle it. + * + * Opens a new connection to the database. + * + * Return value: Returns %TRUE if connection was made, %FALSE otherwise. + **/ +gboolean db_plugin_open (DbPlugin * obj, const gchar * host, + const gchar * schema, const gchar * user, const gchar * pass, GError ** err) +{ + gboolean opened; + + g_return_val_if_fail (DB_IS_PLUGIN (obj), FALSE); + + g_mutex_lock (&obj->mutex); + g_mutex_lock (&obj->kill_mutex); + + DB_PLUGIN_GET_CLASS (obj)->close (obj); + + if (obj->ca) + DB_PLUGIN_GET_CLASS (obj)->set_ssl (obj, obj->ca); + + opened = DB_PLUGIN_GET_CLASS (obj)->open (obj, + host, schema, user, pass, err); + + g_mutex_unlock (&obj->kill_mutex); + g_mutex_unlock (&obj->mutex); + + return opened; +} + +/** + * db_plugin_set_ssl: + * @obj: a #DbPlugin + * @ca: path to the certificate authority file + * + * Sets the certificate authority file needed to use SSL. If the secure + * connection through SSL is not properly set, @db_plugin_open will fail. + */ +void db_plugin_set_ssl (DbPlugin * obj, const gchar * ca) +{ + g_return_if_fail (DB_IS_PLUGIN (obj)); + + g_mutex_lock (&obj->mutex); + g_free (obj->ca); + obj->ca = strdup (ca); + g_mutex_unlock (&obj->mutex); +} + +/** + * db_plugin_query: + * @obj: a #DbPlugin + * @sql: a #gchar string containing an sql query + * @err: (out) (allow-none): destination of #GError, if you want to handle it. + * + * If everything was ok (i.e. the sql query wasn't empty, it was completed + * successfully, etc.), it will return a #GList of #DbResult with the + * database response, depending on the query or queries passed. Note that if + * the input string @sql is a multi-query and any of the queries inside of it + * fails the rest of the queries will fail. + * + * Refer to #DbError for the possible errors. + * + * Return value: (transfer full) (allow-none): a #DbResultSet containing the + * results or %NULL if error + **/ +DbResultSet * db_plugin_query (DbPlugin * obj, const gchar * sql, GError ** err) +{ + DbResultSet * set; + + g_return_val_if_fail (DB_IS_PLUGIN (obj), NULL); + + g_mutex_lock (&obj->mutex); + set = DB_PLUGIN_GET_CLASS (obj)->query (obj, sql, err); + g_mutex_unlock (&obj->mutex); + + return set; +} + +/** + * db_plugin_kill_query: + * @obj: a #DbPlugin + * + * Tryes to kill the current query. + **/ +void db_plugin_kill_query (DbPlugin * obj) +{ + DbPluginClass * klass; + + g_return_if_fail (DB_IS_PLUGIN (obj)); + + klass = DB_PLUGIN_GET_CLASS (obj); + + if (klass->kill_query) + { + g_mutex_lock (&obj->kill_mutex); + klass->kill_query (obj); + g_mutex_unlock (&obj->kill_mutex); + } +} + +/** + * db_plugin_parse: + * @obj: a #DbPlugin. + * @sql: (transfer none): an SQL string. + * + * Parses a string and makes an #SqlStmt from it. + * + * Return value: (transfer none): a new #SqlStmt parsed from @sql. + **/ +SqlStmt * db_plugin_parse (DbPlugin * obj, gchar * sql) +{ + g_return_val_if_fail (DB_IS_PLUGIN (obj), NULL); + + return NULL; +} + +/** + * db_plugin_render: + * @obj: a #DbPlugin + * @object: the #GObject to render + * @err: (out) (allow-none): the return location for #GError + * + * Renders a #GObject object as a SQL string to send it in a database + * query. It takes the plugin to know the codification in wich to escape + * the data. + * + * Return value: (transfer full): the rendered string, or %NULL if error. + **/ +gchar * db_plugin_render (DbPlugin * obj, gpointer object, GError ** err) +{ + g_return_val_if_fail (DB_IS_PLUGIN (obj), NULL); + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + + return sql_render_get_string (obj->render, object, obj, err); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_plugin_init (DbPlugin *obj) +{ + obj->render = NULL; + obj->ca = NULL; + g_mutex_init (&obj->mutex); + g_mutex_init (&obj->kill_mutex); +} + +static void db_plugin_finalize (DbPlugin * obj) +{ + g_clear_object (&obj->render); + g_free (obj->ca); + g_mutex_clear (&obj->mutex); + g_mutex_clear (&obj->kill_mutex); + G_OBJECT_CLASS (db_plugin_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_plugin_class_init (DbPluginClass * k) +{ + G_OBJECT_CLASS (k)->finalize = (GObjectFinalizeFunc) db_plugin_finalize; +} diff --git a/db/db-plugin.h b/db/db-plugin.h new file mode 100644 index 0000000..7d64217 --- /dev/null +++ b/db/db-plugin.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_PLUGIN_H +#define DB_PLUGIN_H + +#include +#include "db-result-set.h" + +#define DB_TYPE_PLUGIN (db_plugin_get_type ()) +#define DB_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_PLUGIN, DbPlugin)) +#define DB_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_PLUGIN)) +#define DB_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_PLUGIN, DbPluginClass)) +#define DB_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_PLUGIN)) +#define DB_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_PLUGIN, DbPluginClass)) + +typedef struct _DbPlugin DbPlugin; +typedef struct _DbPluginClass DbPluginClass; + +typedef GType (* DbPluginGetTypeFunc) (); +typedef gboolean (* DbPluginOpenFunc) (DbPlugin * obj + ,const gchar * host + ,const gchar * schema + ,const gchar * user + ,const gchar * pass + ,GError ** err); +typedef void (* DbPluginCloseFunc) (DbPlugin * obj); +typedef void (* DbPluginSetSSL) (DbPlugin * obj + ,const gchar * ca); +typedef DbResultSet * (* DbPluginQueryFunc) (DbPlugin * obj, const gchar * sql, GError ** err); +typedef void (* DbPluginKillQueryFunc) (DbPlugin * obj); +typedef SqlStmt * (* DbPluginParseFunc) (DbPlugin * obj, const gchar * sql); + +struct _DbPlugin +{ + GObject parent; + GMutex mutex; + GMutex kill_mutex; + SqlRender * render; + gchar * ca; +}; + +struct _DbPluginClass +{ + /* */ + GObjectClass parent; + DbPluginOpenFunc open; + DbPluginCloseFunc close; + DbPluginSetSSL set_ssl; + DbPluginQueryFunc query; + DbPluginKillQueryFunc kill_query; + DbPluginParseFunc parse; +}; + +GType db_plugin_get_type (); +gboolean db_plugin_open (DbPlugin * obj + ,const gchar * host + ,const gchar * schema + ,const gchar * user + ,const gchar * pass + ,GError ** err); +void db_plugin_close (DbPlugin * obj); +void db_plugin_set_ssl (DbPlugin * obj, const gchar * ca); +DbResultSet * db_plugin_query (DbPlugin * obj, const gchar * sql, GError ** err); +void db_plugin_kill_query (DbPlugin * obj); +SqlStmt * db_plugin_parse (DbPlugin * obj, gchar * sql); +gchar * db_plugin_render (DbPlugin * obj, gpointer object, GError ** err); + +#endif \ No newline at end of file diff --git a/db/db-request.c b/db/db-request.c new file mode 100644 index 0000000..9bf13e6 --- /dev/null +++ b/db/db-request.c @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-request.h" + +/** + * SECTION: db-request + * @Short_description: a request to the database sent by the model + * @Title: DbRequest + * @See_also: #DbConn, #DbModel + * + * This class is used to send an SQL query to the database through a #DbConn, + * and to retrieve any data or metadata obtained by it in the model. + **/ +G_DEFINE_TYPE (DbRequest, db_request, G_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static gboolean db_request_idle (DbRequest * obj) +{ + if (obj->callback) + obj->callback (obj, obj->user_data); + + return FALSE; +} + +static void db_request_idle_notify (DbRequest * obj) +{ + if (obj->notify) + obj->notify (obj->user_data); + + g_object_unref (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +/** + * db_request_new: + * @conn: a #DbConn + * @sql: SQL statement to execute + * + * Creates a new request to execute the %sql query. + * + * Return value: a new #DbRequest + **/ +DbRequest * db_request_new (DbConn * conn, const gchar * sql) +{ + g_return_val_if_fail (DB_IS_CONN (conn), NULL); + + return g_object_new (DB_TYPE_REQUEST, "conn", conn, "sql", sql, NULL); +} + +/** + * db_request_new_with_stmt: + * @conn: a #DbConn + * @stmt: the #SqlStmt statement to execute. + * + * Creates a new request to execute the statement. + * + * Return value: a new #DbRequest + **/ +DbRequest * db_request_new_with_stmt (DbConn * conn, SqlStmt * stmt) +{ + g_return_val_if_fail (DB_IS_CONN (conn), NULL); + g_return_val_if_fail (SQL_IS_STMT (stmt), NULL); + + return g_object_new (DB_TYPE_REQUEST, "conn", conn, "stmt", stmt, NULL); +} + +/** + * db_request_fetch_result: + * @obj: a #DbRequest + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Obtains the first result of the query. The request must have been made to + * call this method. + * + * Return value: (transfer full) (allow-none): the #DbResult + **/ +DbResult * db_request_fetch_result (DbRequest * obj, GError ** err) +{ + DbResult * result = NULL; + + g_return_val_if_fail (DB_IS_REQUEST (obj), NULL); + + g_mutex_lock (&obj->mutex); + + if (obj->result_set) + { + result = db_result_set_take_next (obj->result_set); + } + else if (err) + { + g_propagate_error (err, obj->error); + obj->error = NULL; + } + + g_mutex_unlock (&obj->mutex); + return result; +} + +/** + * db_request_fetch_non_select: + * @obj: a #DbRequest + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Fetchs the result of a non select statement. + * + * Return value: returns 0 or the number of affected rows on success, -1 on failure + **/ +gint db_request_fetch_non_select (DbRequest * obj, GError ** err) +{ + DbResult * result; + + g_return_val_if_fail (DB_IS_REQUEST (obj), -1); + + result = db_request_fetch_result (obj, err); + + if (result) + { + gint affected_rows = result->nrows; + db_result_free (result); + return affected_rows; + } + + return -1; +} + +/** + * db_request_fetch_value: + * @obj: a #DbRequest + * @value: (out): return location for a #GValue + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Obtains the first result of the query. The request must have been made to + * call this method. + * + * Return value: %TRUE on success, %FALSE on failure + **/ +gboolean db_request_fetch_value (DbRequest * obj, GValue * value, GError ** err) +{ + DbResult * result; + + g_return_val_if_fail (DB_IS_REQUEST (obj), FALSE); + + result = db_request_fetch_result (obj, err); + + if (result) + { + DbRow * row = db_result_get_row (result, 0); + + if (row) + { + const GValue * row_value = db_row_get_value (row, 0); + + if (row_value) + { + g_value_init (value, G_VALUE_TYPE (row_value)); + g_value_copy (row_value, value); + return TRUE; + } + } + + db_result_free (result); + } + + return FALSE; +} + +/** + * db_request_exec: + * @obj: a #DbRequest + * @err: (out) (allow-none): return location for a #GError or %NULL + * + * Sends the request to the database. + **/ +gboolean db_request_exec (DbRequest * obj, GError ** err) +{ + gboolean ret; + + g_return_val_if_fail (DB_IS_REQUEST (obj), FALSE); + + ret = TRUE; + g_mutex_lock (&obj->mutex); + + switch ((gint) obj->status) + { + case DB_REQUEST_CLEAN: + case DB_REQUEST_ERROR: + { + DbResultSet * set; + GError * query_error = NULL; + + obj->status = DB_REQUEST_LOADING; + g_mutex_unlock (&obj->mutex); + + set = db_conn_exec (obj->conn, obj->sql, &query_error); + + g_mutex_lock (&obj->mutex); + + if (obj->status != DB_REQUEST_CANCELED) + { + if (set) + { + obj->result_set = set; + obj->status = DB_REQUEST_DONE; + } + else if (query_error) + { + g_clear_error (&obj->error); + obj->error = g_error_copy (query_error); + g_propagate_error (err, query_error); + obj->status = DB_REQUEST_ERROR; + ret = FALSE; + } + } + else + { + if (set) + db_result_set_free (set); + else + g_clear_error (&query_error); + } + } + } + + g_mutex_unlock (&obj->mutex); + + return ret; +} + +/** + * db_request_set_callback: + * @obj: a #DbRequest + * @callback: + * @user_data: + * @notify: + * + * Sends the request to the database. + **/ +void db_request_set_callback (DbRequest * obj, + DbRequestDoneCallback callback, gpointer user_data, GDestroyNotify notify) +{ + g_return_if_fail (DB_IS_REQUEST (obj)); + + g_mutex_lock (&obj->mutex); + + obj->callback = callback; + obj->user_data = user_data; + obj->notify = notify; + + g_mutex_unlock (&obj->mutex); +} + +/** + * db_request_complete: + * @obj: a #DbRequest + * + * Emits the ready or error signal of the object. + **/ +void db_request_complete (DbRequest * obj) +{ + g_return_if_fail (DB_IS_REQUEST (obj)); + + g_idle_add_full (G_PRIORITY_HIGH_IDLE, + (GSourceFunc) db_request_idle, g_object_ref (obj), (GDestroyNotify) db_request_idle_notify); +} + +/** + * db_request_cancel: + * @obj: a #DbRequest + * + * Attempts to cancel the request. + * + * Return value: %TRUE if request was canceled %FALSE otherwise. + **/ +void db_request_cancel (DbRequest * obj) +{ + g_return_if_fail (DB_IS_REQUEST (obj)); + + g_mutex_lock (&obj->mutex); + + if (obj->status != DB_REQUEST_CANCELED) + { + if (obj->result_set) + { + db_result_set_free (obj->result_set); + obj->result_set = NULL; + } + else + g_clear_error (&obj->error); + + obj->status = DB_REQUEST_CANCELED; + obj->error = g_error_new ( + DB_REQUEST_LOG_DOMAIN + ,DB_REQUEST_ERROR_CANCELED + ,_("The request was canceled") + ); + } + + g_mutex_unlock (&obj->mutex); +} + +/** + * db_request_get_conn: + * @obj: a #DbRequest + * + * Gets the connection used by request. + * + * Return value: (transfer full): the #DbConn + **/ +DbConn * db_request_get_conn (DbRequest * obj) +{ + g_return_val_if_fail (DB_IS_REQUEST (obj), NULL); + + return obj->conn; +} + +/** + * db_request_get_sql: + * @obj: a #DbRequest + * + * Gets the SQL statement used by #DbRequest. + * + * Return value: the sql string, must be freed with g_free() + **/ +gchar * db_request_get_sql (DbRequest * obj) +{ + g_return_val_if_fail (DB_IS_REQUEST (obj), NULL); + + return g_strdup (obj->sql); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +typedef enum +{ + PROP_CONN = 1 + ,PROP_SQL + ,PROP_STMT + ,PROP_RESULT_SET + ,PROP_ERROR +} +DbRequestProp; + +static void db_request_set_property (DbRequest * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + g_mutex_lock (&obj->mutex); + + switch (property_id) + { + case PROP_CONN: + obj->conn = g_value_dup_object (value); + break; + case PROP_SQL: + { + gchar * sql = g_value_dup_string (value); + + if (sql) + obj->sql = sql; + + break; + } + case PROP_STMT: + { + SqlStmt * stmt = g_value_get_object (value); + + if (obj->conn && stmt) + obj->sql = db_conn_render (obj->conn, stmt, NULL); + else if (!obj->conn) + g_warning ("DbRequest: Can't render stmt, conn property not set"); + + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } + + g_mutex_unlock (&obj->mutex); +} + +static void db_request_get_property (DbRequest * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + g_value_set_object (value, obj->conn); + break; + case PROP_SQL: + g_value_set_string (value, obj->sql); + break; + case PROP_RESULT_SET: + g_value_set_boxed (value, obj->result_set); + break; + case PROP_ERROR: + g_value_set_boxed (value, obj->error); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_request_init (DbRequest * obj) +{ + obj->sql = NULL; + obj->error = NULL; + obj->result_set = NULL; + obj->conn = NULL; + obj->callback = NULL; + obj->notify = NULL; + obj->status = DB_REQUEST_CLEAN; + g_mutex_init (&obj->mutex); +} + +static void db_request_finalize (DbRequest * obj) +{ + if (obj->result_set) + db_result_set_free (obj->result_set); + + g_clear_error (&obj->error); + g_clear_object (&obj->conn); + g_free (obj->sql); + g_mutex_clear (&obj->mutex); + G_OBJECT_CLASS (db_request_parent_class)->finalize (G_OBJECT (obj)); +} + +static void db_request_class_init (DbRequestClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) db_request_finalize; + klass->set_property = (GObjectSetPropertyFunc) db_request_set_property; + klass->get_property = (GObjectGetPropertyFunc) db_request_get_property; + + g_object_class_install_property (klass, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used to render and execute the query") + ,DB_TYPE_CONN + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query to execute") + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_STMT, + g_param_spec_object ("stmt" + ,_("Statement") + ,_("The statement to execute") + ,SQL_TYPE_STMT + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE + )); + g_object_class_install_property (klass, PROP_RESULT_SET, + g_param_spec_boxed ("result-set" + ,_("Result") + ,_("The result data of the query") + ,DB_TYPE_RESULT_SET + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_ERROR, + g_param_spec_boxed ("error" + ,_("Error") + ,_("The GError, if an error ocurred") + ,G_TYPE_ERROR + ,G_PARAM_READABLE + )); +} diff --git a/db/db-request.h b/db/db-request.h new file mode 100644 index 0000000..4ccbbbc --- /dev/null +++ b/db/db-request.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_REQUEST_H +#define DB_REQUEST_H + +#include +#include "db-result-set.h" + +#define DB_REQUEST_LOG_DOMAIN (g_quark_from_string ("DbRequest")) + +#define DB_TYPE_REQUEST (db_request_get_type ()) +#define DB_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_REQUEST, DbRequest)) +#define DB_IS_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_REQUEST)) +#define DB_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_REQUEST, DbRequestClass)) +#define DB_IS_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_REQUEST)) +#define DB_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_REQUEST, DbRequestClass)) + +typedef struct _DbRequest DbRequest; +typedef struct _DbRequestClass DbRequestClass; + +typedef void (*DbRequestDoneCallback) (DbRequest * obj, gpointer user_data); + +#include "db-conn.h" + +typedef enum +{ + DB_REQUEST_CLEAN + ,DB_REQUEST_LOADING + ,DB_REQUEST_DONE + ,DB_REQUEST_CANCELED + ,DB_REQUEST_ERROR +} +DbRequestStatus; + +typedef enum +{ + DB_REQUEST_ERROR_CANCELED + ,DB_REQUEST_ERROR_NO_EXECUTED +} +DbRequestError; + +struct _DbRequest +{ + GObject parent; + + /* */ + DbConn * conn; + gchar * sql; + DbResultSet * result_set; + GError * error; + DbRequestStatus status; + GMutex mutex; + DbRequestDoneCallback callback; + gpointer user_data; + GDestroyNotify notify; +}; + +struct _DbRequestClass +{ + /* */ + GObjectClass parent; +}; + +GType db_request_get_type (); +DbRequest * db_request_new (DbConn * conn, const gchar * sql); +DbRequest * db_request_new_with_stmt (DbConn * conn, SqlStmt * stmt); +DbResult * db_request_fetch_result (DbRequest * obj, GError ** err); +gint db_request_fetch_non_select (DbRequest * obj, GError ** err); +gboolean db_request_fetch_value (DbRequest * obj, GValue * value, GError ** err); +gboolean db_request_exec (DbRequest * obj, GError ** err); +void db_request_set_callback (DbRequest * obj, DbRequestDoneCallback callback, gpointer user_data, GDestroyNotify notify); +void db_request_complete (DbRequest * obj); +void db_request_cancel (DbRequest * obj); +gchar * db_request_get_sql (DbRequest * obj); + +#endif diff --git a/db/db-result-set.c b/db/db-result-set.c new file mode 100644 index 0000000..2242701 --- /dev/null +++ b/db/db-result-set.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-result-set.h" + +/** + * SECTION: db-result-set + * @Short_description: + * @Title: DbResultSet + * @See_also: #DbResult + **/ +G_DEFINE_BOXED_TYPE (DbResultSet, db_result_set, db_result_set_copy, db_result_set_free); + +/** + * db_result_set_new: + * + * Return value: the new #DbResultSet + **/ +DbResultSet * db_result_set_new () +{ + DbResultSet * obj = g_new (DbResultSet, 1); + obj->results = NULL; + return obj; +} + +/** + * db_result_set_take_nth: + * @obj: a #DbResultSet + * @n: the position of the result, counting from 0 + * + * Gets the nth result and removes it from the resultset. The user is + * responsible for releasing the result with db_result_free(). + * + * Return value: (transfer full): the #DbResult or %NULL if nth result doesn't exist + **/ +DbResult * db_result_set_take_nth (DbResultSet * obj, guint n) +{ + GSList * list; + + g_return_val_if_fail (obj, NULL); + + list = g_slist_nth (obj->results, n); + + if (list) + { + DbResult * result = list->data; + obj->results = g_slist_delete_link (obj->results, list); + return result; + } + + return NULL; +} + +/** + * db_result_set_take_next: + * @obj: a #DbResultSet + * + * Gets the next result and removes it from the resultset. The user is + * responsible for releasing the result with db_result_free(). + * + * Return value: (transfer full): the #DbResult or %NULL if no more results + **/ +DbResult * db_result_set_take_next (DbResultSet * obj) +{ + g_return_val_if_fail (obj, NULL); + + return db_result_set_take_nth (obj, 0); +} + +/** + * db_result_set_copy: + * @obj: a #DbResultSet + * + * Makes a copy of a #DbResultSet. + * + * Return value: a newly allocated #DbResultSet, with the same contents + **/ +DbResultSet * db_result_set_copy (const DbResultSet * obj) +{ + GSList * n; + DbResultSet * set; + + g_return_val_if_fail (obj, NULL); + + set = db_result_set_new (); + + for (n = obj->results; n; n = n->next) + set->results = g_slist_append (set->results, db_result_copy (n->data)); + + return set; +} + +/** + * db_result_set_free: + * @obj: a #DbResultSet + * + * Frees a #DbResultSet. + **/ +void db_result_set_free (DbResultSet * obj) +{ + GSList * n; + + g_return_if_fail (obj); + + for (n = obj->results; n; n = n->next) + db_result_free (n->data); + + g_slist_free (obj->results); + g_free (obj); +} diff --git a/db/db-result-set.h b/db/db-result-set.h new file mode 100644 index 0000000..f6c9839 --- /dev/null +++ b/db/db-result-set.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_RESULT_SET_H +#define DB_RESULT_SET_H + +#include +#include "db-result.h" + +#define DB_TYPE_RESULT_SET (db_result_set_get_type()) + +typedef struct _DbResultSet DbResultSet; + +/** + * DbResultSet: (skip) + * @results: (element-type Db.Result): + * + * Has the information of a result. + **/ +struct _DbResultSet +{ + GSList * results; +}; + +GType db_result_set_get_type (); +DbResultSet * db_result_set_new (); +DbResult * db_result_set_take_nth (DbResultSet * obj, guint n); +DbResult * db_result_set_take_next (DbResultSet * obj); +DbResultSet * db_result_set_copy (const DbResultSet * obj); +void db_result_set_free (DbResultSet * obj); + +#endif \ No newline at end of file diff --git a/db/db-result.c b/db/db-result.c new file mode 100644 index 0000000..68ff801 --- /dev/null +++ b/db/db-result.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-result.h" + +/** + * SECTION: db-result + * @Short_description: + * @Title: DbResult + * @See_also: #DbResultSet + **/ +G_DEFINE_BOXED_TYPE (DbResult, db_result, db_result_copy, db_result_free); + +DbResult * db_result_new () +{ + DbResult * obj = g_new (DbResult, 1); + return obj; +} + +DbRow * db_result_get_row (const DbResult * obj, gint row_index) +{ + g_return_val_if_fail (obj, NULL); + g_return_val_if_fail (row_index >= 0 && row_index < obj->nrows, NULL); + + return g_ptr_array_index (obj->data, row_index); +} + +void db_result_remove_row (DbResult * obj, gint row_index) +{ + g_return_if_fail (obj); + g_return_if_fail (row_index >= 0 && row_index < obj->nrows); +} + +/** + * db_result_copy: + * @obj: a #DbResult + * + * Makes a copy of a #DbResult. + * + * Return value: a newly allocated #DbResult, with the same contents + **/ +DbResult * db_result_copy (const DbResult * obj) +{ + gint n; + DbResult * result; + + g_return_val_if_fail (obj, NULL); + + result = g_new (DbResult, 1); + result->nrows = obj->nrows; + result->ncols = obj->ncols; + result->column = g_new (DbColumn, obj->ncols); + result->data = g_ptr_array_new_full (obj->nrows + 15, + (GDestroyNotify) db_row_free); + + for (n = 0; n < obj->ncols; n++) + { + result->column[n].info = obj->column[n].info; + result->column[n].spec = gvn_param_spec_copy (obj->column[n].spec); + result->column[n].table = g_strdup (obj->column[n].table); + result->column[n].name = g_strdup (obj->column[n].name); + result->column[n].display = g_strdup (obj->column[n].display); + } + + for (n = 0; n < obj->nrows; n++) + g_ptr_array_add (result->data, + db_row_copy (g_ptr_array_index (obj->data, n))); + + return result; +} + +/** + * db_result_free: + * @obj: a #DbResult + * + * Frees a #DbResult. + **/ +void db_result_free (DbResult * obj) +{ + g_return_if_fail (obj); + + if (obj->data) + g_ptr_array_unref (obj->data); + + if (obj->column) + { + int i; + + for (i = 0; i < obj->ncols; i++) + { + DbColumn col = obj->column[i]; + gvn_param_spec_free (col.spec); + g_free (col.name); + g_free (col.display); + g_free (col.table); + } + + g_free (obj->column); + } + + g_free (obj); +} \ No newline at end of file diff --git a/db/db-result.h b/db/db-result.h new file mode 100644 index 0000000..08643d5 --- /dev/null +++ b/db/db-result.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_RESULT_H +#define DB_RESULT_H + +#include +#include "db-row.h" + +#define DB_TYPE_RESULT (db_result_get_type()) + +typedef struct _DbResult DbResult; +typedef struct _DbColumn DbColumn; + +/** + * DbResult: + * @nrows: Number of rows. + * @ncols: Number of columns. + * @data: Has a #GList. + * @column: Has a #DbColumn. + * + * Has the information of a row. + **/ +struct _DbResult +{ + gint nrows; + gint ncols; + GPtrArray * data; + DbColumn * column; +}; + +/** + * DbColumnInfo: + * @DB_COLUMN_PRI_KEY: Attribute primary Key. + * @DB_COLUMN_UNIQUE: Attribute unique. + * + * Saves the information about if it is primary key or unique. + **/ +typedef enum +{ + DB_COLUMN_PRI_KEY = 1 << 0 + ,DB_COLUMN_UNIQUE = 1 << 1 +} +DbColumnInfo; + +/** + * DbColumn: + * @info: A #DbColumnInfo + * @spec: A #GvnParamSpec + * @table: Name of the table + * @name: Name of the column + * @display: The name given to it in the query + * + * Has the information of a column. + **/ +struct _DbColumn +{ + DbColumnInfo info; + GvnParamSpec * spec; + gchar * table; + gchar * name; + gchar * display; +}; + +GType db_result_get_type (); +DbResult * db_result_new (); +DbRow * db_result_get_row (const DbResult * obj, gint row_index); +void db_result_remove_row (DbResult * obj, gint row_index); +DbResult * db_result_copy (const DbResult * obj); +void db_result_free (DbResult * obj); + +#endif \ No newline at end of file diff --git a/db/db-row.c b/db/db-row.c new file mode 100644 index 0000000..3ae722f --- /dev/null +++ b/db/db-row.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-row.h" + +/** + * SECTION: db-row + * @Short_description: a row from a #DbModel + * @Title: DbRow + * @See_also: #DbModel + * + * #DbRow represents a row in a #DbModel. + **/ +G_DEFINE_BOXED_TYPE (DbRow, db_row, db_row_copy, db_row_free); + +/** + * db_row_new: + * @len: the lenght of the #DbRow. + * @position: the position where the returned #DbRow will be placed. + * + * Sets a newly allocated #DbRow of lenght @len with position @postition. + * This new structure must be freed using db_row_free(). + * + * Return value: a new #DbRow + **/ +DbRow * db_row_new (gint len, gint position) +{ + DbRow * row = g_slice_new (DbRow); + row->value = g_new0 (GValue, len); + row->position = position; + row->len = len; + return row; +} + +/** + * db_row_free: + * @row: the row that you want to free. + * + * Frees a #DbRow of a #DbResult. + **/ +void db_row_free (DbRow * row) +{ + gint n; + + for (n = 0; n < row->len; n++) + g_value_unset (&row->value[n]); + + g_free (row->value); + g_slice_free (DbRow, row); +} + +/** + * db_row_copy: + * @row: a #DbRow + * + * Copies a #DbRow. + * + * Return value: a new copy of @row + **/ +DbRow * db_row_copy (const DbRow * row) +{ + gint n; + DbRow * new; + + g_return_val_if_fail (row, NULL); + + new = g_new (DbRow, 1); + new->len = row->len; + new->position = row->position; + new->value = g_new (GValue, new->len); + + for (n = 0; n < new->len; n++) + g_value_copy + (g_value_init (&new->value[n], G_VALUE_TYPE (&row->value)) + ,&row->value[n]); + + return new; +} + +/** + * db_row_get_value: + * @row: a #DbRow + * @index: the index of the value in @row + * + * Returns the value placed in the position @index of @row. + * + * Return value: a #GValue + **/ +const GValue * db_row_get_value (const DbRow * row, gint index) +{ + g_return_val_if_fail (row, NULL); + g_return_val_if_fail (index >= 0 && index < row->len, NULL); + + return &row->value[index]; +} + +/** + * db_row_get_position: + * @row: a #DbRow + * + * Returns the position where @row in the model it belongs to. + * + * Return value: the position of @row in its model or -1 if index is out of + **/ +gint db_row_get_position (const DbRow * row) +{ + g_return_val_if_fail (row, -1); + + return row->position; +} diff --git a/db/db-row.h b/db/db-row.h new file mode 100644 index 0000000..f63dc8a --- /dev/null +++ b/db/db-row.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_ROW_H +#define DB_ROW_H + +#include + +#define DB_TYPE_ROW (db_row_get_type()) + +typedef struct _DbRow DbRow; + +/** + * DbRow: + * @value: a #GValue + * @position: the position of the row in the DbModel + * @len: the number of fields + * + * Holds the data and information of a row. + **/ +struct _DbRow +{ + GValue * value; + gint position; + gint len; +}; + +GType db_row_get_type (); +DbRow * db_row_new (gint len, gint position); +void db_row_free (DbRow * row); +DbRow * db_row_copy (const DbRow * row); +const GValue * db_row_get_value (const DbRow * row, gint index); +gint db_row_get_position (const DbRow * row); + +#endif \ No newline at end of file diff --git a/db/db.h b/db/db.h new file mode 100644 index 0000000..4757877 --- /dev/null +++ b/db/db.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_H +#define DB_H + +#include +#include "db-iter.h" +#include "db-model.h" +#include "db-request.h" +#include "db-conn.h" +#include "db-row.h" +#include "db-iterator.h" +#include "db-param.h" +#include "db-plugin.h" +#include "db-calc.h" +#include "db-file-loader.h" + +#endif \ No newline at end of file diff --git a/db/db.pc.in b/db/db.pc.in new file mode 100644 index 0000000..e8081e3 --- /dev/null +++ b/db/db.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Db +Description: Database Access Module for Hedera Library +Version: @VERSION@ +Requires: sql gvn glib-2.0 gobject-2.0 +Libs: -L${libdir} -ldb +Cflags: -I${includedir}/db diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..1cb12cd --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +libhedera (1.0-1) stable; urgency=low + + * Initial Release. + + -- Alejandro T. Colombini Gómez Wed, 02 May 2012 10:20:21 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..301160a --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 \ No newline at end of file diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..fb0d285 --- /dev/null +++ b/debian/control @@ -0,0 +1,73 @@ +Source: libhedera +Priority: extra +Maintainer: Alejandro T. Colombini Gómez +Build-Depends: debhelper (>= 8.0.0), autotools-dev, libgtk-3-dev, gtk-doc-tools, + valac, libgirepository1.0-dev, libgladeui-dev, libgdome2-dev, + libpq-dev, libmysqlclient-dev, ragel +Standards-Version: 3.9.3 +Section: libs +Homepage: http://www.verdnatura.es +Vcs-Svn: svn://www.verdnatura.es/hedera/trunk + +Package: hedera +Section: gnome +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, libhedera1 (= ${binary:Version}) +Suggests: vn-updater (>= 1.0) +Description: Database access and widget provider library (runtime) + The hedera library provides and lets you create widgets bound to SQL queries + and manages the different operations made over the data retrieved from these + queries. + . + The library also has a modular system to add and remove the different widgets + in run time. + . + This package contains the executable and data files. + +Package: libhedera-dev +Section: libdevel +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, libhedera1 (= ${binary:Version}) +Suggests: libgtk-3-doc, libgtk-3-dbg, devhelp, valac (>= 0.16) +Recommends: glade (>= 3.4), anjuta (>= 3.4) +Description: Database access and widget provider library (development) + The hedera library provides and lets you create widgets bound to SQL queries + and manages the different operations made over the data retrieved from these + queries. + . + The library also has a modular system to add and remove the different widgets + in run time. And it includes Glade integration, to design the programs in an + easiest, graphical way. + . + This package contains development files to compile C and Vala programs that + use the hedera library. It also contains the documentation and introspection + files. + +Package: libhedera1 +Section: libs +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, libgtk-3-0, libgtk-3-dev +Description: Database access and widget provider library (core) + The hedera library provides and lets you create widgets bound to SQL queries + and manages the different operations made over the data retrieved from these + queries. + . + The library also has a modular system to add and remove the different widgets + in run time. + . + This package contains the core of the library. It also provides the plugins to + connect a PostgreSQL or MySQL database. + +Package: libhedera-dbg +Section: debug +Architecture: amd64 +Depends: ${misc:Depends}, libhedera-dev (= ${binary:Version}) +Description: Database access and widget provider library (debug) + The hedera library provides and lets you create widgets bound to SQL queries + and manages the different operations made over the data retrieved from these + queries. + . + The library also has a modular system to add and remove the different widgets + in run time. + . + This package contains the debugging symbols for hedera. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..a416160 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,29 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: libhedera +Source: svn://www.verdnatura.es/hedera + +Files: * +Copyright: 2011-2013 Juan Ferrer Toribio + 2013 The Hedera Team +License: GPL-3.0+ + +Files: debian/* +Copyright: 2013 Alejandro T. Colombini Gómez +License: GPL-3.0+ + +License: GPL-3.0+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU General Public + License can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..50bd824 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +NEWS +README diff --git a/debian/hedera.dirs b/debian/hedera.dirs new file mode 100644 index 0000000..cd6f5f3 --- /dev/null +++ b/debian/hedera.dirs @@ -0,0 +1,8 @@ +usr/bin +usr/lib/hedera/module +usr/share/hedera/vn/image +usr/share/hedera/vn +usr/share/hedera/module +usr/share/hedera/module/sql +usr/share/applications +usr/share/ca-certificates/verdnatura.es \ No newline at end of file diff --git a/debian/hedera.install b/debian/hedera.install new file mode 100644 index 0000000..baa912c --- /dev/null +++ b/debian/hedera.install @@ -0,0 +1,7 @@ +usr/bin/* +usr/lib/hedera/module/*.so +usr/share/hedera/vn/image/* +usr/share/hedera/vn/* +usr/share/hedera/module/* +usr/share/applications/* +usr/share/ca-certificates/verdnatura.es/* \ No newline at end of file diff --git a/debian/hedera.links b/debian/hedera.links new file mode 100644 index 0000000..374fd1f --- /dev/null +++ b/debian/hedera.links @@ -0,0 +1 @@ +usr/share/ca-certificates/verdnatura.es/cacert.pem etc/ssl/certs/verdnatura.es.pem \ No newline at end of file diff --git a/debian/hedera.manpages b/debian/hedera.manpages new file mode 100644 index 0000000..a0ae287 --- /dev/null +++ b/debian/hedera.manpages @@ -0,0 +1 @@ +main/hedera.1 \ No newline at end of file diff --git a/debian/libhedera-dev.dirs b/debian/libhedera-dev.dirs new file mode 100644 index 0000000..8ee5a77 --- /dev/null +++ b/debian/libhedera-dev.dirs @@ -0,0 +1,15 @@ +usr/include/hedera +usr/include/hedera/gvn +usr/include/hedera/sql +usr/include/hedera/db +usr/include/hedera/vn +usr/include/hedera/vn/field +usr/include/hedera/vn/column +usr/lib/hedera +usr/lib/glade/modules +usr/lib/girepository-1.0 +usr/share/gir-1.0 +usr/share/glade/catalogs +usr/share/pkgconfig +usr/share/vala-0.20/vapi +usr/share/gtk-doc/html/hedera diff --git a/debian/libhedera-dev.install b/debian/libhedera-dev.install new file mode 100644 index 0000000..4938212 --- /dev/null +++ b/debian/libhedera-dev.install @@ -0,0 +1,25 @@ +usr/include/hedera/gvn/* +usr/include/hedera/sql/* +usr/include/hedera/db/* +usr/include/hedera/vn/* +usr/include/hedera/vn/field/* +usr/include/hedera/vn/column/* +usr/lib/hedera/libgvn.a +usr/lib/hedera/libgvn.so +usr/lib/hedera/libsql.a +usr/lib/hedera/libsql.so +usr/lib/hedera/libdb.a +usr/lib/hedera/libdb.so +usr/lib/hedera/libvn.a +usr/lib/hedera/libvn.so +usr/lib/hedera/libvnfield.a +usr/lib/hedera/libvnfield.so +usr/lib/hedera/libvncolumn.a +usr/lib/hedera/libvncolumn.so +usr/lib/glade/modules/libgladevn.so +usr/lib/girepository-1.0/* +usr/share/gir-1.0/* +usr/share/glade/catalogs/* +usr/share/pkgconfig/* +usr/share/vala-0.20/vapi/* +usr/share/gtk-doc/html/hedera/* diff --git a/debian/libhedera1.dirs b/debian/libhedera1.dirs new file mode 100644 index 0000000..6b32778 --- /dev/null +++ b/debian/libhedera1.dirs @@ -0,0 +1,6 @@ +usr/lib/hedera +usr/share/glib-2.0/schemas +usr/share/locale/es/LC_MESSAGES +usr/share/locale/ca/LC_MESSAGES +usr/share/locale/nl/LC_MESSAGES +usr/share/xml/hedera \ No newline at end of file diff --git a/debian/libhedera1.install b/debian/libhedera1.install new file mode 100644 index 0000000..201d4d2 --- /dev/null +++ b/debian/libhedera1.install @@ -0,0 +1,13 @@ +usr/lib/hedera/libgvn.so.* +usr/lib/hedera/libsql.so.* +usr/lib/hedera/libdb.so.* +usr/lib/hedera/plugin/pg/libdbpg.so +usr/lib/hedera/plugin/mysql/libdbmysql.so +usr/lib/hedera/libvn.so.* +usr/lib/hedera/libvnfield.so.* +usr/lib/hedera/libvncolumn.so.* +usr/share/glib-2.0/schemas/* +usr/share/locale/es/LC_MESSAGES/hedera.mo +usr/share/locale/ca/LC_MESSAGES/hedera.mo +usr/share/locale/nl/LC_MESSAGES/hedera.mo +usr/share/xml/hedera/* \ No newline at end of file diff --git a/debian/menu b/debian/menu new file mode 100644 index 0000000..44bbb2c --- /dev/null +++ b/debian/menu @@ -0,0 +1,5 @@ +?package(hedera):needs="X11" \ + section="Applications/Office" \ + title="Hedera" command="hedera" \ + icon16x16="/usr/share/hedera/vn/image/hedera16x16.xpm" \ + icon32x32="/usr/share/hedera/vn/image/hedera32x32.xpm" diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..ecf5253 --- /dev/null +++ b/debian/rules @@ -0,0 +1,21 @@ +#!/usr/bin/make -f + +#export DH_VERBOSE=1 +#export CFLAGS=" -O3 " + +%: + dh $@ --with autotools-dev --parallel --builddirectory=debian/build + +.PHONY: override_dh_auto_configure +override_dh_auto_configure: + dh_auto_configure -- \ + --enable-vala --enable-gtk-doc --enable-debug --enable-install + +.PHONY: override_dh_strip +override_dh_strip: + dh_strip --dbg-package=libhedera-dbg + +# Overriden to avoid lintian warnings about postinst/postrm +.PHONY: override_dh_makeshlibs +override_dh_makeshlibs: + dh_makeshlibs --noscripts diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 0000000..f948f36 --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS = reference + diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am new file mode 100644 index 0000000..fc30d06 --- /dev/null +++ b/docs/reference/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS = hedera + diff --git a/docs/reference/hedera/Makefile.am b/docs/reference/hedera/Makefile.am new file mode 100644 index 0000000..4b7b09c --- /dev/null +++ b/docs/reference/hedera/Makefile.am @@ -0,0 +1,99 @@ + +AUTOMAKE_OPTIONS = 1.6 + +DOC_MODULE = hedera + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.sgml + +# Directories containing the source code. +# gtk-doc will search all .c and .h files beneath these paths +# for inline comments documenting functions and macros. +DOC_SOURCE_DIR = \ + $(top_srcdir)/gvn \ + $(top_srcdir)/sql \ + $(top_srcdir)/db \ + $(top_srcdir)/vn \ + $(top_srcdir)/vn/field \ + $(top_srcdir)/vn/column + +MKDB_OPTIONS = --xml-mode --output-format=xml + +# Used for dependencies. The docs will be rebuilt if any of these change. +HFILE_GLOB = \ + $(top_srcdir)/gvn/*.h \ + $(top_srcdir)/sql/*.h \ + $(top_srcdir)/db/*.h \ + $(top_srcdir)/vn/*.h \ + $(top_srcdir)/vn/field/*.h \ + $(top_srcdir)/vn/column/*.h +CFILE_GLOB = \ + $(top_srcdir)/gvn/*.c \ + $(top_srcdir)/sql/*.c \ + $(top_srcdir)/db/*.c \ + $(top_srcdir)/vn/*.c \ + $(top_srcdir)/vn/field/*.c \ + $(top_srcdir)/vn/column/*.c + +# Header files or dirs to ignore when scanning. Use base file/dir names +IGNORE_HFILES = \ + plugin module parser + +# Images to copy into HTML directory. +HTML_IMAGES = \ + $(srcdir)/image/vn-calendar.png + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +content_files = \ + introduction.xml \ + configuring.xml \ + running.xml \ + adding-modules.xml \ + module-tutorial.xml \ + first-tutorial.xml + +# Extra options to supply to gtkdoc-fixref +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS = + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +expand_content_files = #first-tutorial.xml + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +GTKDOC_CFLAGS = -I$(top_srcdir) -I$(top_builddir) $(gtk_CFLAGS) +GTKDOC_LIBS = \ + $(gtk_LIBS) \ + $(top_builddir)/gvn/libgvn.la \ + $(top_builddir)/sql/libsql.la \ + $(top_builddir)/db/libdb.la \ + $(top_builddir)/vn/libvn.la \ + $(top_builddir)/vn/field/libvnfield.la \ + $(top_builddir)/vn/column/libvncolumn.la + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Comment this out if you want 'make check' to test you doc status +# and run some sanity checks +if ENABLE_GTK_DOC +TESTS_ENVIRONMENT = cd $(srcdir) && \ + DOC_MODULE = $(DOC_MODULE) DOC_MAIN_SGML_FILE = $(DOC_MAIN_SGML_FILE) \ + SRCDIR = $(abs_srcdir) BUILDDIR = $(abs_builddir) +#TESTS = $(GTKDOC_CHECK) +endif + +EXTRA_DIST += \ + introduction.xml \ + configuring.xml \ + running.xml \ + adding-modules.xml \ + module-tutorial.xml \ + first-tutorial.xml + +CLEANFILES += \ + hedera-scan.c \ + hedera.types \ + hedera-overrides.txt \ + hedera-sections.txt \ + hedera-decl-list.txt \ + hedera-decl.txt diff --git a/docs/reference/hedera/adding-modules.xml b/docs/reference/hedera/adding-modules.xml new file mode 100644 index 0000000..07b7d87 --- /dev/null +++ b/docs/reference/hedera/adding-modules.xml @@ -0,0 +1,69 @@ + + + + + Adding modules + 0 + Hedera Library + + + + Adding modules + + How to add your own modules + + + + + Previous stage + + Before you can add a module, you have to create it. To do so, + the Hedera Library ships with an Anjuta IDE project wizzard + that creates an empty module written in Vala, almost ready + to build . If you won't be using the Anjuta IDE, you'll + have to work your way to a built module. More on that will be + covered in the section about the module creation on the Tutorials. + + + + + Adding the module + + At this point, you should have a compiled module, this consisting + in a folder with the data (images, UI definitions, etc.) of the + module along with the dynamic library (a .so file) that the + Hedera execution environment (or Hedera application) will link + in runtime to load the module. It can also be the single + .so, due to that may be the module developer has coded + the UI creation or may be beacuse both the module and its data were + compiled in a single file. + + + To run the module in the hedera runtime you must set the environment + variable VN_MODULE_LIB_PATH to the full path to the module or + modules .so files (separated by :), and + VN_MODULE_DATA_PATH pointing to the .xml, + .glade, .ui and any + other data of the module, there is also an optional third variable, + VN_MODULE_QUERY_PATH that points to .sql + files. Once these variables are set you'll have to run + the Hedera application normally, and it will look for the + values on the variables and load the module. In the future there + will be another variable that when set to "yes" the module will + install and when set to "no" (or not set) the module will just be + used as now in the hedera runtime. Note that if you use the Anjuta + IDE with the project created by the wizzard, as you run in Anjuta + the variables will be automatically set pointing to your project + files. + + + If you want to install the module you have to install the + .so in /usr/lib/hedera/module and + the .xml and any other data to + /usr/share/hedera/module. + + + + diff --git a/docs/reference/hedera/configuring.xml b/docs/reference/hedera/configuring.xml new file mode 100644 index 0000000..80036a9 --- /dev/null +++ b/docs/reference/hedera/configuring.xml @@ -0,0 +1,87 @@ + + + + + Configuring + 0 + Hedera Library + + + + Configuring the Hedera Library + + How to configure the Hedera library before start using it + + + + + + This chapter covers how to configure the library from the tarball, + if you have received it packed in any other form, like a .deb + package, this section can be ignored, but you'll need to know how + to use the package you received. + + + + What do you need? + + The Hedera library has some dependencies, so to compile it you'll + need these to be satisfied. Depending on how many things you want + the library to do, you'll need more or less things to start using + it. + + + The main and unavoidable dependencies for the Hedera library + are GLib and GTK+. Also, depending of what type of database you will + be using, you will also need the public API of this database. The + goal of the library is to add more support for other free databases, + but right now, the only supported databases are PostgreSQL and + MySQL, so you will need to install the programming interface for at + least of of these. + + + If you want to generate this documentation with your own compilation + of the library, you'll also need to install GTK-Doc to do so. + + + + Compiling the library + + Supposing you've satisfied all of the existing dependencies, now + you'll need to compile the Hedera library. To do it you just have + to go to the root folder of the library and run the autogen.sh + script with the configure options of your choice (listed in the + README file of the distribution) e.g. './autogen.sh --enable-vala + --prefix=~/installdir' and then run 'make' to compile the library. + + + As we do, we highly recommend developers using the Hedera + library and also GTK+ or GLib, to use the Anjuta IDE, a very + useful and complete free developing environment with a great + support and integration for the GObject system and the GNU + build system. + + + + Vala + + The Hedera library supports Vala as programming language. If + you will be programming in Vala, you must install the Vala + compiler, valac. The only version of Vala supported right now is + the 0.16, plans are to be also compatible with newer versions + too. Note that to build the Vala bindings you'll also need the + vapigen tool, which normally installs with the Vala compiler. + + + + Installing + + When you have the library configured according to your needs, + you'll have to execute 'make install' with root privileges, and + the library directory tree will install in the folder specified + at configure time. + + + diff --git a/docs/reference/hedera/first-tutorial.xml b/docs/reference/hedera/first-tutorial.xml new file mode 100644 index 0000000..98beb26 --- /dev/null +++ b/docs/reference/hedera/first-tutorial.xml @@ -0,0 +1,27 @@ + + + + + First Tutorial + 0 + Hedera Library + + + + First simple tutorial + + A simple tutorial to get started + + + + + The first tutorial + + In this first tutorial you will learn how to create a simple + module with just one form using the Hedera library. + + + + diff --git a/docs/reference/hedera/hedera-docs.sgml b/docs/reference/hedera/hedera-docs.sgml new file mode 100644 index 0000000..7c596be --- /dev/null +++ b/docs/reference/hedera/hedera-docs.sgml @@ -0,0 +1,153 @@ + + +]> + + + Hedera Reference Manual + + This document is for the Hedera library, version 1.0. + The latest version of this documentation can be found on-line + here. + + + + + Overview + + + This part exposes the features of the Hedera library and + what you need to compile and use it the first time. + + + + + + + + + + Using Hedera library + + + Once you've installed it, you'll need to get the hang of it. + With this in mind, we've prepared some tutorials to do it. + + + + + + + + Hedera Reference + + + A quick reference guide of the classes and functions found + in each part of the Hedera library. + + + + Object Hierarchy + + + + GvnLib + + + + + + + + + SqlLib + + + + + + + + + + + + + + + + + + + + + + + DbLib + + + + + + + + + + + + + VnLib + + + + + + + + + + + + Fields + + + + + + + + + + + + Columns + + + + + + + + + + API Index + + + + + + diff --git a/docs/reference/hedera/image/vn-calendar.png b/docs/reference/hedera/image/vn-calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..60e8e2390b9d3b95ad3943ea7b171bd1e88d9cac GIT binary patch literal 15046 zcmb8W1ymhTwk>*);O_3ho!}PS-QC@S2TyQ^Ai>=|xI=JvclY4#uhM8Ba) znivDb{TBCdr)P(f*eEPvp^Mbva2%?+)qVQa#$YFVq}cOKizjRyOeV_+W4@E&#eMsXx2N~>{k4>~s;#g>p5wC(dp?E8Ei0TI*zea4 zj_FuP$Z6{H8orN>^J;?FNplbwbFpX;q4XDR>8w78eIcI{E*Xn##8@G*Srnltd*I;1 zM@HyysYOVNqRO0OmQI0n3Y6>cd{CO$G(%uUSWpk!bFq97+VhRTsFb@ zC*$C!;B?~a6<=#ppw6k!FD+}6a}oRy5db@{Apir3DMEI+{-f+o#15I(*Rvb{fYF#n z+~FGkjH{bTsuQwy2us=DpZG$HA%@EZ@w<}XYRV&Nk2;);IvkvjlXGKyT>K5e$fvzJ zN6G!1NS6^An5Jk5&|t!V;}6N3tGSUzhmC9)L)D+#AA*YO5toOheM8T;SD`@PHeo7D zXb50LY0jUWAdQ%DV4~;d7V_TTz{8DjLPpp(zla)>1{-lj?|=rvuz^dHBz;3e!w>0# zBjopez0Q6*8tmlc?YsfjKYxZuSW_aq44J<`3Tl+GeHoWBIE~PVF@XHV*jWXJOtv_1Osh;rpH=Z(cI>bt_Je$be(Zy~OS|%8nC;2=ktm zFa6c-l*s`8nL)j`)<rytoAPSbC1{_Ypc;;LRhh}x&H9M7reD&nY11xkLgzygUn?clrT(Su< zv9|^je9)%suA}kH%1oZNqR;-S2SU#*)5V7SIlr7NLOQFKR$cKSTfca}ob_5{#L4Ee zz}YxqifrMV+K}n=a!U*DOd%2XFNdY+0-BNu4!ad0-L7kFCO^%N?tv9vmxE}=44&6I zCpBSX<^Sr#ZViD^Hudn16K1Cm8aWoE5K3>?RM*td)Zd@3uKDHieL2|`by*2=78PZ6 zMcp_0#*X?8GMxoqnD_#ez#(I_F6#b09v+_He-vFLXprFp5l8E#)z>*3j@^|E9gh2x zoe+*YdJ%d1v&RS)((3~b&jit6BfrmcgrE6p&lvZf8s zhD$to8e0H?FeGMEOpZ7obO2MGd*|jBA%fN9dyQCqJxS~prm&m7y83%bh?8+Z@e@Oz zDrKe2m+DI&YfMDs3*%4hU-;90l$Nt12ibsNYlyscy)nLIEO}mTeUViAPTztE~vPUUh@illO@gOxiqQN{UJ%Q z0fk_HHLBz2cXzTAk!#s0;On>6PKOmSBqE$Mxi-eY`ZDOS(zw67Z@9=&dI6Xt4Y0%?%!k)D#s@ z-WOU>Q&W?hOZILDu5Vy$K?1t{=-@1)B6F*At7(0F7{;2sI|L`B-{$?wxHi``&ypwZ zX1TH6zwEjD>)gr7*^0WI;A24n>Hv9CAY|=sVKAN;{f||@3!sq|rKP2bK|x45RHQUo zZ;%HZ@}8ca(MU)USo9AE`fZ|Qq<8Ko(@Xu!%hNS#CQ2KqK6k5^gT)N^oLoFyAP^2U z9HFi06mQ>8hRXwm5n`$ z2dh%?Cp0p#nugkkaU`;dw%3C!eWEqI{<_+F)QCRJ#Ka1E`c!AcA4T#8&2DxJ4GjC{ z=DkEBA|f1ViD^j+;)7#jCbe3KX|TfISDOfglrR(~25onCsr{h1jln>5Rx^cl)J&&# zMrPtAgKNDluM~|APpOM7;Gj_6HyD33zuZ_{d%|T1kRpNNC`BohKgsreMgfm39;e$) z=+^FjX!VY}-OBXxddJq`_T6byfeaQR9`m=w(eWWzIDJhW9U1wTAEss&wOaJ(=<)ly zzXAKAAezDR%N;po8w8?^U#fR5l87O8KV1!5bbg;Acjx3ZS0Uy?@mROu4$p@1&$YC( z%MflI_KWej+Le;;j~JfT_2Tr~d{Jv%7jeC9?mZ>N*yQ?+a$(dcTnFrkXXRZ&$Yc;7UNN4GGpZtCdB{K`;VQc_wvJvTQuHD&d{OqtZP<=718r>~+C zADiCx)d;4!sJ^}3tW5n9nfUH}CS)-_^M{qaxw@`$%T6PIPcV{|6O1Sd7V6YU5%j?C z?9$TkMH5R)7*H~+)wI8uOk`xF=T|R5@0&wm+0iq*mC$1~b*y8NccI;^HZx|$-hv7? z2(yPK78uUA|M5RB5 za7MPd^L+he^Z4oQa@76i?8uq-Oy0o2fb4PJVL6gSk`6xN&F5K`fU|+ysI0UMJwA>F zq1E>qtND6Hp?XlID1(5o!FKUNjW>yMB-fm!9wAV(r}JH2iC985 zs~|a$D;gUE>+tCK?szH9*eTpw0F`8Os|VU!U}Jt>{_Q1U_ERI$sTmhqhCc(YX<1Hp zY-pR3WCpLmo2}!95ZHzO&Ov{O1zMQZkDEx@K71b8LQVS8s;U7@V*2-AVQqf$ zN)#A#3k$W$^g&iw_tAu*N2xK+RDhGfChvGgT1WYgGALLPluNCzqC=NOz!mM>yc(CA ztJCQ4iOPx@p$E*~%BatfdH31LSa%!}^iDlOJzu4T=n(N)Qox%<7!*$SwY9-+k4g;$ z&hMKVgsX!W}9|y1DT*iT2 zzoQ}%#K?dxN#vyy!4L>NECeWY zdl(8rv0g{FXBEwLe6fh%Kf$GJ?~<#5K>7LkkOPhhT`$)j9>(Ts8H$CmS=5)7aKCY> zY0r1UNI+mP!hAUwze z6?MqNi0aA8$-O+^rtw(iN>Ugz<4CHj{}d(T_F|!Aqq7;C5L6L-MfmQGih+q)rPsOM zHRZVLE}}(`b+tc+A12D`FcoKF6e1fLC3w3hL~^4zbfAzau+wO#vt-MmLYo*anj0Pt z2RPpx#fOqAMDQGF8@jTqE4Uw$fw|Pc_i-`}*=1m&Be9Yc^jHxzKO_e|kS))lrdWH8 zSt9z3jd8^9_LxUVwSz^;x|kzr;)e#m!;LzH{`_u~EK6yE))+Xbmvxs)g5pU!YV)4B z_lqgS5|_9pef2{vL9Wjx*El5NPbXm#ZHOP!#od*`hByu9lUqyI0cNaPc~q8wqnH#l zloS#&inlO7*u6o<5!Tgt|3;dkrIp&0nY|hGj6~SK>nnHE`8i(BK#w7m3zggXtnJ$t zxr9P`owI_aoE)OZjh43dMO=Z1xNoP+($#=MX5910@JLKnB%Yv|Hn6+kPQ@IHNoS+T zvKGSy6AHe`Qo^q_3O@a5j%Nq%uA{eKlMC<>IZ3ZHl9ArmNoPX306{`3_x0 z`Taxc%#CtMenX}G(=1KM+v4-ib{~Kk8~!Yc5lDNzZ5Jh$`LwmWJFF}hs1#)_l>4N{ zN)kLhpS1_(=Uc^7vD-3*iy-FMV?6f0C7s51^MVF01(Aqaz8+nlZO4m%&XT5h!F~6w{ID zjDL>Cm;**BeN40W-gd!=NGD=4n~7&n-lrO8g~Pl71xR-G{e5$1bJ_bp^HGzjot9zE zz=UWmX`C*(f9kD%@%INNl_`r?gr0Uz@h1dme>6FI z=b=v~$*#OS*cU$=Hf#cVF7@s48vE|n#}+|gEAuUO*hJtafJQV@lq>CcxI!?=XraN?qs1{2qnL~ zT$VCkD>*IR&)&i$V&|*bM}w}&xLnpWm5DLi1V6LpZc9qT$=_}M{>dX6UkI}J`4yds zXVx<_dn0nd0u(a1byRh2O##>U85INA^Z_jPH}`@Sx|n=S0KWtSt*-H)$PetoAjG#; z2U%^dV@2;gBZugiQ;@TJ-3Kxqp`BIY=|SCuBWCa_Y?ys zCJX`v3@H}#gx^`$(4=iptNL(*4AnP3fqoazY2YIC13r$LVs7g#AY|%*2?HBj10E6> z(*ot7=%wy%UCQ{x#H9Gn;mHdViM%Ynkmz9adMVY=`TM=>k8*zdFF_}b_IBExfFE&Bkq_!ZIi+~b z9v&-h=b3EI;_eCx9BKSctAW8iG5-F-7-CUTk$6Zl7LI6JNkJFq4}1EX?d!*sOUXD>nRS#OXaRaNx{YcnMs9ehW!8jF}Sz<%+J zG!u-UG&(3^RM%JM+>^Q6zNzV-b&ABx`SGz%udIxGR*0$7qoRbRjKsg*nV|FF%d}{; zS3pOr?CpPRu_r_oCHq1Bwd~~Z5XA%^*tk}{d5TdZf=G3KD#}Hp(iH3jGqxwMG-F?ZtD2z3Jm)FeiJbwTy8YDoOu`U6?zL$g@>fE zngB4y>j9zDL36PrhM3OEQpKak(q(*6?nH*WrCq069~^+jMcMiUzc%^YJM<0-R%m_z zCG+_bupCG_N)pCCJUkp99}{a-%6~=4N=z&;=YrW~>lzv=i7&dZveCfD!{e~&1^@0n z^_xD#QZUO`7wNkt-eQtS9dYs=$|eL_o! z1hDeU$bf0T>rr(x96Ve2(*5De%#o(IV9Fl z7yeQ3A6iP|a>W@bWzFpGa!6g2Nkz zz*PCOfqIszq%3$rf%nPQWz3z!7D`!}0Stt5n9V}UsLuD3heZRap5=hLU8qEW0ls3- zw;zO|E=5TsN#*cF2BXO{c`PivcWa@>;6v@P{ryIJYM{fX{L5>qp?}g*ME4W={m2m9 z<4dh@fI~JDDsHgG!pNW;0kmM|P~xzv#~*!xaS4&FCw8I5HCEht$skSq>XTkp-!&>~ z*ZAVZJ)jBK&xjT<13_kUZ0{h}wdA-5(!QSo3T-RKx-qb-?%37b$l^VF^ z*ocGMjEqyqCkLbTvRpeW>dCjS?+c1=)gN(fOb|)FL79CtK{TVbG6VmM!cb47Ht6$B zTf>6#gHOjki<0STN8{|8W<2dxfgx+G75}LBveT06u9W)e_fF{#-B_PUwYZ0^rN$o6 z+hz6aZj-WxK8Ck_{Z^pp-$!wI6u!|fOf@;Mx+JVY-9=msgjZ66H<1DkGW&c_;CKJ5iG4lEVuY*O zNfR>Aw*jsC6XUA`ansjp?TZ-_8;)=g$R$P6Bg*BdBz+ToY3woIUO9-qFX7DGPUpzJ zN%x|2rkN6I^oV~fO^pW8T11PX1Xu9P?3R2#?UA59ssDVsgnx*`{%|Y6s0;cQLS9rO{!}O_Y2vMiW!-PI4cb);H^As{ zK2)8DR7My8Lm9dlgx=%VZK6-0y~d$tT^zH#AKTttZOOGBnn`!hx~7NPe#Sh%*eIA2 z)~Z)@d&rvBKmBUk->Nuf#cpQlt;-m<&G70*_THsU9{2tJHQpV6{Lr3KJ(#|d5d!o( zyIgusb?~x~CZuFUxG|<)L&6_id;9${*Yj7}R8XMwhTE#nMA@DM?D4?+qwPBIByay> zCkW2dW_b;vcgERW0@#9;2(@3!TO&N z9-R~Rpx7D4ogNK7{suE-5t!z*^+LZx4k-7Hq3gIW6sPZDqPHwl;{J{T0UG5=O);2T zje#-yq%Bj|`+g8*SXd>-E3+nFfrzw9lf&ujSZ_LUY77+)*@SD4W5j!8*isEvJ4E@5 z!`O(cIR6DnTaBp)>=);hhaOqNhJhAcAJ{KeV~d{=Adq^nEdq36JuAG3tDvaP-932D zXrFRY8=;~i4biT-X_r~^^5J7^sSiT}&LJ}hWCmVB6T&?;qB`SV8;JJD$a4&6^6Kf$ zShK7bx|;?x#GEku-!@iZ8pTZ82tVm`P!}_mJ!))}_`Z-5M(Nt!jB?55Kn{E%N!-Ga z`+U;`#dElOX71Nnils6x9aiwLrs2Q&R>8A)nBttGM~DzA@R`2nyd6vS+-!WtlI2wW6h=Gw~keHao>wBTql;&V~mBerZ;=gY{RpPI;0IM-tr zT`WH4mQx5`=1lrcl3>&}d;QNRT8t02s7b=Q`|VkuZR>u{Mlun9&uvhPW)keT#`$H0 zPJ%eDY9Q>Z5Z?@jR~~A!+S$gvIm~%!o<1`=DA%E0SkMt}ja>y&hyeqkbe=aPhLsXz zk%8-7Ccfs>P$~))ONEKwE2 z_$)lYNfr9^`#|$hB6!P&X}<^Z-TRwQv-mffH!U!L&Ak4@t4a_2bq2)OJR~T$N_TZ( z)Aa8z*xx>rj6p_glKj(uLj3#Yf4kKGu43?|tZvc%a&>dG;`f%4mNc=2b8~g6Rc9y= z(nA_eSyX7h+`>T4HZnf%Hq?#cpn!~78KzBktF?SK*>1(8kAsgJMa1qj+zSgcy{)4e zCN*S)ifT2H1s^2Lxqt+yC5_IHU#d81xIHuu*Et<#R{XASXT#82y}U{k;o!oiYii(G zlE(F#G&NOSIB094;^W~i@pE&2T`6)RGAMFJS=Ti&B@N}X*VhB`a|W-g)9djZY?xHp z{F}cK4(Ky&BIDDR1fW1DWO1D>v@Rfz`~*lM&_rP}ayDoC!R|Bt%pwJXG~!POzqfwv zQ01w{epc+wlS$&|b^C+7W@2K8i9ta2qfO@|Gwz9=`m2$F!A59LNE<)@0h9L2#gvKX zNBh3l$BWCI56%IqU;UJ`|2FLtqMqVx+Hvo|fI+KUg$buZifnLWIu$JJdi!f==hy_( zm-`D^dirk1w%pwAwVty4I@O?L3^X10GwlxRdIT@`!<&>^^g?-+(_txTY3ad{en;=S zwhyhs_i+-O_Uo)PU!dC2aDt6+f_Iu)Ll#zUTwKXU@^XG*401+MDi$~X+&Duk3VPGaNtt2cg^%9dpT4qFBOEXrV6OdIs zJRWH!u;79m)(agL+X_M&lbV}xL7#(-6wuJXp`PLo5bzG)=^5mxF=&xzSYbgRf0EQg zdv=VU;E#nlN#ZARX0zIKE?d(D@kM#>i?wN*FEJ#8%ZYUTa+!Rly^aqe$QP%gI?>o)|fT0ubpYs%>Ia5&$< zP#5aJWq7!>kB^TwMIBU7VGg$EiQMf8uN&Fb<`%i%pQY2)Ld5C=DdFz!0zrBt*ba}| ztI^-Tx#5NFS6eE^``8l>H>%E&Ww73>-wivvz<@D7UM+~Ih^imHYA?`fo;N&!T zngN6lD3D<`H7L8ZC6!Nt0t*bJX^W|Mh%apkjhGg!Z&}GX*S{AM6AX<&tfZ<--|5_& z>{z|v5SyknH3b14T+C-_qv{Xxniyuc@zt(Y9&_A^NMai5$`U2Jyg0&r-vslxncATu z65t1=$lmp1FA+I8Ip}xnw{HfGwy%yu+YNRZwa!i) z4l69yylM?@} zL>W&0Nl_`B_{73;c{+D;*6yWLq(IF5<#Uj-qGV{_c1w%9TmKw5Sk}Y>AneY~spXJd zSyGpSW5|iXW84<9N)zrTbXApJ;WH~5g@3x-^1%>uBoGAVcR4BzS4*pbV;&H8Gnm~X z;WnOVD3}qhFOJNtjki||#}=nH?p0~}z(7BgigI%&Ft9N3@Nw@?cQ_r!Z68ZzrRCSJ zpF6Ot^qcEkkurH3ovGYCKUS_aHQVmdK7t32rr~qBhFhDFPAO`7P^Shn!X6h|q2>qZ zM${63zU_pIBV3ip5~*3JM+p1>e30wUX~aMbpvH);#QlsIR1iBE2&X4k-lAX}zg(A) zLI$JfT1r^(z-83w0{c*h1rVA4ClPruiS7V_@Z|pH0{jP_@*k?`{CAo@lf+7IO)%OZ z-@KGo00B@ghKsoq&QxZ`kzd>MISm3GU zD<5?iyyJ6azwaaBGwLBpGWE`5?VH8qF&5CSQmS!cdC0Vq^}&OK_#Oc_O7 zBriMtaCR_fgMn0#FOXJCV!HlEtL!J!`$?AHK7($Z!-PX7(VBL7PGJB+^bT%HO2_N# z_0=`IX>9C|l%un)0`4?cf&r7N4Hz7OTy2uc^gurF@7Wp~Z;j_KVh);!+z&%}u+W0t zw@417h9YvDg3AQE%m_M-mkU}m5O(Ei?(bGeRr;Os(o!_EjPs%w$sZknCZ!c1oe zQLX!Pf_t`Je*HOkdV0DFhnuip<0#iNF*lbsd5Ih?*Kk;`)M+0ctM^fM8a}SKaRlTW z6P_qEikH$-bgI1h3VpHG90Gq58}&*YL657+A9@`;YfUa^qZNyE7I|ckYA<%JZc`_ex=LEj)TTnnObU!4Gqn=; zn~&EM29Y@${uD0{usS+v>-;{JF)tLPMpM(oSRs%bO-^B0(4E2mev{cj37Z8SEnO$E z;Ez={<54bn=j)RslfzT7LF9T4dHD!`YiGAvr;Dy)?yN{GikhD^S{Af!9cZz(0F)P4d=^ifIW(``%W@=aUP8HZckm^Y<;RN z0S(>aPiJUrJ8E8AQ&U?(&k)8BhZ zRpXvKbG|Q|%fKzJEb;rMPIdv3jjXKvRP2!1;?$Rr`MY-S!}CF~3MAh#QeLWqyNphb zDub$=eaf{Lz`2L<)3!dAB<=x`Mxb1IebucbjVY2J4G!(yern+%7yu%+Xz_9GZw(g9 z2}3vYe~M9iG%ECL?9BFN)}AkC0wJK0?Up6|b}*9?(z*S7uY+&#YQ7dub;W>BcR9T0 z^w9D?42c+dOC+UbK@H5fI35kpk#C`znVHdZtgMgQ59P*oy}il3Ysnlg3{*8#-rc`K ztH0NDMCI)S8_c`iEAO2_utWDmb8++B{SmsJg&T^I!P3#ueskP3GzYd!*9|wfm2}H8U^W}QG=K+US;rB(q z2g|R(q&?sLakP7nf`#D+1nOG>36ZXJWaG5b2 zZp}SNBjS8|@8~PJD{~|M{D6k{RDlyk+g()uzk!*5k>>>V1;L2Lcjqr$( zv5|u>2%h(Unk~D$kwQfBM2(fNwk|H}4km(xbJA==`bgoIo12Oi;m{$>-|yOlD#iD8 z%@sw}yQ`4QRrFdxO0FSnqUt{~=ekT7`9uelRnZd6Q6F0O?<$my z&W=i5ZdHAB+o5|>F#axAHv-OAfiG!k+lmpEzUf6_VenWC&G_zR1*Z$LVbjyoU>itL z?O(g5YZ7Y|hxbRqZW*{he>o5PVTE1Pl8*QaL-?ow#^LNmF*xuwjkM6sp2GQtieF+L zYnTnEeel@|Hm+|1l?B1)sz5$=c6P8H@pAphLaIWw@DVK^wYv1(UT?{G- zXgNukpzhVSm-gqo+lxC^PEHjea1CD=q2K<#74ibqOjyNpNt{~Y8dU}ny~ZQz74$kZ zK1rqYLW^gw57*(d7FOcH!GFU70(PZvjHP3?_O7<~9h+WR*o=BS9z0Jm{H^R;&}bM0 zak0t0J5Di#uz72m#$TYxvBG-`FOCq)Kp{O^Pcu%_ZVo#w^>{UFCyQO|EHsbK4QtQJ z2b$3tp?qH-20JD25V zIa_UAU1a|tn7w##OC&$KjsX&o&$aCje(`J?a&aV|Y6D9pF(B3jPbA#!JO+sf(H z?~*8}+S!IGBYj*eA`vZ42=8EKlw-vJ?+g$6)*&=Ppk5N@sp=?@6SvAIP&TGeLAZ#?w1d+&-oMTC=fp z5Hwm%2J9uRtiMv@QQt@sGBGTGmfhT(Qh6sSl!tKl zE$#Qgh@j3OeMmj@|3f1F570}bE{o|rrFSAK8AWYgpfNP&a>hs-an#B6JI%u*garM+ zWb1#UJxs#gf74fGBOgZ~7pSmeg_X7TkF8x2J;LSPjp9~n@kiyXf>Wfmw7cz~>K{{w zO5{bd>Dk?AINf1qcUgXD@-FS(-n$uxadGkPL1#YK9PO{11_~Km)vb+5a0S`1*^DF% zXtN*k#mHD_xarCK>y~ve_zc*~+9{dT$bQJCV?7+-;wPR+i^)_2JQZ*Mrz)*p(~aL5 zA+NYZ=EcdH&5^=Lud&*ncWMe&R<;0tkrpoxaS5WV|3QmM(hr6AC&)7n)jQmCWu$yK zEQBNhG7N-r+W|7npqsyZ><$MT4nF-+$T5@=7E+9_#f7x|&bGW_pB_ei%(`kwh9Avw zbaXcI*QW9hATnUHQ0e+-9|+K1n13_ExdeFdiJfQ5OU}y+xI1!HcQ+Zq039vuDu0`_ zv`iX$?c=}b@t-xvmw(b@oBc$U3kuS%)<$Afi~r)sAsqkW#~2^-WpW=wEPqhb(2%BO z>W4*+%5J>9c|1PS&L*~oXxQ4?dVBL__!&nv=ht(QDId&Lu@Z#fdNl8sEzUa@3 zrDkBTF*Ew!A$WGU8Jm<;uuy#2w*7A*%{bQi; zR$Ai`K8J(Sl6A~qm%+|I*}trvk?gXL2?RFBhB0@j6o`17Fe3U8Ryt$u_NM<6 z9WHISOl(8fQ*x}9rL1ju`gOPd-voG0PwSJl+d@91-f>ia3VsbUz?nz3CXMThNKk#w z(nZN|*_{GYjdb{of4p-kK0tcB+#JQC=H=$*MaW3s5%a9BHBQ{7HELtjN&z4NSfgAn zyZ7tuJ`ffYOLcb7$zk!ck0jAtzQz~sJsWL4GPY=0Z6AVyc42`$turEhvA#YRsX>v2 z&>&&|Z4?GHGqb(>?Q>*eez)DlOXZRT@v!b(GGM#`Bu&XNhg&_FvPa$@8PV<=uo@X2 zO@zWMnt@?kV@S79{%F-Fo%j)n7!eu3>rMHAtK>f<`Mf>*1;2hi^SF5k3sF$$4hhAA z^{^arf`heo)uw>a@9-?xZ)(fwt(>QbUW zo5C^p{wFS8_YZ1xfc-r3oSDt_qZ?xqFXZ)0U9e)0Og-D|DI#fVj{ zq*(mpAFX39vgua)R$dM$mWM+&ra|t1di)h_EmZt|Rlr zN%ZzB^W3~t7S|1gdEy8~Ys!)T7hP6;euV(u9zC)}%{BF5ER);pNfsR)eM7(fY`qoR z_37pZ1hlFbIZb@_2(W1UpMW8C(K@AxpPczy;sv7y_VBL0()Mcf3E4lE&h_fzuxOz0 zafV(zcRRi;h7AHn*4*4;MNq0GgXf_-&}nVNhip z?lQ%cfA7uDpA@|7^nPura8COmB&5gVo~z-X`^`m45jz&^uSqT%Pybkc_w?{Mp23z@ zru>uhT3PMkJ9<6A?uFmP1l=x+hib+nD;%u$HfI9BeuE%g39W^N%@X$|E$bKtowZAI zpO`8?d43oZEbZtWc+|sYpbm1AoXq`@HScdJ_#dRk|9#a2cKRMyG(04DiRx1c6XaF% z%=W=wprzyJ7`UNvp2;uOSv6W-j%w0RgTwVvplxINmIeb21S(PWe6+Szb#XCQZ&0LO zZM7+ai`Y((x_I;k<`p2rxzQ5dYn6mi1(Nb|udXBN-PR6Axk05`r%E#U0x~Q0R0+S9 z@vmR0=gRK@_>UYC63^iXq>~P=^nUzEb-s!&t$8Y!_DPDpjQ<0SSNngXQZui1zo}{V z7fejd6}Qt4d}0Gd&4mt-2e(-VJk8^A@kKiHV77gHxk>^J57>hWm&{B_6x| z3$RDSBO*SQYLipP1GSj%<_npV=K~6=T>;;7G0?(#C;TQ0?N(}QoB`~<&{I`qO$3I4 zc7%&7;PXT%;Pi=#^IrN7`}w*rDixVptfZ_wu&1}Rmah|LXI=3*o*t0_6V#ito1j{y z-+?-y&?Ywl10Qp^d-MwEX_yokp?t2MPwClo)eEznT-jYE{+|JeJ?M06H%v_dq+x3xkSrM!r`7fvuLov{_*BnS2o8T$0weKQi` zlvQ=h&3DB=8Z#Q4EjM4s2v%Jj&77}nks}iz#n-QH#c2y?n*5+*U|{fizRA|`XDs7~ zgY~Sq!&~mP8n;FkLU|&*n>`wt80fvh$M~E|YG!KwbOU7hwr=@CK()BuCgXmps;iUD zxHlGoeRl}C!{vH7dxiE-O$E~Iu1^FFH@ZY_q+?aD9{IhWSKqFqQ!nuoB_{@NE{=}o z=kjdsiD*hn>SS~~9cNlC&KL1Ht!-=!+Pt|vS09Lcebmk@f!(n;Ki4x*4&ID8Nd~(~@830?Mpew!nXJq21yL*k9bvYs9$w{Lt{tWMU;f@YXA(? z+;F%5cHLhpU1}~fO!4RR50Ri82`75IO8QD2pc;_;v1}5tdDa&YDE(0MGkeN9rUj!H zhZ9(G0HFqlLQFeXw7Wo$7*~aDNoexH@jw+>hlw`^`>bOrI zuXm1dom3<@&nxa&Yieu(ou~ou_ke-zDM|ulbmue88R6jqB8$DZ|5P14*nocR z@^_wlT^=XO=p1Y|JM+3bX~>CX{ud_hP$a$HFEy-!o^^>0_}l>lpiwKt|37H-e`HGh z8*=_Ld7|0PU&M=_dseSNA~|{O?OLI`w>&8(rY#JYfr9d5h?r`scgV7eALz zl;YD^E? z+H5=$7l*PH#hHehYxk)D2P;BM!#fT*5ilw{roUZf6(jp?=izjb?uuQ+A>0I~!E=qN zYNI0q7w7P7zN>dOp=7;9?Za2T^}n}xk;#j$x4!@GTS^hfEKzMa3?Y7fzF(>Hecdb5 z@Y8JIJ6l%cNUKm+WGCFkP&Dro$A+Uc{}czvg)19^Oo9eJ0gz^w>_$~o$VOEf3@DcJ z!bEtdx4Su0`Y{~P+#eFOs!J3^(An_1m~Gek4J_o4>#7?HIcvb{R!)XQ7EfM(*?J&% zb^s)I(yXco;qRkUf$c;YXSn}^fL2R;0`aE6UJAw#XW90G+bFbup)|;LBh1Y&T!Qq^ z%^e*bZMogGI*o`n3&|-d(>Q|UY$tPRjVJ0=PR@2&3F$SSr`iMq78A+xd62N3rN_NP z#ja%p#M+*Dj}^v7ui@3iL|pb5gs$2$7%l_HVvr&nkYSk&J^b3Y)UPV z^`1v1OUB_yKn#e|LCuf#GT2A2ie!Wu%Us+HaQeS&`)Kh3fFQc8oX*N}T$)|R3XC=6 zjWkMvhN@^r^m>y$;wd3FXmf#KWq&-A{mW-R(C_l2@4>-sr>$G#~ww7c}F1z8t%iY4P_v~K~&*=n&?8owX(e0V6fKx`Qtn4l1x$~TzH2&vQ zNm>p%b@xV&Y<>E~lJ~&_y%r$5dvc?eLJI#VzQ{2n?7Nqi381>=6Pf^VFMhbgngtc8 zs5?)JV{>j`3V;@8*{w$`B?NdwuyWOw@h;K?d5d_uf<)`A(-1g)xE&>%=V16 zI5ASd;-tOR{+z6eF|gA+J%2_ne>3yJml#0oEI&OH>p^~{{=)nxG(OK{g6NY%MeIwH zX?1`Wm*?= zNCt&z7L|-}*Al1NJ8<+F28~$gWG6wGH#I;%jOjd&7~G2He-FBlrC-|4i#iZsM9EQ( z5dWrR1&{$r|HakSjz&sTMIAUWfgEsf@N#l;I0(SugYz2i&2Mj=wlA2JJfghR)bzTo z_CD_&K1vv3D3HJbfw0dc4^JmmF+_rnJM$G+SK$giK`5au$*HLbhYJqJy|7IBZJSQb zlET8mdU|?xc6Pw0o?eC`HGo%GQxJNXjY)5cIyPg3e2sitw+$uP!FmT=};!W=#xy0jR6)2U!2rHe1G|0hUb+j9n9){mspU zA1U^hmX?ETz%hz`=JJYK8KAMk#=TO>y+nkFkfdBzAQUCqfo`&9-U))80i2H;&79=P(_2+a& zM2IH6IrHSK1q~9Cd<##@u)Oa*1G*S40#w@AJP=4*_**!cnI{((-UY*OC?nV+Dgew2 O0!fL + + + + Introduction + 0 + Hedera Library + + + + Introduction to the Hedera library + + Presentation of the features of the Hedera Lib + + + + + The Hedera Library + + The Hedera library is a utility library that offers an easy + way to connect to a database and link the retrieved data to + GTK+ graphical interfaces. + + + It features a modular system to easily add new functionalities + to your applications with the minimum maintenance needed. This + system offers the developer the chance to use either GObject/C + or Vala to implement new modules and also comes with an easy + integration method to add them to the existing application in no + time. + + + Althought you can write your own applications, the library is + thought to use the shipped application and link the modules + to it, in order to ease the process. The linking of the + application and the modules will be described in the section + about adding new modules and an example of the process will be + shown in a tutorial. + + + The hedera library also comes with an Anjuta IDE project + wizzard to help on the creation of the Hedera Module project + and integrate it with the GNU Build System. As the motivation + of the Hedera Library Project was to use the library from Vala, + this project wizzard generates a Vala project. + + + \ No newline at end of file diff --git a/docs/reference/hedera/module-tutorial.xml b/docs/reference/hedera/module-tutorial.xml new file mode 100644 index 0000000..351c97d --- /dev/null +++ b/docs/reference/hedera/module-tutorial.xml @@ -0,0 +1,76 @@ + + + + + Creating a module + 0 + Hedera Library + + + + Creating a module + + Tutorial on how to create an Hedera Module + + + + + This section is a tutorial on how to create a new Hedera Module, + here you will learn about the basic programming to set up a + module, then you'll need to add it to the rest of the Hedera + execution environment, to know how to do it, read the chapter + "Adding modules" of the Hedera manual. + + + + The alternatives + + Before starting you have to decide if you're going to use the + Anjuta IDE or not. If you do so, you can use the project wizzard + to generate the tamplate of the project and the build system. If + you can't use the Anjuta IDE, you'll need to write your build + scripts in your preferred build environment. In this tutorial + you are supposed to use the Anjuta IDE, if you don't, the code + will be the same, so you can follow the next tutorials either way. + + + + Setting up the Anjuta project + + As said before, the Hedera Library comes with an Anjuta project + wizzard to help the easy creation of a new Hedera Module on Vala. + To include this wizzard to Anjuta 2.31 or grater you can run + Anjuta passing the path to the module file as an argument. + For previous versions of Anjuta you'll need to unzip the + hedera-mod.wiz.tgz file to the corresponding data directory named + anjuta/project, placed under ~/.local on most distributions. With + the former method Anjuta will do the same by itself. As the + dependancies of the hedera library go beyond these versions, it's + most likely that you have a version greater than the 2.31. + + + When Anjuta starts, it will show the wizzard dialog that will + guide you in the creation of the module. + + + After following the wizzard, you'll have a clean Hedera Module, + you can now verify you have everything you need to work by + building the module. As you try to build the project, Anjuta will + prompt you for the configuration options of the project, configure + the project without options (you can add options like --enable-debug + or others, that will be discussed later). If you can build you are + good to go with the next tutorials. Else you'll need to check the + errors prompted by the build system and fix them, probably + installing missing packages on your system. + + + + Setting up a GNU make project + + To set up a project using a simple GNU make script you'll need to + do some things that will be described here but are not written yet. + + + diff --git a/docs/reference/hedera/running.xml b/docs/reference/hedera/running.xml new file mode 100644 index 0000000..44de8cb --- /dev/null +++ b/docs/reference/hedera/running.xml @@ -0,0 +1,53 @@ + + + + + Running applications + 0 + Hedera Library + + + + Running Hedera applications + + How to run an Hedera application + + + + + Custom modules + + To run your own modules you need to install them in the + search directories of Hedera, these are ${libdir}/hedera/module + for the binary objects (.so files) and ${datadir}/hedera/module + for additional data, thus the GUI files and the configuration file + and also, optionally any additional data you want to add to your + module should be here. + + + It is also possible to put your files in any other path and tell + Hedera where to look for them. To do so, set the following + environment variables before executing Hedera: VN_MODULE_LIB_PATH, + the list of directories put in this variable will be used to look + for the binary files of the module; VN_MODULE_DATA_PATH, this list + of directories must hold the path to the data of the module. + + Example modules + + Once you've succesfully compiled the library you can try and + run some of the provided modules (some of wich you can write + by yourself following the tutorials included in this manual). + If the installation went as expected you'll just need to + run the Hedera executable. The executable will be in the + "bin" directory of the installation tree. If you installed the + library in a folder that is included in the PATH environment + variable, you'll just need to write 'hedera' in the command + line. The library will load the modules at runtime. The details + on how to tell the hedera execution environment where to look + for your modules are explained in + Adding Modules. + + + diff --git a/glade/Makefile.am b/glade/Makefile.am new file mode 100644 index 0000000..56b9ac0 --- /dev/null +++ b/glade/Makefile.am @@ -0,0 +1,23 @@ +include $(top_srcdir)/Makefile.decl + +gladevn_lib_LTLIBRARIES = libgladevn.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(gladeui_CFLAGS) +libgladevn_la_LIBADD = \ + $(top_builddir)/vn/libvn.la \ + $(gladeui_LIBS) +libgladevn_la_LDFLAGS = -avoid-version +libgladevn_la_SOURCES = \ + glade-vn.h \ + glade-vn.c \ + glade-vn-batch.c + +gladevn_data_DATA = vn.xml + +EXTRA_DIST = $(gladevn_data_DATA) + +install-data-hook: + rm -f $(DESTDIR)$(gladevn_libdir)/libgladevn.la + rm -f $(DESTDIR)$(gladevn_libdir)/libgladevn.a \ No newline at end of file diff --git a/glade/glade-vn-batch.c b/glade/glade-vn-batch.c new file mode 100644 index 0000000..409e8f3 --- /dev/null +++ b/glade/glade-vn-batch.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "glade-vn.h" + +gboolean glade_vn_batch_add_verify (GladeWidgetAdaptor * adaptor, + VnBatch * container, GObject * child, gboolean user_feedback) +{ + if (G_IS_OBJECT (child)) + return TRUE; + + if (user_feedback) + { + GladeWidgetAdaptor * object_adaptor = + glade_widget_adaptor_get_by_type (G_TYPE_OBJECT); + + glade_util_ui_message (glade_app_get_window () + ,GLADE_UI_INFO + ,NULL + ,"Only objects of type %s can be added to objects of type %s." + ,glade_widget_adaptor_get_title (object_adaptor) + ,glade_widget_adaptor_get_title (adaptor) + ); + } + + return FALSE; +} + +void glade_vn_batch_add_child (GladeWidgetAdaptor * adaptor, + VnBatch * group, GObject * child) +{ + if (G_IS_OBJECT (child)) + vn_batch_add (group, child); +} + +void glade_vn_batch_remove_child (GladeWidgetAdaptor * adaptor, + VnBatch * group, GObject * child) +{ + if (G_IS_OBJECT (child)) + vn_batch_remove (group, child); +} + +void glade_vn_batch_replace_child (GladeWidgetAdaptor * adaptor, + GObject * container, GObject * current, GObject * new) +{ + VnBatch * group = VN_BATCH (container); + glade_vn_batch_remove_child (adaptor, group, current); + glade_vn_batch_add_child (adaptor, group, new); +} + +GList * glade_vn_batch_get_children (GladeWidgetAdaptor * adaptor, + VnBatch * group) +{ + return vn_batch_get_objects (group); +} diff --git a/glade/glade-vn.c b/glade/glade-vn.c new file mode 100644 index 0000000..3f12661 --- /dev/null +++ b/glade/glade-vn.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "glade-vn.h" + +void glade_vn_init () +{ + g_message ("Hedera objects loaded!"); +} \ No newline at end of file diff --git a/glade/glade-vn.h b/glade/glade-vn.h new file mode 100644 index 0000000..0000d41 --- /dev/null +++ b/glade/glade-vn.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GLADE_VN_H +#define GLADE_VN_H + +#include +#include + +void glade_vn_init (); + +#endif \ No newline at end of file diff --git a/glade/glade.sh b/glade/glade.sh new file mode 100755 index 0000000..ff01e02 --- /dev/null +++ b/glade/glade.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +MODULE_PATH=../build/glade/ + +export GLADE_MODULE_SEARCH_PATH="$MODULE_PATH/.libs" +export GLADE_CATALOG_SEARCH_PATH=. +glade diff --git a/glade/vn.xml b/glade/vn.xml new file mode 100644 index 0000000..cffdac7 --- /dev/null +++ b/glade/vn.xml @@ -0,0 +1,150 @@ + + + + + glade_vn_init + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + glade_vn_batch_add_verify + glade_vn_batch_add_child + glade_vn_batch_remove_child + glade_vn_batch_get_children + glade_vn_batch_replace_child + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gvn/Makefile.am b/gvn/Makefile.am new file mode 100644 index 0000000..719eba6 --- /dev/null +++ b/gvn/Makefile.am @@ -0,0 +1,63 @@ +include $(top_srcdir)/Makefile.decl + +gvn_lib_LTLIBRARIES = libgvn.la +gvn_include_HEADERS = \ + gvn.h \ + gvn-misc.h \ + gvn-null.h \ + gvn-time.h \ + gvn-param.h \ + gvn-param-spec.h \ + gvn-value.h + +AM_CPPFLAGS = $(glib_CFLAGS) +libgvn_la_LIBADD = $(glib_LIBS) +libgvn_la_SOURCES = \ + $(gvn_include_HEADERS) \ + gvn-misc.c \ + gvn-null.c \ + gvn-time.c \ + gvn-param.c \ + gvn-param-spec.c \ + gvn-value.c + +pkgconfig_DATA = gvn.pc + +EXTRA_DIST = gvn.pc.in + +DISTCLEANFILES = gvn.pc + +if ENABLE_VALA +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_SCANNER_ARGS = $(GIR_SCANNER_ARGS) + +introspection_sources = $(libgvn_la_SOURCES) + +Gvn-$(VERSION).gir: $(gvn_lib_LTLIBRARIES) + Gvn_@uVERSION@_gir_INCLUDES = GObject-2.0 + Gvn_@uVERSION@_gir_LIBS = $(gvn_lib_LTLIBRARIES) + Gvn_@uVERSION@_gir_FILES = $(introspection_sources) + Gvn_@uVERSION@_gir_EXPORT_PACKAGES = gvn + INTROSPECTION_GIRS = Gvn-$(VERSION).gir + +gir_DATA = $(INTROSPECTION_GIRS) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) + +$(vapis)/gvn.vapi: $(INTROSPECTION_GIRS) $(vapidata)/Gvn-$(VERSION).metadata + $(vapigen_v)$(VAPIGEN) -q \ + --directory $(vapis) \ + --metadatadir $(vapidata) \ + --library gvn \ + Gvn-$(VERSION).gir + +vapi_DATA = $(vapis)/gvn.vapi + +CLEANFILES += $(vapi_DATA) + +endif +endif diff --git a/gvn/gvn-misc.c b/gvn/gvn-misc.c new file mode 100644 index 0000000..4c65772 --- /dev/null +++ b/gvn/gvn-misc.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "gvn-misc.h" + +/** + * SECTION: gvn-misc + * @Short_description: Miscelaneous utility functions + * @Title: Miscelaneous utility functions + **/ + +const gchar * GVN_ABR_WDAY[] = {N_("Err"), + N_("Mon"), N_("Tue"), N_("Wed"), N_("Thu"), N_("Fri"), N_("Sat"), N_("Sun") +}; + +const gchar * GVN_ABR_MONTH[] = {N_("Err"), + N_("Jan"), N_("Feb"), N_("Mar"), N_("Apr"), N_("May"), N_("Jun"), + N_("Jul"), N_("Aug"), N_("Sep"), N_("Oct"), N_("Nov"), N_("Dec"), +}; + +/** + * GETTEXT_PACKAGE: (skip) + **/ + +/** + * gvn_object_warning: + * @instance: the object instance within which the warning was emitted + * @message: (allow-none): a string with the message to show or #NULL + * + * Prints a more verbose warning message than g_warning(), showing the type of + * the object @instance, the file and line from where it's been called and + * @message, if any. + **/ + +/** + * gvn_key_file_save: + * @obj: a #GKeyFile + * @file: the file name + * + * Creates an ini file with the contents of @obj. + **/ +void gvn_key_file_save (GKeyFile * obj, const gchar * file) +{ + gsize len; + gchar * aux; + + aux = g_key_file_to_data (obj, &len, NULL); + + if (len > 0) + g_file_set_contents (file, aux, len, NULL); + + g_free (aux); +} + +/** + * gvn_encode: + * @string: the string to be encoded. + * + * Encodes an string using base64. + * + * Return value: the encoded string. + **/ +gchar * gvn_encode (const gchar * string) +{ + if (string) + return g_base64_encode ((guchar *) string, strlen (string)); + else + return NULL; +} + +/** + * gvn_decode: + * @string: the string to be decoded. + * + * Decodes an string using base64. + * + * Return value: the decoded string. + **/ +gchar * gvn_decode (const gchar * string) +{ + if (string) + { + gsize len; + gchar * base64; + gchar * str; + + base64 = (gchar *) g_base64_decode (string, &len); + str = g_new (gchar, len + 1); + g_memmove (str, base64, len); + str[len] = '\0'; + g_free (base64); + + return str; + } + else + return NULL; +} diff --git a/gvn/gvn-misc.h b/gvn/gvn-misc.h new file mode 100644 index 0000000..a3dae15 --- /dev/null +++ b/gvn/gvn-misc.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GVN_AUX_H +#define GVN_AUX_H + +#define GETTEXT_PACKAGE "hedera" +#include +#include + +void gvn_key_file_save (GKeyFile * obj, const gchar * file); +gchar * gvn_encode (const gchar * string); +gchar * gvn_decode (const gchar * string); + +extern const gchar * GVN_ABR_WDAY[]; +extern const gchar * GVN_ABR_MONTH[]; + +#endif diff --git a/gvn/gvn-null.c b/gvn/gvn-null.c new file mode 100644 index 0000000..df78bfe --- /dev/null +++ b/gvn/gvn-null.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gvn-null.h" + +/** + * SECTION: gvn-null + * @Short_description: a boxed type to represent a null-valued #GValue + * @Title: GvnNull + **/ + +static GvnNull * gvn_null_copy (GvnNull * obj) +{ + return NULL; +} + +static void gvn_null_free (GvnNull * obj) {} + +G_DEFINE_BOXED_TYPE (GvnNull, gvn_null, gvn_null_copy, gvn_null_free); \ No newline at end of file diff --git a/gvn/gvn-null.h b/gvn/gvn-null.h new file mode 100644 index 0000000..c08cd6c --- /dev/null +++ b/gvn/gvn-null.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GVN_NULL_H +#define GVN_NULL_H + +#include + +/** + * GVN_TYPE_NULL: + * + * The #GType for a boxed type holding a #GvnNull. + **/ +#define GVN_TYPE_NULL (gvn_null_get_type()) + +/** + * GvnNull: + * + * Primary struct to handle the NULL type. This struct has no members. + **/ +typedef struct _GvnNull GvnNull; + +struct _GvnNull; + +GType gvn_null_get_type (); + +#endif diff --git a/gvn/gvn-param-spec.c b/gvn/gvn-param-spec.c new file mode 100644 index 0000000..515813e --- /dev/null +++ b/gvn/gvn-param-spec.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gvn-param-spec.h" +#include "gvn-misc.h" +#include "gvn-value.h" + +/** + * SECTION: gvn-param-spec + * @Short_description: + * @Title: GvnParamSpec + **/ + +/** + * gvn_param_spec_new: + * + * Creates a new #GvnParamSpec. + * + * Return value: the new #GvnParamSpec + **/ +GvnParamSpec * gvn_param_spec_new () +{ + return gvn_param_spec_new_with_attrs (G_TYPE_NONE, TRUE, TRUE, NULL); +} + +/** + * gvn_param_spec_new_with_attrs: + * @gtype: the #GType + * @editable: a %gboolean indicating whether it will be editable + * @null: a %gboolean indicating whether it can be null + * @def: the default #GValue + * + * Creates a new #GvnParamSpec. + * + * Return value: the new #GvnParamSpec + **/ +GvnParamSpec * gvn_param_spec_new_with_attrs (GType gtype, + gboolean editable, gboolean null, const GValue * def) +{ + g_return_val_if_fail (G_IS_VALUE (def) || !def, NULL); + + GvnParamSpec * obj = g_new (GvnParamSpec, 1); + obj->gtype = gtype; + obj->editable = editable; + obj->null = null; + obj->def = NULL; + gvn_param_spec_set_default (obj, def); + return obj; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * gvn_param_spec_get_gtype: + * @obj: a #GvnParamSpec + * + * Gets the type of @obj. + * + * Return value: the type + **/ +GType gvn_param_spec_get_gtype (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, G_TYPE_INVALID); + + return obj->gtype; +} + +/** + * gvn_param_spec_set_gtype: + * @obj: a #GvnParamSpec + * @gtype: the #GType + **/ +void gvn_param_spec_set_gtype (GvnParamSpec * obj, GType gtype) +{ + g_return_if_fail (obj); + g_return_if_fail (G_TYPE_IS_VALUE_TYPE (gtype) || gtype == G_TYPE_NONE); + + obj->gtype = gtype; +} + +/** + * gvn_param_spec_get_editable: + * @obj: a #GvnParamSpec + **/ +gboolean gvn_param_spec_get_editable (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, FALSE); + + return obj->editable; +} + +/** + * gvn_param_spec_set_editable: + * @obj: a #GvnParamSpec + * @editable: a %gboolean indicating whether it will be editable + * + * Sets if the value can be edited. + **/ +void gvn_param_spec_set_editable (GvnParamSpec * obj, gboolean editable) +{ + g_return_if_fail (obj); + + obj->editable = editable; +} + +/** + * gvn_param_spec_get_null: + * @obj: a #GvnParamSpec + **/ +gboolean gvn_param_spec_get_null (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, FALSE); + + return obj->null; +} + +/** + * gvn_param_spec_set_null: + * @obj: a #GvnParamSpec + * @null: a %gboolean indicating whether it can be null + * + * Set if the value can be null. + **/ +void gvn_param_spec_set_null (GvnParamSpec * obj, gboolean null) +{ + g_return_if_fail (obj); + + obj->null = null; +} + +/** + * gvn_param_spec_get_default: + * @obj: a #GvnParamSpec + * + * Gets the default value. + * + * Return value: the #GValue or %NULL if it havent + **/ +const GValue * gvn_param_spec_get_default (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, NULL); + + return obj->def; +} + +/** + * gvn_param_spec_set_default: + * @obj: a #GvnParamSpec + * @def: the default #GValue + * + * Sets the default value. + **/ +void gvn_param_spec_set_default (GvnParamSpec * obj, const GValue * def) +{ + g_return_if_fail (obj); + g_return_if_fail (G_IS_VALUE (def) || !def); + + if (obj->def) + { + g_value_unset (obj->def); + g_free (obj->def); + obj->def = NULL; + } + if (def) + { + GValue * value = g_new0 (GValue, 1); + g_value_init (value, G_VALUE_TYPE (def)); + g_value_copy (def, value); + obj->def = value; + } +} + +/** + * gvn_param_spec_merge: + * @obj: a #GvnParamSpec + * @merge: (allow-none): a #GvnParamSpec + * + * Merges the attributes of @merge into @obj remaining the most restrictive. + **/ +void gvn_param_spec_merge (GvnParamSpec * obj, const GvnParamSpec * merge) +{ + g_return_if_fail (obj); + + if (merge) + { + obj->editable = obj->editable && merge->editable; + obj->null = obj->null && merge->null; + + if (obj->gtype == G_TYPE_NONE) + obj->gtype = merge->gtype; + if (!obj->def && merge->def) + gvn_param_spec_set_default (obj, merge->def); + } +} + +/** + * gvn_param_spec_ccopy_value: + * @obj: a #GvnParamSpec + * @src: source value to be copied + * @dst: destination for the copied value + * + * Return value: %TRUE if the value has been copied, %FALSE otherwise + **/ +gboolean gvn_param_spec_ccopy_value (const GvnParamSpec * obj, const GValue * src, GValue * dst) +{ + g_return_val_if_fail (obj, FALSE); + g_return_val_if_fail (G_IS_VALUE (src), FALSE); + g_return_val_if_fail (G_IS_VALUE (dst), FALSE); + + if (!gvn_value_is_null (src) + && G_VALUE_TYPE (src) != obj->gtype + && obj->gtype != G_TYPE_NONE) + { + if (gvn_value_is_null (dst)) + { + g_value_unset (dst); + g_value_init (dst, obj->gtype); + } + + g_value_transform (src, dst); + } + else + return gvn_value_ccopy (src, dst); + + return TRUE; +} + +/** + * gvn_param_spec_validate: + * @obj: #GvnParamSpec object where @GValue wants to be validated + * @value: new value + * @err: (out) (allow-none): the return location for an allocated @GError, or + * %NULL to ignore errors. + * + * It checks if the value is valid to be saved into @obj + * + * Return value: gboolean + **/ +gboolean gvn_param_spec_validate (const GvnParamSpec * obj, const GValue * value, GError ** err) +{ + g_return_val_if_fail (obj, FALSE); + + gboolean is_null = gvn_value_is_null (value); + + if (!obj->editable) + g_set_error (err + ,GVN_PARAM_SPEC_LOG_DOMAIN + ,GVN_PARAM_SPEC_ERROR_NOT_EDITABLE + ,_("Param not editable")); + else if (is_null && !obj->null) + g_set_error (err + ,GVN_PARAM_SPEC_LOG_DOMAIN + ,GVN_PARAM_SPEC_ERROR_NOT_NULL + ,_("Param can't be NULL")); + else if (!is_null && obj->gtype != G_TYPE_NONE + && !g_value_type_transformable (obj->gtype, G_VALUE_TYPE (value))) + g_set_error (err + ,GVN_PARAM_SPEC_LOG_DOMAIN + ,GVN_PARAM_SPEC_ERROR_WRONG_TYPE + ,_("Incompatible type for this param")); + else + return TRUE; + + return FALSE; +} + +/** + * gvn_param_spec_unset: + * @obj: a #GvnParamSpec + **/ +void gvn_param_spec_unset (GvnParamSpec * obj) +{ + g_return_if_fail (obj); + + obj->null = TRUE; + obj->editable = TRUE; + obj->gtype = G_TYPE_NONE; + gvn_param_spec_set_default (obj, NULL); +} + +/** + * gvn_param_spec_copy: + * @obj: a #GvnParamSpec + * + * Makes a copy of @obj with the same attributes. + * + * Return value: the new created #GvnParamSpec + **/ +GvnParamSpec * gvn_param_spec_copy (const GvnParamSpec * obj) +{ + g_return_val_if_fail (obj, NULL); + + return gvn_param_spec_new_with_attrs (obj->gtype, + obj->editable, obj->null, obj->def); +} + +/** + * gvn_param_spec_free: + * @obj: a #GvnParamSpec + * + * Frees the memory used by @obj + **/ +void gvn_param_spec_free (GvnParamSpec * obj) +{ + g_return_if_fail (obj); + + gvn_param_spec_set_default (obj, NULL); + g_free (obj); +} + +G_DEFINE_BOXED_TYPE (GvnParamSpec, gvn_param_spec, gvn_param_spec_copy, gvn_param_spec_free); diff --git a/gvn/gvn-param-spec.h b/gvn/gvn-param-spec.h new file mode 100644 index 0000000..b93811d --- /dev/null +++ b/gvn/gvn-param-spec.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GVN_PARAM_SPEC_H +#define GVN_PARAM_SPEC_H + +#include + +#define GVN_PARAM_SPEC_LOG_DOMAIN (g_quark_from_string ("GvnParamSpec")) + +/** + * GVN_TYPE_PARAM_SPEC: + * + * The #GType for a boxed type holding a #GvnParamSpec. + **/ +#define GVN_TYPE_PARAM_SPEC (gvn_param_spec_get_type ()) + +typedef struct _GvnParamSpec GvnParamSpec; + +/** + * GvnParamSpec: + * @gtype: Type of the param + * @editable: %TRUE if value is editable + * @null: %TRUE if value can be of type %GVN_TYPE_NULL + * @def: Default value + **/ +struct _GvnParamSpec +{ + GType gtype; + gboolean editable; + gboolean null; + GValue * def; +}; + +/** + * GvnParamError: + * @GVN_PARAM_SPEC_ERROR_NOT_EDITABLE: The value can not be edited + * @GVN_PARAM_SPEC_ERROR_NOT_NULL: The value can not be NULL + * @GVN_PARAM_SPEC_ERROR_WRONG_TYPE: The value have a wrong type + **/ +typedef enum +{ + GVN_PARAM_SPEC_ERROR_NOT_EDITABLE = 1 + ,GVN_PARAM_SPEC_ERROR_NOT_NULL + ,GVN_PARAM_SPEC_ERROR_WRONG_TYPE +} +GvnParamError; + +GType gvn_param_spec_get_type (); +GvnParamSpec * gvn_param_spec_new (); +GvnParamSpec * gvn_param_spec_new_with_attrs (GType gtype, gboolean editable, gboolean null, const GValue * def); +GType gvn_param_spec_get_gtype (const GvnParamSpec * obj); +void gvn_param_spec_set_gtype (GvnParamSpec * obj, GType gtype); +gboolean gvn_param_spec_get_editable (const GvnParamSpec * obj); +void gvn_param_spec_set_editable (GvnParamSpec * obj, gboolean editable); +gboolean gvn_param_spec_get_null (const GvnParamSpec * obj); +void gvn_param_spec_set_null (GvnParamSpec * obj, gboolean null); +const GValue * gvn_param_spec_get_default (const GvnParamSpec * obj); +void gvn_param_spec_set_default (GvnParamSpec * obj, const GValue * def); +void gvn_param_spec_merge (GvnParamSpec * obj, const GvnParamSpec * merge); +gboolean gvn_param_spec_ccopy_value (const GvnParamSpec * obj, const GValue * src, GValue * dst); +gboolean gvn_param_spec_validate (const GvnParamSpec * obj, const GValue * value, GError ** err); +void gvn_param_spec_unset (GvnParamSpec * obj); +GvnParamSpec * gvn_param_spec_copy (const GvnParamSpec * obj); +void gvn_param_spec_free (GvnParamSpec * obj); + +#endif \ No newline at end of file diff --git a/gvn/gvn-param.c b/gvn/gvn-param.c new file mode 100644 index 0000000..94e92fd --- /dev/null +++ b/gvn/gvn-param.c @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gvn-misc.h" +#include "gvn-param.h" +#include "gvn-value.h" + +/** + * SECTION: gvn-param + * @Short_description: + * @Title: GvnParam + **/ +G_DEFINE_TYPE (GvnParam, gvn_param, G_TYPE_INITIALLY_UNOWNED); + +enum { + VALUE_CHANGED + ,SPEC_CHANGED + ,STATUS_CHANGED + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +/** + * gvn_param_new: + * + * Creates a new #GvnParam. + * + * Return value: the new #GvnParam + **/ +GvnParam * gvn_param_new () +{ + return g_object_new (GVN_TYPE_PARAM, NULL); +} + +/** + * gvn_param_new_with_spec: + * @spec: the spec of param. + * + * Creates a new #GvnParam. + * + * Return value: the new #GvnParam + **/ +GvnParam * gvn_param_new_with_spec (const GvnParamSpec * spec) +{ + return g_object_new (GVN_TYPE_PARAM, "spec", spec, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void gvn_param_set_status (GvnParam * obj, GvnParamStatus status) +{ + obj->status = status; + g_signal_emit (obj, signals[STATUS_CHANGED], 0, status); +} + +static void gvn_param_set_error (GvnParam * obj, GError * error) +{ + g_clear_error (&obj->error); + obj->error = error; + gvn_param_set_status (obj, GVN_PARAM_STATUS_ERROR); +} + +static void gvn_param_set_spec (GvnParam * obj, const GvnParamSpec * spec) +{ + GSList * n; + + if (obj->mode == GVN_PARAM_MASTER) + for (n = obj->slaves; n; n = n->next) + gvn_param_spec_merge (GVN_PARAM (n->data)->spec, spec); + + gvn_param_spec_unset (obj->spec); + gvn_param_spec_merge (obj->spec, spec); + g_signal_emit (obj, signals[SPEC_CHANGED], 0); +} + +static void gvn_param_put_value (GvnParam * obj, const GValue * value) +{ + if (gvn_param_spec_ccopy_value (obj->spec, value, obj->value)) + { + GError * tmp_err = NULL; + + if (obj->mode == GVN_PARAM_SLAVE) + { + if (!gvn_param_set_value (obj->master, obj->value, &tmp_err)) + gvn_param_set_error (obj, tmp_err); + } + else if (obj->mode == GVN_PARAM_MASTER) + { + GSList * n; + + for (n = obj->slaves; n; n = n->next) + if (!gvn_param_set_value (n->data, obj->value, &tmp_err)) + gvn_param_set_error (n->data, tmp_err); + } + + gvn_param_value_changed (obj); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * gvn_param_get_value: + * @param: a #GvnParam where be took the value + * + * Gets the value of param. + * + * Return value: (transfer none): the #GValue + **/ +const GValue * gvn_param_get_value (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + return obj->value; +} + +/** + * gvn_param_set_value: + * @param: #GvnParam object where @value wants to be validated. + * @value: new value. + * @err: (out) (allow-none): the return location for an allocated @GError, or + * NULL to ignore errors. + * + * Sets @value into @param. + * + * Return value: %TRUE if assigment is valid, %FALSE otherwise. + **/ +gboolean gvn_param_set_value (GvnParam * obj, const GValue * value, GError ** err) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), FALSE); + g_return_val_if_fail (G_IS_VALUE (value), FALSE); + + if (gvn_param_spec_validate (obj->spec, value, err)) + { + gvn_param_put_value (obj, value); + return TRUE; + } + + return FALSE; +} + +/** + * gvn_param_get_spec: + * @param: #GvnParam object + * + * Gets the #GvnParamSpec of @obj + * + * Return value: (transfer none): the #GvnParamSpec + **/ +const GvnParamSpec * gvn_param_get_spec (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + + return obj->spec; +} + +/** + * gvn_param_get_master: + * @sparam: a #GvnParam where be took the value + * + * Gets the master param of @obj + * + * Return value: (transfer none): the master #GvnParam or %NULL if it haven't. + **/ +GvnParam * gvn_param_get_master (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + + return obj->master; +} + +/** + * gvn_param_bind: + * @src: #GvnParam object + * @dst: #GvnParam master + * + * Sets @dst as a master of @src + **/ +void gvn_param_set_master (GvnParam * obj, GvnParam * master) +{ + g_return_if_fail (GVN_IS_PARAM (obj)); + g_return_if_fail (GVN_IS_PARAM (master) || !master); + g_return_if_fail (obj->mode != GVN_PARAM_MASTER); + g_return_if_fail (obj->mode != GVN_PARAM_SLAVE || !master); + g_return_if_fail (!master || master->mode != GVN_PARAM_SLAVE); + + if (master) + { + obj->master = g_object_ref_sink (master); + obj->mode = GVN_PARAM_SLAVE; + + master->slaves = g_slist_prepend (master->slaves, obj); + master->mode = GVN_PARAM_MASTER; + } + else + { + if (obj->mode == GVN_PARAM_SLAVE) + { + GSList ** slaves = &obj->master->slaves; + + if (!(*slaves = g_slist_remove (*slaves, obj))) + master->mode = GVN_PARAM_FREE; + } + + g_clear_object (&obj->master); + obj->mode = GVN_PARAM_FREE; + } +} + +/** + * gvn_param_get_status: + * @obj: #GvnParam object + * + * Gets the status of @obj + * + * Return value: the current status of the param. + **/ +GvnParamStatus gvn_param_get_status (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), GVN_PARAM_STATUS_OK); + + return obj->status; +} + +/** + * gvn_param_get_error: + * @obj: #GvnParam object + * + * Gets the error of @obj + * + * Return value: the current error of the param. + **/ +const GError * gvn_param_get_error (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + + return obj->error; +} + +/** + * gvn_param_get_null: + * @param: the #GvnParam + **/ +gboolean gvn_param_get_null (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), FALSE); + + return gvn_param_spec_get_null (obj->spec); +} + +/** + * gvn_param_get_editable: + * @param: the #GvnParam + **/ +gboolean gvn_param_get_editable (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), FALSE); + + return gvn_param_spec_get_editable (obj->spec); +} + +/** + * gvn_param_get_default: + * @obj: the #GvnParam + **/ +const GValue * gvn_param_get_default (GvnParam * obj) +{ + g_return_val_if_fail (GVN_IS_PARAM (obj), NULL); + + return gvn_param_spec_get_default (obj->spec); +} + +/** + * gvn_param_set_to_default: + * @obj: the #GvnParam + **/ +void gvn_param_set_to_default (GvnParam * obj) +{ + g_return_if_fail (GVN_IS_PARAM (obj)); + + gvn_param_set_value (obj, + gvn_param_spec_get_default (obj->spec), NULL); +} + +/** + * gvn_param_value_changed: + * @obj: a #GvnParam + * + * Emits the "value-changed" signal for that param. + **/ +void gvn_param_value_changed (GvnParam * obj) +{ + g_signal_emit (obj, signals[VALUE_CHANGED], 0, obj->value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_VALUE = 1 + ,PROP_MASTER + ,PROP_MODE + ,PROP_STATUS + ,PROP_SPEC + ,PROP_GTYPE + ,PROP_EDITABLE + ,PROP_NULL + ,PROP_DEFAULT +}; + +static void gvn_param_set_property (GvnParam * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_VALUE: + gvn_param_set_value (obj, g_value_get_boxed (value), NULL); + break; + case PROP_MASTER: + gvn_param_set_master (obj, g_value_get_object (value)); + break; + case PROP_SPEC: + gvn_param_spec_merge (obj->spec, g_value_get_boxed (value)); + break; + case PROP_GTYPE: + gvn_param_spec_set_gtype (obj->spec, g_value_get_gtype (value)); + break; + case PROP_EDITABLE: + gvn_param_spec_set_editable (obj->spec, g_value_get_boolean (value)); + break; + case PROP_NULL: + gvn_param_spec_set_null (obj->spec, g_value_get_boolean (value)); + break; + case PROP_DEFAULT: + gvn_param_spec_set_default (obj->spec, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void gvn_param_get_property (GvnParam * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_VALUE: + g_value_set_boxed (value, obj->value); + break; + case PROP_MASTER: + g_value_set_object (value, obj->master); + break; + case PROP_MODE: + g_value_set_int (value, obj->mode); + break; + case PROP_STATUS: + g_value_set_boolean (value, obj->status); + break; + case PROP_SPEC: + g_value_set_boxed (value, obj->spec); + break; + case PROP_GTYPE: + g_value_set_gtype (value, gvn_param_spec_get_gtype (obj->spec)); + break; + case PROP_EDITABLE: + g_value_set_boolean (value, gvn_param_spec_get_editable (obj->spec)); + break; + case PROP_NULL: + g_value_set_boolean (value, gvn_param_spec_get_null (obj->spec)); + break; + case PROP_DEFAULT: + g_value_set_boxed (value, gvn_param_spec_get_default (obj->spec)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void gvn_param_init (GvnParam * obj) +{ + obj->error = NULL; + obj->master = NULL; + obj->slaves = NULL; + obj->mode = GVN_PARAM_FREE; + obj->status = GVN_PARAM_STATUS_OK; + obj->spec = gvn_param_spec_new (); + + obj->value = g_new0 (GValue, 1); + g_value_init (obj->value, GVN_TYPE_NULL); +} + +static void gvn_param_finalize (GvnParam * obj) +{ + gvn_param_set_master (obj, NULL); + gvn_param_spec_free (obj->spec); + g_clear_error (&obj->error); + g_value_unset (obj->value); + g_free (obj->value); + G_OBJECT_CLASS (gvn_param_parent_class)->finalize (G_OBJECT (obj)); +} + +static void gvn_param_class_init (GvnParamClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) gvn_param_finalize; + k->set_property = (GObjectSetPropertyFunc) gvn_param_set_property; + k->get_property = (GObjectGetPropertyFunc) gvn_param_get_property; + klass->put_value = gvn_param_put_value; + klass->set_spec = gvn_param_set_spec; + klass->set_status = gvn_param_set_status; + klass->set_error = gvn_param_set_error; + + signals[VALUE_CHANGED] = g_signal_new ("value-changed", + GVN_TYPE_PARAM, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, G_TYPE_VALUE + ); + signals[SPEC_CHANGED] = g_signal_new ("spec-changed", + GVN_TYPE_PARAM, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[STATUS_CHANGED] = g_signal_new ("status-changed", + GVN_TYPE_PARAM, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + + g_object_class_install_property (k, PROP_VALUE, + g_param_spec_boxed ("value" + ,_("Value") + ,_("The value of the param") + ,G_TYPE_VALUE + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MASTER, + g_param_spec_object ("master" + ,_("Master") + ,_("The master GvnParam of this parameter") + ,GVN_TYPE_PARAM + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MODE, + g_param_spec_uint ("mode" + ,_("Mode") + ,_("The mode of the parameter") + ,0 ,256 ,GVN_PARAM_FREE + ,G_PARAM_READABLE + )); + g_object_class_install_property (k, PROP_STATUS, + g_param_spec_uint ("status" + ,_("Status") + ,_("The current status of the parameter") + ,0 ,256 ,GVN_PARAM_STATUS_OK + ,G_PARAM_READABLE + )); + g_object_class_install_property (k, PROP_SPEC, + g_param_spec_boxed ("spec" + ,_("Spec") + ,_("The spec of the parameter") + ,GVN_TYPE_PARAM_SPEC + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_GTYPE, + g_param_spec_gtype ("gtype" + ,_("Glib Type") + ,_("The type of the value") + ,G_TYPE_NONE + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_EDITABLE, + g_param_spec_boolean ("editable" + ,_("Editable") + ,_("Whether the param value can be modified") + ,TRUE + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_NULL, + g_param_spec_boolean ("null" + ,_("Null") + ,_("Whether the param value can be of type GVN_TYPE_NULL") + ,TRUE + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_DEFAULT, + g_param_spec_boxed ("default" + ,_("Default Value") + ,_("The default value") + ,G_TYPE_VALUE + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} diff --git a/gvn/gvn-param.h b/gvn/gvn-param.h new file mode 100644 index 0000000..d36fa34 --- /dev/null +++ b/gvn/gvn-param.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GVN_PARAM_H +#define GVN_PARAM_H + +#include +#include "gvn-param-spec.h" + +#define GVN_PARAM_LOG_DOMAIN (g_quark_from_string ("GvnParam")) + +#define GVN_TYPE_PARAM (gvn_param_get_type ()) +#define GVN_PARAM(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GVN_TYPE_PARAM, GvnParam)) +#define GVN_IS_PARAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GVN_TYPE_PARAM)) +#define GVN_PARAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, GVN_TYPE_PARAM, GvnParamClass)) + +typedef struct _GvnParam GvnParam; +typedef struct _GvnParamClass GvnParamClass; + +/** + * GvnParamMode: + * @GVN_PARAM_FREE: It is not set. + * @GVN_PARAM_SLAVE: It is set as slave. + * @GVN_PARAM_MASTER: It is set as master. + * + * The mode in which is Binded. + **/ +typedef enum +{ + GVN_PARAM_FREE + ,GVN_PARAM_SLAVE + ,GVN_PARAM_MASTER +} +GvnParamMode; + +/** + * GvnParamStatus: + * @GVN_PARAM_STATUS_OK: The parameter is ready. + * @GVN_PARAM_STATUS_BUSY: The parameter is busy. + * @GVN_PARAM_STATUS_ERROR: The parameter value is incompatible to its spec. + * + * The status of the param. + **/ +typedef enum +{ + GVN_PARAM_STATUS_OK + ,GVN_PARAM_STATUS_BUSY + ,GVN_PARAM_STATUS_ERROR +} +GvnParamStatus; + +/** + * GvnParam: + * @slaves: (element-type Gvn.Param): + **/ +struct _GvnParam +{ + GInitiallyUnowned parent; + GValue * value; + GError * error; + GvnParamStatus status; + GvnParamSpec * spec; + GvnParamMode mode; + GvnParam * master; + GSList * slaves; +}; + +struct _GvnParamClass +{ + /* */ + GInitiallyUnownedClass parent; + void (* put_value) (GvnParam * obj, const GValue * value); + void (* set_spec) (GvnParam * obj, const GvnParamSpec * spec); + void (* set_status) (GvnParam * obj, GvnParamStatus status); + void (* set_error) (GvnParam * obj, GError * error); +}; + +GType gvn_param_get_type (); +GvnParam * gvn_param_new (); +GvnParam * gvn_param_new_with_spec (const GvnParamSpec * spec); +const GValue * gvn_param_get_value (GvnParam * param); +gboolean gvn_param_set_value (GvnParam * param, const GValue * value, GError ** err); +const GvnParamSpec * gvn_param_get_spec (GvnParam * param); +GType gvn_param_get_gtype (GvnParam * param); +gboolean gvn_param_get_null (GvnParam * param); +gboolean gvn_param_get_editable (GvnParam * param); +const GValue * gvn_param_get_default (GvnParam * obj); +void gvn_param_set_to_default (GvnParam * obj); +GvnParam * gvn_param_get_master (GvnParam * sparam); +void gvn_param_set_master (GvnParam * sparam, GvnParam * dparam); +void gvn_param_value_changed (GvnParam * obj); +GvnParamStatus gvn_param_get_status (GvnParam * obj); +const GError * gvn_param_get_error (GvnParam * obj); + +#endif \ No newline at end of file diff --git a/gvn/gvn-time.c b/gvn/gvn-time.c new file mode 100644 index 0000000..36cb0fd --- /dev/null +++ b/gvn/gvn-time.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gvn-time.h" + +/** + * SECTION: gvn-time + * @Short_description: a boxed type to represent time with optional time-zone + * @Title: GvnTime + * + * #GvnTime is a boxed type to manage time of a day without specifying the date, + * just hours, minutes, seconds and the time-zone. + **/ +G_DEFINE_BOXED_TYPE (GvnTime, gvn_time, gvn_time_copy, gvn_time_free); + +/** + * gvn_time_copy: + * @obj: a #GvnTime + * + * Copies @obj to a new #GvnTime. + * + * Return value: a new #GvnTime + **/ +GvnTime * gvn_time_copy (GvnTime * obj) +{ + g_return_val_if_fail (obj, NULL); + + return gvn_time_new (obj->hour, obj->min, obj->sec, obj->tz); +} + +/** + * gvn_time_free: + * @obj: a #GvnTime + * + * Releases the memory used by @obj. + **/ +void gvn_time_free (GvnTime * obj) +{ + g_return_if_fail (obj); + g_free (obj); +} + +/** + * gvn_time_new: + * @h: the hour, from 0 to 23 + * @m: the minutes, from 0 to 59 + * @s: the seconds, from 0 to 59 + * @tz: the time-zone, from -12 to 12 + * + * Creates a new #GvnTime with the given parameters, if any parameter is out of + * range, that parameter will be put to 0 instead. + **/ +GvnTime * gvn_time_new (guint h, guint m, guint s, gint tz) +{ + GvnTime * time = g_new (GvnTime, 1); + time->hour = h > 24 ? 0 : h; + time->min = m > 59 ? 0 : m; + time->sec = s > 59 ? 0 : s; + time->tz = tz < -12 || tz > 12 ? 0 : tz; + + return time; +} + +/** + * gvn_time_to_string: + * @obj: a #GvnTime + * @tz: whether or not the time-zone will be used + * + * Returns a newly allocated string containing the time set by @obj. By passing + * %FALSE in @tz, the time-zone is omited from the string. The format is + * hh:mm:ss±tz if @tz is %TRUE and hh:mm:ss otherwise. The string must be freed. + * + * Return value: a string containing the time + **/ +gchar * gvn_time_to_string (GvnTime * obj, gboolean tz) +{ + g_return_val_if_fail (obj, NULL); + if (tz) + return g_strdup_printf ("%d:%d:%d%s%d", obj->hour, obj->min, obj->sec, + obj->tz > 0 ? "+" : "-", obj->tz < 0 ? obj->tz * -1 : obj->tz); + else + return g_strdup_printf ("%d:%d:%d", obj->hour, obj->min, obj->sec); +} diff --git a/gvn/gvn-time.h b/gvn/gvn-time.h new file mode 100644 index 0000000..8cadb5c --- /dev/null +++ b/gvn/gvn-time.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GVN_TIME_H +#define GVN_TIME_H + +#include + +/** + * GVN_TYPE_TIME: + * + * The #GType for a boxed type holding a #GvnTime. + **/ +#define GVN_TYPE_TIME (gvn_time_get_type()) + +/** + * GvnTime: + * @hour: the hour, from 0 to 23 + * @min: the minutes, from 0 to 59 + * @sec: the seconds, from 0 to 59 + * @tz: the time-zone, from -12 to 12 + * + * Primary struct to handle the TIME type. + **/ +typedef struct _GvnTime GvnTime; + +struct _GvnTime +{ + guint hour; + guint min; + guint sec; + gint tz; +}; + +GType gvn_time_get_type (); + +GvnTime * gvn_time_copy (GvnTime * obj); +void gvn_time_free (GvnTime * obj); +GvnTime * gvn_time_new (guint h, guint m, guint s, gint tz); +gchar * gvn_time_to_string (GvnTime * obj, gboolean tz); + +#endif diff --git a/gvn/gvn-value.c b/gvn/gvn-value.c new file mode 100644 index 0000000..ffe9db2 --- /dev/null +++ b/gvn/gvn-value.c @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "gvn-value.h" +#include "gvn-misc.h" + +/** + * SECTION: gvn-value + * @Short_description: additional value manipulation functions + * @Title: GvnValue + * @See_also: #DbForm, #DbConn + * + * This functions are intended to expand or ease the use of the original GObject + * library #GValue utilites. + **/ + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void gvn_value_new_from_string_real (GValue * value, GType type, const gchar * string, gsize len) +{ + switch (type) + { + case G_TYPE_BOOLEAN: + g_value_set_boolean (value, atoi (string)); + break; + case G_TYPE_CHAR: + g_value_set_schar (value, atoi (string)); + break; + case G_TYPE_INT: + g_value_set_int (value, atoi (string)); + break; + case G_TYPE_UINT: + g_value_set_int (value, (guint) atoi (string)); + break; + case G_TYPE_LONG: + g_value_set_long (value, g_ascii_strtoll (string, NULL, 0)); + break; + case G_TYPE_ULONG: + g_value_set_ulong (value, g_ascii_strtoull (string, NULL, 0)); + break; + case G_TYPE_FLOAT: + g_value_set_float (value, atof (string)); + break; + case G_TYPE_DOUBLE: + g_value_set_double (value, g_ascii_strtod (string, NULL)); + break; + case G_TYPE_STRING: + g_value_set_string (value, string); + break; + default: + { + if (len == -1) + len = strlen (string); + + if (type == G_TYPE_DATE) + { + if (len >= 10) + { + GDate * date = g_date_new_dmy ( + atoi (string + 8) + ,atoi (string + 5) + ,atoi (string) + ); + g_value_take_boxed (value, date); + } + else + g_warning ("Gvn: Can't transform string to GDate"); + } + else if (type == G_TYPE_DATE_TIME) + { + if (len >= 19) + { + GDateTime * date_time = g_date_time_new_local ( + atoi (string) + ,atoi (string + 5) + ,atoi (string + 8) + ,atoi (string + 11) + ,atoi (string + 14) + ,atoi (string + 17) + ); + g_value_take_boxed (value, date_time); + } + else + g_warning ("Gvn: Can't transform string to GDateTime"); + } + else if (type == G_TYPE_BYTES) + { + if (len > 0) + g_value_take_boxed (value, g_bytes_new (string, len)); + else + g_warning ("Gvn: Can't transform string to GBytes"); + } + else if (type != GVN_TYPE_NULL) + g_return_if_reached (); + } + } +} + +static void gvn_value_transform_string_to_any (const GValue * src, GValue * dst) +{ + gvn_value_new_from_string_real (dst, + G_VALUE_TYPE (dst), g_value_get_string (src), -1); +} + +static void gvn_value_transform_null_to_any (const GValue * src, GValue * dst) {} + +static void gvn_value_transform_any_to_null (const GValue * src, GValue * dst) +{ + gvn_value_set_null (dst); +} + +static void gvn_value_transform_date_to_string (const GValue * src, GValue * dst) +{ + GDate * date = g_value_get_boxed (src); + + if (date) + { + g_value_take_string (dst, g_strdup_printf ("%04u-%02u-%02u" + ,g_date_get_year (date) + ,g_date_get_month (date) + ,g_date_get_day (date) + )); + } + else + g_value_take_string (dst, NULL); +} + +static void gvn_value_transform_date_time_to_string (const GValue * src, GValue * dst) +{ + gpointer date = g_value_get_boxed (src); + + if (date) + g_value_take_string (dst, g_date_time_format (date, "%Y-%m-%d %T")); + else + g_value_take_string (dst, NULL); +} + +static void gvn_value_transform_date_time_to_date (const GValue * src, GValue * dst) +{ + GDateTime * date_time = g_value_get_boxed (src); + + if (date_time) + { + GDate * date = g_date_new_dmy ( + g_date_time_get_day_of_month (date_time) + ,g_date_time_get_month (date_time) + ,g_date_time_get_year (date_time) + ); + g_value_take_boxed (dst, date); + } + else + g_value_take_boxed (dst, NULL); +} + +static void gvn_value_transform_date_to_date_time (const GValue * src, GValue * dst) +{ + GDate * date = g_value_get_boxed (src); + + if (date) + { + GDateTime * date_time = g_date_time_new_utc ( + g_date_get_year (date) + ,g_date_get_month (date) + ,g_date_get_day (date) + ,0 + ,0 + ,0.0 + ); + g_value_take_boxed (dst, date_time); + } + else + g_value_take_boxed (dst, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * gvn_value_new: + * @type: the type of new #GValue + * + * Creates a new #GValue + * + * Return value: a #GValue. + **/ +GValue * gvn_value_new (GType type) +{ + GValue * value = g_new0 (GValue, 1); + return g_value_init (value, type); +} + +/** + * gvn_value_is_null: + * @value: the value to be checked + * + * Checks if a @value is of type GVN_TYPE_NULL + * + * Return value: %TRUE if its NULL, %FALSE otherwise. + **/ + +/** + * gvn_value_set_null: + * @value: the value to be nulled + * + * Checks if a @value is of type GVN_TYPE_NULL, if not, sets it. + **/ +void gvn_value_set_null (GValue * value) +{ + g_return_if_fail (G_IS_VALUE (value)); + + if (!gvn_value_is_null (value)) + { + g_value_unset (value); + g_value_init (value, GVN_TYPE_NULL); + } +} + +/** + * gvn_value_new_from_string: + * @value: an uninitilized #GValue + * @string: a string to be converted + * @length: length of @string or -1 if its null terminated + * @type: the type to be converted + * + * Creates a new @value from a string + **/ +void gvn_value_new_from_string (GValue * value, GType type, const gchar * string, gsize len) +{ + g_return_if_fail (string != NULL); + + g_value_init (value, type); + gvn_value_new_from_string_real (value, type, string, len); +} + +/** + * gvn_value_new_valist: + * @value: an uninitilized #GValue + * @type: the type of value + * @content: a pointer to the value content + * + * Initializes the @value with the specified type and content. + **/ +void gvn_value_new_with_content (GValue * value, GType type, gpointer content) +{ + g_value_init (value, type); + + switch (type) + { + case G_TYPE_BOOLEAN: + g_value_set_boolean (value, *((gboolean *) content)); + break; + case G_TYPE_CHAR: + g_value_set_schar (value, *((gint8 *) content)); + break; + case G_TYPE_INT: + g_value_set_int (value, *((gint *) content)); + break; + case G_TYPE_UINT: + g_value_set_uint (value, *((guint *) content)); + break; + case G_TYPE_LONG: + g_value_set_long (value, *((glong *) content)); + break; + case G_TYPE_ULONG: + g_value_set_ulong (value, *((gulong *) content)); + break; + case G_TYPE_FLOAT: + g_value_set_float (value, *((gfloat *) content)); + break; + case G_TYPE_DOUBLE: + g_value_set_double (value, *((gdouble *) content)); + break; + case G_TYPE_STRING: + g_value_set_string (value, (gchar *) content); + break; + default: + if (type == G_TYPE_DATE + || type == G_TYPE_BYTES) + g_value_set_boxed (value, content); + else if (G_TYPE_IS_OBJECT (type)) + g_value_set_object (value, content); + else if (type != GVN_TYPE_NULL) + g_return_if_reached (); + } +} + +/** + * gvn_value_get_valist: + * @value: the type of value + * @va: a string to be converted + * + * Sets the value of the initilized @value with the value of next + * element in @va + **/ +void gvn_value_get_valist (const GValue * value, va_list va) +{ + GType type = G_VALUE_TYPE (value); + + switch (type) + { + case G_TYPE_BOOLEAN: + *va_arg (va, gboolean*) = g_value_get_boolean (value); + break; + case G_TYPE_CHAR: + *va_arg (va, gint8*) = g_value_get_schar (value); + break; + case G_TYPE_INT: + *va_arg (va, gint*) = g_value_get_int (value); + break; + case G_TYPE_UINT: + *va_arg (va, guint*) = g_value_get_uint (value); + break; + case G_TYPE_LONG: + *va_arg (va, glong*) = g_value_get_long (value); + break; + case G_TYPE_ULONG: + *va_arg (va, gulong*) = g_value_get_ulong (value); + break; + case G_TYPE_FLOAT: + *va_arg (va, gfloat*) = g_value_get_float (value); + break; + case G_TYPE_DOUBLE: + *va_arg (va, gdouble*) = g_value_get_double (value); + break; + case G_TYPE_STRING: + *va_arg (va, gchar**) = g_value_dup_string (value); + break; + default: + if (type == G_TYPE_DATE + || type == G_TYPE_BYTES) + *va_arg (va, gpointer*) = g_value_dup_boxed (value); + else if (G_TYPE_IS_OBJECT (type)) + *va_arg (va, gpointer*) = g_value_dup_object (value); + else if (type != GVN_TYPE_NULL) + g_return_if_reached (); + } +} + +/** + * gvn_value_compare: + * @a: a #GValue to be compared + * @b: a #GValue to be compared + * + * Does the same as g_value_compare0(), but returns a %gboolean. + * + * Return value: %TRUE if two values are equal, %FALSE otherwise. + **/ + +/** + * gvn_value_compare0: + * @a: #GValue to be compared + * @b: #GValue to be compared + * + * Compares a and b. The two values ​​must be of the same type or GVN_TYPE_NULL. + * + * Return value: -1, 0 or 1, if @a is <, == or > than @b. + **/ +gint gvn_value_compare0 (const GValue * a, const GValue * b) +{ + GType a_type = G_VALUE_TYPE (a); + gboolean a_is_val = G_IS_VALUE (a); + gboolean b_is_val = G_IS_VALUE (b); + + if (!(a_is_val && b_is_val)) + { + if (a_is_val) + return 1; + if (b_is_val) + return -1; + } + else if (a_type == G_VALUE_TYPE (b)) + { + switch (a_type) + { + case G_TYPE_FLOAT: + { + gfloat aux = g_value_get_float (a) - g_value_get_float (b); + return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0; + } + case G_TYPE_DOUBLE: + { + gdouble aux = g_value_get_double (a) - g_value_get_double (b); + return (aux > 0.0) ? 1 : (aux < 0.0) ? -1 : 0; + } + case G_TYPE_INT: + return g_value_get_int (a) - g_value_get_int (b); + case G_TYPE_UINT: + return (gint) (g_value_get_uint (a) - g_value_get_uint (b)); + case G_TYPE_LONG: + return (gint) (g_value_get_long (a) - g_value_get_long (b)); + case G_TYPE_ULONG: + return (gint) (g_value_get_ulong (a) - g_value_get_ulong (b)); + case G_TYPE_BOOLEAN: + return (gint) (g_value_get_boolean (a) - g_value_get_boolean (b)); + case G_TYPE_CHAR: + return (gint) (g_value_get_schar (a) - g_value_get_schar (b)); + case G_TYPE_STRING: + return g_strcmp0 (g_value_get_string (a), g_value_get_string (b)); + default: + if (a_type == GVN_TYPE_NULL) + return 0; + if (G_TYPE_FUNDAMENTAL (a_type) == G_TYPE_BOXED) + { + gpointer a_boxed = g_value_get_boxed (a); + gpointer b_boxed = g_value_get_boxed (b); + + if (!a_boxed) + return (gint) (a_boxed - b_boxed); + if (!b_boxed) + return (gint) (a_boxed - b_boxed); + if (a_type == G_TYPE_DATE) + return g_date_compare (a_boxed, b_boxed); + if (a_type == G_TYPE_DATE_TIME) + return g_date_time_compare (a_boxed, b_boxed); + else if (a_type == G_TYPE_BYTES) + return (gint) (a_boxed - b_boxed); + } + + g_warning (_("Attempting to compare invalid types: %s\n"), + g_type_name (a_type)); + } + } + else if (gvn_value_is_null (a)) + return -1; + else if (gvn_value_is_null (b)) + return 1; + + return 1; +} + +/** + * gvn_value_copy: + * @src: source #GValue + * @dst: destination #GValue + * + * Copies the value of @src into @dst. This function also works with #GvnNull + * values. + **/ +void gvn_value_copy (const GValue * src, GValue * dst) +{ + g_return_if_fail (G_IS_VALUE (src)); + g_return_if_fail (G_IS_VALUE (dst)); + + if (G_VALUE_TYPE (src) != G_VALUE_TYPE (dst)) + { + g_value_unset (dst); + g_value_init (dst, G_VALUE_TYPE (src)); + } + + g_value_copy (src, dst); +} + +/** + * gvn_value_ccopy: + * @src: source #GValue + * @dst: destination #GValue + * + * Compares @src with @dst, if they are different copies the value of @src into + * @dst. This function also works with #GvnNull values. + * + * Return value: %TRUE if they are copied, %FALSE if they are equals. + **/ +gboolean gvn_value_ccopy (const GValue * src, GValue * dst) +{ + if (gvn_value_compare (src, dst)) + return FALSE; + + gvn_value_copy (src, dst); + return TRUE; +} + +/** + * gvn_value_to_format_string: + **/ +void gvn_value_to_format_string (const GValue * src, guint digits, GValue * dst) +{ + GType type = G_VALUE_TYPE (src); + + g_value_init (dst, G_TYPE_STRING); + + switch (type) + { + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + { + gdouble dec; + gchar * format; + gchar buffer [G_ASCII_DTOSTR_BUF_SIZE]; + + if (type == G_TYPE_FLOAT) + dec = (gdouble) g_value_get_float (src); + else + dec = g_value_get_double (src); + + format = g_strdup_printf ("%%.%df", digits); + g_ascii_formatd (buffer, G_ASCII_DTOSTR_BUF_SIZE, format, dec); + g_value_set_string (dst, buffer); + g_free (format); + break; + } + default: + if (!gvn_value_is_null (src)) + g_value_transform (src, dst); + else + g_value_set_string (dst, ""); + } +} + +/** + * gvn_type_init: + * + * Initializes library types, also calls g_type_init(). + **/ +void gvn_type_init () +{ + gint n; + + GType types[] = { + G_TYPE_BOOLEAN + ,G_TYPE_CHAR + ,G_TYPE_INT + ,G_TYPE_UINT + ,G_TYPE_LONG + ,G_TYPE_ULONG + ,G_TYPE_FLOAT + ,G_TYPE_DOUBLE + ,G_TYPE_STRING + ,G_TYPE_DATE + ,G_TYPE_DATE_TIME + ,G_TYPE_BYTES + ,G_TYPE_NONE + }; + + for (n = 0; types[n] != G_TYPE_NONE; n++) + { + g_value_register_transform_func (types[n], GVN_TYPE_NULL, + gvn_value_transform_any_to_null + ); + g_value_register_transform_func (GVN_TYPE_NULL, types[n], + gvn_value_transform_null_to_any + ); + g_value_register_transform_func (G_TYPE_STRING, types[n], + gvn_value_transform_string_to_any + ); + } + + g_value_register_transform_func (G_TYPE_DATE, G_TYPE_STRING, + gvn_value_transform_date_to_string + ); + g_value_register_transform_func (G_TYPE_DATE_TIME, G_TYPE_STRING, + gvn_value_transform_date_time_to_string + ); + g_value_register_transform_func (G_TYPE_DATE_TIME, G_TYPE_DATE, + gvn_value_transform_date_time_to_date + ); + g_value_register_transform_func (G_TYPE_DATE, G_TYPE_DATE_TIME, + gvn_value_transform_date_to_date_time + ); +} + diff --git a/gvn/gvn-value.h b/gvn/gvn-value.h new file mode 100644 index 0000000..63c9aaf --- /dev/null +++ b/gvn/gvn-value.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GVN_VALUE_H +#define GVN_VALUE_H + +#include +#include "gvn-null.h" + +#define gvn_value_compare(a, b) (!gvn_value_compare0 (a, b)) +#define gvn_value_is_null(value) (G_VALUE_TYPE (value) == GVN_TYPE_NULL) + +GValue * gvn_value_new (GType type); +void gvn_value_new_with_content (GValue * value, GType type, gpointer content); +void gvn_value_new_from_string (GValue * value, GType type, const gchar * string, gsize length); +void gvn_value_set_null (GValue * value); +void gvn_value_get_valist (const GValue * value, va_list va); +gint gvn_value_compare0 (const GValue * a, const GValue * b); +void gvn_value_copy (const GValue * src, GValue * dst); +gboolean gvn_value_ccopy (const GValue * src, GValue * dst); +void gvn_value_to_format_string (const GValue * src, guint digits, GValue * dst); +void gvn_type_init (); + +#endif diff --git a/gvn/gvn.h b/gvn/gvn.h new file mode 100644 index 0000000..5434c0c --- /dev/null +++ b/gvn/gvn.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GVN_H +#define GVN_H + +#include +#include +#include "gvn-misc.h" +#include "gvn-null.h" +#include "gvn-time.h" +#include "gvn-param-spec.h" +#include "gvn-param.h" +#include "gvn-value.h" + +#endif \ No newline at end of file diff --git a/gvn/gvn.pc.in b/gvn/gvn.pc.in new file mode 100644 index 0000000..7cd5acf --- /dev/null +++ b/gvn/gvn.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Gvn +Description: Utility Module for Hedera Library +Version: @VERSION@ +Requires: glib-2.0 gobject-2.0 +Libs: -L${libdir} -lgvn +Cflags: -I${includedir}/gvn diff --git a/image/icon.svg b/image/icon.svg new file mode 100644 index 0000000..010caec --- /dev/null +++ b/image/icon.svg @@ -0,0 +1,558 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/image/logo.svg b/image/logo.svg new file mode 100644 index 0000000..aeaea8e --- /dev/null +++ b/image/logo.svg @@ -0,0 +1,172 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/main/Makefile.am b/main/Makefile.am new file mode 100644 index 0000000..c60c904 --- /dev/null +++ b/main/Makefile.am @@ -0,0 +1,45 @@ +include $(top_srcdir)/Makefile.decl + +hedera_bin_PROGRAMS = hedera-bin + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(gtk_CFLAGS) \ + -D_HEDERA_LOCALE_DIR=\"$(datadir)/locale\" +hedera_bin_LDFLAGS = -Wl,--export-dynamic +hedera_bin_SOURCES = main.c +hedera_bin_LDADD = \ + $(top_builddir)/gvn/libgvn.la \ + $(top_builddir)/db/libdb.la \ + $(top_builddir)/vn/libvn.la + +SCRIPT = $(top_srcdir)/main/hedera.sh.in +hedera_bin_SCRIPTS = hedera +$(hedera_bin_SCRIPTS): $(SCRIPT) + $(SED) -e 's,[@]bindir[@],$(bindir),g' $(SCRIPT) > $(hedera_bin_SCRIPTS) + chmod +x $(hedera_bin_SCRIPTS) + +pkgconfig_DATA = hedera.pc + +desktop_DATA = vn-hedera.desktop + +HOST=verdnatura.es +certdir = $(datadir)/ca-certificates/$(HOST) +cert_DATA = cacert.pem +syscertdir = $(sysconfdir)/ssl/certs + +man_MANS = hedera.1 + +EXTRA_DIST = \ + $(SCRIPT) \ + hedera.pc.in + +DISTCLEANFILES = \ + $(hedera_bin_SCRIPTS) \ + hedera.pc \ + vn-hedera.desktop + +install-data-hook: + mkdir -p $(DESTDIR)$(syscertdir) + (cd $(DESTDIR)$(syscertdir) && \ + $(LN_S) -f $(DESTDIR)$(certdir)/$(cert_DATA) $(HOST).pem) diff --git a/main/cacert.pem b/main/cacert.pem new file mode 100644 index 0000000..f7bd939 --- /dev/null +++ b/main/cacert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIJAJimL+J4jUaQMA0GCSqGSIb3DQEBBQUAMIG2MQswCQYD +VQQGEwJFUzEdMBsGA1UECAwUQ29tdW5pZGFkIFZhbGVuY2lhbmExETAPBgNVBAcM +CFZhbGVuY2lhMR4wHAYDVQQKDBVWZXJkbmF0dXJhIExldmFudGUgU0wxFDASBgNV +BAsMC0luZm9ybWF0aWNhMRYwFAYDVQQDDA12ZXJkbmF0dXJhLmVzMScwJQYJKoZI +hvcNAQkBFhhob3N0bWFzdGVyQHZlcmRuYXR1cmEuZXMwIBcNMTMwNjExMTE1MjQ1 +WhgPMjA2MzA1MzAxMTUyNDVaMIG2MQswCQYDVQQGEwJFUzEdMBsGA1UECAwUQ29t +dW5pZGFkIFZhbGVuY2lhbmExETAPBgNVBAcMCFZhbGVuY2lhMR4wHAYDVQQKDBVW +ZXJkbmF0dXJhIExldmFudGUgU0wxFDASBgNVBAsMC0luZm9ybWF0aWNhMRYwFAYD +VQQDDA12ZXJkbmF0dXJhLmVzMScwJQYJKoZIhvcNAQkBFhhob3N0bWFzdGVyQHZl +cmRuYXR1cmEuZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsqq+h +1kUfZE19Inm3M2XF1ytIqXeIsXsiZ5T9aVdedQrF2xcVOr4dvJyE5zp9g7NNxy9T +FyAHPXiPWKSoX3w0uoQfLW9Hm19QZYu40hBD5oRrJy5m6l4JU3Gj0tnkvYc4m8Fl +2uEUT3ain0FE2I1XjKpL6eQi9TKjhgmWNRgDJUbllAB3lsQLkNfB6EFo2QJykiKl +aRi4UaHdSkd3zkUJyYRdcG7IxCFvsViKpwr3JF+TLXSN/oronLFgG8cKedrn+sMW +pBm2FZQKAh8hLT3QQQqQgGpQ2y4t2EVJkYVdcGBXCyWYDKegMXgJR10WunICFCaD +kM0P3pYQdtbgr7e9AgMBAAGjUDBOMB0GA1UdDgQWBBQ9+iZdf7pLNQItxs4o43dN +FZD0CTAfBgNVHSMEGDAWgBQ9+iZdf7pLNQItxs4o43dNFZD0CTAMBgNVHRMEBTAD +AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAcFzJhm3jjuexZf9tTgQfzwtsafLLsYkrm +EZOTkhvZFJYbBd8UngL5UPF1M3buSEV1VqsWCALLrFkxzbmjk5uM8cswZR/3J6un +7GFYWTEllVgkD8KmNA2b6j5uSuGCQmOccsR4069vwNjrvTbtQpEpEAAnDf6d5/N4 +MXuDozHq3iEYi7qyGf++5mp4D+82y2ONSUsiLgxqnMT5JX/yhDKI1IiX1ndyt+6r +ie2i2fzdOgovlAILuTM4hoUjPYFOxwmUwpxU2EAjPqo9Bi7bQiH+fv0QBzbpQ9on +DRHo7NuXaDG49nvatJjuf4DiDELBCTMeKBOk5hG7kyWCIwp5wr+w +-----END CERTIFICATE----- diff --git a/main/hedera.1 b/main/hedera.1 new file mode 100644 index 0000000..bbf6193 --- /dev/null +++ b/main/hedera.1 @@ -0,0 +1,50 @@ +.\"Created with GNOME Manpages Editor Wizard +.\"http://sourceforge.net/projects/gmanedit2 +.TH hedera 1 "2013-01-02" "1.0" "Hedera" + +.SH NAME +hedera \- modular management system + +.SH SYNOPSIS +.B hedera +.\"RI [ options ] +.br + +.SH DESCRIPTION +.B hedera +is an enterprise management and administration application. It features modular +conectivity to user-made modules. +.PP +Being so easily extensible, +.B hedera +can handle virtually any task related to a database if the right module is +available. The modules can be written in C or in Vala. +.PP +By default +.B hedera +will look for the modules in /usr/lib/hedera/module and for its corresponding +data (GUI files, configuration...) in /usr/share/hedera/module but more +directories can be added by setting +.B VN_MODULE_LIB_PATH +(for the binaries) and +.B VN_MODULE_DATA_PATH +(for the additional data). These environment variables are intended for testing +during the developement and the expected is to use the modules installed. +.PP +The format for the configuration files for the modules and some information on +how to write the modules themselves, can be seen in the official documentation +for +.B hedera. + +.SH OPTIONS + +.SH EXAMPLE +Given a module project in the home directory, +.B hedera +should be called like this to load the module without installing it: + hedera --lib-dir ~/module/src/.libs --data-dir ~/module/data + +.SH AUTHORS +Copyright (C) 2012 Juan Ferrer Toribio . +.PP +Manual page written by Alejandro T. Colombini. \ No newline at end of file diff --git a/main/hedera.pc.in b/main/hedera.pc.in new file mode 100644 index 0000000..b5d38fe --- /dev/null +++ b/main/hedera.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Hedera +Description: The Hedera Library +Version: @VERSION@ +Requires: gtk+-3.0 gvn sql db vn +Libs: -L${libdir} +Cflags: -I${includedir} diff --git a/main/hedera.sh.in b/main/hedera.sh.in new file mode 100644 index 0000000..b7acf53 --- /dev/null +++ b/main/hedera.sh.in @@ -0,0 +1,10 @@ +#!/bin/bash + +if [ -x @bindir@/vn-updater-gui ] +then + echo "Running updater..." + @bindir@/vn-updater-gui +fi + +export LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN=1 +exec @bindir@/hedera-bin diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..47ca0d6 --- /dev/null +++ b/main/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +int main (int argc, char * argv[]) +{ +#ifdef ENABLE_NLS + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, _HEDERA_LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); +#endif + + gvn_type_init (); + gtk_init (&argc, &argv); + + VnLogin * login = vn_login_new ("apps.hedera"); + vn_login_run (login); + g_object_unref (login); + return 0; +} diff --git a/main/vn-hedera.desktop.in b/main/vn-hedera.desktop.in new file mode 100644 index 0000000..e949638 --- /dev/null +++ b/main/vn-hedera.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=Hedera +GenericName=Office Tool +Comment=Runtime for the Hedera system +Exec=hedera +Icon=@prefix@/share/hedera/vn/image/icon.svg +Terminal=false +Type=Application +Categories=GNOME;GTK;Office; +StartupNotify=true +Version=@VERSION@ diff --git a/module/Makefile.am b/module/Makefile.am new file mode 100644 index 0000000..b2980bc --- /dev/null +++ b/module/Makefile.am @@ -0,0 +1,5 @@ + +SUBDIRS = \ + src \ + data \ + sql diff --git a/module/data/Makefile.am b/module/data/Makefile.am new file mode 100644 index 0000000..374a026 --- /dev/null +++ b/module/data/Makefile.am @@ -0,0 +1,13 @@ +include $(top_srcdir)/Makefile.decl + +exampledir = $(module_datadir) + +example_DATA = \ + example.xml \ + example.ui \ + consulter.glade \ + consulter.ui \ + users.glade \ + customer.glade + +EXTRA_DIST = $(example_DATA) diff --git a/module/data/consulter.glade b/module/data/consulter.glade new file mode 100644 index 0000000..34a0453 --- /dev/null +++ b/module/data/consulter.glade @@ -0,0 +1,304 @@ + + + + + + + + + Send + Send + Send the current query + system-run + + + + + + + Clean + Clean + Clean the messages + edit-clear + + + + + + + _Consulter + + + + + + + + + + + 300 + True + False + 8 + vertical + 8 + + + True + False + 8 + + + True + False + 1 + Query: + + + False + True + 0 + + + + + True + False + True + 0 + 1 + + + True + True + Set on the code + + + + + + True + True + 1 + + + + + send + True + True + True + send + + + False + True + 2 + + + + + clean + True + True + True + clean + + + False + False + 3 + + + + + gtk-stop + False + True + True + True + False + True + + + + False + False + 4 + + + + + False + True + 0 + + + + + True + True + 120 + True + + + True + True + never + in + + + True + True + model + True + 0 + True + True + + + + + + + + False + True + + + + + True + True + in + + + True + True + + + + + + + + True + True + + + + + True + True + 1 + + + + + True + False + 8 + + + True + False + 8 + start + + + Immediate changes + False + True + False + True + False + False + 0 + True + True + + + + False + True + 0 + + + + + Start + False + True + True + True + False + + + + False + False + 1 + + + + + Commit + False + True + True + True + False + + + + False + False + 2 + + + + + Rollback + False + True + True + True + False + + + + False + False + 3 + + + + + True + True + 0 + + + + + True + False + start + VN_HANDLER_SHOW_REFRESH | VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + + + False + True + 1 + + + + + False + True + 2 + + + + diff --git a/module/data/consulter.ui b/module/data/consulter.ui new file mode 100644 index 0000000..1069a8f --- /dev/null +++ b/module/data/consulter.ui @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/module/data/customer.glade b/module/data/customer.glade new file mode 100644 index 0000000..d34db37 --- /dev/null +++ b/module/data/customer.glade @@ -0,0 +1,415 @@ + + + + + + + SELECT id, street, pc, city, province, ok FROM user_address WHERE #p ORDER BY id + + + SELECT id, name, credit, active, born, photo, object_id FROM "user" ORDER BY id + + + True + False + 6 + vertical + + + True + False + 0 + none + + + True + False + 4 + vertical + 8 + + + True + False + 10 + + + True + False + 8 + 8 + True + + + True + False + 1 + Search: + + + 0 + 0 + 1 + 1 + + + + + True + False + SELECT id, #p FROM article WHERE #p + name + + + 1 + 0 + 1 + 1 + + + + + True + False + 1 + Id: + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + Name: + + + 0 + 2 + 1 + 1 + + + + + True + False + 1 + Credit: + + + 0 + 3 + 1 + 1 + + + + + True + False + 1 + Type: + + + 0 + 4 + 1 + 1 + + + + + True + False + 1 + Active: + + + 0 + 5 + 1 + 1 + + + + + True + False + info + id + + + 1 + 1 + 1 + 1 + + + + + True + False + info + name + + + 1 + 2 + 1 + 1 + + + + + True + False + info + object_id + SELECT id, object FROM object + + + 1 + 4 + 1 + 1 + + + + + True + False + info + active + + + 1 + 5 + 1 + 1 + + + + + True + False + info + credit + + + 1 + 3 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + info + born + + + False + True + 1 + + + + + True + False + vertical + 6 + + + True + False + info + born + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + + + + False + True + 2 + + + + + False + True + 0 + + + + + True + False + end + info + VN_HANDLER_SHOW_REFRESH | VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD | VN_HANDLER_SHOW_SCROLL + + + False + True + 1 + + + + + + + True + False + <b>Info</b> + True + + + + + False + False + 0 + + + + + True + False + 0 + none + + + True + False + 4 + vertical + 8 + + + True + True + in + + + True + True + homes + + + + + + Id + id + + + + + Default + ok + True + + + + + Street + True + street + True + + + + + PC + pc + True + + + + + City + city + + + + + Province + True + province + True + + + + + + + True + True + 0 + + + + + True + False + end + homes + VN_HANDLER_SHOW_REFRESH | VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + + + False + True + 1 + + + + + + + True + False + <b>Homes</b> + True + + + + + True + True + 1 + + + + diff --git a/module/data/example.ui b/module/data/example.ui new file mode 100644 index 0000000..b290204 --- /dev/null +++ b/module/data/example.ui @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/module/data/example.xml b/module/data/example.xml new file mode 100644 index 0000000..9729d1e --- /dev/null +++ b/module/data/example.xml @@ -0,0 +1,37 @@ + + + Example + + + Example + + + +
+ Consulter +
+
+ Users +
+
+ Customer +
+
+
diff --git a/module/data/signer.glade b/module/data/signer.glade new file mode 100644 index 0000000..edc6f34 --- /dev/null +++ b/module/data/signer.glade @@ -0,0 +1,219 @@ + + + + + 100 + 1 + 10 + + + 365 + 10 + 30 + + + True + False + 5 + 5 + + + True + True + 170 + True + + + True + True + adjustment + always + top-right + True + in + + + True + True + True + True + both + + + + + + + + False + True + + + + + True + True + adjustment + always + in + + + True + True + True + True + both + + + + none + + + + + + + True + True + + + + + True + True + 0 + + + + + True + False + vertical + 5 + + + True + False + 0 + + + True + False + 5 + vertical + 5 + + + True + True + 2012 + 2 + 8 + + + False + True + 1 + + + + + True + False + + Department + + + + False + True + 3 + + + + + + + True + False + <b>Date and Department</b> + True + + + + + False + True + 0 + + + + + True + False + 0 + + + True + False + 5 + vertical + 5 + + + gtk-clear + False + True + True + True + False + True + + + False + True + 0 + + + + + 110 + True + True + in + + + + + + True + True + 1 + + + + + + + True + False + <b>Status</b> + True + + + + + True + True + 1 + + + + + False + True + 1 + + + + diff --git a/module/data/users.glade b/module/data/users.glade new file mode 100644 index 0000000..400bf00 --- /dev/null +++ b/module/data/users.glade @@ -0,0 +1,908 @@ + + + + + + SELECT user_id, group_id, uid, last_change, expire FROM account WHERE #p + on-iter + + + SELECT mail_alias_id, user_id FROM mail_alias_account WHERE #p + + + True + False + 6 + vertical + 6 + + + True + False + 0 + none + + + True + False + 30 + + + True + False + 6 + + + True + False + 1 + User name: + + + False + True + 0 + + + + + True + False + search-user + + + False + True + 1 + + + + + + + + + True + False + <b>Search</b> + True + + + + + False + True + 0 + + + + + True + True + in + + + True + True + False + 1 + users + + + + + + Identifier + id + + + + + User + True + name + + + + + MySQL User + user + + + + + Enabled + active + + + + + + + True + True + 1 + + + + + True + False + end + users + VN_HANDLER_SHOW_REFRESH | VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + + + False + True + 2 + + + + + True + True + start + left + + + True + False + 15 + vertical + 20 + + + True + False + 6 + 6 + True + + + True + False + 1 + Name: + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + Password: + + + 0 + 2 + 1 + 1 + + + + + True + False + 1 + MySQL user: + + + 0 + 3 + 1 + 1 + + + + + True + False + users + active + + + 1 + 4 + 1 + 1 + + + + + True + False + 1 + Enabled: + + + 0 + 4 + 1 + 1 + + + + + 130 + True + False + 1 + Identifier: + + + 0 + 0 + 1 + 1 + + + + + 170 + True + False + users + id + False + + + 1 + 0 + 1 + 1 + + + + + True + False + users + name + + + 1 + 1 + 1 + 1 + + + + + True + False + users + mysql_user_id + SELECT id, user FROM mysql_user + + + 1 + 3 + 1 + 1 + + + + + gtk-edit + False + True + True + True + False + True + + + + 1 + 2 + 1 + 1 + + + + + False + True + 0 + + + + + + + 100 + True + False + User + + + False + + + + + True + False + 15 + vertical + 20 + + + True + False + 6 + 6 + True + + + True + False + account + group_id + SELECT id, name FROM `group` + + + 1 + 1 + 1 + 1 + + + + + 170 + True + False + account + uid + + + 1 + 0 + 1 + 1 + + + + + 130 + True + False + 1 + UID: + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Main group: + + + 0 + 1 + 1 + 1 + + + + + True + False + account + last_change + + + 1 + 2 + 1 + 1 + + + + + True + False + account + expire + + + 1 + 3 + 1 + 1 + + + + + True + False + 1 + Last change: + + + 0 + 2 + 1 + 1 + + + + + True + False + 1 + Expires: + + + 0 + 3 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + end + account + True + + + False + True + 1 + + + + + 1 + + + + + 100 + True + False + Account + + + 1 + False + + + + + True + False + 15 + vertical + 6 + + + 160 + True + True + in + + + True + True + False + alias + + + + + + Alias + mail_alias_id + True + SELECT id, alias FROM mail_alias + + + + + + + False + True + 1 + + + + + True + False + 6 + end + alias + VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + + + False + True + 2 + + + + + 2 + + + + + 100 + True + False + Mail alias + + + 2 + False + + + + + True + False + 15 + vertical + 20 + + + True + False + start + 6 + 6 + True + + + 130 + True + False + 1 + Extension: + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Secret: + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + Identifier: + + + 0 + 2 + 1 + 1 + + + + + True + False + 1 + Call group: + + + 0 + 3 + 1 + 1 + + + + + 170 + True + False + sip + extension + + + 1 + 0 + 1 + 1 + + + + + True + False + sip + secret + + + 1 + 1 + 1 + 1 + + + + + True + False + sip + callerid + + + 1 + 2 + 1 + 1 + + + + + True + False + sip + callgroup + + + 1 + 3 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + 6 + end + sip + + + False + True + 1 + + + + + 3 + + + + + 100 + True + False + SIP + + + 3 + False + + + + + False + False + 3 + + + + + False + 5 + Change password + False + True + True + dialog + + + + + False + vertical + 2 + + + True + False + + + False + 8 + 8 + + + True + False + + + False + True + 0 + + + + + True + True + 0 + + + + + False + 5 + vertical + end + + + + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 6 + 6 + 6 + + + True + False + 1 + Password: + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Repeat password: + + + 0 + 1 + 1 + 1 + + + + + True + True + False + + True + + + 1 + 0 + 1 + 1 + + + + + True + True + False + + True + + + 1 + 1 + 1 + 1 + + + + + False + True + 1 + + + + + False + end + + + + + + + + + False + True + end + 2 + + + + + + + + SELECT user_id, extension, secret, callerid, callgroup FROM account_sip WHERE #p + on-iter + + + SELECT u.id, u.name, u.mysql_user_id, m.user, u.active FROM `user` u JOIN mysql_user m ON u.mysql_user_id = m.id + + diff --git a/module/src/Makefile.am b/module/src/Makefile.am new file mode 100644 index 0000000..aed27ca --- /dev/null +++ b/module/src/Makefile.am @@ -0,0 +1,21 @@ +include $(top_srcdir)/Makefile.decl + +example_libdir = $(module_libdir) +example_lib_LTLIBRARIES = libexample.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(gtk_CFLAGS) +libexample_la_LDFLAGS = -avoid-version +libexample_la_LIBADD = $(top_builddir)/vn/libvn.la +libexample_la_SOURCES = \ + vn-consulter.h \ + vn-consulter.c \ + vn-users.h \ + vn-users.c \ + vn-customer.h \ + vn-customer.c + +install-data-hook: + rm -f $(DESTDIR)$(module_libdir)/libexample.la + rm -f $(DESTDIR)$(module_libdir)/libexample.a \ No newline at end of file diff --git a/module/src/vn-consulter.c b/module/src/vn-consulter.c new file mode 100644 index 0000000..8698c61 --- /dev/null +++ b/module/src/vn-consulter.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-consulter.h" +#include "stdlib.h" + +G_DEFINE_TYPE (VnConsulter, vn_consulter, VN_TYPE_FORM); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +void vn_consulter_clean_clicked (GtkButton * button, VnConsulter * obj) +{ + gtk_list_store_clear (obj->model); +} + +void vn_consulter_start_clicked (GtkButton * button, VnConsulter * obj) +{ + db_conn_start_transaction (VN_FORM (obj)->conn); +} + +void vn_consulter_on_stop_clicked (GtkButton * button, VnConsulter * obj) +{ + db_conn_kill_query (VN_FORM (obj)->conn); +} + +void vn_consulter_commit_clicked (GtkButton * button, VnConsulter * obj) +{ + db_conn_commit (VN_FORM (obj)->conn); +} + +void vn_consulter_rollback_clicked (GtkButton * button, VnConsulter * obj) +{ + db_conn_rollback (VN_FORM (obj)->conn); +} + +static void vn_consulter_set_message (VnConsulter * obj, const gchar * msg) +{ + GtkTreePath * path; + GtkTreeIter iter; + + gtk_list_store_append (obj->model, &iter); + gtk_list_store_set (obj->model, &iter, 0, msg, -1); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (obj->model), &iter); + gtk_tree_view_scroll_to_cell (obj->tv, path, NULL, FALSE, 0, 0); + gtk_tree_path_free (path); +} + +static void vn_consulter_model_status_changed (DbModel * model, + DbModelStatus status, VnConsulter * obj) +{ + GList * n; + GtkTreeView * tv = GTK_TREE_VIEW (obj->tree); + GList * cols = gtk_tree_view_get_columns (tv); + + for (n = cols; n; n = n->next) + gtk_tree_view_remove_column (tv, n->data); + + g_list_free (cols); + + switch (status) + { + case DB_MODEL_STATUS_READY: + { + gint i; + gint cols = db_model_get_ncols (model); + + for (i = 0; i < cols; i++) + { + VnColumn * column; + GType col_type, type = db_model_get_column_type (model, i); + + if (type == G_TYPE_BYTES) + col_type = VN_TYPE_COLUMN_IMAGE; + else if (type == G_TYPE_BOOLEAN) + col_type = VN_TYPE_COLUMN_CHECK; + else + col_type = VN_TYPE_COLUMN_ENTRY; + +if (i == 7) col_type = VN_TYPE_COLUMN_IMAGE; + + column = vn_grid_append_column (obj->tree, i + ,db_model_get_column_name (model, i) + ,col_type + ,TRUE, FALSE + ); + + if (type == G_TYPE_FLOAT || type == G_TYPE_DOUBLE) + g_object_set (column, "digits", 3, NULL); + } + + break; + } + case DB_MODEL_STATUS_ERROR: + vn_consulter_set_message (obj, "Error"); + break; + default: + break; + } + + gtk_widget_set_sensitive (GTK_WIDGET (obj->mode), TRUE); +} + +void vn_consulter_mode_toggled (GtkToggleButton * mode, VnConsulter * obj) +{ + DbIterator * iterator = vn_handler_get_iterator (obj->handler); + + db_iterator_set_mode (iterator + ,(gtk_toggle_button_get_active (mode)) ? + DB_ITERATOR_MODE_ON_CHANGE: + DB_ITERATOR_MODE_ON_DEMAND + ); +} + +void vn_consulter_send (GtkButton * button, VnConsulter * obj) +{ + DbIterator * iterator; + DbModel * model; + gchar * sql; + + sql = gtk_combo_box_text_get_active_text (obj->combo); + + vn_consulter_set_message (obj, sql); + + model = db_model_new_with_sql (VN_FORM (obj)->conn, sql); + g_signal_connect (model, "status-changed", + G_CALLBACK (vn_consulter_model_status_changed), obj); + + iterator = db_iterator_new (model); + iterator->mode = DB_ITERATOR_MODE_ON_DEMAND; + vn_handler_set_iterator (obj->handler, iterator); + vn_grid_set_iterator (obj->tree, iterator); + + vn_consulter_mode_toggled (GTK_TOGGLE_BUTTON (obj->mode), obj); + + g_free (sql); + g_object_unref (model); + g_object_unref (iterator); +} + +static void vn_consulter_open (VnConsulter * obj) +{ + gint i; + VnForm * form = VN_FORM (obj); + + gchar * queries[] = + { +//MySQL (kk schema) + "/*my*/SELECT id, name, item_id, item_id2, amount, item.price " + "FROM movement JOIN item USING (item_id, item_id2)" + ,"/*my*/SELECT item_id,item_id2, i.name, m.id, m.amount, m.price " + "FROM item i LEFT JOIN movement m USING (item_id, item_id2)" + ,"/*my*/SELECT item_id, item_id2, name, price FROM item" +//PgSQL (test schema) + ,"/*pg*/SELECT pg_sleep(120)" + ,"/*pg*/SELECT i.id, i.name, i.color, m.id, m.amount " + "FROM item i LEFT JOIN movement m ON i.id = m.item_id" + ,"/*pg*/SELECT m.id, amount, item_id, name, color " + "FROM movement m JOIN item ON m.item_id = item.id" + ,"/*pg*/SELECT * FROM item" + ,"/*pg*/ SELECT id1, id2, name, ok, date, number, floating, image " + "FROM prueben" + ,NULL + }; + + obj->model = vn_form_get (form, "model"); + obj->combo = vn_form_get (form, "query"); + obj->tv = vn_form_get (form, "treeview"); + obj->mode = vn_form_get (form, "mode"); + obj->tree = vn_form_get (form, "consulter"); + obj->handler = vn_form_get (form, "handler"); + gtk_entry_set_placeholder_text (vn_form_get (form, "combo-entry"), + _("Write your query here or select one from the list")); + + gtk_tree_view_insert_column_with_attributes (obj->tv, -1, "Mensaje", + gtk_cell_renderer_text_new (), "text", 0, NULL); + + for (i = 0; i < g_strv_length (queries); i++) + gtk_combo_box_text_prepend_text (obj->combo, queries[i]); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_consulter_init (VnConsulter * obj) {} + +static void vn_consulter_class_init (VnConsulterClass * k) +{ + VN_FORM_CLASS (k)->open = (VnFormOpenFunc) vn_consulter_open; +} diff --git a/module/src/vn-consulter.h b/module/src/vn-consulter.h new file mode 100644 index 0000000..9873519 --- /dev/null +++ b/module/src/vn-consulter.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_CONSULTER_H +#define VN_CONSULTER_H + +#include + +#define VN_TYPE_CONSULTER (vn_consulter_get_type ()) +#define VN_CONSULTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_CONSULTER, VnConsulter)) +#define VN_IS_CONSULTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_CONSULTER)) + +typedef struct _VnConsulter VnConsulter; +typedef struct _VnConsulterClass VnConsulterClass; + +struct _VnConsulter +{ + VnForm parent; + GtkButton * connect; + GtkListStore * model; + GtkCheckButton * mode; + GtkComboBoxText * combo; + GtkTreeView * tv; + GtkContainer * box; + VnGrid * tree; + VnHandler * handler; +}; + +struct _VnConsulterClass +{ + VnFormClass parent; +}; + +GType vn_consulter_get_type (); + +#endif \ No newline at end of file diff --git a/module/src/vn-customer.c b/module/src/vn-customer.c new file mode 100644 index 0000000..70b5a8c --- /dev/null +++ b/module/src/vn-customer.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-customer.h" + +G_DEFINE_TYPE (VnCustomer, vn_customer, VN_TYPE_FORM); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_customer_open (VnForm * obj, gpointer user_data) +{ + DbIterator * info = vn_form_get (obj, "info"); + + DbIterator * homes = vn_form_get (obj, "homes"); + db_iterator_link (homes, "user_id", info, "id"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_customer_init (VnCustomer * obj) {} + +static void vn_customer_class_init (VnCustomerClass * k) +{ + VN_FORM_CLASS (k)->open = (VnFormOpenFunc) vn_customer_open; +} diff --git a/module/src/vn-customer.h b/module/src/vn-customer.h new file mode 100644 index 0000000..efe5868 --- /dev/null +++ b/module/src/vn-customer.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_CUSTOMER_H +#define VN_CUSTOMER_H + +#include + +#define VN_TYPE_CUSTOMER (vn_customer_get_type ()) +#define VN_CUSTOMER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_CUSTOMER, VnCustomer)) +#define VN_IS_CUSTOMER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_CUSTOMER)) + +typedef struct _VnCustomer VnCustomer; +typedef struct _VnCustomerClass VnCustomerClass; + +struct _VnCustomer +{ + VnForm parent; +}; + +struct _VnCustomerClass +{ + VnFormClass parent; +}; + +GType vn_customer_get_type (); + +#endif \ No newline at end of file diff --git a/module/src/vn-users.c b/module/src/vn-users.c new file mode 100644 index 0000000..8c008ae --- /dev/null +++ b/module/src/vn-users.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-users.h" +#include "stdlib.h" + +G_DEFINE_TYPE (VnUsers, vn_users, VN_TYPE_FORM); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_users_reset_dialog (VnUsers * obj, const gchar * error) +{ + GtkWidget * infobar = vn_form_get (VN_FORM (obj), "password-infobar"); + + if (error) + { + GtkLabel * password_error = vn_form_get (VN_FORM (obj), "password-error"); + gtk_label_set_text (password_error, error); + gtk_widget_show (infobar); + } + else + gtk_widget_hide (infobar); + + gtk_entry_set_text (obj->repeat_password, ""); + gtk_entry_set_text (obj->password_entry, ""); + gtk_widget_grab_focus (GTK_WIDGET (obj->password_entry)); +} + +void vn_users_on_set_password_clicked (GtkButton * button, VnUsers * obj) +{ + gtk_window_set_transient_for (GTK_WINDOW (obj->password_dialog), + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (obj)))); + gtk_widget_show (GTK_WIDGET (obj->password_dialog)); + vn_users_reset_dialog (obj, NULL); +} + +static void vn_users_on_password_changed (DbRequest * request, VnUsers * obj) +{ + GValue value = {0}; + + if (db_request_fetch_value (request, &value, NULL)) + { + if (g_value_get_int (&value) != -1) + gtk_widget_hide (GTK_WIDGET (obj->password_dialog)); + else + vn_users_reset_dialog (obj, _("The password is too week.")); + + g_value_unset (&value); + } + + g_object_unref (request); +} + +void vn_users_on_dialog_response (GtkDialog * dialog, gint response_id, VnUsers * obj) +{ + if (response_id == GTK_RESPONSE_ACCEPT) + { + const gchar * password = gtk_entry_get_text (obj->password_entry); + + if (!g_strcmp0 (password, "")) + vn_users_reset_dialog (obj, _("The password can't be empty.")); + else if (g_strcmp0 (password, gtk_entry_get_text (obj->repeat_password))) + vn_users_reset_dialog (obj, _("Passwords do not match.")); + else + { + SqlString * query; + + query = sql_string_new ("SELECT user_set_password (#user, #password)" + ,"user", GVN_TYPE_PARAM, obj->user_id + ,"password", G_TYPE_STRING, password + ,NULL + ); + + db_conn_query_with_stmt_async (VN_FORM (obj)->conn + ,SQL_STMT (query) + ,(DbRequestDoneCallback) vn_users_on_password_changed + ,g_object_ref (obj) + ,g_object_unref + ); + } + } + else + gtk_widget_hide (GTK_WIDGET (dialog)); +} + +static void vn_users_open (VnUsers * obj, gpointer user_data) +{ + DbIterator * users; + VnForm * form = VN_FORM (obj); + + obj->password_entry = vn_form_get (form, "password-entry"); + obj->repeat_password = vn_form_get (form, "repeat-password"); + + obj->password_dialog = vn_form_get (form, "password-dialog"); + gtk_dialog_add_button (obj->password_dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (obj->password_dialog, GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT); + + users = vn_form_get (form, "users"); + obj->user_id = db_iterator_get_param (users, "id"); + + obj->account = vn_form_get (form, "account"); + db_iterator_link_with_param (obj->account, "user_id", obj->user_id); + + obj->alias = vn_form_get (form, "alias"); + db_iterator_link (obj->alias, "user_id", obj->account, "user_id"); + + obj->sip = vn_form_get (form, "sip"); + db_iterator_link (obj->sip, "user_id", obj->account, "user_id"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_users_init (VnUsers * obj) {} + +static void vn_users_class_init (VnUsersClass * k) +{ + VN_FORM_CLASS (k)->open = (VnFormOpenFunc) vn_users_open; +} diff --git a/module/src/vn-users.h b/module/src/vn-users.h new file mode 100644 index 0000000..2f692bb --- /dev/null +++ b/module/src/vn-users.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_USERS_H +#define VN_USERS_H + +#include + +#define VN_TYPE_USERS (vn_users_get_type ()) +#define VN_USERS(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_USERS, VnUsers)) +#define VN_IS_USERS(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_USERS)) + +typedef struct _VnUsers VnUsers; +typedef struct _VnUsersClass VnUsersClass; + +struct _VnUsers +{ + VnForm parent; + GvnParam * user_id; + DbIterator * account; + DbIterator * alias; + DbIterator * sip; + GtkDialog * password_dialog; + GtkEntry * password_entry; + GtkEntry * repeat_password; +}; + +struct _VnUsersClass +{ + VnFormClass parent; +}; + +GType vn_users_get_type (); + +#endif \ No newline at end of file diff --git a/plugin/Makefile.am b/plugin/Makefile.am new file mode 100644 index 0000000..2d415cb --- /dev/null +++ b/plugin/Makefile.am @@ -0,0 +1,4 @@ + +SUBDIRS = \ + pg \ + mysql \ No newline at end of file diff --git a/plugin/mysql/Makefile.am b/plugin/mysql/Makefile.am new file mode 100644 index 0000000..4efb366 --- /dev/null +++ b/plugin/mysql/Makefile.am @@ -0,0 +1,20 @@ +include $(top_srcdir)/Makefile.decl + +mysql_lib_LTLIBRARIES = libdbmysql.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(glib_CFLAGS) +libdbmysql_la_LIBADD = \ + $(glib_LIBS) \ + $(top_builddir)/db/libdb.la \ + -lmysqlclient \ + -lz +libdbmysql_la_LDFLAGS = -avoid-version +libdbmysql_la_SOURCES = \ + db-mysql.h \ + db-mysql.c + +install-data-hook: + rm -f $(DESTDIR)$(mysql_libdir)/libdbmysql.la + rm -f $(DESTDIR)$(mysql_libdir)/libdbmysql.a \ No newline at end of file diff --git a/plugin/mysql/db-mysql.c b/plugin/mysql/db-mysql.c new file mode 100644 index 0000000..4c200e2 --- /dev/null +++ b/plugin/mysql/db-mysql.c @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "db-mysql.h" +#include +#include +#include +#include + +/** + * SECTION: db-mysql + * @Short_description: manages a connection to a PostgreSQL database. + * @Title: DbMysql + * @See_also: #DbConn + * + * This class manages a connection to a MySQL database internally. This + * is accessed through the #DbConn class to internally connect, query and + * disconnect the database. + **/ +G_DEFINE_TYPE (DbMysql, db_mysql, DB_TYPE_PLUGIN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void db_mysql_close (DbMysql * obj) +{ + if (obj->mysql) + { + g_free (obj->host); + g_free (obj->user); + g_free (obj->pass); + obj->host = NULL; + obj->user = NULL; + obj->pass = NULL; + + mysql_close (obj->mysql); + obj->mysql = NULL; + obj->thread_id = 0; + obj->socket = -1; + obj->is_open = FALSE; + } +} + +static gboolean db_mysql_open (DbMysql * obj, const gchar * host, + const gchar * schema, const gchar * user, const gchar * pass, GError ** err) +{ + if (!obj->mysql) + obj->mysql = mysql_init (NULL); + + if (!obj->mysql) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_OPENING + ,_("Can't allocate the needed memory") + ); + return FALSE; + } + + if (!mysql_real_connect (obj->mysql, host, user, pass, schema, 0, NULL, CLIENT_MULTI_STATEMENTS)) + { + gint error_code; + + switch (mysql_errno (obj->mysql)) + { + case ER_ACCESS_DENIED_ERROR: + error_code = DB_CONN_ERROR_BAD_LOGIN; + break; + default: + error_code = DB_CONN_ERROR_OPENING; + } + + g_set_error (err + ,DB_CONN_ERROR_OPENING + ,error_code + ,"%s", mysql_error (obj->mysql) + ); + db_mysql_close (obj); + return FALSE; + } + + obj->host = g_strdup (host); + obj->user = g_strdup (user); + obj->pass = g_strdup (pass); + obj->socket = obj->mysql->net.fd; + obj->thread_id = mysql_thread_id (obj->mysql); + obj->is_open = TRUE; + + mysql_set_character_set (obj->mysql, "utf8"); + return TRUE; +} + +static void db_mysql_set_ssl (DbMysql * obj, const gchar * ca) +{ + if (!obj->mysql) + obj->mysql = mysql_init (NULL); + + mysql_ssl_set (obj->mysql, NULL, NULL, ca, NULL, NULL); +} + +static void db_mysql_set_value (GValue * value, GType type, const gchar * string, gsize len) +{ + if (!string) + { + g_value_init (value, GVN_TYPE_NULL); + return; + } + + g_value_init (value, type); + + switch (type) + { + case G_TYPE_BOOLEAN: + g_value_set_boolean (value, atoi (string)); + break; + case G_TYPE_CHAR: + g_value_set_schar (value, atoi (string)); + break; + case G_TYPE_INT: + g_value_set_int (value, atoi (string)); + break; + case G_TYPE_UINT: + g_value_set_int (value, (guint) atoi (string)); + break; + case G_TYPE_LONG: + g_value_set_long (value, g_ascii_strtoll (string, NULL, 0)); + break; + case G_TYPE_ULONG: + g_value_set_ulong (value, g_ascii_strtoull (string, NULL, 0)); + break; + case G_TYPE_FLOAT: + g_value_set_float (value, atof (string)); + break; + case G_TYPE_DOUBLE: + g_value_set_double (value, g_ascii_strtod (string, NULL)); + break; + case G_TYPE_STRING: + g_value_set_string (value, string); + break; + default: + if (type == G_TYPE_DATE) + { + GDate * date = NULL; + + if (len >= 10) + { + GDateDay d = atoi (string + 8); + GDateMonth m = atoi (string + 5); + GDateYear y = atoi (string); + + if (g_date_valid_dmy (d, m, y)) + date = g_date_new_dmy (d, m, y); + } + + g_value_take_boxed (value, date); + } + else if (type == G_TYPE_DATE_TIME) + { + GDateTime * date_time = NULL; + + if (len >= 19) + date_time = g_date_time_new_local ( + atoi (string) + ,atoi (string + 5) + ,atoi (string + 8) + ,atoi (string + 11) + ,atoi (string + 14) + ,atoi (string + 17) + ); + + g_value_take_boxed (value, date_time); + } + else if (type == G_TYPE_BYTES) + g_value_take_boxed (value, g_bytes_new (string, len)); + else + g_warning ("DbMysql: Can't handle this value type: %s", + g_type_name (type)); + } +} + +static DbResultSet * db_mysql_query (DbMysql * obj, const gchar * sql, GError ** err) +{ + gint i, j; + guint errno = 0; + gulong * lengths; + GValue def = {0}; + MYSQL_ROW myrow; + MYSQL_RES * res; + MYSQL_FIELD * field; + DbRow * row; + DbColumn * column; + DbResult * result; + DbResultSet * set = db_result_set_new (); + + if (!mysql_query (obj->mysql, sql)) + do { + if ((res = mysql_store_result (obj->mysql))) + { + result = g_new (DbResult, 1); + result->nrows = mysql_num_rows (res); + result->ncols = mysql_num_fields (res); + result->column = g_new (DbColumn, result->ncols); + result->data = g_ptr_array_new_full (result->nrows + 1, + (GDestroyNotify) db_row_free); + set->results = g_slist_append (set->results, result); + + field = mysql_fetch_fields (res); + + GType gtypes[result->ncols]; + + for (i = 0; i < result->ncols; i++) + { + column = &result->column[i]; + column->info = 0; + column->name = g_strdup (field[i].org_name); + column->display = g_strdup (field[i].name); + column->table = g_strdup (field[i].org_table); + + switch (field[i].type) + { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + gtypes[i] = G_TYPE_INT; + break; + case MYSQL_TYPE_LONGLONG: + gtypes[i] = G_TYPE_LONG; + break; + case MYSQL_TYPE_FLOAT: + gtypes[i] = G_TYPE_FLOAT; + break; + case MYSQL_TYPE_DOUBLE: + gtypes[i] = G_TYPE_DOUBLE; + break; + case MYSQL_TYPE_DATE: + gtypes[i] = G_TYPE_DATE; + break; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + gtypes[i] = G_TYPE_DATE_TIME; + break; + case MYSQL_TYPE_BLOB: + gtypes[i] = G_TYPE_BYTES; + break; + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_NULL: + default: + gtypes[i] = G_TYPE_STRING; + } + + if (field[i].flags & AUTO_INCREMENT_FLAG) + { + SqlFunction * func = sql_function_new ("LAST_INSERT_ID", NULL); + + g_value_init (&def, SQL_TYPE_FUNCTION); + g_value_take_object (&def, g_object_ref_sink (func)); + } + else + db_mysql_set_value (&def, gtypes[i], field[i].def, field[i].def_length); + + column->spec = gvn_param_spec_new_with_attrs (gtypes[i], + FALSE, !(field[i].flags & NOT_NULL_FLAG), &def); + g_value_unset (&def); + + if (field[i].flags & PRI_KEY_FLAG) + column->info |= DB_COLUMN_PRI_KEY; + } + + for (i = 0; (myrow = mysql_fetch_row (res)); i++) + { + lengths = mysql_fetch_lengths (res); + row = db_row_new (result->ncols, i); + g_ptr_array_add (result->data, row); + + for (j = 0; j < result->ncols; j++) + db_mysql_set_value (&row->value[j], gtypes[j], myrow[j], lengths[j]); + } + + mysql_free_result (res); + } + else if (mysql_field_count (obj->mysql) == 0) + { + result = g_new (DbResult, 1); + result->nrows = mysql_affected_rows (obj->mysql); + result->ncols = 0; + result->column = NULL; + result->data = NULL; + set->results = g_slist_append (set->results, result); + } + } + while (mysql_more_results (obj->mysql) && !mysql_next_result (obj->mysql)); + + if ((errno = mysql_errno (obj->mysql))) + { + switch (errno) + { + case CR_SERVER_LOST: + case CR_SERVER_GONE_ERROR: + errno = DB_CONN_ERROR_LOST; + break; + case CR_OUT_OF_MEMORY: + case CR_COMMANDS_OUT_OF_SYNC: + errno = DB_CONN_ERROR_QUERY_FATAL; + break; + case CR_UNKNOWN_ERROR: + default: + errno = DB_CONN_ERROR_UNKNOW; + } + + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,errno + ,"%s", mysql_error (obj->mysql) + ); + db_result_set_free (set); + set = NULL; + } + + return set; +} + +static void db_mysql_kill_query (DbMysql * obj) +{ + if (obj->mysql) + { + gboolean killed = FALSE; + MYSQL * new_conn = mysql_init (NULL); + + if (new_conn + && mysql_real_connect (new_conn, obj->host, obj->user, obj->pass, NULL, 0, NULL, 0)) + { + gchar * sql = g_strdup_printf ("KILL QUERY %lu", obj->thread_id); + + if (!mysql_query (new_conn, sql)) + { + MYSQL_RES * res = mysql_store_result (new_conn); + mysql_free_result (res); + killed = TRUE; + } + + g_free (sql); + } + if (new_conn) + { + if (mysql_errno (new_conn)) + g_warning ("DbMysql: %s", mysql_error (new_conn)); + + mysql_close (new_conn); + } + + if (!killed && obj->socket != -1 && !shutdown (obj->socket, SHUT_RDWR)) + obj->socket = -1; + } +} + +static void db_mysql_value_render (SqlValue * obj, SqlRender * render) +{ + if (G_VALUE_TYPE (obj->value) == G_TYPE_BYTES) + { + gsize to_size; + gsize from_size; + GString * buffer = render->buffer; + GBytes * bin = g_value_get_boxed (obj->value); + const gchar * from = g_bytes_get_data (bin, &from_size); + + sql_render_add_espace (render); + sql_render_append (render, "'"); + g_string_set_size (buffer, buffer->len + (from_size * 2 + 1)); + to_size = mysql_real_escape_string ( + DB_MYSQL (render->data)->mysql, + &(buffer->str[buffer->len]), from, from_size + ); + buffer->len += to_size; + sql_render_append (render, "'"); + } + else + sql_object_render (SQL_OBJECT (obj), render); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_mysql_init (DbMysql * obj) +{ + SqlRender * render = sql_render_new ('`'); + sql_render_register_function (render, SQL_TYPE_VALUE, + (SqlRenderFunc) db_mysql_value_render); + + DB_PLUGIN (obj)->render = render; + obj->mysql = NULL; + obj->host = NULL; + obj->user = NULL; + obj->pass = NULL; + obj->thread_id = 0; + obj->socket = -1; + obj->is_open = FALSE; +} + +static void db_mysql_class_init (DbMysqlClass * k) +{ + DbPluginClass * klass = DB_PLUGIN_CLASS (k); + klass->open = (DbPluginOpenFunc) db_mysql_open; + klass->close = (DbPluginCloseFunc) db_mysql_close; + klass->set_ssl = (DbPluginSetSSL) db_mysql_set_ssl; + klass->query = (DbPluginQueryFunc) db_mysql_query; + klass->kill_query = (DbPluginKillQueryFunc) db_mysql_kill_query; +} diff --git a/plugin/mysql/db-mysql.h b/plugin/mysql/db-mysql.h new file mode 100644 index 0000000..31fec5d --- /dev/null +++ b/plugin/mysql/db-mysql.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_MYSQL_H +#define DB_MYSQL_H + +#include +#include + +#define DB_TYPE_MYSQL (db_mysql_get_type ()) +#define DB_MYSQL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_MYSQL, DbMysql)) +#define DB_IS_MYSQL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_MYSQL)) +#define DB_MYSQL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_MYSQL, DbMysqlClass)) +#define DB_IS_MYSQL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_MYSQL)) +#define DB_MYSQL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_MYSQL, DbMysqlClass)) + +typedef struct _DbMysql DbMysql; +typedef struct _DbMysqlClass DbMysqlClass; + +struct _DbMysql +{ + DbPlugin parent; + MYSQL * mysql; + gchar * host; + gchar * user; + gchar * pass; + gulong thread_id; + gint socket; + gboolean is_open; +}; + +struct _DbMysqlClass +{ + DbPluginClass parent; +}; + +GType db_mysql_get_type (); +DbConn * db_mysql_new (); + +#endif \ No newline at end of file diff --git a/plugin/pg/Makefile.am b/plugin/pg/Makefile.am new file mode 100644 index 0000000..39731a1 --- /dev/null +++ b/plugin/pg/Makefile.am @@ -0,0 +1,41 @@ +include $(top_srcdir)/Makefile.decl + +pg_lib_LTLIBRARIES = libdbpg.la + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I/usr/include/postgresql \ + $(glib_CFLAGS) +libdbpg_la_LIBADD = \ + $(glib_LIBS) \ + $(top_builddir)/db/libdb.la \ + -lpq +libdbpg_la_LDFLAGS = -avoid-version +libdbpg_la_SOURCES = \ + db-pg.h \ + db-pg.c + +# FIXME error durante la ejecucion del parser: +# llama a la funcion Parse en gram.c de sql en vez de en db-pg-gram.c +# db-pg-parser.h \ +# db-pg-parser.c + +#lemonpath = $(top_builddir)/sql/parser +#db-pg-parser.c: $(SCAN) db-pg-gram.c +# $(ragel_v)$(RAGEL) -C -G2 -o db-pg-parser.c $(SCAN) + +#db-pg-gram.h: db-pg-gram.c +#db-pg-gram.c: $(GRAM) $(lemonpath)/lemon +# $(lemon_v)$(lemonpath)/lemon -q $(GRAM) + +#$(lemonpath)/lemon: $(lemonpath)/lemon.c $(lemonpath)/lempar.c +# $(CC) -o $(lemonpath)/lemon $(lemonpath)/lemon.c + +#SCAN = db-pg-scan.rl +#GRAM = db-pg-gram.y + +#CLEANFILES = db-pg-gram.c db-pg-gram.h db-pg-parser.c + +install-data-hook: + rm -f $(DESTDIR)$(pg_libdir)/libdbpg.la + rm -f $(DESTDIR)$(pg_libdir)/libdbpg.a \ No newline at end of file diff --git a/plugin/pg/db-pg.c b/plugin/pg/db-pg.c new file mode 100644 index 0000000..ad060e6 --- /dev/null +++ b/plugin/pg/db-pg.c @@ -0,0 +1,880 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +// Macros to avoid redefinition warnings for constants +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION +#undef _ + +#include +#include +#include +#include "db-pg.h" + +G_DEFINE_TYPE (DbPg, db_pg, DB_TYPE_PLUGIN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void db_pg_close (DbPlugin * obj) +{ + DbPg * pg = DB_PG (obj); + + if (pg->pg_cancel) + { + PQfreeCancel (pg->pg_cancel); + pg->pg_cancel = NULL; + } + + if (pg->conn) + { + PQfinish (pg->conn); + pg->conn = NULL; + pg->socket = -1; + } +} + +static gboolean db_pg_open (DbPlugin * obj, const gchar * host, + const gchar * schema, const gchar * user, const gchar * pass, GError ** err) +{ + DbPg * pg = DB_PG (obj); + + pg->conn = PQsetdbLogin ( + host + ,NULL + ,NULL + ,NULL + ,schema + ,user + ,pass + ); + + if (!pg->conn) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_OPENING + ,_("Can't allocate the needed memory") + ); + return FALSE; + } + + if (PQstatus (pg->conn) != CONNECTION_OK) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_OPENING + ,"%s", PQerrorMessage (pg->conn) + ); + db_pg_close (obj); + return FALSE; + } + + pg->pg_cancel = PQgetCancel (pg->conn); + pg->socket = PQsocket (pg->conn); + return TRUE; +} + +static void db_pg_set_ssl (DbPg * obj, const gchar * ca) +{ + g_warning ("DbPg: SSL not supported by this plugin"); +} + +//+++++++ Internal Methods for db_pg_query () + +static GType db_pg_get_g_type (Oid type) +{ + switch (type) + { + case BOOLOID: + return G_TYPE_BOOLEAN; + case INT8OID: + return G_TYPE_INT64; + case INT2OID: + case INT4OID: + return G_TYPE_INT; + case TIDOID: + case OIDOID: + return G_TYPE_UINT; + case FLOAT4OID: + return G_TYPE_FLOAT; + case FLOAT8OID: + case NUMERICOID: + return G_TYPE_DOUBLE; + case DATEOID: + return G_TYPE_DATE; + case TIMESTAMPOID: + case TIMESTAMPTZOID: + return G_TYPE_DATE_TIME; + case TIMEOID: + case TIMETZOID: + return GVN_TYPE_TIME; + case BYTEAOID: + return G_TYPE_BYTES; + case ABSTIMEOID: + case RELTIMEOID: + case TINTERVALOID: + case CHAROID: + case TEXTOID: + case NAMEOID: + case XMLOID: + case CSTRINGOID: + case BPCHAROID: + case VARCHAROID: + default: + return G_TYPE_STRING; + } +} + +static void db_pg_set_date_value (GValue * val, Oid oid, gchar * pg_val) +{ + gpointer date = NULL; + + switch(oid) + { + case TIMEOID: // hh:mm:ss + { + gchar ** hms = g_strsplit (pg_val, ":", 3); + + if (g_strv_length (hms) >= 3) + date = gvn_time_new (atoi (hms[0]), atoi (hms[1]), + atoi (hms[2]), 0); + + g_value_init (val, GVN_TYPE_TIME); + + g_strfreev (hms); + break; + } + case TIMETZOID: // hh:mm:ss±zz + { + gchar ** hmsz = g_strsplit_set (pg_val, ":+-", 4); + + if (g_strv_length (hmsz) >= 4) + date = gvn_time_new (atoi (hmsz[0]), atoi(hmsz[1]), + atoi(hmsz[2]), atoi (hmsz[3])); + + g_value_init (val, GVN_TYPE_TIME); + + g_strfreev (hmsz); + break; + } + case TIMESTAMPOID: // yyyy-mm-dd hh:mn:ss + { + gchar ** dt = g_strsplit_set (pg_val, " -:", 6); + + if (g_strv_length (dt) >= 6) + date = g_date_time_new_local + (atoi (dt[0]), atoi (dt[1]), atoi (dt[2]), + atoi (dt[3]), atoi (dt[4]), atoi (dt[5])); + + g_value_init (val, G_TYPE_DATE_TIME); + + g_strfreev (dt); + break; + } + case TIMESTAMPTZOID: // yyyy-mm-dd hh:mm:ss±zz + { + GTimeZone * tz = NULL; + gchar ** dtz = g_strsplit_set (pg_val, " -:+", 7); + + if (strlen (pg_val) >= 16 && g_strv_length (dtz) >= 6) + date = g_date_time_new + (tz = g_time_zone_new (pg_val + 16), + atoi (dtz[0]), atoi (dtz[1]), atoi (dtz[2]), + atoi (dtz[3]), atoi (dtz[4]), atoi (dtz[5])); + + g_value_init (val, G_TYPE_DATE_TIME); + + g_strfreev (dtz); + g_time_zone_unref (tz); + break; + } + case DATEOID: // yyyy-mm-dd + { + gchar ** ymd = g_strsplit (pg_val, "-", 3); + + if (g_strv_length (ymd) >= 3) + date = g_date_new_dmy + (atoi (ymd[2]), atoi (ymd[1]) ,atoi (ymd[0])); + + g_value_init (val, G_TYPE_DATE); + + g_strfreev (ymd); + break; + } + case TINTERVALOID: + // TODO If needed + break; + } + + g_value_take_boxed (val, date); +} + +static void db_pg_set_g_value (GValue * val, GType type, gchar * pg_val) +{ + val = g_value_init (val, type); + + switch (type) + { + case G_TYPE_BOOLEAN: + if(!g_strcmp0(pg_val, "t")) + g_value_set_boolean (val, TRUE); + else + g_value_set_boolean (val, FALSE); + break; + case G_TYPE_UINT: + g_value_set_uint(val, (guint) g_ascii_strtoull (pg_val, NULL, 10)); + break; + case G_TYPE_INT: + g_value_set_int (val, atoi (pg_val)); + break; + case G_TYPE_INT64: + g_value_set_int64 (val, g_ascii_strtoll (pg_val, NULL, 10)); + break; + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + g_value_set_double (val, g_strtod (pg_val, NULL)); + break; + case G_TYPE_STRING: + g_value_set_string (val, pg_val); + break; + default: + if (type == G_TYPE_BYTES) + { + gsize l; + guchar * esc = PQunescapeBytea ((guchar *) pg_val, &l); + GBytes * bin = g_bytes_new (esc, l); + g_free (esc); + g_value_set_boxed (val, bin); + g_bytes_unref (bin); + } + } +} + +static inline gint added (GSList * list, const gchar * target) +{ + gint r = 0; + GSList * n; + + for (n = list; n; n = n->next, r++) + if (!g_strcmp0 ((gchar *) n->data, target)) + return r; + + return -1; +} + +static inline void free_array (const gpointer array) +{ + g_ptr_array_free (array, TRUE); +} + +static DbResultSet * __db_pg_query + (DbPlugin * obj, const gchar * sql, gboolean columns, GError ** err) +{ + DbPg * pg = DB_PG (obj); + + PGresult * res = NULL; + DbResult * result; + DbResultSet * set; + gint i = 0, j = 0; + gint ntup = 0, ncol = 0; + guint error_code = 0; + guint query_count = 0; + guint sel_count = 0; + GSList * list = NULL; + gboolean failed = FALSE; + guint s = 5; + GPtrArray * ind_select = g_ptr_array_sized_new (s); + GPtrArray * types = g_ptr_array_new_full (s, (GDestroyNotify) g_free); + GPtrArray * oid_types = g_ptr_array_new_full (s, (GDestroyNotify) g_free); + GPtrArray * rel_oid = g_ptr_array_new_full (s, (GDestroyNotify) free_array); + GPtrArray * col = g_ptr_array_new_full (s,(GDestroyNotify) free_array); + GPtrArray * name = g_ptr_array_new_full (s,(GDestroyNotify) free_array); + + set = db_result_set_new (); + + if (PQsendQuery (pg->conn, sql) != 1) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_QUERY_FAILED + ,"%s", PQerrorMessage (pg->conn) + ); + failed = TRUE; + } + else while ((res = PQgetResult (pg->conn))) + { + result = g_new (DbResult, 1); + result->data = NULL; + result->column = NULL; + result->nrows = 0; + result->ncols = 0; + + switch (PQresultStatus (res)) + { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + { + gchar ** q_t = NULL; + + q_t = g_strsplit (PQcmdStatus (res), " ", G_MAXINT); + + if (!g_strcmp0 (q_t[0],"SELECT")) + { + ntup = PQntuples (res); + result->nrows = ntup; + ncol = PQnfields (res); + result->ncols = ncol; + + gchar * pg_val; + GPtrArray * row = g_ptr_array_new_full + ((guint) ntup + 15, (GDestroyNotify) db_row_free); + GPtrArray * oid = g_ptr_array_sized_new ((guint) ncol); + GPtrArray * col_num = g_ptr_array_sized_new ((guint) ncol); + GType * type = g_new (GType, ncol); + Oid * type_oid = g_new (Oid, ncol); + GPtrArray * col_name = g_ptr_array_new_full ((guint) ncol, (GDestroyNotify) g_free); + + sel_count++; + + for (j = 0; j < ncol; j++) + { + type_oid[j] = PQftype (res, j); + type[j] = db_pg_get_g_type (type_oid[j]); + + g_ptr_array_add (col_num, GINT_TO_POINTER (PQftablecol (res, j))); + g_ptr_array_add (oid, GUINT_TO_POINTER (PQftable (res, j))); + g_ptr_array_add (col_name, g_strdup (PQfname (res, j))); + } + + if (ntup > 0) + for (i = 0; i < ntup; i++) + { + DbRow * r = db_row_new (result->ncols, i); + + for (j = 0; j < ncol; j++) + { + if (!PQgetisnull (res, i, j)) + { + pg_val = PQgetvalue (res, i, j); + + if (type[j] == G_TYPE_DATE + || type[j] == G_TYPE_DATE_TIME + || type[j] == GVN_TYPE_TIME) + db_pg_set_date_value (&(r->value)[j], + type_oid[j], pg_val); + else + db_pg_set_g_value (&(r->value)[j], type[j], pg_val); + } + else // IF NULL: GvnNull type + g_value_init (&(r->value[j]), GVN_TYPE_NULL); + } + + g_ptr_array_add (row, r); + } + + result->data = row; + + g_ptr_array_add (rel_oid, oid); + g_ptr_array_add (col, col_num); + g_ptr_array_add (name, col_name); + g_ptr_array_add (types, type); + g_ptr_array_add (oid_types, type_oid); + g_ptr_array_add (ind_select, GUINT_TO_POINTER (query_count)); + } + + if (!g_strcmp0 (q_t[0], "INSERT") + || !g_strcmp0 (q_t[0], "UPDATE") + || !g_strcmp0 (q_t[0], "DELETE")) + result->nrows = atoi (PQcmdTuples (res)); + + g_strfreev (q_t); + break; + } + case PGRES_BAD_RESPONSE: + error_code = DB_CONN_ERROR_BAD_RESPONSE; + break; + case PGRES_EMPTY_QUERY: + error_code = DB_CONN_ERROR_QUERY_EMPTY; + break; + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + case PGRES_COPY_BOTH: + case PGRES_NONFATAL_ERROR: + error_code = DB_CONN_ERROR_QUERY_NONFATAL; + break; + case PGRES_FATAL_ERROR: + error_code = DB_CONN_ERROR_QUERY_FATAL; + break; + } // switch STATUS + + if (error_code && !failed) + { + PGresult * discarded_res; + + while ((discarded_res = PQgetResult (pg->conn))) + PQclear (discarded_res); + + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,error_code + ,"%s", PQresultErrorMessage (res) + ); + failed = TRUE; + } + + query_count++; + list = g_slist_prepend (list, result); + + PQclear (res); + } // while (res) END + + if (err && *err && PQstatus (pg->conn) != CONNECTION_OK) + (*err)->code = DB_CONN_ERROR_LOST; + + list = g_slist_reverse (list); + + if (!failed && sel_count > 0 && columns) + { + gint k; + Oid t_oid; + GPtrArray * col_num = NULL; + GPtrArray * oid = NULL; + gchar * info_query = g_strdup (""); + gchar * pkey_query; + gchar * pkey_buff = NULL // buffers for memory leak avoidance + ,* info_buff = NULL; + +// Set info_query string using the previously stored table OIDs and column numbers + for (i = 0; i < (gint) sel_count; i++) + { + pkey_query = g_strdup (""); + oid = g_ptr_array_index (rel_oid, i); + col_num = g_ptr_array_index (col, i); + + info_buff = info_query; + info_query = g_strconcat (info_query, + "SELECT attname, attrelid, attnotnull, adsrc, attnum " + "FROM pg_attribute " + "INNER JOIN pg_class c ON c.oid = attrelid " + "LEFT JOIN pg_attrdef a " + "ON a.adrelid = attrelid AND a.adnum = attnum " + "WHERE attisdropped = false AND (c.oid, attnum) IN (" + , NULL); + g_free (info_buff); + + pkey_buff = pkey_query; + pkey_query = g_strconcat (pkey_query, + "SELECT c.oid, relname, indkey " + "FROM pg_class c " + "LEFT JOIN pg_index i ON indrelid = c.oid " + "AND indisprimary " + "WHERE c.oid IN (" + , NULL); + g_free (pkey_buff); + + gint ncolumns = (gint) oid->len; + gchar * buff_j = NULL; + + for (j = 0; j < ncolumns; j++) + { + t_oid = GPOINTER_TO_UINT (g_ptr_array_index (oid, j)); + + buff_j = g_strdup_printf ("%d", t_oid); + pkey_buff = pkey_query; + pkey_query = g_strconcat (pkey_query, buff_j, NULL); + g_free (pkey_buff); + g_free (buff_j); + + if (j < ncolumns-1 && ncolumns > 1) + { + pkey_buff = pkey_query; + pkey_query = g_strconcat (pkey_query, ",", NULL); + g_free (pkey_buff); + } + + buff_j = g_strdup_printf ("(%d,%d)" + ,t_oid + ,GPOINTER_TO_INT (g_ptr_array_index (col_num, j))); + info_buff = info_query; + info_query = g_strconcat (info_query, buff_j, NULL); + g_free (buff_j); + g_free (info_buff); + + info_buff = info_query; + if (j < ncolumns-1 && ncolumns > 1) + info_query = g_strconcat (info_query, ",", NULL); + else + info_query = g_strconcat (info_query, ")", NULL); + g_free (info_buff); + } + + pkey_buff = pkey_query; + pkey_query = g_strconcat (pkey_query, ");", NULL); + g_free (pkey_buff); + + info_buff = info_query; + info_query = g_strconcat (info_query, ";", NULL); + g_free (info_buff); + + info_buff = info_query; + info_query = g_strconcat (info_query, pkey_query, NULL); + g_free (info_buff); + g_free (pkey_query); + } + + PGresult * res_col; + PQsendQuery (pg->conn, info_query); + + DbResult * r = NULL; + GValue * def = NULL; + Oid * tab_oid = NULL; + gboolean * nullable = NULL; + guint * col_n = NULL; + i = 0; + + while ((res_col = PQgetResult (pg->conn)) && !failed) + { + if (PQresultStatus (res_col) != PGRES_TUPLES_OK) + { + g_set_error (err + ,DB_CONN_LOG_DOMAIN + ,DB_CONN_ERROR_QUERY_FAILED + ,"%s", PQresultErrorMessage (res_col) + ); + failed = TRUE; + } + else + { + guint ind = i/2; + ntup = PQntuples (res_col); + guint x = i%2; + + if (!x) + { + GPtrArray * col_iter = g_ptr_array_index (col, ind) + ,* rel_iter = g_ptr_array_index (rel_oid, ind) + ,* name_array; + + r = g_slist_nth_data + (list, GPOINTER_TO_UINT (g_ptr_array_index (ind_select, ind))); + ncol = r->ncols; + r->column = g_new (DbColumn, ncol); + // Relates each column with its corresponding row in the aux query + gint col_tup[ncol]; + // Array of the default values for the columns + def = g_new0 (GValue, ncol); + // Array of GvnParamSpecFlags for the columns + nullable = g_new (gboolean, ncol); + tab_oid = g_new (Oid, ncol); + col_n = g_new (guint, ncol); + + for (j = 0; j < ncol; j++) + { + r->column[j].spec = NULL; + r->column[j].info = 0; + r->column[j].name = NULL; + r->column[j].table = NULL; + + if (GPOINTER_TO_INT (g_ptr_array_index (col_iter, j)) == 0) + { + gchar * fname; + + name_array = g_ptr_array_index (name, ind); + fname = g_strdup (g_ptr_array_index (name_array, j)); + + // Set the metadata if it is a *CALCULATED FIELD* + col_tup[j] = -1; + + if (fname[0] == '?') + { + r->column[j].name = g_strdup (""); + g_free (fname); + } + else + r->column[j].name = fname; + + r->column[j].display = g_strdup (r->column[j].name); + r->column[j].table = g_strdup (""); + r->column[j].spec = gvn_param_spec_new_with_attrs + (((GType*) g_ptr_array_index (types, ind))[j] + , FALSE, FALSE, NULL); + } + else + for (k = 0; k < ntup; k++) + if (GPOINTER_TO_INT (g_ptr_array_index (col_iter, j)) + == atoi (PQgetvalue (res_col, k, 4)) + && GPOINTER_TO_INT (g_ptr_array_index (rel_iter, j)) + == atoi (PQgetvalue (res_col, k, 1))) + { + col_tup[j] = k; + break; + } + + if (col_tup[j] >= 0) // NOT a calculated field. + { + gint ctup = col_tup[j]; + GType type = ((GType*) g_ptr_array_index (types, ind))[j]; + Oid oid = ((Oid*) g_ptr_array_index (oid_types, ind))[j]; + gchar * fname = PQgetvalue (res_col, ctup, 0); + gchar * fdisp; + + name_array = g_ptr_array_index (name, ind); + fdisp = g_ptr_array_index (name_array, j); + + r->column[j].name = g_strdup (fname); + r->column[j].display = g_strdup (fdisp); + + // Getting the default value from res_col //FIXME use the parser + if (!PQgetisnull (res_col, ctup, 3)) + { + gchar * pg_val = PQgetvalue (res_col, ctup, 3); + + if (type == G_TYPE_STRING || type == G_TYPE_CHAR + || type == G_TYPE_BYTES || type == G_TYPE_DATE + || type == G_TYPE_DATE_TIME || type == GVN_TYPE_TIME) + { + gchar ** split = NULL; + + if (type == G_TYPE_DATE + || type == G_TYPE_DATE_TIME + || type == GVN_TYPE_TIME) + db_pg_set_date_value (&def[j], oid + ,(split = g_strsplit + (pg_val, "'", G_MAXINT))[1]); + else + db_pg_set_g_value (&def[j], type + ,(split = g_strsplit + (pg_val, "'", G_MAXINT))[1]); + + g_strfreev (split); + } + else + { + if (g_str_has_prefix (pg_val, "nextval")) + {// Serial fields + GValue * v = g_new0 (GValue, 1); + gchar ** split = g_strsplit_set (pg_val, "(':)", G_MAXINT8); + SqlObject * function = sql_function_new ("currval", NULL); + g_value_set_string (g_value_init (v, G_TYPE_STRING), split[2]); + sql_object_add_child (function, "params", sql_value_new_with_value (v)); + g_value_unset (v); + g_free (v); + g_value_take_object (g_value_init (&def[j], SQL_TYPE_FUNCTION), + g_object_ref_sink (function)); + g_strfreev (split); + } + else + db_pg_set_g_value (&def[j], type, pg_val); + } + } + else + g_value_init (&def[j], GVN_TYPE_NULL); + + + // Checking whether the column can be NULL + nullable[j] = !g_strcmp0 (PQgetvalue (res_col, ctup, 2),"t") + ? FALSE : TRUE; + + tab_oid[j] = atoi (PQgetvalue (res_col, ctup, 1)); + r->column[j].info = 0; + col_n[j] = atoi (PQgetvalue (res_col, ctup, 4)); + } + } + } + else + { + guint nkeys; + gchar ** pkey = NULL; + GSList * prev_tables = NULL; + guint nedit = 0, l; + struct + { + gchar * name; + guint keys[ncol]; + guint num; + guint total; + } + edit[ntup]; + + for (j = 0; j < ncol; j++) + { + GType type; + + for (k = 0; k < ntup; k++) + if (tab_oid[j] == atoi (PQgetvalue (res_col, k, 0))) + { + guint n; + + if (!r->column[j].table) + r->column[j].table = + g_strdup (PQgetvalue (res_col, k, 1)); + + g_strfreev (pkey); + pkey = g_strsplit (PQgetvalue (res_col, k, 2), " ", G_MAXINT); + nkeys = g_strv_length (pkey); + + for (n = 0; n < nkeys; n++) + if (atoi (pkey[n]) == col_n[j]) + { + gint pos = added (prev_tables, + r->column[j].table); + + if (pos >= 0) + { + edit[pos].keys[edit[pos].num] = j; + edit[pos].num++; + } + else + { + edit[nedit].name = r->column[j].table; + edit[nedit].keys[0] = j; + edit[nedit].num = 1; + edit[nedit].total = nkeys; + nedit++; + prev_tables = g_slist_append + (prev_tables, r->column[j].table); + } + } + } + + type = ((GType *) g_ptr_array_index (types, ind))[j]; + + if (!r->column[j].spec) + r->column[j].spec = gvn_param_spec_new_with_attrs + (type, FALSE, nullable[j], &def[j]); + + if (G_IS_VALUE (&def[j])) + g_value_unset (&def[j]); + } + + for (j = 0; j < ncol; j++) + for (k = 0; k < nedit; k++) + if (edit[k].num == edit[k].total + && !g_strcmp0 (r->column[j].table, edit[k].name)) + for (l = 0; l < edit[k].num; l++) + r->column[edit[k].keys[l]].info |= DB_COLUMN_PRI_KEY; + + g_free (nullable); + g_free (def); + g_free (col_n); + g_free (tab_oid); + g_strfreev (pkey); + g_slist_free (prev_tables); + } + } + + i++; + PQclear (res_col); + } + + g_free (info_query); + + PQclear (res_col); + } + + free_array (ind_select); + free_array (types); + free_array (oid_types); + free_array (rel_oid); + free_array (name); + free_array (col); + + set->results = list; + + if (failed) + { + db_result_set_free (set); + return NULL; + } + + return set; +} + +static DbResultSet * db_pg_query (DbPlugin * obj, const gchar * sql, GError ** err) +{ + return __db_pg_query (obj, sql, TRUE, err); +} + +static void db_pg_kill_query (DbPg * obj) +{ + if (obj->conn) + { + gboolean killed = FALSE; + + if (obj->pg_cancel) + { + gchar errbuf[256] = ""; + + if (PQcancel (obj->pg_cancel, errbuf, 256) == 1) + killed = TRUE; + else + g_warning ("DbPg: %s", errbuf); + } + + if (!killed && obj->socket != -1 && !shutdown (obj->socket, SHUT_RDWR)) + obj->socket = -1; + } +} + +static void db_pg_value_render (SqlValue * obj, SqlRender * render) +{ + if (G_VALUE_TYPE (obj->value) == G_TYPE_BYTES) + { + gsize len; + gchar * buffer; + GBytes * bin = g_value_get_boxed (obj->value); + buffer = (gchar *) PQescapeByteaConn ( + DB_PG (render->data)->conn + ,g_bytes_get_data (bin, NULL) + ,g_bytes_get_size (bin) + ,&len + ); + sql_render_printf (render, "E'%s'::bytea", buffer); + PQfreemem (buffer); + } + else + sql_object_render (SQL_OBJECT (obj), render); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void db_pg_init (DbPg * obj) +{ + SqlRender * render = sql_render_new ('"'); + sql_render_register_function (render, SQL_TYPE_VALUE, + (SqlRenderFunc) db_pg_value_render); + + DB_PLUGIN (obj)->render = render; + obj->conn = NULL; + obj->socket = -1; +} + +static void db_pg_class_init (DbPgClass * k) +{ + DbPluginClass * klass = DB_PLUGIN_CLASS (k); + klass->open = db_pg_open; + klass->close = db_pg_close; + klass->set_ssl = (DbPluginSetSSL) db_pg_set_ssl; + klass->query = db_pg_query; + klass->kill_query = (DbPluginKillQueryFunc) db_pg_kill_query; +} diff --git a/plugin/pg/db-pg.h b/plugin/pg/db-pg.h new file mode 100644 index 0000000..aa4d646 --- /dev/null +++ b/plugin/pg/db-pg.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DB_PG_H +#define DB_PG_H + +#include + +#define DB_TYPE_PG (db_pg_get_type ()) +#define DB_PG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DB_TYPE_PG, DbPg)) +#define DB_IS_PG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DB_TYPE_PG)) +#define DB_PG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DB_TYPE_PG, DbPgClass)) +#define DB_IS_PG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DB_TYPE_PG)) +#define DB_PG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DB_TYPE_PG, DbPgClass)) + +typedef struct _DbPg DbPg; +typedef struct _DbPgClass DbPgClass; + +struct _DbPg +{ + DbPlugin parent; + PGconn * conn; + PGcancel * pg_cancel; + gint socket; +}; + +struct _DbPgClass +{ + DbPluginClass parent; +}; + +GType db_pg_get_type (); +DbConn * db_pg_new (); + +#endif \ No newline at end of file diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..fe62387 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,47 @@ +gvn/gvn-misc.c +gvn/gvn-param-spec.c +gvn/gvn-param.c +gvn/gvn-value.c + +sql/sql-render.c +sql/parser/gram.c +sql/sql-value.c +sql/sql-string.c + +plugin/mysql/db-mysql.c +plugin/pg/db-pg.c + +db/db-iterator.c +db/db-model.c +db/db-calc.c +db/db-param.c +db/db-conn.c +db/db-request.c +db/db-file-loader.c + +vn/vn-gui.c +vn/vn-model.c +vn/vn-grid.c +vn/vn-handler.c +vn/vn-form.c +vn/vn-login.c +vn/vn-field.c +vn/field/vn-entry.c +vn/field/vn-spin.c +vn/field/vn-combo.c +vn/field/vn-completion.c +vn/field/vn-date-chooser.c +vn/field/vn-image.c +vn/field/vn-http-image.c +vn/vn-column.c +vn/column/vn-column-entry.c +vn/column/vn-column-spin.c +vn/column/vn-column-combo.c +vn/column/vn-column-image.c + +vn/gui/login.glade +vn/gui/main.glade +vn/gui/child-window.glade +vn/gui/actions.glade + +module/data/users.glade diff --git a/po/POTFILES.skip b/po/POTFILES.skip new file mode 100644 index 0000000..926cfab --- /dev/null +++ b/po/POTFILES.skip @@ -0,0 +1,13 @@ +module/data/example.xml +module/data/consulter.glade +module/data/customer.glade +module/data/signer.glade +module/src/vn-consulter.c +module/src/vn-customer.c +module/src/vn-signer.c +module/src/vn-users.c + +template/lib-object.c + +main/vn-hedera.desktop.in.in +main/vn-hedera.desktop.in diff --git a/po/ca.po b/po/ca.po new file mode 100644 index 0000000..b1e1b14 --- /dev/null +++ b/po/ca.po @@ -0,0 +1,1075 @@ +# Catalan translations for hedera package. +# Copyright (C) 2012 THE hedera'S COPYRIGHT HOLDER +# This file is distributed under the same license as the hedera package. +# Alejandro T. Colombini Gómez , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: hedera 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-07-10 11:56+0200\n" +"PO-Revision-Date: 2013-06-04 13:38+0200\n" +"Last-Translator: Alejandro T. Colombini Gómez \n" +"Language-Team: Catalan\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. * +#. * SECTION: gvn-misc +#. * @Short_description: Miscelaneous utility functions +#. * @Title: Miscelaneous utility functions +#. * +#: ../gvn/gvn-misc.c:27 ../gvn/gvn-misc.c:31 +msgid "Err" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Mon" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Tue" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Wed" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Thu" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Fri" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Sat" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Sun" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Jan" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Feb" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Mar" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Apr" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "May" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Jun" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Jul" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Aug" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Sep" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Oct" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Nov" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Dec" +msgstr "" + +#: ../gvn/gvn-param-spec.c:263 +#, c-format +msgid "Param not editable" +msgstr "Parametre no editable" + +#: ../gvn/gvn-param-spec.c:268 +#, c-format +msgid "Param can't be NULL" +msgstr "El parametre no pot ser nul" + +#: ../gvn/gvn-param-spec.c:274 +#, c-format +msgid "Incompatible type for this param" +msgstr "Tipus incompatible per a aquest parametre" + +#: ../gvn/gvn-param.c:443 ../sql/sql-value.c:235 ../vn/vn-field.c:518 +msgid "Value" +msgstr "" + +#: ../gvn/gvn-param.c:444 +msgid "The value of the param" +msgstr "" + +#: ../gvn/gvn-param.c:450 +msgid "Master" +msgstr "" + +#: ../gvn/gvn-param.c:451 +msgid "The master GvnParam of this parameter" +msgstr "" + +#: ../gvn/gvn-param.c:457 ../db/db-iterator.c:1070 +msgid "Mode" +msgstr "" + +#: ../gvn/gvn-param.c:458 +msgid "The mode of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:464 +msgid "Status" +msgstr "Estat" + +#: ../gvn/gvn-param.c:465 +msgid "The current status of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:471 +msgid "Spec" +msgstr "" + +#: ../gvn/gvn-param.c:472 +msgid "The spec of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:478 ../vn/vn-field.c:546 +msgid "Glib Type" +msgstr "" + +#: ../gvn/gvn-param.c:479 ../vn/vn-field.c:547 +msgid "The type of the value" +msgstr "" + +#: ../gvn/gvn-param.c:485 ../vn/vn-field.c:553 ../vn/vn-column.c:354 +msgid "Editable" +msgstr "" + +#: ../gvn/gvn-param.c:486 +msgid "Whether the param value can be modified" +msgstr "" + +#: ../gvn/gvn-param.c:492 ../vn/vn-field.c:560 +msgid "Null" +msgstr "" + +#: ../gvn/gvn-param.c:493 +msgid "Whether the param value can be of type GVN_TYPE_NULL" +msgstr "" + +#: ../gvn/gvn-param.c:499 ../vn/vn-field.c:567 +msgid "Default Value" +msgstr "" + +#: ../gvn/gvn-param.c:500 ../vn/vn-field.c:568 +msgid "The default value" +msgstr "" + +#: ../gvn/gvn-value.c:434 +#, c-format +msgid "Attempting to compare invalid types: %s\n" +msgstr "S'ha intentat comparar tipus invàlids: %s\n" + +#: ../sql/sql-render.c:413 +msgid "Delimiter" +msgstr "" + +#: ../sql/sql-render.c:414 +msgid "The character used for delimite the name of fields, tables..." +msgstr "" + +#: ../sql/sql-value.c:228 +msgid "Param" +msgstr "" + +#: ../sql/sql-value.c:229 +msgid "The param which is linked" +msgstr "" + +#: ../sql/sql-value.c:236 +msgid "The value" +msgstr "" + +#: ../sql/sql-string.c:198 ../db/db-iterator.c:1063 ../db/db-model.c:3411 +#: ../db/db-request.c:466 ../vn/field/vn-combo.c:356 +#: ../vn/field/vn-completion.c:315 ../vn/column/vn-column-combo.c:297 +msgid "SQL" +msgstr "" + +#: ../sql/sql-string.c:199 +msgid "An arbitrary SQL string" +msgstr "" + +#: ../plugin/mysql/db-mysql.c:68 ../plugin/pg/db-pg.c:76 +#, c-format +msgid "Can't allocate the needed memory" +msgstr "" + +#: ../db/db-iterator.c:284 +msgid "DbIterator: Can't reassign the 'model' property" +msgstr "" + +#: ../db/db-iterator.c:942 +msgid "DbIterator: Can't set the 'conn' property because model isn't set" +msgstr "" + +#: ../db/db-iterator.c:1049 ../db/db-calc.c:329 ../vn/field/vn-combo.c:342 +#: ../vn/column/vn-column-combo.c:283 +msgid "Model" +msgstr "" + +#: ../db/db-iterator.c:1050 +msgid "The DbModel handled by the iterator" +msgstr "" + +#: ../db/db-iterator.c:1056 ../db/db-model.c:3395 ../db/db-request.c:459 +#: ../vn/vn-gui.c:1531 ../vn/vn-form.c:241 ../vn/field/vn-combo.c:349 +#: ../vn/field/vn-completion.c:308 ../vn/column/vn-column-combo.c:290 +msgid "Connection" +msgstr "" + +#: ../db/db-iterator.c:1057 ../vn/field/vn-combo.c:350 +#: ../vn/field/vn-completion.c:309 ../vn/column/vn-column-combo.c:291 +msgid "The connection used by the model" +msgstr "" + +#: ../db/db-iterator.c:1064 ../vn/field/vn-combo.c:357 +#: ../vn/field/vn-completion.c:316 ../vn/column/vn-column-combo.c:298 +msgid "The SQL query used to create the model" +msgstr "" + +#: ../db/db-iterator.c:1071 +msgid "The mode in which the iterator is working" +msgstr "" + +#: ../db/db-iterator.c:1078 +#, fuzzy +msgid "Remember selection" +msgstr "Recordar" + +#: ../db/db-iterator.c:1079 +msgid "Wether to rememeber the selection when model is refreshed" +msgstr "" + +#: ../db/db-model.c:3396 +msgid "The DbConn that manages the connection to the database" +msgstr "" + +#: ../db/db-model.c:3403 ../db/db-request.c:473 +msgid "Statement" +msgstr "" + +#: ../db/db-model.c:3404 +msgid "The statement which retrieves the data" +msgstr "" + +#: ../db/db-model.c:3412 +msgid "" +"Depending on the \"use-file\" property this will be the path to a file with " +"queries for the model or a SQL string" +msgstr "" + +#: ../db/db-model.c:3421 +msgid "Use file" +msgstr "" + +#: ../db/db-model.c:3422 +msgid "" +"If this is set to TRUE, the \"sql\" property will hold the name of a file " +"containing a query, if set to FALSE, \"sql\" is used as an SQL string" +msgstr "" + +#: ../db/db-model.c:3431 +msgid "Main Table" +msgstr "" + +#: ../db/db-model.c:3432 +msgid "The main table of the model" +msgstr "" + +#: ../db/db-model.c:3439 +msgid "Update flags" +msgstr "" + +#: ../db/db-model.c:3440 +msgid "The flags that indicate how a model can be modified" +msgstr "" + +#: ../db/db-model.c:3448 +msgid "Result position" +msgstr "" + +#: ../db/db-model.c:3449 +msgid "" +"The position where the query that will fill the model will be placed in a " +"multi-query" +msgstr "" + +#: ../db/db-calc.c:330 +msgid "The model where the operations will be applied" +msgstr "" + +#: ../db/db-calc.c:337 +msgid "Operation type" +msgstr "" + +#: ../db/db-calc.c:338 +msgid "The type of the operation applied over the function" +msgstr "" + +#: ../db/db-calc.c:347 +msgid "Function" +msgstr "" + +#: ../db/db-calc.c:348 +msgid "The function to execute" +msgstr "" + +#: ../db/db-calc.c:354 +msgid "Data" +msgstr "" + +#: ../db/db-calc.c:355 +msgid "The user provided data for the function" +msgstr "" + +#: ../db/db-calc.c:361 +msgid "Column" +msgstr "" + +#: ../db/db-calc.c:362 +msgid "A column to apply the operations over it" +msgstr "" + +#: ../db/db-param.c:243 ../vn/vn-grid.c:349 ../vn/vn-field.c:532 +msgid "Iterator" +msgstr "" + +#: ../db/db-param.c:244 +msgid "The iterator owner of param" +msgstr "" + +#: ../db/db-param.c:250 ../vn/vn-column.c:340 +msgid "Column index" +msgstr "" + +#: ../db/db-param.c:251 +msgid "The referenced column index" +msgstr "" + +#: ../db/db-param.c:257 ../vn/vn-field.c:539 ../vn/vn-column.c:347 +msgid "Column name" +msgstr "Nom de la columna" + +#: ../db/db-param.c:258 ../vn/vn-column.c:348 +msgid "The referenced column name" +msgstr "Nom de la columna referenciada" + +#: ../db/db-conn.c:279 ../db/db-conn.c:284 +#, c-format +msgid "Can't load DbPlugin '%s': %s" +msgstr "" + +#: ../db/db-conn.c:297 +#, c-format +msgid "Plugin can't be loaded" +msgstr "" + +#: ../db/db-conn.c:413 +#, c-format +msgid "Can't open a new connection, it's already open." +msgstr "" + +#: ../db/db-conn.c:555 +#, c-format +msgid "The query is empty." +msgstr "" + +#: ../db/db-conn.c:1060 +msgid "Plugin" +msgstr "" + +#: ../db/db-conn.c:1061 +msgid "The name of the plugin" +msgstr "" + +#: ../db/db-conn.c:1067 +msgid "Query path" +msgstr "" + +#: ../db/db-conn.c:1068 +msgid "The path where query files are located" +msgstr "" + +#: ../db/db-conn.c:1074 ../db/db-file-loader.c:724 +#: ../vn/column/vn-column-image.c:475 +msgid "Host" +msgstr "" + +#: ../db/db-conn.c:1075 +msgid "The host name to connect to" +msgstr "" + +#: ../db/db-conn.c:1081 ../module/data/users.glade.h:4 +msgid "User" +msgstr "Usuari" + +#: ../db/db-conn.c:1082 +msgid "The user name" +msgstr "Nom d'usuari" + +#: ../db/db-conn.c:1088 +msgid "DB name" +msgstr "" + +#: ../db/db-conn.c:1089 +msgid "The default schema" +msgstr "" + +#: ../db/db-request.c:315 +#, c-format +msgid "The request was canceled" +msgstr "" + +#: ../db/db-request.c:460 +msgid "The connection used to render and execute the query" +msgstr "" + +#: ../db/db-request.c:467 +msgid "The SQL query to execute" +msgstr "" + +#: ../db/db-request.c:474 +msgid "The statement to execute" +msgstr "" + +#: ../db/db-request.c:480 +msgid "Result" +msgstr "" + +#: ../db/db-request.c:481 +msgid "The result data of the query" +msgstr "" + +#: ../db/db-request.c:487 +msgid "Error" +msgstr "" + +#: ../db/db-request.c:488 +msgid "The GError, if an error ocurred" +msgstr "" + +#: ../db/db-file-loader.c:333 +#, c-format +msgid "%s not cached" +msgstr "" + +#: ../db/db-file-loader.c:431 +#, c-format +msgid "Unknown content length of file %s" +msgstr "" + +#: ../db/db-file-loader.c:725 ../vn/column/vn-column-image.c:476 +msgid "The host web server name to get the images" +msgstr "" + +#: ../db/db-file-loader.c:732 ../vn/column/vn-column-image.c:483 +msgid "Path" +msgstr "" + +#: ../db/db-file-loader.c:733 +msgid "The path of the directory to interact with" +msgstr "" + +#: ../db/db-file-loader.c:740 +msgid "Cache directory" +msgstr "" + +#: ../db/db-file-loader.c:741 +msgid "" +"The local directory where the downloaded files will be stored. The default " +"cache directory is 'hedera', under g_get_user_cache_dir()." +msgstr "" + +#: ../db/db-file-loader.c:749 +msgid "Maximal cache size" +msgstr "" + +#: ../db/db-file-loader.c:750 +msgid "The maximal size for the contents of the cache directory" +msgstr "" + +#: ../vn/vn-gui.c:620 +msgid "Connection has been lost. Do you want to reconnect?" +msgstr "" + +#: ../vn/vn-gui.c:627 +msgid "An error occurred in the connection." +msgstr "" + +#: ../vn/vn-gui.c:630 +msgid "Database error" +msgstr "" + +#: ../vn/vn-gui.c:637 +msgid "Unknown error" +msgstr "" + +#: ../vn/vn-gui.c:977 +msgid "Closing connection" +msgstr "" + +#: ../vn/vn-gui.c:979 +msgid "Transaction started" +msgstr "" + +#: ../vn/vn-gui.c:981 +msgid "Connecting" +msgstr "" + +#: ../vn/vn-gui.c:983 +msgid "Connection lost" +msgstr "" + +#: ../vn/vn-gui.c:985 +msgid "Connection closed" +msgstr "" + +#: ../vn/vn-gui.c:987 ../vn/field/vn-http-image.c:118 +msgid "Loading" +msgstr "" + +#: ../vn/vn-gui.c:989 ../vn/gui/main.glade.h:19 +msgid "Ready" +msgstr "" + +#: ../vn/vn-gui.c:1532 +msgid "The connection used by Gui" +msgstr "" + +#: ../vn/vn-gui.c:1538 +msgid "Application" +msgstr "" + +#: ../vn/vn-gui.c:1539 +msgid "The application handler for the entire program" +msgstr "" + +#: ../vn/vn-model.c:303 +#, c-format +msgid "Function vn_model_set_sort_func not implemented" +msgstr "Funció vn_model_set_sort_func no implementada" + +#: ../vn/vn-model.c:310 +#, c-format +msgid "Function vn_model_set_default_sort_func not implemented" +msgstr "Funció vn_model_set_default_sort_func no implementada" + +#: ../vn/vn-grid.c:350 +msgid "The iterator used by VnGrid" +msgstr "" + +#: ../vn/vn-handler.c:65 +msgid "Are you sure you want to delete the selected record?" +msgstr "" + +#: ../vn/vn-handler.c:90 +msgid "Are you sure that you want to undo all changes?" +msgstr "" + +#: ../vn/vn-handler.c:219 +msgid "Undo changes" +msgstr "" + +#: ../vn/vn-handler.c:226 +msgid "Save changes" +msgstr "" + +#: ../vn/vn-handler.c:233 +msgid "Refresh data" +msgstr "" + +#: ../vn/vn-handler.c:240 +msgid "Remove record" +msgstr "" + +#: ../vn/vn-handler.c:247 +msgid "Add record" +msgstr "" + +#: ../vn/vn-handler.c:254 +msgid "Move to the first row" +msgstr "" + +#: ../vn/vn-handler.c:261 +msgid "Move to the previous row" +msgstr "" + +#: ../vn/vn-handler.c:268 +msgid "Move to the next row" +msgstr "" + +#: ../vn/vn-handler.c:275 +msgid "Move to the last row" +msgstr "" + +#: ../vn/vn-handler.c:514 +msgid "Show flags" +msgstr "" + +#: ../vn/vn-handler.c:515 +msgid "Sets the buttons that will be shown on the interface" +msgstr "" + +#: ../vn/vn-handler.c:522 +msgid "Simple record" +msgstr "" + +#: ../vn/vn-handler.c:523 +msgid "Sets if it is used to handle a iterator with a single record" +msgstr "" + +#: ../vn/vn-form.c:227 +msgid "Name" +msgstr "" + +#: ../vn/vn-form.c:228 +msgid "The form name" +msgstr "" + +#: ../vn/vn-form.c:234 +msgid "Gui" +msgstr "" + +#: ../vn/vn-form.c:235 +msgid "The Gui object" +msgstr "" + +#: ../vn/vn-form.c:242 +msgid "The connection used by the module" +msgstr "" + +#: ../vn/vn-form.c:248 +msgid "Module" +msgstr "" + +#: ../vn/vn-form.c:249 +msgid "The module" +msgstr "" + +#: ../vn/vn-login.c:276 ../vn/vn-login.c:278 +msgid "Login error" +msgstr "" + +#: ../vn/vn-login.c:336 +#, c-format +msgid "Bad connection settings, please check it." +msgstr "" + +#: ../vn/vn-login.c:457 +msgid "Application id" +msgstr "" + +#: ../vn/vn-login.c:458 +msgid "The application identifier" +msgstr "" + +#: ../vn/vn-field.c:519 +msgid "The current value of the field" +msgstr "" + +#: ../vn/vn-field.c:525 +msgid "Parameter" +msgstr "" + +#: ../vn/vn-field.c:526 +msgid "The param where the field can read/write its value" +msgstr "" + +#: ../vn/vn-field.c:533 +msgid "The iterator used to get the field param" +msgstr "" + +#: ../vn/vn-field.c:540 +msgid "The column name on the iterator" +msgstr "" + +#: ../vn/vn-field.c:554 +msgid "Whether the field value is user editable" +msgstr "" + +#: ../vn/vn-field.c:561 +msgid "Whether the field value can be of type GVN_TYPE_NULL" +msgstr "" + +#: ../vn/field/vn-entry.c:153 ../vn/field/vn-spin.c:172 +#: ../vn/column/vn-column-entry.c:119 ../vn/column/vn-column-spin.c:138 +msgid "Digits" +msgstr "" + +#: ../vn/field/vn-entry.c:154 ../vn/field/vn-spin.c:173 +#: ../vn/column/vn-column-entry.c:120 ../vn/column/vn-column-spin.c:139 +msgid "The number of decimal places to display." +msgstr "" + +#: ../vn/field/vn-combo.c:328 ../vn/column/vn-column-combo.c:269 +msgid "Index column" +msgstr "" + +#: ../vn/field/vn-combo.c:329 ../vn/column/vn-column-combo.c:270 +msgid "The column index of the model" +msgstr "" + +#: ../vn/field/vn-combo.c:335 ../vn/column/vn-column-combo.c:276 +msgid "Show column" +msgstr "" + +#: ../vn/field/vn-combo.c:336 ../vn/column/vn-column-combo.c:277 +msgid "The column of the model shown by combo" +msgstr "" + +#: ../vn/field/vn-combo.c:343 ../vn/column/vn-column-combo.c:284 +msgid "The model from which the combo takes the values shown in the list" +msgstr "" + +#: ../vn/field/vn-completion.c:322 +msgid "Field" +msgstr "" + +#: ../vn/field/vn-completion.c:323 +msgid "The name of the field used for the search" +msgstr "" + +#: ../vn/field/vn-date-chooser.c:71 +#, c-format +msgid "%s, %u %s %u" +msgstr "" + +#: ../vn/field/vn-date-chooser.c:280 +msgid "Change date" +msgstr "" + +#: ../vn/field/vn-image.c:133 +msgid "Select the image" +msgstr "" + +#: ../vn/field/vn-http-image.c:69 +msgid "Undefined error" +msgstr "" + +#: ../vn/field/vn-http-image.c:145 +msgid "File loader already set" +msgstr "" + +#. Se usará para subir imagenes y cambiar nombres, o abrir un menu contextual +#. g_signal_connect (obj, "event", G_CALLBACK (vn_http_image_cb_event), obj); +#: ../vn/field/vn-http-image.c:184 +msgid "No image set" +msgstr "" + +#: ../vn/field/vn-http-image.c:210 +msgid "File loader" +msgstr "" + +#: ../vn/field/vn-http-image.c:211 +msgid "A DbFileLoader, used to download the files" +msgstr "" + +#: ../vn/field/vn-http-image.c:218 +msgid "File path" +msgstr "" + +#: ../vn/field/vn-http-image.c:219 +msgid "The relative path to the image from file loader path" +msgstr "" + +#: ../vn/field/vn-http-image.c:226 +msgid "Image bytes" +msgstr "" + +#: ../vn/field/vn-http-image.c:227 +msgid "A GBytes structure with the image data" +msgstr "" + +#: ../vn/vn-column.c:341 +msgid "The column index in the model" +msgstr "" + +#: ../vn/vn-column.c:355 +msgid "Whether the column values are editable" +msgstr "" + +#: ../vn/column/vn-column-spin.c:130 +msgid "Climb rate" +msgstr "" + +#: ../vn/column/vn-column-spin.c:131 +msgid "The acceleration rate when you hold down a button." +msgstr "" + +#: ../vn/column/vn-column-image.c:484 +msgid "Base path from the host where the images will be downloaded" +msgstr "" + +#: ../vn/column/vn-column-image.c:491 +msgid "Tooltip path" +msgstr "" + +#: ../vn/column/vn-column-image.c:492 +msgid "" +"Prefix for the path of the images to be shown in the tooltip. Starting after " +"the path of the column and appending the name on each cell" +msgstr "" + +#: ../vn/column/vn-column-image.c:501 +msgid "Tooltip size" +msgstr "" + +#: ../vn/column/vn-column-image.c:502 +msgid "" +"Size of the bigger side of the tooltip images, the another side will be " +"scaled accordingly and smaller images won't be scaled" +msgstr "" + +#: ../vn/gui/login.glade.h:1 +msgid "Configuration" +msgstr "" + +#: ../vn/gui/login.glade.h:2 +msgid "Plugin:" +msgstr "" + +#: ../vn/gui/login.glade.h:3 +msgid "Host:" +msgstr "" + +#: ../vn/gui/login.glade.h:4 +msgid "Schema:" +msgstr "" + +#: ../vn/gui/login.glade.h:5 +msgid "SSL CA:" +msgstr "" + +#: ../vn/gui/login.glade.h:6 +msgid "" +"Path to the file containing the CA certificate, if this is empty SSL won't " +"be used" +msgstr "" + +#: ../vn/gui/login.glade.h:7 +#, fuzzy +msgid "Access" +msgstr "Accés" + +#: ../vn/gui/login.glade.h:8 ../vn/gui/main.glade.h:20 +msgid "User:" +msgstr "Usuari:" + +#: ../vn/gui/login.glade.h:9 ../module/data/users.glade.h:8 +msgid "Password:" +msgstr "Contrasenya:" + +#: ../vn/gui/login.glade.h:10 +msgid "Remember" +msgstr "Recordar" + +#: ../vn/gui/main.glade.h:1 +msgid "Copyright - Verdnatura Levante S. L." +msgstr "Copyright - Verdnatura Levante S. L." + +#: ../vn/gui/main.glade.h:2 +msgid "Management and administration of companies" +msgstr "" + +#: ../vn/gui/main.glade.h:3 +msgid "www.verdnatura.es" +msgstr "www.verdnatura.es" + +#: ../vn/gui/main.glade.h:4 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU Lesser General Public\n" +"License along with this program; if not, write to the Free\n" +"Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n" +"02111-1307 USA." +msgstr "" + +#: ../vn/gui/main.glade.h:18 ../vn/gui/child-window.glade.h:1 +msgid "Hedera" +msgstr "Hedera" + +#: ../vn/gui/main.glade.h:21 +msgid "[user-name]" +msgstr "[user-name]" + +#: ../vn/gui/actions.glade.h:1 +msgid "_Logout" +msgstr "" + +#: ../vn/gui/actions.glade.h:2 +msgid "Logout" +msgstr "" + +#: ../vn/gui/actions.glade.h:3 +msgid "_Quit" +msgstr "" + +#: ../vn/gui/actions.glade.h:4 +msgid "_About" +msgstr "" + +#: ../vn/gui/actions.glade.h:5 +msgid "_File" +msgstr "" + +#: ../vn/gui/actions.glade.h:6 +msgid "_Help" +msgstr "" + +#: ../vn/gui/actions.glade.h:7 +msgid "_Reconnect" +msgstr "" + +#: ../vn/gui/actions.glade.h:8 +msgid "Reconnect" +msgstr "" + +#: ../vn/gui/actions.glade.h:9 +msgid "_Close" +msgstr "" + +#: ../vn/gui/actions.glade.h:10 +msgid "_View" +msgstr "" + +#: ../vn/gui/actions.glade.h:11 +msgctxt "View menu option" +msgid "Dynamic _Tabs" +msgstr "" + +#: ../vn/gui/actions.glade.h:12 +msgid "Don't show tabs if there is only one tab open" +msgstr "" + +#: ../vn/gui/actions.glade.h:13 +msgctxt "View menu option" +msgid "Tool_bar" +msgstr "" + +#: ../module/data/users.glade.h:1 +#, fuzzy +msgid "User name:" +msgstr "Nom d'usuari" + +#: ../module/data/users.glade.h:2 +msgid "Search" +msgstr "" + +#: ../module/data/users.glade.h:3 +msgid "Identifier" +msgstr "" + +#: ../module/data/users.glade.h:5 +msgid "MySQL User" +msgstr "" + +#: ../module/data/users.glade.h:6 +msgid "Enabled" +msgstr "" + +#: ../module/data/users.glade.h:7 +msgid "Name:" +msgstr "" + +#: ../module/data/users.glade.h:9 +msgid "MySQL user:" +msgstr "" + +#: ../module/data/users.glade.h:10 +msgid "Enabled:" +msgstr "" + +#: ../module/data/users.glade.h:11 +msgid "Identifier:" +msgstr "" + +#: ../module/data/users.glade.h:12 +msgid "UID:" +msgstr "" + +#: ../module/data/users.glade.h:13 +msgid "Main group:" +msgstr "" + +#: ../module/data/users.glade.h:14 +msgid "Last change:" +msgstr "" + +#: ../module/data/users.glade.h:15 +msgid "Expires:" +msgstr "" + +#: ../module/data/users.glade.h:16 +msgid "Account" +msgstr "" + +#: ../module/data/users.glade.h:17 +msgid "Alias" +msgstr "" + +#: ../module/data/users.glade.h:18 +msgid "Mail alias" +msgstr "" + +#: ../module/data/users.glade.h:19 +msgid "Extension:" +msgstr "" + +#: ../module/data/users.glade.h:20 +msgid "Secret:" +msgstr "" + +#: ../module/data/users.glade.h:21 +msgid "Call group:" +msgstr "" + +#: ../module/data/users.glade.h:22 +msgid "SIP" +msgstr "" + +#: ../module/data/users.glade.h:23 +msgid "Repeat password:" +msgstr "Repetir contrasenya:" diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..1536d26 --- /dev/null +++ b/po/es.po @@ -0,0 +1,1107 @@ +# Spanish translations for hedera package +# Traducciones al español para el paquete hedera. +# Copyright (C) 2012 THE hedera'S COPYRIGHT HOLDER +# This file is distributed under the same license as the hedera package. +# Alejandro T. Colombini Gómez , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: hedera 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-07-10 11:56+0200\n" +"PO-Revision-Date: 2013-06-04 13:36+0200\n" +"Last-Translator: Alejandro T. Colombini Gómez \n" +"Language-Team: Spanish\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. * +#. * SECTION: gvn-misc +#. * @Short_description: Miscelaneous utility functions +#. * @Title: Miscelaneous utility functions +#. * +#: ../gvn/gvn-misc.c:27 ../gvn/gvn-misc.c:31 +msgid "Err" +msgstr "Err" + +#: ../gvn/gvn-misc.c:28 +msgid "Mon" +msgstr "Lun" + +#: ../gvn/gvn-misc.c:28 +msgid "Tue" +msgstr "Mar" + +#: ../gvn/gvn-misc.c:28 +msgid "Wed" +msgstr "Mie" + +#: ../gvn/gvn-misc.c:28 +msgid "Thu" +msgstr "Jue" + +#: ../gvn/gvn-misc.c:28 +msgid "Fri" +msgstr "Vie" + +#: ../gvn/gvn-misc.c:28 +msgid "Sat" +msgstr "Sa" + +#: ../gvn/gvn-misc.c:28 +msgid "Sun" +msgstr "Dom" + +#: ../gvn/gvn-misc.c:32 +msgid "Jan" +msgstr "Ene" + +#: ../gvn/gvn-misc.c:32 +msgid "Feb" +msgstr "Feb" + +#: ../gvn/gvn-misc.c:32 +msgid "Mar" +msgstr "Mar" + +#: ../gvn/gvn-misc.c:32 +msgid "Apr" +msgstr "Abr" + +#: ../gvn/gvn-misc.c:32 +msgid "May" +msgstr "May" + +#: ../gvn/gvn-misc.c:32 +msgid "Jun" +msgstr "Jun" + +#: ../gvn/gvn-misc.c:33 +msgid "Jul" +msgstr "Jul" + +#: ../gvn/gvn-misc.c:33 +msgid "Aug" +msgstr "Ago" + +#: ../gvn/gvn-misc.c:33 +msgid "Sep" +msgstr "Sep" + +#: ../gvn/gvn-misc.c:33 +msgid "Oct" +msgstr "Oct" + +#: ../gvn/gvn-misc.c:33 +msgid "Nov" +msgstr "Nov" + +#: ../gvn/gvn-misc.c:33 +msgid "Dec" +msgstr "Dic" + +#: ../gvn/gvn-param-spec.c:263 +#, c-format +msgid "Param not editable" +msgstr "Parametro no editable" + +#: ../gvn/gvn-param-spec.c:268 +#, c-format +msgid "Param can't be NULL" +msgstr "El parámetro no puede ser nulo" + +#: ../gvn/gvn-param-spec.c:274 +#, c-format +msgid "Incompatible type for this param" +msgstr "Tipo incompatible para este parametro" + +#: ../gvn/gvn-param.c:443 ../sql/sql-value.c:235 ../vn/vn-field.c:518 +msgid "Value" +msgstr "Valor" + +#: ../gvn/gvn-param.c:444 +msgid "The value of the param" +msgstr "El valor del parámetro" + +#: ../gvn/gvn-param.c:450 +msgid "Master" +msgstr "Maestro" + +#: ../gvn/gvn-param.c:451 +msgid "The master GvnParam of this parameter" +msgstr "El GvnParam maestro de este parámetro" + +#: ../gvn/gvn-param.c:457 ../db/db-iterator.c:1070 +msgid "Mode" +msgstr "Modo" + +#: ../gvn/gvn-param.c:458 +msgid "The mode of the parameter" +msgstr "El modo del parámetro" + +#: ../gvn/gvn-param.c:464 +msgid "Status" +msgstr "Estado" + +#: ../gvn/gvn-param.c:465 +msgid "The current status of the parameter" +msgstr "El estado actual del parámetro" + +#: ../gvn/gvn-param.c:471 +msgid "Spec" +msgstr "Spec" + +#: ../gvn/gvn-param.c:472 +msgid "The spec of the parameter" +msgstr "Datos específicos del parámetro" + +#: ../gvn/gvn-param.c:478 ../vn/vn-field.c:546 +msgid "Glib Type" +msgstr "Tipo Glib" + +#: ../gvn/gvn-param.c:479 ../vn/vn-field.c:547 +msgid "The type of the value" +msgstr "El tipo del valor" + +#: ../gvn/gvn-param.c:485 ../vn/vn-field.c:553 ../vn/vn-column.c:354 +msgid "Editable" +msgstr "Editable" + +#: ../gvn/gvn-param.c:486 +msgid "Whether the param value can be modified" +msgstr "Indica si el parámetro puede modificarse" + +#: ../gvn/gvn-param.c:492 ../vn/vn-field.c:560 +msgid "Null" +msgstr "Nulo" + +#: ../gvn/gvn-param.c:493 +msgid "Whether the param value can be of type GVN_TYPE_NULL" +msgstr "Indica si el parámetro puede ser nulo" + +#: ../gvn/gvn-param.c:499 ../vn/vn-field.c:567 +msgid "Default Value" +msgstr "Valor por defecto" + +#: ../gvn/gvn-param.c:500 ../vn/vn-field.c:568 +msgid "The default value" +msgstr "El valor por defecto" + +#: ../gvn/gvn-value.c:434 +#, c-format +msgid "Attempting to compare invalid types: %s\n" +msgstr "Se ha intentado comparar tipos incompatibles: %s\n" + +#: ../sql/sql-render.c:413 +msgid "Delimiter" +msgstr "Delimitador" + +#: ../sql/sql-render.c:414 +msgid "The character used for delimite the name of fields, tables..." +msgstr "El carácter usado para delimitar los nombres de campos, tablas..." + +#: ../sql/sql-value.c:228 +msgid "Param" +msgstr "Parámetro" + +#: ../sql/sql-value.c:229 +msgid "The param which is linked" +msgstr "El parametro al cual está vinculado" + +#: ../sql/sql-value.c:236 +msgid "The value" +msgstr "Valor" + +#: ../sql/sql-string.c:198 ../db/db-iterator.c:1063 ../db/db-model.c:3411 +#: ../db/db-request.c:466 ../vn/field/vn-combo.c:356 +#: ../vn/field/vn-completion.c:315 ../vn/column/vn-column-combo.c:297 +msgid "SQL" +msgstr "SQL" + +#: ../sql/sql-string.c:199 +msgid "An arbitrary SQL string" +msgstr "Una cadena SQL arbitraria" + +#: ../plugin/mysql/db-mysql.c:68 ../plugin/pg/db-pg.c:76 +#, c-format +msgid "Can't allocate the needed memory" +msgstr "No es posible asignar la memoria necesaria" + +#: ../db/db-iterator.c:284 +msgid "DbIterator: Can't reassign the 'model' property" +msgstr "DbIterator: No puedo reasignarse la propiedad 'model'" + +#: ../db/db-iterator.c:942 +msgid "DbIterator: Can't set the 'conn' property because model isn't set" +msgstr "" +"DbIterator: No se pudo configurar la propiedad porque 'model' no está " +"configurado" + +#: ../db/db-iterator.c:1049 ../db/db-calc.c:329 ../vn/field/vn-combo.c:342 +#: ../vn/column/vn-column-combo.c:283 +msgid "Model" +msgstr "Modelo" + +#: ../db/db-iterator.c:1050 +msgid "The DbModel handled by the iterator" +msgstr "El DbModel manejado por el iterador" + +#: ../db/db-iterator.c:1056 ../db/db-model.c:3395 ../db/db-request.c:459 +#: ../vn/vn-gui.c:1531 ../vn/vn-form.c:241 ../vn/field/vn-combo.c:349 +#: ../vn/field/vn-completion.c:308 ../vn/column/vn-column-combo.c:290 +msgid "Connection" +msgstr "Conexión" + +#: ../db/db-iterator.c:1057 ../vn/field/vn-combo.c:350 +#: ../vn/field/vn-completion.c:309 ../vn/column/vn-column-combo.c:291 +msgid "The connection used by the model" +msgstr "La conexión empleada por el modelo" + +#: ../db/db-iterator.c:1064 ../vn/field/vn-combo.c:357 +#: ../vn/field/vn-completion.c:316 ../vn/column/vn-column-combo.c:298 +msgid "The SQL query used to create the model" +msgstr "La consulta SQL usada para crear el modelo" + +#: ../db/db-iterator.c:1071 +msgid "The mode in which the iterator is working" +msgstr "El modo en el que está trabajando el iterador" + +#: ../db/db-iterator.c:1078 +msgid "Remember selection" +msgstr "Recordar fila seleccionada" + +#: ../db/db-iterator.c:1079 +msgid "Wether to rememeber the selection when model is refreshed" +msgstr "" +"Si se recordará o no la fila seleccionado cuando se refresque el modelo" + +#: ../db/db-model.c:3396 +msgid "The DbConn that manages the connection to the database" +msgstr "La DbConn que controla la conexión a la base de datos" + +#: ../db/db-model.c:3403 ../db/db-request.c:473 +msgid "Statement" +msgstr "Consulta" + +#: ../db/db-model.c:3404 +msgid "The statement which retrieves the data" +msgstr "La consulta que recupera los datos" + +#: ../db/db-model.c:3412 +msgid "" +"Depending on the \"use-file\" property this will be the path to a file with " +"queries for the model or a SQL string" +msgstr "" +"Dependiendo de la propiedad \"use-file\", esta propiedad será la ruta a un " +"fichero con consultas para el modelo o una cadena SQL" + +#: ../db/db-model.c:3421 +msgid "Use file" +msgstr "Usa fichero" + +#: ../db/db-model.c:3422 +msgid "" +"If this is set to TRUE, the \"sql\" property will hold the name of a file " +"containing a query, if set to FALSE, \"sql\" is used as an SQL string" +msgstr "" +"Si está puesto a TRUE, la propiedad \"sql\" contiene el nombre de un " +"archivo, si es falso, \"sql\" se usa como una cadena SQL" + +#: ../db/db-model.c:3431 +msgid "Main Table" +msgstr "Tabla principal" + +#: ../db/db-model.c:3432 +msgid "The main table of the model" +msgstr "La tabla principal del modelo" + +#: ../db/db-model.c:3439 +msgid "Update flags" +msgstr "Flags de actualización" + +#: ../db/db-model.c:3440 +msgid "The flags that indicate how a model can be modified" +msgstr "Indican como se puede modificar el modelo" + +#: ../db/db-model.c:3448 +msgid "Result position" +msgstr "Posición del resultado" + +#: ../db/db-model.c:3449 +msgid "" +"The position where the query that will fill the model will be placed in a " +"multi-query" +msgstr "" +"La posición en la que se situa la consulta que pone datos en el modelo en " +"una consulta múltiple" + +#: ../db/db-calc.c:330 +msgid "The model where the operations will be applied" +msgstr "El modelo en el que se aplicarán las operaciones" + +#: ../db/db-calc.c:337 +msgid "Operation type" +msgstr "Tipo de operación" + +#: ../db/db-calc.c:338 +msgid "The type of the operation applied over the function" +msgstr "El tipo de operación aplicada sobre la función" + +#: ../db/db-calc.c:347 +msgid "Function" +msgstr "Función" + +#: ../db/db-calc.c:348 +msgid "The function to execute" +msgstr "Función a ejecutar" + +#: ../db/db-calc.c:354 +msgid "Data" +msgstr "Datos" + +#: ../db/db-calc.c:355 +msgid "The user provided data for the function" +msgstr "Datos proporcionados por el usuario" + +#: ../db/db-calc.c:361 +msgid "Column" +msgstr "Columna" + +#: ../db/db-calc.c:362 +msgid "A column to apply the operations over it" +msgstr "Una columna sobre la que se aplicarán las operaciones" + +#: ../db/db-param.c:243 ../vn/vn-grid.c:349 ../vn/vn-field.c:532 +msgid "Iterator" +msgstr "Iterator" + +#: ../db/db-param.c:244 +msgid "The iterator owner of param" +msgstr "El iterador dueño del parámetro" + +#: ../db/db-param.c:250 ../vn/vn-column.c:340 +msgid "Column index" +msgstr "Columna usada como índice" + +#: ../db/db-param.c:251 +msgid "The referenced column index" +msgstr "El índice de la columna referenciada" + +#: ../db/db-param.c:257 ../vn/vn-field.c:539 ../vn/vn-column.c:347 +msgid "Column name" +msgstr "Nombre de la columna" + +#: ../db/db-param.c:258 ../vn/vn-column.c:348 +msgid "The referenced column name" +msgstr "" +"El nombre del campo en el Iterator vinculado al Grid al que pretenece la " +"columna" + +#: ../db/db-conn.c:279 ../db/db-conn.c:284 +#, c-format +msgid "Can't load DbPlugin '%s': %s" +msgstr "No se puede cargar el DbPlugin '%s': %s" + +#: ../db/db-conn.c:297 +#, c-format +msgid "Plugin can't be loaded" +msgstr "No se puede cargar el plug-in" + +#: ../db/db-conn.c:413 +#, c-format +msgid "Can't open a new connection, it's already open." +msgstr "No se puede abrir una nueva conexión, ya está abierta" + +#: ../db/db-conn.c:555 +#, c-format +msgid "The query is empty." +msgstr "La consulta está vacía." + +#: ../db/db-conn.c:1060 +msgid "Plugin" +msgstr "Plugin" + +#: ../db/db-conn.c:1061 +msgid "The name of the plugin" +msgstr "El nombre del plugin" + +#: ../db/db-conn.c:1067 +msgid "Query path" +msgstr "Ruta de consultas" + +#: ../db/db-conn.c:1068 +msgid "The path where query files are located" +msgstr "La ruta donde se encuentran los archivos de consultas" + +#: ../db/db-conn.c:1074 ../db/db-file-loader.c:724 +#: ../vn/column/vn-column-image.c:475 +msgid "Host" +msgstr "Host" + +#: ../db/db-conn.c:1075 +msgid "The host name to connect to" +msgstr "El nombre del host al que se conectará" + +#: ../db/db-conn.c:1081 ../module/data/users.glade.h:4 +msgid "User" +msgstr "Usuario" + +#: ../db/db-conn.c:1082 +msgid "The user name" +msgstr "El nombre de usuario" + +#: ../db/db-conn.c:1088 +msgid "DB name" +msgstr "Nombre de la BD" + +#: ../db/db-conn.c:1089 +msgid "The default schema" +msgstr "El esquema por defecto" + +#: ../db/db-request.c:315 +#, c-format +msgid "The request was canceled" +msgstr "Se ha cancelado la petición" + +#: ../db/db-request.c:460 +msgid "The connection used to render and execute the query" +msgstr "La conexión empleada por el módulo" + +#: ../db/db-request.c:467 +msgid "The SQL query to execute" +msgstr "La consulta SQL a ejecutar" + +#: ../db/db-request.c:474 +msgid "The statement to execute" +msgstr "La instrucción a ejecutar" + +#: ../db/db-request.c:480 +msgid "Result" +msgstr "Result" + +#: ../db/db-request.c:481 +msgid "The result data of the query" +msgstr "Los datos resultantes de la consulta" + +#: ../db/db-request.c:487 +msgid "Error" +msgstr "Error" + +#: ../db/db-request.c:488 +msgid "The GError, if an error ocurred" +msgstr "El GError, si ha habido algún error" + +#: ../db/db-file-loader.c:333 +#, c-format +msgid "%s not cached" +msgstr "No se ha guardado %s en la cache" + +#: ../db/db-file-loader.c:431 +#, c-format +msgid "Unknown content length of file %s" +msgstr "Longitud del contenido del archivo %s desconocida" + +#: ../db/db-file-loader.c:725 ../vn/column/vn-column-image.c:476 +msgid "The host web server name to get the images" +msgstr "El nombre del servidor web al que se conectará" + +#: ../db/db-file-loader.c:732 ../vn/column/vn-column-image.c:483 +msgid "Path" +msgstr "Ruta" + +#: ../db/db-file-loader.c:733 +msgid "The path of the directory to interact with" +msgstr "La ruta del directorio con el que se interactuará" + +#: ../db/db-file-loader.c:740 +msgid "Cache directory" +msgstr "Directorio de caché" + +#: ../db/db-file-loader.c:741 +msgid "" +"The local directory where the downloaded files will be stored. The default " +"cache directory is 'hedera', under g_get_user_cache_dir()." +msgstr "" +"El directorio local en el cual se guardarán los archivos descargados.El " +"directorio por defecto es 'hedera', debajo de g_get_user_cache_dir()." + +#: ../db/db-file-loader.c:749 +msgid "Maximal cache size" +msgstr "Tamaño máximo de la caché" + +#: ../db/db-file-loader.c:750 +msgid "The maximal size for the contents of the cache directory" +msgstr "El tamaño máximo para los contenidos del directorio de caché" + +#: ../vn/vn-gui.c:620 +msgid "Connection has been lost. Do you want to reconnect?" +msgstr "Se ha perdido la conexión. ¿Quieres reconectar?" + +#: ../vn/vn-gui.c:627 +msgid "An error occurred in the connection." +msgstr "Ha habido un error en la conexión." + +#: ../vn/vn-gui.c:630 +msgid "Database error" +msgstr "Error en la base de datos" + +#: ../vn/vn-gui.c:637 +msgid "Unknown error" +msgstr "Error desconocido" + +#: ../vn/vn-gui.c:977 +msgid "Closing connection" +msgstr "Cerrando conexión" + +#: ../vn/vn-gui.c:979 +msgid "Transaction started" +msgstr "Transacción iniciada" + +#: ../vn/vn-gui.c:981 +msgid "Connecting" +msgstr "Conectando" + +#: ../vn/vn-gui.c:983 +msgid "Connection lost" +msgstr "Conexión perdida" + +#: ../vn/vn-gui.c:985 +msgid "Connection closed" +msgstr "Conexión cerrada" + +#: ../vn/vn-gui.c:987 ../vn/field/vn-http-image.c:118 +msgid "Loading" +msgstr "Cargando" + +#: ../vn/vn-gui.c:989 ../vn/gui/main.glade.h:19 +msgid "Ready" +msgstr "Listo" + +#: ../vn/vn-gui.c:1532 +msgid "The connection used by Gui" +msgstr "La conexión empleada por Gui" + +#: ../vn/vn-gui.c:1538 +msgid "Application" +msgstr "Aplicación" + +#: ../vn/vn-gui.c:1539 +msgid "The application handler for the entire program" +msgstr "El manejador de la aplicación para todo el programa" + +#: ../vn/vn-model.c:303 +#, c-format +msgid "Function vn_model_set_sort_func not implemented" +msgstr "Función vn_model_set_sort_func no implementada" + +#: ../vn/vn-model.c:310 +#, c-format +msgid "Function vn_model_set_default_sort_func not implemented" +msgstr "Función vn_model_set_default_sort_func no implementada" + +#: ../vn/vn-grid.c:350 +msgid "The iterator used by VnGrid" +msgstr "El iterador empleado por VnGrid" + +#: ../vn/vn-handler.c:65 +msgid "Are you sure you want to delete the selected record?" +msgstr "¿Estás seguro de que quieres eliminar los registros seleccionados?" + +#: ../vn/vn-handler.c:90 +msgid "Are you sure that you want to undo all changes?" +msgstr "¿Estás seguro de que quieres deshacer todos los cambios?" + +#: ../vn/vn-handler.c:219 +msgid "Undo changes" +msgstr "Deshacer cambios" + +#: ../vn/vn-handler.c:226 +msgid "Save changes" +msgstr "Guardar cambios" + +#: ../vn/vn-handler.c:233 +msgid "Refresh data" +msgstr "Refrescar datos" + +#: ../vn/vn-handler.c:240 +msgid "Remove record" +msgstr "Eliminar registro" + +#: ../vn/vn-handler.c:247 +msgid "Add record" +msgstr "Añadir registro" + +#: ../vn/vn-handler.c:254 +msgid "Move to the first row" +msgstr "Mover a la primera fila" + +#: ../vn/vn-handler.c:261 +msgid "Move to the previous row" +msgstr "Mover a la fila anterior" + +#: ../vn/vn-handler.c:268 +msgid "Move to the next row" +msgstr "Mover a la siguiente fila" + +#: ../vn/vn-handler.c:275 +msgid "Move to the last row" +msgstr "Mover a la última fila" + +#: ../vn/vn-handler.c:514 +msgid "Show flags" +msgstr "Visibilidad" + +#: ../vn/vn-handler.c:515 +msgid "Sets the buttons that will be shown on the interface" +msgstr "Indica los botones que se mostrarán en la interfaz" + +#: ../vn/vn-handler.c:522 +msgid "Simple record" +msgstr "Registro simple" + +#: ../vn/vn-handler.c:523 +msgid "Sets if it is used to handle a iterator with a single record" +msgstr "Indica si se utiliza para manejar solo un registro" + +#: ../vn/vn-form.c:227 +msgid "Name" +msgstr "Nombre" + +#: ../vn/vn-form.c:228 +msgid "The form name" +msgstr "El nombre del form" + +#: ../vn/vn-form.c:234 +msgid "Gui" +msgstr "Gui" + +#: ../vn/vn-form.c:235 +msgid "The Gui object" +msgstr "El objeto Gui" + +#: ../vn/vn-form.c:242 +msgid "The connection used by the module" +msgstr "La conexión empleada por el módulo" + +#: ../vn/vn-form.c:248 +msgid "Module" +msgstr "Módulo" + +#: ../vn/vn-form.c:249 +msgid "The module" +msgstr "El módulo" + +#: ../vn/vn-login.c:276 ../vn/vn-login.c:278 +msgid "Login error" +msgstr "Error de identificación" + +#: ../vn/vn-login.c:336 +#, c-format +msgid "Bad connection settings, please check it." +msgstr "Conexión mal configurado, compruébelo por favor" + +#: ../vn/vn-login.c:457 +msgid "Application id" +msgstr "Id de la aplicación" + +#: ../vn/vn-login.c:458 +msgid "The application identifier" +msgstr "El identificador de la aplicación" + +#: ../vn/vn-field.c:519 +msgid "The current value of the field" +msgstr "El valor actual del campo" + +#: ../vn/vn-field.c:525 +msgid "Parameter" +msgstr "Parámetro" + +#: ../vn/vn-field.c:526 +msgid "The param where the field can read/write its value" +msgstr "El parámetro en el que el campo escribe/lee su valor" + +#: ../vn/vn-field.c:533 +msgid "The iterator used to get the field param" +msgstr "El iterador con el que se obtiene el parámetro" + +#: ../vn/vn-field.c:540 +msgid "The column name on the iterator" +msgstr "El nombre de la columna en el iterador" + +#: ../vn/vn-field.c:554 +msgid "Whether the field value is user editable" +msgstr "Si el valor de campo puede ser editado por el usuario" + +#: ../vn/vn-field.c:561 +msgid "Whether the field value can be of type GVN_TYPE_NULL" +msgstr "Si el campo puede ser del tipo GVN_TYPE_NULL" + +#: ../vn/field/vn-entry.c:153 ../vn/field/vn-spin.c:172 +#: ../vn/column/vn-column-entry.c:119 ../vn/column/vn-column-spin.c:138 +msgid "Digits" +msgstr "Dígitos" + +#: ../vn/field/vn-entry.c:154 ../vn/field/vn-spin.c:173 +#: ../vn/column/vn-column-entry.c:120 ../vn/column/vn-column-spin.c:139 +msgid "The number of decimal places to display." +msgstr "El número de posiciones decimales que se muestran" + +#: ../vn/field/vn-combo.c:328 ../vn/column/vn-column-combo.c:269 +msgid "Index column" +msgstr "Columna índice" + +#: ../vn/field/vn-combo.c:329 ../vn/column/vn-column-combo.c:270 +msgid "The column index of the model" +msgstr "El índice de columna del modelo" + +#: ../vn/field/vn-combo.c:335 ../vn/column/vn-column-combo.c:276 +msgid "Show column" +msgstr "Mostrar columna" + +#: ../vn/field/vn-combo.c:336 ../vn/column/vn-column-combo.c:277 +msgid "The column of the model shown by combo" +msgstr "La columna del modelo que se mostrará en el combo" + +#: ../vn/field/vn-combo.c:343 ../vn/column/vn-column-combo.c:284 +msgid "The model from which the combo takes the values shown in the list" +msgstr "El modelo del cual el combo tomará los valores mostrados en la lista" + +#: ../vn/field/vn-completion.c:322 +msgid "Field" +msgstr "Campo" + +#: ../vn/field/vn-completion.c:323 +msgid "The name of the field used for the search" +msgstr "El nombre del campo usado para la búsqueda" + +#: ../vn/field/vn-date-chooser.c:71 +#, c-format +msgid "%s, %u %s %u" +msgstr "%s, %u %s %u" + +#: ../vn/field/vn-date-chooser.c:280 +msgid "Change date" +msgstr "Cambiar fecha" + +#: ../vn/field/vn-image.c:133 +msgid "Select the image" +msgstr "Selecciona la imagen" + +#: ../vn/field/vn-http-image.c:69 +msgid "Undefined error" +msgstr "Error indefinido" + +#: ../vn/field/vn-http-image.c:145 +msgid "File loader already set" +msgstr "Ya se ha especificado un cargador de archivos" + +#. Se usará para subir imagenes y cambiar nombres, o abrir un menu contextual +#. g_signal_connect (obj, "event", G_CALLBACK (vn_http_image_cb_event), obj); +#: ../vn/field/vn-http-image.c:184 +msgid "No image set" +msgstr "Sin imagen" + +#: ../vn/field/vn-http-image.c:210 +msgid "File loader" +msgstr "Cargador de archivos" + +#: ../vn/field/vn-http-image.c:211 +msgid "A DbFileLoader, used to download the files" +msgstr "Un DbFileLoader que se usará para descargar los archivos" + +#: ../vn/field/vn-http-image.c:218 +msgid "File path" +msgstr "Ruta del archivo" + +#: ../vn/field/vn-http-image.c:219 +msgid "The relative path to the image from file loader path" +msgstr "La ruta relativa a la imagen desde la del cargador de archivos" + +#: ../vn/field/vn-http-image.c:226 +msgid "Image bytes" +msgstr "Bytes" + +#: ../vn/field/vn-http-image.c:227 +msgid "A GBytes structure with the image data" +msgstr "Una estructura GBytes con los datos de la imágen" + +#: ../vn/vn-column.c:341 +msgid "The column index in the model" +msgstr "El índice de la columna en el modelo" + +#: ../vn/vn-column.c:355 +msgid "Whether the column values are editable" +msgstr "Si el valor de campo puede ser editado por el usuario" + +#: ../vn/column/vn-column-spin.c:130 +msgid "Climb rate" +msgstr "Tasa de subida" + +#: ../vn/column/vn-column-spin.c:131 +msgid "The acceleration rate when you hold down a button." +msgstr "La tasa de aceleración cuando se mantiene apretado el botón." + +#: ../vn/column/vn-column-image.c:484 +msgid "Base path from the host where the images will be downloaded" +msgstr "La ruta base del servidor desde donde se descargarán las imágenes." + +#: ../vn/column/vn-column-image.c:491 +msgid "Tooltip path" +msgstr "Ruta del tooltip" + +#: ../vn/column/vn-column-image.c:492 +msgid "" +"Prefix for the path of the images to be shown in the tooltip. Starting after " +"the path of the column and appending the name on each cell" +msgstr "" +"Prefijo para la ruta de las imagenes que se muestren en el tooltip. " +"Empezando desde la ruta de la columna y añadiendo después el nombre de cada " +"celda" + +#: ../vn/column/vn-column-image.c:501 +msgid "Tooltip size" +msgstr "Tamaño del tooltip" + +#: ../vn/column/vn-column-image.c:502 +msgid "" +"Size of the bigger side of the tooltip images, the another side will be " +"scaled accordingly and smaller images won't be scaled" +msgstr "" +"Tamaño del lado más grande de las imágenes de tooltip, el otro lado se " +"escalará de acuerdo a este y las imágenes más pequeñas no se escalarán" + +#: ../vn/gui/login.glade.h:1 +msgid "Configuration" +msgstr "Configuración" + +#: ../vn/gui/login.glade.h:2 +msgid "Plugin:" +msgstr "Plugin:" + +#: ../vn/gui/login.glade.h:3 +msgid "Host:" +msgstr "Host:" + +#: ../vn/gui/login.glade.h:4 +msgid "Schema:" +msgstr "Esquema:" + +#: ../vn/gui/login.glade.h:5 +msgid "SSL CA:" +msgstr "SSL CA" + +#: ../vn/gui/login.glade.h:6 +msgid "" +"Path to the file containing the CA certificate, if this is empty SSL won't " +"be used" +msgstr "" +"Ruta al archivo con el certificado de la CA, si está vacío, no se usará SSL" + +#: ../vn/gui/login.glade.h:7 +msgid "Access" +msgstr "Acceso" + +#: ../vn/gui/login.glade.h:8 ../vn/gui/main.glade.h:20 +msgid "User:" +msgstr "Usuario:" + +#: ../vn/gui/login.glade.h:9 ../module/data/users.glade.h:8 +msgid "Password:" +msgstr "Clave:" + +#: ../vn/gui/login.glade.h:10 +msgid "Remember" +msgstr "Recordar" + +#: ../vn/gui/main.glade.h:1 +msgid "Copyright - Verdnatura Levante S. L." +msgstr "Copyright - Verdnatura Levante S. L." + +#: ../vn/gui/main.glade.h:2 +msgid "Management and administration of companies" +msgstr "Gestión y administración de empresas" + +#: ../vn/gui/main.glade.h:3 +msgid "www.verdnatura.es" +msgstr "www.verdnatura.es" + +#: ../vn/gui/main.glade.h:4 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU Lesser General Public\n" +"License along with this program; if not, write to the Free\n" +"Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n" +"02111-1307 USA." +msgstr "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU Lesser General Public\n" +"License along with this program; if not, write to the Free\n" +"Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n" +"02111-1307 USA." + +#: ../vn/gui/main.glade.h:18 ../vn/gui/child-window.glade.h:1 +msgid "Hedera" +msgstr "Hedera" + +#: ../vn/gui/main.glade.h:21 +msgid "[user-name]" +msgstr "[user-name]" + +#: ../vn/gui/actions.glade.h:1 +msgid "_Logout" +msgstr "_Desconectar" + +#: ../vn/gui/actions.glade.h:2 +msgid "Logout" +msgstr "Desconectar" + +#: ../vn/gui/actions.glade.h:3 +msgid "_Quit" +msgstr "_Salir" + +#: ../vn/gui/actions.glade.h:4 +msgid "_About" +msgstr "_Acerca de" + +#: ../vn/gui/actions.glade.h:5 +msgid "_File" +msgstr "_Archivo" + +#: ../vn/gui/actions.glade.h:6 +msgid "_Help" +msgstr "Ay_uda" + +#: ../vn/gui/actions.glade.h:7 +msgid "_Reconnect" +msgstr "_Reconectar" + +#: ../vn/gui/actions.glade.h:8 +msgid "Reconnect" +msgstr "Reconectar" + +#: ../vn/gui/actions.glade.h:9 +msgid "_Close" +msgstr "_Cerrar" + +#: ../vn/gui/actions.glade.h:10 +msgid "_View" +msgstr "_Ver" + +#: ../vn/gui/actions.glade.h:11 +msgctxt "View menu option" +msgid "Dynamic _Tabs" +msgstr "_Pestañas dinámicas" + +#: ../vn/gui/actions.glade.h:12 +msgid "Don't show tabs if there is only one tab open" +msgstr "No mostrar las pestañas si solo hay una abierta" + +#: ../vn/gui/actions.glade.h:13 +msgctxt "View menu option" +msgid "Tool_bar" +msgstr "_Barra de herramientas" + +#: ../module/data/users.glade.h:1 +msgid "User name:" +msgstr "Nombre de usuario:" + +#: ../module/data/users.glade.h:2 +msgid "Search" +msgstr "Buscar" + +#: ../module/data/users.glade.h:3 +msgid "Identifier" +msgstr "Identificador" + +#: ../module/data/users.glade.h:5 +msgid "MySQL User" +msgstr "Usuario MySQL" + +#: ../module/data/users.glade.h:6 +msgid "Enabled" +msgstr "Habilitado" + +#: ../module/data/users.glade.h:7 +msgid "Name:" +msgstr "Nombre:" + +#: ../module/data/users.glade.h:9 +msgid "MySQL user:" +msgstr "Usuario MySQL:" + +#: ../module/data/users.glade.h:10 +msgid "Enabled:" +msgstr "Habilitado:" + +#: ../module/data/users.glade.h:11 +msgid "Identifier:" +msgstr "Identificador:" + +#: ../module/data/users.glade.h:12 +msgid "UID:" +msgstr "UID:" + +#: ../module/data/users.glade.h:13 +msgid "Main group:" +msgstr "Grupo principal:" + +#: ../module/data/users.glade.h:14 +msgid "Last change:" +msgstr "Último cambio:" + +#: ../module/data/users.glade.h:15 +msgid "Expires:" +msgstr "Expira:" + +#: ../module/data/users.glade.h:16 +msgid "Account" +msgstr "Cuenta" + +#: ../module/data/users.glade.h:17 +msgid "Alias" +msgstr "Alias" + +#: ../module/data/users.glade.h:18 +msgid "Mail alias" +msgstr "Alias de correo" + +#: ../module/data/users.glade.h:19 +msgid "Extension:" +msgstr "Extensión:" + +#: ../module/data/users.glade.h:20 +msgid "Secret:" +msgstr "Clave:" + +#: ../module/data/users.glade.h:21 +msgid "Call group:" +msgstr "Call group:" + +#: ../module/data/users.glade.h:22 +msgid "SIP" +msgstr "SIP" + +#: ../module/data/users.glade.h:23 +msgid "Repeat password:" +msgstr "Repetir contraseña:" diff --git a/po/nl.po b/po/nl.po new file mode 100644 index 0000000..5985f0f --- /dev/null +++ b/po/nl.po @@ -0,0 +1,1074 @@ +# Dutch translations for hedera package +# Traducciones al español para el paquete hedera. +# Copyright (C) 2012 THE hedera'S COPYRIGHT HOLDER +# This file is distributed under the same license as the hedera package. +# Alejandro T. Colombini Gómez , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: hedera 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-07-10 11:56+0200\n" +"PO-Revision-Date: 2012-10-09 11:38+0200\n" +"Last-Translator: Alejandro T. Colombini Gómez \n" +"Language-Team: Dutch\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. * +#. * SECTION: gvn-misc +#. * @Short_description: Miscelaneous utility functions +#. * @Title: Miscelaneous utility functions +#. * +#: ../gvn/gvn-misc.c:27 ../gvn/gvn-misc.c:31 +msgid "Err" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Mon" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Tue" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Wed" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Thu" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Fri" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Sat" +msgstr "" + +#: ../gvn/gvn-misc.c:28 +msgid "Sun" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Jan" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Feb" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Mar" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Apr" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "May" +msgstr "" + +#: ../gvn/gvn-misc.c:32 +msgid "Jun" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Jul" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Aug" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Sep" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Oct" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Nov" +msgstr "" + +#: ../gvn/gvn-misc.c:33 +msgid "Dec" +msgstr "" + +#: ../gvn/gvn-param-spec.c:263 +#, c-format +msgid "Param not editable" +msgstr "" + +#: ../gvn/gvn-param-spec.c:268 +#, c-format +msgid "Param can't be NULL" +msgstr "" + +#: ../gvn/gvn-param-spec.c:274 +#, c-format +msgid "Incompatible type for this param" +msgstr "" + +#: ../gvn/gvn-param.c:443 ../sql/sql-value.c:235 ../vn/vn-field.c:518 +msgid "Value" +msgstr "" + +#: ../gvn/gvn-param.c:444 +msgid "The value of the param" +msgstr "" + +#: ../gvn/gvn-param.c:450 +msgid "Master" +msgstr "" + +#: ../gvn/gvn-param.c:451 +msgid "The master GvnParam of this parameter" +msgstr "" + +#: ../gvn/gvn-param.c:457 ../db/db-iterator.c:1070 +msgid "Mode" +msgstr "" + +#: ../gvn/gvn-param.c:458 +msgid "The mode of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:464 +msgid "Status" +msgstr "" + +#: ../gvn/gvn-param.c:465 +msgid "The current status of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:471 +msgid "Spec" +msgstr "" + +#: ../gvn/gvn-param.c:472 +msgid "The spec of the parameter" +msgstr "" + +#: ../gvn/gvn-param.c:478 ../vn/vn-field.c:546 +msgid "Glib Type" +msgstr "" + +#: ../gvn/gvn-param.c:479 ../vn/vn-field.c:547 +msgid "The type of the value" +msgstr "" + +#: ../gvn/gvn-param.c:485 ../vn/vn-field.c:553 ../vn/vn-column.c:354 +msgid "Editable" +msgstr "" + +#: ../gvn/gvn-param.c:486 +msgid "Whether the param value can be modified" +msgstr "" + +#: ../gvn/gvn-param.c:492 ../vn/vn-field.c:560 +msgid "Null" +msgstr "" + +#: ../gvn/gvn-param.c:493 +msgid "Whether the param value can be of type GVN_TYPE_NULL" +msgstr "" + +#: ../gvn/gvn-param.c:499 ../vn/vn-field.c:567 +msgid "Default Value" +msgstr "" + +#: ../gvn/gvn-param.c:500 ../vn/vn-field.c:568 +msgid "The default value" +msgstr "" + +#: ../gvn/gvn-value.c:434 +#, c-format +msgid "Attempting to compare invalid types: %s\n" +msgstr "" + +#: ../sql/sql-render.c:413 +msgid "Delimiter" +msgstr "" + +#: ../sql/sql-render.c:414 +msgid "The character used for delimite the name of fields, tables..." +msgstr "" + +#: ../sql/sql-value.c:228 +msgid "Param" +msgstr "" + +#: ../sql/sql-value.c:229 +msgid "The param which is linked" +msgstr "" + +#: ../sql/sql-value.c:236 +msgid "The value" +msgstr "" + +#: ../sql/sql-string.c:198 ../db/db-iterator.c:1063 ../db/db-model.c:3411 +#: ../db/db-request.c:466 ../vn/field/vn-combo.c:356 +#: ../vn/field/vn-completion.c:315 ../vn/column/vn-column-combo.c:297 +msgid "SQL" +msgstr "" + +#: ../sql/sql-string.c:199 +msgid "An arbitrary SQL string" +msgstr "" + +#: ../plugin/mysql/db-mysql.c:68 ../plugin/pg/db-pg.c:76 +#, c-format +msgid "Can't allocate the needed memory" +msgstr "" + +#: ../db/db-iterator.c:284 +msgid "DbIterator: Can't reassign the 'model' property" +msgstr "" + +#: ../db/db-iterator.c:942 +msgid "DbIterator: Can't set the 'conn' property because model isn't set" +msgstr "" + +#: ../db/db-iterator.c:1049 ../db/db-calc.c:329 ../vn/field/vn-combo.c:342 +#: ../vn/column/vn-column-combo.c:283 +msgid "Model" +msgstr "" + +#: ../db/db-iterator.c:1050 +msgid "The DbModel handled by the iterator" +msgstr "" + +#: ../db/db-iterator.c:1056 ../db/db-model.c:3395 ../db/db-request.c:459 +#: ../vn/vn-gui.c:1531 ../vn/vn-form.c:241 ../vn/field/vn-combo.c:349 +#: ../vn/field/vn-completion.c:308 ../vn/column/vn-column-combo.c:290 +msgid "Connection" +msgstr "" + +#: ../db/db-iterator.c:1057 ../vn/field/vn-combo.c:350 +#: ../vn/field/vn-completion.c:309 ../vn/column/vn-column-combo.c:291 +msgid "The connection used by the model" +msgstr "" + +#: ../db/db-iterator.c:1064 ../vn/field/vn-combo.c:357 +#: ../vn/field/vn-completion.c:316 ../vn/column/vn-column-combo.c:298 +msgid "The SQL query used to create the model" +msgstr "" + +#: ../db/db-iterator.c:1071 +msgid "The mode in which the iterator is working" +msgstr "" + +#: ../db/db-iterator.c:1078 +msgid "Remember selection" +msgstr "" + +#: ../db/db-iterator.c:1079 +msgid "Wether to rememeber the selection when model is refreshed" +msgstr "" + +#: ../db/db-model.c:3396 +msgid "The DbConn that manages the connection to the database" +msgstr "" + +#: ../db/db-model.c:3403 ../db/db-request.c:473 +msgid "Statement" +msgstr "" + +#: ../db/db-model.c:3404 +msgid "The statement which retrieves the data" +msgstr "" + +#: ../db/db-model.c:3412 +msgid "" +"Depending on the \"use-file\" property this will be the path to a file with " +"queries for the model or a SQL string" +msgstr "" + +#: ../db/db-model.c:3421 +msgid "Use file" +msgstr "" + +#: ../db/db-model.c:3422 +msgid "" +"If this is set to TRUE, the \"sql\" property will hold the name of a file " +"containing a query, if set to FALSE, \"sql\" is used as an SQL string" +msgstr "" + +#: ../db/db-model.c:3431 +msgid "Main Table" +msgstr "" + +#: ../db/db-model.c:3432 +msgid "The main table of the model" +msgstr "" + +#: ../db/db-model.c:3439 +msgid "Update flags" +msgstr "" + +#: ../db/db-model.c:3440 +msgid "The flags that indicate how a model can be modified" +msgstr "" + +#: ../db/db-model.c:3448 +msgid "Result position" +msgstr "" + +#: ../db/db-model.c:3449 +msgid "" +"The position where the query that will fill the model will be placed in a " +"multi-query" +msgstr "" + +#: ../db/db-calc.c:330 +msgid "The model where the operations will be applied" +msgstr "" + +#: ../db/db-calc.c:337 +msgid "Operation type" +msgstr "" + +#: ../db/db-calc.c:338 +msgid "The type of the operation applied over the function" +msgstr "" + +#: ../db/db-calc.c:347 +msgid "Function" +msgstr "" + +#: ../db/db-calc.c:348 +msgid "The function to execute" +msgstr "" + +#: ../db/db-calc.c:354 +msgid "Data" +msgstr "" + +#: ../db/db-calc.c:355 +msgid "The user provided data for the function" +msgstr "" + +#: ../db/db-calc.c:361 +msgid "Column" +msgstr "" + +#: ../db/db-calc.c:362 +msgid "A column to apply the operations over it" +msgstr "" + +#: ../db/db-param.c:243 ../vn/vn-grid.c:349 ../vn/vn-field.c:532 +msgid "Iterator" +msgstr "" + +#: ../db/db-param.c:244 +msgid "The iterator owner of param" +msgstr "" + +#: ../db/db-param.c:250 ../vn/vn-column.c:340 +msgid "Column index" +msgstr "" + +#: ../db/db-param.c:251 +msgid "The referenced column index" +msgstr "" + +#: ../db/db-param.c:257 ../vn/vn-field.c:539 ../vn/vn-column.c:347 +msgid "Column name" +msgstr "" + +#: ../db/db-param.c:258 ../vn/vn-column.c:348 +msgid "The referenced column name" +msgstr "" + +#: ../db/db-conn.c:279 ../db/db-conn.c:284 +#, c-format +msgid "Can't load DbPlugin '%s': %s" +msgstr "" + +#: ../db/db-conn.c:297 +#, c-format +msgid "Plugin can't be loaded" +msgstr "" + +#: ../db/db-conn.c:413 +#, c-format +msgid "Can't open a new connection, it's already open." +msgstr "" + +#: ../db/db-conn.c:555 +#, c-format +msgid "The query is empty." +msgstr "" + +#: ../db/db-conn.c:1060 +msgid "Plugin" +msgstr "" + +#: ../db/db-conn.c:1061 +msgid "The name of the plugin" +msgstr "" + +#: ../db/db-conn.c:1067 +msgid "Query path" +msgstr "" + +#: ../db/db-conn.c:1068 +msgid "The path where query files are located" +msgstr "" + +#: ../db/db-conn.c:1074 ../db/db-file-loader.c:724 +#: ../vn/column/vn-column-image.c:475 +msgid "Host" +msgstr "" + +#: ../db/db-conn.c:1075 +msgid "The host name to connect to" +msgstr "" + +#: ../db/db-conn.c:1081 ../module/data/users.glade.h:4 +msgid "User" +msgstr "" + +#: ../db/db-conn.c:1082 +msgid "The user name" +msgstr "" + +#: ../db/db-conn.c:1088 +msgid "DB name" +msgstr "" + +#: ../db/db-conn.c:1089 +msgid "The default schema" +msgstr "" + +#: ../db/db-request.c:315 +#, c-format +msgid "The request was canceled" +msgstr "" + +#: ../db/db-request.c:460 +msgid "The connection used to render and execute the query" +msgstr "" + +#: ../db/db-request.c:467 +msgid "The SQL query to execute" +msgstr "" + +#: ../db/db-request.c:474 +msgid "The statement to execute" +msgstr "" + +#: ../db/db-request.c:480 +msgid "Result" +msgstr "" + +#: ../db/db-request.c:481 +msgid "The result data of the query" +msgstr "" + +#: ../db/db-request.c:487 +msgid "Error" +msgstr "" + +#: ../db/db-request.c:488 +msgid "The GError, if an error ocurred" +msgstr "" + +#: ../db/db-file-loader.c:333 +#, c-format +msgid "%s not cached" +msgstr "" + +#: ../db/db-file-loader.c:431 +#, c-format +msgid "Unknown content length of file %s" +msgstr "" + +#: ../db/db-file-loader.c:725 ../vn/column/vn-column-image.c:476 +msgid "The host web server name to get the images" +msgstr "" + +#: ../db/db-file-loader.c:732 ../vn/column/vn-column-image.c:483 +msgid "Path" +msgstr "" + +#: ../db/db-file-loader.c:733 +msgid "The path of the directory to interact with" +msgstr "" + +#: ../db/db-file-loader.c:740 +msgid "Cache directory" +msgstr "" + +#: ../db/db-file-loader.c:741 +msgid "" +"The local directory where the downloaded files will be stored. The default " +"cache directory is 'hedera', under g_get_user_cache_dir()." +msgstr "" + +#: ../db/db-file-loader.c:749 +msgid "Maximal cache size" +msgstr "" + +#: ../db/db-file-loader.c:750 +msgid "The maximal size for the contents of the cache directory" +msgstr "" + +#: ../vn/vn-gui.c:620 +msgid "Connection has been lost. Do you want to reconnect?" +msgstr "" + +#: ../vn/vn-gui.c:627 +msgid "An error occurred in the connection." +msgstr "" + +#: ../vn/vn-gui.c:630 +msgid "Database error" +msgstr "" + +#: ../vn/vn-gui.c:637 +msgid "Unknown error" +msgstr "" + +#: ../vn/vn-gui.c:977 +msgid "Closing connection" +msgstr "" + +#: ../vn/vn-gui.c:979 +msgid "Transaction started" +msgstr "" + +#: ../vn/vn-gui.c:981 +msgid "Connecting" +msgstr "" + +#: ../vn/vn-gui.c:983 +msgid "Connection lost" +msgstr "" + +#: ../vn/vn-gui.c:985 +msgid "Connection closed" +msgstr "" + +#: ../vn/vn-gui.c:987 ../vn/field/vn-http-image.c:118 +msgid "Loading" +msgstr "" + +#: ../vn/vn-gui.c:989 ../vn/gui/main.glade.h:19 +msgid "Ready" +msgstr "" + +#: ../vn/vn-gui.c:1532 +msgid "The connection used by Gui" +msgstr "" + +#: ../vn/vn-gui.c:1538 +msgid "Application" +msgstr "" + +#: ../vn/vn-gui.c:1539 +msgid "The application handler for the entire program" +msgstr "" + +#: ../vn/vn-model.c:303 +#, c-format +msgid "Function vn_model_set_sort_func not implemented" +msgstr "" + +#: ../vn/vn-model.c:310 +#, c-format +msgid "Function vn_model_set_default_sort_func not implemented" +msgstr "" + +#: ../vn/vn-grid.c:350 +msgid "The iterator used by VnGrid" +msgstr "" + +#: ../vn/vn-handler.c:65 +msgid "Are you sure you want to delete the selected record?" +msgstr "" + +#: ../vn/vn-handler.c:90 +msgid "Are you sure that you want to undo all changes?" +msgstr "" + +#: ../vn/vn-handler.c:219 +msgid "Undo changes" +msgstr "" + +#: ../vn/vn-handler.c:226 +msgid "Save changes" +msgstr "" + +#: ../vn/vn-handler.c:233 +msgid "Refresh data" +msgstr "" + +#: ../vn/vn-handler.c:240 +msgid "Remove record" +msgstr "" + +#: ../vn/vn-handler.c:247 +msgid "Add record" +msgstr "" + +#: ../vn/vn-handler.c:254 +msgid "Move to the first row" +msgstr "" + +#: ../vn/vn-handler.c:261 +msgid "Move to the previous row" +msgstr "" + +#: ../vn/vn-handler.c:268 +msgid "Move to the next row" +msgstr "" + +#: ../vn/vn-handler.c:275 +msgid "Move to the last row" +msgstr "" + +#: ../vn/vn-handler.c:514 +msgid "Show flags" +msgstr "" + +#: ../vn/vn-handler.c:515 +msgid "Sets the buttons that will be shown on the interface" +msgstr "" + +#: ../vn/vn-handler.c:522 +msgid "Simple record" +msgstr "" + +#: ../vn/vn-handler.c:523 +msgid "Sets if it is used to handle a iterator with a single record" +msgstr "" + +#: ../vn/vn-form.c:227 +msgid "Name" +msgstr "" + +#: ../vn/vn-form.c:228 +msgid "The form name" +msgstr "" + +#: ../vn/vn-form.c:234 +msgid "Gui" +msgstr "" + +#: ../vn/vn-form.c:235 +msgid "The Gui object" +msgstr "" + +#: ../vn/vn-form.c:242 +msgid "The connection used by the module" +msgstr "" + +#: ../vn/vn-form.c:248 +msgid "Module" +msgstr "" + +#: ../vn/vn-form.c:249 +msgid "The module" +msgstr "" + +#: ../vn/vn-login.c:276 ../vn/vn-login.c:278 +msgid "Login error" +msgstr "" + +#: ../vn/vn-login.c:336 +#, c-format +msgid "Bad connection settings, please check it." +msgstr "" + +#: ../vn/vn-login.c:457 +msgid "Application id" +msgstr "" + +#: ../vn/vn-login.c:458 +msgid "The application identifier" +msgstr "" + +#: ../vn/vn-field.c:519 +msgid "The current value of the field" +msgstr "" + +#: ../vn/vn-field.c:525 +msgid "Parameter" +msgstr "" + +#: ../vn/vn-field.c:526 +msgid "The param where the field can read/write its value" +msgstr "" + +#: ../vn/vn-field.c:533 +msgid "The iterator used to get the field param" +msgstr "" + +#: ../vn/vn-field.c:540 +msgid "The column name on the iterator" +msgstr "" + +#: ../vn/vn-field.c:554 +msgid "Whether the field value is user editable" +msgstr "" + +#: ../vn/vn-field.c:561 +msgid "Whether the field value can be of type GVN_TYPE_NULL" +msgstr "" + +#: ../vn/field/vn-entry.c:153 ../vn/field/vn-spin.c:172 +#: ../vn/column/vn-column-entry.c:119 ../vn/column/vn-column-spin.c:138 +msgid "Digits" +msgstr "" + +#: ../vn/field/vn-entry.c:154 ../vn/field/vn-spin.c:173 +#: ../vn/column/vn-column-entry.c:120 ../vn/column/vn-column-spin.c:139 +msgid "The number of decimal places to display." +msgstr "" + +#: ../vn/field/vn-combo.c:328 ../vn/column/vn-column-combo.c:269 +msgid "Index column" +msgstr "" + +#: ../vn/field/vn-combo.c:329 ../vn/column/vn-column-combo.c:270 +msgid "The column index of the model" +msgstr "" + +#: ../vn/field/vn-combo.c:335 ../vn/column/vn-column-combo.c:276 +msgid "Show column" +msgstr "" + +#: ../vn/field/vn-combo.c:336 ../vn/column/vn-column-combo.c:277 +msgid "The column of the model shown by combo" +msgstr "" + +#: ../vn/field/vn-combo.c:343 ../vn/column/vn-column-combo.c:284 +msgid "The model from which the combo takes the values shown in the list" +msgstr "" + +#: ../vn/field/vn-completion.c:322 +msgid "Field" +msgstr "" + +#: ../vn/field/vn-completion.c:323 +msgid "The name of the field used for the search" +msgstr "" + +#: ../vn/field/vn-date-chooser.c:71 +#, c-format +msgid "%s, %u %s %u" +msgstr "" + +#: ../vn/field/vn-date-chooser.c:280 +msgid "Change date" +msgstr "" + +#: ../vn/field/vn-image.c:133 +msgid "Select the image" +msgstr "" + +#: ../vn/field/vn-http-image.c:69 +msgid "Undefined error" +msgstr "" + +#: ../vn/field/vn-http-image.c:145 +msgid "File loader already set" +msgstr "" + +#. Se usará para subir imagenes y cambiar nombres, o abrir un menu contextual +#. g_signal_connect (obj, "event", G_CALLBACK (vn_http_image_cb_event), obj); +#: ../vn/field/vn-http-image.c:184 +msgid "No image set" +msgstr "" + +#: ../vn/field/vn-http-image.c:210 +msgid "File loader" +msgstr "" + +#: ../vn/field/vn-http-image.c:211 +msgid "A DbFileLoader, used to download the files" +msgstr "" + +#: ../vn/field/vn-http-image.c:218 +msgid "File path" +msgstr "" + +#: ../vn/field/vn-http-image.c:219 +msgid "The relative path to the image from file loader path" +msgstr "" + +#: ../vn/field/vn-http-image.c:226 +msgid "Image bytes" +msgstr "" + +#: ../vn/field/vn-http-image.c:227 +msgid "A GBytes structure with the image data" +msgstr "" + +#: ../vn/vn-column.c:341 +msgid "The column index in the model" +msgstr "" + +#: ../vn/vn-column.c:355 +msgid "Whether the column values are editable" +msgstr "" + +#: ../vn/column/vn-column-spin.c:130 +msgid "Climb rate" +msgstr "" + +#: ../vn/column/vn-column-spin.c:131 +msgid "The acceleration rate when you hold down a button." +msgstr "" + +#: ../vn/column/vn-column-image.c:484 +msgid "Base path from the host where the images will be downloaded" +msgstr "" + +#: ../vn/column/vn-column-image.c:491 +msgid "Tooltip path" +msgstr "" + +#: ../vn/column/vn-column-image.c:492 +msgid "" +"Prefix for the path of the images to be shown in the tooltip. Starting after " +"the path of the column and appending the name on each cell" +msgstr "" + +#: ../vn/column/vn-column-image.c:501 +msgid "Tooltip size" +msgstr "" + +#: ../vn/column/vn-column-image.c:502 +msgid "" +"Size of the bigger side of the tooltip images, the another side will be " +"scaled accordingly and smaller images won't be scaled" +msgstr "" + +#: ../vn/gui/login.glade.h:1 +msgid "Configuration" +msgstr "" + +#: ../vn/gui/login.glade.h:2 +msgid "Plugin:" +msgstr "" + +#: ../vn/gui/login.glade.h:3 +msgid "Host:" +msgstr "" + +#: ../vn/gui/login.glade.h:4 +msgid "Schema:" +msgstr "" + +#: ../vn/gui/login.glade.h:5 +msgid "SSL CA:" +msgstr "" + +#: ../vn/gui/login.glade.h:6 +msgid "" +"Path to the file containing the CA certificate, if this is empty SSL won't " +"be used" +msgstr "" + +#: ../vn/gui/login.glade.h:7 +msgid "Access" +msgstr "" + +#: ../vn/gui/login.glade.h:8 ../vn/gui/main.glade.h:20 +msgid "User:" +msgstr "" + +#: ../vn/gui/login.glade.h:9 ../module/data/users.glade.h:8 +msgid "Password:" +msgstr "" + +#: ../vn/gui/login.glade.h:10 +msgid "Remember" +msgstr "" + +#: ../vn/gui/main.glade.h:1 +msgid "Copyright - Verdnatura Levante S. L." +msgstr "" + +#: ../vn/gui/main.glade.h:2 +msgid "Management and administration of companies" +msgstr "" + +#: ../vn/gui/main.glade.h:3 +msgid "www.verdnatura.es" +msgstr "" + +#: ../vn/gui/main.glade.h:4 +msgid "" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU Lesser General Public\n" +"License as published by the Free Software Foundation; either\n" +"version 2.1 of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +"Lesser General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU Lesser General Public\n" +"License along with this program; if not, write to the Free\n" +"Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n" +"02111-1307 USA." +msgstr "" + +#: ../vn/gui/main.glade.h:18 ../vn/gui/child-window.glade.h:1 +msgid "Hedera" +msgstr "" + +#: ../vn/gui/main.glade.h:21 +msgid "[user-name]" +msgstr "" + +#: ../vn/gui/actions.glade.h:1 +msgid "_Logout" +msgstr "" + +#: ../vn/gui/actions.glade.h:2 +msgid "Logout" +msgstr "" + +#: ../vn/gui/actions.glade.h:3 +msgid "_Quit" +msgstr "" + +#: ../vn/gui/actions.glade.h:4 +msgid "_About" +msgstr "" + +#: ../vn/gui/actions.glade.h:5 +msgid "_File" +msgstr "" + +#: ../vn/gui/actions.glade.h:6 +msgid "_Help" +msgstr "" + +#: ../vn/gui/actions.glade.h:7 +msgid "_Reconnect" +msgstr "" + +#: ../vn/gui/actions.glade.h:8 +msgid "Reconnect" +msgstr "" + +#: ../vn/gui/actions.glade.h:9 +msgid "_Close" +msgstr "" + +#: ../vn/gui/actions.glade.h:10 +msgid "_View" +msgstr "" + +#: ../vn/gui/actions.glade.h:11 +msgctxt "View menu option" +msgid "Dynamic _Tabs" +msgstr "" + +#: ../vn/gui/actions.glade.h:12 +msgid "Don't show tabs if there is only one tab open" +msgstr "" + +#: ../vn/gui/actions.glade.h:13 +msgctxt "View menu option" +msgid "Tool_bar" +msgstr "" + +#: ../module/data/users.glade.h:1 +msgid "User name:" +msgstr "" + +#: ../module/data/users.glade.h:2 +msgid "Search" +msgstr "" + +#: ../module/data/users.glade.h:3 +msgid "Identifier" +msgstr "" + +#: ../module/data/users.glade.h:5 +msgid "MySQL User" +msgstr "" + +#: ../module/data/users.glade.h:6 +msgid "Enabled" +msgstr "" + +#: ../module/data/users.glade.h:7 +msgid "Name:" +msgstr "" + +#: ../module/data/users.glade.h:9 +msgid "MySQL user:" +msgstr "" + +#: ../module/data/users.glade.h:10 +msgid "Enabled:" +msgstr "" + +#: ../module/data/users.glade.h:11 +msgid "Identifier:" +msgstr "" + +#: ../module/data/users.glade.h:12 +msgid "UID:" +msgstr "" + +#: ../module/data/users.glade.h:13 +msgid "Main group:" +msgstr "" + +#: ../module/data/users.glade.h:14 +msgid "Last change:" +msgstr "" + +#: ../module/data/users.glade.h:15 +msgid "Expires:" +msgstr "" + +#: ../module/data/users.glade.h:16 +msgid "Account" +msgstr "" + +#: ../module/data/users.glade.h:17 +msgid "Alias" +msgstr "" + +#: ../module/data/users.glade.h:18 +msgid "Mail alias" +msgstr "" + +#: ../module/data/users.glade.h:19 +msgid "Extension:" +msgstr "" + +#: ../module/data/users.glade.h:20 +msgid "Secret:" +msgstr "" + +#: ../module/data/users.glade.h:21 +msgid "Call group:" +msgstr "" + +#: ../module/data/users.glade.h:22 +msgid "SIP" +msgstr "" + +#: ../module/data/users.glade.h:23 +msgid "Repeat password:" +msgstr "" diff --git a/sql/Makefile.am b/sql/Makefile.am new file mode 100644 index 0000000..66d2c13 --- /dev/null +++ b/sql/Makefile.am @@ -0,0 +1,119 @@ +include $(top_srcdir)/Makefile.decl + +SUBDIRS = parser + +sql_lib_LTLIBRARIES = libsql.la +sql_include_HEADERS = \ + sql.h \ + sql-object.h \ + sql-param-object.h \ + sql-param-list.h \ + sql-holder.h \ + sql-list.h \ + sql-set.h \ + sql-multi-stmt.h \ + sql-string.h \ + sql-target.h \ + sql-expr.h \ + sql-insert.h \ + sql-select.h \ + sql-select-field.h \ + sql-select-order.h \ + sql-subquery.h \ + sql-update.h \ + sql-update-set.h \ + sql-delete.h \ + sql-field.h \ + sql-join.h \ + sql-operation.h \ + sql-stmt.h \ + sql-table.h \ + sql-value.h \ + sql-dml.h \ + sql-function.h \ + sql-render.h \ + sql-parser.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(glib_CFLAGS) +libsql_la_LIBADD = \ + $(glib_LIBS) \ + $(top_builddir)/gvn/libgvn.la +libsql_la_SOURCES = \ + $(sql_include_HEADERS) \ + sql-object.c \ + sql-param-object.c \ + sql-param-list.c \ + sql-holder.c \ + sql-list.c \ + sql-set.c \ + sql-expr.c \ + sql-insert.c \ + sql-select.c \ + sql-select-field.c \ + sql-select-order.c \ + sql-subquery.c \ + sql-update.c \ + sql-update-set.c \ + sql-delete.c \ + sql-field.c \ + sql-join.c \ + sql-operation.c \ + sql-stmt.c \ + sql-table.c \ + sql-value.c \ + sql-dml.c \ + sql-function.c \ + sql-multi-stmt.c \ + sql-string.c \ + sql-target.c \ + sql-render.c \ + $(top_builddir)/sql/sql-parser.c + +pkgconfig_DATA = sql.pc + +EXTRA_DIST = sql.pc.in + +DISTCLEANFILES = sql.pc + +if ENABLE_VALA +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_SCANNER_ARGS = $(GIR_SCANNER_ARGS) + +INTROSPECTION_COMPILER_ARGS = --includedir=$(top_builddir)/gvn + +introspection_sources = $(libsql_la_SOURCES) + +Sql-$(VERSION).gir: $(sql_lib_LTLIBRARIES) $(top_builddir)/gvn/Gvn-$(VERSION).gir + Sql_@uVERSION@_gir_SCANNERFLAGS = \ + --include-uninstalled=$(top_builddir)/gvn/Gvn-$(VERSION).gir + Sql_@uVERSION@_gir_CFLAGS = -I$(top_srcdir) + Sql_@uVERSION@_gir_LIBS = $(sql_lib_LTLIBRARIES) + Sql_@uVERSION@_gir_FILES = $(introspection_sources) + Sql_@uVERSION@_gir_EXPORT_PACKAGES = sql + INTROSPECTION_GIRS = Sql-$(VERSION).gir + +gir_DATA = $(INTROSPECTION_GIRS) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) + +$(vapis)/sql.vapi: $(INTROSPECTION_GIRS) $(vapidata)/Sql-$(VERSION).metadata + $(vapigen_v)$(VAPIGEN) -q \ + --directory $(vapis) \ + --vapidir $(vapis) \ + --girdir $(top_builddir)/gvn \ + --metadatadir $(vapidata) \ + --library sql \ + Sql-$(VERSION).gir + +vapi_DATA = $(vapis)/sql.vapi + +CLEANFILES += $(vapi_DATA) + +endif +endif diff --git a/sql/parser/Makefile.am b/sql/parser/Makefile.am new file mode 100644 index 0000000..da1df2b --- /dev/null +++ b/sql/parser/Makefile.am @@ -0,0 +1,34 @@ +include $(top_srcdir)/Makefile.decl + +PARSER = $(top_builddir)/sql/sql-parser.c + +DIR = $(top_srcdir)/sql/parser +TMPL = $(DIR)/lempar-tmpl.c +SCAN = scan.rl +GRAM = gram.y + +all: $(PARSER) +$(PARSER): $(SCAN) gram.c gram.h + $(ragel_v)$(RAGEL) -C -G2 -o $(PARSER) $< + +gram.h: gram.c +gram.c: $(GRAM) lemon $(TMPL) + ln -fs $(TMPL) $(top_builddir)/sql/parser/lempar.c + $(lemon_v)./lemon -q $< + +lemon: $(DIR)/lemon.c + $(CC) -o lemon $< + +EXTRA_DIST = gram.y scan.rl lemon.c lempar-tmpl.c + +CLEANFILES = $(DIR)/gram.c $(DIR)/gram.h $(PARSER) + +DISTCLEANFILES = lemon lempar.c + +ragel_v = $(ragel_v_@AM_V@) +ragel_v_ = $(ragel_v_@AM_DEFAULT_V@) +ragel_v_0 = @echo " RAGEL $@"; + +lemon_v = $(lemon_v_@AM_V@) +lemon_v_ = $(lemon_v_@AM_DEFAULT_V@) +lemon_v_0 = @echo " LEMON $(subst y,c,$(GRAM)) $(subst y,h,$(GRAM))"; \ No newline at end of file diff --git a/sql/parser/gram.y b/sql/parser/gram.y new file mode 100644 index 0000000..07fac6e --- /dev/null +++ b/sql/parser/gram.y @@ -0,0 +1,650 @@ +%include +{ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + #include + #include + #include + #include "gram.h" + + typedef struct + { + SqlObject * object; + gchar * string; + gchar * current; + gboolean error; + } + ParseState; + + static inline SqlList * list_add (gpointer item, SqlList * list) + { + sql_list_add (list, item); + return list; + } + + static inline SqlList * list_new (gpointer item, GType gtype) + { + SqlList * list = sql_list_new (gtype); + return list_add (item, list); + } +} + +%token_type {gchar *} +%token_destructor {g_free ($$);} +%token_prefix SQL_PARSER_ + +// Return pointer +%extra_argument { ParseState * state } + +// Error treatment +%syntax_error +{ + gchar * pref = ".", * err_str = NULL; + + if (state->current) + { + err_str = state->current - 15 >= state->string ? + state->current - 10 : state->string; + pref = err_str == state->string ? " near: " : " near: [...]"; + } + + g_log (g_quark_to_string (SQL_PARSER_LOG_DOMAIN), G_LOG_LEVEL_WARNING, + "Parsing error%s%s", pref, err_str); + + state->error = TRUE; +} + +%stack_overflow +{ + g_log (g_quark_to_string (SQL_PARSER_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING + ,"Parser stack overflow. Parsing terminated.\n"); +} + +// Operator precedence +%left UNION EXCEPT. +%left INSERSECT. +%left OR. +%left XOR. +%left AND. +%right NOT. +%right EQ NE. +%left GT GE LT LE. +%left LIKE. +%left IS. +%left PLUS MINUS. +%left STAR DIV MOD. +%right SIGN. + +// Start symbol: +%start_symbol start + +%type start {SqlObject *} +start ::= multi_stmt (A). +{ + state->object = SQL_OBJECT (A); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Multi statement + +%type multi_stmt {SqlStmt *} +multi_stmt(A) ::= stmt_list(stmts). +{ + guint len = sql_list_length (stmts); + + if (len == 1) + { + A = sql_list_get (stmts, 0); + g_object_unref (g_object_ref_sink (stmts)); + } + else + A = g_object_new (SQL_TYPE_MULTI_STMT, "stmts", stmts, NULL); +} + +%type stmt_list {SqlList *} +stmt_list(A) ::= stmt(X). { A = list_new (X, SQL_TYPE_STMT); } +stmt_list(A) ::= stmt(X) SC. { A = list_new (X, SQL_TYPE_STMT); } +stmt_list(A) ::= stmt(X) SC stmt_list(B). { A = list_add (X, B); } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Statemet + +%type stmt {SqlStmt *} +stmt(A) ::= select_stmt(X). { A = X; } +stmt(A) ::= delete_stmt(X). { A = X; } +stmt(A) ::= update_stmt(X). { A = X; } +stmt(A) ::= insert_stmt(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Select + +%type select_stmt {SqlStmt *} +select_stmt(A) ::= select_stmt(B) set_op(type) simple_select(X). +{ + g_object_set (B, "type", type, "next", X, NULL); + A = B; +} +select_stmt(A) ::= simple_select(X). { A = X; } + +%type set_op {SqlSelectType} +set_op(A) ::= UNION ANY. { A = SQL_SELECT_UNION_ANY; } +set_op(A) ::= UNION ALL. { A = SQL_SELECT_UNION_ALL; } +set_op(A) ::= UNION. { A = SQL_SELECT_UNION_ALL; } +set_op(A) ::= INTERSECT. { A = SQL_SELECT_INTERSECT; } +set_op(A) ::= EXCEPT. { A = SQL_SELECT_EXCEPT; } + +%type simple_select {SqlStmt *} +simple_select(A) ::= LP simple_select(X) RP. { A = X; } +simple_select(A) ::= SELECT + distinct(distinct) + select_field_list(fields) + from(targets) + where(where) + group(group) having(having) + order(order) + limit(limit). +{ + A = g_object_new (SQL_TYPE_SELECT + ,"distinct" ,distinct + ,"fields" ,fields + ,"targets" ,targets + ,"where" ,where + ,"group" ,group + ,"having" ,having + ,"having" ,having + ,"order" ,order + ,NULL + ); + + if (limit) + g_object_set (A + ,"limit-offset" ,limit->offset + ,"limit-count" ,limit->count + ,NULL + ); +} + +%type distinct {gboolean} +distinct(A) ::= DISTINCT. { A = TRUE; } +distinct(A) ::= ALL. { A = FALSE; } +distinct(A) ::= . { A = FALSE; } + +%type select_field {SqlSelectField *} +select_field(A) ::= expr(X). + { A = g_object_new (SQL_TYPE_SELECT_FIELD, "expr", X, NULL); } +select_field(A) ::= expr(X) alias(Y). + { A = g_object_new (SQL_TYPE_SELECT_FIELD, "expr", X, "alias", Y, NULL); } + +%type select_field_list {SqlList *} +select_field_list(A) ::= select_field(X). { A = list_new (X, SQL_TYPE_SELECT_FIELD); } +select_field_list(A) ::= select_field_list(B) CM select_field(X). { A = list_add (X, B); } + +%type from {SqlList *} +from(A) ::= FROM target_list(X). { A = X; } +from(A) ::= . { A = NULL; } + +%type group {SqlList *} +group(A) ::= GROUP expr_list(X). { A = X; } +group(A) ::= . { A = NULL; } + +%type having {SqlExpr *} +having(A) ::= HAVING expr(X). { A = X; } +having(A) ::= . { A = NULL; } + +%type order {SqlList *} +order(A) ::= ORDER order_list (X). { A = X; } +order(A) ::= . { A = NULL; } + +%type order_list {SqlList *} +order_list(A) ::= order_expr(X). { A = list_new (X, SQL_TYPE_SELECT_ORDER); } +order_list(A) ::= order_list(B) CM order_expr(X). { A = list_add (X, B); } + +%type order_expr {SqlSelectOrder *} +order_expr(A) ::= expr(X) order_way(Y). { A = g_object_new (SQL_TYPE_SELECT_ORDER, "expr", X, "way", Y, NULL); } + +%type order_way {SqlSelectOrderWay} +order_way(A) ::= ASC. { A = SQL_SELECT_ORDER_ASC; } +order_way(A) ::= DESC. { A = SQL_SELECT_ORDER_DESC; } +order_way(A) ::= . { A = SQL_SELECT_ORDER_ASC; } + +%type limit {SelectLimit *} +%destructor limit {g_free ($$);} +%include +{ + typedef struct + { + guint offset; + guint count; + } + SelectLimit; + + static inline SelectLimit * create_limit (gchar * offset, gchar * count) + { + SelectLimit * limit = g_new (SelectLimit, 1); + limit->offset = offset ? atoi (offset) : 0; + limit->count = count ? atoi (count) : 0; + return limit; + } +} +limit(A) ::= LIMIT INTEGER(X). { A = create_limit (NULL, X); } +limit(A) ::= OFFSET INTEGER(Y). { A = create_limit (Y, NULL); } +limit(A) ::= LIMIT INTEGER(X) OFFSET INTEGER(Y). { A = create_limit (Y, X); } +limit(A) ::= LIMIT INTEGER(X) CM INTEGER(Y). { A = create_limit (Y, X); } +limit(A) ::= . { A = NULL; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Delete + +%type delete_stmt {SqlStmt *} +delete_stmt(A) ::= DELETE table_list(targets) del_using(tables) where(where). +{ + A = g_object_new (SQL_TYPE_DELETE + ,"targets" ,targets + ,"tables" ,tables + ,"where" ,where + ,NULL + ); +} + +%type table_list {SqlList *} +table_list(A) ::= table(X). { A = list_new (X, SQL_TYPE_TABLE); } +table_list(A) ::= table_list(B) CM table(X). { A = list_add (X, B); } + +%type del_using {SqlList *} +del_using(A) ::= USING target_list(X). { A = X; } +del_using(A) ::= . { A = NULL; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Update + +%type update_stmt {SqlStmt *} +update_stmt(A) ::= UPDATE table(table) SET update_list(sets) where(where). +{ + A = g_object_new (SQL_TYPE_UPDATE + ,"targets" ,table + ,"sets" ,sets + ,"where" ,where + ,NULL + ); +} + +%type update_list {SqlList *} +update_list(A) ::= update_set(X). { A = list_new (X, SQL_TYPE_UPDATE_SET); } +update_list(A) ::= update_list(B) CM update_set(X). { A = list_add (X, B); } + +%type update_set {SqlUpdateSet *} +update_set(A) ::= field(X) EQ def_expr(Y). +{ + A = g_object_new (SQL_TYPE_UPDATE_SET + ,"field" ,X + ,"expr" ,Y + ,NULL + ); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Insert + +%type insert_stmt {SqlStmt *} +insert_stmt(A) ::= INSERT table(table) insert_fields(fields) insert_values(values). +{ + A = g_object_new (SQL_TYPE_INSERT + ,"table" ,table + ,"fields" ,fields + ,NULL + ); + + if (SQL_IS_SELECT (values)) + g_object_set (A, "select", values, NULL); + else + g_object_set (A, "values", values, NULL); +} + +%type insert_fields {SqlList *} +insert_fields(A) ::= LP field_list(X) RP. { A = X; } +insert_fields(A) ::= . { A = NULL; } + +%type insert_values {gpointer} +insert_values(A) ::= DEFAULT VALUES. { A = NULL; } +insert_values(A) ::= VALUES set_list(X). { A = X; } +//insert_values(A) ::= select_stmt(X). { A = X; } + +%type set_list {SqlList *} +set_list(A) ::= set(X). { A = list_new (X, SQL_TYPE_SET); } +set_list(A) ::= set_list(B) CM set(X). { A = list_add (X, B); } + +%type set {SqlSet *} +set(A) ::= LP def_expr_list(X) RP. { A = g_object_new (SQL_TYPE_SET, "exprs", X, NULL); } + +%type def_expr_list {SqlList *} +def_expr_list(A) ::= def_expr(X). { A = list_new (X, SQL_TYPE_EXPR); } +def_expr_list(A) ::= def_expr_list(B) CM def_expr(X). { A = list_add (X, B); } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Common to statements + +%type where {SqlExpr *} +where(A) ::= WHERE expr(X). { A = SQL_EXPR (X); } +where(A) ::= . { A = NULL; } + +%type def_expr {SqlExpr *} +def_expr(A) ::= expr(X). { A = X; } +def_expr(A) ::= DEFAULT. { A = NULL; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Target + +%type target {SqlTarget *} +target(A) ::= unaliased_target(X). { A = X; } +target(A) ::= unaliased_target(X) alias(Y). +{ + g_object_set (X, "alias", Y, NULL); + A = X; +} +//target(A) ::= LP target(X) RP. { A = X; } + +%type target_list {SqlList *} +target_list(A) ::= target(X). { A = list_new (X, SQL_TYPE_TARGET); } +target_list(A) ::= target_list(B) CM target(X). { A = list_add (X, B); } + +%type unaliased_target {SqlTarget *} +unaliased_target(A) ::= join(X). { A = X; } +unaliased_target(A) ::= subquery(X). { A = X; } +unaliased_target(A) ::= table(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Join + +%type join {SqlTarget *} +join(A) ::= target(left) join_type(type) target(right) join_cond(condition). +{ + A = g_object_new (SQL_TYPE_JOIN + ,"target-left" ,left + ,"target-right" ,right + ,"join-type" ,type + ,NULL + ); + + if (SQL_IS_LIST (condition)) // USING + { + GList * n; + SqlList * exprs = sql_list_new (SQL_TYPE_EXPR); + SqlOperation * op_and = g_object_new (SQL_TYPE_OPERATION + ,"type" ,SQL_OPERATION_TYPE_AND + ,"exprs" ,exprs + ,NULL + ); + + for (n = sql_list_get_items (condition); n; n = n->next) + { + SqlList * equal = sql_list_new (SQL_TYPE_EXPR); + SqlOperation * op = g_object_new (SQL_TYPE_OPERATION + ,"type" ,SQL_OPERATION_TYPE_EQUAL + ,"exprs" ,equal + ,NULL + ); + + gchar * target = left->alias ? + left->alias : SQL_TABLE (left)->name; + gchar * schema = SQL_IS_TABLE(left) && SQL_TABLE (left)->schema ? + SQL_TABLE (left)->schema : NULL; + + g_object_set (n->data, "target", target, "schema", schema, NULL); + sql_list_add (equal, n->data); + + target = right->alias ? right->alias : SQL_TABLE (right)->name; + schema = SQL_IS_TABLE (right) && SQL_TABLE (right)->schema ? + SQL_TABLE (right)->schema : NULL; + + sql_list_add (equal, + sql_field_new (SQL_FIELD (n->data)->name, target, schema)); + + sql_list_add (exprs, op); + } + + g_object_set (A, "condition", op_and, NULL); + g_object_unref (g_object_ref_sink (condition)); + } + else + g_object_set (A, "condition", condition, NULL); +} + +%type join_type {SqlJoinType} +join_type(A) ::= INNER JOIN. { A = SQL_JOIN_TYPE_INNER; } +join_type(A) ::= JOIN. { A = SQL_JOIN_TYPE_INNER; } +join_type(A) ::= LEFT JOIN. { A = SQL_JOIN_TYPE_LEFT; } +join_type(A) ::= RIGHT JOIN. { A = SQL_JOIN_TYPE_RIGHT; } + +%type join_cond {gpointer} +join_cond(A) ::= ON expr(X). { A = X; } +join_cond(A) ::= USING LP field_list(X) RP. { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Subquery + +%type subquery {SqlTarget *} +subquery(A) ::= LP select_stmt(stmts) RP. +{ + A = g_object_new (SQL_TYPE_SUBQUERY, "stms", stmts, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Table + +%type table {SqlTarget *} +table(A) ::= identifier(X). +{ + A = g_object_new (SQL_TYPE_TABLE, "table", X, NULL); +} +table(A) ::= identifier(Y) DOT identifier(X). +{ + A = g_object_new (SQL_TYPE_TABLE, "table", X, "schema", Y, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Expr + +%type expr {SqlExpr *} +expr(A) ::= LP expr(X) RP. { A = X; } + +%type expr_list {SqlList *} +expr_list(A) ::= expr(X). { A = list_new (X, SQL_TYPE_EXPR); } +expr_list(A) ::= expr_list(B) CM expr(X). { A = list_add (X, B); } + +expr(A) ::= field(X). { A = X; } +expr(A) ::= function(X). { A = X; } +expr(A) ::= operation(X). { A = X; } +expr(A) ::= value(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Field + +%type field {SqlExpr *} +field(A) ::= unqualified_field(X). { A = X; } +field(A) ::= identifier(Y) DOT unqualified_field(X). +{ + g_object_set (X, "target", Y, NULL); + A = X; +} +field(A) ::= identifier(Z) DOT identifier(Y) DOT unqualified_field(X). +{ + g_object_set (X, "target", Y, "schema", Z, NULL); + A = X; +} + +%type unqualified_field {SqlExpr *} +unqualified_field(A) ::= identifier(X). +{ + A = g_object_new (SQL_TYPE_FIELD, "name", X, NULL); +} +unqualified_field(A) ::= STAR. +{ + A = g_object_new (SQL_TYPE_FIELD, "name", "*", NULL); +} + +%type field_list {SqlList *} +field_list(A) ::= field(X). { A = list_new (X, SQL_TYPE_FIELD); } +field_list(A) ::= field_list(B) CM field(X). { A = list_add (X, B); } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Function + +%type function {SqlExpr *} +function(A) ::= unqualified_function(X). { A = X; } +function(A) ::= identifier(Z) DOT unqualified_function(X). +{ + g_object_set (X, "schema", Z, NULL); + A = X; +} + +%type unqualified_function {SqlExpr *} +unqualified_function(A) ::= identifier(X) LP function_expr_list(Y) RP. +{ + A = g_object_new (SQL_TYPE_FUNCTION, "name", X, "params", Y, NULL); +} + +%type function_expr_list {SqlList *} +function_expr_list(A) ::= . { A = NULL; } +function_expr_list(A) ::= expr_list(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Operation + +%type operation {SqlExpr *} +%include +{ + static inline SqlExpr * create_operation + (const gpointer X, const gpointer Y, const SqlOperationType type) + { + SqlList * exprs = sql_list_new (SQL_TYPE_EXPR); + sql_list_add (exprs, X); + + if (Y) // Binary operation + sql_list_add (exprs, Y); + + return g_object_new (SQL_TYPE_OPERATION, "type", type, "exprs", exprs, NULL); + } +} +operation(A) ::= MINUS expr(X). [SIGN] +{ + GValue value = {0}; + SqlExpr * minus = sql_value_new (); + g_value_init (&value, G_TYPE_INT); + g_value_set_int (&value, -1); + sql_value_set_value (SQL_VALUE (minus), &value); + A = create_operation (X, minus, SQL_OPERATION_TYPE_MULTIPLICATION); +} + +operation(A) ::= NOT expr(X). + { A = create_operation (X, NULL, SQL_OPERATION_TYPE_NOT); } + +operation(A) ::= PLUS expr(X). [SIGN] + { A = create_operation (X, NULL, SQL_OPERATION_TYPE_SUM); } + +operation(A) ::= expr(X) PLUS expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_SUM); } + +operation(A) ::= expr(X) MINUS expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_SUBTRACTION); } + +operation(A) ::= expr(X) STAR expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_MULTIPLICATION); } + +operation(A) ::= expr(X) DIV expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_DIVISION); } + +operation(A) ::= expr(X) OR expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_OR); } + +operation(A) ::= expr(X) XOR expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_XOR); } + +operation(A) ::= expr(X) AND expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_AND); } + +operation(A) ::= expr(X) EQ expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_EQUAL); } + +operation(A) ::= expr(X) NE expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_NOT_EQUAL); } + +//XXX the following should be %nonassoc operators but are %left to avoid +// conflicts. The DB will warn about a bad use if used as %left. +operation(A) ::= expr(X) GT expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_GREATER); } + +operation(A) ::= expr(X) GE expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_GREATER_EQUAL); } + +operation(A) ::= expr(X) LT expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_LOWER); } + +operation(A) ::= expr(X) LE expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_LOWER_EQUAL); } + +operation(A) ::= expr(X) LIKE expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_LIKE); } + +operation(A) ::= expr(X) IS expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_IS); } + +operation(A) ::= expr(X) MOD expr(Y). + { A = create_operation (X, Y, SQL_OPERATION_TYPE_MOD); } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Value + +%type value {SqlExpr *} + +value(A) ::= INTEGER(X). +{ + GValue value = {0}; + g_value_set_int (g_value_init (&value, G_TYPE_INT), atoi (X)); + A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); + g_value_unset (&value); +} + +value(A) ::= FLOAT(X). +{ + GValue value = {0}; + g_value_set_double + (g_value_init (&value, G_TYPE_DOUBLE), g_ascii_strtod (X, NULL)); + A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); + g_value_unset (&value); +} + +value(A) ::= STRING(X). +{ + GValue value = {0}; + g_value_set_string (g_value_init (&value, G_TYPE_STRING), X); + A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); + g_value_unset (&value); +} + +value(A) ::= BOOLEAN(X). +{ + GValue value = {0}; + g_value_set_boolean (g_value_init (&value, G_TYPE_BOOLEAN) + ,(!g_strcmp0 (X, "TRUE") || !g_strcmp0 (X, "true")) ? TRUE : FALSE); + A = g_object_new (SQL_TYPE_VALUE, "value", &value, NULL); + g_value_unset (&value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Alias + +%type alias {gchar *} +%destructor alias {g_free ($$);} +alias(A) ::= AS identifier(X). { A = X; } +alias(A) ::= identifier(X). { A = X; } + +identifier(A) ::= IDENTIFIER(X). { A = X; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Placeholder + +%type placeholder {gchar *} +%destructor expr {g_free ($$);} +placeholder(A) ::= PLACEHOLDER(X). { A = X; } + +stmt ::= placeholder. +target ::= placeholder. +expr ::= placeholder. diff --git a/sql/parser/lemon.c b/sql/parser/lemon.c new file mode 100644 index 0000000..5d96995 --- /dev/null +++ b/sql/parser/lemon.c @@ -0,0 +1,4895 @@ +/* +** This file contains all sources (including headers) to the LEMON +** LALR(1) parser generator. The sources have been combined into a +** single file to make it easy to include LEMON in the source tree +** and Makefile of another program. +** +** The author of this program disclaims copyright. +*/ +#include +#include +#include +#include +#include +#include + +#ifndef __WIN32__ +# if defined(_WIN32) || defined(WIN32) +# define __WIN32__ +# endif +#endif + +#ifdef __WIN32__ +#ifdef __cplusplus +extern "C" { +#endif +extern int access(const char *path, int mode); +#ifdef __cplusplus +} +#endif +#else +#include +#endif + +/* #define PRIVATE static */ +#define PRIVATE + +#ifdef TEST +#define MAXRHS 5 /* Set low to exercise exception code */ +#else +#define MAXRHS 1000 +#endif + +static int showPrecedenceConflict = 0; +static char *msort(char*,char**,int(*)(const char*,const char*)); + +/* +** Compilers are getting increasingly pedantic about type conversions +** as C evolves ever closer to Ada.... To work around the latest problems +** we have to define the following variant of strlen(). +*/ +#define lemonStrlen(X) ((int)strlen(X)) + +/* a few forward declarations... */ +struct rule; +struct lemon; +struct action; + +static struct action *Action_new(void); +static struct action *Action_sort(struct action *); + +/********** From the file "build.h" ************************************/ +void FindRulePrecedences(); +void FindFirstSets(); +void FindStates(); +void FindLinks(); +void FindFollowSets(); +void FindActions(); + +/********* From the file "configlist.h" *********************************/ +void Configlist_init(void); +struct config *Configlist_add(struct rule *, int); +struct config *Configlist_addbasis(struct rule *, int); +void Configlist_closure(struct lemon *); +void Configlist_sort(void); +void Configlist_sortbasis(void); +struct config *Configlist_return(void); +struct config *Configlist_basis(void); +void Configlist_eat(struct config *); +void Configlist_reset(void); + +/********* From the file "error.h" ***************************************/ +void ErrorMsg(const char *, int,const char *, ...); + +/****** From the file "option.h" ******************************************/ +enum option_type { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR, + OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR}; +struct s_options { + enum option_type type; + const char *label; + char *arg; + const char *message; +}; +int OptInit(char**,struct s_options*,FILE*); +int OptNArgs(void); +char *OptArg(int); +void OptErr(int); +void OptPrint(void); + +/******** From the file "parse.h" *****************************************/ +void Parse(struct lemon *lemp); + +/********* From the file "plink.h" ***************************************/ +struct plink *Plink_new(void); +void Plink_add(struct plink **, struct config *); +void Plink_copy(struct plink **, struct plink *); +void Plink_delete(struct plink *); + +/********** From the file "report.h" *************************************/ +void Reprint(struct lemon *); +void ReportOutput(struct lemon *); +void ReportTable(struct lemon *, int); +void ReportHeader(struct lemon *); +void CompressTables(struct lemon *); +void ResortStates(struct lemon *); + +/********** From the file "set.h" ****************************************/ +void SetSize(int); /* All sets will be of size N */ +char *SetNew(void); /* A new set for element 0..N */ +void SetFree(char*); /* Deallocate a set */ +int SetAdd(char*,int); /* Add element to a set */ +int SetUnion(char *,char *); /* A <- A U B, thru element N */ +#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */ + +/********** From the file "struct.h" *************************************/ +/* +** Principal data structures for the LEMON parser generator. +*/ + +typedef enum {LEMON_FALSE=0, LEMON_TRUE} Boolean; + +/* Symbols (terminals and nonterminals) of the grammar are stored +** in the following: */ +enum symbol_type { + TERMINAL, + NONTERMINAL, + MULTITERMINAL +}; +enum e_assoc { + LEFT, + RIGHT, + NONE, + UNK +}; +struct symbol { + const char *name; /* Name of the symbol */ + int index; /* Index number for this symbol */ + enum symbol_type type; /* Symbols are all either TERMINALS or NTs */ + struct rule *rule; /* Linked list of rules of this (if an NT) */ + struct symbol *fallback; /* fallback token in case this token doesn't parse */ + int prec; /* Precedence if defined (-1 otherwise) */ + enum e_assoc assoc; /* Associativity if precedence is defined */ + char *firstset; /* First-set for all rules of this symbol */ + Boolean lambda; /* True if NT and can generate an empty string */ + int useCnt; /* Number of times used */ + char *destructor; /* Code which executes whenever this symbol is + ** popped from the stack during error processing */ + int destLineno; /* Line number for start of destructor */ + char *datatype; /* The data type of information held by this + ** object. Only used if type==NONTERMINAL */ + int dtnum; /* The data type number. In the parser, the value + ** stack is a union. The .yy%d element of this + ** union is the correct data type for this object */ + /* The following fields are used by MULTITERMINALs only */ + int nsubsym; /* Number of constituent symbols in the MULTI */ + struct symbol **subsym; /* Array of constituent symbols */ +}; + +/* Each production rule in the grammar is stored in the following +** structure. */ +struct rule { + struct symbol *lhs; /* Left-hand side of the rule */ + const char *lhsalias; /* Alias for the LHS (NULL if none) */ + int lhsStart; /* True if left-hand side is the start symbol */ + int ruleline; /* Line number for the rule */ + int nrhs; /* Number of RHS symbols */ + struct symbol **rhs; /* The RHS symbols */ + const char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ + int line; /* Line number at which code begins */ + const char *code; /* The code executed when this rule is reduced */ + struct symbol *precsym; /* Precedence symbol for this rule */ + int index; /* An index number for this rule */ + Boolean canReduce; /* True if this rule is ever reduced */ + struct rule *nextlhs; /* Next rule with the same LHS */ + struct rule *next; /* Next rule in the global list */ +}; + +/* A configuration is a production rule of the grammar together with +** a mark (dot) showing how much of that rule has been processed so far. +** Configurations also contain a follow-set which is a list of terminal +** symbols which are allowed to immediately follow the end of the rule. +** Every configuration is recorded as an instance of the following: */ +enum cfgstatus { + COMPLETE, + INCOMPLETE +}; +struct config { + struct rule *rp; /* The rule upon which the configuration is based */ + int dot; /* The parse point */ + char *fws; /* Follow-set for this configuration only */ + struct plink *fplp; /* Follow-set forward propagation links */ + struct plink *bplp; /* Follow-set backwards propagation links */ + struct state *stp; /* Pointer to state which contains this */ + enum cfgstatus status; /* used during followset and shift computations */ + struct config *next; /* Next configuration in the state */ + struct config *bp; /* The next basis configuration */ +}; + +enum e_action { + SHIFT, + ACCEPT, + REDUCE, + ERROR, + SSCONFLICT, /* A shift/shift conflict */ + SRCONFLICT, /* Was a reduce, but part of a conflict */ + RRCONFLICT, /* Was a reduce, but part of a conflict */ + SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ + RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ + NOT_USED /* Deleted by compression */ +}; + +/* Every shift or reduce operation is stored as one of the following */ +struct action { + struct symbol *sp; /* The look-ahead symbol */ + enum e_action type; + union { + struct state *stp; /* The new state, if a shift */ + struct rule *rp; /* The rule, if a reduce */ + } x; + struct action *next; /* Next action for this state */ + struct action *collide; /* Next action with the same hash */ +}; + +/* Each state of the generated parser's finite state machine +** is encoded as an instance of the following structure. */ +struct state { + struct config *bp; /* The basis configurations for this state */ + struct config *cfp; /* All configurations in this set */ + int statenum; /* Sequential number for this state */ + struct action *ap; /* Array of actions for this state */ + int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */ + int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */ + int iDflt; /* Default action */ +}; +#define NO_OFFSET (-2147483647) + +/* A followset propagation link indicates that the contents of one +** configuration followset should be propagated to another whenever +** the first changes. */ +struct plink { + struct config *cfp; /* The configuration to which linked */ + struct plink *next; /* The next propagate link */ +}; + +/* The state vector for the entire parser generator is recorded as +** follows. (LEMON uses no global variables and makes little use of +** static variables. Fields in the following structure can be thought +** of as begin global variables in the program.) */ +struct lemon { + struct state **sorted; /* Table of states sorted by state number */ + struct rule *rule; /* List of all rules */ + int nstate; /* Number of states */ + int nrule; /* Number of rules */ + int nsymbol; /* Number of terminal and nonterminal symbols */ + int nterminal; /* Number of terminal symbols */ + struct symbol **symbols; /* Sorted array of pointers to symbols */ + int errorcnt; /* Number of errors */ + struct symbol *errsym; /* The error symbol */ + struct symbol *wildcard; /* Token that matches anything */ + char *name; /* Name of the generated parser */ + char *arg; /* Declaration of the 3th argument to parser */ + char *tokentype; /* Type of terminal symbols in the parser stack */ + char *vartype; /* The default type of non-terminal symbols */ + char *start; /* Name of the start symbol for the grammar */ + char *stacksize; /* Size of the parser stack */ + char *include; /* Code to put at the start of the C file */ + char *error; /* Code to execute when an error is seen */ + char *overflow; /* Code to execute on a stack overflow */ + char *failure; /* Code to execute on parser failure */ + char *accept; /* Code to execute when the parser excepts */ + char *extracode; /* Code appended to the generated file */ + char *tokendest; /* Code to execute to destroy token data */ + char *vardest; /* Code for the default non-terminal destructor */ + char *filename; /* Name of the input file */ + char *outname; /* Name of the current output file */ + char *tokenprefix; /* A prefix added to token names in the .h file */ + int nconflict; /* Number of parsing conflicts */ + int tablesize; /* Size of the parse tables */ + int basisflag; /* Print only basis configurations */ + int has_fallback; /* True if any %fallback is seen in the grammar */ + int nolinenosflag; /* True if #line statements should not be printed */ + char *argv0; /* Name of the program */ +}; + +#define MemoryCheck(X) if((X)==0){ \ + extern void memory_error(); \ + memory_error(); \ +} + +/**************** From the file "table.h" *********************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ +/* Routines for handling a strings */ + +const char *Strsafe(const char *); + +void Strsafe_init(void); +int Strsafe_insert(const char *); +const char *Strsafe_find(const char *); + +/* Routines for handling symbols of the grammar */ + +struct symbol *Symbol_new(const char *); +int Symbolcmpp(const void *, const void *); +void Symbol_init(void); +int Symbol_insert(struct symbol *, const char *); +struct symbol *Symbol_find(const char *); +struct symbol *Symbol_Nth(int); +int Symbol_count(void); +struct symbol **Symbol_arrayof(void); + +/* Routines to manage the state table */ + +int Configcmp(const char *, const char *); +struct state *State_new(void); +void State_init(void); +int State_insert(struct state *, struct config *); +struct state *State_find(struct config *); +struct state **State_arrayof(/* */); + +/* Routines used for efficiency in Configlist_add */ + +void Configtable_init(void); +int Configtable_insert(struct config *); +struct config *Configtable_find(struct config *); +void Configtable_clear(int(*)(struct config *)); + +/****************** From the file "action.c" *******************************/ +/* +** Routines processing parser actions in the LEMON parser generator. +*/ + +/* Allocate a new parser action */ +static struct action *Action_new(void){ + static struct action *freelist = 0; + struct action *newaction; + + if( freelist==0 ){ + int i; + int amt = 100; + freelist = (struct action *)calloc(amt, sizeof(struct action)); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new parser action."); + exit(1); + } + for(i=0; inext; + return newaction; +} + +/* Compare two actions for sorting purposes. Return negative, zero, or +** positive if the first action is less than, equal to, or greater than +** the first +*/ +static int actioncmp( + struct action *ap1, + struct action *ap2 +){ + int rc; + rc = ap1->sp->index - ap2->sp->index; + if( rc==0 ){ + rc = (int)ap1->type - (int)ap2->type; + } + if( rc==0 && ap1->type==REDUCE ){ + rc = ap1->x.rp->index - ap2->x.rp->index; + } + if( rc==0 ){ + rc = (int) (ap2 - ap1); + } + return rc; +} + +/* Sort parser actions */ +static struct action *Action_sort( + struct action *ap +){ + ap = (struct action *)msort((char *)ap,(char **)&ap->next, + (int(*)(const char*,const char*))actioncmp); + return ap; +} + +void Action_add( + struct action **app, + enum e_action type, + struct symbol *sp, + char *arg +){ + struct action *newaction; + newaction = Action_new(); + newaction->next = *app; + *app = newaction; + newaction->type = type; + newaction->sp = sp; + if( type==SHIFT ){ + newaction->x.stp = (struct state *)arg; + }else{ + newaction->x.rp = (struct rule *)arg; + } +} +/********************** New code to implement the "acttab" module ***********/ +/* +** This module implements routines use to construct the yy_action[] table. +*/ + +/* +** The state of the yy_action table under construction is an instance of +** the following structure. +** +** The yy_action table maps the pair (state_number, lookahead) into an +** action_number. The table is an array of integers pairs. The state_number +** determines an initial offset into the yy_action array. The lookahead +** value is then added to this initial offset to get an index X into the +** yy_action array. If the aAction[X].lookahead equals the value of the +** of the lookahead input, then the value of the action_number output is +** aAction[X].action. If the lookaheads do not match then the +** default action for the state_number is returned. +** +** All actions associated with a single state_number are first entered +** into aLookahead[] using multiple calls to acttab_action(). Then the +** actions for that single state_number are placed into the aAction[] +** array with a single call to acttab_insert(). The acttab_insert() call +** also resets the aLookahead[] array in preparation for the next +** state number. +*/ +struct lookahead_action { + int lookahead; /* Value of the lookahead token */ + int action; /* Action to take on the given lookahead */ +}; +typedef struct acttab acttab; +struct acttab { + int nAction; /* Number of used slots in aAction[] */ + int nActionAlloc; /* Slots allocated for aAction[] */ + struct lookahead_action + *aAction, /* The yy_action[] table under construction */ + *aLookahead; /* A single new transaction set */ + int mnLookahead; /* Minimum aLookahead[].lookahead */ + int mnAction; /* Action associated with mnLookahead */ + int mxLookahead; /* Maximum aLookahead[].lookahead */ + int nLookahead; /* Used slots in aLookahead[] */ + int nLookaheadAlloc; /* Slots allocated in aLookahead[] */ +}; + +/* Return the number of entries in the yy_action table */ +#define acttab_size(X) ((X)->nAction) + +/* The value for the N-th entry in yy_action */ +#define acttab_yyaction(X,N) ((X)->aAction[N].action) + +/* The value for the N-th entry in yy_lookahead */ +#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) + +/* Free all memory associated with the given acttab */ +void acttab_free(acttab *p){ + free( p->aAction ); + free( p->aLookahead ); + free( p ); +} + +/* Allocate a new acttab structure */ +acttab *acttab_alloc(void){ + acttab *p = (acttab *) calloc( 1, sizeof(*p) ); + if( p==0 ){ + fprintf(stderr,"Unable to allocate memory for a new acttab."); + exit(1); + } + memset(p, 0, sizeof(*p)); + return p; +} + +/* Add a new action to the current transaction set. +** +** This routine is called once for each lookahead for a particular +** state. +*/ +void acttab_action(acttab *p, int lookahead, int action){ + if( p->nLookahead>=p->nLookaheadAlloc ){ + p->nLookaheadAlloc += 25; + p->aLookahead = (struct lookahead_action *) realloc( p->aLookahead, + sizeof(p->aLookahead[0])*p->nLookaheadAlloc ); + if( p->aLookahead==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } + if( p->nLookahead==0 ){ + p->mxLookahead = lookahead; + p->mnLookahead = lookahead; + p->mnAction = action; + }else{ + if( p->mxLookaheadmxLookahead = lookahead; + if( p->mnLookahead>lookahead ){ + p->mnLookahead = lookahead; + p->mnAction = action; + } + } + p->aLookahead[p->nLookahead].lookahead = lookahead; + p->aLookahead[p->nLookahead].action = action; + p->nLookahead++; +} + +/* +** Add the transaction set built up with prior calls to acttab_action() +** into the current action table. Then reset the transaction set back +** to an empty set in preparation for a new round of acttab_action() calls. +** +** Return the offset into the action table of the new transaction. +*/ +int acttab_insert(acttab *p){ + int i, j, k, n; + assert( p->nLookahead>0 ); + + /* Make sure we have enough space to hold the expanded action table + ** in the worst case. The worst case occurs if the transaction set + ** must be appended to the current action table + */ + n = p->mxLookahead + 1; + if( p->nAction + n >= p->nActionAlloc ){ + int oldAlloc = p->nActionAlloc; + p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; + p->aAction = (struct lookahead_action *) realloc( p->aAction, + sizeof(p->aAction[0])*p->nActionAlloc); + if( p->aAction==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=oldAlloc; inActionAlloc; i++){ + p->aAction[i].lookahead = -1; + p->aAction[i].action = -1; + } + } + + /* Scan the existing action table looking for an offset that is a + ** duplicate of the current transaction set. Fall out of the loop + ** if and when the duplicate is found. + ** + ** i is the index in p->aAction[] where p->mnLookahead is inserted. + */ + for(i=p->nAction-1; i>=0; i--){ + if( p->aAction[i].lookahead==p->mnLookahead ){ + /* All lookaheads and actions in the aLookahead[] transaction + ** must match against the candidate aAction[i] entry. */ + if( p->aAction[i].action!=p->mnAction ) continue; + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 || k>=p->nAction ) break; + if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break; + if( p->aLookahead[j].action!=p->aAction[k].action ) break; + } + if( jnLookahead ) continue; + + /* No possible lookahead value that is not in the aLookahead[] + ** transaction is allowed to match aAction[i] */ + n = 0; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead<0 ) continue; + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++; + } + if( n==p->nLookahead ){ + break; /* An exact match is found at offset i */ + } + } + } + + /* If no existing offsets exactly match the current transaction, find an + ** an empty offset in the aAction[] table in which we can add the + ** aLookahead[] transaction. + */ + if( i<0 ){ + /* Look for holes in the aAction[] table that fit the current + ** aLookahead[] transaction. Leave i set to the offset of the hole. + ** If no holes are found, i is left at p->nAction, which means the + ** transaction will be appended. */ + for(i=0; inActionAlloc - p->mxLookahead; i++){ + if( p->aAction[i].lookahead<0 ){ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 ) break; + if( p->aAction[k].lookahead>=0 ) break; + } + if( jnLookahead ) continue; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; + } + if( j==p->nAction ){ + break; /* Fits in empty slots */ + } + } + } + } + /* Insert transaction set at index i. */ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + p->aAction[k] = p->aLookahead[j]; + if( k>=p->nAction ) p->nAction = k+1; + } + p->nLookahead = 0; + + /* Return the offset that is added to the lookahead in order to get the + ** index into yy_action of the action */ + return i - p->mnLookahead; +} + +/********************** From the file "build.c" *****************************/ +/* +** Routines to construction the finite state machine for the LEMON +** parser generator. +*/ + +/* Find a precedence symbol of every rule in the grammar. +** +** Those rules which have a precedence symbol coded in the input +** grammar using the "[symbol]" construct will already have the +** rp->precsym field filled. Other rules take as their precedence +** symbol the first RHS symbol with a defined precedence. If there +** are not RHS symbols with a defined precedence, the precedence +** symbol field is left blank. +*/ +void FindRulePrecedences(struct lemon *xp) +{ + struct rule *rp; + for(rp=xp->rule; rp; rp=rp->next){ + if( rp->precsym==0 ){ + int i, j; + for(i=0; inrhs && rp->precsym==0; i++){ + struct symbol *sp = rp->rhs[i]; + if( sp->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + if( sp->subsym[j]->prec>=0 ){ + rp->precsym = sp->subsym[j]; + break; + } + } + }else if( sp->prec>=0 ){ + rp->precsym = rp->rhs[i]; + } + } + } + } + return; +} + +/* Find all nonterminals which will generate the empty string. +** Then go back and compute the first sets of every nonterminal. +** The first set is the set of all terminal symbols which can begin +** a string generated by that nonterminal. +*/ +void FindFirstSets(struct lemon *lemp) +{ + int i, j; + struct rule *rp; + int progress; + + for(i=0; insymbol; i++){ + lemp->symbols[i]->lambda = LEMON_FALSE; + } + for(i=lemp->nterminal; insymbol; i++){ + lemp->symbols[i]->firstset = SetNew(); + } + + /* First compute all lambdas */ + do{ + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->lhs->lambda ) continue; + for(i=0; inrhs; i++){ + struct symbol *sp = rp->rhs[i]; + assert( sp->type==NONTERMINAL || sp->lambda==LEMON_FALSE ); + if( sp->lambda==LEMON_FALSE ) break; + } + if( i==rp->nrhs ){ + rp->lhs->lambda = LEMON_TRUE; + progress = 1; + } + } + }while( progress ); + + /* Now compute all first sets */ + do{ + struct symbol *s1, *s2; + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + s1 = rp->lhs; + for(i=0; inrhs; i++){ + s2 = rp->rhs[i]; + if( s2->type==TERMINAL ){ + progress += SetAdd(s1->firstset,s2->index); + break; + }else if( s2->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + progress += SetAdd(s1->firstset,s2->subsym[j]->index); + } + break; + }else if( s1==s2 ){ + if( s1->lambda==LEMON_FALSE ) break; + }else{ + progress += SetUnion(s1->firstset,s2->firstset); + if( s2->lambda==LEMON_FALSE ) break; + } + } + } + }while( progress ); + return; +} + +/* Compute all LR(0) states for the grammar. Links +** are added to between some states so that the LR(1) follow sets +** can be computed later. +*/ +PRIVATE struct state *getstate(struct lemon *); /* forward reference */ +void FindStates(struct lemon *lemp) +{ + struct symbol *sp; + struct rule *rp; + + Configlist_init(); + + /* Find the start symbol */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ){ + ErrorMsg(lemp->filename,0, +"The specified start symbol \"%s\" is not \ +in a nonterminal of the grammar. \"%s\" will be used as the start \ +symbol instead.",lemp->start,lemp->rule->lhs->name); + lemp->errorcnt++; + sp = lemp->rule->lhs; + } + }else{ + sp = lemp->rule->lhs; + } + + /* Make sure the start symbol doesn't occur on the right-hand side of + ** any rule. Report an error if it does. (YACC would generate a new + ** start symbol in this case.) */ + for(rp=lemp->rule; rp; rp=rp->next){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]==sp ){ /* FIX ME: Deal with multiterminals */ + ErrorMsg(lemp->filename,0, +"The start symbol \"%s\" occurs on the \ +right-hand side of a rule. This will result in a parser which \ +does not work properly.",sp->name); + lemp->errorcnt++; + } + } + } + + /* The basis configuration set for the first state + ** is all rules which have the start symbol as their + ** left-hand side */ + for(rp=sp->rule; rp; rp=rp->nextlhs){ + struct config *newcfp; + rp->lhsStart = 1; + newcfp = Configlist_addbasis(rp,0); + SetAdd(newcfp->fws,0); + } + + /* Compute the first state. All other states will be + ** computed automatically during the computation of the first one. + ** The returned pointer to the first state is not used. */ + (void)getstate(lemp); + return; +} + +/* Return a pointer to a state which is described by the configuration +** list which has been built from calls to Configlist_add. +*/ +PRIVATE void buildshifts(struct lemon *, struct state *); /* Forwd ref */ +PRIVATE struct state *getstate(struct lemon *lemp) +{ + struct config *cfp, *bp; + struct state *stp; + + /* Extract the sorted basis of the new state. The basis was constructed + ** by prior calls to "Configlist_addbasis()". */ + Configlist_sortbasis(); + bp = Configlist_basis(); + + /* Get a state with the same basis */ + stp = State_find(bp); + if( stp ){ + /* A state with the same basis already exists! Copy all the follow-set + ** propagation links from the state under construction into the + ** preexisting state, then return a pointer to the preexisting state */ + struct config *x, *y; + for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){ + Plink_copy(&y->bplp,x->bplp); + Plink_delete(x->fplp); + x->fplp = x->bplp = 0; + } + cfp = Configlist_return(); + Configlist_eat(cfp); + }else{ + /* This really is a new state. Construct all the details */ + Configlist_closure(lemp); /* Compute the configuration closure */ + Configlist_sort(); /* Sort the configuration closure */ + cfp = Configlist_return(); /* Get a pointer to the config list */ + stp = State_new(); /* A new state structure */ + MemoryCheck(stp); + stp->bp = bp; /* Remember the configuration basis */ + stp->cfp = cfp; /* Remember the configuration closure */ + stp->statenum = lemp->nstate++; /* Every state gets a sequence number */ + stp->ap = 0; /* No actions, yet. */ + State_insert(stp,stp->bp); /* Add to the state table */ + buildshifts(lemp,stp); /* Recursively compute successor states */ + } + return stp; +} + +/* +** Return true if two symbols are the same. +*/ +int same_symbol(struct symbol *a, struct symbol *b) +{ + int i; + if( a==b ) return 1; + if( a->type!=MULTITERMINAL ) return 0; + if( b->type!=MULTITERMINAL ) return 0; + if( a->nsubsym!=b->nsubsym ) return 0; + for(i=0; insubsym; i++){ + if( a->subsym[i]!=b->subsym[i] ) return 0; + } + return 1; +} + +/* Construct all successor states to the given state. A "successor" +** state is any state which can be reached by a shift action. +*/ +PRIVATE void buildshifts(struct lemon *lemp, struct state *stp) +{ + struct config *cfp; /* For looping thru the config closure of "stp" */ + struct config *bcfp; /* For the inner loop on config closure of "stp" */ + struct config *newcfg; /* */ + struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ + struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ + struct state *newstp; /* A pointer to a successor state */ + + /* Each configuration becomes complete after it contibutes to a successor + ** state. Initially, all configurations are incomplete */ + for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE; + + /* Loop through all configurations of the state "stp" */ + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */ + if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */ + Configlist_reset(); /* Reset the new config set */ + sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ + + /* For every configuration in the state "stp" which has the symbol "sp" + ** following its dot, add the same configuration to the basis set under + ** construction but with the dot shifted one symbol to the right. */ + for(bcfp=cfp; bcfp; bcfp=bcfp->next){ + if( bcfp->status==COMPLETE ) continue; /* Already used */ + if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ + bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ + if( !same_symbol(bsp,sp) ) continue; /* Must be same as for "cfp" */ + bcfp->status = COMPLETE; /* Mark this config as used */ + newcfg = Configlist_addbasis(bcfp->rp,bcfp->dot+1); + Plink_add(&newcfg->bplp,bcfp); + } + + /* Get a pointer to the state described by the basis configuration set + ** constructed in the preceding loop */ + newstp = getstate(lemp); + + /* The state "newstp" is reached from the state "stp" by a shift action + ** on the symbol "sp" */ + if( sp->type==MULTITERMINAL ){ + int i; + for(i=0; insubsym; i++){ + Action_add(&stp->ap,SHIFT,sp->subsym[i],(char*)newstp); + } + }else{ + Action_add(&stp->ap,SHIFT,sp,(char *)newstp); + } + } +} + +/* +** Construct the propagation links +*/ +void FindLinks(struct lemon *lemp) +{ + int i; + struct config *cfp, *other; + struct state *stp; + struct plink *plp; + + /* Housekeeping detail: + ** Add to every propagate link a pointer back to the state to + ** which the link is attached. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + cfp->stp = stp; + } + } + + /* Convert all backlinks into forward links. Only the forward + ** links are used in the follow-set computation. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + for(plp=cfp->bplp; plp; plp=plp->next){ + other = plp->cfp; + Plink_add(&other->fplp,cfp); + } + } + } +} + +/* Compute all followsets. +** +** A followset is the set of all symbols which can come immediately +** after a configuration. +*/ +void FindFollowSets(struct lemon *lemp) +{ + int i; + struct config *cfp; + struct plink *plp; + int progress; + int change; + + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + cfp->status = INCOMPLETE; + } + } + + do{ + progress = 0; + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; + for(plp=cfp->fplp; plp; plp=plp->next){ + change = SetUnion(plp->cfp->fws,cfp->fws); + if( change ){ + plp->cfp->status = INCOMPLETE; + progress = 1; + } + } + cfp->status = COMPLETE; + } + } + }while( progress ); +} + +static int resolve_conflict(struct action *,struct action *); + +/* Compute the reduce actions, and resolve conflicts. +*/ +void FindActions(struct lemon *lemp) +{ + int i,j; + struct config *cfp; + struct state *stp; + struct symbol *sp; + struct rule *rp; + + /* Add all of the reduce actions + ** A reduce action is added for each element of the followset of + ** a configuration which has its dot at the extreme right. + */ + for(i=0; instate; i++){ /* Loop over all states */ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ + if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ + for(j=0; jnterminal; j++){ + if( SetFind(cfp->fws,j) ){ + /* Add a reduce action to the state "stp" which will reduce by the + ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ + Action_add(&stp->ap,REDUCE,lemp->symbols[j],(char *)cfp->rp); + } + } + } + } + } + + /* Add the accepting token */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ) sp = lemp->rule->lhs; + }else{ + sp = lemp->rule->lhs; + } + /* Add to the first state (which is always the starting state of the + ** finite state machine) an action to ACCEPT if the lookahead is the + ** start nonterminal. */ + Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0); + + /* Resolve conflicts */ + for(i=0; instate; i++){ + struct action *ap, *nap; + struct state *stp; + stp = lemp->sorted[i]; + /* assert( stp->ap ); */ + stp->ap = Action_sort(stp->ap); + for(ap=stp->ap; ap && ap->next; ap=ap->next){ + for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ + /* The two actions "ap" and "nap" have the same lookahead. + ** Figure out which one should be used */ + lemp->nconflict += resolve_conflict(ap,nap); + } + } + } + + /* Report an error for each rule that can never be reduced. */ + for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = LEMON_FALSE; + for(i=0; instate; i++){ + struct action *ap; + for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ + if( ap->type==REDUCE ) ap->x.rp->canReduce = LEMON_TRUE; + } + } + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->canReduce ) continue; + ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n"); + lemp->errorcnt++; + } +} + +/* Resolve a conflict between the two given actions. If the +** conflict can't be resolved, return non-zero. +** +** NO LONGER TRUE: +** To resolve a conflict, first look to see if either action +** is on an error rule. In that case, take the action which +** is not associated with the error rule. If neither or both +** actions are associated with an error rule, then try to +** use precedence to resolve the conflict. +** +** If either action is a SHIFT, then it must be apx. This +** function won't work if apx->type==REDUCE and apy->type==SHIFT. +*/ +static int resolve_conflict( + struct action *apx, + struct action *apy +){ + struct symbol *spx, *spy; + int errcnt = 0; + assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ + if( apx->type==SHIFT && apy->type==SHIFT ){ + apy->type = SSCONFLICT; + errcnt++; + } + if( apx->type==SHIFT && apy->type==REDUCE ){ + spx = apx->sp; + spy = apy->x.rp->precsym; + if( spy==0 || spx->prec<0 || spy->prec<0 ){ + /* Not enough precedence information. */ + apy->type = SRCONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ /* higher precedence wins */ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = SH_RESOLVED; + }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ + apy->type = RD_RESOLVED; /* associativity */ + }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ + apx->type = SH_RESOLVED; + }else{ + assert( spx->prec==spy->prec && spx->assoc==NONE ); + apy->type = SRCONFLICT; + errcnt++; + } + }else if( apx->type==REDUCE && apy->type==REDUCE ){ + spx = apx->x.rp->precsym; + spy = apy->x.rp->precsym; + if( spx==0 || spy==0 || spx->prec<0 || + spy->prec<0 || spx->prec==spy->prec ){ + apy->type = RRCONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = RD_RESOLVED; + } + }else{ + assert( + apx->type==SH_RESOLVED || + apx->type==RD_RESOLVED || + apx->type==SSCONFLICT || + apx->type==SRCONFLICT || + apx->type==RRCONFLICT || + apy->type==SH_RESOLVED || + apy->type==RD_RESOLVED || + apy->type==SSCONFLICT || + apy->type==SRCONFLICT || + apy->type==RRCONFLICT + ); + /* The REDUCE/SHIFT case cannot happen because SHIFTs come before + ** REDUCEs on the list. If we reach this point it must be because + ** the parser conflict had already been resolved. */ + } + return errcnt; +} +/********************* From the file "configlist.c" *************************/ +/* +** Routines to processing a configuration list and building a state +** in the LEMON parser generator. +*/ + +static struct config *freelist = 0; /* List of free configurations */ +static struct config *current = 0; /* Top of list of configurations */ +static struct config **currentend = 0; /* Last on list of configs */ +static struct config *basis = 0; /* Top of list of basis configs */ +static struct config **basisend = 0; /* End of list of basis configs */ + +/* Return a pointer to a new configuration */ +PRIVATE struct config *newconfig(){ + struct config *newcfg; + if( freelist==0 ){ + int i; + int amt = 3; + freelist = (struct config *)calloc( amt, sizeof(struct config) ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new configuration."); + exit(1); + } + for(i=0; inext; + return newcfg; +} + +/* The configuration "old" is no longer used */ +PRIVATE void deleteconfig(struct config *old) +{ + old->next = freelist; + freelist = old; +} + +/* Initialized the configuration list builder */ +void Configlist_init(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_clear(0); + return; +} + +/* Add another configuration to the configuration list */ +struct config *Configlist_add( + struct rule *rp, /* The rule */ + int dot /* Index into the RHS of the rule where the dot goes */ +){ + struct config *cfp, model; + + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + Configtable_insert(cfp); + } + return cfp; +} + +/* Add a basis configuration to the configuration list */ +struct config *Configlist_addbasis(struct rule *rp, int dot) +{ + struct config *cfp, model; + + assert( basisend!=0 ); + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + *basisend = cfp; + basisend = &cfp->bp; + Configtable_insert(cfp); + } + return cfp; +} + +/* Compute the closure of the configuration list */ +void Configlist_closure(struct lemon *lemp) +{ + struct config *cfp, *newcfp; + struct rule *rp, *newrp; + struct symbol *sp, *xsp; + int i, dot; + + assert( currentend!=0 ); + for(cfp=current; cfp; cfp=cfp->next){ + rp = cfp->rp; + dot = cfp->dot; + if( dot>=rp->nrhs ) continue; + sp = rp->rhs[dot]; + if( sp->type==NONTERMINAL ){ + if( sp->rule==0 && sp!=lemp->errsym ){ + ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.", + sp->name); + lemp->errorcnt++; + } + for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){ + newcfp = Configlist_add(newrp,0); + for(i=dot+1; inrhs; i++){ + xsp = rp->rhs[i]; + if( xsp->type==TERMINAL ){ + SetAdd(newcfp->fws,xsp->index); + break; + }else if( xsp->type==MULTITERMINAL ){ + int k; + for(k=0; knsubsym; k++){ + SetAdd(newcfp->fws, xsp->subsym[k]->index); + } + break; + }else{ + SetUnion(newcfp->fws,xsp->firstset); + if( xsp->lambda==LEMON_FALSE ) break; + } + } + if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); + } + } + } + return; +} + +/* Sort the configuration list */ +void Configlist_sort(){ + current = (struct config *)msort((char *)current,(char **)&(current->next),Configcmp); + currentend = 0; + return; +} + +/* Sort the basis configuration list */ +void Configlist_sortbasis(){ + basis = (struct config *)msort((char *)current,(char **)&(current->bp),Configcmp); + basisend = 0; + return; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_return(){ + struct config *old; + old = current; + current = 0; + currentend = 0; + return old; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_basis(){ + struct config *old; + old = basis; + basis = 0; + basisend = 0; + return old; +} + +/* Free all elements of the given configuration list */ +void Configlist_eat(struct config *cfp) +{ + struct config *nextcfp; + for(; cfp; cfp=nextcfp){ + nextcfp = cfp->next; + assert( cfp->fplp==0 ); + assert( cfp->bplp==0 ); + if( cfp->fws ) SetFree(cfp->fws); + deleteconfig(cfp); + } + return; +} +/***************** From the file "error.c" *********************************/ +/* +** Code for printing error message. +*/ + +void ErrorMsg(const char *filename, int lineno, const char *format, ...){ + va_list ap; + fprintf(stderr, "%s:%d: ", filename, lineno); + va_start(ap, format); + vfprintf(stderr,format,ap); + va_end(ap); + fprintf(stderr, "\n"); +} +/**************** From the file "main.c" ************************************/ +/* +** Main program file for the LEMON parser generator. +*/ + +/* Report an out-of-memory condition and abort. This function +** is used mostly by the "MemoryCheck" macro in struct.h +*/ +void memory_error(){ + fprintf(stderr,"Out of memory. Aborting...\n"); + exit(1); +} + +static int nDefine = 0; /* Number of -D options on the command line */ +static char **azDefine = 0; /* Name of the -D macros */ + +/* This routine is called with the argument to each -D command-line option. +** Add the macro defined to the azDefine array. +*/ +static void handle_D_option(char *z){ + char **paz; + nDefine++; + azDefine = (char **) realloc(azDefine, sizeof(azDefine[0])*nDefine); + if( azDefine==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + paz = &azDefine[nDefine-1]; + *paz = (char *) malloc( lemonStrlen(z)+1 ); + if( *paz==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + strcpy(*paz, z); + for(z=*paz; *z && *z!='='; z++){} + *z = 0; +} + +static char *user_templatename = NULL; +static void handle_T_option(char *z){ + user_templatename = (char *) malloc( lemonStrlen(z)+1 ); + if( user_templatename==0 ){ + memory_error(); + } + strcpy(user_templatename, z); +} + +/* The main program. Parse the command line and do it... */ +int main(int argc, char **argv) +{ + static int version = 0; + static int rpflag = 0; + static int basisflag = 0; + static int compress = 0; + static int quiet = 0; + static int statistics = 0; + static int mhflag = 0; + static int nolinenosflag = 0; + static int noResort = 0; + static struct s_options options[] = { + {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, + {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, + {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, + {OPT_FSTR, "T", (char*)handle_T_option, "Specify a template file."}, + {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, + {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file."}, + {OPT_FLAG, "l", (char*)&nolinenosflag, "Do not print #line statements."}, + {OPT_FLAG, "p", (char*)&showPrecedenceConflict, + "Show conflicts resolved by precedence rules"}, + {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, + {OPT_FLAG, "r", (char*)&noResort, "Do not sort or renumber states"}, + {OPT_FLAG, "s", (char*)&statistics, + "Print parser stats to standard output."}, + {OPT_FLAG, "x", (char*)&version, "Print the version number."}, + {OPT_FLAG,0,0,0} + }; + int i; + int exitcode; + struct lemon lem; + + OptInit(argv,options,stderr); + if( version ){ + printf("Lemon version 1.0\n"); + exit(0); + } + if( OptNArgs()!=1 ){ + fprintf(stderr,"Exactly one filename argument is required.\n"); + exit(1); + } + memset(&lem, 0, sizeof(lem)); + lem.errorcnt = 0; + + /* Initialize the machine */ + Strsafe_init(); + Symbol_init(); + State_init(); + lem.argv0 = argv[0]; + lem.filename = OptArg(0); + lem.basisflag = basisflag; + lem.nolinenosflag = nolinenosflag; + Symbol_new("$"); + lem.errsym = Symbol_new("error"); + lem.errsym->useCnt = 0; + + /* Parse the input file */ + Parse(&lem); + if( lem.errorcnt ) exit(lem.errorcnt); + if( lem.nrule==0 ){ + fprintf(stderr,"Empty grammar.\n"); + exit(1); + } + + /* Count and index the symbols of the grammar */ + lem.nsymbol = Symbol_count(); + Symbol_new("{default}"); + lem.symbols = Symbol_arrayof(); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), Symbolcmpp); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + for(i=1; isupper(lem.symbols[i]->name[0]); i++); + lem.nterminal = i; + + /* Generate a reprint of the grammar, if requested on the command line */ + if( rpflag ){ + Reprint(&lem); + }else{ + /* Initialize the size for all follow and first sets */ + SetSize(lem.nterminal+1); + + /* Find the precedence for every production rule (that has one) */ + FindRulePrecedences(&lem); + + /* Compute the lambda-nonterminals and the first-sets for every + ** nonterminal */ + FindFirstSets(&lem); + + /* Compute all LR(0) states. Also record follow-set propagation + ** links so that the follow-set can be computed later */ + lem.nstate = 0; + FindStates(&lem); + lem.sorted = State_arrayof(); + + /* Tie up loose ends on the propagation links */ + FindLinks(&lem); + + /* Compute the follow set of every reducible configuration */ + FindFollowSets(&lem); + + /* Compute the action tables */ + FindActions(&lem); + + /* Compress the action tables */ + if( compress==0 ) CompressTables(&lem); + + /* Reorder and renumber the states so that states with fewer choices + ** occur at the end. This is an optimization that helps make the + ** generated parser tables smaller. */ + if( noResort==0 ) ResortStates(&lem); + + /* Generate a report of the parser generated. (the "y.output" file) */ + if( !quiet ) ReportOutput(&lem); + + /* Generate the source code for the parser */ + ReportTable(&lem, mhflag); + + /* Produce a header file for use by the scanner. (This step is + ** omitted if the "-m" option is used because makeheaders will + ** generate the file for us.) */ + if( !mhflag ) ReportHeader(&lem); + } + if( statistics ){ + printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n", + lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule); + printf(" %d states, %d parser table entries, %d conflicts\n", + lem.nstate, lem.tablesize, lem.nconflict); + } + if( lem.nconflict > 0 ){ + fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); + } + + /* return 0 on success, 1 on failure. */ + exitcode = ((lem.errorcnt > 0) || (lem.nconflict > 0)) ? 1 : 0; + exit(exitcode); + return (exitcode); +} +/******************** From the file "msort.c" *******************************/ +/* +** A generic merge-sort program. +** +** USAGE: +** Let "ptr" be a pointer to some structure which is at the head of +** a null-terminated list. Then to sort the list call: +** +** ptr = msort(ptr,&(ptr->next),cmpfnc); +** +** In the above, "cmpfnc" is a pointer to a function which compares +** two instances of the structure and returns an integer, as in +** strcmp. The second argument is a pointer to the pointer to the +** second element of the linked list. This address is used to compute +** the offset to the "next" field within the structure. The offset to +** the "next" field must be constant for all structures in the list. +** +** The function returns a new pointer which is the head of the list +** after sorting. +** +** ALGORITHM: +** Merge-sort. +*/ + +/* +** Return a pointer to the next structure in the linked list. +*/ +#define NEXT(A) (*(char**)(((char*)A)+offset)) + +/* +** Inputs: +** a: A sorted, null-terminated linked list. (May be null). +** b: A sorted, null-terminated linked list. (May be null). +** cmp: A pointer to the comparison function. +** offset: Offset in the structure to the "next" field. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** of both a and b. +** +** Side effects: +** The "next" pointers for elements in the lists a and b are +** changed. +*/ +static char *merge( + char *a, + char *b, + int (*cmp)(const char*,const char*), + int offset +){ + char *ptr, *head; + + if( a==0 ){ + head = b; + }else if( b==0 ){ + head = a; + }else{ + if( (*cmp)(a,b)<=0 ){ + ptr = a; + a = NEXT(a); + }else{ + ptr = b; + b = NEXT(b); + } + head = ptr; + while( a && b ){ + if( (*cmp)(a,b)<=0 ){ + NEXT(ptr) = a; + ptr = a; + a = NEXT(a); + }else{ + NEXT(ptr) = b; + ptr = b; + b = NEXT(b); + } + } + if( a ) NEXT(ptr) = a; + else NEXT(ptr) = b; + } + return head; +} + +/* +** Inputs: +** list: Pointer to a singly-linked list of structures. +** next: Pointer to pointer to the second element of the list. +** cmp: A comparison function. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** orginally in list. +** +** Side effects: +** The "next" pointers for elements in list are changed. +*/ +#define LISTSIZE 30 +static char *msort( + char *list, + char **next, + int (*cmp)(const char*,const char*) +){ + unsigned long offset; + char *ep; + char *set[LISTSIZE]; + int i; + offset = (unsigned long)next - (unsigned long)list; + for(i=0; istate = WAITING_FOR_DECL_KEYWORD; + }else if( islower(x[0]) ){ + psp->lhs = Symbol_new(x); + psp->nrhs = 0; + psp->lhsalias = 0; + psp->state = WAITING_FOR_ARROW; + }else if( x[0]=='{' ){ + if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"There is no prior rule upon which to attach the code \ +fragment which begins on this line."); + psp->errorcnt++; + }else if( psp->prevrule->code!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Code fragment beginning on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->line = psp->tokenlineno; + psp->prevrule->code = &x[1]; + } + }else if( x[0]=='[' ){ + psp->state = PRECEDENCE_MARK_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Token \"%s\" should be either \"%%\" or a nonterminal name.", + x); + psp->errorcnt++; + } + break; + case PRECEDENCE_MARK_1: + if( !isupper(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The precedence symbol must be a terminal."); + psp->errorcnt++; + }else if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "There is no prior rule to assign precedence \"[%s]\".",x); + psp->errorcnt++; + }else if( psp->prevrule->precsym!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Precedence mark on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->precsym = Symbol_new(x); + } + psp->state = PRECEDENCE_MARK_2; + break; + case PRECEDENCE_MARK_2: + if( x[0]!=']' ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"]\" on precedence mark."); + psp->errorcnt++; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + break; + case WAITING_FOR_ARROW: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else if( x[0]=='(' ){ + psp->state = LHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Expected to see a \":\" following the LHS symbol \"%s\".", + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->lhsalias = x; + psp->state = LHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the LHS \"%s\"\n", + x,psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = LHS_ALIAS_3; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_3: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"->\" following: \"%s(%s)\".", + psp->lhs->name,psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case IN_RHS: + if( x[0]=='.' ){ + struct rule *rp; + rp = (struct rule *)calloc( sizeof(struct rule) + + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1); + if( rp==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't allocate enough memory for this rule."); + psp->errorcnt++; + psp->prevrule = 0; + }else{ + int i; + rp->ruleline = psp->tokenlineno; + rp->rhs = (struct symbol**)&rp[1]; + rp->rhsalias = (const char**)&(rp->rhs[psp->nrhs]); + for(i=0; inrhs; i++){ + rp->rhs[i] = psp->rhs[i]; + rp->rhsalias[i] = psp->alias[i]; + } + rp->lhs = psp->lhs; + rp->lhsalias = psp->lhsalias; + rp->nrhs = psp->nrhs; + rp->code = 0; + rp->precsym = 0; + rp->index = psp->gp->nrule++; + rp->nextlhs = rp->lhs->rule; + rp->lhs->rule = rp; + rp->next = 0; + if( psp->firstrule==0 ){ + psp->firstrule = psp->lastrule = rp; + }else{ + psp->lastrule->next = rp; + psp->lastrule = rp; + } + psp->prevrule = rp; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isalpha(x[0]) ){ + if( psp->nrhs>=MAXRHS ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Too many symbols on RHS of rule beginning at \"%s\".", + x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + }else{ + psp->rhs[psp->nrhs] = Symbol_new(x); + psp->alias[psp->nrhs] = 0; + psp->nrhs++; + } + }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 ){ + struct symbol *msp = psp->rhs[psp->nrhs-1]; + if( msp->type!=MULTITERMINAL ){ + struct symbol *origsp = msp; + msp = (struct symbol *) calloc(1,sizeof(*msp)); + memset(msp, 0, sizeof(*msp)); + msp->type = MULTITERMINAL; + msp->nsubsym = 1; + msp->subsym = (struct symbol **) calloc(1,sizeof(struct symbol*)); + msp->subsym[0] = origsp; + msp->name = origsp->name; + psp->rhs[psp->nrhs-1] = msp; + } + msp->nsubsym++; + msp->subsym = (struct symbol **) realloc(msp->subsym, + sizeof(struct symbol*)*msp->nsubsym); + msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]); + if( islower(x[1]) || islower(msp->subsym[0]->name[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Cannot form a compound containing a non-terminal"); + psp->errorcnt++; + } + }else if( x[0]=='(' && psp->nrhs>0 ){ + psp->state = RHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal character on RHS of rule: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->alias[psp->nrhs-1] = x; + psp->state = RHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", + x,psp->rhs[psp->nrhs-1]->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case WAITING_FOR_DECL_KEYWORD: + if( isalpha(x[0]) ){ + psp->declkeyword = x; + psp->declargslot = 0; + psp->decllinenoslot = 0; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + if( strcmp(x,"name")==0 ){ + psp->declargslot = &(psp->gp->name); + psp->insertLineMacro = 0; + }else if( strcmp(x,"include")==0 ){ + psp->declargslot = &(psp->gp->include); + }else if( strcmp(x,"code")==0 ){ + psp->declargslot = &(psp->gp->extracode); + }else if( strcmp(x,"token_destructor")==0 ){ + psp->declargslot = &psp->gp->tokendest; + }else if( strcmp(x,"default_destructor")==0 ){ + psp->declargslot = &psp->gp->vardest; + }else if( strcmp(x,"token_prefix")==0 ){ + psp->declargslot = &psp->gp->tokenprefix; + psp->insertLineMacro = 0; + }else if( strcmp(x,"syntax_error")==0 ){ + psp->declargslot = &(psp->gp->error); + }else if( strcmp(x,"parse_accept")==0 ){ + psp->declargslot = &(psp->gp->accept); + }else if( strcmp(x,"parse_failure")==0 ){ + psp->declargslot = &(psp->gp->failure); + }else if( strcmp(x,"stack_overflow")==0 ){ + psp->declargslot = &(psp->gp->overflow); + }else if( strcmp(x,"extra_argument")==0 ){ + psp->declargslot = &(psp->gp->arg); + psp->insertLineMacro = 0; + }else if( strcmp(x,"token_type")==0 ){ + psp->declargslot = &(psp->gp->tokentype); + psp->insertLineMacro = 0; + }else if( strcmp(x,"default_type")==0 ){ + psp->declargslot = &(psp->gp->vartype); + psp->insertLineMacro = 0; + }else if( strcmp(x,"stack_size")==0 ){ + psp->declargslot = &(psp->gp->stacksize); + psp->insertLineMacro = 0; + }else if( strcmp(x,"start_symbol")==0 ){ + psp->declargslot = &(psp->gp->start); + psp->insertLineMacro = 0; + }else if( strcmp(x,"left")==0 ){ + psp->preccounter++; + psp->declassoc = LEFT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"right")==0 ){ + psp->preccounter++; + psp->declassoc = RIGHT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"nonassoc")==0 ){ + psp->preccounter++; + psp->declassoc = NONE; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"destructor")==0 ){ + psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; + }else if( strcmp(x,"type")==0 ){ + psp->state = WAITING_FOR_DATATYPE_SYMBOL; + }else if( strcmp(x,"fallback")==0 ){ + psp->fallback = 0; + psp->state = WAITING_FOR_FALLBACK_ID; + }else if( strcmp(x,"wildcard")==0 ){ + psp->state = WAITING_FOR_WILDCARD_ID; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Unknown declaration keyword: \"%%%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal declaration keyword: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_DESTRUCTOR_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %%destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->destructor; + psp->decllinenoslot = &sp->destLineno; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_DATATYPE_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %%type keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_find(x); + if((sp) && (sp->datatype)){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol %%type \"%s\" already defined", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + if (!sp){ + sp = Symbol_new(x); + } + psp->declargslot = &sp->datatype; + psp->insertLineMacro = 0; + psp->state = WAITING_FOR_DECL_ARG; + } + } + break; + case WAITING_FOR_PRECEDENCE_SYMBOL: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isupper(x[0]) ){ + struct symbol *sp; + sp = Symbol_new(x); + if( sp->prec>=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol \"%s\" has already be given a precedence.",x); + psp->errorcnt++; + }else{ + sp->prec = psp->preccounter; + sp->assoc = psp->declassoc; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't assign a precedence to \"%s\".",x); + psp->errorcnt++; + } + break; + case WAITING_FOR_DECL_ARG: + if( x[0]=='{' || x[0]=='\"' || isalnum(x[0]) ){ + const char *zOld, *zNew; + char *zBuf, *z; + int nOld, n, nLine, nNew, nBack; + int addLineMacro; + char zLine[50]; + zNew = x; + if( zNew[0]=='"' || zNew[0]=='{' ) zNew++; + nNew = lemonStrlen(zNew); + if( *psp->declargslot ){ + zOld = *psp->declargslot; + }else{ + zOld = ""; + } + nOld = lemonStrlen(zOld); + n = nOld + nNew + 20; + addLineMacro = !psp->gp->nolinenosflag && psp->insertLineMacro && + (psp->decllinenoslot==0 || psp->decllinenoslot[0]!=0); + if( addLineMacro ){ + for(z=psp->filename, nBack=0; *z; z++){ + if( *z=='\\' ) nBack++; + } + sprintf(zLine, "#line %d ", psp->tokenlineno); + nLine = lemonStrlen(zLine); + n += nLine + lemonStrlen(psp->filename) + nBack; + } + *psp->declargslot = (char *) realloc(*psp->declargslot, n); + zBuf = *psp->declargslot + nOld; + if( addLineMacro ){ + if( nOld && zBuf[-1]!='\n' ){ + *(zBuf++) = '\n'; + } + memcpy(zBuf, zLine, nLine); + zBuf += nLine; + *(zBuf++) = '"'; + for(z=psp->filename; *z; z++){ + if( *z=='\\' ){ + *(zBuf++) = '\\'; + } + *(zBuf++) = *z; + } + *(zBuf++) = '"'; + *(zBuf++) = '\n'; + } + if( psp->decllinenoslot && psp->decllinenoslot[0]==0 ){ + psp->decllinenoslot[0] = psp->tokenlineno; + } + memcpy(zBuf, zNew, nNew); + zBuf += nNew; + *zBuf = 0; + psp->state = WAITING_FOR_DECL_OR_RULE; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal argument to %%%s: %s",psp->declkeyword,x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_FALLBACK_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%fallback argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->fallback==0 ){ + psp->fallback = sp; + }else if( sp->fallback ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "More than one fallback assigned to token %s", x); + psp->errorcnt++; + }else{ + sp->fallback = psp->fallback; + psp->gp->has_fallback = 1; + } + } + break; + case WAITING_FOR_WILDCARD_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%wildcard argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->gp->wildcard==0 ){ + psp->gp->wildcard = sp; + }else{ + ErrorMsg(psp->filename, psp->tokenlineno, + "Extra wildcard to token: %s", x); + psp->errorcnt++; + } + } + break; + case RESYNC_AFTER_RULE_ERROR: +/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; +** break; */ + case RESYNC_AFTER_DECL_ERROR: + if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; + if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD; + break; + } +} + +/* Run the preprocessor over the input file text. The global variables +** azDefine[0] through azDefine[nDefine-1] contains the names of all defined +** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and +** comments them out. Text in between is also commented out as appropriate. +*/ +static void preprocess_input(char *z){ + int i, j, k, n; + int exclude = 0; + int start = 0; + int lineno = 1; + int start_lineno = 1; + for(i=0; z[i]; i++){ + if( z[i]=='\n' ) lineno++; + if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue; + if( strncmp(&z[i],"%endif",6)==0 && isspace(z[i+6]) ){ + if( exclude ){ + exclude--; + if( exclude==0 ){ + for(j=start; jfilename; + ps.errorcnt = 0; + ps.state = INITIALIZE; + + /* Begin by reading the input file */ + fp = fopen(ps.filename,"rb"); + if( fp==0 ){ + ErrorMsg(ps.filename,0,"Can't open this file for reading."); + gp->errorcnt++; + return; + } + fseek(fp,0,2); + filesize = ftell(fp); + rewind(fp); + filebuf = (char *)malloc( filesize+1 ); + if( filebuf==0 ){ + ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", + filesize+1); + gp->errorcnt++; + fclose(fp); + return; + } + if( fread(filebuf,1,filesize,fp)!=filesize ){ + ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", + filesize); + free(filebuf); + gp->errorcnt++; + fclose(fp); + return; + } + fclose(fp); + filebuf[filesize] = 0; + + /* Make an initial pass through the file to handle %ifdef and %ifndef */ + preprocess_input(filebuf); + + /* Now scan the text of the input file */ + lineno = 1; + for(cp=filebuf; (c= *cp)!=0; ){ + if( c=='\n' ) lineno++; /* Keep track of the line number */ + if( isspace(c) ){ cp++; continue; } /* Skip all white space */ + if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */ + cp+=2; + while( (c= *cp)!=0 && c!='\n' ) cp++; + continue; + } + if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */ + cp+=2; + while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c ) cp++; + continue; + } + ps.tokenstart = cp; /* Mark the beginning of the token */ + ps.tokenlineno = lineno; /* Linenumber on which token begins */ + if( c=='\"' ){ /* String literals */ + cp++; + while( (c= *cp)!=0 && c!='\"' ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c==0 ){ + ErrorMsg(ps.filename,startline, +"String starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( c=='{' ){ /* A block of C code */ + int level; + cp++; + for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){ + if( c=='\n' ) lineno++; + else if( c=='{' ) level++; + else if( c=='}' ) level--; + else if( c=='/' && cp[1]=='*' ){ /* Skip comments */ + int prevc; + cp = &cp[2]; + prevc = 0; + while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){ + if( c=='\n' ) lineno++; + prevc = c; + cp++; + } + }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */ + cp = &cp[2]; + while( (c= *cp)!=0 && c!='\n' ) cp++; + if( c ) lineno++; + }else if( c=='\'' || c=='\"' ){ /* String a character literals */ + int startchar, prevc; + startchar = c; + prevc = 0; + for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){ + if( c=='\n' ) lineno++; + if( prevc=='\\' ) prevc = 0; + else prevc = c; + } + } + } + if( c==0 ){ + ErrorMsg(ps.filename,ps.tokenlineno, +"C code starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( isalnum(c) ){ /* Identifiers */ + while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ + cp += 3; + nextcp = cp; + }else if( (c=='/' || c=='|') && isalpha(cp[1]) ){ + cp += 2; + while( (c = *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else{ /* All other (one character) operators */ + cp++; + nextcp = cp; + } + c = *cp; + *cp = 0; /* Null terminate the token */ + parseonetoken(&ps); /* Parse the token */ + *cp = c; /* Restore the buffer */ + cp = nextcp; + } + free(filebuf); /* Release the buffer after parsing */ + gp->rule = ps.firstrule; + gp->errorcnt = ps.errorcnt; +} +/*************************** From the file "plink.c" *********************/ +/* +** Routines processing configuration follow-set propagation links +** in the LEMON parser generator. +*/ +static struct plink *plink_freelist = 0; + +/* Allocate a new plink */ +struct plink *Plink_new(){ + struct plink *newlink; + + if( plink_freelist==0 ){ + int i; + int amt = 100; + plink_freelist = (struct plink *)calloc( amt, sizeof(struct plink) ); + if( plink_freelist==0 ){ + fprintf(stderr, + "Unable to allocate memory for a new follow-set propagation link.\n"); + exit(1); + } + for(i=0; inext; + return newlink; +} + +/* Add a plink to a plink list */ +void Plink_add(struct plink **plpp, struct config *cfp) +{ + struct plink *newlink; + newlink = Plink_new(); + newlink->next = *plpp; + *plpp = newlink; + newlink->cfp = cfp; +} + +/* Transfer every plink on the list "from" to the list "to" */ +void Plink_copy(struct plink **to, struct plink *from) +{ + struct plink *nextpl; + while( from ){ + nextpl = from->next; + from->next = *to; + *to = from; + from = nextpl; + } +} + +/* Delete every plink on the list */ +void Plink_delete(struct plink *plp) +{ + struct plink *nextpl; + + while( plp ){ + nextpl = plp->next; + plp->next = plink_freelist; + plink_freelist = plp; + plp = nextpl; + } +} +/*********************** From the file "report.c" **************************/ +/* +** Procedures for generating reports and tables in the LEMON parser generator. +*/ + +/* Generate a filename with the given suffix. Space to hold the +** name comes from malloc() and must be freed by the calling +** function. +*/ +PRIVATE char *file_makename(struct lemon *lemp, const char *suffix) +{ + char *name; + char *cp; + + name = (char*)malloc( lemonStrlen(lemp->filename) + lemonStrlen(suffix) + 5 ); + if( name==0 ){ + fprintf(stderr,"Can't allocate space for a filename.\n"); + exit(1); + } + strcpy(name,lemp->filename); + cp = strrchr(name,'.'); + if( cp ) *cp = 0; + strcat(name,suffix); + return name; +} + +/* Open a file with a name based on the name of the input file, +** but with a different (specified) suffix, and return a pointer +** to the stream */ +PRIVATE FILE *file_open( + struct lemon *lemp, + const char *suffix, + const char *mode +){ + FILE *fp; + + if( lemp->outname ) free(lemp->outname); + lemp->outname = file_makename(lemp, suffix); + fp = fopen(lemp->outname,mode); + if( fp==0 && *mode=='w' ){ + fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); + lemp->errorcnt++; + return 0; + } + return fp; +} + +/* Duplicate the input file without comments and without actions +** on rules */ +void Reprint(struct lemon *lemp) +{ + struct rule *rp; + struct symbol *sp; + int i, j, maxlen, len, ncolumns, skip; + printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); + maxlen = 10; + for(i=0; insymbol; i++){ + sp = lemp->symbols[i]; + len = lemonStrlen(sp->name); + if( len>maxlen ) maxlen = len; + } + ncolumns = 76/(maxlen+5); + if( ncolumns<1 ) ncolumns = 1; + skip = (lemp->nsymbol + ncolumns - 1)/ncolumns; + for(i=0; insymbol; j+=skip){ + sp = lemp->symbols[j]; + assert( sp->index==j ); + printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name); + } + printf("\n"); + } + for(rp=lemp->rule; rp; rp=rp->next){ + printf("%s",rp->lhs->name); + /* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ + printf(" ::="); + for(i=0; inrhs; i++){ + sp = rp->rhs[i]; + printf(" %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + printf("|%s", sp->subsym[j]->name); + } + } + /* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ + } + printf("."); + if( rp->precsym ) printf(" [%s]",rp->precsym->name); + /* if( rp->code ) printf("\n %s",rp->code); */ + printf("\n"); + } +} + +void ConfigPrint(FILE *fp, struct config *cfp) +{ + struct rule *rp; + struct symbol *sp; + int i, j; + rp = cfp->rp; + fprintf(fp,"%s ::=",rp->lhs->name); + for(i=0; i<=rp->nrhs; i++){ + if( i==cfp->dot ) fprintf(fp," *"); + if( i==rp->nrhs ) break; + sp = rp->rhs[i]; + fprintf(fp," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + fprintf(fp,"|%s",sp->subsym[j]->name); + } + } + } +} + +/* #define TEST */ +#if 0 +/* Print a set */ +PRIVATE void SetPrint(out,set,lemp) +FILE *out; +char *set; +struct lemon *lemp; +{ + int i; + char *spacer; + spacer = ""; + fprintf(out,"%12s[",""); + for(i=0; interminal; i++){ + if( SetFind(set,i) ){ + fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); + spacer = " "; + } + } + fprintf(out,"]\n"); +} + +/* Print a plink chain */ +PRIVATE void PlinkPrint(out,plp,tag) +FILE *out; +struct plink *plp; +char *tag; +{ + while( plp ){ + fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->statenum); + ConfigPrint(out,plp->cfp); + fprintf(out,"\n"); + plp = plp->next; + } +} +#endif + +/* Print an action to the given file descriptor. Return FALSE if +** nothing was actually printed. +*/ +int PrintAction(struct action *ap, FILE *fp, int indent){ + int result = 1; + switch( ap->type ){ + case SHIFT: + fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->statenum); + break; + case REDUCE: + fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index); + break; + case ACCEPT: + fprintf(fp,"%*s accept",indent,ap->sp->name); + break; + case ERROR: + fprintf(fp,"%*s error",indent,ap->sp->name); + break; + case SRCONFLICT: + case RRCONFLICT: + fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.rp->index); + break; + case SSCONFLICT: + fprintf(fp,"%*s shift %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.stp->statenum); + break; + case SH_RESOLVED: + if( showPrecedenceConflict ){ + fprintf(fp,"%*s shift %-3d -- dropped by precedence", + indent,ap->sp->name,ap->x.stp->statenum); + }else{ + result = 0; + } + break; + case RD_RESOLVED: + if( showPrecedenceConflict ){ + fprintf(fp,"%*s reduce %-3d -- dropped by precedence", + indent,ap->sp->name,ap->x.rp->index); + }else{ + result = 0; + } + break; + case NOT_USED: + result = 0; + break; + } + return result; +} + +/* Generate the "y.output" log file */ +void ReportOutput(struct lemon *lemp) +{ + int i; + struct state *stp; + struct config *cfp; + struct action *ap; + FILE *fp; + + fp = file_open(lemp,".out","wb"); + if( fp==0 ) return; + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + fprintf(fp,"State %d:\n",stp->statenum); + if( lemp->basisflag ) cfp=stp->bp; + else cfp=stp->cfp; + while( cfp ){ + char buf[20]; + if( cfp->dot==cfp->rp->nrhs ){ + sprintf(buf,"(%d)",cfp->rp->index); + fprintf(fp," %5s ",buf); + }else{ + fprintf(fp," "); + } + ConfigPrint(fp,cfp); + fprintf(fp,"\n"); +#if 0 + SetPrint(fp,cfp->fws,lemp); + PlinkPrint(fp,cfp->fplp,"To "); + PlinkPrint(fp,cfp->bplp,"From"); +#endif + if( lemp->basisflag ) cfp=cfp->bp; + else cfp=cfp->next; + } + fprintf(fp,"\n"); + for(ap=stp->ap; ap; ap=ap->next){ + if( PrintAction(ap,fp,30) ) fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + } + fprintf(fp, "----------------------------------------------------\n"); + fprintf(fp, "Symbols:\n"); + for(i=0; insymbol; i++){ + int j; + struct symbol *sp; + + sp = lemp->symbols[i]; + fprintf(fp, " %3d: %s", i, sp->name); + if( sp->type==NONTERMINAL ){ + fprintf(fp, ":"); + if( sp->lambda ){ + fprintf(fp, " "); + } + for(j=0; jnterminal; j++){ + if( sp->firstset && SetFind(sp->firstset, j) ){ + fprintf(fp, " %s", lemp->symbols[j]->name); + } + } + } + fprintf(fp, "\n"); + } + fclose(fp); + return; +} + +/* Search for the file "name" which is in the same directory as +** the exacutable */ +PRIVATE char *pathsearch(char *argv0, char *name, int modemask) +{ + const char *pathlist; + char *pathbufptr; + char *pathbuf; + char *path,*cp; + char c; + +#ifdef __WIN32__ + cp = strrchr(argv0,'\\'); +#else + cp = strrchr(argv0,'/'); +#endif + if( cp ){ + c = *cp; + *cp = 0; + path = (char *)malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 ); + if( path ) sprintf(path,"%s/%s",argv0,name); + *cp = c; + }else{ + pathlist = getenv("PATH"); + if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; + pathbuf = (char *) malloc( lemonStrlen(pathlist) + 1 ); + path = (char *)malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 ); + if( (pathbuf != 0) && (path!=0) ){ + pathbufptr = pathbuf; + strcpy(pathbuf, pathlist); + while( *pathbuf ){ + cp = strchr(pathbuf,':'); + if( cp==0 ) cp = &pathbuf[lemonStrlen(pathbuf)]; + c = *cp; + *cp = 0; + sprintf(path,"%s/%s",pathbuf,name); + *cp = c; + if( c==0 ) pathbuf[0] = 0; + else pathbuf = &cp[1]; + if( access(path,modemask)==0 ) break; + } + free(pathbufptr); + } + } + return path; +} + +/* Given an action, compute the integer value for that action +** which is to be put in the action table of the generated machine. +** Return negative if no action should be generated. +*/ +PRIVATE int compute_action(struct lemon *lemp, struct action *ap) +{ + int act; + switch( ap->type ){ + case SHIFT: act = ap->x.stp->statenum; break; + case REDUCE: act = ap->x.rp->index + lemp->nstate; break; + case ERROR: act = lemp->nstate + lemp->nrule; break; + case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break; + default: act = -1; break; + } + return act; +} + +#define LINESIZE 1000 +/* The next cluster of routines are for reading the template file +** and writing the results to the generated parser */ +/* The first function transfers data from "in" to "out" until +** a line is seen which begins with "%%". The line number is +** tracked. +** +** if name!=0, then any word that begin with "Parse" is changed to +** begin with *name instead. +*/ +PRIVATE void tplt_xfer(char *name, FILE *in, FILE *out, int *lineno) +{ + int i, iStart; + char line[LINESIZE]; + while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ + (*lineno)++; + iStart = 0; + if( name ){ + for(i=0; line[i]; i++){ + if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0 + && (i==0 || !isalpha(line[i-1])) + ){ + if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]); + fprintf(out,"%s",name); + i += 4; + iStart = i+1; + } + } + } + fprintf(out,"%s",&line[iStart]); + } +} + +/* The next function finds the template file and opens it, returning +** a pointer to the opened file. */ +PRIVATE FILE *tplt_open(struct lemon *lemp) +{ + static char templatename[] = "lempar.c"; + char buf[1000]; + FILE *in; + char *tpltname; + char *cp; + + /* first, see if user specified a template filename on the command line. */ + if (user_templatename != 0) { + if( access(user_templatename,004)==-1 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + user_templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(user_templatename,"rb"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",user_templatename); + lemp->errorcnt++; + return 0; + } + return in; + } + + cp = strrchr(lemp->filename,'.'); + if( cp ){ + sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); + }else{ + sprintf(buf,"%s.lt",lemp->filename); + } + if( access(buf,004)==0 ){ + tpltname = buf; + }else if( access(templatename,004)==0 ){ + tpltname = templatename; + }else{ + tpltname = pathsearch(lemp->argv0,templatename,0); + } + if( tpltname==0 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(tpltname,"rb"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",templatename); + lemp->errorcnt++; + return 0; + } + return in; +} + +/* Print a #line directive line to the output file. */ +PRIVATE void tplt_linedir(FILE *out, int lineno, char *filename) +{ + fprintf(out,"#line %d \"",lineno); + while( *filename ){ + if( *filename == '\\' ) putc('\\',out); + putc(*filename,out); + filename++; + } + fprintf(out,"\"\n"); +} + +/* Print a string to the file and keep the linenumber up to date */ +PRIVATE void tplt_print(FILE *out, struct lemon *lemp, char *str, int *lineno) +{ + if( str==0 ) return; + while( *str ){ + putc(*str,out); + if( *str=='\n' ) (*lineno)++; + str++; + } + if( str[-1]!='\n' ){ + putc('\n',out); + (*lineno)++; + } + if (!lemp->nolinenosflag) { + (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); + } + return; +} + +/* +** The following routine emits code for the destructor for the +** symbol sp +*/ +void emit_destructor_code( + FILE *out, + struct symbol *sp, + struct lemon *lemp, + int *lineno +){ + char *cp = 0; + + if( sp->type==TERMINAL ){ + cp = lemp->tokendest; + if( cp==0 ) return; + fprintf(out,"{\n"); (*lineno)++; + }else if( sp->destructor ){ + cp = sp->destructor; + fprintf(out,"{\n"); (*lineno)++; + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,sp->destLineno,lemp->filename); } + }else if( lemp->vardest ){ + cp = lemp->vardest; + if( cp==0 ) return; + fprintf(out,"{\n"); (*lineno)++; + }else{ + assert( 0 ); /* Cannot happen */ + } + for(; *cp; cp++){ + if( *cp=='$' && cp[1]=='$' ){ + fprintf(out,"(yypminor->yy%d)",sp->dtnum); + cp++; + continue; + } + if( *cp=='\n' ) (*lineno)++; + fputc(*cp,out); + } + fprintf(out,"\n"); (*lineno)++; + if (!lemp->nolinenosflag) { + (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); + } + fprintf(out,"}\n"); (*lineno)++; + return; +} + +/* +** Return TRUE (non-zero) if the given symbol has a destructor. +*/ +int has_destructor(struct symbol *sp, struct lemon *lemp) +{ + int ret; + if( sp->type==TERMINAL ){ + ret = lemp->tokendest!=0; + }else{ + ret = lemp->vardest!=0 || sp->destructor!=0; + } + return ret; +} + +/* +** Append text to a dynamically allocated string. If zText is 0 then +** reset the string to be empty again. Always return the complete text +** of the string (which is overwritten with each call). +** +** n bytes of zText are stored. If n==0 then all of zText up to the first +** \000 terminator is stored. zText can contain up to two instances of +** %d. The values of p1 and p2 are written into the first and second +** %d. +** +** If n==-1, then the previous character is overwritten. +*/ +PRIVATE char *append_str(const char *zText, int n, int p1, int p2){ + static char empty[1] = { 0 }; + static char *z = 0; + static int alloced = 0; + static int used = 0; + int c; + char zInt[40]; + if( zText==0 ){ + used = 0; + return z; + } + if( n<=0 ){ + if( n<0 ){ + used += n; + assert( used>=0 ); + } + n = lemonStrlen(zText); + } + if( (int) (n+sizeof(zInt)*2+used) >= alloced ){ + alloced = n + sizeof(zInt)*2 + used + 200; + z = (char *) realloc(z, alloced); + } + if( z==0 ) return empty; + while( n-- > 0 ){ + c = *(zText++); + if( c=='%' && n>0 && zText[0]=='d' ){ + sprintf(zInt, "%d", p1); + p1 = p2; + strcpy(&z[used], zInt); + used += lemonStrlen(&z[used]); + zText++; + n--; + }else{ + z[used++] = c; + } + } + z[used] = 0; + return z; +} + +/* +** zCode is a string that is the action associated with a rule. Expand +** the symbols in this string so that the refer to elements of the parser +** stack. +*/ +PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ + char *cp, *xp; + int i; + char lhsused = 0; /* True if the LHS element has been used */ + char used[MAXRHS]; /* True for each RHS element which is used */ + + for(i=0; inrhs; i++) used[i] = 0; + lhsused = 0; + + if( rp->code==0 ){ + static char newlinestr[2] = { '\n', '\0' }; + rp->code = newlinestr; + rp->line = rp->ruleline; + } + + append_str(0,0,0,0); + + /* This const cast is wrong but harmless, if we're careful. */ + for(cp=(char *)rp->code; *cp; cp++){ + if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){ + char saved; + for(xp= &cp[1]; isalnum(*xp) || *xp=='_'; xp++); + saved = *xp; + *xp = 0; + if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ + append_str("yygotominor.yy%d",0,rp->lhs->dtnum,0); + cp = xp; + lhsused = 1; + }else{ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ + if( cp!=rp->code && cp[-1]=='@' ){ + /* If the argument is of the form @X then substituted + ** the token number of X, not the value of X */ + append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0); + }else{ + struct symbol *sp = rp->rhs[i]; + int dtnum; + if( sp->type==MULTITERMINAL ){ + dtnum = sp->subsym[0]->dtnum; + }else{ + dtnum = sp->dtnum; + } + append_str("yymsp[%d].minor.yy%d",0,i-rp->nrhs+1, dtnum); + } + cp = xp; + used[i] = 1; + break; + } + } + } + *xp = saved; + } + append_str(cp, 1, 0, 0); + } /* End loop */ + + /* Check to make sure the LHS has been used */ + if( rp->lhsalias && !lhsused ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label \"%s\" for \"%s(%s)\" is never used.", + rp->lhsalias,rp->lhs->name,rp->lhsalias); + lemp->errorcnt++; + } + + /* Generate destructor code for RHS symbols which are not used in the + ** reduce code */ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && !used[i] ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s for \"%s(%s)\" is never used.", + rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); + lemp->errorcnt++; + }else if( rp->rhsalias[i]==0 ){ + if( has_destructor(rp->rhs[i],lemp) ){ + append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, + rp->rhs[i]->index,i-rp->nrhs+1); + }else{ + /* No destructor defined for this term */ + } + } + } + if( rp->code ){ + cp = append_str(0,0,0,0); + rp->code = Strsafe(cp?cp:""); + } +} + +/* +** Generate code which executes when the rule "rp" is reduced. Write +** the code to "out". Make sure lineno stays up-to-date. +*/ +PRIVATE void emit_code( + FILE *out, + struct rule *rp, + struct lemon *lemp, + int *lineno +){ + const char *cp; + + /* Generate code to do the reduce action */ + if( rp->code ){ + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,rp->line,lemp->filename); } + fprintf(out,"{%s",rp->code); + for(cp=rp->code; *cp; cp++){ + if( *cp=='\n' ) (*lineno)++; + } /* End loop */ + fprintf(out,"}\n"); (*lineno)++; + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); } + } /* End if( rp->code ) */ + + return; +} + +/* +** Print the definition of the union used for the parser's data stack. +** This union contains fields for every possible data type for tokens +** and nonterminals. In the process of computing and printing this +** union, also set the ".dtnum" field of every terminal and nonterminal +** symbol. +*/ +void print_stack_union( + FILE *out, /* The output stream */ + struct lemon *lemp, /* The main info structure for this parser */ + int *plineno, /* Pointer to the line number */ + int mhflag /* True if generating makeheaders output */ +){ + int lineno = *plineno; /* The line number of the output */ + char **types; /* A hash table of datatypes */ + int arraysize; /* Size of the "types" array */ + int maxdtlength; /* Maximum length of any ".datatype" field. */ + char *stddt; /* Standardized name for a datatype */ + int i,j; /* Loop counters */ + int hash; /* For hashing the name of a type */ + const char *name; /* Name of the parser */ + + /* Allocate and initialize types[] and allocate stddt[] */ + arraysize = lemp->nsymbol * 2; + types = (char**)calloc( arraysize, sizeof(char*) ); + if( types==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + for(i=0; ivartype ){ + maxdtlength = lemonStrlen(lemp->vartype); + } + for(i=0; insymbol; i++){ + int len; + struct symbol *sp = lemp->symbols[i]; + if( sp->datatype==0 ) continue; + len = lemonStrlen(sp->datatype); + if( len>maxdtlength ) maxdtlength = len; + } + stddt = (char*)malloc( maxdtlength*2 + 1 ); + if( stddt==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + + /* Build a hash table of datatypes. The ".dtnum" field of each symbol + ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is + ** used for terminal symbols. If there is no %default_type defined then + ** 0 is also used as the .dtnum value for nonterminals which do not specify + ** a datatype using the %type directive. + */ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + char *cp; + if( sp==lemp->errsym ){ + sp->dtnum = arraysize+1; + continue; + } + if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){ + sp->dtnum = 0; + continue; + } + cp = sp->datatype; + if( cp==0 ) cp = lemp->vartype; + j = 0; + while( isspace(*cp) ) cp++; + while( *cp ) stddt[j++] = *cp++; + while( j>0 && isspace(stddt[j-1]) ) j--; + stddt[j] = 0; + if( lemp->tokentype && strcmp(stddt, lemp->tokentype)==0 ){ + sp->dtnum = 0; + continue; + } + hash = 0; + for(j=0; stddt[j]; j++){ + hash = hash*53 + stddt[j]; + } + hash = (hash & 0x7fffffff)%arraysize; + while( types[hash] ){ + if( strcmp(types[hash],stddt)==0 ){ + sp->dtnum = hash + 1; + break; + } + hash++; + if( hash>=arraysize ) hash = 0; + } + if( types[hash]==0 ){ + sp->dtnum = hash + 1; + types[hash] = (char*)malloc( lemonStrlen(stddt)+1 ); + if( types[hash]==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + strcpy(types[hash],stddt); + } + } + + /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ + name = lemp->name ? lemp->name : "Parse"; + lineno = *plineno; + if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; } + fprintf(out,"#define %sTOKENTYPE %s\n",name, + lemp->tokentype?lemp->tokentype:"void*"); lineno++; + if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } + fprintf(out,"typedef union {\n"); lineno++; + fprintf(out," int yyinit;\n"); lineno++; + fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; + for(i=0; ierrsym->useCnt ){ + fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++; + } + free(stddt); + free(types); + fprintf(out,"} YYMINORTYPE;\n"); lineno++; + *plineno = lineno; +} + +/* +** Return the name of a C datatype able to represent values between +** lwr and upr, inclusive. +*/ +static const char *minimum_size_type(int lwr, int upr){ + if( lwr>=0 ){ + if( upr<=255 ){ + return "unsigned char"; + }else if( upr<65535 ){ + return "unsigned short int"; + }else{ + return "unsigned int"; + } + }else if( lwr>=-127 && upr<=127 ){ + return "signed char"; + }else if( lwr>=-32767 && upr<32767 ){ + return "short"; + }else{ + return "int"; + } +} + +/* +** Each state contains a set of token transaction and a set of +** nonterminal transactions. Each of these sets makes an instance +** of the following structure. An array of these structures is used +** to order the creation of entries in the yy_action[] table. +*/ +struct axset { + struct state *stp; /* A pointer to a state */ + int isTkn; /* True to use tokens. False for non-terminals */ + int nAction; /* Number of actions */ + int iOrder; /* Original order of action sets */ +}; + +/* +** Compare to axset structures for sorting purposes +*/ +static int axset_compare(const void *a, const void *b){ + struct axset *p1 = (struct axset*)a; + struct axset *p2 = (struct axset*)b; + int c; + c = p2->nAction - p1->nAction; + if( c==0 ){ + c = p2->iOrder - p1->iOrder; + } + assert( c!=0 || p1==p2 ); + return c; +} + +/* +** Write text on "out" that describes the rule "rp". +*/ +static void writeRuleText(FILE *out, struct rule *rp){ + int j; + fprintf(out,"%s ::=", rp->lhs->name); + for(j=0; jnrhs; j++){ + struct symbol *sp = rp->rhs[j]; + fprintf(out," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + int k; + for(k=1; knsubsym; k++){ + fprintf(out,"|%s",sp->subsym[k]->name); + } + } + } +} + + +/* Generate C source code for the parser */ +void ReportTable( + struct lemon *lemp, + int mhflag /* Output in makeheaders format if true */ +){ + FILE *out, *in; + char line[LINESIZE]; + int lineno; + struct state *stp; + struct action *ap; + struct rule *rp; + struct acttab *pActtab; + int i, j, n; + const char *name; + int mnTknOfst, mxTknOfst; + int mnNtOfst, mxNtOfst; + struct axset *ax; + + in = tplt_open(lemp); + if( in==0 ) return; + out = file_open(lemp,".c","wb"); + if( out==0 ){ + fclose(in); + return; + } + lineno = 1; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the include code, if any */ + tplt_print(out,lemp,lemp->include,&lineno); + if( mhflag ){ + char *name = file_makename(lemp, ".h"); + fprintf(out,"#include \"%s\"\n", name); lineno++; + free(name); + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate #defines for all tokens */ + if( mhflag ){ + const char *prefix; + fprintf(out,"#if INTERFACE\n"); lineno++; + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + lineno++; + } + fprintf(out,"#endif\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the defines */ + fprintf(out,"#define YYCODETYPE %s\n", + minimum_size_type(0, lemp->nsymbol+1)); lineno++; + fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++; + fprintf(out,"#define YYACTIONTYPE %s\n", + minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++; + if( lemp->wildcard ){ + fprintf(out,"#define YYWILDCARD %d\n", + lemp->wildcard->index); lineno++; + } + print_stack_union(out,lemp,&lineno,mhflag); + fprintf(out, "#ifndef YYSTACKDEPTH\n"); lineno++; + if( lemp->stacksize ){ + fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++; + }else{ + fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++; + } + fprintf(out, "#endif\n"); lineno++; + if( mhflag ){ + fprintf(out,"#if INTERFACE\n"); lineno++; + } + name = lemp->name ? lemp->name : "Parse"; + if( lemp->arg && lemp->arg[0] ){ + int i; + i = lemonStrlen(lemp->arg); + while( i>=1 && isspace(lemp->arg[i-1]) ) i--; + while( i>=1 && (isalnum(lemp->arg[i-1]) || lemp->arg[i-1]=='_') ) i--; + fprintf(out,"#define %sARG_SDECL %s;\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_PDECL ,%s\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_FETCH %s = yypParser->%s\n", + name,lemp->arg,&lemp->arg[i]); lineno++; + fprintf(out,"#define %sARG_STORE yypParser->%s = %s\n", + name,&lemp->arg[i],&lemp->arg[i]); lineno++; + }else{ + fprintf(out,"#define %sARG_SDECL\n",name); lineno++; + fprintf(out,"#define %sARG_PDECL\n",name); lineno++; + fprintf(out,"#define %sARG_FETCH\n",name); lineno++; + fprintf(out,"#define %sARG_STORE\n",name); lineno++; + } + if( mhflag ){ + fprintf(out,"#endif\n"); lineno++; + } + fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++; + fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++; + if( lemp->errsym->useCnt ){ + fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++; + fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++; + } + if( lemp->has_fallback ){ + fprintf(out,"#define YYFALLBACK 1\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the action table and its associates: + ** + ** yy_action[] A single table containing all actions. + ** yy_lookahead[] A table containing the lookahead for each entry in + ** yy_action. Used to detect hash collisions. + ** yy_shift_ofst[] For each state, the offset into yy_action for + ** shifting terminals. + ** yy_reduce_ofst[] For each state, the offset into yy_action for + ** shifting non-terminals after a reduce. + ** yy_default[] Default action for each state. + */ + + /* Compute the actions on all states and count them up */ + ax = (struct axset *) calloc(lemp->nstate*2, sizeof(ax[0])); + if( ax==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + ax[i*2].stp = stp; + ax[i*2].isTkn = 1; + ax[i*2].nAction = stp->nTknAct; + ax[i*2+1].stp = stp; + ax[i*2+1].isTkn = 0; + ax[i*2+1].nAction = stp->nNtAct; + } + mxTknOfst = mnTknOfst = 0; + mxNtOfst = mnNtOfst = 0; + + /* Compute the action table. In order to try to keep the size of the + ** action table to a minimum, the heuristic of placing the largest action + ** sets first is used. + */ + for(i=0; instate*2; i++) ax[i].iOrder = i; + qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare); + pActtab = acttab_alloc(); + for(i=0; instate*2 && ax[i].nAction>0; i++){ + stp = ax[i].stp; + if( ax[i].isTkn ){ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->index>=lemp->nterminal ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iTknOfst = acttab_insert(pActtab); + if( stp->iTknOfstiTknOfst; + if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst; + }else{ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->indexnterminal ) continue; + if( ap->sp->index==lemp->nsymbol ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iNtOfst = acttab_insert(pActtab); + if( stp->iNtOfstiNtOfst; + if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst; + } + } + free(ax); + + /* Output the yy_action table */ + n = acttab_size(pActtab); + fprintf(out,"#define YY_ACTTAB_COUNT (%d)\n", n); lineno++; + fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++; + for(i=j=0; instate + lemp->nrule + 2; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", action); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_lookahead table */ + fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++; + for(i=j=0; insymbol; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", la); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_shift_ofst[] table */ + fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; + n = lemp->nstate; + while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--; + fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++; + fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++; + fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++; + fprintf(out, "static const %s yy_shift_ofst[] = {\n", + minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++; + for(i=j=0; isorted[i]; + ofst = stp->iTknOfst; + if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_reduce_ofst[] table */ + fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; + n = lemp->nstate; + while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--; + fprintf(out, "#define YY_REDUCE_COUNT (%d)\n", n-1); lineno++; + fprintf(out, "#define YY_REDUCE_MIN (%d)\n", mnNtOfst); lineno++; + fprintf(out, "#define YY_REDUCE_MAX (%d)\n", mxNtOfst); lineno++; + fprintf(out, "static const %s yy_reduce_ofst[] = {\n", + minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++; + for(i=j=0; isorted[i]; + ofst = stp->iNtOfst; + if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the default action table */ + fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", stp->iDflt); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of fallback tokens. + */ + if( lemp->has_fallback ){ + int mx = lemp->nterminal - 1; + while( mx>0 && lemp->symbols[mx]->fallback==0 ){ mx--; } + for(i=0; i<=mx; i++){ + struct symbol *p = lemp->symbols[i]; + if( p->fallback==0 ){ + fprintf(out, " 0, /* %10s => nothing */\n", p->name); + }else{ + fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index, + p->name, p->fallback->name); + } + lineno++; + } + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate a table containing the symbolic name of every symbol + */ + for(i=0; insymbol; i++){ + sprintf(line,"\"%s\",",lemp->symbols[i]->name); + fprintf(out," %-15s",line); + if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; } + } + if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate a table containing a text string that describes every + ** rule in the rule set of the grammar. This information is used + ** when tracing REDUCE actions. + */ + for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ + assert( rp->index==i ); + fprintf(out," /* %3d */ \"", i); + writeRuleText(out, rp); + fprintf(out,"\",\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes every time a symbol is popped from + ** the stack while processing errors or while destroying the parser. + ** (In other words, generate the %destructor actions) + */ + if( lemp->tokendest ){ + int once = 1; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type!=TERMINAL ) continue; + if( once ){ + fprintf(out, " /* TERMINAL Destructor */\n"); lineno++; + once = 0; + } + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + } + for(i=0; insymbol && lemp->symbols[i]->type!=TERMINAL; i++); + if( insymbol ){ + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + if( lemp->vardest ){ + struct symbol *dflt_sp = 0; + int once = 1; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || + sp->index<=0 || sp->destructor!=0 ) continue; + if( once ){ + fprintf(out, " /* Default NON-TERMINAL Destructor */\n"); lineno++; + once = 0; + } + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + dflt_sp = sp; + } + if( dflt_sp!=0 ){ + emit_destructor_code(out,dflt_sp,lemp,&lineno); + } + fprintf(out," break;\n"); lineno++; + } + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue; + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + + /* Combine duplicate destructors into a single case */ + for(j=i+1; jnsymbol; j++){ + struct symbol *sp2 = lemp->symbols[j]; + if( sp2 && sp2->type!=TERMINAL && sp2->destructor + && sp2->dtnum==sp->dtnum + && strcmp(sp->destructor,sp2->destructor)==0 ){ + fprintf(out," case %d: /* %s */\n", + sp2->index, sp2->name); lineno++; + sp2->destructor = 0; + } + } + + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes whenever the parser stack overflows */ + tplt_print(out,lemp,lemp->overflow,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of rule information + ** + ** Note: This code depends on the fact that rules are number + ** sequentually beginning with 0. + */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which execution during each REDUCE action */ + for(rp=lemp->rule; rp; rp=rp->next){ + translate_code(lemp, rp); + } + /* First output rules other than the default: rule */ + for(rp=lemp->rule; rp; rp=rp->next){ + struct rule *rp2; /* Other rules with the same action */ + if( rp->code==0 ) continue; + if( rp->code[0]=='\n' && rp->code[1]==0 ) continue; /* Will be default: */ + fprintf(out," case %d: /* ", rp->index); + writeRuleText(out, rp); + fprintf(out, " */\n"); lineno++; + for(rp2=rp->next; rp2; rp2=rp2->next){ + if( rp2->code==rp->code ){ + fprintf(out," case %d: /* ", rp2->index); + writeRuleText(out, rp2); + fprintf(out," */ yytestcase(yyruleno==%d);\n", rp2->index); lineno++; + rp2->code = 0; + } + } + emit_code(out,rp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + rp->code = 0; + } + /* Finally, output the default: rule. We choose as the default: all + ** empty actions. */ + fprintf(out," default:\n"); lineno++; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->code==0 ) continue; + assert( rp->code[0]=='\n' && rp->code[1]==0 ); + fprintf(out," /* (%d) ", rp->index); + writeRuleText(out, rp); + fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp->index); lineno++; + } + fprintf(out," break;\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes if a parse fails */ + tplt_print(out,lemp,lemp->failure,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when a syntax error occurs */ + tplt_print(out,lemp,lemp->error,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when the parser accepts its input */ + tplt_print(out,lemp,lemp->accept,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Append any addition code the user desires */ + tplt_print(out,lemp,lemp->extracode,&lineno); + + fclose(in); + fclose(out); + return; +} + +/* Generate a header file for the parser */ +void ReportHeader(struct lemon *lemp) +{ + FILE *out, *in; + const char *prefix; + char line[LINESIZE]; + char pattern[LINESIZE]; + int i; + + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + in = file_open(lemp,".h","rb"); + if( in ){ + for(i=1; interminal && fgets(line,LINESIZE,in); i++){ + sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + if( strcmp(line,pattern) ) break; + } + fclose(in); + if( i==lemp->nterminal ){ + /* No change in the file. Don't rewrite it. */ + return; + } + } + out = file_open(lemp,".h","wb"); + if( out ){ + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + } + fclose(out); + } + return; +} + +/* Reduce the size of the action tables, if possible, by making use +** of defaults. +** +** In this version, we take the most frequent REDUCE action and make +** it the default. Except, there is no default if the wildcard token +** is a possible look-ahead. +*/ +void CompressTables(struct lemon *lemp) +{ + struct state *stp; + struct action *ap, *ap2; + struct rule *rp, *rp2, *rbest; + int nbest, n; + int i; + int usesWildcard; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + nbest = 0; + rbest = 0; + usesWildcard = 0; + + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==SHIFT && ap->sp==lemp->wildcard ){ + usesWildcard = 1; + } + if( ap->type!=REDUCE ) continue; + rp = ap->x.rp; + if( rp->lhsStart ) continue; + if( rp==rbest ) continue; + n = 1; + for(ap2=ap->next; ap2; ap2=ap2->next){ + if( ap2->type!=REDUCE ) continue; + rp2 = ap2->x.rp; + if( rp2==rbest ) continue; + if( rp2==rp ) n++; + } + if( n>nbest ){ + nbest = n; + rbest = rp; + } + } + + /* Do not make a default if the number of rules to default + ** is not at least 1 or if the wildcard token is a possible + ** lookahead. + */ + if( nbest<1 || usesWildcard ) continue; + + + /* Combine matching REDUCE actions into a single default */ + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) break; + } + assert( ap ); + ap->sp = Symbol_new("{default}"); + for(ap=ap->next; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED; + } + stp->ap = Action_sort(stp->ap); + } +} + + +/* +** Compare two states for sorting purposes. The smaller state is the +** one with the most non-terminal actions. If they have the same number +** of non-terminal actions, then the smaller is the one with the most +** token actions. +*/ +static int stateResortCompare(const void *a, const void *b){ + const struct state *pA = *(const struct state**)a; + const struct state *pB = *(const struct state**)b; + int n; + + n = pB->nNtAct - pA->nNtAct; + if( n==0 ){ + n = pB->nTknAct - pA->nTknAct; + if( n==0 ){ + n = pB->statenum - pA->statenum; + } + } + assert( n!=0 ); + return n; +} + + +/* +** Renumber and resort states so that states with fewer choices +** occur at the end. Except, keep state 0 as the first state. +*/ +void ResortStates(struct lemon *lemp) +{ + int i; + struct state *stp; + struct action *ap; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + stp->nTknAct = stp->nNtAct = 0; + stp->iDflt = lemp->nstate + lemp->nrule; + stp->iTknOfst = NO_OFFSET; + stp->iNtOfst = NO_OFFSET; + for(ap=stp->ap; ap; ap=ap->next){ + if( compute_action(lemp,ap)>=0 ){ + if( ap->sp->indexnterminal ){ + stp->nTknAct++; + }else if( ap->sp->indexnsymbol ){ + stp->nNtAct++; + }else{ + stp->iDflt = compute_action(lemp, ap); + } + } + } + } + qsort(&lemp->sorted[1], lemp->nstate-1, sizeof(lemp->sorted[0]), + stateResortCompare); + for(i=0; instate; i++){ + lemp->sorted[i]->statenum = i; + } +} + + +/***************** From the file "set.c" ************************************/ +/* +** Set manipulation routines for the LEMON parser generator. +*/ + +static int size = 0; + +/* Set the set size */ +void SetSize(int n) +{ + size = n+1; +} + +/* Allocate a new set */ +char *SetNew(){ + char *s; + s = (char*)calloc( size, 1); + if( s==0 ){ + extern void memory_error(); + memory_error(); + } + return s; +} + +/* Deallocate a set */ +void SetFree(char *s) +{ + free(s); +} + +/* Add a new element to the set. Return TRUE if the element was added +** and FALSE if it was already there. */ +int SetAdd(char *s, int e) +{ + int rv; + assert( e>=0 && esize = 1024; + x1a->count = 0; + x1a->tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*1024 ); + if( x1a->tbl==0 ){ + free(x1a); + x1a = 0; + }else{ + int i; + x1a->ht = (x1node**)&(x1a->tbl[1024]); + for(i=0; i<1024; i++) x1a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Strsafe_insert(const char *data) +{ + x1node *np; + int h; + int ph; + + if( x1a==0 ) return 0; + ph = strhash(data); + h = ph & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x1a->count>=x1a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x1 array; + array.size = size = x1a->size*2; + array.count = x1a->count; + array.tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x1node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x1node *oldnp, *newnp; + oldnp = &(x1a->tbl[i]); + h = strhash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x1a->tbl); + *x1a = array; + } + /* Insert the new data */ + h = ph & (x1a->size-1); + np = &(x1a->tbl[x1a->count++]); + np->data = data; + if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next); + np->next = x1a->ht[h]; + x1a->ht[h] = np; + np->from = &(x1a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +const char *Strsafe_find(const char *key) +{ + int h; + x1node *np; + + if( x1a==0 ) return 0; + h = strhash(key) & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return a pointer to the (terminal or nonterminal) symbol "x". +** Create a new symbol if this is the first time "x" has been seen. +*/ +struct symbol *Symbol_new(const char *x) +{ + struct symbol *sp; + + sp = Symbol_find(x); + if( sp==0 ){ + sp = (struct symbol *)calloc(1, sizeof(struct symbol) ); + MemoryCheck(sp); + sp->name = Strsafe(x); + sp->type = isupper(*x) ? TERMINAL : NONTERMINAL; + sp->rule = 0; + sp->fallback = 0; + sp->prec = -1; + sp->assoc = UNK; + sp->firstset = 0; + sp->lambda = LEMON_FALSE; + sp->destructor = 0; + sp->destLineno = 0; + sp->datatype = 0; + sp->useCnt = 0; + Symbol_insert(sp,sp->name); + } + sp->useCnt++; + return sp; +} + +/* Compare two symbols for working purposes +** +** Symbols that begin with upper case letters (terminals or tokens) +** must sort before symbols that begin with lower case letters +** (non-terminals). Other than that, the order does not matter. +** +** We find experimentally that leaving the symbols in their original +** order (the order they appeared in the grammar file) gives the +** smallest parser tables in SQLite. +*/ +int Symbolcmpp(const void *_a, const void *_b) +{ + const struct symbol **a = (const struct symbol **) _a; + const struct symbol **b = (const struct symbol **) _b; + int i1 = (**a).index + 10000000*((**a).name[0]>'Z'); + int i2 = (**b).index + 10000000*((**b).name[0]>'Z'); + assert( i1!=i2 || strcmp((**a).name,(**b).name)==0 ); + return i1-i2; +} + +/* There is one instance of the following structure for each +** associative array of type "x2". +*/ +struct s_x2 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x2node *tbl; /* The data stored here */ + struct s_x2node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x2". +*/ +typedef struct s_x2node { + struct symbol *data; /* The data */ + const char *key; /* The key */ + struct s_x2node *next; /* Next entry with the same hash */ + struct s_x2node **from; /* Previous link */ +} x2node; + +/* There is only one instance of the array, which is the following */ +static struct s_x2 *x2a; + +/* Allocate a new associative array */ +void Symbol_init(){ + if( x2a ) return; + x2a = (struct s_x2*)malloc( sizeof(struct s_x2) ); + if( x2a ){ + x2a->size = 128; + x2a->count = 0; + x2a->tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*128 ); + if( x2a->tbl==0 ){ + free(x2a); + x2a = 0; + }else{ + int i; + x2a->ht = (x2node**)&(x2a->tbl[128]); + for(i=0; i<128; i++) x2a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Symbol_insert(struct symbol *data, const char *key) +{ + x2node *np; + int h; + int ph; + + if( x2a==0 ) return 0; + ph = strhash(key); + h = ph & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x2a->count>=x2a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x2 array; + array.size = size = x2a->size*2; + array.count = x2a->count; + array.tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x2node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x2node *oldnp, *newnp; + oldnp = &(x2a->tbl[i]); + h = strhash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x2a->tbl); + *x2a = array; + } + /* Insert the new data */ + h = ph & (x2a->size-1); + np = &(x2a->tbl[x2a->count++]); + np->key = key; + np->data = data; + if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next); + np->next = x2a->ht[h]; + x2a->ht[h] = np; + np->from = &(x2a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct symbol *Symbol_find(const char *key) +{ + int h; + x2node *np; + + if( x2a==0 ) return 0; + h = strhash(key) & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the n-th data. Return NULL if n is out of range. */ +struct symbol *Symbol_Nth(int n) +{ + struct symbol *data; + if( x2a && n>0 && n<=x2a->count ){ + data = x2a->tbl[n-1].data; + }else{ + data = 0; + } + return data; +} + +/* Return the size of the array */ +int Symbol_count() +{ + return x2a ? x2a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct symbol **Symbol_arrayof() +{ + struct symbol **array; + int i,size; + if( x2a==0 ) return 0; + size = x2a->count; + array = (struct symbol **)calloc(size, sizeof(struct symbol *)); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Compare two configurations */ +int Configcmp(const char *_a,const char *_b) +{ + const struct config *a = (struct config *) _a; + const struct config *b = (struct config *) _b; + int x; + x = a->rp->index - b->rp->index; + if( x==0 ) x = a->dot - b->dot; + return x; +} + +/* Compare two states */ +PRIVATE int statecmp(struct config *a, struct config *b) +{ + int rc; + for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){ + rc = a->rp->index - b->rp->index; + if( rc==0 ) rc = a->dot - b->dot; + } + if( rc==0 ){ + if( a ) rc = 1; + if( b ) rc = -1; + } + return rc; +} + +/* Hash a state */ +PRIVATE int statehash(struct config *a) +{ + int h=0; + while( a ){ + h = h*571 + a->rp->index*37 + a->dot; + a = a->bp; + } + return h; +} + +/* Allocate a new state structure */ +struct state *State_new() +{ + struct state *newstate; + newstate = (struct state *)calloc(1, sizeof(struct state) ); + MemoryCheck(newstate); + return newstate; +} + +/* There is one instance of the following structure for each +** associative array of type "x3". +*/ +struct s_x3 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x3node *tbl; /* The data stored here */ + struct s_x3node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x3". +*/ +typedef struct s_x3node { + struct state *data; /* The data */ + struct config *key; /* The key */ + struct s_x3node *next; /* Next entry with the same hash */ + struct s_x3node **from; /* Previous link */ +} x3node; + +/* There is only one instance of the array, which is the following */ +static struct s_x3 *x3a; + +/* Allocate a new associative array */ +void State_init(){ + if( x3a ) return; + x3a = (struct s_x3*)malloc( sizeof(struct s_x3) ); + if( x3a ){ + x3a->size = 128; + x3a->count = 0; + x3a->tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*128 ); + if( x3a->tbl==0 ){ + free(x3a); + x3a = 0; + }else{ + int i; + x3a->ht = (x3node**)&(x3a->tbl[128]); + for(i=0; i<128; i++) x3a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int State_insert(struct state *data, struct config *key) +{ + x3node *np; + int h; + int ph; + + if( x3a==0 ) return 0; + ph = statehash(key); + h = ph & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x3a->count>=x3a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x3 array; + array.size = size = x3a->size*2; + array.count = x3a->count; + array.tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x3node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x3node *oldnp, *newnp; + oldnp = &(x3a->tbl[i]); + h = statehash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x3a->tbl); + *x3a = array; + } + /* Insert the new data */ + h = ph & (x3a->size-1); + np = &(x3a->tbl[x3a->count++]); + np->key = key; + np->data = data; + if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next); + np->next = x3a->ht[h]; + x3a->ht[h] = np; + np->from = &(x3a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct state *State_find(struct config *key) +{ + int h; + x3node *np; + + if( x3a==0 ) return 0; + h = statehash(key) & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct state **State_arrayof() +{ + struct state **array; + int i,size; + if( x3a==0 ) return 0; + size = x3a->count; + array = (struct state **)malloc( sizeof(struct state *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Hash a configuration */ +PRIVATE int confighash(struct config *a) +{ + int h=0; + h = h*571 + a->rp->index*37 + a->dot; + return h; +} + +/* There is one instance of the following structure for each +** associative array of type "x4". +*/ +struct s_x4 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x4node *tbl; /* The data stored here */ + struct s_x4node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x4". +*/ +typedef struct s_x4node { + struct config *data; /* The data */ + struct s_x4node *next; /* Next entry with the same hash */ + struct s_x4node **from; /* Previous link */ +} x4node; + +/* There is only one instance of the array, which is the following */ +static struct s_x4 *x4a; + +/* Allocate a new associative array */ +void Configtable_init(){ + if( x4a ) return; + x4a = (struct s_x4*)malloc( sizeof(struct s_x4) ); + if( x4a ){ + x4a->size = 64; + x4a->count = 0; + x4a->tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*64 ); + if( x4a->tbl==0 ){ + free(x4a); + x4a = 0; + }else{ + int i; + x4a->ht = (x4node**)&(x4a->tbl[64]); + for(i=0; i<64; i++) x4a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Configtable_insert(struct config *data) +{ + x4node *np; + int h; + int ph; + + if( x4a==0 ) return 0; + ph = confighash(data); + h = ph & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp((const char *) np->data,(const char *) data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x4a->count>=x4a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x4 array; + array.size = size = x4a->size*2; + array.count = x4a->count; + array.tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x4node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x4node *oldnp, *newnp; + oldnp = &(x4a->tbl[i]); + h = confighash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x4a->tbl); + *x4a = array; + } + /* Insert the new data */ + h = ph & (x4a->size-1); + np = &(x4a->tbl[x4a->count++]); + np->data = data; + if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); + np->next = x4a->ht[h]; + x4a->ht[h] = np; + np->from = &(x4a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct config *Configtable_find(struct config *key) +{ + int h; + x4node *np; + + if( x4a==0 ) return 0; + h = confighash(key) & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp((const char *) np->data,(const char *) key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Remove all data from the table. Pass each data to the function "f" +** as it is removed. ("f" may be null to avoid this step.) */ +void Configtable_clear(int(*f)(struct config *)) +{ + int i; + if( x4a==0 || x4a->count==0 ) return; + if( f ) for(i=0; icount; i++) (*f)(x4a->tbl[i].data); + for(i=0; isize; i++) x4a->ht[i] = 0; + x4a->count = 0; + return; +} diff --git a/sql/parser/lempar-tmpl.c b/sql/parser/lempar-tmpl.c new file mode 100644 index 0000000..3bb7b0f --- /dev/null +++ b/sql/parser/lempar-tmpl.c @@ -0,0 +1,851 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +%% +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +%% +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +%% +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +%% + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +%% +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { +%% +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { +%% +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ +%% + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + if( pParser->yyidx<0 ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==0 ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_COUNT + || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( +#if YY_SHIFT_MIN+YYWILDCARD<0 + j>=0 && +#endif +#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT + j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_COUNT ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_COUNT ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { +%% +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ +%% + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/sql/parser/scan.rl b/sql/parser/scan.rl new file mode 100644 index 0000000..8b96bc1 --- /dev/null +++ b/sql/parser/scan.rl @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* THIS FILE HAS BEEN GENERATED USING THE SOURCE IN parser/scan.rl */ + +#include +#include "sql-parser.h" +#include "parser/gram.c" + +#define PARSE(token, value) \ + state->current = ts; \ + Parse(parser, SQL_PARSER_##token, value, state) + +/** + * SECTION: sql-parser + * @Short_description: a simple DML query parser + * @Title: SqlParser + * + * This is the parser used by Hedera to transform a string containing one or + * more SQL data management queries into an #SqlObject and thus have an idea + * of what must happen when a change is made in the affected data. + **/ +static gchar * get_token (const gchar * ts, const gchar * te) +{ + if (!(ts && te)) return NULL; + gsize len = (gsize) (te-ts); + gchar * str = g_malloc (len+1); + g_memmove (str, ts, len); + str[len] = '\0'; + return str; +} + +%%{ + machine sql_scanner; + +#/*-----------------------# SYMBOL Definition #-----------------------*/# +# Keywords + AS = "AS"i; + UNION = "UNION"i; + ALL = "ALL"i; + ANY = "ANY"i; + EXCEPT = "EXCEPT"i; + INTERSECT = "INTERSECT"i; + SELECT = "SELECT"i; + FROM = "FROM"i; + WHERE = "WHERE"i; + DISTINCT = "DISTINCT"i; + JOIN = "JOIN"i; + INNER = "INNER"i; + LEFT = ("OUTER"i space+|"") "LEFT"i; + RIGHT = ("OUTER"i space+|"") "RIGHT"i; + ON = "ON"i; + USING = "USING"i; + NATURAL = "NATURAL"i; + GROUP = "GROUP" space+ "BY"i; + HAVING = "HAVING"i; + ORDER = "ORDER"i space+ "BY"i; + ASC = "ASC"i; + DESC = "DESC"i; + LIMIT = "LIMIT"i; + OFFSET = "OFFSET"i; + UPDATE = "UPDATE"i; + SET = "SET"i; + INSERT = "INSERT"i space+ "INTO"i; + DEFAULT = "DEFAULT"i; + VALUES = "VALUES"i; + DELETE = "DELETE"i space+ "FROM"i; + RETURNING = "RETURNING"i; +# Parenthesis + LP = "("; + RP = ")"; +# Colon + DOT = "."; +# Semicolon + SC = ";"; +# Comma + CM = ","; +# Identifier quote + IQ = ("\"" | "`"); +# Operators + PLUS = "+"; + MINUS = "-"; + STAR = "*"; + DIV = "/"; + MOD = "%"; + EQ = "="; + NE = ("<>" | "!="); + GT = ">"; + GE = ">="; + LT = "<"; + LE = "<="; +# Operator Keywords + AND = "AND"i; + OR = "OR"i; + NOT = ("NOT"i | "!"); + LIKE = "LIKE"i; + IS = "IS"i; + XOR = "XOR"i; +# Boolean Keywords + BOOLEAN = ("TRUE"i | "FALSE"i); + + Op = (AND | OR | NOT | LIKE | IS | XOR); + keyword = ( Op | BOOLEAN | AS | UNION | ALL | ANY | EXCEPT | INTERSECT | + SELECT | FROM | WHERE | DISTINCT | JOIN | INNER | LEFT| RIGHT | ON | + USING | NATURAL | GROUP | HAVING | ORDER | ASC | DESC | LIMIT | OFFSET | + UPDATE | SET | INSERT | DEFAULT | VALUES | RETURNING | DELETE); + +# Identifiers + Iescape = ("\"\"" | "" ); + IDENTIFIER = ((alpha | "_" | "$") (alnum | "_" | "$")*) - keyword; + QIDENTIFIER = IQ ((any - IQ)* Iescape (any - IQ)*) IQ; + PLACEHOLDER = "#" IDENTIFIER; + +# Escaped string characters + escape = ("\\'"| "''" | ""); + +# Data values + STRING = "'" (any - "'")* escape (any - "'")* "'"; + INTEGER = digit+; + FLOAT = digit+ ("." digit+)?; + +# Comments + comment = ("-- " (any - "\n")* "\n" | + "/*" (any* -- "*/") "*/"); + +#/*-----------------------# SCANNER Definition #-----------------------*/# + main := + |* +#// Keywords + AS => { PARSE (AS, 0); }; + UNION => { PARSE (UNION, 0); }; + ALL => { PARSE (ALL, 0); }; + ANY => { PARSE (ANY, 0); }; + EXCEPT => { PARSE (EXCEPT, 0); }; + INTERSECT => { PARSE (INTERSECT, 0); }; + SELECT => { PARSE (SELECT, 0); }; + DISTINCT => { PARSE (DISTINCT, 0); }; + FROM => { PARSE (FROM, 0); }; + WHERE => { PARSE (WHERE, 0); }; + JOIN => { PARSE (JOIN, 0); }; + INNER => { PARSE (INNER, 0); }; + LEFT => { PARSE (LEFT, 0); }; + RIGHT => { PARSE (RIGHT, 0); }; + ON => { PARSE (ON, 0); }; + USING => { PARSE (USING, 0); }; +#// NATURAL => { PARSE (NATURAL, 0); }; + GROUP => { PARSE (GROUP, 0); }; + HAVING => { PARSE (HAVING, 0); }; + ORDER => { PARSE (ORDER, 0); }; + ASC => { PARSE (ASC, 0); }; + DESC => { PARSE (DESC, 0); }; + LIMIT => { PARSE (LIMIT, 0); }; + OFFSET => { PARSE (OFFSET, 0); }; + UPDATE => { PARSE (UPDATE, 0); }; + SET => { PARSE (SET, 0); }; + INSERT => { PARSE (INSERT, 0); }; + DEFAULT => { PARSE (DEFAULT, 0); }; + VALUES => { PARSE (VALUES, 0); }; + DELETE => { PARSE (DELETE, 0); }; + +#// Punctuation symbols + LP => { PARSE (LP, 0); }; + RP => { PARSE (RP, 0); }; + DOT => { PARSE (DOT, 0); }; + SC => { PARSE (SC, 0); }; + CM => { PARSE (CM, 0); }; + +#// Identifiers + IDENTIFIER => { PARSE (IDENTIFIER, get_token (ts, te)); }; + QIDENTIFIER => { PARSE (IDENTIFIER, get_token (ts+1, te-1)); }; + PLACEHOLDER => { PARSE (PLACEHOLDER, get_token (ts+1, te)); }; + +#// Data values + STRING => { PARSE (STRING, get_token (ts+1, te-1)); }; + INTEGER => { PARSE (INTEGER, get_token (ts, te)); }; + FLOAT => { PARSE (FLOAT, get_token (ts, te)); }; + BOOLEAN => { PARSE (BOOLEAN, get_token(ts, te)); }; + +#// Operators + PLUS => { PARSE (PLUS, 0); }; + MINUS => { PARSE (MINUS, 0); }; + STAR => { PARSE (STAR, 0); }; + DIV => { PARSE (DIV, 0); }; + MOD => { PARSE (MOD, 0); }; + EQ => { PARSE (EQ, 0); }; + NE => { PARSE (NE, 0); }; + GT => { PARSE (GT, 0); }; + GE => { PARSE (GE, 0); }; + LT => { PARSE (LT, 0); }; + LE => { PARSE (LE, 0); }; + +#// Operator Keywords + AND => { PARSE (AND, 0); }; + OR => { PARSE (OR, 0); }; + NOT => { PARSE (NOT, 0); }; + LIKE => { PARSE (LIKE, 0); }; + IS => { PARSE (IS, 0); }; + XOR => { PARSE (XOR, 0); }; + +#// Ignored + space; + comment; + *|; +}%% +%% write data; + +SqlObject * sql_parser_parse (gchar * sql) +{ + gint cs, act; + gchar * p, * pe, * ts, * te; + gpointer eof, parser; + ParseState * state; + SqlObject * object; + + if (!sql) + { + g_log (g_quark_to_string (SQL_PARSER_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING ,"Empty query!\n"); + return NULL; + } + + state = g_new (ParseState, 1); + state->object = NULL; + state->error = FALSE; + state->string = state->current = p = sql; + pe = p + strlen (p) + 1; + eof = pe; + + parser = ParseAlloc (g_malloc); + + %% write init; + %% write exec; + + Parse (parser, 0, 0, state); + ParseFree (parser, g_free); + + if (state->error) + { + if (state->object && G_IS_OBJECT (state->object)) + g_object_unref (g_object_ref_sink (state->object)); + object = NULL; + } + else + object = g_object_ref_sink (state->object); + + g_free (state); + + return object; +} diff --git a/sql/sql-delete.c b/sql/sql-delete.c new file mode 100644 index 0000000..63dc205 --- /dev/null +++ b/sql/sql-delete.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-delete.h" + +/** + * SECTION: sql-delete + * @Short_description: the equivalent of the DELETE statement in SQL. + * @Title: SqlDelete + * + * The #SqlDelete represents a deletion statement. + **/ +G_DEFINE_TYPE (SqlDelete, sql_delete, SQL_TYPE_DML); + +SqlObject * sql_delete_new () +{ + return g_object_new (SQL_TYPE_DELETE, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_delete_render (SqlDelete * obj, SqlRender * render) +{ + sql_render_add_token (render, "DELETE"); + sql_render_add_list (render, TRUE, NULL, obj->tables, ","); + + if (SQL_DML (obj)->targets) + { + sql_render_add_list (render, TRUE, "FROM", SQL_DML (obj)->targets, ","); + sql_render_add_item (render, FALSE, "WHERE", SQL_DML (obj)->where); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TABLES = 1 +}; + +static void sql_delete_set_property (SqlDelete * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TABLES: + sql_object_remove (obj, obj->tables); + obj->tables = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_delete_get_property (SqlDelete * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TABLES: + g_value_set_object (value, obj->tables); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_delete_init (SqlDelete * obj) +{ + obj->tables = NULL; +} + +static void sql_delete_finalize (SqlDelete * obj) +{ + sql_object_remove (obj, obj->tables); + G_OBJECT_CLASS (sql_delete_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_delete_class_init (SqlDeleteClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_delete_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_delete_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_delete_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_delete_render; + + g_object_class_install_property (k, PROP_TABLES, + sql_param_list ("tables" + ,"Tables" + ,"A list of tables" + ,SQL_TYPE_TABLE + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} diff --git a/sql/sql-delete.h b/sql/sql-delete.h new file mode 100644 index 0000000..fcb0783 --- /dev/null +++ b/sql/sql-delete.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_DELETE_H +#define SQL_DELETE_H + +#include "sql-dml.h" +#include "sql-table.h" + +#define SQL_TYPE_DELETE (sql_delete_get_type ()) +#define SQL_DELETE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_DELETE, SqlDelete)) +#define SQL_IS_DELETE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_DELETE)) + +typedef struct _SqlDelete SqlDelete; +typedef struct _SqlDeleteClass SqlDeleteClass; + +/** + * SqlDelete: + * @table: (element-type Sql.Table): list of targeted tables + **/ +struct _SqlDelete +{ + SqlDml parent; + SqlList * tables; +}; + +struct _SqlDeleteClass +{ + /* */ + SqlDmlClass parent; +}; + +GType sql_delete_get_type (); +SqlObject * sql_delete_new (); + +#endif diff --git a/sql/sql-dml.c b/sql/sql-dml.c new file mode 100644 index 0000000..7d61719 --- /dev/null +++ b/sql/sql-dml.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-dml.h" + +/** + * SECTION: sql-dml + * @Short_description: parent class of data manipulation statements. + * @Title: SqlDml + * + * The #SqlDml is the base class for all SQL statements that manipulate data. + **/ +G_DEFINE_ABSTRACT_TYPE (SqlDml, sql_dml, SQL_TYPE_STMT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_dml_set_where (SqlDml * obj, SqlExpr * where) +{ + g_return_if_fail (SQL_IS_DML (obj)); + g_return_if_fail (SQL_IS_EXPR (where) || SQL_IS_HOLDER (where) || !where); + + sql_object_remove (obj, obj->where); + obj->where = sql_object_add (obj, where); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TARGETS = 1 + ,PROP_WHERE +}; + +static void sql_dml_set_property (SqlDml * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TARGETS: + sql_object_remove (obj, obj->targets); + obj->targets = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_WHERE: + sql_dml_set_where (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_dml_get_property (SqlDml * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TARGETS: + g_value_set_object (value, obj->targets); + break; + case PROP_WHERE: + g_value_set_object (value, obj->where); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_dml_init (SqlDml * obj) +{ + obj->targets = NULL; + obj->where = NULL; +} + +static void sql_dml_finalize (SqlDml * obj) +{ + sql_object_remove (obj, obj->targets); + sql_object_remove (obj, obj->where); + G_OBJECT_CLASS (sql_dml_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_dml_class_init (SqlDmlClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_dml_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_dml_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_dml_get_property; + + g_object_class_install_property (k, PROP_TARGETS, + sql_param_list ("targets" + ,"Targets" + ,"A list of targets" + ,SQL_TYPE_TARGET + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); + g_object_class_install_property (k, PROP_WHERE, + sql_param_object ("where" + ,"Where" + ,"The WHERE section of an statement" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-dml.h b/sql/sql-dml.h new file mode 100644 index 0000000..d7c528f --- /dev/null +++ b/sql/sql-dml.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_DML_H +#define SQL_DML_H + +#include "sql-stmt.h" +#include "sql-expr.h" +#include "sql-target.h" + +#define SQL_TYPE_DML (sql_dml_get_type ()) +#define SQL_DML(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_DML, SqlDml)) +#define SQL_IS_DML(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_DML)) + +typedef struct _SqlDml SqlDml; +typedef struct _SqlDmlClass SqlDmlClass; + +/** + * SqlDml: + * @target: (element-type Sql.Target): list of targets for the DML query + * @where: an #SqlExpr + **/ +struct _SqlDml +{ + SqlStmt parent; + SqlList * targets; + SqlExpr * where; +}; + +struct _SqlDmlClass +{ + /* */ + SqlStmtClass parent; +}; + +GType sql_dml_get_type (); +void sql_dml_set_where (SqlDml * obj, SqlExpr * expr); + +#endif diff --git a/sql/sql-expr.c b/sql/sql-expr.c new file mode 100644 index 0000000..00b9993 --- /dev/null +++ b/sql/sql-expr.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-expr.h" + +/** + * SECTION: sql-expr + * @Short_description: represents any expression used in SQL. + * @Title: SqlExpr + * + * The #SqlExpr is the base class for all SQL expressions such as an + * arithmetic or logical operation, a comparison between values, columns, any + * type of value... + **/ +G_DEFINE_ABSTRACT_TYPE (SqlExpr, sql_expr, SQL_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_expr_init (SqlExpr * obj) {} + +static void sql_expr_class_init (SqlExprClass * klass) {} diff --git a/sql/sql-expr.h b/sql/sql-expr.h new file mode 100644 index 0000000..0a4400d --- /dev/null +++ b/sql/sql-expr.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_EXPR_H +#define SQL_EXPR_H + +#include "sql-object.h" + +#define SQL_TYPE_EXPR (sql_expr_get_type ()) +#define SQL_EXPR(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_EXPR, SqlExpr)) +#define SQL_IS_EXPR(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_EXPR)) + +typedef struct _SqlExpr SqlExpr; +typedef struct _SqlExprClass SqlExprClass; + +struct _SqlExpr +{ + SqlObject parent; +}; + +struct _SqlExprClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_expr_get_type (); + +#endif diff --git a/sql/sql-field.c b/sql/sql-field.c new file mode 100644 index 0000000..8511016 --- /dev/null +++ b/sql/sql-field.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-field.h" + +/** + * SECTION: sql-field + * @Short_description: a column of an SQL target. + * @Title: SqlField + * + * The #SqlField represents the column of an SQL target. + **/ +G_DEFINE_TYPE (SqlField, sql_field, SQL_TYPE_EXPR); + +/** + * sql_field_new: + * @name: the name of the field + * @target: (allow-none): the table from which the field is selected + * @schema: (allow-none): the schema from which the table and field are selected + * + * Creates a new #SqlField. + * + * Return value: an #SqlExpr + */ +SqlObject * sql_field_new (const gchar * name, const gchar * target, const gchar * schema) +{ + return g_object_new (SQL_TYPE_FIELD + ,"name", name + ,"target", target + ,"schema", schema + ,NULL + ); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_field_render (SqlField * obj, SqlRender * render) +{ + if (obj->target) + { + if (obj->schema) + { + sql_render_add_identifier (render, obj->schema); + sql_render_append (render, "."); + } + + sql_render_add_identifier (render, obj->target); + sql_render_append (render, "."); + } + + if (!g_strcmp0 (obj->name, "*")) + { + sql_render_add_espace (render); + sql_render_append (render, "*"); + } + else + sql_render_add_identifier (render, obj->name); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_field_set_name (SqlField * obj, const gchar * name) +{ + g_return_if_fail (SQL_IS_FIELD (obj)); + g_return_if_fail (name); + + g_free (obj->name); + obj->name = g_strdup (name); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_TARGET + ,PROP_SCHEMA +}; + +static void sql_field_set_property (SqlField * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + sql_field_set_name (obj, g_value_get_string (value)); + break; + case PROP_TARGET: + g_free (obj->target); + obj->target = g_value_dup_string (value); + break; + case PROP_SCHEMA: + g_free (obj->schema); + obj->schema = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_field_get_property (SqlField * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->name); + break; + case PROP_TARGET: + g_value_set_string (value, obj->target); + break; + case PROP_SCHEMA: + g_value_set_string (value, obj->schema); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_field_init (SqlField * obj) +{ + obj->name = NULL; + obj->target = NULL; + obj->schema = NULL; +} + +static void sql_field_finalize (SqlField * obj) +{ + g_free (obj->name); + g_free (obj->target); + g_free (obj->schema); + G_OBJECT_CLASS (sql_field_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_field_class_init (SqlFieldClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_field_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_field_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_field_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_field_render; + + g_object_class_install_property (k, PROP_NAME, + g_param_spec_string ("name" + ,"Name" + ,"The column name" + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_TARGET, + g_param_spec_string ("target" + ,"Target" + ,"The target name" + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SCHEMA, + g_param_spec_string ("schema" + ,"Schema" + ,"The schema name" + ,NULL + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-field.h b/sql/sql-field.h new file mode 100644 index 0000000..c33efe8 --- /dev/null +++ b/sql/sql-field.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_FIELD_H +#define SQL_FIELD_H + +#include "sql-expr.h" +#include "sql-target.h" + +#define SQL_TYPE_FIELD (sql_field_get_type ()) +#define SQL_FIELD(object) (G_TYPE_CHECK_INSTANCE_CAST (object, SQL_TYPE_FIELD, SqlField)) +#define SQL_IS_FIELD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SQL_TYPE_FIELD)) + +typedef struct _SqlField SqlField; +typedef struct _SqlFieldClass SqlFieldClass; + +struct _SqlField +{ + SqlExpr parent; + gchar * name; + gchar * target; + gchar * schema; +}; + +struct _SqlFieldClass +{ + /* */ + SqlExprClass parent; +}; + +GType sql_field_get_type (); +SqlObject * sql_field_new (const gchar * name + ,const gchar * target + ,const gchar * schema); +void sql_field_set_name (SqlField * obj, const gchar * name); + +#endif diff --git a/sql/sql-function.c b/sql/sql-function.c new file mode 100644 index 0000000..d5c7249 --- /dev/null +++ b/sql/sql-function.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-function.h" + +/** + * SECTION: sql-function + * @Short_description: the equivalent of an SQL function. + * @Title: SqlFunction + * + * The #SqlFunction represents a function and its parameters. + **/ +G_DEFINE_TYPE (SqlFunction, sql_function, SQL_TYPE_EXPR); + +SqlFunction * sql_function_new (const gchar * name, const gchar * schema) +{ + return g_object_new (SQL_TYPE_FUNCTION, "name", name, "schema", schema, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_function_render (SqlFunction * obj, SqlRender * render) +{ + if (obj->schema) + { + sql_render_add_identifier (render, obj->schema); + sql_render_append (render, "."); + } + + sql_render_add_identifier (render, obj->name); + + if (obj->name) + { + sql_render_append (render, "("); + sql_render_add_list (render, FALSE, NULL, obj->params, ","); + sql_render_append (render, ")"); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_SCHEMA + ,PROP_PARAMS +}; + +static void sql_function_set_property (SqlFunction * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_free (obj->name); + obj->name = g_value_dup_string (value); + break; + case PROP_SCHEMA: + g_free (obj->schema); + obj->schema = g_value_dup_string (value); + break; + case PROP_PARAMS: + sql_object_remove (obj, obj->params); + obj->params = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_function_get_property (SqlFunction * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->name); + break; + case PROP_SCHEMA: + g_value_set_string (value, obj->schema); + break; + case PROP_PARAMS: + g_value_set_object (value, obj->params); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_function_init (SqlFunction * obj) +{ + obj->name = NULL; + obj->schema = NULL; + obj->params = NULL; +} + +static void sql_function_finalize (SqlFunction * obj) +{ + g_free (obj->name); + g_free (obj->schema); + sql_object_remove (obj, obj->params); + G_OBJECT_CLASS (sql_function_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_function_class_init (SqlFunctionClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_function_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_function_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_function_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_function_render; + + g_object_class_install_property (k, PROP_NAME, + g_param_spec_string ("name" + ,"Name" + ,"The function name" + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SCHEMA, + g_param_spec_string ("schema" + ,"Schema" + ,"The schema to which the function belongs" + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_PARAMS, + sql_param_list ("params" + ,"Parameters" + ,"The function parameters" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} diff --git a/sql/sql-function.h b/sql/sql-function.h new file mode 100644 index 0000000..ac4190c --- /dev/null +++ b/sql/sql-function.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_FUNCTION_H +#define SQL_FUNCTION_H + +#include "sql-expr.h" + +#define SQL_TYPE_FUNCTION (sql_function_get_type ()) +#define SQL_FUNCTION(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_FUNCTION, SqlFunction)) +#define SQL_IS_FUNCTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_FUNCTION)) + +typedef struct _SqlFunction SqlFunction; +typedef struct _SqlFunctionClass SqlFunctionClass; + +/** + * SqlFunction: + * @name: name of the called function + * @param: (element-type Sql.Expr): list of #SqlExpr parameters + **/ +struct _SqlFunction +{ + SqlExpr parent; + gchar * schema; + gchar * name; + SqlList * params; // List of SqlExpr +}; + +struct _SqlFunctionClass +{ + /* */ + SqlExprClass parent; +}; + +GType sql_function_get_type (); +SqlFunction * sql_function_new (const gchar * name, const gchar * schema); + +#endif diff --git a/sql/sql-holder.c b/sql/sql-holder.c new file mode 100644 index 0000000..b3c3fe2 --- /dev/null +++ b/sql/sql-holder.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-holder.h" + +/** + * SECTION: sql-holder + * @Short_description: + * @Title: SqlHolder + **/ +G_DEFINE_TYPE (SqlHolder, sql_holder, SQL_TYPE_OBJECT); + +SqlHolder * sql_holder_new (const gchar * id) +{ + return g_object_new (SQL_TYPE_HOLDER, "id", id, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_holder_render (SqlHolder * obj, SqlRender * render) +{ + SqlObject * held_object = NULL; + GSList * i = sql_render_get_ancestors (render); + + for (; i && !held_object; i = i->next) + held_object = sql_object_get_held_object (held_object, obj->id); + + if (held_object) + sql_render_add_object (render, held_object); + else + sql_render_printf (render, "#%s", obj->id); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_holder_get_id: + * @obj: the #SqlHolder + * + * Gets the identifier assigned to the holder. + * + * Return value: (transfer none): the id + **/ +const gchar * sql_holder_get_id (SqlHolder * obj) +{ + g_return_val_if_fail (SQL_IS_HOLDER (obj), NULL); + + return obj->id; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_ID = 1 +}; + +static void sql_holder_set_property (SqlHolder * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ID: + g_free (obj->id); + obj->id = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_holder_get_property (SqlHolder * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ID: + g_value_set_string (value, sql_holder_get_id (obj)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_holder_init (SqlHolder * obj) +{ + obj->id = NULL; +} + +static void sql_holder_finalize (SqlHolder * obj) +{ + g_free (obj->id); + G_OBJECT_CLASS (sql_holder_finalize)->finalize (G_OBJECT (obj)); +} + +static void sql_holder_class_init (SqlHolderClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) sql_holder_finalize; + klass->set_property = (GObjectSetPropertyFunc) sql_holder_set_property; + klass->get_property = (GObjectGetPropertyFunc) sql_holder_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_holder_render; + + g_object_class_install_property (klass, PROP_ID, + g_param_spec_string ("id" + ,_("Identifier") + ,_("The holder identifier") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/sql/sql-holder.h b/sql/sql-holder.h new file mode 100644 index 0000000..ab18735 --- /dev/null +++ b/sql/sql-holder.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_HOLDER_H +#define SQL_HOLDER_H + +#include "sql-object.h" + +#define SQL_TYPE_HOLDER (sql_holder_get_type ()) +#define SQL_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_HOLDER, SqlHolder)) +#define SQL_IS_HOLDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_HOLDER)) + +typedef struct _SqlHolder SqlHolder; +typedef struct _SqlHolderClass SqlHolderClass; + +struct _SqlHolder +{ + SqlObject parent; + gchar * id; +}; + +struct _SqlHolderClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_holder_get_type (); +SqlHolder * sql_holder_new (const gchar * id); +const gchar * sql_holder_get_id (SqlHolder * obj); + +#endif \ No newline at end of file diff --git a/sql/sql-insert.c b/sql/sql-insert.c new file mode 100644 index 0000000..6270dd0 --- /dev/null +++ b/sql/sql-insert.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-insert.h" + +/** + * SECTION: sql-insert + * @Short_description: the equivalent of the INSERT statement in SQL. + * @Title: SqlInsert + * + * The #SqlInsert represents a insertion statement. + **/ +G_DEFINE_TYPE (SqlInsert, sql_insert, SQL_TYPE_STMT); + +SqlObject * sql_insert_new () +{ + return g_object_new (SQL_TYPE_INSERT, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_insert_render (SqlInsert * obj, SqlRender * render) +{ + sql_render_add_item (render, TRUE, "INSERT INTO", obj->table); + + if (obj->table) + { + if (obj->values) + { + sql_render_add_espace (render); + sql_render_append (render, "("); + sql_render_add_list (render, FALSE, NULL, obj->fields, ","); + sql_render_append (render, ")"); + sql_render_add_token (render, "VALUES"); + sql_render_add_list (render, FALSE, NULL, obj->values, ","); + } + else + sql_render_add_token (render, "DEFAULT VALUES"); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_insert_set_table_from_name (SqlInsert * obj, const gchar * table) +{ + g_return_if_fail (SQL_IS_INSERT (obj)); + g_return_if_fail (table); + + sql_object_remove (obj, obj->table); + obj->table = sql_object_add (obj, sql_table_new (table)); +} + +void sql_insert_add_expr (SqlInsert * obj, SqlExpr * expr) +{ + g_return_if_fail (SQL_IS_INSERT (obj)); + g_return_if_fail (SQL_IS_EXPR (expr) || !expr); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TABLE = 1 + ,PROP_FIELDS + ,PROP_VALUES +}; + +static void sql_insert_set_property (SqlInsert * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TABLE: + sql_object_remove (obj, obj->table); + obj->table = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_FIELDS: + sql_object_remove (obj, obj->fields); + obj->fields = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_VALUES: + sql_object_remove (obj, obj->values); + obj->values = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_insert_get_property (SqlInsert * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TABLE: + g_value_set_object (value, obj->table); + break; + case PROP_FIELDS: + g_value_set_object (value, obj->fields); + break; + case PROP_VALUES: + g_value_set_object (value, obj->values); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_insert_init (SqlInsert * obj) +{ + obj->table = NULL; + obj->fields = NULL; + obj->values = NULL; +} + +static void sql_insert_finalize (SqlInsert * obj) +{ + sql_object_remove (obj, obj->table); + sql_object_remove (obj, obj->fields); + sql_object_remove (obj, obj->values); + G_OBJECT_CLASS (sql_insert_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_insert_class_init (SqlInsertClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_insert_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_insert_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_insert_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_insert_render; + + g_object_class_install_property (k, PROP_TABLE, + sql_param_object ("table" + ,"Table" + ,"The table where the row is inserted" + ,SQL_TYPE_TABLE + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_FIELDS, + sql_param_list ("fields" + ,"Fields" + ,"The list of fields" + ,SQL_TYPE_FIELD + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); + g_object_class_install_property (k, PROP_VALUES, + sql_param_list ("values" + ,"Values" + ,"The list of values" + ,SQL_TYPE_SET + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} diff --git a/sql/sql-insert.h b/sql/sql-insert.h new file mode 100644 index 0000000..3be108a --- /dev/null +++ b/sql/sql-insert.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_INSERT_H +#define SQL_INSERT_H + +#define SQL_TYPE_INSERT (sql_insert_get_type ()) +#define SQL_INSERT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_INSERT, SqlInsert)) +#define SQL_IS_INSERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_INSERT)) + +#include "sql-stmt.h" +#include "sql-field.h" +#include "sql-value.h" +#include "sql-table.h" +#include "sql-set.h" + +typedef struct _SqlInsert SqlInsert; +typedef struct _SqlInsertClass SqlInsertClass; + +/** + * SqlInsert: + * @table: an #SqlTable + * @field: (element-type Sql.Field): list of #SqlField + * @expr: (element-type Sql.Expr): list of #SqlExpr + **/ +struct _SqlInsert +{ + SqlStmt parent; + SqlTable * table; + SqlList * fields; // List of SqlField + SqlList * values; // List of SqlSet +}; + +struct _SqlInsertClass +{ + /* */ + SqlStmtClass parent; +}; + +GType sql_insert_get_type (); +SqlObject * sql_insert_new (); +void sql_insert_set_table_from_name (SqlInsert * obj, const gchar * table); + +#endif \ No newline at end of file diff --git a/sql/sql-join.c b/sql/sql-join.c new file mode 100644 index 0000000..4836962 --- /dev/null +++ b/sql/sql-join.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-join.h" +#include "sql-field.h" + +/** + * SECTION: sql-join + * @Short_description: the equivalent of the SQL JOIN. + * @Title: SqlJoin + * + * The #SqlJoin represents a joins between any targets. + **/ +G_DEFINE_TYPE (SqlJoin, sql_join, SQL_TYPE_TARGET); + +SqlTarget * sql_join_new (SqlTarget * left, SqlTarget * right, SqlJoinType type) +{ + return g_object_new (SQL_TYPE_JOIN + ,"target-left" ,left + ,"target-right" ,right + ,"join-type" ,type + ,NULL + ); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static const gchar * SQL_JOIN_TYPE[] = +{ + "INNER" + ,"LEFT" + ,"RIGHT" +}; + +static void sql_join_render (SqlJoin * obj, SqlRender * render) +{ + sql_render_add_item (render, TRUE, NULL, obj->target_left); + sql_render_add_token (render, SQL_JOIN_TYPE[obj->type]); + sql_render_add_token (render, "JOIN"); + sql_render_add_item (render, TRUE, NULL, obj->target_right); + + if (obj->has_using) + { + sql_render_add_token (render, "USING"); + sql_render_append (render, "("); + sql_render_add_list (render, TRUE, NULL, obj->using_fields, ","); + sql_render_append (render, ")"); + } + else + sql_render_add_item (render, FALSE, "ON", obj->condition); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_join_set_target_left (SqlJoin * obj, SqlTarget * target) +{ + g_return_if_fail (SQL_IS_JOIN (obj)); + g_return_if_fail (SQL_IS_TARGET (target) || SQL_IS_HOLDER (target) || !target); + + sql_object_remove (obj, obj->target_left); + obj->target_left = sql_object_add (obj, target); +} + +void sql_join_set_target_right (SqlJoin * obj, SqlTarget * target) +{ + g_return_if_fail (SQL_IS_JOIN (obj)); + g_return_if_fail (SQL_IS_TARGET (target) || SQL_IS_HOLDER (target) || !target); + + sql_object_remove (obj, obj->target_right); + obj->target_right = sql_object_add (obj, target); +} + +void sql_join_set_condition (SqlJoin * obj, SqlExpr * condition) +{ + g_return_if_fail (SQL_IS_JOIN (obj)); + g_return_if_fail (SQL_IS_EXPR (condition) || SQL_IS_HOLDER (condition) || !condition); + + sql_object_remove (obj, obj->condition); + obj->condition = sql_object_add (obj, condition); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TARGET_LEFT = 1 + ,PROP_TARGET_RIGHT + ,PROP_TYPE + ,PROP_CONDITION + ,PROP_HAS_USING + ,PROP_USING_FIELDS +}; + +static void sql_join_set_property (SqlJoin * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TARGET_LEFT: + sql_join_set_target_left (obj, g_value_get_object (value)); + break; + case PROP_TARGET_RIGHT: + sql_join_set_target_right (obj, g_value_get_object (value)); + break; + case PROP_TYPE: + obj->type = g_value_get_enum (value); + break; + case PROP_CONDITION: + sql_join_set_condition (obj, g_value_get_object (value)); + break; + case PROP_HAS_USING: + obj->has_using = g_value_get_boolean (value); + break; + case PROP_USING_FIELDS: + sql_object_remove (obj, obj->using_fields); + obj->using_fields = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_join_get_property (SqlJoin * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TARGET_LEFT: + g_value_set_object (value, obj->target_left); + break; + case PROP_TARGET_RIGHT: + g_value_set_object (value, obj->target_right); + break; + case PROP_TYPE: + g_value_set_enum (value, obj->type); + break; + case PROP_CONDITION: + g_value_set_object (value, obj->condition); + break; + case PROP_HAS_USING: + g_value_set_boolean (value, obj->has_using); + break; + case PROP_USING_FIELDS: + g_value_set_object (value, obj->using_fields); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_join_init (SqlJoin * obj) +{ + obj->target_left = NULL; + obj->target_right = NULL; + obj->condition = NULL; + obj->using_fields = NULL; +} + +static void sql_join_finalize (SqlJoin * obj) +{ + sql_object_remove (obj, obj->target_left); + sql_object_remove (obj, obj->target_right); + sql_object_remove (obj, obj->condition); + sql_object_remove (obj, obj->using_fields); + G_OBJECT_CLASS (sql_join_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_join_class_init (SqlJoinClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_join_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_join_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_join_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_join_render; + + g_object_class_install_property (k, PROP_TARGET_LEFT, + sql_param_list ("target-left" + ,"Left target" + ,"The left target in the join" + ,SQL_TYPE_TARGET + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_TARGET_RIGHT, + sql_param_list ("target-right" + ,"Right target" + ,"The right target in the join" + ,SQL_TYPE_TARGET + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_TYPE, + g_param_spec_enum ("join-type" + ,"Type" + ,"The type of join" + ,SQL_TYPE_JOIN_TYPE + ,SQL_JOIN_TYPE_INNER + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_CONDITION, + sql_param_list ("condition" + ,"Condition" + ,"The condition used for the join" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_HAS_USING, + g_param_spec_boolean ("has-using" + ,"Has using" + ,"Wether the condition is a USING" + ,FALSE + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); + g_object_class_install_property (k, PROP_CONDITION, + sql_param_list ("using-fields" + ,"Using fields" + ,"The list of fields of the USING" + ,SQL_TYPE_FIELD + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} + +GType sql_join_type_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GEnumValue values[] = + { + {SQL_JOIN_TYPE_INNER ,"SQL_JOIN_TYPE_INNER" ,"inner" + },{SQL_JOIN_TYPE_LEFT ,"SQL_JOIN_TYPE_LEFT" ,"left" + },{SQL_JOIN_TYPE_RIGHT ,"SQL_JOIN_TYPE_RIGHT" ,"right" + },{0, NULL, NULL} + }; + + type = g_enum_register_static + (g_intern_static_string ("SqlJoinType"), values); + } + + return type; +} diff --git a/sql/sql-join.h b/sql/sql-join.h new file mode 100644 index 0000000..91a929c --- /dev/null +++ b/sql/sql-join.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_JOIN_H +#define SQL_JOIN_H + +#include "sql-target.h" +#include "sql-expr.h" + +#define SQL_TYPE_JOIN (sql_join_get_type ()) +#define SQL_IS_JOIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_JOIN)) +#define SQL_JOIN(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_JOIN, SqlJoin)) + +#define SQL_TYPE_JOIN_TYPE (sql_join_type_get_type ()) + +typedef struct _SqlJoin SqlJoin; +typedef struct _SqlJoinClass SqlJoinClass; + +typedef enum +{ + SQL_JOIN_TYPE_INNER + ,SQL_JOIN_TYPE_LEFT + ,SQL_JOIN_TYPE_RIGHT + ,SQL_JOIN_TYPE_COUNT +} +SqlJoinType; + +struct _SqlJoin +{ + SqlTarget parent; + SqlTarget * target_left; + SqlTarget * target_right; + SqlJoinType type; + SqlExpr * condition; + gboolean has_using; + SqlList * using_fields; +}; + +struct _SqlJoinClass +{ + /* */ + SqlTargetClass parent; +}; + +GType sql_join_get_type (); +GType sql_join_type_get_type (); + +SqlTarget * sql_join_new (SqlTarget * left, SqlTarget * right, SqlJoinType type); +void sql_join_set_condition (SqlJoin * obj, SqlExpr * condition); +void sql_join_set_target_right (SqlJoin * obj, SqlTarget * target); +void sql_join_set_target_left (SqlJoin * obj, SqlTarget * target); + +#endif \ No newline at end of file diff --git a/sql/sql-list.c b/sql/sql-list.c new file mode 100644 index 0000000..0b52408 --- /dev/null +++ b/sql/sql-list.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-list.h" + +/** + * SECTION: sql-list + * @Short_description: Container for a list of #SqlObject of a certain type + * @Title: SqlList + **/ +G_DEFINE_TYPE (SqlList, sql_list, SQL_TYPE_OBJECT); + +SqlList * sql_list_new (GType gtype) +{ + return g_object_new (SQL_TYPE_LIST, "gtype", gtype, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_list_render (SqlList * obj, SqlRender * render) +{ + GList * i; + + for (i = obj->items.tail; i; i = i->next) + { + sql_render_add_object (render, i->data); + + if (i->next) + sql_render_append (render, ", "); + } +} + +static gboolean sql_list_is_ready (SqlList * obj) +{ + GList * i; + + for (i = obj->items.tail; i; i = i->next) + if (!sql_object_is_ready (i->data)) + return FALSE; + + return TRUE; +} + +static void sql_list_item_changed (SqlObject * item, SqlObject * obj) +{ + sql_object_changed (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_list_add: + * @obj: the #SqlList + * @item: (allow-none): the #SqlObject + * + * Adds an item to the end of the list. + **/ +void sql_list_add (SqlList * obj, gpointer item) +{ + g_return_if_fail (SQL_IS_LIST (obj)); + + sql_list_insert (obj, item, -1); +} + +/** + * sql_list_insert: + * @obj: the #SqlList + * @item: (allow-none): the #SqlObject + * @n: the position in which to place the item + * + * Adds an item to the list. If position is a negative number the element will + * be added to the end of the list. + **/ +void sql_list_insert (SqlList * obj, gpointer item, guint n) +{ + g_return_if_fail (SQL_IS_LIST (obj)); + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (item, obj->gtype) || SQL_IS_HOLDER (item) || !item); + + if (n > 0) + g_queue_push_nth (&obj->items, g_object_ref_sink (item), n); + else + g_queue_push_head (&obj->items, g_object_ref_sink (item)); + + g_signal_connect (item, "changed", + G_CALLBACK (sql_list_item_changed), obj + ); +} + +/** + * sql_list_get: + * @obj: the #SqlList + * + * Gets an item from the list. + * + * Return value: (transfer none) (allow-none): the selected #SqlObject + **/ +gpointer sql_list_get (SqlList * obj, guint n) +{ + g_return_val_if_fail (SQL_IS_LIST (obj), NULL); + + return g_queue_peek_nth (&obj->items, n); +} + +/** + * sql_list_remove: + * @obj: the #SqlList + * + * Removes an item from the list. + * + * Return value: (transfer none) (allow-none): the removed #SqlObject + **/ +gpointer sql_list_remove (SqlList * obj, guint n) +{ + SqlObject * item; + + g_return_val_if_fail (SQL_IS_LIST (obj), NULL); + + item = g_queue_pop_nth (&obj->items, n); + g_signal_handlers_disconnect_by_func (item, + sql_list_item_changed, obj); + + return item; +} + +/** + * sql_list_get_items: + * @obj: the #SqlList + * + * Gets all list elements. + * + * Return value: (transfer none) (allow-none): the list items + **/ +GList * sql_list_get_items (SqlList * obj) +{ + g_return_val_if_fail (SQL_IS_LIST (obj), NULL); + + return obj->items.tail; +} + +/** + * sql_list_get_items_type: + * @obj: the #SqlList + * + * Gets the allowed type for the list elements. + * + * Return value: the #GType + **/ +GType sql_list_get_items_type (SqlList * obj) +{ + g_return_val_if_fail (SQL_IS_LIST (obj), 0); + + return obj->gtype; +} + +/** + * sql_list_lenght: + * @obj: the #SqlList + * + * Gets the number of items in the list. + * + * Return value: the number of items + **/ +guint sql_list_length (SqlList * obj) +{ + g_return_val_if_fail (SQL_IS_LIST (obj), 0); + + return obj->items.length; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_GTYPE = 1 + ,PROP_LENGTH +}; + +static void sql_list_set_property (SqlList * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_GTYPE: + obj->gtype = g_value_get_gtype (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_list_get_property (SqlList * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_GTYPE: + g_value_set_gtype (value, obj->gtype); + break; + case PROP_LENGTH: + g_value_set_uint (value, sql_list_length (obj)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_list_init (SqlList * obj) +{ + g_queue_init (&obj->items); +} + +static void sql_list_finalize (SqlList * obj) +{ + GList * i; + + for (i = obj->items.tail; i; i = i->next) + { + g_signal_handlers_disconnect_by_func (i->data, + sql_list_item_changed, obj); + g_object_unref (i->data); + } + + g_queue_clear (&obj->items); + G_OBJECT_CLASS (sql_list_finalize)->finalize (G_OBJECT (obj)); +} + +static void sql_list_class_init (SqlListClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) sql_list_finalize; + klass->set_property = (GObjectSetPropertyFunc) sql_list_set_property; + klass->get_property = (GObjectGetPropertyFunc) sql_list_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_list_render; + SQL_OBJECT_CLASS (klass)->is_ready = (SqlObjectIsReadyFunc) sql_list_is_ready; + + g_object_class_install_property (klass, PROP_LENGTH, + g_param_spec_uint ("length" + ,_("Length") + ,_("The length of the list") + ,0, G_MAXUINT, 0 + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_GTYPE, + g_param_spec_gtype ("gtype" + ,_("GType") + ,_("The allowed type for the items") + ,SQL_TYPE_OBJECT + ,G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/sql/sql-list.h b/sql/sql-list.h new file mode 100644 index 0000000..d1db8af --- /dev/null +++ b/sql/sql-list.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_LIST_H +#define SQL_LIST_H + +#define SQL_TYPE_LIST (sql_list_get_type ()) +#define SQL_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_LIST, SqlList)) +#define SQL_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_LIST)) + +typedef struct _SqlList SqlList; +typedef struct _SqlListClass SqlListClass; + +#include "sql-object.h" + +struct _SqlList +{ + SqlObject parent; + GType gtype; + GQueue items; +}; + +struct _SqlListClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_list_get_type (); +SqlList * sql_list_new (GType gtype); +void sql_list_add (SqlList * obj, gpointer item); +void sql_list_insert (SqlList * obj, gpointer item, guint n); +gpointer sql_list_get (SqlList * obj, guint n); +gpointer sql_list_remove (SqlList * obj, guint n); +GList * sql_list_get_items (SqlList * obj); +GType sql_list_get_items_type (SqlList * obj); +guint sql_list_length (SqlList * obj); + +#endif \ No newline at end of file diff --git a/sql/sql-multi-stmt.c b/sql/sql-multi-stmt.c new file mode 100644 index 0000000..2f7047e --- /dev/null +++ b/sql/sql-multi-stmt.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-multi-stmt.h" + +/** + * SECTION: sql-multi-stmt + * @Short_description: the equivalent multiple statements separated by semicolon. + * @Title: SqlMultiStmt + * + * The #SqlMultiStmt represents multiple SQL statements separated by semicolon. + **/ +G_DEFINE_TYPE (SqlMultiStmt, sql_multi_stmt, SQL_TYPE_STMT); + +SqlMultiStmt * sql_multi_stmt_new () +{ + return g_object_new (SQL_TYPE_MULTI_STMT, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_multi_stmt_render (SqlMultiStmt * obj, SqlRender * render) +{ + sql_render_add_list (render, TRUE, NULL, obj->stmts, ";"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_STMTS = 1 +}; + +static void sql_mutli_stmt_set_property (SqlMultiStmt * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_STMTS: + sql_object_remove (obj, obj->stmts); + obj->stmts = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_mutli_stmt_get_property (SqlMultiStmt * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_STMTS: + g_value_set_object (value, obj->stmts); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_multi_stmt_init (SqlMultiStmt * obj) +{ + obj->stmts = NULL; +} + +static void sql_multi_stmt_finalize (SqlMultiStmt * obj) +{ + sql_object_remove (obj, obj->stmts); + G_OBJECT_CLASS (sql_multi_stmt_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_multi_stmt_class_init (SqlMultiStmtClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_multi_stmt_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_mutli_stmt_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_mutli_stmt_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_multi_stmt_render; + + g_object_class_install_property (k, PROP_STMTS, + sql_param_list ("stmts" + ,"Statements" + ,"The list of statements" + ,SQL_TYPE_STMT + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT + )); +} diff --git a/sql/sql-multi-stmt.h b/sql/sql-multi-stmt.h new file mode 100644 index 0000000..2b544be --- /dev/null +++ b/sql/sql-multi-stmt.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_MULTI_STMT_H +#define SQL_MULTI_STMT_H + +#include "sql-stmt.h" + +#define SQL_TYPE_MULTI_STMT (sql_multi_stmt_get_type ()) +#define SQL_MULTI_STMT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_MULTI_STMT, SqlMultiStmt)) +#define SQL_IS_MULTI_STMT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_MULTI_STMT)) + +typedef struct _SqlMultiStmt SqlMultiStmt; +typedef struct _SqlMultiStmtClass SqlMultiStmtClass; + +/** + * SqlMultiStmt: + * @stmt: (element-type Sql.Stmt): list of #SqlStmt + **/ +struct _SqlMultiStmt +{ + SqlStmt parent; + SqlList * stmts; +}; + +struct _SqlMultiStmtClass +{ + /* */ + SqlStmtClass parent; +}; + +GType sql_multi_stmt_get_type (); +SqlMultiStmt * sql_multi_stmt_new (); + +#endif diff --git a/sql/sql-object.c b/sql/sql-object.c new file mode 100644 index 0000000..d3363b0 --- /dev/null +++ b/sql/sql-object.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-object.h" + +/** + * SECTION: sql-object + * @Short_description: represents an SQL statement or a part of it + * @Title: SqlObject + * + * The #SqlObject is the base class for all objects in SqlLib. + **/ +G_DEFINE_ABSTRACT_TYPE (SqlObject, sql_object, G_TYPE_INITIALLY_UNOWNED); + +enum { + CHANGED + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_object_child_changed (SqlObject * child, SqlObject * obj) +{ + sql_object_changed (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Protected + +/** + * sql_object_add: + * @obj: #SqlObject where the new item will be added + * @child: #SqlObject the object is added + * + * Note that this function is considered a protected method and should only + * be used in classes that inherit from #SqlObjectClass. + **/ +gpointer sql_object_add (gpointer obj, gpointer child) +{ + if (child) + { + g_object_ref_sink (child); + g_signal_connect (child, "changed", + G_CALLBACK (sql_object_child_changed), obj); + } + + return child; +} + +/** + * sql_object_remove: + * @obj: #SqlObject where the new item will be added + * @child: #SqlObject the object is added + * + * Note that this function is considered a protected method and should only + * be used in classes that inherit from #SqlObjectClass. + **/ +void sql_object_remove (gpointer obj, gpointer child) +{ + if (child) + { + g_signal_handlers_disconnect_by_func (child, + sql_object_child_changed, obj); + g_object_unref (child); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_object_render: + * @obj: a #SqlObject + * @render: an #SqlRender + * + * Renders the object into a SQL string and stores it into the renderer. + **/ +void sql_object_render (SqlObject * obj, SqlRender * render) +{ + g_return_if_fail (SQL_IS_OBJECT (obj)); + g_return_if_fail (SQL_IS_RENDER (render)); + +// SQL_OBJECT_GET_CLASS (obj)->render (obj, render); +} + +/** + * sql_object_is_ready: + * @obj: a #SqlObject + * + * Checks if @obj and all of its elemens are ready to be rendered. + * + * Return value: %TRUE if ready, %FALSE otherwise. + **/ +gboolean sql_object_is_ready (SqlObject * obj) +{ + SqlObjectClass * klass = SQL_OBJECT_GET_CLASS (obj); + + g_return_val_if_fail (SQL_IS_OBJECT (obj), FALSE); + + if (obj->held_objects) + { + GList * i; + GList * held_objects = g_hash_table_get_values (obj->held_objects); + gboolean is_ready = TRUE; + + for (i = held_objects; i; i = i->next) + if (!sql_object_is_ready (i->data)) + { + is_ready = FALSE; + break; + } + + g_list_free (held_objects); + + if (!is_ready) + return FALSE; + } + + if (klass->is_ready) + return klass->is_ready (obj); + else + return TRUE; +} + +void sql_object_set (SqlObject * obj, const gchar * property, SqlObject * value) +{ + g_return_if_fail (SQL_IS_OBJECT (obj)); + g_return_if_fail (SQL_IS_OBJECT (value)); + + g_object_set (obj, property, value, NULL); +} + + +SqlObject * sql_object_get (SqlObject * obj, const gchar * property) +{ + SqlObject * value; + + g_return_val_if_fail (SQL_IS_OBJECT (obj), NULL); + + g_object_get (obj, property, &value, NULL); + return value; +} + +void sql_object_add_child (SqlObject * obj, const gchar * property, SqlObject * child) +{ + SqlList * list; + + g_return_if_fail (SQL_IS_OBJECT (obj)); + g_return_if_fail (SQL_IS_OBJECT (child)); + + g_object_get (obj, property, &list, NULL); + sql_list_add (list, child); +} + +void sql_object_remove_child (SqlObject * obj, const gchar * property, guint n) +{ + SqlList * list; + + g_return_if_fail (SQL_IS_OBJECT (obj)); + + g_object_get (obj, property, &list, NULL); + sql_list_remove (list, n); +} + +/** + * sql_object_add_held_object: + * @obj: a #SqlObject + * @id: the id of the #SqlHolder + * @held_object: the held object + * + * Adds a held object. + **/ +void sql_object_add_held_object (SqlObject * obj, const gchar * id, SqlObject * held_object) +{ + g_return_if_fail (SQL_IS_OBJECT (obj)); + g_return_if_fail (id); + + if (!obj->held_objects) + obj->held_objects = g_hash_table_new_full ( + g_str_hash + ,g_str_equal + ,g_free + ,g_object_unref + ); + + g_signal_connect (held_object, "changed", + G_CALLBACK (sql_object_child_changed), obj + ); + g_hash_table_replace (obj->held_objects, + g_strdup (id), g_object_ref_sink (held_object)); +} + +/** + * sql_object_get_held_object: + * @obj: a #SqlObject + * @id: the id of the #SqlHolder + * + * Gets a held object by its id. + * + * Return value: the #SqlObject if an object with that id exists, %NULL otherwise + **/ +SqlObject * sql_object_get_held_object (SqlObject * obj, const gchar * id) +{ + g_return_val_if_fail (SQL_IS_OBJECT (obj), NULL); + g_return_val_if_fail (id, NULL); + + if (obj->held_objects) + return g_hash_table_lookup (obj->held_objects, id); + + return NULL; +} + +/** + * sql_object_changed: + * @obj: a #SqlObject + * + * Emits the changed signal on #SqlObject. + **/ +void sql_object_changed (SqlObject * obj) +{ + g_signal_emit (obj, signals[CHANGED], 0); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_object_init (SqlObject * obj) +{ + obj->child_groups = NULL; + obj->held_objects = NULL; +} + +static void sql_object_finalize (SqlObject * obj) +{ + if (obj->child_groups) + g_hash_table_destroy (obj->child_groups); + if (obj->held_objects) + g_hash_table_destroy (obj->held_objects); + + G_OBJECT_CLASS (sql_object_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_object_class_init (SqlObjectClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) sql_object_finalize; + klass->is_ready = NULL; + klass->render = NULL; + + /** + * SqlObject::changed: + * @obj: the object which emit the signal. + * + * This signal is emitted every time the statement or one of its attributes + * is modified. + */ + signals[CHANGED] = g_signal_new ("changed", SQL_TYPE_OBJECT, + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); +} diff --git a/sql/sql-object.h b/sql/sql-object.h new file mode 100644 index 0000000..fc81513 --- /dev/null +++ b/sql/sql-object.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_OBJECT_H +#define SQL_OBJECT_H + +#include +#include "sql-param-object.h" +#include "sql-param-list.h" + +#define SQL_TYPE_OBJECT (sql_object_get_type ()) +#define SQL_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_OBJECT, SqlObject)) +#define SQL_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_OBJECT)) +#define SQL_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, SQL_TYPE_OBJECT, SqlObjectClass)) +#define SQL_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, SQL_TYPE_OBJECT, SqlObjectClass)) + +typedef struct _SqlObject SqlObject; +typedef struct _SqlObjectClass SqlObjectClass; + +typedef gboolean (* SqlObjectIsReadyFunc) (SqlObject * obj); + +struct _SqlObject +{ + GInitiallyUnowned parent; + GHashTable * child_groups; + GHashTable * held_objects; +}; + +struct _SqlObjectClass +{ + /* */ + GInitiallyUnownedClass parent; + gpointer /* SqlRenderFunc */ render; + SqlObjectIsReadyFunc is_ready; +}; + +#include "sql-holder.h" +#include "sql-list.h" +#include "sql-render.h" + +GType sql_object_get_type (); +void sql_object_render (SqlObject * obj, SqlRender * render); +gboolean sql_object_is_ready (SqlObject * obj); + +void sql_object_set (SqlObject * obj, const gchar * property, SqlObject * set); +SqlObject * sql_object_get (SqlObject * obj, const gchar * property); +void sql_object_add_child (SqlObject * obj, const gchar * property, SqlObject * child); +void sql_object_remove_child (SqlObject * obj, const gchar * property, guint n); + +void sql_object_add_held_object (SqlObject * obj, const gchar * id, SqlObject * held_object); +SqlObject * sql_object_get_held_object (SqlObject * obj, const gchar * id); +void sql_object_changed (SqlObject * obj); + +gpointer sql_object_add (gpointer obj, gpointer child); +void sql_object_remove (gpointer obj, gpointer child); + +#endif diff --git a/sql/sql-operation.c b/sql/sql-operation.c new file mode 100644 index 0000000..b0fa96f --- /dev/null +++ b/sql/sql-operation.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-operation.h" +#include "sql-value.h" + +/** + * SECTION: sql-operation + * @Short_description: operations between #SqlExpr + * @Title: SqlOperation + * + * Any operation over or between one or more #SqlExpr is represented by the + * #SqlOperation of the corresponding type. To see the different types of + * #SqlOperation see #SqlOperationType. + **/ +G_DEFINE_TYPE (SqlOperation, sql_operation, SQL_TYPE_EXPR); + +SqlObject * sql_operation_new (SqlOperationType type) +{ + return g_object_new (SQL_TYPE_OPERATION, "type", type, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static const gchar * SQL_OPERATION_TYPE[] = +{ + "NOT" + ,"-" + ,"+" + + ,"*" + ,"/" + ,"+" + ,"-" + ,"IS" + ,"=" + ,"!=" + ,">=" + ,">" + ,"<=" + ,"<" + ,"LIKE" + ,"AND" + ,"OR" + ,"XOR" + ,"%" + ,"IN" +}; + +static void sql_operation_render (SqlOperation * obj, SqlRender * render) +{ + sql_render_add_espace (render); + sql_render_append (render, "("); + sql_render_add_list (render, TRUE, NULL, obj->operators, + SQL_OPERATION_TYPE[obj->type]); + sql_render_append (render, ")"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_TYPE = 1 + ,PROP_OPERATORS +}; + +static void sql_operation_set_property (SqlOperation * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TYPE: + obj->type = g_value_get_enum (value); + break; + case PROP_OPERATORS: + sql_object_remove (obj, obj->operators); + obj->operators = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_operation_get_property (SqlOperation * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_TYPE: + g_value_set_enum (value, obj->type); + break; + case PROP_OPERATORS: + g_value_set_object (value, obj->operators); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_operation_init (SqlOperation * obj) +{ + obj->operators = NULL; +} + +static void sql_operation_finalize (SqlOperation * obj) +{ + sql_object_remove (obj, obj->operators); + G_OBJECT_CLASS (sql_operation_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_operation_class_init (SqlOperationClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_operation_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_operation_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_operation_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_operation_render; + + g_object_class_install_property (k, PROP_TYPE, + g_param_spec_enum ("type" + ,"Type" + ,"The type of the operation" + ,SQL_TYPE_OPERATION_TYPE + ,SQL_OPERATION_TYPE_AND + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_OPERATORS, + sql_param_list ("operators" + ,"Operators" + ,"The list of operators" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); +} + +GType sql_operation_type_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GEnumValue values[] = + { + {SQL_OPERATION_TYPE_NOT ,"SQL_OPERATION_TYPE_NOT" ,"not" + },{SQL_OPERATION_TYPE_MINUS ,"SQL_OPERATION_TYPE_MINUS" ,"minus" + },{SQL_OPERATION_TYPE_PLUS ,"SQL_OPERATION_TYPE_PLUS" ,"plus" + },{SQL_OPERATION_TYPE_MULTIPLICATION ,"SQL_OPERATION_TYPE_MULTIPLICATION" ,"multiplication" + },{SQL_OPERATION_TYPE_DIVISION ,"SQL_OPERATION_TYPE_DIVISION" ,"division" + },{SQL_OPERATION_TYPE_SUM ,"SQL_OPERATION_TYPE_SUM" ,"sum" + },{SQL_OPERATION_TYPE_SUBTRACTION ,"SQL_OPERATION_TYPE_SUBTRACTION" ,"subtraction" + },{SQL_OPERATION_TYPE_IS ,"SQL_OPERATION_TYPE_IS" ,"is" + },{SQL_OPERATION_TYPE_EQUAL ,"SQL_OPERATION_TYPE_EQUAL" ,"equal" + },{SQL_OPERATION_TYPE_NOT_EQUAL ,"SQL_OPERATION_TYPE_NOT_EQUAL" ,"not-equal" + },{SQL_OPERATION_TYPE_GREATER_EQUAL ,"SQL_OPERATION_TYPE_GREATER_EQUAL" ,"greater-equal" + },{SQL_OPERATION_TYPE_GREATER ,"SQL_OPERATION_TYPE_GREATER" ,"greater" + },{SQL_OPERATION_TYPE_LOWER_EQUAL ,"SQL_OPERATION_TYPE_LOWER_EQUAL" ,"lower-equal" + },{SQL_OPERATION_TYPE_LOWER ,"SQL_OPERATION_TYPE_LOWER" ,"lower" + },{SQL_OPERATION_TYPE_LIKE ,"SQL_OPERATION_TYPE_LIKE" ,"like" + },{SQL_OPERATION_TYPE_AND ,"SQL_OPERATION_TYPE_AND" ,"and" + },{SQL_OPERATION_TYPE_OR ,"SQL_OPERATION_TYPE_OR" ,"or" + },{SQL_OPERATION_TYPE_XOR ,"SQL_OPERATION_TYPE_XOR" ,"xor" + },{SQL_OPERATION_TYPE_MOD ,"SQL_OPERATION_TYPE_MOD" ,"mod" + },{SQL_OPERATION_TYPE_IN ,"SQL_OPERATION_TYPE_IN" ,"in" + },{0, NULL, NULL} + }; + + type = g_enum_register_static + (g_intern_static_string ("SqlOperationType"), values); + } + + return type; +} diff --git a/sql/sql-operation.h b/sql/sql-operation.h new file mode 100644 index 0000000..78da757 --- /dev/null +++ b/sql/sql-operation.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_OPERATION_H +#define SQL_OPERATION_H + +#include "sql-expr.h" + +#define SQL_TYPE_OPERATION (sql_operation_get_type ()) +#define SQL_OPERATION(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_OPERATION, SqlOperation)) +#define SQL_IS_OPERATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_OPERATION)) + +#define SQL_TYPE_OPERATION_TYPE (sql_operation_type_get_type ()) + +typedef struct _SqlOperation SqlOperation; +typedef struct _SqlOperationClass SqlOperationClass; + +typedef enum +{ + // Unary + SQL_OPERATION_TYPE_NOT + ,SQL_OPERATION_TYPE_MINUS + ,SQL_OPERATION_TYPE_PLUS + + // Binary + ,SQL_OPERATION_TYPE_MULTIPLICATION + ,SQL_OPERATION_TYPE_DIVISION + ,SQL_OPERATION_TYPE_SUM + ,SQL_OPERATION_TYPE_SUBTRACTION + ,SQL_OPERATION_TYPE_IS + ,SQL_OPERATION_TYPE_EQUAL + ,SQL_OPERATION_TYPE_NOT_EQUAL + ,SQL_OPERATION_TYPE_GREATER_EQUAL + ,SQL_OPERATION_TYPE_GREATER + ,SQL_OPERATION_TYPE_LOWER_EQUAL + ,SQL_OPERATION_TYPE_LOWER + ,SQL_OPERATION_TYPE_LIKE + ,SQL_OPERATION_TYPE_AND + ,SQL_OPERATION_TYPE_OR + ,SQL_OPERATION_TYPE_XOR + ,SQL_OPERATION_TYPE_MOD + ,SQL_OPERATION_TYPE_IN + ,SQL_OPERATION_TYPE_COUNT +} +SqlOperationType; + +/** + * SqlOperation: + * @expr: (element-type Sql.Expr): list of #SqlExpr + * @type: the type of operation. See #SqlOperationType for the possible values + **/ +struct _SqlOperation +{ + SqlExpr parent; + SqlList * operators; // List of SqlExpr + SqlOperationType type; +}; + +struct _SqlOperationClass +{ + /* */ + SqlExprClass parent; +}; + +GType sql_operation_get_type (); +GType sql_operation_type_get_type (); + +SqlObject * sql_operation_new (SqlOperationType type); + +#endif diff --git a/sql/sql-param-list.c b/sql/sql-param-list.c new file mode 100644 index 0000000..61b5963 --- /dev/null +++ b/sql/sql-param-list.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-param-list.h" +#include "sql-holder.h" + +GParamSpec * +sql_param_list (const gchar * name, const gchar * nick, + const gchar * blurb, GType items_type, GParamFlags flags) +{ + SqlParamList * pspec; + + g_return_val_if_fail (g_type_is_a (items_type, SQL_TYPE_OBJECT), NULL); + + pspec = g_param_spec_internal (SQL_TYPE_PARAM_OBJECT + ,name + ,nick + ,blurb + ,flags + ); + + pspec->items_type = items_type; + G_PARAM_SPEC (pspec)->value_type = SQL_TYPE_LIST; + return G_PARAM_SPEC (pspec); +} + +static void +sql_param_list_init (GParamSpec * pspec) {} + +static void +sql_param_list_set_default (GParamSpec * pspec, GValue * value) +{ + SqlList * list = sql_list_new (SQL_PARAM_LIST (pspec)->items_type); + g_value_set_object (value, list); +} + +static gboolean +sql_param_list_validate (GParamSpec * pspec, GValue * value) +{ + GType type; + gboolean change; + gpointer object = g_value_get_object (value); + + if (!object) + return TRUE; + + type = G_OBJECT_TYPE (object); + + change = !( + (g_value_type_compatible (type, SQL_TYPE_LIST) + && SQL_PARAM_LIST (pspec)->items_type == sql_list_get_items_type (object)) + || g_value_type_compatible (type, SQL_TYPE_HOLDER) + ); + + if (change) + g_value_set_object (value, NULL); + + return change; +} + +static gint +sql_param_list_values_cmp (GParamSpec * pspec, const GValue * a, const GValue * b) +{ + guint8 * pa = g_value_get_object (a); + guint8 * pb = g_value_get_object (b); + + return pa < pb ? -1 : pa > pb; +} + +GType +sql_param_list_get_type () +{ + static GType type = 0; + + if (!type) + { + GParamSpecTypeInfo pspec_info = + { + sizeof (SqlParamList) + ,16 + ,sql_param_list_init + ,SQL_TYPE_LIST + ,NULL + ,sql_param_list_set_default + ,sql_param_list_validate + ,sql_param_list_values_cmp + }; + + type = g_param_type_register_static ( + g_intern_static_string ("SqlParamList"), &pspec_info); + } + + return type; +} + diff --git a/sql/sql-param-list.h b/sql/sql-param-list.h new file mode 100644 index 0000000..0067b6f --- /dev/null +++ b/sql/sql-param-list.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_PARAM_LIST_H +#define SQL_PARAM_LIST_H + +#include "sql-object.h" + +#define SQL_TYPE_PARAM_LIST (sql_param_list_get_type ()) +#define SQL_PARAM_LIST(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), SQL_TYPE_PARAM_LIST, SqlParamList)) + + +typedef struct +{ + GParamSpec parent; + GType items_type; +} +SqlParamList; + +GType sql_param_list_get_type (); +GParamSpec * sql_param_list (const gchar * name + ,const gchar * nick + ,const gchar * blurb + ,GType items_type + ,GParamFlags flags); + +#endif + diff --git a/sql/sql-param-object.c b/sql/sql-param-object.c new file mode 100644 index 0000000..490956e --- /dev/null +++ b/sql/sql-param-object.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-param-object.h" +#include "sql-holder.h" + +GParamSpec * +sql_param_object (const gchar * name, const gchar * nick, + const gchar * blurb, GType object_type, GParamFlags flags) +{ + GParamSpec * pspec; + + g_return_val_if_fail (g_type_is_a (object_type, SQL_TYPE_OBJECT), NULL); + + pspec = g_param_spec_internal (SQL_TYPE_PARAM_OBJECT + ,name + ,nick + ,blurb + ,flags + ); + + pspec->value_type = object_type; + return pspec; +} + +static void +sql_param_object_init (GParamSpec * pspec) {} + +static void +sql_param_object_set_default (GParamSpec * pspec, GValue * value) +{ + g_value_set_object (value, NULL); +} + +static gboolean +sql_param_object_validate (GParamSpec * pspec, GValue * value) +{ + GType type; + gboolean change; + GObject * object = g_value_get_object (value); + + if (!object) + return TRUE; + + type = G_OBJECT_TYPE (object); + + change = !( + g_value_type_compatible (type, G_PARAM_SPEC_VALUE_TYPE (pspec)) + || g_value_type_compatible (type, SQL_TYPE_HOLDER) + ); + + if (change) + g_value_set_object (value, NULL); + + return change; +} + +static gint +sql_param_object_values_cmp (GParamSpec * pspec, const GValue * a, const GValue * b) +{ + guint8 * pa = g_value_get_object (a); + guint8 * pb = g_value_get_object (b); + + return pa < pb ? -1 : pa > pb; +} + +GType +sql_param_object_get_type () +{ + static GType type = 0; + + if (!type) + { + GParamSpecTypeInfo pspec_info = + { + sizeof (SqlParamObject) + ,16 + ,sql_param_object_init + ,SQL_TYPE_OBJECT + ,NULL + ,sql_param_object_set_default + ,sql_param_object_validate + ,sql_param_object_values_cmp + }; + + type = g_param_type_register_static ( + g_intern_static_string ("SqlParamObject"), &pspec_info); + } + + return type; +} + diff --git a/sql/sql-param-object.h b/sql/sql-param-object.h new file mode 100644 index 0000000..72a5f07 --- /dev/null +++ b/sql/sql-param-object.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_PARAM_OBJECT_H +#define SQL_PARAM_OBJECT_H + +#include "sql-object.h" + +#define SQL_TYPE_PARAM_OBJECT (sql_param_object_get_type ()) +#define SQL_PARAM_OBJECT(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), SQL_TYPE_PARAM_OBJECT, SqlParamObject)) + +typedef struct +{ + GParamSpec parent; +} +SqlParamObject; + +GType sql_param_object_get_type (); +GParamSpec * sql_param_object (const gchar * name + ,const gchar * nick + ,const gchar * blurb + ,GType object_type + ,GParamFlags flags); + +#endif diff --git a/sql/sql-parser.h b/sql/sql-parser.h new file mode 100644 index 0000000..9c68dd2 --- /dev/null +++ b/sql/sql-parser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_PARSER_H +#define SQL_PARSER_H + +#include "sql-object.h" + +#define SQL_PARSER_LOG_DOMAIN (g_quark_from_string ("SqlParser")) + +/** + * sql_parser_parse: + * @sql: An string containing an SQL query. + * + * Parses @sql and makes an #SqlObject from the query on it. + * + * Return value: (transfer full): an #SqlObject. + */ +SqlObject * sql_parser_parse (gchar * sql) G_GNUC_WARN_UNUSED_RESULT; + +#endif \ No newline at end of file diff --git a/sql/sql-render.c b/sql/sql-render.c new file mode 100644 index 0000000..ce7dfa9 --- /dev/null +++ b/sql/sql-render.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-render.h" +#include + +/** + * SECTION: sql-render + * @Short_description: renders an #SqlObject to a string + * @Title: SqlRender + * + * #SqlRender takes care of rendering the different types of #SqlObject to a + * valid SQL string, ready to pass to a Database. In most cases the user won't + * need to use any method besides the creation and destruction ones and + * sql_render_get_string(). + **/ +G_DEFINE_TYPE (SqlRender, sql_render, G_TYPE_OBJECT); + +/** + * sql_render_new: + * @delimiter: the delimiter character + * + * Creates an #SqlRender object + * + * Return value: the new #SqlRender + **/ +SqlRender * sql_render_new (gchar delimiter) +{ + return g_object_new (SQL_TYPE_RENDER, "delimiter", delimiter, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_render_get_string: + * @obj: a #SqlRender + * @object: the #SqlObject + * @data: pointer to custom data + * @err: (out) (allow-none): the return location for a #GError or %NULL + * + * Transforms the #SqlObject into a SQL string. + * + * Return value: a string with the rendered statement of %NULL if error. + **/ +gchar * sql_render_get_string (SqlRender * obj, gpointer object, gpointer data, GError ** err) +{ + gchar * sql; + + g_return_val_if_fail (SQL_IS_RENDER (obj), NULL); + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + + obj->data = data; + obj->object = g_object_ref (object); + obj->buffer = g_string_sized_new (SQL_BUFFER_SIZE); + obj->ancestors = NULL; + + sql_render_add_object (obj, object); + + if (obj->error) + { + if (!err) + g_warning ("%s",obj->error->message); + else + g_propagate_error (err, obj->error); + + g_clear_error (&obj->error); + sql = g_string_free (obj->buffer, TRUE); + } + else + sql = g_string_free (obj->buffer, FALSE); + + g_clear_object (&obj->object); + g_slist_free (obj->ancestors); + obj->ancestors = NULL; + obj->buffer = NULL; + obj->data = NULL; + return sql; +} + +/** + * sql_render_register_function: + * @obj: a #SqlRender + * @type: #GType for which the function will be used + * @function: (scope async): render function to use with @type + * + * Registers @function as the function to be used to render the objects of type + * @type. Users most likely won't need to use it. + **/ +void sql_render_register_function (SqlRender * obj, GType type, SqlRenderFunc function) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + g_hash_table_insert (obj->custom_renderers, GUINT_TO_POINTER (type), function); +} + +/** + * sql_render_get_ancestors: + * @obj: the #SqlRender + * + * Obtains a list of parents of the currently rendered object, including it. + * + * Return value: (transfer none): the #GSList with the parents, the list should + * not be edited or freed. + **/ +GSList * sql_render_get_ancestors (SqlRender * obj) +{ + g_return_val_if_fail (SQL_IS_RENDER (obj), NULL); + + return obj->ancestors; +} + +/** + * sql_render_add_object: + * @obj: a #SqlRender + * @object: a #gpointer to an object + * + * Adds an object to a render. + **/ +void sql_render_add_object (SqlRender * obj, gpointer object) +{ + SqlRenderFunc function; + + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (G_IS_OBJECT (object) || !object); + + if (object) + { + function = g_hash_table_lookup (obj->custom_renderers, + GUINT_TO_POINTER (G_OBJECT_TYPE (object))); + obj->ancestors = g_slist_prepend (obj->ancestors, object); + + if (function) + function (object, obj); + else + sql_object_render (object, obj); + + obj->ancestors = g_slist_delete_link (obj->ancestors, obj->ancestors); + } +} + +/** + * sql_render_add_espace: + * @obj: a #SqlRender + * + * Adds an space character to a render. + **/ +void sql_render_add_espace (SqlRender * obj) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + gsize len = obj->buffer->len; + + if (len > 0) + switch (obj->buffer->str[len-1]) + { + case ' ': + case '(': + case '.': + return; + default: + g_string_append_c (obj->buffer, ' '); + } +} + +/** + * sql_render_printf: + * @obj: a #SqlRender + * @string: a format string. + * @...: a %NULL terminated list of variables. + * + * Creates an string from a format string. + **/ +void sql_render_printf (SqlRender * obj, const gchar * string, ...) +{ + va_list vl; + + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (string); + + va_start (vl, string); + sql_render_add_espace (obj); + g_string_append_vprintf (obj->buffer, string, vl); + va_end (vl); +} + +/** + * sql_render_append: + * @obj: a #SqlRender + * @string: a character string. + * + * Appends @string to the current contents of @obj. + **/ +void sql_render_append (SqlRender * obj, const gchar * string) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (string); + + g_string_append (obj->buffer, string); +} + +/** + * sql_render_append_with_delimiter: + * @obj: a #SqlRender + * @string: a character string. + * @delimiter: the delimiter character to use. + * + * Appends @string to the current contents of @obj. @delimiter is used to add + * spaces into the string depending where required. + **/ +void sql_render_append_with_delimiter (SqlRender * obj, const gchar * string, gchar delimiter) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + if (string) + { + sql_render_add_espace (obj); + g_string_append_c (obj->buffer, delimiter); + g_string_append (obj->buffer, string); + g_string_append_c (obj->buffer, delimiter); + } +} + +/** + * sql_render_add_token: + * @obj: a #SqlRender + * @token: an SQL reserved keyword + * + * Adds @token to @obj. Note that @token must be a reserved SQL keyword. + **/ +void sql_render_add_token (SqlRender * obj, const gchar * token) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + if (token) + { + sql_render_add_espace (obj); + g_string_append (obj->buffer, token); + } +} + +/** + * sql_render_add_identifier: + * @obj: a #SqlRender + * @identifier: a character string. + * + * Adds @identifier to @obj. @identifier is taken as the name of an identifier + * for an expression in an SQL string. + **/ +void sql_render_add_identifier (SqlRender * obj, const gchar * identifier) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + sql_render_append_with_delimiter (obj, identifier, + obj->delimiter); +} + +/** + * sql_render_add_item: + * @obj: a #SqlRender + * @required: + * @token: + * @item: + * + * + **/ +void sql_render_add_item (SqlRender * obj, gboolean required, const gchar * token, gpointer item) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (G_IS_OBJECT (item) || !item); + + if (item) + { + sql_render_add_token (obj, token); + sql_render_add_object (obj, item); + } + else if (required) + sql_render_set_error (obj); +} + +/** + * sql_render_add_list: + * @obj: a #SqlRender + * @required: + * @token: + * @list: (element-type GObject.Object): a list of objects to add + * @separator: + * + * + **/ +void sql_render_add_list (SqlRender * obj, gboolean required, const gchar * token, + SqlList * list, const gchar * separator) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (SQL_IS_LIST (list)); + + if (list) + { + GList * n; + sql_render_add_token (obj, token); + + for (n = sql_list_get_items (list); n; n = n->next) + { + sql_render_add_object (obj, n->data); + + if (n->next) + g_string_append_printf (obj->buffer, " %s", separator); + } + } + else if (required) + sql_render_set_error (obj); +} + +/** + * sql_render_add_list_with_func: + * @obj: a #SqlRender + * @required: + * @token: + * @list: (element-type GObject.Object): + * @separator: + * @function: (scope call): + * + * + **/ +void sql_render_add_list_with_func (SqlRender * obj, gboolean required, const gchar * token, + SqlList * list, const gchar * separator, SqlRenderFunc function) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + g_return_if_fail (SQL_IS_LIST (list)); + + if (list) + { + GList * n; + sql_render_add_token (obj, token); + + for (n = sql_list_get_items (list); n; n = n->next) + { + function (n->data, obj); + + if (n->next) + g_string_append_printf (obj->buffer, " %s", separator); + } + } + else if (required) + sql_render_set_error (obj); +} + +/** + * sql_render_set_error: + * @obj: a #SqlRender + **/ +void sql_render_set_error (SqlRender * obj) +{ + g_return_if_fail (SQL_IS_RENDER (obj)); + + obj->error = g_error_new ( + SQL_RENDER_LOG_DOMAIN + ,SQL_RENDER_ERROR + ,"An error ocurred during query render: [ %s ] <-- Error is here" + ,obj->buffer->str + ); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DELIMITER = 1 +}; + +static void sql_render_set_property (SqlRender * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DELIMITER: + obj->delimiter = g_value_get_schar (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_render_get_property (SqlRender * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DELIMITER: + g_value_set_schar (value, obj->delimiter); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_render_init (SqlRender * obj) +{ + obj->buffer = NULL; + obj->error = NULL; + obj->custom_renderers = g_hash_table_new ( + g_direct_hash + ,g_direct_equal + ); +} + +static void sql_render_finalize (SqlRender * obj) +{ + g_hash_table_unref (obj->custom_renderers); + G_OBJECT_CLASS (sql_render_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_render_class_init (SqlRenderClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_render_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_render_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_render_get_property; + + g_object_class_install_property (k, PROP_DELIMITER, + g_param_spec_char ("delimiter" + ,_("Delimiter") + ,_("The character used for delimite the name of fields, tables...") + ,G_MININT8 ,G_MAXINT8 ,'`' + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} diff --git a/sql/sql-render.h b/sql/sql-render.h new file mode 100644 index 0000000..da29abf --- /dev/null +++ b/sql/sql-render.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_RENDER_H +#define SQL_RENDER_H + +#include + +#define SQL_RENDER_LOG_DOMAIN (g_quark_from_string ("SqlRender")) + +#define SQL_TYPE_RENDER (sql_render_get_type ()) +#define SQL_RENDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_RENDER, SqlRender)) +#define SQL_IS_RENDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_RENDER)) + +#define SQL_BUFFER_SIZE 500 + +typedef struct _SqlRender SqlRender; +typedef struct _SqlRenderClass SqlRenderClass; + +typedef void (* SqlRenderFunc) (gpointer obj, SqlRender * render); + +#include "sql-object.h" + +/** + * SqlRender: + * @custom_renderers: (element-type GType Sql.RenderFunc): + **/ +struct _SqlRender +{ + GObject parent; + GError * error; + GString * buffer; + gpointer object; + GSList * ancestors; + gpointer data; + GHashTable * custom_renderers; + gint8 delimiter; +}; + +struct _SqlRenderClass +{ + /* */ + GObjectClass parent; +}; + +/** + * SqlRenderError: + * @SQL_RENDER_ERROR: an error from an #SqlRender + * + * Identifies the error codes for #SqlRender + **/ +typedef enum +{ + SQL_RENDER_ERROR +} +SqlRenderError; + +GType sql_render_get_type (); +SqlRender * sql_render_new (gchar delimiter); +gchar * sql_render_get_string (SqlRender * obj, gpointer object, gpointer data, GError ** err); +void sql_render_register_function (SqlRender * obj, GType type, SqlRenderFunc function); +GSList * sql_render_get_ancestors (SqlRender * obj); +void sql_render_add_espace (SqlRender * obj); +void sql_render_printf (SqlRender * obj, const gchar * string, ...); +void sql_render_append (SqlRender * obj, const gchar * string); +void sql_render_append_with_delimiter (SqlRender * obj, const gchar * string, gchar delimiter); +void sql_render_add_token (SqlRender * obj, const gchar * token); +void sql_render_add_identifier (SqlRender * obj, const gchar * identifier); +void sql_render_add_object (SqlRender * obj, gpointer object); +void sql_render_add_item (SqlRender * obj, gboolean required, const gchar * token, gpointer item); +void sql_render_add_list (SqlRender * obj, gboolean required, const gchar * token, SqlList * list, const gchar * separator); +void sql_render_add_list_with_func (SqlRender * obj, gboolean required, const gchar * token, SqlList * list, const gchar * separator, SqlRenderFunc function); +void sql_render_set_error (SqlRender * obj); + +#endif diff --git a/sql/sql-select-field.c b/sql/sql-select-field.c new file mode 100644 index 0000000..f6915d9 --- /dev/null +++ b/sql/sql-select-field.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-select-field.h" + +/** + * SECTION: sql-select_field + * @Short_description: + * @Title: SqlSelectField + **/ +G_DEFINE_TYPE (SqlSelectField, sql_select_field, SQL_TYPE_OBJECT); + +SqlSelectField * sql_select_field_new () +{ + return g_object_new (SQL_TYPE_SELECT_FIELD, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_select_field_render (SqlSelectField * obj, SqlRender * render) +{ + sql_render_add_object (render, obj->expr); + + if (obj->alias) + { + sql_render_add_token (render, "AS"); + sql_render_add_identifier (render, obj->alias); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_EXPR = 1 + ,PROP_ALIAS +}; + +static void sql_select_field_set_property (SqlSelectField * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPR: + sql_object_remove (obj, obj->expr); + obj->expr = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_ALIAS: + g_free (obj->alias); + obj->alias = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_select_field_get_property (SqlSelectField * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPR: + g_value_set_object (value, obj->expr); + break; + case PROP_ALIAS: + g_value_set_string (value, obj->alias); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_select_field_init (SqlSelectField * obj) +{ + obj->expr = NULL; + obj->alias = NULL; +} + +static void sql_select_field_finalize (SqlSelectField * obj) +{ + g_free (obj->alias); + sql_object_remove (obj, obj->expr); + G_OBJECT_CLASS (sql_select_field_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_select_field_class_init (SqlSelectFieldClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_select_field_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_select_field_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_select_field_get_property; + SQL_OBJECT_CLASS (k)->render = (SqlRenderFunc) sql_select_field_render; + + g_object_class_install_property (k, PROP_EXPR, + sql_param_object ("expr" + ,"Expression" + ,"The expression" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_ALIAS, + g_param_spec_string ("alias" + ,"Alias" + ,"The alias for the expression" + ,NULL + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-select-field.h b/sql/sql-select-field.h new file mode 100644 index 0000000..61ffd7d --- /dev/null +++ b/sql/sql-select-field.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_SELECT_FIELD_H +#define SQL_SELECT_FIELD_H + +#include "sql-object.h" +#include "sql-expr.h" + +#define SQL_TYPE_SELECT_FIELD (sql_select_field_get_type ()) +#define SQL_SELECT_FIELD(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SELECT_FIELD, SqlSelectField)) +#define SQL_IS_SELECT_FIELD(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SELECT_FIELD)) + +typedef struct _SqlSelectField SqlSelectField; +typedef struct _SqlSelectFieldClass SqlSelectFieldClass; + +struct _SqlSelectField +{ + SqlObject parent; + SqlExpr * expr; + gchar * alias; +}; + +struct _SqlSelectFieldClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_select_field_get_type (); +SqlSelectField * sql_select_field_new (); + +#endif diff --git a/sql/sql-select-order.c b/sql/sql-select-order.c new file mode 100644 index 0000000..8735789 --- /dev/null +++ b/sql/sql-select-order.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-select-order.h" + +/** + * SECTION: sql-select-order + * @Short_description: + * @Title: SqlSelectOrder + **/ +G_DEFINE_TYPE (SqlSelectOrder, sql_select_order, SQL_TYPE_OBJECT); + +SqlSelectOrder * sql_select_order_new () +{ + return g_object_new (SQL_TYPE_SELECT_ORDER, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_select_order_render (SqlSelectOrder * obj, SqlRender * render) +{ + sql_render_add_object (render, obj->expr); + + if (obj->way == SQL_SELECT_ORDER_DESC) + sql_render_add_token (render, "DESC"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_EXPR = 1 + ,PROP_WAY +}; + +static void sql_select_order_set_property (SqlSelectOrder * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPR: + sql_object_remove (obj, obj->expr); + obj->expr = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_WAY: + obj->way = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_select_order_get_property (SqlSelectOrder * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPR: + g_value_set_object (value, obj->expr); + break; + case PROP_WAY: + g_value_set_enum (value, obj->way); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_select_order_init (SqlSelectOrder * obj) +{ + obj->expr = NULL; +} + +static void sql_select_order_finalize (SqlSelectOrder * obj) +{ + sql_object_remove (obj, obj->expr); + G_OBJECT_CLASS (sql_select_order_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_select_order_class_init (SqlSelectOrderClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_select_order_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_select_order_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_select_order_get_property; + SQL_OBJECT_CLASS (k)->render = (SqlRenderFunc) sql_select_order_render; + + g_object_class_install_property (k, PROP_EXPR, + sql_param_object ("expr" + ,"Expression" + ,"The expression" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_WAY, + g_param_spec_enum ("way" + ,"Way" + ,"The order way" + ,SQL_TYPE_SELECT_ORDER_WAY + ,SQL_SELECT_ORDER_ASC + ,G_PARAM_READWRITE + )); +} + +GType sql_select_order_way_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GEnumValue values[] = + { + {SQL_SELECT_ORDER_ASC ,"SQL_SELECT_ORDER_ASC" ,"asc" + },{SQL_SELECT_ORDER_DESC ,"SQL_SELECT_ORDER_DESC" ,"desc" + },{0, NULL, NULL} + }; + + type = g_enum_register_static + (g_intern_static_string ("SqlSelectOrderWay"), values); + } + + return type; +} diff --git a/sql/sql-select-order.h b/sql/sql-select-order.h new file mode 100644 index 0000000..6454c79 --- /dev/null +++ b/sql/sql-select-order.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_SELECT_ORDER_H +#define SQL_SELECT_ORDER_H + +#include "sql-object.h" +#include "sql-expr.h" + +#define SQL_TYPE_SELECT_ORDER (sql_select_order_get_type ()) +#define SQL_SELECT_ORDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SELECT_ORDER, SqlSelectOrder)) +#define SQL_IS_SELECT_ORDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SELECT_ORDER)) + +#define SQL_TYPE_SELECT_ORDER_WAY (sql_select_order_way_get_type ()) + +typedef struct _SqlSelectOrder SqlSelectOrder; +typedef struct _SqlSelectOrderClass SqlSelectOrderClass; + +/** + * SqlSelectOrderWay: + * @SQL_SELECT_ORDER_ASC: ascendent order + * @SQL_SELECT_ORDER_DESC: descendent order + **/ +typedef enum +{ + SQL_SELECT_ORDER_ASC + ,SQL_SELECT_ORDER_DESC +} +SqlSelectOrderWay; + +struct _SqlSelectOrder +{ + SqlObject parent; + SqlExpr * expr; + SqlSelectOrderWay way; +}; + +struct _SqlSelectOrderClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_select_order_get_type (); +GType sql_select_order_way_get_type (); + +SqlSelectOrder * sql_select_order_new (); + +#endif diff --git a/sql/sql-select.c b/sql/sql-select.c new file mode 100644 index 0000000..dfc4179 --- /dev/null +++ b/sql/sql-select.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-select.h" + +/** + * SECTION: sql-select + * @Short_description: an SQL SELECT statement + * @Title: SqlSelect + * + * This object represents a SELECT SQL statement + **/ +G_DEFINE_TYPE (SqlSelect, sql_select, SQL_TYPE_DML); + +SqlObject * sql_select_new () +{ + return g_object_new (SQL_TYPE_SELECT, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static const char * SQL_SELECT_TYPE[] = +{ + "" + ,"UNION ALL" + ,"UNION ANY" + ,"INTERSECT" + ,"EXCEPT" +}; + +static void sql_select_render (SqlSelect * obj, SqlRender * render) +{ + sql_render_add_list (render, TRUE, "SELECT", obj->fields, ","); + sql_render_add_list (render, FALSE, "FROM", SQL_DML (obj)->targets, ","); + + if (SQL_DML (obj)->targets) + { + sql_render_add_item (render, FALSE, "WHERE", SQL_DML (obj)->where); + sql_render_add_list (render, FALSE, "GROUP BY", obj->group, ","); + sql_render_add_item (render, FALSE, "HAVING", obj->having); + sql_render_add_list (render, FALSE, "ORDER", obj->order, ","); + + if (obj->limit_count) + sql_render_printf (render, "LIMIT %u OFFSET %u" + ,obj->limit_count + ,obj->limit_offset + ); + } + + if (obj->next) + sql_render_add_item (render, FALSE, SQL_SELECT_TYPE[obj->type], obj->next); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_select_add_expr (SqlSelect * obj, SqlExpr * expr) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_EXPR (expr)); +} + +void sql_select_add_group (SqlSelect * obj, SqlExpr * expr) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_EXPR (expr)); +} + +void sql_select_set_having (SqlSelect * obj, SqlExpr * having) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_EXPR (having) || having); + + sql_object_remove (obj, obj->having); + obj->having = sql_object_add (obj, having); +} + +void sql_select_add_order (SqlSelect * obj, SqlExpr * expr, SqlSelectOrderWay way) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_EXPR (expr)); +} + +void sql_select_set_distinct (SqlSelect * obj, gboolean distinct) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + + obj->distinct = distinct; +} + +void sql_select_set_limit (SqlSelect * obj, guint count, guint offset) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + + obj->limit_count = count; + obj->limit_offset = offset; +} + +void sql_select_set_next (SqlSelect * obj, SqlSelect * next, SqlSelectType type) +{ + g_return_if_fail (SQL_IS_SELECT (obj)); + g_return_if_fail (SQL_IS_SELECT (next) || !next); + + sql_object_remove (obj, obj->next); + obj->next = sql_object_add (obj, next); + obj->type = next ? type : SQL_SELECT_NONE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DISTINCT = 1 + ,PROP_FIELDS + ,PROP_GROUP + ,PROP_HAVING + ,PROP_ORDER + ,PROP_LIMIT_COUNT + ,PROP_LIMIT_OFFSET + ,PROP_TYPE + ,PROP_NEXT +}; + +static void sql_select_set_property (SqlSelect * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DISTINCT: + obj->distinct = g_value_get_boolean (value); + break; + case PROP_FIELDS: + sql_object_remove (obj, obj->fields); + obj->fields = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_GROUP: + sql_object_remove (obj, obj->group); + obj->group = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_HAVING: + sql_select_set_having (obj, g_value_get_object (value)); + break; + case PROP_ORDER: + sql_object_remove (obj, obj->order); + obj->order = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_LIMIT_COUNT: + obj->limit_count = g_value_get_uint (value); + break; + case PROP_LIMIT_OFFSET: + obj->limit_offset = g_value_get_uint (value); + break; + case PROP_NEXT: + sql_select_set_next (obj, g_value_get_object (value), obj->type); + break; + case PROP_TYPE: + obj->type = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_select_get_property (SqlSelect * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DISTINCT: + g_value_set_boolean (value, obj->distinct); + break; + case PROP_FIELDS: + g_value_set_object (value, obj->fields); + break; + case PROP_GROUP: + g_value_set_object (value, obj->group); + break; + case PROP_HAVING: + g_value_set_object (value, obj->having); + break; + case PROP_ORDER: + g_value_set_object (value, obj->order); + break; + case PROP_LIMIT_COUNT: + g_value_set_uint (value, obj->limit_count); + break; + case PROP_LIMIT_OFFSET: + g_value_set_uint (value, obj->limit_offset); + break; + case PROP_NEXT: + g_value_set_object (value, obj->next); + break; + case PROP_TYPE: + g_value_set_int (value, obj->type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_select_init (SqlSelect * obj) +{ + obj->distinct = FALSE; + obj->fields = NULL; + obj->group = NULL; + obj->having = NULL; + obj->order = NULL; + obj->next = NULL; + obj->type = SQL_SELECT_NONE; +} + +static void sql_select_finalize (SqlSelect * obj) +{ + g_object_unref (obj->group); + g_object_unref (obj->order); + sql_object_remove (obj, obj->fields); + sql_object_remove (obj, obj->having); + sql_object_remove (obj, obj->next); + G_OBJECT_CLASS (sql_select_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_select_class_init (SqlSelectClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_select_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_select_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_select_get_property; + SQL_OBJECT_CLASS (k)->render = (SqlRenderFunc) sql_select_render; + + g_object_class_install_property (k, PROP_LIMIT_COUNT, + g_param_spec_boolean ("distinct" + ,"Distinct" + ,"Determines if the #SqlSelect uses the DISTINCT clause" + ,FALSE + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_FIELDS, + sql_param_list ("fields" + ,"Fields" + ,"The list of fields" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_GROUP, + sql_param_object ("group" + ,"Group" + ,"The GROUP BY section" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_HAVING, + sql_param_object ("having" + ,"Having" + ,"The HAVING clause" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_ORDER, + sql_param_list ("order" + ,"Order" + ,"The ORDER BY section" + ,SQL_TYPE_SELECT_ORDER + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_LIMIT_COUNT, + g_param_spec_uint ("limit-count" + ,"Limit count" + ,"The COUNT field of the LIMIT clause" + ,0, G_MAXUINT, 0 + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_LIMIT_OFFSET, + g_param_spec_uint ("limit-offset" + ,"Limit offset" + ,"The OFFSET field of the LIMIT clause" + ,0, G_MAXUINT, 0 + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_NEXT, + sql_param_object ("next" + ,"Next" + ,"The next #SqlSelect in case of a statement with more than one" + ,SQL_TYPE_SELECT + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_TYPE, + g_param_spec_int ("type" + ,"Type" + ,"One of the possible options of #SqlSelectType" + ,0, SQL_SELECT_COUNT - 1, 0 + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-select.h b/sql/sql-select.h new file mode 100644 index 0000000..420deb3 --- /dev/null +++ b/sql/sql-select.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_SELECT_H +#define SQL_SELECT_H + +#include "sql-dml.h" +#include "sql-select-field.h" +#include "sql-select-order.h" + +#define SQL_TYPE_SELECT (sql_select_get_type ()) +#define SQL_SELECT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SELECT, SqlSelect)) +#define SQL_IS_SELECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SELECT)) + +typedef struct _SqlSelect SqlSelect; +typedef struct _SqlSelectClass SqlSelectClass; + +typedef enum +{ + SQL_SELECT_NONE + ,SQL_SELECT_UNION_ALL + ,SQL_SELECT_UNION_ANY + ,SQL_SELECT_INTERSECT + ,SQL_SELECT_EXCEPT + ,SQL_SELECT_COUNT +} +SqlSelectType; + +/** + * SqlSelect: + * @expr: (element-type Sql.Expr): + * @group: (element-type Sql.Expr): + * @order: (element-type Sql.Expr): + **/ +struct _SqlSelect +{ + SqlDml parent; + gboolean distinct; + SqlList * fields; + SqlList * group; + SqlExpr * having; + SqlList * order; + guint limit_offset; + guint limit_count; + + SqlSelect * next; + SqlSelectType type; +}; + +struct _SqlSelectClass +{ + /* */ + SqlDmlClass parent; +}; + +GType sql_select_get_type (); +SqlObject * sql_select_new (); +void sql_select_set_distinct (SqlSelect * obj, gboolean distinct); +void sql_select_add_expr (SqlSelect * obj, SqlExpr * expr); +void sql_select_set_alias (SqlSelect * obj, SqlExpr * expr, const gchar * alias); +void sql_select_add_group (SqlSelect * obj, SqlExpr * expr); +void sql_select_set_having (SqlSelect * obj, SqlExpr * expr); +void sql_select_add_order (SqlSelect * obj, SqlExpr * expr, SqlSelectOrderWay way); +void sql_select_set_limit (SqlSelect * obj, guint count, guint offset); +void sql_select_set_next (SqlSelect * obj, SqlSelect * next, SqlSelectType type); + +#endif diff --git a/sql/sql-set.c b/sql/sql-set.c new file mode 100644 index 0000000..521b6fc --- /dev/null +++ b/sql/sql-set.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-set.h" + +/** + * SECTION: sql-set + * @Short_description: represents any set used in SQL. + * @Title: SqlSet + **/ +G_DEFINE_TYPE (SqlSet, sql_set, SQL_TYPE_EXPR); + +SqlSet * sql_set_new () +{ + return g_object_new (SQL_TYPE_SET, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_set_render (SqlSet * obj, SqlRender * render) +{ +/* if (obj) + sql_render_add_object (render, obj); + else + sql_render_add_token (render, "DEFAULT"); +*/ + sql_render_append (render, "("); + sql_render_add_list (render, FALSE, NULL, obj->exprs, ","); + sql_render_append (render, ")"); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_EXPRS = 1 +}; + +static void sql_set_set_property (SqlSet * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPRS: + sql_object_remove (obj, obj->exprs); + obj->exprs = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_set_get_property (SqlSet * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_EXPRS: + g_value_set_object (value, obj->exprs); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_set_init (SqlSet * obj) +{ + obj->exprs = NULL; +} + +static void sql_set_finalize (SqlSet * obj) +{ + sql_object_remove (obj, obj->exprs); + G_OBJECT_CLASS (sql_set_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_set_class_init (SqlSetClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_set_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_set_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_set_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_set_render; + + g_object_class_install_property (k, PROP_EXPRS, + sql_param_list ("exprs" + ,"Expressions" + ,"The list of expressions" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-set.h b/sql/sql-set.h new file mode 100644 index 0000000..14419e6 --- /dev/null +++ b/sql/sql-set.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_SET_H +#define SQL_SET_H + +#include "sql-expr.h" + +#define SQL_TYPE_SET (sql_set_get_type ()) +#define SQL_SET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SET, SqlSet)) +#define SQL_IS_SET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SET)) + +typedef struct _SqlSet SqlSet; +typedef struct _SqlSetClass SqlSetClass; + +struct _SqlSet +{ + SqlExpr parent; + SqlList * exprs; +}; + +struct _SqlSetClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_set_get_type (); +SqlSet * sql_set_new (); + +#endif diff --git a/sql/sql-stmt.c b/sql/sql-stmt.c new file mode 100644 index 0000000..712a089 --- /dev/null +++ b/sql/sql-stmt.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-stmt.h" + +/** + * SECTION: sql-stmt + * @Short_description: any kind of SQL statement + * @Title: SqlStmt + * + * Any kind of SQL statement including groups of multiple #SqlStmt as an + * @SqlMultiStmt, all the SQL DML statements (#SqlDml) including but as a + * separated object the INSERT statement (#SqlStmt) and arbitrary SQL strings + * in the form of #SqlString. + **/ +G_DEFINE_ABSTRACT_TYPE (SqlStmt, sql_stmt, SQL_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_stmt_init (SqlStmt * obj) {} + +static void sql_stmt_class_init (SqlStmtClass * klass) {} diff --git a/sql/sql-stmt.h b/sql/sql-stmt.h new file mode 100644 index 0000000..adaf930 --- /dev/null +++ b/sql/sql-stmt.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_STMT_H +#define SQL_STMT_H + +#include "sql-object.h" + +#define SQL_TYPE_STMT (sql_stmt_get_type ()) +#define SQL_STMT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_STMT, SqlStmt)) +#define SQL_IS_STMT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_STMT)) +#define SQL_STMT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, SQL_TYPE_STMT, SqlStmtClass)) +#define SQL_STMT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, SQL_TYPE_STMT, SqlStmtClass)) + +typedef struct _SqlStmt SqlStmt; +typedef struct _SqlStmtClass SqlStmtClass; + +struct _SqlStmt +{ + SqlObject parent; +}; + +struct _SqlStmtClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_stmt_get_type (); + +#endif diff --git a/sql/sql-string.c b/sql/sql-string.c new file mode 100644 index 0000000..8d119b1 --- /dev/null +++ b/sql/sql-string.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-string.h" +#include "sql-holder.h" + +typedef struct +{ + gchar * start; + gchar * end; + SqlHolder * holder; +} +HolderData; + +/** + * SECTION: sql-string + * @Short_description: an arbitrary SQL string + * @Title: SqlString + * + * An arbitrary SQL string. + **/ +G_DEFINE_TYPE (SqlString, sql_string, SQL_TYPE_STMT); + +SqlString * sql_string_new (const gchar * sql) +{ + return g_object_new (SQL_TYPE_STRING, "sql", sql, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_string_render (SqlString * obj, SqlRender * render) +{ + GSList * i; + gchar * ptr = obj->sql; + + for (i = obj->holders; i; i = i->next) + { + HolderData * holder_data = i->data; + g_string_append_len (render->buffer, ptr, holder_data->start - ptr - 1); + ptr = holder_data->end; + + sql_render_add_object (render, holder_data->holder); + } + + sql_render_append (render, ptr); +} + +static void sql_string_free_holder_data (HolderData * holder_data) +{ + g_object_unref (holder_data->holder); + g_free (holder_data); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * sql_string_add_param: + * @obj: the #SqlString + * @id: the id assigned to the item + **/ +void sql_string_add_param (SqlString * obj, const gchar * id, GvnParam * param) +{ + g_return_if_fail (SQL_IS_STRING (obj)); + g_return_if_fail (GVN_IS_PARAM (param)); + + SqlExpr * value = sql_value_new (); + sql_value_set_param (SQL_VALUE (value), param); + sql_object_add_held_object (SQL_OBJECT (obj), id, SQL_OBJECT (value)); + g_object_unref (value); +} + +/** + * sql_string_add_value: + * @obj: the #SqlString + * @id: the id assigned to the item + **/ +void sql_string_add_value (SqlString * obj, const gchar * id, GType type, gpointer content) +{ + g_return_if_fail (SQL_IS_STRING (obj)); + + GValue gvalue = {0}; + SqlExpr * value; + + gvn_value_new_with_content (&gvalue, type, content); + value = sql_value_new (); + sql_value_set_value (SQL_VALUE (value), &gvalue); + sql_object_add_held_object (SQL_OBJECT (obj), id, SQL_OBJECT (value)); + g_object_unref (value); + g_value_unset (&gvalue); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_SQL = 1 +}; + +static void sql_string_set_property (SqlString * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SQL: + { + gchar * i; + + obj->sql = g_value_dup_string (value); + + if (obj->sql) + for (i = obj->sql; ; i++) + { + switch (*i) + { + case '#': + { + gchar * ptr = ++i; + gchar * holder_id; + HolderData * holder_data; + + while (g_ascii_isalpha (*i)) + i++; + + holder_data = g_new (HolderData, 1); + holder_data->start = ptr; + holder_data->end = i; + obj->holders = g_slist_prepend (obj->holders, holder_data); + + holder_id = g_strndup (ptr, i - ptr); + holder_data->holder = sql_holder_new (holder_id); + g_free (holder_id); + + break; + } + case '\'': + case '"': + case '`': + { + gchar delimiter = *i; + + while (*(++i) != delimiter) + if (*i == '\\') + i++; + + break; + } + } + + if (*i == '\0') + break; + } + + obj->holders = g_slist_reverse (obj->holders); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_string_get_property (SqlString * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SQL: + g_value_set_string (value, obj->sql); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_string_init (SqlString * obj) +{ + obj->holders = NULL; +} + +static void sql_string_finalize (SqlString * obj) +{ + g_free (obj->sql); + g_slist_free_full (obj->holders, + (GDestroyNotify) sql_string_free_holder_data); + G_OBJECT_CLASS (sql_string_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_string_class_init (SqlStringClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) sql_string_finalize; + klass->set_property = (GObjectSetPropertyFunc) sql_string_set_property; + klass->get_property = (GObjectGetPropertyFunc) sql_string_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_string_render; + + g_object_class_install_property (klass, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("An arbitrary SQL string") + ,NULL + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/sql/sql-string.h b/sql/sql-string.h new file mode 100644 index 0000000..75193d5 --- /dev/null +++ b/sql/sql-string.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_STRING_H +#define SQL_STRING_H + +#include +#include +#include "sql-stmt.h" +#include "sql-value.h" + +#define SQL_TYPE_STRING (sql_string_get_type ()) +#define SQL_IS_STRING(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_STRING)) +#define SQL_STRING(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_STRING, SqlString)) + +typedef struct _SqlString SqlString; +typedef struct _SqlStringClass SqlStringClass; + +/** + * SqlString: + * @expr: (element-type Sql.Expr): + **/ +struct _SqlString +{ + SqlStmt parent; + gchar * sql; + GSList * holders; +}; + +struct _SqlStringClass +{ + /* */ + SqlStmtClass parent; +}; + +GType sql_string_get_type (); +SqlString * sql_string_new (const gchar * sql); +void sql_string_add_param (SqlString * obj, const gchar * id, GvnParam * param); +void sql_string_add_value (SqlString * obj, const gchar * id, GType type, gpointer content); + +#endif diff --git a/sql/sql-subquery.c b/sql/sql-subquery.c new file mode 100644 index 0000000..c7cdbf0 --- /dev/null +++ b/sql/sql-subquery.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-subquery.h" + +/** + * SECTION: sql-subquery + * @Short_description: an #SqlTarget containing an #SqlSelect + * @Title: SqlSubquery + * + * An #SqlTarget containing an #SqlSelect, used to add a sub-query to the + * targets field in an SQL query. + **/ +G_DEFINE_TYPE (SqlSubquery, sql_subquery, SQL_TYPE_TARGET); + +SqlSubquery * sql_subquery_new (SqlSelect * select) +{ + return g_object_new (SQL_TYPE_SUBQUERY, "select", select, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_subquery_render (SqlSubquery * obj, SqlRender * render) +{ + if (obj->select) + { + sql_render_append (render, "("); + sql_render_add_item (render, TRUE, NULL, obj->select); + sql_render_append (render, ")"); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_subquery_set_select (SqlSubquery * obj, SqlSelect * select) +{ + g_return_if_fail (SQL_IS_SUBQUERY (obj)); + g_return_if_fail (SQL_IS_SELECT (select) || !select); + + sql_object_remove (obj, obj->select); + obj->select = sql_object_add (obj, select); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_SELECT = 1 +}; + +static void sql_subquery_set_property (SqlSubquery * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SELECT: + sql_subquery_set_select (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_subquery_get_property (SqlSubquery * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SELECT: + g_value_set_object (value, obj->select); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_subquery_init (SqlSubquery * obj) +{ + obj->select = NULL; +} + +static void sql_subquery_finalize (SqlSubquery * obj) +{ + sql_object_remove (obj, obj->select); + G_OBJECT_CLASS (sql_subquery_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_subquery_class_init (SqlSubqueryClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_subquery_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_subquery_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_subquery_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_subquery_render; + + g_object_class_install_property (k, PROP_SELECT, + sql_param_object ("select" + ,"Select" + ,"The SELECT statement" + ,SQL_TYPE_SELECT + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-subquery.h b/sql/sql-subquery.h new file mode 100644 index 0000000..24581fc --- /dev/null +++ b/sql/sql-subquery.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_SUBQUERY_H +#define SQL_SUBQUERY_H + +#include "sql-target.h" +#include "sql-select.h" + +#define SQL_TYPE_SUBQUERY (sql_subquery_get_type ()) +#define SQL_IS_SUBQUERY(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_SUBQUERY)) +#define SQL_SUBQUERY(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_SUBQUERY, SqlSubquery)) + +typedef struct _SqlSubquery SqlSubquery; +typedef struct _SqlSubqueryClass SqlSubqueryClass; + +struct _SqlSubquery +{ + SqlTarget parent; + SqlSelect * select; +}; + +struct _SqlSubqueryClass +{ + /* */ + SqlTargetClass parent; +}; + +GType sql_subquery_get_type (); +SqlSubquery * sql_subquery_new (SqlSelect * select); +void sql_subquery_set_select (SqlSubquery * obj, SqlSelect * select); + +#endif + diff --git a/sql/sql-table.c b/sql/sql-table.c new file mode 100644 index 0000000..6ef7056 --- /dev/null +++ b/sql/sql-table.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-table.h" + +/** + * SECTION: sql-table + * @Short_description: a qualified table name. + * @Title: SqlTable + * + * A qualified table name. + **/ +G_DEFINE_TYPE (SqlTable, sql_table, SQL_TYPE_TARGET); + +SqlObject * sql_table_new (const gchar * name) +{ + return g_object_new (SQL_TYPE_TABLE, "name", name, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_table_render (SqlTable * obj, SqlRender * render) +{ + if (obj->schema) + { + sql_render_add_identifier (render, obj->schema); + sql_render_append (render, "."); + } + + sql_render_add_identifier (render, obj->name); + + if (g_strcmp0 (obj->name, SQL_TARGET (obj)->alias)) + sql_render_add_identifier (render, SQL_TARGET (obj)->alias); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_SCHEMA +}; + +static void sql_table_set_property (SqlTable * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_free (obj->name); + obj->name = g_value_dup_string (value); + break; + case PROP_SCHEMA: + g_free (obj->schema); + obj->schema = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_table_get_property (SqlTable * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->name); + break; + case PROP_SCHEMA: + g_value_set_string (value, obj->schema); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_table_init (SqlTable * obj) +{ + obj->name = NULL; + obj->schema = NULL; +} + +static void sql_table_finalize (SqlTable * obj) +{ + g_free (obj->name); + g_free (obj->schema); + G_OBJECT_CLASS (sql_table_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_table_class_init (SqlTableClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_table_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_table_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_table_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_table_render; + + g_object_class_install_property (k, PROP_NAME, + g_param_spec_string ("name" + , "Name" + , "The table name" + , NULL, G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_SCHEMA, + g_param_spec_string ("schema" + ,"Schema" + ,"The schema where the table is" + ,NULL, G_PARAM_READWRITE + )); +} diff --git a/sql/sql-table.h b/sql/sql-table.h new file mode 100644 index 0000000..9544872 --- /dev/null +++ b/sql/sql-table.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_TABLE_H +#define SQL_TABLE_H + +#include "sql-target.h" + +#define SQL_TYPE_TABLE (sql_table_get_type ()) +#define SQL_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_TABLE, SqlTable)) +#define SQL_IS_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_TABLE)) + +typedef struct _SqlTable SqlTable; +typedef struct _SqlTableClass SqlTableClass; + +struct _SqlTable +{ + SqlTarget parent; + gchar * name; + gchar * schema; +}; + +struct _SqlTableClass +{ + /* */ + SqlTargetClass parent; +}; + +GType sql_table_get_type (); +SqlObject * sql_table_new (const gchar * name); + +#endif diff --git a/sql/sql-target.c b/sql/sql-target.c new file mode 100644 index 0000000..2dd8ace --- /dev/null +++ b/sql/sql-target.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-target.h" + +/** + * SECTION: sql-target + * @Short_description: any target for an SQL statement + * @Title: SqlTarget + * + * A target that can be used as such in any SQL statement (here represented by + * the #SqlStmt class). + **/ +G_DEFINE_ABSTRACT_TYPE (SqlTarget, sql_target, SQL_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_target_set_alias (SqlTarget * obj, const gchar * alias) +{ + g_return_if_fail (SQL_IS_TARGET (obj)); + + g_free (obj->alias); + obj->alias = g_strdup (alias); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_ALIAS = 1 +}; + +static void sql_target_set_property (SqlTarget * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ALIAS: + sql_target_set_alias (obj, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_target_get_property (SqlTarget * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ALIAS: + g_value_set_string (value, obj->alias); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_target_init (SqlTarget * obj) +{ + obj->alias = NULL; +} + +static void sql_target_finalize (SqlTarget * obj) +{ + g_free (obj->alias); + G_OBJECT_CLASS (sql_target_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_target_class_init (SqlTargetClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) sql_target_finalize; + klass->set_property = (GObjectSetPropertyFunc) sql_target_set_property; + klass->get_property = (GObjectGetPropertyFunc) sql_target_get_property; + + g_object_class_install_property (klass, PROP_ALIAS, + g_param_spec_string ("alias" + ,"Alias" + ,"The alias for the target" + ,NULL + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-target.h b/sql/sql-target.h new file mode 100644 index 0000000..43a20e4 --- /dev/null +++ b/sql/sql-target.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_TARGET_H +#define SQL_TARGET_H + +#include "sql-object.h" + +#define SQL_TYPE_TARGET (sql_target_get_type ()) +#define SQL_TARGET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_TARGET, SqlTarget)) +#define SQL_IS_TARGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_TARGET)) + +typedef struct _SqlTarget SqlTarget; +typedef struct _SqlTargetClass SqlTargetClass; + +struct _SqlTarget +{ + SqlObject parent; + gchar * alias; +}; + +struct _SqlTargetClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_target_get_type (); +void sql_target_set_alias (SqlTarget * obj, const gchar * alias); + +#endif \ No newline at end of file diff --git a/sql/sql-update-set.c b/sql/sql-update-set.c new file mode 100644 index 0000000..2b204e1 --- /dev/null +++ b/sql/sql-update-set.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-update-set.h" + +/** + * SECTION: sql-update_set + * @Short_description: Defines a field for the SET clause for an #SqlUpdate. + * @Title: SqlUpdateSet + **/ +G_DEFINE_TYPE (SqlUpdateSet, sql_update_set, SQL_TYPE_OBJECT); + +SqlUpdateSet * sql_update_set_new () +{ + return g_object_new (SQL_TYPE_UPDATE_SET, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_update_set_render (SqlUpdateSet * set, SqlRender * render) +{ + sql_render_add_object (render, set->field); + sql_render_add_token (render, "="); + sql_render_add_object (render, set->expr); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_FIELD = 1 + ,PROP_EXPR +}; + +static void sql_update_set_set_property (SqlUpdateSet * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_FIELD: + sql_object_remove (obj, obj->field); + obj->field = sql_object_add (obj, g_value_get_object (value)); + break; + case PROP_EXPR: + sql_object_remove (obj, obj->expr); + obj->expr = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_update_set_get_property (SqlUpdateSet * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_FIELD: + g_value_set_object (value, obj->field); + break; + case PROP_EXPR: + g_value_set_object (value, obj->expr); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_update_set_init (SqlUpdateSet * obj) +{ + obj->field = NULL; + obj->expr = NULL; +} + +static void sql_update_set_finalize (SqlUpdateSet * obj) +{ + sql_object_remove (obj, obj->field); + sql_object_remove (obj, obj->expr); + G_OBJECT_CLASS (sql_update_set_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_update_set_class_init (SqlUpdateSetClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_update_set_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_update_set_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_update_set_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_update_set_render; + + g_object_class_install_property (k, PROP_FIELD, + g_param_spec_object ("field" + ,"Field" + ,"The field" + ,SQL_TYPE_FIELD + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_EXPR, + g_param_spec_object ("expr" + ,"Expression" + ,"The expression" + ,SQL_TYPE_EXPR + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-update-set.h b/sql/sql-update-set.h new file mode 100644 index 0000000..ffa1037 --- /dev/null +++ b/sql/sql-update-set.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_UPDATE_SET_H +#define SQL_UPDATE_SET_H + +#include "sql-object.h" +#include "sql-field.h" +#include "sql-expr.h" + +#define SQL_TYPE_UPDATE_SET (sql_update_set_get_type ()) +#define SQL_UPDATE_SET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_UPDATE_SET, SqlUpdateSet)) +#define SQL_IS_UPDATE_SET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_UPDATE_SET)) + +typedef struct _SqlUpdateSet SqlUpdateSet; +typedef struct _SqlUpdateSetClass SqlUpdateSetClass; + +struct _SqlUpdateSet +{ + SqlObject parent; + SqlField * field; + SqlExpr * expr; +}; + +struct _SqlUpdateSetClass +{ + /* */ + SqlObjectClass parent; +}; + +GType sql_update_set_get_type (); +SqlUpdateSet * sql_update_set_new (); + +#endif diff --git a/sql/sql-update.c b/sql/sql-update.c new file mode 100644 index 0000000..b582722 --- /dev/null +++ b/sql/sql-update.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sql-update.h" + +/** + * SECTION: sql-update + * @Short_description: an SQL UPDATE statement + * @Title: SqlUpdate + * + * Represents an SQL UPDATE statement. + **/ +G_DEFINE_TYPE (SqlUpdate, sql_update, SQL_TYPE_DML); + +SqlObject * sql_update_new () +{ + return g_object_new (SQL_TYPE_UPDATE, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_update_render (SqlUpdate * obj, SqlRender * render) +{ + sql_render_add_list (render, TRUE, "UPDATE", SQL_DML (obj)->targets, ","); + + if (SQL_DML (obj)->targets) + { + sql_render_add_list (render, TRUE, "SET", obj->sets, ","); + sql_render_add_item (render, FALSE, "WHERE", SQL_DML (obj)->where); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +void sql_update_add_set (SqlUpdate * obj, SqlField * field, SqlExpr * expr) +{ + g_return_if_fail (SQL_IS_UPDATE (obj)); + g_return_if_fail (SQL_IS_FIELD (field) && SQL_IS_EXPR (expr)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_SETS = 1 +}; + +static void sql_update_set_property (SqlUpdate * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SETS: + sql_object_remove (obj, obj->sets); + obj->sets = sql_object_add (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_update_get_property (SqlUpdate * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_SETS: + g_value_set_object (value, obj->sets); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_update_init (SqlUpdate * obj) +{ + obj->sets = NULL; +} + +static void sql_update_finalize (SqlUpdate * obj) +{ + sql_object_remove (obj, obj->sets); + G_OBJECT_CLASS (sql_update_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_update_class_init (SqlUpdateClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_update_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_update_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_update_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_update_render; + + g_object_class_install_property (k, PROP_SETS, + sql_param_list ("sets" + ,"Sets" + ,"A list of sets" + ,SQL_TYPE_UPDATE_SET + ,G_PARAM_READWRITE + )); +} diff --git a/sql/sql-update.h b/sql/sql-update.h new file mode 100644 index 0000000..cb2d472 --- /dev/null +++ b/sql/sql-update.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_UPDATE_H +#define SQL_UPDATE_H + +#include "sql-dml.h" +#include "sql-field.h" +#include "sql-update-set.h" + +#define SQL_TYPE_UPDATE (sql_update_get_type ()) +#define SQL_UPDATE(object) (G_TYPE_CHECK_INSTANCE_CAST (object, SQL_TYPE_UPDATE, SqlUpdate)) +#define SQL_IS_UPDATE(object) (G_TYPE_CHECK_INSTANCE_TYPE (object, SQL_TYPE_UPDATE)) + +typedef struct _SqlUpdate SqlUpdate; +typedef struct _SqlUpdateClass SqlUpdateClass; + +/** + * SqlUpdate: + **/ +struct _SqlUpdate +{ + SqlDml parent; + SqlList * sets; // List of SqlUpdateSet +}; + +struct _SqlUpdateClass +{ + /* */ + SqlDmlClass parent; +}; + + +GType sql_update_get_type (); +SqlObject * sql_update_new (); +void sql_update_add_set (SqlUpdate * obj, SqlField * field, SqlExpr * expr); + +#endif diff --git a/sql/sql-value.c b/sql/sql-value.c new file mode 100644 index 0000000..7940aa5 --- /dev/null +++ b/sql/sql-value.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "sql-value.h" + +/** + * SECTION: sql-value + * @Short_description: generic value + * @Title: SqlValue + * + * A generic value that can be used in operations (#SqlOperation) and of course + * in any place where an #SqlExpr can be used. + **/ +G_DEFINE_TYPE (SqlValue, sql_value, SQL_TYPE_EXPR); + +SqlObject * sql_value_new () +{ + return g_object_new (SQL_TYPE_VALUE, NULL); +} + +SqlObject * sql_value_new_with_value (const GValue * value) +{ + g_return_val_if_fail (value, NULL); + g_return_val_if_fail (G_IS_VALUE (value), NULL); + + return g_object_new (SQL_TYPE_VALUE, "value", value, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void sql_value_render (SqlValue * obj, SqlRender * render) +{ + GValue * value = obj->value; + GType type = G_VALUE_TYPE (value); + + switch (type) + { + case G_TYPE_BOOLEAN: + sql_render_add_token (render, g_value_get_boolean (value) ? "TRUE" : "FALSE"); + break; + case G_TYPE_CHAR: + sql_render_printf (render, "'%c'", g_value_get_schar (value)); + break; + case G_TYPE_INT: + sql_render_printf (render, "%i", g_value_get_int (value)); + break; + case G_TYPE_UINT: + sql_render_printf (render, "%u", g_value_get_uint (value)); + break; + case G_TYPE_LONG: + sql_render_printf (render, "%ld", g_value_get_long (value)); + break; + case G_TYPE_ULONG: + sql_render_printf (render, "%lu", g_value_get_ulong (value)); + break; + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + { + gchar buffer[G_ASCII_DTOSTR_BUF_SIZE]; + g_ascii_dtostr (buffer, G_ASCII_DTOSTR_BUF_SIZE, + (type == G_TYPE_FLOAT) + ?(gdouble) g_value_get_float (value) + :g_value_get_double (value) + ); + sql_render_add_espace (render); + sql_render_append (render, buffer); + break; + } + case G_TYPE_STRING: + sql_render_append_with_delimiter (render, g_value_get_string (value), '\''); + break; + default: + if (type == G_TYPE_DATE) + { + GDate * date = g_value_get_boxed (value); + sql_render_printf (render, "'%04i-%02i-%02i'" + ,g_date_get_year (date) + ,g_date_get_month (date) + ,g_date_get_day (date) + ); + } + else if (type == G_TYPE_DATE_TIME) + { + GDateTime * date_time = g_value_get_boxed (value); + sql_render_printf (render, g_date_time_format (date_time, "'%Y-%m-%d %T'")); + } + else if (type == GVN_TYPE_NULL) + sql_render_add_token (render, "NULL"); + else + sql_render_set_error (render); + } +} + +static gboolean sql_value_is_ready (SqlValue * obj) +{ + return !gvn_value_is_null (obj->value); +} + +static void sql_value_set_real_value (SqlValue * obj, const GValue * value) +{ + if (gvn_value_ccopy (value, obj->value)) + g_signal_emit_by_name (obj, "changed"); +} + +static void sql_value_cb_value_changed (GvnParam * param, const GValue * value, SqlValue * obj) +{ + sql_value_set_real_value (obj, value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +const GValue * sql_value_get_value (SqlValue * obj) +{ + g_return_val_if_fail (SQL_IS_VALUE (obj), NULL); + + return obj->value; +} + +void sql_value_set_value (SqlValue * obj, const GValue * value) +{ + g_return_if_fail (SQL_IS_VALUE (obj)); + g_return_if_fail (!obj->param); + + sql_value_set_real_value (obj, value); +} + +void sql_value_set_param (SqlValue * obj, GvnParam * param) +{ + g_return_if_fail (SQL_IS_VALUE (obj)); + g_return_if_fail (GVN_IS_PARAM (param) || !param); + + if (obj->param) + { + g_signal_handlers_disconnect_by_func (obj->param, + sql_value_cb_value_changed, obj); + g_clear_object (&obj->param); + } + if (param) + { + obj->param = g_object_ref_sink (param); + g_signal_connect (param, "value-changed", + G_CALLBACK (sql_value_cb_value_changed), obj); + sql_value_set_real_value (obj, gvn_param_get_value (param)); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_PARAM = 1 + ,PROP_VALUE +}; + +static void sql_value_set_property (SqlValue * obj, guint id, const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PARAM: + sql_value_set_param (obj, g_value_get_object (value)); + break; + case PROP_VALUE: + sql_value_set_value (obj, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void sql_value_get_property (SqlValue * obj, guint id, GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PARAM: + g_value_set_object (value, obj->param); + break; + case PROP_VALUE: + g_value_set_boxed (value, obj->value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void sql_value_init (SqlValue * obj) +{ + obj->param = NULL; + obj->value = g_new0 (GValue, 1); + g_value_init (obj->value, GVN_TYPE_NULL); +} + +static void sql_value_finalize (SqlValue * obj) +{ + sql_value_set_param (obj, NULL); + g_value_unset (obj->value); + g_free (obj->value); + G_OBJECT_CLASS (sql_value_parent_class)->finalize (G_OBJECT (obj)); +} + +static void sql_value_class_init (SqlValueClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) sql_value_finalize; + k->set_property = (GObjectSetPropertyFunc) sql_value_set_property; + k->get_property = (GObjectGetPropertyFunc) sql_value_get_property; + SQL_OBJECT_CLASS (klass)->render = (SqlRenderFunc) sql_value_render; + SQL_OBJECT_CLASS (klass)->is_ready = (SqlObjectIsReadyFunc) sql_value_is_ready; + + g_object_class_install_property (k, PROP_PARAM, + g_param_spec_object ("param" + ,_("Param") + ,_("The param which is linked") + ,GVN_TYPE_PARAM, G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_VALUE, + g_param_spec_boxed ("value" + ,_("Value") + ,_("The value") + ,G_TYPE_VALUE, G_PARAM_READWRITE + )); +} diff --git a/sql/sql-value.h b/sql/sql-value.h new file mode 100644 index 0000000..6b75448 --- /dev/null +++ b/sql/sql-value.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_VALUE_H +#define SQL_VALUE_H + +#include +#include "sql-expr.h" + +#define SQL_TYPE_VALUE (sql_value_get_type ()) +#define SQL_VALUE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, SQL_TYPE_VALUE, SqlValue)) +#define SQL_IS_VALUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, SQL_TYPE_VALUE)) + +typedef struct _SqlValue SqlValue; +typedef struct _SqlValueClass SqlValueClass; + +struct _SqlValue +{ + SqlExpr parent; + GValue * value; + GvnParam * param; +}; + +struct _SqlValueClass +{ + /* */ + SqlExprClass parent; +}; + +GType sql_value_get_type (); +SqlObject * sql_value_new (); +SqlObject * sql_value_new_with_value (const GValue * value); +const GValue * sql_value_get_value (SqlValue * obj); +void sql_value_set_value (SqlValue * obj, const GValue * value); +void sql_value_set_param (SqlValue * obj, GvnParam * param); + +#endif diff --git a/sql/sql.h b/sql/sql.h new file mode 100644 index 0000000..91c5d28 --- /dev/null +++ b/sql/sql.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_H +#define SQL_H + +#include +#include "sql-object.h" +#include "sql-holder.h" +#include "sql-list.h" +#include "sql-set.h" +#include "sql-string.h" +#include "sql-stmt.h" +#include "sql-multi-stmt.h" +#include "sql-select.h" +#include "sql-select-field.h" +#include "sql-select-order.h" +#include "sql-update.h" +#include "sql-update-set.h" +#include "sql-insert.h" +#include "sql-delete.h" +#include "sql-expr.h" +#include "sql-field.h" +#include "sql-value.h" +#include "sql-operation.h" +#include "sql-function.h" +#include "sql-target.h" +#include "sql-table.h" +#include "sql-join.h" +#include "sql-subquery.h" +#include "sql-render.h" +#include "sql-parser.h" + +#endif \ No newline at end of file diff --git a/sql/sql.pc.in b/sql/sql.pc.in new file mode 100644 index 0000000..ad446c8 --- /dev/null +++ b/sql/sql.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Sql +Description: Sql Classes Module for Hedera Library +Version: @VERSION@ +Requires: gvn glib-2.0 gobject-2.0 +Libs: -L${libdir} -lsql +Cflags: -I${includedir}/sql diff --git a/template/lib-object.c b/template/lib-object.c new file mode 100644 index 0000000..2b488d9 --- /dev/null +++ b/template/lib-object.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "lib-object.h" + +/** + * SECTION: lib-object + * @Short_description: a template object + * @Title: LibObject + * + * Long description of the object. + */ +G_DEFINE_TYPE (LibObject, lib_object, G_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +/** + * lib_object_method: + * @obj: a #LibObject + * + * Description of method here. + * + * Return value: #void + **/ +void lib_object_method (LibObject * obj) +{ + +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_PROPERTY = 1 +}; + +static void lib_object_set_property (LibObject * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PROPERTY: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void lib_object_get_property (LibObject * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PROPERTY: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void lib_object_init (LibObject * obj) +{ + +} + +static void lib_object_finalize (LibObject * obj) +{ + G_OBJECT_CLASS (lib_object_parent_class)->finalize (G_OBJECT (obj)); +} + +static void lib_object_class_init (LibObjectClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) lib_object_finalize; + k->set_property = (GObjectSetPropertyFunc) lib_object_set_property; + k->get_property = (GObjectGetPropertyFunc) lib_object_get_property; + + g_object_class_install_property (k, PROP_FIRST, + g_param_spec_string ("property" + ,"Property" + ,"Description" + ,NULL + ,G_PARAM_READWRITE + )); +} diff --git a/template/lib-object.h b/template/lib-object.h new file mode 100644 index 0000000..6a47b47 --- /dev/null +++ b/template/lib-object.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIB_OBJECT_H +#define LIB_OBJECT_H + +#include + +#define LIB_TYPE_OBJECT (lib_object_get_type ()) +#define LIB_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, LIB_TYPE_OBJECT, LibObject)) +#define LIB_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, LIB_TYPE_OBJECT)) +#define LIB_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, LIB_TYPE_OBJECT, LibObjectClass)) +#define LIB_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, LIB_TYPE_OBJECT)) +#define LIB_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, LIB_TYPE_OBJECT, LibObjectClass)) + +typedef struct _LibObject LibObject; +typedef struct _LibObjectClass LibObjectClass; + +struct _LibObject +{ + GObject parent; +}; + +struct _LibObjectClass +{ + GObjectClass parent; +}; + +GType lib_object_get_type (); + +#endif \ No newline at end of file diff --git a/vapi/Db-1.0.metadata b/vapi/Db-1.0.metadata new file mode 100644 index 0000000..e4c464e --- /dev/null +++ b/vapi/Db-1.0.metadata @@ -0,0 +1,10 @@ +Db cheader_filename="db/db.h" + +Iter struct +Iter.*#method skip + +Row struct + +Model.get skip=false +Model.set skip=false +Model.search skip=false \ No newline at end of file diff --git a/vapi/Gvn-1.0.metadata b/vapi/Gvn-1.0.metadata new file mode 100644 index 0000000..0c3e898 --- /dev/null +++ b/vapi/Gvn-1.0.metadata @@ -0,0 +1,9 @@ +Gvn cheader_filename="gvn/gvn.h" + +value_new_with_content skip=false +value_get_valist skip=false + +Param.value_changed#method name="emit_value_changed" + +//FIXME +ParamSpec struct \ No newline at end of file diff --git a/vapi/Makefile.am b/vapi/Makefile.am new file mode 100644 index 0000000..d235411 --- /dev/null +++ b/vapi/Makefile.am @@ -0,0 +1,17 @@ +include $(top_srcdir)/Makefile.decl + +if ENABLE_VALA +vapi_DATA = \ + gvn.deps \ + sql.deps \ + db.deps \ + vn.deps \ + hedera.deps \ + hedera.vapi +EXTRA_DIST = \ + Db.metadata \ + Gvn.metadata \ + Sql.metadata \ + Vn.metadata \ + $(vapi_DATA) +endif diff --git a/vapi/Sql-1.0.metadata b/vapi/Sql-1.0.metadata new file mode 100644 index 0000000..aa7ddbf --- /dev/null +++ b/vapi/Sql-1.0.metadata @@ -0,0 +1 @@ +Sql cheader_filename="sql/sql.h" diff --git a/vapi/Vn-1.0.metadata b/vapi/Vn-1.0.metadata new file mode 100644 index 0000000..e931908 --- /dev/null +++ b/vapi/Vn-1.0.metadata @@ -0,0 +1,8 @@ +Vn cheader_filename="vn/vn.h" + +Builder.bind_fields skip=false +Builder.bind_columns skip=false + +Grid.append_columns skip=false + +Batch.objects skip \ No newline at end of file diff --git a/vapi/db.deps b/vapi/db.deps new file mode 100644 index 0000000..0272653 --- /dev/null +++ b/vapi/db.deps @@ -0,0 +1,2 @@ +sql +glib-2.0 \ No newline at end of file diff --git a/vapi/gvn.deps b/vapi/gvn.deps new file mode 100644 index 0000000..aaded57 --- /dev/null +++ b/vapi/gvn.deps @@ -0,0 +1 @@ +glib-2.0 \ No newline at end of file diff --git a/vapi/hedera.deps b/vapi/hedera.deps new file mode 100644 index 0000000..fb3fcbc --- /dev/null +++ b/vapi/hedera.deps @@ -0,0 +1 @@ +vn \ No newline at end of file diff --git a/vapi/hedera.vapi b/vapi/hedera.vapi new file mode 100644 index 0000000..e69de29 diff --git a/vapi/sql.deps b/vapi/sql.deps new file mode 100644 index 0000000..85946d5 --- /dev/null +++ b/vapi/sql.deps @@ -0,0 +1,2 @@ +gvn +glib-2.0 \ No newline at end of file diff --git a/vapi/vn.deps b/vapi/vn.deps new file mode 100644 index 0000000..47a66cf --- /dev/null +++ b/vapi/vn.deps @@ -0,0 +1,2 @@ +db +gtk+-3.0 \ No newline at end of file diff --git a/vn/Makefile.am b/vn/Makefile.am new file mode 100644 index 0000000..909060e --- /dev/null +++ b/vn/Makefile.am @@ -0,0 +1,137 @@ +include $(top_srcdir)/Makefile.decl + +SUBDIRS = \ + field \ + column + +vn_lib_LTLIBRARIES = libvn.la +vn_include_HEADERS = \ + vn.h \ + vn-builder.h \ + vn-field.h \ + vn-column.h \ + vn-login.h \ + vn-gui.h \ + vn-mod.h \ + vn-form.h \ + vn-model.h \ + vn-grid.h \ + vn-handler.h \ + vn-batch.h \ + field/field.h \ + column/column.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(DEFINES) \ + $(gdome_CFLAGS) \ + $(gtk_CFLAGS) +libvn_la_LIBADD = \ + $(gdome_LIBS) \ + $(gtk_LIBS) \ + $(top_builddir)/db/libdb.la \ + $(top_builddir)/vn/field/libvnfield.la \ + $(top_builddir)/vn/column/libvncolumn.la +libvn_la_SOURCES = \ + $(vn_include_HEADERS) \ + vn-builder.c \ + vn-field.c \ + vn-column.c \ + vn-login.c \ + vn-gui.c \ + vn-mod.c \ + vn-form.c \ + vn-model.c \ + vn-grid.c \ + vn-handler.c \ + vn-batch.c + +pkgconfig_DATA = vn.pc + +vn_xml_DATA = \ + schema/module.dtd +vn_image_DATA = \ + image/hedera16x16.xpm \ + image/hedera32x32.xpm \ + image/icon.svg \ + image/logo.svg \ + image/load.gif +vn_gui_DATA = \ + gui/login.glade \ + gui/main.glade \ + gui/child-window.glade \ + gui/actions.glade \ + gui/menubar.ui + +gsettings_SCHEMAS = $(top_srcdir)/vn/schema/$(PACKAGE).gschema.xml +@GSETTINGS_RULES@ + +DEFINES = \ + -D_GUI_DIR=\"$(vn_guidir)\" \ + -D_VN_MODULE_QUERY_DIR=\"$(module_querydir)/sql\" \ + -D_VN_MODULE_LIB_DIR=\"$(module_libdir)\" \ + -D_VN_MODULE_DATA_DIR=\"$(module_datadir)\" \ + -D_HEDERA_LOCALE_DIR=\"$(datadir)/locale\" \ + -D_DTD_DIR=\"$(vn_xmldir)\" + +EXTRA_DIST = vn.pc.in + +DISTCLEANFILES = vn.pc + +if ENABLE_VALA +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_SCANNER_ARGS = $(GIR_SCANNER_ARGS) + +INTROSPECTION_COMPILER_ARGS = \ + --includedir=$(top_builddir)/gvn \ + --includedir=$(top_builddir)/sql \ + --includedir=$(top_builddir)/db + +introspection_sources = \ + $(libvn_la_SOURCES) \ + $(top_srcdir)/vn/field/vn-*.h \ + $(top_srcdir)/vn/column/vn-*.h + +introspection_libs = \ + libvn.la \ + field/libvnfield.la \ + column/libvncolumn.la + +Vn-$(VERSION).gir: $(introspection_libs) $(top_builddir)/db/Db-$(VERSION).gir + Vn_@uVERSION@_gir_SCANNERFLAGS = \ + --include-uninstalled=$(top_builddir)/gvn/Gvn-$(VERSION).gir \ + --include-uninstalled=$(top_builddir)/sql/Sql-$(VERSION).gir \ + --include-uninstalled=$(top_builddir)/db/Db-$(VERSION).gir + Vn_@uVERSION@_gir_INCLUDES = Gtk-3.0 + Vn_@uVERSION@_gir_CFLAGS = -I$(top_srcdir) + Vn_@uVERSION@_gir_LIBS = $(introspection_libs) + Vn_@uVERSION@_gir_FILES = $(introspection_sources) + Vn_@uVERSION@_gir_EXPORT_PACKAGES = vn + INTROSPECTION_GIRS = Vn-$(VERSION).gir + +gir_DATA = $(INTROSPECTION_GIRS) +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES = $(gir_DATA) $(typelib_DATA) + +$(vapis)/vn.vapi: $(INTROSPECTION_GIRS) $(vapidata)/Vn-$(VERSION).metadata + $(vapigen_v)$(VAPIGEN) -q \ + --pkg gtk+-3.0 \ + --directory $(vapis) \ + --vapidir $(vapis) \ + --girdir $(top_builddir)/gvn \ + --girdir $(top_builddir)/sql \ + --girdir $(top_builddir)/db \ + --metadatadir $(vapidata) \ + --library vn \ + Vn-$(VERSION).gir + +vapi_DATA = $(vapis)/vn.vapi + +CLEANFILES += $(vapis)/$(vapi_DATA) + +endif +endif diff --git a/vn/column/Makefile.am b/vn/column/Makefile.am new file mode 100644 index 0000000..64943e8 --- /dev/null +++ b/vn/column/Makefile.am @@ -0,0 +1,23 @@ +include $(top_srcdir)/Makefile.decl + +column_lib_LTLIBRARIES = libvncolumn.la +column_include_HEADERS = \ + column.h \ + vn-column-check.h \ + vn-column-combo.h \ + vn-column-entry.h \ + vn-column-image.h \ + vn-column-spin.h + +AM_CPPFLAGS = \ + -D_IMAGE_DIR=\"$(vn_imagedir)\" \ + -I$(top_srcdir) \ + $(gtk_CFLAGS) +libvncolumn_la_LIBADD = $(gtk_LIBS) +libvncolumn_la_SOURCES = \ + $(column_include_HEADERS) \ + vn-column-check.c \ + vn-column-combo.c \ + vn-column-entry.c \ + vn-column-image.c \ + vn-column-spin.c diff --git a/vn/column/column.h b/vn/column/column.h new file mode 100644 index 0000000..64d7e40 --- /dev/null +++ b/vn/column/column.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef COLUMN_H +#define COLUMN_H + +#include "vn-column-check.h" +#include "vn-column-combo.h" +#include "vn-column-entry.h" +#include "vn-column-image.h" +#include "vn-column-spin.h" + +#endif \ No newline at end of file diff --git a/vn/column/vn-column-check.c b/vn/column/vn-column-check.c new file mode 100644 index 0000000..ee2ccd3 --- /dev/null +++ b/vn/column/vn-column-check.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-column-check.h" + +G_DEFINE_TYPE (VnColumnCheck, vn_column_check, VN_TYPE_COLUMN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_column_check_cb_toggled (GtkCellRendererToggle * cell, + const gchar * path, VnColumn * obj) +{ + gint col; + DbIter iter; + DbModel * model; + GValue new_value = {0}; + const GValue * old_value; + + col = vn_column_get_column_index (obj); + model = vn_column_get_model (obj); + + vn_column_get_iter (obj, path, &iter); + old_value = db_model_get_value (model, &iter, col, NULL); + + g_value_init (&new_value, G_TYPE_BOOLEAN); + g_value_set_boolean (&new_value, + gvn_value_is_null (old_value) || !g_value_get_boolean (old_value)); + db_model_set_value (model, &iter, col, &new_value, NULL); +} + +static void vn_column_check_set_editable (VnColumn * obj, gboolean editable) +{ + if (editable) + g_signal_connect (obj->cell, "toggled", + G_CALLBACK (vn_column_check_cb_toggled), obj); + else + g_signal_handlers_disconnect_by_func (obj->cell, + vn_column_check_cb_toggled, obj); +} + +static void vn_column_check_set_value (VnColumnCheck * obj, GtkTreeModel * model, + GtkTreeIter * iter, GtkCellRendererToggle * cell, const GValue * value) +{ + GValue new_value = {0}; + g_value_init (&new_value, G_TYPE_BOOLEAN); + + if (!gvn_value_is_null (value)) + g_value_transform (value, &new_value); + else + g_value_set_boolean (&new_value, FALSE); + + gtk_cell_renderer_toggle_set_active (cell, + g_value_get_boolean (&new_value)); + + g_value_unset (&new_value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_check_init (VnColumnCheck * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_toggle_new (); + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); +} + +static void vn_column_check_finalize (VnColumnCheck * obj) +{ + G_OBJECT_CLASS (vn_column_check_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_check_class_init (VnColumnCheckClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_check_finalize; + VN_COLUMN_CLASS (klass)->set_value = (VnColumnSetValueFunc) vn_column_check_set_value; + VN_COLUMN_CLASS (klass)->set_editable = (VnColumnSetEditableFunc) vn_column_check_set_editable; +} diff --git a/vn/column/vn-column-check.h b/vn/column/vn-column-check.h new file mode 100644 index 0000000..b25bb54 --- /dev/null +++ b/vn/column/vn-column-check.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_COLUMN_CHECK_H +#define VN_COLUMN_CHECK_H + +#include + +#define VN_TYPE_COLUMN_CHECK (vn_column_check_get_type ()) +#define VN_COLUMN_CHECK(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_CHECK, VnColumnCheck)) +#define VN_IS_COLUMN_CHECK(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_CHECK)) +#define VN_COLUMN_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_CHECK, VnColumnCheckClass)) +#define VN_IS_COLUMN_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_CHECK)) +#define VN_COLUMN_CHECK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_CHECK, VnColumnCheckClass)) + +typedef struct _VnColumnCheck VnColumnCheck; +typedef struct _VnColumnCheckClass VnColumnCheckClass; + +struct _VnColumnCheck +{ + VnColumn parent; +}; + +struct _VnColumnCheckClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_check_get_type (); + +#endif \ No newline at end of file diff --git a/vn/column/vn-column-combo.c b/vn/column/vn-column-combo.c new file mode 100644 index 0000000..ce17af7 --- /dev/null +++ b/vn/column/vn-column-combo.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2013 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-column-combo.h" +#include "../vn-model.h" + +G_DEFINE_TYPE (VnColumnCombo, vn_column_combo, VN_TYPE_COLUMN); + +static void vn_column_combo_on_status_changed (DbModel * model, + DbModelStatus status, VnColumn * obj) +{ + if (status == DB_MODEL_STATUS_READY) + g_object_set (obj->cell, "model", VN_COLUMN_COMBO (obj)->tree_model, NULL); + else + g_object_set (obj->cell, "model", NULL, NULL); +} + +static void vn_column_combo_set_model (VnColumnCombo * obj, DbModel * model) +{ + g_return_if_fail (!model || DB_IS_MODEL (model)); + + if (obj->model) + { + g_signal_handlers_disconnect_by_func (obj->model, + vn_column_combo_on_status_changed, obj); + g_clear_object (&obj->model); + g_clear_object (&obj->tree_model); + } + + if (model) + { + obj->model = g_object_ref (model); + obj->tree_model = GTK_TREE_MODEL (vn_model_new (model)); + g_signal_connect (model, "status-changed", + G_CALLBACK (vn_column_combo_on_status_changed), obj); + } +} + +static void vn_column_combo_set_value (VnColumnCombo * obj, GtkTreeModel * model, + GtkTreeIter * tree_iter, GObject * cell, const GValue * value) +{ + if (obj->model && db_model_get_status (obj->model) == DB_MODEL_STATUS_READY) + { + if (gvn_value_is_null (value)) + { + g_object_set_property (cell, "text", value); + } + else + { + DbIter iter; + + if (db_model_search_value (obj->model, obj->index_column, &iter, value)) + g_object_set_property (cell, "text", + db_model_get_value (obj->model, &iter, obj->show_column, NULL)); + } + } +} + +/* + * Restores the focus to the tree view after its edition. + */ +static void vn_column_combo_restore_focus (VnColumnCombo * obj) +{ + gtk_widget_grab_focus + (gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (obj))); +} + +/* + * Sets the autocompletion functionality to the cell being edited. + */ +static void vn_column_combo_on_editing_started (GtkCellRenderer * cell, + GtkCellEditable * editable, const gchar * path, VnColumnCombo * obj) +{ + GtkEntry * entry; + + if (!GTK_IS_COMBO_BOX (editable) || !obj->tree_model) + return; + + entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (editable))); + + if (!obj->completion_ready) + { + gtk_entry_completion_set_model (obj->completion, obj->tree_model); + g_object_set (obj->completion, "text-column", obj->show_column, NULL); + obj->completion_ready = TRUE; + + g_signal_connect_swapped (editable, "editing-done", + G_CALLBACK (vn_column_combo_restore_focus), obj); + obj->editable = editable; + } + + gtk_entry_set_completion (entry, obj->completion); +} + +/* + * When the text is edited using an entry, tries to set the corresponding + * underlying value. + */ +static void vn_column_combo_on_edited (GtkCellRendererText *renderer, + gchar * path, gchar * new_text, VnColumnCombo * obj) +{ + DbIter iter; + GValue value = {0}; + gvn_value_new_with_content (&value, G_TYPE_STRING, new_text); + + if (db_model_search_value (obj->model, obj->show_column, &iter, &value)) + { + const GValue * value; + + if ((value = db_model_get_value (obj->model, &iter, obj->index_column, NULL))) + VN_COLUMN_GET_CLASS (obj)->value_changed (VN_COLUMN (obj), path, value); + } + + g_value_unset (&value); + + vn_column_combo_restore_focus (obj); +} + +static void vn_column_combo_set_editable (VnColumnCombo * obj, gboolean editable) +{ + VnColumn * parent = VN_COLUMN (obj); + g_object_set (parent->cell, "editable", editable, NULL); + + if (editable) + { + g_signal_connect (parent->cell, "editing-started", + G_CALLBACK (vn_column_combo_on_editing_started), obj); + g_signal_connect (parent->cell, "edited", + G_CALLBACK (vn_column_combo_on_edited), obj); + g_signal_connect_swapped (parent->cell, "editing-canceled", + G_CALLBACK (vn_column_combo_restore_focus), obj); + + if (!obj->completion) + obj->completion = gtk_entry_completion_new (); + } + else + { + g_signal_handlers_disconnect_by_func (parent->cell, + vn_column_combo_on_editing_started, obj); + g_signal_handlers_disconnect_by_func (parent->cell, + vn_column_combo_on_edited, obj); + g_signal_handlers_disconnect_by_func (parent->cell, + vn_column_combo_restore_focus, obj); + + if (obj->editable) + g_signal_handlers_disconnect_by_func (obj->editable, + vn_column_combo_restore_focus, obj); + + obj->completion = NULL; + obj->completion_ready = FALSE; + obj->editable = NULL; + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_INDEX_COLUMN = 1 + ,PROP_SHOW_COLUMN + ,PROP_MODEL + ,PROP_CONN + ,PROP_SQL +}; + +static void vn_column_combo_set_property (VnColumnCombo * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_INDEX_COLUMN: + obj->index_column = g_value_get_uint (value); + break; + case PROP_SHOW_COLUMN: + obj->show_column = g_value_get_uint (value); + g_object_set (VN_COLUMN (obj)->cell, "text-column", obj->show_column, + NULL); + break; + case PROP_MODEL: + vn_column_combo_set_model (obj, g_value_get_object (value)); + break; + case PROP_CONN: + db_model_set_conn (obj->model, g_value_get_object (value)); + break; + case PROP_SQL: + { + const gchar * sql = g_value_get_string (value); + + if (sql) + { + DbModel * model = db_model_new_with_sql (NULL, g_value_get_string (value)); + vn_column_combo_set_model (obj, model); + g_object_unref (model); + } + + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_column_combo_get_property (VnColumnCombo * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_INDEX_COLUMN: + g_value_set_uint (value, obj->index_column); + break; + case PROP_SHOW_COLUMN: + g_value_set_uint (value, obj->show_column); + break; + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + case PROP_CONN: + g_value_set_object (value, db_model_get_conn (obj->model)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_combo_init (VnColumnCombo * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_combo_new (); + obj->model = NULL; + obj->tree_model = NULL; + obj->completion = NULL; + obj->completion_ready = FALSE; + obj->editable = NULL; + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); +} + +static void vn_column_combo_finalize (VnColumnCombo * obj) +{ + vn_column_combo_set_model (obj, NULL); + G_OBJECT_CLASS (vn_column_combo_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_combo_class_init (VnColumnComboClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_combo_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_column_combo_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_column_combo_get_property; + VN_COLUMN_CLASS (klass)->set_value = (VnColumnSetValueFunc) vn_column_combo_set_value; + VN_COLUMN_CLASS (klass)->set_editable = (VnColumnSetEditableFunc) vn_column_combo_set_editable; + + g_object_class_install_property (k, PROP_INDEX_COLUMN, + g_param_spec_uint ("index-column" + ,_("Index column") + ,_("The column index of the model") + ,0, 255, 0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SHOW_COLUMN, + g_param_spec_uint ("show-column" + ,_("Show column") + ,_("The column of the model shown by combo") + ,0, 255, 1 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MODEL, + g_param_spec_object ("model" + ,_("Model") + ,_("The model from which the combo takes the values shown in the list") + ,DB_TYPE_MODEL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the model") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query used to create the model") + , NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE + )); +} diff --git a/vn/column/vn-column-combo.h b/vn/column/vn-column-combo.h new file mode 100644 index 0000000..4ae712f --- /dev/null +++ b/vn/column/vn-column-combo.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_COLUMN_COMBO_H +#define VN_COLUMN_COMBO_H + +#include + +#define VN_TYPE_COLUMN_COMBO (vn_column_combo_get_type ()) +#define VN_COLUMN_COMBO(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_COMBO, VnColumnCombo)) +#define VN_IS_COLUMN_COMBO(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_COMBO)) +#define VN_COLUMN_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_COMBO, VnColumnComboClass)) +#define VN_IS_COLUMN_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_COMBO)) +#define VN_COLUMN_COMBO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_COMBO, VnColumnComboClass)) + +typedef struct _VnColumnCombo VnColumnCombo; +typedef struct _VnColumnComboClass VnColumnComboClass; + +struct _VnColumnCombo +{ + VnColumn parent; + /* */ + DbModel * model; + GtkTreeModel * tree_model; + guint index_column; + guint show_column; + GtkEntryCompletion * completion; + gboolean completion_ready; + GtkCellEditable * editable; +}; + +struct _VnColumnComboClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_combo_get_type (); + +#endif \ No newline at end of file diff --git a/vn/column/vn-column-entry.c b/vn/column/vn-column-entry.c new file mode 100644 index 0000000..c388018 --- /dev/null +++ b/vn/column/vn-column-entry.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-column-entry.h" + +G_DEFINE_TYPE (VnColumnEntry, vn_column_entry, VN_TYPE_COLUMN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_column_entry_cb_edited (GtkCellRendererText * cell, + const gchar * path, gchar * text, VnColumnEntry * obj) +{ + GValue value = {0}; + + if (g_strcmp0 (text, "")) + { + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, text); + } + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_COLUMN_GET_CLASS (obj)->value_changed (VN_COLUMN (obj), path, &value); + g_value_unset (&value); +} + +static void vn_column_entry_set_editable (VnColumn * obj, gboolean editable) +{ + g_object_set (obj->cell, "editable", editable, NULL); + + if (editable) + g_signal_connect (obj->cell, "edited", + G_CALLBACK (vn_column_entry_cb_edited), obj); + else + g_signal_handlers_disconnect_by_func (obj->cell, + vn_column_entry_cb_edited, obj); +} + +static void vn_column_entry_set_value (VnColumnEntry * obj, GtkTreeModel * model, + GtkTreeIter * iter, GObject * cell, const GValue * value) +{ + GValue new_value = {0}; + gvn_value_to_format_string (value, obj->digits, &new_value); + g_object_set_property (cell, "text", &new_value); + g_value_unset (&new_value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DIGITS = 1 +}; + +static void vn_column_entry_set_property (VnColumnEntry * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + obj->digits = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_column_entry_get_property (VnColumnEntry * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + g_value_set_uint (value, obj->digits); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_entry_init (VnColumnEntry * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_text_new (); + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); +} + +static void vn_column_entry_finalize (VnColumnEntry * obj) +{ + G_OBJECT_CLASS (vn_column_entry_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_entry_class_init (VnColumnEntryClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_entry_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_column_entry_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_column_entry_get_property; + VN_COLUMN_CLASS (klass)->set_value = (VnColumnSetValueFunc) vn_column_entry_set_value; + VN_COLUMN_CLASS (klass)->set_editable = (VnColumnSetEditableFunc) vn_column_entry_set_editable; + + g_object_class_install_property (k, PROP_DIGITS, + g_param_spec_uint ("digits" + ,_("Digits") + ,_("The number of decimal places to display.") + ,0 ,20 ,0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/column/vn-column-entry.h b/vn/column/vn-column-entry.h new file mode 100644 index 0000000..d80b80a --- /dev/null +++ b/vn/column/vn-column-entry.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_COLUMN_ENTRY_H +#define VN_COLUMN_ENTRY_H + +#include + +#define VN_TYPE_COLUMN_ENTRY (vn_column_entry_get_type ()) +#define VN_COLUMN_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_ENTRY, VnColumnEntry)) +#define VN_IS_COLUMN_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_ENTRY)) +#define VN_COLUMN_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_ENTRY, VnColumnEntryClass)) +#define VN_IS_COLUMN_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_ENTRY)) +#define VN_COLUMN_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_ENTRY, VnColumnEntryClass)) + +typedef struct _VnColumnEntry VnColumnEntry; +typedef struct _VnColumnEntryClass VnColumnEntryClass; + +struct _VnColumnEntry +{ + VnColumn parent; + guint digits; +}; + +struct _VnColumnEntryClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_entry_get_type (); + +#endif \ No newline at end of file diff --git a/vn/column/vn-column-image.c b/vn/column/vn-column-image.c new file mode 100644 index 0000000..5177f6c --- /dev/null +++ b/vn/column/vn-column-image.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-column-image.h" +#include "../vn-model.h" + +#define LOAD_IMAGE _IMAGE_DIR"/load.gif" + +G_DEFINE_TYPE (VnColumnImage, vn_column_image, VN_TYPE_COLUMN); + +typedef struct +{ + VnColumnImage * obj; + GtkTreeModel * model; + GtkTreeIter * iter; + GtkCellRenderer * cell; + gchar * name; +} +DownloadData; + +typedef struct +{ + gchar * path; + gboolean error; + gint tooltip_size; + GtkWidget * image; +} +TooltipData; + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +static void vn_column_image_download_error (VnColumnImage * obj, const GError * error) +{ + g_object_set (VN_COLUMN (obj)->cell, "icon-name", "image-missing", NULL); +} + +static void free_object (gpointer object) +{ + if (object != NULL) + g_object_unref (object); +} + +static void free_tooltip_data (TooltipData * data) +{ + if (data) + { + g_free (data->path); + + if (data->image) + g_object_unref (data->image); + } + + g_free (data); +} + +static void vn_column_image_on_tooltip_size (GdkPixbufLoader * loader, gint width, + gint height, gpointer tsize) +{ + gint h, w, size = GPOINTER_TO_INT (tsize); + + if (width > size || height > size) + { + if (width >= height) + { + w = size; + h = height * w / width; + } + else + { + h = size; + w = width * h / height; + } + } + else + { + w = width; + h = height; + } + + gdk_pixbuf_loader_set_size (loader, w, h); +} + +static void vn_column_image_on_download_tooltip (DbFileLoader * fl, + GBytes * bytes, const GError * error, TooltipData * data) +{ + if (error) + data->image = g_object_ref_sink (gtk_image_new_from_icon_name + ("image-missing", GTK_ICON_SIZE_MENU)); + else if (bytes && data) + { + gsize size; + GError * err = NULL; + const guchar * raw_data = g_bytes_get_data (bytes, &size); + GdkPixbufLoader * loader = gdk_pixbuf_loader_new (); + + if (data->tooltip_size > 0) + g_signal_connect (loader, "size-prepared", + G_CALLBACK (vn_column_image_on_tooltip_size), + GINT_TO_POINTER (data->tooltip_size)); + + if (gdk_pixbuf_loader_write (loader, raw_data, size, &err) + && gdk_pixbuf_loader_close (loader, &err)) + { + GdkPixbuf * pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + if (data->image) + g_object_unref (data->image); + + data->image = g_object_ref_sink (gtk_image_new_from_pixbuf (pixbuf)); + } + else + { + gdk_pixbuf_loader_close (loader, NULL); + g_error_free (err); + } + + g_object_unref (loader); + } + + gtk_tooltip_trigger_tooltip_query (gdk_display_get_default()); +} + +static gboolean vn_column_image_on_query_tooltip (GtkTreeView * view, + gint x, gint y, gboolean k, GtkTooltip * tip, VnColumnImage * obj) +{ + gboolean ret = FALSE; + TooltipData * data; + GtkTreeIter iter; + GtkTreePath * path = NULL; + + if (obj && VN_IS_COLUMN_IMAGE (obj) && !k + && gtk_tree_view_get_tooltip_context (view, &x, &y, k, NULL, &path, &iter)) + { + gint wx, wy; + GdkRectangle rect; + + gtk_tree_view_convert_bin_window_to_widget_coords (view, x, y, &wx, &wy); + gtk_tree_view_get_background_area + (view, path, GTK_TREE_VIEW_COLUMN (obj), &rect); + + if (!(rect.x < wx && wx < rect.x + rect.width)) + { + gtk_tree_path_free (path); + return FALSE; + } + } + else + return FALSE; + + if (obj->tooltips + && (data = g_hash_table_lookup (obj->tooltips, iter.user_data)) + && !data->error && data->path) + { + gtk_tree_view_set_tooltip_cell (view, tip, path, + GTK_TREE_VIEW_COLUMN (obj), VN_COLUMN (obj)->cell); + + if (!data->image) + { + data->image = g_object_ref_sink (gtk_image_new_from_file (LOAD_IMAGE)); + gtk_tooltip_set_custom (tip, data->image); + + db_file_loader_download (obj->loader, data->path, + (DbFileLoaderCallbackFunc) vn_column_image_on_download_tooltip, data); + } + else + gtk_tooltip_set_custom (tip, data->image); + + ret = TRUE; + } + + gtk_tree_path_free (path); + return ret; +} + +static GdkPixbuf * vn_column_image_set_image (VnColumnImage * obj, + GtkCellRenderer * cell, GBytes * bytes, gboolean pix) +{ + gsize size; + GdkPixbuf * pixbuf = NULL; + GError * error = NULL; + const guchar * raw_data = g_bytes_get_data (bytes, &size); + GdkPixbufLoader * loader = gdk_pixbuf_loader_new (); + + if (raw_data) + { + if (gdk_pixbuf_loader_write (loader, raw_data, size, &error) + && gdk_pixbuf_loader_close (loader, &error)) + { + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + g_object_set (cell, "pixbuf", pixbuf, NULL); + } + else + { + gdk_pixbuf_loader_close (loader, NULL); + vn_column_image_download_error (obj, error); + g_error_free (error); + } + } + else + gdk_pixbuf_loader_close (loader, NULL); + + g_object_unref (loader); + + return pix && pixbuf ? g_object_ref (pixbuf) : NULL; +} + +static void vn_column_image_on_download (DbFileLoader * obj, GBytes * bytes, + const GError * error, DownloadData * data) +{ + GtkTreePath * path; + + if (error) + vn_column_image_download_error (data->obj, error); + else if (bytes && vn_model_iter_is_valid (data->iter, data->model)) + { + if (data->obj->loaded) + g_hash_table_replace (data->obj->loaded, g_strdup (data->name), + vn_column_image_set_image (data->obj, data->cell, bytes, TRUE)); + + path = gtk_tree_model_get_path (data->model, data->iter); + gtk_tree_model_row_changed (data->model, path, data->iter); + gtk_tree_path_free (path); + } + + g_object_unref (data->obj); + g_object_unref (data->model); + gtk_tree_iter_free (data->iter); + g_free (data->name); + g_free (data); +} + +static void vn_column_image_set_value (VnColumnImage * obj, GtkTreeModel * model, + GtkTreeIter * iter, GtkCellRenderer * cell, const GValue * value) +{ + GType type = G_VALUE_TYPE (value); + + if (type == GVN_TYPE_NULL) + g_object_set (cell, "pixbuf", NULL, "icon-name", NULL, NULL); + else if (type == G_TYPE_BYTES) + { + GBytes * bytes = g_value_get_boxed (value); + + if (bytes) + vn_column_image_set_image (obj, cell, bytes, FALSE); + else + vn_column_image_download_error (obj, NULL); + } + else if (type == G_TYPE_STRING) + { + DownloadData * data; + gchar * name = g_value_dup_string (value); + GtkTreeView * view = GTK_TREE_VIEW + (gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (obj))); + + if (obj->loaded) + { + if (g_hash_table_contains (obj->loaded, name)) + { + GdkPixbuf * pixbuf = g_hash_table_lookup (obj->loaded, name); + + if (pixbuf) + g_object_set (cell, "pixbuf", pixbuf, NULL); + else + vn_column_image_download_error (obj, NULL); + + if (obj->tooltips + && !g_hash_table_contains (obj->tooltips, iter->user_data)) + { + TooltipData * data = g_new (TooltipData, 1); + data->path = g_strconcat ("/", obj->tooltip_path, "/", name, NULL); + data->error = FALSE; + data->tooltip_size = obj->tooltip_size; + data->image = NULL; + + g_hash_table_insert (obj->tooltips, iter->user_data, data); + } + + g_free (name); + + return; + } + else + { + gint view_x, view_y; + GdkRectangle view_rect, cell_rect; + GtkTreePath * path = gtk_tree_model_get_path (model, iter); + + gtk_tree_view_get_cell_area + (view, path, GTK_TREE_VIEW_COLUMN (obj), &cell_rect); + gtk_tree_path_free (path); + gtk_tree_view_get_visible_rect (view, &view_rect); + gtk_tree_view_convert_tree_to_bin_window_coords + (view, view_rect.x, view_rect.y, &view_x, &view_y); + + if (!(view_x <= cell_rect.x && cell_rect.x <= view_x + view_rect.width + && view_y <= cell_rect.y && cell_rect.y <= view_y + view_rect.height)) + { + g_free (name); + return; + } + } + } + else + obj->loaded = g_hash_table_new_full + ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, (GDestroyNotify) free_object); + + g_hash_table_insert (obj->loaded, g_strdup (name), NULL); + + if (!obj->loader) + obj->loader = db_file_loader_new (obj->host, obj->path); + + if (obj->tooltip_path && !obj->tooltips) + obj->tooltips = g_hash_table_new_full + ((GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal, + (GDestroyNotify) NULL, (GDestroyNotify) free_tooltip_data); + g_object_set (view, "has-tooltip", TRUE, NULL); + g_signal_connect (view, "query-tooltip", + G_CALLBACK (vn_column_image_on_query_tooltip), obj); + + data = g_new (DownloadData, 1); + data->obj = g_object_ref (obj); + data->model = g_object_ref (model); + data->iter = gtk_tree_iter_copy (iter); + data->cell = cell; + data->name = name; + + db_file_loader_download (obj->loader, name, + (DbFileLoaderCallbackFunc) vn_column_image_on_download, data); + } +} + +static void vn_column_image_set_editable (VnColumn * obj, gboolean editable){} + +static void vn_column_image_on_model_changed (VnColumnImage * obj) +{ + if (obj->loader) + db_file_loader_cancel_all (obj->loader); + + if (obj->loaded) + g_hash_table_destroy (obj->loaded); + + if (obj->tooltips) + { + g_hash_table_destroy (obj->tooltips); + g_signal_handlers_disconnect_by_func (GTK_TREE_VIEW + (gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (obj))), + vn_column_image_on_query_tooltip, obj); + } + + obj->loaded = NULL; + obj->tooltips = NULL; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Property + +enum +{ + PROP_HOST = 1 + ,PROP_PATH + ,PROP_TOOLTIP_PATH + ,PROP_TOOLTIP_SIZE +}; + +static void vn_column_image_set_property (VnColumnImage * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_HOST: + g_free (obj->host); + obj->host = g_value_dup_string (value); + break; + case PROP_PATH: + g_free (obj->path); + obj->path = g_value_dup_string (value); + break; + case PROP_TOOLTIP_PATH: + g_free (obj->tooltip_path); + obj->tooltip_path = g_value_dup_string (value); + break; + case PROP_TOOLTIP_SIZE: + obj->tooltip_size = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_column_image_get_property (VnColumnImage * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_HOST: + g_value_set_string (value, obj->host); + break; + case PROP_PATH: + g_value_set_string (value, obj->path); + break; + case PROP_TOOLTIP_PATH: + g_value_set_string (value, obj->tooltip_path); + break; + case PROP_TOOLTIP_SIZE: + g_value_set_int (value, obj->tooltip_size); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_image_init (VnColumnImage * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_pixbuf_new (); + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); + + obj->host = NULL; + obj->path = NULL; + obj->tooltip_path = NULL; + obj->loader = NULL; + obj->loaded = NULL; + obj->tooltips = NULL; +} + +static void vn_column_image_finalize (VnColumnImage * obj) +{ + g_free (obj->host); + g_free (obj->path); + g_free (obj->tooltip_path); + + if (obj->loader) + { + db_file_loader_cancel_all (obj->loader); + g_object_unref (obj->loader); + } + + if (obj->loaded) + g_hash_table_destroy (obj->loaded); + + if (obj->tooltips) + g_hash_table_destroy (obj->tooltips); + + G_OBJECT_CLASS (vn_column_image_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_image_class_init (VnColumnImageClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + VnColumnClass * col_k = VN_COLUMN_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_image_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_column_image_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_column_image_get_property; + col_k->set_value = (VnColumnSetValueFunc) vn_column_image_set_value; + col_k->set_editable = (VnColumnSetEditableFunc) vn_column_image_set_editable; + col_k->model_changed = (VnColumnModelChangedFunc) vn_column_image_on_model_changed; + + g_object_class_install_property (k, PROP_HOST, + g_param_spec_string ("host" + ,_("Host") + ,_("The host web server name to get the images") + ,"www.verdnatura.es"//XXX NULL + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_PATH, + g_param_spec_string ("path" + ,_("Path") + ,_("Base path from the host where the images will be downloaded") + ,"image/cache/catalog/icon"//XXX NULL + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_TOOLTIP_PATH, + g_param_spec_string ("tooltip-path" + ,_("Tooltip path") + ,_("Prefix for the path of the images to be shown in the tooltip. " + "Starting after the path of the column and appending the name on " + "each cell") + ,"../full"//XXX NULL + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_TOOLTIP_SIZE, + g_param_spec_int ("tooltip-size" + ,_("Tooltip size") + ,_("Size of the bigger side of the tooltip images, the another side " + "will be scaled accordingly and smaller images won't be scaled") + ,-1 + ,G_MAXINT + ,300 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/column/vn-column-image.h b/vn/column/vn-column-image.h new file mode 100644 index 0000000..6c12ac3 --- /dev/null +++ b/vn/column/vn-column-image.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_COLUMN_IMAGE_H +#define VN_COLUMN_IMAGE_H + +#include + +#define VN_TYPE_COLUMN_IMAGE (vn_column_image_get_type ()) +#define VN_COLUMN_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_IMAGE, VnColumnImage)) +#define VN_IS_COLUMN_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_IMAGE)) +#define VN_COLUMN_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_IMAGE, VnColumnImageClass)) +#define VN_IS_COLUMN_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_IMAGE)) +#define VN_COLUMN_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_IMAGE, VnColumnImageClass)) + +typedef struct _VnColumnImage VnColumnImage; +typedef struct _VnColumnImageClass VnColumnImageClass; + +/** + * VnColumnImage: + **/ +struct _VnColumnImage +{ + /* */ + VnColumn parent; + gchar * host; + gchar * path; + gchar * tooltip_path; + gint tooltip_size; + DbFileLoader * loader; + GHashTable * loaded; + GHashTable * tooltips; +}; + +struct _VnColumnImageClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_image_get_type (); + +#endif diff --git a/vn/column/vn-column-spin.c b/vn/column/vn-column-spin.c new file mode 100644 index 0000000..9145a4c --- /dev/null +++ b/vn/column/vn-column-spin.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-column-spin.h" + +G_DEFINE_TYPE (VnColumnSpin, vn_column_spin, VN_TYPE_COLUMN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_spin_init (VnColumnSpin * obj) +{ + GtkCellRenderer * cell = gtk_cell_renderer_spin_new (); + VN_COLUMN_GET_CLASS (obj)->set_renderer (VN_COLUMN (obj), cell); +} + +static void vn_column_spin_finalize (VnColumnSpin * obj) +{ + G_OBJECT_CLASS (vn_column_spin_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_spin_class_init (VnColumnSpinClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_spin_finalize; +} diff --git a/vn/column/vn-column-spin.h b/vn/column/vn-column-spin.h new file mode 100644 index 0000000..fab80ef --- /dev/null +++ b/vn/column/vn-column-spin.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_COLUMN_SPIN_H +#define VN_COLUMN_SPIN_H + +#include + +#define VN_TYPE_COLUMN_SPIN (vn_column_spin_get_type ()) +#define VN_COLUMN_SPIN(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN_SPIN, VnColumnSpin)) +#define VN_IS_COLUMN_SPIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN_SPIN)) +#define VN_COLUMN_SPIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN_SPIN, VnColumnSpinClass)) +#define VN_IS_COLUMN_SPIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN_SPIN)) +#define VN_COLUMN_SPIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN_SPIN, VnColumnSpinClass)) + +typedef struct _VnColumnSpin VnColumnSpin; +typedef struct _VnColumnSpinClass VnColumnSpinClass; + +struct _VnColumnSpin +{ + VnColumn parent; +}; + +struct _VnColumnSpinClass +{ + /* */ + VnColumnClass parent; +}; + +GType vn_column_spin_get_type (); + +#endif \ No newline at end of file diff --git a/vn/field/Makefile.am b/vn/field/Makefile.am new file mode 100644 index 0000000..10f75cd --- /dev/null +++ b/vn/field/Makefile.am @@ -0,0 +1,30 @@ +include $(top_srcdir)/Makefile.decl + +field_lib_LTLIBRARIES = libvnfield.la +field_include_HEADERS = \ + field.h \ + vn-calendar.h \ + vn-check.h \ + vn-combo.h \ + vn-spin.h \ + vn-entry.h \ + vn-image.h \ + vn-completion.h \ + vn-date-chooser.h \ + vn-http-image.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(gtk_CFLAGS) +libvnfield_la_LIBADD = $(gtk_LIBS) +libvnfield_la_SOURCES = \ + $(field_include_HEADERS) \ + vn-calendar.c \ + vn-check.c \ + vn-combo.c \ + vn-spin.c \ + vn-entry.c \ + vn-image.c \ + vn-completion.c \ + vn-date-chooser.c \ + vn-http-image.c diff --git a/vn/field/field.h b/vn/field/field.h new file mode 100644 index 0000000..a66e177 --- /dev/null +++ b/vn/field/field.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef FIELD_H +#define FIELD_H + +#include "vn-entry.h" +#include "vn-spin.h" +#include "vn-calendar.h" +#include "vn-combo.h" +#include "vn-check.h" +#include "vn-image.h" +#include "vn-completion.h" +#include "vn-date-chooser.h" +#include "vn-http-image.h" + +#endif \ No newline at end of file diff --git a/vn/field/vn-calendar.c b/vn/field/vn-calendar.c new file mode 100644 index 0000000..986748c --- /dev/null +++ b/vn/field/vn-calendar.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-calendar.h" +#include + +/** + * SECTION:vn-calendar + * @Short_description: a calendar widget + * @Title: VnCalendar + * @See_also: #VnField + * @Image: vn-calendar.png + * + * A calendar widget to see or set the date. + */ +G_DEFINE_TYPE (VnCalendar, vn_calendar, VN_TYPE_FIELD); + +/** + * vn_calendar_new: + * + * Creates a new #VnCalendar + * + * Return value: a #VnCalendar + **/ +VnField * vn_calendar_new () +{ + return g_object_new (VN_TYPE_CALENDAR, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_calendar_cb_day_selected (GtkCalendar * calendar, VnCalendar * obj) +{ + guint year; + guint month; + guint day; + GDate new_date; + GValue value = {0}; + + gtk_calendar_clear_marks (calendar); + gtk_calendar_get_date (calendar, &year, &month, &day); + month++; + + g_date_clear (&new_date, 1); + g_date_set_dmy (&new_date, day, month, year); + + if (!g_date_valid (&obj->date) || g_date_compare (&new_date, &obj->date)) + { + g_date_set_julian (&obj->date, g_date_get_julian (&new_date)); + gtk_calendar_mark_day (calendar, day); + g_value_init (&value, G_TYPE_DATE); + g_value_set_boxed (&value, &obj->date); + } + else + { + g_date_clear (&obj->date, 1); + g_value_init (&value, GVN_TYPE_NULL); + } + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); +} + +void vn_calendar_cb_month_changed (GtkCalendar * calendar, VnCalendar * obj) +{ + guint year; + guint month; + GDate * date = &obj->date; + + gtk_calendar_clear_marks (calendar); + gtk_calendar_get_date (calendar, &year, &month, NULL); + + if (g_date_valid (date) + && g_date_get_month (date) == month + 1 + && g_date_get_year (date) == year) + { + guint day = g_date_get_day (date); + gtk_calendar_mark_day (calendar, day); + gtk_calendar_select_day (obj->calendar, day); + } + else + gtk_calendar_select_day (obj->calendar, 0); +} + +static void vn_calendar_set_value (VnCalendar * obj, const GValue * value) +{ + GDate * date = NULL; + + if (G_VALUE_TYPE (value) != G_TYPE_DATE) + { + GValue new_value = {0}; + g_value_init (&new_value, G_TYPE_DATE); + g_value_transform (value, &new_value); + date = g_value_get_boxed (&new_value); + } + else if (!gvn_value_is_null (value)) + date = g_value_get_boxed (value); + + if (date) + { + obj->date = *date; + gtk_calendar_select_month (obj->calendar + ,g_date_get_month (date) - 1 + ,g_date_get_year (date) + ); + } + else + { + g_date_clear (&obj->date, 1); + gtk_calendar_select_day (obj->calendar, 0); + gtk_calendar_clear_marks (obj->calendar); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_calendar_init (VnCalendar * obj) +{ + g_date_clear (&obj->date, 1); + + obj->calendar = GTK_CALENDAR (gtk_calendar_new ()); + gtk_calendar_set_display_options (obj->calendar, + GTK_CALENDAR_SHOW_HEADING | GTK_CALENDAR_SHOW_DAY_NAMES); + g_object_connect (obj->calendar + ,"signal::day-selected-double-click", vn_calendar_cb_day_selected, obj + ,"signal::month-changed", vn_calendar_cb_month_changed, obj + ,NULL + ); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->calendar)); + VN_FIELD (obj)->field = GTK_WIDGET (obj->calendar); +} + +static void vn_calendar_finalize (VnCalendar * obj) +{ + G_OBJECT_CLASS (vn_calendar_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_calendar_class_init (VnCalendarClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_calendar_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_calendar_set_value; +} diff --git a/vn/field/vn-calendar.h b/vn/field/vn-calendar.h new file mode 100644 index 0000000..037b4a5 --- /dev/null +++ b/vn/field/vn-calendar.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_CALENDAR_H +#define VN_CALENDAR_H + +#include + +#define VN_TYPE_CALENDAR (vn_calendar_get_type ()) +#define VN_CALENDAR(object) (G_TYPE_CHECK_INSTANCE_CAST (object, VN_TYPE_CALENDAR , VnCalendar)) +#define VN_IS_CALENDAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_CALENDAR)) +#define VN_CALENDAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_CALENDAR, VnCalendarClass)) +#define VN_IS_CALENDAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_CALENDAR)) +#define VN_CALENDAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_CALENDAR, VnCalendarClass)) + +typedef struct _VnCalendar VnCalendar; +typedef struct _VnCalendarClass VnCalendarClass; + +struct _VnCalendar +{ + VnField parent; + GtkCalendar * calendar; + GDate date; +}; + +struct _VnCalendarClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_calendar_get_type (); +VnField * vn_calendar_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-check.c b/vn/field/vn-check.c new file mode 100644 index 0000000..61d3aec --- /dev/null +++ b/vn/field/vn-check.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-check.h" +#include + +/** + * SECTION:vn-check + * @Short_description: a checkbox widget + * @Title: VnCheck + * @See_also: #VnField + * @Image: check.png + * + * A checkbox widget to use in boolean fields. + */ +G_DEFINE_TYPE (VnCheck, vn_check, VN_TYPE_FIELD); + +/** + * vn_check_new: + * + * Creates a new #VnCheck + * + * Return value: a #VnCheck + **/ +VnField * vn_check_new () +{ + return VN_FIELD (g_object_new (VN_TYPE_CHECK, NULL)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_check_changed (VnCheck * obj) +{ + GValue value = {0}; + + if (!gtk_toggle_button_get_inconsistent (obj->button)) + { + g_value_init (&value, G_TYPE_BOOLEAN); + g_value_set_boolean (&value, gtk_toggle_button_get_active (obj->button)); + } + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); +} + +static void vn_check_on_toggled (GtkToggleButton * button, VnCheck * obj) +{ + gtk_toggle_button_set_inconsistent (button, FALSE); + vn_check_changed (obj); +} + +static gboolean vn_check_on_secondary_button (GtkToggleButton * button, GdkEventButton * event, VnField * obj) +{ + if (event->type != GDK_BUTTON_RELEASE || event->button != 3 || !vn_field_get_null (obj)) + return FALSE; + + if (gtk_toggle_button_get_inconsistent (button)) + gtk_toggle_button_set_inconsistent (button, FALSE); + else + gtk_toggle_button_set_inconsistent (button, TRUE); + + g_signal_handlers_block_by_func (button, vn_check_on_toggled, obj); + gtk_toggle_button_set_active (button, FALSE); + g_signal_handlers_unblock_by_func (button, vn_check_on_toggled, obj); + + vn_check_changed (VN_CHECK (obj)); + return FALSE; +} + +static void vn_check_set_value (VnCheck * obj, const GValue * value) +{ + GValue new_value = {0}; + + if (!gvn_value_is_null (value)) + { + g_value_init (&new_value, G_TYPE_BOOLEAN); + g_value_transform (value, &new_value); + } + else + g_value_init (&new_value, GVN_TYPE_NULL); + + g_signal_handlers_block_by_func (obj->button, vn_check_on_toggled, obj); + + if (!gvn_value_is_null (&new_value)) + { + gtk_toggle_button_set_inconsistent (obj->button, FALSE); + gtk_toggle_button_set_active (obj->button, + g_value_get_boolean (&new_value)); + } + else + { + gtk_toggle_button_set_inconsistent (obj->button, TRUE); + gtk_toggle_button_set_active (obj->button, FALSE); + } + + g_signal_handlers_unblock_by_func (obj->button, vn_check_on_toggled, obj); + + g_value_unset (&new_value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_check_init (VnCheck * obj) +{ + obj->button = GTK_TOGGLE_BUTTON (gtk_check_button_new ()); + gtk_button_set_use_underline (GTK_BUTTON (obj->button), TRUE); + g_object_connect (obj->button + ,"signal::toggled", vn_check_on_toggled, obj + ,"signal::button-release-event", vn_check_on_secondary_button, obj + ,NULL + ); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->button)); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->button); +} + +static void vn_check_finalize (VnCheck * obj) +{ + G_OBJECT_CLASS (vn_check_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_check_class_init (VnCheckClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_check_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_check_set_value; +} diff --git a/vn/field/vn-check.h b/vn/field/vn-check.h new file mode 100644 index 0000000..a02e1c2 --- /dev/null +++ b/vn/field/vn-check.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_CHECK_H +#define VN_CHECK_H + +#include + +#define VN_TYPE_CHECK (vn_check_get_type ()) +#define VN_CHECK(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_CHECK , VnCheck)) +#define VN_IS_CHECK(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_CHECK)) +#define VN_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_CHECK, VnCheckClass)) +#define VN_IS_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_CHECK)) +#define VN_CHECK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_CHECK, VnCheckClass)) + +typedef struct _VnCheck VnCheck; +typedef struct _VnCheckClass VnCheckClass; + +struct _VnCheck +{ + VnField parent; + GtkToggleButton * button; +}; + +struct _VnCheckClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_check_get_type (); +VnField * vn_check_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-combo.c b/vn/field/vn-combo.c new file mode 100644 index 0000000..8d4d5a4 --- /dev/null +++ b/vn/field/vn-combo.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-combo.h" +#include "../vn-model.h" +#include + +/** + * SECTION:vn-combo + * @Short_description: a combo box widget + * @Title: VnCombo + * @See_also: #VnField + * @Image: combo.png + * + * A combo box widget to select from a list of items, selected from a + * database. + */ +G_DEFINE_TYPE (VnCombo, vn_combo, VN_TYPE_FIELD); + +/** + * vn_combo_new: + * @model: the model used by combo box + * + * Creates a new #VnCombo + * + * Return value: a #VnCombo + **/ +VnField * vn_combo_new (DbModel * model) +{ + return g_object_new (VN_TYPE_COMBO, "model", model, NULL); +} + +/** + * vn_combo_new_with_sql: + * @conn: the connection used to create the combo model + * @sql: the SQL query used to create the combo model + * + * Creates a new #VnCombo + * + * Return value: a #VnCombo + **/ +VnField * vn_combo_new_with_sql (DbConn * conn, const gchar * sql) +{ + return g_object_new (VN_TYPE_COMBO, "sql", sql, "conn", conn, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_combo_cb_changed (GtkComboBox * combo, VnCombo * obj) +{ + GtkTreeIter iter; + GValue value = {0}; + + if (gtk_combo_box_get_active_iter (combo, &iter)) + gtk_tree_model_get_value (obj->tree, &iter, obj->index_column, &value); + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); +} + +static void vn_combo_on_model_ready (VnCombo * obj, const GValue * value) +{ + DbIter iter; + + g_signal_handlers_block_by_func (obj->combo, + vn_combo_cb_changed, obj); + + if (!gvn_value_is_null (value) + && db_model_search_value (obj->model, obj->index_column, &iter, value)) + { + GtkTreeIter tree_iter; + + vn_gtk_tree_iter_from_db_iter (&tree_iter, &iter); + gtk_combo_box_set_active_iter (obj->combo, &tree_iter); + } + else + gtk_combo_box_set_active_iter (obj->combo, NULL); + + g_signal_handlers_unblock_by_func (obj->combo, + vn_combo_cb_changed, obj); +} + +static void vn_combo_set_value (VnCombo * obj, const GValue * value) +{ + if (obj->model && db_model_get_status (obj->model) == DB_MODEL_STATUS_READY) + vn_combo_on_model_ready (obj, value); +} + +static void vn_combo_cb_status_changed (DbModel * model, DbModelStatus status, VnCombo * obj) +{ + if (status == DB_MODEL_STATUS_READY) + { + gtk_combo_box_set_model (obj->combo, obj->tree); + vn_combo_on_model_ready (obj, vn_field_get_value (VN_FIELD (obj))); + } + else + gtk_combo_box_set_model (obj->combo, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_combo_set_conn: + * @obj: a #VnCombo + * @conn: the #DbConn + * + * Sets the connection used to create the combo model. + **/ +void vn_combo_set_conn (VnCombo * obj, DbConn * conn) +{ + g_return_if_fail (VN_IS_COMBO (obj)); + g_return_if_fail (DB_IS_CONN (conn)); + g_return_if_fail (obj->model); + + db_model_set_conn (obj->model, conn); +} + +/** + * vn_combo_set_model: + * @obj: a #VnCombo + * @model: the #DbModel + * + * Sets the SQL query used to create the combo model. + **/ +void vn_combo_set_model (VnCombo * obj, DbModel * model) +{ + g_return_if_fail (VN_IS_COMBO (obj)); + g_return_if_fail (DB_IS_MODEL (model) || !model); + + if (obj->model) + { + g_signal_handlers_disconnect_by_func (obj->model, + vn_combo_cb_status_changed, obj); + g_clear_object (&obj->model); + g_clear_object (&obj->tree); + } + if (model) + { + obj->tree = GTK_TREE_MODEL (vn_model_new (model)); + obj->model = g_object_ref (model); + g_signal_connect (model, "status-changed", + G_CALLBACK (vn_combo_cb_status_changed), obj); + vn_combo_cb_status_changed (model, + db_model_get_status (model), obj); + } +} + +/** + * vn_combo_get_index_column: + * @obj: a #VnCombo + * + * Gets the combo column index. + * + * Return value: the column + **/ +guint vn_combo_get_index_column (VnCombo * obj) +{ + g_return_val_if_fail (VN_IS_COMBO (obj), 0); + + return obj->index_column; +} + +/** + * vn_combo_set_index_column: + * @obj: a #VnCombo + * @column: the column + * + * Sets the column index used by combo. + **/ +void vn_combo_set_index_column (VnCombo * obj, guint column) +{ + g_return_if_fail (VN_IS_COMBO (obj)); + + obj->index_column = column; + vn_combo_set_value (obj, vn_field_get_value (VN_FIELD (obj))); +} + +/** + * vn_combo_get_show_column: + * @obj: a #VnCombo + * + * Gets the column shown by combo. + * + * Return value: the column + **/ +guint vn_combo_get_show_column (VnCombo * obj) +{ + g_return_val_if_fail (VN_IS_COMBO (obj), 0); + + return obj->show_column; +} + +/** + * vn_combo_set_show_column: + * @obj: a #VnCombo + * @column: the column + * + * Sets the column shown by combo. + **/ +void vn_combo_set_show_column (VnCombo * obj, guint column) +{ + g_return_if_fail (VN_IS_COMBO (obj)); + + obj->show_column = column; + gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (obj->combo), obj->cell); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (obj->combo), + obj->cell, "text", (gint) obj->show_column); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_INDEX_COLUMN = 1 + ,PROP_SHOW_COLUMN + ,PROP_MODEL + ,PROP_CONN + ,PROP_SQL +}; + +static void vn_combo_set_property (VnCombo * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_INDEX_COLUMN: + vn_combo_set_index_column (obj, g_value_get_uint (value)); + break; + case PROP_SHOW_COLUMN: + vn_combo_set_show_column (obj, g_value_get_uint (value)); + break; + case PROP_MODEL: + vn_combo_set_model (obj, g_value_get_object (value)); + break; + case PROP_CONN: + vn_combo_set_conn (obj, g_value_get_object (value)); + break; + case PROP_SQL: + { + const gchar * sql = g_value_get_string (value); + + if (sql) + { + DbModel * model = db_model_new_with_sql (NULL, g_value_get_string (value)); + vn_combo_set_model (obj, model); + g_object_unref (model); + } + + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void vn_combo_get_property (VnCombo * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_INDEX_COLUMN: + g_value_set_uint (value, obj->index_column); + break; + case PROP_SHOW_COLUMN: + g_value_set_uint (value, obj->show_column); + break; + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + case PROP_CONN: + g_value_set_object (value, db_model_get_conn (obj->model)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_combo_init (VnCombo * obj) +{ + obj->tree = NULL; + obj->model = NULL; + + obj->combo = GTK_COMBO_BOX (gtk_combo_box_new ()); + g_signal_connect (obj->combo, "changed", + G_CALLBACK (vn_combo_cb_changed), obj); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->combo)); + + obj->cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (obj->combo), obj->cell, TRUE); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->combo); +} + +static void vn_combo_finalize (VnCombo * obj) +{ + vn_combo_set_model (obj, NULL); + G_OBJECT_CLASS (vn_combo_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_combo_class_init (VnComboClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_combo_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_combo_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_combo_get_property; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_combo_set_value; + + g_object_class_install_property (k, PROP_INDEX_COLUMN, + g_param_spec_uint ("index-column" + ,_("Index column") + ,_("The column index of the model") + ,0, 255, 0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SHOW_COLUMN, + g_param_spec_uint ("show-column" + ,_("Show column") + ,_("The column of the model shown by combo") + ,0, 255, 1 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_MODEL, + g_param_spec_object ("model" + ,_("Model") + ,_("The model from which the combo takes the values shown in the list") + ,DB_TYPE_MODEL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the model") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query used to create the model") + , NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE + )); +} diff --git a/vn/field/vn-combo.h b/vn/field/vn-combo.h new file mode 100644 index 0000000..34436a7 --- /dev/null +++ b/vn/field/vn-combo.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_COMBO_H +#define VN_COMBO_H + +#include + +#define VN_TYPE_COMBO (vn_combo_get_type ()) +#define VN_COMBO(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COMBO , VnCombo)) +#define VN_IS_COMBO(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COMBO)) +#define VN_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COMBO, VnComboClass)) +#define VN_IS_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COMBO)) +#define VN_COMBO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COMBO, VnComboClass)) + +typedef struct _VnCombo VnCombo; +typedef struct _VnComboClass VnComboClass; + +struct _VnCombo +{ + VnField parent; + DbModel * model; + GtkComboBox * combo; + GtkCellRenderer * cell; + GtkTreeModel * tree; + guint show_column; + guint index_column; +}; + +struct _VnComboClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_combo_get_type (); +VnField * vn_combo_new (DbModel * model); +VnField * vn_combo_new_with_sql (DbConn * conn, const gchar * sql); +void vn_combo_set_conn (VnCombo * obj, DbConn * conn); +void vn_combo_set_model (VnCombo * obj, DbModel * model); +guint vn_combo_get_index_column (VnCombo * obj); +void vn_combo_set_index_column (VnCombo * obj, guint column); +guint vn_combo_get_show_column (VnCombo * obj); +void vn_combo_set_show_column (VnCombo * obj, guint column); + +#endif \ No newline at end of file diff --git a/vn/field/vn-completion.c b/vn/field/vn-completion.c new file mode 100644 index 0000000..5e00843 --- /dev/null +++ b/vn/field/vn-completion.c @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-completion.h" +#include +#include +#include + +#define set_icon(obj,icon_name) (gtk_entry_set_icon_from_icon_name (obj->entry, GTK_ENTRY_ICON_SECONDARY, icon_name)) + +/** + * SECTION:vn-completion + * @Short_description: an auto-completable text box + * @Title: VnCompletion + * @See_also: #VnField + * @Image: completion.png + * + * A text box widget that auto-completes as the user writes using the data + * retrieved from a database. + */ +G_DEFINE_TYPE (VnCompletion, vn_completion, VN_TYPE_FIELD); + +/** + * vn_completion_new: + * @sql: the SQL string used by completion. + * @field: the field where to search the values. + * + * Creates a new #VnCompletion. + * + * Return value: a #VnCompletion. + **/ +VnField * vn_completion_new (const gchar * sql, const gchar * field) +{ + return g_object_new (VN_TYPE_COMPLETION, "sql", sql, "field", field, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_completion_cb_status_changed (DbModel * model, DbModelStatus status, VnCompletion * obj) +{ + const gchar * icon_name; + + if (status == DB_MODEL_STATUS_READY) + { + GtkTreeModel * tree = GTK_TREE_MODEL (vn_model_new (model)); + gtk_entry_completion_set_model (obj->completion, tree); + g_signal_emit_by_name (obj->entry, "changed"); + g_object_unref (tree); + icon_name = NULL; + } + else + { + switch (status) + { + case DB_MODEL_STATUS_LOADING: + icon_name = "edit-find"; + break; + case DB_MODEL_STATUS_ERROR: + icon_name = "edit-delete"; + break; + default: + icon_name = NULL; + } + + gtk_entry_completion_set_model (obj->completion, NULL); + } + + set_icon (obj, icon_name); +} + +static void vn_completion_cb_changed (GtkEditable * entry, VnCompletion * obj) +{ + const gchar * text = gtk_entry_get_text (GTK_ENTRY (entry)); + + if (obj->invalid) + { + obj->invalid = FALSE; + set_icon (obj, NULL); + } + + if (obj->sql && strlen (text) == 1 + && (!obj->last_match || g_strcmp0 (text, obj->last_match))) + { + gchar * pattern; + GValue value = {0}; + + set_icon (obj, "edit-find"); + + g_free (obj->last_match); + obj->last_match = g_strdup (text); + pattern = g_strconcat (text, "%", NULL); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, pattern); + sql_value_set_value (SQL_VALUE (obj->value), &value); + g_value_unset (&value); + + g_free (pattern); + } +} + +static void vn_completion_iter_changed (VnCompletion * obj, DbIter * iter) +{ + const GValue * value = db_model_get_value (obj->model, iter, 0, NULL); + + if (value) + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), value); + + set_icon (obj, "gtk-apply"); +} + +static void vn_completion_cb_activate (GtkEntry * entry, VnCompletion * obj) +{ + gboolean ok = FALSE; + const gchar * text = gtk_entry_get_text (entry); + + if (text && g_strcmp0 (text, "")) + { + DbModel * model = obj->model; + + if (db_model_get_column_type (model, obj->column) == G_TYPE_STRING) + { + DbIter iter; + const GValue * v; + + if (db_model_get_iter_first (model, &iter)) + { + do { + v = db_model_get_value (model, &iter, obj->column, NULL); + + if (!gvn_value_is_null (v) + && !g_ascii_strcasecmp (g_value_get_string (v), text)) + { + vn_completion_iter_changed (obj, &iter); + ok = TRUE; + } + } + while (!ok && db_model_iter_next (model, &iter)); + } + } + } + + if (!ok) + { + GValue value = {0}; + + g_value_init (&value, GVN_TYPE_NULL); + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); + + if (g_strcmp0 (text, "")) + { + obj->invalid = TRUE; + set_icon (obj, "edit-delete"); + } + else + set_icon (obj, NULL); + } +} + +static gboolean vn_completion_match_selected (GtkEntryCompletion * completion, + GtkTreeModel * model, GtkTreeIter * tree_iter, VnCompletion * obj) +{ + DbIter iter; + + vn_gtk_tree_iter_to_db_iter (tree_iter, &iter); + vn_completion_iter_changed (obj, &iter); + + return FALSE; +} + +static void vn_completion_create_model (VnCompletion * obj) +{ + SqlExpr * field; + SqlString * stmt; + SqlOperation * op; + + if (!(obj->sql && obj->field)) + return; + + op = sql_operation_new (SQL_OPERATION_TYPE_LIKE); + + field = sql_field_new (obj->field, NULL, NULL); + sql_operation_add_expr (op, field); + + obj->value = sql_value_new (); + sql_operation_add_expr (op, obj->value); + + stmt = sql_string_new (obj->sql); + sql_string_add_expr (stmt, field); + sql_string_add_expr (stmt, SQL_EXPR (op)); + db_model_set_stmt (obj->model, SQL_STMT (stmt)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_CONN = 1 + ,PROP_SQL + ,PROP_FIELD +}; + +static void vn_completion_set_property (VnCompletion * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + db_model_set_conn (obj->model, g_value_get_object (value)); + break; + case PROP_SQL: + obj->sql = g_value_dup_string (value); + vn_completion_create_model (obj); + break; + case PROP_FIELD: + obj->field = g_value_dup_string (value); + vn_completion_create_model (obj); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void vn_completion_get_property (VnCompletion * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_CONN: + g_value_set_object (value, db_model_get_conn (obj->model)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_completion_init (VnCompletion * obj) +{ + GtkEntryCompletion * completion; + + obj->sql = NULL; + obj->field = NULL; + obj->last_match = NULL; + obj->invalid = FALSE; + obj->column = 1; + + obj->entry = GTK_ENTRY (gtk_entry_new ()); + gtk_entry_set_icon_sensitive (obj->entry, GTK_ENTRY_ICON_SECONDARY, FALSE); + g_object_connect (obj->entry, + "signal::changed", vn_completion_cb_changed, obj + ,"signal::activate", vn_completion_cb_activate, obj + ,NULL + ); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->entry)); + VN_FIELD (obj)->field = GTK_WIDGET (obj->entry); + + completion = gtk_entry_completion_new (); + gtk_entry_completion_set_minimum_key_length (completion, 1); + gtk_entry_completion_set_text_column (completion, obj->column); + gtk_entry_completion_set_inline_selection (completion, FALSE); +// gtk_entry_completion_set_inline_completion (completion, TRUE); + g_signal_connect (completion, "match-selected", + G_CALLBACK (vn_completion_match_selected), obj); + gtk_entry_set_completion (obj->entry, completion); + obj->completion = completion; + + obj->model = db_model_new (NULL, NULL); + g_signal_connect (obj->model, "status-changed", + G_CALLBACK (vn_completion_cb_status_changed), obj); +} + +static void vn_completion_finalize (VnCompletion * obj) +{ + g_free (obj->sql); + g_free (obj->field); + g_free (obj->last_match); + g_object_unref (obj->completion); + g_object_unref (obj->model); + G_OBJECT_CLASS (vn_completion_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_completion_class_init (VnCompletionClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_completion_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_completion_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_completion_get_property; + + g_object_class_install_property (k, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the model") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SQL, + g_param_spec_string ("sql" + ,_("SQL") + ,_("The SQL query used to create the model") + ,NULL + ,G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + )); + g_object_class_install_property (k, PROP_FIELD, + g_param_spec_string ("field" + ,_("Field") + ,_("The name of the field used for the search") + ,NULL + ,G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/vn/field/vn-completion.h b/vn/field/vn-completion.h new file mode 100644 index 0000000..7bbac31 --- /dev/null +++ b/vn/field/vn-completion.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_COMPLETION_H +#define VN_COMPLETION_H + +#include + +#define VN_TYPE_COMPLETION (vn_completion_get_type ()) +#define VN_COMPLETION(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COMPLETION, VnCompletion)) +#define VN_IS_COMPLETION(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COMPLETION)) +#define VN_COMPLETION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COMPLETION, VnCompletionClass)) +#define VN_IS_COMPLETION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COMPLETION)) +#define VN_COMPLETION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COMPLETION, VnCompletionClass)) + +typedef struct _VnCompletion VnCompletion; +typedef struct _VnCompletionClass VnCompletionClass; + +struct _VnCompletion +{ + VnField parent; + GtkEntry * entry; + GtkEntryCompletion * completion; + gboolean invalid; + guint column; + gchar * last_match; + DbModel * model; + SqlExpr * value; + gchar * field; + gchar * sql; +}; + +struct _VnCompletionClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_completion_get_type (); +VnField * vn_completion_new (const gchar * sql, const gchar * field); + +#endif \ No newline at end of file diff --git a/vn/field/vn-date-chooser.c b/vn/field/vn-date-chooser.c new file mode 100644 index 0000000..c356f95 --- /dev/null +++ b/vn/field/vn-date-chooser.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-date-chooser.h" +#include + +/** + * SECTION:vn-date-chooser + * @Short_description: an embedded popup date selector + * @Title: VnDateChooser + * @See_also: #VnField + * @Image: date-chooser.png + * + * An embedded button and text field that pops-up a #GtkCalendar. + */ +G_DEFINE_TYPE (VnDateChooser, vn_date_chooser, VN_TYPE_FIELD); + +/** + * vn_date_chooser_new: + * + * Creates a new #VnDateChooser + * + * Return value: a #VnDateChooser + **/ +VnField * vn_date_chooser_new () +{ + return VN_FIELD (g_object_new (VN_TYPE_DATE_CHOOSER, NULL)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_date_chooser_hide_popup (VnDateChooser * obj) +{ + if (obj->device) + { + gdk_device_ungrab (obj->device, GDK_CURRENT_TIME); + gtk_device_grab_remove (obj->popup, obj->device); + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (obj->button), FALSE); + gtk_widget_hide (obj->popup); +} + +static void vn_date_chooser_changed (VnDateChooser * obj) +{ + GDate * date = &obj->date; + + if (g_date_valid (date)) + { + gchar * str; + GDateWeekday wday; + GDateMonth month; + + month = g_date_get_month (date); + wday = g_date_get_weekday (date); + + str = g_strdup_printf (_("%s, %u %s %u") + ,GVN_ABR_WDAY[wday] + ,g_date_get_day (date) + ,GVN_ABR_MONTH[month] + ,g_date_get_year (date) + ); + gtk_label_set_text (obj->label, str); + g_free (str); + } + else + gtk_label_set_text (obj->label, ""); +} + +static void vn_date_chooser_set_value (VnDateChooser * obj, const GValue * value) +{ + GDate * date = NULL; + + if (G_VALUE_TYPE (value) != G_TYPE_DATE) + { + GValue new_value = {0}; + g_value_init (&new_value, G_TYPE_DATE); + g_value_transform (value, &new_value); + date = g_value_get_boxed (&new_value); + } + else if (!gvn_value_is_null (value)) + date = g_value_get_boxed (value); + + if (date) + obj->date = *date; + else + g_date_clear (&obj->date, 1); + + vn_date_chooser_changed (obj); +} + +static void vn_date_chooser_on_day_selected (GtkCalendar * calendar, VnDateChooser * obj) +{ + guint year; + guint month; + guint day; + GDate new_date; + GValue value = {0}; + + gtk_calendar_get_date (calendar, &year, &month, &day); + month++; + + g_date_clear (&new_date, 1); + g_date_set_dmy (&new_date, day, month, year); + + if (!g_date_valid (&obj->date) || g_date_compare (&new_date, &obj->date)) + { + g_date_set_julian (&obj->date, g_date_get_julian (&new_date)); + g_value_init (&value, G_TYPE_DATE); + g_value_set_boxed (&value, &obj->date); + } + else + { + g_value_init (&value, GVN_TYPE_NULL); + g_date_clear (&obj->date, 1); + } + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + g_value_unset (&value); + + vn_date_chooser_hide_popup (obj); + vn_date_chooser_changed (obj); +} + +static gboolean vn_date_chooser_on_buton_press (GtkWidget * widget, + GdkEventButton * event, VnDateChooser * obj) +{ + gint x, y; + GtkAllocation allocation; + + gdk_window_get_origin ( + gtk_widget_get_window (obj->popup), &x, &y); + gtk_widget_get_allocation (obj->popup, &allocation); + + if (!( event->x_root >= x && event->x_root <= x + allocation.width + && event->y_root >= y && event->y_root <= y + allocation.height)) + { + vn_date_chooser_hide_popup (obj); + return TRUE; + } + + return FALSE; +} + +static gboolean vn_date_chooser_on_key_press (GtkWidget * widget, + GdkEventKey * event, VnDateChooser * obj) +{ + if (event->keyval == GDK_KEY_Escape) + { + vn_date_chooser_hide_popup (obj); + return TRUE; + } + + return FALSE; +} + +static void vn_date_chooser_on_toggled (GtkToggleButton * button, VnDateChooser * obj) +{ + if (gtk_toggle_button_get_active (button)) + { + gint x, y; + GdkWindow * window; + GdkScreen * screen; + GtkRequisition req; + GdkRectangle monitor; + GtkAllocation allocation; + GtkWidget * widget = GTK_WIDGET (button); + + // Set the date on the calendar + + if (g_date_valid (&obj->date)) + { + gtk_calendar_select_month (obj->calendar + ,g_date_get_month (&obj->date) - 1 + ,g_date_get_year (&obj->date) + ); + gtk_calendar_select_day (obj->calendar, + g_date_get_day (&obj->date)); + } + else + gtk_calendar_select_day (obj->calendar, 0); + + // Setting the position of the popup + + window = gtk_widget_get_window (widget); + + gtk_widget_get_allocation (widget, &allocation); + + gdk_window_get_origin (window, &x, &y); + x += allocation.x; + y += allocation.y; + + screen = gtk_widget_get_screen (widget); + gdk_screen_get_monitor_geometry (screen, + gdk_screen_get_monitor_at_point (screen, x, y), &monitor); + + gtk_widget_show (GTK_WIDGET (obj->calendar)); + gtk_widget_get_preferred_size (obj->popup, &req, NULL); + + if (y - monitor.y > monitor.height) + y = monitor.y + monitor.height - req.height; + else if ((y - monitor.y) + req.height + allocation.height > monitor.height) + y -= req.height; + else + y += allocation.height; + + if ((x + allocation.width) - monitor.x > monitor.width) + x = monitor.x + monitor.width - req.width; + else if ((x - monitor.x) + req.width > monitor.width) + x -= req.width - allocation.width; + + gtk_window_set_screen (GTK_WINDOW (obj->popup), screen); + gtk_window_move (GTK_WINDOW (obj->popup), x, y); + gtk_widget_show_all (obj->popup); + + // Graving the focus on the popup window + + obj->device = gtk_get_current_event_device (); + + if (obj->device && gdk_device_get_source (obj->device) == GDK_SOURCE_KEYBOARD) + obj->device = gdk_device_get_associated_device (obj->device); + + if (!obj->device) + { + GList * devices; + GdkDisplay * display; + GdkDeviceManager * device_manager; + + display = gtk_widget_get_display (GTK_WIDGET (obj->popup)); + device_manager = gdk_display_get_device_manager (display); + + devices = gdk_device_manager_list_devices (device_manager, + GDK_DEVICE_TYPE_MASTER); + obj->device = devices->data; + g_list_free (devices); + } + +// gtk_widget_grab_focus (obj->popup); + gtk_device_grab_add (obj->popup, obj->device, TRUE); + gdk_device_grab (obj->device + ,gtk_widget_get_window (obj->popup) + ,GDK_OWNERSHIP_WINDOW, TRUE + ,GDK_BUTTON_PRESS_MASK, NULL + ,GDK_CURRENT_TIME + ); + } + else + vn_date_chooser_hide_popup (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_date_chooser_init (VnDateChooser * obj) +{ + GtkWidget * image; + + obj->popup = NULL; + obj->device = NULL; + g_date_clear (&obj->date, 1); + + obj->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->box)); + VN_FIELD (obj)->field = obj->box; + + obj->button = gtk_toggle_button_new (); + gtk_widget_set_tooltip_text (GTK_WIDGET (obj->button), _("Change date")); + g_signal_connect (obj->button, "toggled", + G_CALLBACK (vn_date_chooser_on_toggled), obj); + gtk_box_pack_start (GTK_BOX (obj->box), + GTK_WIDGET (obj->button), FALSE, FALSE, 0); + + image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (obj->button), image); + + obj->label = GTK_LABEL (gtk_label_new (NULL)); + gtk_misc_set_alignment (GTK_MISC (obj->label), 0, 0.5); + gtk_box_pack_end (GTK_BOX (obj->box), + GTK_WIDGET (obj->label), TRUE, TRUE, 0); + + obj->popup = gtk_window_new (GTK_WINDOW_POPUP); + g_object_connect (obj->popup + ,"signal::button-press-event", vn_date_chooser_on_buton_press, obj + ,"signal::key-press-event", vn_date_chooser_on_key_press, obj + ,NULL + ); + + obj->calendar = GTK_CALENDAR (gtk_calendar_new ()); + g_signal_connect (obj->calendar, "day-selected-double-click", + G_CALLBACK (vn_date_chooser_on_day_selected), obj); + gtk_container_add (GTK_CONTAINER (obj->popup), GTK_WIDGET (obj->calendar)); +} + +static void vn_date_chooser_finalize (VnDateChooser * obj) +{ + gtk_widget_destroy (obj->popup); + G_OBJECT_CLASS (vn_date_chooser_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_date_chooser_class_init (VnDateChooserClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_date_chooser_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_date_chooser_set_value; +} diff --git a/vn/field/vn-date-chooser.h b/vn/field/vn-date-chooser.h new file mode 100644 index 0000000..2d7135f --- /dev/null +++ b/vn/field/vn-date-chooser.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_DATE_CHOOSER_H +#define VN_DATE_CHOOSER_H + +#include + +#define VN_TYPE_DATE_CHOOSER (vn_date_chooser_get_type ()) +#define VN_DATE_CHOOSER(object) (G_TYPE_CHECK_INSTANCE_CAST (object, VN_TYPE_DATE_CHOOSER , VnDateChooser)) +#define VN_IS_DATE_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_DATE_CHOOSER)) +#define VN_DATE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_DATE_CHOOSER, VnDateChooserClass)) +#define VN_IS_DATE_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_DATE_CHOOSER)) +#define VN_DATE_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_DATE_CHOOSER, VnDateChooserClass)) + +typedef struct _VnDateChooser VnDateChooser; +typedef struct _VnDateChooserClass VnDateChooserClass; + +struct _VnDateChooser +{ + VnField parent; + GtkWidget * box; + GtkLabel * label; + GtkWidget * popup; + GtkWidget * button; + GdkDevice * device; + GtkCalendar * calendar; + GDate date; +}; + +struct _VnDateChooserClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_date_chooser_get_type (); +VnField * vn_date_chooser_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-entry.c b/vn/field/vn-entry.c new file mode 100644 index 0000000..18d7ab7 --- /dev/null +++ b/vn/field/vn-entry.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-entry.h" +#include + +/** + * SECTION:vn-entry + * @Short_description: a text box widget + * @Title: VnEntry + * @See_also: #VnField + * @Image: entry.png + * + * A text box representing a generic value field. + */ +G_DEFINE_TYPE (VnEntry, vn_entry, VN_TYPE_FIELD); + +/** + * vn_entry_new: + * + * Creates a new #VnEntry + * + * Return value: a #VnEntry + **/ +VnField * vn_entry_new (VnEntry * obj) +{ + return g_object_new (VN_TYPE_ENTRY, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_entry_cb_editing_done (GtkEntry * entry, VnField * obj) +{ + GValue value = {0}; + const gchar * text = gtk_entry_get_text (entry); + + if (g_strcmp0 (text, "") || !vn_field_get_null (obj)) + { + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, text); + } + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_FIELD_GET_CLASS (obj)->value_changed (obj, &value); + g_value_unset (&value); +} + +static gboolean vn_entry_cb_focus_out (GtkEntry * entry, GdkEvent * event, VnField * obj) +{ + vn_entry_cb_editing_done (entry, obj); + return FALSE; +} + +static void vn_entry_set_value (VnEntry * obj, const GValue * value) +{ + GValue new_value = {0}; + gvn_value_to_format_string (value, obj->digits, &new_value); + + g_signal_handlers_block_by_func (obj->entry, + vn_entry_cb_editing_done, obj); + g_signal_handlers_block_by_func (obj->entry, + vn_entry_cb_focus_out, obj); + + gtk_entry_set_text (obj->entry, + g_value_get_string (&new_value)); + + g_signal_handlers_unblock_by_func (obj->entry, + vn_entry_cb_editing_done, obj); + g_signal_handlers_unblock_by_func (obj->entry, + vn_entry_cb_focus_out, obj); + + g_value_unset (&new_value); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DIGITS = 1 +}; + +static void vn_entry_set_property (VnEntry * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + obj->digits = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_entry_get_property (VnEntry * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + g_value_set_uint (value, obj->digits); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_entry_init (VnEntry * obj) +{ + obj->entry = GTK_ENTRY (gtk_entry_new ()); + g_object_connect (obj->entry + ,"signal::activate", vn_entry_cb_editing_done, obj + ,"signal::focus-out-event", vn_entry_cb_focus_out, obj + ,NULL + ); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->entry)); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->entry); +} + +static void vn_entry_finalize (VnEntry * obj) +{ + G_OBJECT_CLASS (vn_entry_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_entry_class_init (VnEntryClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->set_property = (GObjectSetPropertyFunc) vn_entry_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_entry_get_property; + k->finalize = (GObjectFinalizeFunc) vn_entry_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_entry_set_value; + + g_object_class_install_property (k, PROP_DIGITS, + g_param_spec_uint ("digits" + ,_("Digits") + ,_("The number of decimal places to display.") + ,0 ,20 ,0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/field/vn-entry.h b/vn/field/vn-entry.h new file mode 100644 index 0000000..24cbe96 --- /dev/null +++ b/vn/field/vn-entry.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_ENTRY_H +#define VN_ENTRY_H + +#include + +#define VN_TYPE_ENTRY (vn_entry_get_type ()) +#define VN_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_ENTRY, VnEntry)) +#define VN_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_ENTRY)) +#define VN_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_ENTRY, VnEntryClass)) +#define VN_IS_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_ENTRY)) +#define VN_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_ENTRY, VnEntryClass)) + +typedef struct _VnEntry VnEntry; +typedef struct _VnEntryClass VnEntryClass; + +struct _VnEntry +{ + VnField parent; + GtkEntry * entry; + guint digits; + +}; + +struct _VnEntryClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_entry_get_type (); +VnField * vn_entry_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-http-image.c b/vn/field/vn-http-image.c new file mode 100644 index 0000000..ec5c6c0 --- /dev/null +++ b/vn/field/vn-http-image.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-http-image.h" +#include + +/* + * img: an icon name + * msg: text message or NULL + */ +#define set_icon(img, msg) \ + g_object_set \ + (obj->priv->image, "icon-name", img, \ + "icon-size", GTK_ICON_SIZE_MENU, NULL); \ + gtk_widget_set_tooltip_text (GTK_WIDGET (obj->priv->image), msg) + +/** + * SECTION: vn-http-image + * @Short_description: an image from an http server + * @Title: VnHttpImage + * + * The image is loaded using a, previously created, #DbFileLoader. + */ + +struct _VnHttpImagePrivate +{ + DbFileLoader * loader; + gchar * path; + GtkImage * image; + GBytes * bytes; +}; + +G_DEFINE_TYPE (VnHttpImage, vn_http_image, VN_TYPE_FIELD); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +/** + * vn_http_image_new: + * @loader: a #DbFileLoader + * @name: (allow-none): the name of the file, with or without type extension + * + * Create an image named after @name located in an HTTP server. + * + * Return value: a #VnHttpImage + **/ +VnField * vn_http_image_new (DbFileLoader * loader, const gchar * name) +{ + g_return_val_if_fail (DB_IS_FILE_LOADER (loader), NULL); + + return g_object_new (VN_TYPE_HTTP_IMAGE, "name", name, "loader", loader, NULL); +} + +static void vn_http_image_cb_error (VnHttpImage * obj, const GError * error) +{ + gchar * e = error ? error->message : _("Undefined error"); + const gchar * name = g_value_get_string (vn_field_get_value (VN_FIELD (obj))); + g_warning ("VnHttpImage (%s%s): %s", obj->priv->path, name, e); + set_icon ("image-missing", e); +} + +static void vn_http_image_cb_download + (DbFileLoader * fl, GBytes * bytes, const GError * error, VnHttpImage * obj) +{ + gtk_widget_set_tooltip_text (GTK_WIDGET (obj->priv->image), NULL); + + if (error) + { + vn_http_image_cb_error (obj, error); + return; + } + + if (bytes) + { + gsize len; + const gchar * data = g_bytes_get_data (bytes, &len); + GError * err = NULL; + GdkPixbufLoader * loader = gdk_pixbuf_loader_new (); + + if (gdk_pixbuf_loader_write (loader, (guchar *) data, len, &err) + && gdk_pixbuf_loader_close (loader, &err)) + { + GdkPixbuf * pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + gtk_image_set_from_pixbuf (obj->priv->image, pixbuf); + obj->priv->bytes = g_bytes_ref (bytes); + } + else + { + vn_http_image_cb_error (obj, err); + g_error_free (err); + } + + g_object_unref (loader); + } +} +/* +static void vn_http_image_cb_upload + (DbFileLoader * loader, GBytes * data, GError * error, VnHttpImage * obj) +{} +*/ +static void vn_http_image_set_value (VnHttpImage * obj, const GValue * value) +{ + gchar * full_path = g_strconcat (obj->priv->path, "/", + g_value_get_string (value), NULL); + set_icon ("view-refresh", _("Loading")); + + db_file_loader_download (obj->priv->loader, full_path, + (DbFileLoaderCallbackFunc) vn_http_image_cb_download, obj); + g_free (full_path); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_PATH = 1 + ,PROP_LOADER + ,PROP_BYTES +}; + +static void vn_http_image_set_property (VnHttpImage * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PATH: + g_free (obj->priv->path); + obj->priv->path = g_value_dup_string (value); + break; + case PROP_LOADER: + if (obj->priv->loader) + g_warning (_("File loader already set")); + else + obj->priv->loader = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_http_image_get_property (VnHttpImage * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_PATH: + g_value_set_string (value, obj->priv->path); + break; + case PROP_LOADER: + g_value_set_object (value, obj->priv->loader); + break; + case PROP_BYTES: + g_value_set_boxed (value, obj->priv->bytes); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_http_image_init (VnHttpImage * obj) +{ + obj->priv = G_TYPE_INSTANCE_GET_PRIVATE + (obj, VN_TYPE_HTTP_IMAGE, VnHttpImagePrivate); + + obj->priv->path = g_strdup (""); + obj->priv->loader = NULL; + obj->priv->image = GTK_IMAGE (gtk_image_new ()); +//Se usará para subir imagenes y cambiar nombres, o abrir un menu contextual +// g_signal_connect (obj, "event", G_CALLBACK (vn_http_image_cb_event), obj); + set_icon ("image-missing", _("No image set")); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->priv->image)); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->priv->image); +} + +static void vn_http_image_finalize (VnHttpImage * obj) +{ + g_clear_object (&obj->priv->loader); + g_free (obj->priv->path); + g_bytes_unref (obj->priv->bytes); + G_OBJECT_CLASS (vn_http_image_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_http_image_class_init (VnHttpImageClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->set_property = (GObjectSetPropertyFunc) vn_http_image_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_http_image_get_property; + k->finalize = (GObjectFinalizeFunc) vn_http_image_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_http_image_set_value; + + g_type_class_add_private (klass, sizeof (VnHttpImagePrivate)); + + g_object_class_install_property (k, PROP_LOADER, + g_param_spec_object ("loader" + ,_("File loader") + ,_("A DbFileLoader, used to download the files") + ,DB_TYPE_FILE_LOADER + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_PATH, + g_param_spec_string ("path" + ,_("File path") + ,_("The relative path to the image from file loader path") + ,NULL + ,G_PARAM_READWRITE + )); + + g_object_class_install_property (k, PROP_BYTES, + g_param_spec_boxed ("bytes" + ,_("Image bytes") + ,_("A GBytes structure with the image data") + ,G_TYPE_BYTES + ,G_PARAM_READABLE + )); +} diff --git a/vn/field/vn-http-image.h b/vn/field/vn-http-image.h new file mode 100644 index 0000000..50e85db --- /dev/null +++ b/vn/field/vn-http-image.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_HTTP_IMAGE_H +#define VN_HTTP_IMAGE_H + +#include + +#define VN_TYPE_HTTP_IMAGE (vn_http_image_get_type ()) +#define VN_HTTP_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_HTTP_IMAGE, VnHttpImage)) +#define VN_IS_HTTP_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_HTTP_IMAGE)) +#define VN_HTTP_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_HTTP_IMAGE, VnHttpImageClass)) +#define VN_IS_HTTP_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_HTTP_IMAGE)) +#define VN_HTTP_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_HTTP_IMAGE, VnHttpImageClass)) + +typedef struct _VnHttpImage VnHttpImage; +typedef struct _VnHttpImageClass VnHttpImageClass; +typedef struct _VnHttpImagePrivate VnHttpImagePrivate; + +struct _VnHttpImage +{ + VnField parent; + VnHttpImagePrivate * priv; +}; + +struct _VnHttpImageClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_http_image_get_type (); +VnField * vn_http_image_new (DbFileLoader * loader + ,const gchar * name); + +#endif diff --git a/vn/field/vn-image.c b/vn/field/vn-image.c new file mode 100644 index 0000000..075232d --- /dev/null +++ b/vn/field/vn-image.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-image.h" +#include + +#define set_default_image(obj) g_object_set (obj->image, \ + "icon-name", "dialog-question", "icon-size", GTK_ICON_SIZE_DIALOG, NULL) + +/** + * SECTION:vn-image + * @Short_description: an image widget + * @Title: VnImage + * @See_also: #VnField + * @Image: image.png + * + * An image widget that allows to set a new image with a double click. + */ +G_DEFINE_TYPE (VnImage, vn_image, VN_TYPE_FIELD); + +/** + * vn_image_new: + * + * Creates a new #VnImage + * + * Return value: a #VnImage + **/ +VnField * vn_image_new () +{ + return g_object_new (VN_TYPE_IMAGE, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_image_set_value (VnImage * obj, const GValue * value) +{ + GBytes * bytes = NULL; + + if (!gvn_value_is_null (value) + && G_VALUE_TYPE (value) == G_TYPE_BYTES) + bytes = g_value_get_boxed (value); + + if (bytes) + { + gsize size; + const guchar * data; + GError * err = NULL; + GdkPixbufLoader * loader; + + loader = gdk_pixbuf_loader_new (); + data = g_bytes_get_data (bytes, &size); + + if (gdk_pixbuf_loader_write (loader, data, size, &err) + && gdk_pixbuf_loader_close (loader, &err)) + { + GdkPixbuf * pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + gtk_image_set_from_pixbuf (obj->image, pixbuf); + } + else + { + g_warning ("%s",err->message); + g_error_free (err); + set_default_image (obj); + } + + g_object_unref (loader); + } + else + set_default_image (obj); +} + +static void vn_image_cb_update_preview (GtkFileChooser * chooser, GtkImage * image) +{ + char *filename; + GdkPixbuf *pixbuf; + + filename = gtk_file_chooser_get_preview_filename (chooser); + + if (filename) + { + pixbuf = gdk_pixbuf_new_from_file_at_size (filename, 128, 128, NULL); + g_free (filename); + + if (pixbuf) + { + gtk_image_set_from_pixbuf (image, pixbuf); + g_object_unref (pixbuf); + } + } + else + pixbuf = NULL; + + gtk_file_chooser_set_preview_widget_active (chooser, pixbuf != NULL); +} + +static gboolean vn_image_cb_event (VnField * field, GdkEvent * event, VnImage * obj) +{ + if (event->type != GDK_2BUTTON_PRESS) + return FALSE; + + GtkWidget * image; + GtkWidget * toplevel; + GtkFileChooser * chooser; + GtkFileFilter * filter; + + filter = gtk_file_filter_new (); + gtk_file_filter_add_mime_type (filter, "image/png"); + gtk_file_filter_add_mime_type (filter, "image/jpeg"); + gtk_file_filter_add_mime_type (filter, "image/gif"); + gtk_file_filter_add_mime_type (filter, "image/bmp"); + gtk_file_filter_add_mime_type (filter, "image/svg+xml"); + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (obj)); + + if (!gtk_widget_is_toplevel (toplevel)) + toplevel = NULL; + + chooser = GTK_FILE_CHOOSER (gtk_file_chooser_dialog_new ( + _("Select the image") + ,GTK_WINDOW (toplevel) + ,GTK_FILE_CHOOSER_ACTION_OPEN + ,"Cancel", GTK_RESPONSE_CANCEL + ,"Open", GTK_RESPONSE_ACCEPT + ,NULL + )); + gtk_file_chooser_set_filter (chooser, filter); + + image = gtk_image_new (); + gtk_file_chooser_set_preview_widget (chooser, image); + g_signal_connect (chooser, "update-preview", + G_CALLBACK (vn_image_cb_update_preview), image); + + if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT) + { + gsize len; + gchar * data; + gchar * filename; + GError * err = NULL; + + filename = gtk_file_chooser_get_filename (chooser); + + if (g_file_get_contents (filename, &data, &len, &err)) + { + GValue value = {0}; + g_value_init (&value, G_TYPE_BYTES); + g_value_take_boxed (&value, g_bytes_new_take (data, len)); + + VN_FIELD_GET_CLASS (obj)->value_changed (VN_FIELD (obj), &value); + vn_image_set_value (obj, &value); + g_value_unset (&value); + } + else + { + g_warning ("%s", err->message); + g_error_free (err); + } + + g_free (filename); + } + + gtk_widget_destroy (GTK_WIDGET (chooser)); + return FALSE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_image_init (VnImage * obj) +{ + obj->image = GTK_IMAGE (gtk_image_new ()); + g_signal_connect (obj, "event", + G_CALLBACK (vn_image_cb_event), obj); + set_default_image (obj); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->image)); + VN_FIELD (obj)->field = GTK_WIDGET (obj->image); +} + +static void vn_image_finalize (VnImage * obj) +{ + G_OBJECT_CLASS (vn_image_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_image_class_init (VnImageClass * klass) +{ + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_image_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_image_set_value; +} diff --git a/vn/field/vn-image.h b/vn/field/vn-image.h new file mode 100644 index 0000000..ba15377 --- /dev/null +++ b/vn/field/vn-image.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_IMAGE_H +#define VN_IMAGE_H + +#include + +#define VN_TYPE_IMAGE (vn_image_get_type ()) +#define VN_IMAGE(object) (G_TYPE_CHECK_INSTANCE_CAST (object, VN_TYPE_IMAGE , VnImage)) +#define VN_IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_IMAGE)) +#define VN_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_IMAGE, VnImageClass)) +#define VN_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_IMAGE)) +#define VN_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_IMAGE, VnImageClass)) + +typedef struct _VnImage VnImage; +typedef struct _VnImageClass VnImageClass; + +struct _VnImage +{ + VnField parent; + GtkImage * image; +}; + +struct _VnImageClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_image_get_type (); +VnField * vn_image_new (); + +#endif \ No newline at end of file diff --git a/vn/field/vn-spin.c b/vn/field/vn-spin.c new file mode 100644 index 0000000..d0c5a67 --- /dev/null +++ b/vn/field/vn-spin.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-spin.h" +#include + +/** + * SECTION:vn-spin + * @Short_description: an incrementable numeric field + * @Title: VnSpin + * @See_also: #VnField + * @Image: spin.png + * + * An incrementable numeric field + */ +G_DEFINE_TYPE (VnSpin, vn_spin, VN_TYPE_FIELD); + +/** + * vn_spin_new: + * + * Creates a new #VnSpin + * + * Return value: a #VnSpin + **/ +VnField * vn_spin_new () +{ + return g_object_new (VN_TYPE_SPIN, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_spin_on_value_changed (GtkSpinButton * spin, VnField * obj) +{ + GValue value = {0}; + + if (g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (spin)), "")) + { + g_value_init (&value, G_TYPE_DOUBLE); + g_value_set_double (&value, gtk_spin_button_get_value (spin)); + } + else + g_value_init (&value, GVN_TYPE_NULL); + + VN_FIELD_GET_CLASS (obj)->value_changed (obj, &value); + g_value_unset (&value); +} + +static gboolean vn_spin_cb_focus_out (GtkSpinButton * spin, GdkEvent * event, VnField * obj) +{ + vn_spin_on_value_changed (spin, obj); + return FALSE; +} + +static gboolean vn_spin_on_output (GtkSpinButton * spin, VnField * obj) +{ + const gchar * text = gtk_entry_get_text (GTK_ENTRY (spin)); + + return vn_field_get_null (obj) + && (!text || !g_strcmp0 (text, "")) + && gtk_spin_button_get_value (spin) == 0.0; +} + +static void vn_spin_set_value (VnSpin * obj, const GValue * value) +{ + g_signal_handlers_block_by_func (obj->spin, + vn_spin_on_value_changed, obj); + + if (!gvn_value_is_null (value)) + { + GValue new_value = {0}; + g_value_init (&new_value, G_TYPE_DOUBLE); + g_value_transform (value, &new_value); + gtk_spin_button_set_value (obj->spin, + g_value_get_double (&new_value)); + g_value_unset (&new_value); + } + else + gtk_entry_set_text (GTK_ENTRY (obj->spin), ""); + + g_signal_handlers_unblock_by_func (obj->spin, + vn_spin_on_value_changed, obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_DIGITS = 1 +}; + +static void vn_spin_set_property (VnSpin * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + gtk_spin_button_set_digits (obj->spin, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_spin_get_property (VnSpin * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_DIGITS: + g_value_set_uint (value, gtk_spin_button_get_digits (obj->spin)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_spin_init (VnSpin * obj) +{ + obj->spin = GTK_SPIN_BUTTON (gtk_spin_button_new (NULL, 1, 0)); + gtk_spin_button_set_range (obj->spin, G_MININT, G_MAXINT); + gtk_spin_button_set_update_policy (obj->spin, GTK_UPDATE_ALWAYS); + gtk_spin_button_set_numeric (obj->spin, TRUE); + gtk_spin_button_set_increments (obj->spin, 1, 1); + gtk_container_add (GTK_CONTAINER (obj), GTK_WIDGET (obj->spin)); + + g_object_connect (obj->spin +// ,"signal::activate", vn_spin_on_value_changed, obj + ,"signal::value-changed", vn_spin_on_value_changed, obj + ,"signal::focus-out-event", vn_spin_cb_focus_out, obj + ,"signal::output", vn_spin_on_output, obj + ,NULL + ); + g_signal_connect (obj->spin, "value-changed", + G_CALLBACK (vn_spin_on_value_changed), obj); + g_signal_connect (obj->spin, "output", + G_CALLBACK (vn_spin_on_output), obj); + + VN_FIELD (obj)->field = GTK_WIDGET (obj->spin); +} + +static void vn_spin_finalize (VnSpin * obj) +{ + G_OBJECT_CLASS (vn_spin_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_spin_class_init (VnSpinClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->set_property = (GObjectSetPropertyFunc) vn_spin_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_spin_get_property; + G_OBJECT_CLASS (klass)->finalize = (GObjectFinalizeFunc) vn_spin_finalize; + VN_FIELD_CLASS (klass)->set_value = (VnFieldSetValueFunc) vn_spin_set_value; + + g_object_class_install_property (k, PROP_DIGITS, + g_param_spec_uint ("digits" + ,_("Digits") + ,_("The number of decimal places to display.") + ,0 ,20 ,0 + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/field/vn-spin.h b/vn/field/vn-spin.h new file mode 100644 index 0000000..5f26a22 --- /dev/null +++ b/vn/field/vn-spin.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_SPIN_H +#define VN_SPIN_H + +#include + +#define VN_TYPE_SPIN (vn_spin_get_type ()) +#define VN_SPIN(object) (G_TYPE_CHECK_INSTANCE_CAST (object, VN_TYPE_SPIN , VnSpin)) +#define VN_IS_SPIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_SPIN)) +#define VN_SPIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_SPIN, VnSpinClass)) +#define VN_IS_SPIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_SPIN)) +#define VN_SPIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_SPIN, VnSpinClass)) + +typedef struct _VnSpin VnSpin; +typedef struct _VnSpinClass VnSpinClass; + +struct _VnSpin +{ + VnField parent; + GtkSpinButton * spin; +}; + +struct _VnSpinClass +{ + /* */ + VnFieldClass parent; +}; + +GType vn_spin_get_type (); +VnField * vn_spin_new (); + +#endif \ No newline at end of file diff --git a/vn/gui/actions.glade b/vn/gui/actions.glade new file mode 100644 index 0000000..8178cc0 --- /dev/null +++ b/vn/gui/actions.glade @@ -0,0 +1,82 @@ + + + + + + + _Logout + Logout + gtk-quit + contact-new + + + + + + + _Quit + application-exit + + + + + + + _About + help-about + + + + + + _File + system-file-manager + + + + + _Help + help-contents + + + + + _Reconnect + Reconnect + gtk-connect + + + + + + + _Close + window-close + + + + + + + _View + + + + + Dynamic _Tabs + Don't show tabs if there is only one tab open + True + + + + + + + Tool_bar + True + + + + + + diff --git a/vn/gui/child-window.glade b/vn/gui/child-window.glade new file mode 100644 index 0000000..067fc10 --- /dev/null +++ b/vn/gui/child-window.glade @@ -0,0 +1,47 @@ + + + + + + 400 + 300 + False + Hedera + True + ../image/icon.svg + + + + True + False + vertical + + + True + False + 4 + + + True + True + False + False + True + + + + + + + + + True + True + end + 1 + + + + + + diff --git a/vn/gui/login.glade b/vn/gui/login.glade new file mode 100644 index 0000000..017ff1d --- /dev/null +++ b/vn/gui/login.glade @@ -0,0 +1,403 @@ + + + + + + False + 5 + Configuration + False + True + ../image/icon.svg + dialog + window + + + + False + vertical + 2 + + + False + spread + + + gtk-cancel + False + True + True + True + False + True + + + + False + True + 0 + + + + + gtk-apply + False + True + True + True + False + True + + + + + + False + True + 1 + + + + + False + True + end + 0 + + + + + True + False + 6 + 6 + + + True + True + + True + + + 1 + 0 + 1 + 1 + + + + + True + True + + True + + + 1 + 1 + 1 + 1 + + + + + True + False + 1 + Plugin: + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Host: + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + Schema: + + + 0 + 2 + 1 + 1 + + + + + True + True + + True + + + 1 + 2 + 1 + 1 + + + + + True + False + SSL CA: + + + 0 + 3 + 1 + 1 + + + + + True + True + + gtk-info + False + False + Path to the file containing the CA certificate, if this is empty SSL won't be used + + + 1 + 3 + 1 + 1 + + + + + False + True + 1 + + + + + + cancel + apply + + + + False + 15 + Access + False + center + ../image/icon.svg + + + + True + False + vertical + 10 + + + 40 + True + False + 15 + + + True + False + ../image/logo.svg + + + False + True + 0 + + + + + 25 + False + True + + + False + True + 1 + + + + + True + True + 0 + + + + + True + False + 6 + 6 + True + + + True + True + + True + + + 1 + 0 + 1 + 1 + + + + + True + False + 1 + User: + + + + + + 0 + 0 + 1 + 1 + + + + + True + False + 1 + Password: + + + + + + 0 + 1 + 1 + 1 + + + + + True + True + False + + True + + + 1 + 1 + 1 + 1 + + + + + Remember + False + True + True + False + False + 0 + True + + + 1 + 2 + 1 + 1 + + + + + + + + False + False + 1 + + + + + True + False + 8 + spread + + + gtk-edit + False + True + False + True + False + True + + + + False + True + 0 + + + + + gtk-connect + False + True + True + True + False + True + + + + + + False + True + 1 + + + + + False + True + 3 + + + + + + diff --git a/vn/gui/main.glade b/vn/gui/main.glade new file mode 100644 index 0000000..ffa3370 --- /dev/null +++ b/vn/gui/main.glade @@ -0,0 +1,181 @@ + + + + + + False + 5 + True + ../image/icon.svg + dialog + window + Hedera + Versión 1.0 + Copyright - Verdnatura Levante S. L. + Management and administration of companies + http://www.verdnatura.es + www.verdnatura.es + This program is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this program; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + Juan Ferrer Toribio +Alejandro Colombini Gómez +Javier Gallego Ferris + Juan Ferrer Toribio +Alejandro Colombini Gómez +Javier Gallego Ferris + Jordi Cuquerella +Juan Ferrer Toribio + ../image/icon.svg + gpl-3-0 + + + + False + vertical + 2 + + + False + end + + + False + True + end + 0 + + + + + + + + + + False + Hedera + center + ../image/icon.svg + + + + True + False + vertical + + + True + False + 4 + vertical + 4 + + + True + True + False + False + True + vn-notebook + + + + + + + True + True + 0 + + + + + True + False + 6 + 6 + 6 + + + 18 + True + False + + + False + True + 0 + + + + + True + False + 0 + Ready + + + True + True + 1 + + + + + True + False + 1 + User: + + + + + + False + True + 3 + + + + + True + False + [user-name] + + + False + True + 4 + + + + + False + True + 1 + + + + + True + True + end + 1 + + + + + + diff --git a/vn/gui/menubar.ui b/vn/gui/menubar.ui new file mode 100644 index 0000000..755cc18 --- /dev/null +++ b/vn/gui/menubar.ui @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vn/image/hedera16x16.xpm b/vn/image/hedera16x16.xpm new file mode 100644 index 0000000..000647d --- /dev/null +++ b/vn/image/hedera16x16.xpm @@ -0,0 +1,48 @@ +/* XPM */ +static char * icon16x16_xpm[] = { +"16 16 29 1", +" c None", +". c #536E00", +"+ c #638300", +"@ c #678700", +"# c #587400", +"$ c #AABA09", +"% c #C9DD0A", +"& c #A6B608", +"* c #5D7A00", +"= c #719500", +"- c #607F00", +"; c #ADBF09", +"> c #C8DC0A", +", c #709300", +"' c #5B7800", +") c #AABB08", +"! c #597500", +"~ c #A3B507", +"{ c #618000", +"] c #C4D70A", +"^ c #648300", +"/ c #516B00", +"( c #A7B808", +"_ c #899706", +": c #5E7C00", +"< c #9AA907", +"[ c #567200", +"} c #668700", +"| c #648400", +" ", +" ", +" .+@@@# ", +"$%%%%& *=====- ", +";%%%%> ,=====' ", +")%%%%% !====== ", +"~%%%%% -====={ ", +" ]%%%% ^====+ ", +" ", +" !**/ ((_ ", +" +==={ %%> ", +" ====: ]%% ", +"*==== << ", +"[}}| ", +" ", +" "}; diff --git a/vn/image/hedera32x32.xpm b/vn/image/hedera32x32.xpm new file mode 100644 index 0000000..58d2401 --- /dev/null +++ b/vn/image/hedera32x32.xpm @@ -0,0 +1,107 @@ +/* XPM */ +static char * icon32x32_xpm[] = { +"32 32 72 1", +" c None", +". c #617E00", +"+ c #628100", +"@ c #546E00", +"# c #A3B507", +"$ c #9DAD07", +"% c #638100", +"& c #719500", +"* c #607F00", +"= c #96A608", +"- c #C9DD0A", +"; c #A3B409", +"> c #668600", +", c #5C7A00", +"' c #9FB108", +") c #9BAB08", +"! c #5E7B00", +"~ c #587400", +"{ c #A6B708", +"] c #C3D60A", +"^ c #719400", +"/ c #ABBB08", +"( c #A1B008", +"_ c #5F7C00", +": c #ACBD09", +"< c #A3B307", +"[ c #6D8F00", +"} c #688900", +"| c #AABC08", +"1 c #A4B509", +"2 c #5D7C00", +"3 c #A9BA09", +"4 c #587500", +"5 c #97A807", +"6 c #A2B208", +"7 c #5E7A00", +"8 c #C4D70A", +"9 c #9BAB07", +"0 c #5F7F00", +"a c #6B8D00", +"b c #95A407", +"c c #628000", +"d c #6A8A00", +"e c #94A507", +"f c #B6C809", +"g c #6A8C00", +"h c #5B7800", +"i c #5C7900", +"j c #516A00", +"k c #AABA09", +"l c #A6B808", +"m c #5D7A00", +"n c #9CAC07", +"o c #C7DA0A", +"p c #577400", +"q c #A4B408", +"r c #A7B909", +"s c #ADBE09", +"t c #5D7B00", +"u c #99AB07", +"v c #6C8E00", +"w c #648400", +"x c #C1D30A", +"y c #A9B909", +"z c #A6B608", +"A c #ACBD08", +"B c #8F9C06", +"C c #678800", +"D c #5D7900", +"E c #536E00", +"F c #608000", +"G c #587300", +" ", +" ", +" ", +" ", +" .+++++++++@", +" ########$ %&&&&&&&&&&&*", +"=----------; >&&&&&&&&&&&&,", +"'-----------) !&&&&&&&&&&&&&~", +"{-----------] ^&&&&&&&&&&&&& ", +"/------------( _&&&&&&&&&&&&&& ", +":------------< [&&&&&&&&&&&&&} ", +"|------------1 &&&&&&&&&&&&&&2 ", +"3------------{ 4&&&&&&&&&&&&&& ", +"5------------6 7&&&&&&&&&&&&&7 ", +" 8-----------9 0&&&&&&&&&&&&a ", +" b----------- c&&&&&&&&&&&d ", +" ef--------- a&&&&&&&&&gh ", +" ", +" ", +" i______j kkkkl ", +" _&&&&&&&&m n-----o ", +" m&&&&&&&&&p q------r ", +" &&&&&&&&&& #------s ", +" t&&&&&&&&&& u------s ", +" v&&&&&&&&&w x-----y ", +" &&&&&&&&&&h zAAAAB ", +" &&&&&&&&&C ", +"D&&&&&&&&} ", +"E++++++FG ", +" ", +" ", +" "}; diff --git a/vn/image/icon.svg b/vn/image/icon.svg new file mode 100644 index 0000000..010caec --- /dev/null +++ b/vn/image/icon.svg @@ -0,0 +1,558 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vn/image/load.gif b/vn/image/load.gif new file mode 100644 index 0000000000000000000000000000000000000000..7947e82e47543f72a6e0f5f2c4d62d25cdb6da12 GIT binary patch literal 9427 zcmb`Ndsq`^-uEX%266xrk_q9E;3Py0r-YLb5Hy4UQ4kYAML^CdSVXFz*xEKsNXUVZ zgoARHlZt0h>wzi)qEd?o+}5Q^TSU91wcYBryX!u?PoHN9wtf4)*Lyv7uV?+tz#}4dNYilcZDV0jq)YMd7UcPPHHtbK%o;|y7-@dZ4vhCZq zZ{EBaJ9y#3g}r;PMkP)>{x$)|H+dlu^sGSNlD4^*|Vp&xA)MYLrSHxxw*NoudlniyR);ir>6%yc;(8K z;o;${SFc{WbP0Pd>>}9nR#sMabaY@xuzMaG8|&)o!mhdBx?#_P-L1)F8W~?Y$N)8i)bs){n)!QWq2#s9H2PFI{fr;+!<`d8h;8`er ziVvGvo^IS=jYP;7)$@DXtb6A5NR#{V#Yw)Qk$BlSQ(V8-DIT(`2WXo%vK#^>Tr zWpF`)OOf=R#OYj-vlBx$J2F;7M_|TCG@>1olHk6{kMG9XN#U#rpAp=3L$O+R!iM&B z#yKJ?o*c&|eZ>W*vT|qn0!u{zA z1nn~M;?^X}&yZ3#q;KxRa1GT$@d;}ci7CMPjgeUxu15kB*(*WrMnIDf0rW?FxIyfZ zu!d>H)h!7O`ly;6(tt#q*fuUykh!tu6E0dSt?8vbvq-nYvSipIzZvbM%~19S{qZQ3>2PElTu++t9pHE4dvHpyXm$l2*YcBQ3JfzRAE&h}^OUX;Vpp_^Ob#ky{f<6q`S+oQQ&GY9AOg#I%OeYxtayaPv6h6PvL?_%2)QB;4 z^$M1y+_A6d8YGSj;&4ohc`)yKqpF4kxQfMDT;~|UiRRI0Ct@W(!so4>Jx_`6>Z+A+ zi*^ivg{|H`N|l&u2+oe$HT8@NwckZY>^EwwS0ZiB%64R;3t=Hu0vf5IYYKNIbVRFJqU8%xM`9IrE!&~o&aU!Ix zjBhfK^3zH;W1OI{Xcb(LRA9_sKWS=CbIO)Wt#Xj>D@M%Qw-S?0q*H! z9Ig%w0$-rlVVFQ?06|J%X{_jhT7FjSM1+xWzgB?OM~;=O(Wml&n?`rK^Md~-Vp~qr zhUP1@V{5ac<(Ea9wBux09QqF4^=z7ywe-Cpu6JtZl>3$B))Ug)5rBpgl!>IAs-s#H zzUxZh{?sH%Dph?sDm4c;wP?GIJ6_9n8i{iZ+hmj}xUm?dHx#aKB5l>P*yLUnWT1V}XRH7wJ*P~wIx{k8wcX#mcUJXqNp5;s zT6yTyTx_f)G<3YXY)l45^FsZK?m>rQ;MA2q)bS;!CA_RI;LPg$*oJF(!dR}ttCz^g z&Y9e+N}kY%vAvN1WilCo{JCHJ zS_%^QmXJ)wrB12AO$NlvX{{&Nrep6qn1`T5e!a-)?a)v@%E=?`;qEGUy7bYEWmbds@TyWBX|uW-VJ64PdK&duI1*62yI2q3K* zZPSu2V&M%&u2~3g#Q>F9D-OAGi$EU_X1OZQ4i zoG_H$43)cj2V@_wLs)ZUjnsQ3Rnl_uw1!>bo4IcrJ`Y_PCm~Rq2Z|&vVQvL}AkG2O zzpMSr{B*OlimP`P`b6CCEud2k&NCNBRGiXa4HHm^-|3pR=G@F^&74x&A*Wb|Wv?g}(n ziIpDD&5&S9!_KG(>8KSa&R>@i7MvDUq^cse#e%lVe;%07uzWW(D&_E-JzV!ql5Lp< z&*LPqlyPm^U#PR)Alw)}cHs^cdgt%xKl%xMMe^TTRO0fF_M>I5?#j_kwQ?%>^I_MBoi_&e5B5Be>3ze z6C{|>Ab!hUV35dS@!zx(JigU?E}&)&%_~pNqXmqle-IO%+&3a|8uOOoSw2Hg4(G=c z^qJI)oB$_B!kR{TOU!I%H^|P*D){l#H4ZgKGhLmEXakuVR?$?D?Yt-L0X~V=Xsh>N z2O?4YEslULJyh*7(py8|Q9YOACbTH5go1n;%WIA`Z^{zDc5C^&eM_o^r!I5T*9GXt*dLBUzSS;z(v7FM>~fwvb7OV8ZJ~5>~#E#A2{PC+71hEh?g4r% znIsI*7iuuf=F5XRNJ61p)x!&b=zEuM&vV$Yv|cUQ*t4Wys9ZAaI_Z3yhXZsnqu06S>(HdoAqYGvDC!r+# z?*Jphz=-}2Fe)Ak{*{Z^&z57Rzru2i#|f$^X|AKZqQbf!e9;7AP>!iZ6-$Wsu=IJP z5k)^fsD(m!ya}C^30Z3A;UyF62l*lJJJSP)jnTZH_CtwJb0(Vu9Jll9_w;shY4OAR zTJmjbh9p=On;&xO0Ee_g(7Z1dk%clfbr(m2zWT(I_K_b-;48_Lkd^cN%09mgF-srR+d!6+KvCsLPr=Bt%}iLUoGbfl3XVpO zdw0fKvYB7kWSD|LIvKKpBDbiT7X+k&1r|0QlTR-uA39cR{elU;-0m+lliNEm<(%RB zZxfp#jM0c%;-A9r#=_@;Msnbw;Rt(1YXv0njZF$&a#DAiAEFPmb6_w$GT_fXIxs(W zKxLQDPu|np!R1JKGnbwKjBp7Ow0qa!M`i<~Bw{yNa+}L>cMS;%_*6R{ShH@8_beI< zb=q)Uqb8atT2ct^_{$L#XF3p%Y_L#&lw7Y1gv6CD`rK+|kt+S=bhK>)m9R4TUoU^0 z)7NniPgnm-`dHS~&Rm>i3;JGbl5OpdKs;W99upC{S&MIDsn~1_iG`P-T~;!}113tF z@Q~G3lD3m(^3@IiL{48lW?H~hW*q0?4j^SzcKp7&5q51fFR3T+gXb>Y}*2No;Nbd5yNaA{u z5?|Yw+c)$a;p(Av+v|UFk&Xg!>*#g=GJiU-Xs@{6VJ|Bf`Y&J*Q^Za20xdrg-qA9Q2613kIb^g9w6;z=6xlwZj}3uF!e3zDG-!`S9lzVPe< z7(mj-r~FKYe)U)##5#XjZS9;{CxMt~phH$L21smUBc9M>WOS5u{>8-){(SX?Hah61 zpi?kL=2|YvnZ2FKbsW#)m~4{yw^zNU?q%ZR!2AfB?sD`My1*AuLKj@)I%y~DO0 z6sCLeTdM11VA;y&E`*V~qM+ws1|WpOvH>9uv<(cX7d3Y>d$%qt#ZB_=8U{Q_bkEUH$an)$g?5 zx4o!=Sz%B0P_)pt?^)dQbSh3G?z&h-O+L#_d)K9YbmWFJWUfX6&f1`ZA;Q}5qh((a z+BC*3XtWJ8~><@JdBOweE@y^Jo1GjT??*aK_rse>41@Rx%YogIRY=&*OR^| z-P;p=;~5)O|GDy{YCly+sUV#H--AOAi~cv^bi9INb4LHnO2_bu4xz07k0;$7?Up@d zaNqR4k6*f}GA*Dy#@2v*;l{4=Jdy|jP}VuHEbsb$Z%_7KbN-FnR3Pi($CaR1uV$2d ztgQSIPwM1?@QJVU`tTFpSsU7?{~;p;0)nw}-LC3JC6jn8cc;^lgyu-s52mP3K8YKZ z#9ww3xqpY^4wu+sJs~pkn&l}rl0*Mr-C)h=KGQFr!gOVjnO|gc6eIIU#~Tj&Tx+w97}%kcV43WnyVmOhZJ|3z_#l>D{q2)Df}MqfI9|F4 zd&`)?r7V{o#xo~fK;fyEW}pkeZ3E=hW^``U(~k35e;RI9;y|D3>2@3s8k0RfJZCqLfD1Z;Ii=so26D={-91dyMgMEWJ`^`4*Z*zF) z*n_Cl0M=9dlf&yz3jY@gT?(UPxjb}0FUeW6oEE0%4dV?w@2IF1OXgqHG84U*Pm>l1 z$wp$>nUznr6rpMtjF8BVIN`Fy=PwMjhWo&ivwdG5)nW&P=K)Ilx1Zu~$oLgNiA8>h z5fAscRn69OV_-W-98fg-w7QFU-y<^59Ia~5!n}|PdI}ZN8~GEW?vZagYzdkG9-d2? z_RlA}ie`CR(;HZJ{Z~2?4wU|mNdAp_IssDjtBDQ|FNS^%oeORJ4@~4MSm+D_^ea~} z^_=^SdMuKDGFTAYRAXImi$j$VV8XX48F$>Rn$Z8d%$ zKMx0`OGFL=UfSO`+`BeZ`NM;MTRs0iA;AKn7_N)w0wEjxv_?|7p{$IFG3wDv6kE0w z?s)mP+Pw1Bb5-o67$KLCKBX;2$T?Gn%wmM}UXzM3wGktv6z^12ky=Dq_$fI;kO_Af zS$-V)mTEU3Ij`^2rG0F3iJ$))Pmur5L%KD7!l~yLC4>c# zSf0IZaxsBKj@N70Ay_HFlYqtvJulnMmFwPb#dJZJwR?ADWTE3%!#%p-83H^XYoTVJ zpjo>9fWK95d*JiOm4|W^OKDE=9PX$WK0F5Q3r{aYl8ktbEY$|o*`gx%`H!9YfNBI8 zo>~4@e!(>saUFo)8#Mc1bnPP-qIuwPf|?GH2D+}(Mr`&n*tg4WY1)D1=YGP8Y*$w` zVl{qW_pw2$d702bdj634{gVsTy3g;KH2-2T6&tJ7+gP1I@!D03g%;+rZqu$PZ`;Lr z&j)S%t0vlts5lA=o}-Ums}rqWQMhjL#j0pk$vJFzH$fr$LE{%XKlinXtFsriG^9o1 z(*79`Ak*{ED`OWps%VuXOeES$6$XcbiP~H$MS)|T5&MB0saBMt%j8womr~VRO)^o- zt|Mv=ZQ5;Rk6eAHDw2q=x<>%w2TaE4W|4CCrUMXQH=lm7c+`_?=YqUR>}r~X{#qHD z+;BwODu+r-+z4Ib)p7KC-NQZy9w!U$0AKVk*lq1+c>H^WV96pO|Fzz+)>71(LmrzV zHrQGV3o9Y}vh)=+j1XW&lv=iR@nvc~eQCzzA|gr1tp00l@veddCy#Eckk~q8xkBOQ zR;lH4oxWvoapkT2R8fq@oFj%;2TmQnO>qB#${690$GKENrZ~7l8`tXo46ktcdP1#^ zTvduS4E^UksXOXY|Dlvn2_~Qn@BS4*-Qbgz7YT@WiHB?2A{Zz09*+SV+DZf$?aoj{xN? z0l0ZKp10xS@D~M{lQ<5$bm-{>c}HksE9q^z!7io!l?l3Cr1(v7830Cp|FYOnemFR7Qarw;3|@y^fFUAyQcBL_-a6OeyZG6GH=)uX;&^kzjkXcXd}N2KuU33 zG`V!P|E6pdJRa8km?qs#qi!Kn(%onQk6T~8!tj3mIU+eWnACtun4IeJ6%p|*pq+KK zN9Bqrua=NZS8tz%#U3Z5is#i-(ipeY=d+_yFqSujQ^2aKScBh>t5ct%MjN{&N#*iXAYMqV zh56!^Vhh0Zq$mh4`vE)a%fhNsF0b^C7Xcil#fP=l5=3uvOkXK2w>zUjv;iNwbtQ8~ rZg*5^PywYPau7c|p_hN+df!rkb_=gmH)1oO6$?vXtlNJB!uj`? literal 0 HcmV?d00001 diff --git a/vn/image/logo.svg b/vn/image/logo.svg new file mode 100644 index 0000000..aeaea8e --- /dev/null +++ b/vn/image/logo.svg @@ -0,0 +1,172 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vn/schema/hedera.gschema.xml b/vn/schema/hedera.gschema.xml new file mode 100644 index 0000000..2f71881 --- /dev/null +++ b/vn/schema/hedera.gschema.xml @@ -0,0 +1,23 @@ + + + + + "" + + + "localhost" + + + "" + + + "" + + + "" + + + "" + + + diff --git a/vn/schema/module.dtd b/vn/schema/module.dtd new file mode 100644 index 0000000..bdef5bf --- /dev/null +++ b/vn/schema/module.dtd @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/vn/vn-batch.c b/vn/vn-batch.c new file mode 100644 index 0000000..1938ee6 --- /dev/null +++ b/vn/vn-batch.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-batch.h" + +/** + * SECTION: vn-batch + * @Short_description: a group of objects + * @Title: VnBatch + * + * A group of GObjects. + */ + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods + +/** + * vn_batch_add: + * @obj: a #VnBatch + * @child: a #GObject + * + * Adds a child identified whith @id to the group. + **/ +void vn_batch_add (VnBatch * obj, GObject * child) +{ + g_return_if_fail (VN_IS_BATCH (obj)); + g_return_if_fail (G_IS_OBJECT (child)); + + obj->objects = g_list_prepend (obj->objects, child); +} + +/** + * vn_batch_remove: + * @obj: a #VnBatch + * @child: a #GObject contained by @obj + * + * Removes a child from the group. + **/ +void vn_batch_remove (VnBatch * obj, GObject * child) +{ + g_return_if_fail (VN_IS_BATCH (obj)); + + obj->objects = g_list_remove (obj->objects, child); +} + +/** + * vn_batch_get_objects: + * @obj: a #VnBatch + * + * Returns all the #GObjects in @obj. + * + * Return value: (transfer container) (element-type GObject): a #GList with all + * the objects, that must be freed with #g_list_free + **/ +GList * vn_batch_get_objects (VnBatch * obj) +{ + g_return_val_if_fail (VN_IS_BATCH (obj), NULL); + + return g_list_copy (obj->objects); +} + +static void vn_batch_buildable_add_child (GtkBuildable * obj, + GtkBuilder * builder, GObject * child, const gchar * type) +{ + vn_batch_add (VN_BATCH (obj), child); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_batch_init (VnBatch * obj) +{ + obj->objects = NULL; +} + +static void vn_batch_finalize (VnBatch * obj) +{ + GObjectClass * parent; + parent = g_type_class_peek_parent (VN_BATCH_GET_CLASS (obj)); + + g_list_free (obj->objects); + + parent->finalize (G_OBJECT (obj)); +} + +static void vn_batch_class_init (VnBatchClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_batch_finalize; +} + +static void vn_batch_buildable_interface_init (GtkBuildableIface * iface) +{ + iface->add_child = vn_batch_buildable_add_child; +} + +G_DEFINE_TYPE_WITH_CODE (VnBatch, vn_batch, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + vn_batch_buildable_interface_init) +); diff --git a/vn/vn-batch.h b/vn/vn-batch.h new file mode 100644 index 0000000..dd38fb6 --- /dev/null +++ b/vn/vn-batch.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_BATCH_H +#define VN_BATCH_H + +#include +#include + +#define VN_TYPE_BATCH (vn_batch_get_type ()) +#define VN_BATCH(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_BATCH, VnBatch)) +#define VN_IS_BATCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_BATCH)) +#define VN_BATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_BATCH, VnBatchClass)) +#define VN_IS_BATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_BATCH)) +#define VN_BATCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_BATCH, VnBatchClass)) + +typedef struct _VnBatch VnBatch; +typedef struct _VnBatchClass VnBatchClass; + +struct _VnBatch +{ + GObject parent; + GList * objects; +}; + +struct _VnBatchClass +{ + GObjectClass parent; +}; + +GType vn_batch_get_type (); + +void vn_batch_add (VnBatch * obj + ,GObject * child); +void vn_batch_remove (VnBatch * obj + ,GObject * child); +GList * vn_batch_get_objects (VnBatch * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-builder.c b/vn/vn-builder.c new file mode 100644 index 0000000..931cb63 --- /dev/null +++ b/vn/vn-builder.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-builder.h" +#include "vn-field.h" +#include +#include + +/** + * SECTION: vn-builder + * @Short_description: functions to use with the GtkBuilder + * @Title: VnBuilder + * + * These are a set of functions that make it easy to create and bind interfaces + * to data using the #GtkBuilder and the #DbLib, part of the Hedera library. + **/ +G_DEFINE_TYPE (VnBuilder, vn_builder, GTK_TYPE_BUILDER); + +VnBuilder * vn_builder_new () +{ + return g_object_new (VN_TYPE_BUILDER, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_builder_load_file: + * @obj: a #VnBuilder + * @conn: a #DbConn + * @filename: the name of the file to parse + * @err: (out) (allow-none): return location for an error, or %NULL + * + * Parses a file containing a GtkBuilder UI definition and merges it with the + * current contents of builder. Also sets de conn property of objects that use + * the #DbConn + * + * Return value: A positive value on success, 0 if an error occurred + **/ +guint vn_builder_load_file (VnBuilder * obj, DbConn * conn, const gchar * filename, GError ** err) +{ + guint success; + GtkBuilder * builder; + + g_return_val_if_fail (VN_IS_BUILDER (obj), 0); + g_return_val_if_fail (DB_IS_CONN (conn), 0); + + builder = GTK_BUILDER (obj); + success = gtk_builder_add_from_file (builder, filename, err); + + if (success) + { + GSList * n; + GSList * objects = gtk_builder_get_objects (builder); + + for (n = objects; n; n = n->next) + if (VN_IS_COMBO (n->data) + || VN_IS_COMPLETION (n->data) + || VN_IS_COLUMN_COMBO (n->data) + || DB_IS_MODEL (n->data) + || DB_IS_ITERATOR (n->data)) + g_object_set (n->data, "conn", conn, NULL); + + g_slist_free (objects); + } + + return success; +} + +/** + * vn_builder_bind_fields: + * @obj: a #VnBuilder + * @iterator: where params are obtained. + * @...: pairs of column name and field id, terminated with -1 + * + * Binds several iterator parameters to #VnEntry entries. + * The variable list argument should contain a string with the column name and + * a string the #VnField id. + **/ +void vn_builder_bind_fields (VnBuilder * obj, DbIterator * iterator, ...) +{ + va_list vl; + gchar * column; + const gchar * id; + VnField * field; + + g_return_if_fail (VN_IS_BUILDER (obj)); + g_return_if_fail (DB_IS_ITERATOR (iterator)); + + va_start (vl, iterator); + + while ((column = va_arg (vl, gchar *)) != NULL) + { + id = va_arg (vl, gchar *); + field = vn_builder_get (obj, id); + + if (VN_IS_FIELD (field)) + vn_field_set_param (field, db_iterator_get_param (iterator, column)); + else + g_warning ("VnBuilder: Object '%s' isn't a VnField!", id); + } + + va_end (vl); +} + +/** + * vn_builder_bind_columns: + * @obj: a #VnBuilder + * @...: pairs of column name and column id, terminated with -1 + * + * Binds several model columns to #VnColumn columns. + * The variable list argument should contain a string with the column name and + * a string the #VnColumn id. + **/ +void vn_builder_bind_columns (VnBuilder * obj, ...) +{ + va_list vl; + gchar * column_name; + const gchar * id; + VnColumn * column; + + g_return_if_fail (VN_IS_BUILDER (obj)); + + va_start (vl, obj); + + while ((column_name = va_arg (vl, gchar *)) != NULL) + { + id = va_arg (vl, gchar *); + column = vn_builder_get (obj, id); + + if (VN_IS_COLUMN (column)) + vn_column_set_column_name (column, column_name); + else + g_warning ("VnBuilder: Object '%s' isn't a VnColumn!", id); + } + + va_end (vl); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_builder_init (VnBuilder * obj) {} + +static void vn_builder_class_init (VnBuilderClass * k) {} diff --git a/vn/vn-builder.h b/vn/vn-builder.h new file mode 100644 index 0000000..adf9ef9 --- /dev/null +++ b/vn/vn-builder.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_BUILDER_H +#define VN_BUILDER_H + +#include +#include +#include "vn-column.h" + +#define VN_TYPE_BUILDER (vn_builder_get_type ()) +#define VN_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_BUILDER, VnBuilder)) +#define VN_IS_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_BUILDER)) +#define VN_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_BUILDER, VnBuilderClass)) +#define VN_IS_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_BUILDER)) +#define VN_BUILDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_BUILDER, VnBuilderClass)) + +#define vn_builder_get(obj,name) ((gpointer) gtk_builder_get_object (GTK_BUILDER (obj), name)) + +typedef struct _VnBuilder VnBuilder; +typedef struct _VnBuilderClass VnBuilderClass; + +struct _VnBuilder +{ + GtkBuilder parent; +}; + +struct _VnBuilderClass +{ + /* */ + GtkBuilderClass parent; +}; + +GType vn_builder_get_type (); +VnBuilder * vn_builder_new (); +guint vn_builder_load_file (VnBuilder * obj, DbConn * conn, const gchar * filename, GError ** err); +void vn_builder_bind_fields (VnBuilder * obj, DbIterator * iterator, ...); +void vn_builder_bind_columns (VnBuilder * obj, ...); + +#endif \ No newline at end of file diff --git a/vn/vn-column.c b/vn/vn-column.c new file mode 100644 index 0000000..7a122e4 --- /dev/null +++ b/vn/vn-column.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-column.h" +#include "vn-grid.h" + +G_DEFINE_ABSTRACT_TYPE (VnColumn, vn_column, GTK_TYPE_TREE_VIEW_COLUMN); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_column_value_changed (VnColumn * obj, const gchar * path, const GValue * value) +{ + DbIter iter; + + vn_column_get_iter (obj, path, &iter); + db_model_set_value (vn_column_get_model (obj), + &iter, obj->column_index, value, NULL); +} + +static void vn_column_data_func (GtkTreeViewColumn * col, GtkCellRenderer * cell, + GtkTreeModel * model, GtkTreeIter * iter, VnColumn * obj) +{ + gchar * background; + DbIter dbiter; + DbModel * dbmodel; + + GValue value = {0}; + gtk_tree_model_get_value (model, iter, obj->column_index, &value); + obj->set_value (obj, model, iter, cell, &value); + g_value_unset (&value); + + vn_gtk_tree_iter_to_db_iter (iter, &dbiter); + g_object_get (model, "model", &dbmodel, NULL); + DbModelRowOp ops = db_model_get_row_operations (dbmodel, &dbiter); + g_object_unref (dbmodel); + + if (ops & DB_MODEL_ROW_OP_DELETE) + background = "#fcc"; + else if (ops & DB_MODEL_ROW_OP_INSERT || ops & DB_MODEL_ROW_OP_UPDATE) + background = "#cfc"; + else + background = NULL; + + g_object_set (cell, "cell-background", background, NULL); +} + +static void vn_column_set_renderer (VnColumn * obj, GtkCellRenderer * cell) +{ + GtkTreeViewColumn * column; + + obj->cell = cell; + obj->set_value = VN_COLUMN_GET_CLASS (obj)->set_value; + VN_COLUMN_GET_CLASS (obj)->set_editable (obj, obj->editable); + + column = GTK_TREE_VIEW_COLUMN (obj); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_set_cell_data_func (column, cell, + (GtkTreeCellDataFunc) vn_column_data_func, obj, NULL); +} + +static void vn_column_update_column_index (VnColumn * obj) +{ + if (obj->column_name) + { + DbModel * model = vn_column_get_model (obj); + + if (model) + { + obj->column_index = db_model_get_column_index (model, obj->column_name); + + if (obj->column_index >= 0) + gtk_tree_view_column_set_sort_column_id ( + GTK_TREE_VIEW_COLUMN (obj), obj->column_index); + } + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_column_get_column_name: + * @obj: the #VnColumn + * + * Gets the column name. + * + * Return value: the column name + **/ +const gchar * vn_column_get_column_name (VnColumn * obj) +{ + g_return_val_if_fail (VN_IS_COLUMN (obj), NULL); + + return obj->column_name; +} + +/** + * vn_column_set_column_name: + * @obj: the #VnColumn + * @name: the column name + * + * Sets the column name. + **/ +void vn_column_set_column_name (VnColumn * obj, const gchar * name) +{ + g_return_if_fail (VN_IS_COLUMN (obj)); + + g_free (obj->column_name); + obj->column_name = g_strdup (name); + vn_column_update_column_index (obj); +} + +/** + * vn_column_get_column_index: + * @obj: the #VnColumn + * + * Gets the column index. + * + * Return value: the column index + **/ +gint vn_column_get_column_index (VnColumn * obj) +{ + g_return_val_if_fail (VN_IS_COLUMN (obj), -1); + + return obj->column_index; +} + +/** + * vn_column_set_column_index: + * @obj: the #VnColumn + * @index: the column index + * + * Sets the column index. + **/ +void vn_column_set_column_index (VnColumn * obj, gint index) +{ + g_return_if_fail (VN_IS_COLUMN (obj)); + g_return_if_fail (index >= -1); + + obj->column_index = index; + vn_column_set_column_name (obj, NULL); + + if (index >= 0) + gtk_tree_view_column_set_sort_column_id ( + GTK_TREE_VIEW_COLUMN (obj), index); +} + +/** + * vn_column_get_editable: + * @obj: the #VnColumn + * + * Gets if the column can be edited by the user. + * + * Return value: %TRUE if its editable, %FALSE otherwhise + **/ +gboolean vn_column_get_editable (VnColumn * obj) +{ + g_return_val_if_fail (VN_IS_COLUMN (obj), FALSE); + + return obj->editable; +} + +/** + * vn_column_set_editable: + * @obj: the #VnColumn + * @editable: a %gboolean indicating whether the column value is editable + * + * Sets if the column could be edited by the user. + **/ +void vn_column_set_editable (VnColumn * obj, gboolean editable) +{ + g_return_if_fail (VN_IS_COLUMN (obj)); + + obj->editable = editable; + VN_COLUMN_GET_CLASS (obj)->set_editable (obj, editable); +} + +/** + * vn_column_get_model: + * @obj: the #VnColumn + * + * Gets the model used by the main #VnGrid. + * + * Return value: (transfer none): the #DbModel if has it, %NULL otherwise. + **/ +DbModel * vn_column_get_model (VnColumn * obj) +{ + g_return_val_if_fail (VN_IS_COLUMN (obj), NULL); + + GtkWidget * grid = gtk_tree_view_column_get_tree_view ( + GTK_TREE_VIEW_COLUMN (obj)); + + if (grid) + return vn_grid_get_model (VN_GRID (grid)); + + return NULL; +} + +/** + * vn_column_get_iter: + * @obj: the #VnColumn + * @path: the path string + * @iter: (out): the #DbIter to be set + * + * Gets a #DbIter from a #GtkTreePath string. + * + * Return value: %TRUE if the iter could be obtained, %FALSE otherwise. + **/ +gboolean vn_column_get_iter (VnColumn * obj, const gchar * path, DbIter * iter) +{ + DbModel * model; + GtkTreePath * tree_path; + + g_return_val_if_fail (VN_IS_COLUMN (obj), FALSE); + g_return_val_if_fail (iter, FALSE); + + tree_path = gtk_tree_path_new_from_string (path); + model = vn_column_get_model (obj); + + if (!tree_path) + g_warning ("VnColumn: Invalid path."); + else if (gtk_tree_path_get_depth (tree_path) < 1) + g_warning ("VnColumn: Path has no indices."); + else if (!model) + g_warning ("VnColumn: Tree view has no model assigned."); + else + { + db_model_get_iter (model, iter, gtk_tree_path_get_indices (tree_path)[0]); + return TRUE; + } + + gtk_tree_path_free (tree_path); + return FALSE; +} + +/** + * vn_column_model_changed: + * @obj: the #VnColumn + * + * This function is used primarily by the #VnGrid. Warns the column that the + * model has changed. + **/ +void vn_column_model_changed (VnColumn * obj) +{ + VnColumnClass * klass; + + g_return_if_fail (VN_IS_COLUMN (obj)); + + klass = VN_COLUMN_GET_CLASS (obj); + vn_column_update_column_index (obj); + + if (klass->model_changed) + klass->model_changed (obj); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_COLUMN_INDEX = 1 + ,PROP_COLUMN_NAME + ,PROP_EDITABLE +}; + +static void vn_column_set_property (VnColumn * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_COLUMN_INDEX: + vn_column_set_column_index (obj, g_value_get_int (value)); + break; + case PROP_COLUMN_NAME: + vn_column_set_column_name (obj, g_value_get_string (value)); + break; + case PROP_EDITABLE: + vn_column_set_editable (obj, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_column_get_property (VnColumn * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_COLUMN_INDEX: + g_value_set_int (value, obj->column_index); + break; + case PROP_COLUMN_NAME: + g_value_set_string (value, obj->column_name); + break; + case PROP_EDITABLE: + g_value_set_boolean (value, obj->editable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_column_init (VnColumn * obj) +{ + obj->column_index = -1; + obj->column_name = NULL; +} + +static void vn_column_finalize (VnColumn * obj) +{ + G_OBJECT_CLASS (vn_column_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_column_class_init (VnColumnClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_column_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_column_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_column_get_property; + klass->value_changed = vn_column_value_changed; + klass->set_renderer = vn_column_set_renderer; + klass->model_changed = NULL; + + g_object_class_install_property (k, PROP_COLUMN_INDEX, + g_param_spec_int ("column-index" + ,_("Column index") + ,_("The column index in the model") + ,-1, 255, -1 + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_COLUMN_NAME, + g_param_spec_string ("column-name" + ,_("Column name") + ,_("The referenced column name") + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_EDITABLE, + g_param_spec_boolean ("editable" + ,_("Editable") + ,_("Whether the column values are editable") + ,FALSE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} diff --git a/vn/vn-column.h b/vn/vn-column.h new file mode 100644 index 0000000..09a3447 --- /dev/null +++ b/vn/vn-column.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_COLUMN_H +#define VN_COLUMN_H + +#include +#include + +#define VN_TYPE_COLUMN (vn_column_get_type ()) +#define VN_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_COLUMN, VnColumn)) +#define VN_IS_COLUMN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_COLUMN)) +#define VN_COLUMN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_COLUMN, VnColumnClass)) +#define VN_IS_COLUMN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_COLUMN)) +#define VN_COLUMN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_COLUMN, VnColumnClass)) + +typedef struct _VnColumn VnColumn; +typedef struct _VnColumnClass VnColumnClass; + +typedef void (*VnColumnSetValueFunc) (VnColumn * obj, GtkTreeModel * model, GtkTreeIter * iter, + GtkCellRenderer * cell, const GValue * value); +typedef void (*VnColumnValuechangedFunc) (VnColumn * obj, const gchar * path, const GValue * value); +typedef void (*VnColumnSetRendererFunc) (VnColumn * obj, GtkCellRenderer * cell); +typedef void (*VnColumnSetEditableFunc) (VnColumn * obj, gboolean editable); +typedef void (*VnColumnModelChangedFunc) (VnColumn * obj); + +struct _VnColumn +{ + GtkTreeViewColumn parent; + GtkCellRenderer * cell; + gint column_index; + gchar * column_name; + gboolean editable; + VnColumnSetValueFunc set_value; +}; + +struct _VnColumnClass +{ + GtkTreeViewColumnClass parent; + VnColumnSetValueFunc set_value; + VnColumnValuechangedFunc value_changed; + VnColumnSetRendererFunc set_renderer; + VnColumnSetEditableFunc set_editable; + VnColumnModelChangedFunc model_changed; +}; + +GType vn_column_get_type (); +const gchar * vn_column_get_column_name (VnColumn * obj); +void vn_column_set_column_name (VnColumn * obj, const gchar * name); +gint vn_column_get_column_index (VnColumn * obj); +void vn_column_set_column_index (VnColumn * obj, gint index); +gboolean vn_column_get_editable (VnColumn * obj); +void vn_column_set_editable (VnColumn * obj, gboolean editable); +DbModel * vn_column_get_model (VnColumn * obj); +gboolean vn_column_get_iter (VnColumn * obj, const gchar * path, DbIter * iter); +void vn_column_model_changed (VnColumn * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-field.c b/vn/vn-field.c new file mode 100644 index 0000000..f71decf --- /dev/null +++ b/vn/vn-field.c @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-field.h" + +G_DEFINE_ABSTRACT_TYPE (VnField, vn_field, GTK_TYPE_EVENT_BOX); + +enum { + VALUE_CHANGED + ,LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_field_spec_changed (VnField * obj) +{ + gvn_param_spec_unset (obj->spec); + gvn_param_spec_merge (obj->spec, obj->user_spec); + + if (obj->param) + gvn_param_spec_merge (obj->spec, + gvn_param_get_spec (obj->param)); +} + +static void vn_field_on_param_spec_changed (GvnParam * param, VnField * obj) +{ + vn_field_spec_changed (obj); +} + +static void vn_field_on_param_value_changed (GvnParam * param, const GValue * value, VnField * obj) +{ + obj->update_param = FALSE; + vn_field_set_value (obj, value); + obj->update_param = TRUE; +} + +static void vn_field_set_invalid (VnField * obj, const GError * err) +{ +/* GdkRGBA color = {0.95, 0.8, 0.8, 1}; + gtk_widget_override_background_color (GTK_WIDGET (obj->field), + GTK_STATE_FLAG_NORMAL, &color); +*/} + +static void vn_field_on_param_status_changed (GvnParam * param, VnField * obj) +{ + switch (gvn_param_get_status (param)) + { + case GVN_PARAM_STATUS_OK: + gtk_widget_set_sensitive (obj->field, TRUE); + break; + case GVN_PARAM_STATUS_BUSY: + gtk_widget_set_sensitive (obj->field, FALSE); + break; + case GVN_PARAM_STATUS_ERROR: + vn_field_set_invalid (obj, gvn_param_get_error (param)); + break; + } +} + +static void vn_field_changed (VnField * obj) +{ + if (obj->param && obj->update_param) + { + GError * err = NULL; + + g_signal_handlers_block_by_func (obj->param, + vn_field_on_param_value_changed, obj); + + if (!gvn_param_set_value (obj->param, obj->value, &err)) + { + vn_field_set_invalid (obj, err); + g_error_free (err); + } + + g_signal_handlers_unblock_by_func (obj->param, + vn_field_on_param_value_changed, obj); + } + + g_signal_emit (obj, signals[VALUE_CHANGED], 0, obj->value); +} + +static void vn_field_value_changed (VnField * obj, const GValue * value) +{ + GType type; + gboolean changed; + + if (obj->lock_changed_done) + return; + + type = gvn_param_spec_get_gtype (obj->spec); + + if (type != G_TYPE_NONE && !gvn_value_is_null (value)) + { + if (G_VALUE_TYPE (obj->value) != type) + { + g_value_unset (obj->value); + g_value_init (obj->value, type); + } + + g_value_transform (value, obj->value); + changed = TRUE; + } + else + changed = gvn_value_ccopy (value, obj->value); + + if (changed) + vn_field_changed (obj); +} + +static void vn_field_make_param (VnField * obj) +{ + GvnParam * param; + + if (!obj->iterator || !obj->column_name) + return; + + param = db_iterator_get_param (obj->iterator, obj->column_name); + vn_field_set_param (obj, param); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_field_get_value: + * @obj: the #VnField + * + * Gets the current field value. + * + * Return value: (transfer none): the #GValue + **/ +const GValue * vn_field_get_value (VnField * obj) +{ + return obj->value; +} + +/** + * vn_field_set_value: + * @obj: the #VnField + * @value: a #GValue + * + * Sets the field value. + **/ +void vn_field_set_value (VnField * obj, const GValue * value) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + g_return_if_fail (G_IS_VALUE (value)); + + if (gvn_value_ccopy (value, obj->value)) + { + obj->lock_changed_done = TRUE; + VN_FIELD_GET_CLASS (obj)->set_value (obj, obj->value); + obj->lock_changed_done = FALSE; + vn_field_changed (obj); + } +} + +/** + * vn_field_get_param: + * @obj: the #VnField + * + * Return value: (transfer none): +**/ +GvnParam * vn_field_get_param (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), NULL); + + return obj->param; +} + +/** + * vn_field_set_param: + * @obj: the #VnField + * @param: a #GvnParam + * + * Binds the field with a #GvnParam. + **/ +void vn_field_set_param (VnField * obj, GvnParam * param) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + g_return_if_fail (GVN_IS_PARAM (param) || !param); + + if (obj->param) + { + g_object_disconnect (obj->param + ,"any_signal", vn_field_on_param_value_changed, obj + ,"any_signal", vn_field_on_param_spec_changed, obj + ,"any_signal", vn_field_on_param_status_changed, obj + ,NULL + ); + g_clear_object (&obj->param); + } + if (param) + { + obj->param = g_object_ref_sink (param); + g_object_connect (param + ,"signal::value-changed", vn_field_on_param_value_changed, obj + ,"signal::spec-changed", vn_field_on_param_spec_changed, obj + ,"signal::status-changed", vn_field_on_param_status_changed, obj + ,NULL + ); + vn_field_on_param_spec_changed (param, obj); + vn_field_on_param_value_changed (param, + gvn_param_get_value (param), obj); + vn_field_on_param_status_changed (param, obj); + } + +/* GFile * css = g_file_new_for_path ("vn/gui/style.css"); + + if (g_file_query_exists (css, NULL)) + { + GError * err = NULL; + GtkCssProvider * provider = gtk_css_provider_get_default (); + + if (gtk_css_provider_load_from_file (provider, css, &err)) + { + GtkStyleContext * style; + style = gtk_widget_get_style_context (GTK_WIDGET (obj->field)); + gtk_style_context_add_provider (style, GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + gtk_style_context_add_class (style, "invalid"); + } + else + { + g_warning ("Vn: %s", err->message); + g_error_free (err); + } + } + else + g_warning ("Vn: Can't load CSS styles"); + + g_object_unref (css); +*/ +} + +/** + * vn_field_get_gtype: + * @obj: the #VnField + * + * Gets the field type. + * + * Return value: the field type or %G_TYPE_NONE if it havent. + **/ +GType vn_field_get_gtype (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), G_TYPE_INVALID); + + return gvn_param_spec_get_gtype (obj->spec); +} + +/** + * vn_field_set_gtype: + * @obj: the #VnField + * @type: a valid #GType + * + * Sets the type of the field. When you set a value, it will be converted to + * this type. + **/ +void vn_field_set_gtype (VnField * obj, GType type) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + gvn_param_spec_set_gtype (obj->user_spec, type); + vn_field_spec_changed (obj); +} + +/** + * vn_field_get_null: + * @obj: the #VnField + * + * If field allows values with %GVN_TYPE_NULL type. + * + * Return value: %TRUE if it can, %FALSE otherwise. + **/ +gboolean vn_field_get_null (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), FALSE); + + return gvn_param_spec_get_null (obj->spec); +} + +/** + * vn_field_set_null: + * @obj: the #VnField + * @null: %TRUE if field should accept NULL values + * + * Sets if field allows values with %GVN_TYPE_NULL type. If param property of + * the field is set, this attribute will be merged with the same #GvnParam + * attribute, remaining the most restrictive. + **/ +void vn_field_set_null (VnField * obj, gboolean null) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + gvn_param_spec_set_null (obj->user_spec, null); + vn_field_spec_changed (obj); +} + +/** + * vn_field_get_editable: + * @obj: the #VnField + * + * Gets whether the field value can be modified. + * + * Return value: %TRUE if field value can be edited, %FALSE otherwise. + **/ +gboolean vn_field_get_editable (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), FALSE); + + return gvn_param_spec_get_editable (obj->spec); +} + +/** + * vn_field_set_editable: + * @obj: the #VnField + * @editable: %TRUE if field can be editable + * + * Sets if field value can be modified. If param property of + * the field is set, this attribute will be merged with the same #GvnParam + * attribute, remaining the most restrictive. + **/ +void vn_field_set_editable (VnField * obj, gboolean editable) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + gvn_param_spec_set_editable (obj->user_spec, editable); + vn_field_spec_changed (obj); +} + +/** + * vn_field_get_default: + * @obj: the #VnField + **/ +const GValue * vn_field_get_default (VnField * obj) +{ + g_return_val_if_fail (VN_IS_FIELD (obj), NULL); + + return gvn_param_spec_get_default (obj->spec); +} + +/** + * vn_field_set_default: + * @obj: the #VnField + * @def: the default #GValue + * + * Sets the default value of the field. + **/ +void vn_field_set_default (VnField * obj, const GValue * def) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + gvn_param_spec_set_default (obj->user_spec, def); + vn_field_spec_changed (obj); +} + +/** + * vn_field_set_to_default: + * @obj: the #VnField + **/ +void vn_field_set_to_default (VnField * obj) +{ + g_return_if_fail (VN_IS_FIELD (obj)); + + vn_field_set_value (obj, + gvn_param_spec_get_default (obj->spec)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_VALUE = 1 + ,PROP_PARAM + ,PROP_ITERATOR + ,PROP_COLUMN_NAME + ,PROP_GTYPE + ,PROP_EDITABLE + ,PROP_NULL + ,PROP_DEFAULT +}; + +static void vn_field_set_property (VnField * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_VALUE: + vn_field_set_value (obj, g_value_get_boxed (value)); + break; + case PROP_PARAM: + vn_field_set_param (obj, g_value_get_object (value)); + break; + case PROP_ITERATOR: + g_clear_object (&obj->iterator); + obj->iterator = g_value_dup_object (value); + vn_field_make_param (obj); + break; + case PROP_COLUMN_NAME: + g_free (obj->column_name); + obj->column_name = g_value_dup_string (value); + vn_field_make_param (obj); + break; + case PROP_GTYPE: + vn_field_set_gtype (obj, g_value_get_gtype (value)); + break; + case PROP_EDITABLE: + vn_field_set_editable (obj, g_value_get_boolean (value)); + break; + case PROP_NULL: + vn_field_set_null (obj, g_value_get_boolean (value)); + break; + case PROP_DEFAULT: + vn_field_set_default (obj, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_field_get_property (VnField * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_VALUE: + g_value_set_boxed (value, obj->value); + break; + case PROP_PARAM: + g_value_set_object (value, obj->param); + break; + case PROP_ITERATOR: + g_value_set_object (value, obj->iterator); + break; + case PROP_COLUMN_NAME: + g_value_set_string (value, obj->column_name); + break; + case PROP_GTYPE: + g_value_set_gtype (value, vn_field_get_gtype (obj)); + break; + case PROP_EDITABLE: + g_value_set_boolean (value, vn_field_get_editable (obj)); + break; + case PROP_NULL: + g_value_set_boolean (value, vn_field_get_null (obj)); + break; + case PROP_DEFAULT: + g_value_set_boxed (value, vn_field_get_default (obj)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_field_init (VnField * obj) +{ + GdkRGBA color = {0.0, 0.0, 0.0, 0.0}; + + obj->param = NULL; + obj->update_param = TRUE; + obj->lock_changed_done = FALSE; + obj->user_spec = gvn_param_spec_new (); + obj->spec = gvn_param_spec_new (); + obj->iterator = NULL; + obj->column_name = NULL; + + obj->value = g_new0 (GValue, 1); + g_value_init (obj->value, GVN_TYPE_NULL); + + gtk_widget_override_background_color (GTK_WIDGET (obj), + GTK_STATE_FLAG_NORMAL, &color); +} + +static void vn_field_finalize (VnField * obj) +{ + gvn_param_spec_free (obj->spec); + gvn_param_spec_free (obj->user_spec); + g_clear_object (&obj->iterator); + g_clear_object (&obj->param); + g_value_unset (obj->value); + g_free (obj->value); + g_free (obj->column_name); + G_OBJECT_CLASS (vn_field_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_field_class_init (VnFieldClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_field_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_field_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_field_get_property; + klass->value_changed = vn_field_value_changed; + + signals[VALUE_CHANGED] = g_signal_new ("value-changed", + VN_TYPE_FIELD, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, G_TYPE_VALUE + ); + + g_object_class_install_property (k, PROP_VALUE, + g_param_spec_boxed ("value" + ,_("Value") + ,_("The current value of the field") + ,G_TYPE_VALUE + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_PARAM, + g_param_spec_object ("param" + ,_("Parameter") + ,_("The param where the field can read/write its value") + ,GVN_TYPE_PARAM + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_ITERATOR, + g_param_spec_object ("iterator" + ,_("Iterator") + ,_("The iterator used to get the field param") + ,DB_TYPE_ITERATOR + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_COLUMN_NAME, + g_param_spec_string ("column-name" + ,_("Column name") + ,_("The column name on the iterator") + ,NULL + ,G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_GTYPE, + g_param_spec_gtype ("gtype" + ,_("Glib Type") + ,_("The type of the value") + ,G_TYPE_NONE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_EDITABLE, + g_param_spec_boolean ("editable" + ,_("Editable") + ,_("Whether the field value is user editable") + ,TRUE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_NULL, + g_param_spec_boolean ("null" + ,_("Null") + ,_("Whether the field value can be of type GVN_TYPE_NULL") + ,TRUE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_DEFAULT, + g_param_spec_boxed ("default" + ,_("Default Value") + ,_("The default value") + ,G_TYPE_VALUE + ,G_PARAM_READWRITE + )); +} diff --git a/vn/vn-field.h b/vn/vn-field.h new file mode 100644 index 0000000..a24672a --- /dev/null +++ b/vn/vn-field.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_FIELD_H +#define VN_FIELD_H + +#include +#include + +#define VN_TYPE_FIELD (vn_field_get_type ()) +#define VN_FIELD(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_FIELD, VnField)) +#define VN_IS_FIELD(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_FIELD)) +#define VN_FIELD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_FIELD, VnFieldClass)) +#define VN_FIELD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_FIELD, VnFieldClass)) + +typedef struct _VnField VnField; +typedef struct _VnFieldClass VnFieldClass; + +typedef void (*VnFieldSetValueFunc) (VnField * obj, const GValue * value); +typedef void (*VnFieldValueChangedFunc) (VnField * obj, const GValue * value); + +struct _VnField +{ + GtkEventBox parent; + GtkWidget * field; + GValue * value; + GvnParam * param; + GvnParamSpec * spec; + GvnParamSpec * user_spec; + gboolean lock_changed_done; + gboolean update_param; + DbIterator * iterator; + gchar * column_name; +}; + +struct _VnFieldClass +{ + GtkEventBoxClass parent; + GCallback changed; + VnFieldSetValueFunc set_value; + VnFieldValueChangedFunc value_changed; +}; + +GType vn_field_get_type (); +const GValue * vn_field_get_value (VnField * obj); +void vn_field_set_value (VnField * obj, const GValue * value); +GvnParam * vn_field_get_param (VnField * obj); +void vn_field_set_param (VnField * obj, GvnParam * param); +GType vn_field_get_gtype (VnField * obj); +void vn_field_set_gtype (VnField * obj, GType type); +gboolean vn_field_get_null (VnField * obj); +void vn_field_set_null (VnField * obj, gboolean null); +gboolean vn_field_get_editable (VnField * obj); +void vn_field_set_editable (VnField * obj, gboolean editable); +const GValue * vn_field_get_default (VnField * obj); +void vn_field_set_default (VnField * obj, const GValue * def); +void vn_field_set_to_default (VnField * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-form.c b/vn/vn-form.c new file mode 100644 index 0000000..f83435e --- /dev/null +++ b/vn/vn-form.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-form.h" + +G_DEFINE_ABSTRACT_TYPE (VnForm, vn_form, GTK_TYPE_ALIGNMENT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_form_open: + * @obj: the #VnForm + * + * Activates the form. + * Virtual: open + **/ +void vn_form_open (VnForm * obj) +{ + const gchar * dir; + gchar * file; + GError * err = NULL; + + obj->builder = GTK_BUILDER (vn_builder_new ()); + gtk_builder_set_translation_domain (obj->builder, vn_mod_get_text_domain (obj->mod)); + + dir = vn_mod_get_data_dir (obj->mod); + file = g_strdup_printf ("%s/%s.glade", dir, obj->name); + + if (vn_builder_load_file (VN_BUILDER (obj->builder), obj->conn, file, &err)) + { + gtk_builder_connect_signals (obj->builder, obj); + VN_FORM_GET_CLASS (obj)->open (obj, obj->builder, NULL); + gtk_container_add (GTK_CONTAINER (obj), vn_form_get (obj, "main")); + gtk_widget_show_all (GTK_WIDGET (obj)); + + // Loading actions from the .glade and the GtkUIManager definition + + if ((obj->actions = vn_form_get (obj, "actions"))) + { + gchar * buffer; + gchar * ui_file = g_strdup_printf ("%s/%s.ui", dir, obj->name); + GError * err = NULL; + + if (g_file_get_contents (ui_file, &buffer, NULL, &err)) + { + obj->ui = buffer; + g_object_ref (obj->actions); + } + else + { + g_warning ("VnForm: Actions were defined for %s but no UI " + "definition was found.", obj->name); + g_error_free (err); + obj->actions = NULL; + } + + g_free (ui_file); + } + } + else + { + g_warning ("VnForm: %s", err->message); + g_error_free (err); + } + + g_free (file); +} + +/** + * vn_form_get: + * @obj: the #VnForm + * @name: the object name + * + * Gets an object from the form builder using its name. + * + * Return value: (transfer none): the object, or %NULL if there is no object + * with that name + **/ +gpointer vn_form_get (VnForm * obj, const gchar * name) +{ + return (gpointer) gtk_builder_get_object (obj->builder, name); +} + +/** + * vn_form_get_name: + * @obj: the #VnForm + * + * Gets the name of the form. + **/ +const gchar * vn_form_get_name (VnForm * obj) +{ + g_return_val_if_fail (VN_IS_FORM (obj), NULL); + + return obj->name; +} + +/** + * vn_form_get_ui_manager: + * @obj: the #VnForm + * + * Returns the string containing the path of the #GtkUIManager UI definition + * file for the form @obj. + * + * Return value: a string + **/ +const gchar * vn_form_get_ui_manager (VnForm * obj) +{ + g_return_val_if_fail (VN_IS_FORM (obj), NULL); + + return obj->ui; +} + +/** + * vn_form_get_action_group: + * @obj: the #VnForm + * + * Returns the group actions implemented by @obj. + * + * Return value: (transfer none): a #GtkActionGroup + **/ +GtkActionGroup * vn_form_get_action_group (VnForm * obj) +{ + g_return_val_if_fail (VN_IS_FORM (obj), NULL); + + return obj->actions; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_GUI + ,PROP_CONN + ,PROP_MODULE +}; + +static void vn_form_set_property (VnForm * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + obj->name = g_value_dup_string (value); + break; + case PROP_GUI: + obj->gui = g_value_dup_object (value); + obj->conn = vn_gui_get_conn (obj->gui); + break; + case PROP_MODULE: + obj->mod = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_form_get_property (VnForm * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->name); + break; + case PROP_GUI: + g_value_set_object (value, obj->gui); + break; + case PROP_CONN: + g_value_set_object (value, obj->conn); + break; + case PROP_MODULE: + g_value_set_object (value, obj->mod); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_form_init (VnForm * obj) +{ + obj->name = NULL; + obj->gui = NULL; + obj->conn = NULL; + obj->builder = NULL; + obj->mod = NULL; + obj->actions = NULL; + obj->ui = NULL; +} + +static void vn_form_finalize (VnForm * obj) +{ + g_free (obj->name); + g_free (obj->ui); + g_clear_object (&obj->gui); + g_clear_object (&obj->builder); + g_clear_object (&obj->mod); + g_clear_object (&obj->actions); + G_OBJECT_CLASS (vn_form_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_form_class_init (VnFormClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) vn_form_finalize; + klass->set_property = (GObjectSetPropertyFunc) vn_form_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_form_get_property; + + g_object_class_install_property (klass, PROP_NAME, + g_param_spec_string ("name" + ,_("Name") + ,_("The form name") + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_GUI, + g_param_spec_object ("gui" + ,_("Gui") + ,_("The Gui object") + ,VN_TYPE_GUI + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by the module") + ,DB_TYPE_CONN + ,G_PARAM_READABLE + )); + g_object_class_install_property (klass, PROP_MODULE, + g_param_spec_object ("module" + ,_("Module") + ,_("The module") + ,VN_TYPE_MOD + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} diff --git a/vn/vn-form.h b/vn/vn-form.h new file mode 100644 index 0000000..4787a51 --- /dev/null +++ b/vn/vn-form.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_FORM_H +#define VN_FORM_H + +#include "vn-builder.h" + +#define VN_TYPE_FORM (vn_form_get_type ()) +#define VN_FORM(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_FORM, VnForm)) +#define VN_IS_FORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_FORM)) +#define VN_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_FORM, VnFormClass)) +#define VN_IS_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_FORM)) +#define VN_FORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_FORM, VnFormClass)) + +typedef struct _VnForm VnForm; +typedef struct _VnFormClass VnFormClass; + +#include "vn-gui.h" + +typedef GType (* VnFormGetTypeFunc) (); +typedef void (* VnFormOpenFunc) (VnForm * obj, GtkBuilder * builder, gpointer user_data); +typedef void (* VnFormActivateFunc) (VnForm * obj); +typedef void (* VnFormDeactivateFunc) (VnForm * obj); + +struct _VnForm +{ + GtkAlignment parent; + gchar * name; + VnGui * gui; + DbConn * conn; + GtkBuilder * builder; + VnMod * mod; + GtkActionGroup * actions; + gchar * ui; +}; + +struct _VnFormClass +{ + GtkAlignmentClass parent; + void (* open) (VnForm * obj, GtkBuilder * builder, gpointer user_data); + void (* activate) (VnForm * obj); + void (* deactivate) (VnForm * obj); +}; + +GType vn_form_get_type (); +void vn_form_open (VnForm * obj); +gpointer vn_form_get (VnForm * obj, const gchar * name); +const gchar * vn_form_get_name (VnForm * obj); +const gchar * vn_form_get_ui_manager (VnForm * obj); +GtkActionGroup * vn_form_get_action_group (VnForm * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-grid.c b/vn/vn-grid.c new file mode 100644 index 0000000..4e24e07 --- /dev/null +++ b/vn/vn-grid.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-grid.h" + +G_DEFINE_TYPE (VnGrid, vn_grid, GTK_TYPE_TREE_VIEW); + +/** + * vn_grid_new: + * + * Creates a new grid + * + * Return value: #VnGrid created. + **/ +VnGrid * vn_grid_new () +{ + return g_object_new (VN_TYPE_GRID, "rules-hint", TRUE, NULL); +} + +/** + * vn_grid_new_with_iterator: + * @iterator: #DbIterator where the grid will be created + * + * Creates a new grid into a iterator + * + * Return value: #VnGrid created. + **/ +VnGrid * vn_grid_new_with_iterator (DbIterator * iterator) +{ + VnGrid * obj = vn_grid_new (); + g_object_set (obj, "iterator", iterator, NULL); + return obj; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_grid_on_iter_changed (DbIterator * iterator, GtkTreeView * obj); + +static void vn_grid_on_cursor_changed (GtkTreeView * obj, DbIterator * iterator) +{ + GtkTreeIter iter; + GtkTreeSelection * selection = gtk_tree_view_get_selection (obj); + + if (selection && gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + DbIter dbiter; + vn_gtk_tree_iter_to_db_iter (&iter, &dbiter); + + g_signal_handlers_block_by_func (iterator, vn_grid_on_iter_changed, obj); + db_iterator_move_iter (iterator, &dbiter); + g_signal_handlers_unblock_by_func (iterator, vn_grid_on_iter_changed, obj); + } +} + +static void vn_grid_on_iter_changed (DbIterator * iterator, GtkTreeView * obj) +{ + DbIter dbiter; + GtkTreeSelection * selection = gtk_tree_view_get_selection (obj); + + if (db_iterator_get_iter (iterator, &dbiter) + && gtk_tree_view_get_model (obj)) + { + GtkTreeIter iter; + GtkTreePath * path; + + vn_gtk_tree_iter_from_db_iter (&iter, &dbiter); + g_signal_handlers_block_by_func (obj, vn_grid_on_cursor_changed, iterator); + gtk_tree_selection_select_iter (selection, &iter); + g_signal_handlers_unblock_by_func (obj, vn_grid_on_cursor_changed, iterator); + + path = gtk_tree_model_get_path ( + gtk_tree_view_get_model (obj), &iter); + gtk_tree_view_scroll_to_cell (obj, path, NULL, FALSE, 0, 0); + gtk_tree_path_free (path); + } + else if (selection) + gtk_tree_selection_unselect_all (selection); +} + +static void vn_grid_on_status_changed (DbIterator * iterator, gboolean ready, VnGrid * obj) +{ + GList * n; + GList * columns; + + obj->model = db_iterator_get_model (iterator); + columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (obj)); + + for (n = columns; n; n = n->next) + if (VN_IS_COLUMN (n->data)) + vn_column_model_changed (n->data); + + g_list_free (columns); + + if (ready) + { + GtkTreeModel * tree_model = GTK_TREE_MODEL (vn_model_new (obj->model)); + gtk_tree_view_set_model (GTK_TREE_VIEW (obj), tree_model); + vn_grid_on_iter_changed (obj->iterator, GTK_TREE_VIEW (obj)); + g_object_unref (tree_model); + } + else + gtk_tree_view_set_model (GTK_TREE_VIEW (obj), NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_grid_get_iterator: + * @obj: a #VnGrid + * + * Gets the iterator used by @obj. + * + * Return value: (transfer none): the #DbIterator + **/ +DbIterator * vn_grid_get_iterator (VnGrid * obj) +{ + g_return_val_if_fail (VN_IS_GRID (obj), NULL); + + return obj->iterator; +} + +/** + * vn_grid_set_iterator: + * @obj: a #VnGrid + * @iterator: the #DbIterator + * + * Sets the iterator handled by tree view. + **/ +void vn_grid_set_iterator (VnGrid * obj, DbIterator * iterator) +{ + g_return_if_fail (VN_IS_GRID (obj)); + g_return_if_fail (DB_IS_ITERATOR (iterator) || !iterator); + + if (obj->iterator) + { + g_signal_handlers_disconnect_by_func (obj, + vn_grid_on_cursor_changed, obj->iterator); + + g_object_disconnect (obj->iterator + ,"any-signal", vn_grid_on_iter_changed, obj + ,"any-signal", vn_grid_on_status_changed, obj + ,NULL + ); + g_clear_object (&obj->iterator); + obj->model = NULL; + } + if (iterator) + { + obj->iterator = g_object_ref (iterator); + g_object_connect (iterator + ,"signal::iter-changed", vn_grid_on_iter_changed, obj + ,"signal::status-changed", vn_grid_on_status_changed, obj + ,NULL + ); + vn_grid_on_status_changed (iterator, + db_iterator_is_ready (iterator), obj); + + g_signal_connect (obj, "cursor-changed", + G_CALLBACK (vn_grid_on_cursor_changed), iterator); + } +} + +/** + * vn_grid_get_model: + * @obj: a #VnGrid + * + * Gets the model used by @obj. + * + * Return value: (transfer none): the #DbModel + **/ +DbModel * vn_grid_get_model (VnGrid * obj) +{ + g_return_val_if_fail (VN_IS_GRID (obj), NULL); + + return obj->model; +} + +/** + * vn_grid_insert_column: + * @obj: #VnGrid where the new column will be appended + * @position: the position where the column will be inserted + * @column_index: the index of the source column in the model + * @title: the title + * @column_type: the #GType of the assigned #VnColumn + * @editable: a %gboolean indicating whether the column is editable + * @expand: a %gboolean indicating whether the column is expandable + * + * Creates and inserts a new column in the grid with the given column and + * position, if @position is -1 the column will be appended to the end. + * + * Return value: (transfer none): the #VnColumn assigned to the new column. + **/ +VnColumn * vn_grid_insert_column (VnGrid * obj, gint position, gint column_index, + const gchar * title, GType column_type, gboolean editable, gboolean expand) +{ + GtkTreeViewColumn * column; + + g_return_val_if_fail (VN_IS_GRID (obj), NULL); + g_return_val_if_fail (index >= 0, NULL); + g_return_val_if_fail (g_type_is_a (column_type, VN_TYPE_COLUMN), NULL); + + column = g_object_new (column_type + ,"title" ,title + ,"expand" ,expand + ,"resizable" ,TRUE + ,"sort-indicator" ,TRUE + ,"sizing" ,GTK_TREE_VIEW_COLUMN_GROW_ONLY + ,"column-index" ,column_index + ,"editable" ,editable + ,NULL + ); + gtk_tree_view_append_column (GTK_TREE_VIEW (obj), column); + return VN_COLUMN (column); +} + +/** + * vn_grid_append_column: + * @obj: #VnGrid where the new column will be appended + * @column_index: the index of the source column in the model + * @title: the title + * @column_type: the #GType of the assigned #VnCell + * @editable: a %gboolean indicating whether the column is editable + * @expand: a %gboolean indicating whether the column is expandable + * + * Creates and appends a new column at the end of the grid with the given column. + * + * Return value: (transfer none): the #VnCell assigned to the new column. + **/ +VnColumn * vn_grid_append_column (VnGrid * obj, gint column_index, const gchar * title, + GType column_type, gboolean editable, gboolean expand) +{ + g_return_val_if_fail (VN_IS_GRID (obj), NULL); + g_return_val_if_fail (index >= 0, NULL); + g_return_val_if_fail (g_type_is_a (column_type, VN_TYPE_COLUMN), NULL); + + return vn_grid_insert_column (obj, -1, column_index, title, column_type, editable, expand); +} + +/** + * vn_grid_append_columns: + * @obj: the #VnGrid + * @...: columns information, see vn_grid_append_column(), terminated with -1 + * + * Appends columns into a #VnGrid. + **/ +void vn_grid_append_columns (VnGrid * obj, ...) +{ + va_list vl; + gint index; + gchar * title; + GType column_type; + gboolean editable; + gboolean expand; + + g_return_if_fail (VN_IS_GRID (obj)); + + va_start (vl, obj); + + while ((index = va_arg (vl, gint)) != -1) + { + title = va_arg (vl, gchar *); + column_type = va_arg (vl, GType); + editable = va_arg (vl, gboolean); + expand = va_arg (vl, gboolean); + + vn_grid_append_column (obj, + index, title, column_type, editable, expand); + } + + va_end (vl); + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (obj)); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +typedef enum +{ + PROP_ITERATOR = 1 +} +VnGridProp; + +static void vn_grid_set_property (VnGrid * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ITERATOR: + vn_grid_set_iterator (obj, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_grid_get_property (VnGrid * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_ITERATOR: + g_value_set_object (value, obj->iterator); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_grid_init (VnGrid * obj) +{ + obj->iterator = NULL; + obj->model = NULL; + gtk_tree_selection_set_mode ( + gtk_tree_view_get_selection (GTK_TREE_VIEW (obj)), + GTK_SELECTION_SINGLE + ); +} + +static void vn_grid_finalize (VnGrid * obj) +{ + vn_grid_set_iterator (obj, NULL); + G_OBJECT_CLASS (vn_grid_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_grid_class_init (VnGridClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->set_property = (GObjectSetPropertyFunc) vn_grid_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_grid_get_property; + klass->finalize = (GObjectFinalizeFunc) vn_grid_finalize; + + g_object_class_install_property (klass, PROP_ITERATOR, + g_param_spec_object ("iterator" + ,_("Iterator") + ,_("The iterator used by VnGrid") + ,DB_TYPE_ITERATOR + ,G_PARAM_READWRITE + )); +} diff --git a/vn/vn-grid.h b/vn/vn-grid.h new file mode 100644 index 0000000..cf8418a --- /dev/null +++ b/vn/vn-grid.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_GRID_H +#define VN_GRID_H + +#include +#include +#include "vn-model.h" +#include "vn-column.h" + +#define VN_TYPE_GRID (vn_grid_get_type ()) +#define VN_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_GRID, VnGrid)) +#define VN_IS_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_GRID)) +#define VN_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_GRID, VnGridClass)) +#define VN_IS_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_GRID)) +#define VN_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_GRID, VnGridClass)) + +typedef struct _VnGrid VnGrid; +typedef struct _VnGridClass VnGridClass; + +struct _VnGrid +{ + GtkTreeView parent; + DbIterator * iterator; + DbModel * model; + +}; + +struct _VnGridClass +{ + /* */ + GtkTreeViewClass parent; +}; + +GType vn_grid_get_type (); +VnGrid * vn_grid_new (); +VnGrid * vn_grid_new_with_iterator (DbIterator * iterator); +DbIterator * vn_grid_get_iterator (VnGrid * obj); +void vn_grid_set_iterator (VnGrid * obj, DbIterator * iterator); +DbModel * vn_grid_get_model (VnGrid * obj); +VnColumn * vn_grid_insert_column (VnGrid * obj, gint position, gint column_index, const gchar * title, GType column_type, gboolean editable, gboolean expand); +VnColumn * vn_grid_append_column (VnGrid * obj, gint column_index, const gchar * title, GType column_type, gboolean editable, gboolean expand); +void vn_grid_append_columns (VnGrid * obj, ...); + +#endif \ No newline at end of file diff --git a/vn/vn-gui.c b/vn/vn-gui.c new file mode 100644 index 0000000..f7cd705 --- /dev/null +++ b/vn/vn-gui.c @@ -0,0 +1,1543 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-gui.h" +#include +#include +#include + +#include +#include +#include + +#define MAIN_UI _GUI_DIR"/main.glade" +#define CHILD_WINDOW_UI _GUI_DIR"/child-window.glade" +#define ACTIONS_UI _GUI_DIR"/actions.glade" +#define MENUBAR_XML _GUI_DIR"/menubar.ui" +#define MODULE_DTD _DTD_DIR"/module.dtd" + +#define S(string) (gdome_str_mkref (string)) +#define gtk_builder_get(builder, id) ((gpointer) gtk_builder_get_object (builder, id)) + +/** + * SECTION: vn-gui + * @Short_description: GUI manager + * @Title: VnGui + * + * Manages most of the GUI operations. + **/ +G_DEFINE_TYPE (VnGui, vn_gui, G_TYPE_OBJECT); + +struct _VnWindow +{ + VnGui * obj; + GtkWindow * widget; + GtkNotebook * notebook; + GtkUIManager * manager; + GtkWidget * toolbar; + VnForm * active_form; + GtkToggleAction * dynamic_tabs; + GtkToggleAction * view_toolbar; + guint merge_id; +}; + +typedef struct +{ + VnGui * gui; + gchar * name; +} +FormData; + +typedef struct +{ + GtkActionEntry * entry; + FormData * form; +} +ActionData; + +typedef struct +{ + GSList * action_data; + VnMod * mod; +} +ModData; + +enum { + COL_ICON + ,COL_NAME + ,COL_TITLE + ,COL_TYPE + ,COL_MODULE + ,COL_COUNT +}; + +enum { + LOGOUT + ,EXIT + ,LAST_SIGNAL +}; + +typedef struct +{ + VnGui * obj; + gboolean aux; + GError * error; + GThread * thread; +} +GuiData; + +static void vn_gui_reconnect (VnGui * obj); +static void vn_gui_on_conn_error (DbConn * conn, const GError * error, VnGui * obj); +static void vn_gui_on_conn_status_changed (DbConn * conn, DbConnStatus status, VnGui * obj); +void vn_gui_on_open_form_activated (GtkAction * action, FormData * form_data); +void vn_gui_on_child_destroyed (GtkWindow * widget, VnWindow * window); +void vn_gui_on_page_removed (GtkNotebook * notebook, GtkWidget * page, guint num, VnWindow * window); +void vn_gui_on_main_page_removed (GtkNotebook * notebook, GtkWidget * page, guint num, VnWindow * window); + +static guint signals[LAST_SIGNAL] = {0}; + +/** + * vn_gui_new: + * @app: a #GtkApplication + * @conn: the #VnLogin + * + * Creates a new Gui object. + * + * Return value: the created #VnGui + **/ +VnGui * vn_gui_new (GtkApplication * app, DbConn * conn) +{ + return g_object_new (VN_TYPE_GUI, "app", app, "conn", conn, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_gui_free_action_data (ActionData * data) +{ + if (data) + { + if (data->form) + g_free (data->form->name); + + g_free (data->form); + + if (data->entry) + { + g_free ((gchar *) data->entry->name); + g_free ((gchar *) data->entry->stock_id); + g_free ((gchar *) data->entry->label); + g_free ((gchar *) data->entry->accelerator); + g_free ((gchar *) data->entry->tooltip); + } + + g_free (data->entry); + } + + g_free (data); +} + +static void vn_gui_free_mod_data (ModData * data) +{ + if (data->action_data) + g_slist_free_full (data->action_data, + (GDestroyNotify) vn_gui_free_action_data); +} + +/* + * Frees the #GuiData struct. + */ +static void gui_data_free (GuiData * gui_data) +{ + if (gui_data->error) + g_error_free (gui_data->error); + + g_object_unref (gui_data->obj); + g_thread_unref (gui_data->thread); + g_free (gui_data); +} + +static void vn_gui_free_window (VnGui * obj, VnWindow * window) +{ + g_signal_handlers_disconnect_by_func (window->widget, + vn_gui_on_child_destroyed, window); + g_signal_handlers_disconnect_by_func (window->notebook, + vn_gui_on_page_removed, window); + g_signal_handlers_disconnect_by_func (window->notebook, + vn_gui_on_main_page_removed, window); + + gtk_widget_destroy (GTK_WIDGET (window->widget)); + g_object_unref (window->manager); + g_free (window); +} + +static VnWindow * vn_gui_get_active_window (VnGui * obj) +{ + GSList * n; + + for (n = obj->windows; n; n = n->next) + if (gtk_window_is_active (((VnWindow *) n->data)->widget)) + return n->data; + + return obj->main_window; +} + +/* + * Loads a module and all of its forms. + */ +static void vn_gui_load_module (VnGui * obj, const gchar * dir, const gchar * file) +{ + gint n; + gchar * path; + gchar * mod_title_strip, * mod_name; + GModule * module = NULL; + VnMod * mod = NULL; + GdomeException e; + GdomeDocument * doc; + GdomeDOMImplementation * di; + GdomeNodeList * nl, * action_nl; + GdomeElement * el; + GdomeNode * node; + GdomeDOMString * mod_title; + GdomeDOMString * library; + + // Validating the module definition against the DTD (using libxml2 directly) + + xmlValidCtxtPtr ctxt = xmlNewValidCtxt (); + + path = g_strdup_printf ("%s/%s", dir, file); + + if (ctxt) + { + gboolean valid = FALSE; + xmlDocPtr doc = xmlParseFile (path); + + if (doc) + { + xmlDtdPtr dtd = xmlParseDTD (NULL, (const xmlChar *) MODULE_DTD); + + if (dtd) + valid = xmlValidateDtd (ctxt, doc, dtd); + else + g_warning ("The DTD is not well formed"); + + xmlFreeDtd (dtd); + } + + xmlFreeValidCtxt (ctxt); + xmlFreeDoc (doc); + + if (!valid) + { + g_free (path); + g_warning ("The module definition at \"%s\" is not valid", file); + return; + } + } + + // Getting the module info from XML file + + di = gdome_di_mkref (); + doc = gdome_di_createDocFromURI (di, path, 0, &e); + + g_free (path); + + gdome_di_unref (di, &e); + + if (doc) + { + nl = gdome_doc_getElementsByTagName (doc, S("library"), &e); + el = (GdomeElement *) gdome_nl_item (nl, 0, &e); + gdome_nl_unref (nl, &e); + + library = gdome_el_getAttribute (el, S("name"), &e); + mod_name = g_strdup (library->str); + + node = gdome_el_firstChild (el, &e); + mod_title = gdome_n_nodeValue (node, &e); + mod_title_strip = g_strstrip (g_strdup (mod_title->str)); + + nl = gdome_doc_getElementsByTagName (doc, S("form"), &e); + action_nl = gdome_doc_getElementsByTagName (doc, S("action"), &e); + + gdome_str_unref (library); + gdome_doc_unref (doc, &e); + gdome_n_unref (node, &e); + gdome_el_unref (el, &e); + } + else + { + g_warning ("VnGui: Error loading module info file: %s", (gchar *) file); + return; + } + + // Loading the module dynamically + + for (n = 0; obj->lib_dirs[n] && !module; n++) + { + path = g_module_build_path (obj->lib_dirs[n], mod_name); + module = g_module_open (path, 0); + g_free (path); + } + + if (module) + { + g_module_make_resident (module); + mod = g_object_new (VN_TYPE_MOD + ,"name" ,mod_name + ,"data-dir" ,dir + ,"module" ,module + ,NULL + ); + } + else + g_warning ("VnGui: Can't load module %s: %s" + ,mod_name + ,g_module_error () + ); + + // If successful, load forms, actions and UI + + if (mod) + { + GType type; + gulong len, n; + gchar * c_name; + gchar * title_strip; + gchar * ui_file; + gchar * buffer; + GdomeDOMString * name; + GdomeDOMString * icon; + GdomeDOMString * action_name; + GdomeDOMString * accel; + GdomeDOMString * title; + gchar * symbol_name; + const gchar * text_dom; + GtkTreeIter parent_iter; + GtkTreeIter * iter; + VnFormGetTypeFunc mod_get_type_func; + GError * err = NULL; + GSList * mod_actions = NULL; + ActionData * action_data; + GtkActionGroup * actions = gtk_action_group_new (mod_name); + + // Creating folder to put forms inside + + text_dom = vn_mod_get_text_domain (mod); + gtk_tree_store_append (obj->tree, &parent_iter, NULL); + gtk_tree_store_set (obj->tree, &parent_iter + ,COL_ICON ,"gtk-directory" + ,COL_TITLE ,g_dgettext (text_dom, mod_title_strip) + ,COL_TYPE ,G_TYPE_NONE + ,COL_NAME ,NULL + ,-1 + ); + + len = gdome_nl_length (nl, &e); + + for (n = 0; n < len; n++) + { + // Getting form info + + el = (GdomeElement *) gdome_nl_item (nl, n, &e); + icon = gdome_el_getAttribute (el, S("icon"), &e); + action_name = gdome_el_getAttribute (el, S("action-name"), &e); + accel = gdome_el_getAttribute (el, S("accel"), &e); + + name = gdome_el_getAttribute (el, S("name"), &e); + c_name = g_strdelimit (g_strdup (name->str), "-. ", '_'); + + node = gdome_el_firstChild (el, &e); + title = gdome_n_nodeValue (node, &e); + title_strip = g_strstrip (g_strdup (title->str)); + + gdome_n_unref (node, &e); + gdome_el_unref (el, &e); + + // Loading form + + symbol_name = g_strdup_printf ("vn_%s_get_type", c_name); + + if (g_module_symbol (module, symbol_name, (gpointer) &mod_get_type_func)) + { + type = mod_get_type_func (); + + if (g_type_is_a (type, VN_TYPE_FORM)) + { + iter = g_new (GtkTreeIter, 1); + gtk_tree_store_append (obj->tree, iter, &parent_iter); + gtk_tree_store_set (obj->tree, iter + ,COL_NAME ,name->str + ,COL_ICON ,icon->str + ,COL_TITLE ,g_dgettext (text_dom, title_strip) + ,COL_TYPE ,type + ,COL_MODULE ,mod + ,-1 + ); + g_hash_table_replace (obj->forms, g_strdup (name->str), iter); + + if (g_strcmp0 ("", action_name->str)) + { + action_data = g_new (ActionData, 1); + action_data->entry = g_new (GtkActionEntry, 1); + action_data->entry->name = g_strdup (action_name->str); + action_data->entry->stock_id = g_strdup (icon->str); + action_data->entry->label = g_strdup (title_strip); + action_data->entry->accelerator = g_strdup (accel->str); + action_data->entry->tooltip = NULL; + action_data->entry->callback = + (GCallback) vn_gui_on_open_form_activated; + + action_data->form = g_new (FormData, 1); + action_data->form->name = g_strdup (name->str); + action_data->form->gui = obj; + + mod_actions = g_slist_prepend (mod_actions, action_data); + + gtk_action_group_add_actions (actions + ,action_data->entry, 1 + ,action_data->form + ); + } + } + else + g_warning ("VnGui: %s isn't a VnForm", g_type_name (type)); + } + else + g_warning ("VnGui: Error loading form: %s", g_module_error ()); + + g_free (c_name); + gdome_str_unref (name); + gdome_str_unref (icon); + gdome_str_unref (action_name); + gdome_str_unref (accel); + gdome_str_unref (title); + g_free (title_strip); + g_free (symbol_name); + } + + len = gdome_nl_length (action_nl, &e); + + for (n = 0; n < len; n++) + { + el = (GdomeElement *) gdome_nl_item (action_nl, n, &e); + name = gdome_el_getAttribute (el, S("name"), &e); + + node = gdome_el_firstChild (el, &e); + title = gdome_n_nodeValue (node, &e); + title_strip = g_strstrip (g_strdup (title->str)); + + gdome_el_unref (el, &e); + gdome_n_unref (node, &e); + gdome_str_unref (title); + + action_data = g_new (ActionData, 1); + action_data->entry = g_new0 (GtkActionEntry, 1); + action_data->entry->name = g_strdup (name->str); + action_data->entry->label = g_strdup (title_strip); + action_data->form = NULL; + + mod_actions = g_slist_prepend (mod_actions, action_data); + + gtk_action_group_add_actions (actions, action_data->entry, 1, NULL); + + gdome_str_unref (name); + g_free (title_strip); + } + + ui_file = g_strdup_printf ("%s/%s.ui", dir, mod_name); + + if (g_file_get_contents (ui_file, &buffer, NULL, &err)) + { + ModData * mod_data = g_new (ModData, 1); + mod_data->action_data = mod_actions; + mod_data->mod = mod; + + vn_mod_set_ui (mod, buffer); + obj->modules = g_slist_prepend (obj->modules, mod_data); + } + else + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + g_slist_free_full (mod_actions, + (GDestroyNotify) vn_gui_free_action_data); + } + + g_free (ui_file); +// g_object_unref (mod); + g_object_unref (actions); + } + + g_free (mod_title_strip); + g_free (mod_name); + gdome_str_unref (mod_title); + gdome_nl_unref (nl, &e); + gdome_nl_unref (action_nl, &e); +} + +static VnWindow * vn_gui_add_window (VnGui * obj, GtkWindow * widget, GtkNotebook * notebook) +{ + GdkRGBA color; + GSList * n, * m; + GError * err = NULL; + GtkActionGroup * actions = NULL; + GtkStyleContext * style; + GtkBuilder * builder; + VnWindow * window; + + window = g_new (VnWindow, 1); + window->obj = obj; + window->widget = widget; + window->notebook = notebook; + window->manager = gtk_ui_manager_new (); + window->active_form = NULL; + window->merge_id = 0; + + obj->windows = g_slist_prepend (obj->windows, window); + gtk_application_add_window (obj->app, widget); + + gtk_notebook_set_group_name (notebook, + g_application_get_application_id (G_APPLICATION (obj->app))); + + style = gtk_widget_get_style_context (GTK_WIDGET (widget)); + gtk_style_context_get_background_color (style, + GTK_STATE_FLAG_NORMAL, &color); + gtk_widget_override_background_color (GTK_WIDGET (notebook), + GTK_STATE_FLAG_NORMAL, &color); + + gtk_widget_show_all (GTK_WIDGET (widget)); + + // Loading the bars and associated actions + + builder = gtk_builder_new (); + + if (gtk_builder_add_from_file (builder, ACTIONS_UI, &err)) + { + actions = gtk_builder_get (builder, "main-actions"); + gtk_builder_connect_signals (builder, window); + gtk_ui_manager_insert_action_group (window->manager, actions, -1); + } + else + { + g_warning ("VnGui: %s", err->message); + g_clear_error (&err); + } + + if (gtk_ui_manager_add_ui_from_file (window->manager, MENUBAR_XML, &err)) + { + GtkBox * box = GTK_BOX (gtk_bin_get_child (GTK_BIN (widget))); + GtkWidget * menubar = gtk_ui_manager_get_widget (window->manager, "/MenuBar"); + GtkWidget * toolbar = gtk_ui_manager_get_widget (window->manager, "/Toolbar"); + + gtk_box_pack_start (box, menubar, FALSE, FALSE, 0); + gtk_box_pack_start (box, toolbar, FALSE, FALSE, 0); + gtk_style_context_add_class (gtk_widget_get_style_context (toolbar), + GTK_STYLE_CLASS_PRIMARY_TOOLBAR); + gtk_window_add_accel_group (widget, + gtk_ui_manager_get_accel_group (window->manager)); + + window->toolbar = toolbar; + } + else + { + window->toolbar = NULL; + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + + if (actions) + { + // TODO: Load from config file the default value for toggle actions. + + window->dynamic_tabs = gtk_builder_get (builder, "menu-view-tabs"); + gtk_toggle_action_set_active (window->dynamic_tabs, TRUE); + + window->view_toolbar = gtk_builder_get (builder, "menu-view-toolbar"); + gtk_toggle_action_set_active (window->view_toolbar, TRUE); + } + + g_object_unref (builder); + + // Loading the modules actions + + for (n = obj->modules; n; n = n->next) + { + ModData * mod_data = n->data; + GtkActionGroup * mod_actions = gtk_action_group_new (vn_mod_get_name (mod_data->mod)); + + for (m = mod_data->action_data; m; m = m->next) + { + ActionData * action_data = m->data; + + gtk_action_group_add_actions (mod_actions + ,action_data->entry, 1 + ,action_data->form + ); + } + + gtk_ui_manager_insert_action_group (window->manager, mod_actions, -1); + g_object_unref (mod_actions); + + if (!gtk_ui_manager_add_ui_from_string (window->manager, + vn_mod_get_ui (mod_data->mod), -1, &err)) + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + } + + gtk_ui_manager_ensure_update (window->manager); + return window; +} + +/* + * Shows an error dialog. + */ +static void vn_gui_show_error (VnGui * obj, const GError * error) +{ + GtkWidget * dialog; + GtkWindow * window = vn_gui_get_active_window (obj)->widget; + + if (error && error->code == DB_CONN_ERROR_LOST) + dialog = gtk_message_dialog_new (window + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_QUESTION + ,GTK_BUTTONS_YES_NO + ,_("Connection has been lost. Do you want to reconnect?") + ); + else + dialog = gtk_message_dialog_new (window + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_WARNING + ,GTK_BUTTONS_CLOSE + ,_("An error occurred in the connection.") + ); + + gtk_window_set_title (GTK_WINDOW (dialog), _("Database error")); + + if (error) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", error->message); + else + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Unknown error")); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) + vn_gui_reconnect (obj); + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +/* + * Closes and saves the GUI interface. + */ +static void vn_gui_close (VnGui * obj) +{ + GSList * n; + + g_return_if_fail (VN_IS_GUI (obj)); + + if (obj->main_window) + { + vn_gui_save (obj); + g_object_disconnect (obj->conn + ,"any_signal", vn_gui_on_conn_error, obj + ,"any_signal", vn_gui_on_conn_status_changed, obj + ,NULL + ); + + for (n = obj->windows; n; n = n->next) + vn_gui_free_window (obj, n->data); + + g_slist_free (obj->windows); + obj->windows = NULL; + obj->main_window = NULL; + } +} + +/* + * Idle function that completes the reopen thread. + */ +static gboolean vn_gui_reconnect_idle (GuiData * gui_data) +{ + if (!gui_data->aux) + vn_gui_show_error (gui_data->obj, gui_data->error); + + return FALSE; +} + +/* + * Thread function that tryes to reopen the connection asynchronously. + */ +static void vn_gui_reconnect_thread (GuiData * gui_data) +{ + gui_data->aux = db_conn_reconnect (gui_data->obj->conn, &gui_data->error); + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) vn_gui_reconnect_idle, gui_data, (GDestroyNotify) gui_data_free); +} + +/* + * Reconnects to database. + */ +void vn_gui_reconnect (VnGui * obj) +{ + GuiData * gui_data = g_new (GuiData, 1); + gui_data->obj = g_object_ref (obj); + gui_data->aux = FALSE; + gui_data->error = NULL; + gui_data->thread = g_thread_new ("vn-gui-reconnect", + (GThreadFunc) vn_gui_reconnect_thread, gui_data); +} + +/* + * Saves the login information and closes the main GUI. + */ +static gboolean vn_gui_logout_idle (GuiData * gui_data) +{ + vn_gui_close (gui_data->obj); + + if (gui_data->aux) + g_signal_emit (gui_data->obj, signals[EXIT], 0); + else + g_signal_emit (gui_data->obj, signals[LOGOUT], 0); + + return FALSE; +} + +/* + * Thread function that tryes to close the connection asynchronously. + */ +static void vn_gui_logout_thread (GuiData * gui_data) +{ + db_conn_close (gui_data->obj->conn, TRUE); + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) vn_gui_logout_idle, gui_data, (GDestroyNotify) gui_data_free); +} + +/* + * Closes the connection and the user interface. + */ +void vn_gui_logout (VnGui * obj, gboolean exit) +{ + GuiData * gui_data = g_new (GuiData, 1); + gui_data->obj = g_object_ref (obj); + gui_data->aux = exit; + gui_data->error = NULL; + gui_data->thread = g_thread_new ("vn-gui-close", + (GThreadFunc) vn_gui_logout_thread, gui_data); +} + +static void vn_gui_hide_form (VnWindow * window) +{ + if (window->active_form) + { + GtkActionGroup * actions = + vn_form_get_action_group (window->active_form); + + if (actions) + { + gtk_ui_manager_remove_ui (window->manager, window->merge_id); + gtk_ui_manager_remove_action_group (window->manager, actions); + } + + window->active_form = NULL; + } +} + +static void vn_gui_set_show_tabs (VnWindow * window) +{ + gboolean show_tabs = gtk_notebook_get_n_pages (window->notebook) > 1 + || !gtk_toggle_action_get_active (window->dynamic_tabs); + + gtk_notebook_set_show_tabs (window->notebook, show_tabs); +} + +//--------------------------------------------------- Window handlers + +/* + * Called when the main window is closed. + */ +gboolean vn_gui_on_main_deleted (GtkWidget * widget, GdkEvent * event, VnWindow * window) +{ + vn_gui_logout (window->obj, TRUE); + return TRUE; +} + +/* + * Called when a child window is closed. + */ +void vn_gui_on_child_destroyed (GtkWindow * widget, VnWindow * window) +{ + VnGui * obj = window->obj; + + vn_gui_free_window (obj, window); + obj->windows = g_slist_remove (obj->windows, window); +} + +//--------------------------------------------------- Notebook handlers + +/* + * Called when a page is detached from a notebook. This function creates a new + * window with a notebook and puts the page in. + * Connected to the "create-window" signal of GtkNotebook. + */ +GtkNotebook * vn_gui_on_page_detached (GtkNotebook * old_notebook, + GtkWidget * page, gint x, gint y, VnWindow * window) +{ + VnWindow * new_window = vn_gui_create_window (window->obj, x, y); + return (window) ? new_window->notebook : NULL; +} + +/* + * Called when the focus changes from a page to another. It is also used in + * newly opened pages. + */ +void vn_gui_on_switch_page (GtkNotebook * notebook, VnForm * form, guint num, VnWindow * window) +{ + GError * err = NULL; + GtkTreeIter * iter; + GtkActionGroup * actions; + VnGui * obj = window->obj; + + vn_gui_hide_form (window); + + // Merge form UI with the window UI + + window->active_form = form; + + if ((iter = g_hash_table_lookup (obj->forms, vn_form_get_name (form)))) + { + gchar * window_title, * form_title; + + gtk_tree_model_get (GTK_TREE_MODEL (obj->tree), + iter, COL_TITLE, &form_title, -1); + + window_title = g_strdup_printf ("%s - %s", form_title, obj->app_title); + gtk_window_set_title (window->widget, window_title); + + g_free (form_title); + g_free (window_title); + } + + if ((actions = vn_form_get_action_group (form))) + { + guint merge_id; + const gchar * form_ui = vn_form_get_ui_manager (form); + + gtk_ui_manager_insert_action_group (window->manager, actions, -1); + + if ((merge_id = gtk_ui_manager_add_ui_from_string (window->manager, + form_ui, -1, &err))) + { + window->merge_id = merge_id; + } + else + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + } + + gtk_ui_manager_ensure_update (window->manager); +} + +void vn_gui_on_page_added (GtkNotebook * notebook, + GtkWidget * page, guint num, VnWindow * window) +{ + vn_gui_set_show_tabs (window); +} + +/* + * Called when a page of the main window is removed from its notebook. + */ +void vn_gui_on_main_page_removed (GtkNotebook * notebook, + GtkWidget * page, guint num, VnWindow * window) +{ + vn_gui_set_show_tabs (window); + + if (gtk_notebook_get_n_pages (notebook) < 1) + { + vn_gui_hide_form (window); + gtk_window_set_title (window->widget, window->obj->app_title); + } +} + +/* + * Called when a page of a child window is removed from its notebook, if its + * the last page destroys the window too. + */ +void vn_gui_on_page_removed (GtkNotebook * notebook, + GtkWidget * page, guint num, VnWindow * window) +{ + vn_gui_set_show_tabs (window); + + if (gtk_notebook_get_n_pages (notebook) < 1) + gtk_widget_destroy (GTK_WIDGET (window->widget)); +} + +//--------------------------------------------------- Action handlers + +/* + * Opens a form when the action associated to it is activated. + */ +void vn_gui_on_open_form_activated (GtkAction * action, FormData * form_data) +{ + vn_gui_open_form (form_data->gui, form_data->name); +} + +/* + * Reconnects to database. + */ +void vn_gui_on_open_activated (GtkAction * action, VnWindow * window) +{ + vn_gui_reconnect (window->obj); +} + +/* + * Logout action handler. + */ +void vn_gui_on_logout_activated (GtkAction * action, VnWindow * window) +{ + vn_gui_logout (window->obj, FALSE); +} + +/* + * Exit action handler. + */ +void vn_gui_on_exit_activated (GtkAction * action, VnWindow * window) +{ + vn_gui_logout (window->obj, TRUE); +} + +/* + * Closes the current tab when the close-tab action is activated. + */ +void vn_gui_on_close_tab_activated (GtkAction * action, VnWindow * window) +{ + if (window->active_form) + vn_gui_close_form (window->obj, window->active_form); +} + +/* + * Shows/hides the tabs when the view-tabs action is activated + */ +void vn_gui_on_dynamic_tabs_activated (GtkToggleAction * action, VnWindow * window) +{ + vn_gui_set_show_tabs (window); +} + +/* + * Shows/hides the toolbar when the view-toolbar action is activated + */ +void vn_gui_on_view_toolbar_activated (GtkToggleAction * action, VnWindow * window) +{ + gtk_widget_set_visible (window->toolbar, gtk_toggle_action_get_active (action)); +} + +/* + * Shows a window with program information. + */ +void vn_gui_on_about_activated (GtkAction * action, VnWindow * window) +{ + gtk_dialog_run (window->obj->about); +} + +//--------------------------------------------------- Connection handlers + +/* + * Called when there is a query error in the connection. + */ +static void vn_gui_on_conn_error (DbConn * conn, const GError * error, VnGui * obj) +{ + vn_gui_show_error (obj, error); +} + +/* + * Enables/disables the #GtkSpinner when connection is loading. + */ +static void vn_gui_on_conn_status_changed (DbConn * conn, DbConnStatus status, VnGui * obj) +{ + gchar * status_text; + + if (status & DB_CONN_CLOSING) + status_text = _("Closing connection"); + else if (status & DB_CONN_TRANSACTION) + status_text = _("Transaction started"); + else if (status & DB_CONN_OPENING) + status_text = _("Connecting"); + else if (status & DB_CONN_LOST) + status_text = _("Connection lost"); + else if (status == DB_CONN_CLOSED) + status_text = _("Connection closed"); + else if (status & DB_CONN_LOADING) + status_text = _("Loading"); + else + status_text = _("Ready"); + + gtk_label_set_text (obj->status_label, status_text); + + if (status & DB_CONN_LOADING) + { + // XXX: Used to fix a bug that causes the GtkSpinner not spin. + gtk_widget_hide (GTK_WIDGET (obj->spinner)); + gtk_widget_show (GTK_WIDGET (obj->spinner)); + + gtk_spinner_start (obj->spinner); + } + else + gtk_spinner_stop (obj->spinner); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_gui_open: + * @obj: a #VnGui + * + * Shows the main GUI. + **/ +void vn_gui_open (VnGui * obj) +{ + GtkBuilder * builder; + GError * err = NULL; + + g_return_if_fail (VN_IS_GUI (obj)); + g_return_if_fail (!obj->main_window); + + builder = gtk_builder_new (); + + if (gtk_builder_add_from_file (builder, MAIN_UI, &err)) + { + gchar * user; + GtkLabel * label; + GKeyFile * config; + GtkWindow * widget; + GtkNotebook * notebook; + + obj->spinner = gtk_builder_get (builder, "spinner"); + obj->about = gtk_builder_get (builder, "about"); + obj->menu = gtk_builder_get (builder, "menu"); + obj->status_label = gtk_builder_get (builder, "status-label"); + + user = db_conn_get_user (obj->conn); + label = gtk_builder_get (builder, "user-name"); + gtk_label_set_text (label, user); + g_free (user); + + widget = gtk_builder_get (builder, "window"); + notebook = gtk_builder_get (builder, "notebook"); + obj->main_window = vn_gui_add_window (obj, widget, notebook); + gtk_builder_connect_signals (GTK_BUILDER (builder), obj->main_window); + + // Restoring interface + + config = g_key_file_new (); + + if (g_key_file_load_from_file (config, obj->config_file, 0, NULL)) + { + gsize m, n; + gsize len; + gint x, y; + gint width, height; + gchar ** windows; + gchar ** forms; + VnWindow * window; + + windows = g_key_file_get_groups (config, &len); + + for (m = 0; m < len; m++) + { + x = g_key_file_get_integer (config, windows[m], "x", NULL); + y = g_key_file_get_integer (config, windows[m], "y", NULL); + width = g_key_file_get_integer (config, windows[m], "width", NULL); + height = g_key_file_get_integer (config, windows[m], "height", NULL); + + if (g_key_file_get_boolean (config, windows[m], "main", NULL)) + { + window = obj->main_window; + gtk_window_move (window->widget, x, y); + } + else + window = vn_gui_create_window (obj, x, y); + + gtk_window_resize (window->widget, width, height); + + forms = g_key_file_get_string_list (config, + windows[m], "forms", NULL, NULL); + + for (n = 0; forms[n]; n++) + vn_gui_open_form_at_window (obj, forms[n], window); + + g_key_file_remove_group (config, windows[m], NULL); + g_strfreev (forms); + } + + g_strfreev (windows); + } + + g_key_file_free (config); + + g_object_connect (obj->conn + ,"signal::error", vn_gui_on_conn_error, obj + ,"signal::status-changed", vn_gui_on_conn_status_changed, obj + ,NULL + ); + } + else if (err) + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + + g_object_unref (builder); +} + +/** + * vn_gui_save: + * @obj: the #VnGui + * + * Saves the GUI configuration. + **/ +void vn_gui_save (VnGui * obj) +{ + gint len; + gint j, i = 0; + gchar * group; + const gchar ** forms; + GSList * m; + GList * nb_pages, * n; + VnWindow * window; + gint x, y, width, height; + GKeyFile * config; + + g_return_if_fail (VN_IS_GUI (obj)); + g_return_if_fail (obj->main_window); + + // Saving the interface configuration + + config = g_key_file_new (); + + for (m = obj->windows; m; m = m->next) + { + window = (VnWindow *) m->data; + group = g_strdup_printf ("window%d", i++); + + // Saving the window position and size + + gtk_window_get_position (window->widget, &x, &y); + gtk_window_get_size (window->widget, &width, &height); + + g_key_file_set_integer (config, group, "x", x); + g_key_file_set_integer (config, group, "y", y); + g_key_file_set_integer (config, group, "width", width); + g_key_file_set_integer (config, group, "height", height); + + // Saving the forms opened at window + + if (window == obj->main_window) + g_key_file_set_boolean (config, group, "main", TRUE); + + nb_pages = gtk_container_get_children (GTK_CONTAINER (window->notebook)); + len = gtk_notebook_get_n_pages (window->notebook); + forms = g_new (const gchar *, len); + + for (j = 0, n = nb_pages; n; n = n->next) + forms[j++] = vn_form_get_name (n->data); + + g_key_file_set_string_list (config, group, "forms", forms, len); + + g_list_free (nb_pages); + g_free (forms); + g_free (group); + } + + gvn_key_file_save (config, obj->config_file); + g_key_file_free (config); +} + +/** + * vn_gui_create_window: + * @obj: the #VnGui + * @x: the x coordinate + * @y: the y coordinate + * + * Creates a new window. + * + * Return value: VnWindow. + **/ +VnWindow * vn_gui_create_window (VnGui * obj, gint x, gint y) +{ + GtkBuilder * builder; + VnWindow * window = NULL; + GError * err = NULL; + + g_return_val_if_fail (VN_IS_GUI (obj), NULL); + g_return_val_if_fail (obj->main_window, NULL); + + builder = gtk_builder_new (); + + if (gtk_builder_add_from_file (builder, CHILD_WINDOW_UI, &err)) + { + GtkWindow * widget; + GtkNotebook * notebook; + + widget = gtk_builder_get (builder, "child"); + notebook = gtk_builder_get (builder, "notebook"); + gtk_window_move (widget, x, y); + + window = vn_gui_add_window (obj, widget, notebook); + gtk_builder_connect_signals (builder, window); + } + else + { + g_warning ("VnGui: %s", err->message); + g_error_free (err); + } + + g_object_unref (builder); + return window; +} + +/** + * vn_gui_open_form_at_window: + * @obj: the #VnGui + * @form_name: the name of the form that you want to open + * @window: the #VnWindow where the form will be opened + * + * Opens a new form at the specified window. + * + * Return value: (transfer none): the created #VnForm + **/ +VnForm * vn_gui_open_form_at_window (VnGui * obj, const gchar * form_name, VnWindow * window) +{ + gchar * icon; + gchar * title; + VnMod * module; + GType form_type; + GtkBox * hbox; + GtkWidget * form; + GtkWidget * widget; + GtkWidget * button; + GtkTreeIter * iter; + GtkNotebook * notebook = NULL; + + g_return_val_if_fail (VN_IS_GUI (obj), NULL); + g_return_val_if_fail (obj->main_window, NULL); + + iter = g_hash_table_lookup (obj->forms, form_name); + + if (!iter) + { + g_warning ("VnGui: Form %s doesn't exist", form_name); + return NULL; + } + + gtk_tree_model_get (GTK_TREE_MODEL (obj->tree), iter + ,COL_ICON ,&icon + ,COL_TITLE ,&title + ,COL_MODULE ,&module + ,COL_TYPE ,&form_type + ,-1 + ); + + form = g_object_new (form_type + ,"name" ,form_name + ,"gui" ,obj + ,"module" ,module + ,NULL + ); + vn_form_open (VN_FORM (form)); + + hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5)); + + widget = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU); + gtk_box_pack_start (hbox, widget, FALSE, FALSE, 0); + + widget = gtk_label_new (title); + gtk_box_pack_start (hbox, widget, TRUE, TRUE, 0); + + button = gtk_button_new (); + g_signal_connect (button, "clicked", + G_CALLBACK (vn_gui_close_form), form); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_box_pack_start (hbox, button, FALSE, FALSE, 0); + + widget = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_MENU); + gtk_button_set_image (GTK_BUTTON (button), widget); + + if (!window) + window = vn_gui_get_active_window (obj); + + notebook = window->notebook; + gtk_notebook_set_current_page (notebook, + gtk_notebook_append_page (notebook, form, GTK_WIDGET (hbox))); + gtk_notebook_set_tab_detachable (notebook, form, TRUE); + gtk_notebook_set_tab_reorderable (notebook, form, TRUE); + gtk_widget_show_all (GTK_WIDGET (hbox)); + gtk_widget_show (form); + + g_free (icon); + g_free (title); + + return VN_FORM (form); +} + +/** + * vn_gui_open_form: + * @obj: the #VnGui + * @form_name: the name of the form that you want to open + * + * Opens a new form, creating a new page for it at the main notebook. + * + * Return value: (transfer none): the created #VnForm + **/ +VnForm * vn_gui_open_form (VnGui * obj, const gchar * form_name) +{ + g_return_val_if_fail (VN_IS_GUI (obj), NULL); + g_return_val_if_fail (form_name, NULL); + + return vn_gui_open_form_at_window (obj, form_name, NULL); +} + +/** + * vn_gui_close_form: + * @obj: the #VnGui + * @form: the #VnForm that you want to close + * + * Closes a form. + **/ +void vn_gui_close_form (VnGui * obj, VnForm * form) +{ + gint num; + GtkNotebook * notebook; + + g_return_if_fail (VN_IS_FORM (form)); + + notebook = GTK_NOTEBOOK (gtk_widget_get_ancestor ( + GTK_WIDGET (form), GTK_TYPE_NOTEBOOK)); + num = gtk_notebook_page_num (notebook, GTK_WIDGET (form)); + gtk_notebook_remove_page (notebook, num); +} + +/** + * vn_gui_get_conn: + * @obj: a #VnGui + * + * Gets the Data Base connection used by @obj + * + * Return value: (transfer none): the #DbConn + **/ +DbConn * vn_gui_get_conn (VnGui * obj) +{ + g_return_val_if_fail (VN_IS_GUI (obj), NULL); + + return obj->conn; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_CONN = 1 + ,PROP_APP +}; + +static void vn_gui_set_property (VnGui * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_CONN: + { + gchar * query_path = g_build_path (G_SEARCHPATH_SEPARATOR_S + ,_VN_MODULE_QUERY_DIR + ,g_getenv ("VN_MODULE_QUERY_PATH") + ,NULL + ); + + obj->conn = g_value_dup_object (value); + db_conn_set_query_path (obj->conn, query_path); + + g_free (query_path); + break; + } + case PROP_APP: + { + gint n; + const gchar * app_id; + gchar * app_name; + gchar * config_dir; + gchar * lib_path, * data_path; + + obj->app = g_value_dup_object (value); + + app_id = g_application_get_application_id (G_APPLICATION (obj->app)); + app_name = g_strrstr (app_id, ".") + 1; + + obj->app_title = g_strdup (app_name); + obj->app_title[0] = g_ascii_toupper (obj->app_title[0]); + + config_dir = g_build_filename (g_get_user_config_dir (), app_name, NULL); + g_mkdir_with_parents (config_dir, 0600); + obj->config_file = g_build_filename (config_dir, "gui.ini", NULL); + g_free (config_dir); + + // Setting module search paths + + lib_path = g_build_path (G_SEARCHPATH_SEPARATOR_S + ,_VN_MODULE_LIB_DIR + ,g_getenv ("VN_MODULE_LIB_PATH") + ,NULL + ); + data_path = g_build_path (G_SEARCHPATH_SEPARATOR_S + ,_VN_MODULE_DATA_DIR + ,g_getenv ("VN_MODULE_DATA_PATH") + ,NULL + ); + + obj->lib_dirs = g_strsplit (lib_path, G_SEARCHPATH_SEPARATOR_S, 0); + obj->data_dirs = g_strsplit (data_path, G_SEARCHPATH_SEPARATOR_S, 0); + + g_free (data_path); + g_free (lib_path); + + // Initializing modules + + for (n = 0; obj->data_dirs[n]; n++) + { + const gchar * file; + GError * err = NULL; + GDir * dir = g_dir_open (obj->data_dirs[n], 0, &err); + + if (dir) + { + while ((file = g_dir_read_name (dir))) + if (!g_strcmp0 (".xml", g_strrstr (file, "."))) + vn_gui_load_module (obj, obj->data_dirs[n], file); + + g_dir_close (dir); + } + else + { + g_warning ("VnGui: Error opening directory at module data path: %s" + ,err->message + ); + g_error_free (err); + } + } + + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_gui_get_property (VnGui * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_CONN: + g_value_set_object (value, obj->conn); + break; + case PROP_APP: + g_value_set_object (value, obj->app); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_gui_init (VnGui * obj) +{ + obj->main_window = NULL; + obj->about = NULL; + obj->windows = NULL; + obj->conn = NULL; + obj->app = NULL; + obj->app_title = NULL; + obj->modules = NULL; + obj->lib_dirs = NULL; + obj->data_dirs = NULL; + obj->config_file = NULL; + + obj->forms = g_hash_table_new_full ( + (GHashFunc) g_str_hash + ,(GEqualFunc) g_str_equal + ,(GDestroyNotify) g_free + ,(GDestroyNotify) g_free + ); + obj->tree = gtk_tree_store_new (COL_COUNT + ,G_TYPE_STRING // COL_ICON + ,G_TYPE_STRING // COL_NAME + ,G_TYPE_STRING // COL_TITLE + ,G_TYPE_GTYPE // COL_TYPE + ,G_TYPE_OBJECT // COL_MODULE + ); +} + +static void vn_gui_finalize (VnGui * obj) +{ + vn_gui_close (obj); + db_conn_close (obj->conn, FALSE); + + g_hash_table_unref (obj->forms); + g_slist_free_full (obj->modules, (GDestroyNotify) vn_gui_free_mod_data); + g_clear_object (&obj->conn); + g_clear_object (&obj->tree); + g_clear_object (&obj->app); + g_strfreev (obj->lib_dirs); + g_strfreev (obj->data_dirs); + g_free (obj->config_file); + g_free (obj->app_title); + G_OBJECT_CLASS (vn_gui_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_gui_class_init (VnGuiClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) vn_gui_finalize; + klass->set_property = (GObjectSetPropertyFunc) vn_gui_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_gui_get_property; + + signals[LOGOUT] = g_signal_new ("logout", + VN_TYPE_GUI, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + signals[EXIT] = g_signal_new ("exit", + VN_TYPE_GUI, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 + ); + + g_object_class_install_property (klass, PROP_CONN, + g_param_spec_object ("conn" + ,_("Connection") + ,_("The connection used by Gui") + ,DB_TYPE_CONN + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); + g_object_class_install_property (klass, PROP_APP, + g_param_spec_object ("app" + ,_("Application") + ,_("The application handler for the entire program") + ,GTK_TYPE_APPLICATION + ,G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/vn/vn-gui.h b/vn/vn-gui.h new file mode 100644 index 0000000..8d3ffa7 --- /dev/null +++ b/vn/vn-gui.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_GUI_H +#define VN_GUI_H + +#include "vn-builder.h" + +#define VN_TYPE_GUI (vn_gui_get_type ()) +#define VN_GUI(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_GUI, VnGui)) +#define VN_IS_GUI(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_GUI)) +#define VN_GUI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_GUI, VnGuiClass)) +#define VN_IS_GUI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_GUI)) +#define VN_GUI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_GUI, VnGuiClass)) + +typedef struct _VnGui VnGui; +typedef struct _VnGuiClass VnGuiClass; + +typedef struct _VnWindow VnWindow; + +#include "vn-mod.h" +#include "vn-form.h" + +/* + * @windows: (element-type VnWindow): + * @modules: (element-type ModData): + */ +struct _VnGui +{ + GObject parent; + DbConn * conn; + GtkApplication * app; + gchar * app_title; + + VnWindow * main_window; + GtkTreeStore * tree; + GtkTreeView * menu; + GtkSpinner * spinner; + GtkDialog * about; + GtkLabel * status_label; + + GHashTable * forms; + GSList * windows; + GSList * modules; + + gchar * config_file; + gchar ** lib_dirs; + gchar ** data_dirs; +}; + +struct _VnGuiClass +{ + GObjectClass parent; +}; + +GType vn_gui_get_type (); +VnGui * vn_gui_new (GtkApplication * app, DbConn * conn); +void vn_gui_open (VnGui * obj); +void vn_gui_save (VnGui * obj); +VnWindow * vn_gui_create_window (VnGui * obj, gint x, gint y); +VnForm * vn_gui_open_form_at_window (VnGui * obj, const gchar * form_name, VnWindow * window); +VnForm * vn_gui_open_form (VnGui * obj, const gchar * form_name); +void vn_gui_close_form (VnGui * obj, VnForm * form); +DbConn * vn_gui_get_conn (VnGui * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-handler.c b/vn/vn-handler.c new file mode 100644 index 0000000..4ff1464 --- /dev/null +++ b/vn/vn-handler.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-handler.h" + +G_DEFINE_TYPE (VnHandler, vn_handler, GTK_TYPE_HBUTTON_BOX); + +/** + * vn_handler_new: + * + * Creates a new handler + * + * Return value: #GtkWidget created. + **/ +GtkWidget * vn_handler_new () +{ + return g_object_new (VN_TYPE_HANDLER, NULL); +} + +/** + * vn_handler_new_with_form: + * @form: #DbIterator where the handler will be created + * + * Creates a new handler into a iterator gived + * + * Return value: #GtkWidget created. + **/ +GtkWidget * vn_handler_new_with_iterator (DbIterator * iterator) +{ + return g_object_new (VN_TYPE_HANDLER, "iterator", iterator, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +static void vn_handler_on_add_activated (GtkAction * action, VnHandler * obj) +{ + db_iterator_insert (obj->iterator); +} + +static void vn_handler_on_remove_activated (GtkAction * action, VnHandler * obj) +{ + GtkWidget * toplevel; + GtkWidget * dialog; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (obj)); + + dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel) + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_QUESTION + ,GTK_BUTTONS_OK_CANCEL + ,_("Are you sure you want to delete the selected record?") + ); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) + db_iterator_delete (obj->iterator); + + gtk_widget_destroy (dialog); +} + +static void vn_handler_on_save_activated (GtkAction * action, VnHandler * obj) +{ + db_iterator_perform_operations (obj->iterator); +} + +static void vn_handler_on_undo_activated (GtkAction * action, VnHandler * obj) +{ + GtkWidget * toplevel; + GtkWidget * dialog; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (obj)); + + dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel) + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_QUESTION + ,GTK_BUTTONS_OK_CANCEL + ,_("Are you sure that you want to undo all changes?") + ); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) + db_iterator_reverse_operations (obj->iterator); + + gtk_widget_destroy (dialog); +} + +static void vn_handler_on_refresh_activated (GtkAction * action, VnHandler * obj) +{ + db_iterator_refresh (obj->iterator); +} + +static void vn_handler_on_move_activated (GtkAction * action, VnHandler * obj) +{ + DbIteratorMove move; + + if (action == obj->move_previous) + move = DB_ITERATOR_MOVE_PREVIOUS; + else if (action == obj->move_next) + move = DB_ITERATOR_MOVE_NEXT; + else if (action == obj->move_last) + move = DB_ITERATOR_MOVE_LAST; + else + move = DB_ITERATOR_MOVE_FIRST; + + db_iterator_move_to (obj->iterator, move); +} + +static void vn_handler_refresh_save_undo_status (VnHandler * obj) +{ + gboolean sensitive = obj->iterator + && db_iterator_has_pending_operations (obj->iterator); + + if (obj->show_flags & VN_HANDLER_SHOW_SAVE) + gtk_action_set_sensitive (obj->save, sensitive); + + if (obj->show_flags & VN_HANDLER_SHOW_UNDO) + gtk_action_set_sensitive (obj->undo, sensitive); +} + +static void vn_handler_refresh_scroll_status (VnHandler * obj) +{ + if (obj->show_flags & VN_HANDLER_SHOW_SCROLL) + { + gboolean sensitive; + gint row = db_iterator_get_row (obj->iterator); + + sensitive = row > 0; + gtk_action_set_sensitive (obj->move_first, sensitive); + gtk_action_set_sensitive (obj->move_previous, sensitive); + + sensitive = row != -1 && row < db_iterator_get_nrows (obj->iterator) - 1; + gtk_action_set_sensitive (obj->move_next, sensitive); + gtk_action_set_sensitive (obj->move_last, sensitive); + } +} + +static void vn_handler_on_data_changed (DbIterator * iterator, VnHandler * obj) +{ + if (obj->show_flags & VN_HANDLER_SHOW_ADD) + { + gboolean sensitive = + db_iterator_get_update_flags (iterator) & DB_MODEL_INSERT + && (db_iterator_get_nrows (iterator) < 1 || !obj->simple_record); + + gtk_action_set_sensitive (obj->add, sensitive); + } + + vn_handler_refresh_save_undo_status (obj); + vn_handler_refresh_scroll_status (obj); +} + +static void vn_handler_on_row_num_changed (DbIterator * iterator, VnHandler * obj) +{ + if (obj->show_flags & VN_HANDLER_SHOW_REMOVE) + { + gboolean sensitive = + db_iterator_get_update_flags (iterator) & DB_MODEL_DELETE + && db_iterator_get_row (iterator) != -1; + + gtk_action_set_sensitive (obj->remove, sensitive); + } + + vn_handler_refresh_scroll_status (obj); +} + +static void vn_handler_on_operations_done (DbIterator * iterator, VnHandler * obj) +{ + vn_handler_refresh_save_undo_status (obj); +} + +static void vn_handler_on_status_changed (DbIterator * iterator, gboolean ready, VnHandler * obj) +{ + gtk_action_group_set_sensitive (obj->group, ready); + vn_handler_on_row_num_changed (iterator, obj); + vn_handler_on_data_changed (iterator, obj); +} + +static void vn_handler_refresh_status (VnHandler * obj) +{ + if (!obj->iterator) + gtk_action_group_set_sensitive (obj->group, FALSE); + else + vn_handler_on_status_changed (obj->iterator, + db_iterator_is_ready (obj->iterator), obj); +} + +static VnHandlerShowFlags entries_flags[] = +{ + VN_HANDLER_SHOW_UNDO + ,VN_HANDLER_SHOW_SAVE + ,VN_HANDLER_SHOW_REFRESH + ,VN_HANDLER_SHOW_REMOVE + ,VN_HANDLER_SHOW_ADD + ,VN_HANDLER_SHOW_SCROLL + ,VN_HANDLER_SHOW_SCROLL + ,VN_HANDLER_SHOW_SCROLL + ,VN_HANDLER_SHOW_SCROLL +}; + +static GtkActionEntry entries[] = +{ + { + "undo" + ,"edit-undo" + ,NULL + ,NULL + ,N_("Undo changes") + ,G_CALLBACK (vn_handler_on_undo_activated) + },{ + "save" + ,"document-save" + ,NULL + ,NULL + ,N_("Save changes") + ,G_CALLBACK (vn_handler_on_save_activated) + },{ + "refresh" + ,"view-refresh" + ,NULL + ,NULL + ,N_("Refresh data") + ,G_CALLBACK (vn_handler_on_refresh_activated) + },{ + "remove" + ,"list-remove" + ,NULL + ,NULL + ,N_("Remove record") + ,G_CALLBACK (vn_handler_on_remove_activated) + },{ + "add" + ,"list-add" + ,NULL + ,NULL + ,N_("Add record") + ,G_CALLBACK (vn_handler_on_add_activated) + },{ + "move-first" + ,"go-first" + ,NULL + ,NULL + ,N_("Move to the first row") + ,G_CALLBACK (vn_handler_on_move_activated) + },{ + "move-previous" + ,"go-previous" + ,NULL + ,NULL + ,N_("Move to the previous row") + ,G_CALLBACK (vn_handler_on_move_activated) + },{ + "move-next" + ,"go-next" + ,NULL + ,NULL + ,N_("Move to the next row") + ,G_CALLBACK (vn_handler_on_move_activated) + },{ + "move-last" + ,"go-last" + ,NULL + ,NULL + ,N_("Move to the last row") + ,G_CALLBACK (vn_handler_on_move_activated) + } +}; + +static guint n_entries = G_N_ELEMENTS (entries); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_handler_get_iterator: + * @obj: a #VnHandler + * + * Gets the iterator handled by #VnHandler + * + * Return value: (transfer none) (allow-none): the #DbIterator + **/ +DbIterator * vn_handler_get_iterator (VnHandler * obj) +{ + return obj->iterator; +} + +/** + * vn_handler_set_iterator: + * @obj: a #VnHandler + * @iterator: the iterator to be handled + * + * Sets the iterator handled by #VnHandler + **/ +void vn_handler_set_iterator (VnHandler * obj, DbIterator * iterator) +{ + g_return_if_fail (VN_IS_HANDLER (obj)); + g_return_if_fail (DB_IS_ITERATOR (iterator) || !iterator); + + if (obj->iterator) + { + g_object_disconnect (obj->iterator + ,"any-signal", vn_handler_on_status_changed, obj + ,"any-signal", vn_handler_on_data_changed, obj + ,"any-signal", vn_handler_on_row_num_changed, obj + ,"any-signal", vn_handler_on_operations_done, obj + ,NULL + ); + g_clear_object (&obj->iterator); + } + if (iterator) + { + obj->iterator = g_object_ref (iterator); + g_object_connect (iterator + ,"signal::status-changed", vn_handler_on_status_changed, obj + ,"signal::data-changed", vn_handler_on_data_changed, obj + ,"signal::row-num-changed", vn_handler_on_row_num_changed, obj + ,"signal::operations-done", vn_handler_on_operations_done, obj + ,NULL + ); + } + + vn_handler_refresh_status (obj); +} + +/** + * vn_handler_set_show_flags: + * @obj: a #VnHandler + * @show_flags: the buttons to be shown + * + * Sets the buttons that will be shown on the interface. + **/ +void vn_handler_set_show_flags (VnHandler * obj, VnHandlerShowFlags show_flags) +{ + gint i; + gint items; + GList * j; + + g_return_if_fail (VN_IS_HANDLER (obj)); + + for (j = gtk_action_group_list_actions (obj->group); j; j = j->next) + gtk_action_group_remove_action (obj->group, j->data); + + items = gtk_toolbar_get_n_items (obj->toolbar); + + for (i = 0; i < items; i++) + { + GtkToolItem * tool_item = gtk_toolbar_get_nth_item (obj->toolbar, 0); + gtk_container_remove (GTK_CONTAINER (obj->toolbar), GTK_WIDGET (tool_item)); + } + + obj->show_flags = show_flags; + + for (i = 0; i < n_entries; i++) + { + if (obj->show_flags & entries_flags[i]) + { + gtk_action_group_add_actions (obj->group, &entries[i], 1, obj); + + GtkAction * action = gtk_action_group_get_action (obj->group, entries[i].name); + + GtkToolItem * item = GTK_TOOL_ITEM (gtk_action_create_tool_item (action)); + gtk_tool_item_set_homogeneous (item, TRUE); + gtk_toolbar_insert (obj->toolbar, item, -1); + } + } + + if (obj->show_flags & VN_HANDLER_SHOW_ADD) + obj->add = gtk_action_group_get_action (obj->group, "add"); + + if (obj->show_flags & VN_HANDLER_SHOW_REMOVE) + obj->remove = gtk_action_group_get_action (obj->group, "remove"); + + if (obj->show_flags & VN_HANDLER_SHOW_SAVE) + obj->save = gtk_action_group_get_action (obj->group, "save"); + + if (obj->show_flags & VN_HANDLER_SHOW_UNDO) + obj->undo = gtk_action_group_get_action (obj->group, "undo"); + + if (obj->show_flags & VN_HANDLER_SHOW_SCROLL) + { + obj->move_first = gtk_action_group_get_action (obj->group, "move-first"); + obj->move_previous = gtk_action_group_get_action (obj->group, "move-previous"); + obj->move_next = gtk_action_group_get_action (obj->group, "move-next"); + obj->move_last = gtk_action_group_get_action (obj->group, "move-last"); + } + + vn_handler_refresh_status (obj); +} + +/** + * vn_handler_set_simple_record: + * @obj: a #VnHandler + * @simple: %FALSE if model allows multiple records, %TRUE otherwise + * + * Sets if it is used to handle a iterator with a single record. + **/ +void vn_handler_set_simple_record (VnHandler * obj, gboolean simple) +{ + g_return_if_fail (VN_IS_HANDLER (obj)); + + obj->simple_record = simple; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_FORM = 1 + ,PROP_SHOW_FLAGS + ,PROP_SIMPLE_RECORD +}; + +static void vn_handler_set_property (VnHandler * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_FORM: + vn_handler_set_iterator (obj, g_value_get_object (value)); + break; + case PROP_SHOW_FLAGS: + vn_handler_set_show_flags (obj, g_value_get_flags (value)); + break; + case PROP_SIMPLE_RECORD: + vn_handler_set_simple_record (obj, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_handler_get_property (VnHandler * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_FORM: + g_value_set_object (value, obj->iterator); + break; + case PROP_SHOW_FLAGS: + g_value_set_flags (value, obj->show_flags); + break; + case PROP_SIMPLE_RECORD: + g_value_set_boolean (value, obj->simple_record); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_handler_init (VnHandler * obj) +{ + GdkRGBA color = {0.0, 0.0, 0.0, 0.0}; + + obj->add = NULL; + obj->remove = NULL; + obj->move_first = NULL; + obj->move_previous = NULL; + obj->move_next = NULL; + obj->move_last = NULL; + obj->iterator = NULL; + + gtk_box_set_spacing (GTK_BOX (obj), 6); + gtk_box_set_homogeneous (GTK_BOX (obj), FALSE); + gtk_button_box_set_layout (GTK_BUTTON_BOX (obj), GTK_BUTTONBOX_END); + + obj->group = gtk_action_group_new ("form-handler"); + + obj->toolbar = GTK_TOOLBAR (g_object_new (GTK_TYPE_TOOLBAR + ,"icon-size", GTK_ICON_SIZE_LARGE_TOOLBAR + ,"toolbar-style", GTK_TOOLBAR_BOTH_HORIZ + ,"show-arrow", FALSE + ,NULL + )); + gtk_widget_override_background_color (GTK_WIDGET (obj->toolbar), + GTK_STATE_FLAG_NORMAL, &color); + gtk_box_pack_end (GTK_BOX (obj), GTK_WIDGET (obj->toolbar), FALSE, FALSE, 0); +} + +static void vn_handler_finalize (VnHandler * obj) +{ + vn_handler_set_iterator (obj, NULL); + G_OBJECT_CLASS (vn_handler_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_handler_class_init (VnHandlerClass * klass) +{ + GObjectClass * k = G_OBJECT_CLASS (klass); + k->finalize = (GObjectFinalizeFunc) vn_handler_finalize; + k->set_property = (GObjectSetPropertyFunc) vn_handler_set_property; + k->get_property = (GObjectGetPropertyFunc) vn_handler_get_property; + + g_object_class_install_property (k, PROP_FORM, + g_param_spec_object ("iterator" + ,"Iterator" + ,"The handled iterator" + ,DB_TYPE_ITERATOR + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SHOW_FLAGS, + g_param_spec_flags ("show-flags" + ,_("Show flags") + ,_("Sets the buttons that will be shown on the interface") + ,VN_TYPE_HANDLER_SHOW_FLAGS + ,VN_HANDLER_SHOW_UNDO | VN_HANDLER_SHOW_SAVE | VN_HANDLER_SHOW_REMOVE | VN_HANDLER_SHOW_ADD + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); + g_object_class_install_property (k, PROP_SIMPLE_RECORD, + g_param_spec_boolean ("simple-record" + ,_("Simple record") + ,_("Sets if it is used to handle a iterator with a single record") + ,FALSE + ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE + )); +} + +GType vn_handler_show_flags_get_type () +{ + static GType type = 0; + + if (type == 0) + { + static const GFlagsValue values[] = + { + {VN_HANDLER_SHOW_REFRESH, "VN_HANDLER_SHOW_REFRESH", "refresh"} + ,{VN_HANDLER_SHOW_UNDO, "VN_HANDLER_SHOW_UNDO", "undo"} + ,{VN_HANDLER_SHOW_SAVE, "VN_HANDLER_SHOW_SAVE", "save"} + ,{VN_HANDLER_SHOW_REMOVE, "VN_HANDLER_SHOW_REMOVE", "remove"} + ,{VN_HANDLER_SHOW_ADD, "VN_HANDLER_SHOW_ADD", "add"} + ,{VN_HANDLER_SHOW_SCROLL, "VN_HANDLER_SHOW_SCROLL", "scroll"} + ,{0, NULL, NULL} + }; + + type = g_flags_register_static + (g_intern_static_string ("VnHandlerShowFlags"), values); + } + + return type; +} diff --git a/vn/vn-handler.h b/vn/vn-handler.h new file mode 100644 index 0000000..d11e962 --- /dev/null +++ b/vn/vn-handler.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_HANDLER_H +#define VN_HANDLER_H + +#include +#include + +#define VN_TYPE_HANDLER (vn_handler_get_type ()) +#define VN_IS_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_HANDLER)) +#define VN_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_HANDLER , VnHandler)) +#define VN_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_HANDLER, VnHandlerClass)) +#define VN_HANDLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_HANDLER, VnHandlerClass)) + +#define VN_TYPE_HANDLER_SHOW_FLAGS (vn_handler_show_flags_get_type ()) + +typedef struct _VnHandler VnHandler; +typedef struct _VnHandlerClass VnHandlerClass; + +typedef enum +{ + VN_HANDLER_SHOW_REFRESH = 1 << 0 + ,VN_HANDLER_SHOW_UNDO = 1 << 1 + ,VN_HANDLER_SHOW_SAVE = 1 << 2 + ,VN_HANDLER_SHOW_REMOVE = 1 << 3 + ,VN_HANDLER_SHOW_ADD = 1 << 4 + ,VN_HANDLER_SHOW_SCROLL = 1 << 5 +} +VnHandlerShowFlags; + +struct _VnHandler +{ + GtkHButtonBox parent; + DbIterator * iterator; + VnHandlerShowFlags show_flags; + gboolean simple_record; + GtkToolbar * toolbar; + GtkActionGroup * group; + GtkAction * move_first; + GtkAction * move_previous; + GtkAction * move_next; + GtkAction * move_last; + GtkAction * add; + GtkAction * remove; + GtkAction * save; + GtkAction * undo; +}; + +struct _VnHandlerClass +{ + /* */ + GtkHButtonBoxClass parent; +}; + +GType vn_handler_get_type (); +GType vn_handler_show_flags_get_type () G_GNUC_CONST; + +GtkWidget * vn_handler_new (); +GtkWidget * vn_handler_new_with_iterator (DbIterator * iterator); +DbIterator * vn_handler_get_iterator (VnHandler * obj); +void vn_handler_set_iterator (VnHandler * obj, DbIterator * iterator); +void vn_handler_set_show_flags (VnHandler * obj, VnHandlerShowFlags show_flags); +void vn_handler_set_simple_record (VnHandler * obj, gboolean simple); + +#endif diff --git a/vn/vn-login.c b/vn/vn-login.c new file mode 100644 index 0000000..520597b --- /dev/null +++ b/vn/vn-login.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-login.h" + +#define LOGIN_UI _GUI_DIR"/login.glade" + +#define IS_DEFINED(string) (string && g_strcmp0 (string, "")) + +typedef struct +{ + VnLogin * obj; + gchar * user; + gchar * pass; + DbConn * conn; + GThread * thread; + gboolean connected; + GError * error; +} +ConnectData; + +void vn_login_cb_gui_logout (VnGui * gui, VnLogin * obj); +void vn_login_cb_gui_exit (VnGui * gui, VnLogin * obj); + +G_DEFINE_TYPE (VnLogin, vn_login, G_TYPE_OBJECT); + +/** + * vn_login_new: + * + * Creates a new Gui object. + * + * Return value: the created #VnLogin + **/ +VnLogin * vn_login_new (const gchar * application_id) +{ + return g_object_new (VN_TYPE_LOGIN, "application-id", application_id, NULL); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private + +/* + * Frees the #ConnectData struct + */ +void connect_data_free (ConnectData * connect_data) +{ + if (connect_data->error) + g_error_free (connect_data->error); + + g_free (connect_data->user); + g_free (connect_data->pass); + g_object_unref (connect_data->conn); + g_object_unref (connect_data->obj); + g_thread_unref (connect_data->thread); + g_free (connect_data); +} + +/* + * Shows the login window to the user. + */ +static void vn_login_show (VnLogin * obj) +{ + gboolean autologin = FALSE; + + if (!obj->window) + { + gchar * user; + gchar * pass; + VnBuilder * builder = vn_builder_new (); + + if (gtk_builder_add_from_file (GTK_BUILDER (builder), LOGIN_UI, NULL)) + { + obj->window = vn_builder_get (builder, "window"); + obj->user = vn_builder_get (builder, "user"); + obj->pass = vn_builder_get (builder, "password"); + obj->remember = vn_builder_get (builder, "remember"); + obj->connect = vn_builder_get (builder, "connect"); + obj->settings_button = vn_builder_get (builder, "settings"); + obj->spinner = vn_builder_get (builder, "spinner"); + obj->settings_dialog = vn_builder_get (builder, "settings-dialog"); + obj->plugin = vn_builder_get (builder, "plugin"); + obj->host = vn_builder_get (builder, "host"); + obj->schema = vn_builder_get (builder, "schema"); + obj->ssl_ca = vn_builder_get (builder, "ssl-ca"); + gtk_builder_connect_signals (GTK_BUILDER (builder), obj); + + user = g_settings_get_string (obj->settings, "user"); + pass = g_settings_get_string (obj->settings, "pass"); + + if (user && g_strcmp0 (user, "")) + gtk_entry_set_text (obj->user, user); + + if (pass && g_strcmp0 (pass, "")) + { + gchar * aux = pass; + pass = gvn_decode (aux); + gtk_entry_set_text (obj->pass, pass); + gtk_toggle_button_set_active (obj->remember, TRUE); + g_free (aux); + autologin = TRUE; + } + + g_free (user); + g_free (pass); + } + + g_object_unref (builder); + } + + gtk_widget_show_all (GTK_WIDGET (obj->window)); + gtk_widget_grab_focus (GTK_WIDGET (obj->user)); + gtk_widget_hide (obj->spinner); + + if (autologin) + gtk_button_clicked (obj->connect); +} + +/* + * Enables/disables the #GtkSpinner at login dialog. + */ +static void vn_login_set_loading (VnLogin * obj, gboolean loading) +{ + gtk_widget_set_sensitive (GTK_WIDGET (obj->connect), !loading); + gtk_widget_set_sensitive (GTK_WIDGET (obj->settings_button), !loading); + gtk_widget_set_sensitive (GTK_WIDGET (obj->user), !loading); + gtk_widget_set_sensitive (GTK_WIDGET (obj->pass), !loading); + gtk_widget_set_sensitive (GTK_WIDGET (obj->remember), !loading); + + if (loading) + gtk_widget_show (obj->spinner); + else + gtk_widget_hide (obj->spinner); +} + +/* + * Frees the GUI object. + */ +static void vn_login_free_gui (VnLogin * obj) +{ + if (obj->gui) + { + g_object_disconnect (obj->gui + ,"any_signal", vn_login_cb_gui_exit, obj + ,"any_signal", vn_login_cb_gui_logout, obj + ,NULL + ); + g_clear_object (&obj->gui); + } +} + +/* + * Shows the settings dialog. + */ +void vn_login_cb_settings_clicked (GtkButton * button, VnLogin * obj) +{ + gchar * plugin = g_settings_get_string (obj->settings, "plugin"); + gchar * host = g_settings_get_string (obj->settings, "host"); + gchar * schema = g_settings_get_string (obj->settings, "schema"); + gchar * ssl_ca = g_settings_get_string (obj->settings, "ssl-ca"); + + gtk_entry_set_text (obj->plugin, plugin); + gtk_entry_set_text (obj->host, host); + gtk_entry_set_text (obj->schema, schema); + gtk_entry_set_text (obj->ssl_ca, ssl_ca); + gtk_dialog_run (obj->settings_dialog); + + g_free (plugin); + g_free (host); + g_free (schema); + g_free (ssl_ca); +} + +/* + * Hides the settings dialog. + */ +void vn_login_cb_settings_cancel_clicked (GtkButton * button, VnLogin * obj) +{ + gtk_widget_hide (GTK_WIDGET (obj->settings_dialog)); +} + +/* + * Hides the settings dialog when escape is pressed. + */ +void vn_login_settings_on_delete_event (GtkWidget * settings_dialog) +{ + gtk_widget_hide (settings_dialog); +} + +/* + * Applies the changes made on settings dialog. + */ +void vn_login_cb_settings_apply_clicked (GtkButton * button, VnLogin * obj) +{ + const gchar * plugin = gtk_entry_get_text (obj->plugin); + const gchar * host = gtk_entry_get_text (obj->host); + const gchar * schema = gtk_entry_get_text (obj->schema); + const gchar * ssl_ca = gtk_entry_get_text (obj->ssl_ca); + + g_settings_set_string (obj->settings, "plugin", plugin); + g_settings_set_string (obj->settings, "host", host); + g_settings_set_string (obj->settings, "schema", schema); + g_settings_set_string (obj->settings, "ssl-ca", ssl_ca); + gtk_widget_hide (GTK_WIDGET (obj->settings_dialog)); +} + +/* + * Shows the login dialog when user logout from GUI. + */ +void vn_login_cb_gui_logout (VnGui * gui, VnLogin * obj) +{ + g_settings_set_string (obj->settings, "pass", ""); + vn_login_free_gui (obj); + vn_login_show (obj); +} + +/* + * Destroys the login dialog when user exits from GUI. + */ +void vn_login_cb_gui_exit (VnGui * gui, VnLogin * obj) +{ + vn_login_free_gui (obj); + gtk_widget_destroy (GTK_WIDGET (obj->window)); +} + +/* + * Saves the login information and opens the main GUI. + */ +static gboolean vn_login_done (ConnectData * connect_data) +{ + VnLogin * obj = connect_data->obj; + + if (connect_data->connected) + { + const gchar * user; + + user = gtk_entry_get_text (obj->user); + g_settings_set_string (obj->settings, "user", user); + + if (gtk_toggle_button_get_active (obj->remember)) + { + const gchar * pass = gtk_entry_get_text (obj->pass); + gchar * aux = gvn_encode (pass); + + gtk_toggle_button_set_active (obj->remember, FALSE); + g_settings_set_string (obj->settings, "pass", aux); + g_free (aux); + } + + gtk_entry_set_text (obj->pass, ""); + gtk_widget_hide (GTK_WIDGET (obj->window)); + obj->gui = vn_gui_new (obj->app, connect_data->conn); + g_object_connect (obj->gui + ,"signal::logout", vn_login_cb_gui_logout, obj + ,"signal::exit", vn_login_cb_gui_exit, obj + ,NULL + ); + vn_gui_open (obj->gui); + } + else + { + GtkWidget * dialog; + + dialog = gtk_message_dialog_new (obj->window + ,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT + ,GTK_MESSAGE_ERROR + ,GTK_BUTTONS_OK + ,_("Login error") + ); + gtk_window_set_title (GTK_WINDOW (dialog), _("Login error")); + + if (connect_data->error) + { + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", connect_data->error->message); + + if (connect_data->error->code == DB_CONN_ERROR_BAD_LOGIN) + gtk_entry_set_text (obj->pass, ""); + } + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + vn_login_show (obj); + } + + vn_login_set_loading (obj, FALSE); + return FALSE; +} + +/* + * Thread function that tries to open the connection asynchronously. + */ +static void vn_login_thread (ConnectData * connect_data) +{ + gchar * plugin; + gchar * host; + gchar * schema; + gchar * ssl_ca; + VnLogin * obj = connect_data->obj; + + plugin = g_settings_get_string (obj->settings, "plugin"); + host = g_settings_get_string (obj->settings, "host"); + schema = g_settings_get_string (obj->settings, "schema"); + ssl_ca = g_settings_get_string (obj->settings, "ssl-ca"); + + if (IS_DEFINED (plugin) && IS_DEFINED (host) && IS_DEFINED (schema)) + { + if (db_conn_load_plugin (connect_data->conn, plugin, &connect_data->error)) + { + if (IS_DEFINED (ssl_ca)) + db_conn_set_ssl (connect_data->conn, ssl_ca); + + connect_data->connected = db_conn_open (connect_data->conn + ,host + ,schema + ,connect_data->user + ,connect_data->pass + ,&connect_data->error + ); + } + } + else + connect_data->error = g_error_new ( + VN_LOGIN_LOG_DOMAIN + ,VN_LOGIN_ERR_BAD_SETTINGS + ,_("Bad connection settings, please check it.") + ); + + g_free (plugin); + g_free (host); + g_free (schema); + g_free (ssl_ca); + + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) vn_login_done, connect_data, (GDestroyNotify) connect_data_free); +} + +/* + * Opens the connection. + */ +void vn_login_cb_connect_clicked (GtkButton * button, VnLogin * obj) +{ + ConnectData * connect_data; + + vn_login_set_loading (obj, TRUE); + + connect_data = g_new (ConnectData, 1); + connect_data->obj = g_object_ref (obj); + connect_data->user = g_strdup (gtk_entry_get_text (obj->user)); + connect_data->pass = g_strdup (gtk_entry_get_text (obj->pass)); + connect_data->conn = db_conn_new (); + connect_data->connected = FALSE; + connect_data->error = NULL; + connect_data->thread = g_thread_new ("vn-login", + (GThreadFunc) vn_login_thread, connect_data); +} + +/* + * Closes the application when login window is destroyed. + */ +void vn_login_cb_destroyed (GtkWidget * window, VnLogin * obj) +{ + obj->window = NULL; + gtk_main_quit (); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_login_run: + * @obj: a #VnLogin + * + * Starts the main gui application. + **/ +void vn_login_run (VnLogin * obj) +{ + g_return_if_fail (VN_IS_LOGIN (obj)); + + if (g_application_register (G_APPLICATION (obj->app), NULL, NULL)) + { + g_object_set (gtk_settings_get_default (), + "gtk-button-images", TRUE, NULL + ); + vn_login_show (obj); + gtk_main (); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_APPLICATION_ID = 1 +}; + +static void vn_login_set_property (VnLogin * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_APPLICATION_ID: + { + const gchar * id = g_value_get_string (value); + obj->app = gtk_application_new (id, G_APPLICATION_FLAGS_NONE); + obj->settings = g_settings_new (id); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_login_get_property (VnLogin * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_login_init (VnLogin * obj) +{ + obj->gui = NULL; + obj->window = NULL; + obj->settings = NULL; + obj->app = NULL; +} + +static void vn_login_finalize (VnLogin * obj) +{ + vn_login_free_gui (obj); + g_clear_object (&obj->settings); + g_clear_object (&obj->app); + G_OBJECT_CLASS (vn_login_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_login_class_init (VnLoginClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) vn_login_finalize; + klass->set_property = (GObjectSetPropertyFunc) vn_login_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_login_get_property; + + g_object_class_install_property (klass, PROP_APPLICATION_ID, + g_param_spec_string ("application-id" + ,_("Application id") + ,_("The application identifier") + ,"app.default" + ,G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + )); +} diff --git a/vn/vn-login.h b/vn/vn-login.h new file mode 100644 index 0000000..a40d813 --- /dev/null +++ b/vn/vn-login.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_LOGIN_H +#define VN_LOGIN_H + +#include +#include + +#define VN_LOGIN_LOG_DOMAIN (g_quark_from_string ("VnLogin")) + +#define VN_TYPE_LOGIN (vn_login_get_type ()) +#define VN_LOGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_LOGIN, VnLogin)) +#define VN_IS_LOGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_LOGIN)) +#define VN_LOGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_LOGIN, VnLoginClass)) +#define VN_IS_LOGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_LOGIN)) +#define VN_LOGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_LOGIN, VnLoginClass)) + +typedef struct _VnLogin VnLogin; +typedef struct _VnLoginClass VnLoginClass; + +#include "vn-gui.h" + +struct _VnLogin +{ + GObject parent; + + GtkApplication * app; + VnGui * gui; + GtkWindow * window; + GtkEntry * user; + GtkEntry * pass; + GtkToggleButton * remember; + GtkButton * connect; + GtkButton * settings_button; + GtkWidget * spinner; + + GtkDialog * settings_dialog; + GtkEntry * plugin; + GtkEntry * host; + GtkEntry * schema; + GtkEntry * ssl_ca; + GSettings * settings; +}; + +struct _VnLoginClass +{ + GObjectClass parent; +}; + +typedef enum +{ + VN_LOGIN_ERR_BAD_SETTINGS = 1 +} +VnLoginError; + +GType vn_login_get_type (); +VnLogin * vn_login_new (); +void vn_login_run (VnLogin * obj); + +#endif \ No newline at end of file diff --git a/vn/vn-mod.c b/vn/vn-mod.c new file mode 100644 index 0000000..fe94bd2 --- /dev/null +++ b/vn/vn-mod.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-mod.h" + +struct _VnModPrivate +{ + gchar * name; + gchar * data_dir; + gchar * text_domain; + gchar * ui; + GModule * module; + GtkActionGroup * actions; +}; + +G_DEFINE_TYPE (VnMod, vn_mod, G_TYPE_OBJECT); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public + +/** + * vn_mod_activate: + * @obj: the #VnMod + * + * Activates the module. + * Virtual: activate + **/ +void vn_mod_activate (VnMod * obj) +{ + g_return_if_fail (VN_IS_MOD (obj)); + + VN_MOD_GET_CLASS (obj)->activate (obj); +} + +const gchar * vn_mod_get_name (VnMod * obj) +{ + g_return_val_if_fail (VN_IS_MOD (obj), NULL); + + return obj->priv->name; +} + +const gchar * vn_mod_get_text_domain (VnMod * obj) +{ + g_return_val_if_fail (VN_IS_MOD (obj), NULL); + + return obj->priv->text_domain; +} + +const gchar * vn_mod_get_data_dir (VnMod * obj) +{ + g_return_val_if_fail (VN_IS_MOD (obj), NULL); + + return obj->priv->data_dir; +} + +const gchar * vn_mod_get_ui (VnMod * obj) +{ + g_return_val_if_fail (VN_IS_MOD (obj), NULL); + + return obj->priv->ui; +} + +void vn_mod_set_ui (VnMod * obj, gchar * ui) +{ + g_return_if_fail (VN_IS_MOD (obj)); + + g_free (obj->priv->ui); + obj->priv->ui = ui; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +enum +{ + PROP_NAME = 1 + ,PROP_DATA_DIR + ,PROP_MODULE + ,PROP_TEXT_DOMAIN +}; + +static void vn_mod_set_property (VnMod * obj, guint id, + const GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + obj->priv->name = g_value_dup_string (value); + obj->priv->text_domain = g_strdup_printf ("hedera-%s", obj->priv->name); + bindtextdomain (obj->priv->text_domain, _HEDERA_LOCALE_DIR); + break; + case PROP_DATA_DIR: + obj->priv->data_dir = g_value_dup_string (value); + break; + case PROP_MODULE: + obj->priv->module = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +static void vn_mod_get_property (VnMod * obj, guint id, + GValue * value, GParamSpec * pspec) +{ + switch (id) + { + case PROP_NAME: + g_value_set_string (value, obj->priv->name); + break; + case PROP_DATA_DIR: + g_value_set_string (value, obj->priv->data_dir); + break; + case PROP_MODULE: + g_value_set_pointer (value, obj->priv->module); + break; + case PROP_TEXT_DOMAIN: + g_value_set_string (value, obj->priv->text_domain); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_mod_init (VnMod * obj) +{ + obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, VN_TYPE_MOD, VnModPrivate); + obj->priv->name = NULL; + obj->priv->data_dir = NULL; + obj->priv->module = NULL; + obj->priv->text_domain = NULL; + obj->priv->ui = NULL; + obj->priv->actions = NULL; +} + +static void vn_mod_finalize (VnMod * obj) +{ + g_free (obj->priv->name); + g_free (obj->priv->data_dir); + g_free (obj->priv->text_domain); + g_free (obj->priv->ui); + g_object_unref (obj->priv->actions); + G_OBJECT_CLASS (vn_mod_parent_class)->finalize (G_OBJECT (obj)); +} + +static void vn_mod_class_init (VnModClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->finalize = (GObjectFinalizeFunc) vn_mod_finalize; + klass->set_property = (GObjectSetPropertyFunc) vn_mod_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_mod_get_property; + g_type_class_add_private (klass, sizeof (VnModPrivate)); + + g_object_class_install_property (klass, PROP_NAME, + g_param_spec_string ("name" + ,"Name" + ,"The module name" + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_DATA_DIR, + g_param_spec_string ("data-dir" + ,"Data directory" + ,"The directory in which the data of the module is allocated" + ,NULL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); + g_object_class_install_property (klass, PROP_MODULE, + g_param_spec_pointer ("module" + ,"Module" + ,"The GModule" + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE + )); + g_object_class_install_property (klass, PROP_TEXT_DOMAIN, + g_param_spec_string ("text-domain" + ,"Text domain" + ,"The domain in wich the module is" + ,NULL + ,G_PARAM_READABLE + )); +} diff --git a/vn/vn-mod.h b/vn/vn-mod.h new file mode 100644 index 0000000..e023d96 --- /dev/null +++ b/vn/vn-mod.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_MOD_H +#define VN_MOD_H + +#include "vn-builder.h" + +#define VN_TYPE_MOD (vn_mod_get_type ()) +#define VN_MOD(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, VN_TYPE_MOD, VnMod)) +#define VN_IS_MOD(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, VN_TYPE_MOD)) +#define VN_MOD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, VN_TYPE_MOD, VnModClass)) +#define VN_IS_MOD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (klass, VN_TYPE_MOD)) +#define VN_MOD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (obj, VN_TYPE_MOD, VnModClass)) + +typedef struct _VnMod VnMod; +typedef struct _VnModClass VnModClass; +typedef struct _VnModPrivate VnModPrivate; + +#include "vn-gui.h" + +typedef GType (* VnModGetTypeFunc) (); +typedef void (* VnModActivateFunc) (VnMod * obj); + +struct _VnMod +{ + GObject parent; + VnModPrivate * priv; +}; + + +struct _VnModClass +{ + GObjectClass parent; + void (* activate) (VnMod * obj); +}; + +GType vn_mod_get_type (); +void vn_mod_activate (VnMod * obj); +const gchar * vn_mod_get_name (VnMod * obj); +const gchar * vn_mod_get_text_domain (VnMod * obj); +const gchar * vn_mod_get_data_dir (VnMod * obj); +const gchar * vn_mod_get_ui (VnMod * obj); +void vn_mod_set_ui (VnMod * obj, gchar * ui); + +#endif \ No newline at end of file diff --git a/vn/vn-model.c b/vn/vn-model.c new file mode 100644 index 0000000..ff48c1f --- /dev/null +++ b/vn/vn-model.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vn-model.h" + +/** + * vn_model_new: + * @model: a #DbModel + * + * Creates a new #VnModel with @model as the data model. + * + * Return value: (transfer full): a #VnModel. + **/ +VnModel * vn_model_new (DbModel * model) +{ + return g_object_new (VN_TYPE_MODEL, "model", model, NULL); +} + +/** + * vn_gtk_tree_iter_from_db_iter: + * @dest_iter: the #GtkTreeIter to set using the data on @src_iter + * @src_iter: a valid #DbIter + * + * Sets a #GtkTreeIter with the data of a #DbIter. This function is mostly used + * internally. + **/ +void gtk_tree_iter_from_db_iter (GtkTreeIter * dest_iter, const DbIter * src_iter) +{ + dest_iter->stamp = src_iter->stamp; + dest_iter->user_data = src_iter->data; +} + +/** + * vn_gtk_tree_iter_to_db_iter: + * @src_iter: a valid #GtkTreeIter + * @dest_iter: the #DbIter to set using the data on @src_iter + * + * Sets a with #DbIter the data of a #GtkTreeIter. This function is mostly used + * internally. + **/ +void gtk_tree_iter_to_db_iter (const GtkTreeIter * src_iter, DbIter * dest_iter) +{ + dest_iter->stamp = src_iter->stamp; + dest_iter->data = src_iter->user_data; +} + +/** + * vn_model_iter_is_valid: + * @iter: a #GtkTreeIter + * @model: a #GtkTreeModel + * + * Checks if @iter is a valid #GtkTreeIter pointing inside #GtkTreeModel. + **/ +gboolean vn_model_iter_is_valid (GtkTreeIter * iter, GtkTreeModel * model) +{ + DbIter db_iter; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE); + + gtk_tree_iter_to_db_iter (iter, &db_iter); + return db_model_iter_is_valid (&db_iter, VN_MODEL (model)->model); +} + +static gint vn_model_get_nrows (GtkTreeModel * obj) +{ + return db_model_get_nrows (VN_MODEL (obj)->model); +} + +//++++ Broadcast of GtkTreeModel and GtkTreeSortable signals: + +static void vn_model_on_line_updated (DbModel * model, DbIter * db_iter, GtkTreeModel * obj) +{ + GtkTreeIter iter; + gtk_tree_iter_from_db_iter (&iter, db_iter); + + GtkTreePath * path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, db_model_get_path (model, db_iter)); + + gtk_tree_model_row_changed (obj, path, &iter); + + gtk_tree_path_free (path); +} + +static void vn_model_on_line_inserted (DbModel * model, DbIter * db_iter, GtkTreeModel * obj) +{ + GtkTreeIter iter; + gtk_tree_iter_from_db_iter (&iter, db_iter); + + GtkTreePath * path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, + db_model_get_path (model, db_iter)); + + gtk_tree_model_row_inserted (obj, path, &iter); + + gtk_tree_path_free (path); +} + +static void vn_model_on_line_deleted (DbModel * model, gint position, GtkTreeModel * obj) +{ + GtkTreePath * path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, position); + + if (gtk_tree_path_get_indices (path)) + gtk_tree_model_row_deleted (obj, path); + + gtk_tree_path_free (path); +} + +static void vn_model_on_lines_reordered (DbModel * model, guint col, gint * new_order, GtkTreeModel * obj) +{ + GtkTreePath * path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (obj, path, NULL, new_order); + gtk_tree_path_free (path); +} + +static void vn_model_on_sort_changed (DbModel * model, GtkTreeSortable * obj) +{ + g_signal_emit_by_name (obj, "sort-column-changed"); +} + +//++++ Implementation of GtkTreeModel methods (using DbModel methods) + +static GtkTreeModelFlags vn_model_get_flags (GtkTreeModel * obj) +{ + return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; +} + +static gint vn_model_get_ncols (GtkTreeModel * obj) +{ + return db_model_get_ncols (VN_MODEL (obj)->model); +} + +static GType vn_model_get_column_type (GtkTreeModel * obj, gint index_) +{ + return db_model_get_column_type (VN_MODEL (obj)->model, index_); +} + +static gboolean vn_model_get_iter (GtkTreeModel * obj, GtkTreeIter * iter, + GtkTreePath * path) +{ + DbIter db_iter; + gint p = gtk_tree_path_get_indices (path)[0]; + + gboolean ret = db_model_get_iter (VN_MODEL (obj)->model, &db_iter, p); + + if (ret) + gtk_tree_iter_from_db_iter (iter, &db_iter); + + return ret; +} + +static GtkTreePath * vn_model_get_path (GtkTreeModel * obj, GtkTreeIter * iter) +{ + GtkTreePath * path; + DbIter db_iter; + + gtk_tree_iter_to_db_iter (iter, &db_iter); + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, + db_model_get_path (VN_MODEL (obj)->model, &db_iter)); + + return path; +} + +static void vn_model_get_value (GtkTreeModel * obj, GtkTreeIter * iter, + gint column, GValue * value) +{ + const GValue * v; + DbIter db_iter; + + gtk_tree_iter_to_db_iter (iter, &db_iter); + + v = db_model_get_value (VN_MODEL (obj)->model, &db_iter, column, NULL); + + if (v) + { + g_value_init (value, G_VALUE_TYPE (v)); + g_value_copy (v, value); + } + else + g_value_init (value, GVN_TYPE_NULL); +} + +static gboolean vn_model_iter_next (GtkTreeModel * obj, GtkTreeIter * iter) +{ + DbIter db_iter; + gtk_tree_iter_to_db_iter (iter, &db_iter); + + gboolean ret_val = db_model_iter_next (VN_MODEL (obj)->model, &db_iter); + + iter->user_data = db_iter.data; + + return ret_val; +} + +static gboolean vn_model_iter_previous (GtkTreeModel * obj, GtkTreeIter * iter) +{ + DbIter db_iter; + gtk_tree_iter_to_db_iter (iter, &db_iter); + + gboolean ret_val = db_model_iter_prev (VN_MODEL (obj)->model, &db_iter); + + iter->user_data = db_iter.data; + + return ret_val; +} + +static gboolean vn_model_iter_children (GtkTreeModel * obj, GtkTreeIter * iter, + GtkTreeIter * parent) +{ + if (parent == NULL) + { + DbIter db_iter; + + gboolean ret_val = db_model_get_iter_first (VN_MODEL (obj)->model + ,&db_iter); + + if (ret_val) + gtk_tree_iter_from_db_iter (iter, &db_iter); + + return ret_val; + } + + return FALSE; +} + +static gboolean vn_model_iter_has_child (GtkTreeModel * obj, GtkTreeIter * iter) +{ + return FALSE; +} + +static gint vn_model_iter_n_children (GtkTreeModel * obj, GtkTreeIter * iter) +{ + if (iter == NULL) + return vn_model_get_nrows (obj); + + return 0; +} + +static gboolean vn_model_iter_nth_child (GtkTreeModel * obj, GtkTreeIter * iter + ,GtkTreeIter * parent, gint n) +{ + if (parent == NULL) + { + DbIter db_iter; + gboolean ret = db_model_get_iter (VN_MODEL (obj)->model, &db_iter, n); + + if (ret) + gtk_tree_iter_from_db_iter (iter, &db_iter); + } + + return FALSE; +} + +static gboolean vn_model_iter_parent (GtkTreeModel * obj, GtkTreeIter * iter, + GtkTreeIter * child) +{ + return FALSE; +} + +//++++ Implementation of GtkTreeSortable methods (using DbModel methods) + +static gboolean vn_model_get_sort_column_id (GtkTreeSortable * obj, + gint * sort_column_id, + GtkSortType * order) +{ + return db_model_get_sort_column_id (VN_MODEL (obj)->model + ,sort_column_id, (DbSortType *) order); +} + +static void vn_model_set_sort_column_id (GtkTreeSortable * obj, gint sort_column_id, + GtkSortType order) +{ + DbSortType db_order; + + if (order == GTK_SORT_ASCENDING) + db_order = DB_SORT_ASCENDING; + else + db_order = DB_SORT_DESCENDING; + + db_model_set_sort_column_id (VN_MODEL (obj)->model, sort_column_id, db_order); +} + +static void vn_model_set_sort_func (GtkTreeSortable *sortable, gint sort_column_id, + GtkTreeIterCompareFunc sort_func, gpointer user_data, GDestroyNotify destroy) +{ + g_log (g_quark_to_string (VN_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, _("Function vn_model_set_sort_func not implemented")); +} + +static void vn_model_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc sort_func, gpointer user_data, GDestroyNotify destroy) +{ + g_log (g_quark_to_string (VN_MODEL_LOG_DOMAIN) + ,G_LOG_LEVEL_WARNING, _("Function vn_model_set_default_sort_func not implemented")); +} + +static gboolean vn_model_has_default_sort_func (GtkTreeSortable * obj) +{ + return TRUE; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Properties + +typedef enum +{ + PROP_MODEL = 1 +} +VnModelProp; + +static void vn_model_set_property (VnModel * obj, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_MODEL: + obj->model = g_value_get_object (value); + g_object_ref_sink (obj->model); + g_object_connect (obj->model + ,"signal-after::line-updated", vn_model_on_line_updated, obj + ,"signal::line-inserted", vn_model_on_line_inserted, obj + ,"signal-after::line-deleted", vn_model_on_line_deleted, obj + ,"signal::lines-reordered", vn_model_on_lines_reordered, obj + ,"signal::sort-changed", vn_model_on_sort_changed, obj, NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +static void vn_model_get_property (VnModel * obj, guint property_id, + GValue * value, GParamSpec * pspec) +{ + switch (property_id) + { + case PROP_MODEL: + g_value_set_object (value, obj->model); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + } +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Class + +static void vn_model_finalize (VnModel * obj) +{ + GObjectClass * parent; + parent = g_type_class_peek_parent (VN_MODEL_GET_CLASS (obj)); + + g_object_disconnect (obj->model + ,"any_signal", vn_model_on_line_updated, obj + ,"any_signal", vn_model_on_line_inserted, obj + ,"any_signal", vn_model_on_line_deleted, obj + ,"any_signal", vn_model_on_lines_reordered, obj + ,"any_signal", vn_model_on_sort_changed, obj, NULL); + + g_object_unref (obj->model); + + parent->finalize (G_OBJECT (obj)); +} + +static void vn_model_class_init (VnModelClass * k) +{ + GObjectClass * klass = G_OBJECT_CLASS (k); + klass->set_property = (GObjectSetPropertyFunc) vn_model_set_property; + klass->get_property = (GObjectGetPropertyFunc) vn_model_get_property; + klass->finalize = (GObjectFinalizeFunc) vn_model_finalize; + + g_object_class_install_property (klass, PROP_MODEL, + g_param_spec_object ("model" + ,"Model" + ,"The #DbModel with the data used by the #VnModel" + ,DB_TYPE_MODEL + ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE + )); +} + +// tienen que implementarse con métodos heredados de db-model +static void vn_model_tree_model_interface_init (GtkTreeModelIface * iface) +{ + iface->get_flags = vn_model_get_flags; + iface->get_n_columns = vn_model_get_ncols; + iface->get_column_type = vn_model_get_column_type; + iface->get_iter = vn_model_get_iter; + iface->get_path = vn_model_get_path; + iface->get_value = vn_model_get_value; + iface->iter_next = vn_model_iter_next; + iface->iter_previous = vn_model_iter_previous; + + iface->iter_children = vn_model_iter_children; + iface->iter_has_child = vn_model_iter_has_child; + iface->iter_n_children = vn_model_iter_n_children; + iface->iter_nth_child = vn_model_iter_nth_child; + iface->iter_parent = vn_model_iter_parent; +} + +static void vn_model_tree_sortable_interface_init (GtkTreeSortableIface * iface) +{ + iface->get_sort_column_id = vn_model_get_sort_column_id; + iface->set_sort_column_id = vn_model_set_sort_column_id; + iface->set_sort_func = vn_model_set_sort_func; + iface->set_default_sort_func = vn_model_set_default_sort_func; + iface->has_default_sort_func = vn_model_has_default_sort_func; + +} + +static void vn_model_init (VnModel * obj) +{ + obj->model = NULL; +} + +G_DEFINE_TYPE_WITH_CODE (VnModel, vn_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + vn_model_tree_model_interface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, + vn_model_tree_sortable_interface_init) +); diff --git a/vn/vn-model.h b/vn/vn-model.h new file mode 100644 index 0000000..1783fb8 --- /dev/null +++ b/vn/vn-model.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_MODEL_H +#define VN_MODEL_H + +#include +#include +#include + +#define VN_TYPE_MODEL (vn_model_get_type()) +#define VN_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VN_TYPE_MODEL, VnModel)) +#define VN_IS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VN_TYPE_MODEL)) +#define VN_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VN_TYPE_MODEL, VnModelClass)) +#define VN_IS_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VN_TYPE_MODEL)) +#define VN_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VN_TYPE_MODEL, VnModelClass)) + +#define VN_MODEL_LOG_DOMAIN (g_quark_from_string ("VnModel")) + +#define gtk_tree_iter_from_db_iter(d,s) vn_gtk_tree_iter_from_db_iter(d,s) +#define gtk_tree_iter_to_db_iter(s,d) vn_gtk_tree_iter_to_db_iter(s,d) + +typedef struct _VnModel VnModel; +typedef struct _VnModelClass VnModelClass; + +struct _VnModel +{ + GObject parent; + DbModel * model; +}; + +struct _VnModelClass +{ + /* */ + GObjectClass parent; +}; + +GType vn_model_get_type (); +VnModel * vn_model_new (DbModel * model); +gboolean vn_model_iter_is_valid (GtkTreeIter * iter, GtkTreeModel * model); +void vn_gtk_tree_iter_from_db_iter (GtkTreeIter * dest_iter + ,const DbIter * src_iter); +void vn_gtk_tree_iter_to_db_iter (const GtkTreeIter * src_iter + ,DbIter * dest_iter); + +#endif \ No newline at end of file diff --git a/vn/vn.h b/vn/vn.h new file mode 100644 index 0000000..0eb78e1 --- /dev/null +++ b/vn/vn.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 - Juan Ferrer Toribio + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VN_H +#define VN_H + +#include +#include +#include "vn-model.h" +#include "vn-handler.h" +#include "vn-grid.h" +#include "vn-builder.h" +#include "vn-login.h" +#include "vn-gui.h" +#include "vn-mod.h" +#include "vn-form.h" +#include "vn-batch.h" +#include "vn-field.h" +#include "vn-column.h" +#include "field/field.h" +#include "column/column.h" + +#endif diff --git a/vn/vn.pc.in b/vn/vn.pc.in new file mode 100644 index 0000000..3b93c7b --- /dev/null +++ b/vn/vn.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@/hedera +includedir=@includedir@/hedera + +Name: Vn +Description: Graphical UI Module for Hedera Library +Version: @VERSION@ +Requires: db sql gvn gtk+-3.0 +Libs: -L${libdir} -lvn -lvnfield -lvncolumn +Cflags: -I${includedir}/vn