debug-manager: use g_spawn_sync() instead of fork() and waitpid()
[anjuta.git] / plugins / class-gen / cell-renderer-flags.c
blob67a9c2138ce1a137de78e0973b15cd4e110f819f
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /* cell-renderer-flags.c
3 * Copyright (C) 2006 Armin Burgmeier
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Library General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "cell-renderer-flags.h"
21 #include "combo-flags.h"
23 #include <gtk/gtk.h>
25 typedef struct _CgCellRendererFlagsPrivate CgCellRendererFlagsPrivate;
26 struct _CgCellRendererFlagsPrivate
28 GtkTreeModel *model;
29 gint text_column;
30 gint abbr_column;
32 GHashTable *edit_status;
33 guint focus_out_id;
36 #define CG_CELL_RENDERER_FLAGS_PRIVATE(o) \
37 (G_TYPE_INSTANCE_GET_PRIVATE( \
38 (o), \
39 CG_TYPE_CELL_RENDERER_FLAGS, \
40 CgCellRendererFlagsPrivate \
43 enum {
44 PROP_0,
46 PROP_MODEL,
47 PROP_TEXT_COLUMN,
48 PROP_ABBR_COLUMN
51 #define CG_CELL_RENDERER_FLAGS_PATH "cg-cell-renderer-flags-path"
53 static GtkCellRendererTextClass *parent_class = NULL;
55 static void
56 cg_cell_renderer_flags_init (CgCellRendererFlags *cell_renderer_flags)
58 CgCellRendererFlagsPrivate *priv;
59 priv = CG_CELL_RENDERER_FLAGS_PRIVATE (cell_renderer_flags);
61 priv->model = NULL;
62 priv->text_column = -1;
63 priv->abbr_column = -1;
65 priv->edit_status = NULL;
66 priv->focus_out_id = 0;
69 static void
70 cg_cell_renderer_flags_finalize (GObject *object)
72 CgCellRendererFlags *cell_renderer_flags;
73 CgCellRendererFlagsPrivate *priv;
75 cell_renderer_flags = CG_CELL_RENDERER_FLAGS (object);
76 priv = CG_CELL_RENDERER_FLAGS_PRIVATE (cell_renderer_flags);
78 if (priv->edit_status != NULL)
80 g_hash_table_destroy (priv->edit_status);
81 priv->edit_status = NULL;
84 if (priv->model != NULL)
86 g_object_unref (G_OBJECT(priv->model));
87 priv->model = NULL;
90 G_OBJECT_CLASS (parent_class)-> finalize(object);
93 static void
94 cg_cell_renderer_flags_set_property (GObject *object,
95 guint prop_id,
96 const GValue *value,
97 GParamSpec *pspec)
99 CgCellRendererFlags *renderer;
100 CgCellRendererFlagsPrivate *priv;
102 g_return_if_fail (CG_IS_CELL_RENDERER_FLAGS (object));
104 renderer = CG_CELL_RENDERER_FLAGS (object);
105 priv = CG_CELL_RENDERER_FLAGS_PRIVATE (renderer);
107 switch (prop_id)
109 case PROP_MODEL:
110 if(priv->model != NULL) g_object_unref (G_OBJECT (priv->model));
111 priv->model = GTK_TREE_MODEL (g_value_dup_object (value));
112 break;
113 case PROP_TEXT_COLUMN:
114 priv->text_column = g_value_get_int (value);
115 break;
116 case PROP_ABBR_COLUMN:
117 priv->abbr_column = g_value_get_int (value);
118 break;
119 default:
120 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
121 break;
125 static void
126 cg_cell_renderer_flags_get_property (GObject *object,
127 guint prop_id,
128 GValue *value,
129 GParamSpec *pspec)
131 CgCellRendererFlags *renderer;
132 CgCellRendererFlagsPrivate *priv;
134 g_return_if_fail (CG_IS_CELL_RENDERER_FLAGS (object));
136 renderer = CG_CELL_RENDERER_FLAGS (object);
137 priv = CG_CELL_RENDERER_FLAGS_PRIVATE (renderer);
139 switch(prop_id)
141 case PROP_MODEL:
142 g_value_set_object (value, G_OBJECT (priv->model));
143 break;
144 case PROP_TEXT_COLUMN:
145 g_value_set_int (value, priv->text_column);
146 break;
147 case PROP_ABBR_COLUMN:
148 g_value_set_int (value, priv->abbr_column);
149 break;
150 default:
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
152 break;
156 static void
157 cg_cell_renderer_flags_editing_done (GtkCellEditable *editable,
158 G_GNUC_UNUSED gpointer data)
160 CgCellRendererFlags *cell_flags;
161 CgCellRendererFlagsPrivate *priv;
163 const gchar *path;
164 GString *str;
165 gchar *abbr;
166 GtkTreeIter iter;
167 gboolean result;
168 gboolean canceled;
170 cell_flags = CG_CELL_RENDERER_FLAGS (data);
171 priv = CG_CELL_RENDERER_FLAGS_PRIVATE (cell_flags);
173 g_assert (priv->edit_status != NULL);
175 if (priv->focus_out_id > 0)
177 g_signal_handler_disconnect (G_OBJECT (editable), priv->focus_out_id);
178 priv->focus_out_id = 0;
181 canceled = cg_combo_flags_editing_canceled (CG_COMBO_FLAGS (editable));
182 gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER(cell_flags), canceled);
184 if (canceled == FALSE)
186 str = g_string_sized_new (128);
188 /* We do not just call g_hash_table_foreach to get the flags
189 * in the correct order. */
190 for (result = gtk_tree_model_get_iter_first (priv->model, &iter);
191 result != FALSE;
192 result = gtk_tree_model_iter_next (priv->model, &iter))
194 gtk_tree_model_get (priv->model, &iter,
195 priv->abbr_column, &abbr, -1);
197 if (g_hash_table_lookup (priv->edit_status, abbr) != NULL)
199 if (str->len > 0) g_string_append_c (str, '|');
200 g_string_append (str, abbr);
203 g_free (abbr);
206 path = g_object_get_data (G_OBJECT (editable),
207 CG_CELL_RENDERER_FLAGS_PATH);
209 g_signal_emit_by_name (G_OBJECT (cell_flags), "edited",
210 path, str->str);
212 g_string_free (str, TRUE);
215 g_hash_table_destroy (priv->edit_status);
216 priv->edit_status = NULL;
219 static void
220 cg_cell_renderer_flags_selected (CgComboFlags *combo,
221 GtkTreeIter *iter,
222 CgComboFlagsSelectionType type,
223 gpointer user_data)
225 CgCellRendererFlags *cell_flags;
226 CgCellRendererFlagsPrivate *priv;
227 gpointer result;
228 gchar *name;
229 gchar *abbr;
231 cell_flags = CG_CELL_RENDERER_FLAGS (user_data);
232 priv = CG_CELL_RENDERER_FLAGS_PRIVATE (cell_flags);
234 gtk_tree_model_get (priv->model, iter, priv->text_column, &name,
235 priv->abbr_column, &abbr, -1);
237 g_assert (priv->edit_status != NULL);
238 result = g_hash_table_lookup (priv->edit_status, abbr);
240 /* abbr needs not to be freed if it gets inserted into the hash table
241 * because the hash table then takes ownership of it. */
242 switch (type)
244 case CG_COMBO_FLAGS_SELECTION_NONE:
245 g_free (abbr);
246 break;
247 case CG_COMBO_FLAGS_SELECTION_SELECT:
248 if (GPOINTER_TO_INT(result) != 1)
249 g_hash_table_insert (priv->edit_status, abbr, GINT_TO_POINTER (1));
250 else
251 g_free (abbr);
253 break;
254 case CG_COMBO_FLAGS_SELECTION_UNSELECT:
255 if (GPOINTER_TO_INT (result) == 1)
256 g_hash_table_remove(priv->edit_status, abbr);
258 g_free (abbr);
259 break;
260 case CG_COMBO_FLAGS_SELECTION_TOGGLE:
261 if (GPOINTER_TO_INT (result) == 1)
263 g_hash_table_remove (priv->edit_status, abbr);
264 g_free(abbr);
266 else
268 g_hash_table_insert (priv->edit_status, abbr, GINT_TO_POINTER (1));
271 break;
272 default:
273 g_assert_not_reached ();
274 break;
277 /* This is done to get GTK+ to re-render this row with the changed flag
278 * status that is set via the cell data func, but GTK+ does not call it
279 * again because it does not know that the hash table changed. There are
280 * probably better means to achieve this, but I am not aware of those. */
281 gtk_list_store_set (GTK_LIST_STORE (priv->model), iter,
282 priv->text_column, name, -1);
284 g_free (name);
287 static gboolean
288 cg_cell_renderer_flags_focus_out_event (GtkWidget *widget,
289 G_GNUC_UNUSED GdkEvent *event,
290 gpointer data)
292 cg_cell_renderer_flags_editing_done (GTK_CELL_EDITABLE (widget), data);
293 return FALSE;
296 static void
297 cg_cell_renderer_flags_set_data_func (G_GNUC_UNUSED GtkCellLayout *cell_layout,
298 GtkCellRenderer *cell,
299 GtkTreeModel *model,
300 GtkTreeIter *iter,
301 gpointer data)
303 CgCellRendererFlags *cell_flags;
304 CgCellRendererFlagsPrivate *priv;
305 gchar *abbr;
307 cell_flags = CG_CELL_RENDERER_FLAGS (data);
308 priv = CG_CELL_RENDERER_FLAGS_PRIVATE (cell_flags);
310 if(priv->edit_status != NULL)
312 gtk_tree_model_get (model, iter, priv->abbr_column, &abbr, -1);
314 if (g_hash_table_lookup (priv->edit_status, abbr) != NULL)
315 g_object_set (G_OBJECT (cell), "active", TRUE, NULL);
316 else
317 g_object_set (G_OBJECT (cell), "active", FALSE, NULL);
319 g_free (abbr);
323 static GtkCellEditable *
324 cg_cell_renderer_flags_start_editing (GtkCellRenderer *cell,
325 G_GNUC_UNUSED GdkEvent *event,
326 G_GNUC_UNUSED GtkWidget *widget,
327 const gchar *path,
328 G_GNUC_UNUSED GdkRectangle
329 *background_area,
330 G_GNUC_UNUSED GdkRectangle *cell_area,
331 G_GNUC_UNUSED GtkCellRendererState flags)
333 CgCellRendererFlags *cell_flags;
334 CgCellRendererFlagsPrivate *priv;
335 GtkCellRendererText *cell_text;
336 gboolean editable;
337 gchar *text;
338 const gchar *prev;
339 const gchar *pos;
341 GtkWidget *combo;
342 GtkCellRenderer *cell_combo_set;
343 GtkCellRenderer *cell_combo_text;
345 cell_flags = CG_CELL_RENDERER_FLAGS (cell);
346 priv = CG_CELL_RENDERER_FLAGS_PRIVATE (cell_flags);
348 cell_text = GTK_CELL_RENDERER_TEXT (cell);
350 g_object_get (cell_text,
351 "editable", &editable,
352 "text", &text,
353 NULL);
355 if (editable == FALSE) return NULL;
357 if (priv->model == NULL || priv->text_column < 0 || priv->abbr_column < 0)
358 return NULL;
360 cell_combo_set = gtk_cell_renderer_toggle_new ();
361 cell_combo_text = gtk_cell_renderer_text_new ();
363 combo = cg_combo_flags_new_with_model (priv->model);
365 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo),
366 cell_combo_set, FALSE);
367 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo),
368 cell_combo_text, TRUE);
370 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo),
371 cell_combo_text, "text", priv->text_column);
373 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo),
374 cell_combo_set,
375 cg_cell_renderer_flags_set_data_func,
376 cell_flags, NULL);
378 g_object_set (G_OBJECT (cell_combo_set), "activatable", FALSE, NULL);
380 /* Create hash table with current status. We could also operate
381 * directly on a string here, but a hash table is probably more
382 * efficient. */
383 g_assert (priv->edit_status == NULL);
384 priv->edit_status = g_hash_table_new_full (g_str_hash, g_str_equal,
385 (GDestroyNotify) g_free, NULL);
387 pos = text;
388 prev = text;
390 while (prev != NULL && *prev != '\0')
392 while (*pos != '|' && *pos != '\0') ++ pos;
394 g_hash_table_insert (priv->edit_status, g_strndup(prev, pos - prev),
395 GINT_TO_POINTER(1));
397 if(*pos != '\0') ++ pos;
398 prev = pos;
400 g_free (text);
402 g_object_set_data_full (G_OBJECT (combo), CG_CELL_RENDERER_FLAGS_PATH,
403 g_strdup (path), g_free);
405 gtk_widget_show (combo);
407 g_signal_connect (G_OBJECT (combo), "editing-done",
408 G_CALLBACK (cg_cell_renderer_flags_editing_done),
409 cell_flags);
411 g_signal_connect (G_OBJECT (combo), "selected",
412 G_CALLBACK (cg_cell_renderer_flags_selected),
413 cell_flags);
415 priv->focus_out_id =
416 g_signal_connect (G_OBJECT (combo), "focus_out_event",
417 G_CALLBACK (cg_cell_renderer_flags_focus_out_event),
418 cell_flags);
420 return GTK_CELL_EDITABLE (combo);
423 static void
424 cg_cell_renderer_flags_class_init (CgCellRendererFlagsClass *klass)
426 GObjectClass *object_class = G_OBJECT_CLASS (klass);
427 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
428 parent_class = g_type_class_peek_parent (klass);
430 g_type_class_add_private (klass, sizeof (CgCellRendererFlagsPrivate));
432 object_class->finalize = cg_cell_renderer_flags_finalize;
433 object_class->set_property = cg_cell_renderer_flags_set_property;
434 object_class->get_property = cg_cell_renderer_flags_get_property;
436 cell_class->start_editing = cg_cell_renderer_flags_start_editing;
438 g_object_class_install_property (object_class,
439 PROP_MODEL,
440 g_param_spec_object ("model",
441 "Model",
442 "Model holding the available flags",
443 GTK_TYPE_TREE_MODEL,
444 G_PARAM_READWRITE));
446 g_object_class_install_property (object_class,
447 PROP_TEXT_COLUMN,
448 g_param_spec_int ("text-column",
449 "Text column",
450 "Column in the model holding the text for a flag",
452 G_MAXINT,
454 G_PARAM_READWRITE));
456 g_object_class_install_property (object_class,
457 PROP_ABBR_COLUMN,
458 g_param_spec_int ("abbrevation-column",
459 "Abbrevation column",
460 "Column in the model holding the abbrevation for a flag",
462 G_MAXINT,
464 G_PARAM_READWRITE));
467 GType
468 cg_cell_renderer_flags_get_type (void)
470 static GType our_type = 0;
472 if(our_type == 0)
474 static const GTypeInfo our_info =
476 sizeof (CgCellRendererFlagsClass),
477 (GBaseInitFunc) NULL,
478 (GBaseFinalizeFunc) NULL,
479 (GClassInitFunc) cg_cell_renderer_flags_class_init,
480 NULL,
481 NULL,
482 sizeof (CgCellRendererFlags),
484 (GInstanceInitFunc) cg_cell_renderer_flags_init,
485 NULL
488 our_type = g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT,
489 "CgCellRendererFlags",
490 &our_info, 0);
493 return our_type;
496 GtkCellRenderer *
497 cg_cell_renderer_flags_new (void)
499 GObject *object;
501 object = g_object_new (CG_TYPE_CELL_RENDERER_FLAGS, NULL);
503 return GTK_CELL_RENDERER (object);