pcb 4.1.1
An interactive printed circuit board layout editor.

ghid-layer-selector.c

Go to the documentation of this file.
00001 
00009 #include <glib.h>
00010 #include <glib-object.h>
00011 #include <gdk/gdkkeysyms.h>
00012 #include <gtk/gtk.h>
00013 
00014 #include "gtkhid.h"
00015 #include "gui.h"
00016 #include "pcb-printf.h"
00017 
00018 #include "ghid-layer-selector.h"
00019 #include "ghid-cell-renderer-visibility.h"
00020 
00021 #define INITIAL_ACTION_MAX      40
00022 
00023 /* Forward dec'ls */
00024 struct _layer;
00025 static void ghid_layer_selector_finalize (GObject *object);
00026 static void menu_pick_cb (GtkRadioAction *action, struct _layer *ldata);
00027 
00029 enum {
00030   SELECT_LAYER_SIGNAL,
00031   TOGGLE_LAYER_SIGNAL,
00032   RENAME_LAYER_SIGNAL,
00033   LAST_SIGNAL
00034 };
00035 
00037 enum {
00038   STRUCT_COL,
00039   USER_ID_COL,
00040   VISIBLE_COL,
00041   COLOR_COL,
00042   TEXT_COL,
00043   FONT_COL,
00044   EDITABLE_COL,
00045   SELECTABLE_COL,
00046   SEPARATOR_COL,
00047   N_COLS
00048 };
00049 
00050 static GtkTreeView *ghid_layer_selector_parent_class;
00051 static guint ghid_layer_selector_signals[LAST_SIGNAL] = { 0 };
00052 
00053 struct _GHidLayerSelector
00054 {
00055   GtkTreeView parent;
00056 
00057   GtkListStore *list_store;
00058   GtkTreeSelection *selection;
00059   GtkTreeViewColumn *visibility_column;
00060 
00061   GtkActionGroup *action_group;
00062   GtkAccelGroup *accel_group;
00063 
00064   GSList *radio_group;
00065   int n_actions;
00066 
00067   gboolean accel_available[20];
00068 
00069   gulong selection_changed_sig_id;
00070 };
00071 
00072 struct _GHidLayerSelectorClass
00073 {
00074   GtkTreeViewClass parent_class;
00075 
00076   void (* select_layer) (GHidLayerSelector *, gint);
00077   void (* toggle_layer) (GHidLayerSelector *, gint);
00078   void (* rename_layer) (GHidLayerSelector *, gint, gchar *);
00079 };
00080 
00081 struct _layer
00082 {
00083   gint accel_index;   /* Index into ls->accel_available */
00084   GtkWidget *pick_item;
00085   GtkWidget *view_item;
00086   GtkToggleAction *view_action;
00087   GtkRadioAction  *pick_action;
00088   GtkTreeRowReference *rref;
00089 };
00090 
00091 static void
00092 g_cclosure_user_marshal_VOID__INT_STRING (GClosure     *closure,
00093                                           GValue       *return_value G_GNUC_UNUSED,
00094                                           guint         n_param_values,
00095                                           const GValue *param_values,
00096                                           gpointer      invocation_hint G_GNUC_UNUSED,
00097                                           gpointer      marshal_data)
00098 {
00099   typedef void (*GMarshalFunc_VOID__INT_STRING) (gpointer     data1,
00100                                                  gint         arg_1,
00101                                                  gpointer     arg_2,
00102                                                  gpointer     data2);
00103   register GMarshalFunc_VOID__INT_STRING callback;
00104   register GCClosure *cc = (GCClosure*) closure;
00105   register gpointer data1, data2;
00106 
00107   g_return_if_fail (n_param_values == 3);
00108 
00109   if (G_CCLOSURE_SWAP_DATA (closure))
00110     {
00111       data1 = closure->data;
00112       data2 = g_value_peek_pointer (param_values + 0);
00113     }
00114   else
00115     {
00116       data1 = g_value_peek_pointer (param_values + 0);
00117       data2 = closure->data;
00118     }
00119   callback = (GMarshalFunc_VOID__INT_STRING) (marshal_data ? marshal_data : cc->callback);
00120 
00121   callback (data1,
00122             g_value_get_int (param_values + 1),
00123             (char *)g_value_get_string (param_values + 2),
00124             data2);
00125 }
00126 
00128 static void
00129 free_ldata (GHidLayerSelector *ls, struct _layer *ldata)
00130 {
00131   if (ldata->pick_action)
00132     {
00133       gtk_action_disconnect_accelerator
00134         (GTK_ACTION (ldata->pick_action));
00135       gtk_action_group_remove_action (ls->action_group,
00136                                     GTK_ACTION (ldata->pick_action));
00137 /* TODO: make this work without wrecking the radio action group
00138  *           g_object_unref (G_OBJECT (ldata->pick_action)); 
00139  *                   */
00140     }
00141   if (ldata->view_action)
00142     {
00143       gtk_action_disconnect_accelerator
00144         (GTK_ACTION (ldata->view_action));
00145       gtk_action_group_remove_action (ls->action_group,
00146                             GTK_ACTION (ldata->view_action));
00147       g_object_unref (G_OBJECT (ldata->view_action));
00148     }
00149   gtk_tree_row_reference_free (ldata->rref);
00150   if (ldata->accel_index >= 0)
00151     ls->accel_available[ldata->accel_index] = TRUE;
00152   g_free (ldata);
00153 
00154 }
00155 
00157 static void
00158 set_visibility (GHidLayerSelector *ls, GtkTreeIter *iter,
00159                 struct _layer *ldata, gboolean state)
00160 {
00161   gtk_list_store_set (ls->list_store, iter, VISIBLE_COL, state, -1);
00162   
00163   if (ldata)
00164     {
00165       gtk_action_block_activate (GTK_ACTION (ldata->view_action));
00166       gtk_check_menu_item_set_active
00167         (GTK_CHECK_MENU_ITEM (ldata->view_item), state);
00168       gtk_action_unblock_activate (GTK_ACTION (ldata->view_action));
00169     }
00170 }
00171 
00181 static void
00182 toggle_visibility (GHidLayerSelector *ls, GtkTreeIter *iter, gboolean emit)
00183 {
00184   gint user_id;
00185   struct _layer *ldata;
00186   gboolean toggle;
00187   gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), iter,
00188                       USER_ID_COL, &user_id, VISIBLE_COL, &toggle,
00189                       STRUCT_COL, &ldata, -1);
00190   set_visibility (ls, iter, ldata, !toggle);
00191   if (emit)
00192     g_signal_emit (ls, ghid_layer_selector_signals[TOGGLE_LAYER_SIGNAL],
00193                    0, user_id);
00194 }
00195 
00197 static gboolean
00198 tree_view_separator_func (GtkTreeModel *model, GtkTreeIter *iter,
00199                           gpointer data)
00200 {
00201   gboolean ret_val;
00202   gtk_tree_model_get (model, iter, SEPARATOR_COL, &ret_val, -1);
00203   return ret_val;
00204 }
00205 
00207 static gboolean
00208 tree_selection_func (GtkTreeSelection *selection, GtkTreeModel *model,
00209                      GtkTreePath *path, gboolean selected, gpointer data)
00210 {
00211   GtkTreeIter iter;
00212 
00213   if (gtk_tree_model_get_iter (model, &iter, path))
00214     {
00215       gboolean selectable;
00216       gtk_tree_model_get (model, &iter, SELECTABLE_COL, &selectable, -1);
00217       return selectable;
00218     }
00219 
00220   return FALSE;
00221 }
00222 
00223 /* SIGNAL HANDLERS */
00225 static gboolean
00226 button_press_cb (GHidLayerSelector *ls, GdkEventButton *event)
00227 {
00228   /* Handle visibility independently to prevent changing the active
00229    *  layer, which will happen if we let this event propagate.  */
00230   GtkTreeViewColumn *column;
00231   GtkTreePath *path;
00232 
00233   /* Ignore the synthetic presses caused by double and tripple clicks, and
00234    * also ignore all but left-clicks
00235    */
00236   if (event->type != GDK_BUTTON_PRESS ||
00237       event->button != 1)
00238     return TRUE;
00239 
00240   if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (ls),
00241                                      event->x, event->y,
00242                                      &path, &column, NULL, NULL))
00243     {
00244       GtkTreeIter iter;
00245       gboolean selectable;
00246       gboolean separator;
00247       gtk_tree_model_get_iter (GTK_TREE_MODEL (ls->list_store), &iter, path);
00248       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), &iter,
00249                           SELECTABLE_COL, &selectable,
00250                           SEPARATOR_COL, &separator, -1);
00251       /* Toggle visibility for non-selectable layers no matter
00252        *  where you click. */
00253       if (!separator && (column == ls->visibility_column || !selectable))
00254         {
00255           toggle_visibility (ls, &iter, TRUE);
00256           return TRUE; 
00257         }
00258     }
00259   return FALSE;
00260 }
00261 
00263 static void
00264 selection_changed_cb (GtkTreeSelection *selection, GHidLayerSelector *ls)
00265 {
00266   GtkTreeIter iter;
00267   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
00268     {
00269       gint user_id;
00270       struct _layer *ldata;
00271       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), &iter,
00272                           STRUCT_COL, &ldata, USER_ID_COL, &user_id, -1);
00273 
00274       if (ldata && ldata->pick_action)
00275         {
00276           gtk_action_block_activate (GTK_ACTION (ldata->pick_action));
00277           gtk_radio_action_set_current_value (ldata->pick_action, user_id);
00278           gtk_action_unblock_activate (GTK_ACTION (ldata->pick_action));
00279         }
00280       g_signal_emit (ls, ghid_layer_selector_signals[SELECT_LAYER_SIGNAL],
00281                      0, user_id);
00282     }
00283 }
00284 
00286 static void
00287 layer_name_editing_started_cb (GtkCellRenderer *renderer,
00288                                GtkCellEditable *editable,
00289                                gchar           *path,
00290                                gpointer         user_data)
00291 {
00292   /* When editing begins, we need to detach PCB's accelerators
00293    * so they don't steal all the user's keystrokes.
00294    *
00295    * XXX: We should not have to do this within a simple widget,
00296    *
00297    *      and this quick hack workaround breaks the widget's
00298    *      abstraction from the rest of the application :(
00299    */
00300   ghid_remove_accel_groups (GTK_WINDOW (gport->top_window), ghidgui);
00301 }
00302 
00304 static void
00305 layer_name_editing_canceled_cb (GtkCellRenderer *renderer,
00306                                  gpointer         user_data)
00307 {
00308   /* Put PCB's accelerators back.
00309    *
00310    * XXX: We should not have to do this within a simple widget,
00311    *      and this quick hack workaround breaks the widget's
00312    *      abstraction from the rest of the application :(
00313    */
00314   ghid_install_accel_groups (GTK_WINDOW (gport->top_window), ghidgui);
00315 }
00316 
00318 static void
00319 layer_name_edited_cb (GtkCellRendererText *renderer,
00320                       gchar               *path,
00321                       gchar               *new_text,
00322                       gpointer             user_data)
00323 {
00324   GHidLayerSelector *ls = user_data;
00325   GtkTreeIter iter;
00326   int user_id;
00327 
00328   /* Put PCB's accelerators back.
00329    *
00330    * XXX: We should not have to do this within a simple widget,
00331    *      and this quick hack workaround breaks the widget's
00332    *      abstraction from the rest of the application :(
00333    */
00334   ghid_install_accel_groups (GTK_WINDOW (gport->top_window), ghidgui);
00335 
00336   if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (ls->list_store), &iter, path))
00337     return;
00338 
00339   gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
00340                       &iter,
00341                       USER_ID_COL, &user_id,
00342                       -1);
00343 
00344   g_signal_emit (ls, ghid_layer_selector_signals[RENAME_LAYER_SIGNAL],
00345                  0, user_id, new_text);
00346 }
00347 
00348 
00350 static void
00351 menu_view_cb (GtkToggleAction *action, struct _layer *ldata)
00352 {
00353   GHidLayerSelector *ls;
00354   GtkTreeModel *model = gtk_tree_row_reference_get_model (ldata->rref);
00355   GtkTreePath *path = gtk_tree_row_reference_get_path (ldata->rref);
00356   gboolean state = gtk_toggle_action_get_active (action);
00357   GtkTreeIter iter;
00358   gint user_id;
00359 
00360   gtk_tree_model_get_iter (model, &iter, path);
00361   gtk_list_store_set (GTK_LIST_STORE (model), &iter, VISIBLE_COL, state, -1);
00362   gtk_tree_model_get (model, &iter, USER_ID_COL, &user_id, -1);
00363 
00364   ls = g_object_get_data (G_OBJECT (model), "layer-selector");
00365   g_signal_emit (ls, ghid_layer_selector_signals[TOGGLE_LAYER_SIGNAL],
00366                  0, user_id);
00367 }
00368 
00370 static void
00371 menu_pick_cb (GtkRadioAction *action, struct _layer *ldata)
00372 {
00373   /* We only care about the activation signal (as opposed to deactivation).
00374    * A row we are /deactivating/ might not even exist anymore! */
00375   if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
00376     {
00377       GHidLayerSelector *ls;
00378       GtkTreeModel *model = gtk_tree_row_reference_get_model (ldata->rref);
00379       GtkTreePath *path = gtk_tree_row_reference_get_path (ldata->rref);
00380       GtkTreeIter iter;
00381       gint user_id;
00382 
00383       gtk_tree_model_get_iter (model, &iter, path);
00384       gtk_tree_model_get (model, &iter, USER_ID_COL, &user_id, -1);
00385 
00386       ls = g_object_get_data (G_OBJECT (model), "layer-selector");
00387       g_signal_handler_block (ls->selection, ls->selection_changed_sig_id);
00388       gtk_tree_selection_select_path (ls->selection, path);
00389       g_signal_handler_unblock (ls->selection, ls->selection_changed_sig_id);
00390       g_signal_emit (ls, ghid_layer_selector_signals[SELECT_LAYER_SIGNAL],
00391                      0, user_id);
00392     }
00393 }
00394 
00395 static void
00396 ghid_layer_selector_init (GHidLayerSelector *ls)
00397 {
00398   int i;
00399   GtkCellRenderer *renderer1;
00400   GtkCellRenderer *renderer2;
00401   GtkTreeViewColumn *opacity_col;
00402   GtkTreeViewColumn *name_col;
00403 
00404   renderer1 = ghid_cell_renderer_visibility_new ();
00405   renderer2 = gtk_cell_renderer_text_new ();
00406   g_object_set (renderer2, "editable-set", TRUE, NULL);
00407   g_signal_connect (renderer2, "editing-started",
00408                     G_CALLBACK (layer_name_editing_started_cb), ls);
00409   g_signal_connect (renderer2, "editing-canceled",
00410                     G_CALLBACK (layer_name_editing_canceled_cb), ls);
00411   g_signal_connect (renderer2, "edited",
00412                     G_CALLBACK (layer_name_edited_cb), ls);
00413 
00414   opacity_col = gtk_tree_view_column_new_with_attributes ("",
00415                                                           renderer1,
00416                                                           "active", VISIBLE_COL,
00417                                                           "color",  COLOR_COL,
00418                                                           NULL);
00419   name_col = gtk_tree_view_column_new_with_attributes ("",
00420                                                        renderer2,
00421                                                        "text", TEXT_COL,
00422                                                        "font", FONT_COL,
00423                                                        "editable", EDITABLE_COL,
00424                                                        NULL);
00425 
00426   ls->list_store = gtk_list_store_new (N_COLS,
00427                  /* STRUCT_COL      */ G_TYPE_POINTER,
00428                  /* USER_ID_COL     */ G_TYPE_INT,
00429                  /* VISIBLE_COL     */ G_TYPE_BOOLEAN,
00430                  /* COLOR_COL       */ G_TYPE_STRING,
00431                  /* TEXT_COL        */ G_TYPE_STRING,
00432                  /* FONT_COL        */ G_TYPE_STRING,
00433                  /* EDITABLE_COL    */ G_TYPE_BOOLEAN,
00434                  /* ACTIVATABLE_COL */ G_TYPE_BOOLEAN,
00435                  /* SEPARATOR_COL   */ G_TYPE_BOOLEAN);
00436 
00437   gtk_tree_view_insert_column (GTK_TREE_VIEW (ls), opacity_col, -1);
00438   gtk_tree_view_insert_column (GTK_TREE_VIEW (ls), name_col, -1);
00439   gtk_tree_view_set_model (GTK_TREE_VIEW (ls), GTK_TREE_MODEL (ls->list_store));
00440   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ls), FALSE);
00441 
00442   ls->visibility_column = opacity_col;
00443   ls->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ls));
00444   ls->accel_group = gtk_accel_group_new ();
00445   ls->action_group = gtk_action_group_new ("LayerSelector");
00446   ls->n_actions = 0;
00447 
00448   for (i = 0; i < 20; ++i)
00449     ls->accel_available[i] = TRUE;
00450 
00451   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (ls),
00452                                         tree_view_separator_func,
00453                                         NULL, NULL);
00454   gtk_tree_selection_set_select_function (ls->selection, tree_selection_func,
00455                                           NULL, NULL);
00456   gtk_tree_selection_set_mode (ls->selection, GTK_SELECTION_BROWSE);
00457 
00458   g_object_set_data (G_OBJECT (ls->list_store), "layer-selector", ls);
00459   g_signal_connect (ls, "button_press_event",
00460                     G_CALLBACK (button_press_cb), NULL);
00461   ls->selection_changed_sig_id =
00462     g_signal_connect (ls->selection, "changed",
00463                       G_CALLBACK (selection_changed_cb), ls);
00464 }
00465 
00466 static void
00467 ghid_layer_selector_class_init (GHidLayerSelectorClass *klass)
00468 {
00469   GObjectClass *object_class = (GObjectClass *) klass;
00470 
00471   ghid_layer_selector_signals[SELECT_LAYER_SIGNAL] =
00472     g_signal_new ("select-layer",
00473                   G_TYPE_FROM_CLASS (klass),
00474                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
00475                   G_STRUCT_OFFSET (GHidLayerSelectorClass, select_layer),
00476                   NULL, NULL,
00477                   g_cclosure_marshal_VOID__INT, G_TYPE_NONE,
00478                   1, G_TYPE_INT);
00479   ghid_layer_selector_signals[TOGGLE_LAYER_SIGNAL] =
00480     g_signal_new ("toggle-layer",
00481                   G_TYPE_FROM_CLASS (klass),
00482                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
00483                   G_STRUCT_OFFSET (GHidLayerSelectorClass, toggle_layer),
00484                   NULL, NULL,
00485                   g_cclosure_marshal_VOID__INT, G_TYPE_NONE,
00486                   1, G_TYPE_INT);
00487   ghid_layer_selector_signals[RENAME_LAYER_SIGNAL] =
00488     g_signal_new ("rename-layer",
00489                   G_TYPE_FROM_CLASS (klass),
00490                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
00491                   G_STRUCT_OFFSET (GHidLayerSelectorClass, rename_layer),
00492                   NULL, NULL,
00493                   g_cclosure_user_marshal_VOID__INT_STRING, G_TYPE_NONE,
00494                   2, G_TYPE_INT, G_TYPE_STRING);
00495 
00496   object_class->finalize = ghid_layer_selector_finalize;
00497 }
00498 
00501 static void
00502 ghid_layer_selector_finalize (GObject *object)
00503 {
00504   GtkTreeIter iter;
00505   GHidLayerSelector *ls = (GHidLayerSelector *) object;
00506 
00507   g_object_unref (ls->accel_group);
00508   g_object_unref (ls->action_group);
00509 
00510   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
00511   do
00512     {
00513       struct _layer *ldata;
00514       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
00515                           &iter, STRUCT_COL, &ldata, -1);
00516       free_ldata (ls, ldata);
00517     }
00518   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
00519 
00520   G_OBJECT_CLASS (ghid_layer_selector_parent_class)->finalize (object);
00521 }
00522 
00523 /* PUBLIC FUNCTIONS */
00524 GType
00525 ghid_layer_selector_get_type (void)
00526 {
00527   static GType ls_type = 0;
00528 
00529   if (!ls_type)
00530     {
00531       const GTypeInfo ls_info =
00532       {
00533         sizeof (GHidLayerSelectorClass),
00534         NULL, /* base_init */
00535         NULL, /* base_finalize */
00536         (GClassInitFunc) ghid_layer_selector_class_init,
00537         NULL, /* class_finalize */
00538         NULL, /* class_data */
00539         sizeof (GHidLayerSelector),
00540         0,    /* n_preallocs */
00541         (GInstanceInitFunc) ghid_layer_selector_init,
00542       };
00543 
00544       ls_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
00545                                         "GHidLayerSelector",
00546                                         &ls_info,
00547                                         0);
00548     }
00549 
00550   return ls_type;
00551 }
00552 
00557 GtkWidget *
00558 ghid_layer_selector_new (void)
00559 {
00560   return GTK_WIDGET (g_object_new (GHID_LAYER_SELECTOR_TYPE, NULL));
00561 }
00562 
00581 void
00582 ghid_layer_selector_add_layer (GHidLayerSelector *ls,
00583                                gint user_id,
00584                                const gchar *name,
00585                                const gchar *color_string,
00586                                gboolean visible,
00587                                gboolean selectable,
00588                                gboolean renameable)
00589 {
00590   struct _layer *new_layer = NULL;
00591   gchar *pname, *vname;
00592   gboolean new_iter = TRUE;
00593   gboolean last_selectable = TRUE;
00594   GtkTreePath *path;
00595   GtkTreeIter iter;
00596   int i;
00597 
00598   /* Look for existing layer with this ID */
00599   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter))
00600     do
00601       {
00602         gboolean is_sep;
00603         gboolean this_selectable;
00604         gint read_id;
00605 
00606         gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
00607                             &iter, USER_ID_COL, &read_id,
00608                             SEPARATOR_COL, &is_sep,
00609                             SELECTABLE_COL, &this_selectable, -1);
00610 
00611         if (is_sep)
00612           continue;
00613 
00614         last_selectable = this_selectable;
00615         if (read_id == user_id)
00616           {
00617             new_iter = FALSE;
00618             break;
00619           }
00620       }
00621     while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
00622 
00623   /* Handle separator addition */
00624   if (new_iter)
00625     {
00626       if (selectable != last_selectable)
00627         {
00628           /* Add separator between selectable / non-selectable boundaries */
00629           gtk_list_store_append (ls->list_store, &iter);
00630           gtk_list_store_set (ls->list_store, &iter,
00631                               STRUCT_COL, NULL,
00632                               SEPARATOR_COL, TRUE, -1);
00633         }
00634       /* Create new layer */
00635       gtk_list_store_append (ls->list_store, &iter);
00636     }
00637   else
00638     {
00639       /* If the row exists, we clear out its ldata to create
00640        * a new action, accelerator and menu item. */
00641       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), &iter,
00642                           STRUCT_COL, &new_layer, -1);
00643       free_ldata (ls, new_layer);
00644     }
00645 
00646   new_layer = g_malloc (sizeof (*new_layer));
00647 
00648   gtk_list_store_set (ls->list_store,
00649                       &iter,
00650                       STRUCT_COL,      new_layer,
00651                       USER_ID_COL,     user_id,
00652                       VISIBLE_COL,     visible,
00653                       COLOR_COL,       color_string,
00654                       TEXT_COL,        name,
00655                       FONT_COL,        selectable ? NULL : "Italic",
00656                       EDITABLE_COL,    renameable,
00657                       SELECTABLE_COL,  selectable,
00658                       SEPARATOR_COL,   FALSE,
00659                       -1);
00660 
00661   /* -- Setup new actions -- */
00662   vname = g_strdup_printf ("LayerView%d", ls->n_actions);
00663   pname = g_strdup_printf ("LayerPick%d", ls->n_actions);
00664 
00665   /* Create row reference for actions */
00666   path = gtk_tree_model_get_path (GTK_TREE_MODEL (ls->list_store), &iter);
00667   new_layer->rref = gtk_tree_row_reference_new
00668                       (GTK_TREE_MODEL (ls->list_store), path);
00669   gtk_tree_path_free (path);
00670 
00671   /* Create selection action */
00672   if (selectable)
00673     {
00674       new_layer->pick_action
00675         = gtk_radio_action_new (pname, name, NULL, NULL, user_id);
00676       gtk_radio_action_set_group (new_layer->pick_action, ls->radio_group);
00677       ls->radio_group = gtk_radio_action_get_group (new_layer->pick_action);
00678     }
00679   else
00680     new_layer->pick_action = NULL;
00681 
00682   /* Create visibility action */
00683   new_layer->view_action = gtk_toggle_action_new (vname, name, NULL, NULL);
00684   gtk_toggle_action_set_active (new_layer->view_action, visible);
00685 
00686   /* Determine keyboard accelerators */
00687   for (i = 0; i < 20; ++i)
00688     if (ls->accel_available[i])
00689       break;
00690   if (i < 20)
00691     {
00692       /* Map 1-0 to actions 1-10 (with '0' meaning 10) */
00693       gchar *accel1 = g_strdup_printf ("%s%d",
00694                                        i < 10 ? "" : "<Alt>",
00695                                        (i + 1) % 10);
00696       gchar *accel2 = g_strdup_printf ("<Ctrl>%s%d",
00697                                        i < 10 ? "" : "<Alt>",
00698                                        (i + 1) % 10);
00699 
00700       if (selectable)
00701         {
00702           GtkAction *action = GTK_ACTION (new_layer->pick_action);
00703           gtk_action_set_accel_group (action, ls->accel_group);
00704           gtk_action_group_add_action_with_accel (ls->action_group,
00705                                                   action,
00706                                                   accel1);
00707           gtk_action_connect_accelerator (action);
00708           g_signal_connect (G_OBJECT (action), "activate",
00709                             G_CALLBACK (menu_pick_cb), new_layer);
00710         }
00711       gtk_action_set_accel_group (GTK_ACTION (new_layer->view_action),
00712                                   ls->accel_group);
00713       gtk_action_group_add_action_with_accel
00714           (ls->action_group, GTK_ACTION (new_layer->view_action), accel2);
00715       gtk_action_connect_accelerator (GTK_ACTION (new_layer->view_action));
00716       g_signal_connect (G_OBJECT (new_layer->view_action), "activate",
00717                         G_CALLBACK (menu_view_cb), new_layer);
00718 
00719       ls->accel_available[i] = FALSE;
00720       new_layer->accel_index = i;
00721       g_free (accel2);
00722       g_free (accel1);
00723     }
00724   else
00725     {
00726       new_layer->accel_index = -1;
00727     }
00728   /* finalize new layer struct */
00729   new_layer->pick_item = new_layer->view_item = NULL;
00730 
00731   /* cleanup */
00732   g_free (vname);
00733   g_free (pname);
00734 
00735   ls->n_actions++;
00736 }
00737 
00749 gint
00750 ghid_layer_selector_install_pick_items (GHidLayerSelector *ls,
00751                                         GtkMenuShell *shell, gint pos)
00752 {
00753   GtkTreeIter iter;
00754   int n = 0;
00755 
00756   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
00757   do
00758     {
00759       struct _layer *ldata;
00760       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
00761                           &iter, STRUCT_COL, &ldata, -1);
00762       if (ldata && ldata->pick_action)
00763         {
00764           GtkAction *action = GTK_ACTION (ldata->pick_action);
00765           ldata->pick_item = gtk_action_create_menu_item (action);
00766           gtk_menu_shell_insert (shell, ldata->pick_item, pos + n);
00767           ++n;
00768         }
00769     }
00770   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
00771 
00772   return n;
00773 }
00774 
00786 gint
00787 ghid_layer_selector_install_view_items (GHidLayerSelector *ls,
00788                                         GtkMenuShell *shell, gint pos)
00789 {
00790   GtkTreeIter iter;
00791   int n = 0;
00792 
00793   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
00794   do
00795     {
00796       struct _layer *ldata;
00797       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
00798                           &iter, STRUCT_COL, &ldata, -1);
00799       if (ldata && ldata->view_action)
00800         {
00801           GtkAction *action = GTK_ACTION (ldata->view_action);
00802           ldata->view_item = gtk_action_create_menu_item (action);
00803           gtk_menu_shell_insert (shell, ldata->view_item, pos + n);
00804           ++n;
00805         }
00806     }
00807   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
00808 
00809   return n;
00810 }
00811 
00819 GtkAccelGroup *
00820 ghid_layer_selector_get_accel_group (GHidLayerSelector *ls)
00821 {
00822   return ls->accel_group;
00823 }
00824 
00825 struct layer_data {
00826   GHidLayerSelector *ls;
00827   gint user_id;
00828 };
00829 
00831 static gboolean
00832 toggle_foreach_func (GtkTreeModel *model, GtkTreePath *path,
00833                      GtkTreeIter *iter, gpointer user_data)
00834 {
00835   struct layer_data *data = (struct layer_data *) user_data;
00836   gint id;
00837 
00838   gtk_tree_model_get (model, iter, USER_ID_COL, &id, -1);
00839   if (id == data->user_id)
00840     {
00841       toggle_visibility (data->ls, iter, TRUE);
00842       return TRUE;
00843     }
00844   return FALSE;
00845 }
00846 
00854 void
00855 ghid_layer_selector_toggle_layer (GHidLayerSelector *ls, gint user_id)
00856 {
00857   struct layer_data data;
00858 
00859   data.ls = ls;
00860   data.user_id = user_id;
00861 
00862   gtk_tree_model_foreach (GTK_TREE_MODEL (ls->list_store),
00863                           toggle_foreach_func, &data);
00864 }
00865 
00867 static gboolean
00868 select_foreach_func (GtkTreeModel *model, GtkTreePath *path,
00869                      GtkTreeIter *iter, gpointer user_data)
00870 {
00871   struct layer_data *data = (struct layer_data *) user_data;
00872   gint id;
00873 
00874   gtk_tree_model_get (model, iter, USER_ID_COL, &id, -1);
00875   if (id == data->user_id)
00876     {
00877       gtk_tree_selection_select_path (data->ls->selection, path);
00878       return TRUE;
00879     }
00880   return FALSE;
00881 }
00882 
00890 void
00891 ghid_layer_selector_select_layer (GHidLayerSelector *ls, gint user_id)
00892 {
00893   struct layer_data data;
00894 
00895   data.ls = ls;
00896   data.user_id = user_id;
00897 
00898   gtk_tree_model_foreach (GTK_TREE_MODEL (ls->list_store),
00899                           select_foreach_func, &data);
00900 }
00901 
00913 gboolean
00914 ghid_layer_selector_select_next_visible (GHidLayerSelector *ls)
00915 {
00916   GtkTreeIter iter;
00917   if (gtk_tree_selection_get_selected (ls->selection, NULL, &iter))
00918     {
00919       /* Scan forward, looking for selectable iter */
00920       do
00921         {
00922           gboolean visible;
00923           gboolean selectable;
00924 
00925           gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
00926                               &iter, VISIBLE_COL, &visible,
00927                               SELECTABLE_COL, &selectable, -1);
00928           if (visible && selectable)
00929             {
00930               gtk_tree_selection_select_iter (ls->selection, &iter);
00931               return TRUE;
00932             }
00933         }
00934       while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
00935       /* Move iter to start, and repeat. */
00936       gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
00937       do
00938         {
00939           gboolean visible;
00940           gboolean selectable;
00941 
00942           gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
00943                               &iter, VISIBLE_COL, &visible,
00944                               SELECTABLE_COL, &selectable, -1);
00945           if (visible && selectable)
00946             {
00947               gtk_tree_selection_select_iter (ls->selection, &iter);
00948               return TRUE;
00949             }
00950         }
00951       while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
00952       /* Failing this, just emit a selected signal on the original layer. */
00953       selection_changed_cb (ls->selection, ls);
00954     }
00955   /* If we get here, nothing is selectable, so fail. */
00956   return FALSE;
00957 }
00958 
00966 void
00967 ghid_layer_selector_make_selected_visible (GHidLayerSelector *ls)
00968 {
00969   GtkTreeIter iter;
00970   if (gtk_tree_selection_get_selected (ls->selection, NULL, &iter))
00971     {
00972       gboolean visible;
00973       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
00974                           &iter, VISIBLE_COL, &visible, -1);
00975       if (!visible)
00976         toggle_visibility (ls, &iter, FALSE);
00977     }
00978 }
00979 
00990 void
00991 ghid_layer_selector_update_colors (GHidLayerSelector *ls,
00992                                    const gchar *(*callback)(int user_id))
00993 {
00994   GtkTreeIter iter;
00995   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
00996   do
00997     {
00998       gint user_id;
00999       const gchar *new_color;
01000       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
01001                           &iter, USER_ID_COL, &user_id, -1);
01002       new_color = callback (user_id);
01003       if (new_color != NULL)
01004         gtk_list_store_set (ls->list_store, &iter, COLOR_COL, new_color, -1);
01005     }
01006   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
01007 }
01008 
01021 void
01022 ghid_layer_selector_delete_layers (GHidLayerSelector *ls,
01023                                    gboolean (*callback)(int user_id))
01024 {
01025   GtkTreeIter iter, last_iter;
01026  
01027   gboolean iter_valid =
01028     gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
01029   while (iter_valid)
01030     {
01031       struct _layer *ldata;
01032       gboolean sep, was_sep = FALSE;
01033       gint user_id;
01034 
01035       /* Find next iter to delete */
01036       while (iter_valid)
01037         {
01038           gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
01039                               &iter, USER_ID_COL, &user_id,
01040                               STRUCT_COL, &ldata, SEPARATOR_COL, &sep, -1);
01041           if (!sep && callback (user_id))
01042             break;
01043 
01044           /* save iter in case it's a bad separator */
01045           was_sep = sep;
01046           last_iter = iter;
01047           /* iterate */
01048           iter_valid =
01049             gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter);
01050         }
01051 
01052       if (iter_valid)
01053         {
01054           /* remove preceeding separator */
01055           if (was_sep)
01056             gtk_list_store_remove (ls->list_store, &last_iter);
01057 
01058           /*** remove row ***/
01059           iter_valid = gtk_list_store_remove (ls->list_store, &iter);
01060           free_ldata (ls, ldata);
01061         }
01062       last_iter = iter;
01063     }
01064 }
01065 
01074 void
01075 ghid_layer_selector_show_layers (GHidLayerSelector *ls,
01076                                  gboolean (*callback)(int user_id))
01077 {
01078   GtkTreeIter iter;
01079   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
01080   do
01081     {
01082       struct _layer *ldata;
01083       gboolean sep;
01084       gint user_id;
01085 
01086       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
01087                           &iter, USER_ID_COL, &user_id,
01088                           STRUCT_COL, &ldata,
01089                           SEPARATOR_COL, &sep, -1);
01090       if (!sep)
01091         set_visibility (ls, &iter, ldata, callback (user_id));
01092     }
01093   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
01094 }
01095