From 694d5184b72505a6e22f6c359db882789cb9ec7b Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 1 May 2022 08:39:49 +0100 Subject: [PATCH] Permit button-only file selectors. Instead of an edit box together with a Browse button that pops up a sub-dialog, this is _just_ the browse button, only now it has a user-defined title. I'm about to want to use this for loading CA public keys from files. --- dialog.h | 31 +++++++++++ unix/dialog.c | 125 ++++++++++++++++++++++++++++----------------- windows/controls.c | 57 +++++++++++++++------ 3 files changed, 149 insertions(+), 64 deletions(-) diff --git a/dialog.h b/dialog.h index 51d481fa..b85bdcd5 100644 --- a/dialog.h +++ b/dialog.h @@ -369,6 +369,37 @@ struct dlgcontrol { * This value _is_ expected to require freeing. */ char *title; + /* + * Reduce the file selector to just a single browse + * button. + * + * Normally, a file selector is used to set a config + * option that consists of a file name, so that that file + * will be read or written at run time. In that situation, + * it makes sense to have an edit box showing the + * currently selected file name, and a button to change it + * interactively. + * + * But occasionally a file selector is used to load a file + * _during_ configuration. For example, host CA public + * keys are entered directly into the configuration as + * strings, not stored by reference to a filename; but if + * you have one in a file, you want to be able to load it + * during the lifetime of the CA config box rather than + * awkwardly copy-pasting it. So in that case you just + * want a 'pop up a file chooser' button, and when that + * delivers a file name, you'll deal with it there and + * then and write some other thing (like the file's + * contents) into a nearby edit box. + * + * If you set this flag, then you may not call + * dlg_filesel_set on the file selector at all, because it + * doesn't store a filename. And you can only call + * dlg_filesel_get on it in the handler for EVENT_ACTION, + * which is what will be sent to you when the user has + * used it to choose a filename. + */ + bool just_button; } fileselect; struct { /* for CTRL_COLUMNS */ /* In this variant, `label' MUST be NULL. */ diff --git a/unix/dialog.c b/unix/dialog.c index 1d9944c5..bd5d535f 100644 --- a/unix/dialog.c +++ b/unix/dialog.c @@ -74,6 +74,7 @@ struct uctrl { guint entrysig; guint textsig; int nclicks; + const char *textvalue; /* temporary, for button-only file selectors */ }; struct dlgparam { @@ -869,8 +870,10 @@ void dlg_label_change(dlgcontrol *ctrl, dlgparam *dp, char const *text) shortcut_highlight(uc->label, ctrl->editbox.shortcut); break; case CTRL_FILESELECT: - gtk_label_set_text(GTK_LABEL(uc->label), text); - shortcut_highlight(uc->label, ctrl->fileselect.shortcut); + if (uc->label) { + gtk_label_set_text(GTK_LABEL(uc->label), text); + shortcut_highlight(uc->label, ctrl->fileselect.shortcut); + } break; case CTRL_FONTSELECT: gtk_label_set_text(GTK_LABEL(uc->label), text); @@ -901,8 +904,12 @@ Filename *dlg_filesel_get(dlgcontrol *ctrl, dlgparam *dp) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->type == CTRL_FILESELECT); - assert(uc->entry != NULL); - return filename_from_str(gtk_entry_get_text(GTK_ENTRY(uc->entry))); + if (!uc->entry) { + assert(uc->textvalue); + return filename_from_str(uc->textvalue); + } else { + return filename_from_str(gtk_entry_get_text(GTK_ENTRY(uc->entry))); + } } void dlg_fontsel_set(dlgcontrol *ctrl, dlgparam *dp, FontSpec *fs) @@ -1562,15 +1569,27 @@ static void droplist_selchange(GtkComboBox *combo, gpointer data) #endif /* !GTK_CHECK_VERSION(2,4,0) */ +static void filechoose_emit_value(struct dlgparam *dp, struct uctrl *uc, + const char *name) +{ + if (uc->entry) { + gtk_entry_set_text(GTK_ENTRY(uc->entry), name); + } else { + uc->textvalue = name; + uc->ctrl->handler(uc->ctrl, dp, dp->data, EVENT_ACTION); + uc->textvalue = NULL; + } +} + #ifdef USE_GTK_FILE_CHOOSER_DIALOG static void filechoose_response(GtkDialog *dialog, gint response, gpointer data) { - /* struct dlgparam *dp = (struct dlgparam *)data; */ + struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = g_object_get_data(G_OBJECT(dialog), "user-data"); if (response == GTK_RESPONSE_ACCEPT) { gchar *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - gtk_entry_set_text(GTK_ENTRY(uc->entry), name); + filechoose_emit_value(dp, uc, name); g_free(name); } gtk_widget_destroy(GTK_WIDGET(dialog)); @@ -1578,12 +1597,12 @@ static void filechoose_response(GtkDialog *dialog, gint response, #else static void filesel_ok(GtkButton *button, gpointer data) { - /* struct dlgparam *dp = (struct dlgparam *)data; */ + struct dlgparam *dp = (struct dlgparam *)data; gpointer filesel = g_object_get_data(G_OBJECT(button), "user-data"); struct uctrl *uc = g_object_get_data(G_OBJECT(filesel), "user-data"); const char *name = gtk_file_selection_get_filename (GTK_FILE_SELECTION(filesel)); - gtk_entry_set_text(GTK_ENTRY(uc->entry), name); + filechoose_emit_value(dp, uc, name); } #endif @@ -2104,56 +2123,65 @@ GtkWidget *layout_ctrls( case CTRL_FILESELECT: case CTRL_FONTSELECT: { GtkWidget *ww; - const char *browsebtn = - (ctrl->type == CTRL_FILESELECT ? - "Browse..." : "Change..."); - gint percentages[] = { 75, 25 }; - w = columns_new(4); - columns_set_cols(COLUMNS(w), 2, percentages); + if (!ctrl->fileselect.just_button) { + const char *browsebtn = + (ctrl->type == CTRL_FILESELECT ? + "Browse..." : "Change..."); - if (ctrl->label) { - ww = gtk_label_new(ctrl->label); - columns_add(COLUMNS(w), ww, 0, 2); - columns_force_left_align(COLUMNS(w), ww); - gtk_widget_show(ww); - shortcut_add(scs, ww, - (ctrl->type == CTRL_FILESELECT ? - ctrl->fileselect.shortcut : - ctrl->fontselect.shortcut), - SHORTCUT_UCTRL, uc); - uc->label = ww; - } + gint percentages[] = { 75, 25 }; + w = columns_new(4); + columns_set_cols(COLUMNS(w), 2, percentages); - uc->entry = ww = gtk_entry_new(); + if (ctrl->label) { + ww = gtk_label_new(ctrl->label); + columns_add(COLUMNS(w), ww, 0, 2); + columns_force_left_align(COLUMNS(w), ww); + gtk_widget_show(ww); + shortcut_add(scs, ww, + (ctrl->type == CTRL_FILESELECT ? + ctrl->fileselect.shortcut : + ctrl->fontselect.shortcut), + SHORTCUT_UCTRL, uc); + uc->label = ww; + } + + uc->entry = ww = gtk_entry_new(); #if !GTK_CHECK_VERSION(3,0,0) - { - GtkRequisition req; - gtk_widget_size_request(ww, &req); - gtk_widget_set_size_request(ww, 10, req.height); - } + { + 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); + gtk_entry_set_width_chars(GTK_ENTRY(ww), 1); #endif - columns_add(COLUMNS(w), ww, 0, 1); - gtk_widget_show(ww); + columns_add(COLUMNS(w), ww, 0, 1); + gtk_widget_show(ww); - uc->button = ww = gtk_button_new_with_label(browsebtn); - columns_add(COLUMNS(w), ww, 1, 1); - gtk_widget_show(ww); + uc->button = ww = gtk_button_new_with_label(browsebtn); + columns_add(COLUMNS(w), ww, 1, 1); + gtk_widget_show(ww); - columns_force_same_height(COLUMNS(w), uc->entry, uc->button); + 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 = - g_signal_connect(G_OBJECT(uc->entry), "changed", - G_CALLBACK(editbox_changed), dp); - g_signal_connect(G_OBJECT(uc->entry), "focus_in_event", - G_CALLBACK(widget_focus), dp); + g_signal_connect(G_OBJECT(uc->entry), "key_press_event", + G_CALLBACK(editbox_key), dp); + uc->entrysig = + g_signal_connect(G_OBJECT(uc->entry), "changed", + G_CALLBACK(editbox_changed), dp); + g_signal_connect(G_OBJECT(uc->entry), "focus_in_event", + G_CALLBACK(widget_focus), dp); + } else { + uc->button = w = gtk_button_new_with_label(ctrl->label); + shortcut_add(scs, gtk_bin_get_child(GTK_BIN(w)), + ctrl->fileselect.shortcut, SHORTCUT_UCTRL, uc); + gtk_widget_show(w); + + } g_signal_connect(G_OBJECT(uc->button), "focus_in_event", G_CALLBACK(widget_focus), dp); - g_signal_connect(G_OBJECT(ww), "clicked", + g_signal_connect(G_OBJECT(uc->button), "clicked", G_CALLBACK(filefont_clicked), dp); break; } @@ -2669,7 +2697,8 @@ gint win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) /* File/font selectors have their buttons pressed (ooer), * and focus transferred to the edit box. */ g_signal_emit_by_name(G_OBJECT(sc->uc->button), "clicked"); - gtk_widget_grab_focus(sc->uc->entry); + if (sc->uc->entry) + gtk_widget_grab_focus(sc->uc->entry); break; case CTRL_RADIO: /* diff --git a/windows/controls.c b/windows/controls.c index d458496c..e82d215d 100644 --- a/windows/controls.c +++ b/windows/controls.c @@ -1663,12 +1663,15 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, sfree(escaped); break; case CTRL_FILESELECT: - num_ids = 3; - escaped = shortcut_escape(ctrl->label, - ctrl->fileselect.shortcut); + escaped = shortcut_escape(ctrl->label, ctrl->fileselect.shortcut); shortcuts[nshortcuts++] = ctrl->fileselect.shortcut; - editbutton(&pos, escaped, base_id, base_id+1, - "Browse...", base_id+2); + num_ids = 3; + if (!ctrl->fileselect.just_button) { + editbutton(&pos, escaped, base_id, base_id+1, + "Browse...", base_id+2); + } else { + button(&pos, escaped, base_id+2, false); + } sfree(escaped); break; case CTRL_FONTSELECT: @@ -1980,15 +1983,27 @@ bool winctrl_handle_command(struct dlgparam *dp, UINT msg, of.lpstrCustomFilter = NULL; of.nFilterIndex = 1; of.lpstrFile = filename; - GetDlgItemText(dp->hwnd, c->base_id+1, filename, lenof(filename)); - filename[lenof(filename)-1] = '\0'; + if (!ctrl->fileselect.just_button) { + GetDlgItemText(dp->hwnd, c->base_id+1, + filename, lenof(filename)); + filename[lenof(filename)-1] = '\0'; + } else { + *filename = '\0'; + } of.nMaxFile = lenof(filename); of.lpstrFileTitle = NULL; of.lpstrTitle = ctrl->fileselect.title; of.Flags = 0; if (request_file(NULL, &of, false, ctrl->fileselect.for_writing)) { - SetDlgItemText(dp->hwnd, c->base_id + 1, filename); - ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE); + if (!ctrl->fileselect.just_button) { + SetDlgItemText(dp->hwnd, c->base_id + 1, filename); + ctrl->handler(ctrl, dp, dp->data, EVENT_VALCHANGE); + } else { + assert(!c->data); + c->data = filename; + ctrl->handler(ctrl, dp, dp->data, EVENT_ACTION); + c->data = NULL; + } } } break; @@ -2330,7 +2345,10 @@ void dlg_label_change(dlgcontrol *ctrl, dlgparam *dp, char const *text) break; case CTRL_FILESELECT: escaped = shortcut_escape(text, ctrl->fileselect.shortcut); - id = c->base_id; + if (ctrl->fileselect.just_button) + id = c->base_id + 2; /* the button */ + else + id = c->base_id; /* the label */ break; case CTRL_FONTSELECT: escaped = shortcut_escape(text, ctrl->fontselect.shortcut); @@ -2348,7 +2366,9 @@ void dlg_label_change(dlgcontrol *ctrl, dlgparam *dp, char const *text) void dlg_filesel_set(dlgcontrol *ctrl, dlgparam *dp, Filename *fn) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); - assert(c && c->ctrl->type == CTRL_FILESELECT); + assert(c); + assert(c->ctrl->type == CTRL_FILESELECT); + assert(!c->ctrl->fileselect.just_button); SetDlgItemText(dp->hwnd, c->base_id+1, fn->path); } @@ -2357,11 +2377,16 @@ Filename *dlg_filesel_get(dlgcontrol *ctrl, dlgparam *dp) struct winctrl *c = dlg_findbyctrl(dp, ctrl); char *tmp; Filename *ret; - assert(c && c->ctrl->type == CTRL_FILESELECT); - tmp = GetDlgItemText_alloc(dp->hwnd, c->base_id+1); - ret = filename_from_str(tmp); - sfree(tmp); - return ret; + assert(c); + assert(c->ctrl->type == CTRL_FILESELECT); + if (!c->ctrl->fileselect.just_button) { + tmp = GetDlgItemText_alloc(dp->hwnd, c->base_id+1); + ret = filename_from_str(tmp); + sfree(tmp); + return ret; + } else { + return filename_from_str(c->data); + } } void dlg_fontsel_set(dlgcontrol *ctrl, dlgparam *dp, FontSpec *fs)