2 * gtkdlg.c - GTK implementation of the PuTTY configuration box.
6 * TODO when porting to GTK 2.0:
8 * - GtkTree is apparently deprecated and we should switch to
10 * - GtkLabel has a built-in mnemonic scheme, so we should at
11 * least consider switching to that from the current adhockery.
19 #include <gdk/gdkkeysyms.h>
22 #include <X11/Xutil.h>
28 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
43 struct Shortcut sc
[128];
50 int privdata_needs_free
;
51 GtkWidget
**buttons
; int nbuttons
; /* for radio buttons */
52 GtkWidget
*entry
; /* for editbox, combobox, filesel, fontsel */
53 GtkWidget
*button
; /* for filesel, fontsel */
54 GtkWidget
*list
; /* for combobox, listbox */
55 GtkWidget
*menu
; /* for optionmenu (==droplist) */
56 GtkWidget
*optmenu
; /* also for optionmenu */
57 GtkWidget
*text
; /* for text */
58 GtkAdjustment
*adj
; /* for the scrollbar in a list box */
63 tree234
*byctrl
, *bywidget
;
65 struct { unsigned char r
, g
, b
, ok
; } coloursel_result
; /* 0-255 */
66 /* `flags' are set to indicate when a GTK signal handler is being called
67 * due to automatic processing and should not flag a user event. */
69 struct Shortcuts
*shortcuts
;
70 GtkWidget
*window
, *cancelbutton
, *currtreeitem
, **treeitems
;
71 union control
*currfocus
, *lastfocus
;
75 #define FLAG_UPDATING_COMBO_LIST 1
77 enum { /* values for Shortcut.action */
78 SHORTCUT_EMPTY
, /* no shortcut on this key */
79 SHORTCUT_TREE
, /* focus a tree item */
80 SHORTCUT_FOCUS
, /* focus the supplied widget */
81 SHORTCUT_UCTRL
, /* do something sane with uctrl */
82 SHORTCUT_UCTRL_UP
, /* uctrl is a draglist, move Up */
83 SHORTCUT_UCTRL_DOWN
, /* uctrl is a draglist, move Down */
89 static gboolean
widget_focus(GtkWidget
*widget
, GdkEventFocus
*event
,
91 static void shortcut_add(struct Shortcuts
*scs
, GtkWidget
*labelw
,
92 int chr
, int action
, void *ptr
);
93 static int listitem_single_key(GtkWidget
*item
, GdkEventKey
*event
,
95 static int listitem_multi_key(GtkWidget
*item
, GdkEventKey
*event
,
97 static int listitem_button(GtkWidget
*item
, GdkEventButton
*event
,
99 static void menuitem_activate(GtkMenuItem
*item
, gpointer data
);
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
);
104 static int uctrl_cmp_byctrl(void *av
, void *bv
)
106 struct uctrl
*a
= (struct uctrl
*)av
;
107 struct uctrl
*b
= (struct uctrl
*)bv
;
108 if (a
->ctrl
< b
->ctrl
)
110 else if (a
->ctrl
> b
->ctrl
)
115 static int uctrl_cmp_byctrl_find(void *av
, void *bv
)
117 union control
*a
= (union control
*)av
;
118 struct uctrl
*b
= (struct uctrl
*)bv
;
121 else if (a
> b
->ctrl
)
126 static int uctrl_cmp_bywidget(void *av
, void *bv
)
128 struct uctrl
*a
= (struct uctrl
*)av
;
129 struct uctrl
*b
= (struct uctrl
*)bv
;
130 if (a
->toplevel
< b
->toplevel
)
132 else if (a
->toplevel
> b
->toplevel
)
137 static int uctrl_cmp_bywidget_find(void *av
, void *bv
)
139 GtkWidget
*a
= (GtkWidget
*)av
;
140 struct uctrl
*b
= (struct uctrl
*)bv
;
143 else if (a
> b
->toplevel
)
148 static void dlg_init(struct dlgparam
*dp
)
150 dp
->byctrl
= newtree234(uctrl_cmp_byctrl
);
151 dp
->bywidget
= newtree234(uctrl_cmp_bywidget
);
152 dp
->coloursel_result
.ok
= FALSE
;
153 dp
->treeitems
= NULL
;
154 dp
->window
= dp
->cancelbutton
= dp
->currtreeitem
= NULL
;
156 dp
->currfocus
= NULL
;
159 static void dlg_cleanup(struct dlgparam
*dp
)
163 freetree234(dp
->byctrl
); /* doesn't free the uctrls inside */
165 while ( (uc
= index234(dp
->bywidget
, 0)) != NULL
) {
166 del234(dp
->bywidget
, uc
);
167 if (uc
->privdata_needs_free
)
172 freetree234(dp
->bywidget
);
174 sfree(dp
->treeitems
);
177 static void dlg_add_uctrl(struct dlgparam
*dp
, struct uctrl
*uc
)
179 add234(dp
->byctrl
, uc
);
180 add234(dp
->bywidget
, uc
);
183 static struct uctrl
*dlg_find_byctrl(struct dlgparam
*dp
, union control
*ctrl
)
187 return find234(dp
->byctrl
, ctrl
, uctrl_cmp_byctrl_find
);
190 static struct uctrl
*dlg_find_bywidget(struct dlgparam
*dp
, GtkWidget
*w
)
192 struct uctrl
*ret
= NULL
;
196 ret
= find234(dp
->bywidget
, w
, uctrl_cmp_bywidget_find
);
204 void *dlg_get_privdata(union control
*ctrl
, void *dlg
)
206 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
207 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
211 void dlg_set_privdata(union control
*ctrl
, void *dlg
, void *ptr
)
213 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
214 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
216 uc
->privdata_needs_free
= FALSE
;
219 void *dlg_alloc_privdata(union control
*ctrl
, void *dlg
, size_t size
)
221 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
222 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
224 * This is an internal allocation routine, so it's allowed to
225 * use smalloc directly.
227 uc
->privdata
= smalloc(size
);
228 uc
->privdata_needs_free
= FALSE
;
232 union control
*dlg_last_focused(union control
*ctrl
, void *dlg
)
234 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
235 if (dp
->currfocus
!= ctrl
)
236 return dp
->currfocus
;
238 return dp
->lastfocus
;
241 void dlg_radiobutton_set(union control
*ctrl
, void *dlg
, int which
)
243 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
244 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
245 assert(uc
->ctrl
->generic
.type
== CTRL_RADIO
);
246 assert(uc
->buttons
!= NULL
);
247 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc
->buttons
[which
]), TRUE
);
250 int dlg_radiobutton_get(union control
*ctrl
, void *dlg
)
252 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
253 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
256 assert(uc
->ctrl
->generic
.type
== CTRL_RADIO
);
257 assert(uc
->buttons
!= NULL
);
258 for (i
= 0; i
< uc
->nbuttons
; i
++)
259 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc
->buttons
[i
])))
261 return 0; /* got to return something */
264 void dlg_checkbox_set(union control
*ctrl
, void *dlg
, int checked
)
266 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
267 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
268 assert(uc
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
269 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc
->toplevel
), checked
);
272 int dlg_checkbox_get(union control
*ctrl
, void *dlg
)
274 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
275 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
276 assert(uc
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
277 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc
->toplevel
));
280 void dlg_editbox_set(union control
*ctrl
, void *dlg
, char const *text
)
282 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
283 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
284 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
);
285 assert(uc
->entry
!= NULL
);
286 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), text
);
289 void dlg_editbox_get(union control
*ctrl
, void *dlg
, char *buffer
, int length
)
291 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
292 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
293 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
);
294 assert(uc
->entry
!= NULL
);
295 strncpy(buffer
, gtk_entry_get_text(GTK_ENTRY(uc
->entry
)),
297 buffer
[length
-1] = '\0';
300 static void container_remove_and_destroy(GtkWidget
*w
, gpointer data
)
302 GtkContainer
*cont
= GTK_CONTAINER(data
);
303 /* gtk_container_remove will unref the widget for us; we need not. */
304 gtk_container_remove(cont
, w
);
307 /* The `listbox' functions can also apply to combo boxes. */
308 void dlg_listbox_clear(union control
*ctrl
, void *dlg
)
310 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
311 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
313 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
314 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
315 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
318 gtk_container_foreach(GTK_CONTAINER(uc
->menu
),
319 container_remove_and_destroy
,
320 GTK_CONTAINER(uc
->menu
));
322 gtk_list_clear_items(GTK_LIST(uc
->list
), 0, -1);
326 void dlg_listbox_del(union control
*ctrl
, void *dlg
, int index
)
328 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
329 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
331 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
332 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
333 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
337 (GTK_CONTAINER(uc
->menu
),
338 g_list_nth_data(GTK_MENU_SHELL(uc
->menu
)->children
, index
));
340 gtk_list_clear_items(GTK_LIST(uc
->list
), index
, index
+1);
344 void dlg_listbox_add(union control
*ctrl
, void *dlg
, char const *text
)
346 dlg_listbox_addwithid(ctrl
, dlg
, text
, 0);
350 * Each listbox entry may have a numeric id associated with it.
351 * Note that some front ends only permit a string to be stored at
352 * each position, which means that _if_ you put two identical
353 * strings in any listbox then you MUST not assign them different
354 * IDs and expect to get meaningful results back.
356 void dlg_listbox_addwithid(union control
*ctrl
, void *dlg
,
357 char const *text
, int id
)
359 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
360 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
362 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
363 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
364 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
366 dp
->flags
|= FLAG_UPDATING_COMBO_LIST
;
370 * List item in a drop-down (but non-combo) list. Tabs are
371 * ignored; we just provide a standard menu item with the
374 GtkWidget
*menuitem
= gtk_menu_item_new_with_label(text
);
376 gtk_container_add(GTK_CONTAINER(uc
->menu
), menuitem
);
377 gtk_widget_show(menuitem
);
379 gtk_object_set_data(GTK_OBJECT(menuitem
), "user-data",
380 GINT_TO_POINTER(id
));
381 gtk_signal_connect(GTK_OBJECT(menuitem
), "activate",
382 GTK_SIGNAL_FUNC(menuitem_activate
), dp
);
383 } else if (!uc
->entry
) {
385 * List item in a non-combo-box list box. We make all of
386 * these Columns containing GtkLabels. This allows us to do
387 * the nasty force_left hack irrespective of whether there
388 * are tabs in the thing.
390 GtkWidget
*listitem
= gtk_list_item_new();
391 GtkWidget
*cols
= columns_new(10);
395 /* Count the tabs in the text, and hence determine # of columns. */
397 for (i
= 0; text
[i
]; i
++)
402 (uc
->ctrl
->listbox
.ncols ? uc
->ctrl
->listbox
.ncols
: 1));
403 percents
= snewn(ncols
, gint
);
404 percents
[ncols
-1] = 100;
405 for (i
= 0; i
< ncols
-1; i
++) {
406 percents
[i
] = uc
->ctrl
->listbox
.percentages
[i
];
407 percents
[ncols
-1] -= percents
[i
];
409 columns_set_cols(COLUMNS(cols
), ncols
, percents
);
412 for (i
= 0; i
< ncols
; i
++) {
413 int len
= strcspn(text
, "\t");
414 char *dup
= dupprintf("%.*s", len
, text
);
419 label
= gtk_label_new(dup
);
422 columns_add(COLUMNS(cols
), label
, i
, 1);
423 columns_force_left_align(COLUMNS(cols
), label
);
424 gtk_widget_show(label
);
426 gtk_container_add(GTK_CONTAINER(listitem
), cols
);
427 gtk_widget_show(cols
);
428 gtk_container_add(GTK_CONTAINER(uc
->list
), listitem
);
429 gtk_widget_show(listitem
);
431 if (ctrl
->listbox
.multisel
) {
432 gtk_signal_connect(GTK_OBJECT(listitem
), "key_press_event",
433 GTK_SIGNAL_FUNC(listitem_multi_key
), uc
->adj
);
435 gtk_signal_connect(GTK_OBJECT(listitem
), "key_press_event",
436 GTK_SIGNAL_FUNC(listitem_single_key
), uc
->adj
);
438 gtk_signal_connect(GTK_OBJECT(listitem
), "focus_in_event",
439 GTK_SIGNAL_FUNC(widget_focus
), dp
);
440 gtk_signal_connect(GTK_OBJECT(listitem
), "button_press_event",
441 GTK_SIGNAL_FUNC(listitem_button
), dp
);
442 gtk_object_set_data(GTK_OBJECT(listitem
), "user-data",
443 GINT_TO_POINTER(id
));
446 * List item in a combo-box list, which means the sensible
447 * thing to do is make it a perfectly normal label. Hence
448 * tabs are disregarded.
450 GtkWidget
*listitem
= gtk_list_item_new_with_label(text
);
452 gtk_container_add(GTK_CONTAINER(uc
->list
), listitem
);
453 gtk_widget_show(listitem
);
455 gtk_object_set_data(GTK_OBJECT(listitem
), "user-data",
456 GINT_TO_POINTER(id
));
459 dp
->flags
&= ~FLAG_UPDATING_COMBO_LIST
;
462 int dlg_listbox_getid(union control
*ctrl
, void *dlg
, int index
)
464 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
465 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
469 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
470 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
471 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
473 children
= gtk_container_children(GTK_CONTAINER(uc
->menu ? uc
->menu
:
475 item
= GTK_OBJECT(g_list_nth_data(children
, index
));
476 g_list_free(children
);
478 return GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item
), "user-data"));
481 /* dlg_listbox_index returns <0 if no single element is selected. */
482 int dlg_listbox_index(union control
*ctrl
, void *dlg
)
484 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
485 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
487 GtkWidget
*item
, *activeitem
;
491 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
492 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
493 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
496 activeitem
= gtk_menu_get_active(GTK_MENU(uc
->menu
));
498 activeitem
= NULL
; /* unnecessarily placate gcc */
500 children
= gtk_container_children(GTK_CONTAINER(uc
->menu ? uc
->menu
:
502 for (i
= 0; children
!=NULL
&& (item
= GTK_WIDGET(children
->data
))!=NULL
;
503 i
++, children
= children
->next
) {
504 if (uc
->menu ? activeitem
== item
:
505 GTK_WIDGET_STATE(item
) == GTK_STATE_SELECTED
) {
512 g_list_free(children
);
513 return selected
< 0 ?
-1 : selected
;
516 int dlg_listbox_issel(union control
*ctrl
, void *dlg
, int index
)
518 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
519 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
521 GtkWidget
*item
, *activeitem
;
523 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
524 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
525 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
527 children
= gtk_container_children(GTK_CONTAINER(uc
->menu ? uc
->menu
:
529 item
= GTK_WIDGET(g_list_nth_data(children
, index
));
530 g_list_free(children
);
533 activeitem
= gtk_menu_get_active(GTK_MENU(uc
->menu
));
534 return item
== activeitem
;
536 return GTK_WIDGET_STATE(item
) == GTK_STATE_SELECTED
;
540 void dlg_listbox_select(union control
*ctrl
, void *dlg
, int index
)
542 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
543 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
545 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
546 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
547 assert(uc
->optmenu
!= NULL
|| uc
->list
!= NULL
);
550 gtk_option_menu_set_history(GTK_OPTION_MENU(uc
->optmenu
), index
);
554 gdouble newtop
, newbot
;
556 gtk_list_select_item(GTK_LIST(uc
->list
), index
);
559 * Scroll the list box if necessary to ensure the newly
560 * selected item is visible.
562 items
= gtk_container_children(GTK_CONTAINER(uc
->list
));
563 nitems
= g_list_length(items
);
565 int modified
= FALSE
;
567 newtop
= uc
->adj
->lower
+
568 (uc
->adj
->upper
- uc
->adj
->lower
) * index
/ nitems
;
569 newbot
= uc
->adj
->lower
+
570 (uc
->adj
->upper
- uc
->adj
->lower
) * (index
+1) / nitems
;
571 if (uc
->adj
->value
> newtop
) {
573 uc
->adj
->value
= newtop
;
574 } else if (uc
->adj
->value
< newbot
- uc
->adj
->page_size
) {
576 uc
->adj
->value
= newbot
- uc
->adj
->page_size
;
579 gtk_adjustment_value_changed(uc
->adj
);
584 void dlg_text_set(union control
*ctrl
, void *dlg
, char const *text
)
586 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
587 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
589 assert(uc
->ctrl
->generic
.type
== CTRL_TEXT
);
590 assert(uc
->text
!= NULL
);
592 gtk_label_set_text(GTK_LABEL(uc
->text
), text
);
595 void dlg_label_change(union control
*ctrl
, void *dlg
, char const *text
)
598 * This function is currently only used by the config box to
599 * switch the labels on the host and port boxes between serial
600 * and network modes. Since Unix does not (yet) have a serial
601 * back end, this function can safely do nothing for the
606 void dlg_filesel_set(union control
*ctrl
, void *dlg
, Filename fn
)
608 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
609 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
610 assert(uc
->ctrl
->generic
.type
== CTRL_FILESELECT
);
611 assert(uc
->entry
!= NULL
);
612 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), fn
.path
);
615 void dlg_filesel_get(union control
*ctrl
, void *dlg
, Filename
*fn
)
617 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
618 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
619 assert(uc
->ctrl
->generic
.type
== CTRL_FILESELECT
);
620 assert(uc
->entry
!= NULL
);
621 strncpy(fn
->path
, gtk_entry_get_text(GTK_ENTRY(uc
->entry
)),
623 fn
->path
[lenof(fn
->path
)-1] = '\0';
626 void dlg_fontsel_set(union control
*ctrl
, void *dlg
, FontSpec fs
)
628 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
629 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
630 assert(uc
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
631 assert(uc
->entry
!= NULL
);
632 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), fs
.name
);
635 void dlg_fontsel_get(union control
*ctrl
, void *dlg
, FontSpec
*fs
)
637 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
638 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
639 assert(uc
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
640 assert(uc
->entry
!= NULL
);
641 strncpy(fs
->name
, gtk_entry_get_text(GTK_ENTRY(uc
->entry
)),
643 fs
->name
[lenof(fs
->name
)-1] = '\0';
647 * Bracketing a large set of updates in these two functions will
648 * cause the front end (if possible) to delay updating the screen
649 * until it's all complete, thus avoiding flicker.
651 void dlg_update_start(union control
*ctrl
, void *dlg
)
654 * Apparently we can't do this at all in GTK. GtkCList supports
655 * freeze and thaw, but not GtkList. Bah.
659 void dlg_update_done(union control
*ctrl
, void *dlg
)
662 * Apparently we can't do this at all in GTK. GtkCList supports
663 * freeze and thaw, but not GtkList. Bah.
667 void dlg_set_focus(union control
*ctrl
, void *dlg
)
669 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
670 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
672 switch (ctrl
->generic
.type
) {
675 /* Check boxes and buttons get the focus _and_ get toggled. */
676 gtk_widget_grab_focus(uc
->toplevel
);
678 case CTRL_FILESELECT
:
679 case CTRL_FONTSELECT
:
681 /* Anything containing an edit box gets that focused. */
682 gtk_widget_grab_focus(uc
->entry
);
686 * Radio buttons: we find the currently selected button and
691 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++)
692 if (gtk_toggle_button_get_active
693 (GTK_TOGGLE_BUTTON(uc
->buttons
[i
]))) {
694 gtk_widget_grab_focus(uc
->buttons
[i
]);
700 * If the list is really an option menu, we focus it.
701 * Otherwise we tell it to focus one of its children, which
702 * appears to do the Right Thing.
705 gtk_widget_grab_focus(uc
->optmenu
);
707 assert(uc
->list
!= NULL
);
708 gtk_container_focus(GTK_CONTAINER(uc
->list
), GTK_DIR_TAB_FORWARD
);
715 * During event processing, you might well want to give an error
716 * indication to the user. dlg_beep() is a quick and easy generic
717 * error; dlg_error() puts up a message-box or equivalent.
719 void dlg_beep(void *dlg
)
724 static void errmsg_button_clicked(GtkButton
*button
, gpointer data
)
726 gtk_widget_destroy(GTK_WIDGET(data
));
729 static void set_transient_window_pos(GtkWidget
*parent
, GtkWidget
*child
)
731 gint x
, y
, w
, h
, dx
, dy
;
733 gtk_window_set_position(GTK_WINDOW(child
), GTK_WIN_POS_NONE
);
734 gtk_widget_size_request(GTK_WIDGET(child
), &req
);
736 gdk_window_get_origin(GTK_WIDGET(parent
)->window
, &x
, &y
);
737 gdk_window_get_size(GTK_WIDGET(parent
)->window
, &w
, &h
);
740 * One corner of the transient will be offset inwards, by 1/4
741 * of the parent window's size, from the corresponding corner
742 * of the parent window. The corner will be chosen so as to
743 * place the transient closer to the centre of the screen; this
744 * should avoid transients going off the edge of the screen on
747 if (x
+ w
/2 < gdk_screen_width() / 2)
748 dx
= x
+ w
/4; /* work from left edges */
750 dx
= x
+ 3*w
/4 - req
.width
; /* work from right edges */
751 if (y
+ h
/2 < gdk_screen_height() / 2)
752 dy
= y
+ h
/4; /* work from top edges */
754 dy
= y
+ 3*h
/4 - req
.height
; /* work from bottom edges */
755 gtk_widget_set_uposition(GTK_WIDGET(child
), dx
, dy
);
758 void dlg_error_msg(void *dlg
, char *msg
)
760 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
761 GtkWidget
*window
, *hbox
, *text
, *ok
;
763 window
= gtk_dialog_new();
764 text
= gtk_label_new(msg
);
765 gtk_misc_set_alignment(GTK_MISC(text
), 0.0, 0.0);
766 hbox
= gtk_hbox_new(FALSE
, 0);
767 gtk_box_pack_start(GTK_BOX(hbox
), text
, FALSE
, FALSE
, 20);
768 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->vbox
),
769 hbox
, FALSE
, FALSE
, 20);
770 gtk_widget_show(text
);
771 gtk_widget_show(hbox
);
772 gtk_window_set_title(GTK_WINDOW(window
), "Error");
773 gtk_label_set_line_wrap(GTK_LABEL(text
), TRUE
);
774 ok
= gtk_button_new_with_label("OK");
775 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(window
)->action_area
),
776 ok
, FALSE
, FALSE
, 0);
778 GTK_WIDGET_SET_FLAGS(ok
, GTK_CAN_DEFAULT
);
779 gtk_window_set_default(GTK_WINDOW(window
), ok
);
780 gtk_signal_connect(GTK_OBJECT(ok
), "clicked",
781 GTK_SIGNAL_FUNC(errmsg_button_clicked
), window
);
782 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
783 GTK_SIGNAL_FUNC(window_destroy
), NULL
);
784 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
785 gtk_window_set_transient_for(GTK_WINDOW(window
), GTK_WINDOW(dp
->window
));
786 set_transient_window_pos(dp
->window
, window
);
787 gtk_widget_show(window
);
792 * This function signals to the front end that the dialog's
793 * processing is completed, and passes an integer value (typically
796 void dlg_end(void *dlg
, int value
)
798 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
800 gtk_widget_destroy(dp
->window
);
803 void dlg_refresh(union control
*ctrl
, void *dlg
)
805 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
809 if (ctrl
->generic
.handler
!= NULL
)
810 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
814 for (i
= 0; (uc
= index234(dp
->byctrl
, i
)) != NULL
; i
++) {
815 assert(uc
->ctrl
!= NULL
);
816 if (uc
->ctrl
->generic
.handler
!= NULL
)
817 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
,
818 dp
->data
, EVENT_REFRESH
);
823 void dlg_coloursel_start(union control
*ctrl
, void *dlg
, int r
, int g
, int b
)
825 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
826 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
829 GtkWidget
*coloursel
=
830 gtk_color_selection_dialog_new("Select a colour");
831 GtkColorSelectionDialog
*ccs
= GTK_COLOR_SELECTION_DIALOG(coloursel
);
833 dp
->coloursel_result
.ok
= FALSE
;
835 gtk_window_set_modal(GTK_WINDOW(coloursel
), TRUE
);
836 gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(ccs
->colorsel
), FALSE
);
837 cvals
[0] = r
/ 255.0;
838 cvals
[1] = g
/ 255.0;
839 cvals
[2] = b
/ 255.0;
840 cvals
[3] = 1.0; /* fully opaque! */
841 gtk_color_selection_set_color(GTK_COLOR_SELECTION(ccs
->colorsel
), cvals
);
843 gtk_object_set_data(GTK_OBJECT(ccs
->ok_button
), "user-data",
844 (gpointer
)coloursel
);
845 gtk_object_set_data(GTK_OBJECT(ccs
->cancel_button
), "user-data",
846 (gpointer
)coloursel
);
847 gtk_object_set_data(GTK_OBJECT(coloursel
), "user-data", (gpointer
)uc
);
848 gtk_signal_connect(GTK_OBJECT(ccs
->ok_button
), "clicked",
849 GTK_SIGNAL_FUNC(coloursel_ok
), (gpointer
)dp
);
850 gtk_signal_connect(GTK_OBJECT(ccs
->cancel_button
), "clicked",
851 GTK_SIGNAL_FUNC(coloursel_cancel
), (gpointer
)dp
);
852 gtk_signal_connect_object(GTK_OBJECT(ccs
->ok_button
), "clicked",
853 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
854 (gpointer
)coloursel
);
855 gtk_signal_connect_object(GTK_OBJECT(ccs
->cancel_button
), "clicked",
856 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
857 (gpointer
)coloursel
);
858 gtk_widget_show(coloursel
);
861 int dlg_coloursel_results(union control
*ctrl
, void *dlg
,
862 int *r
, int *g
, int *b
)
864 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
865 if (dp
->coloursel_result
.ok
) {
866 *r
= dp
->coloursel_result
.r
;
867 *g
= dp
->coloursel_result
.g
;
868 *b
= dp
->coloursel_result
.b
;
874 /* ----------------------------------------------------------------------
875 * Signal handlers while the dialog box is active.
878 static gboolean
widget_focus(GtkWidget
*widget
, GdkEventFocus
*event
,
881 struct dlgparam
*dp
= (struct dlgparam
*)data
;
882 struct uctrl
*uc
= dlg_find_bywidget(dp
, widget
);
883 union control
*focus
;
890 if (focus
!= dp
->currfocus
) {
891 dp
->lastfocus
= dp
->currfocus
;
892 dp
->currfocus
= focus
;
898 static void button_clicked(GtkButton
*button
, gpointer data
)
900 struct dlgparam
*dp
= (struct dlgparam
*)data
;
901 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
902 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_ACTION
);
905 static void button_toggled(GtkToggleButton
*tb
, gpointer data
)
907 struct dlgparam
*dp
= (struct dlgparam
*)data
;
908 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(tb
));
909 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
912 static int editbox_key(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
915 * GtkEntry has a nasty habit of eating the Return key, which
916 * is unhelpful since it doesn't actually _do_ anything with it
917 * (it calls gtk_widget_activate, but our edit boxes never need
918 * activating). So I catch Return before GtkEntry sees it, and
919 * pass it straight on to the parent widget. Effect: hitting
920 * Return in an edit box will now activate the default button
921 * in the dialog just like it will everywhere else.
923 if (event
->keyval
== GDK_Return
&& widget
->parent
!= NULL
) {
925 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget
), "key_press_event");
926 gtk_signal_emit_by_name(GTK_OBJECT(widget
->parent
), "key_press_event",
933 static void editbox_changed(GtkEditable
*ed
, gpointer data
)
935 struct dlgparam
*dp
= (struct dlgparam
*)data
;
936 if (!(dp
->flags
& FLAG_UPDATING_COMBO_LIST
)) {
937 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(ed
));
938 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
942 static void editbox_lostfocus(GtkWidget
*ed
, GdkEventFocus
*event
,
945 struct dlgparam
*dp
= (struct dlgparam
*)data
;
946 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(ed
));
947 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
950 static int listitem_key(GtkWidget
*item
, GdkEventKey
*event
, gpointer data
,
953 GtkAdjustment
*adj
= GTK_ADJUSTMENT(data
);
955 if (event
->keyval
== GDK_Up
|| event
->keyval
== GDK_KP_Up
||
956 event
->keyval
== GDK_Down
|| event
->keyval
== GDK_KP_Down
||
957 event
->keyval
== GDK_Page_Up
|| event
->keyval
== GDK_KP_Page_Up
||
958 event
->keyval
== GDK_Page_Down
|| event
->keyval
== GDK_KP_Page_Down
) {
960 * Up, Down, PgUp or PgDn have been pressed on a ListItem
961 * in a list box. So, if the list box is single-selection:
963 * - if the list item in question isn't already selected,
964 * we simply select it.
965 * - otherwise, we find the next one (or next
966 * however-far-away) in whichever direction we're going,
968 * + in this case, we must also fiddle with the
969 * scrollbar to ensure the newly selected item is
972 * If it's multiple-selection, we do all of the above
973 * except actually selecting anything, so we move the focus
974 * and fiddle the scrollbar to follow it.
976 GtkWidget
*list
= item
->parent
;
978 gtk_signal_emit_stop_by_name(GTK_OBJECT(item
), "key_press_event");
981 GTK_WIDGET_STATE(item
) != GTK_STATE_SELECTED
) {
982 gtk_list_select_child(GTK_LIST(list
), item
);
985 (event
->keyval
==GDK_Up
|| event
->keyval
==GDK_KP_Up
||
986 event
->keyval
==GDK_Page_Up
|| event
->keyval
==GDK_KP_Page_Up
)
989 (event
->keyval
==GDK_Page_Down
||
990 event
->keyval
==GDK_KP_Page_Down
||
991 event
->keyval
==GDK_Page_Up
|| event
->keyval
==GDK_KP_Page_Up
)
994 GList
*children
, *chead
;
996 chead
= children
= gtk_container_children(GTK_CONTAINER(list
));
998 n
= g_list_length(children
);
1002 * Figure out how many list items to a screenful,
1003 * and adjust the step appropriately.
1005 step
= 0.5 + adj
->page_size
* n
/ (adj
->upper
- adj
->lower
);
1006 step
--; /* go by one less than that */
1010 while (children
!= NULL
) {
1011 if (item
== children
->data
)
1013 children
= children
->next
;
1018 if (direction
< 0 && i
> 0)
1019 children
= children
->prev
, i
--;
1020 else if (direction
> 0 && i
< n
-1)
1021 children
= children
->next
, i
++;
1025 if (children
&& children
->data
) {
1027 gtk_list_select_child(GTK_LIST(list
),
1028 GTK_WIDGET(children
->data
));
1029 gtk_widget_grab_focus(GTK_WIDGET(children
->data
));
1030 gtk_adjustment_clamp_page
1032 adj
->lower
+ (adj
->upper
-adj
->lower
) * i
/ n
,
1033 adj
->lower
+ (adj
->upper
-adj
->lower
) * (i
+1) / n
);
1044 static int listitem_single_key(GtkWidget
*item
, GdkEventKey
*event
,
1047 return listitem_key(item
, event
, data
, FALSE
);
1050 static int listitem_multi_key(GtkWidget
*item
, GdkEventKey
*event
,
1053 return listitem_key(item
, event
, data
, TRUE
);
1056 static int listitem_button(GtkWidget
*item
, GdkEventButton
*event
,
1059 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1060 if (event
->type
== GDK_2BUTTON_PRESS
||
1061 event
->type
== GDK_3BUTTON_PRESS
) {
1062 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(item
));
1063 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_ACTION
);
1069 static void list_selchange(GtkList
*list
, gpointer data
)
1071 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1072 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(list
));
1074 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_SELCHANGE
);
1077 static void menuitem_activate(GtkMenuItem
*item
, gpointer data
)
1079 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1080 GtkWidget
*menushell
= GTK_WIDGET(item
)->parent
;
1081 gpointer optmenu
= gtk_object_get_data(GTK_OBJECT(menushell
), "user-data");
1082 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(optmenu
));
1083 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_SELCHANGE
);
1086 static void draglist_move(struct dlgparam
*dp
, struct uctrl
*uc
, int direction
)
1088 int index
= dlg_listbox_index(uc
->ctrl
, dp
);
1089 GList
*children
= gtk_container_children(GTK_CONTAINER(uc
->list
));
1093 (index
== 0 && direction
< 0) ||
1094 (index
== g_list_length(children
)-1 && direction
> 0)) {
1099 child
= g_list_nth_data(children
, index
);
1100 gtk_widget_ref(child
);
1101 gtk_list_clear_items(GTK_LIST(uc
->list
), index
, index
+1);
1102 g_list_free(children
);
1105 children
= g_list_append(children
, child
);
1106 gtk_list_insert_items(GTK_LIST(uc
->list
), children
, index
+ direction
);
1107 gtk_list_select_item(GTK_LIST(uc
->list
), index
+ direction
);
1108 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1111 static void draglist_up(GtkButton
*button
, gpointer data
)
1113 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1114 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
1115 draglist_move(dp
, uc
, -1);
1118 static void draglist_down(GtkButton
*button
, gpointer data
)
1120 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1121 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
1122 draglist_move(dp
, uc
, +1);
1125 static void filesel_ok(GtkButton
*button
, gpointer data
)
1127 /* struct dlgparam *dp = (struct dlgparam *)data; */
1128 gpointer filesel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
1129 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(filesel
), "user-data");
1130 char *name
= gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel
));
1131 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), name
);
1134 static void fontsel_ok(GtkButton
*button
, gpointer data
)
1136 /* struct dlgparam *dp = (struct dlgparam *)data; */
1137 gpointer fontsel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
1138 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(fontsel
), "user-data");
1139 char *name
= gtk_font_selection_dialog_get_font_name
1140 (GTK_FONT_SELECTION_DIALOG(fontsel
));
1141 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), name
);
1144 static void coloursel_ok(GtkButton
*button
, gpointer data
)
1146 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1147 gpointer coloursel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
1148 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(coloursel
), "user-data");
1150 gtk_color_selection_get_color
1151 (GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(coloursel
)->colorsel
),
1153 dp
->coloursel_result
.r
= (int) (255 * cvals
[0]);
1154 dp
->coloursel_result
.g
= (int) (255 * cvals
[1]);
1155 dp
->coloursel_result
.b
= (int) (255 * cvals
[2]);
1156 dp
->coloursel_result
.ok
= TRUE
;
1157 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_CALLBACK
);
1160 static void coloursel_cancel(GtkButton
*button
, gpointer data
)
1162 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1163 gpointer coloursel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
1164 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(coloursel
), "user-data");
1165 dp
->coloursel_result
.ok
= FALSE
;
1166 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_CALLBACK
);
1169 static void filefont_clicked(GtkButton
*button
, gpointer data
)
1171 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1172 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
1174 if (uc
->ctrl
->generic
.type
== CTRL_FILESELECT
) {
1175 GtkWidget
*filesel
=
1176 gtk_file_selection_new(uc
->ctrl
->fileselect
.title
);
1177 gtk_window_set_modal(GTK_WINDOW(filesel
), TRUE
);
1179 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->ok_button
), "user-data",
1181 gtk_object_set_data(GTK_OBJECT(filesel
), "user-data", (gpointer
)uc
);
1183 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->ok_button
), "clicked",
1184 GTK_SIGNAL_FUNC(filesel_ok
), (gpointer
)dp
);
1185 gtk_signal_connect_object
1186 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->ok_button
), "clicked",
1187 GTK_SIGNAL_FUNC(gtk_widget_destroy
), (gpointer
)filesel
);
1188 gtk_signal_connect_object
1189 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->cancel_button
), "clicked",
1190 GTK_SIGNAL_FUNC(gtk_widget_destroy
), (gpointer
)filesel
);
1191 gtk_widget_show(filesel
);
1194 if (uc
->ctrl
->generic
.type
== CTRL_FONTSELECT
) {
1195 gchar
*spacings
[] = { "c", "m", NULL
};
1196 gchar
*fontname
= gtk_entry_get_text(GTK_ENTRY(uc
->entry
));
1197 GtkWidget
*fontsel
=
1198 gtk_font_selection_dialog_new("Select a font");
1199 gtk_window_set_modal(GTK_WINDOW(fontsel
), TRUE
);
1200 gtk_font_selection_dialog_set_filter
1201 (GTK_FONT_SELECTION_DIALOG(fontsel
),
1202 GTK_FONT_FILTER_BASE
, GTK_FONT_ALL
,
1203 NULL
, NULL
, NULL
, NULL
, spacings
, NULL
);
1204 if (!gtk_font_selection_dialog_set_font_name
1205 (GTK_FONT_SELECTION_DIALOG(fontsel
), fontname
)) {
1207 * If the font name wasn't found as it was, try opening
1208 * it and extracting its FONT property. This should
1209 * have the effect of mapping short aliases into true
1212 GdkFont
*font
= gdk_font_load(fontname
);
1214 XFontStruct
*xfs
= GDK_FONT_XFONT(font
);
1215 Display
*disp
= GDK_FONT_XDISPLAY(font
);
1216 Atom fontprop
= XInternAtom(disp
, "FONT", False
);
1218 if (XGetFontProperty(xfs
, fontprop
, &ret
)) {
1219 char *name
= XGetAtomName(disp
, (Atom
)ret
);
1221 gtk_font_selection_dialog_set_font_name
1222 (GTK_FONT_SELECTION_DIALOG(fontsel
), name
);
1224 gdk_font_unref(font
);
1228 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->ok_button
),
1229 "user-data", (gpointer
)fontsel
);
1230 gtk_object_set_data(GTK_OBJECT(fontsel
), "user-data", (gpointer
)uc
);
1232 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->ok_button
),
1233 "clicked", GTK_SIGNAL_FUNC(fontsel_ok
), (gpointer
)dp
);
1234 gtk_signal_connect_object
1235 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->ok_button
),
1236 "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy
),
1238 gtk_signal_connect_object
1239 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->cancel_button
),
1240 "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy
),
1242 gtk_widget_show(fontsel
);
1246 static void label_sizealloc(GtkWidget
*widget
, GtkAllocation
*alloc
,
1249 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1250 struct uctrl
*uc
= dlg_find_bywidget(dp
, widget
);
1252 gtk_widget_set_usize(uc
->text
, alloc
->width
, -1);
1253 gtk_label_set_text(GTK_LABEL(uc
->text
), uc
->ctrl
->generic
.label
);
1254 gtk_signal_disconnect(GTK_OBJECT(uc
->text
), uc
->textsig
);
1257 /* ----------------------------------------------------------------------
1258 * This function does the main layout work: it reads a controlset,
1259 * it creates the relevant GTK controls, and returns a GtkWidget
1260 * containing the result. (This widget might be a title of some
1261 * sort, it might be a Columns containing many controls, or it
1262 * might be a GtkFrame containing a Columns; whatever it is, it's
1263 * definitely a GtkWidget and should probably be added to a
1266 * `listitemheight' is used to calculate a usize for list boxes: it
1267 * should be the height from the size request of a GtkListItem.
1269 * `win' is required for setting the default button. If it is
1270 * non-NULL, all buttons created will be default-capable (so they
1271 * have extra space round them for the default highlight).
1273 GtkWidget
*layout_ctrls(struct dlgparam
*dp
, struct Shortcuts
*scs
,
1274 struct controlset
*s
, int listitemheight
,
1281 if (!s
->boxname
&& s
->boxtitle
) {
1282 /* This controlset is a panel title. */
1283 return gtk_label_new(s
->boxtitle
);
1287 * Otherwise, we expect to be laying out actual controls, so
1288 * we'll start by creating a Columns for the purpose.
1290 cols
= COLUMNS(columns_new(4));
1291 ret
= GTK_WIDGET(cols
);
1292 gtk_widget_show(ret
);
1295 * Create a containing frame if we have a box name.
1298 ret
= gtk_frame_new(s
->boxtitle
); /* NULL is valid here */
1299 gtk_container_set_border_width(GTK_CONTAINER(cols
), 4);
1300 gtk_container_add(GTK_CONTAINER(ret
), GTK_WIDGET(cols
));
1301 gtk_widget_show(ret
);
1305 * Now iterate through the controls themselves, create them,
1306 * and add them to the Columns.
1308 for (i
= 0; i
< s
->ncontrols
; i
++) {
1309 union control
*ctrl
= s
->ctrls
[i
];
1312 GtkWidget
*w
= NULL
;
1314 switch (ctrl
->generic
.type
) {
1317 static const int simplecols
[1] = { 100 };
1318 columns_set_cols(cols
, ctrl
->columns
.ncols
,
1319 (ctrl
->columns
.percentages ?
1320 ctrl
->columns
.percentages
: simplecols
));
1322 continue; /* no actual control created */
1325 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
->tabdelay
.ctrl
);
1327 columns_taborder_last(cols
, uc
->toplevel
);
1329 continue; /* no actual control created */
1332 uc
= snew(struct uctrl
);
1334 uc
->privdata
= NULL
;
1335 uc
->privdata_needs_free
= FALSE
;
1337 uc
->entry
= uc
->list
= uc
->menu
= NULL
;
1338 uc
->button
= uc
->optmenu
= uc
->text
= NULL
;
1340 switch (ctrl
->generic
.type
) {
1342 w
= gtk_button_new_with_label(ctrl
->generic
.label
);
1344 GTK_WIDGET_SET_FLAGS(w
, GTK_CAN_DEFAULT
);
1345 if (ctrl
->button
.isdefault
)
1346 gtk_window_set_default(win
, w
);
1347 if (ctrl
->button
.iscancel
)
1348 dp
->cancelbutton
= w
;
1350 gtk_signal_connect(GTK_OBJECT(w
), "clicked",
1351 GTK_SIGNAL_FUNC(button_clicked
), dp
);
1352 gtk_signal_connect(GTK_OBJECT(w
), "focus_in_event",
1353 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1354 shortcut_add(scs
, GTK_BIN(w
)->child
, ctrl
->button
.shortcut
,
1355 SHORTCUT_UCTRL
, uc
);
1358 w
= gtk_check_button_new_with_label(ctrl
->generic
.label
);
1359 gtk_signal_connect(GTK_OBJECT(w
), "toggled",
1360 GTK_SIGNAL_FUNC(button_toggled
), dp
);
1361 gtk_signal_connect(GTK_OBJECT(w
), "focus_in_event",
1362 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1363 shortcut_add(scs
, GTK_BIN(w
)->child
, ctrl
->checkbox
.shortcut
,
1364 SHORTCUT_UCTRL
, uc
);
1369 * Radio buttons get to go inside their own Columns, no
1373 gint i
, *percentages
;
1377 if (ctrl
->generic
.label
) {
1378 GtkWidget
*label
= gtk_label_new(ctrl
->generic
.label
);
1379 columns_add(COLUMNS(w
), label
, 0, 1);
1380 columns_force_left_align(COLUMNS(w
), label
);
1381 gtk_widget_show(label
);
1382 shortcut_add(scs
, label
, ctrl
->radio
.shortcut
,
1383 SHORTCUT_UCTRL
, uc
);
1385 percentages
= g_new(gint
, ctrl
->radio
.ncolumns
);
1386 for (i
= 0; i
< ctrl
->radio
.ncolumns
; i
++) {
1388 ((100 * (i
+1) / ctrl
->radio
.ncolumns
) -
1389 100 * i
/ ctrl
->radio
.ncolumns
);
1391 columns_set_cols(COLUMNS(w
), ctrl
->radio
.ncolumns
,
1393 g_free(percentages
);
1396 uc
->nbuttons
= ctrl
->radio
.nbuttons
;
1397 uc
->buttons
= snewn(uc
->nbuttons
, GtkWidget
*);
1399 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
1403 b
= (gtk_radio_button_new_with_label
1404 (group
, ctrl
->radio
.buttons
[i
]));
1406 group
= gtk_radio_button_group(GTK_RADIO_BUTTON(b
));
1407 colstart
= i
% ctrl
->radio
.ncolumns
;
1408 columns_add(COLUMNS(w
), b
, colstart
,
1409 (i
== ctrl
->radio
.nbuttons
-1 ?
1410 ctrl
->radio
.ncolumns
- colstart
: 1));
1411 columns_force_left_align(COLUMNS(w
), b
);
1413 gtk_signal_connect(GTK_OBJECT(b
), "toggled",
1414 GTK_SIGNAL_FUNC(button_toggled
), dp
);
1415 gtk_signal_connect(GTK_OBJECT(b
), "focus_in_event",
1416 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1417 if (ctrl
->radio
.shortcuts
) {
1418 shortcut_add(scs
, GTK_BIN(b
)->child
,
1419 ctrl
->radio
.shortcuts
[i
],
1420 SHORTCUT_UCTRL
, uc
);
1429 if (ctrl
->editbox
.has_list
) {
1430 w
= gtk_combo_new();
1431 gtk_combo_set_value_in_list(GTK_COMBO(w
), FALSE
, TRUE
);
1432 uc
->entry
= GTK_COMBO(w
)->entry
;
1433 uc
->list
= GTK_COMBO(w
)->list
;
1435 w
= gtk_entry_new();
1436 if (ctrl
->editbox
.password
)
1437 gtk_entry_set_visibility(GTK_ENTRY(w
), FALSE
);
1440 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "changed",
1441 GTK_SIGNAL_FUNC(editbox_changed
), dp
);
1442 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "key_press_event",
1443 GTK_SIGNAL_FUNC(editbox_key
), dp
);
1444 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "focus_in_event",
1445 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1447 * Edit boxes, for some strange reason, have a minimum
1448 * width of 150 in GTK 1.2. We don't want this - we'd
1449 * rather the edit boxes acquired their natural width
1450 * from the column layout of the rest of the box.
1452 * Also, while we're here, we'll squirrel away the
1453 * edit box height so we can use that to centre its
1454 * label vertically beside it.
1456 gtk_widget_size_request(w
, &req
);
1457 gtk_widget_set_usize(w
, 10, req
.height
);
1459 if (ctrl
->generic
.label
) {
1460 GtkWidget
*label
, *container
;
1462 label
= gtk_label_new(ctrl
->generic
.label
);
1464 shortcut_add(scs
, label
, ctrl
->editbox
.shortcut
,
1465 SHORTCUT_FOCUS
, uc
->entry
);
1467 container
= columns_new(4);
1468 if (ctrl
->editbox
.percentwidth
== 100) {
1469 columns_add(COLUMNS(container
), label
, 0, 1);
1470 columns_force_left_align(COLUMNS(container
), label
);
1471 columns_add(COLUMNS(container
), w
, 0, 1);
1473 gint percentages
[2];
1474 percentages
[1] = ctrl
->editbox
.percentwidth
;
1475 percentages
[0] = 100 - ctrl
->editbox
.percentwidth
;
1476 columns_set_cols(COLUMNS(container
), 2, percentages
);
1477 columns_add(COLUMNS(container
), label
, 0, 1);
1478 columns_force_left_align(COLUMNS(container
), label
);
1479 columns_add(COLUMNS(container
), w
, 1, 1);
1480 /* Centre the label vertically. */
1481 gtk_widget_set_usize(label
, -1, req
.height
);
1482 gtk_misc_set_alignment(GTK_MISC(label
), 0.0, 0.5);
1484 gtk_widget_show(label
);
1489 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "focus_out_event",
1490 GTK_SIGNAL_FUNC(editbox_lostfocus
), dp
);
1493 case CTRL_FILESELECT
:
1494 case CTRL_FONTSELECT
:
1499 (ctrl
->generic
.type
== CTRL_FILESELECT ?
1500 "Browse..." : "Change...");
1502 gint percentages
[] = { 75, 25 };
1504 columns_set_cols(COLUMNS(w
), 2, percentages
);
1506 if (ctrl
->generic
.label
) {
1507 ww
= gtk_label_new(ctrl
->generic
.label
);
1508 columns_add(COLUMNS(w
), ww
, 0, 2);
1509 columns_force_left_align(COLUMNS(w
), ww
);
1510 gtk_widget_show(ww
);
1511 shortcut_add(scs
, ww
,
1512 (ctrl
->generic
.type
== CTRL_FILESELECT ?
1513 ctrl
->fileselect
.shortcut
:
1514 ctrl
->fontselect
.shortcut
),
1515 SHORTCUT_UCTRL
, uc
);
1518 uc
->entry
= ww
= gtk_entry_new();
1519 gtk_widget_size_request(ww
, &req
);
1520 gtk_widget_set_usize(ww
, 10, req
.height
);
1521 columns_add(COLUMNS(w
), ww
, 0, 1);
1522 gtk_widget_show(ww
);
1524 uc
->button
= ww
= gtk_button_new_with_label(browsebtn
);
1525 columns_add(COLUMNS(w
), ww
, 1, 1);
1526 gtk_widget_show(ww
);
1528 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "key_press_event",
1529 GTK_SIGNAL_FUNC(editbox_key
), dp
);
1530 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "changed",
1531 GTK_SIGNAL_FUNC(editbox_changed
), dp
);
1532 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "focus_in_event",
1533 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1534 gtk_signal_connect(GTK_OBJECT(uc
->button
), "focus_in_event",
1535 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1536 gtk_signal_connect(GTK_OBJECT(ww
), "clicked",
1537 GTK_SIGNAL_FUNC(filefont_clicked
), dp
);
1541 if (ctrl
->listbox
.height
== 0) {
1542 uc
->optmenu
= w
= gtk_option_menu_new();
1543 uc
->menu
= gtk_menu_new();
1544 gtk_option_menu_set_menu(GTK_OPTION_MENU(w
), uc
->menu
);
1545 gtk_object_set_data(GTK_OBJECT(uc
->menu
), "user-data",
1546 (gpointer
)uc
->optmenu
);
1547 gtk_signal_connect(GTK_OBJECT(uc
->optmenu
), "focus_in_event",
1548 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1550 uc
->list
= gtk_list_new();
1551 if (ctrl
->listbox
.multisel
== 2) {
1552 gtk_list_set_selection_mode(GTK_LIST(uc
->list
),
1553 GTK_SELECTION_EXTENDED
);
1554 } else if (ctrl
->listbox
.multisel
== 1) {
1555 gtk_list_set_selection_mode(GTK_LIST(uc
->list
),
1556 GTK_SELECTION_MULTIPLE
);
1558 gtk_list_set_selection_mode(GTK_LIST(uc
->list
),
1559 GTK_SELECTION_SINGLE
);
1561 w
= gtk_scrolled_window_new(NULL
, NULL
);
1562 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(w
),
1564 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
1566 GTK_POLICY_AUTOMATIC
);
1567 uc
->adj
= gtk_scrolled_window_get_vadjustment
1568 (GTK_SCROLLED_WINDOW(w
));
1570 gtk_widget_show(uc
->list
);
1571 gtk_signal_connect(GTK_OBJECT(uc
->list
), "selection-changed",
1572 GTK_SIGNAL_FUNC(list_selchange
), dp
);
1573 gtk_signal_connect(GTK_OBJECT(uc
->list
), "focus_in_event",
1574 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1577 * Adjust the height of the scrolled window to the
1578 * minimum given by the height parameter.
1580 * This piece of guesswork is a horrid hack based
1581 * on looking inside the GTK 1.2 sources
1582 * (specifically gtkviewport.c, which appears to be
1583 * the widget which provides the border around the
1584 * scrolling area). Anyone lets me know how I can
1585 * do this in a way which isn't at risk from GTK
1586 * upgrades, I'd be grateful.
1589 int edge
= GTK_WIDGET(uc
->list
)->style
->klass
->ythickness
;
1590 gtk_widget_set_usize(w
, 10,
1591 2*edge
+ (ctrl
->listbox
.height
*
1595 if (ctrl
->listbox
.draglist
) {
1597 * GTK doesn't appear to make it easy to
1598 * implement a proper draggable list; so
1599 * instead I'm just going to have to put an Up
1600 * and a Down button to the right of the actual
1601 * list box. Ah well.
1603 GtkWidget
*cols
, *button
;
1604 static const gint percentages
[2] = { 80, 20 };
1606 cols
= columns_new(4);
1607 columns_set_cols(COLUMNS(cols
), 2, percentages
);
1608 columns_add(COLUMNS(cols
), w
, 0, 1);
1610 button
= gtk_button_new_with_label("Up");
1611 columns_add(COLUMNS(cols
), button
, 1, 1);
1612 gtk_widget_show(button
);
1613 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1614 GTK_SIGNAL_FUNC(draglist_up
), dp
);
1615 gtk_signal_connect(GTK_OBJECT(button
), "focus_in_event",
1616 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1617 button
= gtk_button_new_with_label("Down");
1618 columns_add(COLUMNS(cols
), button
, 1, 1);
1619 gtk_widget_show(button
);
1620 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1621 GTK_SIGNAL_FUNC(draglist_down
), dp
);
1622 gtk_signal_connect(GTK_OBJECT(button
), "focus_in_event",
1623 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1629 if (ctrl
->generic
.label
) {
1630 GtkWidget
*label
, *container
;
1632 label
= gtk_label_new(ctrl
->generic
.label
);
1634 container
= columns_new(4);
1635 if (ctrl
->listbox
.percentwidth
== 100) {
1636 columns_add(COLUMNS(container
), label
, 0, 1);
1637 columns_force_left_align(COLUMNS(container
), label
);
1638 columns_add(COLUMNS(container
), w
, 0, 1);
1640 gint percentages
[2];
1641 percentages
[1] = ctrl
->listbox
.percentwidth
;
1642 percentages
[0] = 100 - ctrl
->listbox
.percentwidth
;
1643 columns_set_cols(COLUMNS(container
), 2, percentages
);
1644 columns_add(COLUMNS(container
), label
, 0, 1);
1645 columns_force_left_align(COLUMNS(container
), label
);
1646 columns_add(COLUMNS(container
), w
, 1, 1);
1648 gtk_widget_show(label
);
1650 shortcut_add(scs
, label
, ctrl
->listbox
.shortcut
,
1651 SHORTCUT_UCTRL
, uc
);
1657 * Wrapping text widgets don't sit well with the GTK
1658 * layout model, in which widgets state a minimum size
1659 * and the whole window then adjusts to the smallest
1660 * size it can sensibly take given its contents. A
1661 * wrapping text widget _has_ no clear minimum size;
1662 * instead it has a range of possibilities. It can be
1663 * one line deep but 2000 wide, or two lines deep and
1664 * 1000 pixels, or three by 867, or four by 500 and so
1665 * on. It can be as short as you like provided you
1666 * don't mind it being wide, or as narrow as you like
1667 * provided you don't mind it being tall.
1669 * Therefore, it fits very badly into the layout model.
1670 * Hence the only thing to do is pick a width and let
1671 * it choose its own number of lines. To do this I'm
1672 * going to cheat a little. All new wrapping text
1673 * widgets will be created with a minimal text content
1674 * "X"; then, after the rest of the dialog box is set
1675 * up and its size calculated, the text widgets will be
1676 * told their width and given their real text, which
1677 * will cause the size to be recomputed in the y
1678 * direction (because many of them will expand to more
1681 uc
->text
= w
= gtk_label_new("X");
1682 gtk_misc_set_alignment(GTK_MISC(w
), 0.0, 0.0);
1683 gtk_label_set_line_wrap(GTK_LABEL(w
), TRUE
);
1685 gtk_signal_connect(GTK_OBJECT(w
), "size-allocate",
1686 GTK_SIGNAL_FUNC(label_sizealloc
), dp
);
1692 columns_add(cols
, w
,
1693 COLUMN_START(ctrl
->generic
.column
),
1694 COLUMN_SPAN(ctrl
->generic
.column
));
1696 columns_force_left_align(cols
, w
);
1700 dlg_add_uctrl(dp
, uc
);
1707 struct dlgparam
*dp
;
1709 GtkWidget
*panel
, *treeitem
;
1710 struct Shortcuts shortcuts
;
1713 static void treeitem_sel(GtkItem
*item
, gpointer data
)
1715 struct selparam
*sp
= (struct selparam
*)data
;
1717 panels_switch_to(sp
->panels
, sp
->panel
);
1719 sp
->dp
->shortcuts
= &sp
->shortcuts
;
1720 sp
->dp
->currtreeitem
= sp
->treeitem
;
1723 static void window_destroy(GtkWidget
*widget
, gpointer data
)
1728 static int tree_grab_focus(struct dlgparam
*dp
)
1733 * See if any of the treeitems has the focus.
1736 for (i
= 0; i
< dp
->ntreeitems
; i
++)
1737 if (GTK_WIDGET_HAS_FOCUS(dp
->treeitems
[i
])) {
1745 gtk_widget_grab_focus(dp
->currtreeitem
);
1750 gint
tree_focus(GtkContainer
*container
, GtkDirectionType direction
,
1753 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1755 gtk_signal_emit_stop_by_name(GTK_OBJECT(container
), "focus");
1757 * If there's a focused treeitem, we return FALSE to cause the
1758 * focus to move on to some totally other control. If not, we
1759 * focus the selected one.
1761 return tree_grab_focus(dp
);
1764 int win_key_press(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1766 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1768 if (event
->keyval
== GDK_Escape
&& dp
->cancelbutton
) {
1769 gtk_signal_emit_by_name(GTK_OBJECT(dp
->cancelbutton
), "clicked");
1773 if ((event
->state
& GDK_MOD1_MASK
) &&
1774 (unsigned char)event
->string
[0] > 0 &&
1775 (unsigned char)event
->string
[0] <= 127) {
1776 int schr
= (unsigned char)event
->string
[0];
1777 struct Shortcut
*sc
= &dp
->shortcuts
->sc
[schr
];
1779 switch (sc
->action
) {
1781 tree_grab_focus(dp
);
1783 case SHORTCUT_FOCUS
:
1784 gtk_widget_grab_focus(sc
->widget
);
1786 case SHORTCUT_UCTRL
:
1788 * We must do something sensible with a uctrl.
1789 * Precisely what this is depends on the type of
1792 switch (sc
->uc
->ctrl
->generic
.type
) {
1795 /* Check boxes and buttons get the focus _and_ get toggled. */
1796 gtk_widget_grab_focus(sc
->uc
->toplevel
);
1797 gtk_signal_emit_by_name(GTK_OBJECT(sc
->uc
->toplevel
),
1800 case CTRL_FILESELECT
:
1801 case CTRL_FONTSELECT
:
1802 /* File/font selectors have their buttons pressed (ooer),
1803 * and focus transferred to the edit box. */
1804 gtk_signal_emit_by_name(GTK_OBJECT(sc
->uc
->button
),
1806 gtk_widget_grab_focus(sc
->uc
->entry
);
1810 * Radio buttons are fun, because they have
1811 * multiple shortcuts. We must find whether the
1812 * activated shortcut is the shortcut for the whole
1813 * group, or for a particular button. In the former
1814 * case, we find the currently selected button and
1815 * focus it; in the latter, we focus-and-click the
1816 * button whose shortcut was pressed.
1818 if (schr
== sc
->uc
->ctrl
->radio
.shortcut
) {
1820 for (i
= 0; i
< sc
->uc
->ctrl
->radio
.nbuttons
; i
++)
1821 if (gtk_toggle_button_get_active
1822 (GTK_TOGGLE_BUTTON(sc
->uc
->buttons
[i
]))) {
1823 gtk_widget_grab_focus(sc
->uc
->buttons
[i
]);
1825 } else if (sc
->uc
->ctrl
->radio
.shortcuts
) {
1827 for (i
= 0; i
< sc
->uc
->ctrl
->radio
.nbuttons
; i
++)
1828 if (schr
== sc
->uc
->ctrl
->radio
.shortcuts
[i
]) {
1829 gtk_widget_grab_focus(sc
->uc
->buttons
[i
]);
1830 gtk_signal_emit_by_name
1831 (GTK_OBJECT(sc
->uc
->buttons
[i
]), "clicked");
1837 * If the list is really an option menu, we focus
1838 * and click it. Otherwise we tell it to focus one
1839 * of its children, which appears to do the Right
1842 if (sc
->uc
->optmenu
) {
1846 gtk_widget_grab_focus(sc
->uc
->optmenu
);
1847 /* Option menus don't work using the "clicked" signal.
1848 * We need to manufacture a button press event :-/ */
1849 bev
.type
= GDK_BUTTON_PRESS
;
1851 gtk_signal_emit_by_name(GTK_OBJECT(sc
->uc
->optmenu
),
1852 "button_press_event",
1855 assert(sc
->uc
->list
!= NULL
);
1857 gtk_container_focus(GTK_CONTAINER(sc
->uc
->list
),
1858 GTK_DIR_TAB_FORWARD
);
1869 int tree_key_press(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1871 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1873 if (event
->keyval
== GDK_Up
|| event
->keyval
== GDK_KP_Up
||
1874 event
->keyval
== GDK_Down
|| event
->keyval
== GDK_KP_Down
) {
1876 for (i
= 0; i
< dp
->ntreeitems
; i
++)
1877 if (widget
== dp
->treeitems
[i
])
1879 if (i
< dp
->ntreeitems
) {
1880 if (event
->keyval
== GDK_Up
|| event
->keyval
== GDK_KP_Up
)
1887 if (i
< 0 || i
>= dp
->ntreeitems
)
1888 break; /* nothing in that dir to select */
1890 * Determine if this tree item is visible.
1893 GtkWidget
*w
= dp
->treeitems
[i
];
1895 while (w
&& (GTK_IS_TREE_ITEM(w
) || GTK_IS_TREE(w
))) {
1896 if (!GTK_WIDGET_VISIBLE(w
)) {
1903 j
= i
; /* got one */
1909 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget
),
1912 gtk_signal_emit_by_name(GTK_OBJECT(dp
->treeitems
[j
]), "toggle");
1913 gtk_widget_grab_focus(dp
->treeitems
[j
]);
1919 * It's nice for Left and Right to expand and collapse tree
1922 if (event
->keyval
== GDK_Left
|| event
->keyval
== GDK_KP_Left
) {
1923 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget
),
1925 gtk_tree_item_collapse(GTK_TREE_ITEM(widget
));
1928 if (event
->keyval
== GDK_Right
|| event
->keyval
== GDK_KP_Right
) {
1929 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget
),
1931 gtk_tree_item_expand(GTK_TREE_ITEM(widget
));
1938 void shortcut_add(struct Shortcuts
*scs
, GtkWidget
*labelw
,
1939 int chr
, int action
, void *ptr
)
1941 GtkLabel
*label
= GTK_LABEL(labelw
);
1942 gchar
*currstr
, *pattern
;
1945 if (chr
== NO_SHORTCUT
)
1948 chr
= tolower((unsigned char)chr
);
1950 assert(scs
->sc
[chr
].action
== SHORTCUT_EMPTY
);
1952 scs
->sc
[chr
].action
= action
;
1954 if (action
== SHORTCUT_FOCUS
) {
1955 scs
->sc
[chr
].uc
= NULL
;
1956 scs
->sc
[chr
].widget
= (GtkWidget
*)ptr
;
1958 scs
->sc
[chr
].widget
= NULL
;
1959 scs
->sc
[chr
].uc
= (struct uctrl
*)ptr
;
1962 gtk_label_get(label
, &currstr
);
1963 for (i
= 0; currstr
[i
]; i
++)
1964 if (tolower((unsigned char)currstr
[i
]) == chr
) {
1967 pattern
= dupprintf("%*s_", i
, "");
1969 gtk_widget_size_request(GTK_WIDGET(label
), &req
);
1970 gtk_label_set_pattern(label
, pattern
);
1971 gtk_widget_set_usize(GTK_WIDGET(label
), -1, req
.height
);
1978 int get_listitemheight(void)
1980 GtkWidget
*listitem
= gtk_list_item_new_with_label("foo");
1982 gtk_widget_size_request(listitem
, &req
);
1983 gtk_widget_unref(listitem
);
1987 int do_config_box(const char *title
, Config
*cfg
, int midsession
,
1990 GtkWidget
*window
, *hbox
, *vbox
, *cols
, *label
,
1991 *tree
, *treescroll
, *panels
, *panelvbox
;
1992 int index
, level
, listitemheight
;
1993 struct controlbox
*ctrlbox
;
1995 GtkTreeItem
*treeitemlevels
[8];
1996 GtkTree
*treelevels
[8];
1998 struct Shortcuts scs
;
2000 struct selparam
*selparams
= NULL
;
2001 int nselparams
= 0, selparamsize
= 0;
2005 listitemheight
= get_listitemheight();
2007 for (index
= 0; index
< lenof(scs
.sc
); index
++) {
2008 scs
.sc
[index
].action
= SHORTCUT_EMPTY
;
2011 window
= gtk_dialog_new();
2013 ctrlbox
= ctrl_new_box();
2014 setup_config_box(ctrlbox
, midsession
, cfg
->protocol
, protcfginfo
);
2015 unix_setup_config_box(ctrlbox
, midsession
);
2016 gtk_setup_config_box(ctrlbox
, midsession
, window
);
2018 gtk_window_set_title(GTK_WINDOW(window
), title
);
2019 hbox
= gtk_hbox_new(FALSE
, 4);
2020 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->vbox
), hbox
, TRUE
, TRUE
, 0);
2021 gtk_container_set_border_width(GTK_CONTAINER(hbox
), 10);
2022 gtk_widget_show(hbox
);
2023 vbox
= gtk_vbox_new(FALSE
, 4);
2024 gtk_box_pack_start(GTK_BOX(hbox
), vbox
, FALSE
, FALSE
, 0);
2025 gtk_widget_show(vbox
);
2026 cols
= columns_new(4);
2027 gtk_box_pack_start(GTK_BOX(vbox
), cols
, FALSE
, FALSE
, 0);
2028 gtk_widget_show(cols
);
2029 label
= gtk_label_new("Category:");
2030 columns_add(COLUMNS(cols
), label
, 0, 1);
2031 columns_force_left_align(COLUMNS(cols
), label
);
2032 gtk_widget_show(label
);
2033 treescroll
= gtk_scrolled_window_new(NULL
, NULL
);
2034 tree
= gtk_tree_new();
2035 gtk_signal_connect(GTK_OBJECT(tree
), "focus_in_event",
2036 GTK_SIGNAL_FUNC(widget_focus
), &dp
);
2037 shortcut_add(&scs
, label
, 'g', SHORTCUT_TREE
, tree
);
2038 gtk_tree_set_view_mode(GTK_TREE(tree
), GTK_TREE_VIEW_ITEM
);
2039 gtk_tree_set_selection_mode(GTK_TREE(tree
), GTK_SELECTION_BROWSE
);
2040 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(treescroll
),
2042 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll
),
2044 GTK_POLICY_AUTOMATIC
);
2045 gtk_signal_connect(GTK_OBJECT(tree
), "focus",
2046 GTK_SIGNAL_FUNC(tree_focus
), &dp
);
2047 gtk_widget_show(tree
);
2048 gtk_widget_show(treescroll
);
2049 gtk_box_pack_start(GTK_BOX(vbox
), treescroll
, TRUE
, TRUE
, 0);
2050 panels
= panels_new();
2051 gtk_box_pack_start(GTK_BOX(hbox
), panels
, TRUE
, TRUE
, 0);
2052 gtk_widget_show(panels
);
2057 for (index
= 0; index
< ctrlbox
->nctrlsets
; index
++) {
2058 struct controlset
*s
= ctrlbox
->ctrlsets
[index
];
2061 if (!*s
->pathname
) {
2062 w
= layout_ctrls(&dp
, &scs
, s
, listitemheight
, GTK_WINDOW(window
));
2063 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->action_area
),
2066 int j
= path ?
ctrl_path_compare(s
->pathname
, path
) : 0;
2067 if (j
!= INT_MAX
) { /* add to treeview, start new panel */
2069 GtkWidget
*treeitem
;
2073 * We expect never to find an implicit path
2074 * component. For example, we expect never to see
2075 * A/B/C followed by A/D/E, because that would
2076 * _implicitly_ create A/D. All our path prefixes
2077 * are expected to contain actual controls and be
2078 * selectable in the treeview; so we would expect
2079 * to see A/D _explicitly_ before encountering
2082 assert(j
== ctrl_path_elements(s
->pathname
) - 1);
2084 c
= strrchr(s
->pathname
, '/');
2090 treeitem
= gtk_tree_item_new_with_label(c
);
2091 assert(j
-1 < level
);
2093 if (!treelevels
[j
-1]) {
2094 treelevels
[j
-1] = GTK_TREE(gtk_tree_new());
2095 gtk_tree_item_set_subtree
2096 (treeitemlevels
[j
-1],
2097 GTK_WIDGET(treelevels
[j
-1]));
2098 gtk_tree_item_expand(treeitemlevels
[j
-1]);
2100 gtk_tree_append(treelevels
[j
-1], treeitem
);
2102 gtk_tree_append(GTK_TREE(tree
), treeitem
);
2104 treeitemlevels
[j
] = GTK_TREE_ITEM(treeitem
);
2105 treelevels
[j
] = NULL
;
2108 gtk_signal_connect(GTK_OBJECT(treeitem
), "key_press_event",
2109 GTK_SIGNAL_FUNC(tree_key_press
), &dp
);
2110 gtk_signal_connect(GTK_OBJECT(treeitem
), "focus_in_event",
2111 GTK_SIGNAL_FUNC(widget_focus
), &dp
);
2113 gtk_widget_show(treeitem
);
2117 first
= (panelvbox
== NULL
);
2119 panelvbox
= gtk_vbox_new(FALSE
, 4);
2120 gtk_container_add(GTK_CONTAINER(panels
), panelvbox
);
2122 panels_switch_to(PANELS(panels
), panelvbox
);
2123 gtk_tree_select_child(GTK_TREE(tree
), treeitem
);
2126 if (nselparams
>= selparamsize
) {
2128 selparams
= sresize(selparams
, selparamsize
,
2131 selparams
[nselparams
].dp
= &dp
;
2132 selparams
[nselparams
].panels
= PANELS(panels
);
2133 selparams
[nselparams
].panel
= panelvbox
;
2134 selparams
[nselparams
].shortcuts
= scs
; /* structure copy */
2135 selparams
[nselparams
].treeitem
= treeitem
;
2140 w
= layout_ctrls(&dp
,
2141 &selparams
[nselparams
-1].shortcuts
,
2142 s
, listitemheight
, NULL
);
2143 gtk_box_pack_start(GTK_BOX(panelvbox
), w
, FALSE
, FALSE
, 0);
2148 dp
.ntreeitems
= nselparams
;
2149 dp
.treeitems
= snewn(dp
.ntreeitems
, GtkWidget
*);
2151 for (index
= 0; index
< nselparams
; index
++) {
2152 gtk_signal_connect(GTK_OBJECT(selparams
[index
].treeitem
), "select",
2153 GTK_SIGNAL_FUNC(treeitem_sel
),
2155 dp
.treeitems
[index
] = selparams
[index
].treeitem
;
2159 dlg_refresh(NULL
, &dp
);
2161 dp
.shortcuts
= &selparams
[0].shortcuts
;
2162 dp
.currtreeitem
= dp
.treeitems
[0];
2163 dp
.lastfocus
= NULL
;
2167 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
2168 gtk_widget_show(window
);
2171 * Set focus into the first available control.
2173 for (index
= 0; index
< ctrlbox
->nctrlsets
; index
++) {
2174 struct controlset
*s
= ctrlbox
->ctrlsets
[index
];
2179 for (j
= 0; j
< s
->ncontrols
; j
++)
2180 if (s
->ctrls
[j
]->generic
.type
!= CTRL_TABDELAY
&&
2181 s
->ctrls
[j
]->generic
.type
!= CTRL_COLUMNS
&&
2182 s
->ctrls
[j
]->generic
.type
!= CTRL_TEXT
) {
2183 dlg_set_focus(s
->ctrls
[j
], &dp
);
2184 dp
.lastfocus
= s
->ctrls
[j
];
2193 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
2194 GTK_SIGNAL_FUNC(window_destroy
), NULL
);
2195 gtk_signal_connect(GTK_OBJECT(window
), "key_press_event",
2196 GTK_SIGNAL_FUNC(win_key_press
), &dp
);
2206 static void messagebox_handler(union control
*ctrl
, void *dlg
,
2207 void *data
, int event
)
2209 if (event
== EVENT_ACTION
)
2210 dlg_end(dlg
, ctrl
->generic
.context
.i
);
2212 int messagebox(GtkWidget
*parentwin
, char *title
, char *msg
, int minwid
, ...)
2214 GtkWidget
*window
, *w0
, *w1
;
2215 struct controlbox
*ctrlbox
;
2216 struct controlset
*s0
, *s1
;
2219 struct Shortcuts scs
;
2225 for (index
= 0; index
< lenof(scs
.sc
); index
++) {
2226 scs
.sc
[index
].action
= SHORTCUT_EMPTY
;
2229 ctrlbox
= ctrl_new_box();
2232 va_start(ap
, minwid
);
2233 while (va_arg(ap
, char *) != NULL
) {
2235 (void) va_arg(ap
, int); /* shortcut */
2236 (void) va_arg(ap
, int); /* normal/default/cancel */
2237 (void) va_arg(ap
, int); /* end value */
2241 s0
= ctrl_getset(ctrlbox
, "", "", "");
2242 c
= ctrl_columns(s0
, 2, 50, 50);
2243 c
->columns
.ncols
= s0
->ncolumns
= ncols
;
2244 c
->columns
.percentages
= sresize(c
->columns
.percentages
, ncols
, int);
2245 for (index
= 0; index
< ncols
; index
++)
2246 c
->columns
.percentages
[index
] = (index
+1)*100/ncols
- index
*100/ncols
;
2247 va_start(ap
, minwid
);
2250 char *title
= va_arg(ap
, char *);
2251 int shortcut
, type
, value
;
2254 shortcut
= va_arg(ap
, int);
2255 type
= va_arg(ap
, int);
2256 value
= va_arg(ap
, int);
2257 c
= ctrl_pushbutton(s0
, title
, shortcut
, HELPCTX(no_help
),
2258 messagebox_handler
, I(value
));
2259 c
->generic
.column
= index
++;
2261 c
->button
.isdefault
= TRUE
;
2263 c
->button
.iscancel
= TRUE
;
2267 s1
= ctrl_getset(ctrlbox
, "x", "", "");
2268 ctrl_text(s1
, msg
, HELPCTX(no_help
));
2270 window
= gtk_dialog_new();
2271 gtk_window_set_title(GTK_WINDOW(window
), title
);
2272 w0
= layout_ctrls(&dp
, &scs
, s0
, 0, GTK_WINDOW(window
));
2273 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->action_area
),
2275 gtk_widget_show(w0
);
2276 w1
= layout_ctrls(&dp
, &scs
, s1
, 0, GTK_WINDOW(window
));
2277 gtk_container_set_border_width(GTK_CONTAINER(w1
), 10);
2278 gtk_widget_set_usize(w1
, minwid
+20, -1);
2279 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->vbox
),
2281 gtk_widget_show(w1
);
2283 dp
.shortcuts
= &scs
;
2284 dp
.lastfocus
= NULL
;
2288 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
2290 set_transient_window_pos(parentwin
, window
);
2291 gtk_window_set_transient_for(GTK_WINDOW(window
),
2292 GTK_WINDOW(parentwin
));
2294 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
2295 gtk_widget_show(window
);
2297 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
2298 GTK_SIGNAL_FUNC(window_destroy
), NULL
);
2299 gtk_signal_connect(GTK_OBJECT(window
), "key_press_event",
2300 GTK_SIGNAL_FUNC(win_key_press
), &dp
);
2305 ctrl_free_box(ctrlbox
);
2310 static int string_width(char *text
)
2312 GtkWidget
*label
= gtk_label_new(text
);
2314 gtk_widget_size_request(label
, &req
);
2315 gtk_widget_unref(label
);
2319 int reallyclose(void *frontend
)
2321 char *title
= dupcat(appname
, " Exit Confirmation", NULL
);
2322 int ret
= messagebox(GTK_WIDGET(get_window(frontend
)),
2323 title
, "Are you sure you want to close this session?",
2324 string_width("Most of the width of the above text"),
2332 int verify_ssh_host_key(void *frontend
, char *host
, int port
, char *keytype
,
2333 char *keystr
, char *fingerprint
,
2334 void (*callback
)(void *ctx
, int result
), void *ctx
)
2336 static const char absenttxt
[] =
2337 "The server's host key is not cached. You have no guarantee "
2338 "that the server is the computer you think it is.\n"
2339 "The server's %s key fingerprint is:\n"
2341 "If you trust this host, press \"Accept\" to add the key to "
2342 "PuTTY's cache and carry on connecting.\n"
2343 "If you want to carry on connecting just once, without "
2344 "adding the key to the cache, press \"Connect Once\".\n"
2345 "If you do not trust this host, press \"Cancel\" to abandon the "
2347 static const char wrongtxt
[] =
2348 "WARNING - POTENTIAL SECURITY BREACH!\n"
2349 "The server's host key does not match the one PuTTY has "
2350 "cached. This means that either the server administrator "
2351 "has changed the host key, or you have actually connected "
2352 "to another computer pretending to be the server.\n"
2353 "The new %s key fingerprint is:\n"
2355 "If you were expecting this change and trust the new key, "
2356 "press \"Accept\" to update PuTTY's cache and continue connecting.\n"
2357 "If you want to carry on connecting but without updating "
2358 "the cache, press \"Connect Once\".\n"
2359 "If you want to abandon the connection completely, press "
2360 "\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed "
2368 ret
= verify_host_key(host
, port
, keytype
, keystr
);
2370 if (ret
== 0) /* success - key matched OK */
2373 text
= dupprintf((ret
== 2 ? wrongtxt
: absenttxt
), keytype
, fingerprint
);
2375 ret
= messagebox(GTK_WIDGET(get_window(frontend
)),
2376 "PuTTY Security Alert", text
,
2377 string_width(fingerprint
),
2378 "Accept", 'a', 0, 2,
2379 "Connect Once", 'o', 0, 1,
2380 "Cancel", 'c', -1, 0,
2386 store_host_key(host
, port
, keytype
, keystr
);
2387 return 1; /* continue with connection */
2388 } else if (ret
== 1)
2389 return 1; /* continue with connection */
2390 return 0; /* do not continue with connection */
2394 * Ask whether the selected algorithm is acceptable (since it was
2395 * below the configured 'warn' threshold).
2397 int askalg(void *frontend
, const char *algtype
, const char *algname
,
2398 void (*callback
)(void *ctx
, int result
), void *ctx
)
2400 static const char msg
[] =
2401 "The first %s supported by the server is "
2402 "%s, which is below the configured warning threshold.\n"
2403 "Continue with connection?";
2407 text
= dupprintf(msg
, algtype
, algname
);
2408 ret
= messagebox(GTK_WIDGET(get_window(frontend
)),
2409 "PuTTY Security Alert", text
,
2410 string_width("Continue with connection?"),
2423 void old_keyfile_warning(void)
2426 * This should never happen on Unix. We hope.
2430 void fatal_message_box(void *window
, char *msg
)
2432 messagebox(window
, "PuTTY Fatal Error", msg
,
2433 string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"),
2434 "OK", 'o', 1, 1, NULL
);
2437 void fatalbox(char *p
, ...)
2442 msg
= dupvprintf(p
, ap
);
2444 fatal_message_box(NULL
, msg
);
2449 static GtkWidget
*aboutbox
= NULL
;
2451 static void about_close_clicked(GtkButton
*button
, gpointer data
)
2453 gtk_widget_destroy(aboutbox
);
2457 static void licence_clicked(GtkButton
*button
, gpointer data
)
2462 "Copyright 1997-2006 Simon Tatham.\n\n"
2464 "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
2465 "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "
2466 "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, "
2467 "Markus Kuhn, and CORE SDI S.A.\n\n"
2469 "Permission is hereby granted, free of charge, to any person "
2470 "obtaining a copy of this software and associated documentation "
2471 "files (the ""Software""), to deal in the Software without restriction, "
2472 "including without limitation the rights to use, copy, modify, merge, "
2473 "publish, distribute, sublicense, and/or sell copies of the Software, "
2474 "and to permit persons to whom the Software is furnished to do so, "
2475 "subject to the following conditions:\n\n"
2477 "The above copyright notice and this permission notice shall be "
2478 "included in all copies or substantial portions of the Software.\n\n"
2480 "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT "
2481 "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, "
2482 "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "
2483 "MERCHANTABILITY, FITNESS FOR A PARTICULAR "
2484 "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE "
2485 "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES "
2486 "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, "
2487 "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN "
2488 "CONNECTION WITH THE SOFTWARE OR THE USE OR "
2489 "OTHER DEALINGS IN THE SOFTWARE.";
2491 title
= dupcat(appname
, " Licence", NULL
);
2492 assert(aboutbox
!= NULL
);
2493 messagebox(aboutbox
, title
, licence
,
2494 string_width("LONGISH LINE OF TEXT SO THE LICENCE"
2495 " BOX ISN'T EXCESSIVELY TALL AND THIN"),
2496 "OK", 'o', 1, 1, NULL
);
2500 void about_box(void *window
)
2506 gtk_widget_grab_focus(aboutbox
);
2510 aboutbox
= gtk_dialog_new();
2511 gtk_container_set_border_width(GTK_CONTAINER(aboutbox
), 10);
2512 title
= dupcat("About ", appname
, NULL
);
2513 gtk_window_set_title(GTK_WINDOW(aboutbox
), title
);
2516 w
= gtk_button_new_with_label("Close");
2517 GTK_WIDGET_SET_FLAGS(w
, GTK_CAN_DEFAULT
);
2518 gtk_window_set_default(GTK_WINDOW(aboutbox
), w
);
2519 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(aboutbox
)->action_area
),
2520 w
, FALSE
, FALSE
, 0);
2521 gtk_signal_connect(GTK_OBJECT(w
), "clicked",
2522 GTK_SIGNAL_FUNC(about_close_clicked
), NULL
);
2525 w
= gtk_button_new_with_label("View Licence");
2526 GTK_WIDGET_SET_FLAGS(w
, GTK_CAN_DEFAULT
);
2527 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(aboutbox
)->action_area
),
2528 w
, FALSE
, FALSE
, 0);
2529 gtk_signal_connect(GTK_OBJECT(w
), "clicked",
2530 GTK_SIGNAL_FUNC(licence_clicked
), NULL
);
2533 w
= gtk_label_new(appname
);
2534 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox
)->vbox
),
2535 w
, FALSE
, FALSE
, 0);
2538 w
= gtk_label_new(ver
);
2539 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox
)->vbox
),
2540 w
, FALSE
, FALSE
, 5);
2543 w
= gtk_label_new("Copyright 1997-2006 Simon Tatham. All rights reserved");
2544 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox
)->vbox
),
2545 w
, FALSE
, FALSE
, 5);
2548 set_transient_window_pos(GTK_WIDGET(window
), aboutbox
);
2549 gtk_widget_show(aboutbox
);
2552 struct eventlog_stuff
{
2553 GtkWidget
*parentwin
, *window
;
2554 struct controlbox
*eventbox
;
2555 struct Shortcuts scs
;
2557 union control
*listctrl
;
2559 int nevents
, negsize
;
2562 int ignore_selchange
;
2565 static void eventlog_destroy(GtkWidget
*widget
, gpointer data
)
2567 struct eventlog_stuff
*es
= (struct eventlog_stuff
*)data
;
2571 dlg_cleanup(&es
->dp
);
2572 ctrl_free_box(es
->eventbox
);
2574 static void eventlog_ok_handler(union control
*ctrl
, void *dlg
,
2575 void *data
, int event
)
2577 if (event
== EVENT_ACTION
)
2580 static void eventlog_list_handler(union control
*ctrl
, void *dlg
,
2581 void *data
, int event
)
2583 struct eventlog_stuff
*es
= (struct eventlog_stuff
*)data
;
2585 if (event
== EVENT_REFRESH
) {
2588 dlg_update_start(ctrl
, dlg
);
2589 dlg_listbox_clear(ctrl
, dlg
);
2590 for (i
= 0; i
< es
->nevents
; i
++) {
2591 dlg_listbox_add(ctrl
, dlg
, es
->events
[i
]);
2593 dlg_update_done(ctrl
, dlg
);
2594 } else if (event
== EVENT_SELCHANGE
) {
2599 * If this SELCHANGE event is happening as a result of
2600 * deliberate deselection because someone else has grabbed
2601 * the selection, the last thing we want to do is pre-empt
2604 if (es
->ignore_selchange
)
2608 * Construct the data to use as the selection.
2613 for (i
= 0; i
< es
->nevents
; i
++) {
2614 if (dlg_listbox_issel(ctrl
, dlg
, i
)) {
2615 int extralen
= strlen(es
->events
[i
]);
2617 if (es
->sellen
+ extralen
+ 2 > selsize
) {
2618 selsize
= es
->sellen
+ extralen
+ 512;
2619 es
->seldata
= sresize(es
->seldata
, selsize
, char);
2622 strcpy(es
->seldata
+ es
->sellen
, es
->events
[i
]);
2623 es
->sellen
+= extralen
;
2624 es
->seldata
[es
->sellen
++] = '\n';
2628 if (gtk_selection_owner_set(es
->window
, GDK_SELECTION_PRIMARY
,
2629 GDK_CURRENT_TIME
)) {
2630 extern GdkAtom compound_text_atom
;
2632 gtk_selection_add_target(es
->window
, GDK_SELECTION_PRIMARY
,
2633 GDK_SELECTION_TYPE_STRING
, 1);
2634 gtk_selection_add_target(es
->window
, GDK_SELECTION_PRIMARY
,
2635 compound_text_atom
, 1);
2641 void eventlog_selection_get(GtkWidget
*widget
, GtkSelectionData
*seldata
,
2642 guint info
, guint time_stamp
, gpointer data
)
2644 struct eventlog_stuff
*es
= (struct eventlog_stuff
*)data
;
2646 gtk_selection_data_set(seldata
, seldata
->target
, 8,
2647 es
->seldata
, es
->sellen
);
2650 gint
eventlog_selection_clear(GtkWidget
*widget
, GdkEventSelection
*seldata
,
2653 struct eventlog_stuff
*es
= (struct eventlog_stuff
*)data
;
2657 * Deselect everything in the list box.
2659 uc
= dlg_find_byctrl(&es
->dp
, es
->listctrl
);
2660 es
->ignore_selchange
= 1;
2661 gtk_list_unselect_all(GTK_LIST(uc
->list
));
2662 es
->ignore_selchange
= 0;
2670 void showeventlog(void *estuff
, void *parentwin
)
2672 struct eventlog_stuff
*es
= (struct eventlog_stuff
*)estuff
;
2673 GtkWidget
*window
, *w0
, *w1
;
2674 GtkWidget
*parent
= GTK_WIDGET(parentwin
);
2675 struct controlset
*s0
, *s1
;
2677 int listitemheight
, index
;
2681 gtk_widget_grab_focus(es
->window
);
2687 for (index
= 0; index
< lenof(es
->scs
.sc
); index
++) {
2688 es
->scs
.sc
[index
].action
= SHORTCUT_EMPTY
;
2691 es
->eventbox
= ctrl_new_box();
2693 s0
= ctrl_getset(es
->eventbox
, "", "", "");
2694 ctrl_columns(s0
, 3, 33, 34, 33);
2695 c
= ctrl_pushbutton(s0
, "Close", 'c', HELPCTX(no_help
),
2696 eventlog_ok_handler
, P(NULL
));
2697 c
->button
.column
= 1;
2698 c
->button
.isdefault
= TRUE
;
2700 s1
= ctrl_getset(es
->eventbox
, "x", "", "");
2701 es
->listctrl
= c
= ctrl_listbox(s1
, NULL
, NO_SHORTCUT
, HELPCTX(no_help
),
2702 eventlog_list_handler
, P(es
));
2703 c
->listbox
.height
= 10;
2704 c
->listbox
.multisel
= 2;
2705 c
->listbox
.ncols
= 3;
2706 c
->listbox
.percentages
= snewn(3, int);
2707 c
->listbox
.percentages
[0] = 25;
2708 c
->listbox
.percentages
[1] = 10;
2709 c
->listbox
.percentages
[2] = 65;
2711 listitemheight
= get_listitemheight();
2713 es
->window
= window
= gtk_dialog_new();
2714 title
= dupcat(appname
, " Event Log", NULL
);
2715 gtk_window_set_title(GTK_WINDOW(window
), title
);
2717 w0
= layout_ctrls(&es
->dp
, &es
->scs
, s0
,
2718 listitemheight
, GTK_WINDOW(window
));
2719 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->action_area
),
2721 gtk_widget_show(w0
);
2722 w1
= layout_ctrls(&es
->dp
, &es
->scs
, s1
,
2723 listitemheight
, GTK_WINDOW(window
));
2724 gtk_container_set_border_width(GTK_CONTAINER(w1
), 10);
2725 gtk_widget_set_usize(w1
, 20 +
2726 string_width("LINE OF TEXT GIVING WIDTH OF EVENT LOG"
2727 " IS QUITE LONG 'COS SSH LOG ENTRIES"
2729 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->vbox
),
2731 gtk_widget_show(w1
);
2734 es
->dp
.shortcuts
= &es
->scs
;
2735 es
->dp
.lastfocus
= NULL
;
2737 es
->dp
.window
= window
;
2739 dlg_refresh(NULL
, &es
->dp
);
2742 set_transient_window_pos(parent
, window
);
2743 gtk_window_set_transient_for(GTK_WINDOW(window
),
2744 GTK_WINDOW(parent
));
2746 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
2747 gtk_widget_show(window
);
2749 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
2750 GTK_SIGNAL_FUNC(eventlog_destroy
), es
);
2751 gtk_signal_connect(GTK_OBJECT(window
), "key_press_event",
2752 GTK_SIGNAL_FUNC(win_key_press
), &es
->dp
);
2753 gtk_signal_connect(GTK_OBJECT(window
), "selection_get",
2754 GTK_SIGNAL_FUNC(eventlog_selection_get
), es
);
2755 gtk_signal_connect(GTK_OBJECT(window
), "selection_clear_event",
2756 GTK_SIGNAL_FUNC(eventlog_selection_clear
), es
);
2759 void *eventlogstuff_new(void)
2761 struct eventlog_stuff
*es
;
2762 es
= snew(struct eventlog_stuff
);
2763 memset(es
, 0, sizeof(*es
));
2767 void logevent_dlg(void *estuff
, const char *string
)
2769 struct eventlog_stuff
*es
= (struct eventlog_stuff
*)estuff
;
2774 if (es
->nevents
>= es
->negsize
) {
2776 es
->events
= sresize(es
->events
, es
->negsize
, char *);
2780 strftime(timebuf
, sizeof(timebuf
), "%Y-%m-%d %H:%M:%S\t", &tm
);
2782 es
->events
[es
->nevents
] = snewn(strlen(timebuf
) + strlen(string
) + 1, char);
2783 strcpy(es
->events
[es
->nevents
], timebuf
);
2784 strcat(es
->events
[es
->nevents
], string
);
2786 dlg_listbox_add(es
->listctrl
, &es
->dp
, es
->events
[es
->nevents
]);
2791 int askappend(void *frontend
, Filename filename
,
2792 void (*callback
)(void *ctx
, int result
), void *ctx
)
2794 static const char msgtemplate
[] =
2795 "The session log file \"%.*s\" already exists. "
2796 "You can overwrite it with a new session log, "
2797 "append your session log to the end of it, "
2798 "or disable session logging for this session.";
2803 message
= dupprintf(msgtemplate
, FILENAME_MAX
, filename
.path
);
2804 mbtitle
= dupprintf("%s Log to File", appname
);
2806 mbret
= messagebox(get_window(frontend
), mbtitle
, message
,
2807 string_width("LINE OF TEXT SUITABLE FOR THE"
2808 " ASKAPPEND WIDTH"),
2809 "Overwrite", 'o', 1, 2,
2810 "Append", 'a', 0, 1,
2811 "Disable", 'd', -1, 0,