]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/gtkdlg.c
Widen the GTK askalg() message box.
[PuTTY.git] / unix / gtkdlg.c
index c044c0623347d1435e641edcb45a28b358501514..573111aeede967676f5f2d6c4a86c87c43226046 100644 (file)
@@ -6,26 +6,30 @@
 #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
+
+#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>
 #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"
@@ -1035,10 +1039,12 @@ void dlg_beep(void *dlg)
     gdk_beep();
 }
 
+#if !GTK_CHECK_VERSION(3,0,0)
 static void errmsg_button_clicked(GtkButton *button, gpointer data)
 {
     gtk_widget_destroy(GTK_WIDGET(data));
 }
+#endif
 
 static void set_transient_window_pos(GtkWidget *parent, GtkWidget *child)
 {
@@ -1074,11 +1080,23 @@ static void set_transient_window_pos(GtkWidget *parent, GtkWidget *child)
 void dlg_error_msg(void *dlg, const char *msg)
 {
     struct dlgparam *dp = (struct dlgparam *)dlg;
-    GtkWidget *window, *hbox, *text, *ok;
+    GtkWidget *window;
+
+#if GTK_CHECK_VERSION(3,0,0)
+    window = gtk_message_dialog_new(GTK_WINDOW(dp->window),
+                                    (GTK_DIALOG_MODAL |
+                                     GTK_DIALOG_DESTROY_WITH_PARENT),
+                                    GTK_MESSAGE_ERROR,
+                                    GTK_BUTTONS_CLOSE,
+                                    "%s", msg);
+    gtk_dialog_run(GTK_DIALOG(window));
+    gtk_widget_destroy(window);
+#else
+    GtkWidget *hbox, *text, *ok;
 
     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))),
@@ -1102,6 +1120,8 @@ void dlg_error_msg(void *dlg, const char *msg)
     set_transient_window_pos(dp->window, window);
     gtk_widget_show(window);
     gtk_main();
+#endif
+
     post_main();
 }
 
@@ -2274,6 +2294,9 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
                 */
                g_signal_connect(G_OBJECT(w), "changed",
                                 G_CALLBACK(droplist_selchange), dp);
+
+                g_signal_connect(G_OBJECT(w), "focus_in_event",
+                                 G_CALLBACK(widget_focus), dp);
 #endif
             } else {
 #if !GTK_CHECK_VERSION(2,0,0)
@@ -2380,6 +2403,8 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
                uc->treeview = w;
                 g_signal_connect(G_OBJECT(w), "row-activated",
                                  G_CALLBACK(listbox_doubleclick), dp);
+                g_signal_connect(G_OBJECT(w), "focus_in_event",
+                                 G_CALLBACK(widget_focus), dp);
                g_signal_connect(G_OBJECT(sel), "changed",
                                 G_CALLBACK(listbox_selchange), dp);
 
@@ -2409,7 +2434,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
                             g_object_set(G_OBJECT(cellrend),
                                          "ellipsize", PANGO_ELLIPSIZE_END,
                                          "ellipsize-set", TRUE,
-                                         NULL);
+                                         (const char *)NULL);
                         }
                        column = gtk_tree_view_column_new_with_attributes
                            ("heading", cellrend, "text", i+1, (char *)NULL);
@@ -2448,7 +2473,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
 #endif
 
                shortcut_add(scs, label, ctrl->listbox.shortcut,
-                            SHORTCUT_FOCUS, w);
+                            SHORTCUT_UCTRL, uc);
 
                container = columns_new(4);
                if (ctrl->listbox.percentwidth == 100) {
@@ -2514,7 +2539,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
              */
             uc->text = w = gtk_label_new(uc->ctrl->generic.label);
 #endif
-            gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.0);
+            align_label_left(GTK_LABEL(w));
             gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
             break;
         }
@@ -2860,7 +2885,7 @@ void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
 
     scs->sc[chr].action = action;
 
