/* * 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 )); }