/* * 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-list-model.h" static void vn_column_combo_model_holder_init (DbModelHolderInterface * iface); G_DEFINE_TYPE_WITH_CODE (VnColumnCombo, vn_column_combo, VN_TYPE_COLUMN, G_IMPLEMENT_INTERFACE (DB_TYPE_MODEL_HOLDER, vn_column_combo_model_holder_init) ); VnColumn * vn_column_combo_new () { return g_object_new (VN_TYPE_COLUMN_COMBO, NULL); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Private static void vn_column_combo_set_value (VnColumnCombo * self, GtkTreeModel * model, GtkTreeIter * tree_iter, GObject * cell, const GValue * value) { if (self->model && db_model_get_status (self->model) == DB_MODEL_STATUS_READY) { DbIter iter; if (gvn_value_is_null (value)) g_object_set_property (cell, "text", value); else if (db_model_search_value (self->model, self->index_column, &iter, value)) g_object_set_property (cell, "text", db_model_get_value (self->model, &iter, self->show_column, NULL)); } } /* * Restores the focus to the tree view after its edition. */ static void vn_column_combo_restore_focus (VnColumnCombo * self) { gtk_widget_grab_focus (gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (self))); } /* * Sets the value of the entry to an empty string if a null value is selected. */ static gchar * vn_column_combo_on_format_entry_text (GtkComboBox * combo, gchar * path, VnColumnCombo * self) { gchar * str; GtkTreeIter iter; gtk_tree_model_get_iter_from_string (self->tree, &iter, path); gtk_tree_model_get (self->tree, &iter, self->show_column, &str, -1); return str ? str : g_strdup (""); } /* * Sets the autocompletion functionality to the cell being edited. */ static void vn_column_combo_on_editing_started (GtkCellRenderer * cell, GtkCellEditable * cell_editable, const gchar * path, VnColumnCombo * self) { GtkEntry * entry; if (!GTK_IS_COMBO_BOX (cell_editable) || !self->tree) return; entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (cell_editable))); // Each edition uses a new cell_editable. if (self->cell_editable) { g_signal_handlers_disconnect_by_func (self->cell_editable, vn_column_combo_on_format_entry_text, self); g_object_unref (self->cell_editable); } if (!self->completion_ready) { gtk_entry_completion_set_model (self->completion, self->tree); gtk_entry_completion_set_text_column (self->completion, self->show_column); gtk_entry_completion_set_minimum_key_length (self->completion, 1); self->completion_ready = TRUE; g_signal_connect_swapped (cell_editable, "editing-done", G_CALLBACK (vn_column_combo_restore_focus), self); self->cell_editable = g_object_ref (cell_editable); } gtk_entry_set_completion (entry, self->completion); g_signal_connect (cell_editable, "format-entry-text", G_CALLBACK (vn_column_combo_on_format_entry_text), self); self->cell_editable = g_object_ref (cell_editable); } /* * When the text is edited using an entry, tries to set its underlying value. */ static void vn_column_combo_on_edited (GtkCellRendererText * renderer, gchar * path, gchar * new_text, VnColumnCombo * self) { DbIter iter; GValue value = G_VALUE_INIT; if (!g_strcmp0 (new_text, "")) { g_value_init (&value, GVN_TYPE_NULL); VN_COLUMN_GET_CLASS (self)->value_changed (VN_COLUMN (self), path, &value); } else { gvn_value_new_with_content (&value, G_TYPE_STRING, new_text); if (db_model_search_value (self->model, self->show_column, &iter, &value)) { const GValue * val; if ((val = db_model_get_value (self->model, &iter, self->index_column, NULL))) VN_COLUMN_GET_CLASS (self)->value_changed (VN_COLUMN (self), path, val); } } g_value_unset (&value); vn_column_combo_restore_focus (self); } static void vn_column_combo_set_editable (VnColumnCombo * self, gboolean editable) { VnColumn * parent = VN_COLUMN (self); if (editable) { g_signal_connect (parent->cell, "editing-started", G_CALLBACK (vn_column_combo_on_editing_started), self); g_signal_connect (parent->cell, "edited", G_CALLBACK (vn_column_combo_on_edited), self); g_signal_connect_swapped (parent->cell, "editing-canceled", G_CALLBACK (vn_column_combo_restore_focus), self); if (!self->completion) self->completion = gtk_entry_completion_new (); } else { g_signal_handlers_disconnect_by_data (parent->cell, self); if (self->cell_editable) g_signal_handlers_disconnect_by_data (self->cell_editable, self); if (self->completion) g_clear_object (&self->completion); self->completion_ready = FALSE; self->cell_editable = NULL; } g_object_set (parent->cell, "editable", editable, NULL); } static void vn_column_combo_set_null (VnColumnCombo * self, gboolean null) { if (vn_column_get_model (VN_COLUMN (self)) && self->model) vn_list_model_use_null_row (VN_LIST_MODEL (self->tree), null); } //+++++++++++++++++++++++++++++++++++++++++++++++++ DbModelHolder implementation static void vn_column_combo_model_changed (VnColumnCombo * self) { if (self->model) { vn_list_model_use_null_row (VN_LIST_MODEL (self->tree), VN_COLUMN (self)->null); if (self->sort_column >= 0) gtk_tree_view_column_set_sort_column_id ( GTK_TREE_VIEW_COLUMN (self), self->sort_column); } } static void vn_column_combo_on_status_changed (DbModel * model, DbModelStatus status, VnColumnCombo * self) { if (status == DB_MODEL_STATUS_READY) { g_object_set (VN_COLUMN (self)->cell, "model", self->tree, NULL); vn_list_model_use_null_row (VN_LIST_MODEL (self->tree), VN_COLUMN (self)->null); } else g_object_set (VN_COLUMN (self)->cell, "model", NULL, NULL); } static void vn_column_combo_set_model (VnColumnCombo * self, DbModel * model) { g_return_if_fail (!model || DB_IS_MODEL (model)); if (self->model) { g_signal_handlers_disconnect_by_func (self->model, vn_column_combo_on_status_changed, self); g_clear_object (&self->model); g_clear_object (&self->tree); if (self->completion_ready) gtk_entry_completion_set_model (self->completion, NULL); } if (model) { self->model = g_object_ref (model); self->tree = GTK_TREE_MODEL (vn_list_model_new (model)); g_signal_connect (model, "status-changed", G_CALLBACK (vn_column_combo_on_status_changed), self); if (self->completion_ready) gtk_entry_completion_set_model (self->completion, self->tree); } } static DbModel * vn_column_combo_get_model (VnColumnCombo * self) { return self->model; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties enum { PROP_INDEX_COLUMN = 1 ,PROP_SHOW_COLUMN ,PROP_SORT_COLUMN ,PROP_MODEL }; static void vn_column_combo_set_property (VnColumnCombo * self, guint id, const GValue * value, GParamSpec * pspec) { switch (id) { case PROP_INDEX_COLUMN: self->index_column = g_value_get_uint (value); break; case PROP_SHOW_COLUMN: self->show_column = g_value_get_uint (value); g_object_set (VN_COLUMN (self)->cell, "text-column", self->show_column, NULL); break; case PROP_SORT_COLUMN: self->sort_column = g_value_get_int (value); break; case PROP_MODEL: vn_column_combo_set_model (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec); } } static void vn_column_combo_get_property (VnColumnCombo * self, guint id, GValue * value, GParamSpec * pspec) { switch (id) { case PROP_INDEX_COLUMN: g_value_set_uint (value, self->index_column); break; case PROP_SHOW_COLUMN: g_value_set_uint (value, self->show_column); break; case PROP_SORT_COLUMN: g_value_set_int (value, self->sort_column); break; case PROP_MODEL: g_value_set_object (value, self->model); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec); } } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Class static void vn_column_combo_init (VnColumnCombo * self) { GtkCellRenderer * cell = gtk_cell_renderer_combo_new (); self->model = NULL; self->tree = NULL; self->completion = NULL; self->completion_ready = FALSE; self->cell_editable = NULL; VN_COLUMN_GET_CLASS (self)->set_renderer (VN_COLUMN (self), cell); } static void vn_column_combo_finalize (VnColumnCombo * self) { vn_column_combo_set_model (self, NULL); if (self->completion) g_object_unref (self->completion); G_OBJECT_CLASS (vn_column_combo_parent_class)->finalize (G_OBJECT (self)); } 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; VN_COLUMN_CLASS (klass)->set_null = (VnColumnSetNullFunc) vn_column_combo_set_null; VN_COLUMN_CLASS (klass)->model_changed = (VnColumnModelChangedFunc) vn_column_combo_model_changed; g_object_class_override_property (k, PROP_MODEL, "data-model"); 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_SORT_COLUMN, g_param_spec_int ("sort-column" ,_("Sort column") ,_("The field number of the Grid model used to sort the column") ,-1, 255, -1 ,G_PARAM_CONSTRUCT | G_PARAM_READWRITE )); } static void vn_column_combo_model_holder_init (DbModelHolderInterface * iface) { iface->get_model = (DbModelHolderGetModelFunc) vn_column_combo_get_model; iface->set_model = (DbModelHolderSetModelFunc) vn_column_combo_set_model; }