diff --git a/garcon/garcon-menu.c b/garcon/garcon-menu.c index 37ec5080f615877356a8e370f60826698feb2578..f01259cf81005976442911bf764eb933c56e84fe 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 545a1aa10f5ba73f4333c409975af391cd28d293..585f3830a8024a8f3b05099da9da57ce02ee1cc1 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 c62dbd8751bb0bcaed068f46d4601bc067f9d69d..f805d98e2bdc2ddc9c33a3eb9039e32d1326c67a 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; @@ -144,6 +145,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); } } @@ -254,6 +367,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; }