]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/gtkdlg.c
Avoid deprecated gtk_misc_set_alignment().
[PuTTY.git] / unix / gtkdlg.c
index b18ef996674396457a8bd523d44e417dee04ee18..8f6641d8e331ab083abeba93abdd45a085be4340 100644 (file)
@@ -6,26 +6,27 @@
 #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"
+#include "gtkcols.h"
+#include "gtkfont.h"
+
 #ifndef NOT_X_WINDOWS
 #include <gdk/gdkx.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #endif
 
-#include "gtkcompat.h"
-
-#include "gtkcols.h"
-#include "gtkfont.h"
-
 #ifdef TESTMODE
 #define PUTTY_DO_GLOBALS              /* actually _define_ globals */
 #endif
 
-#include "putty.h"
 #include "storage.h"
 #include "dialog.h"
 #include "tree234.h"
@@ -83,6 +84,9 @@ struct dlgparam {
 #if !GTK_CHECK_VERSION(2,0,0)
     GtkWidget *currtreeitem, **treeitems;
     int ntreeitems;
+#else
+    int nselparams;
+    struct selparam *selparams;
 #endif
     int retval;
 };
@@ -127,8 +131,13 @@ static gboolean listitem_button_release(GtkWidget *item, GdkEventButton *event,
 #if !GTK_CHECK_VERSION(2,4,0)
 static void menuitem_activate(GtkMenuItem *item, gpointer data);
 #endif
+#if GTK_CHECK_VERSION(3,0,0)
+static void colourchoose_response(GtkDialog *dialog,
+                                  gint response_id, gpointer data);
+#else
 static void coloursel_ok(GtkButton *button, gpointer data);
 static void coloursel_cancel(GtkButton *button, gpointer data);
+#endif
 static void window_destroy(GtkWidget *widget, gpointer data);
 int get_listitemheight(GtkWidget *widget);
 
@@ -1063,6 +1072,15 @@ static void set_transient_window_pos(GtkWidget *parent, GtkWidget *child)
 #endif
 }
 
+void align_label_left(GtkLabel *label)
+{
+#if GTK_CHECK_VERSION(3,16,0)
+    gtk_label_set_xalign(label, 0.0);
+#else
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+#endif
+}
+
 void dlg_error_msg(void *dlg, const char *msg)
 {
     struct dlgparam *dp = (struct dlgparam *)dlg;
@@ -1070,7 +1088,7 @@ void dlg_error_msg(void *dlg, const char *msg)
 
     window = gtk_dialog_new();
     text = gtk_label_new(msg);
-    gtk_misc_set_alignment(GTK_MISC(text), 0.0, 0.0);
+    align_label_left(GTK_LABEL(text));
     hbox = gtk_hbox_new(FALSE, 0);
     gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 20);
     gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(window))),
@@ -1133,20 +1151,36 @@ void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
 {
     struct dlgparam *dp = (struct dlgparam *)dlg;
     struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
-    GtkWidget *okbutton, *cancelbutton;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkWidget *coloursel =
+       gtk_color_chooser_dialog_new("Select a colour",
+                                     GTK_WINDOW(dp->window));
+    gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(coloursel), FALSE);
+#else
+    GtkWidget *okbutton, *cancelbutton;
     GtkWidget *coloursel =
        gtk_color_selection_dialog_new("Select a colour");
     GtkColorSelectionDialog *ccs = GTK_COLOR_SELECTION_DIALOG(coloursel);
     GtkColorSelection *cs = GTK_COLOR_SELECTION
         (gtk_color_selection_dialog_get_color_selection(ccs));
+    gtk_color_selection_set_has_opacity_control(cs, FALSE);
+#endif
 
     dp->coloursel_result.ok = FALSE;
 
     gtk_window_set_modal(GTK_WINDOW(coloursel), TRUE);
-    gtk_color_selection_set_has_opacity_control(cs, FALSE);
 
