[balsa/gtk4: 195/196] toolbar-factory: Build with gtk4




commit dbf88a88a369b4d3e41817697af793fbbb959385
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Fri Oct 16 16:30:27 2020 -0400

    toolbar-factory: Build with gtk4
    
    GtkToolbar and friends have gone away, so we have to use a GtkBox
    and GtkButtons instead.

 src/toolbar-factory.c | 273 +++++++++++++++++++++++++++++++-------------------
 src/toolbar-factory.h |  24 ++++-
 2 files changed, 191 insertions(+), 106 deletions(-)
---
diff --git a/src/toolbar-factory.c b/src/toolbar-factory.c
index 4acd70334..581c2b2ad 100644
--- a/src/toolbar-factory.c
+++ b/src/toolbar-factory.c
@@ -50,7 +50,7 @@ struct _BalsaToolbarModel {
     GArray          *standard;
     GArray          *current;
     BalsaToolbarType type;
-    GtkToolbarStyle  style;
+    BalsaToolbarStyle  style;
     GSettings       *settings;
 };
 
@@ -112,7 +112,7 @@ balsa_toolbar_model_init(BalsaToolbarModel * model)
 /* End of class boilerplate */
 
 /* The descriptions must be SHORT */
-button_data toolbar_buttons[]={
+button_data toolbar_buttons[] = {
     {"",                         N_("Separator"),       FALSE},
     {"application-exit",         N_("Quit"),            FALSE},
     {BALSA_PIXMAP_RECEIVE,       N_("Check"),           TRUE},
@@ -128,32 +128,32 @@ button_data toolbar_buttons[]={
     {BALSA_PIXMAP_NEXT_FLAGGED,  N_("Next\nflagged"),   FALSE},
     {BALSA_PIXMAP_PREVIOUS_PART, N_("Previous\npart"),  FALSE},
     {BALSA_PIXMAP_NEXT_PART,     N_("Next\npart"),      FALSE},
-    {"edit-delete",           N_("Trash /\nDelete"), FALSE},
+    {"edit-delete",              N_("Trash /\nDelete"), FALSE},
     {BALSA_PIXMAP_POSTPONE,      N_("Postpone"),        FALSE},
-    {"document-print",            N_("Print"),           FALSE},
+    {"document-print",           N_("Print"),           FALSE},
     {BALSA_PIXMAP_REQUEST_MDN,   N_("Request\nMDN"),    FALSE},
     {BALSA_PIXMAP_SEND,          N_("Send"),            TRUE},
     {BALSA_PIXMAP_SEND_QUEUED,   N_("Send queued"),     TRUE},
     {BALSA_PIXMAP_SEND_RECEIVE,  N_("Exchange"),        FALSE},
-       {BALSA_PIXMAP_QUEUE,         N_("Queue"),           TRUE},
+    {BALSA_PIXMAP_QUEUE,         N_("Queue"),           TRUE},
     {BALSA_PIXMAP_ATTACHMENT,    N_("Attach"),          TRUE},
-    {"document-save",             N_("Save"),            TRUE},
+    {"document-save",            N_("Save"),            TRUE},
     {BALSA_PIXMAP_IDENTITY,      N_("Identity"),        FALSE},
-    {"tools-check-spelling",      N_("Spelling"),        TRUE},
+    {"tools-check-spelling",     N_("Spelling"),        TRUE},
     {"window-close-symbolic",    N_("Close"),           FALSE},
     {BALSA_PIXMAP_MARKED_NEW,    N_("Toggle\nnew"),     FALSE},
     {BALSA_PIXMAP_MARK_ALL,      N_("Mark all"),        FALSE},
     {BALSA_PIXMAP_SHOW_HEADERS,  N_("All\nheaders"),    FALSE},
-       {BALSA_PIXMAP_GPG_RECHECK,   N_("Recheck\ncryptography"), FALSE},
-    {"gtk-cancel",           N_("Reset\nFilter"),   FALSE},
+    {BALSA_PIXMAP_GPG_RECHECK,   N_("Recheck\ncryptography"), FALSE},
+    {"gtk-cancel",               N_("Reset\nFilter"),   FALSE},
     {BALSA_PIXMAP_SHOW_PREVIEW,  N_("Message Preview"),     FALSE},
     {BALSA_PIXMAP_GPG_SIGN,      N_("Sign"),            FALSE},
     {BALSA_PIXMAP_GPG_ENCRYPT,   N_("Encrypt"),         FALSE},
-    {"edit-undo",             N_("Undo"),            FALSE},
-    {"edit-redo",             N_("Redo"),            FALSE},
-    {"edit-clear",            N_("Expunge"),         FALSE},
-    {"list-remove",           N_("Empty\nTrash"),    FALSE},
-    {"gtk-edit",             N_("Edit"),            FALSE},
+    {"edit-undo",                N_("Undo"),            FALSE},
+    {"edit-redo",                N_("Redo"),            FALSE},
+    {"edit-clear",               N_("Expunge"),         FALSE},
+    {"list-remove",              N_("Empty\nTrash"),    FALSE},
+    {"gtk-edit",                 N_("Edit"),            FALSE},
 };
 
 const int toolbar_button_count = G_N_ELEMENTS(toolbar_buttons);
@@ -179,14 +179,13 @@ balsa_toolbar_sanitize_id(const gchar *id)
        return NULL;
 }
 
-/* this should go to GTK because it modifies its internal structures. */
 static void
 balsa_toolbar_remove_all(GtkWidget * widget)
 {
-    GtkToolItem *item;
+    GtkWidget *child;
 
-    while ((item = gtk_toolbar_get_nth_item((GtkToolbar *) widget, 0)))
-        gtk_container_remove((GtkContainer *) widget, (GtkWidget *) item);
+    while ((child = gtk_widget_get_first_child(widget)) != NULL)
+        gtk_box_remove(GTK_BOX(widget), child);
 }
 
 /* Load and save config
@@ -235,7 +234,7 @@ tm_save_model(BalsaToolbarModel * model)
     libbalsa_conf_push_group(key);
     g_free(key);
 
-    if (model->style != (GtkToolbarStyle) (-1))
+    if (model->style != (BalsaToolbarStyle) (-1))
         libbalsa_conf_set_int("Style", model->style);
 
 
@@ -264,7 +263,7 @@ tm_gsettings_change_cb(GSettings   * settings,
     BalsaToolbarModel *model = user_data;
 
     if (!strcmp(key, "toolbar-style") &&
-        model->style == (GtkToolbarStyle) (-1))
+        model->style == (BalsaToolbarStyle) (-1))
         balsa_toolbar_model_changed(model);
 }
 
@@ -416,63 +415,50 @@ tm_has_second_line(BalsaToolbarModel * model)
     return FALSE;
 }
 
-static gint
-tm_set_tool_item_label(GtkToolItem * tool_item, const gchar * stock_id,
-                       gboolean make_two_line)
+static char *
+tm_label(int button, gboolean make_two_line)
 {
-    gint button = get_toolbar_button_index(stock_id);
-    const gchar *text;
-    gchar *label;
+    const char *text;
+    char *label;
 
     if (button < 0)
-        return button;
+        return NULL;
 
     text = balsa_toolbar_button_text(button);
     if (balsa_app.toolbar_wrap_button_text) {
         /* Make sure all buttons have the same number of lines of
          * text (1 or 2), to keep icons aligned */
-        label = make_two_line && !strchr(text, '\n') ?
+        label = make_two_line && strchr(text, '\n') == NULL ?
             g_strconcat(text, "\n", NULL) : g_strdup(text);
     } else {
-        gchar *p = label = g_strdup(text);
-        while ((p = strchr(p, '\n')))
+        char *p = label = g_strdup(text);
+        while ((p = strchr(p, '\n')) != NULL)
             *p++ = ' ';
     }
 
