[balsa/gtk3] Add menu accelerator keys to the source-viewer



commit 92ca6be53896dd4acc34f77330cfa3c796d16058
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Sun Jul 21 16:00:35 2013 -0400

    Add menu accelerator keys to the source-viewer
    
        * libbalsa/application-helpers.c (accel_info_free),
        (accel_activate), (extract_accel_from_menu_item),
        (extract_accels_from_menu), (libbalsa_window_get_accel_group),
        (libbalsa_window_get_menu_bar): new file defining
        libbalsa_window_get_menu_bar.
        * libbalsa/application-helpers.h: ditto.
        * libbalsa/Makefile.am: add them to SOURCES.
        * libbalsa/source-viewer.c: use libbalsa_window_get_menu_bar to
        construct the menu-bar and add the accelerators to the window.

 ChangeLog                      |   12 +++
 libbalsa/Makefile.am           |    2 +
 libbalsa/application-helpers.c |  188 ++++++++++++++++++++++++++++++++++++++++
 libbalsa/application-helpers.h |   40 +++++++++
 libbalsa/source-viewer.c       |   64 ++++++--------
 5 files changed, 267 insertions(+), 39 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index b178d2a..3d62c4a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2013-07-21  Peter Bloomfield
 
+       * libbalsa/application-helpers.c (accel_info_free),
+       (accel_activate), (extract_accel_from_menu_item),
+       (extract_accels_from_menu), (libbalsa_window_get_accel_group),
+       (libbalsa_window_get_menu_bar): new file defining
+       libbalsa_window_get_menu_bar.
+       * libbalsa/application-helpers.h: ditto.
+       * libbalsa/Makefile.am: add them to SOURCES.
+       * libbalsa/source-viewer.c: use libbalsa_window_get_menu_bar to
+       construct the menu-bar and add the accelerators to the window.
+
+2013-07-21  Peter Bloomfield
+
        More GtkStock deprecation cleanup
 
        * src/balsa-index.c (bndx_popup_menu_create),
diff --git a/libbalsa/Makefile.am b/libbalsa/Makefile.am
index 00bda16..8be3815 100644
--- a/libbalsa/Makefile.am
+++ b/libbalsa/Makefile.am
@@ -66,6 +66,8 @@ libbalsa_a_SOURCES =          \
        address-view.h          \
        address.c               \
        address.h               \
+       application-helpers.c   \
+       application-helpers.h   \
        body.c                  \
        body.h                  \
        cell-renderer-button.c  \
