[balsa/popover: 27/34] toolbars: Port the popup menu to GtkPopover



commit d172bb29fc78e34cd929994b59c72f61d52d889a
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Mon May 18 20:47:18 2020 -0400

    toolbars: Port the popup menu to GtkPopover
    
    and refactor the customize-toobars dialog.
    
    configure.ac: Harmonize with master

 configure.ac          |   2 +-
 src/main-window.c     |   4 +-
 src/toolbar-factory.c | 247 +++++++++++++++++++++++++++-----------------------
 src/toolbar-factory.h |  11 +--
 src/toolbar-prefs.c   |  18 ++--
 src/toolbar-prefs.h   |  10 +-
 6 files changed, 154 insertions(+), 138 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7c432c1fe..464bf9209 100644
--- a/configure.ac
+++ b/configure.ac
@@ -235,7 +235,7 @@ libical >= 2.0.0
 
 PKG_CHECK_MODULES(BALSA_AB, [
    glib-2.0
-   gtk+-3.0 >= 3.22.0
+   gtk+-3.0
    gmime-3.0 >= 3.2.6
 ])
 
diff --git a/src/main-window.c b/src/main-window.c
index eddc210e4..b2b43e11a 100644
--- a/src/main-window.c
+++ b/src/main-window.c
@@ -922,7 +922,9 @@ toolbars_activated(GSimpleAction * action,
                    GVariant      * parameter,
                    gpointer        user_data)
 {
-    customize_dialog_cb(user_data, user_data);
+    GtkWindow *window = GTK_WINDOW(user_data);
+
+    balsa_toolbar_customize(window, BALSA_TOOLBAR_TYPE_MAIN_WINDOW);
 }
 
 static void
diff --git a/src/toolbar-factory.c b/src/toolbar-factory.c
index d79378112..182749ffe 100644
--- a/src/toolbar-factory.c
+++ b/src/toolbar-factory.c
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <glib/gi18n.h>
 
+#include "application-helpers.h"
 #include "balsa-app.h"
 #include "balsa-icons.h"
 #include "main-window.h"
