/* * recent-uimanager.c * * Copyright (C) 2006 - Emmanuele Bassi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include #include #include #include #include /* * Simple action */ #define RECENT_TYPE_ACTION (recent_action_get_type ()) #define RECENT_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RECENT_TYPE_ACTION, RecentAction)) #define RECENT_IS_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RECENT_TYPE_ACTION)) typedef struct _RecentAction RecentAction; typedef struct _RecentActionClass RecentActionClass; struct _RecentAction { GtkAction parent_instance; }; struct _RecentActionClass { GtkActionClass parent_class; /* maps the GtkRecentChooser::item-activated signal */ void (*item_activated) (RecentAction *action, GtkRecentInfo *info); }; static guint item_activated = 0; G_DEFINE_TYPE (RecentAction, recent_action, GTK_TYPE_ACTION); static void more_activated_cb (GtkMenuItem *menu_item, RecentAction *action) { GtkWidget *dialog; GtkRecentFilter *filter; dialog = gtk_recent_chooser_dialog_new ("Recent Documents", NULL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); filter = gtk_recent_filter_new (); gtk_recent_filter_set_name (filter, "All files"); gtk_recent_filter_add_pattern (filter, "*"); gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (dialog), filter); gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (dialog), filter); filter = gtk_recent_filter_new (); gtk_recent_filter_set_name (filter, "All text files"); gtk_recent_filter_add_mime_type (filter, "text/plain"); gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (dialog), filter); filter = gtk_recent_filter_new (); gtk_recent_filter_set_name (filter, "All image files"); gtk_recent_filter_add_pixbuf_formats (filter); gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (dialog), filter); filter = gtk_recent_filter_new (); gtk_recent_filter_set_name (filter, "All files used by Gedit"); gtk_recent_filter_add_application (filter, "Gedit"); gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (dialog), filter); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { GtkRecentInfo *info; info = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (dialog)); if (!info) return; g_signal_emit (action, item_activated, 0, info); gtk_recent_info_unref (info); } gtk_widget_destroy (dialog); } static void recent_chooser_item_activated_cb (GtkRecentChooser *chooser, GtkAction *action) { GtkRecentInfo *current; current = gtk_recent_chooser_get_current_item (chooser); if (!current) return; g_signal_emit (action, item_activated, 0, current); gtk_recent_info_unref (current); } static GtkWidget * recent_action_create_menu_item (GtkAction *action) { GtkWidget *retval; GtkWidget *chooser_menu; GtkWidget *item; chooser_menu = gtk_recent_chooser_menu_new (); g_signal_connect (chooser_menu, "item-activated", G_CALLBACK (recent_chooser_item_activated_cb), action); item = gtk_separator_menu_item_new (); gtk_menu_shell_append (GTK_MENU_SHELL (chooser_menu), item); gtk_widget_show (item); item = gtk_menu_item_new_with_mnemonic ("_More..."); gtk_menu_shell_append (GTK_MENU_SHELL (chooser_menu), item); g_signal_connect (item, "activate", G_CALLBACK (more_activated_cb), action); gtk_widget_show (item); retval = g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, NULL); gtk_menu_item_set_submenu (GTK_MENU_ITEM (retval), chooser_menu); return retval; } static GtkWidget * recent_action_create_tool_item (GtkAction *action) { GtkWidget *chooser_menu; GtkWidget *item; GtkWidget *retval; chooser_menu = gtk_recent_chooser_menu_new (); g_signal_connect (chooser_menu, "item-activated", G_CALLBACK (recent_chooser_item_activated_cb), action); item = gtk_separator_menu_item_new (); gtk_menu_shell_append (GTK_MENU_SHELL (chooser_menu), item); gtk_widget_show (item); item = gtk_menu_item_new_with_mnemonic ("_More..."); gtk_menu_shell_append (GTK_MENU_SHELL (chooser_menu), item); g_signal_connect (item, "activate", G_CALLBACK (more_activated_cb), action); gtk_widget_show (item); retval = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON, NULL); gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (retval), chooser_menu); return retval; } static void recent_action_class_init (RecentActionClass *klass) { GtkActionClass *action_class; action_class = GTK_ACTION_CLASS (klass); action_class->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM; action_class->create_menu_item = recent_action_create_menu_item; action_class->toolbar_item_type = GTK_TYPE_MENU_TOOL_BUTTON; action_class->create_tool_item = recent_action_create_tool_item; item_activated = g_signal_new ("item-activated", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (RecentActionClass, item_activated), NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, GTK_TYPE_RECENT_INFO); } static void recent_action_init (RecentAction *action) { } static GtkAction * recent_action_new (const gchar *name) { return g_object_new (RECENT_TYPE_ACTION, "name", name, "label", "Open _Recent", "tooltip", NULL, "stock-id", NULL, NULL); } /* * Real application code */ static void recent_item_activated_cb (RecentAction *action, GtkRecentInfo *info, gpointer user_data) { g_message ("*** recent item activated: %s ***", gtk_recent_info_get_display_name (info)); } static void activate_cb (GtkAction *action, gpointer user_data) { g_message ("*** action activated: %s ***", gtk_action_get_name (action)); } static void recent_activate_cb (GtkAction *action, gpointer user_data) { recent_item_activated_cb (NULL, g_object_get_data (G_OBJECT (action), "gtk-recent-info"), user_data); } /* forward declaration */ static void add_inline_recent_items (guint merge_id, GtkUIManager *ui_manager); static void recent_manager_changed_cb (GtkRecentManager *manager, gpointer user_data) { GtkUIManager *ui_manager = GTK_UI_MANAGER (user_data); guint merge_id; GList *action_groups, *l; g_print ("Regenerating the UI\n"); merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (ui_manager), "recent-files-merge-id")); /* remove the UIs */ gtk_ui_manager_remove_ui (ui_manager, merge_id); /* remove the action group; gtk_ui_manager_remove_action_group() * should really take the action group's name instead of its * pointer. */ action_groups = gtk_ui_manager_get_action_groups (ui_manager); for (l = action_groups; l != NULL; l = l->next) { GtkActionGroup *group = l->data; if (strcmp (gtk_action_group_get_name (group), "recent-files-group") == 0) { /* this unrefs the action group and all of its actions */ gtk_ui_manager_remove_action_group (ui_manager, group); break; } } /* generate a new merge id and re-add everything */ merge_id = gtk_ui_manager_new_merge_id (ui_manager); add_inline_recent_items (merge_id, ui_manager); } static gint sort_mru_func (GtkRecentInfo *a, GtkRecentInfo *b) { if (a == b) return 0; if (!a || !b) return (a == NULL ? -1 : 1); return (gtk_recent_info_get_modified (b) - gtk_recent_info_get_modified (a)); } static void add_inline_recent_items (guint merge_id, GtkUIManager *ui_manager) { GtkActionGroup *action_group; GtkRecentManager *manager; GList *items, *l; guint i; static guint changed_id = 0; action_group = gtk_action_group_new ("recent-files-group"); manager = gtk_recent_manager_get_default (); items = gtk_recent_manager_get_items (manager); items = g_list_sort (items, (GCompareFunc) sort_mru_func); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); g_object_set_data (G_OBJECT (ui_manager), "recent-files-merge-id", GUINT_TO_POINTER (merge_id)); /* no items */ if (!items) { GtkAction *action; action = g_object_new (GTK_TYPE_ACTION, "name", "recent-info-empty", "label", "No recently used files", "sensitive", FALSE, NULL); gtk_action_group_add_action (action_group, action); g_object_unref (action); gtk_ui_manager_add_ui (ui_manager, merge_id, "/MenuBar/FileMenu/RecentFiles", "recent-info-empty", "recent-info-empty", GTK_UI_MANAGER_MENUITEM, FALSE); return; } for (i = 0, l = items; i < 4 && l != NULL; i +=1, l = l->next) { GtkRecentInfo *info = l->data; gchar *name = g_strdup_printf ("recent-info-%d-%lu", i, (gulong) time (NULL)); gchar *action_name = g_strdup (name); GtkAction *action; action = g_object_new (GTK_TYPE_ACTION, "name", action_name, "label", gtk_recent_info_get_display_name (info), "stock_id", NULL, NULL); g_object_set_data_full (G_OBJECT (action), "gtk-recent-info", gtk_recent_info_ref (info), (GDestroyNotify) gtk_recent_info_unref); g_signal_connect (action, "activate", G_CALLBACK (recent_activate_cb), NULL); gtk_action_group_add_action (action_group, action); g_object_unref (action); gtk_ui_manager_add_ui (ui_manager, merge_id, "/MenuBar/FileMenu/RecentFiles", name, action_name, GTK_UI_MANAGER_MENUITEM, FALSE); g_print ("* adding action `%s'\n", action_name); g_free (action_name); g_free (name); } g_list_foreach (items, (GFunc) gtk_recent_info_unref, NULL); g_list_free (items); /* don't connect twice to the same signal */ if (!changed_id) { changed_id = g_signal_connect (manager, "changed", G_CALLBACK (recent_manager_changed_cb), ui_manager); } } static GtkActionEntry entries[] = { { "FileMenu", NULL, "_File" }, { "New", GTK_STOCK_NEW, "_New", "N", NULL, G_CALLBACK (activate_cb) }, { "Open", GTK_STOCK_OPEN, "_Open", "O", NULL, G_CALLBACK (activate_cb) }, { "Save", GTK_STOCK_SAVE, "_Save", "S", NULL, G_CALLBACK (activate_cb) }, { "Quit", GTK_STOCK_QUIT, "_Quit", "Q", NULL, G_CALLBACK (gtk_main_quit) } }; static const gchar *ui_info = "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; int main (int argc, char *argv[]) { GtkWidget *window; GtkUIManager *manager; GtkAction *recent; GtkActionGroup *actions; GtkWidget *box; GError *err; guint merge_id; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 400, 300); g_signal_connect (window, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL); /* RecentAction, an action creating a menu item with an attached * GtkRecentChooserMenu inside the File menu */ recent = recent_action_new ("OpenRecent"); g_signal_connect (recent, "item-activated", G_CALLBACK (recent_item_activated_cb), NULL); actions = gtk_action_group_new ("Actions"); gtk_action_group_add_actions (actions, entries, G_N_ELEMENTS (entries), NULL); gtk_action_group_add_action (actions, recent); g_object_unref (recent); manager = gtk_ui_manager_new (); gtk_ui_manager_insert_action_group (manager, actions, 0); gtk_window_add_accel_group (GTK_WINDOW (window), gtk_ui_manager_get_accel_group (manager)); err = NULL; if (!gtk_ui_manager_add_ui_from_string (manager, ui_info, -1, &err)) { g_warning ("Unable to create menus: %s", err->message); g_error_free (err); } /* add some item from the list for those nostalgic of the inlined * recent files list inside the File menu; we use place holders and * gtk_ui_manager_add_ui(). */ merge_id = gtk_ui_manager_new_merge_id (manager); add_inline_recent_items (merge_id, manager); box = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (window), box); gtk_box_pack_start (GTK_BOX (box), gtk_ui_manager_get_widget (manager, "/MenuBar"), FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box), gtk_ui_manager_get_widget (manager, "/ToolBar"), FALSE, FALSE, 0); gtk_widget_show_all (box); gtk_widget_show (window); gtk_main (); return EXIT_SUCCESS; }