]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Stop using GtkDialog (for most purposes) in GTK 3!
authorSimon Tatham <anakin@pobox.com>
Mon, 31 Aug 2015 14:45:18 +0000 (15:45 +0100)
committerSimon Tatham <anakin@pobox.com>
Mon, 31 Aug 2015 14:45:18 +0000 (15:45 +0100)
They've now deprecated gtk_dialog_get_action_area, because they really
want a dialog box's action area to be filled with nothing but buttons
controlled by GTK which end the dialog with a response code. But we're
accustomed to putting all sorts of other things in our action area -
non-buttons, buttons that don't end the dialog, and sub-widgets that
do layout - and so I think it's no longer sensible to be trying to
coerce our use cases into GtkDialog.

Hence, I'm introducing a set of wrapper functions which equivocate
between a GtkDialog for GTK1 and GTK2, and a GtkWindow with a vbox in
it for GTK3, and I'll lay out the action area by hand.

(Not everything has sensible layout and margins in the new GTK3 system
yet, but I can sort that out later.)

Because the new functions are needed by gtkask.c, which doesn't link
against gtkdlg.c or include putty.h, I've put them in a new source
file and header file pair gtkmisc.[ch] which is common to gtkask and
the main GTK edifice.

Recipe
unix/gtkask.c
unix/gtkdlg.c
unix/gtkmisc.c [new file with mode: 0644]
unix/gtkmisc.h [new file with mode: 0644]

diff --git a/Recipe b/Recipe
index 6552fc617a25b0a8123810ff82c410bd8d5e31bf..89609df68c4d3313c6c431c1df190b71edc0327d 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -207,7 +207,7 @@ GUITERM  = TERMINAL window windlg winctrls sizetip winucs winprint
 
 # Same thing on Unix.
 UXTERM   = TERMINAL uxcfg sercfg uxucs uxprint timing callback miscucs
-GTKTERM  = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols xkeysym
+GTKTERM  = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym
 OSXTERM  = UXTERM osxwin osxdlg osxctrls
 
 # Non-SSH back ends (putty, puttytel, plink).
@@ -306,7 +306,7 @@ psftp    : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
 pageant  : [X] uxpgnt uxagentc pageant sshrsa sshpubk sshdes sshbn sshmd5
         + version tree234 misc sshaes sshsha sshdss sshsh256 sshsh512 sshecc
         + conf uxsignal nocproxy nogss be_none x11fwd ux_x11 uxcons gtkask
-        + UXMISC
+        + gtkmisc UXMISC
 
 PuTTY    : [MX] osxmain OSXTERM OSXMISC CHARSET U_BE_ALL NONSSH UXSSH
          + ux_x11 uxpty uxsignal testback putty.icns info.plist
index b58cc42ba459f864caa423d02043c53c27c6e944..8d984a3528e8194840fd6428ab62fbe941af01fa 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "gtkfont.h"
 #include "gtkcompat.h"
+#include "gtkmisc.h"
 
 #include "misc.h"
 
@@ -289,6 +290,7 @@ static const char *gtk_askpass_setup(struct askpass_ctx *ctx,
                                      const char *prompt_text)
 {
     int i;
+    GtkBox *action_area;
 
     ctx->passlen = 0;
     ctx->passsize = 2048;
@@ -297,13 +299,13 @@ static const char *gtk_askpass_setup(struct askpass_ctx *ctx,
     /*
      * Create widgets.
      */
-    ctx->dialog = gtk_dialog_new();
+    ctx->dialog = our_dialog_new();
     gtk_window_set_title(GTK_WINDOW(ctx->dialog), window_title);
+    gtk_window_set_position(GTK_WINDOW(ctx->dialog), GTK_WIN_POS_CENTER);
     ctx->promptlabel = gtk_label_new(prompt_text);
     gtk_label_set_line_wrap(GTK_LABEL(ctx->promptlabel), TRUE);
-    gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area
-                                    (GTK_DIALOG(ctx->dialog))),
-                      ctx->promptlabel);
+    our_dialog_add_to_content_area(GTK_WINDOW(ctx->dialog),
+                                   ctx->promptlabel, TRUE, TRUE, 0);
 #if GTK_CHECK_VERSION(2,0,0)
     ctx->imc = gtk_im_multicontext_new();
 #endif
@@ -319,6 +321,9 @@ static const char *gtk_askpass_setup(struct askpass_ctx *ctx,
             return "unable to allocate colours";
     }
 #endif