@@ -454,11 +455,13 @@ tm_set_tool_item_label(GtkToolItem * tool_item, const gchar * stock_id,
 
 static GtkToolbarStyle tm_default_style(void);
 
-static const struct {
+typedef struct {
     const gchar *text;
     const gchar *config_name;
     GtkToolbarStyle style;
-} tm_toolbar_options[] = {
+} ToolbarOption;
+
+static const ToolbarOption tm_toolbar_options[] = {
     {N_("Text Be_low Icons"),           "both",       GTK_TOOLBAR_BOTH},
     {N_("Priority Text Be_side Icons"), "both-horiz", GTK_TOOLBAR_BOTH_HORIZ},
     {NULL,                              "both_horiz", GTK_TOOLBAR_BOTH_HORIZ},
@@ -479,11 +482,14 @@ tm_default_style(void)
     if (str) {
         guint i;
 
-        for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++)
-            if (strcmp(tm_toolbar_options[i].config_name, str) == 0) {
-                default_style = tm_toolbar_options[i].style;
+        for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++) {
+            const ToolbarOption *option = &tm_toolbar_options[i];
+
+            if (strcmp(option->config_name, str) == 0) {
+                default_style = option->style;
                 break;
             }
+        }
         g_free(str);
     }
     g_object_unref(settings);
@@ -567,7 +573,8 @@ tm_changed_cb(BalsaToolbarModel * model, GtkWidget * toolbar)
 
 typedef struct {
     BalsaToolbarModel *model;
-    GtkWidget         *menu;
+    GtkWidget         *toolbar;
+    GtkWidget         *popup_menu;
 } toolbar_info;
 
 static void
@@ -577,39 +584,6 @@ tm_toolbar_weak_notify(toolbar_info * info, GtkWidget * toolbar)
     g_free(info);
 }
 
-#define BALSA_TOOLBAR_STYLE "balsa-toolbar-style"
-static void
-menu_item_toggled_cb(GtkCheckMenuItem * item, toolbar_info * info)
-{
-    if (gtk_check_menu_item_get_active(item)) {
-        info->model->style =
-            GPOINTER_TO_INT(g_object_get_data
-                            (G_OBJECT(item), BALSA_TOOLBAR_STYLE));
-        balsa_toolbar_model_changed(info->model);
-        if (info->menu)
-            gtk_menu_shell_deactivate(GTK_MENU_SHELL(info->menu));
-    }
-}
-
-/* We want to destroy the popup menu after handling the "toggled"
- * signal; the "deactivate" signal is apparently emitted before
- * "toggled", so we have to use an idle callback. */
-static gboolean
-tm_popup_idle_cb(GtkWidget *menu)
-{
-    gtk_widget_destroy(menu);
-    return FALSE;
-}
-
-static void
-tm_popup_deactivated_cb(GtkWidget * menu, toolbar_info * info)
-{
-    if (info->menu) {
-        g_idle_add((GSourceFunc) tm_popup_idle_cb, menu);
-        info->menu = NULL;
-    }
-}
-
 static gchar *
 tm_remove_underscore(const gchar * text)
 {
@@ -623,6 +597,38 @@ tm_remove_underscore(const gchar * text)
     return r;
 }
 
+static void
+tm_set_style_changed(GSimpleAction *action,
+                     GVariant      *parameter,
+                     gpointer       user_data)
+{
+    toolbar_info *info = user_data;
+    GtkToolbarStyle style;
+
+    style = g_variant_get_int32(parameter);
+    if (info->model->style != style) {
+        info->model->style = style;
+        balsa_toolbar_model_changed(info->model);
+    }
+
+    if (info->popup_menu != NULL)
+        gtk_popover_popdown(GTK_POPOVER(info->popup_menu));
+
+    g_simple_action_set_state(action, parameter);
+}
+
+static void
+tm_customize_activated(GSimpleAction *action,
+                       GVariant      *parameter,
+                       gpointer       user_data)
+{
+    toolbar_info *info = user_data;
+    GtkWidget *toplevel;
+
+    toplevel = gtk_widget_get_toplevel(info->toolbar);
+    balsa_toolbar_customize(GTK_WINDOW(toplevel), info->model->type);
+}
+
 
 static gboolean
 tm_popup_context_menu_cb(GtkWidget    * toolbar,
@@ -631,113 +637,130 @@ tm_popup_context_menu_cb(GtkWidget    * toolbar,
                          gint           button,
                          toolbar_info * info)
 {
-    GtkWidget *menu;
+    GSimpleActionGroup *simple;
+    static const char namespace[] = "toolbar";
+    static const GActionEntry entries[] = {
+        {"set-style", libbalsa_radio_activated, "i", "-1", tm_set_style_changed},
+        {"customize", tm_customize_activated}
+    };
+    GAction *set_style_action;
+    GMenu *menu;
+    GMenu *section;
     guint i;
-    GSList *group = NULL;
     GtkToolbarStyle default_style;
-    GdkEvent *event;
+    GtkWidget *popup_menu;
 
-    if (info->menu != NULL)
-        return FALSE;
+    simple = g_simple_action_group_new();
+    g_action_map_add_action_entries(G_ACTION_MAP(simple),
+                                    entries,
+                                    G_N_ELEMENTS(entries),
+                                    info);
+    set_style_action = g_action_map_lookup_action(G_ACTION_MAP(simple), "set-style");
+    gtk_widget_insert_action_group(toolbar, namespace, G_ACTION_GROUP(simple));
+    g_object_unref(simple);
 
-    info->menu = menu = gtk_menu_new();
-    g_signal_connect(menu, "deactivate",
-                     G_CALLBACK(tm_popup_deactivated_cb), info);
+    menu = g_menu_new();
 
     /* ... add menu items ... */
+    section = g_menu_new();
+
     for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++) {
-        GtkWidget *item;
+        const ToolbarOption *option = &tm_toolbar_options[i];
+        GMenuItem *item;
 
-        if (!tm_toolbar_options[i].text)
+        if (option->text == NULL)
             continue;
 
-        item =
-            gtk_radio_menu_item_new_with_mnemonic(group,
-                                                  _(tm_toolbar_options[i].
-                                                    text));
-        group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
-
-        if (tm_toolbar_options[i].style == info->model->style)
-            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),
-                                           TRUE);
-
-        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-        g_object_set_data(G_OBJECT(item), BALSA_TOOLBAR_STYLE,
-                          GINT_TO_POINTER(tm_toolbar_options[i].style));
-        g_signal_connect(item, "toggled", G_CALLBACK(menu_item_toggled_cb),
-                         info);
+        item = g_menu_item_new(_(option->text), NULL);
+        g_menu_item_set_action_and_target(item, "set-style", "i", option->style);
+        g_menu_append_item(section, item);
+        g_object_unref(item);
     }