-#if GTK_CHECK_VERSION(2,0,0)
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+        GdkRGBA rgba;
+        rgba.red = r / 255.0;
+        rgba.green = g / 255.0;
+        rgba.blue = b / 255.0;
+        rgba.alpha = 1.0;              /* fully opaque! */
+        gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(coloursel), &rgba);
+    }
+#elif GTK_CHECK_VERSION(2,0,0)
     {
         GdkColor col;
         col.red = r * 0x0101;
@@ -1165,6 +1199,13 @@ void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
     }
 #endif
 
+    g_object_set_data(G_OBJECT(coloursel), "user-data", (gpointer)uc);
+
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(coloursel), "response",
+                     G_CALLBACK(colourchoose_response), (gpointer)dp);
+#else
+
 #if GTK_CHECK_VERSION(2,0,0)
     g_object_get(G_OBJECT(ccs),
                  "ok-button", &okbutton,
@@ -1178,7 +1219,6 @@ void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
                        (gpointer)coloursel);
     g_object_set_data(G_OBJECT(cancelbutton), "user-data",
                        (gpointer)coloursel);
-    g_object_set_data(G_OBJECT(coloursel), "user-data", (gpointer)uc);
     g_signal_connect(G_OBJECT(okbutton), "clicked",
                      G_CALLBACK(coloursel_ok), (gpointer)dp);
     g_signal_connect(G_OBJECT(cancelbutton), "clicked",
@@ -1189,6 +1229,7 @@ void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
     g_signal_connect_swapped(G_OBJECT(cancelbutton), "clicked",
                              G_CALLBACK(gtk_widget_destroy),
                              (gpointer)coloursel);
+#endif
     gtk_widget_show(coloursel);
 }
 
@@ -1624,16 +1665,43 @@ static void fontsel_ok(GtkButton *button, gpointer data)
 #endif
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+
+static void colourchoose_response(GtkDialog *dialog,
+                                  gint response_id, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    struct uctrl *uc = g_object_get_data(G_OBJECT(dialog), "user-data");
+
+    if (response_id == GTK_RESPONSE_OK) {
+        GdkRGBA rgba;
+        gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &rgba);
+        dp->coloursel_result.r = (int) (255 * rgba.red);
+        dp->coloursel_result.g = (int) (255 * rgba.green);
+        dp->coloursel_result.b = (int) (255 * rgba.blue);
+        dp->coloursel_result.ok = TRUE;
+    } else {
+        dp->coloursel_result.ok = FALSE;
+    }
+
+    uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
+
+    gtk_widget_destroy(GTK_WIDGET(dialog));
+}
+
+#else /* GTK 1/2 coloursel response handlers */
+
 static void coloursel_ok(GtkButton *button, gpointer data)
 {
     struct dlgparam *dp = (struct dlgparam *)data;
     gpointer coloursel = g_object_get_data(G_OBJECT(button), "user-data");
     struct uctrl *uc = g_object_get_data(G_OBJECT(coloursel), "user-data");
-    GtkColorSelection *cs = GTK_COLOR_SELECTION
-        (gtk_color_selection_dialog_get_color_selection
-         (GTK_COLOR_SELECTION_DIALOG(coloursel)));
+
 #if GTK_CHECK_VERSION(2,0,0)
     {
+        GtkColorSelection *cs = GTK_COLOR_SELECTION
+            (gtk_color_selection_dialog_get_color_selection
+             (GTK_COLOR_SELECTION_DIALOG(coloursel)));
         GdkColor col;
         gtk_color_selection_get_current_color(cs, &col);
         dp->coloursel_result.r = col.red / 0x0100;
@@ -1642,6 +1710,9 @@ static void coloursel_ok(GtkButton *button, gpointer data)
     }
 #else
     {
+        GtkColorSelection *cs = GTK_COLOR_SELECTION
+            (gtk_color_selection_dialog_get_color_selection
+             (GTK_COLOR_SELECTION_DIALOG(coloursel)));
         gdouble cvals[4];
         gtk_color_selection_get_color(cs, cvals);
         dp->coloursel_result.r = (int) (255 * cvals[0]);
@@ -1662,6 +1733,8 @@ static void coloursel_cancel(GtkButton *button, gpointer data)
     uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
 }
 
+#endif /* end of coloursel response handlers */
+
 static void filefont_clicked(GtkButton *button, gpointer data)
 {
     struct dlgparam *dp = (struct dlgparam *)data;
@@ -1674,8 +1747,8 @@ static void filefont_clicked(GtkButton *button, gpointer data)
              (uc->ctrl->fileselect.for_writing ?
               GTK_FILE_CHOOSER_ACTION_SAVE :
               GTK_FILE_CHOOSER_ACTION_OPEN),
-             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+             STANDARD_CANCEL_LABEL, GTK_RESPONSE_CANCEL,
+             STANDARD_OPEN_LABEL, GTK_RESPONSE_ACCEPT,
              (const gchar *)NULL);
        gtk_window_set_modal(GTK_WINDOW(filechoose), TRUE);
        g_object_set_data(G_OBJECT(filechoose), "user-data", (gpointer)uc);
@@ -1791,6 +1864,7 @@ static void filefont_clicked(GtkButton *button, gpointer data)
     }
 }
 