+
+    action_area = our_dialog_make_action_hbox(GTK_WINDOW(ctx->dialog));
+
     for (i = 0; i < N_DRAWING_AREAS; i++) {
         ctx->drawingareas[i].area = gtk_drawing_area_new();
 #ifndef DRAW_DEFAULT_CAIRO
@@ -330,9 +335,8 @@ static const char *gtk_askpass_setup(struct askpass_ctx *ctx,
          * context-sensitive way, like measuring the size of some
          * piece of template text. */
         gtk_widget_set_size_request(ctx->drawingareas[i].area, 32, 32);
-        gtk_container_add(GTK_CONTAINER(gtk_dialog_get_action_area
-                                        (GTK_DIALOG(ctx->dialog))),
-                          ctx->drawingareas[i].area);
+        gtk_box_pack_end(action_area, ctx->drawingareas[i].area,
+                         TRUE, TRUE, 5);
         g_signal_connect(G_OBJECT(ctx->drawingareas[i].area),
                          "configure_event",
                          G_CALLBACK(configure_area),
index 6c608ab7a34fb4cf76f9b61c57094ba5ddc425b9..47a8fae12edc7d219c7cde84b117bd755103ff85 100644 (file)
 #include <gdk/gdkkeysyms.h>
 #endif
 
+#define MAY_REFER_TO_GTK_IN_HEADERS
+
 #include "putty.h"
 #include "gtkcompat.h"
 #include "gtkcols.h"
 #include "gtkfont.h"
+#include "gtkmisc.h"
 
 #ifndef NOT_X_WINDOWS
 #include <gdk/gdkx.h>
@@ -2930,84 +2933,6 @@ int get_listitemheight(GtkWidget *w)
 #endif
 }
 
-void set_dialog_action_area(GtkDialog *dlg, GtkWidget *w)
-{
-#if !GTK_CHECK_VERSION(2,0,0)
-
-    /*
-     * In GTK 1, laying out the buttons at the bottom of the
-     * configuration box is nice and easy, because a GtkDialog's
-     * action_area is a GtkHBox which stretches to cover the full
-     * width of the dialog. So we just put our Columns widget
-     * straight into that hbox, and it ends up just where we want
-     * it.
-     */
-    gtk_box_pack_start(GTK_BOX(dlg->action_area), w, TRUE, TRUE, 0);
-
-#else
-    /*
-     * In GTK 2, the action area is now a GtkHButtonBox and its
-     * layout behaviour seems to be different: it doesn't stretch
-     * to cover the full width of the window, but instead finds its
-     * own preferred width and right-aligns that within the window.
-     * This isn't what we want, because we have both left-aligned
-     * and right-aligned buttons coming out of the above call to
-     * layout_ctrls(), and right-aligning the whole thing will
-     * result in the former being centred and looking weird.
-     *
-     * So instead we abandon the dialog's action area completely:
-     * we gtk_widget_hide() it in the below code, and we also call
-     * gtk_dialog_set_has_separator() to remove the separator above
-     * it. We then insert our own action area into the end of the
-     * dialog's main vbox, and add our own separator above that.
-     *
-     * (Ideally, if we were a native GTK app, we would use the
-     * GtkHButtonBox's _own_ innate ability to support one set of
-     * buttons being right-aligned and one left-aligned. But to do
-     * that here, we would have to either (a) pick apart our cross-
-     * platform layout structures and treat them specially for this
-     * particular set of controls, which would be painful, or else
-     * (b) develop a special and simpler cross-platform
-     * representation for these particular controls, and introduce
-     * special-case code into all the _other_ platforms to handle
-     * it. Neither appeals. Therefore, I regretfully discard the
-     * GTKHButtonBox and go it alone.)
-     */
-
-#if GTK_CHECK_VERSION(3,0,0)
-    gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(dlg)),
-                     w, FALSE, TRUE, 0);
-#else
-    GtkWidget *align;
-    align = gtk_alignment_new(0, 0, 1, 1);
-    gtk_container_add(GTK_CONTAINER(align), w);
-    /*
-     * The purpose of this GtkAlignment is to provide padding
-     * around the buttons. The padding we use is twice the padding
-     * used in our GtkColumns, because we nest two GtkColumns most
-     * of the time (one separating the tree view from the main
-     * controls, and another for the main controls themselves).
-     */
-#if GTK_CHECK_VERSION(2,4,0)
-    gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 8, 8);
-#endif
-    gtk_widget_show(align);
-    gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(dlg)),
-                     align, FALSE, TRUE, 0);
-#endif
-
-    w = gtk_hseparator_new();
-    gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(dlg)),
-                     w, FALSE, TRUE, 0);
-    gtk_widget_show(w);
-    gtk_widget_hide(gtk_dialog_get_action_area(dlg));
-#if !GTK_CHECK_VERSION(3,0,0)
-    /* This cosmetic property is withdrawn in GTK 3's GtkDialog */
-    g_object_set(G_OBJECT(dlg), "has-separator", TRUE, (const char *)NULL);
-#endif
-#endif
-}
-
 #if GTK_CHECK_VERSION(2,0,0)
 void initial_treeview_collapse(struct dlgparam *dp, GtkWidget *tree)
 {
@@ -3064,7 +2989,7 @@ int do_config_box(const char *title, Conf *conf, int midsession,
        scs.sc[index].action = SHORTCUT_EMPTY;
     }
 
-    window = gtk_dialog_new();
+    window = our_dialog_new();
 
     ctrlbox = ctrl_new_box();
     protocol = conf_get_int(conf, CONF_protocol);
@@ -3074,9 +2999,7 @@ int do_config_box(const char *title, Conf *conf, int midsession,
 
     gtk_window_set_title(GTK_WINDOW(window), title);
     hbox = gtk_hbox_new(FALSE, 4);
-    gtk_box_pack_start
-        (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(window))),
-         hbox, TRUE, TRUE, 0);
+    our_dialog_add_to_content_area(GTK_WINDOW(window), hbox, TRUE, TRUE, 0);
     gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
     gtk_widget_show(hbox);
     vbox = gtk_vbox_new(FALSE, 4);
