From c14f0e02cce022c7bee77935238a4b34f3c3a261 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 27 Nov 2022 11:14:23 +0000 Subject: [PATCH] Stop selectable GTK message boxes clobbering PRIMARY. I noticed today that when GTK PuTTY puts up a message box such as a host key dialog, which calls our create_message_box function with selectable=true (so that the host key fingerprint can be conveniently copy-pasted), a side effect is to take the X11 PRIMARY selection away from whoever previously had it, even though the message box isn't actually selecting anything right now. I don't fully understand what's going on, but it apparently has something to do with 'select on focus' behaviour, in which tabbing into a selectable text control automatically selects its entire contents. That makes sense for edit boxes, but not really for this kind of thing. Unfortunately, GTK apparently has no per-widget configuration to turn that off. (The closest I found is not even per _application_: it lives in GtkSettings, whose documentation says that it's general across all GTK apps run by a user!) So instead I work around it by moving the gtk_label_set_selectable call to after the focus of the new window has already been sorted out. Ugly, but it seems to work. --- unix/dialog.c | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/unix/dialog.c b/unix/dialog.c index 95a08f86..a2296c4d 100644 --- a/unix/dialog.c +++ b/unix/dialog.c @@ -3446,26 +3446,6 @@ static GtkWidget *create_message_box_general( dp->retval = 0; dp->window = window; - if (selectable) { -#if GTK_CHECK_VERSION(2,0,0) - struct uctrl *uc = dlg_find_byctrl(dp, textctrl); - gtk_label_set_selectable(GTK_LABEL(uc->text), true); - - /* - * GTK selectable labels have a habit of selecting their - * entire contents when they gain focus. It's ugly to have - * text in a message box start up all selected, so we suppress - * this by manually selecting none of it - but we must do this - * when the widget _already has_ focus, otherwise our work - * will be undone when it gains it shortly. - */ - gtk_widget_grab_focus(uc->text); - gtk_label_select_region(GTK_LABEL(uc->text), 0, 0); -#else - (void)textctrl; /* placate warning */ -#endif - } - if (parentwin) { set_transient_window_pos(parentwin, window); gtk_window_set_transient_for(GTK_WINDOW(window), @@ -3476,6 +3456,34 @@ static GtkWidget *create_message_box_general( gtk_widget_show(window); gtk_window_set_focus(GTK_WINDOW(window), NULL); +#if GTK_CHECK_VERSION(2,0,0) + if (selectable) { + /* + * GTK selectable labels have a habit of selecting their + * entire contents when they gain focus. As far as I can see, + * an individual GtkLabel has no way to turn this off - source + * diving suggests that the only configurable option for it is + * "gtk-label-select-on-focus" in the cross-application + * GtkSettings, and there's no per-label or even + * per-application override. + * + * It's ugly to have text in a message box start up all + * selected, and also it interferes with any PRIMARY selection + * you might already have had. So for this purpose we'd prefer + * that the text doesn't _start off_ selected, but it should + * be selectable later. + * + * So we make the label selectable _now_, after the widget is + * shown and the focus has already gone wherever it's going. + */ + struct uctrl *uc = dlg_find_byctrl(dp, textctrl); + gtk_label_select_region(GTK_LABEL(uc->text), 0, 0); + gtk_label_set_selectable(GTK_LABEL(uc->text), true); + } +#else + (void)textctrl; /* placate warning */ +#endif + #if !GTK_CHECK_VERSION(2,0,0) dp->currtreeitem = NULL; dp->treeitems = NULL;