+    g_action_change_state(set_style_action, g_variant_new_int32(info->model->style));
+
+    g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
+    g_object_unref(section);
 
     default_style = tm_default_style();
 
     for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++) {
+        const ToolbarOption *option = &tm_toolbar_options[i];
 
-        if (!tm_toolbar_options[i].text)
+        if (option->text == NULL)
             continue;
 
-        if (tm_toolbar_options[i].style == default_style) {
+        if (option->style == default_style) {
             gchar *option_text, *text;
-            GtkWidget *item;
+            GMenuItem *item;
 
-            gtk_menu_shell_append(GTK_MENU_SHELL(menu),
-                                  gtk_separator_menu_item_new());
+            section = g_menu_new();
 
-            option_text =
-                tm_remove_underscore(_(tm_toolbar_options[i].text));
+            option_text = tm_remove_underscore(_(option->text));
             text =
                 g_strdup_printf(_("Use Desktop _Default (%s)"),
                                 option_text);
             g_free(option_text);
 
-            item = gtk_radio_menu_item_new_with_mnemonic(group, text);
+            item = g_menu_item_new(text, NULL);
             g_free(text);
 
-            if (info->model->style == (GtkToolbarStyle) (-1))
-                gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
-                                               (item), TRUE);
-            gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-            g_object_set_data(G_OBJECT(item), BALSA_TOOLBAR_STYLE,
-                              GINT_TO_POINTER(-1));
-            g_signal_connect(item, "toggled",
-                             G_CALLBACK(menu_item_toggled_cb), info);
+            g_menu_item_set_action_and_target(item, "set-style", "i", -1);
+
+            g_menu_append_item(section, item);
+            g_object_unref(item);
+
+            g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
+            g_object_unref(section);
         }
     }
 
     if (gtk_widget_is_sensitive(toolbar)) {
         /* This is a real toolbar, not the template from the
          * toolbar-prefs dialog. */
-        GtkWidget *item;
-
-        gtk_menu_shell_append(GTK_MENU_SHELL(menu),
-                              gtk_separator_menu_item_new());
-        item =
-            gtk_menu_item_new_with_mnemonic(_("_Customize Toolbars…"));
-        g_signal_connect(item, "activate", G_CALLBACK(customize_dialog_cb),
-                         gtk_widget_get_toplevel(toolbar));
-        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-
-        /* Pass the model type to the customize widget, so that it can
-         * show the appropriate notebook page. */
-        g_object_set_data(G_OBJECT(item), BALSA_TOOLBAR_MODEL_TYPE,
-                          GINT_TO_POINTER(info->model->type));
+        section = g_menu_new();
+
+        g_menu_append(section, _("_Customize Toolbars…"), "customize");
+
+        g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
+        g_object_unref(section);
     }
 
