[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

gEDA-cvs: pcb.git: branch: master updated (2fecf3a75bdbc47f8317fa92f361444bb885d23d)



The branch, master has been updated
       via  2fecf3a75bdbc47f8317fa92f361444bb885d23d (commit)
       via  4e63bedd088a4830735d9161c50da94f6784314f (commit)
       via  8eddcff25d69024c5afe336f9f1ddab7e341df49 (commit)
       via  707dd2dc7245cec6f15adc1ae1f735944602c561 (commit)
       via  d721448d923feaad8a7c89410caff698fa52d0fd (commit)
      from  448a50d473e83f4202a70df36fcffe30373ffc38 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


=========
 Summary
=========

 src/Makefile.am                   |    6 +-
 src/hid/gtk/ghid-layer-selector.c |  432 ++++++++------
 src/hid/gtk/ghid-layer-selector.h |   10 +-
 src/hid/gtk/ghid-main-menu.c      |  559 +++++++++++++++++
 src/hid/gtk/ghid-main-menu.h      |   42 ++
 src/hid/gtk/gtkhid-main.c         |   18 +-
 src/hid/gtk/gui-command-window.c  |    8 +-
 src/hid/gtk/gui-output-events.c   |    8 +-
 src/hid/gtk/gui-top-window.c      | 1235 ++++---------------------------------
 src/hid/gtk/gui.h                 |   14 +-
 10 files changed, 1001 insertions(+), 1331 deletions(-)
 create mode 100644 src/hid/gtk/ghid-main-menu.c
 create mode 100644 src/hid/gtk/ghid-main-menu.h


=================
 Commit Messages
=================

commit 2fecf3a75bdbc47f8317fa92f361444bb885d23d
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Link GHidLayerSelector and GHidMainMenu
    
    Layer visibility-toggle and selection menu items now appear
    in the main menu. Accelerators work correctly and are reassigned
    on deletion/addition of layers to simulate the old behavior.

:100644 100644 684f061... 582fe67... M	src/hid/gtk/ghid-layer-selector.c
:100644 100644 bfeffe9... 10355a7... M	src/hid/gtk/ghid-layer-selector.h
:100644 100644 945b522... 5693c56... M	src/hid/gtk/ghid-main-menu.c
:100644 100644 3b8c728... d667e34... M	src/hid/gtk/ghid-main-menu.h
:100644 100644 b2d9c22... c292660... M	src/hid/gtk/gui-top-window.c

commit 4e63bedd088a4830735d9161c50da94f6784314f
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Clean up layer data handling in ghid-layer-selector.c

:100644 100644 bd57f57... 684f061... M	src/hid/gtk/ghid-layer-selector.c

commit 8eddcff25d69024c5afe336f9f1ddab7e341df49
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Give GHidLayerSelector an internal layer structure
    
    Before we had various arrays to resize and index; now each
    row has a structure associated with it, that is easier to
    access and maintain.

:100644 100644 7f11f7f... bd57f57... M	src/hid/gtk/ghid-layer-selector.c

commit 707dd2dc7245cec6f15adc1ae1f735944602c561
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Replace gtk UI manager with GHidMainMenu widget
    
    This commit replaces the old UI manager with a customize gtk
    widget that builds menus directly from a resource tree. This
    eliminates the translate-to-XML step, the ugly hacks used to
    access individual actions, and all the associated manual
    memory management.
    
    This will also give us the ability to have more dynamic menus,
    in particular layer lists without maximum capacities.
    
    Layers and route styles are still not hooked into the menu.
    This means that those accelerators DO NOT WORK. (This will
    be fixed in a later commit.) Checkboxes have been replaced
    with radio buttons where appropriate. There are now tearoffs
    on the context-menu's submenus.
    
    Other than that, there should be no user-visible changes. ;)

:100644 100644 0c9019d... 46a9347... M	src/hid/gtk/gtkhid-main.c
:100644 100644 b08836e... 1728bd3... M	src/hid/gtk/gui-command-window.c
:100644 100644 5929a37... b9c5ab3... M	src/hid/gtk/gui-output-events.c
:100644 100644 b4dbcaa... b2d9c22... M	src/hid/gtk/gui-top-window.c
:100644 100644 30ff593... e7e5f55... M	src/hid/gtk/gui.h

commit d721448d923feaad8a7c89410caff698fa52d0fd
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Created ghid-main-menu.[ch]
    
    Still need to hook up layer selector and route styles.

:100644 100644 4b66ac7... a9c51b2... M	src/Makefile.am
:000000 100644 0000000... 945b522... A	src/hid/gtk/ghid-main-menu.c
:000000 100644 0000000... 3b8c728... A	src/hid/gtk/ghid-main-menu.h

=========
 Changes
=========

commit 2fecf3a75bdbc47f8317fa92f361444bb885d23d
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Link GHidLayerSelector and GHidMainMenu
    
    Layer visibility-toggle and selection menu items now appear
    in the main menu. Accelerators work correctly and are reassigned
    on deletion/addition of layers to simulate the old behavior.

diff --git a/src/hid/gtk/ghid-layer-selector.c b/src/hid/gtk/ghid-layer-selector.c
index 684f061..582fe67 100644
--- a/src/hid/gtk/ghid-layer-selector.c
+++ b/src/hid/gtk/ghid-layer-selector.c
@@ -56,10 +56,13 @@ struct _GHidLayerSelector
   GtkTreeViewColumn *visibility_column;
 
   GtkActionGroup *action_group;
+  GtkAccelGroup *accel_group;
 
   GSList *radio_group;
   int n_actions;
 
+  gboolean accel_available[20];
+
   gboolean last_activatable;
   gboolean prevent_recursion;
 };
@@ -74,6 +77,9 @@ struct _GHidLayerSelectorClass
 
 struct _layer
 {
+  gint accel_index;   /* Index into ls->accel_available */
+  GtkWidget *pick_item;
+  GtkWidget *view_item;
   GtkToggleAction *view_action;
   GtkRadioAction  *pick_action;
   GtkTreeRowReference *rref;
@@ -102,6 +108,8 @@ free_ldata (GHidLayerSelector *ls, struct _layer *ldata)
       g_object_unref (G_OBJECT (ldata->view_action));
     }
   gtk_tree_row_reference_free (ldata->rref);
+  if (ldata->accel_index >= 0)
+    ls->accel_available[ldata->accel_index] = TRUE;
   g_free (ldata);
 
 }
@@ -278,6 +286,7 @@ ghid_layer_selector_finalize (GObject *object)
   GtkTreeIter iter;
   GHidLayerSelector *ls = (GHidLayerSelector *) object;
 
+  g_object_unref (ls->accel_group);
   g_object_unref (ls->action_group);
 
   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
@@ -334,6 +343,7 @@ ghid_layer_selector_get_type (void)
 GtkWidget *
 ghid_layer_selector_new (void)
 {
+  int i;
   GtkCellRenderer *renderer1 = ghid_cell_renderer_visibility_new ();
   GtkCellRenderer *renderer2 = gtk_cell_renderer_text_new ();
   GtkTreeViewColumn *opacity_col =
@@ -361,9 +371,12 @@ ghid_layer_selector_new (void)
   ls->last_activatable = TRUE;
   ls->visibility_column = opacity_col;
   ls->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ls));
+  ls->accel_group = gtk_accel_group_new ();
   ls->action_group = gtk_action_group_new ("LayerSelector");
   ls->prevent_recursion = FALSE;
   ls->n_actions = 0;
+  for (i = 0; i < 20; ++i)
+    ls->accel_available[i] = TRUE;
 
   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (ls),
                                         tree_view_separator_func,
@@ -378,7 +391,7 @@ ghid_layer_selector_new (void)
   g_signal_connect (ls->selection, "changed",
                     G_CALLBACK (selection_changed_cb), ls);
 
-  g_object_ref (ls->action_group);
+  g_object_ref (ls->accel_group);
 
   return GTK_WIDGET (ls);
 }
@@ -409,11 +422,12 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
                                gboolean activatable)
 {
   struct _layer *new_layer = NULL;
-  gchar *pname, *vname, *paccel, *vaccel;
+  gchar *pname, *vname;
   gboolean new_iter = TRUE;
   gboolean last_activatable = TRUE;
   GtkTreePath *path;
   GtkTreeIter iter;
+  int i;
 
   /* Look for existing layer with this ID */
   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter))
@@ -484,24 +498,6 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
   /* -- Setup new actions -- */
   vname = g_strdup_printf ("LayerView%d", ls->n_actions);
   pname = g_strdup_printf ("LayerPick%d", ls->n_actions);
-  vaccel = NULL;
-  paccel = NULL;
-
-  /* Determine keyboard accelerators */
-  if (ls->n_actions < 10)
-    {
-      /* Map 1-0 to actions 1-10 (with '0' meaning 10) */
-      int i = (ls->n_actions + 1) % 10;
-      vaccel = g_strdup_printf ("<Ctrl>%d", i);
-      paccel = g_strdup_printf ("%d", i);
-    }
-  else
-    {
-      /* Map 1-0 to actions 11-20 (with '0' meaning 10) */
-      int i = (ls->n_actions + 1) % 10;
-      vaccel = g_strdup_printf ("<Alt><Ctrl>%d", i);
-      paccel = g_strdup_printf ("<Alt>%d", i);
-    }
 
   /* Create row reference for actions */
   path = gtk_tree_model_get_path (GTK_TREE_MODEL (ls->list_store), &iter);
@@ -516,12 +512,6 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
         = gtk_radio_action_new (pname, name, NULL, NULL, user_id);
       gtk_radio_action_set_group (new_layer->pick_action, ls->radio_group);
       ls->radio_group = gtk_radio_action_get_group (new_layer->pick_action);
-      gtk_action_group_add_action_with_accel
-        (ls->action_group,
-         GTK_ACTION (new_layer->pick_action),
-         paccel);
-      g_signal_connect (new_layer->pick_action, "toggled",
-                        G_CALLBACK (menu_pick_cb), new_layer);
     }
   else
     new_layer->pick_action = NULL;
@@ -530,112 +520,148 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
   new_layer->view_action = gtk_toggle_action_new (vname, name, NULL, NULL);
   gtk_toggle_action_set_active (new_layer->view_action, visible);
 
-  gtk_action_group_add_action_with_accel
-    (ls->action_group,
-     GTK_ACTION (new_layer->view_action),
-     vaccel);
-  g_signal_connect (new_layer->view_action, "toggled",
-                    G_CALLBACK (menu_view_cb), new_layer->rref);
-
-
   /* Select new layer, if we need */
   if (activatable
       && !gtk_tree_selection_get_selected (ls->selection, NULL, NULL))
     gtk_tree_selection_select_iter (ls->selection, &iter);
 
-  /* cleanup */
-  if (vaccel)
+  /* Determine keyboard accelerators */
+  for (i = 0; i < 20; ++i)
+    if (ls->accel_available[i])
+      break;
+  if (i < 20)
     {
-      g_free (vaccel);
-      g_free (paccel); 
+      /* Map 1-0 to actions 1-10 (with '0' meaning 10) */
+      gchar *accel1 = g_strdup_printf ("%s%d",
+                                       i < 10 ? "" : "<Alt>",
+                                       (i + 1) % 10);
+      gchar *accel2 = g_strdup_printf ("<Ctrl>%s%d",
+                                       i < 10 ? "" : "<Alt>",
+                                       (i + 1) % 10);
+
+      if (activatable)
+        {
+          GtkAction *action = GTK_ACTION (new_layer->pick_action);
+          gtk_action_set_accel_group (action, ls->accel_group);
+          gtk_action_group_add_action_with_accel (ls->action_group,
+                                                  action,
+                                                  accel1);
+          gtk_action_connect_accelerator (action);
+          g_signal_connect (G_OBJECT (action), "activate",
+                            G_CALLBACK (menu_pick_cb), new_layer);
+        }
+      gtk_action_set_accel_group (GTK_ACTION (new_layer->view_action),
+                                  ls->accel_group);
+      gtk_action_group_add_action_with_accel
+          (ls->action_group, GTK_ACTION (new_layer->view_action), accel2);
+      gtk_action_connect_accelerator (GTK_ACTION (new_layer->view_action));
+      g_signal_connect (G_OBJECT (new_layer->view_action), "activate",
+                        G_CALLBACK (menu_view_cb), new_layer);
+
+      ls->accel_available[i] = FALSE;
+      new_layer->accel_index = i;
+      g_free (accel2);
+      g_free (accel1);
     }
+  else
+    {
+      new_layer->accel_index = -1;
+    }
+  /* finalize new layer struct */
+  new_layer->pick_item = new_layer->view_item = NULL;
+
+  /* cleanup */
   g_free (vname);
   g_free (pname);
 
   ls->n_actions++;
 }
 
-/*! \brief used internally */
-static gboolean
-pick_xml_foreach_func (GtkTreeModel *model, GtkTreePath *path,
-                       GtkTreeIter *iter, gpointer data)
-{
-  struct _layer *ldata;
-  GString *str = data;
-  
-  gtk_tree_model_get (model, iter, STRUCT_COL, &ldata, -1);
-  if (ldata && ldata->pick_action)
-    g_string_append_printf (str, "<menuitem action=\"%s\" />\n",
-                     gtk_action_get_name (GTK_ACTION (ldata->pick_action)));
-  return FALSE;
-}
-/*! \brief used internally */
-static gboolean
-view_xml_foreach_func (GtkTreeModel *model, GtkTreePath *path,
-                       GtkTreeIter *iter, gpointer data)
-{
-  struct _layer *ldata;
-  GString *str = data;
-  
-  gtk_tree_model_get (model, iter, STRUCT_COL, &ldata, -1);
-  if (ldata && ldata->view_action)
-    g_string_append_printf (str, "<menuitem action=\"%s\" />\n",
-                     gtk_action_get_name (GTK_ACTION (ldata->view_action)));
-  return FALSE;
-}
- 
-/*! \brief Get the "Current Layer" menu description of a layer selector
+/*! \brief Install the "Current Layer" menu items for a layer selector
  *  \par Function Description
- *  Returns the XML content used by Gtk in building the layer-selection
- *  part of the menu. This is a radio-button list describing which layer
- *  is active.
+ *  Takes a menu shell and installs menu items for layer selection in
+ *  the shell, at the given position.
  *
- *  \param [in] ls            The selector to be acted on
+ *  \param [in] ls      The selector to be acted on
+ *  \param [in] shell   The menu to install the items in
+ *  \param [in] pos     The position in the menu to install items
  *
- *  \return the requested XML
+ *  \return the number of items installed
  */
-gchar *
-ghid_layer_selector_get_pick_xml (GHidLayerSelector *ls)
+gint
+ghid_layer_selector_install_pick_items (GHidLayerSelector *ls,
+                                        GtkMenuShell *shell, gint pos)
 {
-  GString *str = g_string_new ("");
-  gtk_tree_model_foreach (GTK_TREE_MODEL (ls->list_store),
-                          pick_xml_foreach_func, str);
-  return g_string_free (str, FALSE);
+  GtkTreeIter iter;
+  int n = 0;
+
+  gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
+  do
+    {
+      struct _layer *ldata;
+      gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
+                          &iter, STRUCT_COL, &ldata, -1);
+      if (ldata && ldata->pick_action)
+        {
+          GtkAction *action = GTK_ACTION (ldata->pick_action);
+          ldata->pick_item = gtk_action_create_menu_item (action);
+          gtk_menu_shell_insert (shell, ldata->pick_item, pos + n);
+          ++n;
+        }
+    }
+  while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
+
+  return n;
 }
 
-/*! \brief Get the "Shown Layers" menu description of a layer selector
+/*! \brief Install the "Shown Layers" menu items for a layer selector
  *  \par Function Description
- *  Returns the XML content used by Gtk in building the layer-selection
- *  part of the menu. This is a toggle-button list describing which layer(s)
- *  are visible.
+ *  Takes a menu shell and installs menu items for layer selection in
+ *  the shell, at the given position.
  *
- *  \param [in] ls            The selector to be acted on
+ *  \param [in] ls      The selector to be acted on
+ *  \param [in] shell   The menu to install the items in
+ *  \param [in] pos     The position in the menu to install items
  *
- *  \return the requested XML
+ *  \return the number of items installed
  */
