/* * Copyright (C) 2008 Juan Pablo Ugarte. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Authors: * Juan Pablo Ugarte * Tristan Van Berkom */ #include #include #include #include #include #include #include "glade-column-types.h" #include "glade-model-data.h" enum { COLUMN_NAME, COLUMN_COLUMN_NAME, COLUMN_TYPE_EDITABLE, COLUMN_NAME_EDITABLE, COLUMN_TYPE_FOREGROUND, COLUMN_TYPE_STYLE, N_COLUMNS }; static GtkTreeModel *types_model = NULL; static gint find_by_type_name (const gchar * a, const gchar * b) { return strcmp (a, b); } static void column_types_store_populate_enums_flags (GtkListStore * store, gboolean enums) { GtkTreeIter iter; GList *types = NULL, *list; GList *adaptors = glade_widget_adaptor_list_adaptors (); const GList *l; for (list = adaptors; list; list = list->next) { GladeWidgetAdaptor *adaptor = list->data; GladePropertyClass *pclass; GParamSpec *pspec; for (l = glade_widget_adaptor_get_properties (adaptor); l; l = l->next) { pclass = l->data; pspec = glade_property_class_get_pspec (pclass); /* special case out a few of these... */ if (strcmp (g_type_name (pspec->value_type), "GladeStock") == 0 || strcmp (g_type_name (pspec->value_type), "GladeStockImage") == 0 || strcmp (g_type_name (pspec->value_type), "GladeGtkImageType") == 0 || strcmp (g_type_name (pspec->value_type), "GladeGtkButtonType") == 0 || strcmp (g_type_name (pspec->value_type), "GladeGnomeDruidPagePosition") == 0 || strcmp (g_type_name (pspec->value_type), "GladeGnomeIconListSelectionMode") == 0 || strcmp (g_type_name (pspec->value_type), "GladeGnomeMessageBoxType") == 0) continue; if ((enums ? G_TYPE_IS_ENUM (pspec->value_type) : G_TYPE_IS_FLAGS (pspec->value_type)) && !g_list_find_custom (types, g_type_name (pspec->value_type), (GCompareFunc) find_by_type_name)) types = g_list_prepend (types, g_strdup (g_type_name (pspec->value_type))); } } g_list_free (adaptors); types = g_list_sort (types, (GCompareFunc) find_by_type_name); for (l = types; l; l = l->next) { gchar *type_name = l->data; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COLUMN_NAME, type_name, -1); g_free (type_name); } g_list_free (types); } static void column_types_store_populate (GtkListStore * store) { GtkTreeIter iter; gint i; GType types[] = { G_TYPE_CHAR, G_TYPE_UCHAR, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_UINT, G_TYPE_LONG, G_TYPE_ULONG, G_TYPE_INT64, G_TYPE_UINT64, G_TYPE_FLOAT, G_TYPE_DOUBLE, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_OBJECT, GDK_TYPE_PIXBUF }; for (i = 0; i < sizeof (types) / sizeof (GType); i++) { gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COLUMN_NAME, g_type_name (types[i]), -1); } column_types_store_populate_enums_flags (store, TRUE); column_types_store_populate_enums_flags (store, FALSE); } GList * glade_column_list_copy (GList * list) { GList *l, *retval = NULL; for (l = list; l; l = g_list_next (l)) { GladeColumnType *data = l->data; GladeColumnType *new_data = glade_column_type_new (data->type_name, data->column_name); retval = g_list_prepend (retval, new_data); } return g_list_reverse (retval); } GladeColumnType * glade_column_type_new (const gchar * type_name, const gchar * column_name) { GladeColumnType *column = g_slice_new0 (GladeColumnType); column->type_name = g_strdup (type_name); column->column_name = g_strdup (column_name); return column; } void glade_column_type_free (GladeColumnType * column) { g_free (column->type_name); g_free (column->column_name); g_slice_free (GladeColumnType, column); } void glade_column_list_free (GList * list) { g_list_foreach (list, (GFunc) glade_column_type_free, NULL); g_list_free (list); } GladeColumnType * glade_column_list_find_column (GList * list, const gchar * column_name) { GladeColumnType *column = NULL, *iter; GList *l; for (l = g_list_first (list); l; l = l->next) { iter = l->data; if (strcmp (column_name, iter->column_name) == 0) { column = iter; break; } } return column; } GType glade_column_type_list_get_type (void) { static GType type_id = 0; if (!type_id) { type_id = g_boxed_type_register_static ("GladeColumnTypeList", (GBoxedCopyFunc) glade_column_list_copy, (GBoxedFreeFunc) glade_column_list_free); } return type_id; } /**************************** GladeEditorProperty *****************************/ typedef struct { GladeEditorProperty parent_instance; GtkListStore *store; GtkTreeView *view; GtkTreeSelection *selection; GladeNameContext *context; gboolean adding_column; gboolean want_focus; gboolean setting_cursor; GtkTreeViewColumn *name_column; GtkTreeViewColumn *type_column; } GladeEPropColumnTypes; GLADE_MAKE_EPROP (GladeEPropColumnTypes, glade_eprop_column_types) #define GLADE_EPROP_COLUMN_TYPES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_COLUMN_TYPES, GladeEPropColumnTypes)) #define GLADE_EPROP_COLUMN_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_COLUMN_TYPES, GladeEPropColumnTypesClass)) #define GLADE_IS_EPROP_COLUMN_TYPES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_COLUMN_TYPES)) #define GLADE_IS_EPROP_COLUMN_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_COLUMN_TYPES)) #define GLADE_EPROP_COLUMN_TYPES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_COLUMN_TYPES, GladeEPropColumnTypesClass)) static void glade_eprop_column_types_finalize (GObject * object) { /* Chain up */ GObjectClass *parent_class = g_type_class_peek_parent (G_OBJECT_GET_CLASS (object)); G_OBJECT_CLASS (parent_class)->finalize (object); } static gint get_extra_column (GNode * data_tree, GList * columns) { GladeModelData *data; GNode *iter; gint idx = -1; /* extra columns trail at the end so walk backwards... */ for (iter = g_node_last_child (data_tree->children); iter; iter = iter->prev) { data = iter->data; if (!glade_column_list_find_column (columns, data->name)) { idx = g_node_child_position (data_tree->children, iter); break; } } return idx; } static void eprop_column_adjust_rows (GladeEditorProperty * eprop, GList * columns) { GladeColumnType *column; GNode *data_tree = NULL; GladeProperty *property, *prop = glade_editor_property_get_property (eprop); GladeWidget *widget = glade_property_get_widget (prop); GList *list; gint idx; property = glade_widget_get_property (widget, "data"); glade_property_get (property, &data_tree); if (!data_tree) return; data_tree = glade_model_data_tree_copy (data_tree); /* Add mising columns and reorder... */ for (list = g_list_last (columns); list; list = list->prev) { GType data_type; column = list->data; data_type = g_type_from_name (column->type_name); if ((idx = glade_model_data_column_index (data_tree, column->column_name)) < 0) { glade_model_data_insert_column (data_tree, data_type, column->column_name, 0); } else glade_model_data_reorder_column (data_tree, idx, 0); } /* Remove trailing obsolete */ while ((idx = get_extra_column (data_tree, columns)) >= 0) glade_model_data_remove_column (data_tree, idx); glade_command_set_property (property, data_tree); glade_model_data_tree_free (data_tree); } static void eprop_column_append (GladeEditorProperty *eprop, const gchar *type_name, const gchar *column_name) { GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); GList *columns = NULL; GladeColumnType *data; GValue value = { 0, }; GladeProperty *property; property = glade_editor_property_get_property (eprop); glade_property_get (property, &columns); if (columns) columns = glade_column_list_copy (columns); data = glade_column_type_new (type_name, column_name); columns = g_list_append (columns, data); eprop_types->adding_column = TRUE; glade_command_push_group (_("Setting columns on %s"), glade_widget_get_name (glade_property_get_widget (property))); g_value_init (&value, GLADE_TYPE_COLUMN_TYPE_LIST); g_value_take_boxed (&value, columns); glade_editor_property_commit (eprop, &value); eprop_column_adjust_rows (eprop, columns); g_value_unset (&value); glade_command_pop_group (); eprop_types->adding_column = FALSE; } static gboolean eprop_treeview_key_press (GtkWidget *treeview, GdkEventKey *event, GladeEditorProperty *eprop) { /* Remove from list and commit value, dont touch the liststore except in load() */ GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); GtkTreeIter iter; GList *columns = NULL; GladeColumnType *column; GValue value = { 0, }; gchar *column_name; GladeProperty *property; property = glade_editor_property_get_property (eprop); if (event->keyval == GDK_KEY_Delete && gtk_tree_selection_get_selected (eprop_types->selection, NULL, &iter)) { gtk_tree_model_get (GTK_TREE_MODEL (eprop_types->store), &iter, COLUMN_COLUMN_NAME, &column_name, -1); /* Cant delete the symbolic "add new" row */ if (!column_name) return TRUE; glade_property_get (property, &columns); if (columns) columns = glade_column_list_copy (columns); g_assert (columns); /* Find and remove the offending column... */ column = glade_column_list_find_column (columns, column_name); g_assert (column); columns = g_list_remove (columns, column); glade_column_type_free (column); glade_command_push_group (_("Setting columns on %s"), glade_widget_get_name (glade_property_get_widget (property))); eprop_types->want_focus = TRUE; g_value_init (&value, GLADE_TYPE_COLUMN_TYPE_LIST); g_value_take_boxed (&value, columns); glade_editor_property_commit (eprop, &value); eprop_column_adjust_rows (eprop, columns); g_value_unset (&value); glade_command_pop_group (); g_free (column_name); eprop_types->want_focus = FALSE; } return (event->keyval == GDK_KEY_Delete); } static gboolean columns_changed_idle (GladeEditorProperty * eprop) { GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); GladeColumnType *column; GValue value = { 0, }; GList *new_list = NULL, *columns = NULL, *list; GtkTreeIter iter; gchar *column_name; GladeProperty *property; property = glade_editor_property_get_property (eprop); glade_property_get (property, &columns); /* This can happen when the user performs DnD and there * are no columns yet */ if (!columns) return FALSE; columns = glade_column_list_copy (columns); if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (eprop_types->store), &iter)) { do { column_name = NULL; gtk_tree_model_get (GTK_TREE_MODEL (eprop_types->store), &iter, COLUMN_COLUMN_NAME, &column_name, -1); if (!column_name) continue; column = glade_column_list_find_column (columns, column_name); g_assert (column); new_list = g_list_prepend (new_list, column); g_free (column_name); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (eprop_types->store), &iter)); } /* any missing columns to free ? */ for (list = columns; list; list = list->next) { if (!g_list_find (new_list, list->data)) glade_column_type_free ((GladeColumnType *) list->data); } g_list_free (columns); glade_command_push_group (_("Setting columns on %s"), glade_widget_get_name (glade_property_get_widget (property))); g_value_init (&value, GLADE_TYPE_COLUMN_TYPE_LIST); g_value_take_boxed (&value, g_list_reverse (new_list)); glade_editor_property_commit (eprop, &value); eprop_column_adjust_rows (eprop, new_list); g_value_unset (&value); glade_command_pop_group (); return FALSE; } static void eprop_treeview_row_deleted (GtkTreeModel * tree_model, GtkTreePath * path, GladeEditorProperty * eprop) { if (glade_editor_property_loading (eprop)) return; g_idle_add ((GSourceFunc) columns_changed_idle, eprop); } static void eprop_column_add_new (GladeEPropColumnTypes * eprop_types) { gtk_list_store_insert_with_values (eprop_types->store, NULL, -1, COLUMN_NAME, _("< define a new column >"), COLUMN_TYPE_EDITABLE, TRUE, COLUMN_NAME_EDITABLE, FALSE, COLUMN_TYPE_FOREGROUND, "Gray", COLUMN_TYPE_STYLE, PANGO_STYLE_ITALIC, -1); } static void eprop_column_load (GladeEPropColumnTypes * eprop_types, const gchar * type_name, const gchar * column_name) { gtk_list_store_insert_with_values (eprop_types->store, NULL, -1, COLUMN_NAME, type_name, COLUMN_COLUMN_NAME, column_name, COLUMN_TYPE_EDITABLE, FALSE, COLUMN_NAME_EDITABLE, TRUE, COLUMN_TYPE_FOREGROUND, "Black", COLUMN_TYPE_STYLE, PANGO_STYLE_NORMAL, -1); } static void eprop_types_focus_cell (GladeEPropColumnTypes * eprop_types, gboolean use_path, gboolean add_cell, gboolean edit_cell) { /* Focus and edit the first column of a newly added row */ if (eprop_types->store) { GtkTreePath *new_item_path; GtkTreeIter iter; gint n_children; gint needed_row; n_children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (eprop_types->store), NULL); needed_row = n_children - (add_cell ? 1 : 2); if (use_path) new_item_path = gtk_tree_path_new_from_string (g_object_get_data (G_OBJECT (eprop_types), "current-path-str")); else if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (eprop_types->store), &iter, NULL, needed_row)) new_item_path = gtk_tree_model_get_path (GTK_TREE_MODEL (eprop_types->store), &iter); else return; eprop_types->setting_cursor = TRUE; gtk_widget_grab_focus (GTK_WIDGET (eprop_types->view)); gtk_tree_view_expand_to_path (eprop_types->view, new_item_path); gtk_tree_view_set_cursor (eprop_types->view, new_item_path, add_cell ? eprop_types-> type_column : eprop_types->name_column, edit_cell); eprop_types->setting_cursor = FALSE; gtk_tree_path_free (new_item_path); } } static gboolean eprop_types_focus_new (GladeEPropColumnTypes * eprop_types) { eprop_types_focus_cell (eprop_types, FALSE, TRUE, FALSE); return FALSE; } static gboolean eprop_types_focus_name (GladeEPropColumnTypes * eprop_types) { eprop_types_focus_cell (eprop_types, FALSE, FALSE, TRUE); return FALSE; } static gboolean eprop_types_focus_name_no_edit (GladeEPropColumnTypes * eprop_types) { eprop_types_focus_cell (eprop_types, TRUE, FALSE, FALSE); return FALSE; } static void glade_eprop_column_types_load (GladeEditorProperty * eprop, GladeProperty * property) { GladeEditorPropertyClass *parent_class = g_type_class_peek_parent (GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)); GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); GList *l, *list = NULL; /* Chain up first */ parent_class->load (eprop, property); if (eprop_types->context) glade_name_context_destroy (eprop_types->context); eprop_types->context = NULL; if (!property) return; eprop_types->context = glade_name_context_new (); g_signal_handlers_block_by_func (G_OBJECT (eprop_types->store), eprop_treeview_row_deleted, eprop); /* Clear Store */ gtk_list_store_clear (eprop_types->store); glade_property_get (property, &list); for (l = list; l; l = g_list_next (l)) { GladeColumnType *data = l->data; eprop_column_load (eprop_types, data->type_name, data->column_name); glade_name_context_add_name (eprop_types->context, data->column_name); } eprop_column_add_new (eprop_types); if (eprop_types->adding_column && list) g_idle_add ((GSourceFunc) eprop_types_focus_name, eprop_types); else if (eprop_types->want_focus) g_idle_add ((GSourceFunc) eprop_types_focus_new, eprop_types); g_signal_handlers_unblock_by_func (G_OBJECT (eprop_types->store), eprop_treeview_row_deleted, eprop); } static void column_name_edited (GtkCellRendererText * cell, const gchar * path, const gchar * new_column_name, GladeEditorProperty * eprop) { GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); GtkTreeIter iter; gchar *old_column_name = NULL, *column_name; GList *columns = NULL; GladeColumnType *column; GValue value = { 0, }; GNode *data_tree = NULL; GladeProperty *property, *prop; prop = glade_editor_property_get_property (eprop); if (eprop_types->adding_column) return; if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (eprop_types->store), &iter, path)) return; gtk_tree_model_get (GTK_TREE_MODEL (eprop_types->store), &iter, COLUMN_COLUMN_NAME, &old_column_name, -1); if (new_column_name && old_column_name && strcmp (new_column_name, old_column_name) == 0) return; /* Attempt to rename the column, and commit if successfull... */ glade_property_get (prop, &columns); if (columns) columns = glade_column_list_copy (columns); g_assert (columns); column = glade_column_list_find_column (columns, old_column_name); /* Bookkeep the exclusive names... */ if (!new_column_name || !new_column_name[0] || glade_name_context_has_name (eprop_types->context, new_column_name)) column_name = glade_name_context_new_name (eprop_types->context, new_column_name && new_column_name[0] ? new_column_name : "column"); else column_name = g_strdup (new_column_name); glade_name_context_add_name (eprop_types->context, column_name); glade_name_context_release_name (eprop_types->context, old_column_name); /* Set real column name */ g_free (column->column_name); column->column_name = column_name; /* The "columns" copy of this string doesnt last long... */ column_name = g_strdup (column_name); glade_command_push_group (_("Setting columns on %s"), glade_widget_get_name (glade_property_get_widget (prop))); eprop_types->want_focus = TRUE; g_value_init (&value, GLADE_TYPE_COLUMN_TYPE_LIST); g_value_take_boxed (&value, columns); glade_editor_property_commit (eprop, &value); g_value_unset (&value); property = glade_widget_get_property (glade_property_get_widget (prop), "data"); glade_property_get (property, &data_tree); if (data_tree) { data_tree = glade_model_data_tree_copy (data_tree); glade_model_data_column_rename (data_tree, old_column_name, column_name); glade_command_set_property (property, data_tree); glade_model_data_tree_free (data_tree); } glade_command_pop_group (); eprop_types->want_focus = FALSE; g_free (old_column_name); g_free (column_name); } static void column_type_edited (GtkCellRendererText * cell, const gchar * path, const gchar * type_name, GladeEditorProperty * eprop) { GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); GtkTreeIter iter; GladeProperty *property; gchar *column_name; if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (eprop_types->store), &iter, path)) return; property = glade_editor_property_get_property (eprop); if (type_name && type_name[0]) { column_name = glade_name_context_new_name (eprop_types->context, type_name); eprop_column_append (eprop, type_name, column_name); g_free (column_name); } else { eprop_types->want_focus = TRUE; glade_editor_property_load (eprop, property); eprop_types->want_focus = FALSE; } } static void types_combo_editing_started (GtkCellRenderer * renderer, GtkCellEditable * editable, gchar * path, GladeEditorProperty * eprop) { GtkEntryCompletion *completion = gtk_entry_completion_new (); g_object_set_data_full (G_OBJECT (eprop), "current-path-str", g_strdup (path), g_free); gtk_entry_completion_set_model (completion, types_model); gtk_entry_completion_set_text_column (completion, 0); gtk_entry_completion_set_inline_completion (completion, TRUE); gtk_entry_set_completion (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (editable))), completion); g_object_unref (G_OBJECT (completion)); } static void types_combo_editing_canceled (GtkCellRenderer * renderer, GladeEditorProperty * eprop) { g_idle_add ((GSourceFunc) eprop_types_focus_new, eprop); } static void types_name_editing_started (GtkCellRenderer * renderer, GtkCellEditable * editable, gchar * path_str, GladeEditorProperty * eprop) { g_object_set_data_full (G_OBJECT (eprop), "current-path-str", g_strdup (path_str), g_free); } static void types_name_editing_canceled (GtkCellRenderer * renderer, GladeEditorProperty * eprop) { GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); if (eprop_types->adding_column || eprop_types->setting_cursor) return; g_idle_add ((GSourceFunc) eprop_types_focus_name_no_edit, eprop); } static GtkWidget * glade_eprop_column_types_create_input (GladeEditorProperty * eprop) { GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); GtkWidget *vbox, *swin, *label; GtkCellRenderer *cell; gchar *string; vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); /* hbox = gtk_hbox_new (FALSE, 4); */ if (!types_model) { /* We make sure to do this after all the adaptors are parsed * because we load the enums/flags from the adaptors */ types_model = (GtkTreeModel *) gtk_list_store_new (1, G_TYPE_STRING); column_types_store_populate (GTK_LIST_STORE (types_model)); } string = g_strdup_printf ("%s", _("Add and remove columns:")); label = gtk_label_new (string); g_free (string); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0); swin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin), GTK_SHADOW_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_box_pack_start (GTK_BOX (vbox), swin, TRUE, TRUE, 0); eprop_types->store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, PANGO_TYPE_STYLE); g_signal_connect (eprop_types->store, "row-deleted", G_CALLBACK (eprop_treeview_row_deleted), eprop); eprop_types->view = (GtkTreeView *) gtk_tree_view_new_with_model (GTK_TREE_MODEL (eprop_types->store)); eprop_types->selection = gtk_tree_view_get_selection (eprop_types->view); gtk_tree_view_set_reorderable (eprop_types->view, TRUE); gtk_tree_view_set_enable_search (eprop_types->view, FALSE); //gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); g_signal_connect (eprop_types->view, "key-press-event", G_CALLBACK (eprop_treeview_key_press), eprop); /* type column */ cell = gtk_cell_renderer_combo_new (); g_object_set (G_OBJECT (cell), "text-column", COLUMN_NAME, "model", types_model, NULL); g_signal_connect (G_OBJECT (cell), "editing-started", G_CALLBACK (types_combo_editing_started), eprop); g_signal_connect (G_OBJECT (cell), "editing-canceled", G_CALLBACK (types_combo_editing_canceled), eprop); g_signal_connect (G_OBJECT (cell), "edited", G_CALLBACK (column_type_edited), eprop); eprop_types->type_column = gtk_tree_view_column_new_with_attributes (_("Column type"), cell, "foreground", COLUMN_TYPE_FOREGROUND, "style", COLUMN_TYPE_STYLE, "editable", COLUMN_TYPE_EDITABLE, "text", COLUMN_NAME, NULL); gtk_tree_view_column_set_expand (eprop_types->type_column, TRUE); gtk_tree_view_append_column (eprop_types->view, eprop_types->type_column); /* name column */ cell = gtk_cell_renderer_text_new (); g_signal_connect (G_OBJECT (cell), "edited", G_CALLBACK (column_name_edited), eprop); g_signal_connect (G_OBJECT (cell), "editing-started", G_CALLBACK (types_name_editing_started), eprop); g_signal_connect (G_OBJECT (cell), "editing-canceled", G_CALLBACK (types_name_editing_canceled), eprop); eprop_types->name_column = gtk_tree_view_column_new_with_attributes (_("Column name"), cell, "editable", COLUMN_NAME_EDITABLE, "text", COLUMN_COLUMN_NAME, NULL); gtk_tree_view_column_set_expand (eprop_types->name_column, TRUE); gtk_tree_view_append_column (eprop_types->view, eprop_types->name_column); gtk_container_add (GTK_CONTAINER (swin), GTK_WIDGET (eprop_types->view)); g_object_set (G_OBJECT (vbox), "height-request", 200, NULL); gtk_widget_show_all (vbox); return vbox; }