-    gtk_widget_show_all(menu);
-    gtk_menu_attach_to_widget(GTK_MENU(menu), toolbar, NULL);
+    popup_menu = gtk_popover_new(toolbar);
+    gtk_popover_bind_model(GTK_POPOVER(popup_menu), G_MENU_MODEL(menu), namespace);
+    g_object_unref(menu);
+    info->popup_menu = popup_menu;
+
+    if (button != -1) {
+        /* We are called with (x, y) coordinates, but they are
+         * "relative to the root of the screen", and we want them
+         * "relative to the window". */
+        GdkEvent *event;
+        gdouble x_win, y_win;
+
+        event = gtk_get_current_event();
+
+        if (event != NULL &&
+            gdk_event_triggers_context_menu(event) &&
+            gdk_event_get_coords(event, &x_win, &y_win)) {
+            GdkRectangle rectangle;
+
+            /* Pop up above the pointer */
+            rectangle.x = (gint) x_win;
+            rectangle.width = 0;
+            rectangle.y = (gint) y_win;
+            rectangle.height = 0;
+            gtk_popover_set_pointing_to(GTK_POPOVER(popup_menu), &rectangle);
+        }
 
-    event = gtk_get_current_event();
-    if (event != NULL && gdk_event_get_event_type(event) == GDK_BUTTON_PRESS) {
-        gtk_menu_popup_at_pointer(GTK_MENU(menu), event);
-    } else {
-        gtk_menu_popup_at_widget(GTK_MENU(menu),
-                                 GTK_WIDGET(toolbar),
-                                 GDK_GRAVITY_NORTH,
-                                 GDK_GRAVITY_SOUTH,
-                                 NULL);
+        if (event != NULL)
+            gdk_event_free(event);
     }
 
-    if (event != NULL)
-        gdk_event_free(event);
+    /* Apparently, the popover is insensitive if the toolbar is
+     * insensitive, but we always want it to be sensitive. */
+    gtk_widget_set_sensitive(popup_menu, TRUE);
+
+    gtk_popover_popup(GTK_POPOVER(popup_menu));
 
     return TRUE;
 }
