X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/46a3419bd5693b8b3e42290e370d10aa82c67b61..8a9977e54db756281cf0d9f4a8322ce24b001a61:/unix/gtkdlg.c diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index 6f906df1..b7947b52 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,6 @@ #include #include "gtkcols.h" -#include "gtkpanel.h" #ifdef TESTMODE #define PUTTY_DO_GLOBALS /* actually _define_ globals */ @@ -54,6 +54,7 @@ struct uctrl { GtkWidget *menu; /* for optionmenu (==droplist) */ GtkWidget *optmenu; /* also for optionmenu */ GtkWidget *text; /* for text */ + GtkWidget *label; /* for dlg_label_change */ GtkAdjustment *adj; /* for the scrollbar in a list box */ guint textsig; }; @@ -89,6 +90,7 @@ static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event, gpointer data); static void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw, int chr, int action, void *ptr); +static void shortcut_highlight(GtkWidget *label, int chr); static int listitem_single_key(GtkWidget *item, GdkEventKey *event, gpointer data); static int listitem_multi_key(GtkWidget *item, GdkEventKey *event, @@ -151,6 +153,8 @@ static void dlg_init(struct dlgparam *dp) dp->coloursel_result.ok = FALSE; dp->treeitems = NULL; dp->window = dp->cancelbutton = dp->currtreeitem = NULL; + dp->flags = 0; + dp->currfocus = NULL; } static void dlg_cleanup(struct dlgparam *dp) @@ -158,6 +162,7 @@ static void dlg_cleanup(struct dlgparam *dp) struct uctrl *uc; freetree234(dp->byctrl); /* doesn't free the uctrls inside */ + dp->byctrl = NULL; while ( (uc = index234(dp->bywidget, 0)) != NULL) { del234(dp->bywidget, uc); if (uc->privdata_needs_free) @@ -166,6 +171,7 @@ static void dlg_cleanup(struct dlgparam *dp) sfree(uc); } freetree234(dp->bywidget); + dp->bywidget = NULL; sfree(dp->treeitems); } @@ -177,12 +183,16 @@ static void dlg_add_uctrl(struct dlgparam *dp, struct uctrl *uc) static struct uctrl *dlg_find_byctrl(struct dlgparam *dp, union control *ctrl) { + if (!dp->byctrl) + return NULL; return find234(dp->byctrl, ctrl, uctrl_cmp_byctrl_find); } static struct uctrl *dlg_find_bywidget(struct dlgparam *dp, GtkWidget *w) { struct uctrl *ret = NULL; + if (!dp->bywidget) + return NULL; do { ret = find234(dp->bywidget, w, uctrl_cmp_bywidget_find); if (ret) @@ -300,15 +310,18 @@ void dlg_listbox_clear(union control *ctrl, void *dlg) { struct dlgparam *dp = (struct dlgparam *)dlg; struct uctrl *uc = dlg_find_byctrl(dp, ctrl); - GtkContainer *cont; assert(uc->ctrl->generic.type == CTRL_EDITBOX || uc->ctrl->generic.type == CTRL_LISTBOX); assert(uc->menu != NULL || uc->list != NULL); - cont = (uc->menu ? GTK_CONTAINER(uc->menu) : GTK_CONTAINER(uc->list)); - - gtk_container_foreach(cont, container_remove_and_destroy, cont); + if (uc->menu) { + gtk_container_foreach(GTK_CONTAINER(uc->menu), + container_remove_and_destroy, + GTK_CONTAINER(uc->menu)); + } else { + gtk_list_clear_items(GTK_LIST(uc->list), 0, -1); + } } void dlg_listbox_del(union control *ctrl, void *dlg, int index) @@ -364,7 +377,8 @@ void dlg_listbox_addwithid(union control *ctrl, void *dlg, gtk_container_add(GTK_CONTAINER(uc->menu), menuitem); gtk_widget_show(menuitem); - gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", (gpointer)id); + gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", + GINT_TO_POINTER(id)); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menuitem_activate), dp); } else if (!uc->entry) { @@ -426,7 +440,8 @@ void dlg_listbox_addwithid(union control *ctrl, void *dlg, GTK_SIGNAL_FUNC(widget_focus), dp); gtk_signal_connect(GTK_OBJECT(listitem), "button_press_event", GTK_SIGNAL_FUNC(listitem_button), dp); - gtk_object_set_data(GTK_OBJECT(listitem), "user-data", (gpointer)id); + gtk_object_set_data(GTK_OBJECT(listitem), "user-data", + GINT_TO_POINTER(id)); } else { /* * List item in a combo-box list, which means the sensible @@ -438,7 +453,8 @@ void dlg_listbox_addwithid(union control *ctrl, void *dlg, gtk_container_add(GTK_CONTAINER(uc->list), listitem); gtk_widget_show(listitem); - gtk_object_set_data(GTK_OBJECT(listitem), "user-data", (gpointer)id); + gtk_object_set_data(GTK_OBJECT(listitem), "user-data", + GINT_TO_POINTER(id)); } dp->flags &= ~FLAG_UPDATING_COMBO_LIST; @@ -460,7 +476,7 @@ int dlg_listbox_getid(union control *ctrl, void *dlg, int index) item = GTK_OBJECT(g_list_nth_data(children, index)); g_list_free(children); - return (int)gtk_object_get_data(GTK_OBJECT(item), "user-data"); + return GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item), "user-data")); } /* dlg_listbox_index returns <0 if no single element is selected. */ @@ -479,6 +495,8 @@ int dlg_listbox_index(union control *ctrl, void *dlg) if (uc->menu) activeitem = gtk_menu_get_active(GTK_MENU(uc->menu)); + else + activeitem = NULL; /* unnecessarily placate gcc */ children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu : uc->list)); @@ -532,7 +550,35 @@ void dlg_listbox_select(union control *ctrl, void *dlg, int index) if (uc->optmenu) { gtk_option_menu_set_history(GTK_OPTION_MENU(uc->optmenu), index); } else { + int nitems; + GList *items; + gdouble newtop, newbot; + gtk_list_select_item(GTK_LIST(uc->list), index); + + /* + * Scroll the list box if necessary to ensure the newly + * selected item is visible. + */ + items = gtk_container_children(GTK_CONTAINER(uc->list)); + nitems = g_list_length(items); + if (nitems > 0) { + int modified = FALSE; + g_list_free(items); + newtop = uc->adj->lower + + (uc->adj->upper - uc->adj->lower) * index / nitems; + newbot = uc->adj->lower + + (uc->adj->upper - uc->adj->lower) * (index+1) / nitems; + if (uc->adj->value > newtop) { + modified = TRUE; + uc->adj->value = newtop; + } else if (uc->adj->value < newbot - uc->adj->page_size) { + modified = TRUE; + uc->adj->value = newbot - uc->adj->page_size; + } + if (modified) + gtk_adjustment_value_changed(uc->adj); + } } } @@ -547,6 +593,46 @@ void dlg_text_set(union control *ctrl, void *dlg, char const *text) gtk_label_set_text(GTK_LABEL(uc->text), text); } +void dlg_label_change(union control *ctrl, void *dlg, char const *text) +{ + struct dlgparam *dp = (struct dlgparam *)dlg; + struct uctrl *uc = dlg_find_byctrl(dp, ctrl); + + switch (uc->ctrl->generic.type) { + case CTRL_BUTTON: + gtk_label_set_text(GTK_LABEL(uc->toplevel), text); + shortcut_highlight(uc->toplevel, ctrl->button.shortcut); + break; + case CTRL_CHECKBOX: + gtk_label_set_text(GTK_LABEL(uc->toplevel), text); + shortcut_highlight(uc->toplevel, ctrl->checkbox.shortcut); + break; + case CTRL_RADIO: + gtk_label_set_text(GTK_LABEL(uc->label), text); + shortcut_highlight(uc->label, ctrl->radio.shortcut); + break; + case CTRL_EDITBOX: + gtk_label_set_text(GTK_LABEL(uc->label), 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); + break; + case CTRL_FONTSELECT: + gtk_label_set_text(GTK_LABEL(uc->label), text); + shortcut_highlight(uc->label, ctrl->fontselect.shortcut); + break; + case CTRL_LISTBOX: + gtk_label_set_text(GTK_LABEL(uc->label), text); + shortcut_highlight(uc->label, ctrl->listbox.shortcut); + break; + default: + assert(!"This shouldn't happen"); + break; + } +} + void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn) { struct dlgparam *dp = (struct dlgparam *)dlg; @@ -670,6 +756,35 @@ static void errmsg_button_clicked(GtkButton *button, gpointer data) gtk_widget_destroy(GTK_WIDGET(data)); } +static void set_transient_window_pos(GtkWidget *parent, GtkWidget *child) +{ + gint x, y, w, h, dx, dy; + GtkRequisition req; + gtk_window_set_position(GTK_WINDOW(child), GTK_WIN_POS_NONE); + gtk_widget_size_request(GTK_WIDGET(child), &req); + + gdk_window_get_origin(GTK_WIDGET(parent)->window, &x, &y); + gdk_window_get_size(GTK_WIDGET(parent)->window, &w, &h); + + /* + * One corner of the transient will be offset inwards, by 1/4 + * of the parent window's size, from the corresponding corner + * of the parent window. The corner will be chosen so as to + * place the transient closer to the centre of the screen; this + * should avoid transients going off the edge of the screen on + * a regular basis. + */ + if (x + w/2 < gdk_screen_width() / 2) + dx = x + w/4; /* work from left edges */ + else + dx = x + 3*w/4 - req.width; /* work from right edges */ + if (y + h/2 < gdk_screen_height() / 2) + dy = y + h/4; /* work from top edges */ + else + dy = y + 3*h/4 - req.height; /* work from bottom edges */ + gtk_widget_set_uposition(GTK_WIDGET(child), dx, dy); +} + void dlg_error_msg(void *dlg, char *msg) { struct dlgparam *dp = (struct dlgparam *)dlg; @@ -698,15 +813,7 @@ void dlg_error_msg(void *dlg, char *msg) GTK_SIGNAL_FUNC(window_destroy), NULL); gtk_window_set_modal(GTK_WINDOW(window), TRUE); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dp->window)); - { - gint x, y, w, h, dx, dy; - gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_NONE); - gdk_window_get_origin(GTK_WIDGET(dp->window)->window, &x, &y); - gdk_window_get_size(GTK_WIDGET(dp->window)->window, &w, &h); - dx = x + w/4; - dy = y + h/4; - gtk_widget_set_uposition(GTK_WIDGET(window), dx, dy); - } + set_transient_window_pos(dp->window, window); gtk_widget_show(window); gtk_main(); } @@ -1259,6 +1366,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs, uc->buttons = NULL; uc->entry = uc->list = uc->menu = NULL; uc->button = uc->optmenu = uc->text = NULL; + uc->label = NULL; switch (ctrl->generic.type) { case CTRL_BUTTON: @@ -1304,6 +1412,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs, gtk_widget_show(label); shortcut_add(scs, label, ctrl->radio.shortcut, SHORTCUT_UCTRL, uc); + uc->label = label; } percentages = g_new(gint, ctrl->radio.ncolumns); for (i = 0; i < ctrl->radio.ncolumns; i++) { @@ -1408,6 +1517,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs, gtk_widget_show(w); w = container; + uc->label = label; } gtk_signal_connect(GTK_OBJECT(uc->entry), "focus_out_event", GTK_SIGNAL_FUNC(editbox_lostfocus), dp); @@ -1436,6 +1546,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs, ctrl->fileselect.shortcut : ctrl->fontselect.shortcut), SHORTCUT_UCTRL, uc); + uc->label = ww; } uc->entry = ww = gtk_entry_new(); @@ -1471,7 +1582,10 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs, GTK_SIGNAL_FUNC(widget_focus), dp); } else { uc->list = gtk_list_new(); - if (ctrl->listbox.multisel) { + if (ctrl->listbox.multisel == 2) { + gtk_list_set_selection_mode(GTK_LIST(uc->list), + GTK_SELECTION_EXTENDED); + } else if (ctrl->listbox.multisel == 1) { gtk_list_set_selection_mode(GTK_LIST(uc->list), GTK_SELECTION_MULTIPLE); } else { @@ -1570,6 +1684,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs, shortcut_add(scs, label, ctrl->listbox.shortcut, SHORTCUT_UCTRL, uc); w = container; + uc->label = label; } break; case CTRL_TEXT: @@ -1625,7 +1740,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs, struct selparam { struct dlgparam *dp; - Panels *panels; + GtkNotebook *panels; GtkWidget *panel, *treeitem; struct Shortcuts shortcuts; }; @@ -1633,8 +1748,12 @@ struct selparam { static void treeitem_sel(GtkItem *item, gpointer data) { struct selparam *sp = (struct selparam *)data; + gint page_num; + + page_num = gtk_notebook_page_num(sp->panels, sp->panel); + gtk_notebook_set_page(sp->panels, page_num); - panels_switch_to(sp->panels, sp->panel); + dlg_refresh(NULL, sp->dp); sp->dp->shortcuts = &sp->shortcuts; sp->dp->currtreeitem = sp->treeitem; @@ -1855,13 +1974,31 @@ int tree_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) return FALSE; } -void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw, - int chr, int action, void *ptr) +static void shortcut_highlight(GtkWidget *labelw, int chr) { GtkLabel *label = GTK_LABEL(labelw); gchar *currstr, *pattern; int i; + gtk_label_get(label, &currstr); + 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_usize(GTK_WIDGET(label), -1, req.height); + + sfree(pattern); + break; + } +} + +void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw, + int chr, int action, void *ptr) +{ if (chr == NO_SHORTCUT) return; @@ -1879,23 +2016,20 @@ void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw, scs->sc[chr].uc = (struct uctrl *)ptr; } - gtk_label_get(label, &currstr); - 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_usize(GTK_WIDGET(label), -1, req.height); + shortcut_highlight(labelw, chr); +} - sfree(pattern); - break; - } +int get_listitemheight(void) +{ + GtkWidget *listitem = gtk_list_item_new_with_label("foo"); + GtkRequisition req; + gtk_widget_size_request(listitem, &req); + gtk_widget_unref(listitem); + return req.height; } -int do_config_box(const char *title, Config *cfg) +int do_config_box(const char *title, Config *cfg, int midsession, + int protcfginfo) { GtkWidget *window, *hbox, *vbox, *cols, *label, *tree, *treescroll, *panels, *panelvbox; @@ -1905,7 +2039,6 @@ int do_config_box(const char *title, Config *cfg) GtkTreeItem *treeitemlevels[8]; GtkTree *treelevels[8]; struct dlgparam dp; - struct sesslist sl; struct Shortcuts scs; struct selparam *selparams = NULL; @@ -1913,25 +2046,19 @@ int do_config_box(const char *title, Config *cfg) dlg_init(&dp); - { - GtkWidget *listitem = gtk_list_item_new_with_label("foo"); - GtkRequisition req; - gtk_widget_size_request(listitem, &req); - listitemheight = req.height; - gtk_widget_unref(listitem); - } - - get_sesslist(&sl, TRUE); + listitemheight = get_listitemheight(); for (index = 0; index < lenof(scs.sc); index++) { scs.sc[index].action = SHORTCUT_EMPTY; } + window = gtk_dialog_new(); + ctrlbox = ctrl_new_box(); - setup_config_box(ctrlbox, &sl, FALSE, 0); - unix_setup_config_box(ctrlbox, FALSE); + setup_config_box(ctrlbox, midsession, cfg->protocol, protcfginfo); + unix_setup_config_box(ctrlbox, midsession, cfg->protocol); + gtk_setup_config_box(ctrlbox, midsession, window); - window = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(window), title); hbox = gtk_hbox_new(FALSE, 4); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), hbox, TRUE, TRUE, 0); @@ -1964,7 +2091,9 @@ int do_config_box(const char *title, Config *cfg) gtk_widget_show(tree); gtk_widget_show(treescroll); gtk_box_pack_start(GTK_BOX(vbox), treescroll, TRUE, TRUE, 0); - panels = panels_new(); + panels = gtk_notebook_new(); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(panels), FALSE); + gtk_notebook_set_show_border(GTK_NOTEBOOK(panels), FALSE); gtk_box_pack_start(GTK_BOX(hbox), panels, TRUE, TRUE, 0); gtk_widget_show(panels); @@ -2034,9 +2163,15 @@ int do_config_box(const char *title, Config *cfg) first = (panelvbox == NULL); panelvbox = gtk_vbox_new(FALSE, 4); - gtk_container_add(GTK_CONTAINER(panels), panelvbox); + gtk_widget_show(panelvbox); + gtk_notebook_append_page(GTK_NOTEBOOK(panels), panelvbox, + NULL); if (first) { - panels_switch_to(PANELS(panels), panelvbox); + gint page_num; + + page_num = gtk_notebook_page_num(GTK_NOTEBOOK(panels), + panelvbox); + gtk_notebook_set_page(GTK_NOTEBOOK(panels), page_num); gtk_tree_select_child(GTK_TREE(tree), treeitem); } @@ -2046,7 +2181,7 @@ int do_config_box(const char *title, Config *cfg) struct selparam); } selparams[nselparams].dp = &dp; - selparams[nselparams].panels = PANELS(panels); + selparams[nselparams].panels = GTK_NOTEBOOK(panels); selparams[nselparams].panel = panelvbox; selparams[nselparams].shortcuts = scs; /* structure copy */ selparams[nselparams].treeitem = treeitem; @@ -2114,7 +2249,6 @@ int do_config_box(const char *title, Config *cfg) gtk_main(); - get_sesslist(&sl, FALSE); dlg_cleanup(&dp); sfree(selparams); @@ -2205,13 +2339,7 @@ int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) gtk_window_set_modal(GTK_WINDOW(window), TRUE); if (parentwin) { - gint x, y, w, h, dx, dy; - gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_NONE); - gdk_window_get_origin(parentwin->window, &x, &y); - gdk_window_get_size(parentwin->window, &w, &h); - dx = x + w/4; - dy = y + h/4; - gtk_widget_set_uposition(GTK_WIDGET(window), dx, dy); + set_transient_window_pos(parentwin, window); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parentwin)); } else @@ -2240,13 +2368,27 @@ static int string_width(char *text) return req.width; } -void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, - char *keystr, char *fingerprint) +int reallyclose(void *frontend) +{ + char *title = dupcat(appname, " Exit Confirmation", NULL); + int ret = messagebox(GTK_WIDGET(get_window(frontend)), + title, "Are you sure you want to close this session?", + string_width("Most of the width of the above text"), + "Yes", 'y', +1, 1, + "No", 'n', -1, 0, + NULL); + sfree(title); + return ret; +} + +int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, + char *keystr, char *fingerprint, + void (*callback)(void *ctx, int result), void *ctx) { static const char absenttxt[] = "The server's host key is not cached. You have no guarantee " "that the server is the computer you think it is.\n" - "The server's key fingerprint is:\n" + "The server's %s key fingerprint is:\n" "%s\n" "If you trust this host, press \"Accept\" to add the key to " "PuTTY's cache and carry on connecting.\n" @@ -2260,7 +2402,7 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, "cached. This means that either the server administrator " "has changed the host key, or you have actually connected " "to another computer pretending to be the server.\n" - "The new key fingerprint is:\n" + "The new %s key fingerprint is:\n" "%s\n" "If you were expecting this change and trust the new key, " "press \"Accept\" to update PuTTY's cache and continue connecting.\n" @@ -2278,9 +2420,9 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, ret = verify_host_key(host, port, keytype, keystr); if (ret == 0) /* success - key matched OK */ - return; + return 1; - text = dupprintf((ret == 2 ? wrongtxt : absenttxt), fingerprint); + text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint); ret = messagebox(GTK_WIDGET(get_window(frontend)), "PuTTY Security Alert", text, @@ -2292,29 +2434,29 @@ void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, sfree(text); - if (ret == 0) - cleanup_exit(0); - else if (ret == 2) + if (ret == 2) { store_host_key(host, port, keytype, keystr); + return 1; /* continue with connection */ + } else if (ret == 1) + return 1; /* continue with connection */ + return 0; /* do not continue with connection */ } /* - * Ask whether the selected cipher is acceptable (since it was + * Ask whether the selected algorithm is acceptable (since it was * below the configured 'warn' threshold). - * cs: 0 = both ways, 1 = client->server, 2 = server->client */ -void askcipher(void *frontend, char *ciphername, int cs) +int askalg(void *frontend, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx) { static const char msg[] = - "The first %scipher supported by the server is " + "The first %s supported by the server is " "%s, which is below the configured warning threshold.\n" "Continue with connection?"; char *text; int ret; - text = dupprintf(msg, (cs == 0) ? "" : - (cs == 1) ? "client-to-server " : "server-to-client ", - ciphername); + text = dupprintf(msg, algtype, algname); ret = messagebox(GTK_WIDGET(get_window(frontend)), "PuTTY Security Alert", text, string_width("Continue with connection?"), @@ -2324,9 +2466,9 @@ void askcipher(void *frontend, char *ciphername, int cs) sfree(text); if (ret) { - return; + return 1; } else { - cleanup_exit(0); + return 0; } } @@ -2337,30 +2479,392 @@ void old_keyfile_warning(void) */ } -void fatalbox(char *p, ...) +void fatal_message_box(void *window, char *msg) { - va_list ap; - char *msg; - va_start(ap, p); - msg = dupvprintf(p, ap); - va_end(ap); - messagebox(NULL, "PuTTY Fatal Error", msg, + messagebox(window, "PuTTY Fatal Error", msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), "OK", 'o', 1, 1, NULL); - sfree(msg); - cleanup_exit(1); } -void connection_fatal(void *frontend, char *p, ...) + +void fatalbox(char *p, ...) { va_list ap; char *msg; va_start(ap, p); msg = dupvprintf(p, ap); va_end(ap); - messagebox(GTK_WIDGET(get_window(frontend)), - "PuTTY Fatal Error", msg, - string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), - "OK", 'o', 1, 1, NULL); + fatal_message_box(NULL, msg); sfree(msg); cleanup_exit(1); } + +static GtkWidget *aboutbox = NULL; + +static void about_close_clicked(GtkButton *button, gpointer data) +{ + gtk_widget_destroy(aboutbox); + aboutbox = NULL; +} + +static void licence_clicked(GtkButton *button, gpointer data) +{ + char *title; + + char *licence = + "Copyright 1997-2006 Simon Tatham.\n\n" + + "Portions copyright Robert de Bath, Joris van Rantwijk, Delian " + "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas " + "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, " + "Markus Kuhn, and CORE SDI S.A.\n\n" + + "Permission is hereby granted, free of charge, to any person " + "obtaining a copy of this software and associated documentation " + "files (the ""Software""), to deal in the Software without restriction, " + "including without limitation the rights to use, copy, modify, merge, " + "publish, distribute, sublicense, and/or sell copies of the Software, " + "and to permit persons to whom the Software is furnished to do so, " + "subject to the following conditions:\n\n" + + "The above copyright notice and this permission notice shall be " + "included in all copies or substantial portions of the Software.\n\n" + + "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT " + "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, " + "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " + "MERCHANTABILITY, FITNESS FOR A PARTICULAR " + "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE " + "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES " + "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, " + "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN " + "CONNECTION WITH THE SOFTWARE OR THE USE OR " + "OTHER DEALINGS IN THE SOFTWARE."; + + title = dupcat(appname, " Licence", NULL); + assert(aboutbox != NULL); + messagebox(aboutbox, title, licence, + string_width("LONGISH LINE OF TEXT SO THE LICENCE" + " BOX ISN'T EXCESSIVELY TALL AND THIN"), + "OK", 'o', 1, 1, NULL); + sfree(title); +} + +void about_box(void *window) +{ + GtkWidget *w; + char *title; + + if (aboutbox) { + gtk_widget_grab_focus(aboutbox); + return; + } + + aboutbox = gtk_dialog_new(); + gtk_container_set_border_width(GTK_CONTAINER(aboutbox), 10); + title = dupcat("About ", appname, NULL); + gtk_window_set_title(GTK_WINDOW(aboutbox), title); + sfree(title); + + w = gtk_button_new_with_label("Close"); + GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT); + gtk_window_set_default(GTK_WINDOW(aboutbox), w); + gtk_box_pack_end(GTK_BOX(GTK_DIALOG(aboutbox)->action_area), + w, FALSE, FALSE, 0); + gtk_signal_connect(GTK_OBJECT(w), "clicked", + GTK_SIGNAL_FUNC(about_close_clicked), NULL); + gtk_widget_show(w); + + w = gtk_button_new_with_label("View Licence"); + GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT); + gtk_box_pack_end(GTK_BOX(GTK_DIALOG(aboutbox)->action_area), + w, FALSE, FALSE, 0); + gtk_signal_connect(GTK_OBJECT(w), "clicked", + GTK_SIGNAL_FUNC(licence_clicked), NULL); + gtk_widget_show(w); + + w = gtk_label_new(appname); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), + w, FALSE, FALSE, 0); + gtk_widget_show(w); + + w = gtk_label_new(ver); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), + w, FALSE, FALSE, 5); + gtk_widget_show(w); + + w = gtk_label_new("Copyright 1997-2006 Simon Tatham. All rights reserved"); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), + w, FALSE, FALSE, 5); + gtk_widget_show(w); + + set_transient_window_pos(GTK_WIDGET(window), aboutbox); + gtk_widget_show(aboutbox); +} + +struct eventlog_stuff { + GtkWidget *parentwin, *window; + struct controlbox *eventbox; + struct Shortcuts scs; + struct dlgparam dp; + union control *listctrl; + char **events; + int nevents, negsize; + char *seldata; + int sellen; + int ignore_selchange; +}; + +static void eventlog_destroy(GtkWidget *widget, gpointer data) +{ + struct eventlog_stuff *es = (struct eventlog_stuff *)data; + + es->window = NULL; + sfree(es->seldata); + dlg_cleanup(&es->dp); + ctrl_free_box(es->eventbox); +} +static void eventlog_ok_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + if (event == EVENT_ACTION) + dlg_end(dlg, 0); +} +static void eventlog_list_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + struct eventlog_stuff *es = (struct eventlog_stuff *)data; + + if (event == EVENT_REFRESH) { + int i; + + dlg_update_start(ctrl, dlg); + dlg_listbox_clear(ctrl, dlg); + for (i = 0; i < es->nevents; i++) { + dlg_listbox_add(ctrl, dlg, es->events[i]); + } + dlg_update_done(ctrl, dlg); + } else if (event == EVENT_SELCHANGE) { + int i; + int selsize = 0; + + /* + * If this SELCHANGE event is happening as a result of + * deliberate deselection because someone else has grabbed + * the selection, the last thing we want to do is pre-empt + * them. + */ + if (es->ignore_selchange) + return; + + /* + * Construct the data to use as the selection. + */ + sfree(es->seldata); + es->seldata = NULL; + es->sellen = 0; + for (i = 0; i < es->nevents; i++) { + if (dlg_listbox_issel(ctrl, dlg, i)) { + int extralen = strlen(es->events[i]); + + if (es->sellen + extralen + 2 > selsize) { + selsize = es->sellen + extralen + 512; + es->seldata = sresize(es->seldata, selsize, char); + } + + strcpy(es->seldata + es->sellen, es->events[i]); + es->sellen += extralen; + es->seldata[es->sellen++] = '\n'; + } + } + + if (gtk_selection_owner_set(es->window, GDK_SELECTION_PRIMARY, + GDK_CURRENT_TIME)) { + extern GdkAtom compound_text_atom; + + gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY, + GDK_SELECTION_TYPE_STRING, 1); + gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY, + compound_text_atom, 1); + } + + } +} + +void eventlog_selection_get(GtkWidget *widget, GtkSelectionData *seldata, + guint info, guint time_stamp, gpointer data) +{ + struct eventlog_stuff *es = (struct eventlog_stuff *)data; + + gtk_selection_data_set(seldata, seldata->target, 8, + (unsigned char *)es->seldata, es->sellen); +} + +gint eventlog_selection_clear(GtkWidget *widget, GdkEventSelection *seldata, + gpointer data) +{ + struct eventlog_stuff *es = (struct eventlog_stuff *)data; + struct uctrl *uc; + + /* + * Deselect everything in the list box. + */ + uc = dlg_find_byctrl(&es->dp, es->listctrl); + es->ignore_selchange = 1; + gtk_list_unselect_all(GTK_LIST(uc->list)); + es->ignore_selchange = 0; + + sfree(es->seldata); + es->sellen = 0; + es->seldata = NULL; + return TRUE; +} + +void showeventlog(void *estuff, void *parentwin) +{ + struct eventlog_stuff *es = (struct eventlog_stuff *)estuff; + GtkWidget *window, *w0, *w1; + GtkWidget *parent = GTK_WIDGET(parentwin); + struct controlset *s0, *s1; + union control *c; + int listitemheight, index; + char *title; + + if (es->window) { + gtk_widget_grab_focus(es->window); + return; + } + + dlg_init(&es->dp); + + for (index = 0; index < lenof(es->scs.sc); index++) { + es->scs.sc[index].action = SHORTCUT_EMPTY; + } + + es->eventbox = ctrl_new_box(); + + s0 = ctrl_getset(es->eventbox, "", "", ""); + ctrl_columns(s0, 3, 33, 34, 33); + c = ctrl_pushbutton(s0, "Close", 'c', HELPCTX(no_help), + eventlog_ok_handler, P(NULL)); + c->button.column = 1; + c->button.isdefault = TRUE; + + s1 = ctrl_getset(es->eventbox, "x", "", ""); + es->listctrl = c = ctrl_listbox(s1, NULL, NO_SHORTCUT, HELPCTX(no_help), + eventlog_list_handler, P(es)); + c->listbox.height = 10; + c->listbox.multisel = 2; + c->listbox.ncols = 3; + c->listbox.percentages = snewn(3, int); + c->listbox.percentages[0] = 25; + c->listbox.percentages[1] = 10; + c->listbox.percentages[2] = 65; + + listitemheight = get_listitemheight(); + + es->window = window = gtk_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, + listitemheight, GTK_WINDOW(window)); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->action_area), + w0, TRUE, TRUE, 0); + gtk_widget_show(w0); + w1 = layout_ctrls(&es->dp, &es->scs, s1, + listitemheight, GTK_WINDOW(window)); + gtk_container_set_border_width(GTK_CONTAINER(w1), 10); + gtk_widget_set_usize(w1, 20 + + string_width("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(window)->vbox), + w1, TRUE, TRUE, 0); + gtk_widget_show(w1); + + es->dp.data = es; + es->dp.shortcuts = &es->scs; + es->dp.lastfocus = NULL; + es->dp.retval = 0; + es->dp.window = window; + + dlg_refresh(NULL, &es->dp); + + if (parent) { + set_transient_window_pos(parent, window); + gtk_window_set_transient_for(GTK_WINDOW(window), + GTK_WINDOW(parent)); + } else + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_widget_show(window); + + gtk_signal_connect(GTK_OBJECT(window), "destroy", + GTK_SIGNAL_FUNC(eventlog_destroy), es); + gtk_signal_connect(GTK_OBJECT(window), "key_press_event", + GTK_SIGNAL_FUNC(win_key_press), &es->dp); + gtk_signal_connect(GTK_OBJECT(window), "selection_get", + GTK_SIGNAL_FUNC(eventlog_selection_get), es); + gtk_signal_connect(GTK_OBJECT(window), "selection_clear_event", + GTK_SIGNAL_FUNC(eventlog_selection_clear), es); +} + +void *eventlogstuff_new(void) +{ + struct eventlog_stuff *es; + es = snew(struct eventlog_stuff); + memset(es, 0, sizeof(*es)); + return es; +} + +void logevent_dlg(void *estuff, const char *string) +{ + struct eventlog_stuff *es = (struct eventlog_stuff *)estuff; + + char timebuf[40]; + struct tm tm; + + if (es->nevents >= es->negsize) { + es->negsize += 64; + es->events = sresize(es->events, es->negsize, char *); + } + + tm=ltime(); + strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm); + + es->events[es->nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char); + strcpy(es->events[es->nevents], timebuf); + strcat(es->events[es->nevents], string); + if (es->window) { + dlg_listbox_add(es->listctrl, &es->dp, es->events[es->nevents]); + } + es->nevents++; +} + +int askappend(void *frontend, Filename filename, + void (*callback)(void *ctx, int result), void *ctx) +{ + static const char msgtemplate[] = + "The session log file \"%.*s\" already exists. " + "You can overwrite it with a new session log, " + "append your session log to the end of it, " + "or disable session logging for this session."; + char *message; + char *mbtitle; + int mbret; + + message = dupprintf(msgtemplate, FILENAME_MAX, filename.path); + mbtitle = dupprintf("%s Log to File", appname); + + mbret = messagebox(get_window(frontend), mbtitle, message, + string_width("LINE OF TEXT SUITABLE FOR THE" + " ASKAPPEND WIDTH"), + "Overwrite", 'o', 1, 2, + "Append", 'a', 0, 1, + "Disable", 'd', -1, 0, + NULL); + + sfree(message); + sfree(mbtitle); + + return mbret; +}