2 * gtkdlg.c - GTK implementation of the PuTTY configuration box.
10 #include <gdk/gdkkeysyms.h>
13 #include <X11/Xutil.h>
19 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
34 struct Shortcut sc[128];
41 int privdata_needs_free;
42 GtkWidget **buttons; int nbuttons; /* for radio buttons */
43 GtkWidget *entry; /* for editbox, filesel, fontsel */
44 GtkWidget *combo; /* for combo box (either editable or not) */
45 GtkWidget *list; /* for list box (list, droplist, combo box) */
46 GtkListStore *listmodel; /* for all types of list box */
47 GtkWidget *button; /* for filesel, fontsel */
48 GtkWidget *text; /* for text */
49 GtkWidget *label; /* for dlg_label_change */
50 GtkAdjustment *adj; /* for the scrollbar in a list box */
57 tree234 *byctrl, *bywidget;
59 struct { unsigned char r, g, b, ok; } coloursel_result; /* 0-255 */
60 /* `flags' are set to indicate when a GTK signal handler is being called
61 * due to automatic processing and should not flag a user event. */
63 struct Shortcuts *shortcuts;
64 GtkWidget *window, *cancelbutton;
65 union control *currfocus, *lastfocus;
66 #if !GTK_CHECK_VERSION(2,0,0)
67 GtkWidget *currtreeitem, **treeitems;
72 #define FLAG_UPDATING_COMBO_LIST 1
73 #define FLAG_UPDATING_LISTBOX 2
75 enum { /* values for Shortcut.action */
76 SHORTCUT_EMPTY, /* no shortcut on this key */
77 SHORTCUT_TREE, /* focus a tree item */
78 SHORTCUT_FOCUS, /* focus the supplied widget */
79 SHORTCUT_UCTRL, /* do something sane with uctrl */
80 SHORTCUT_UCTRL_UP, /* uctrl is a draglist, move Up */
81 SHORTCUT_UCTRL_DOWN, /* uctrl is a draglist, move Down */
84 #if GTK_CHECK_VERSION(2,0,0)
95 static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event,
97 static void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
98 int chr, int action, void *ptr);
99 static void shortcut_highlight(GtkWidget *label, int chr);
100 static void coloursel_ok(GtkButton *button, gpointer data);
101 static void coloursel_cancel(GtkButton *button, gpointer data);
102 static void window_destroy(GtkWidget *widget, gpointer data);
103 int get_listitemheight(GtkWidget *widget);
105 static int uctrl_cmp_byctrl(void *av, void *bv)
107 struct uctrl *a = (struct uctrl *)av;
108 struct uctrl *b = (struct uctrl *)bv;
109 if (a->ctrl < b->ctrl)
111 else if (a->ctrl > b->ctrl)
116 static int uctrl_cmp_byctrl_find(void *av, void *bv)
118 union control *a = (union control *)av;
119 struct uctrl *b = (struct uctrl *)bv;
122 else if (a > b->ctrl)
127 static int uctrl_cmp_bywidget(void *av, void *bv)
129 struct uctrl *a = (struct uctrl *)av;
130 struct uctrl *b = (struct uctrl *)bv;
131 if (a->toplevel < b->toplevel)
133 else if (a->toplevel > b->toplevel)
138 static int uctrl_cmp_bywidget_find(void *av, void *bv)
140 GtkWidget *a = (GtkWidget *)av;
141 struct uctrl *b = (struct uctrl *)bv;
144 else if (a > b->toplevel)
149 static void dlg_init(struct dlgparam *dp)
151 dp->byctrl = newtree234(uctrl_cmp_byctrl);
152 dp->bywidget = newtree234(uctrl_cmp_bywidget);
153 dp->coloursel_result.ok = FALSE;
154 dp->window = dp->cancelbutton = NULL;
155 #if !GTK_CHECK_VERSION(2,0,0)
156 dp->treeitems = NULL;
157 dp->currtreeitem = NULL;
160 dp->currfocus = NULL;
163 static void dlg_cleanup(struct dlgparam *dp)
167 freetree234(dp->byctrl); /* doesn't free the uctrls inside */
169 while ( (uc = index234(dp->bywidget, 0)) != NULL) {
170 del234(dp->bywidget, uc);
171 if (uc->privdata_needs_free)
176 freetree234(dp->bywidget);
178 #if !GTK_CHECK_VERSION(2,0,0)
179 sfree(dp->treeitems);
183 static void dlg_add_uctrl(struct dlgparam *dp, struct uctrl *uc)
185 add234(dp->byctrl, uc);
186 add234(dp->bywidget, uc);
189 static struct uctrl *dlg_find_byctrl(struct dlgparam *dp, union control *ctrl)
193 return find234(dp->byctrl, ctrl, uctrl_cmp_byctrl_find);
196 static struct uctrl *dlg_find_bywidget(struct dlgparam *dp, GtkWidget *w)
198 struct uctrl *ret = NULL;
202 ret = find234(dp->bywidget, w, uctrl_cmp_bywidget_find);
210 void *dlg_get_privdata(union control *ctrl, void *dlg)
212 struct dlgparam *dp = (struct dlgparam *)dlg;
213 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
217 void dlg_set_privdata(union control *ctrl, void *dlg, void *ptr)
219 struct dlgparam *dp = (struct dlgparam *)dlg;
220 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
222 uc->privdata_needs_free = FALSE;
225 void *dlg_alloc_privdata(union control *ctrl, void *dlg, size_t size)
227 struct dlgparam *dp = (struct dlgparam *)dlg;
228 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
230 * This is an internal allocation routine, so it's allowed to
231 * use smalloc directly.
233 uc->privdata = smalloc(size);
234 uc->privdata_needs_free = FALSE;
238 union control *dlg_last_focused(union control *ctrl, void *dlg)
240 struct dlgparam *dp = (struct dlgparam *)dlg;
241 if (dp->currfocus != ctrl)
242 return dp->currfocus;
244 return dp->lastfocus;
247 void dlg_radiobutton_set(union control *ctrl, void *dlg, int which)
249 struct dlgparam *dp = (struct dlgparam *)dlg;
250 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
251 assert(uc->ctrl->generic.type == CTRL_RADIO);
252 assert(uc->buttons != NULL);
253 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->buttons[which]), TRUE);
256 int dlg_radiobutton_get(union control *ctrl, void *dlg)
258 struct dlgparam *dp = (struct dlgparam *)dlg;
259 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
262 assert(uc->ctrl->generic.type == CTRL_RADIO);
263 assert(uc->buttons != NULL);
264 for (i = 0; i < uc->nbuttons; i++)
265 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->buttons[i])))
267 return 0; /* got to return something */
270 void dlg_checkbox_set(union control *ctrl, void *dlg, int checked)
272 struct dlgparam *dp = (struct dlgparam *)dlg;
273 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
274 assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
275 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->toplevel), checked);
278 int dlg_checkbox_get(union control *ctrl, void *dlg)
280 struct dlgparam *dp = (struct dlgparam *)dlg;
281 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
282 assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
283 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->toplevel));
286 void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
288 struct dlgparam *dp = (struct dlgparam *)dlg;
289 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
292 assert(uc->ctrl->generic.type == CTRL_EDITBOX);
294 if (!uc->ctrl->editbox.has_list) {
295 assert(uc->entry != NULL);
298 assert(uc->combo != NULL);
299 entry = gtk_bin_get_child(GTK_BIN(uc->combo));
303 * GTK 2 implements gtk_entry_set_text by means of two separate
304 * operations: first delete the previous text leaving the empty
305 * string, then insert the new text. This causes two calls to
306 * the "changed" signal.
308 * The first call to "changed", if allowed to proceed normally,
309 * will cause an EVENT_VALCHANGE event on the edit box, causing
310 * a call to dlg_editbox_get() which will read the empty string
311 * out of the GtkEntry - and promptly write it straight into
312 * the Config structure, which is precisely where our `text'
313 * pointer is probably pointing, so the second editing
314 * operation will insert that instead of the string we
315 * originally asked for.
317 * Hence, we must take our own copy of the text before we do
320 tmpstring = dupstr(text);
321 gtk_entry_set_text(GTK_ENTRY(entry), tmpstring);
325 void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)
327 struct dlgparam *dp = (struct dlgparam *)dlg;
328 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
329 assert(uc->ctrl->generic.type == CTRL_EDITBOX);
331 if (!uc->ctrl->editbox.has_list) {
332 assert(uc->entry != NULL);
333 strncpy(buffer, gtk_entry_get_text(GTK_ENTRY(uc->entry)),
335 buffer[length-1] = '\0';
337 assert(uc->combo != NULL);
339 gtk_combo_box_get_active_text(GTK_COMBO_BOX(uc->combo)),
341 buffer[length-1] = '\0';
345 /* The `listbox' functions can also apply to combo boxes. */
346 void dlg_listbox_clear(union control *ctrl, void *dlg)
348 struct dlgparam *dp = (struct dlgparam *)dlg;
349 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
351 assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
352 uc->ctrl->generic.type == CTRL_LISTBOX);
353 assert(uc->listmodel != NULL);
355 gtk_list_store_clear(uc->listmodel);
358 void dlg_listbox_del(union control *ctrl, void *dlg, int index)
360 struct dlgparam *dp = (struct dlgparam *)dlg;
361 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
365 assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
366 uc->ctrl->generic.type == CTRL_LISTBOX);
367 assert(uc->listmodel != NULL);
369 path = gtk_tree_path_new_from_indices(index, -1);
370 gtk_tree_model_get_iter(GTK_TREE_MODEL(uc->listmodel), &iter, path);
371 gtk_list_store_remove(uc->listmodel, &iter);
372 gtk_tree_path_free(path);
375 void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
377 dlg_listbox_addwithid(ctrl, dlg, text, 0);
381 * Each listbox entry may have a numeric id associated with it.
382 * Note that some front ends only permit a string to be stored at
383 * each position, which means that _if_ you put two identical
384 * strings in any listbox then you MUST not assign them different
385 * IDs and expect to get meaningful results back.
387 void dlg_listbox_addwithid(union control *ctrl, void *dlg,
388 char const *text, int id)
390 struct dlgparam *dp = (struct dlgparam *)dlg;
391 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
395 assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
396 uc->ctrl->generic.type == CTRL_LISTBOX);
397 assert(uc->listmodel);
399 dp->flags |= FLAG_UPDATING_LISTBOX;/* inhibit drag-list update function */
400 gtk_list_store_append(uc->listmodel, &iter);
401 dp->flags &= ~FLAG_UPDATING_LISTBOX;
402 gtk_list_store_set(uc->listmodel, &iter, 0, id, -1);
405 * Now go through text and divide it into columns at the tabs,
408 cols = (uc->ctrl->generic.type == CTRL_LISTBOX ? ctrl->listbox.ncols : 1);
409 cols = cols ? cols : 1;
410 for (i = 0; i < cols; i++) {
411 int collen = strcspn(text, "\t");
412 char *tmpstr = snewn(collen+1, char);
413 memcpy(tmpstr, text, collen);
414 tmpstr[collen] = '\0';
415 gtk_list_store_set(uc->listmodel, &iter, i+1, tmpstr, -1);
422 int dlg_listbox_getid(union control *ctrl, void *dlg, int index)
424 struct dlgparam *dp = (struct dlgparam *)dlg;
425 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
430 assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
431 uc->ctrl->generic.type == CTRL_LISTBOX);
432 assert(uc->listmodel != NULL);
434 path = gtk_tree_path_new_from_indices(index, -1);
435 gtk_tree_model_get_iter(GTK_TREE_MODEL(uc->listmodel), &iter, path);
436 gtk_tree_model_get(GTK_TREE_MODEL(uc->listmodel), &iter, 0, &ret, -1);
437 gtk_tree_path_free(path);
442 /* dlg_listbox_index returns <0 if no single element is selected. */
443 int dlg_listbox_index(union control *ctrl, void *dlg)
445 struct dlgparam *dp = (struct dlgparam *)dlg;
446 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
448 assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
449 uc->ctrl->generic.type == CTRL_LISTBOX);
452 * We have to do this completely differently for a combo box
453 * (editable or otherwise) and a full-size list box.
457 * This API function already does the right thing in the
458 * case of no current selection.
460 return gtk_combo_box_get_active(GTK_COMBO_BOX(uc->combo));
462 GtkTreeSelection *treesel;
468 assert(uc->list != NULL);
469 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->list));
471 if (gtk_tree_selection_count_selected_rows(treesel) != 1)
474 sellist = gtk_tree_selection_get_selected_rows(treesel, NULL);
476 assert(sellist && sellist->data);
477 path = sellist->data;
479 if (gtk_tree_path_get_depth(path) != 1) {
482 indices = gtk_tree_path_get_indices(path);
490 g_list_foreach(sellist, (GFunc)gtk_tree_path_free, NULL);
491 g_list_free(sellist);
497 int dlg_listbox_issel(union control *ctrl, void *dlg, int index)
499 struct dlgparam *dp = (struct dlgparam *)dlg;
500 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
502 assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
503 uc->ctrl->generic.type == CTRL_LISTBOX);
506 * We have to do this completely differently for a combo box
507 * (editable or otherwise) and a full-size list box.
511 * This API function already does the right thing in the
512 * case of no current selection.
514 return gtk_combo_box_get_active(GTK_COMBO_BOX(uc->combo)) == index;
516 GtkTreeSelection *treesel;
520 assert(uc->list != NULL);
521 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->list));
523 path = gtk_tree_path_new_from_indices(index, -1);
524 ret = gtk_tree_selection_path_is_selected(treesel, path);
525 gtk_tree_path_free(path);
531 void dlg_listbox_select(union control *ctrl, void *dlg, int index)
533 struct dlgparam *dp = (struct dlgparam *)dlg;
534 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
536 assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
537 uc->ctrl->generic.type == CTRL_LISTBOX);
540 * We have to do this completely differently for a combo box
541 * (editable or otherwise) and a full-size list box.
544 gtk_combo_box_set_active(GTK_COMBO_BOX(uc->combo), index);
546 GtkTreeSelection *treesel;
549 assert(uc->list != NULL);
550 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->list));
552 path = gtk_tree_path_new_from_indices(index, -1);
553 gtk_tree_selection_select_path(treesel, path);
554 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(uc->list),
555 path, NULL, FALSE, 0.0, 0.0);
556 gtk_tree_path_free(path);
560 void dlg_text_set(union control *ctrl, void *dlg, char const *text)
562 struct dlgparam *dp = (struct dlgparam *)dlg;
563 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
565 assert(uc->ctrl->generic.type == CTRL_TEXT);
566 assert(uc->text != NULL);
568 gtk_label_set_text(GTK_LABEL(uc->text), text);
571 void dlg_label_change(union control *ctrl, void *dlg, char const *text)
573 struct dlgparam *dp = (struct dlgparam *)dlg;
574 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
576 switch (uc->ctrl->generic.type) {
578 gtk_label_set_text(GTK_LABEL(uc->toplevel), text);
579 shortcut_highlight(uc->toplevel, ctrl->button.shortcut);
582 gtk_label_set_text(GTK_LABEL(uc->toplevel), text);
583 shortcut_highlight(uc->toplevel, ctrl->checkbox.shortcut);
586 gtk_label_set_text(GTK_LABEL(uc->label), text);
587 shortcut_highlight(uc->label, ctrl->radio.shortcut);
590 gtk_label_set_text(GTK_LABEL(uc->label), text);
591 shortcut_highlight(uc->label, ctrl->editbox.shortcut);
593 case CTRL_FILESELECT:
594 gtk_label_set_text(GTK_LABEL(uc->label), text);
595 shortcut_highlight(uc->label, ctrl->fileselect.shortcut);
597 case CTRL_FONTSELECT:
598 gtk_label_set_text(GTK_LABEL(uc->label), text);
599 shortcut_highlight(uc->label, ctrl->fontselect.shortcut);
602 gtk_label_set_text(GTK_LABEL(uc->label), text);
603 shortcut_highlight(uc->label, ctrl->listbox.shortcut);
606 assert(!"This shouldn't happen");
611 void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)
613 struct dlgparam *dp = (struct dlgparam *)dlg;
614 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
615 assert(uc->ctrl->generic.type == CTRL_FILESELECT);
616 assert(uc->entry != NULL);
617 gtk_entry_set_text(GTK_ENTRY(uc->entry), fn.path);
620 void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn)
622 struct dlgparam *dp = (struct dlgparam *)dlg;
623 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
624 assert(uc->ctrl->generic.type == CTRL_FILESELECT);
625 assert(uc->entry != NULL);
626 strncpy(fn->path, gtk_entry_get_text(GTK_ENTRY(uc->entry)),
628 fn->path[lenof(fn->path)-1] = '\0';
631 void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs)
633 struct dlgparam *dp = (struct dlgparam *)dlg;
634 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
635 assert(uc->ctrl->generic.type == CTRL_FONTSELECT);
636 assert(uc->entry != NULL);
637 gtk_entry_set_text(GTK_ENTRY(uc->entry), fs.name);
640 void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs)
642 struct dlgparam *dp = (struct dlgparam *)dlg;
643 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
644 assert(uc->ctrl->generic.type == CTRL_FONTSELECT);
645 assert(uc->entry != NULL);
646 strncpy(fs->name, gtk_entry_get_text(GTK_ENTRY(uc->entry)),
648 fs->name[lenof(fs->name)-1] = '\0';
652 * Bracketing a large set of updates in these two functions will
653 * cause the front end (if possible) to delay updating the screen
654 * until it's all complete, thus avoiding flicker.
656 void dlg_update_start(union control *ctrl, void *dlg)
659 * Apparently we can't do this at all in GTK. GtkCList supports
660 * freeze and thaw, but not GtkList. Bah.
664 void dlg_update_done(union control *ctrl, void *dlg)
667 * Apparently we can't do this at all in GTK. GtkCList supports
668 * freeze and thaw, but not GtkList. Bah.
672 void dlg_set_focus(union control *ctrl, void *dlg)
674 struct dlgparam *dp = (struct dlgparam *)dlg;
675 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
677 switch (ctrl->generic.type) {
680 /* Check boxes and buttons get the focus _and_ get toggled. */
681 gtk_widget_grab_focus(uc->toplevel);
683 case CTRL_FILESELECT:
684 case CTRL_FONTSELECT:
687 /* Anything containing an edit box gets that focused. */
688 gtk_widget_grab_focus(uc->entry);
689 } else if (uc->combo) {
690 /* Failing that, there'll be a combo box. */
691 gtk_widget_grab_focus(uc->combo);
696 * Radio buttons: we find the currently selected button and
701 for (i = 0; i < ctrl->radio.nbuttons; i++)
702 if (gtk_toggle_button_get_active
703 (GTK_TOGGLE_BUTTON(uc->buttons[i]))) {
704 gtk_widget_grab_focus(uc->buttons[i]);
710 * There might be a combo box (drop-down list) here, or a
714 gtk_widget_grab_focus(uc->list);
715 } else if (uc->combo) {
716 gtk_widget_grab_focus(uc->combo);
723 * During event processing, you might well want to give an error
724 * indication to the user. dlg_beep() is a quick and easy generic
725 * error; dlg_error() puts up a message-box or equivalent.
727 void dlg_beep(void *dlg)
732 static void errmsg_button_clicked(GtkButton *button, gpointer data)
734 gtk_widget_destroy(GTK_WIDGET(data));
737 static void set_transient_window_pos(GtkWidget *parent, GtkWidget *child)
739 gint x, y, w, h, dx, dy;
741 gtk_window_set_position(GTK_WINDOW(child), GTK_WIN_POS_NONE);
742 gtk_widget_size_request(GTK_WIDGET(child), &req);
744 gdk_window_get_origin(GTK_WIDGET(parent)->window, &x, &y);
745 gdk_window_get_size(GTK_WIDGET(parent)->window, &w, &h);
748 * One corner of the transient will be offset inwards, by 1/4
749 * of the parent window's size, from the corresponding corner
750 * of the parent window. The corner will be chosen so as to
751 * place the transient closer to the centre of the screen; this
752 * should avoid transients going off the edge of the screen on
755 if (x + w/2 < gdk_screen_width() / 2)
756 dx = x + w/4; /* work from left edges */
758 dx = x + 3*w/4 - req.width; /* work from right edges */
759 if (y + h/2 < gdk_screen_height() / 2)
760 dy = y + h/4; /* work from top edges */
762 dy = y + 3*h/4 - req.height; /* work from bottom edges */
763 gtk_widget_set_uposition(GTK_WIDGET(child), dx, dy);
766 void dlg_error_msg(void *dlg, char *msg)
768 struct dlgparam *dp = (struct dlgparam *)dlg;
769 GtkWidget *window, *hbox, *text, *ok;
771 window = gtk_dialog_new();
772 text = gtk_label_new(msg);
773 gtk_misc_set_alignment(GTK_MISC(text), 0.0, 0.0);
774 hbox = gtk_hbox_new(FALSE, 0);
775 gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 20);
776 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),
777 hbox, FALSE, FALSE, 20);
778 gtk_widget_show(text);
779 gtk_widget_show(hbox);
780 gtk_window_set_title(GTK_WINDOW(window), "Error");
781 gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
782 ok = gtk_button_new_with_label("OK");
783 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(window)->action_area),
784 ok, FALSE, FALSE, 0);
786 GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
787 gtk_window_set_default(GTK_WINDOW(window), ok);
788 gtk_signal_connect(GTK_OBJECT(ok), "clicked",
789 GTK_SIGNAL_FUNC(errmsg_button_clicked), window);
790 gtk_signal_connect(GTK_OBJECT(window), "destroy",
791 GTK_SIGNAL_FUNC(window_destroy), NULL);
792 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
793 gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(dp->window));
794 set_transient_window_pos(dp->window, window);
795 gtk_widget_show(window);
800 * This function signals to the front end that the dialog's
801 * processing is completed, and passes an integer value (typically
804 void dlg_end(void *dlg, int value)
806 struct dlgparam *dp = (struct dlgparam *)dlg;
808 gtk_widget_destroy(dp->window);
811 void dlg_refresh(union control *ctrl, void *dlg)
813 struct dlgparam *dp = (struct dlgparam *)dlg;
817 if (ctrl->generic.handler != NULL)
818 ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH);
822 for (i = 0; (uc = index234(dp->byctrl, i)) != NULL; i++) {
823 assert(uc->ctrl != NULL);
824 if (uc->ctrl->generic.handler != NULL)
825 uc->ctrl->generic.handler(uc->ctrl, dp,
826 dp->data, EVENT_REFRESH);
831 void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b)
833 struct dlgparam *dp = (struct dlgparam *)dlg;
834 struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
837 GtkWidget *coloursel =
838 gtk_color_selection_dialog_new("Select a colour");
839 GtkColorSelectionDialog *ccs = GTK_COLOR_SELECTION_DIALOG(coloursel);
841 dp->coloursel_result.ok = FALSE;
843 gtk_window_set_modal(GTK_WINDOW(coloursel), TRUE);
844 #if GTK_CHECK_VERSION(2,0,0)
845 gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(ccs->colorsel), FALSE);
847 gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(ccs->colorsel), FALSE);
849 cvals[0] = r / 255.0;
850 cvals[1] = g / 255.0;
851 cvals[2] = b / 255.0;
852 cvals[3] = 1.0; /* fully opaque! */
853 gtk_color_selection_set_color(GTK_COLOR_SELECTION(ccs->colorsel), cvals);
855 gtk_object_set_data(GTK_OBJECT(ccs->ok_button), "user-data",
856 (gpointer)coloursel);
857 gtk_object_set_data(GTK_OBJECT(ccs->cancel_button), "user-data",
858 (gpointer)coloursel);
859 gtk_object_set_data(GTK_OBJECT(coloursel), "user-data", (gpointer)uc);
860 gtk_signal_connect(GTK_OBJECT(ccs->ok_button), "clicked",
861 GTK_SIGNAL_FUNC(coloursel_ok), (gpointer)dp);
862 gtk_signal_connect(GTK_OBJECT(ccs->cancel_button), "clicked",
863 GTK_SIGNAL_FUNC(coloursel_cancel), (gpointer)dp);
864 gtk_signal_connect_object(GTK_OBJECT(ccs->ok_button), "clicked",
865 GTK_SIGNAL_FUNC(gtk_widget_destroy),
866 (gpointer)coloursel);
867 gtk_signal_connect_object(GTK_OBJECT(ccs->cancel_button), "clicked",
868 GTK_SIGNAL_FUNC(gtk_widget_destroy),
869 (gpointer)coloursel);
870 gtk_widget_show(coloursel);
873 int dlg_coloursel_results(union control *ctrl, void *dlg,
874 int *r, int *g, int *b)
876 struct dlgparam *dp = (struct dlgparam *)dlg;
877 if (dp->coloursel_result.ok) {
878 *r = dp->coloursel_result.r;
879 *g = dp->coloursel_result.g;
880 *b = dp->coloursel_result.b;
886 /* ----------------------------------------------------------------------
887 * Signal handlers while the dialog box is active.
890 static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event,
893 struct dlgparam *dp = (struct dlgparam *)data;
894 struct uctrl *uc = dlg_find_bywidget(dp, widget);
895 union control *focus;
902 if (focus != dp->currfocus) {
903 dp->lastfocus = dp->currfocus;
904 dp->currfocus = focus;
910 static void button_clicked(GtkButton *button, gpointer data)
912 struct dlgparam *dp = (struct dlgparam *)data;
913 struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
914 uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);
917 static void button_toggled(GtkToggleButton *tb, gpointer data)
919 struct dlgparam *dp = (struct dlgparam *)data;
920 struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(tb));
921 uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);
924 static gboolean editbox_key(GtkWidget *widget, GdkEventKey *event,
928 * GtkEntry has a nasty habit of eating the Return key, which
929 * is unhelpful since it doesn't actually _do_ anything with it
930 * (it calls gtk_widget_activate, but our edit boxes never need
931 * activating). So I catch Return before GtkEntry sees it, and
932 * pass it straight on to the parent widget. Effect: hitting
933 * Return in an edit box will now activate the default button
934 * in the dialog just like it will everywhere else.
936 if (event->keyval == GDK_Return && widget->parent != NULL) {
938 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");
939 gtk_signal_emit_by_name(GTK_OBJECT(widget->parent), "key_press_event",
946 static void editbox_changed(GtkEditable *ed, gpointer data)
948 struct dlgparam *dp = (struct dlgparam *)data;
949 if (!(dp->flags & FLAG_UPDATING_COMBO_LIST)) {
950 struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed));
951 uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE);
955 static gboolean editbox_lostfocus(GtkWidget *ed, GdkEventFocus *event,
958 struct dlgparam *dp = (struct dlgparam *)data;
959 struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed));
960 uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_REFRESH);
964 static void listbox_doubleclick(GtkTreeView *treeview, GtkTreePath *path,
965 GtkTreeViewColumn *column, gpointer data)
967 struct dlgparam *dp = (struct dlgparam *)data;
968 struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(treeview));
970 uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION);
973 static void droplist_selchange(GtkComboBox *combo, gpointer data)
975 struct dlgparam *dp = (struct dlgparam *)data;
976 struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(combo));
978 uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);
981 static void listbox_selchange(GtkTreeSelection *treeselection,
984 struct dlgparam *dp = (struct dlgparam *)data;
985 GtkTreeView *tree = gtk_tree_selection_get_tree_view(treeselection);
986 struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(tree));
988 uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE);
991 struct draglist_valchange_ctx {
996 static gboolean draglist_valchange(gpointer data)
998 struct draglist_valchange_ctx *ctx =
999 (struct draglist_valchange_ctx *)data;
1001 ctx->uc->ctrl->generic.handler(ctx->uc->ctrl, ctx->dp,
1002 ctx->dp->data, EVENT_VALCHANGE);
1009 static void listbox_reorder(GtkTreeModel *treemodel, GtkTreePath *path,
1010 GtkTreeIter *iter, gpointer data)
1012 struct dlgparam *dp = (struct dlgparam *)data;
1016 if (dp->flags & FLAG_UPDATING_LISTBOX)
1017 return; /* not a user drag operation */
1019 tree = g_object_get_data(G_OBJECT(treemodel), "user-data");
1020 uc = dlg_find_bywidget(dp, GTK_WIDGET(tree));
1023 * We should cause EVENT_VALCHANGE on the list box, now
1024 * that its rows have been reordered. However, the GTK 2
1025 * docs say that at the point this signal is received the
1026 * new row might not have actually been filled in yet.
1028 * (So what smegging use is it then, eh? Don't suppose it
1029 * occurred to you at any point that letting the
1030 * application know _after_ the reordering was compelete
1031 * might be helpful to someone?)
1033 * To get round this, I schedule an idle function, which I
1034 * hope won't be called until the main event loop is
1035 * re-entered after the drag-and-drop handler has finished
1036 * furtling with the list store.
1038 struct draglist_valchange_ctx *ctx =
1039 snew(struct draglist_valchange_ctx);
1042 g_idle_add(draglist_valchange, ctx);
1046 static void filesel_ok(GtkButton *button, gpointer data)
1048 /* struct dlgparam *dp = (struct dlgparam *)data; */
1049 gpointer filesel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
1050 struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(filesel), "user-data");
1051 const char *name = gtk_file_selection_get_filename
1052 (GTK_FILE_SELECTION(filesel));
1053 gtk_entry_set_text(GTK_ENTRY(uc->entry), name);
1056 static void fontsel_ok(GtkButton *button, gpointer data)
1058 /* struct dlgparam *dp = (struct dlgparam *)data; */
1059 unifontsel *fontsel = (unifontsel *)gtk_object_get_data
1060 (GTK_OBJECT(button), "user-data");
1061 struct uctrl *uc = (struct uctrl *)fontsel->user_data;
1062 char *name = unifontsel_get_name(fontsel);
1063 assert(name); /* should always be ok after OK pressed */
1064 gtk_entry_set_text(GTK_ENTRY(uc->entry), name);
1068 static void coloursel_ok(GtkButton *button, gpointer data)
1070 struct dlgparam *dp = (struct dlgparam *)data;
1071 gpointer coloursel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
1072 struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(coloursel), "user-data");
1074 gtk_color_selection_get_color
1075 (GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(coloursel)->colorsel),
1077 dp->coloursel_result.r = (int) (255 * cvals[0]);
1078 dp->coloursel_result.g = (int) (255 * cvals[1]);
1079 dp->coloursel_result.b = (int) (255 * cvals[2]);
1080 dp->coloursel_result.ok = TRUE;
1081 uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
1084 static void coloursel_cancel(GtkButton *button, gpointer data)
1086 struct dlgparam *dp = (struct dlgparam *)data;
1087 gpointer coloursel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
1088 struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(coloursel), "user-data");
1089 dp->coloursel_result.ok = FALSE;
1090 uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK);
1093 static void filefont_clicked(GtkButton *button, gpointer data)
1095 struct dlgparam *dp = (struct dlgparam *)data;
1096 struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button));
1098 if (uc->ctrl->generic.type == CTRL_FILESELECT) {
1099 GtkWidget *filesel =
1100 gtk_file_selection_new(uc->ctrl->fileselect.title);
1101 gtk_window_set_modal(GTK_WINDOW(filesel), TRUE);
1103 (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data",
1105 gtk_object_set_data(GTK_OBJECT(filesel), "user-data", (gpointer)uc);
1107 (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
1108 GTK_SIGNAL_FUNC(filesel_ok), (gpointer)dp);
1109 gtk_signal_connect_object
1110 (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
1111 GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel);
1112 gtk_signal_connect_object
1113 (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked",
1114 GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel);
1115 gtk_widget_show(filesel);
1118 if (uc->ctrl->generic.type == CTRL_FONTSELECT) {
1119 const gchar *fontname = gtk_entry_get_text(GTK_ENTRY(uc->entry));
1120 unifontsel *fontsel = unifontsel_new("Select a font");
1122 gtk_window_set_modal(fontsel->window, TRUE);
1123 unifontsel_set_name(fontsel, fontname);
1125 gtk_object_set_data(GTK_OBJECT(fontsel->ok_button),
1126 "user-data", (gpointer)fontsel);
1127 fontsel->user_data = uc;
1128 gtk_signal_connect(GTK_OBJECT(fontsel->ok_button), "clicked",
1129 GTK_SIGNAL_FUNC(fontsel_ok), (gpointer)dp);
1130 gtk_signal_connect_object(GTK_OBJECT(fontsel->ok_button), "clicked",
1131 GTK_SIGNAL_FUNC(unifontsel_destroy),
1133 gtk_signal_connect_object(GTK_OBJECT(fontsel->cancel_button),"clicked",
1134 GTK_SIGNAL_FUNC(unifontsel_destroy),
1137 gtk_widget_show(GTK_WIDGET(fontsel->window));
1141 static void label_sizealloc(GtkWidget *widget, GtkAllocation *alloc,
1144 struct dlgparam *dp = (struct dlgparam *)data;
1145 struct uctrl *uc = dlg_find_bywidget(dp, widget);
1147 gtk_widget_set_usize(uc->text, alloc->width, -1);
1148 gtk_label_set_text(GTK_LABEL(uc->text), uc->ctrl->generic.label);
1149 gtk_signal_disconnect(GTK_OBJECT(uc->text), uc->textsig);
1152 /* ----------------------------------------------------------------------
1153 * This function does the main layout work: it reads a controlset,
1154 * it creates the relevant GTK controls, and returns a GtkWidget
1155 * containing the result. (This widget might be a title of some
1156 * sort, it might be a Columns containing many controls, or it
1157 * might be a GtkFrame containing a Columns; whatever it is, it's
1158 * definitely a GtkWidget and should probably be added to a
1161 * `win' is required for setting the default button. If it is
1162 * non-NULL, all buttons created will be default-capable (so they
1163 * have extra space round them for the default highlight).
1165 GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
1166 struct controlset *s, GtkWindow *win)
1172 if (!s->boxname && s->boxtitle) {
1173 /* This controlset is a panel title. */
1174 return gtk_label_new(s->boxtitle);
1178 * Otherwise, we expect to be laying out actual controls, so
1179 * we'll start by creating a Columns for the purpose.
1181 cols = COLUMNS(columns_new(4));
1182 ret = GTK_WIDGET(cols);
1183 gtk_widget_show(ret);
1186 * Create a containing frame if we have a box name.
1189 ret = gtk_frame_new(s->boxtitle); /* NULL is valid here */
1190 gtk_container_set_border_width(GTK_CONTAINER(cols), 4);
1191 gtk_container_add(GTK_CONTAINER(ret), GTK_WIDGET(cols));
1192 gtk_widget_show(ret);
1196 * Now iterate through the controls themselves, create them,
1197 * and add them to the Columns.
1199 for (i = 0; i < s->ncontrols; i++) {
1200 union control *ctrl = s->ctrls[i];
1203 GtkWidget *w = NULL;
1205 switch (ctrl->generic.type) {
1208 static const int simplecols[1] = { 100 };
1209 columns_set_cols(cols, ctrl->columns.ncols,
1210 (ctrl->columns.percentages ?
1211 ctrl->columns.percentages : simplecols));
1213 continue; /* no actual control created */
1216 struct uctrl *uc = dlg_find_byctrl(dp, ctrl->tabdelay.ctrl);
1218 columns_taborder_last(cols, uc->toplevel);
1220 continue; /* no actual control created */
1223 uc = snew(struct uctrl);
1225 uc->privdata = NULL;
1226 uc->privdata_needs_free = FALSE;
1228 uc->entry = uc->combo = uc->list = NULL;
1229 uc->listmodel = NULL;
1230 uc->button = uc->text = NULL;
1234 switch (ctrl->generic.type) {
1236 w = gtk_button_new_with_label(ctrl->generic.label);
1238 GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
1239 if (ctrl->button.isdefault)
1240 gtk_window_set_default(win, w);
1241 if (ctrl->button.iscancel)
1242 dp->cancelbutton = w;
1244 gtk_signal_connect(GTK_OBJECT(w), "clicked",
1245 GTK_SIGNAL_FUNC(button_clicked), dp);
1246 gtk_signal_connect(GTK_OBJECT(w), "focus_in_event",
1247 GTK_SIGNAL_FUNC(widget_focus), dp);
1248 shortcut_add(scs, GTK_BIN(w)->child, ctrl->button.shortcut,
1249 SHORTCUT_UCTRL, uc);
1252 w = gtk_check_button_new_with_label(ctrl->generic.label);
1253 gtk_signal_connect(GTK_OBJECT(w), "toggled",
1254 GTK_SIGNAL_FUNC(button_toggled), dp);
1255 gtk_signal_connect(GTK_OBJECT(w), "focus_in_event",
1256 GTK_SIGNAL_FUNC(widget_focus), dp);
1257 shortcut_add(scs, GTK_BIN(w)->child, ctrl->checkbox.shortcut,
1258 SHORTCUT_UCTRL, uc);
1263 * Radio buttons get to go inside their own Columns, no
1267 gint i, *percentages;
1271 if (ctrl->generic.label) {
1272 GtkWidget *label = gtk_label_new(ctrl->generic.label);
1273 columns_add(COLUMNS(w), label, 0, 1);
1274 columns_force_left_align(COLUMNS(w), label);
1275 gtk_widget_show(label);
1276 shortcut_add(scs, label, ctrl->radio.shortcut,
1277 SHORTCUT_UCTRL, uc);
1280 percentages = g_new(gint, ctrl->radio.ncolumns);
1281 for (i = 0; i < ctrl->radio.ncolumns; i++) {
1283 ((100 * (i+1) / ctrl->radio.ncolumns) -
1284 100 * i / ctrl->radio.ncolumns);
1286 columns_set_cols(COLUMNS(w), ctrl->radio.ncolumns,
1288 g_free(percentages);
1291 uc->nbuttons = ctrl->radio.nbuttons;
1292 uc->buttons = snewn(uc->nbuttons, GtkWidget *);
1294 for (i = 0; i < ctrl->radio.nbuttons; i++) {
1298 b = (gtk_radio_button_new_with_label
1299 (group, ctrl->radio.buttons[i]));
1301 group = gtk_radio_button_group(GTK_RADIO_BUTTON(b));
1302 colstart = i % ctrl->radio.ncolumns;
1303 columns_add(COLUMNS(w), b, colstart,
1304 (i == ctrl->radio.nbuttons-1 ?
1305 ctrl->radio.ncolumns - colstart : 1));
1306 columns_force_left_align(COLUMNS(w), b);
1308 gtk_signal_connect(GTK_OBJECT(b), "toggled",
1309 GTK_SIGNAL_FUNC(button_toggled), dp);
1310 gtk_signal_connect(GTK_OBJECT(b), "focus_in_event",
1311 GTK_SIGNAL_FUNC(widget_focus), dp);
1312 if (ctrl->radio.shortcuts) {
1313 shortcut_add(scs, GTK_BIN(b)->child,
1314 ctrl->radio.shortcuts[i],
1315 SHORTCUT_UCTRL, uc);
1324 if (ctrl->editbox.has_list) {
1325 uc->listmodel = gtk_list_store_new(2, G_TYPE_INT,
1327 w = gtk_combo_box_entry_new_with_model
1328 (GTK_TREE_MODEL(uc->listmodel), 1);
1329 /* We cannot support password combo boxes. */
1330 assert(!ctrl->editbox.password);
1333 gtk_signal_connect(GTK_OBJECT(uc->combo), "changed",
1334 GTK_SIGNAL_FUNC(editbox_changed), dp);
1335 gtk_signal_connect(GTK_OBJECT(uc->combo), "key_press_event",
1336 GTK_SIGNAL_FUNC(editbox_key), dp);
1337 gtk_signal_connect(GTK_OBJECT(uc->combo), "focus_in_event",
1338 GTK_SIGNAL_FUNC(widget_focus), dp);
1339 gtk_signal_connect(GTK_OBJECT(uc->combo), "focus_out_event",
1340 GTK_SIGNAL_FUNC(editbox_lostfocus), dp);
1342 w = gtk_entry_new();
1343 if (ctrl->editbox.password)
1344 gtk_entry_set_visibility(GTK_ENTRY(w), FALSE);
1347 gtk_signal_connect(GTK_OBJECT(uc->entry), "changed",
1348 GTK_SIGNAL_FUNC(editbox_changed), dp);
1349 gtk_signal_connect(GTK_OBJECT(uc->entry), "key_press_event",
1350 GTK_SIGNAL_FUNC(editbox_key), dp);
1351 gtk_signal_connect(GTK_OBJECT(uc->entry), "focus_in_event",
1352 GTK_SIGNAL_FUNC(widget_focus), dp);
1353 gtk_signal_connect(GTK_OBJECT(uc->entry), "focus_out_event",
1354 GTK_SIGNAL_FUNC(editbox_lostfocus), dp);
1357 * Edit boxes, for some strange reason, have a minimum
1358 * width of 150 in GTK 1.2. We don't want this - we'd
1359 * rather the edit boxes acquired their natural width
1360 * from the column layout of the rest of the box.
1362 * Also, while we're here, we'll squirrel away the
1363 * edit box height so we can use that to centre its
1364 * label vertically beside it.
1366 gtk_widget_size_request(w, &req);
1367 gtk_widget_set_usize(w, 10, req.height);
1369 if (ctrl->generic.label) {
1370 GtkWidget *label, *container;
1372 label = gtk_label_new(ctrl->generic.label);
1374 shortcut_add(scs, label, ctrl->editbox.shortcut,
1375 SHORTCUT_FOCUS, uc->entry);
1377 container = columns_new(4);
1378 if (ctrl->editbox.percentwidth == 100) {
1379 columns_add(COLUMNS(container), label, 0, 1);
1380 columns_force_left_align(COLUMNS(container), label);
1381 columns_add(COLUMNS(container), w, 0, 1);
1383 gint percentages[2];
1384 percentages[1] = ctrl->editbox.percentwidth;
1385 percentages[0] = 100 - ctrl->editbox.percentwidth;
1386 columns_set_cols(COLUMNS(container), 2, percentages);
1387 columns_add(COLUMNS(container), label, 0, 1);
1388 columns_force_left_align(COLUMNS(container), label);
1389 columns_add(COLUMNS(container), w, 1, 1);
1390 /* Centre the label vertically. */
1391 gtk_widget_set_usize(label, -1, req.height);
1392 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1394 gtk_widget_show(label);
1402 case CTRL_FILESELECT:
1403 case CTRL_FONTSELECT:
1408 (ctrl->generic.type == CTRL_FILESELECT ?
1409 "Browse..." : "Change...");
1411 gint percentages[] = { 75, 25 };
1413 columns_set_cols(COLUMNS(w), 2, percentages);
1415 if (ctrl->generic.label) {
1416 ww = gtk_label_new(ctrl->generic.label);
1417 columns_add(COLUMNS(w), ww, 0, 2);
1418 columns_force_left_align(COLUMNS(w), ww);
1419 gtk_widget_show(ww);
1420 shortcut_add(scs, ww,
1421 (ctrl->generic.type == CTRL_FILESELECT ?
1422 ctrl->fileselect.shortcut :
1423 ctrl->fontselect.shortcut),
1424 SHORTCUT_UCTRL, uc);
1428 uc->entry = ww = gtk_entry_new();
1429 gtk_widget_size_request(ww, &req);
1430 gtk_widget_set_usize(ww, 10, req.height);
1431 columns_add(COLUMNS(w), ww, 0, 1);
1432 gtk_widget_show(ww);
1434 uc->button = ww = gtk_button_new_with_label(browsebtn);
1435 columns_add(COLUMNS(w), ww, 1, 1);
1436 gtk_widget_show(ww);
1438 gtk_signal_connect(GTK_OBJECT(uc->entry), "key_press_event",
1439 GTK_SIGNAL_FUNC(editbox_key), dp);
1441 gtk_signal_connect(GTK_OBJECT(uc->entry), "changed",
1442 GTK_SIGNAL_FUNC(editbox_changed), dp);
1443 gtk_signal_connect(GTK_OBJECT(uc->entry), "focus_in_event",
1444 GTK_SIGNAL_FUNC(widget_focus), dp);
1445 gtk_signal_connect(GTK_OBJECT(uc->button), "focus_in_event",
1446 GTK_SIGNAL_FUNC(widget_focus), dp);
1447 gtk_signal_connect(GTK_OBJECT(ww), "clicked",
1448 GTK_SIGNAL_FUNC(filefont_clicked), dp);
1453 * First construct the list data store, with the right
1454 * number of columns.
1461 cols = ctrl->listbox.ncols;
1462 cols = cols ? cols : 1;
1463 types = snewn(1 + cols, GType);
1465 types[0] = G_TYPE_INT;
1466 for (i = 0; i < cols; i++)
1467 types[i+1] = G_TYPE_STRING;
1469 uc->listmodel = gtk_list_store_newv(1 + cols, types);
1475 * Drop-down lists are done completely differently.
1477 if (ctrl->listbox.height == 0) {
1478 GtkCellRenderer *cr;
1481 * Create a non-editable GtkComboBox (that is, not
1482 * its subclass GtkComboBoxEntry).
1484 w = gtk_combo_box_new_with_model
1485 (GTK_TREE_MODEL(uc->listmodel));
1489 * Tell it how to render a list item (i.e. which
1490 * column to look at in the list model).
1492 cr = gtk_cell_renderer_text_new();
1493 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), cr, TRUE);
1494 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), cr,
1498 * And tell it to notify us when the selection
1501 g_signal_connect(G_OBJECT(w), "changed",
1502 G_CALLBACK(droplist_selchange), dp);
1504 GtkTreeSelection *sel;
1507 * Create the list box itself, its columns, and
1508 * its containing scrolled window.
1510 w = gtk_tree_view_new_with_model
1511 (GTK_TREE_MODEL(uc->listmodel));
1512 g_object_set_data(G_OBJECT(uc->listmodel), "user-data",
1514 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), FALSE);
1515 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(w));
1516 gtk_tree_selection_set_mode
1517 (sel, ctrl->listbox.multisel ? GTK_SELECTION_MULTIPLE :
1518 GTK_SELECTION_SINGLE);
1520 gtk_signal_connect(GTK_OBJECT(w), "row-activated",
1521 GTK_SIGNAL_FUNC(listbox_doubleclick), dp);
1522 g_signal_connect(G_OBJECT(sel), "changed",
1523 G_CALLBACK(listbox_selchange), dp);
1525 if (ctrl->listbox.draglist) {
1526 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(w), TRUE);
1527 g_signal_connect(G_OBJECT(uc->listmodel), "row-inserted",
1528 G_CALLBACK(listbox_reorder), dp);
1535 cols = ctrl->listbox.ncols;
1536 cols = cols ? cols : 1;
1537 for (i = 0; i < cols; i++) {
1538 GtkTreeViewColumn *column;
1540 * It appears that GTK 2 doesn't leave us any
1541 * particularly sensible way to honour the
1542 * "percentages" specification in the ctrl
1545 column = gtk_tree_view_column_new_with_attributes
1546 ("heading", gtk_cell_renderer_text_new(),
1547 "text", i+1, (char *)NULL);
1548 gtk_tree_view_column_set_sizing
1549 (column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1550 gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
1557 scroll = gtk_scrolled_window_new(NULL, NULL);
1558 gtk_scrolled_window_set_shadow_type
1559 (GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);
1561 gtk_container_add(GTK_CONTAINER(scroll), w);
1562 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
1563 GTK_POLICY_AUTOMATIC,
1565 gtk_widget_set_size_request
1567 ctrl->listbox.height * get_listitemheight(w));
1573 if (ctrl->generic.label) {
1574 GtkWidget *label, *container;
1577 label = gtk_label_new(ctrl->generic.label);
1579 shortcut_add(scs, label, ctrl->listbox.shortcut,
1582 container = columns_new(4);
1583 if (ctrl->listbox.percentwidth == 100) {
1584 columns_add(COLUMNS(container), label, 0, 1);
1585 columns_force_left_align(COLUMNS(container), label);
1586 columns_add(COLUMNS(container), w, 0, 1);
1588 gint percentages[2];
1589 percentages[1] = ctrl->listbox.percentwidth;
1590 percentages[0] = 100 - ctrl->listbox.percentwidth;
1591 columns_set_cols(COLUMNS(container), 2, percentages);
1592 columns_add(COLUMNS(container), label, 0, 1);
1593 columns_force_left_align(COLUMNS(container), label);
1594 columns_add(COLUMNS(container), w, 1, 1);
1595 /* Centre the label vertically. */
1596 gtk_widget_size_request(w, &req);
1597 gtk_widget_set_usize(label, -1, req.height);
1598 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1600 gtk_widget_show(label);
1610 * Wrapping text widgets don't sit well with the GTK
1611 * layout model, in which widgets state a minimum size
1612 * and the whole window then adjusts to the smallest
1613 * size it can sensibly take given its contents. A
1614 * wrapping text widget _has_ no clear minimum size;
1615 * instead it has a range of possibilities. It can be
1616 * one line deep but 2000 wide, or two lines deep and
1617 * 1000 pixels, or three by 867, or four by 500 and so
1618 * on. It can be as short as you like provided you
1619 * don't mind it being wide, or as narrow as you like
1620 * provided you don't mind it being tall.
1622 * Therefore, it fits very badly into the layout model.
1623 * Hence the only thing to do is pick a width and let
1624 * it choose its own number of lines. To do this I'm
1625 * going to cheat a little. All new wrapping text
1626 * widgets will be created with a minimal text content
1627 * "X"; then, after the rest of the dialog box is set
1628 * up and its size calculated, the text widgets will be
1629 * told their width and given their real text, which
1630 * will cause the size to be recomputed in the y
1631 * direction (because many of them will expand to more
1634 uc->text = w = gtk_label_new("X");
1635 gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.0);
1636 gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
1638 gtk_signal_connect(GTK_OBJECT(w), "size-allocate",
1639 GTK_SIGNAL_FUNC(label_sizealloc), dp);
1645 columns_add(cols, w,
1646 COLUMN_START(ctrl->generic.column),
1647 COLUMN_SPAN(ctrl->generic.column));
1649 columns_force_left_align(cols, w);
1653 dlg_add_uctrl(dp, uc);
1660 struct dlgparam *dp;
1661 GtkNotebook *panels;
1663 #if !GTK_CHECK_VERSION(2,0,0)
1664 GtkWidget *treeitem;
1666 struct Shortcuts shortcuts;
1669 #if GTK_CHECK_VERSION(2,0,0)
1670 static void treeselection_changed(GtkTreeSelection *treeselection,
1673 struct selparam *sps = (struct selparam *)data, *sp;
1674 GtkTreeModel *treemodel;
1675 GtkTreeIter treeiter;
1679 if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter))
1682 gtk_tree_model_get(treemodel, &treeiter, TREESTORE_PARAMS, &spindex, -1);
1685 page_num = gtk_notebook_page_num(sp->panels, sp->panel);
1686 gtk_notebook_set_page(sp->panels, page_num);
1688 dlg_refresh(NULL, sp->dp);
1690 sp->dp->shortcuts = &sp->shortcuts;
1693 static void treeitem_sel(GtkItem *item, gpointer data)
1695 struct selparam *sp = (struct selparam *)data;
1698 page_num = gtk_notebook_page_num(sp->panels, sp->panel);
1699 gtk_notebook_set_page(sp->panels, page_num);
1701 dlg_refresh(NULL, sp->dp);
1703 sp->dp->shortcuts = &sp->shortcuts;
1704 sp->dp->currtreeitem = sp->treeitem;
1708 static void window_destroy(GtkWidget *widget, gpointer data)
1713 #if !GTK_CHECK_VERSION(2,0,0)
1714 static int tree_grab_focus(struct dlgparam *dp)
1719 * See if any of the treeitems has the focus.
1722 for (i = 0; i < dp->ntreeitems; i++)
1723 if (GTK_WIDGET_HAS_FOCUS(dp->treeitems[i])) {
1731 gtk_widget_grab_focus(dp->currtreeitem);
1736 gint tree_focus(GtkContainer *container, GtkDirectionType direction,
1739 struct dlgparam *dp = (struct dlgparam *)data;
1741 gtk_signal_emit_stop_by_name(GTK_OBJECT(container), "focus");
1743 * If there's a focused treeitem, we return FALSE to cause the
1744 * focus to move on to some totally other control. If not, we
1745 * focus the selected one.
1747 return tree_grab_focus(dp);
1751 int win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
1753 struct dlgparam *dp = (struct dlgparam *)data;
1755 if (event->keyval == GDK_Escape && dp->cancelbutton) {
1756 gtk_signal_emit_by_name(GTK_OBJECT(dp->cancelbutton), "clicked");
1760 if ((event->state & GDK_MOD1_MASK) &&
1761 (unsigned char)event->string[0] > 0 &&
1762 (unsigned char)event->string[0] <= 127) {
1763 int schr = (unsigned char)event->string[0];
1764 struct Shortcut *sc = &dp->shortcuts->sc[schr];
1766 switch (sc->action) {
1768 #if GTK_CHECK_VERSION(2,0,0)
1769 gtk_widget_grab_focus(sc->widget);
1771 tree_grab_focus(dp);
1774 case SHORTCUT_FOCUS:
1775 gtk_widget_grab_focus(sc->widget);
1777 case SHORTCUT_UCTRL:
1779 * We must do something sensible with a uctrl.
1780 * Precisely what this is depends on the type of
1783 switch (sc->uc->ctrl->generic.type) {
1786 /* Check boxes and buttons get the focus _and_ get toggled. */
1787 gtk_widget_grab_focus(sc->uc->toplevel);
1788 gtk_signal_emit_by_name(GTK_OBJECT(sc->uc->toplevel),
1791 case CTRL_FILESELECT:
1792 case CTRL_FONTSELECT:
1793 /* File/font selectors have their buttons pressed (ooer),
1794 * and focus transferred to the edit box. */
1795 gtk_signal_emit_by_name(GTK_OBJECT(sc->uc->button),
1797 gtk_widget_grab_focus(sc->uc->entry);
1801 * Radio buttons are fun, because they have
1802 * multiple shortcuts. We must find whether the
1803 * activated shortcut is the shortcut for the whole
1804 * group, or for a particular button. In the former
1805 * case, we find the currently selected button and
1806 * focus it; in the latter, we focus-and-click the
1807 * button whose shortcut was pressed.
1809 if (schr == sc->uc->ctrl->radio.shortcut) {
1811 for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++)
1812 if (gtk_toggle_button_get_active
1813 (GTK_TOGGLE_BUTTON(sc->uc->buttons[i]))) {
1814 gtk_widget_grab_focus(sc->uc->buttons[i]);
1816 } else if (sc->uc->ctrl->radio.shortcuts) {
1818 for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++)
1819 if (schr == sc->uc->ctrl->radio.shortcuts[i]) {
1820 gtk_widget_grab_focus(sc->uc->buttons[i]);
1821 gtk_signal_emit_by_name
1822 (GTK_OBJECT(sc->uc->buttons[i]), "clicked");
1834 #if !GTK_CHECK_VERSION(2,0,0)
1835 int tree_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
1837 struct dlgparam *dp = (struct dlgparam *)data;
1839 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1840 event->keyval == GDK_Down || event->keyval == GDK_KP_Down) {
1842 for (i = 0; i < dp->ntreeitems; i++)
1843 if (widget == dp->treeitems[i])
1845 if (i < dp->ntreeitems) {
1846 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
1853 if (i < 0 || i >= dp->ntreeitems)
1854 break; /* nothing in that dir to select */
1856 * Determine if this tree item is visible.
1859 GtkWidget *w = dp->treeitems[i];
1861 while (w && (GTK_IS_TREE_ITEM(w) || GTK_IS_TREE(w))) {
1862 if (!GTK_WIDGET_VISIBLE(w)) {
1869 j = i; /* got one */
1875 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
1878 gtk_signal_emit_by_name(GTK_OBJECT(dp->treeitems[j]), "toggle");
1879 gtk_widget_grab_focus(dp->treeitems[j]);
1885 * It's nice for Left and Right to expand and collapse tree
1888 if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left) {
1889 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
1891 gtk_tree_item_collapse(GTK_TREE_ITEM(widget));
1894 if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right) {
1895 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
1897 gtk_tree_item_expand(GTK_TREE_ITEM(widget));
1905 static void shortcut_highlight(GtkWidget *labelw, int chr)
1907 GtkLabel *label = GTK_LABEL(labelw);
1908 gchar *currstr, *pattern;
1911 gtk_label_get(label, &currstr);
1912 for (i = 0; currstr[i]; i++)
1913 if (tolower((unsigned char)currstr[i]) == chr) {
1916 pattern = dupprintf("%*s_", i, "");
1918 gtk_widget_size_request(GTK_WIDGET(label), &req);
1919 gtk_label_set_pattern(label, pattern);
1920 gtk_widget_set_usize(GTK_WIDGET(label), -1, req.height);
1927 void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
1928 int chr, int action, void *ptr)
1930 if (chr == NO_SHORTCUT)
1933 chr = tolower((unsigned char)chr);
1935 assert(scs->sc[chr].action == SHORTCUT_EMPTY);
1937 scs->sc[chr].action = action;
1939 if (action == SHORTCUT_FOCUS) {
1940 scs->sc[chr].uc = NULL;
1941 scs->sc[chr].widget = (GtkWidget *)ptr;
1943 scs->sc[chr].widget = NULL;
1944 scs->sc[chr].uc = (struct uctrl *)ptr;
1947 shortcut_highlight(labelw, chr);
1950 int get_listitemheight(GtkWidget *w)
1953 GtkCellRenderer *cr = gtk_cell_renderer_text_new();
1954 gtk_cell_renderer_get_size(cr, w, NULL, NULL, NULL, NULL, &height);
1955 g_object_ref(G_OBJECT(cr));
1956 gtk_object_sink(GTK_OBJECT(cr));
1957 g_object_unref(G_OBJECT(cr));
1961 void set_dialog_action_area(GtkDialog *dlg, GtkWidget *w)
1963 #if !GTK_CHECK_VERSION(2,0,0)
1966 * In GTK 1, laying out the buttons at the bottom of the
1967 * configuration box is nice and easy, because a GtkDialog's
1968 * action_area is a GtkHBox which stretches to cover the full
1969 * width of the dialog. So we just put our Columns widget
1970 * straight into that hbox, and it ends up just where we want
1973 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->action_area),
1978 * In GTK 2, the action area is now a GtkHButtonBox and its
1979 * layout behaviour seems to be different: it doesn't stretch
1980 * to cover the full width of the window, but instead finds its
1981 * own preferred width and right-aligns that within the window.
1982 * This isn't what we want, because we have both left-aligned
1983 * and right-aligned buttons coming out of the above call to
1984 * layout_ctrls(), and right-aligning the whole thing will
1985 * result in the former being centred and looking weird.
1987 * So instead we abandon the dialog's action area completely:
1988 * we gtk_widget_hide() it in the below code, and we also call
1989 * gtk_dialog_set_has_separator() to remove the separator above
1990 * it. We then insert our own action area into the end of the
1991 * dialog's main vbox, and add our own separator above that.
1993 * (Ideally, if we were a native GTK app, we would use the
1994 * GtkHButtonBox's _own_ innate ability to support one set of
1995 * buttons being right-aligned and one left-aligned. But to do
1996 * that here, we would have to either (a) pick apart our cross-
1997 * platform layout structures and treat them specially for this
1998 * particular set of controls, which would be painful, or else
1999 * (b) develop a special and simpler cross-platform
2000 * representation for these particular controls, and introduce
2001 * special-case code into all the _other_ platforms to handle
2002 * it. Neither appeals. Therefore, I regretfully discard the
2003 * GTKHButtonBox and go it alone.)
2007 align = gtk_alignment_new(0, 0, 1, 1);
2008 gtk_container_add(GTK_CONTAINER(align), w);
2010 * The purpose of this GtkAlignment is to provide padding
2011 * around the buttons. The padding we use is twice the padding
2012 * used in our GtkColumns, because we nest two GtkColumns most
2013 * of the time (one separating the tree view from the main
2014 * controls, and another for the main controls themselves).
2016 #if GTK_CHECK_VERSION(2,4,0)
2017 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 8, 8);
2019 gtk_widget_show(align);
2020 gtk_box_pack_end(GTK_BOX(dlg->vbox), align, FALSE, TRUE, 0);
2021 w = gtk_hseparator_new();
2022 gtk_box_pack_end(GTK_BOX(dlg->vbox), w, FALSE, TRUE, 0);
2024 gtk_widget_hide(dlg->action_area);
2025 gtk_dialog_set_has_separator(dlg, FALSE);
2029 int do_config_box(const char *title, Config *cfg, int midsession,
2032 GtkWidget *window, *hbox, *vbox, *cols, *label,
2033 *tree, *treescroll, *panels, *panelvbox;
2035 struct controlbox *ctrlbox;
2037 #if GTK_CHECK_VERSION(2,0,0)
2038 GtkTreeStore *treestore;
2039 GtkCellRenderer *treerenderer;
2040 GtkTreeViewColumn *treecolumn;
2041 GtkTreeSelection *treeselection;
2042 GtkTreeIter treeiterlevels[8];
2044 GtkTreeItem *treeitemlevels[8];
2045 GtkTree *treelevels[8];
2048 struct Shortcuts scs;
2050 struct selparam *selparams = NULL;
2051 int nselparams = 0, selparamsize = 0;
2055 for (index = 0; index < lenof(scs.sc); index++) {
2056 scs.sc[index].action = SHORTCUT_EMPTY;
2059 window = gtk_dialog_new();
2061 ctrlbox = ctrl_new_box();
2062 setup_config_box(ctrlbox, midsession, cfg->protocol, protcfginfo);
2063 unix_setup_config_box(ctrlbox, midsession, cfg->protocol);
2064 gtk_setup_config_box(ctrlbox, midsession, window);
2066 gtk_window_set_title(GTK_WINDOW(window), title);
2067 hbox = gtk_hbox_new(FALSE, 4);
2068 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), hbox, TRUE, TRUE, 0);
2069 gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
2070 gtk_widget_show(hbox);
2071 vbox = gtk_vbox_new(FALSE, 4);
2072 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
2073 gtk_widget_show(vbox);
2074 cols = columns_new(4);
2075 gtk_box_pack_start(GTK_BOX(vbox), cols, FALSE, FALSE, 0);
2076 gtk_widget_show(cols);
2077 label = gtk_label_new("Category:");
2078 columns_add(COLUMNS(cols), label, 0, 1);
2079 columns_force_left_align(COLUMNS(cols), label);
2080 gtk_widget_show(label);
2081 treescroll = gtk_scrolled_window_new(NULL, NULL);
2082 #if GTK_CHECK_VERSION(2,0,0)
2083 treestore = gtk_tree_store_new
2084 (TREESTORE_NUM, G_TYPE_STRING, G_TYPE_INT);
2085 tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(treestore));
2086 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
2087 treerenderer = gtk_cell_renderer_text_new();
2088 treecolumn = gtk_tree_view_column_new_with_attributes
2089 ("Label", treerenderer, "text", 0, NULL);
2090 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), treecolumn);
2091 treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
2092 gtk_tree_selection_set_mode(treeselection, GTK_SELECTION_BROWSE);
2093 gtk_container_add(GTK_CONTAINER(treescroll), tree);
2095 tree = gtk_tree_new();
2096 gtk_tree_set_view_mode(GTK_TREE(tree), GTK_TREE_VIEW_ITEM);
2097 gtk_tree_set_selection_mode(GTK_TREE(tree), GTK_SELECTION_BROWSE);
2098 gtk_signal_connect(GTK_OBJECT(tree), "focus",
2099 GTK_SIGNAL_FUNC(tree_focus), &dp);
2101 gtk_signal_connect(GTK_OBJECT(tree), "focus_in_event",
2102 GTK_SIGNAL_FUNC(widget_focus), &dp);
2103 shortcut_add(&scs, label, 'g', SHORTCUT_TREE, tree);
2104 gtk_widget_show(treescroll);
2105 gtk_box_pack_start(GTK_BOX(vbox), treescroll, TRUE, TRUE, 0);
2106 panels = gtk_notebook_new();
2107 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(panels), FALSE);
2108 gtk_notebook_set_show_border(GTK_NOTEBOOK(panels), FALSE);
2109 gtk_box_pack_start(GTK_BOX(hbox), panels, TRUE, TRUE, 0);
2110 gtk_widget_show(panels);
2115 for (index = 0; index < ctrlbox->nctrlsets; index++) {
2116 struct controlset *s = ctrlbox->ctrlsets[index];
2119 if (!*s->pathname) {
2120 w = layout_ctrls(&dp, &scs, s, GTK_WINDOW(window));
2122 set_dialog_action_area(GTK_DIALOG(window), w);
2124 int j = path ? ctrl_path_compare(s->pathname, path) : 0;
2125 if (j != INT_MAX) { /* add to treeview, start new panel */
2127 #if GTK_CHECK_VERSION(2,0,0)
2128 GtkTreeIter treeiter;
2130 GtkWidget *treeitem;
2135 * We expect never to find an implicit path
2136 * component. For example, we expect never to see
2137 * A/B/C followed by A/D/E, because that would
2138 * _implicitly_ create A/D. All our path prefixes
2139 * are expected to contain actual controls and be
2140 * selectable in the treeview; so we would expect
2141 * to see A/D _explicitly_ before encountering
2144 assert(j == ctrl_path_elements(s->pathname) - 1);
2146 c = strrchr(s->pathname, '/');
2154 first = (panelvbox == NULL);
2156 panelvbox = gtk_vbox_new(FALSE, 4);
2157 gtk_widget_show(panelvbox);
2158 gtk_notebook_append_page(GTK_NOTEBOOK(panels), panelvbox,
2163 page_num = gtk_notebook_page_num(GTK_NOTEBOOK(panels),
2165 gtk_notebook_set_page(GTK_NOTEBOOK(panels), page_num);
2168 if (nselparams >= selparamsize) {
2170 selparams = sresize(selparams, selparamsize,
2173 selparams[nselparams].dp = &dp;
2174 selparams[nselparams].panels = GTK_NOTEBOOK(panels);
2175 selparams[nselparams].panel = panelvbox;
2176 selparams[nselparams].shortcuts = scs; /* structure copy */
2178 assert(j-1 < level);
2180 #if GTK_CHECK_VERSION(2,0,0)
2182 /* treeiterlevels[j-1] will always be valid because we
2183 * don't allow implicit path components; see above.
2185 gtk_tree_store_append(treestore, &treeiter,
2186 &treeiterlevels[j-1]);
2188 gtk_tree_store_append(treestore, &treeiter, NULL);
2189 gtk_tree_store_set(treestore, &treeiter,
2191 TREESTORE_PARAMS, nselparams,
2193 treeiterlevels[j] = treeiter;
2198 path = gtk_tree_model_get_path(GTK_TREE_MODEL(treestore),
2199 &treeiterlevels[j-1]);
2201 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path,
2204 gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
2205 gtk_tree_path_free(path);
2208 treeitem = gtk_tree_item_new_with_label(c);
2210 if (!treelevels[j-1]) {
2211 treelevels[j-1] = GTK_TREE(gtk_tree_new());
2212 gtk_tree_item_set_subtree
2213 (treeitemlevels[j-1],
2214 GTK_WIDGET(treelevels[j-1]));
2216 gtk_tree_item_expand(treeitemlevels[j-1]);
2218 gtk_tree_item_collapse(treeitemlevels[j-1]);
2220 gtk_tree_append(treelevels[j-1], treeitem);
2222 gtk_tree_append(GTK_TREE(tree), treeitem);
2224 treeitemlevels[j] = GTK_TREE_ITEM(treeitem);
2225 treelevels[j] = NULL;
2227 gtk_signal_connect(GTK_OBJECT(treeitem), "key_press_event",
2228 GTK_SIGNAL_FUNC(tree_key_press), &dp);
2229 gtk_signal_connect(GTK_OBJECT(treeitem), "focus_in_event",
2230 GTK_SIGNAL_FUNC(widget_focus), &dp);
2232 gtk_widget_show(treeitem);
2235 gtk_tree_select_child(GTK_TREE(tree), treeitem);
2236 selparams[nselparams].treeitem = treeitem;
2243 w = layout_ctrls(&dp, &selparams[nselparams-1].shortcuts, s, NULL);
2244 gtk_box_pack_start(GTK_BOX(panelvbox), w, FALSE, FALSE, 0);
2249 #if GTK_CHECK_VERSION(2,0,0)
2250 g_signal_connect(G_OBJECT(treeselection), "changed",
2251 G_CALLBACK(treeselection_changed), selparams);
2253 dp.ntreeitems = nselparams;
2254 dp.treeitems = snewn(dp.ntreeitems, GtkWidget *);
2256 for (index = 0; index < nselparams; index++) {
2257 gtk_signal_connect(GTK_OBJECT(selparams[index].treeitem), "select",
2258 GTK_SIGNAL_FUNC(treeitem_sel),
2260 dp.treeitems[index] = selparams[index].treeitem;
2265 dlg_refresh(NULL, &dp);
2267 dp.shortcuts = &selparams[0].shortcuts;
2268 #if !GTK_CHECK_VERSION(2,0,0)
2269 dp.currtreeitem = dp.treeitems[0];
2271 dp.lastfocus = NULL;
2277 extern void set_window_icon(GtkWidget *window,
2278 const char *const *const *icon,
2280 extern const char *const *const cfg_icon[];
2281 extern const int n_cfg_icon;
2282 set_window_icon(window, cfg_icon, n_cfg_icon);
2285 #if !GTK_CHECK_VERSION(2,0,0)
2286 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(treescroll),
2289 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll),
2291 GTK_POLICY_AUTOMATIC);
2292 gtk_widget_show(tree);
2294 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
2295 gtk_widget_show(window);
2298 * Set focus into the first available control.
2300 for (index = 0; index < ctrlbox->nctrlsets; index++) {
2301 struct controlset *s = ctrlbox->ctrlsets[index];
2306 for (j = 0; j < s->ncontrols; j++)
2307 if (s->ctrls[j]->generic.type != CTRL_TABDELAY &&
2308 s->ctrls[j]->generic.type != CTRL_COLUMNS &&
2309 s->ctrls[j]->generic.type != CTRL_TEXT) {
2310 dlg_set_focus(s->ctrls[j], &dp);
2311 dp.lastfocus = s->ctrls[j];
2320 gtk_signal_connect(GTK_OBJECT(window), "destroy",
2321 GTK_SIGNAL_FUNC(window_destroy), NULL);
2322 gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
2323 GTK_SIGNAL_FUNC(win_key_press), &dp);
2333 static void messagebox_handler(union control *ctrl, void *dlg,
2334 void *data, int event)
2336 if (event == EVENT_ACTION)
2337 dlg_end(dlg, ctrl->generic.context.i);
2339 int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...)
2341 GtkWidget *window, *w0, *w1;
2342 struct controlbox *ctrlbox;
2343 struct controlset *s0, *s1;
2346 struct Shortcuts scs;
2352 for (index = 0; index < lenof(scs.sc); index++) {
2353 scs.sc[index].action = SHORTCUT_EMPTY;
2356 ctrlbox = ctrl_new_box();
2359 va_start(ap, minwid);
2360 while (va_arg(ap, char *) != NULL) {
2362 (void) va_arg(ap, int); /* shortcut */
2363 (void) va_arg(ap, int); /* normal/default/cancel */
2364 (void) va_arg(ap, int); /* end value */
2368 s0 = ctrl_getset(ctrlbox, "", "", "");
2369 c = ctrl_columns(s0, 2, 50, 50);
2370 c->columns.ncols = s0->ncolumns = ncols;
2371 c->columns.percentages = sresize(c->columns.percentages, ncols, int);
2372 for (index = 0; index < ncols; index++)
2373 c->columns.percentages[index] = (index+1)*100/ncols - index*100/ncols;
2374 va_start(ap, minwid);
2377 char *title = va_arg(ap, char *);
2378 int shortcut, type, value;
2381 shortcut = va_arg(ap, int);
2382 type = va_arg(ap, int);
2383 value = va_arg(ap, int);
2384 c = ctrl_pushbutton(s0, title, shortcut, HELPCTX(no_help),
2385 messagebox_handler, I(value));
2386 c->generic.column = index++;
2388 c->button.isdefault = TRUE;
2390 c->button.iscancel = TRUE;
2394 s1 = ctrl_getset(ctrlbox, "x", "", "");
2395 ctrl_text(s1, msg, HELPCTX(no_help));
2397 window = gtk_dialog_new();
2398 gtk_window_set_title(GTK_WINDOW(window), title);
2399 w0 = layout_ctrls(&dp, &scs, s0, GTK_WINDOW(window));
2400 set_dialog_action_area(GTK_DIALOG(window), w0);
2401 gtk_widget_show(w0);
2402 w1 = layout_ctrls(&dp, &scs, s1, GTK_WINDOW(window));
2403 gtk_container_set_border_width(GTK_CONTAINER(w1), 10);
2404 gtk_widget_set_usize(w1, minwid+20, -1);
2405 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),
2407 gtk_widget_show(w1);
2409 dp.shortcuts = &scs;
2410 dp.lastfocus = NULL;
2414 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
2416 set_transient_window_pos(parentwin, window);
2417 gtk_window_set_transient_for(GTK_WINDOW(window),
2418 GTK_WINDOW(parentwin));
2420 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
2421 gtk_widget_show(window);
2423 gtk_signal_connect(GTK_OBJECT(window), "destroy",
2424 GTK_SIGNAL_FUNC(window_destroy), NULL);
2425 gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
2426 GTK_SIGNAL_FUNC(win_key_press), &dp);
2431 ctrl_free_box(ctrlbox);
2436 static int string_width(char *text)
2438 GtkWidget *label = gtk_label_new(text);
2440 gtk_widget_size_request(label, &req);
2441 gtk_object_sink(GTK_OBJECT(label));
2445 int reallyclose(void *frontend)
2447 char *title = dupcat(appname, " Exit Confirmation", NULL);
2448 int ret = messagebox(GTK_WIDGET(get_window(frontend)),
2449 title, "Are you sure you want to close this session?",
2450 string_width("Most of the width of the above text"),
2458 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
2459 char *keystr, char *fingerprint,
2460 void (*callback)(void *ctx, int result), void *ctx)
2462 static const char absenttxt[] =
2463 "The server's host key is not cached. You have no guarantee "
2464 "that the server is the computer you think it is.\n"
2465 "The server's %s key fingerprint is:\n"
2467 "If you trust this host, press \"Accept\" to add the key to "
2468 "PuTTY's cache and carry on connecting.\n"
2469 "If you want to carry on connecting just once, without "
2470 "adding the key to the cache, press \"Connect Once\".\n"
2471 "If you do not trust this host, press \"Cancel\" to abandon the "
2473 static const char wrongtxt[] =
2474 "WARNING - POTENTIAL SECURITY BREACH!\n"
2475 "The server's host key does not match the one PuTTY has "
2476 "cached. This means that either the server administrator "
2477 "has changed the host key, or you have actually connected "
2478 "to another computer pretending to be the server.\n"
2479 "The new %s key fingerprint is:\n"
2481 "If you were expecting this change and trust the new key, "
2482 "press \"Accept\" to update PuTTY's cache and continue connecting.\n"
2483 "If you want to carry on connecting but without updating "
2484 "the cache, press \"Connect Once\".\n"
2485 "If you want to abandon the connection completely, press "
2486 "\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed "
2494 ret = verify_host_key(host, port, keytype, keystr);
2496 if (ret == 0) /* success - key matched OK */
2499 text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint);
2501 ret = messagebox(GTK_WIDGET(get_window(frontend)),
2502 "PuTTY Security Alert", text,
2503 string_width(fingerprint),
2504 "Accept", 'a', 0, 2,
2505 "Connect Once", 'o', 0, 1,
2506 "Cancel", 'c', -1, 0,
2512 store_host_key(host, port, keytype, keystr);
2513 return 1; /* continue with connection */
2514 } else if (ret == 1)
2515 return 1; /* continue with connection */
2516 return 0; /* do not continue with connection */
2520 * Ask whether the selected algorithm is acceptable (since it was
2521 * below the configured 'warn' threshold).
2523 int askalg(void *frontend, const char *algtype, const char *algname,
2524 void (*callback)(void *ctx, int result), void *ctx)
2526 static const char msg[] =
2527 "The first %s supported by the server is "
2528 "%s, which is below the configured warning threshold.\n"
2529 "Continue with connection?";
2533 text = dupprintf(msg, algtype, algname);
2534 ret = messagebox(GTK_WIDGET(get_window(frontend)),
2535 "PuTTY Security Alert", text,
2536 string_width("Continue with connection?"),
2549 void old_keyfile_warning(void)
2552 * This should never happen on Unix. We hope.
2556 void fatal_message_box(void *window, char *msg)
2558 messagebox(window, "PuTTY Fatal Error", msg,
2559 string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"),
2560 "OK", 'o', 1, 1, NULL);
2563 void fatalbox(char *p, ...)
2568 msg = dupvprintf(p, ap);
2570 fatal_message_box(NULL, msg);
2575 static GtkWidget *aboutbox = NULL;
2577 static void about_close_clicked(GtkButton *button, gpointer data)
2579 gtk_widget_destroy(aboutbox);
2583 static void licence_clicked(GtkButton *button, gpointer data)
2588 "Copyright 1997-2008 Simon Tatham.\n\n"
2590 "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
2591 "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "
2592 "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, "
2593 "Markus Kuhn, Colin Watson, and CORE SDI S.A.\n\n"
2595 "Permission is hereby granted, free of charge, to any person "
2596 "obtaining a copy of this software and associated documentation "
2597 "files (the ""Software""), to deal in the Software without restriction, "
2598 "including without limitation the rights to use, copy, modify, merge, "
2599 "publish, distribute, sublicense, and/or sell copies of the Software, "
2600 "and to permit persons to whom the Software is furnished to do so, "
2601 "subject to the following conditions:\n\n"
2603 "The above copyright notice and this permission notice shall be "
2604 "included in all copies or substantial portions of the Software.\n\n"
2606 "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT "
2607 "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, "
2608 "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "
2609 "MERCHANTABILITY, FITNESS FOR A PARTICULAR "
2610 "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE "
2611 "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES "
2612 "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, "
2613 "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN "
2614 "CONNECTION WITH THE SOFTWARE OR THE USE OR "
2615 "OTHER DEALINGS IN THE SOFTWARE.";
2617 title = dupcat(appname, " Licence", NULL);
2618 assert(aboutbox != NULL);
2619 messagebox(aboutbox, title, licence,
2620 string_width("LONGISH LINE OF TEXT SO THE LICENCE"
2621 " BOX ISN'T EXCESSIVELY TALL AND THIN"),
2622 "OK", 'o', 1, 1, NULL);
2626 void about_box(void *window)
2632 gtk_widget_grab_focus(aboutbox);
2636 aboutbox = gtk_dialog_new();
2637 gtk_container_set_border_width(GTK_CONTAINER(aboutbox), 10);
2638 title = dupcat("About ", appname, NULL);
2639 gtk_window_set_title(GTK_WINDOW(aboutbox), title);
2642 w = gtk_button_new_with_label("Close");
2643 GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
2644 gtk_window_set_default(GTK_WINDOW(aboutbox), w);
2645 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(aboutbox)->action_area),
2646 w, FALSE, FALSE, 0);
2647 gtk_signal_connect(GTK_OBJECT(w), "clicked",
2648 GTK_SIGNAL_FUNC(about_close_clicked), NULL);
2651 w = gtk_button_new_with_label("View Licence");
2652 GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
2653 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(aboutbox)->action_area),
2654 w, FALSE, FALSE, 0);
2655 gtk_signal_connect(GTK_OBJECT(w), "clicked",
2656 GTK_SIGNAL_FUNC(licence_clicked), NULL);
2659 w = gtk_label_new(appname);
2660 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox),
2661 w, FALSE, FALSE, 0);
2664 w = gtk_label_new(ver);
2665 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox),
2666 w, FALSE, FALSE, 5);
2669 w = gtk_label_new("Copyright 1997-2008 Simon Tatham. All rights reserved");
2670 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox),
2671 w, FALSE, FALSE, 5);
2674 set_transient_window_pos(GTK_WIDGET(window), aboutbox);
2675 gtk_widget_show(aboutbox);
2678 struct eventlog_stuff {
2679 GtkWidget *parentwin, *window;
2680 struct controlbox *eventbox;
2681 struct Shortcuts scs;
2683 union control *listctrl;
2685 int nevents, negsize;
2688 int ignore_selchange;
2691 static void eventlog_destroy(GtkWidget *widget, gpointer data)
2693 struct eventlog_stuff *es = (struct eventlog_stuff *)data;
2697 dlg_cleanup(&es->dp);
2698 ctrl_free_box(es->eventbox);
2700 static void eventlog_ok_handler(union control *ctrl, void *dlg,
2701 void *data, int event)
2703 if (event == EVENT_ACTION)
2706 static void eventlog_list_handler(union control *ctrl, void *dlg,
2707 void *data, int event)
2709 struct eventlog_stuff *es = (struct eventlog_stuff *)data;
2711 if (event == EVENT_REFRESH) {
2714 dlg_update_start(ctrl, dlg);
2715 dlg_listbox_clear(ctrl, dlg);
2716 for (i = 0; i < es->nevents; i++) {
2717 dlg_listbox_add(ctrl, dlg, es->events[i]);
2719 dlg_update_done(ctrl, dlg);
2720 } else if (event == EVENT_SELCHANGE) {
2725 * If this SELCHANGE event is happening as a result of
2726 * deliberate deselection because someone else has grabbed
2727 * the selection, the last thing we want to do is pre-empt
2730 if (es->ignore_selchange)
2734 * Construct the data to use as the selection.
2739 for (i = 0; i < es->nevents; i++) {
2740 if (dlg_listbox_issel(ctrl, dlg, i)) {
2741 int extralen = strlen(es->events[i]);
2743 if (es->sellen + extralen + 2 > selsize) {
2744 selsize = es->sellen + extralen + 512;
2745 es->seldata = sresize(es->seldata, selsize, char);
2748 strcpy(es->seldata + es->sellen, es->events[i]);
2749 es->sellen += extralen;
2750 es->seldata[es->sellen++] = '\n';
2754 if (gtk_selection_owner_set(es->window, GDK_SELECTION_PRIMARY,
2755 GDK_CURRENT_TIME)) {
2756 extern GdkAtom compound_text_atom;
2758 gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY,
2759 GDK_SELECTION_TYPE_STRING, 1);
2760 gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY,
2761 compound_text_atom, 1);
2767 void eventlog_selection_get(GtkWidget *widget, GtkSelectionData *seldata,
2768 guint info, guint time_stamp, gpointer data)
2770 struct eventlog_stuff *es = (struct eventlog_stuff *)data;
2772 gtk_selection_data_set(seldata, seldata->target, 8,
2773 (unsigned char *)es->seldata, es->sellen);
2776 gint eventlog_selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
2779 struct eventlog_stuff *es = (struct eventlog_stuff *)data;
2783 * Deselect everything in the list box.
2785 uc = dlg_find_byctrl(&es->dp, es->listctrl);
2787 gtk_tree_selection_unselect_all
2788 (gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->list)));
2796 void showeventlog(void *estuff, void *parentwin)
2798 struct eventlog_stuff *es = (struct eventlog_stuff *)estuff;
2799 GtkWidget *window, *w0, *w1;
2800 GtkWidget *parent = GTK_WIDGET(parentwin);
2801 struct controlset *s0, *s1;
2807 gtk_widget_grab_focus(es->window);
2813 for (index = 0; index < lenof(es->scs.sc); index++) {
2814 es->scs.sc[index].action = SHORTCUT_EMPTY;
2817 es->eventbox = ctrl_new_box();
2819 s0 = ctrl_getset(es->eventbox, "", "", "");
2820 ctrl_columns(s0, 3, 33, 34, 33);
2821 c = ctrl_pushbutton(s0, "Close", 'c', HELPCTX(no_help),
2822 eventlog_ok_handler, P(NULL));
2823 c->button.column = 1;
2824 c->button.isdefault = TRUE;
2826 s1 = ctrl_getset(es->eventbox, "x", "", "");
2827 es->listctrl = c = ctrl_listbox(s1, NULL, NO_SHORTCUT, HELPCTX(no_help),
2828 eventlog_list_handler, P(es));
2829 c->listbox.height = 10;
2830 c->listbox.multisel = 2;
2831 c->listbox.ncols = 3;
2832 c->listbox.percentages = snewn(3, int);
2833 c->listbox.percentages[0] = 25;
2834 c->listbox.percentages[1] = 10;
2835 c->listbox.percentages[2] = 65;
2837 es->window = window = gtk_dialog_new();
2838 title = dupcat(appname, " Event Log", NULL);
2839 gtk_window_set_title(GTK_WINDOW(window), title);
2841 w0 = layout_ctrls(&es->dp, &es->scs, s0, GTK_WINDOW(window));
2842 set_dialog_action_area(GTK_DIALOG(window), w0);
2843 gtk_widget_show(w0);
2844 w1 = layout_ctrls(&es->dp, &es->scs, s1, GTK_WINDOW(window));
2845 gtk_container_set_border_width(GTK_CONTAINER(w1), 10);
2846 gtk_widget_set_usize(w1, 20 +
2847 string_width("LINE OF TEXT GIVING WIDTH OF EVENT LOG"
2848 " IS QUITE LONG 'COS SSH LOG ENTRIES"
2850 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),
2852 gtk_widget_show(w1);
2855 es->dp.shortcuts = &es->scs;
2856 es->dp.lastfocus = NULL;
2858 es->dp.window = window;
2860 dlg_refresh(NULL, &es->dp);
2863 set_transient_window_pos(parent, window);
2864 gtk_window_set_transient_for(GTK_WINDOW(window),
2865 GTK_WINDOW(parent));
2867 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
2868 gtk_widget_show(window);
2870 gtk_signal_connect(GTK_OBJECT(window), "destroy",
2871 GTK_SIGNAL_FUNC(eventlog_destroy), es);
2872 gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
2873 GTK_SIGNAL_FUNC(win_key_press), &es->dp);
2874 gtk_signal_connect(GTK_OBJECT(window), "selection_get",
2875 GTK_SIGNAL_FUNC(eventlog_selection_get), es);
2876 gtk_signal_connect(GTK_OBJECT(window), "selection_clear_event",
2877 GTK_SIGNAL_FUNC(eventlog_selection_clear), es);
2880 void *eventlogstuff_new(void)
2882 struct eventlog_stuff *es;
2883 es = snew(struct eventlog_stuff);
2884 memset(es, 0, sizeof(*es));
2888 void logevent_dlg(void *estuff, const char *string)
2890 struct eventlog_stuff *es = (struct eventlog_stuff *)estuff;
2895 if (es->nevents >= es->negsize) {
2897 es->events = sresize(es->events, es->negsize, char *);
2901 strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm);
2903 es->events[es->nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
2904 strcpy(es->events[es->nevents], timebuf);
2905 strcat(es->events[es->nevents], string);
2907 dlg_listbox_add(es->listctrl, &es->dp, es->events[es->nevents]);
2912 int askappend(void *frontend, Filename filename,
2913 void (*callback)(void *ctx, int result), void *ctx)
2915 static const char msgtemplate[] =
2916 "The session log file \"%.*s\" already exists. "
2917 "You can overwrite it with a new session log, "
2918 "append your session log to the end of it, "
2919 "or disable session logging for this session.";
2924 message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
2925 mbtitle = dupprintf("%s Log to File", appname);
2927 mbret = messagebox(get_window(frontend), mbtitle, message,
2928 string_width("LINE OF TEXT SUITABLE FOR THE"
2929 " ASKAPPEND WIDTH"),
2930 "Overwrite", 'o', 1, 2,
2931 "Append", 'a', 0, 1,
2932 "Disable", 'd', -1, 0,