hid/gtk: Extend layer selector to allow editing layer names in situ
[geda-pcb/pcjc2.git] / src / hid / gtk / ghid-layer-selector.c
blob432e57e9b9fec8d5ae5f50872bc27c38d929f289
1 /*! \file <gtk-pcb-layer-selector.c>
2 * \brief Implementation of GHidLayerSelector widget
3 * \par Description
4 * This widget is the layer selector on the left side of the Gtk
5 * GUI. It also builds the relevant sections of the menu for layer
6 * selection and visibility toggling, and keeps these in sync.
7 */
9 #include <glib.h>
10 #include <glib-object.h>
11 #include <gdk/gdkkeysyms.h>
12 #include <gtk/gtk.h>
14 #include "gtkhid.h"
15 #include "gui.h"
16 #include "pcb-printf.h"
18 #include "ghid-layer-selector.h"
19 #include "ghid-cell-renderer-visibility.h"
21 #define INITIAL_ACTION_MAX 40
23 /* Forward dec'ls */
24 struct _layer;
25 static void ghid_layer_selector_finalize (GObject *object);
26 static void menu_pick_cb (GtkRadioAction *action, struct _layer *ldata);
28 /*! \brief Signals exposed by the widget */
29 enum {
30 SELECT_LAYER_SIGNAL,
31 TOGGLE_LAYER_SIGNAL,
32 RENAME_LAYER_SIGNAL,
33 LAST_SIGNAL
36 /*! \brief Columns used for internal data store */
37 enum {
38 STRUCT_COL,
39 USER_ID_COL,
40 VISIBLE_COL,
41 COLOR_COL,
42 TEXT_COL,
43 FONT_COL,
44 EDITABLE_COL,
45 SELECTABLE_COL,
46 SEPARATOR_COL,
47 N_COLS
50 static GtkTreeView *ghid_layer_selector_parent_class;
51 static guint ghid_layer_selector_signals[LAST_SIGNAL] = { 0 };
53 struct _GHidLayerSelector
55 GtkTreeView parent;
57 GtkListStore *list_store;
58 GtkTreeSelection *selection;
59 GtkTreeViewColumn *visibility_column;
61 GtkActionGroup *action_group;
62 GtkAccelGroup *accel_group;
64 GSList *radio_group;
65 int n_actions;
67 gboolean accel_available[20];
69 gulong selection_changed_sig_id;
72 struct _GHidLayerSelectorClass
74 GtkTreeViewClass parent_class;
76 void (* select_layer) (GHidLayerSelector *, gint);
77 void (* toggle_layer) (GHidLayerSelector *, gint);
78 void (* rename_layer) (GHidLayerSelector *, gint, gchar *);
81 struct _layer
83 gint accel_index; /* Index into ls->accel_available */
84 GtkWidget *pick_item;
85 GtkWidget *view_item;
86 GtkToggleAction *view_action;
87 GtkRadioAction *pick_action;
88 GtkTreeRowReference *rref;
91 static void
92 g_cclosure_user_marshal_VOID__INT_STRING (GClosure *closure,
93 GValue *return_value G_GNUC_UNUSED,
94 guint n_param_values,
95 const GValue *param_values,
96 gpointer invocation_hint G_GNUC_UNUSED,
97 gpointer marshal_data)
99 typedef void (*GMarshalFunc_VOID__INT_STRING) (gpointer data1,
100 gint arg_1,
101 gpointer arg_2,
102 gpointer data2);
103 register GMarshalFunc_VOID__INT_STRING callback;
104 register GCClosure *cc = (GCClosure*) closure;
105 register gpointer data1, data2;
107 g_return_if_fail (n_param_values == 3);
109 if (G_CCLOSURE_SWAP_DATA (closure))
111 data1 = closure->data;
112 data2 = g_value_peek_pointer (param_values + 0);
114 else
116 data1 = g_value_peek_pointer (param_values + 0);
117 data2 = closure->data;
119 callback = (GMarshalFunc_VOID__INT_STRING) (marshal_data ? marshal_data : cc->callback);
121 callback (data1,
122 g_value_get_int (param_values + 1),
123 (char *)g_value_get_string (param_values + 2),
124 data2);
127 /*! \brief Deletes the action and accelerator from a layer */
128 static void
129 free_ldata (GHidLayerSelector *ls, struct _layer *ldata)
131 if (ldata->pick_action)
133 gtk_action_disconnect_accelerator
134 (GTK_ACTION (ldata->pick_action));
135 gtk_action_group_remove_action (ls->action_group,
136 GTK_ACTION (ldata->pick_action));
137 /* TODO: make this work without wrecking the radio action group
138 * g_object_unref (G_OBJECT (ldata->pick_action));
139 * */
141 if (ldata->view_action)
143 gtk_action_disconnect_accelerator
144 (GTK_ACTION (ldata->view_action));
145 gtk_action_group_remove_action (ls->action_group,
146 GTK_ACTION (ldata->view_action));
147 g_object_unref (G_OBJECT (ldata->view_action));
149 gtk_tree_row_reference_free (ldata->rref);
150 if (ldata->accel_index >= 0)
151 ls->accel_available[ldata->accel_index] = TRUE;
152 g_free (ldata);
156 /*! \brief internal set-visibility function -- emits no signals */
157 static void
158 set_visibility (GHidLayerSelector *ls, GtkTreeIter *iter,
159 struct _layer *ldata, gboolean state)
161 gtk_list_store_set (ls->list_store, iter, VISIBLE_COL, state, -1);
163 if (ldata)
165 gtk_action_block_activate (GTK_ACTION (ldata->view_action));
166 gtk_check_menu_item_set_active
167 (GTK_CHECK_MENU_ITEM (ldata->view_item), state);
168 gtk_action_unblock_activate (GTK_ACTION (ldata->view_action));
172 /*! \brief Flip the visibility state of a given layer
173 * \par Function Description
174 * Changes the internal toggle state and menu checkbox state
175 * of the layer pointed to by iter. Emits a toggle-layer signal.
177 * \param [in] ls The selector to be acted on
178 * \param [in] iter A GtkTreeIter pointed at the relevant layer
179 * \param [in] emit Whether or not to emit a signal
181 static void
182 toggle_visibility (GHidLayerSelector *ls, GtkTreeIter *iter, gboolean emit)
184 gint user_id;
185 struct _layer *ldata;
186 gboolean toggle;
187 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), iter,
188 USER_ID_COL, &user_id, VISIBLE_COL, &toggle,
189 STRUCT_COL, &ldata, -1);
190 set_visibility (ls, iter, ldata, !toggle);
191 if (emit)
192 g_signal_emit (ls, ghid_layer_selector_signals[TOGGLE_LAYER_SIGNAL],
193 0, user_id);
196 /*! \brief Decide if a GtkListStore entry is a layer or separator */
197 static gboolean
198 tree_view_separator_func (GtkTreeModel *model, GtkTreeIter *iter,
199 gpointer data)
201 gboolean ret_val;
202 gtk_tree_model_get (model, iter, SEPARATOR_COL, &ret_val, -1);
203 return ret_val;
206 /*! \brief Decide if a GtkListStore entry may be selected */
207 static gboolean
208 tree_selection_func (GtkTreeSelection *selection, GtkTreeModel *model,
209 GtkTreePath *path, gboolean selected, gpointer data)
211 GtkTreeIter iter;
213 if (gtk_tree_model_get_iter (model, &iter, path))
215 gboolean selectable;
216 gtk_tree_model_get (model, &iter, SELECTABLE_COL, &selectable, -1);
217 return selectable;
220 return FALSE;
223 /* SIGNAL HANDLERS */
224 /*! \brief Callback for mouse-click: toggle visibility */
225 static gboolean
226 button_press_cb (GHidLayerSelector *ls, GdkEventButton *event)
228 /* Handle visibility independently to prevent changing the active
229 * layer, which will happen if we let this event propagate. */
230 GtkTreeViewColumn *column;
231 GtkTreePath *path;
233 /* Ignore the synthetic presses caused by double and tripple clicks, and
234 * also ignore all but left-clicks
236 if (event->type != GDK_BUTTON_PRESS ||
237 event->button != 1)
238 return TRUE;
240 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (ls),
241 event->x, event->y,
242 &path, &column, NULL, NULL))
244 GtkTreeIter iter;
245 gboolean selectable;
246 gboolean separator;
247 gtk_tree_model_get_iter (GTK_TREE_MODEL (ls->list_store), &iter, path);
248 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), &iter,
249 SELECTABLE_COL, &selectable,
250 SEPARATOR_COL, &separator, -1);
251 /* Toggle visibility for non-selectable layers no matter
252 * where you click. */
253 if (!separator && (column == ls->visibility_column || !selectable))
255 toggle_visibility (ls, &iter, TRUE);
256 return TRUE;
259 return FALSE;
262 /*! \brief Callback for layer selection change: sync menu, emit signal */
263 static void
264 selection_changed_cb (GtkTreeSelection *selection, GHidLayerSelector *ls)
266 GtkTreeIter iter;
267 if (gtk_tree_selection_get_selected (selection, NULL, &iter))
269 gint user_id;
270 struct _layer *ldata;
271 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), &iter,
272 STRUCT_COL, &ldata, USER_ID_COL, &user_id, -1);
274 if (ldata && ldata->pick_action)
276 gtk_action_block_activate (GTK_ACTION (ldata->pick_action));
277 gtk_radio_action_set_current_value (ldata->pick_action, user_id);
278 gtk_action_unblock_activate (GTK_ACTION (ldata->pick_action));
280 g_signal_emit (ls, ghid_layer_selector_signals[SELECT_LAYER_SIGNAL],
281 0, user_id);
285 /*! \brief Callback for when a layer name has been edited */
286 static void
287 layer_name_editing_started_cb (GtkCellRenderer *renderer,
288 GtkCellEditable *editable,
289 gchar *path,
290 gpointer user_data)
292 /* When editing begins, we need to detach PCB's accelerators
293 * so they don't steal all the user's keystrokes.
295 * XXX: We should not have to do this within a simple widget,
297 * and this quick hack workaround breaks the widget's
298 * abstraction from the rest of the application :(
300 ghid_remove_accel_groups (GTK_WINDOW (gport->top_window), ghidgui);
303 /*! \brief Callback for when layer name editing has been canceled */
304 static void
305 layer_name_editing_canceled_cb (GtkCellRenderer *renderer,
306 gpointer user_data)
308 /* Put PCB's accelerators back.
310 * XXX: We should not have to do this within a simple widget,
311 * and this quick hack workaround breaks the widget's
312 * abstraction from the rest of the application :(
314 ghid_install_accel_groups (GTK_WINDOW (gport->top_window), ghidgui);
317 /*! \brief Callback for when a layer name has been edited */
318 static void
319 layer_name_edited_cb (GtkCellRendererText *renderer,
320 gchar *path,
321 gchar *new_text,
322 gpointer user_data)
324 GHidLayerSelector *ls = user_data;
325 GtkTreeIter iter;
326 int user_id;
328 /* Put PCB's accelerators back.
330 * XXX: We should not have to do this within a simple widget,
331 * and this quick hack workaround breaks the widget's
332 * abstraction from the rest of the application :(
334 ghid_install_accel_groups (GTK_WINDOW (gport->top_window), ghidgui);
336 if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (ls->list_store), &iter, path))
337 return;
339 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
340 &iter,
341 USER_ID_COL, &user_id,
342 -1);
344 g_signal_emit (ls, ghid_layer_selector_signals[RENAME_LAYER_SIGNAL],
345 0, user_id, new_text);
349 /*! \brief Callback for menu actions: sync layer selection list, emit signal */
350 static void
351 menu_view_cb (GtkToggleAction *action, struct _layer *ldata)
353 GHidLayerSelector *ls;
354 GtkTreeModel *model = gtk_tree_row_reference_get_model (ldata->rref);
355 GtkTreePath *path = gtk_tree_row_reference_get_path (ldata->rref);
356 gboolean state = gtk_toggle_action_get_active (action);
357 GtkTreeIter iter;
358 gint user_id;
360 gtk_tree_model_get_iter (model, &iter, path);
361 gtk_list_store_set (GTK_LIST_STORE (model), &iter, VISIBLE_COL, state, -1);
362 gtk_tree_model_get (model, &iter, USER_ID_COL, &user_id, -1);
364 ls = g_object_get_data (G_OBJECT (model), "layer-selector");
365 g_signal_emit (ls, ghid_layer_selector_signals[TOGGLE_LAYER_SIGNAL],
366 0, user_id);
369 /*! \brief Callback for menu actions: sync layer selection list, emit signal */
370 static void
371 menu_pick_cb (GtkRadioAction *action, struct _layer *ldata)
373 /* We only care about the activation signal (as opposed to deactivation).
374 * A row we are /deactivating/ might not even exist anymore! */
375 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
377 GHidLayerSelector *ls;
378 GtkTreeModel *model = gtk_tree_row_reference_get_model (ldata->rref);
379 GtkTreePath *path = gtk_tree_row_reference_get_path (ldata->rref);
380 GtkTreeIter iter;
381 gint user_id;
383 gtk_tree_model_get_iter (model, &iter, path);
384 gtk_tree_model_get (model, &iter, USER_ID_COL, &user_id, -1);
386 ls = g_object_get_data (G_OBJECT (model), "layer-selector");
387 g_signal_handler_block (ls->selection, ls->selection_changed_sig_id);
388 gtk_tree_selection_select_path (ls->selection, path);
389 g_signal_handler_unblock (ls->selection, ls->selection_changed_sig_id);
390 g_signal_emit (ls, ghid_layer_selector_signals[SELECT_LAYER_SIGNAL],
391 0, user_id);
395 static void
396 ghid_layer_selector_init (GHidLayerSelector *ls)
398 int i;
399 GtkCellRenderer *renderer1;
400 GtkCellRenderer *renderer2;
401 GtkTreeViewColumn *opacity_col;
402 GtkTreeViewColumn *name_col;
404 renderer1 = ghid_cell_renderer_visibility_new ();
405 renderer2 = gtk_cell_renderer_text_new ();
406 g_object_set (renderer2, "editable-set", TRUE, NULL);
407 g_signal_connect (renderer2, "editing-started",
408 G_CALLBACK (layer_name_editing_started_cb), ls);
409 g_signal_connect (renderer2, "editing-canceled",
410 G_CALLBACK (layer_name_editing_canceled_cb), ls);
411 g_signal_connect (renderer2, "edited",
412 G_CALLBACK (layer_name_edited_cb), ls);
414 opacity_col = gtk_tree_view_column_new_with_attributes ("",
415 renderer1,
416 "active", VISIBLE_COL,
417 "color", COLOR_COL,
418 NULL);
419 name_col = gtk_tree_view_column_new_with_attributes ("",
420 renderer2,
421 "text", TEXT_COL,
422 "font", FONT_COL,
423 "editable", EDITABLE_COL,
424 NULL);
426 ls->list_store = gtk_list_store_new (N_COLS,
427 /* STRUCT_COL */ G_TYPE_POINTER,
428 /* USER_ID_COL */ G_TYPE_INT,
429 /* VISIBLE_COL */ G_TYPE_BOOLEAN,
430 /* COLOR_COL */ G_TYPE_STRING,
431 /* TEXT_COL */ G_TYPE_STRING,
432 /* FONT_COL */ G_TYPE_STRING,
433 /* EDITABLE_COL */ G_TYPE_BOOLEAN,
434 /* ACTIVATABLE_COL */ G_TYPE_BOOLEAN,
435 /* SEPARATOR_COL */ G_TYPE_BOOLEAN);
437 gtk_tree_view_insert_column (GTK_TREE_VIEW (ls), opacity_col, -1);
438 gtk_tree_view_insert_column (GTK_TREE_VIEW (ls), name_col, -1);
439 gtk_tree_view_set_model (GTK_TREE_VIEW (ls), GTK_TREE_MODEL (ls->list_store));
440 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ls), FALSE);
442 ls->visibility_column = opacity_col;
443 ls->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ls));
444 ls->accel_group = gtk_accel_group_new ();
445 ls->action_group = gtk_action_group_new ("LayerSelector");
446 ls->n_actions = 0;
448 for (i = 0; i < 20; ++i)
449 ls->accel_available[i] = TRUE;
451 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (ls),
452 tree_view_separator_func,
453 NULL, NULL);
454 gtk_tree_selection_set_select_function (ls->selection, tree_selection_func,
455 NULL, NULL);
456 gtk_tree_selection_set_mode (ls->selection, GTK_SELECTION_BROWSE);
458 g_object_set_data (G_OBJECT (ls->list_store), "layer-selector", ls);
459 g_signal_connect (ls, "button_press_event",
460 G_CALLBACK (button_press_cb), NULL);
461 ls->selection_changed_sig_id =
462 g_signal_connect (ls->selection, "changed",
463 G_CALLBACK (selection_changed_cb), ls);
466 static void
467 ghid_layer_selector_class_init (GHidLayerSelectorClass *klass)
469 GObjectClass *object_class = (GObjectClass *) klass;
471 ghid_layer_selector_signals[SELECT_LAYER_SIGNAL] =
472 g_signal_new ("select-layer",
473 G_TYPE_FROM_CLASS (klass),
474 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
475 G_STRUCT_OFFSET (GHidLayerSelectorClass, select_layer),
476 NULL, NULL,
477 g_cclosure_marshal_VOID__INT, G_TYPE_NONE,
478 1, G_TYPE_INT);
479 ghid_layer_selector_signals[TOGGLE_LAYER_SIGNAL] =
480 g_signal_new ("toggle-layer",
481 G_TYPE_FROM_CLASS (klass),
482 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
483 G_STRUCT_OFFSET (GHidLayerSelectorClass, toggle_layer),
484 NULL, NULL,
485 g_cclosure_marshal_VOID__INT, G_TYPE_NONE,
486 1, G_TYPE_INT);
487 ghid_layer_selector_signals[RENAME_LAYER_SIGNAL] =
488 g_signal_new ("rename-layer",
489 G_TYPE_FROM_CLASS (klass),
490 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
491 G_STRUCT_OFFSET (GHidLayerSelectorClass, rename_layer),
492 NULL, NULL,
493 g_cclosure_user_marshal_VOID__INT_STRING, G_TYPE_NONE,
494 2, G_TYPE_INT, G_TYPE_STRING);
496 object_class->finalize = ghid_layer_selector_finalize;
499 /*! \brief Clean up object before garbage collection
501 static void
502 ghid_layer_selector_finalize (GObject *object)
504 GtkTreeIter iter;
505 GHidLayerSelector *ls = (GHidLayerSelector *) object;
507 g_object_unref (ls->accel_group);
508 g_object_unref (ls->action_group);
510 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
513 struct _layer *ldata;
514 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
515 &iter, STRUCT_COL, &ldata, -1);
516 free_ldata (ls, ldata);
518 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
520 G_OBJECT_CLASS (ghid_layer_selector_parent_class)->finalize (object);
523 /* PUBLIC FUNCTIONS */
524 GType
525 ghid_layer_selector_get_type (void)
527 static GType ls_type = 0;
529 if (!ls_type)
531 const GTypeInfo ls_info =
533 sizeof (GHidLayerSelectorClass),
534 NULL, /* base_init */
535 NULL, /* base_finalize */
536 (GClassInitFunc) ghid_layer_selector_class_init,
537 NULL, /* class_finalize */
538 NULL, /* class_data */
539 sizeof (GHidLayerSelector),
540 0, /* n_preallocs */
541 (GInstanceInitFunc) ghid_layer_selector_init,
544 ls_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
545 "GHidLayerSelector",
546 &ls_info,
550 return ls_type;
553 /*! \brief Create a new GHidLayerSelector
555 * \return a freshly-allocated GHidLayerSelector.
557 GtkWidget *
558 ghid_layer_selector_new (void)
560 return GTK_WIDGET (g_object_new (GHID_LAYER_SELECTOR_TYPE, NULL));
563 /*! \brief Add a layer to a GHidLayerSelector.
564 * \par Function Description
565 * This function adds an entry to a GHidLayerSelector, which will
566 * appear in the layer-selection list as well as visibility and selection
567 * menus (assuming this is a selectable layer). For the first 20 layers,
568 * keyboard accelerators will be added for selection/visibility toggling.
570 * If the user_id passed already exists in the layer selector, that layer
571 * will have its data overwritten with the new stuff.
573 * \param [in] ls The selector to be acted on
574 * \param [in] user_id An ID used to identify the layer; will be passed to selection/visibility callbacks
575 * \param [in] name The name of the layer; will be used on selector and menus
576 * \param [in] color_string The color of the layer on selector
577 * \param [in] visibile Whether the layer is visible
578 * \param [in] selectable Whether the layer appears in menus and can be selected
579 * \param [in] renameable Whether the layer is renameable
581 void
582 ghid_layer_selector_add_layer (GHidLayerSelector *ls,
583 gint user_id,
584 const gchar *name,
585 const gchar *color_string,
586 gboolean visible,
587 gboolean selectable,
588 gboolean renameable)
590 struct _layer *new_layer = NULL;
591 gchar *pname, *vname;
592 gboolean new_iter = TRUE;
593 gboolean last_selectable = TRUE;
594 GtkTreePath *path;
595 GtkTreeIter iter;
596 int i;
598 /* Look for existing layer with this ID */
599 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter))
602 gboolean is_sep;
603 gboolean this_selectable;
604 gint read_id;
606 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
607 &iter, USER_ID_COL, &read_id,
608 SEPARATOR_COL, &is_sep,
609 SELECTABLE_COL, &this_selectable, -1);
611 if (is_sep)
612 continue;
614 last_selectable = this_selectable;
615 if (read_id == user_id)
617 new_iter = FALSE;
618 break;
621 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
623 /* Handle separator addition */
624 if (new_iter)
626 if (selectable != last_selectable)
628 /* Add separator between selectable / non-selectable boundaries */
629 gtk_list_store_append (ls->list_store, &iter);
630 gtk_list_store_set (ls->list_store, &iter,
631 STRUCT_COL, NULL,
632 SEPARATOR_COL, TRUE, -1);
634 /* Create new layer */
635 gtk_list_store_append (ls->list_store, &iter);
637 else
639 /* If the row exists, we clear out its ldata to create
640 * a new action, accelerator and menu item. */
641 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), &iter,
642 STRUCT_COL, &new_layer, -1);
643 free_ldata (ls, new_layer);
646 new_layer = malloc (sizeof (*new_layer));
648 gtk_list_store_set (ls->list_store,
649 &iter,
650 STRUCT_COL, new_layer,
651 USER_ID_COL, user_id,
652 VISIBLE_COL, visible,
653 COLOR_COL, color_string,
654 TEXT_COL, name,
655 FONT_COL, selectable ? NULL : "Italic",
656 EDITABLE_COL, renameable,
657 SELECTABLE_COL, selectable,
658 SEPARATOR_COL, FALSE,
659 -1);
661 /* -- Setup new actions -- */
662 vname = g_strdup_printf ("LayerView%d", ls->n_actions);
663 pname = g_strdup_printf ("LayerPick%d", ls->n_actions);
665 /* Create row reference for actions */
666 path = gtk_tree_model_get_path (GTK_TREE_MODEL (ls->list_store), &iter);
667 new_layer->rref = gtk_tree_row_reference_new
668 (GTK_TREE_MODEL (ls->list_store), path);
669 gtk_tree_path_free (path);
671 /* Create selection action */
672 if (selectable)
674 new_layer->pick_action
675 = gtk_radio_action_new (pname, name, NULL, NULL, user_id);
676 gtk_radio_action_set_group (new_layer->pick_action, ls->radio_group);
677 ls->radio_group = gtk_radio_action_get_group (new_layer->pick_action);
679 else
680 new_layer->pick_action = NULL;
682 /* Create visibility action */
683 new_layer->view_action = gtk_toggle_action_new (vname, name, NULL, NULL);
684 gtk_toggle_action_set_active (new_layer->view_action, visible);
686 /* Determine keyboard accelerators */
687 for (i = 0; i < 20; ++i)
688 if (ls->accel_available[i])
689 break;
690 if (i < 20)
692 /* Map 1-0 to actions 1-10 (with '0' meaning 10) */
693 gchar *accel1 = g_strdup_printf ("%s%d",
694 i < 10 ? "" : "<Alt>",
695 (i + 1) % 10);
696 gchar *accel2 = g_strdup_printf ("<Ctrl>%s%d",
697 i < 10 ? "" : "<Alt>",
698 (i + 1) % 10);
700 if (selectable)
702 GtkAction *action = GTK_ACTION (new_layer->pick_action);
703 gtk_action_set_accel_group (action, ls->accel_group);
704 gtk_action_group_add_action_with_accel (ls->action_group,
705 action,
706 accel1);
707 gtk_action_connect_accelerator (action);
708 g_signal_connect (G_OBJECT (action), "activate",
709 G_CALLBACK (menu_pick_cb), new_layer);
711 gtk_action_set_accel_group (GTK_ACTION (new_layer->view_action),
712 ls->accel_group);
713 gtk_action_group_add_action_with_accel
714 (ls->action_group, GTK_ACTION (new_layer->view_action), accel2);
715 gtk_action_connect_accelerator (GTK_ACTION (new_layer->view_action));
716 g_signal_connect (G_OBJECT (new_layer->view_action), "activate",
717 G_CALLBACK (menu_view_cb), new_layer);
719 ls->accel_available[i] = FALSE;
720 new_layer->accel_index = i;
721 g_free (accel2);
722 g_free (accel1);
724 else
726 new_layer->accel_index = -1;
728 /* finalize new layer struct */
729 new_layer->pick_item = new_layer->view_item = NULL;
731 /* cleanup */
732 g_free (vname);
733 g_free (pname);
735 ls->n_actions++;
738 /*! \brief Install the "Current Layer" menu items for a layer selector
739 * \par Function Description
740 * Takes a menu shell and installs menu items for layer selection in
741 * the shell, at the given position.
743 * \param [in] ls The selector to be acted on
744 * \param [in] shell The menu to install the items in
745 * \param [in] pos The position in the menu to install items
747 * \return the number of items installed
749 gint
750 ghid_layer_selector_install_pick_items (GHidLayerSelector *ls,
751 GtkMenuShell *shell, gint pos)
753 GtkTreeIter iter;
754 int n = 0;
756 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
759 struct _layer *ldata;
760 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
761 &iter, STRUCT_COL, &ldata, -1);
762 if (ldata && ldata->pick_action)
764 GtkAction *action = GTK_ACTION (ldata->pick_action);
765 ldata->pick_item = gtk_action_create_menu_item (action);
766 gtk_menu_shell_insert (shell, ldata->pick_item, pos + n);
767 ++n;
770 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
772 return n;
775 /*! \brief Install the "Shown Layers" menu items for a layer selector
776 * \par Function Description
777 * Takes a menu shell and installs menu items for layer selection in
778 * the shell, at the given position.
780 * \param [in] ls The selector to be acted on
781 * \param [in] shell The menu to install the items in
782 * \param [in] pos The position in the menu to install items
784 * \return the number of items installed
786 gint
787 ghid_layer_selector_install_view_items (GHidLayerSelector *ls,
788 GtkMenuShell *shell, gint pos)
790 GtkTreeIter iter;
791 int n = 0;
793 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
796 struct _layer *ldata;
797 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
798 &iter, STRUCT_COL, &ldata, -1);
799 if (ldata && ldata->view_action)
801 GtkAction *action = GTK_ACTION (ldata->view_action);
802 ldata->view_item = gtk_action_create_menu_item (action);
803 gtk_menu_shell_insert (shell, ldata->view_item, pos + n);
804 ++n;
807 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
809 return n;
812 /*! \brief Returns the GtkAccelGroup of a layer selector
813 * \par Function Description
815 * \param [in] ls The selector to be acted on
817 * \return the accel group of the selector
819 GtkAccelGroup *
820 ghid_layer_selector_get_accel_group (GHidLayerSelector *ls)
822 return ls->accel_group;
825 /*! \brief used internally */
826 static gboolean
827 toggle_foreach_func (GtkTreeModel *model, GtkTreePath *path,
828 GtkTreeIter *iter, gpointer data)
830 gint id;
831 GHidLayerSelector *ls = g_object_get_data (G_OBJECT (model),
832 "layer-selector");
834 gtk_tree_model_get (model, iter, USER_ID_COL, &id, -1);
835 if (id == *(gint *) data)
837 toggle_visibility (ls, iter, TRUE);
838 return TRUE;
840 return FALSE;
843 /*! \brief Toggle a layer's visibility
844 * \par Function Description
845 * Toggle the layer indicated by user_id, emitting a layer-toggle signal.
847 * \param [in] ls The selector to be acted on
848 * \param [in] user_id The ID of the layer to be affected
850 void
851 ghid_layer_selector_toggle_layer (GHidLayerSelector *ls, gint user_id)
853 gtk_tree_model_foreach (GTK_TREE_MODEL (ls->list_store),
854 toggle_foreach_func, &user_id);
857 /*! \brief used internally */
858 static gboolean
859 select_foreach_func (GtkTreeModel *model, GtkTreePath *path,
860 GtkTreeIter *iter, gpointer data)
862 gint id;
863 GHidLayerSelector *ls = g_object_get_data (G_OBJECT (model),
864 "layer-selector");
866 gtk_tree_model_get (model, iter, USER_ID_COL, &id, -1);
867 if (id == *(gint *) data)
869 gtk_tree_selection_select_path (ls->selection, path);
870 return TRUE;
872 return FALSE;
875 /*! \brief Select a layer
876 * \par Function Description
877 * Select the layer indicated by user_id, emitting a layer-select signal.
879 * \param [in] ls The selector to be acted on
880 * \param [in] user_id The ID of the layer to be affected
882 void
883 ghid_layer_selector_select_layer (GHidLayerSelector *ls, gint user_id)
885 gtk_tree_model_foreach (GTK_TREE_MODEL (ls->list_store),
886 select_foreach_func, &user_id);
889 /*! \brief Selects the next visible layer
890 * \par Function Description
891 * Used to ensure hidden layers are not active; if the active layer is
892 * visible, this function is a noop. Otherwise, it will look for the
893 * next layer that IS visible, and select that. Failing that, it will
894 * return FALSE.
896 * \param [in] ls The selector to be acted on
898 * \return TRUE on success, FALSE if all selectable layers are hidden
900 gboolean
901 ghid_layer_selector_select_next_visible (GHidLayerSelector *ls)
903 GtkTreeIter iter;
904 if (gtk_tree_selection_get_selected (ls->selection, NULL, &iter))
906 /* Scan forward, looking for selectable iter */
909 gboolean visible;
910 gboolean selectable;
912 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
913 &iter, VISIBLE_COL, &visible,
914 SELECTABLE_COL, &selectable, -1);
915 if (visible && selectable)
917 gtk_tree_selection_select_iter (ls->selection, &iter);
918 return TRUE;
921 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
922 /* Move iter to start, and repeat. */
923 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
926 gboolean visible;
927 gboolean selectable;
929 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
930 &iter, VISIBLE_COL, &visible,
931 SELECTABLE_COL, &selectable, -1);
932 if (visible && selectable)
934 gtk_tree_selection_select_iter (ls->selection, &iter);
935 return TRUE;
938 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
939 /* Failing this, just emit a selected signal on the original layer. */
940 selection_changed_cb (ls->selection, ls);
942 /* If we get here, nothing is selectable, so fail. */
943 return FALSE;
946 /*! \brief Makes the selected layer visible
947 * \par Function Description
948 * Used to ensure hidden layers are not active; un-hides the currently
949 * selected layer.
951 * \param [in] ls The selector to be acted on
953 void
954 ghid_layer_selector_make_selected_visible (GHidLayerSelector *ls)
956 GtkTreeIter iter;
957 if (gtk_tree_selection_get_selected (ls->selection, NULL, &iter))
959 gboolean visible;
960 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
961 &iter, VISIBLE_COL, &visible, -1);
962 if (!visible)
963 toggle_visibility (ls, &iter, FALSE);
967 /*! \brief Sets the colors of all layers in a layer-selector
968 * \par Function Description
969 * Updates the colors of a layer selector via a callback mechanism:
970 * the user_id of each layer is passed to the callback function,
971 * which returns a color string to update the layer's color, or NULL
972 * to leave it alone.
974 * \param [in] ls The selector to be acted on
975 * \param [in] callback Takes the user_id of the layer and returns a color string
977 void
978 ghid_layer_selector_update_colors (GHidLayerSelector *ls,
979 const gchar *(*callback)(int user_id))
981 GtkTreeIter iter;
982 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
985 gint user_id;
986 const gchar *new_color;
987 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
988 &iter, USER_ID_COL, &user_id, -1);
989 new_color = callback (user_id);
990 if (new_color != NULL)
991 gtk_list_store_set (ls->list_store, &iter, COLOR_COL, new_color, -1);
993 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
996 /*! \brief Deletes layers from a layer selector
997 * \par Function Description
998 * Deletes layers according to a callback function: a return value of TRUE
999 * means delete, FALSE means leave it alone. Do not try to delete all layers
1000 * using this function; with nothing left to select, pcb will likely go into
1001 * an infinite recursion between hid_action() and g_signal().
1003 * Separators will be deleted if the layer AFTER them is deleted.
1005 * \param [in] ls The selector to be acted on
1006 * \param [in] callback Takes the user_id of the layer and returns a boolean
1008 void
1009 ghid_layer_selector_delete_layers (GHidLayerSelector *ls,
1010 gboolean (*callback)(int user_id))
1012 GtkTreeIter iter, last_iter;
1014 gboolean iter_valid =
1015 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
1016 while (iter_valid)
1018 struct _layer *ldata;
1019 gboolean sep, was_sep = FALSE;
1020 gint user_id;
1022 /* Find next iter to delete */
1023 while (iter_valid)
1025 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
1026 &iter, USER_ID_COL, &user_id,
1027 STRUCT_COL, &ldata, SEPARATOR_COL, &sep, -1);
1028 if (!sep && callback (user_id))
1029 break;
1031 /* save iter in case it's a bad separator */
1032 was_sep = sep;
1033 last_iter = iter;
1034 /* iterate */
1035 iter_valid =
1036 gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter);
1039 if (iter_valid)
1041 /* remove preceeding separator */
1042 if (was_sep)
1043 gtk_list_store_remove (ls->list_store, &last_iter);
1045 /*** remove row ***/
1046 iter_valid = gtk_list_store_remove (ls->list_store, &iter);
1047 free_ldata (ls, ldata);
1049 last_iter = iter;
1053 /*! \brief Sets the visibility toggle-state of all layers
1054 * \par Function Description
1055 * Shows layers according to a callback function: a return value of TRUE
1056 * means show, FALSE means hide.
1058 * \param [in] ls The selector to be acted on
1059 * \param [in] callback Takes the user_id of the layer and returns a boolean
1061 void
1062 ghid_layer_selector_show_layers (GHidLayerSelector *ls,
1063 gboolean (*callback)(int user_id))
1065 GtkTreeIter iter;
1066 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
1069 struct _layer *ldata;
1070 gboolean sep;
1071 gint user_id;
1073 gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
1074 &iter, USER_ID_COL, &user_id,
1075 STRUCT_COL, &ldata,
1076 SEPARATOR_COL, &sep, -1);
1077 if (!sep)
1078 set_visibility (ls, &iter, ldata, callback (user_id));
1080 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));