-    if (action == SHORTCUT_FOCUS) {
+    if (action == SHORTCUT_FOCUS || action == SHORTCUT_TREE) {
        scs->sc[chr].uc = NULL;
        scs->sc[chr].widget = (GtkWidget *)ptr;
     } else {
@@ -2904,78 +2929,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.)
-     */
-
-    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);
-    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)
 {
@@ -3032,7 +2985,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);
@@ -3042,9 +2995,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);
@@ -3098,7 +3049,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 */
@@ -3369,7 +3320,7 @@ int messagebox(GtkWidget *parentwin, const char *title, const char *msg,
     union control *c;
     struct dlgparam dp;
     struct Shortcuts scs;
-    int index, ncols;
+    int index, ncols, min_type;
     va_list ap;
 
     dlg_init(&dp);
@@ -3380,13 +3331,23 @@ int messagebox(GtkWidget *parentwin, const char *title, const char *msg,
 
     ctrlbox = ctrl_new_box();
 
+    /*
+     * Preliminary pass over the va_list, to count up the number of
+     * buttons and find out what kinds there are.
+     */
     ncols = 0;
     va_start(ap, minwid);
+    min_type = +1;
     while (va_arg(ap, char *) != NULL) {
-       ncols++;
+        int type;
+
        (void) va_arg(ap, int);        /* shortcut */
-       (void) va_arg(ap, int);        /* normal/default/cancel */
+       type = va_arg(ap, int);        /* normal/default/cancel */
        (void) va_arg(ap, int);        /* end value */
+
+       ncols++;
+        if (min_type > type)
+            min_type = type;
     }
     va_end(ap);
 
@@ -3411,7 +3372,17 @@ int messagebox(GtkWidget *parentwin, const char *title, const char *msg,
        c->generic.column = index++;
        if (type > 0)
            c->button.isdefault = TRUE;
-       else if (type < 0)
+
+        /* We always arrange that _some_ button is labelled as
+         * 'iscancel', so that pressing Escape will always cause
+         * win_key_press to do something. The button we choose is
+         * whichever has the smallest type value: this means that real
+         * cancel buttons (labelled -1) will be picked if one is
+         * there, or in cases where the options are yes/no (1,0) then
+         * no will be picked, and if there's only one option (a box
+         * that really is just showing a _message_ and not even asking
+         * a question) then that will be picked. */
+       if (type == min_type)
            c->button.iscancel = TRUE;
     }
     va_end(ap);
@@ -3419,17 +3390,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;
@@ -3460,13 +3429,6 @@ int messagebox(GtkWidget *parentwin, const char *title, const char *msg,
     return dp.retval;
 }
 
-int string_width(const char *text)
-{
-    int ret;
-    get_label_text_dimensions(text, &ret, NULL);
-    return ret;
-}
-
 int reallyclose(void *frontend)
 {
     char *title = dupcat(appname, " Exit Confirmation", NULL);
@@ -3558,7 +3520,8 @@ int askalg(void *frontend, const char *algtype, const char *algname,
     text = dupprintf(msg, algtype, algname);
     ret = messagebox(GTK_WIDGET(get_window(frontend)),
                     "PuTTY Security Alert", text,
-                    string_width("Continue with connection?"),
+                    string_width("Reasonably long line of text as a width"
+                                  " template"),
                     "Yes", 'y', 0, 1,
                     "No", 'n', 0, 0,
                     NULL);
@@ -3623,6 +3586,15 @@ static void about_close_clicked(GtkButton *button, gpointer data)
     aboutbox = NULL;
 }
 
+static void about_key_press(GtkWidget *widget, GdkEventKey *event,
+                            gpointer data)
+{
+    if (event->keyval == GDK_KEY_Escape && aboutbox) {
+        gtk_widget_destroy(aboutbox);
+        aboutbox = NULL;
+    }
+}
+
 static void licence_clicked(GtkButton *button, gpointer data)
 {
     char *title;
@@ -3669,6 +3641,7 @@ static void licence_clicked(GtkButton *button, gpointer data)
 void about_box(void *window)
 {
     GtkWidget *w;
+    GtkBox *action_area;
     char *title;
 
     if (aboutbox) {
@@ -3676,7 +3649,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);
@@ -3685,38 +3658,34 @@ 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);
 
+    g_signal_connect(G_OBJECT(aboutbox), "key_press_event",
+                     G_CALLBACK(about_key_press), NULL);
+
     set_transient_window_pos(GTK_WIDGET(window), aboutbox);
     gtk_window_set_transient_for(GTK_WINDOW(aboutbox),
                                 GTK_WINDOW(window));
@@ -3890,12 +3859,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);
@@ -3903,9 +3872,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;