[gnome-panel] menu: add GpMenu



commit 9861fd0e429165d4aeef8801dcdd1ff9a6e0bfa8
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Sun Jan 21 01:53:42 2018 +0200

    menu: add GpMenu

 configure.ac             |    1 +
 modules/menu/Makefile.am |    3 +
 modules/menu/gp-menu.c   |  452 ++++++++++++++++++++++++++++++++++++++++++++++
 modules/menu/gp-menu.h   |   32 ++++
 po/POTFILES.in           |    1 +
 5 files changed, 489 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 41fdc27..d08eb9d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -156,6 +156,7 @@ AC_SUBST(FISH_CFLAGS)
 AC_SUBST(FISH_LIBS)
 
 PKG_CHECK_MODULES([MENU], [
+  gio-unix-2.0 >= $GLIB_REQUIRED
   gtk+-3.0 >= $GTK_REQUIRED
   libgnome-menu-3.0 >= $LIBGNOME_MENU_REQUIRED
 ])
diff --git a/modules/menu/Makefile.am b/modules/menu/Makefile.am
index b2a7942..b997c5b 100644
--- a/modules/menu/Makefile.am
+++ b/modules/menu/Makefile.am
@@ -5,6 +5,7 @@ menu_lib_LTLIBRARIES = menu.la
 
 menu_la_CPPFLAGS = \
        -DLOCALEDIR=\""$(localedir)"\" \
+       -DGMENU_I_KNOW_THIS_IS_UNSTABLE \
        -DG_LOG_DOMAIN=\""menu"\" \
        -DG_LOG_USE_STRUCTURED=1 \
        -I$(top_srcdir) \
@@ -19,6 +20,8 @@ menu_la_CFLAGS = \
        $(NULL)
 
 menu_la_SOURCES = \
+       gp-menu.c \
+       gp-menu.h \
        main-menu-applet.c \
        main-menu-applet.h \
        menu-bar-applet.c \
