[gnome-panel] menu: add GpRecentMenu



commit cfba6a9fabcd94c075cb365ce0f868b8dc66903c
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Sat Feb 17 20:04:14 2018 +0200

    menu: add GpRecentMenu

 modules/menu/Makefile.am      |    2 +
 modules/menu/gp-recent-menu.c |  460 +++++++++++++++++++++++++++++++++++++++++
 modules/menu/gp-recent-menu.h |   32 +++
 3 files changed, 494 insertions(+), 0 deletions(-)
---
diff --git a/modules/menu/Makefile.am b/modules/menu/Makefile.am
index ed765b6..35d51c3 100644
--- a/modules/menu/Makefile.am
+++ b/modules/menu/Makefile.am
@@ -35,6 +35,8 @@ menu_la_SOURCES = \
        gp-menu.h \
        gp-places-menu.c \
        gp-places-menu.h \
+       gp-recent-menu.c \
+       gp-recent-menu.h \
        gp-user-menu-applet.c \
        gp-user-menu-applet.h \
        gp-volumes.c \
diff --git a/modules/menu/gp-recent-menu.c b/modules/menu/gp-recent-menu.c
new file mode 100644
index 0000000..a3405fb
--- /dev/null
+++ b/modules/menu/gp-recent-menu.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2002 James Willcox
+ * Copyright (C) 2018 Alberts Muktupāvels
+ *
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Alberts Muktupāvels <alberts muktupavels gmail com>
+ *     James Willcox <jwillcox gnome org>
+ */
+
+#include "config.h"
+#include "gp-recent-menu.h"
+#include "gp-menu-utils.h"
+
+#include <glib/gi18n-lib.h>
+#include <libgnome-panel/gp-image-menu-item.h>
+
+struct _GpRecentMenu
+{
+  GtkMenu    parent;
+
+  GpApplet  *applet;
+
+  gboolean   empty;
+
+  GtkWidget *clear_dialog;
+
+  guint      reload_id;
+
+  gulong     changed_id;
+  gulong     menu_icon_size_id;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_APPLET,
+  PROP_EMPTY,
+
+  LAST_PROP
+};
+
+static GParamSpec *menu_properties[LAST_PROP] = { NULL };
+
+G_DEFINE_TYPE (GpRecentMenu, gp_recent_menu, GTK_TYPE_MENU)
+
+static void
+activate_cb (GtkWidget     *item,
+             GtkRecentInfo *info)
+{
+  const gchar *uri;
+
+  uri = gtk_recent_info_get_uri (info);
+
+  gp_menu_utils_launch_uri (uri);
+}
+
+static gint
+items_sort_func (GtkRecentInfo *a,
+                 GtkRecentInfo *b)
+{
+  return gtk_recent_info_get_modified (b) - gtk_recent_info_get_modified (a);
+}
+
+static void
+append_recent_items (GpRecentMenu *menu)
+{
+  GtkRecentManager *manager;
+  GList *items;
+  guint icon_size;
+  gint count;
+  GList *l;
+
+  manager = gtk_recent_manager_get_default ();
+
+  items = gtk_recent_manager_get_items (manager);
+  items = g_list_sort (items, (GCompareFunc) items_sort_func);
+
+  icon_size = gp_applet_get_menu_icon_size (menu->applet);
+  count = 0;
+
+  for (l = items; l != NULL; l = l->next)
+    {
+      GtkRecentInfo *info;
+      GIcon *icon;
+      GtkWidget *image;
+      const gchar *label;
+      GtkWidget *item;
+      gchar *uri_display;
+
+      info = l->data;
+
+      icon = gtk_recent_info_get_gicon (info);
+      image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
+      gtk_image_set_pixel_size (GTK_IMAGE (image), icon_size);
+      g_clear_object (&icon);
+
+      label = gtk_recent_info_get_display_name (info);
+      item = gp_image_menu_item_new_with_label (label);
+      gp_image_menu_item_set_image (GP_IMAGE_MENU_ITEM (item), image);
+
+      uri_display = gtk_recent_info_get_uri_display (info);
+      if (uri_display != NULL)
+        {
+          gchar *tooltip;
+
+          tooltip = g_strdup_printf (_("Open '%s'"), uri_display);
+          g_free (uri_display);
+
+          gtk_widget_set_tooltip_text (item, tooltip);
+          g_free (tooltip);
+
+          g_object_bind_property (menu->applet, "enable-tooltips",
+                                  item, "has-tooltip",
+                                  G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+        }
+
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+      gtk_widget_show (item);
+
+      g_signal_connect_data (item, "activate",
+                             G_CALLBACK (activate_cb),
+                             gtk_recent_info_ref (info),
+                             (GClosureNotify) gtk_recent_info_unref,
+                             0);
+
+      if (++count == 10)
+        break;
+    }
+
+  g_list_free_full (items, (GDestroyNotify) gtk_recent_info_unref);
+}
+
+static void
+response_cb (GtkWidget    *widget,
+             gint          response,
+             GpRecentMenu *menu)
+{
+  if (response == GTK_RESPONSE_ACCEPT)
+    {
+      GtkRecentManager *manager;
+
+      manager = gtk_recent_manager_get_default ();
+      gtk_recent_manager_purge_items (manager, NULL);
+    }
+
+  gtk_widget_destroy (widget);
+}
+
+static void
+clear_cb (GtkWidget    *item,
+          GpRecentMenu *menu)
+{
+  GtkDialogFlags flags;
+  const gchar *message;
+  GtkWidget *dialog;
+  const gchar *text;
+
+  if (menu->clear_dialog != NULL)
+    {
+      gtk_window_present (GTK_WINDOW (menu->clear_dialog));
+      return;
+    }
+
+  flags = 0;
+  message = _("Clear the Recent Documents list?");
+
+  dialog = gtk_message_dialog_new (NULL, flags, GTK_MESSAGE_WARNING,
+                                   GTK_BUTTONS_NONE, "%s", message);
+
+  text = _("If you clear the Recent Documents list, you clear the following:\n"
+           "\342\200\242 All items from the Places \342\206\222 Recent Documents menu item.\n"
+           "\342\200\242 All items from the recent documents list in all applications.");
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", text);
+
+  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                          _("_Cancel"), GTK_RESPONSE_CANCEL,
+                          _("C_lear"), GTK_RESPONSE_ACCEPT,
+                          NULL);
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+  gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), FALSE);
+
+  g_signal_connect (dialog, "response", G_CALLBACK (response_cb), menu);
+
+  menu->clear_dialog = dialog;
+  g_object_add_weak_pointer (G_OBJECT (menu->clear_dialog),
+                             (gpointer *) &menu->clear_dialog);
+
+  gtk_widget_show (dialog);
+}
+
+static void
+append_clear (GpRecentMenu *menu)
+{
+  GtkWidget *item;
+  guint icon_size;
+  GtkWidget *icon;
+  const gchar *tooltip;
+
+  item = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  gtk_widget_set_sensitive (item, FALSE);
+  gtk_widget_show (item);
+
+  icon_size = gp_applet_get_menu_icon_size (menu->applet);
+  icon = gtk_image_new_from_icon_name ("edit-clear", GTK_ICON_SIZE_MENU);
+  gtk_image_set_pixel_size (GTK_IMAGE (icon), icon_size);
+
+  item = gp_image_menu_item_new_with_label (_("Clear Recent Documents..."));
+  gp_image_menu_item_set_image (GP_IMAGE_MENU_ITEM (item), icon);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+  gtk_widget_show (item);
+
+  tooltip = _("Clear all items from the recent documents list");
+  gtk_widget_set_tooltip_text (item, tooltip);
+
+  g_object_bind_property (menu->applet, "enable-tooltips", item, "has-tooltip",
+                          G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
+  g_signal_connect (item, "activate", G_CALLBACK (clear_cb), menu);
+}
+
+static void
+remove_item (GtkWidget *widget,
+             gpointer   user_data)
+{
+  gtk_widget_destroy (widget);
+}
+
+static void
+menu_reload (GpRecentMenu *menu)
+{
+  GList *children;
+  gboolean empty;
+
+  gtk_container_foreach (GTK_CONTAINER (menu), remove_item, NULL);
+
+  append_recent_items (menu);
+  append_clear (menu);
+
+  children = gtk_container_get_children (GTK_CONTAINER (menu));
+  empty = g_list_length (children) - 2 == 0;
+  g_list_free (children);
+
+  if (menu->empty == empty)
+    return;
+
+  menu->empty = empty;
+
+  g_object_notify_by_pspec (G_OBJECT (menu), menu_properties[PROP_EMPTY]);
+}
+
+static gboolean
+reload_cb (gpointer user_data)
+{
+  GpRecentMenu *menu;
+
+  menu = GP_RECENT_MENU (user_data);
+
+  menu_reload (menu);
+  menu->reload_id = 0;
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+queue_reload (GpRecentMenu *menu)
+{
+  if (menu->reload_id != 0)
+    return;
+
+  menu->reload_id = g_timeout_add_full (G_PRIORITY_LOW, 200,
+                                        reload_cb, menu,
+                                        NULL);
+
+  g_source_set_name_by_id (menu->reload_id, "[menu] reload_cb");
+}
+
+static void
+changed_cb (GtkRecentManager *manager,
+            GpRecentMenu     *menu)
+{
+  queue_reload (menu);
+}
+
+static void
+menu_icon_size_cb (GpApplet     *applet,
+                   GParamSpec   *pspec,
+                   GpRecentMenu *menu)
+{
+  queue_reload (menu);
+}
+
+static void
+gp_recent_menu_constructed (GObject *object)
+{
+  GpRecentMenu *menu;
+  GtkRecentManager *manager;
+
+  menu = GP_RECENT_MENU (object);
+
+  G_OBJECT_CLASS (gp_recent_menu_parent_class)->constructed (object);
+
+  manager = gtk_recent_manager_get_default ();
+  menu->changed_id = g_signal_connect (manager, "changed",
+                                       G_CALLBACK (changed_cb), menu);
+
+  menu->menu_icon_size_id = g_signal_connect (menu->applet,
+                                              "notify::menu-icon-size",
+                                              G_CALLBACK (menu_icon_size_cb),
+                                              menu);
+
+  queue_reload (menu);
+}
+
+static void
+gp_recent_menu_dispose (GObject *object)
+{
+  GpRecentMenu *menu;
+
+  menu = GP_RECENT_MENU (object);
+
+  if (menu->reload_id != 0)
+    {
+      g_source_remove (menu->reload_id);
+      menu->reload_id = 0;
+    }
+
+  if (menu->changed_id != 0)
+    {
+      GtkRecentManager *manager;
+
+      manager = gtk_recent_manager_get_default ();
+
+      g_signal_handler_disconnect (manager, menu->changed_id);
+      menu->changed_id = 0;
+    }
+
+  if (menu->menu_icon_size_id != 0)
+    {
+      g_signal_handler_disconnect (menu->applet, menu->menu_icon_size_id);
+      menu->menu_icon_size_id = 0;
+    }
+
+  g_clear_pointer (&menu->clear_dialog, gtk_widget_destroy);
+
+  menu->applet = NULL;
+
+  G_OBJECT_CLASS (gp_recent_menu_parent_class)->dispose (object);
+}
+
+static void
+gp_recent_menu_get_property (GObject    *object,
+                             guint       property_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GpRecentMenu *menu;
+
+  menu = GP_RECENT_MENU (object);
+
+  switch (property_id)
+    {
+      case PROP_APPLET:
+        g_assert_not_reached ();
+        break;
+
+      case PROP_EMPTY:
+        g_value_set_boolean (value, menu->empty);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+gp_recent_menu_set_property (GObject      *object,
+                             guint         property_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GpRecentMenu *menu;
+
+  menu = GP_RECENT_MENU (object);
+
+  switch (property_id)
+    {
+      case PROP_APPLET:
+        g_assert (menu->applet == NULL);
+        menu->applet = g_value_get_object (value);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+install_properties (GObjectClass *object_class)
+{
+  menu_properties[PROP_APPLET] =
+    g_param_spec_object ("applet", "Applet", "Applet",
+                         GP_TYPE_APPLET,
+                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
+                         G_PARAM_STATIC_STRINGS);
+
+  menu_properties[PROP_EMPTY] =
+    g_param_spec_boolean ("empty", "Empty", "Empty",
+                          TRUE,
+                          G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY |
+                          G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, LAST_PROP, menu_properties);
+}
+
+static void
+gp_recent_menu_class_init (GpRecentMenuClass *menu_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (menu_class);
+
+  object_class->constructed = gp_recent_menu_constructed;
+  object_class->dispose = gp_recent_menu_dispose;
+  object_class->get_property = gp_recent_menu_get_property;
+  object_class->set_property = gp_recent_menu_set_property;
+
+  install_properties (object_class);
+}
+
+static void
+gp_recent_menu_init (GpRecentMenu *menu)
+{
+}
+
+GtkWidget *
+gp_recent_menu_new (GpApplet *applet)
+{
+  return g_object_new (GP_TYPE_RECENT_MENU,
+                       "applet", applet,
+                       NULL);
+}
diff --git a/modules/menu/gp-recent-menu.h b/modules/menu/gp-recent-menu.h
new file mode 100644
index 0000000..5d0947d
--- /dev/null
+++ b/modules/menu/gp-recent-menu.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 Alberts Muktupāvels
+ *
+ * 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GP_RECENT_MENU_H
+#define GP_RECENT_MENU_H
+
+#include <libgnome-panel/gp-applet.h>
+
+G_BEGIN_DECLS
+
+#define GP_TYPE_RECENT_MENU (gp_recent_menu_get_type ())
+G_DECLARE_FINAL_TYPE (GpRecentMenu, gp_recent_menu, GP, RECENT_MENU, GtkMenu)
+
+GtkWidget *gp_recent_menu_new (GpApplet *applet);
+
+G_END_DECLS
+
+#endif


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