+#if !GTK_CHECK_VERSION(3,0,0)
 static void label_sizealloc(GtkWidget *widget, GtkAllocation *alloc,
                            gpointer data)
 {
@@ -1801,6 +1875,7 @@ static void label_sizealloc(GtkWidget *widget, GtkAllocation *alloc,
     gtk_label_set_text(GTK_LABEL(uc->text), uc->ctrl->generic.label);
     g_signal_handler_disconnect(G_OBJECT(uc->text), uc->textsig);
 }
+#endif
 
 /* ----------------------------------------------------------------------
  * This function does the main layout work: it reads a controlset,
@@ -1978,7 +2053,6 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
             break;
           case CTRL_EDITBOX:
            {
-                GtkRequisition req;
                GtkWidget *signalobject;
 
                if (ctrl->editbox.has_list) {
@@ -2023,13 +2097,6 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
                 g_signal_connect(G_OBJECT(signalobject), "focus_out_event",
                                  G_CALLBACK(editbox_lostfocus), dp);
 
-                /*
-                 * Find out the edit box's height, which we'll need
-                 * for vertical centring below (and, in GTK2, size
-                 * tweaking as well).
-                 */
-                gtk_widget_size_request(w, &req);
-
 #if !GTK_CHECK_VERSION(3,0,0)
                /*
                 * Edit boxes, for some strange reason, have a minimum
@@ -2037,7 +2104,11 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
                 * rather the edit boxes acquired their natural width
                 * from the column layout of the rest of the box.
                 */
-                gtk_widget_set_size_request(w, 10, req.height);
+                {
+                    GtkRequisition req;
+                    gtk_widget_size_request(w, &req);
+                    gtk_widget_set_size_request(w, 10, req.height);
+                }
 #else
                 /*
                  * In GTK 3, this is still true, but there's a special
@@ -2068,9 +2139,8 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
                        columns_add(COLUMNS(container), label, 0, 1);
                        columns_force_left_align(COLUMNS(container), label);
                        columns_add(COLUMNS(container), w, 1, 1);
-                       /* Centre the label vertically. */
-                       gtk_widget_set_size_request(label, -1, req.height);
-                       gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+                        columns_force_same_height(COLUMNS(container),
+                                                  label, w);
                    }
                    gtk_widget_show(label);
                    gtk_widget_show(w);
@@ -2084,7 +2154,6 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
           case CTRL_FONTSELECT:
             {
                 GtkWidget *ww;
-                GtkRequisition req;
                 const char *browsebtn =
                     (ctrl->generic.type == CTRL_FILESELECT ?
                      "Browse..." : "Change...");
@@ -2107,8 +2176,15 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
                 }
 
                 uc->entry = ww = gtk_entry_new();
-                gtk_widget_size_request(ww, &req);
-                gtk_widget_set_size_request(ww, 10, req.height);
+#if !GTK_CHECK_VERSION(3,0,0)
+                {
+                    GtkRequisition req;
+                    gtk_widget_size_request(ww, &req);
+                    gtk_widget_set_size_request(ww, 10, req.height);
+                }
+#else
+                gtk_entry_set_width_chars(GTK_ENTRY(ww), 1);
+#endif
                 columns_add(COLUMNS(w), ww, 0, 1);
                 gtk_widget_show(ww);
 