@@ -3130,7 +3053,7 @@ int do_config_box(const char *title, Conf *conf, int midsession,
        if (!*s->pathname) {
            w = layout_ctrls(&dp, &scs, s, GTK_WINDOW(window));
 
-           set_dialog_action_area(GTK_DIALOG(window), w);
+           our_dialog_set_action_area(GTK_WINDOW(window), w);
        } else {
            int j = path ? ctrl_path_compare(s->pathname, path) : 0;
            if (j != INT_MAX) {        /* add to treeview, start new panel */
@@ -3451,17 +3374,15 @@ int messagebox(GtkWidget *parentwin, const char *title, const char *msg,
     s1 = ctrl_getset(ctrlbox, "x", "", "");
     ctrl_text(s1, msg, HELPCTX(no_help));
 
-    window = gtk_dialog_new();
+    window = our_dialog_new();
     gtk_window_set_title(GTK_WINDOW(window), title);
     w0 = layout_ctrls(&dp, &scs, s0, GTK_WINDOW(window));
-    set_dialog_action_area(GTK_DIALOG(window), w0);
+    our_dialog_set_action_area(GTK_WINDOW(window), w0);
     gtk_widget_show(w0);
     w1 = layout_ctrls(&dp, &scs, s1, GTK_WINDOW(window));
     gtk_container_set_border_width(GTK_CONTAINER(w1), 10);
     gtk_widget_set_size_request(w1, minwid+20, -1);
-    gtk_box_pack_start
-        (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(window))),
-         w1, TRUE, TRUE, 0);
+    our_dialog_add_to_content_area(GTK_WINDOW(window), w1, TRUE, TRUE, 0);
     gtk_widget_show(w1);
 
     dp.shortcuts = &scs;
@@ -3701,6 +3622,7 @@ static void licence_clicked(GtkButton *button, gpointer data)
 void about_box(void *window)
 {
     GtkWidget *w;
+    GtkBox *action_area;
     char *title;
 
     if (aboutbox) {
@@ -3708,7 +3630,7 @@ void about_box(void *window)
        return;
     }
 
-    aboutbox = gtk_dialog_new();
+    aboutbox = our_dialog_new();
     gtk_container_set_border_width(GTK_CONTAINER(aboutbox), 10);
     title = dupcat("About ", appname, NULL);
     gtk_window_set_title(GTK_WINDOW(aboutbox), title);
@@ -3717,36 +3639,29 @@ void about_box(void *window)
     w = gtk_button_new_with_label("Close");
     gtk_widget_set_can_default(w, TRUE);
     gtk_window_set_default(GTK_WINDOW(aboutbox), w);
-    gtk_box_pack_end(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(aboutbox))),
-                    w, FALSE, FALSE, 0);
+    action_area = our_dialog_make_action_hbox(GTK_WINDOW(aboutbox));
+    gtk_box_pack_end(action_area, w, FALSE, FALSE, 0);
     g_signal_connect(G_OBJECT(w), "clicked",
                      G_CALLBACK(about_close_clicked), NULL);
     gtk_widget_show(w);
 
     w = gtk_button_new_with_label("View Licence");
     gtk_widget_set_can_default(w, TRUE);
-    gtk_box_pack_end(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(aboutbox))),
-                    w, FALSE, FALSE, 0);
+    gtk_box_pack_end(action_area, w, FALSE, FALSE, 0);
     g_signal_connect(G_OBJECT(w), "clicked",
                      G_CALLBACK(licence_clicked), NULL);
     gtk_widget_show(w);
 
     w = gtk_label_new(appname);
