/* Tree View/Editable Cells * * This demo demonstrates the use of editable cells in a GtkTreeView. If * you're new to the GtkTreeView widgets and associates, look into * the GtkListStore example first. It also shows how to use the * GtkCellRenderer::editing-started signal to do custom setup of the * editable widget. * * The cell renderers used in this demo are GtkCellRendererText and * GtkCellRendererCombo. */ #include #include #include static GtkWidget *window = NULL; typedef struct { gint number; gchar *product; } Item; enum { COLUMN_ITEM_NUMBER, COLUMN_ITEM_PRODUCT, NUM_ITEM_COLUMNS }; enum { COLUMN_NUMBER_TEXT, NUM_NUMBER_COLUMNS }; static GArray *articles = NULL; static void add_items (void) { Item foo; g_return_if_fail (articles != NULL); foo.number = 3; foo.product = g_strdup ("bottles of coke"); g_array_append_vals (articles, &foo, 1); foo.number = 5; foo.product = g_strdup ("packages of noodles"); g_array_append_vals (articles, &foo, 1); foo.number = 2; foo.product = g_strdup ("packages of chocolate chip cookies"); g_array_append_vals (articles, &foo, 1); foo.number = 1; foo.product = g_strdup ("can vanilla ice cream"); g_array_append_vals (articles, &foo, 1); foo.number = 6; foo.product = g_strdup ("eggs"); g_array_append_vals (articles, &foo, 1); } static GtkTreeModel * create_items_model (void) { gint i = 0; GtkListStore *model; GtkTreeIter iter; /* create array */ articles = g_array_sized_new (FALSE, FALSE, sizeof (Item), 1); add_items (); /* create list store */ model = gtk_list_store_new (NUM_ITEM_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN); /* add items */ for (i = 0; i < articles->len; i++) { gtk_list_store_append (model, &iter); gtk_list_store_set (model, &iter, COLUMN_ITEM_NUMBER, g_array_index (articles, Item, i).number, COLUMN_ITEM_PRODUCT, g_array_index (articles, Item, i).product, -1); } return GTK_TREE_MODEL (model); } static GtkTreeModel * create_numbers_model (void) { #define N_NUMBERS 10 gint i = 0; GtkListStore *model; GtkTreeIter iter; /* create list store */ model = gtk_list_store_new (NUM_NUMBER_COLUMNS, G_TYPE_STRING, G_TYPE_INT); /* add numbers */ for (i = 0; i < N_NUMBERS; i++) { char str[2] = { '0' + i, '\0' }; gtk_list_store_append (model, &iter); gtk_list_store_set (model, &iter, COLUMN_NUMBER_TEXT, str, -1); } return GTK_TREE_MODEL (model); #undef N_NUMBERS } static void add_item (GtkWidget *button, gpointer data) { Item foo; GtkTreeIter iter; GtkTreeModel *model = (GtkTreeModel *)data; g_return_if_fail (articles != NULL); foo.number = 0; foo.product = g_strdup ("Description here"); g_array_append_vals (articles, &foo, 1); gtk_list_store_append (GTK_LIST_STORE (model), &iter); gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_ITEM_NUMBER, foo.number, COLUMN_ITEM_PRODUCT, foo.product, -1); } static void remove_item (GtkWidget *widget, gpointer data) { GtkTreeIter iter; GtkTreeView *treeview = (GtkTreeView *)data; GtkTreeModel *model = gtk_tree_view_get_model (treeview); GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview); if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { gint i; GtkTreePath *path; path = gtk_tree_model_get_path (model, &iter); i = gtk_tree_path_get_indices (path)[0]; gtk_list_store_remove (GTK_LIST_STORE (model), &iter); g_array_remove_index (articles, i); gtk_tree_path_free (path); } } static gboolean separator_row (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { GtkTreePath *path; gint idx; path = gtk_tree_model_get_path (model, iter); idx = gtk_tree_path_get_indices (path)[0]; gtk_tree_path_free (path); return idx == 5; } static void editing_started (GtkCellRenderer *cell, GtkCellEditable *editable, const gchar *path, gpointer data) { gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (editable), separator_row, NULL, NULL); } static void cell_edited (GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text, gpointer data) { GtkTreeModel *model = (GtkTreeModel *)data; GtkTreePath *path = gtk_tree_path_new_from_string (path_string); GtkTreeIter iter; gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column")); gtk_tree_model_get_iter (model, &iter, path); switch (column) { case COLUMN_ITEM_NUMBER: { gint i; i = gtk_tree_path_get_indices (path)[0]; g_array_index (articles, Item, i).number = atoi (new_text); gtk_list_store_set (GTK_LIST_STORE (model), &iter, column, g_array_index (articles, Item, i).number, -1); } break; case COLUMN_ITEM_PRODUCT: { gint i; gchar *old_text; gtk_tree_model_get (model, &iter, column, &old_text, -1); g_free (old_text); i = gtk_tree_path_get_indices (path)[0]; g_free (g_array_index (articles, Item, i).product); g_array_index (articles, Item, i).product = g_strdup (new_text); gtk_list_store_set (GTK_LIST_STORE (model), &iter, column, g_array_index (articles, Item, i).product, -1); } break; } gtk_tree_path_free (path); } static void add_columns (GtkTreeView *treeview, GtkTreeModel *items_model, GtkTreeModel *numbers_model) { GtkCellRenderer *renderer; /* number column */ renderer = gtk_cell_renderer_combo_new (); g_object_set (renderer, "model", numbers_model, "text-column", COLUMN_NUMBER_TEXT, "has-entry", FALSE, "editable", TRUE, NULL); g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited), items_model); g_signal_connect (renderer, "editing-started", G_CALLBACK (editing_started), NULL); g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (COLUMN_ITEM_NUMBER)); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), -1, "Number", renderer, "text", COLUMN_ITEM_NUMBER, NULL); /* product column */ renderer = gtk_cell_renderer_text_new (); g_object_set (renderer, "editable", TRUE, NULL); g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited), items_model); g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (COLUMN_ITEM_PRODUCT)); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview), -1, "Product", renderer, "text", COLUMN_ITEM_PRODUCT, NULL); } GtkWidget * do_editable_cells (GtkWidget *do_widget) { if (!window) { GtkWidget *vbox; GtkWidget *hbox; GtkWidget *sw; GtkWidget *treeview; GtkWidget *button; GtkTreeModel *items_model; GtkTreeModel *numbers_model; /* create window, etc */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_screen (GTK_WINDOW (window), gtk_widget_get_screen (do_widget)); gtk_window_set_title (GTK_WINDOW (window), "Shopping list"); gtk_container_set_border_width (GTK_CONTAINER (window), 5); g_signal_connect (window, "destroy", G_CALLBACK (gtk_widget_destroyed), &window); vbox = gtk_vbox_new (FALSE, 5); gtk_container_add (GTK_CONTAINER (window), vbox); gtk_box_pack_start (GTK_BOX (vbox), gtk_label_new ("Shopping list (you can edit the cells!)"), FALSE, FALSE, 0); sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); /* create models */ items_model = create_items_model (); numbers_model = create_numbers_model (); /* create tree view */ treeview = gtk_tree_view_new_with_model (items_model); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE); gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), GTK_SELECTION_SINGLE); add_columns (GTK_TREE_VIEW (treeview), items_model, numbers_model); g_object_unref (numbers_model); g_object_unref (items_model); gtk_container_add (GTK_CONTAINER (sw), treeview); /* some buttons */ hbox = gtk_hbox_new (TRUE, 4); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); button = gtk_button_new_with_label ("Add item"); g_signal_connect (button, "clicked", G_CALLBACK (add_item), items_model); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); button = gtk_button_new_with_label ("Remove item"); g_signal_connect (button, "clicked", G_CALLBACK (remove_item), treeview); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_window_set_default_size (GTK_WINDOW (window), 320, 200); } if (!GTK_WIDGET_VISIBLE (window)) gtk_widget_show_all (window); else { gtk_widget_destroy (window); window = NULL; } return window; }