-gchar *
-ghid_layer_selector_get_view_xml (GHidLayerSelector *ls)
+gint
+ghid_layer_selector_install_view_items (GHidLayerSelector *ls,
+                                        GtkMenuShell *shell, gint pos)
 {
-  GString *str = g_string_new ("");
-  gtk_tree_model_foreach (GTK_TREE_MODEL (ls->list_store),
-                          view_xml_foreach_func, str);
-  return g_string_free (str, FALSE);
+  GtkTreeIter iter;
+  int n = 0;
+
+  gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
+  do
+    {
+      struct _layer *ldata;
+      gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
+                          &iter, STRUCT_COL, &ldata, -1);
+      if (ldata && ldata->view_action)
+        {
+          GtkAction *action = GTK_ACTION (ldata->view_action);
+          ldata->view_item = gtk_action_create_menu_item (action);
+          gtk_menu_shell_insert (shell, ldata->view_item, pos + n);
+          ++n;
+        }
+    }
+  while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
+
+  return n;
 }
 
-/*! \brief Get the GtkActionGroup containing accelerators, etc, of a layer selector
+/*! \brief Returns the GtkAccelGroup of a layer selector
  *  \par Function Description
- *  Returns the GtkActionGroup containing the toggle and radio buttons used
- *  in the menu. Also contains the accelerators. This action group should be
- *  added to the main UI. See Gtk docs for details.
  *
  *  \param [in] ls            The selector to be acted on
  *
- *  \return the action group of the selector
+ *  \return the accel group of the selector
  */
-GtkActionGroup *
-ghid_layer_selector_get_action_group (GHidLayerSelector *ls)
+GtkAccelGroup *
+ghid_layer_selector_get_accel_group (GHidLayerSelector *ls)
 {
-  return ls->action_group;
+  return ls->accel_group;
 }
 
 /*! \brief used internally */
diff --git a/src/hid/gtk/ghid-layer-selector.h b/src/hid/gtk/ghid-layer-selector.h
index bfeffe9..10355a7 100644
--- a/src/hid/gtk/ghid-layer-selector.h
+++ b/src/hid/gtk/ghid-layer-selector.h
@@ -26,9 +26,13 @@ void ghid_layer_selector_add_layer (GHidLayerSelector *ls,
                                     gboolean visible,
                                     gboolean activatable);
 GtkAccelGroup *ghid_layer_selector_get_accel_group (GHidLayerSelector *ls);
-gchar *ghid_layer_selector_get_pick_xml (GHidLayerSelector *ls);
-gchar *ghid_layer_selector_get_view_xml (GHidLayerSelector *ls);
-GtkActionGroup *ghid_layer_selector_get_action_group (GHidLayerSelector *ls);
+
+gint ghid_layer_selector_install_pick_items (GHidLayerSelector *ls,
+                                             GtkMenuShell *shell, gint pos);
+gint ghid_layer_selector_install_view_items (GHidLayerSelector *ls,
+                                             GtkMenuShell *shell, gint pos);
+
+GtkAccelGroup *ghid_layer_selector_get_accel_group (GHidLayerSelector *ls);
 
 void ghid_layer_selector_toggle_layer (GHidLayerSelector *ls, 
                                        gint user_id);
diff --git a/src/hid/gtk/ghid-main-menu.c b/src/hid/gtk/ghid-main-menu.c
index 945b522..5693c56 100644
--- a/src/hid/gtk/ghid-main-menu.c
+++ b/src/hid/gtk/ghid-main-menu.c
@@ -13,6 +13,7 @@
 #include "pcb-printf.h"
 
 #include "ghid-main-menu.h"
+#include "ghid-layer-selector.h"
 
 void Message (const char *, ...);
 
@@ -36,10 +37,13 @@ struct _GHidMainMenu
   GList *actions;
   GHashTable *popup_table;
 
+  gint n_layer_views;
+  gint n_layer_picks;
+  gint n_route_styles;
+
   GCallback action_cb;
   void (*special_key_cb) (const char *accel, GtkAction *action,
                           const Resource *node);
-
 };
 
 struct _GHidMainMenuClass
@@ -433,6 +437,9 @@ ghid_main_menu_new (GCallback action_cb,
   mm->layer_view_pos = 0;
   mm->layer_pick_pos = 0;
   mm->route_style_pos = 0;
+  mm->n_layer_views = 0;
+  mm->n_layer_picks = 0;
+  mm->n_route_styles = 0;
   mm->layer_view_shell = NULL;
   mm->layer_pick_shell = NULL;
   mm->route_style_shell = NULL;
@@ -499,6 +506,50 @@ ghid_main_menu_update_toggle_state (GHidMainMenu *menu,
     }
 }
 
+/*! \brief Installs or updates layer selector items */
+void
+ghid_main_menu_install_layer_selector (GHidMainMenu *mm,
+                                       GHidLayerSelector *ls)
+{
+  GList *children;
+
+  /* @layerview */
+  if (mm->layer_view_shell)
+    {
+      /* Remove old children */
+      children = gtk_container_get_children
+                   (GTK_CONTAINER (mm->layer_view_shell));
+      children = g_list_nth (children, mm->layer_view_pos);
+      while (children && mm->n_layer_views--)
+        {
+          gtk_container_remove (GTK_CONTAINER (mm->layer_view_shell),
+                                children->data);
+          children = g_list_next (children);
+        }
+      /* Install new ones */
+      mm->n_layer_views = ghid_layer_selector_install_view_items
+                            (ls, mm->layer_view_shell, mm->layer_view_pos);
+    }
+
+  /* @layerpick */
+  if (mm->layer_pick_shell)
+    {
+      /* Remove old children */
+      children = gtk_container_get_children
+                   (GTK_CONTAINER (mm->layer_pick_shell));
+      children = g_list_nth (children, mm->layer_pick_pos);
+      while (children && mm->n_layer_picks--)
+        {
+          gtk_container_remove (GTK_CONTAINER (mm->layer_pick_shell),
+                                children->data);
+          children = g_list_next (children);
+        }
+      /* Install new ones */
+      mm->n_layer_picks = ghid_layer_selector_install_pick_items
+                            (ls, mm->layer_pick_shell, mm->layer_pick_pos);
+    }
+}
+
 /*! \brief Returns the menu bar's accelerator group */
 GtkAccelGroup *
 ghid_main_menu_get_accel_group (GHidMainMenu *menu)
diff --git a/src/hid/gtk/ghid-main-menu.h b/src/hid/gtk/ghid-main-menu.h
index 3b8c728..d667e34 100644
--- a/src/hid/gtk/ghid-main-menu.h
+++ b/src/hid/gtk/ghid-main-menu.h
@@ -1,12 +1,13 @@
 #ifndef GHID_MAIN_MENU_H__
 #define GHID_MAIN_MENU_H__
 
-#include "resource.h"
-
 #include <glib.h>
 #include <glib-object.h>
 #include <gtk/gtk.h>
 
+#include "ghid-layer-selector.h"
+#include "resource.h"
+
 G_BEGIN_DECLS  /* keep c++ happy */
 
 #define GHID_MAIN_MENU_TYPE            (ghid_main_menu_get_type ())
@@ -34,5 +35,8 @@ void ghid_main_menu_add_popup_resource (GHidMainMenu *menu, const char *name,
                                         const Resource *res);
 GtkMenu *ghid_main_menu_get_popup (GHidMainMenu *menu, const char *name);
 
+void ghid_main_menu_install_layer_selector (GHidMainMenu *mm,
+                                            GHidLayerSelector *ls);
+
 G_END_DECLS  /* keep c++ happy */
 #endif
diff --git a/src/hid/gtk/gui-top-window.c b/src/hid/gtk/gui-top-window.c
index b2d9c22..c292660 100644
--- a/src/hid/gtk/gui-top-window.c
+++ b/src/hid/gtk/gui-top-window.c
@@ -598,7 +598,6 @@ make_top_menubar (GtkWidget *menu_bar, GtkWidget * hbox, GHidPort * port)
 {
   GtkWidget *frame;
   GtkActionGroup *actions;
-  GtkActionGroup *layer_actions;
 
   frame = gtk_frame_new (NULL);
   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
@@ -609,12 +608,13 @@ make_top_menubar (GtkWidget *menu_bar, GtkWidget * hbox, GHidPort * port)
   ghidgui->main_actions = actions;
 
   make_menu_actions (actions, port);
-  layer_actions = ghid_layer_selector_get_action_group
-          (GHID_LAYER_SELECTOR (ghidgui->layer_selector));
  
   gtk_window_add_accel_group (GTK_WINDOW (gport->top_window),
 			      ghid_main_menu_get_accel_group
                                 (GHID_MAIN_MENU (ghidgui->menu_bar)));
+  gtk_window_add_accel_group (GTK_WINDOW (gport->top_window),
+			      ghid_layer_selector_get_accel_group
+                                (GHID_LAYER_SELECTOR (ghidgui->layer_selector)));
 
   gtk_container_add (GTK_CONTAINER (frame), menu_bar);
 
@@ -842,6 +842,9 @@ ghid_layer_buttons_update (void)
      get_layer_delete);
   make_layer_buttons (ghidgui->layer_selector);
   make_virtual_layer_buttons (ghidgui->layer_selector);
+  ghid_main_menu_install_layer_selector
+      (GHID_MAIN_MENU (ghidgui->menu_bar),
+       GHID_LAYER_SELECTOR (ghidgui->layer_selector));
 
   /* Sync selected layer with PCB's state */
   if (PCB->RatDraw)

commit 4e63bedd088a4830735d9161c50da94f6784314f
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Clean up layer data handling in ghid-layer-selector.c

diff --git a/src/hid/gtk/ghid-layer-selector.c b/src/hid/gtk/ghid-layer-selector.c
index bd57f57..684f061 100644
--- a/src/hid/gtk/ghid-layer-selector.c
+++ b/src/hid/gtk/ghid-layer-selector.c
@@ -79,6 +79,33 @@ struct _layer
   GtkTreeRowReference *rref;
 };
 
+/*! \brief Deletes the action and accelerator from a layer */
+static void
+free_ldata (GHidLayerSelector *ls, struct _layer *ldata)
+{
+  if (ldata->pick_action)
+    {
+      gtk_action_disconnect_accelerator
+        (GTK_ACTION (ldata->pick_action));
+      gtk_action_group_remove_action (ls->action_group,
+                                    GTK_ACTION (ldata->pick_action));
+/* TODO: make this work without wrecking the radio action group
+ *           g_object_unref (G_OBJECT (ldata->pick_action)); 
+ *                   */
+    }
+  if (ldata->view_action)
+    {
+      gtk_action_disconnect_accelerator
+        (GTK_ACTION (ldata->view_action));
+      gtk_action_group_remove_action (ls->action_group,
+                            GTK_ACTION (ldata->view_action));
+      g_object_unref (G_OBJECT (ldata->view_action));
+    }
+  gtk_tree_row_reference_free (ldata->rref);
+  g_free (ldata);
+
+}
+
 /*! \brief Flip the visibility state of a given layer 
  *  \par Function Description
  *  Changes the internal toggle state and menu checkbox state
@@ -173,11 +200,11 @@ selection_changed_cb (GtkTreeSelection *selection, GHidLayerSelector *ls)
 
 /*! \brief Callback for menu actions: sync layer selection list, emit signal */
 static void
-menu_view_cb (GtkToggleAction *action, GtkTreeRowReference *rref)
+menu_view_cb (GtkToggleAction *action, struct _layer *ldata)
 {
   GHidLayerSelector *ls;
-  GtkTreeModel *model = gtk_tree_row_reference_get_model (rref);
-  GtkTreePath *path = gtk_tree_row_reference_get_path (rref);
+  GtkTreeModel *model = gtk_tree_row_reference_get_model (ldata->rref);
+  GtkTreePath *path = gtk_tree_row_reference_get_path (ldata->rref);
   gboolean state = gtk_toggle_action_get_active (action);
   GtkTreeIter iter;
   gint user_id;
@@ -193,11 +220,11 @@ menu_view_cb (GtkToggleAction *action, GtkTreeRowReference *rref)
 
 /*! \brief Callback for menu actions: sync layer selection list, emit signal */
 static void
-menu_pick_cb (GtkRadioAction *action, GtkTreeRowReference *rref)
+menu_pick_cb (GtkRadioAction *action, struct _layer *ldata)
 {
   GHidLayerSelector *ls;
-  GtkTreeModel *model = gtk_tree_row_reference_get_model (rref);
-  GtkTreePath *path = gtk_tree_row_reference_get_path (rref);
+  GtkTreeModel *model = gtk_tree_row_reference_get_model (ldata->rref);
+  GtkTreePath *path = gtk_tree_row_reference_get_path (ldata->rref);
   GtkTreeIter iter;
   gint user_id;
 
@@ -437,14 +464,21 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
     }
   else
     {
+      /* If the row exists, we clear out its ldata to create
+       * a new action, accelerator and menu item. */
+      gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), &iter,
+                          STRUCT_COL, &new_layer, -1);
+      free_ldata (ls, new_layer);
+      new_layer = malloc (sizeof (*new_layer));
+
       gtk_list_store_set (ls->list_store, &iter,
+                          STRUCT_COL, new_layer,
                           VISIBLE_COL, visible,
                           COLOR_COL, color_string,
                           TEXT_COL, name,
                           FONT_COL, activatable ? NULL : "Italic",
                           ACTIVATABLE_COL, activatable,
                           -1);
-      return;
     }
 
   /* -- Setup new actions -- */
@@ -487,7 +521,7 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
          GTK_ACTION (new_layer->pick_action),
          paccel);
       g_signal_connect (new_layer->pick_action, "toggled",
-                        G_CALLBACK (menu_pick_cb), new_layer->rref);
+                        G_CALLBACK (menu_pick_cb), new_layer);
     }
   else
     new_layer->pick_action = NULL;
@@ -786,44 +820,44 @@ ghid_layer_selector_delete_layers (GHidLayerSelector *ls,
                                    gboolean (*callback)(int user_id))
 {
   GtkTreeIter iter, last_iter;
-  gboolean needs_inc;
-  gboolean was_separator = FALSE;
-  gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
-  do
+ 
+  gboolean iter_valid =
+    gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
+  while (iter_valid)
     {
       struct _layer *ldata;
-      gboolean sep;
+      gboolean sep, was_sep = FALSE;
       gint user_id;
-      gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
-                          &iter, USER_ID_COL, &user_id,
-                          STRUCT_COL, &ldata, SEPARATOR_COL, &sep, -1);
-      /* gtk_list_store_remove will increment the iter for us, so we
-       *  don't want to do it again in the loop condition */
-      needs_inc = TRUE;
-      if (!sep && callback (user_id))
+
+      /* Find next iter to delete */
+      while (iter_valid)
         {
-          if (gtk_list_store_remove (ls->list_store, &iter))
-            {
-              if (ldata->view_action)
-                gtk_action_group_remove_action (ls->action_group,
-                                                GTK_ACTION (ldata->view_action));
-              if (ldata->pick_action)
-                gtk_action_group_remove_action (ls->action_group,
-                                                GTK_ACTION (ldata->pick_action));
-              gtk_tree_row_reference_free (ldata->rref);
-              g_free (ldata);
-              needs_inc = FALSE;
-            }
-          else
-            return;
-          if (was_separator)
+          gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
+                              &iter, USER_ID_COL, &user_id,
+                              STRUCT_COL, &ldata, SEPARATOR_COL, &sep, -1);
+          if (!sep && callback (user_id))
+            break;
+
+          /* save iter in case it's a bad separator */
+          was_sep = sep;
+          last_iter = iter;
+          /* iterate */
+          iter_valid =
+            gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter);
+        }
+
+      if (iter_valid)
+        {
+          /* remove preceeding separator */
+          if (was_sep)
             gtk_list_store_remove (ls->list_store, &last_iter);
+
+          /*** remove row ***/
+          iter_valid = gtk_list_store_remove (ls->list_store, &iter);
+          free_ldata (ls, ldata);
         }
       last_iter = iter;
-      was_separator = sep;
     }
-  while (!needs_inc ||
-         gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
 }
 
 