-    gtk_box_pack_start
-        (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(aboutbox))),
-         w, FALSE, FALSE, 0);
+    our_dialog_add_to_content_area(GTK_WINDOW(aboutbox), w, FALSE, FALSE, 0);
     gtk_widget_show(w);
 
     w = gtk_label_new(ver);
-    gtk_box_pack_start
-        (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(aboutbox))),
-         w, FALSE, FALSE, 5);
+    our_dialog_add_to_content_area(GTK_WINDOW(aboutbox), w, FALSE, FALSE, 5);
     gtk_widget_show(w);
 
     w = gtk_label_new("Copyright 1997-2015 Simon Tatham. All rights reserved");
-    gtk_box_pack_start
-        (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(aboutbox))),
-         w, FALSE, FALSE, 5);
+    our_dialog_add_to_content_area(GTK_WINDOW(aboutbox), w, FALSE, FALSE, 5);
     gtk_widget_show(w);
 
     set_transient_window_pos(GTK_WIDGET(window), aboutbox);
@@ -3922,12 +3837,12 @@ void showeventlog(void *estuff, void *parentwin)
     c->listbox.percentages[1] = 10;
     c->listbox.percentages[2] = 65;
 
-    es->window = window = gtk_dialog_new();
+    es->window = window = our_dialog_new();
     title = dupcat(appname, " Event Log", NULL);
     gtk_window_set_title(GTK_WINDOW(window), title);
     sfree(title);
     w0 = layout_ctrls(&es->dp, &es->scs, s0, GTK_WINDOW(window));
-    set_dialog_action_area(GTK_DIALOG(window), w0);
+    our_dialog_set_action_area(GTK_WINDOW(window), w0);
     gtk_widget_show(w0);
     w1 = layout_ctrls(&es->dp, &es->scs, s1, GTK_WINDOW(window));
     gtk_container_set_border_width(GTK_CONTAINER(w1), 10);
@@ -3935,9 +3850,7 @@ void showeventlog(void *estuff, void *parentwin)
                                 ("LINE OF TEXT GIVING WIDTH OF EVENT LOG IS "
                                  "QUITE LONG 'COS SSH LOG ENTRIES ARE WIDE"),
                                 -1);
-    gtk_box_pack_start
-        (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(window))),
-         w1, TRUE, TRUE, 0);
+    our_dialog_add_to_content_area(GTK_WINDOW(window), w1, TRUE, TRUE, 0);
     gtk_widget_show(w1);
 
     es->dp.data = es;
