/* * 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" static void vn_field_param_init (GvnParamInterface * iface); static void vn_field_buildable_init (GtkBuildableIface * iface); static void vn_field_on_master_value_changed (GvnParam * master, const GValue * value, VnField * self); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (VnField, vn_field, GTK_TYPE_EVENT_BOX, G_IMPLEMENT_INTERFACE (GVN_TYPE_PARAM, vn_field_param_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, vn_field_buildable_init) ); /** * VnFieldStyleFunc: * @self: 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_set_invalid (VnField * self, const GError * err) { /* GdkRGBA color = {0.95, 0.8, 0.8, 1}; gtk_widget_override_background_color (GTK_WIDGET (self->widget), GTK_STATE_FLAG_NORMAL, &color); */ } static void vn_field_update_widget (VnField * self) { self->lock_changed_done = TRUE; if (VN_FIELD_GET_CLASS (self)->set_value) VN_FIELD_GET_CLASS (self)->set_value (self, self->value); self->lock_changed_done = FALSE; } static void vn_field_changed (VnField * self) { g_signal_emit_by_name (self, "value-changed", self->value); if (self->master && self->change_master) { GError * err = NULL; self->change_master = FALSE; if (!gvn_param_request_value (self->master, self->value, &err)) { vn_field_set_invalid (self, err); g_error_free (err); } self->change_master = TRUE; } if (self->style_func) self->style_func (self, self->widget, self->value); } static void vn_field_value_changed (VnField * self, const GValue * value) { GType type; gboolean changed; if (self->lock_changed_done) return; type = gvn_param_spec_get_gtype (self->spec); if (type != G_TYPE_NONE && !gvn_value_is_null (value)) { if (G_VALUE_TYPE (self->value) != type) { g_value_unset (self->value); g_value_init (self->value, type); } g_value_transform (value, self->value); changed = TRUE; } else changed = gvn_value_ccopy (value, self->value); if (changed) vn_field_changed (self); } static void vn_field_spec_changed (VnField * self) { gvn_param_spec_unset (self->spec); gvn_param_spec_merge (self->spec, self->user_spec); if (self->master) gvn_param_spec_merge (self->spec, gvn_param_get_spec (self->master)); g_signal_emit_by_name (self, "spec-changed"); } static void vn_field_set_value (VnField * self, const GValue * value) { if (!gvn_value_ccopy (value, self->value)) return; vn_field_update_widget (self); vn_field_changed (self); } static const GValue * vn_field_get_value (VnField * self) { return self->value; } static gboolean vn_field_request_value (VnField * self, const GValue * value, GError ** err) { if (gvn_param_spec_validate (self->spec, value, err)) { vn_field_set_value (self, value); return TRUE; } return FALSE; } static void vn_field_on_master_value_changed (GvnParam * master, const GValue * value, VnField * self) { if (self->change_master) { self->change_master = FALSE; vn_field_set_value (self, value); self->change_master = TRUE; } } static void vn_field_on_master_spec_changed (GvnParam * master, VnField * self) { vn_field_spec_changed (self); } static void vn_field_on_master_status_changed (GvnParam * master, VnField * self) { switch (gvn_param_get_status (master)) { case GVN_PARAM_STATUS_OK: gtk_widget_set_sensitive (self->widget, TRUE); break; case GVN_PARAM_STATUS_BUSY: gtk_widget_set_sensitive (self->widget, FALSE); break; case GVN_PARAM_STATUS_ERROR: // vn_field_set_invalid (self, gvn_param_get_error (master)); break; } } static GvnParam * vn_field_get_master (VnField * self) { return self->master; } void vn_field_set_master (VnField * self, GvnParam * master) { if (self->master) { g_object_disconnect (self->master ,"any_signal", vn_field_on_master_value_changed, self ,"any_signal", vn_field_on_master_spec_changed, self ,"any_signal", vn_field_on_master_status_changed, self ,NULL ); g_clear_object (&self->master); } if (master) { self->master = g_object_ref_sink (master); g_object_connect (master ,"signal::value-changed", vn_field_on_master_value_changed, self ,"signal::spec-changed", vn_field_on_master_spec_changed, self ,"signal::status-changed", vn_field_on_master_status_changed, self ,NULL ); vn_field_on_master_spec_changed (master, self); vn_field_on_master_value_changed (master, gvn_param_get_value (master), self); vn_field_on_master_status_changed (master, self); } /* 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 (self->widget)); 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); */ } static const GvnParamSpec * vn_field_get_spec (VnField * self) { return self->spec; } static GvnParamStatus vn_field_get_status (VnField * self) { return GVN_PARAM_STATUS_OK; } static void vn_field_make_param (VnField * self) { GvnParam * param; if (!self->iterator || !self->column_name) return; param = db_iterator_get_param (self->iterator, self->column_name); vn_field_set_master (self, param); } static void vn_field_set_widget (VnField * self, GtkWidget * widget) { self->widget = widget; vn_field_update_widget (self); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Public /** * vn_field_get_gtype: * @self: the #VnField * * Gets the field type. * * Return value: the field type or %G_TYPE_NONE if it havent. **/ GType vn_field_get_gtype (VnField * self) { g_return_val_if_fail (VN_IS_FIELD (self), G_TYPE_INVALID); return gvn_param_spec_get_gtype (self->spec); } /** * vn_field_set_gtype: * @self: 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 * self, GType type) { g_return_if_fail (VN_IS_FIELD (self)); gvn_param_spec_set_gtype (self->user_spec, type); vn_field_spec_changed (self); } /** * vn_field_get_null: * @self: 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 * self) { g_return_val_if_fail (VN_IS_FIELD (self), FALSE); return gvn_param_spec_get_null (self->spec); } /** * vn_field_set_null: * @self: 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 * self, gboolean null) { g_return_if_fail (VN_IS_FIELD (self)); gvn_param_spec_set_null (self->user_spec, null); vn_field_spec_changed (self); } /** * vn_field_get_editable: * @self: 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 * self) { g_return_val_if_fail (VN_IS_FIELD (self), FALSE); return gvn_param_spec_get_editable (self->spec); } /** * vn_field_set_editable: * @self: 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 * self, gboolean editable) { g_return_if_fail (VN_IS_FIELD (self)); gvn_param_spec_set_editable (self->user_spec, editable); vn_field_spec_changed (self); } /** * vn_field_get_default: * @self: the #VnField **/ const GValue * vn_field_get_default (VnField * self) { g_return_val_if_fail (VN_IS_FIELD (self), NULL); return gvn_param_spec_get_default (self->spec); } /** * vn_field_set_default: * @self: the #VnField * @def: the default #GValue * * Sets the default value of the field. **/ void vn_field_set_default (VnField * self, const GValue * def) { g_return_if_fail (VN_IS_FIELD (self)); gvn_param_spec_set_default (self->user_spec, def); vn_field_spec_changed (self); } /** * vn_field_set_to_default: * @self: the #VnField **/ void vn_field_set_to_default (VnField * self) { g_return_if_fail (VN_IS_FIELD (self)); vn_field_set_value (self, gvn_param_spec_get_default (self->spec)); } /** * vn_field_get_widget: * @self: the #VnField * * Gets the internal widget of @self. * * Return value: (transfer none): the widget **/ GtkWidget * vn_field_get_widget (VnField * self) { g_return_if_fail (VN_IS_FIELD (self)); return self->widget; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Buildable static GtkBuildableIface *parent_buildable_iface; static GObject * vn_field_buildable_get_internal_child (GtkBuildable * buildable, GtkBuilder * builder, const gchar * childname) { if (!g_strcmp0 (childname, "widget")) return G_OBJECT (vn_field_get_widget (VN_FIELD (buildable))); return parent_buildable_iface->get_internal_child (buildable, builder, childname); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties enum { PROP_VALUE = 1 ,PROP_MASTER ,PROP_ITERATOR ,PROP_COLUMN_NAME ,PROP_GTYPE ,PROP_EDITABLE ,PROP_NULL ,PROP_DEFAULT ,PROP_STYLE_FUNC }; static void vn_field_set_property (VnField * self, guint id, const GValue * value, GParamSpec * pspec) { switch (id) { case PROP_VALUE: vn_field_set_value (self, g_value_get_boxed (value)); break; case PROP_MASTER: vn_field_set_master (self, g_value_get_object (value)); break; case PROP_ITERATOR: g_clear_object (&self->iterator); self->iterator = g_value_dup_object (value); vn_field_make_param (self); break; case PROP_COLUMN_NAME: g_free (self->column_name); self->column_name = g_value_dup_string (value); vn_field_make_param (self); break; case PROP_GTYPE: vn_field_set_gtype (self, g_value_get_gtype (value)); break; case PROP_EDITABLE: vn_field_set_editable (self, g_value_get_boolean (value)); break; case PROP_NULL: vn_field_set_null (self, g_value_get_boolean (value)); break; case PROP_DEFAULT: vn_field_set_default (self, g_value_get_boxed (value)); break; case PROP_STYLE_FUNC: self->style_func = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec); } } static void vn_field_get_property (VnField * self, guint id, GValue * value, GParamSpec * pspec) { switch (id) { case PROP_VALUE: g_value_set_boxed (value, self->value); break; case PROP_MASTER: g_value_set_object (value, self->master); break; case PROP_ITERATOR: g_value_set_object (value, self->iterator); break; case PROP_COLUMN_NAME: g_value_set_string (value, self->column_name); break; case PROP_GTYPE: g_value_set_gtype (value, vn_field_get_gtype (self)); break; case PROP_EDITABLE: g_value_set_boolean (value, vn_field_get_editable (self)); break; case PROP_NULL: g_value_set_boolean (value, vn_field_get_null (self)); break; case PROP_DEFAULT: g_value_set_boxed (value, vn_field_get_default (self)); break; case PROP_STYLE_FUNC: g_value_set_pointer (value, self->style_func); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec); } } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Class static void vn_field_init (VnField * self) { GdkRGBA color = {0.0, 0.0, 0.0, 0.0}; self->master = NULL; self->change_master = TRUE; self->lock_changed_done = FALSE; self->user_spec = gvn_param_spec_new (); self->spec = gvn_param_spec_new (); self->iterator = NULL; self->column_name = NULL; self->style_func = NULL; self->value = g_new0 (GValue, 1); g_value_init (self->value, GVN_TYPE_NULL); gtk_widget_override_background_color (GTK_WIDGET (self), GTK_STATE_FLAG_NORMAL, &color); } static void vn_field_finalize (VnField * self) { gvn_param_spec_free (self->spec); gvn_param_spec_free (self->user_spec); g_clear_object (&self->iterator); vn_field_set_master (self, NULL); g_value_unset (self->value); g_free (self->value); g_free (self->column_name); G_OBJECT_CLASS (vn_field_parent_class)->finalize (G_OBJECT (self)); } 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; klass->set_widget = vn_field_set_widget; g_object_class_override_property (k, PROP_VALUE, "value"); g_object_class_override_property (k, PROP_MASTER, "master"); 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 )); } static void vn_field_buildable_init (GtkBuildableIface * iface) { parent_buildable_iface = g_type_interface_peek_parent (iface); iface->get_internal_child = vn_field_buildable_get_internal_child; } static void vn_field_param_init (GvnParamInterface * iface) { iface->get_value = (GvnParamGetValueFunc) vn_field_get_value; iface->request_value = (GvnParamRequestValueFunc) vn_field_request_value; iface->get_master = (GvnParamGetMasterFunc) vn_field_get_master; iface->set_master = (GvnParamSetMasterFunc) vn_field_set_master; iface->get_spec = (GvnParamGetSpecFunc) vn_field_get_spec; iface->get_status = (GvnParamGetStatusFunc) vn_field_get_status; }