commit 8eddcff25d69024c5afe336f9f1ddab7e341df49
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Give GHidLayerSelector an internal layer structure
    
    Before we had various arrays to resize and index; now each
    row has a structure associated with it, that is easier to
    access and maintain.

diff --git a/src/hid/gtk/ghid-layer-selector.c b/src/hid/gtk/ghid-layer-selector.c
index 7f11f7f..bd57f57 100644
--- a/src/hid/gtk/ghid-layer-selector.c
+++ b/src/hid/gtk/ghid-layer-selector.c
@@ -33,7 +33,7 @@ enum {
 
 /*! \brief Columns used for internal data store */
 enum {
-  INDEX_COL,
+  STRUCT_COL,
   USER_ID_COL,
   VISIBLE_COL,
   COLOR_COL,
@@ -57,11 +57,7 @@ struct _GHidLayerSelector
 
   GtkActionGroup *action_group;
 
-  GtkToggleAction **view_actions;
-  GtkRadioAction  **pick_actions;
-  GtkTreeRowReference **rows;
   GSList *radio_group;
-  int max_actions;
   int n_actions;
 
   gboolean last_activatable;
@@ -76,6 +72,13 @@ struct _GHidLayerSelectorClass
   void (* toggle_layer) (GHidLayerSelector *, gint);
 };
 
+struct _layer
+{
+  GtkToggleAction *view_action;
+  GtkRadioAction  *pick_action;
+  GtkTreeRowReference *rref;
+};
+
 /*! \brief Flip the visibility state of a given layer 
  *  \par Function Description
  *  Changes the internal toggle state and menu checkbox state
@@ -90,12 +93,13 @@ struct _GHidLayerSelectorClass
 static void
 toggle_visibility (GHidLayerSelector *ls, GtkTreeIter *iter)
 {
-  gint idx;
+  struct _layer *ldata;
   gboolean toggle;
   gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), iter,
-                     VISIBLE_COL, &toggle, INDEX_COL, &idx, -1);
+                     VISIBLE_COL, &toggle, STRUCT_COL, &ldata, -1);
   gtk_list_store_set (ls->list_store, iter, VISIBLE_COL, !toggle, -1);
-  gtk_toggle_action_set_active (ls->view_actions[idx], !toggle);
+  if (ldata)
+    gtk_toggle_action_set_active (ldata->view_action, !toggle);
 }
 
 /*! \brief Decide if a GtkListStore entry is a layer or separator */
@@ -157,11 +161,12 @@ selection_changed_cb (GtkTreeSelection *selection, GHidLayerSelector *ls)
   ls->prevent_recursion = TRUE;
   if (gtk_tree_selection_get_selected (selection, NULL, &iter))
     {
-      gint idx;
+      gint user_id;
+      struct _layer *ldata;
       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store), &iter,
-                          INDEX_COL, &idx, -1);
-      if (ls->pick_actions[0])
-        gtk_radio_action_set_current_value (ls->pick_actions[0], idx);
+                          STRUCT_COL, &ldata, USER_ID_COL, &user_id, -1);
+      if (ldata && ldata->pick_action)
+        gtk_radio_action_set_current_value (ldata->pick_action, user_id);
     }
   ls->prevent_recursion = FALSE;
 }
@@ -243,16 +248,24 @@ ghid_layer_selector_class_init (GHidLayerSelectorClass *klass)
 static void
 ghid_layer_selector_finalize (GObject *object)
 {
-  int i;
+  GtkTreeIter iter;
   GHidLayerSelector *ls = (GHidLayerSelector *) object;
 
   g_object_unref (ls->action_group);
-  g_free (ls->view_actions);
-  g_free (ls->pick_actions);
-  for (i = 0; i < ls->n_actions; ++i)
-    if (ls->rows[i])
-      gtk_tree_row_reference_free (ls->rows[i]);
-  g_free (ls->rows);
+
+  gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
+  do
+    {
+      struct _layer *ldata;
+      gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
+                          &iter, STRUCT_COL, &ldata, -1);
+
+      g_object_unref (G_OBJECT (ldata->pick_action));
+      g_object_unref (G_OBJECT (ldata->view_action));
+      gtk_tree_row_reference_free (ldata->rref);
+      g_free (ldata);
+    }
+  while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ls->list_store), &iter));
 
   G_OBJECT_CLASS (ghid_layer_selector_parent_class)->finalize (object);
 }
@@ -309,7 +322,7 @@ ghid_layer_selector_new (void)
   GHidLayerSelector *ls = g_object_new (GHID_LAYER_SELECTOR_TYPE, NULL);
 
   /* action index, active, color, text, font, is_separator */
-  ls->list_store = gtk_list_store_new (N_COLS, G_TYPE_INT, G_TYPE_INT,
+  ls->list_store = gtk_list_store_new (N_COLS, G_TYPE_POINTER, G_TYPE_INT,
                                        G_TYPE_BOOLEAN, G_TYPE_STRING,
                                        G_TYPE_STRING, G_TYPE_STRING,
                                        G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
@@ -324,10 +337,6 @@ ghid_layer_selector_new (void)
   ls->action_group = gtk_action_group_new ("LayerSelector");
   ls->prevent_recursion = FALSE;
   ls->n_actions = 0;
-  ls->max_actions = INITIAL_ACTION_MAX;
-  ls->view_actions = g_malloc0 (ls->max_actions * sizeof (*ls->view_actions));
-  ls->pick_actions = g_malloc0 (ls->max_actions * sizeof (*ls->pick_actions));
-  ls->rows = g_malloc0 (ls->max_actions * sizeof (*ls->rows));
 
   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (ls),
                                         tree_view_separator_func,
@@ -372,6 +381,7 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
                                gboolean visible,
                                gboolean activatable)
 {
+  struct _layer *new_layer = NULL;
   gchar *pname, *vname, *paccel, *vaccel;
   gboolean new_iter = TRUE;
   gboolean last_activatable = TRUE;
@@ -408,12 +418,14 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
           /* Add separator between activatable/non-activatable boundaries */
           gtk_list_store_append (ls->list_store, &iter);
           gtk_list_store_set (ls->list_store, &iter,
+                              STRUCT_COL, NULL,
                               SEPARATOR_COL, TRUE, -1);
         }
       /* Create new layer */
+      new_layer = malloc (sizeof (*new_layer));
       gtk_list_store_append (ls->list_store, &iter);
       gtk_list_store_set (ls->list_store, &iter,
-                          INDEX_COL, ls->n_actions,
+                          STRUCT_COL, new_layer,
                           USER_ID_COL, user_id,
                           VISIBLE_COL, visible,
                           COLOR_COL, color_string,
@@ -424,40 +436,15 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
                           -1);
     }
   else
-    gtk_list_store_set (ls->list_store, &iter,
-                        VISIBLE_COL, visible,
-                        COLOR_COL, color_string,
-                        TEXT_COL, name,
-                        FONT_COL, activatable ? NULL : "Italic",
-                        ACTIVATABLE_COL, activatable,
-                        -1);
-
-  /* Unless we're adding new actions, we're done now */
-  if (!new_iter)
-    return;
-
-  if (activatable && ls->n_actions == 0)
-    gtk_tree_selection_select_iter (ls->selection, &iter);
-
-  /* Allocate new actions if necessary */
-  if (ls->n_actions == ls->max_actions)
     {
-      void *tmp[2];
-      ls->max_actions *= 2;
-      tmp[0] = g_realloc (ls->view_actions,
-                          ls->max_actions * sizeof (*ls->view_actions));
-      tmp[1] = g_realloc (ls->pick_actions,
-                          ls->max_actions * sizeof (*ls->pick_actions));
-      tmp[2] = g_realloc (ls->rows,
-                          ls->max_actions * sizeof (*ls->rows));
-      if (tmp[0] == NULL || tmp[1] == NULL || tmp[2] == NULL)
-        g_critical ("realloc failed allocating new actions");
-      else
-        {
-          ls->view_actions = tmp[0];
-          ls->pick_actions = tmp[1];
-          ls->rows = tmp[2];
-        }
+      gtk_list_store_set (ls->list_store, &iter,
+                          VISIBLE_COL, visible,
+                          COLOR_COL, color_string,
+                          TEXT_COL, name,
+                          FONT_COL, activatable ? NULL : "Italic",
+                          ACTIVATABLE_COL, activatable,
+                          -1);
+      return;
     }
 
   /* -- Setup new actions -- */
@@ -484,40 +471,43 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
 
   /* Create row reference for actions */
   path = gtk_tree_model_get_path (GTK_TREE_MODEL (ls->list_store), &iter);
-  ls->rows[ls->n_actions] = gtk_tree_row_reference_new
-                              (GTK_TREE_MODEL (ls->list_store), path);
+  new_layer->rref = gtk_tree_row_reference_new
+                      (GTK_TREE_MODEL (ls->list_store), path);
   gtk_tree_path_free (path);
 
   /* Create selection action */
   if (activatable)
     {
-      ls->pick_actions[ls->n_actions]
-        = gtk_radio_action_new (pname, name, NULL, NULL, ls->n_actions);
-      gtk_radio_action_set_group (ls->pick_actions[ls->n_actions],
-                                  ls->radio_group);
-      ls->radio_group
-         = gtk_radio_action_get_group (ls->pick_actions[ls->n_actions]);
+      new_layer->pick_action
+        = gtk_radio_action_new (pname, name, NULL, NULL, user_id);
+      gtk_radio_action_set_group (new_layer->pick_action, ls->radio_group);
+      ls->radio_group = gtk_radio_action_get_group (new_layer->pick_action);
       gtk_action_group_add_action_with_accel
         (ls->action_group,
-         GTK_ACTION (ls->pick_actions[ls->n_actions]),
+         GTK_ACTION (new_layer->pick_action),
          paccel);
-      g_signal_connect (ls->pick_actions[ls->n_actions], "toggled",
-                        G_CALLBACK (menu_pick_cb), ls->rows[ls->n_actions]);
+      g_signal_connect (new_layer->pick_action, "toggled",
+                        G_CALLBACK (menu_pick_cb), new_layer->rref);
     }
   else
-    ls->pick_actions[ls->n_actions] = NULL;
+    new_layer->pick_action = NULL;
 
   /* Create visibility action */
-  ls->view_actions[ls->n_actions] = gtk_toggle_action_new (vname, name,
-                                                           NULL, NULL);
-  gtk_toggle_action_set_active (ls->view_actions[ls->n_actions], visible);
+  new_layer->view_action = gtk_toggle_action_new (vname, name, NULL, NULL);
+  gtk_toggle_action_set_active (new_layer->view_action, visible);
 
   gtk_action_group_add_action_with_accel
     (ls->action_group,
-     GTK_ACTION (ls->view_actions[ls->n_actions]),
+     GTK_ACTION (new_layer->view_action),
      vaccel);
-  g_signal_connect (ls->view_actions[ls->n_actions], "toggled",
-                    G_CALLBACK (menu_view_cb), ls->rows[ls->n_actions]);
+  g_signal_connect (new_layer->view_action, "toggled",
+                    G_CALLBACK (menu_view_cb), new_layer->rref);
+
+
+  /* Select new layer, if we need */
+  if (activatable
+      && !gtk_tree_selection_get_selected (ls->selection, NULL, NULL))
+    gtk_tree_selection_select_iter (ls->selection, &iter);
 
   /* cleanup */
   if (vaccel)
@@ -531,6 +521,35 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
   ls->n_actions++;
 }
 
+/*! \brief used internally */
+static gboolean
+pick_xml_foreach_func (GtkTreeModel *model, GtkTreePath *path,
+                       GtkTreeIter *iter, gpointer data)
+{
+  struct _layer *ldata;
+  GString *str = data;
+  
+  gtk_tree_model_get (model, iter, STRUCT_COL, &ldata, -1);
+  if (ldata && ldata->pick_action)
+    g_string_append_printf (str, "<menuitem action=\"%s\" />\n",
+                     gtk_action_get_name (GTK_ACTION (ldata->pick_action)));
+  return FALSE;
+}
+/*! \brief used internally */
+static gboolean
+view_xml_foreach_func (GtkTreeModel *model, GtkTreePath *path,
+                       GtkTreeIter *iter, gpointer data)
+{
+  struct _layer *ldata;
+  GString *str = data;
+  
+  gtk_tree_model_get (model, iter, STRUCT_COL, &ldata, -1);
+  if (ldata && ldata->view_action)
+    g_string_append_printf (str, "<menuitem action=\"%s\" />\n",
+                     gtk_action_get_name (GTK_ACTION (ldata->view_action)));
+  return FALSE;
+}
+ 
 /*! \brief Get the "Current Layer" menu description of a layer selector
  *  \par Function Description
  *  Returns the XML content used by Gtk in building the layer-selection
@@ -544,13 +563,9 @@ ghid_layer_selector_add_layer (GHidLayerSelector *ls,
 gchar *
 ghid_layer_selector_get_pick_xml (GHidLayerSelector *ls)
 {
-  int i;
   GString *str = g_string_new ("");
-
-  for (i = 0; i < ls->n_actions; ++i)
-    if (ls->pick_actions[i])
-      g_string_append_printf (str, "<menuitem action=\"LayerPick%d\" />\n", i);
-
+  gtk_tree_model_foreach (GTK_TREE_MODEL (ls->list_store),
+                          pick_xml_foreach_func, str);
   return g_string_free (str, FALSE);
 }
 
@@ -567,13 +582,9 @@ ghid_layer_selector_get_pick_xml (GHidLayerSelector *ls)
 gchar *
 ghid_layer_selector_get_view_xml (GHidLayerSelector *ls)
 {
-  int i;
   GString *str = g_string_new ("");
-
-  for (i = 0; i < ls->n_actions; ++i)
-    if (ls->view_actions[i])
-      g_string_append_printf (str, "<menuitem action=\"LayerView%d\" />\n", i);
-
+  gtk_tree_model_foreach (GTK_TREE_MODEL (ls->list_store),
+                          view_xml_foreach_func, str);
   return g_string_free (str, FALSE);
 }
 
@@ -780,11 +791,12 @@ ghid_layer_selector_delete_layers (GHidLayerSelector *ls,
   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls->list_store), &iter);
   do
     {
+      struct _layer *ldata;
       gboolean sep;
-      gint user_id, idx;
+      gint user_id;
       gtk_tree_model_get (GTK_TREE_MODEL (ls->list_store),
                           &iter, USER_ID_COL, &user_id,
-                          INDEX_COL, &idx, SEPARATOR_COL, &sep, -1);
+                          STRUCT_COL, &ldata, SEPARATOR_COL, &sep, -1);
       /* gtk_list_store_remove will increment the iter for us, so we
        *  don't want to do it again in the loop condition */
       needs_inc = TRUE;
@@ -792,16 +804,14 @@ ghid_layer_selector_delete_layers (GHidLayerSelector *ls,
         {
           if (gtk_list_store_remove (ls->list_store, &iter))
             {
-              if (ls->view_actions[idx])
+              if (ldata->view_action)
                 gtk_action_group_remove_action (ls->action_group,
-                                                GTK_ACTION (ls->view_actions[idx]));
-              if (ls->pick_actions[idx])
+                                                GTK_ACTION (ldata->view_action));
+              if (ldata->pick_action)
                 gtk_action_group_remove_action (ls->action_group,
-                                                GTK_ACTION (ls->pick_actions[idx]));
-              gtk_tree_row_reference_free (ls->rows[idx]);
-              ls->view_actions[idx] = NULL;
-              ls->pick_actions[idx] = NULL;
-              ls->rows[idx] = NULL;
+                                                GTK_ACTION (ldata->pick_action));
+              gtk_tree_row_reference_free (ldata->rref);
+              g_free (ldata);
               needs_inc = FALSE;
             }
           else

commit 707dd2dc7245cec6f15adc1ae1f735944602c561
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Replace gtk UI manager with GHidMainMenu widget
    
    This commit replaces the old UI manager with a customize gtk
    widget that builds menus directly from a resource tree. This
    eliminates the translate-to-XML step, the ugly hacks used to
    access individual actions, and all the associated manual
    memory management.
    
    This will also give us the ability to have more dynamic menus,
    in particular layer lists without maximum capacities.
    
    Layers and route styles are still not hooked into the menu.
    This means that those accelerators DO NOT WORK. (This will
    be fixed in a later commit.) Checkboxes have been replaced
    with radio buttons where appropriate. There are now tearoffs
    on the context-menu's submenus.
    
    Other than that, there should be no user-visible changes. ;)