diff --git a/unix/gtkmisc.c b/unix/gtkmisc.c
new file mode 100644 (file)
index 0000000..d6cfccc
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Miscellaneous GTK helper functions.
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <gtk/gtk.h>
+#if !GTK_CHECK_VERSION(3,0,0)
+#include <gdk/gdkkeysyms.h>
+#endif
+
+#include "putty.h"
+#include "gtkcompat.h"
+
+/* ----------------------------------------------------------------------
+ * Functions to arrange controls in a basically dialog-like window.
+ *
+ * The best method for doing this has varied wildly with versions of
+ * GTK, hence the set of wrapper functions here.
+ *
+ * In GTK 1, a GtkDialog has an 'action_area' at the bottom, which is
+ * a GtkHBox which stretches to cover the full width of the dialog. So
+ * we can either add buttons or other widgets to that box directly, or
+ * alternatively we can fill the hbox with some layout class of our
+ * own such as a Columns widget.
+ *
+ * In GTK 2, the action area has become a GtkHButtonBox, and its
+ * layout behaviour seems to be different and not what we want. So
+ * instead we abandon the dialog's action area completely: we
+ * gtk_widget_hide() it in the below code, and we also call
+ * gtk_dialog_set_has_separator() to remove the separator above it. We
+ * then insert our own action area into the end of the dialog's main
+ * vbox, and add our own separator above that.
+ *
+ * In GTK 3, we typically don't even want to use GtkDialog at all,
+ * because GTK 3 has become a lot more restrictive about what you can
+ * sensibly use GtkDialog for - it deprecates direct access to the
+ * action area in favour of making you provide nothing but
+ * dialog-ending buttons in the form of (text, response code) pairs,
+ * so you can't put any other kind of control in there, or fiddle with
+ * alignment and positioning, or even have a button that _doesn't_ end
+ * the dialog (e.g. 'View Licence' in our About box). So instead of
+ * GtkDialog, we use a straight-up GtkWindow and have it contain a
+ * vbox as its (unique) child widget; and we implement the action area
+ * by adding a separator and another widget at the bottom of that
+ * vbox.
+ */
+
+GtkWidget *our_dialog_new(void)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+    /*
+     * See comment in our_dialog_set_action_area(): in GTK 3, we use
+     * GtkWindow in place of GtkDialog for most purposes.
+     */
+    GtkWidget *w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
+    gtk_container_add(GTK_CONTAINER(w), vbox);
+    gtk_widget_show(vbox);
+    return w;
+#else
+    return gtk_dialog_new();
+#endif
+}
+
+void our_dialog_set_action_area(GtkWindow *dlg, GtkWidget *w)
+{
+#if !GTK_CHECK_VERSION(2,0,0)
+
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area),
+                       w, TRUE, TRUE, 0);
+
+#elif !GTK_CHECK_VERSION(3,0,0)
+
+    GtkWidget *align;
+    align = gtk_alignment_new(0, 0, 1, 1);
+    gtk_container_add(GTK_CONTAINER(align), w);
+    /*
+     * The purpose of this GtkAlignment is to provide padding
+     * around the buttons. The padding we use is twice the padding
+     * used in our GtkColumns, because we nest two GtkColumns most
+     * of the time (one separating the tree view from the main
+     * controls, and another for the main controls themselves).
+     */
+#if GTK_CHECK_VERSION(2,4,0)
+    gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 8, 8);
+#endif
+    gtk_widget_show(align);
+    gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))),
+                     align, FALSE, TRUE, 0);
+
+    w = gtk_hseparator_new();
+    gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))),
+                     w, FALSE, TRUE, 0);
+    gtk_widget_show(w);
+    gtk_widget_hide(gtk_dialog_get_action_area(GTK_DIALOG(dlg)));
+    g_object_set(G_OBJECT(dlg), "has-separator", TRUE, (const char *)NULL);
+
+#else /* GTK 3 */
+
+    /* GtkWindow is a GtkBin, hence contains exactly one child, which
+     * here we always expect to be a vbox */
+    GtkBox *vbox = GTK_BOX(gtk_bin_get_child(GTK_BIN(dlg)));
+
+    GtkWidget *sep = gtk_hseparator_new();
+    gtk_box_pack_end(vbox, sep, FALSE, TRUE, 0);
+    gtk_widget_show(sep);
+
+    g_object_set(G_OBJECT(w), "margin", 8, (const char *)NULL);
+    gtk_box_pack_end(vbox, w, FALSE, TRUE, 0);
+
+#endif
+}
+
+GtkBox *our_dialog_make_action_hbox(GtkWindow *dlg)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+    our_dialog_set_action_area(dlg, hbox);
+    gtk_widget_show(hbox);
+    return GTK_BOX(hbox);
+#else /* not GTK 3 */
+    return GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dlg)));
+#endif
+}
+
+void our_dialog_add_to_content_area(GtkWindow *dlg, GtkWidget *w,
+                                    gboolean expand, gboolean fill,
+                                    guint padding)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+    /* GtkWindow is a GtkBin, hence contains exactly one child, which
+     * here we always expect to be a vbox */
+    GtkBox *vbox = GTK_BOX(gtk_bin_get_child(GTK_BIN(dlg)));
+
+    gtk_box_pack_start(vbox, w, expand, fill, padding);
+#else
+    gtk_box_pack_start
+        (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))),
+         w, expand, fill, padding);
+#endif
+}
diff --git a/unix/gtkmisc.h b/unix/gtkmisc.h
new file mode 100644 (file)
index 0000000..08daa85
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Miscellaneous helper functions for GTK.
+ */
+
+#ifndef PUTTY_GTKMISC_H
+#define PUTTY_GTKMISC_H
+
+GtkWidget *our_dialog_new(void);
+void our_dialog_add_to_content_area(GtkWindow *dlg, GtkWidget *w,
+                                    gboolean expand, gboolean fill,
+                                    guint padding);
+void our_dialog_set_action_area(GtkWindow *dlg, GtkWidget *w);
+GtkBox *our_dialog_make_action_hbox(GtkWindow *dlg);
+
+#endif /* PUTTY_GTKMISC_H */