-    gtk_tool_button_set_label(GTK_TOOL_BUTTON(tool_item), label);
-    g_free(label);
-
-    gtk_tool_item_set_is_important(tool_item,
-                                   toolbar_buttons[button].is_important);
-
-    if (strcmp(toolbar_buttons[button].pixmap_id, BALSA_PIXMAP_SEND) == 0
-        && balsa_app.always_queue_sent_mail)
-        gtk_tool_item_set_tooltip_text(tool_item,
-                                       _("Queue this message for sending"));
-
-    return button;
+    return label;
 }
 
-static GtkToolbarStyle tm_default_style(void);
+static BalsaToolbarStyle tm_default_style(void);
 
 typedef struct {
     const gchar *text;
     const gchar *config_name;
-    GtkToolbarStyle style;
+    BalsaToolbarStyle style;
 } 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},
-    {N_("_Icons Only"),                 "icons",      GTK_TOOLBAR_ICONS},
-    {N_("_Text Only"),                  "text",       GTK_TOOLBAR_TEXT}
+    {N_("Text Be_low Icons"),           "both",       BALSA_TOOLBAR_BOTH},
+    {N_("Priority Text Be_side Icons"), "both-horiz", BALSA_TOOLBAR_BOTH_HORIZ},
+    {NULL,                              "both_horiz", BALSA_TOOLBAR_BOTH_HORIZ},
+    {N_("_Icons Only"),                 "icons",      BALSA_TOOLBAR_ICONS},
+    {N_("_Text Only"),                  "text",       BALSA_TOOLBAR_TEXT}
 };
 