diff --git a/src/hid/gtk/gtkhid-main.c b/src/hid/gtk/gtkhid-main.c
index 0c9019d..46a9347 100644
--- a/src/hid/gtk/gtkhid-main.c
+++ b/src/hid/gtk/gtkhid-main.c
@@ -1937,8 +1937,7 @@ button number must be specified as the second argument.
 static int
 Popup (int argc, char **argv, Coord x, Coord y)
 {
-  GtkWidget *menu;
-  char *element;
+  GtkMenu *menu;
   guint button;
 
   if (argc != 1 && argc != 2)
@@ -1949,18 +1948,7 @@ Popup (int argc, char **argv, Coord x, Coord y)
   else
     button = atoi (argv[1]);
 
-  if ( (element = (char *) malloc ( (strlen (argv[0]) + 2) * sizeof (char))) == NULL )
-    {
-      fprintf (stderr, _("Popup():  malloc failed\n"));
-      exit (1);
-    }
-
-  sprintf (element, "/%s", argv[0]);
-  printf (_("Loading popup \"%s\". Button = %u\n"), element, button);
-
-  menu = gtk_ui_manager_get_widget (ghidgui->ui_manager, element);
-  free (element);
-
+  menu = ghid_main_menu_get_popup (GHID_MAIN_MENU (ghidgui->menu_bar), argv[0]);
   if (! GTK_IS_MENU (menu))
     {
       Message (_("The specified popup menu \"%s\" has not been defined.\n"), argv[0]);
@@ -1970,7 +1958,7 @@ Popup (int argc, char **argv, Coord x, Coord y)
     {
       ghidgui->in_popup = TRUE;
       gtk_widget_grab_focus (ghid_port.drawing_area);
-      gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, 
+      gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, 
 		      gtk_get_current_event_time());
     }
   return 0;
diff --git a/src/hid/gtk/gui-command-window.c b/src/hid/gtk/gui-command-window.c
index b08836e..1728bd3 100644
--- a/src/hid/gtk/gui-command-window.c
+++ b/src/hid/gtk/gui-command-window.c
@@ -415,8 +415,8 @@ ghid_command_entry_get (gchar * prompt, gchar * command)
      |  and connect a handler to look for the escape key.
    */
   gtk_window_remove_accel_group (GTK_WINDOW (out->top_window),
-				 gtk_ui_manager_get_accel_group (ghidgui->
-								 ui_manager));
+                                 ghid_main_menu_get_accel_group
+                                   (GHID_MAIN_MENU (ghidgui->menu_bar)));
   ghid_interface_input_signals_disconnect ();
   ghid_interface_set_sensitive (FALSE);
   gtk_widget_grab_focus (GTK_WIDGET (ghidgui->command_entry));
@@ -438,8 +438,8 @@ ghid_command_entry_get (gchar * prompt, gchar * command)
   ghid_interface_input_signals_connect ();
   ghid_interface_set_sensitive (TRUE);
   gtk_window_add_accel_group (GTK_WINDOW (out->top_window),
-			      gtk_ui_manager_get_accel_group (ghidgui->
-							      ui_manager));
+                              ghid_main_menu_get_accel_group
+                                (GHID_MAIN_MENU (ghidgui->menu_bar)));
 
   /* Restore the status line label and give focus back to the drawing area
    */
diff --git a/src/hid/gtk/gui-output-events.c b/src/hid/gtk/gui-output-events.c
index 5929a37..b9c5ab3 100644
--- a/src/hid/gtk/gui-output-events.c
+++ b/src/hid/gtk/gui-output-events.c
@@ -199,7 +199,7 @@ ghid_idle_cb (gpointer data)
 
 gboolean
 ghid_port_key_release_cb (GtkWidget * drawing_area, GdkEventKey * kev,
-			  GtkUIManager * ui)
+			  gpointer data)
 {
   gint ksym = kev->keyval;
 
@@ -225,7 +225,7 @@ ghid_port_key_release_cb (GtkWidget * drawing_area, GdkEventKey * kev,
 
 gboolean
 ghid_port_key_press_cb (GtkWidget * drawing_area,
-			GdkEventKey * kev, GtkUIManager * ui)
+			GdkEventKey * kev, gpointer data)
 {
   ModifierKeysState mk;
   gint  ksym = kev->keyval;
@@ -332,7 +332,7 @@ ghid_port_key_press_cb (GtkWidget * drawing_area,
 
 gboolean
 ghid_port_button_press_cb (GtkWidget * drawing_area,
-			   GdkEventButton * ev, GtkUIManager * ui)
+			   GdkEventButton * ev, gpointer data)
 {
   ModifierKeysState mk;
   GdkModifierType state;
@@ -357,7 +357,7 @@ ghid_port_button_press_cb (GtkWidget * drawing_area,
 
 gboolean
 ghid_port_button_release_cb (GtkWidget * drawing_area,
-			     GdkEventButton * ev, GtkUIManager * ui)
+			     GdkEventButton * ev, gpointer data)
 {
   ModifierKeysState mk;
   GdkModifierType state;
diff --git a/src/hid/gtk/gui-top-window.c b/src/hid/gtk/gui-top-window.c
index b4dbcaa..b2d9c22 100644
--- a/src/hid/gtk/gui-top-window.c
+++ b/src/hid/gtk/gui-top-window.c
@@ -136,19 +136,6 @@ RCSID ("$Id$");
  * local types
  */
 
-
-typedef enum {GHID_FLAG_ACTIVE, GHID_FLAG_CHECKED, GHID_FLAG_VISIBLE} MenuFlagType;
-
-/* Used by the menuitems that are toggle actions */
-typedef struct
-{
-  const char *actionname;
-  const char *flagname;
-  MenuFlagType flagtype;
-  int oldval;
-  const char *xres;
-} ToggleFlagType;
-
 /* Used by the route style buttons and menu */
 typedef struct
 {
@@ -169,47 +156,25 @@ RouteStyleButton;
 
 #define N_ROUTE_STYLES (NUM_STYLES + 3)
 
-static void ghid_load_menus (void);
-static void ghid_ui_info_append (const gchar *);
-static void ghid_ui_info_indent (int);
-
 static bool ignore_layer_update;
-static gchar * new_ui_info;
-static size_t new_ui_info_sz = 0;
-
-/* the array of actions for "normal" menuitems */
-static GtkActionEntry *new_entries = NULL;
-static gint menuitem_cnt = 0;
-
-/* the array of actions for "toggle" menuitems */
-static GtkToggleActionEntry *new_toggle_entries = NULL;
-static gint tmenuitem_cnt = 0;
 
-static Resource **action_resources = NULL;
-static Resource **toggle_action_resources = NULL;
+static GtkWidget *ghid_load_menus (void);
 
 /* actions for the @routestyles menuitems */
 static GtkToggleActionEntry routestyle_toggle_entries[N_ROUTE_STYLES];
 static Resource *routestyle_resources[N_ROUTE_STYLES];
-
-#define MENUITEM "MenuItem"
-#define TMENUITEM "TMenuItem"
 #define ROUTESTYLE "RouteStyle"
 
-
-static ToggleFlagType *tflags = 0;
-static int n_tflags = 0;
-static int max_tflags = 0;
-
 GhidGui _ghidgui, *ghidgui = NULL;
 
 GHidPort ghid_port, *gport;
 
-static GdkColor WhitePixel;
-
-static gchar		*bg_image_file;
+static gchar *bg_image_file;
 
-static char *ghid_hotkey_actions[256];
+static struct { GtkAction *action; const Resource *node; }
+  ghid_hotkey_actions[256];
+#define N_HOTKEY_ACTIONS \
+        (sizeof (ghid_hotkey_actions) / sizeof (ghid_hotkey_actions[0]))
 
 
 /* ------------------------------------------------------------------
@@ -228,125 +193,39 @@ static gint route_style_index;
 
 static GtkWidget *route_style_edit_button;
 
-static const char *
-ghid_check_unique_accel (const char *accelerator)
+/*! \brief callback for ghid_main_menu_update_toggle_state () */
+void
+menu_toggle_update_cb (GtkAction *act, const char *tflag, const char *aflag)
 {
-  static int n_list = 0;
-  static char **accel_list;
-  static int amax = 0;
-  int i;
-  const char * a = accelerator;
-
-  if (accelerator == NULL)
-    return NULL;
-
-  if (strlen (accelerator) == 0)
-    return accelerator;
-
-  if (amax >= n_list) 
+  if (tflag != NULL)
     {
-      n_list += 128;
-      if ( (accel_list = (char **)realloc (accel_list, n_list * sizeof (char *))) == NULL)
-	{
-	  fprintf (stderr, "%s():  realloc failed\n", __FUNCTION__);
-	  exit (1);
-	}
+      int v = hid_get_flag (tflag);
+      gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (act), !!v);
     }
-
-  for (i = 0; i < amax ; i++) 
-    {
-      if (strcmp (accel_list[i], accelerator) == 0)
-	{
-	  Message (_("Duplicate accelerator found: \"%s\"\n"
-		   "The second occurance will be dropped\n"),
-		   accelerator);
-	  a = NULL;
-	  break;
-	}
-    }
-  accel_list[amax] = strdup (accelerator);
-  amax++;
-
-  return a;
-}
-
-
-/* ------------------------------------------------------------------
- *  note_toggle_flag()
- */
-
-static void
-note_toggle_flag (const char *actionname, MenuFlagType type, const char *name)
-{
-
-  #ifdef DEBUG_MENUS
-  printf ("note_toggle_flag(\"%s\", %d, \"%s\")\n", actionname, type, name);
-  #endif
-
-  if (n_tflags >= max_tflags)
+  if (aflag != NULL)
     {
-      max_tflags += 20;
-      tflags = (ToggleFlagType *)realloc (tflags, max_tflags * sizeof (ToggleFlagType));
+      int v = hid_get_flag (aflag);
+      gtk_action_set_sensitive (act, !!v);
     }
-  tflags[n_tflags].actionname = strdup (actionname);
-  tflags[n_tflags].flagname = name;
-  tflags[n_tflags].flagtype = type;
-  tflags[n_tflags].oldval = -1;
-  tflags[n_tflags].xres = "none";
-  n_tflags++;
 }
 
-
+/*! \brief sync the menu checkboxes with actual pcb state */
 void
 ghid_update_toggle_flags ()
 {
   int i;
 
   GtkAction *a;
-  gboolean old_holdoff;
   gboolean active;
+  gboolean old_holdoff;
   char tmpnm[40];
-  GValue setfalse = { 0 };
-  GValue settrue = { 0 };
-  GValue setlabel = { 0 };
-
-  g_value_init (&setfalse, G_TYPE_BOOLEAN);
-  g_value_init (&settrue, G_TYPE_BOOLEAN);
-  g_value_set_boolean (&setfalse, FALSE);
-  g_value_set_boolean (&settrue, TRUE);
-  g_value_init (&setlabel, G_TYPE_STRING);
 
   /* mask the callbacks */
   old_holdoff = ghidgui->toggle_holdoff;
   ghidgui->toggle_holdoff = TRUE;
 
-  for (i = 0; i < n_tflags; i++)
-    {
-      switch (tflags[i].flagtype)
-	{
-	case GHID_FLAG_ACTIVE:
-	  {
-	    int v = hid_get_flag (tflags[i].flagname);
-	    a = gtk_action_group_get_action (ghidgui->main_actions, tflags[i].actionname);
-	    g_object_set_property (G_OBJECT (a), "sensitive", v? &settrue : &setfalse);
-	    tflags[i].oldval = v;
-	  }
-	  break;
-
-	case GHID_FLAG_CHECKED:
-	  {
-	    int v = hid_get_flag (tflags[i].flagname);
-	    a = gtk_action_group_get_action (ghidgui->main_actions, tflags[i].actionname);
-	    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (a), v? TRUE : FALSE);
-	    tflags[i].oldval = v;
-	  }
-	  break;
-
-	default:
-	  printf ("Skipping flagtype %d\n", tflags[i].flagtype);
-	  break;
-	}
-    }
+  ghid_main_menu_update_toggle_state (GHID_MAIN_MENU (ghidgui->menu_bar),
+                                      menu_toggle_update_cb);
 
   for (i = 0; i < N_ROUTE_STYLES; i++)
     {
@@ -354,7 +233,7 @@ ghid_update_toggle_flags ()
       a = gtk_action_group_get_action (ghidgui->main_actions, tmpnm);
       if (i >= NUM_STYLES)
 	{
-	  g_object_set_property (G_OBJECT (a), "visible", &setfalse);
+	  gtk_action_set_visible (a, FALSE);
 	}
 
       /* Update the toggle states */
@@ -366,11 +245,7 @@ ghid_update_toggle_flags ()
 	
     }
 
-  g_value_unset (&setfalse);
-  g_value_unset (&settrue);
-  g_value_unset (&setlabel);
   ghidgui->toggle_holdoff = old_holdoff;
-
 }
 
 static void
@@ -412,103 +287,47 @@ top_window_configure_event_cb (GtkWidget * widget, GdkEventConfigure * ev,
 }
 
 
-/*
- * This is the main menu callback function.  The callback looks at
- * the gtk action name to figure out which menuitem was chosen.  Then
- * it looks up in a table to find the pcb actions which should be
- * executed.  All menus go through this callback.  The tables of
- * actions are loaded from the menu resource file at startup.
+/*! \brief Menu action callback function
+ *  \par Function Description
+ *  This is the main menu callback function.  The callback receives
+ *  the original Resource pointer containing the HID actions to be
+ *  executed.
  *
- * In addition, all hotkeys go through the menus which means they go
- * through here.
+ *  All hotkeys go through the menus which means they go through here.
+ *  Some, such as tab, are caught by Gtk instead of passed here, so
+ *  pcb calls this function directly through ghid_hotkey_cb() for them.
+ *
+ *  \param [in]   The action that was activated
+ *  \param [in]   The menu resource associated with the action
  */
 
 static void
-ghid_menu_cb (GtkAction * action, gpointer data)
+ghid_menu_cb (GtkAction *action, const Resource *node)
 {
-  const gchar * name;
-  int id = 0;
-  int vi;
-  const Resource *node = NULL;
-  static int in_cb = 0;
-  gboolean old_holdoff;
+  const gchar *name = gtk_action_get_name (action);
 
-  /* If we don't do this then we can end up in loops where changing
-   * the state of the toggle actions triggers the callbacks and
-   * the call back updates the state of the actions.
-   */
-  if (in_cb)
+  if (action == NULL || node == NULL) 
+    return;
+
+  /* Prevent recursion */
+  if (ghidgui->toggle_holdoff == TRUE) 
     return;
-  else
-    in_cb = 1;
 
-  /* 
-   * Normally this callback is triggered by the menus in which case
-   * action will be the gtk action which was triggered.  In the case
-   * of the "special" hotkeys we will call this callback directly and
-   * pass in the name of the menu that it corresponds to in via the
-   * data argument
-   */
-  if (action != NULL) 
-    {
-      name = gtk_action_get_name (action);
-    }
-  else
-    {
-      name = (const char *) data;
 #ifdef DEBUG_MENUS
-      printf ("ghid_menu_cb():  name = \"%s\"\n", UNKNOWN (name));
+  printf ("ghid_menu_cb():  name = \"%s\"\n", name);
 #endif
-    }
 
-  if (name == NULL)
+  /* Special-case route styles */
+  if (strncmp (name, ROUTESTYLE, strlen (ROUTESTYLE)) == 0)
     {
-      fprintf (stderr, "%s(%p, %p):  name == NULL\n", 
-	       __FUNCTION__, action, data);
-      in_cb = 0;
-      return;
-    }
-
-  if ( strncmp (name, MENUITEM, strlen (MENUITEM)) == 0)
-    {
-      /* This is a "normal" menuitem as opposed to a toggle menuitem
-       */
-      id = atoi (name + strlen (MENUITEM));
-      node = action_resources[id];
-    }
-  else if ( strncmp (name, TMENUITEM, strlen (TMENUITEM)) == 0)
-    {
-      /* This is a "toggle" menuitem */
-      id = atoi (name + strlen (TMENUITEM));
-
-      /* toggle_holdoff lets us update the state of the menus without
-       * actually triggering all the callbacks
-       */
-      if (ghidgui->toggle_holdoff == TRUE) 
-	node = NULL;
-      else
-	node = toggle_action_resources[id];
-    }
-  else if ( strncmp (name, ROUTESTYLE, strlen (ROUTESTYLE)) == 0)
-    {
-      id = atoi (name + strlen (ROUTESTYLE));
+      int id = atoi (name + strlen (ROUTESTYLE));
       if (ghidgui->toggle_holdoff != TRUE) 
 	ghid_route_style_button_set_active (id);
       node = NULL;
     }
   else
     {
-      fprintf (stderr, "ERROR:  ghid_menu_cb():  name = \"%s\" is unknown\n", name);
-    }
-    
-
-#ifdef DEBUG_MENUS
-  printf ("ghid_menu_cb():  name = \"%s\", id = %d\n", name, id);
-#endif
-
-  /* Now we should have a pointer to the actions to execute */
-  if (node != NULL)
-    {
+      int vi;
       for (vi = 1; vi < node->c; vi++)
 	if (resource_type (node->v[vi]) == 10)
 	  {
@@ -518,67 +337,24 @@ ghid_menu_cb (GtkAction * action, gpointer data)
 	    hid_parse_actions (node->v[vi].value);
 	  }
     }
-  else {
-#ifdef DEBUG_MENUS
-    printf ("    NOOP\n");
-#endif
-  }
-
 
-  /*
-   * Now mask off any callbacks and update the state of any toggle
-   * menuitems.  This is where we do things like sync the layer or
-   * tool checks marks in the menus with the layer or tool buttons
-   */
-  old_holdoff = ghidgui->toggle_holdoff;
-  ghidgui->toggle_holdoff = TRUE;
+  /* Sync gui widgets with pcb state */
   ghid_update_toggle_flags ();
-  ghidgui->toggle_holdoff = old_holdoff;
-  
-  in_cb = 0;
-
-  /*
-   * and finally, make any changes show up in the status line and the
-   * screen 
-   */
-  if (ghidgui->toggle_holdoff == FALSE) 
-    {
-      AdjustAttachedObjects ();
-      ghid_invalidate_all ();
-      ghid_window_set_name_label (PCB->Name);
-      ghid_set_status_line_label ();
-#ifdef FIXME
-      g_idle_add (ghid_idle_cb, NULL);
-#endif
-    }
+  ghid_mode_buttons_update ();
 
+  /* Sync gui status display with pcb state */
+  AdjustAttachedObjects ();
+  ghid_invalidate_all ();
+  ghid_window_set_name_label (PCB->Name);
+  ghid_set_status_line_label ();
 }
 
+/* \brief Accelerator callback for accelerators gtk tries to hide from us */
 void ghid_hotkey_cb (int which)
 {
-#ifdef DEBUG_MENUS
-  printf ("%s(%d) -> \"%s\"\n", __FUNCTION__, 
-	  which, UNKNOWN (ghid_hotkey_actions[which]));
-#endif
-  if (ghid_hotkey_actions[which] != NULL)
-    ghid_menu_cb (NULL, ghid_hotkey_actions[which]);
-}
-
-
-/* ============== ViewMenu callbacks =============== */
-void
-ghid_set_menu_toggle_button (GtkActionGroup * ag, gchar * name,
-			     gboolean state)
-{
-  GtkAction *action;
-  gboolean old_holdoff;
-
-  old_holdoff = ghidgui->toggle_holdoff;
-  ghidgui->toggle_holdoff = TRUE;
-  action = gtk_action_group_get_action (ag, name);
-  if (action)
-    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), state);
-  ghidgui->toggle_holdoff = old_holdoff;
+  if (ghid_hotkey_actions[which].action != NULL)
+    ghid_menu_cb (ghid_hotkey_actions[which].action,
+                  (gpointer) ghid_hotkey_actions[which].node);
 }
 
   /* Sync toggle states that were saved with the layout and notify the
@@ -804,11 +580,6 @@ ghid_make_programmed_menu_actions ()
 static void
 make_menu_actions (GtkActionGroup * actions, GHidPort * port)
 {
-  gtk_action_group_add_actions (actions, new_entries, menuitem_cnt, port);
-
-  gtk_action_group_add_toggle_actions (actions, new_toggle_entries,
-				       tmenuitem_cnt, port);
-
   ghid_make_programmed_menu_actions ();
 
   gtk_action_group_add_toggle_actions (actions,
@@ -823,21 +594,16 @@ make_menu_actions (GtkActionGroup * actions, GHidPort * port)
  * load the ui_manager string.
  */
 static void
-make_top_menubar (GtkWidget * hbox, GHidPort * port)
+make_top_menubar (GtkWidget *menu_bar, GtkWidget * hbox, GHidPort * port)
 {
-  GtkUIManager *ui;
   GtkWidget *frame;
   GtkActionGroup *actions;
   GtkActionGroup *layer_actions;
-  GError *error = NULL;
 
   frame = gtk_frame_new (NULL);
   gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
 
-  ui = gtk_ui_manager_new ();
-  ghidgui->ui_manager = ui;
-
   actions = gtk_action_group_new ("Actions");
   gtk_action_group_set_translation_domain (actions, NULL);
   ghidgui->main_actions = actions;
@@ -846,22 +612,11 @@ make_top_menubar (GtkWidget * hbox, GHidPort * port)
   layer_actions = ghid_layer_selector_get_action_group
           (GHID_LAYER_SELECTOR (ghidgui->layer_selector));
  
-  gtk_ui_manager_insert_action_group (ui, actions, 0);
-  gtk_ui_manager_insert_action_group (ui, layer_actions, 0);
-
   gtk_window_add_accel_group (GTK_WINDOW (gport->top_window),
-			      gtk_ui_manager_get_accel_group (ui));
+			      ghid_main_menu_get_accel_group
+                                (GHID_MAIN_MENU (ghidgui->menu_bar)));
 
-  if (!gtk_ui_manager_add_ui_from_string (ui, new_ui_info, -1, &error))
-    {
-      g_message ("building menus failed: %s", error->message);
-      g_error_free (error);
-    }
-
-  gtk_ui_manager_set_add_tearoffs (ui, TRUE);
-
-  gtk_container_add (GTK_CONTAINER (frame),
-		     gtk_ui_manager_get_widget (ui, "/MenuBar"));
+  gtk_container_add (GTK_CONTAINER (frame), menu_bar);
 
 }
 
