640 lines
16 KiB
C
640 lines
16 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#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);
|
|
gtk_widget_show (widget);
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++ 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_dispose (VnField * self)
|
|
{
|
|
vn_field_set_master (self, NULL);
|
|
G_OBJECT_CLASS (vn_field_parent_class)->dispose (G_OBJECT (self));
|
|
}
|
|
|
|
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);
|
|
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->dispose = (GObjectFinalizeFunc) vn_field_dispose;
|
|
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;
|
|
}
|