[Xfce4-commits] <garcon:master> Add GarconMenu::reload-required signal. Monitor menu files.

Jannis Pohlmann noreply at xfce.org
Sun Sep 5 21:42:08 CEST 2010


Updating branch refs/heads/master
         to 7b9ad615f0fb2c0945edd08a848afaa96e5e4dd1 (commit)
       from ab3b66cf90382fd8dfe161783d423858e9dd3180 (commit)

commit 7b9ad615f0fb2c0945edd08a848afaa96e5e4dd1
Author: Jannis Pohlmann <jannis at xfce.org>
Date:   Sun Sep 5 16:36:51 2010 +0200

    Add GarconMenu::reload-required signal. Monitor menu files.
    
    Emit a "reload-required" signal iff a menu file with higher priority is
    changed.
    
    Also update the test-display-menu helper.

 garcon/garcon-menu.c        |   84 +++++++++++++++++++--
 tests/gdb-test-display-menu |    2 +-
 tests/test-display-menu.c   |  175 ++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 240 insertions(+), 21 deletions(-)

diff --git a/garcon/garcon-menu.c b/garcon/garcon-menu.c
index 37ec508..f01259c 100644
--- a/garcon/garcon-menu.c
+++ b/garcon/garcon-menu.c
@@ -97,6 +97,15 @@ enum
 
 
 
+/* Signal identifiers */
+enum
+{
+  RELOAD_REQUIRED,
+  LAST_SIGNAL
+};
+
+
+
 static void                 garcon_menu_element_init                    (GarconMenuElementIface  *iface);
 static void                 garcon_menu_clear                           (GarconMenu              *menu);
 static void                 garcon_menu_finalize                        (GObject                 *object);
@@ -184,7 +193,12 @@ struct _GarconMenuPrivate
 
 
 G_DEFINE_TYPE_WITH_CODE (GarconMenu, garcon_menu, G_TYPE_OBJECT,
-    G_IMPLEMENT_INTERFACE (GARCON_TYPE_MENU_ELEMENT, garcon_menu_element_init))
+                         G_IMPLEMENT_INTERFACE (GARCON_TYPE_MENU_ELEMENT, 
+                                                garcon_menu_element_init))
+
+
+
+static guint menu_signals[LAST_SIGNAL];
 
 
 
@@ -228,6 +242,17 @@ garcon_menu_class_init (GarconMenuClass *klass)
                                                         GARCON_TYPE_MENU_DIRECTORY,
                                                         G_PARAM_READWRITE |
                                                         G_PARAM_STATIC_STRINGS));
+
+  menu_signals[RELOAD_REQUIRED] = 
+    g_signal_new ("reload-required",
+                  GARCON_TYPE_MENU,
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
+                  0, 
+                  NULL,
+                  NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
 }
 
 
@@ -1653,7 +1678,7 @@ garcon_menu_start_monitoring (GarconMenu *menu)
 
   g_return_if_fail (GARCON_IS_MENU (menu));
 