diff --git a/libbalsa/application-helpers.c b/libbalsa/application-helpers.c
new file mode 100644
index 0000000..1d7d49d
--- /dev/null
+++ b/libbalsa/application-helpers.c
@@ -0,0 +1,188 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* Balsa E-Mail Client
+ * Copyright (C) 1998-2013 Stuart Parmenter and others, see AUTHORS file.
+ *
+ * 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 3 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/>.
+ */
+
+/*
+ * Helpers for GtkApplicationWindow
+ *
+ * Currently only one helper
+ */
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
+# include "config.h"
+#endif                          /* HAVE_CONFIG_H */
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include "application-helpers.h"
+
+/*
+ * libbalsa_window_get_menu_bar
+ *
+ * Construct a menu-bar for a GtkApplicationWindow that does not use the
+ * GApplication's menubar
+ *
+ * window       the GtkApplicationWindow
+ * entries      array of GActionEntry structures
+ * n_entries    length of the array
+ * ui_file      filename for GtkBuilder input defining a menu named
+ *              "menubar"
+ *
+ * returns:     the GtkMenuBar
+ */
+
+typedef struct {
+    GAction  *action;
+    GVariant *parameter;
+} AccelInfo;
+
+static void
+accel_info_free(AccelInfo * info)
+{
+    if (info->parameter)
+        g_variant_unref(info->parameter);
+    g_free(info);
+}
+
+static gboolean
+accel_activate(GtkAccelGroup * accel_group,
+               GObject       * acceleratable,
+               guint           keyval,
+               GdkModifierType modifier,
+               gpointer        user_data)
+{
+    AccelInfo *info = user_data;
+
+    g_action_activate(info->action, info->parameter);
+
+    return TRUE;
+}
+
+static void
+extract_accel_from_menu_item(GMenuModel    * model,
+                             gint            item,
+                             GActionMap    * action_map,
+                             GtkAccelGroup * accel_group)
+{
+    GMenuAttributeIter *iter;
+    const gchar *key;
+    GVariant *value;
+    const gchar *accel = NULL;
+    const gchar *action = NULL;
+    GVariant *target = NULL;
+
+    iter = g_menu_model_iterate_item_attributes(model, item);
+    while (g_menu_attribute_iter_get_next(iter, &key, &value)) {
+        if (g_str_equal(key, "action")
+            && g_variant_is_of_type(value, G_VARIANT_TYPE_STRING))
+            action = g_variant_get_string(value, NULL);
+        else if (g_str_equal(key, "accel")
+                 && g_variant_is_of_type(value, G_VARIANT_TYPE_STRING))
+            accel = g_variant_get_string(value, NULL);
+        else if (g_str_equal(key, "target"))
+            target = g_variant_ref(value);
+        g_variant_unref(value);
+    }
+    g_object_unref(iter);
+
+    if (accel && action) {
+        guint accel_key;
+        GdkModifierType accel_mods;
+        AccelInfo *info;
+        const gchar *basename;
+        GClosure *closure;
+
+        gtk_accelerator_parse(accel, &accel_key, &accel_mods);
+        basename = strchr(action, '.');
+        basename = basename ? basename + 1 : action;
+        info = g_new(AccelInfo, 1);
+        info->action = g_action_map_lookup_action(action_map, basename);
+        info->parameter = target ? g_variant_ref(target) : NULL;
+        closure = g_cclosure_new(G_CALLBACK(accel_activate), info,
+                                 (GClosureNotify) accel_info_free);
+        gtk_accel_group_connect(accel_group, accel_key, accel_mods, 0,
+                                closure);
+    }
+
+    if (target)
+        g_variant_unref(target);
+}
+
+static void
+extract_accels_from_menu(GMenuModel    * model,
+                         GActionMap    * action_map,
+                         GtkAccelGroup * accel_group)
+{
+    gint i;
+    GMenuLinkIter *iter;
+    const gchar *key;
+    GMenuModel *m;
+
+    for (i = 0; i < g_menu_model_get_n_items(model); i++) {
+        extract_accel_from_menu_item(model, i, action_map, accel_group);
+
+        iter = g_menu_model_iterate_item_links(model, i);
+        while (g_menu_link_iter_get_next(iter, &key, &m)) {
+            extract_accels_from_menu(m, action_map, accel_group);
+            g_object_unref(m);
+        }
+        g_object_unref(iter);
+    }
+}
+
+static GtkAccelGroup *
+libbalsa_window_get_accel_group(GMenuModel * model,
+                GActionMap * action_map)
+{
+    GtkAccelGroup *accel_group;
+
+    accel_group = gtk_accel_group_new();
+    extract_accels_from_menu(model, action_map, accel_group);
+
+    return accel_group;
+}
+
+GtkWidget *
+libbalsa_window_get_menu_bar(GtkApplicationWindow * window,
+                             const GActionEntry   * entries,
+                             gint                   n_entries,
+                             const gchar          * ui_file,
+                             GError              ** error)
+{
+    GActionMap *map = G_ACTION_MAP(window);
+    GtkBuilder *builder;
+    GtkWidget *menu_bar = NULL;
+
+    g_action_map_add_action_entries(map, entries, n_entries, window);
+
+    builder = gtk_builder_new();
+    if (gtk_builder_add_from_file(builder, ui_file, error)) {
+        GMenuModel *menu_model;
+        GtkAccelGroup *accel_group;
+
+        menu_model =
+            G_MENU_MODEL(gtk_builder_get_object(builder, "menubar"));
+
+        menu_bar = gtk_menu_bar_new_from_model(menu_model);
+
+        accel_group = libbalsa_window_get_accel_group(menu_model, map);
+        gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
+    }
+    g_object_unref(builder);
+
+    return menu_bar;
+}
diff --git a/libbalsa/application-helpers.h b/libbalsa/application-helpers.h
new file mode 100644
index 0000000..91a41b6
--- /dev/null
+++ b/libbalsa/application-helpers.h
@@ -0,0 +1,40 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* Balsa E-Mail Client
+ * Copyright (C) 1998-2013 Stuart Parmenter and others, see AUTHORS file.
+ *
+ * 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 3 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/>.
+ */
+
+/*
+ * Helpers for GtkApplicationWindow
+ *
+ * Currently only one helper
+ */
+
+#ifndef   __LIBBALSA_APPLICATION_HELPERS_H__
+#define   __LIBBALSA_APPLICATION_HELPERS_H__
+
+#ifndef BALSA_VERSION
+#error "Include config.h before this file."
+#endif
+
+#include <gtk/gtk.h>
+
+GtkWidget *libbalsa_window_get_menu_bar(GtkApplicationWindow * window,
+                                        const GActionEntry   * entries,
+                                        gint                   n_entries,
+                                        const gchar          * ui_file,
+                                        GError              ** error);
+
+#endif                         /* __LIBBALSA_APPLICATION_HELPERS_H__ */
diff --git a/libbalsa/source-viewer.c b/libbalsa/source-viewer.c
index 99dc72c..9d00f70 100644
--- a/libbalsa/source-viewer.c
+++ b/libbalsa/source-viewer.c
@@ -28,6 +28,7 @@
 
 #include <stdio.h>
 