-static GtkToolbarStyle
+static BalsaToolbarStyle
 tm_default_style(void)
 {
-    GtkToolbarStyle default_style = GTK_TOOLBAR_BOTH;
+    BalsaToolbarStyle default_style = BALSA_TOOLBAR_BOTH;
     GSettings *settings;
     gchar *str;
 
@@ -497,7 +483,8 @@ tm_default_style(void)
     return default_style;
 }
 
-/* Populate a model
+/*
+ * Populate a toolbar from a model
  */
 #define BALSA_TOOLBAR_ACTION_MAP "balsa-toolbar-action-map"
 static void
@@ -510,40 +497,77 @@ tm_populate(GtkWidget * toolbar, BalsaToolbarModel * model)
     GActionMap *action_map =
         g_object_get_data(G_OBJECT(toolbar), BALSA_TOOLBAR_ACTION_MAP);
 
-    style_is_both = (model->style == GTK_TOOLBAR_BOTH
-                     || (model->style == (GtkToolbarStyle) - 1
-                         && tm_default_style() == GTK_TOOLBAR_BOTH));
+    style_is_both = (model->style == BALSA_TOOLBAR_BOTH
+                     || (model->style == (BalsaToolbarStyle) (-1)
+                         && tm_default_style() == BALSA_TOOLBAR_BOTH));
     make_two_line = style_is_both && tm_has_second_line(model);
 
     current = balsa_toolbar_model_get_current(model);
     for (j = 0; j < current->len; j++) {
         BalsaToolbarEntry *entry;
-        GtkToolItem *item;
+        GtkWidget *item;
 
         entry = &g_array_index(current, BalsaToolbarEntry, j);
 
-        if (!*entry->action) {
-            item = gtk_separator_tool_item_new();
+        if (entry->action[0] == '\0') {
+            item = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
         } else {
-            GtkWidget *icon;
             GAction *action;
             const GVariantType *type;
+            int button = get_toolbar_button_index(entry->icon);
             gchar *prefixed_action;
 
-            icon = gtk_image_new_from_icon_name
-                (balsa_icon_id(entry->icon), GTK_ICON_SIZE_SMALL_TOOLBAR);
             action = g_action_map_lookup_action(action_map, entry->action);
-            if (action &&
-                (type = g_action_get_state_type(action)) &&
+            if (action != NULL &&
+                (type = g_action_get_state_type(action)) != NULL &&
                 g_variant_type_equal(type, G_VARIANT_TYPE_BOOLEAN)) {
-                item = gtk_toggle_tool_button_new();
-                g_object_set(item, "icon-widget", icon,
-                             "label", entry->action, NULL);
+                item = gtk_toggle_button_new();
             } else {
-                item = gtk_tool_button_new(icon, entry->action);
+                item = gtk_button_new();
+            }
+
+            switch (model->style) {
+                GtkWidget *box;
+                GtkWidget *icon;
+                char *label;
+
+                case BALSA_TOOLBAR_ICONS:
+                    gtk_button_set_icon_name(GTK_BUTTON(item), entry->icon);
+                    break;
+
+                case BALSA_TOOLBAR_TEXT:
+                    label = tm_label(button, make_two_line);
+                    gtk_button_set_label(GTK_BUTTON(item), label);
+                    g_free(label);
+                    break;
+
+                case BALSA_TOOLBAR_BOTH_HORIZ:
+                    if (button >= 0 && !toolbar_buttons[button].is_important) {
+                        gtk_button_set_icon_name(GTK_BUTTON(item), entry->icon);
+                        break;
+                    } /* else fall through */
+
+                case BALSA_TOOLBAR_BOTH:
+                    box = gtk_box_new(model->style == BALSA_TOOLBAR_BOTH ?
+                                      GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL,
+                                      0);
+                    icon = gtk_image_new_from_icon_name(balsa_icon_id(entry->icon));
+                    gtk_image_set_icon_size(GTK_IMAGE(icon), GTK_ICON_SIZE_LARGE);
+                    gtk_box_append(GTK_BOX(box), icon);
+
+                    label = tm_label(button, make_two_line);
+                    gtk_box_append(GTK_BOX(box), gtk_label_new(label));
+                    g_free(label);
+                    break;
+
+                default:
+                    break;
+            }
+
+            if (strcmp(toolbar_buttons[button].pixmap_id, BALSA_PIXMAP_SEND) == 0
+                && balsa_app.always_queue_sent_mail) {
+                gtk_widget_set_tooltip_text(item, _("Queue this message for sending"));
             }
-            tm_set_tool_item_label(GTK_TOOL_ITEM(item), entry->icon,
-                                   make_two_line);
 
             prefixed_action =
                 g_strconcat(action ? "win." : "app.", entry->action, NULL);
@@ -551,14 +575,10 @@ tm_populate(GtkWidget * toolbar, BalsaToolbarModel * model)
                                            prefixed_action);
             g_free(prefixed_action);
         }
-        gtk_toolbar_insert((GtkToolbar *) toolbar, item, -1);
+        gtk_box_append(GTK_BOX(toolbar), item);
     }
 
-    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar),
-                          model->style != (GtkToolbarStyle) (-1) ?
-                          model->style : tm_default_style());
-
-    gtk_widget_show_all(toolbar);
+    gtk_widget_show(toolbar);
 }
 
 /* Update a real toolbar when the model has changed.
@@ -597,13 +617,16 @@ tm_remove_underscore(const gchar * text)
     return r;
 }
 
+/*
+ * Context menu callbacks
+ */
 static void
 tm_set_style_changed(GSimpleAction *action,
                      GVariant      *parameter,
                      gpointer       user_data)
 {
     toolbar_info *info = user_data;
-    GtkToolbarStyle style;
+    BalsaToolbarStyle style;
 
     style = g_variant_get_int32(parameter);
     if (info->model->style != style) {
@@ -623,19 +646,17 @@ tm_customize_activated(GSimpleAction *action,
                        gpointer       user_data)
 {
     toolbar_info *info = user_data;
-    GtkWidget *toplevel;
+    GtkRoot *root;
 
-    toplevel = gtk_widget_get_toplevel(info->toolbar);
-    balsa_toolbar_customize(GTK_WINDOW(toplevel), info->model->type);
+    root = gtk_widget_get_root(info->toolbar);
+    balsa_toolbar_customize(GTK_WINDOW(root), info->model->type);
 }
 
-
-static gboolean
-tm_popup_context_menu_cb(GtkWidget    * toolbar,
-                         gint           x,
-                         gint           y,
-                         gint           button,
-                         toolbar_info * info)
+/*
+ * Context menu
+ */
+static void
+tm_popup_context_menu(toolbar_info *info, GdkEvent *event)
 {
     GSimpleActionGroup *simple;
     static const GActionEntry entries[] = {
@@ -646,9 +667,8 @@ tm_popup_context_menu_cb(GtkWidget    * toolbar,
     GMenu *menu;
     GMenu *section;
     guint i;
-    GtkToolbarStyle default_style;
+    BalsaToolbarStyle default_style;
     GtkWidget *popup_menu;
-    GdkEvent *event;
 
     simple = g_simple_action_group_new();
     g_action_map_add_action_entries(G_ACTION_MAP(simple),
@@ -656,7 +676,7 @@ tm_popup_context_menu_cb(GtkWidget    * toolbar,
                                     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, "toolbar", G_ACTION_GROUP(simple));
+    gtk_widget_insert_action_group(info->toolbar, "toolbar", G_ACTION_GROUP(simple));
     g_object_unref(simple);
 
     menu = g_menu_new();
@@ -714,7 +734,7 @@ tm_popup_context_menu_cb(GtkWidget    * toolbar,
         }
     }
 
-    if (gtk_widget_is_sensitive(toolbar)) {
+    if (gtk_widget_is_sensitive(info->toolbar)) {
         /* This is a real toolbar, not the template from the
          * toolbar-prefs dialog. */
         section = g_menu_new();
@@ -725,29 +745,71 @@ tm_popup_context_menu_cb(GtkWidget    * toolbar,
         g_object_unref(section);
     }
 
-    popup_menu = libbalsa_popup_widget_new(toolbar, G_MENU_MODEL(menu), "toolbar");
+    popup_menu = libbalsa_popup_widget_new(info->toolbar, G_MENU_MODEL(menu), "toolbar");
 
     g_object_unref(menu);
     info->popup_menu = popup_menu;
 
-    event = gtk_get_current_event();
-
     libbalsa_popup_widget_popup(popup_menu, event);
     gtk_widget_set_sensitive(popup_menu, TRUE);
+}
 
-    if (event != NULL)
-        gdk_event_free(event);
+/*
+ * Toolbar callbacks
+ */
+static void
+tm_gesture_pressed(GtkGestureClick *click_gesture,
+                   gint             n_press,
+                   gdouble          x,
+                   gdouble          y,
+                   gpointer         user_data)
+{
+    toolbar_info *info = user_data;
+
+    if (n_press == 1) {
+        GtkGesture *gesture;
+        GdkEventSequence *sequence;
+        GdkEvent *event;
 
-    return TRUE;
+        gesture  = GTK_GESTURE(click_gesture);
+        sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture));
+        event    = gtk_gesture_get_last_event(gesture, sequence);
+
+        tm_popup_context_menu(info, event);
+    }
 }
 
-GtkWidget *balsa_toolbar_new(BalsaToolbarModel * model,
-                             GActionMap        * action_map)
+static gboolean
+tm_key_pressed(GtkEventControllerKey *controller,
+               guint                  keyval,
+               guint                  keycode,
+               GdkModifierType        state,
+               gpointer               user_data)
+{
+    toolbar_info *info = user_data;
+
+    if (keyval == GDK_KEY_F10 && (state & GDK_SHIFT_MASK) != 0) {
+        tm_popup_context_menu(info, NULL);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+
+/*
+ * Create a toolbar
+ */
+GtkWidget *
+balsa_toolbar_new(BalsaToolbarModel * model,
+                  GActionMap        * action_map)
 {
     GtkWidget *toolbar;
     toolbar_info *info;
+    GtkGesture *gesture;
+    GtkEventController *key_controller;
 
-    toolbar = gtk_toolbar_new();
+    toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
     g_object_set_data_full(G_OBJECT(toolbar), BALSA_TOOLBAR_ACTION_MAP,
                            g_object_ref(action_map),
                            (GDestroyNotify) g_object_unref);
@@ -758,13 +820,16 @@ GtkWidget *balsa_toolbar_new(BalsaToolbarModel * 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);
+    g_object_weak_ref(G_OBJECT(toolbar), (GWeakNotify) tm_toolbar_weak_notify, info);
 
-    g_signal_connect(toolbar, "popup-context-menu",
-                     G_CALLBACK(tm_popup_context_menu_cb), info);
+    gesture = gtk_gesture_click_new();
+    gtk_widget_add_controller(toolbar, GTK_EVENT_CONTROLLER(gesture));
+    gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), GDK_BUTTON_SECONDARY);
+    g_signal_connect(gesture, "pressed", G_CALLBACK(tm_gesture_pressed), info);
 
-    gtk_widget_show_all(toolbar);
+    key_controller = gtk_event_controller_key_new();
+    gtk_widget_add_controller(toolbar, GTK_EVENT_CONTROLLER(key_controller));
+    g_signal_connect(key_controller, "key-pressed", G_CALLBACK(tm_key_pressed), info);
 
     return toolbar;
 }
diff --git a/src/toolbar-factory.h b/src/toolbar-factory.h
index a464f1360..699ce2241 100644
--- a/src/toolbar-factory.h
+++ b/src/toolbar-factory.h
@@ -23,6 +23,26 @@
 #include <glib-object.h>
 #include "toolbar-prefs.h"
 
+/* Copied from Gtk 3 GtkToolbarStyle
+ *
+ * BalsaToolbarStyle:
+ * BALSA_TOOLBAR_ICONS: Buttons display only icons in the toolbar.
+ * BALSA_TOOLBAR_TEXT: Buttons display only text labels in the toolbar.
+ * BALSA_TOOLBAR_BOTH: Buttons display text and icons in the toolbar.
+ * BALSA_TOOLBAR_BOTH_HORIZ: Buttons display icons and text alongside each
+ *  other, rather than vertically stacked
+ *
+ * Used to customize the appearance of a BalsaToolbar. Note that
+ * setting the toolbar style overrides the user’s preferences
+ * for the default toolbar style.
+ */
+typedef enum {
+    BALSA_TOOLBAR_ICONS,
+    BALSA_TOOLBAR_TEXT,
+    BALSA_TOOLBAR_BOTH,
+    BALSA_TOOLBAR_BOTH_HORIZ
+} BalsaToolbarStyle;
+
 #define BALSA_TYPE_TOOLBAR_MODEL balsa_toolbar_model_get_type()
 
 G_DECLARE_FINAL_TYPE(BalsaToolbarModel,
@@ -41,8 +61,8 @@ extern button_data toolbar_buttons[];
 extern const int toolbar_button_count;
 
 typedef struct {
-    gchar *action;
-    gchar *icon;
+    char *action;
+    char *icon;
 } BalsaToolbarEntry;
 
 void update_all_toolbars(void);


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