-  /* Let only the root menu monitor menu files, merge fileS/directories and app dirs */
+  /* Let only the root menu monitor menu files, merge files/directories and app dirs */
   if (menu->priv->parent == NULL)
     {
       garcon_menu_monitor_menu_files (menu);
@@ -1700,14 +1725,12 @@ garcon_menu_monitor_menu_files (GarconMenu *menu)
   GFile        *file;
   gchar       **paths;
   guint         n;
-  guint         i;
+  gint          i;
 
   g_return_if_fail (GARCON_IS_MENU (menu));
 
   if (menu->priv->uses_custom_path)
     {
-      g_debug ("monitor menu file: %s", g_file_get_path (menu->priv->file));
-
       /* Monitor the root .menu file */
       monitor = g_file_monitor (menu->priv->file, G_FILE_MONITOR_NONE, NULL, NULL);
       if (monitor != NULL)
@@ -1724,12 +1747,10 @@ garcon_menu_monitor_menu_files (GarconMenu *menu)
         {
           paths = garcon_config_build_paths (GARCON_MENU_ROOT_SPECS[n]);
 
-          for (i = 0; paths != NULL && paths[i] != NULL; ++i)
+          for (i = g_strv_length (paths)-1; paths != NULL && i >= 0; --i)
             {
               file = g_file_new_for_path (paths[i]);
               
-              g_debug ("monitor menu file: %s", g_file_get_path (file));
-
               monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL);
               if (monitor != NULL)
                 {
@@ -1755,6 +1776,53 @@ garcon_menu_file_changed (GarconMenu       *menu,
                           GFileMonitorEvent event_type,
                           GFileMonitor     *monitor)
 {
+  gboolean higher_priority = FALSE;
+  gboolean lower_priority = FALSE;
+  GFile   *menu_file;
+  gchar  **paths;
+  guint    n;
+  guint    i;
+
   g_return_if_fail (GARCON_IS_MENU (menu));
   g_return_if_fail (menu->priv->parent == NULL);
+
+  /* Quick check: reloading is needed if the menu file being used has changed */
+  if (g_file_equal (menu->priv->file, file))
+    {
+      g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
+      return;
+    }
+
+  /* Check if the event file has higher priority than the file currently being used */
+  for (n = 0; !lower_priority && !higher_priority && n < G_N_ELEMENTS (GARCON_MENU_ROOT_SPECS); ++n)
+    {
+      /* Get XDG config paths for the root spec (e.g. menus/xfce-applications.menu) */
+      paths = garcon_config_build_paths (GARCON_MENU_ROOT_SPECS[n]);
+
+      for (i = 0; !higher_priority && paths != NULL && paths[i] != NULL; ++i) 
+        {
+          menu_file = g_file_new_for_path (paths[i]);
+
+          if (g_file_equal (menu_file, menu->priv->file))
+            {
+              /* the menu's file comes before the changed file in the load
+               * priority order, so the changed file has a lower priority */
+              lower_priority = TRUE;
+            }
+          else if (g_file_equal (menu_file, file))
+            {
+              /* the changed file comes before the menu's file in the load
+               * priority order, so the changed file has a higher priority */
+              higher_priority = TRUE;
+            }
+
+          g_object_unref (menu_file);
+        }
+
+      g_strfreev (paths);
+    }
+
+  /* If the event file has higher priority, a menu reload is needed */
+  if (!lower_priority && higher_priority)
+    g_signal_emit (menu, menu_signals[RELOAD_REQUIRED], 0);
 }
diff --git a/tests/gdb-test-display-menu b/tests/gdb-test-display-menu
index 545a1aa..585f383 100644
--- a/tests/gdb-test-display-menu
+++ b/tests/gdb-test-display-menu
@@ -1,3 +1,3 @@
-run /usr/local/etc/xdg/menus/xfce-applications.menu
+run # /usr/local/etc/xdg/menus/xfce-applications.menu
 bt
 quit
diff --git a/tests/test-display-menu.c b/tests/test-display-menu.c
index c62dbd8..f805d98 100644
--- a/tests/test-display-menu.c
+++ b/tests/test-display-menu.c
@@ -38,6 +38,7 @@
 
 /* Root menu */
 static GarconMenu *root = NULL;
+static GtkWidget  *gtk_root = NULL;
 
 
 
@@ -145,6 +146,27 @@ create_item_icon (GarconMenuItem *item)
 
 
 static void
+item_changed (GarconMenuItem *item,
+              GtkWidget      *gtk_item)
+{
+  GdkPixbuf *icon;
+  GtkWidget *image;
+
+  /* Try reloading the icon */
+  icon = create_item_icon (item);
+
+  if (icon != NULL)
+    image = gtk_image_new_from_pixbuf (icon);
+  else
+    image = gtk_image_new_from_icon_name ("applications-other", ICON_SIZE);
+
+  gtk_menu_item_set_label (GTK_MENU_ITEM (gtk_item), garcon_menu_element_get_name (GARCON_MENU_ELEMENT (item)));
+  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (gtk_item), image);
+}
+
+
+
+static void
 create_item_widgets (GarconMenuItem *item,
                      GtkWidget      *parent_menu)
 {
@@ -165,6 +187,11 @@ create_item_widgets (GarconMenuItem *item,
   gtk_menu_shell_append (GTK_MENU_SHELL (parent_menu), gtk_item);
   gtk_widget_show (gtk_item);
 
+  g_object_set_data_full (G_OBJECT (gtk_item), "garcon-menu-item", g_object_ref (item), g_object_unref);
+
+  /* React to changes made to the item on disk */
+  g_signal_connect (item, "changed", G_CALLBACK (item_changed), gtk_item);
+
   /* Execute command if item is clicked */
   g_signal_connect (gtk_item, "activate", G_CALLBACK (execute_item_command), item);
 }
@@ -172,7 +199,72 @@ create_item_widgets (GarconMenuItem *item,
 
 
 static void
-create_menu_widgets (GtkWidget *gtk_menu,
+directory_changed (GarconMenu          *menu,
+                   GarconMenuDirectory *old_directory,
+                   GarconMenuDirectory *new_directory,
+                   GtkWidget           *item)
+{
+  const gchar *display_name;
+  const gchar *icon_name;
+  GtkWidget   *image;
+
+  g_debug ("directory changed from %s to %s", 
+           old_directory != NULL ? garcon_menu_directory_get_name (old_directory) : NULL,
+           new_directory != NULL ? garcon_menu_directory_get_name (new_directory) : NULL);
+
+  display_name = garcon_menu_element_get_name (GARCON_MENU_ELEMENT (menu));
+  gtk_menu_item_set_label (GTK_MENU_ITEM (item), display_name);
+  
+  icon_name = garcon_menu_element_get_icon_name (GARCON_MENU_ELEMENT (menu));
+  if (icon_name == NULL)
+    icon_name = "applications-other";
+  image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (item));
+  gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, ICON_SIZE);
+}
+
+
+
+static void
+item_added (GarconMenu     *menu,
+            GarconMenuItem *item,
+            guint           position,
+            GtkWidget      *gtk_menu)
+{
+  /* Add menu item to the menu */
+  create_item_widgets (item, gtk_menu);
+}
+
+
+
+static void
+item_removed (GarconMenu     *menu,
+              GarconMenuItem *item,
+              GtkWidget      *gtk_menu)
+{
+  GarconMenuItem *corresponding_item;
+  GList          *children;
+  GList          *lp;
+
+  children = gtk_container_get_children (GTK_CONTAINER (gtk_menu));
+
+  for (lp = children; lp != NULL; lp = lp->next)
+    {
+      corresponding_item = g_object_get_data (G_OBJECT (lp->data), "garcon-menu-item");
+      if (corresponding_item != NULL)
+        {
+          if (garcon_menu_element_equal (GARCON_MENU_ELEMENT (item),
+                                         GARCON_MENU_ELEMENT (corresponding_item)))
+            {
+              gtk_container_remove (GTK_CONTAINER (gtk_menu), lp->data);
+            }
+        }
+    }
+}
+
+
+
+static void
+create_menu_widgets (GtkWidget   *gtk_menu,
                      GarconMenu  *menu)
 {
   GarconMenuDirectory *directory;
@@ -239,12 +331,33 @@ create_menu_widgets (GtkWidget *gtk_menu,
           gtk_submenu = gtk_menu_new ();
           gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_item), gtk_submenu);
 
+#if 0
+          /* Update the menu item when the menu directory changes */
+          g_signal_connect (submenu, "directory-changed", G_CALLBACK (directory_changed), gtk_item);
+#endif
+
+          /* Remvoe the menu item and submenu if there are no menu items left in it */
+          /* g_signal_connect (submenu, "destroy", G_CALLBACK (menu_destroyed), gtk_item); */
+
+#if 0
+          /* Remove menu items if they are removed on disk */
+          g_signal_connect (submenu, "item-added", G_CALLBACK (item_added), gtk_submenu);
+          g_signal_connect (submenu, "item-removed", G_CALLBACK (item_removed), gtk_submenu);
+#endif
+
           /* Create widgets for submenu */
           create_menu_widgets (gtk_submenu, submenu);
 
           /* Destroy submenu if it is empty */
+          /* FIXME destroying menus will not work with monitoring. if a menu is destroyed and
+           * an item is added to it at runtime, where should we add it...? instead, we should
+           * just hide the empty menu */
+#if 0
           if (G_UNLIKELY (gtk_container_get_children (GTK_CONTAINER (gtk_submenu)) == NULL))
             gtk_widget_destroy (gtk_item);
+#endif
+          if (G_UNLIKELY (gtk_container_get_children (GTK_CONTAINER (gtk_submenu)) == NULL))
+            gtk_widget_hide (gtk_item);
         }
     }
 
@@ -255,6 +368,15 @@ create_menu_widgets (GtkWidget *gtk_menu,
 
 
 static void
+remove_child (GtkWidget *child,
+              GtkMenu   *menu)
+{
+  gtk_container_remove (GTK_CONTAINER (menu), child);
+}
+
+
+
+static void
 show_menu (GtkButton *button,
            GtkWidget *menu)
 {
@@ -281,7 +403,6 @@ create_main_window (void)
 {
   GtkWidget *window;
   GtkWidget *button;
-  GtkWidget *menu;
 
   /* Create main window */
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
@@ -299,10 +420,39 @@ create_main_window (void)
   gtk_widget_show (button);
 
   /* Create GTK+ root menu */
-  menu = gtk_menu_new ();
+  gtk_root = gtk_menu_new ();
 
   /* Display root menu when the button is clicked */
-  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (show_menu), menu);
+  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (show_menu), gtk_root);
+}
+
+
+
+static gboolean
+reload_menu (GError **error)
+{
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  return garcon_menu_load (root, NULL, error);
+}
+
+
+
+static void
+reload_required (GarconMenu *menu)
+{
+  GError *error = NULL;
+
+  g_return_if_fail (GARCON_IS_MENU (menu));
+
+  g_debug ("reload required");
+
+  if (!reload_menu (&error)) 
+    {
+      g_warning ("Failed to reload the menu: %s", error != NULL ? error->message : "No error");
+      g_error_free (error);
+    }
+  
+  gtk_container_foreach (GTK_CONTAINER (gtk_root), (GtkCallback) remove_child, gtk_root);
 }
 
 
@@ -327,25 +477,26 @@ main (gint    argc,
     root = garcon_menu_new_applications ();
 
   /* Check if the menu was loaded */
-  if (root != NULL
-      && garcon_menu_load (root, NULL, &error))
+  if (reload_menu (&error))
     {
-      /* Create main window */
+      /* create the main window */
       create_main_window ();
 
+      /* be notified when a menu rebuild is required */
+      g_signal_connect (root, "reload-required", G_CALLBACK (reload_required), NULL);
+
       /* Enter main loop */
       gtk_main ();
-
-      /* Destroy the root menu */
-      g_object_unref (root);
     }
   else
     {
       g_error ("Failed to load the menu: %s", error != NULL ? error->message : "No error");
-      if (error != NULL)
-        g_error_free (error);
+      g_error_free (error);
       exit_code = EXIT_FAILURE;
     }
 
+  /* Destroy the root menu */
+  g_object_unref (root);
+
   return exit_code;
 }



More information about the Xfce4-commits mailing list