+#include "application-helpers.h"
 #include "misc.h"
 #include "libbalsa.h"
 #include "libbalsa_private.h"
@@ -183,41 +184,6 @@ lsv_window_destroy_notify(LibBalsaSourceViewerInfo * lsvi)
    pops up a window containing the source of the message msg.
 */
 
-static GtkWidget*
-lsv_app_set_menus(GtkWindow * app, GAction ** action)
-{
-    GtkWidget *window;
-    GtkWidget *menu_bar = NULL;
-    GtkBuilder *builder;
-    GError *err = NULL;
-
-    window = GTK_WIDGET(app);
-
-    g_action_map_add_action_entries(G_ACTION_MAP(app), win_entries,
-                                    G_N_ELEMENTS(win_entries), window);
-    *action = g_action_map_lookup_action(G_ACTION_MAP(app), "lsv-escape");
-
-    builder = gtk_builder_new();
-    if (gtk_builder_add_from_file(builder,
-                                  BALSA_DATA_PREFIX "/ui/source-viewer.ui",
-                                  &err)) {
-        GMenuModel *menu_model;
-
-        menu_model =
-            G_MENU_MODEL(gtk_builder_get_object(builder, "menubar"));
-        menu_bar = gtk_menu_bar_new_from_model(menu_model);
-    } else {
-        g_print("%s error: %s\n", __func__, err->message);
-        libbalsa_information(LIBBALSA_INFORMATION_WARNING,
-                             _("Error adding from %s: %s\n"),
-                             "source-viewer.ui", err->message);
-        g_error_free(err);
-    }
-    g_object_unref(builder);
-
-    return menu_bar;
-}
-
 static void
 lsv_size_allocate_cb(GtkWindow * window, GtkAllocation * alloc,
                      LibBalsaSourceViewerInfo * lsvi)
@@ -244,9 +210,11 @@ libbalsa_show_message_source(GtkApplication  * application,
     PangoFontDescription *desc;
     GtkWidget *vbox, *interior;
     GtkWidget *window;
-    GAction *escape_action = NULL;
-    LibBalsaSourceViewerInfo *lsvi;
+    gchar *ui_file;
     GtkWidget *menu_bar;
+    GError *err = NULL;
+    LibBalsaSourceViewerInfo *lsvi;
+    GAction *escape_action;
 
     g_return_if_fail(msg);
     g_return_if_fail(MAILBOX_OPEN(msg->mailbox));
@@ -272,9 +240,24 @@ libbalsa_show_message_source(GtkApplication  * application,
     gtk_window_set_title(GTK_WINDOW(window), _("Message Source"));
     gtk_window_set_wmclass(GTK_WINDOW(window), "message-source", "Balsa");
     gtk_window_set_default_size(GTK_WINDOW(window), *width, *height);
-    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
-    menu_bar = lsv_app_set_menus(GTK_WINDOW(window), &escape_action);
 
+    ui_file = g_build_filename(BALSA_DATA_PREFIX, "ui", "source-viewer.ui",
+                               NULL);
+    menu_bar = libbalsa_window_get_menu_bar(GTK_APPLICATION_WINDOW(window),
+                                            win_entries,
+                                            G_N_ELEMENTS(win_entries),
+                                            ui_file, &err);
+    if (!menu_bar) {
+        libbalsa_information(LIBBALSA_INFORMATION_WARNING,
+                             _("Error adding from %s: %s\n"), ui_file,
+                             err->message);
+        g_free(ui_file);
+        g_error_free(err);
+        return;
+    }
+    g_free(ui_file);
+
+    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
 #if HAVE_MACOSX_DESKTOP
     libbalsa_macosx_menu(window, GTK_MENU_SHELL(menu_bar));
 #else
@@ -298,6 +281,9 @@ libbalsa_show_message_source(GtkApplication  * application,
                      G_CALLBACK(lsv_size_allocate_cb), lsvi);
 
     gtk_widget_show_all(window);
+
+    escape_action =
+        g_action_map_lookup_action(G_ACTION_MAP(window), "lsv-escape");
     lsv_escape_change_state(G_SIMPLE_ACTION(escape_action),
                             g_variant_new_boolean(*escape_specials),
                             window);


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