@@ -1503,8 +1258,8 @@ ghid_build_pcb_top_window (void)
                     G_CALLBACK (layer_selector_toggle_callback),
                     NULL);
   /* Build main menu */
-  ghid_load_menus ();
-  make_top_menubar(hbox, port);
+  ghidgui->menu_bar = ghid_load_menus ();
+  make_top_menubar (ghidgui->menu_bar, hbox, port);
 
   frame = gtk_frame_new(NULL);
   gtk_widget_show(frame);
@@ -1736,23 +1491,19 @@ ghid_interface_input_signals_connect (void)
 {
   button_press_handler =
     g_signal_connect (G_OBJECT (gport->drawing_area), "button_press_event",
-		      G_CALLBACK (ghid_port_button_press_cb),
-		      ghidgui->ui_manager);
+		      G_CALLBACK (ghid_port_button_press_cb), NULL);
 
   button_release_handler =
     g_signal_connect (G_OBJECT (gport->drawing_area), "button_release_event",
-		      G_CALLBACK (ghid_port_button_release_cb),
-		      ghidgui->ui_manager);
+		      G_CALLBACK (ghid_port_button_release_cb), NULL);
 
   key_press_handler =
     g_signal_connect (G_OBJECT (gport->drawing_area), "key_press_event",
-		      G_CALLBACK (ghid_port_key_press_cb),
-		      ghidgui->ui_manager);
+		      G_CALLBACK (ghid_port_key_press_cb), NULL);
 
   key_release_handler =
     g_signal_connect (G_OBJECT (gport->drawing_area), "key_release_event",
-		      G_CALLBACK (ghid_port_key_release_cb),
-		      ghidgui->ui_manager);
+		      G_CALLBACK (ghid_port_key_release_cb), NULL);
 }
 
 void
@@ -1825,8 +1576,6 @@ ghid_create_pcb_widgets (void)
   GHidPort *port = &ghid_port;
   GError	*err = NULL;
 
-  gdk_color_parse ("white", &WhitePixel);
-
   if (bg_image_file)
     ghidgui->bg_pixbuf = gdk_pixbuf_new_from_file(bg_image_file, &err);
   if (err)