diff --git a/modules/menu/gp-menu.c b/modules/menu/gp-menu.c
new file mode 100644
index 0000000..c8702b0
--- /dev/null
+++ b/modules/menu/gp-menu.c
@@ -0,0 +1,452 @@
+/*
+ * 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/>.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <gio/gdesktopappinfo.h>
+#include <gmenu-tree.h>
+#include <libgnome-panel/gp-image-menu-item.h>
+
+#include "gp-menu.h"
+
+struct _GpMenu
+{
+  GtkMenu    parent;
+
+  gchar     *name;
+  gboolean   enable_tooltips;
+
+  GMenuTree *tree;
+
+  guint      load_id;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_NAME,
+  PROP_ENABLE_TOOLTIPS,
+
+  LAST_PROP
+};
+
+static GParamSpec *menu_properties[LAST_PROP] = { NULL };
+
+G_DEFINE_TYPE (GpMenu, gp_menu, GTK_TYPE_MENU)
+
+static void directory_to_menu_items (GMenuTreeDirectory *directory,
+                                     GtkWidget          *widget,
+                                     GpMenu             *menu);
+
+static void
+pid_cb (GDesktopAppInfo *info,
+        GPid             pid,
+        gpointer         user_data)
+{
+  g_child_watch_add (pid, (GChildWatchFunc) g_spawn_close_pid, NULL);
+}
+
+static void
+activate_cb (GtkWidget       *item,
+             GDesktopAppInfo *info)
+{
+  GSpawnFlags flags;
+  GError *error;
+  gboolean ret;
+
+  flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD;
+  error = NULL;
+
+  ret = g_desktop_app_info_launch_uris_as_manager (info, NULL, NULL,
+                                                   flags, NULL, NULL,
+                                                   pid_cb, NULL,
+                                                   &error);
+
+  if (ret == FALSE)
+    {
+      const gchar *display_name;
+      GtkWidget *dialog;
+
+      display_name = g_app_info_get_display_name (G_APP_INFO (info));
+      dialog = gtk_message_dialog_new (NULL, 0,
+                                       GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
+                                       _("Could not launch '%s'"),
+                                       display_name);
+
+      if (error != NULL)
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                                  "%s", error->message);
+
+      g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+      gtk_window_present (GTK_WINDOW (dialog));
+    }
+
+  g_clear_error (&error);
+}
+
+static void
+append_directory (GtkMenuShell  *shell,
+                  GMenuTreeIter *iter,
+                  GpMenu        *menu)
+{
+  GMenuTreeDirectory *directory;
+  GtkWidget *submenu;
+  const gchar *name;
+  GIcon *icon;
+  GtkWidget *item;
+
+  directory = gmenu_tree_iter_get_directory (iter);
+  submenu = gtk_menu_new ();
+
+  directory_to_menu_items (directory, submenu, menu);
+
+  name = gmenu_tree_directory_get_name (directory);
+  icon = gmenu_tree_directory_get_icon (directory);
+  gmenu_tree_item_unref (directory);
+
+  item = gp_image_menu_item_new_with_label (name);
+  gtk_menu_shell_append (shell, item);
+  gtk_widget_show (item);
+
+  if (icon != NULL)
+    {
+      GtkWidget *image;
+
+      image = gtk_image_new ();
+
+      gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_MENU);
+      gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
+
+      gp_image_menu_item_set_image (GP_IMAGE_MENU_ITEM (item), image);
+    }
+
+  gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
+}
+
+static void
+append_entry (GtkMenuShell  *shell,
+              GMenuTreeIter *iter,
+              GpMenu        *menu)
+{
+  GMenuTreeEntry *entry;
+  GDesktopAppInfo *info;
+  const gchar *name;
+  const gchar *description;
+  GIcon *icon;
+  GtkWidget *item;
+
+  entry = gmenu_tree_iter_get_entry (iter);
+  info = gmenu_tree_entry_get_app_info (entry);
+  gmenu_tree_item_unref (entry);
+
+  name = g_app_info_get_display_name (G_APP_INFO (info));
+  description = g_app_info_get_description (G_APP_INFO (info));
+  icon = g_app_info_get_icon (G_APP_INFO (info));
+
+  item = gp_image_menu_item_new_with_label (name);
+  gtk_menu_shell_append (shell, item);
+  gtk_widget_show (item);
+
+  if (icon != NULL)
+    {
+      GtkWidget *image;
+
+      image = gtk_image_new ();
+
+      gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_MENU);
+      gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
+
+      gp_image_menu_item_set_image (GP_IMAGE_MENU_ITEM (item), image);
+    }
+
+  if (description == NULL)
+    description = g_desktop_app_info_get_generic_name (info);
+
+  if (description != NULL)
+    {
+      gtk_widget_set_tooltip_text (item, description);
+
+      g_object_bind_property (menu, "enable-tooltips",
+                              item, "has-tooltip",
+                              G_BINDING_DEFAULT |
+                              G_BINDING_SYNC_CREATE);
+    }
+
+  g_signal_connect (item, "activate", G_CALLBACK (activate_cb), info);
+}
+
+static void
+append_separator (GtkMenuShell *shell)
+{
+  GtkWidget *item;
+
+  item = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (shell, item);
+  gtk_widget_show (item);
+
+  gtk_widget_set_sensitive (item, FALSE);
+}
+
+static void
+directory_to_menu_items (GMenuTreeDirectory *directory,
+                         GtkWidget          *widget,
+                         GpMenu             *menu)
+{
+  GMenuTreeIter *iter;
+  GMenuTreeItemType next_type;
+
+  iter = gmenu_tree_directory_iter (directory);
+  next_type = gmenu_tree_iter_next (iter);
+
+  while (next_type != GMENU_TREE_ITEM_INVALID)
+    {
+      switch (next_type)
+        {
+          case GMENU_TREE_ITEM_DIRECTORY:
+            append_directory (GTK_MENU_SHELL (widget), iter, menu);
+            break;
+
+          case GMENU_TREE_ITEM_ENTRY:
+            append_entry (GTK_MENU_SHELL (widget), iter, menu);
+            break;
+
+          case GMENU_TREE_ITEM_SEPARATOR:
+            append_separator (GTK_MENU_SHELL (widget));
+            break;
+
+          case GMENU_TREE_ITEM_ALIAS:
+            break;
+
+          case GMENU_TREE_ITEM_HEADER:
+            break;
+
+          case GMENU_TREE_ITEM_INVALID:
+          default:
+            g_assert_not_reached ();
+            break;
+        }
+
+      next_type = gmenu_tree_iter_next (iter);
+    }
+
+  gmenu_tree_iter_unref (iter);
+}
+
+static void
+remove_item (GtkWidget *widget,
+             gpointer   user_data)
+{
+  gtk_widget_destroy (widget);
+}
+
+static void
+menu_tree_load (GpMenu *menu)
+{
+  GError *error;
+  GMenuTreeDirectory *directory;
+
+  gtk_container_foreach (GTK_CONTAINER (menu), remove_item, NULL);
+
+  error = NULL;
+  if (!gmenu_tree_load_sync (menu->tree, &error))
+    {
+      g_warning ("Failed to load menu: %s", error->message);
+      g_clear_error (&error);
+
+      return;
+    }
+
+  directory = gmenu_tree_get_directory_from_path (menu->tree, "/");
+  directory_to_menu_items (directory, GTK_WIDGET (menu), menu);
+  gmenu_tree_item_unref (directory);
+}
+
+static gboolean
+menu_tree_load_cb (gpointer user_data)
+{
+  GpMenu *menu;
+
+  menu = GP_MENU (user_data);
+
+  menu_tree_load (menu);
+  menu->load_id = 0;
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+menu_tree_changed_cb (GMenuTree *tree,
+                      GpMenu    *menu)
+{
+  if (menu->load_id != 0)
+    return;
+
+  menu->load_id = g_timeout_add_full (G_PRIORITY_LOW, 200,
+                                      menu_tree_load_cb, menu,
+                                      NULL);
+
+  g_source_set_name_by_id (menu->load_id, "[menu] menu_tree_load_cb");
+}
+
+static void
+gp_menu_constructed (GObject *object)
+{
+  GpMenu *menu;
+  GMenuTreeFlags flags;
+
+  menu = GP_MENU (object);
+
+  G_OBJECT_CLASS (gp_menu_parent_class)->constructed (object);
+
+  flags = GMENU_TREE_FLAGS_SORT_DISPLAY_NAME;
+  menu->tree = gmenu_tree_new (menu->name, flags);
+
+  g_signal_connect (menu->tree, "changed",
+                    G_CALLBACK (menu_tree_changed_cb), menu);
+
+  menu_tree_changed_cb (menu->tree, menu);
+}
+
+static void
+gp_menu_dispose (GObject *object)
+{
+  GpMenu *menu;
+
+  menu = GP_MENU (object);
+
+  g_clear_object (&menu->tree);
+
+  if (menu->load_id != 0)
+    {
+      g_source_remove (menu->load_id);
+      menu->load_id = 0;
+    }
+
+  G_OBJECT_CLASS (gp_menu_parent_class)->dispose (object);
+}
+
+static void
+gp_menu_finalize (GObject *object)
+{
+  GpMenu *menu;
+
+  menu = GP_MENU (object);
+
+  g_clear_pointer (&menu->name, g_free);
+
+  G_OBJECT_CLASS (gp_menu_parent_class)->finalize (object);
+}
+
+static void
+gp_menu_get_property (GObject    *object,
+                      guint       property_id,
+                      GValue     *value,
+                      GParamSpec *pspec)
+{
+  GpMenu *menu;
+
+  menu = GP_MENU (object);
+
+  switch (property_id)
+    {
+      case PROP_NAME:
+        g_value_set_string (value, menu->name);
+        break;
+
+      case PROP_ENABLE_TOOLTIPS:
+        g_value_set_boolean (value, menu->enable_tooltips);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+gp_menu_set_property (GObject      *object,
+                      guint         property_id,
+                      const GValue *value,
+                      GParamSpec   *pspec)
+{
+  GpMenu *menu;
+
+  menu = GP_MENU (object);
+
+  switch (property_id)
+    {
+      case PROP_NAME:
+        g_assert (menu->name == NULL);
+        menu->name = g_value_dup_string (value);
+        break;
+
+      case PROP_ENABLE_TOOLTIPS:
+        menu->enable_tooltips = g_value_get_boolean (value);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+install_properties (GObjectClass *object_class)
+{
+  menu_properties[PROP_NAME] =
+    g_param_spec_string ("name", "Name", "Name",
+                         NULL,
+                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS);
+
+  menu_properties[PROP_ENABLE_TOOLTIPS] =
+    g_param_spec_boolean ("enable-tooltips", "Enable Tooltips", "Enable Tooltips",
+                          TRUE,
+                          G_PARAM_CONSTRUCT | G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, LAST_PROP, menu_properties);
+}
+
+static void
+gp_menu_class_init (GpMenuClass *menu_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (menu_class);
+
+  object_class->constructed = gp_menu_constructed;
+  object_class->dispose = gp_menu_dispose;
+  object_class->finalize = gp_menu_finalize;
+  object_class->get_property = gp_menu_get_property;
+  object_class->set_property = gp_menu_set_property;
+
+  install_properties (object_class);
+}
+
+static void
+gp_menu_init (GpMenu *menu)
+{
+}
+
+GtkWidget *
+gp_menu_new_from_name (const gchar *name)
+{
+  return g_object_new (GP_TYPE_MENU, "name", name, NULL);
+}
diff --git a/modules/menu/gp-menu.h b/modules/menu/gp-menu.h
new file mode 100644
index 0000000..4603d75
--- /dev/null
+++ b/modules/menu/gp-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_MENU_H
+#define GP_MENU_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GP_TYPE_MENU (gp_menu_get_type ())
+G_DECLARE_FINAL_TYPE (GpMenu, gp_menu, GP, MENU, GtkMenu)
+
+GtkWidget *gp_menu_new_from_name (const gchar *name);
+
+G_END_DECLS
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6dee977..764d2fe 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -59,6 +59,7 @@ modules/fish/fish-applet.c
 modules/fish/fish-module.c
 modules/fish/fish-menu.ui
 modules/fish/fish.ui
+modules/menu/gp-menu.c
 modules/menu/menu-module.c
 modules/notification-area/na-applet.c
 modules/notification-area/na-module.c


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