@@ -745,12 +768,8 @@ tm_popup_context_menu_cb(GtkWidget    * toolbar,
 GtkWidget *balsa_toolbar_new(BalsaToolbarModel * model,
                              GActionMap        * action_map)
 {
-    toolbar_info *info;
     GtkWidget *toolbar;
-
-    info = g_new(toolbar_info, 1);
-    info->model = model;
-    info->menu = NULL;
+    toolbar_info *info;
 
     toolbar = gtk_toolbar_new();
     g_object_set_data_full(G_OBJECT(toolbar), BALSA_TOOLBAR_ACTION_MAP,
@@ -758,6 +777,10 @@ GtkWidget *balsa_toolbar_new(BalsaToolbarModel * model,
                            (GDestroyNotify) g_object_unref);
     tm_populate(toolbar, model);
 
+    info = g_new0(toolbar_info, 1);
+    info->model = model;
+    info->toolbar = toolbar;
+
     g_signal_connect(model, "changed", G_CALLBACK(tm_changed_cb), toolbar);
     g_object_weak_ref(G_OBJECT(toolbar),
                       (GWeakNotify) tm_toolbar_weak_notify, info);
diff --git a/src/toolbar-factory.h b/src/toolbar-factory.h
index e307fed49..a464f1360 100644
--- a/src/toolbar-factory.h
+++ b/src/toolbar-factory.h
@@ -31,10 +31,7 @@ G_DECLARE_FINAL_TYPE(BalsaToolbarModel,
                      TOOLBAR_MODEL,
                      GObject);
 
-typedef void (*BalsaToolbarFunc) (GtkWidget *, gpointer);
-#define BALSA_TOOLBAR_FUNC(f) ((BalsaToolbarFunc) (f))
-
-typedef struct t_button_data {
+typedef struct {
     char *pixmap_id;            /* not translatable */
     char *button_text;          /* translatable */
     gboolean is_important;      /* whether to show beside icon */
@@ -48,12 +45,6 @@ typedef struct {
     gchar *icon;
 } BalsaToolbarEntry;
 
-typedef enum {
-    BALSA_TOOLBAR_TYPE_MAIN_WINDOW,
-    BALSA_TOOLBAR_TYPE_COMPOSE_WINDOW,
-    BALSA_TOOLBAR_TYPE_MESSAGE_WINDOW
-} BalsaToolbarType;
-
 void update_all_toolbars(void);
 
 /* toolbar code for gtk+-2 */
diff --git a/src/toolbar-prefs.c b/src/toolbar-prefs.c
index 8bcf46c97..8a69901c2 100644
--- a/src/toolbar-prefs.c
+++ b/src/toolbar-prefs.c
@@ -51,8 +51,6 @@ enum {
     TP_N_COLUMNS
 };
 
-static GtkWidget *customize_widget;
-
 /* Structure associated with each notebook page. */
 typedef struct ToolbarPage_ ToolbarPage;
 struct ToolbarPage_ {
@@ -113,19 +111,19 @@ static void replace_nl_with_space(char* str);
 /* create the toolbar-customization dialog
  */
 void
-customize_dialog_cb(GtkWidget * widget, gpointer data)
+balsa_toolbar_customize(GtkWindow * active_window, BalsaToolbarType type)
 {
     GtkWidget *notebook;
     GtkWidget *child;
     GtkWidget *option_frame;
     GtkWidget *option_box;
     GtkWidget *wrap_button;
-    GtkWidget *active_window = data;
-    BalsaToolbarType   type;
     BalsaToolbarModel *model;
     GSimpleActionGroup *group;
     GtkWidget *content_area;
 
+    static GtkWidget *customize_widget;
+
     /* There can only be one */
     if (customize_widget) {
         gtk_window_present_with_time(GTK_WINDOW(customize_widget),
@@ -135,14 +133,14 @@ customize_dialog_cb(GtkWidget * widget, gpointer data)
 
     customize_widget =
         gtk_dialog_new_with_buttons(_("Customize Toolbars"),
-                                    GTK_WINDOW(active_window),
+                                    active_window,
                                     GTK_DIALOG_DESTROY_WITH_PARENT |
                                     libbalsa_dialog_flags(),
                                     _("_Close"), GTK_RESPONSE_CLOSE,
                                     _("_Help"),  GTK_RESPONSE_HELP,
                                     NULL);
 #if HAVE_MACOSX_DESKTOP
-    libbalsa_macosx_menu_for_parent(customize_widget, GTK_WINDOW(active_window));
+    libbalsa_macosx_menu_for_parent(customize_widget, active_window);
 #endif
     g_object_add_weak_pointer(G_OBJECT(customize_widget),
                               (gpointer *) & customize_widget);
@@ -212,9 +210,6 @@ customize_dialog_cb(GtkWidget * widget, gpointer data)
 
     /* Now that the pages are shown, we can switch to the page
      * corresponding to the toolbar that the user clicked on. */
-    type =
-        GPOINTER_TO_INT(g_object_get_data
-                        (G_OBJECT(widget), BALSA_TOOLBAR_MODEL_TYPE));
     gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), type);
 }
 
@@ -709,9 +704,10 @@ tp_page_refresh_preview(ToolbarPage * page)
         gint item;
 
         gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
-        if (item >= 0 && item < toolbar_button_count)
+        if (item >= 0 && item < toolbar_button_count) {
             balsa_toolbar_model_append_icon(page->model,
                                             toolbar_buttons[item].pixmap_id);
+        }
     }
 }
 
diff --git a/src/toolbar-prefs.h b/src/toolbar-prefs.h
index 75fe39fba..ae4f984f4 100644
--- a/src/toolbar-prefs.h
+++ b/src/toolbar-prefs.h
@@ -22,10 +22,14 @@
 
 #include <gtk/gtk.h>
 
-int get_toolbar_button_index(const char *id);
+typedef enum {
+    BALSA_TOOLBAR_TYPE_MAIN_WINDOW,
+    BALSA_TOOLBAR_TYPE_COMPOSE_WINDOW,
+    BALSA_TOOLBAR_TYPE_MESSAGE_WINDOW
+} BalsaToolbarType;
 
-void customize_dialog_cb(GtkWidget *, gpointer);
+int get_toolbar_button_index(const char *id);
 
-#define BALSA_TOOLBAR_MODEL_TYPE "balsa-toolbar-model-type"
+void balsa_toolbar_customize(GtkWindow * window, BalsaToolbarType type);
 
 #endif


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