/* * 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}; /** * VnFieldStyleFunc: * @obj: a #VnField * @widget: the #GtkWidget held by the field * @value: (transfer none): the current value in the cell * * Function used to modify the field depending on @value. **/ //+++++++++++++++++++++++++++++++++++++++++++++++++++ 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); } if (obj->style_func) obj->style_func (obj, obj->field, obj->value); 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 ,PROP_STYLE_FUNC }; 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; case PROP_STYLE_FUNC: obj->style_func = g_value_get_pointer (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; case PROP_STYLE_FUNC: g_value_set_pointer (value, obj->style_func); 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->style_func = 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); vn_field_set_param (obj, NULL); 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 )); g_object_class_install_property (k, PROP_STYLE_FUNC, g_param_spec_pointer ("style-func" ,_("Style function") ,_("A VnFieldStyleFunc to set the properties of each field " "depending on its value") ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE )); }