@@ -2078,7 +1827,7 @@ ghid_do_export (HID_Attr_Val * options)
 gint
 LayersChanged (int argc, char **argv, Coord x, Coord y)
 {
-  if (!ghidgui || !ghidgui->ui_manager)
+  if (!ghidgui || !ghidgui->menu_bar)
     return 0;
 
   ghid_config_groups_changed();
@@ -2241,16 +1990,16 @@ REGISTER_ACTIONS (gtk_topwindow_action_list)
  * into the menu callbacks.  This function is called as new
  * accelerators are added when the menus are being built
  */
-static void ghid_check_special_key (const char *accel, const char *name)
+static void
+ghid_check_special_key (const char *accel, GtkAction *action,
+                        const Resource *node)
 {
   size_t len;
   unsigned int mods;
   unsigned int ind;
 
-  if ( accel == NULL || *accel == '\0' )
-    {
-      return ;
-    }
+  if (action == NULL || accel == NULL || *accel == '\0')
+    return ;
 
 #ifdef DEBUG_MENUS
   printf ("%s(\"%s\", \"%s\")\n", __FUNCTION__, accel, name);
@@ -2261,7 +2010,7 @@ static void ghid_check_special_key (const char *accel, const char *name)
     {
       mods |= GHID_KEY_ALT;
     }
-  if (strstr (accel, "<control>") )
+  if (strstr (accel, "<ctrl>") )
     {
       mods |= GHID_KEY_CONTROL;
     }
@@ -2299,14 +2048,15 @@ static void ghid_check_special_key (const char *accel, const char *name)
 
   if (ind > 0) 
     {
-      if (ind >= (sizeof (ghid_hotkey_actions) / sizeof (char *)) )
+      if (ind >= N_HOTKEY_ACTIONS)
 	{
 	  fprintf (stderr, "ERROR:  overflow of the ghid_hotkey_actions array.  Index = %d\n"
 		   "Please report this.\n", ind);
 	  exit (1);
 	}
 
-      ghid_hotkey_actions[ind] = g_strdup (name);
+      ghid_hotkey_actions[ind].action = action;
+      ghid_hotkey_actions[ind].node = node;
 #ifdef DEBUG_MENUS
       printf ("Adding \"special\" hotkey to ghid_hotkey_actions[%u] :"
 	      " %s (%s)\n", ind, accel, name);
@@ -2314,772 +2064,49 @@ static void ghid_check_special_key (const char *accel, const char *name)
     }
 }
 
-
-#define INDENT_INC 5
-
-static void
-ghid_append_action (const char * name, const char *stock_id, 
-		    const char *label, const char *accelerator,
-		    const char *tooltip)
-{
-
-#ifdef DEBUG_MENUS
-  printf ("ghid_append_action(\"%s\", \"%s\", \"%s\",  \"%s\", \"%s\")\n",
-	  UNKNOWN (name), 
-	  UNKNOWN (stock_id), 
-	  UNKNOWN (label), 
-	  UNKNOWN (accelerator), 
-	  UNKNOWN (tooltip));
-#endif
-
-  accelerator = ghid_check_unique_accel (accelerator);
-
-  if ( (new_entries = (GtkActionEntry *)realloc (new_entries, 
-			       (menuitem_cnt + 1) * sizeof (GtkActionEntry))) == NULL)
-    {
-      fprintf (stderr, "ghid_append_action():  realloc of new_entries failed\n");
-      exit (1);
-    }
-  
-
-  if ( (action_resources = (Resource **)realloc (action_resources,
-				    (menuitem_cnt + 1) * sizeof (Resource *))) == NULL)
-    {
-      fprintf (stderr, "ghid_append_action():  realloc of action_resources failed\n");
-      exit (1);
-    }
-  action_resources[menuitem_cnt] = NULL;
-
-  /* name, stock_id, label, accelerator, tooltip, callback */
-  new_entries[menuitem_cnt].name = strdup (name);
-  new_entries[menuitem_cnt].stock_id = (stock_id == NULL ? NULL : strdup (stock_id));
-  new_entries[menuitem_cnt].label = strdup (label);
-  new_entries[menuitem_cnt].accelerator = ( (accelerator == NULL || *accelerator == '\0')
-					    ? NULL : strdup (accelerator));
-  new_entries[menuitem_cnt].tooltip = (tooltip == NULL ? NULL : strdup (tooltip));
-  new_entries[menuitem_cnt].callback = G_CALLBACK (ghid_menu_cb);
-
-  ghid_check_special_key (accelerator, name);
-  menuitem_cnt++;
-}
-
-static void
-ghid_append_toggle_action (const char * name, const char *stock_id, 
-			   const char *label, const char *accelerator,
-			   const char *tooltip, int active)
-{
-
-  accelerator = ghid_check_unique_accel (accelerator);
-
-  if ( (new_toggle_entries = (GtkToggleActionEntry *)realloc (new_toggle_entries, 
-				      (tmenuitem_cnt + 1) * sizeof (GtkToggleActionEntry))) == NULL)
-    {
-      fprintf (stderr, "ghid_append_toggle_action():  realloc of new_toggle_entries failed\n");
-      exit (1);
-    }
-  
-
-  if ( (toggle_action_resources = (Resource **)realloc (toggle_action_resources,
-				    (tmenuitem_cnt + 1) * sizeof (Resource *))) == NULL)
-    {
-      fprintf (stderr, "ghid_append_toggle_action():  realloc of toggle_action_resources failed\n");
-      exit (1);
-    }
-  toggle_action_resources[tmenuitem_cnt] = NULL;
-
-  /* name, stock_id, label, accelerator, tooltip, callback */
-  new_toggle_entries[tmenuitem_cnt].name = strdup (name);
-  new_toggle_entries[tmenuitem_cnt].stock_id = (stock_id == NULL ? NULL : strdup (stock_id));
-  new_toggle_entries[tmenuitem_cnt].label = strdup (label);
-  new_toggle_entries[tmenuitem_cnt].accelerator = (accelerator == NULL ? NULL : strdup (accelerator));
-  new_toggle_entries[tmenuitem_cnt].tooltip = (tooltip == NULL ? NULL : strdup (tooltip));
-  new_toggle_entries[tmenuitem_cnt].callback = G_CALLBACK (ghid_menu_cb);
-  new_toggle_entries[tmenuitem_cnt].is_active = active ? TRUE : FALSE;
-
-  ghid_check_special_key (accelerator, name);
-  tmenuitem_cnt++;
-}
-
-/*
- * Some keys need to be replaced by a name for the gtk accelerators to
- * work.  This table contains the translations.  The "in" character is
- * what would appear in gpcb-menu.res and the "out" string is what we
- * have to feed to gtk.  I was able to find these by using xev to find
- * the keycode and then looked at gtk+-2.10.9/gdk/keynames.txt (from the
- * gtk source distribution) to figure out the names that go with the 
- * codes.
- */
-typedef struct
-{
-  const char in;
-  const char *out;
-} KeyTable;
-static KeyTable key_table[] = 
-  {
-    {':', "colon"},
-    {'=', "equal"},
-    {'/', "slash"},
-    {'[', "bracketleft"},
-    {']', "bracketright"},
-    {'.', "period"},
-    {'|', "bar"}
-  };
-static int n_key_table = sizeof (key_table) / sizeof (key_table[0]);
-
-static void
-add_resource_to_menu (const char * menu, const Resource * node, int indent)
-{
-  int i, j;
-  const char *v;
-  const Resource *r;
-  char tmps[32];
-  char accel[64];
-  int accel_n;
-  char *menulabel = NULL;
-  char ch[2];
-  char m = '\0';
-  char *cname = NULL;
-
-  ch[1] = '\0';
-
-  for (i = 0; i < node->c; i++)
-    switch (resource_type (node->v[i]))
-      {
-      case 101:		/* named subnode */
-	add_resource_to_menu (node->v[i].name, node->v[i].subres, 
-			      indent + INDENT_INC);
-	break;
-
-      case 1:			/* unnamed subres */
-	accel[0] = '\0';
-	/* remaining number of chars available in accel (- 1 for '\0')*/
-	accel_n = sizeof (accel) - 1;
-	/* This is a menu choice.  The first value in the unnamed
-	 * subres is what the menu choice gets called.
-	 *
-	 * This may be a top level menu on the menubar,
-	 * a menu choice under, say the File menu, or
-	 * a menu choice under a submenu of a menu choice.
-	 *
-	 * We need to pick off an "m" named resource which is
-	 * the menu accelerator key and an "a" named subresource
-	 * which contains the information for the hotkey.
-	 */
-	if ((v = resource_value (node->v[i].subres, "m")))
-	  {
-#ifdef DEBUG_MENUS
-	    printf ("    found resource value m=\"%s\"\n", v);
-#endif
-	    m = *v;
-	  }
-	if ((r = resource_subres (node->v[i].subres, "a")))
-	  {
-	    /* for the accelerator, it has 2 values  like
-	     *
-	     * a={"Ctrl-Q" "Ctrl<Key>q"}
-	     * The first one is what's displayed in the menu and the
-	     * second actually defines the hotkey.  Actually, the
-	     * first value is only used by the lesstif HID and is
-	     * ignored by the gtk HID.  The second value is used by both.
-	     *
-	     * We have to translate some strings.  See
-	     * gtk+-2.10.9/gdk/keynames.txt from the gtk distribution
-	     * as well as the output from xev(1).
-	     *
-	     * Modifiers:
-	     *
-	     * "Ctrl" -> "<control>"
-	     * "Shift" -> "<shift>"
-	     * "Alt" -> "<alt>"
-	     * "<Key>" -> ""
-	     *
-	     * keys:
-	     *
-	     * " " -> ""
-	     * "Enter" -> "Return"
-	     *
-	     */
-	    const char *p;
-	    int j;
-	    enum {KEY, MOD} state;
-
-	    state = MOD;
-#ifdef DEBUG_MENUS
-	    printf ("    accelerator a=%p.  r->v[0].value = \"%s\", r->v[1].value = \"%s\" ", 
-		    r, r->v[0].value, r->v[1].value);
-#endif
-	    p = r->v[1].value;
-	    while (*p != '\0')
-	      {
-		switch (state)
-		  {
-		  case MOD:
-		    if (*p == ' ')
-		      {
-			p++;
-		      }
-		    else if (strncmp (p, "<Key>", 5) == 0)
-		      {
-			state = KEY;
-			p += 5;
-		      }
-		    else if (strncmp (p, "Ctrl", 4) == 0)
-		      {
-			strncat (accel, "<control>", accel_n);
-			accel_n -= strlen ("<control>");
-			p += 4;
-		      }
-		    else if (strncmp (p, "Shift", 5) == 0)
-		      {
-			strncat (accel, "<shift>", accel_n);
-			accel_n -= strlen ("<shift>");
-			p += 5;
-		      }
-		    else if (strncmp (p, "Alt", 3) == 0)
-		      {
-			strncat (accel, "<alt>", accel_n);
-			accel_n -= strlen ("<alt>");
-			p += 3;
-		      }
-		    else
-		      {
-			static int gave_msg = 0;
-			Message (_("Don't know how to parse \"%s\" as an accelerator in the menu resource file.\n"),
-				 p);
-			
-			if (! gave_msg) 
-			  {
-			    gave_msg = 1;
-			    Message (_("Format is:\n"
-				     "modifiers<Key>k\n"
-				     "where \"modifiers\" is a space separated list of key modifiers\n"
-				     "and \"k\" is the name of the key.\n"
-				     "Allowed modifiers are:\n"
-				     "   Ctrl\n"
-				     "   Shift\n"
-				     "   Alt\n"
-				     "Please note that case is important.\n"));
-			  }
-			/* skip processing the rest */
-			accel[0] = '\0';
-			accel_n = sizeof (accel) - 1;
-			p += strlen (p);
-		      }
-		    break;
-
-		  case KEY:
-		    if (strncmp (p, "Enter", 5) == 0)
-		      {
-			strncat (accel, "Return", accel_n);
-			accel_n -= strlen ("Return");
-			p += 5;
-		      }
-		    else
-		      {
-			ch[0] = *p;
-			for (j = 0; j < n_key_table; j++)
-			  {
-			    if ( *p == key_table[j].in)
-			      {
-				strncat (accel, key_table[j].out, accel_n);
-				accel_n -= strlen (key_table[j].out);
-				j = n_key_table;
-			      }
-			  }
-			
-			if (j == n_key_table)
-			  {
-			    strncat (accel, ch, accel_n);
-			    accel_n -= strlen (ch);
-			  }
-		    
-			p++;
-		      }
-		    break;
-
-		  }
-
-		if (G_UNLIKELY (accel_n < 0))
-		  {
-		    accel_n = 0;
-		    Message ("Accelerator \"%s\" is too long to be parsed.\n", r->v[1].value);
-		    accel[0] = '\0';
-		    accel_n = 0;
-		    /* skip processing the rest */
-		    p += strlen (p);
-		  }
-	      }
-#ifdef DEBUG_MENUS
-	    printf ("\n    translated = \"%s\"\n", accel);
-#endif
-	  }
-	v = "button";
-
-	/* Now look for the first unnamed value (not a subresource) to
-	 * figure out the name of the menu or the menuitem.
-	 *
-	 * After this loop, v will be the name of the menu or menuitem.
-	 *
-	 */
-	for (j = 0; j < node->v[i].subres->c; j++)
-	  if (resource_type (node->v[i].subres->v[j]) == 10)
-	    {
-	      v = node->v[i].subres->v[j].value;
-	      break;
-	    }
-	
-	if (m == '\0')
-	  menulabel = strdup (v);
-	else
-	  {
-	    /* we've been given a mneumonic so we need to insert an
-	     * "_" into the label.  For example if the string is
-	     * "Quit Program" and we have m=Q, we'd need to produce
-	     * "_Quit Program".
-	     */
-	    char *s1, *s2;
-	    size_t l;
-
-	    l = strlen (_(v)) + 2;
-#ifdef DEBUG_MENUS
-	    printf ("allocate %ld bytes\n", l);
-#endif
-	    if ( (menulabel = (char *) malloc (l)) == NULL)
-	      {
-		fprintf (stderr, "add_resource_to_menu():  malloc failed\n");
-		exit (1);
-	      }
-	    
-	    s1 = menulabel;
-	    s2 = _(v);
-	    while (*s2 != '\0')
-	      {
-		if (*s2 == m)
-		  {
-		    /* add the underscore and quit looking for more 
-		     * matches since we only want to add 1 underscore
-		     */
-		    *s1 = '_';
-		    s1++;
-		    m = '\0';
-		  }
-		*s1 = *s2;
-		s1++;
-		s2++;
-	      }
-	    *s1 = '\0';
-	  }
-#ifdef DEBUG_MENUS
-	printf ("v = \"%s\", label = \"%s\"\n", v, menulabel);
-#endif
-	/* if the subresource we're processing also has unnamed
-	 * subresources then this is either a menu (that goes on the
-	 * menu bar) or it is a submenu.  It isn't a menuitem.
-	 */
-	if (node->v[i].subres->flags & FLAG_S)
-	  {
-	    /* This is a menu */
-
-	    /* add menus to the same entries list as the "normal"
-	     * menuitems.  We'll just use NULL for what happens so the
-	     * callback doesn't have anything to do.
-	     */
-
-	    sprintf (tmps, "%s%d", MENUITEM, menuitem_cnt);
-	    cname = strdup (tmps);
-
-	    /* add to the action entries */
-	    /* name, stock_id, label, accelerator, tooltip */
-	    ghid_append_action (tmps, NULL, menulabel, accel, NULL);
-
-	    /* and add to the user interfact XML description */
-	    ghid_ui_info_indent (indent);
-	    ghid_ui_info_append ("<menu action='");
-	    ghid_ui_info_append (tmps);
-	    ghid_ui_info_append ("'>\n");
-
-
-	    /* recursively add more submenus or menuitems to this
-	     * menu/submenu
-	     */
-	    add_resource_to_menu ("sub menu", node->v[i].subres, 
-				  indent + INDENT_INC);
-	    ghid_ui_info_indent (indent);
-
-	    /* and close this menu */
-	    ghid_ui_info_append ("</menu>\n");
-	  }
-	else
-	  {
-	    /* We are in a specific menu choice and need to figure out
-	     * if it is a "normal" one 
-	     * or if there is some condtion under which it is checked
-	     * or if it has sensitive=false which is simply a label 
-	     */
-	    
-	    char *checked = resource_value (node->v[i].subres, "checked");
-	    char *label = resource_value (node->v[i].subres, "sensitive");
-	    char *tip = resource_value (node->v[i].subres, "tip");
-	    if (checked)
-	      {
-		/* We have the "checked=" named value for this
-		 * menuitem.  Now see if it is
-		 *   checked=foo
-		 * or
-		 *   checked=foo,bar
-		 *
-		 * where the former is just a binary flag and the
-		 * latter is checking a flag against a value
-		 */
-#ifdef DEBUG_MENUS
-		printf ("Found a \"checked\" menu choice \"%s\", \"%s\"\n", v, checked);
-#endif
-		if (strchr (checked, ','))
-		  {
-		    /* we're comparing a flag against a value */
-#ifdef DEBUG_MENUS
-		    printf ("Found checked comparing a flag to a value\n");
-#endif
-		  }
-		else
-		  {
-		    /* we're looking at a binary flag */
-		    /* name, stock_id, label, accelerator, tooltip, callback, is_active
-		    printf ("Found checked using a flag as a binary\n");
-
-		     */
-		  }
-
-		sprintf (tmps, "%s%d", TMENUITEM, tmenuitem_cnt);
-		cname = strdup (tmps);
-
-		/* add to the action entries */
-		/* name, stock_id, label, accelerator, tooltip, is_active */
-		ghid_append_toggle_action (tmps, NULL, menulabel, accel, tip, 1);
-
-		ghid_ui_info_indent (indent);
-		ghid_ui_info_append ("<menuitem action='");
-		ghid_ui_info_append (tmps);
-		ghid_ui_info_append ("'/>\n");
-
-		toggle_action_resources[tmenuitem_cnt-1] = node->v[i].subres;
-		
-	      }
-	    else if (label && strcmp (label, "false") == 0)
-	      {
-		/* we have sensitive=false so just put a label in the
-		 * GUI  -- FIXME -- actually do something here....
-		 */
-	      }
-	    else
-	      {
-		/*
-		 * Here we are finally at the rest of an actual
-		 * menuitem.  So, we need to get the subresource
-		 * that has all the actions in it (actually, it will
-		 * be the entire subresource that defines the
-		 * menuitem, the callbacks later will pick out the
-		 * actions part.
-		 *
-		 * We add this resource to an array of action
-		 * resources that is used by the main menu callback to
-		 * figure out what really needs to be done.
-		 */
-
-		sprintf (tmps, "%s%d", MENUITEM, menuitem_cnt);
-		cname = strdup (tmps);
-
-		/* add to the action entries */
-		/* name, stock_id, label, accelerator, tooltip */
-		ghid_append_action (tmps, NULL, menulabel, accel, tip);
-
-		ghid_ui_info_indent (indent);
-		ghid_ui_info_append ("<menuitem action='");
-		ghid_ui_info_append (tmps);
-		ghid_ui_info_append ("'/>\n");
-
-
-		action_resources[menuitem_cnt-1] = node->v[i].subres;
-
-#ifdef DEBUG_MENUS
-		/* Print out the actions to help with debugging */
-		{
-		  int vi;
-		  Resource *mynode  = node->v[i].subres;
-		 
-		  /* Start at the 2nd sub resource because the first
-		   * is the text that shows up in the menu.
-		   * 
-		   * We're looking for the unnamed values since those
-		   * are the ones which are actions.
-		   */
-		  for (vi = 1; vi < mynode->c; vi++)
-		    if (resource_type (mynode->v[vi]) == 10)
-		      printf("   action value=\"%s\"\n", mynode->v[vi].value);
-		}
-#endif
-
-		
-	      }
-	    
-
-	    /* now keep looking over our menuitem to see if there is
-	     * any more work.
-	     */
-	    for (j = 0; j < node->v[i].subres->c; j++)
-	      switch (resource_type (node->v[i].subres->v[j]))
-		{
-		case 110:	/* named value = X resource */
-		  {
-		    const char *n = node->v[i].subres->v[j].name;
-		    /* allow fg and bg to be abbreviations for
-		     * foreground and background
-		     */
-		    if (strcmp (n, "fg") == 0)
-		      n = "foreground";
-		    if (strcmp (n, "bg") == 0)
-		      n = "background";
-
-		    /* ignore special named values (m, a, sensitive) */
-		    if (strcmp (n, "m") == 0
-			|| strcmp (n, "a") == 0
-			|| strcmp (n, "sensitive") == 0
-			|| strcmp (n, "tip") == 0
-			)
-		      break;
-
-		    /* log checked and active special values */
-		    if (strcmp (n, "checked") == 0)
-		      {
-#ifdef DEBUG_MENUS
-			printf ("%s is checked\n", node->v[i].subres->v[j].value);
-#endif
-			note_toggle_flag (new_toggle_entries[tmenuitem_cnt-1].name,
-					  GHID_FLAG_CHECKED,
-					  node->v[i].subres->v[j].value);
-			break;
-		      }
-		    if (strcmp (n, "active") == 0)
-		      {
-			if (cname != NULL) 
-			  {
-			    note_toggle_flag (cname,
-					      GHID_FLAG_ACTIVE,
-					      node->v[i].subres->v[j].value);
-			  }
-			else
-			  {
-			    printf ("WARNING: %s cname == NULL\n", __FUNCTION__);
-			  }
-			break;
-		      }
-
-		    /* if we got this far it is supposed to be an X
-		     * resource.  For now ignore it and warn the user
-		     */
-		    Message (_("The gtk gui currently ignores \"%s\""
-				"as part of a menuitem resource.\n"
-				"Feel free to provide patches\n"),
-			     node->v[i].subres->v[j].value);
-		  }
-		  break;
-		}
-
-	  }
-	break;
-
-      case 10:			/* unnamed value */
-	/* in the resource file we may have something like:
-	 *
-	 * {File
-	 *   {Open OpenAction()}
-	 *   {Close CloseAction()}
-	 *   -
-	 *   {"Some Choice" MyAction()}
-	 *   {"Some Other Choice" MyOtherAction()}
-	 *   @foo
-	 *   {Quit QuitAction()}
-	 *  }
-	 *
-	 * If we get here in the code it is becuase we found the "-"
-	 * or the "@foo".  
-	 * 
-	 */
-#ifdef DEBUG_MENUS
-	printf ("resource_type for node #%d is 10 (unnamed value).  value=\"%s\"\n", 
-		i, node->v[i].value);
-#endif
-
-	if (node->v[i].value[0] == '@')
-	  {
-	    if (strcmp (node->v[i].value, "@layerview") == 0)
-	      {
-                gchar *tmp = ghid_layer_selector_get_view_xml
-                  (GHID_LAYER_SELECTOR (ghidgui->layer_selector));
-                ghid_ui_info_append (tmp);
-                g_free (tmp);
-	      }
-	    else if (strcmp (node->v[i].value, "@layerpick") == 0)
-	      {
-                gchar *tmp = ghid_layer_selector_get_pick_xml
-                  (GHID_LAYER_SELECTOR (ghidgui->layer_selector));
-                ghid_ui_info_append (tmp);
-                g_free (tmp);
-	      }
-	    else if (strcmp (node->v[i].value, "@routestyles") == 0)
-	      {
-		int i;
-		char tmpid[40];
-		for (i = 0 ; i <  N_ROUTE_STYLES; i++)
-		  {
-		    sprintf (tmpid, "<menuitem action='%s%d' />\n", 
-			     ROUTESTYLE, i);
-		    ghid_ui_info_indent (indent);
-		    ghid_ui_info_append (tmpid);
-		  }
-	      }
-	    else
-	      {
-		Message (_("GTK GUI currently ignores \"%s\" in the menu\n"
-			"resource file.\n"), node->v[i].value);
-	      }
-	    
-	  }
-	
-	else if (strcmp (node->v[i].value, "-") == 0)
-	  {
-	    ghid_ui_info_indent (indent);
-	    ghid_ui_info_append ("<separator/>\n");
-	  }
-	else if (i > 0)
-	  {
-	    /* This is where you get with an action-less menuitem.
-	     * It is really just useful when you're starting to build
-	     * a new menu and you're looking to get the layout
-	     * right.
-	     */
-	    sprintf (tmps, "%s%d", MENUITEM, menuitem_cnt);
-	    cname = strdup (tmps);
-	    
-	    /* add to the action entries 
-	     * name, stock_id, label, accelerator, tooltip 
-	     * Note that we didn't get the mneumonic added in here,
-	     * but since this is really for a dummy menu (no
-	     * associated actions), I'm not concerned.
-	     */
-	    
-	    ghid_append_action (tmps, NULL, node->v[i].value, accel, NULL);
-
-	    ghid_ui_info_indent (indent);
-	    ghid_ui_info_append ("<menuitem action='");
-	    ghid_ui_info_append (tmps);
-	    ghid_ui_info_append ("'/>\n");
-	    
-	    action_resources[menuitem_cnt-1] = NULL;
-
-	  }
-	break;
-      }
-  
-  if (cname != NULL)
-    free (cname);
-
-  if (menulabel != NULL)
-    free (menulabel);
-}
-
-
-static void
-ghid_ui_info_indent (int indent)
-{
-  int i;
-
-  for (i = 0; i < indent ; i++)
-    {
-      ghid_ui_info_append (" ");
-    }
-}
-
-/* 
- *appends a string to the ui_info string 
- * This function is used 
- */
-
-static void
-ghid_ui_info_append (const gchar * newone)
+/*! \brief Finds the gpcb-menu.res file */
+const char *
+get_menu_filename (void)
 {
-  gchar *p;
+  const char *rv = NULL;
+  char *home_pcbmenu = NULL;
 
-  if (new_ui_info_sz == 0) 
+  /* homedir is set by the core */
+  if (homedir)
     {
-      new_ui_info_sz = 1024;
-      new_ui_info = (gchar *)leaky_calloc (new_ui_info_sz, sizeof (gchar));
+      Message (_("Note:  home directory is \"%s\"\n"), homedir);
+      home_pcbmenu = Concat (homedir, PCB_DIR_SEPARATOR_S, ".pcb",
+                             PCB_DIR_SEPARATOR_S, "gpcb-menu.res", NULL);
     }
+  else
+    Message (_("Warning:  could not determine home directory\n"));
 
-  while (strlen (new_ui_info) + strlen (newone) + 1 > new_ui_info_sz)
-    {
-      size_t n;
-      gchar * np;
-
-      n = new_ui_info_sz + 1024;
-      if ((np = (gchar *)leaky_realloc (new_ui_info, n)) == NULL)
-	{
-	  fprintf (stderr, "ghid_ui_info_append():  realloc of size %ld failed\n",
-		   (long int) n);
-	  exit (1);
-	}
-      new_ui_info = np;
-      new_ui_info_sz = n;
-    }
+  if (access ("gpcb-menu.res", R_OK) == 0)
+    rv = "gpcb-menu.res";
+  else if (home_pcbmenu != NULL && (access (home_pcbmenu, R_OK) == 0) )
+    rv = home_pcbmenu;
+  else if (access (pcbmenu_path, R_OK) == 0)
+    rv = pcbmenu_path;
 
-  p = new_ui_info + strlen (new_ui_info) ;
-  while (*newone != '\0')
-    {
-      *p = *newone;
-      p++;
-      newone++;
-    }
-  
-  *p = '\0';
+  free (home_pcbmenu);
+  return rv;
 }
 
-
-static void
+static GtkWidget *
 ghid_load_menus (void)
 {
   const char *filename;
   const Resource *r = 0, *bir;
-  char *home_pcbmenu;
   const Resource *mr;
+  GtkWidget *menu_bar = NULL;
   int i;
 
-  for (i = 0; i < sizeof (ghid_hotkey_actions) / sizeof (char *) ; i++)
+  for (i = 0; i < N_HOTKEY_ACTIONS; i++)
     {
-      ghid_hotkey_actions[i] = NULL;
+      ghid_hotkey_actions[i].action = NULL;
+      ghid_hotkey_actions[i].node = NULL;
     }
  
-  /* homedir is set by the core */
-  home_pcbmenu = NULL;
-  if (homedir == NULL)
-    {
-      Message (_("Warning:  could not determine home directory\n"));
-    }
-  else
-    {
-      Message (_("Note:  home directory is \"%s\"\n"), homedir);
-      home_pcbmenu = Concat (homedir, PCB_DIR_SEPARATOR_S, ".pcb",
-                  PCB_DIR_SEPARATOR_S, "gpcb-menu.res", NULL);
-    }
-
-  if (access ("gpcb-menu.res", R_OK) == 0)
-    filename = "gpcb-menu.res";
-  else if (home_pcbmenu != NULL && (access (home_pcbmenu, R_OK) == 0) )
-    filename = home_pcbmenu;
-  else if (access (pcbmenu_path, R_OK) == 0)
-    filename = pcbmenu_path;
-  else
-    filename = 0;
-
   bir = resource_parse (0, gpcb_menu_default);
   if (!bir)
     {
@@ -3087,17 +2114,13 @@ ghid_load_menus (void)
       exit(1);
     }
 
+  filename = get_menu_filename ();
   if (filename)
     {
       Message ("Loading menus from %s\n", filename);
       r = resource_parse (filename, 0);
     }
 
-  if (home_pcbmenu != NULL) 
-    {
-       free (home_pcbmenu);
-    }
-
   if (!r)
     {
       Message ("Using default menus\n");
@@ -3110,55 +2133,36 @@ ghid_load_menus (void)
     
   if (mr)
     {
-      ghid_ui_info_append ("<ui>\n");
-      ghid_ui_info_indent (INDENT_INC);
-      ghid_ui_info_append ("<menubar name='MenuBar'>\n");
-      add_resource_to_menu ("Initial Call", mr, 2*INDENT_INC);
-      ghid_ui_info_indent (INDENT_INC);
-      ghid_ui_info_append ("</menubar>\n");
+      menu_bar = ghid_main_menu_new (G_CALLBACK (ghid_menu_cb),
+                                     ghid_check_special_key);
+      ghid_main_menu_add_resource (GHID_MAIN_MENU (menu_bar), mr);
     }
 
   mr = resource_subres (r, "PopupMenus");
   if (!mr)
     mr = resource_subres (bir, "PopupMenus");
-   
+
   if (mr)
     {
       int i;
-
       for (i = 0; i < mr->c; i++)
-	{
-	  if (resource_type (mr->v[i]) == 101)
-	    {
-	      /* This is a named resource which defines a popup menu */
-	      ghid_ui_info_indent (INDENT_INC);
-	      ghid_ui_info_append ("<popup name='");
-	      ghid_ui_info_append (mr->v[i].name);
-	      ghid_ui_info_append ("'>\n");
-	      add_resource_to_menu ("Initial Call", mr->v[i].subres, 
-				    2*INDENT_INC);
-	      ghid_ui_info_indent (INDENT_INC);
-	      ghid_ui_info_append ("</popup>\n");
-	    }
-	  else
-	    {
-	    }
-	}
+        if (resource_type (mr->v[i]) == 101)
+          /* This is a named resource which defines a popup menu */
+          ghid_main_menu_add_popup_resource (GHID_MAIN_MENU (menu_bar),
+                                             mr->v[i].name, mr->v[i].subres);
     }
 
-    ghid_ui_info_append ("</ui>\n");
-
 #ifdef DEBUG_MENUS
-      printf ("Finished loading menus.  ui_info = \n");
-      printf ("%s\n", new_ui_info);
+   puts ("Finished loading menus.");
 #endif
 
-  mr = resource_subres (r, "Mouse");
-  if (!mr)
-    mr = resource_subres (bir, "Mouse");
-  if (mr)
-    load_mouse_resource (mr);
+    mr = resource_subres (r, "Mouse");
+    if (!mr)
+      mr = resource_subres (bir, "Mouse");
+    if (mr)
+      load_mouse_resource (mr);
 
+  return menu_bar;
 }
 
 /* ------------------------------------------------------------ */
diff --git a/src/hid/gtk/gui.h b/src/hid/gtk/gui.h
index 30ff593..e7e5f55 100644
--- a/src/hid/gtk/gui.h
+++ b/src/hid/gtk/gui.h
@@ -37,6 +37,7 @@
 
 #include <gtk/gtk.h>
 #include "ghid-coord-entry.h"
+#include "ghid-main-menu.h"
 #include "gui-pinout-preview.h"
 
 
@@ -92,7 +93,6 @@
 
 typedef struct
 {
-  GtkUIManager *ui_manager;
   GtkActionGroup *main_actions,
     *change_selected_actions, *displayed_name_actions;
 
@@ -107,7 +107,7 @@ typedef struct
     *menu_hbox, *compact_vbox, *compact_hbox, *position_hbox, *label_hbox,
     *mode_buttons0_vbox, *mode_buttons1_hbox, *mode_buttons1_vbox,
     *mode_buttons0_frame, *mode_buttons1_frame, *mode_buttons0_frame_vbox;
-  GtkWidget *layer_selector;
+  GtkWidget *menu_bar, *layer_selector;
 
   GtkWidget *h_range, *v_range;
   GtkObject *h_adjustment, *v_adjustment;
@@ -221,8 +221,6 @@ void ghid_interface_set_sensitive (gboolean sensitive);
 void ghid_interface_input_signals_connect (void);
 void ghid_interface_input_signals_disconnect (void);
 
-void ghid_set_menu_toggle_button (GtkActionGroup * ag,
-				  gchar * name, gboolean state);
 void ghid_pcb_saved_toggle_states_set (void);
 void ghid_sync_with_new_layout (void);
 
@@ -272,13 +270,13 @@ void ghid_port_ranges_scale (void);
 gboolean ghid_note_event_location (GdkEventButton * ev);
 gboolean have_crosshair_attachments (void);
 gboolean ghid_port_key_press_cb (GtkWidget * drawing_area,
-				 GdkEventKey * kev, GtkUIManager * ui);
+				 GdkEventKey * kev, gpointer data);
 gboolean ghid_port_key_release_cb (GtkWidget * drawing_area,
-				   GdkEventKey * kev, GtkUIManager * ui);
+				   GdkEventKey * kev, gpointer data);
 gboolean ghid_port_button_press_cb (GtkWidget * drawing_area,
-				    GdkEventButton * ev, GtkUIManager * ui);
+				    GdkEventButton * ev, gpointer data);
 gboolean ghid_port_button_release_cb (GtkWidget * drawing_area,
-				      GdkEventButton * ev, GtkUIManager * ui);
+				      GdkEventButton * ev, gpointer data);
 
 
 gint ghid_port_window_enter_cb (GtkWidget * widget,

commit d721448d923feaad8a7c89410caff698fa52d0fd
Author: Andrew Poelstra <asp11@xxxxxx>
Commit: Andrew Poelstra <asp11@xxxxxx>

    Created ghid-main-menu.[ch]
    
    Still need to hook up layer selector and route styles.

diff --git a/src/Makefile.am b/src/Makefile.am
index 4b66ac7..a9c51b2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -270,12 +270,14 @@ libgtk_a_CPPFLAGS = -I./hid/gtk
 LIBGTK_SRCS = \
 	dolists.h \
 	hid/hidint.h \
+	hid/gtk/ghid-cell-renderer-visibility.c \
+	hid/gtk/ghid-cell-renderer-visibility.h \
 	hid/gtk/ghid-coord-entry.c \
 	hid/gtk/ghid-coord-entry.h \
 	hid/gtk/ghid-layer-selector.c \
 	hid/gtk/ghid-layer-selector.h \
-	hid/gtk/ghid-cell-renderer-visibility.c \
-	hid/gtk/ghid-cell-renderer-visibility.h \
+	hid/gtk/ghid-main-menu.c \
+	hid/gtk/ghid-main-menu.h \
 	hid/gtk/gtkhid-main.c \
 	hid/gtk/gtkhid.h \
 	hid/gtk/gui.h \
diff --git a/src/hid/gtk/ghid-main-menu.c b/src/hid/gtk/ghid-main-menu.c
new file mode 100644
index 0000000..945b522
--- /dev/null
+++ b/src/hid/gtk/ghid-main-menu.c
@@ -0,0 +1,508 @@
+/*! \file <ghid-main-menu.c>
+ *  \brief Implementation of GHidMainMenu widget
+ *  \par Description
+ *  This widget is the main pcb menu.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gtkhid.h"
+#include "gui.h"
+#include "pcb-printf.h"
+
+#include "ghid-main-menu.h"
+
+void Message (const char *, ...);
+
+static int action_counter;
+
+struct _GHidMainMenu
+{
+  GtkMenuBar parent;
+
+  GtkActionGroup *action_group;
+  GtkAccelGroup *accel_group;
+
+  gint layer_view_pos;
+  gint layer_pick_pos;
+  gint route_style_pos;
+
+  GtkMenuShell *layer_view_shell;
+  GtkMenuShell *layer_pick_shell;
+  GtkMenuShell *route_style_shell;
+
+  GList *actions;
+  GHashTable *popup_table;
+
+  GCallback action_cb;
+  void (*special_key_cb) (const char *accel, GtkAction *action,
+                          const Resource *node);
+
+};
+
+struct _GHidMainMenuClass
+{
+  GtkMenuBarClass parent_class;
+};
+
+/* TODO: write finalize function */
+
+/* SIGNAL HANDLERS */
+
+/* RESOURCE HANDLER */
+/* \brief Translate gpcb-menu.res accelerators to gtk ones
+ * \par Function Description
+ * Some keys need to be replaced by a name for the gtk accelerators to
+ * work.  This table contains the translations.  The "in" character is
+ * what would appear in gpcb-menu.res and the "out" string is what we
+ * have to feed to gtk.  I was able to find these by using xev to find
+ * the keycode and then looked at gtk+-2.10.9/gdk/keynames.txt (from the
+ * gtk source distribution) to figure out the names that go with the 
+ * codes.
+ */
+static gchar *
+translate_accelerator (const char *text)
+{
+  GString *ret_val = g_string_new ("");
+  static struct { const char *in, *out; } key_table[] = 
+  {
+    {"Enter", "Return"},
+    {"Alt",   "<alt>"},
+    {"Shift", "<shift>"},
+    {"Ctrl",  "<ctrl>"},
+    {" ", ""},
+    {":", "colon"},
+    {"=", "equal"},
+    {"/", "slash"},
+    {"[", "bracketleft"},
+    {"]", "bracketright"},
+    {".", "period"},
+    {"|", "bar"},
+    {NULL, NULL}
+  };
+
+  enum {MOD, KEY} state = MOD;
+  while (*text != '\0')
+    {
+      static gboolean gave_msg;
+      gboolean found = FALSE;
+      int i;
+
+      if (state == MOD && strncmp (text, "<Key>", 5) == 0)
+        {
+          state = KEY;
+          text += 5;
+        }
+      for (i = 0; key_table[i].in != NULL; ++i)
+        {
+          int len = strlen (key_table[i].in);
+          if (strncmp (text, key_table[i].in, len) == 0)
+            {
+              found = TRUE;
+              g_string_append (ret_val, key_table[i].out);
+              text += len;
+            }
+        }
+      if (found == FALSE)
+        switch (state)
+          {
+          case MOD:
+            Message (_("Don't know how to parse \"%s\" as an "
+                       "accelerator in the menu resource file.\n"),
+                     text);
+            if (!gave_msg)
+              {
+                gave_msg = TRUE;
+                Message (_("Format is:\n"
+                           "modifiers<Key>k\n"
+                           "where \"modifiers\" is a space "
+                           "separated list of key modifiers\n"
+                           "and \"k\" is the name of the key.\n"
+                           "Allowed modifiers are:\n"
+                           "   Ctrl\n"
+                           "   Shift\n"
+                           "   Alt\n"
+                           "Please note that case is important.\n"));
+              }
+            break;
+          case KEY:
+            g_string_append_c (ret_val, *text);
+            ++text;
+            break;
+          }
+    }
+  return g_string_free (ret_val, FALSE);
+}
+
+/*! \brief Check that translated accelerators are unique; warn otherwise. */
+static const char *
+check_unique_accel (const char *accelerator)
+{
+  static GHashTable *accel_table;
+
+  if (!accelerator ||*accelerator)
+    return accelerator;
+
+  if (!accel_table)
+    accel_table = g_hash_table_new (g_str_hash, g_str_equal);
+
+  if (g_hash_table_lookup (accel_table, accelerator))
+    {
+       Message (_("Duplicate accelerator found: \"%s\"\n"
+                  "The second occurance will be dropped\n"),
+                accelerator);
+        return NULL;
+    }
+
+  g_hash_table_insert (accel_table,
+                       (gpointer) accelerator, (gpointer) accelerator);
+
+  return accelerator;
+}
+
+
+/*! \brief Translate a resource tree into a menu structure
+ *
+ *  \param [in] menu    The GHidMainMenu widget to be acted on
+ *  \param [in] shall   The base menu shell (a menu bar or popup menu)
+ *  \param [in] res     The base of the resource tree
+ * */
+void
+ghid_main_menu_real_add_resource (GHidMainMenu *menu, GtkMenuShell *shell,
+                                  const Resource *res)
+{
+  int i, j;
+  const Resource *tmp_res;
+  gchar mnemonic = 0;
+
+  for (i = 0; i < res->c; ++i)
+    {
+      const gchar *accel = NULL;
+      char *menu_label;
+      const char *res_val;
+      const Resource *sub_res = res->v[i].subres;
+      GtkAction *action = NULL;
+
+      switch (resource_type (res->v[i]))
+        {
+        case 101:   /* name, subres: passthrough */
+          ghid_main_menu_real_add_resource (menu, shell, sub_res);
+          break;
+        case   1:   /* no name, subres */
+          tmp_res = resource_subres (sub_res, "a");  /* accelerator */
+          res_val = resource_value (sub_res, "m");   /* mnemonic */
+          if (res_val)
+            mnemonic = res_val[0];
+          /* The accelerator resource will have two values, like 
+           *   a={"Ctrl-Q" "Ctrl<Key>q"}
+           * The first Gtk ignores. The second needs to be translated. */
+          if (tmp_res)
+            accel = check_unique_accel
+                      (translate_accelerator (tmp_res->v[1].value));
+
+          /* Now look for the first unnamed value (not a subresource) to
+           * figure out the name of the menu or the menuitem. */
+          res_val = "button";
+          for (j = 0; j < sub_res->c; ++j)
+            if (resource_type (sub_res->v[j]) == 10)
+              {
+                res_val = sub_res->v[j].value;
+                break;
+              }
+          /* Hack '_' in based on mnemonic value */
+          if (!mnemonic)
+            menu_label = g_strdup (res_val);
+          else
+            {
+              char *post_ = strchr (res_val, mnemonic);
+              if (post_ == NULL)
+                menu_label = g_strdup (res_val);
+              else
+                {
+                  GString *tmp = g_string_new ("");
+                  g_string_append_len (tmp, res_val, post_ - res_val);
+                  g_string_append_c (tmp, '_');
+                  g_string_append (tmp, post_);
+                  menu_label = g_string_free (tmp, FALSE);
+                }
+            }
+          /* If the subresource we're processing also has unnamed
+           * subresources, it's a submenu, not a regular menuitem. */
+          if (sub_res->flags & FLAG_S)
+            {
+              /* SUBMENU */
+              GtkWidget *submenu = gtk_menu_new ();
+              GtkWidget *item = gtk_menu_item_new_with_mnemonic (menu_label);
+              GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
+
+              gtk_menu_shell_append (shell, item);
+              gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
+
+              /* add tearoff to menu */
+              gtk_menu_shell_append (GTK_MENU_SHELL (submenu), tearoff);
+              /* recurse on the newly-added submenu */
+              ghid_main_menu_real_add_resource (menu,
+                                                GTK_MENU_SHELL (submenu),
+                                                sub_res);
+            }
+          else
+            {
+              /* NON-SUBMENU: MENU ITEM */
+              const char *checked = resource_value (sub_res, "checked");
+              const char *label = resource_value (sub_res, "sensitive");
+              const char *tip = resource_value (sub_res, "tip");
+              if (checked)
+                {
+                  /* TOGGLE ITEM */
+                  gchar *name = g_strdup_printf ("MainMenuAction%d",
+                                                 action_counter++);
+
+                  action = GTK_ACTION (gtk_toggle_action_new (name, menu_label,
+                                                              tip, NULL));
+                  /* checked=foo       is a binary flag (checkbox)
+                   * checked=foo,bar   is a flag compared to a value (radio) */
+                  gtk_toggle_action_set_draw_as_radio
+                    (GTK_TOGGLE_ACTION (action), !!strchr (checked, ','));
+                }
+              else if (label && strcmp (label, "false") == 0)
+                {
+                  /* INSENSITIVE ITEM */
+                  GtkWidget *item = gtk_menu_item_new_with_label (menu_label);
+                  gtk_widget_set_sensitive (item, FALSE);
+                  gtk_menu_shell_append (shell, item);
+                }
+              else
+                {
+                  /* NORMAL ITEM */
+                  gchar *name = g_strdup_printf ("MainMenuAction%d", action_counter++);
+                  action = gtk_action_new (name, menu_label, tip, NULL);
+                }
+            }
+          /* Connect accelerator, if there is one */
+          if (action)
+            {
+              GtkWidget *item;
+              gtk_action_set_accel_group (action, menu->accel_group);
+              gtk_action_group_add_action_with_accel (menu->action_group,
+                                                      action, accel);
+              gtk_action_connect_accelerator (action);
+              g_signal_connect (G_OBJECT (action), "activate", menu->action_cb,
+                                (gpointer) sub_res);
+              item = gtk_action_create_menu_item (action);
+              gtk_menu_shell_append (shell, item);
+              menu->actions = g_list_append (menu->actions, action);
+              menu->special_key_cb (accel, action, sub_res);
+            }
+          /* Scan rest of resource in case there is more work */
+          for (j = 0; j < sub_res->c; j++)
+            {
+              const char *res_name;
+              /* named value = X resource */
+              if (resource_type (sub_res->v[j]) == 110)
+                {
+                  res_name = sub_res->v[j].name;
+
+                  /* translate bg, fg to background, foreground */
+                  if (strcmp (res_name, "fg") == 0)   res_name = "foreground";
+                  if (strcmp (res_name, "bg") == 0)   res_name = "background";
+
+                  /* ignore special named values (m, a, sensitive) */
+                  if (strcmp (res_name, "m") == 0
+                      || strcmp (res_name, "a") == 0
+                      || strcmp (res_name, "sensitive") == 0
+                      || strcmp (res_name, "tip") == 0)
+                    break;
+
+                  /* log checked and active special values */
+                  if (action && strcmp (res_name, "checked") == 0)
+                    g_object_set_data (G_OBJECT (action), "checked-flag",
+                                       sub_res->v[j].value);
+                  else if (action && strcmp (res_name, "active") == 0)
+                    g_object_set_data (G_OBJECT (action), "active-flag",
+                                       sub_res->v[j].value);
+                  else
+                    /* if we got this far it is supposed to be an X
+                     * resource.  For now ignore it and warn the user */
+                    Message (_("The gtk gui currently ignores \"%s\""
+                               "as part of a menuitem resource.\n"
+                               "Feel free to provide patches\n"),
+                             sub_res->v[j].value);
+                }
+            }
+          break;
+        case  10:   /* no name, value */
+          /* If we get here, the resource is "-" or "@foo" for some foo */
+          if (res->v[i].value[0] == '@')
+            {
+              if (strcmp (res->v[i].value, "@layerview") == 0)
+                {
+                  menu->layer_view_shell = shell;
+                  menu->layer_view_pos = g_list_length (shell->children);
+                }
+              else if (strcmp (res->v[i].value, "@layerpick") == 0)
+                {
+                  menu->layer_pick_shell = shell;
+                  menu->layer_pick_pos = g_list_length (shell->children);
+                }
+              else if (strcmp (res->v[i].value, "@routestyles") == 0)
+                {
+                  menu->route_style_shell = shell;
+                  menu->route_style_pos = g_list_length (shell->children);
+                }
+              else
+                Message (_("GTK GUI currently ignores \"%s\" in the menu\n"
+                           "resource file.\n"), res->v[i].value);
+            }
+          else if (strcmp (res->v[i].value, "-") == 0)
+            {
+              GtkWidget *item = gtk_separator_menu_item_new ();
+              gtk_menu_shell_append (shell, item);
+            }
+          else if (i > 0)
+            {
+              /* This is an action-less menuitem. It is really only useful
+               * when you're starting to build a new menu and you're looking
+               * to get the layout right. */
+              GtkWidget *item
+                = gtk_menu_item_new_with_label (res->v[i].value);
+              gtk_menu_shell_append (shell, item);
+            }
+          break;
+      }
+  }
+}
+
+/* CONSTRUCTOR */
+static void
+ghid_main_menu_init (GHidMainMenu *mm)
+{
+  /* Hookup signal handlers */
+}
+
+static void
+ghid_main_menu_class_init (GHidMainMenuClass *klass)
+{
+}
+
+/* PUBLIC FUNCTIONS */
+GType
+ghid_main_menu_get_type (void)
+{
+  static GType mm_type = 0;
+
+  if (!mm_type)
+    {
+      const GTypeInfo mm_info =
+        {
+          sizeof (GHidMainMenuClass),
+          NULL, /* base_init */
+          NULL, /* base_finalize */
+          (GClassInitFunc) ghid_main_menu_class_init,
+          NULL, /* class_finalize */
+          NULL, /* class_data */
+          sizeof (GHidMainMenu),
+          0,    /* n_preallocs */
+          (GInstanceInitFunc) ghid_main_menu_init,
+        };
+
+      mm_type = g_type_register_static (GTK_TYPE_MENU_BAR,
+                                        "GHidMainMenu",
+                                        &mm_info, 0);
+    }
+
+  return mm_type;
+}
+
+/*! \brief Create a new GHidMainMenu
+ *
+ *  \return a freshly-allocated GHidMainMenu
+ */
+GtkWidget *
+ghid_main_menu_new (GCallback action_cb,
+                    void (*special_key_cb) (const char *accel,
+                                            GtkAction *action,
+                                            const Resource *node))
+{
+  GHidMainMenu *mm = g_object_new (GHID_MAIN_MENU_TYPE, NULL);
+
+  mm->accel_group = gtk_accel_group_new ();
+  mm->action_group = gtk_action_group_new ("MainMenu");
+
+  mm->layer_view_pos = 0;
+  mm->layer_pick_pos = 0;
+  mm->route_style_pos = 0;
+  mm->layer_view_shell = NULL;
+  mm->layer_pick_shell = NULL;
+  mm->route_style_shell = NULL;
+
+  mm->special_key_cb = special_key_cb;
+  mm->action_cb = action_cb;
+  mm->actions = NULL;
+  mm->popup_table = g_hash_table_new (g_str_hash, g_str_equal);
+
+  return GTK_WIDGET (mm);
+}
+
+/*! \brief Turn a pcb resource into the main menu */
+void
+ghid_main_menu_add_resource (GHidMainMenu *menu, const Resource *res)
+{
+  ghid_main_menu_real_add_resource (menu, GTK_MENU_SHELL (menu), res);
+}
+
+/*! \brief Turn a pcb resource into a popup menu */
+void
+ghid_main_menu_add_popup_resource (GHidMainMenu *menu, const char *name,
+                                   const Resource *res)
+{
+  GtkWidget *new_menu = gtk_menu_new ();
+  g_object_ref_sink (new_menu);
+  ghid_main_menu_real_add_resource (menu, GTK_MENU_SHELL (new_menu), res);
+  g_hash_table_insert (menu->popup_table, (gpointer) name, new_menu);
+  gtk_widget_show_all (new_menu);
+}
+
+/*! \brief Returns a registered popup menu by name */
+GtkMenu *
+ghid_main_menu_get_popup (GHidMainMenu *menu, const char *name)
+{
+  return g_hash_table_lookup (menu->popup_table, name);
+}
+
+
+/*! \brief Updates the toggle/active state of all items 
+ *  \par Function Description
+ *  Loops through all actions, passing the action, its toggle
+ *  flag (maybe NULL), and its active flag (maybe NULL), to a
+ *  callback function. It is the responsibility of the function
+ *  to actually change the state of the action.
+ *
+ *  \param [in] menu    The menu to be acted on.
+ *  \param [in] cb      The callback that toggles the actions
+ */
+void
+ghid_main_menu_update_toggle_state (GHidMainMenu *menu,
+                                    void (*cb) (GtkAction *,
+                                                const char *toggle_flag,
+                                                const char *active_flag))
+{
+  GList *list;
+  for (list = menu->actions; list; list = list->next)
+    {
+      const char *tf = g_object_get_data (G_OBJECT (list->data),
+                                          "checked-flag");
+      const char *af = g_object_get_data (G_OBJECT (list->data),
+                                          "active-flag");
+      cb (GTK_ACTION (list->data), tf, af);
+    }
+}
+
+/*! \brief Returns the menu bar's accelerator group */
+GtkAccelGroup *
+ghid_main_menu_get_accel_group (GHidMainMenu *menu)
+{
+  return menu->accel_group;
+}
+
diff --git a/src/hid/gtk/ghid-main-menu.h b/src/hid/gtk/ghid-main-menu.h
new file mode 100644
index 0000000..3b8c728
--- /dev/null
+++ b/src/hid/gtk/ghid-main-menu.h
@@ -0,0 +1,38 @@
+#ifndef GHID_MAIN_MENU_H__
+#define GHID_MAIN_MENU_H__
+
+#include "resource.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS  /* keep c++ happy */
+
+#define GHID_MAIN_MENU_TYPE            (ghid_main_menu_get_type ())
+#define GHID_MAIN_MENU(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHID_MAIN_MENU_TYPE, GHidMainMenu))
+#define GHID_MAIN_MENU_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GHID_MAIN_MENU_TYPE, GHidMainMenuClass))
+#define IS_GHID_MAIN_MENU(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHID_MAIN_MENU_TYPE))
+#define IS_GHID_MAIN_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GHID_MAIN_MENU_TYPE))
+
+typedef struct _GHidMainMenu       GHidMainMenu;
+typedef struct _GHidMainMenuClass  GHidMainMenuClass;
+
+GType ghid_main_menu_get_type (void);
+GtkWidget *ghid_main_menu_new (GCallback action_cb,
+                               void (*special_key_cb) (const char *accel,
+                                                       GtkAction *action,
+                                                       const Resource *node));
+void ghid_main_menu_add_resource (GHidMainMenu *menu, const Resource *res);
+GtkAccelGroup *ghid_main_menu_get_accel_group (GHidMainMenu *menu);
+void ghid_main_menu_update_toggle_state (GHidMainMenu *menu,
+                                         void (*cb) (GtkAction *,
+                                                     const char *toggle_flag,
+                                                     const char *active_flag));
+
+void ghid_main_menu_add_popup_resource (GHidMainMenu *menu, const char *name,
+                                        const Resource *res);
+GtkMenu *ghid_main_menu_get_popup (GHidMainMenu *menu, const char *name);
+
+G_END_DECLS  /* keep c++ happy */
+#endif




_______________________________________________
geda-cvs mailing list
geda-cvs@xxxxxxxxxxxxxx
http://www.seul.org/cgi-bin/mailman/listinfo/geda-cvs