@@ -2116,6 +2192,8 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
                 columns_add(COLUMNS(w), ww, 1, 1);
                 gtk_widget_show(ww);
 
+                columns_force_same_height(COLUMNS(w), uc->entry, uc->button);
+
                 g_signal_connect(G_OBJECT(uc->entry), "key_press_event",
                                  G_CALLBACK(editbox_key), dp);
                uc->entrysig =
@@ -2373,9 +2451,11 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
 
            if (ctrl->generic.label) {
                GtkWidget *label, *container;
-                GtkRequisition req;
 
                label = gtk_label_new(ctrl->generic.label);
+#if GTK_CHECK_VERSION(3,0,0)
+                gtk_label_set_width_chars(GTK_LABEL(label), 3);
+#endif
 
                shortcut_add(scs, label, ctrl->listbox.shortcut,
                             SHORTCUT_FOCUS, w);
@@ -2393,10 +2473,8 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
                    columns_add(COLUMNS(container), label, 0, 1);
                    columns_force_left_align(COLUMNS(container), label);
                    columns_add(COLUMNS(container), w, 1, 1);
-                   /* Centre the label vertically. */
-                   gtk_widget_size_request(w, &req);
-                   gtk_widget_set_size_request(label, -1, req.height);
-                   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+                    columns_force_same_height(COLUMNS(container),
+                                              label, w);
                }
                gtk_widget_show(label);
                gtk_widget_show(w);
@@ -2407,8 +2485,9 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
 
            break;
           case CTRL_TEXT:
+#if !GTK_CHECK_VERSION(3,0,0)
            /*
-            * Wrapping text widgets don't sit well with the GTK
+            * Wrapping text widgets don't sit well with the GTK2
             * layout model, in which widgets state a minimum size
             * and the whole window then adjusts to the smallest
             * size it can sensibly take given its contents. A
@@ -2433,11 +2512,20 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
             * than one line).
             */
             uc->text = w = gtk_label_new("X");
-            gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.0);
-            gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
            uc->textsig =
                 g_signal_connect(G_OBJECT(w), "size-allocate",
                                  G_CALLBACK(label_sizealloc), dp);
+#else
+            /*
+             * In GTK3, this is all fixed, because the main aim of the
+             * new 'height-for-width' geometry management is to make
+             * wrapping labels behave sensibly. So now we can just do
+             * the obvious thing.
+             */
+            uc->text = w = gtk_label_new(uc->ctrl->generic.label);
+#endif
+            align_label_left(GTK_LABEL(w));
+            gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
             break;
         }
 
@@ -2763,14 +2851,8 @@ static void shortcut_highlight(GtkWidget *labelw, int chr)
 
     for (i = 0; currstr[i]; i++)
        if (tolower((unsigned char)currstr[i]) == chr) {
-           GtkRequisition req;
-
            pattern = dupprintf("%*s_", i, "");
-
-           gtk_widget_size_request(GTK_WIDGET(label), &req);
            gtk_label_set_pattern(label, pattern);
-           gtk_widget_set_size_request(GTK_WIDGET(label), -1, req.height);
-
            sfree(pattern);
            break;
        }
@@ -2810,7 +2892,21 @@ int get_listitemheight(GtkWidget *w)
 #else
     int height;
     GtkCellRenderer *cr = gtk_cell_renderer_text_new();
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+        GtkRequisition req;
+        /*
+         * Since none of my list items wraps in this GUI, no
+         * interesting width-for-height behaviour should be happening,
+         * so I don't think it should matter here whether I ask for
+         * the minimum or natural height.
+         */
+        gtk_cell_renderer_get_preferred_size(cr, w, &req, NULL);
+        height = req.height;
+    }
+#else
     gtk_cell_renderer_get_size(cr, w, NULL, NULL, NULL, NULL, &height);
+#endif
     g_object_ref(G_OBJECT(cr));
     g_object_ref_sink(G_OBJECT(cr));
     g_object_unref(G_OBJECT(cr));
@@ -2883,10 +2979,39 @@ void set_dialog_action_area(GtkDialog *dlg, GtkWidget *w)
                      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)
+{
+    /*
+     * Collapse the deeper branches of the treeview into the state we
+     * like them to start off in. See comment below in do_config_box.
+     */
+    int i;
+    for (i = 0; i < dp->nselparams; i++)
+        if (dp->selparams[i].depth >= 2)
+            gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree),
+                                       dp->selparams[i].treepath);
+}
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+void treeview_map_event(GtkWidget *tree, gpointer data)
+{
+    struct dlgparam *dp = (struct dlgparam *)data;
+    GtkAllocation alloc;
+    gtk_widget_get_allocation(tree, &alloc);
+    gtk_widget_set_size_request(tree, alloc.width, -1);
+    initial_treeview_collapse(dp, tree);
+}
+#endif
+
 int do_config_box(const char *title, Conf *conf, int midsession,
                  int protcfginfo)
 {
@@ -3117,33 +3242,43 @@ int do_config_box(const char *title, Conf *conf, int midsession,
     }
 
 #if GTK_CHECK_VERSION(2,0,0)
-    {
-       GtkRequisition req;
-       int i;
+    /*
+     * We want our tree view to come up with all branches at depth 2
+     * or more collapsed. However, if we start off with those branches
+     * collapsed, then the tree view's size request will be calculated
+     * based on the width of the collapsed tree, and then when the
+     * collapsed branches are expanded later, the tree view will
+     * jarringly change size.
+     *
+     * So instead we start with everything expanded; then, once the
+     * tree view has computed its resulting width requirement, we
+     * collapse the relevant rows, but force the width to be the value
+     * we just retrieved. This arranges that the tree view is wide
+     * enough to have all branches expanded without further resizing.
+     */
 
-       /*
-        * We want our tree view to come up with all branches at
-        * depth 2 or more collapsed. However, if we start off
-        * with those branches collapsed, then the tree view's
-        * size request will be calculated based on the width of
-        * the collapsed tree. So instead we start with them all
-        * expanded; then we ask for the current size request,
-        * collapse the relevant rows, and force the width to the
-        * value we just computed. This arranges that the tree
-        * view is wide enough to have all branches expanded
-        * safely.
-        */
+    dp.nselparams = nselparams;
+    dp.selparams = selparams;
 
+#if !GTK_CHECK_VERSION(3,0,0)
+    {
+        /*
+         * In GTK2, we can just do the job right now.
+         */
+       GtkRequisition req;
        gtk_widget_size_request(tree, &req);
-
-       for (i = 0; i < nselparams; i++)
-           if (selparams[i].depth >= 2)
-               gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree),
-                                          selparams[i].treepath);
-
+        initial_treeview_collapse(&dp, tree);
        gtk_widget_set_size_request(tree, req.width, -1);
     }
-#endif
+#else
+    /*
+     * But in GTK3, we have to wait until the widget is about to be
+     * mapped, because the size computation won't have been done yet.
+     */
+    g_signal_connect(G_OBJECT(tree), "map",
+                     G_CALLBACK(treeview_map_event), &dp);
+#endif /* GTK 2 vs 3 */
+#endif /* GTK 2+ vs 1 */
 
 #if GTK_CHECK_VERSION(2,0,0)
     g_signal_connect(G_OBJECT(treeselection), "changed",
@@ -3151,7 +3286,6 @@ int do_config_box(const char *title, Conf *conf, int midsession,
 #else
     dp.ntreeitems = nselparams;
     dp.treeitems = snewn(dp.ntreeitems, GtkWidget *);
-
     for (index = 0; index < nselparams; index++) {
         g_signal_connect(G_OBJECT(selparams[index].treeitem), "select",
                          G_CALLBACK(treeitem_sel),
@@ -3338,11 +3472,9 @@ int messagebox(GtkWidget *parentwin, const char *title, const char *msg,
 
 int string_width(const char *text)
 {
-    GtkWidget *label = gtk_label_new(text);
-    GtkRequisition req;
-    gtk_widget_size_request(label, &req);
-    g_object_ref_sink(G_OBJECT(label));
-    return req.width;
+    int ret;
+    get_label_text_dimensions(text, &ret, NULL);
+    return ret;
 }
 
 int reallyclose(void *frontend)