2 * gtkdlg.c - GTK implementation of the PuTTY configuration box.
8 * - keyboard shortcuts on list boxes and the treeview
9 * + find the currently selected item and focus it?
10 * + what if there are many selected items, or none?
11 * + none of this is any use unless Up and Down are made to do
12 * something plausibly sane anyway
15 * + `last focused' for nasty sessionsaver hack
16 * + set focus into a sensible control to start with
17 * + perhaps we need uc->primary as the right widget to
19 * * no, because I think the right widget to focus with
20 * radio buttons is the currently selected one. Hmm.
24 * - must return a value from the dialog box!
25 * + easy, just put it in dp.
27 * - font selection hiccup: the default `fixed' is not
28 * automatically translated into its expanded XLFD form when the
29 * font selector is started. It should be.
32 * + can't we _somehow_ have less leading between radio buttons?
33 * + wrapping text widgets, the horror, the horror
34 * + labels and their associated edit boxes don't line up
36 * + IWBNI arrows in list boxes didn't do such a bloody silly
37 * thing (moving focus without moving selection, and
38 * furthermore not feeding back to the scrollbar). And arrows
39 * in the treeview doubly so. Gah.
40 * + don't suppose we can fix the vertical offset labels get
41 * from their underlines?
42 * + why the hell are the Up/Down focus movement keys sorting
43 * things by _width_? (See the Logging and Features panels
48 * TODO when porting to GTK 2.0:
50 * - GtkTree is apparently deprecated and we should switch to
51 * GtkTreeView instead.
52 * - GtkLabel has a built-in mnemonic scheme, so we should at
53 * least consider switching to that from the current adhockery.
59 #include <gdk/gdkkeysyms.h>
65 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
79 struct Shortcut sc
[128];
86 int privdata_needs_free
;
87 GtkWidget
**buttons
; int nbuttons
; /* for radio buttons */
88 GtkWidget
*entry
; /* for editbox, combobox, filesel, fontsel */
89 GtkWidget
*button
; /* for filesel, fontsel */
90 GtkWidget
*list
; /* for combobox, listbox */
91 GtkWidget
*menu
; /* for optionmenu (==droplist) */
92 GtkWidget
*optmenu
; /* also for optionmenu */
93 GtkWidget
*text
; /* for text */
97 tree234
*byctrl
, *bywidget
;
99 struct { unsigned char r
, g
, b
, ok
; } coloursel_result
; /* 0-255 */
100 /* `flags' are set to indicate when a GTK signal handler is being called
101 * due to automatic processing and should not flag a user event. */
103 struct Shortcuts
*shortcuts
;
105 #define FLAG_UPDATING_COMBO_LIST 1
107 enum { /* values for Shortcut.action */
108 SHORTCUT_EMPTY
, /* no shortcut on this key */
109 SHORTCUT_FOCUS
, /* focus the supplied widget */
110 SHORTCUT_UCTRL
, /* do something sane with uctrl */
111 SHORTCUT_UCTRL_UP
, /* uctrl is a draglist, move Up */
112 SHORTCUT_UCTRL_DOWN
, /* uctrl is a draglist, move Down */
116 * Forward references.
118 static void shortcut_add(struct Shortcuts
*scs
, GtkWidget
*labelw
,
119 int chr
, int action
, void *ptr
);
120 static void listitem_button(GtkWidget
*item
, GdkEventButton
*event
,
122 static void menuitem_activate(GtkMenuItem
*item
, gpointer data
);
123 static void coloursel_ok(GtkButton
*button
, gpointer data
);
124 static void coloursel_cancel(GtkButton
*button
, gpointer data
);
126 static int uctrl_cmp_byctrl(void *av
, void *bv
)
128 struct uctrl
*a
= (struct uctrl
*)av
;
129 struct uctrl
*b
= (struct uctrl
*)bv
;
130 if (a
->ctrl
< b
->ctrl
)
132 else if (a
->ctrl
> b
->ctrl
)
137 static int uctrl_cmp_byctrl_find(void *av
, void *bv
)
139 union control
*a
= (union control
*)av
;
140 struct uctrl
*b
= (struct uctrl
*)bv
;
143 else if (a
> b
->ctrl
)
148 static int uctrl_cmp_bywidget(void *av
, void *bv
)
150 struct uctrl
*a
= (struct uctrl
*)av
;
151 struct uctrl
*b
= (struct uctrl
*)bv
;
152 if (a
->toplevel
< b
->toplevel
)
154 else if (a
->toplevel
> b
->toplevel
)
159 static int uctrl_cmp_bywidget_find(void *av
, void *bv
)
161 GtkWidget
*a
= (GtkWidget
*)av
;
162 struct uctrl
*b
= (struct uctrl
*)bv
;
165 else if (a
> b
->toplevel
)
170 static void dlg_init(struct dlgparam
*dp
)
172 dp
->byctrl
= newtree234(uctrl_cmp_byctrl
);
173 dp
->bywidget
= newtree234(uctrl_cmp_bywidget
);
174 dp
->coloursel_result
.ok
= FALSE
;
177 static void dlg_cleanup(struct dlgparam
*dp
)
181 freetree234(dp
->byctrl
); /* doesn't free the uctrls inside */
182 while ( (uc
= index234(dp
->bywidget
, 0)) != NULL
) {
183 del234(dp
->bywidget
, uc
);
184 if (uc
->privdata_needs_free
)
189 freetree234(dp
->bywidget
);
192 static void dlg_add_uctrl(struct dlgparam
*dp
, struct uctrl
*uc
)
194 add234(dp
->byctrl
, uc
);
195 add234(dp
->bywidget
, uc
);
198 static struct uctrl
*dlg_find_byctrl(struct dlgparam
*dp
, union control
*ctrl
)
200 return find234(dp
->byctrl
, ctrl
, uctrl_cmp_byctrl_find
);
203 static struct uctrl
*dlg_find_bywidget(struct dlgparam
*dp
, GtkWidget
*w
)
205 struct uctrl
*ret
= NULL
;
207 ret
= find234(dp
->bywidget
, w
, uctrl_cmp_bywidget_find
);
215 void *dlg_get_privdata(union control
*ctrl
, void *dlg
)
217 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
218 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
222 void dlg_set_privdata(union control
*ctrl
, void *dlg
, void *ptr
)
224 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
225 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
227 uc
->privdata_needs_free
= FALSE
;
230 void *dlg_alloc_privdata(union control
*ctrl
, void *dlg
, size_t size
)
232 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
233 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
234 uc
->privdata
= smalloc(size
);
235 uc
->privdata_needs_free
= FALSE
;
239 union control
*dlg_last_focused(void *dlg
)
241 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
242 return NULL
; /* FIXME */
245 void dlg_radiobutton_set(union control
*ctrl
, void *dlg
, int which
)
247 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
248 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
249 assert(uc
->ctrl
->generic
.type
== CTRL_RADIO
);
250 assert(uc
->buttons
!= NULL
);
251 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc
->buttons
[which
]), TRUE
);
254 int dlg_radiobutton_get(union control
*ctrl
, void *dlg
)
256 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
257 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
260 assert(uc
->ctrl
->generic
.type
== CTRL_RADIO
);
261 assert(uc
->buttons
!= NULL
);
262 for (i
= 0; i
< uc
->nbuttons
; i
++)
263 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc
->buttons
[i
])))
265 return 0; /* got to return something */
268 void dlg_checkbox_set(union control
*ctrl
, void *dlg
, int checked
)
270 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
271 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
272 assert(uc
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
273 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc
->toplevel
), checked
);
276 int dlg_checkbox_get(union control
*ctrl
, void *dlg
)
278 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
279 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
280 assert(uc
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
281 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc
->toplevel
));
284 void dlg_editbox_set(union control
*ctrl
, void *dlg
, char const *text
)
286 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
287 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
288 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
);
289 assert(uc
->entry
!= NULL
);
290 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), text
);
293 void dlg_editbox_get(union control
*ctrl
, void *dlg
, char *buffer
, int length
)
295 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
296 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
297 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
);
298 assert(uc
->entry
!= NULL
);
299 strncpy(buffer
, gtk_entry_get_text(GTK_ENTRY(uc
->entry
)),
301 buffer
[length
-1] = '\0';
304 static void container_remove_and_destroy(GtkWidget
*w
, gpointer data
)
306 GtkContainer
*cont
= GTK_CONTAINER(data
);
307 /* gtk_container_remove will unref the widget for us; we need not. */
308 gtk_container_remove(cont
, w
);
311 /* The `listbox' functions can also apply to combo boxes. */
312 void dlg_listbox_clear(union control
*ctrl
, void *dlg
)
314 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
315 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
318 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
319 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
320 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
322 cont
= (uc
->menu ?
GTK_CONTAINER(uc
->menu
) : GTK_CONTAINER(uc
->list
));
324 gtk_container_foreach(cont
, container_remove_and_destroy
, cont
);
327 void dlg_listbox_del(union control
*ctrl
, void *dlg
, int index
)
329 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
330 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
332 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
333 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
334 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
338 (GTK_CONTAINER(uc
->menu
),
339 g_list_nth_data(GTK_MENU_SHELL(uc
->menu
)->children
, index
));
341 gtk_list_clear_items(GTK_LIST(uc
->list
), index
, index
+1);
345 void dlg_listbox_add(union control
*ctrl
, void *dlg
, char const *text
)
347 dlg_listbox_addwithindex(ctrl
, dlg
, text
, 0);
351 * Each listbox entry may have a numeric id associated with it.
352 * Note that some front ends only permit a string to be stored at
353 * each position, which means that _if_ you put two identical
354 * strings in any listbox then you MUST not assign them different
355 * IDs and expect to get meaningful results back.
357 void dlg_listbox_addwithindex(union control
*ctrl
, void *dlg
,
358 char const *text
, int id
)
360 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
361 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
363 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
364 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
365 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
367 dp
->flags
|= FLAG_UPDATING_COMBO_LIST
;
371 * List item in a drop-down (but non-combo) list. Tabs are
372 * ignored; we just provide a standard menu item with the
375 GtkWidget
*menuitem
= gtk_menu_item_new_with_label(text
);
377 gtk_container_add(GTK_CONTAINER(uc
->menu
), menuitem
);
378 gtk_widget_show(menuitem
);
380 gtk_object_set_data(GTK_OBJECT(menuitem
), "user-data", (gpointer
)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
= smalloc(ncols
* sizeof(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 gtk_signal_connect(GTK_OBJECT(listitem
), "button_press_event",
432 GTK_SIGNAL_FUNC(listitem_button
), dp
);
433 gtk_object_set_data(GTK_OBJECT(listitem
), "user-data", (gpointer
)id
);
436 * List item in a combo-box list, which means the sensible
437 * thing to do is make it a perfectly normal label. Hence
438 * tabs are disregarded.
440 GtkWidget
*listitem
= gtk_list_item_new_with_label(text
);
442 gtk_container_add(GTK_CONTAINER(uc
->list
), listitem
);
443 gtk_widget_show(listitem
);
445 gtk_object_set_data(GTK_OBJECT(listitem
), "user-data", (gpointer
)id
);
448 dp
->flags
&= ~FLAG_UPDATING_COMBO_LIST
;
451 int dlg_listbox_getid(union control
*ctrl
, void *dlg
, int index
)
453 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
454 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
458 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
459 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
460 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
462 children
= gtk_container_children(GTK_CONTAINER(uc
->menu ? uc
->menu
:
464 item
= GTK_OBJECT(g_list_nth_data(children
, index
));
466 return (int)gtk_object_get_data(GTK_OBJECT(item
), "user-data");
469 /* dlg_listbox_index returns <0 if no single element is selected. */
470 int dlg_listbox_index(union control
*ctrl
, void *dlg
)
472 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
473 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
475 GtkWidget
*item
, *activeitem
;
479 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
480 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
481 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
484 activeitem
= gtk_menu_get_active(GTK_MENU(uc
->menu
));
486 children
= gtk_container_children(GTK_CONTAINER(uc
->menu ? uc
->menu
:
488 for (i
= 0; children
!=NULL
&& (item
= GTK_WIDGET(children
->data
))!=NULL
;
489 i
++, children
= children
->next
) {
490 if (uc
->menu ? activeitem
== item
:
491 GTK_WIDGET_STATE(item
) == GTK_STATE_SELECTED
) {
501 int dlg_listbox_issel(union control
*ctrl
, void *dlg
, int index
)
503 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
504 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
506 GtkWidget
*item
, *activeitem
;
508 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
509 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
510 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
512 children
= gtk_container_children(GTK_CONTAINER(uc
->menu ? uc
->menu
:
514 item
= GTK_WIDGET(g_list_nth_data(children
, index
));
517 activeitem
= gtk_menu_get_active(GTK_MENU(uc
->menu
));
518 return item
== activeitem
;
520 return GTK_WIDGET_STATE(item
) == GTK_STATE_SELECTED
;
524 void dlg_listbox_select(union control
*ctrl
, void *dlg
, int index
)
526 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
527 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
529 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
530 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
531 assert(uc
->optmenu
!= NULL
|| uc
->list
!= NULL
);
534 gtk_option_menu_set_history(GTK_OPTION_MENU(uc
->optmenu
), index
);
536 gtk_list_select_item(GTK_LIST(uc
->list
), index
);
540 void dlg_text_set(union control
*ctrl
, void *dlg
, char const *text
)
542 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
543 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
545 assert(uc
->ctrl
->generic
.type
== CTRL_TEXT
);
546 assert(uc
->text
!= NULL
);
548 gtk_label_set_text(GTK_LABEL(uc
->text
), text
);
551 void dlg_filesel_set(union control
*ctrl
, void *dlg
, Filename fn
)
553 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
554 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
555 assert(uc
->ctrl
->generic
.type
== CTRL_FILESELECT
);
556 assert(uc
->entry
!= NULL
);
557 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), fn
.path
);
560 void dlg_filesel_get(union control
*ctrl
, void *dlg
, Filename
*fn
)
562 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
563 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
564 assert(uc
->ctrl
->generic
.type
== CTRL_FILESELECT
);
565 assert(uc
->entry
!= NULL
);
566 strncpy(fn
->path
, gtk_entry_get_text(GTK_ENTRY(uc
->entry
)),
568 fn
->path
[lenof(fn
->path
)-1] = '\0';
571 void dlg_fontsel_set(union control
*ctrl
, void *dlg
, FontSpec fs
)
573 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
574 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
575 assert(uc
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
576 assert(uc
->entry
!= NULL
);
577 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), fs
.name
);
580 void dlg_fontsel_get(union control
*ctrl
, void *dlg
, FontSpec
*fs
)
582 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
583 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
584 assert(uc
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
585 assert(uc
->entry
!= NULL
);
586 strncpy(fs
->name
, gtk_entry_get_text(GTK_ENTRY(uc
->entry
)),
588 fs
->name
[lenof(fs
->name
)-1] = '\0';
592 * Bracketing a large set of updates in these two functions will
593 * cause the front end (if possible) to delay updating the screen
594 * until it's all complete, thus avoiding flicker.
596 void dlg_update_start(union control
*ctrl
, void *dlg
)
599 * Apparently we can't do this at all in GTK. GtkCList supports
600 * freeze and thaw, but not GtkList. Bah.
604 void dlg_update_done(union control
*ctrl
, void *dlg
)
607 * Apparently we can't do this at all in GTK. GtkCList supports
608 * freeze and thaw, but not GtkList. Bah.
612 void dlg_set_focus(union control
*ctrl
, void *dlg
)
614 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
619 * During event processing, you might well want to give an error
620 * indication to the user. dlg_beep() is a quick and easy generic
621 * error; dlg_error() puts up a message-box or equivalent.
623 void dlg_beep(void *dlg
)
628 void dlg_error_msg(void *dlg
, char *msg
)
630 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
635 * This function signals to the front end that the dialog's
636 * processing is completed, and passes an integer value (typically
639 void dlg_end(void *dlg
, int value
)
641 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
643 /* FIXME: don't forget to faff about with returning a value */
646 void dlg_refresh(union control
*ctrl
, void *dlg
)
648 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
652 if (ctrl
->generic
.handler
!= NULL
)
653 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
657 for (i
= 0; (uc
= index234(dp
->byctrl
, i
)) != NULL
; i
++) {
658 assert(uc
->ctrl
!= NULL
);
659 if (uc
->ctrl
->generic
.handler
!= NULL
)
660 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
,
661 dp
->data
, EVENT_REFRESH
);
666 void dlg_coloursel_start(union control
*ctrl
, void *dlg
, int r
, int g
, int b
)
668 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
669 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
672 GtkWidget
*coloursel
=
673 gtk_color_selection_dialog_new("Select a colour");
674 GtkColorSelectionDialog
*ccs
= GTK_COLOR_SELECTION_DIALOG(coloursel
);
676 dp
->coloursel_result
.ok
= FALSE
;
678 gtk_window_set_modal(GTK_WINDOW(coloursel
), TRUE
);
679 gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(ccs
->colorsel
), FALSE
);
680 cvals
[0] = r
/ 255.0;
681 cvals
[1] = g
/ 255.0;
682 cvals
[2] = b
/ 255.0;
683 cvals
[3] = 1.0; /* fully opaque! */
684 gtk_color_selection_set_color(GTK_COLOR_SELECTION(ccs
->colorsel
), cvals
);
686 gtk_object_set_data(GTK_OBJECT(ccs
->ok_button
), "user-data",
687 (gpointer
)coloursel
);
688 gtk_object_set_data(GTK_OBJECT(ccs
->cancel_button
), "user-data",
689 (gpointer
)coloursel
);
690 gtk_object_set_data(GTK_OBJECT(coloursel
), "user-data", (gpointer
)uc
);
691 gtk_signal_connect(GTK_OBJECT(ccs
->ok_button
), "clicked",
692 GTK_SIGNAL_FUNC(coloursel_ok
), (gpointer
)dp
);
693 gtk_signal_connect(GTK_OBJECT(ccs
->cancel_button
), "clicked",
694 GTK_SIGNAL_FUNC(coloursel_cancel
), (gpointer
)dp
);
695 gtk_signal_connect_object(GTK_OBJECT(ccs
->ok_button
), "clicked",
696 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
697 (gpointer
)coloursel
);
698 gtk_signal_connect_object(GTK_OBJECT(ccs
->cancel_button
), "clicked",
699 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
700 (gpointer
)coloursel
);
701 gtk_widget_show(coloursel
);
704 int dlg_coloursel_results(union control
*ctrl
, void *dlg
,
705 int *r
, int *g
, int *b
)
707 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
708 if (dp
->coloursel_result
.ok
) {
709 *r
= dp
->coloursel_result
.r
;
710 *g
= dp
->coloursel_result
.g
;
711 *b
= dp
->coloursel_result
.b
;
717 /* ----------------------------------------------------------------------
718 * Signal handlers while the dialog box is active.
721 static void button_clicked(GtkButton
*button
, gpointer data
)
723 struct dlgparam
*dp
= (struct dlgparam
*)data
;
724 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
725 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_ACTION
);
728 static void button_toggled(GtkToggleButton
*tb
, gpointer data
)
730 struct dlgparam
*dp
= (struct dlgparam
*)data
;
731 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(tb
));
732 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
735 static int editbox_key(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
738 * GtkEntry has a nasty habit of eating the Return key, which
739 * is unhelpful since it doesn't actually _do_ anything with it
740 * (it calls gtk_widget_activate, but our edit boxes never need
741 * activating). So I catch Return before GtkEntry sees it, and
742 * pass it straight on to the parent widget. Effect: hitting
743 * Return in an edit box will now activate the default button
744 * in the dialog just like it will everywhere else.
746 if (event
->keyval
== GDK_Return
&& widget
->parent
!= NULL
) {
748 gtk_signal_emit_stop_by_name (GTK_OBJECT(widget
), "key_press_event");
749 gtk_signal_emit_by_name(GTK_OBJECT(widget
->parent
), "key_press_event",
756 static void editbox_changed(GtkEditable
*ed
, gpointer data
)
758 struct dlgparam
*dp
= (struct dlgparam
*)data
;
759 if (!(dp
->flags
& FLAG_UPDATING_COMBO_LIST
)) {
760 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(ed
));
761 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
765 static void editbox_lostfocus(GtkWidget
*ed
, GdkEventFocus
*event
,
768 struct dlgparam
*dp
= (struct dlgparam
*)data
;
769 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(ed
));
770 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
773 static void listitem_button(GtkWidget
*item
, GdkEventButton
*event
,
776 struct dlgparam
*dp
= (struct dlgparam
*)data
;
777 if (event
->type
== GDK_2BUTTON_PRESS
||
778 event
->type
== GDK_3BUTTON_PRESS
) {
779 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(item
));
780 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_ACTION
);
784 static void list_selchange(GtkList
*list
, gpointer data
)
786 struct dlgparam
*dp
= (struct dlgparam
*)data
;
787 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(list
));
788 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_SELCHANGE
);
791 static void menuitem_activate(GtkMenuItem
*item
, gpointer data
)
793 struct dlgparam
*dp
= (struct dlgparam
*)data
;
794 GtkWidget
*menushell
= GTK_WIDGET(item
)->parent
;
795 gpointer optmenu
= gtk_object_get_data(GTK_OBJECT(menushell
), "user-data");
796 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(optmenu
));
797 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_SELCHANGE
);
800 static void draglist_move(struct dlgparam
*dp
, struct uctrl
*uc
, int direction
)
802 int index
= dlg_listbox_index(uc
->ctrl
, dp
);
803 GList
*children
= gtk_container_children(GTK_CONTAINER(uc
->list
));
807 (index
== 0 && direction
< 0) ||
808 (index
== g_list_length(children
)-1 && direction
> 0)) {
813 child
= g_list_nth_data(children
, index
);
814 gtk_widget_ref(child
);
815 gtk_list_clear_items(GTK_LIST(uc
->list
), index
, index
+1);
817 children
= g_list_append(children
, child
);
818 gtk_list_insert_items(GTK_LIST(uc
->list
), children
, index
+ direction
);
819 gtk_list_select_item(GTK_LIST(uc
->list
), index
+ direction
);
820 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
823 static void draglist_up(GtkButton
*button
, gpointer data
)
825 struct dlgparam
*dp
= (struct dlgparam
*)data
;
826 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
827 draglist_move(dp
, uc
, -1);
830 static void draglist_down(GtkButton
*button
, gpointer data
)
832 struct dlgparam
*dp
= (struct dlgparam
*)data
;
833 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
834 draglist_move(dp
, uc
, +1);
837 static void filesel_ok(GtkButton
*button
, gpointer data
)
839 struct dlgparam
*dp
= (struct dlgparam
*)data
;
840 gpointer filesel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
841 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(filesel
), "user-data");
842 char *name
= gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel
));
843 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), name
);
846 static void fontsel_ok(GtkButton
*button
, gpointer data
)
848 struct dlgparam
*dp
= (struct dlgparam
*)data
;
849 gpointer fontsel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
850 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(fontsel
), "user-data");
851 char *name
= gtk_font_selection_dialog_get_font_name
852 (GTK_FONT_SELECTION_DIALOG(fontsel
));
853 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), name
);
856 static void coloursel_ok(GtkButton
*button
, gpointer data
)
858 struct dlgparam
*dp
= (struct dlgparam
*)data
;
859 gpointer coloursel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
860 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(coloursel
), "user-data");
862 gtk_color_selection_get_color
863 (GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(coloursel
)->colorsel
),
865 dp
->coloursel_result
.r
= (int) (255 * cvals
[0]);
866 dp
->coloursel_result
.g
= (int) (255 * cvals
[1]);
867 dp
->coloursel_result
.b
= (int) (255 * cvals
[2]);
868 dp
->coloursel_result
.ok
= TRUE
;
869 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_CALLBACK
);
872 static void coloursel_cancel(GtkButton
*button
, gpointer data
)
874 struct dlgparam
*dp
= (struct dlgparam
*)data
;
875 gpointer coloursel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
876 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(coloursel
), "user-data");
877 dp
->coloursel_result
.ok
= FALSE
;
878 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_CALLBACK
);
881 static void filefont_clicked(GtkButton
*button
, gpointer data
)
883 struct dlgparam
*dp
= (struct dlgparam
*)data
;
884 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
886 if (uc
->ctrl
->generic
.type
== CTRL_FILESELECT
) {
888 gtk_file_selection_new(uc
->ctrl
->fileselect
.title
);
889 gtk_window_set_modal(GTK_WINDOW(filesel
), TRUE
);
891 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->ok_button
), "user-data",
893 gtk_object_set_data(GTK_OBJECT(filesel
), "user-data", (gpointer
)uc
);
895 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->ok_button
), "clicked",
896 GTK_SIGNAL_FUNC(filesel_ok
), (gpointer
)dp
);
897 gtk_signal_connect_object
898 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->ok_button
), "clicked",
899 GTK_SIGNAL_FUNC(gtk_widget_destroy
), (gpointer
)filesel
);
900 gtk_signal_connect_object
901 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->cancel_button
), "clicked",
902 GTK_SIGNAL_FUNC(gtk_widget_destroy
), (gpointer
)filesel
);
903 gtk_widget_show(filesel
);
906 if (uc
->ctrl
->generic
.type
== CTRL_FONTSELECT
) {
907 gchar
*spacings
[] = { "c", "m", NULL
};
909 gtk_font_selection_dialog_new("Select a font");
910 gtk_window_set_modal(GTK_WINDOW(fontsel
), TRUE
);
911 gtk_font_selection_dialog_set_filter
912 (GTK_FONT_SELECTION_DIALOG(fontsel
),
913 GTK_FONT_FILTER_BASE
, GTK_FONT_ALL
,
914 NULL
, NULL
, NULL
, NULL
, spacings
, NULL
);
915 gtk_font_selection_dialog_set_font_name
916 (GTK_FONT_SELECTION_DIALOG(fontsel
),
917 gtk_entry_get_text(GTK_ENTRY(uc
->entry
)));
919 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->ok_button
),
920 "user-data", (gpointer
)fontsel
);
921 gtk_object_set_data(GTK_OBJECT(fontsel
), "user-data", (gpointer
)uc
);
923 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->ok_button
),
924 "clicked", GTK_SIGNAL_FUNC(fontsel_ok
), (gpointer
)dp
);
925 gtk_signal_connect_object
926 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->ok_button
),
927 "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy
),
929 gtk_signal_connect_object
930 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->cancel_button
),
931 "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy
),
933 gtk_widget_show(fontsel
);
937 /* ----------------------------------------------------------------------
938 * This function does the main layout work: it reads a controlset,
939 * it creates the relevant GTK controls, and returns a GtkWidget
940 * containing the result. (This widget might be a title of some
941 * sort, it might be a Columns containing many controls, or it
942 * might be a GtkFrame containing a Columns; whatever it is, it's
943 * definitely a GtkWidget and should probably be added to a
946 * `listitemheight' is used to calculate a usize for list boxes: it
947 * should be the height from the size request of a GtkListItem.
949 * `win' is required for setting the default button. If it is
950 * non-NULL, all buttons created will be default-capable (so they
951 * have extra space round them for the default highlight).
953 GtkWidget
*layout_ctrls(struct dlgparam
*dp
, struct Shortcuts
*scs
,
954 struct controlset
*s
, int listitemheight
,
961 if (!s
->boxname
&& s
->boxtitle
) {
962 /* This controlset is a panel title. */
963 return gtk_label_new(s
->boxtitle
);
967 * Otherwise, we expect to be laying out actual controls, so
968 * we'll start by creating a Columns for the purpose.
970 cols
= COLUMNS(columns_new(4));
971 ret
= GTK_WIDGET(cols
);
972 gtk_widget_show(ret
);
975 * Create a containing frame if we have a box name.
978 ret
= gtk_frame_new(s
->boxtitle
); /* NULL is valid here */
979 gtk_container_set_border_width(GTK_CONTAINER(cols
), 4);
980 gtk_container_add(GTK_CONTAINER(ret
), GTK_WIDGET(cols
));
981 gtk_widget_show(ret
);
985 * Now iterate through the controls themselves, create them,
986 * and add them to the Columns.
988 for (i
= 0; i
< s
->ncontrols
; i
++) {
989 union control
*ctrl
= s
->ctrls
[i
];
994 switch (ctrl
->generic
.type
) {
997 static const int simplecols
[1] = { 100 };
998 columns_set_cols(cols
, ctrl
->columns
.ncols
,
999 (ctrl
->columns
.percentages ?
1000 ctrl
->columns
.percentages
: simplecols
));
1002 continue; /* no actual control created */
1005 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
->tabdelay
.ctrl
);
1007 columns_taborder_last(cols
, uc
->toplevel
);
1009 continue; /* no actual control created */
1012 uc
= smalloc(sizeof(struct uctrl
));
1014 uc
->privdata
= NULL
;
1015 uc
->privdata_needs_free
= FALSE
;
1017 uc
->entry
= uc
->list
= uc
->menu
= NULL
;
1018 uc
->button
= uc
->optmenu
= uc
->text
= NULL
;
1020 switch (ctrl
->generic
.type
) {
1022 w
= gtk_button_new_with_label(ctrl
->generic
.label
);
1024 GTK_WIDGET_SET_FLAGS(w
, GTK_CAN_DEFAULT
);
1025 if (ctrl
->button
.isdefault
)
1026 gtk_window_set_default(win
, w
);
1028 gtk_signal_connect(GTK_OBJECT(w
), "clicked",
1029 GTK_SIGNAL_FUNC(button_clicked
), dp
);
1030 shortcut_add(scs
, GTK_BIN(w
)->child
, ctrl
->button
.shortcut
,
1031 SHORTCUT_UCTRL
, uc
);
1034 w
= gtk_check_button_new_with_label(ctrl
->generic
.label
);
1035 gtk_signal_connect(GTK_OBJECT(w
), "toggled",
1036 GTK_SIGNAL_FUNC(button_toggled
), dp
);
1037 shortcut_add(scs
, GTK_BIN(w
)->child
, ctrl
->checkbox
.shortcut
,
1038 SHORTCUT_UCTRL
, uc
);
1043 * Radio buttons get to go inside their own Columns, no
1047 gint i
, *percentages
;
1051 if (ctrl
->generic
.label
) {
1052 GtkWidget
*label
= gtk_label_new(ctrl
->generic
.label
);
1053 columns_add(COLUMNS(w
), label
, 0, 1);
1054 columns_force_left_align(COLUMNS(w
), label
);
1055 gtk_widget_show(label
);
1056 shortcut_add(scs
, label
, ctrl
->radio
.shortcut
,
1057 SHORTCUT_UCTRL
, uc
);
1059 percentages
= g_new(gint
, ctrl
->radio
.ncolumns
);
1060 for (i
= 0; i
< ctrl
->radio
.ncolumns
; i
++) {
1062 ((100 * (i
+1) / ctrl
->radio
.ncolumns
) -
1063 100 * i
/ ctrl
->radio
.ncolumns
);
1065 columns_set_cols(COLUMNS(w
), ctrl
->radio
.ncolumns
,
1067 g_free(percentages
);
1070 uc
->nbuttons
= ctrl
->radio
.nbuttons
;
1071 uc
->buttons
= smalloc(uc
->nbuttons
* sizeof(GtkWidget
*));
1073 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
1077 b
= (gtk_radio_button_new_with_label
1078 (group
, ctrl
->radio
.buttons
[i
]));
1080 group
= gtk_radio_button_group(GTK_RADIO_BUTTON(b
));
1081 colstart
= i
% ctrl
->radio
.ncolumns
;
1082 columns_add(COLUMNS(w
), b
, colstart
,
1083 (i
== ctrl
->radio
.nbuttons
-1 ?
1084 ctrl
->radio
.ncolumns
- colstart
: 1));
1085 columns_force_left_align(COLUMNS(w
), b
);
1087 gtk_signal_connect(GTK_OBJECT(b
), "toggled",
1088 GTK_SIGNAL_FUNC(button_toggled
), dp
);
1089 if (ctrl
->radio
.shortcuts
) {
1090 shortcut_add(scs
, GTK_BIN(b
)->child
,
1091 ctrl
->radio
.shortcuts
[i
],
1092 SHORTCUT_UCTRL
, uc
);
1098 if (ctrl
->editbox
.has_list
) {
1099 w
= gtk_combo_new();
1100 gtk_combo_set_value_in_list(GTK_COMBO(w
), FALSE
, TRUE
);
1101 uc
->entry
= GTK_COMBO(w
)->entry
;
1102 uc
->list
= GTK_COMBO(w
)->list
;
1104 w
= gtk_entry_new();
1105 if (ctrl
->editbox
.password
)
1106 gtk_entry_set_visibility(GTK_ENTRY(w
), FALSE
);
1109 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "changed",
1110 GTK_SIGNAL_FUNC(editbox_changed
), dp
);
1111 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "key_press_event",
1112 GTK_SIGNAL_FUNC(editbox_key
), dp
);
1114 * Edit boxes, for some strange reason, have a minimum
1115 * width of 150 in GTK 1.2. We don't want this - we'd
1116 * rather the edit boxes acquired their natural width
1117 * from the column layout of the rest of the box.
1121 gtk_widget_size_request(w
, &req
);
1122 gtk_widget_set_usize(w
, 10, req
.height
);
1124 if (ctrl
->generic
.label
) {
1125 GtkWidget
*label
, *container
;
1127 label
= gtk_label_new(ctrl
->generic
.label
);
1128 shortcut_add(scs
, label
, ctrl
->editbox
.shortcut
,
1129 SHORTCUT_FOCUS
, uc
->entry
);
1131 container
= columns_new(4);
1132 if (ctrl
->editbox
.percentwidth
== 100) {
1133 columns_add(COLUMNS(container
), label
, 0, 1);
1134 columns_force_left_align(COLUMNS(container
), label
);
1135 columns_add(COLUMNS(container
), w
, 0, 1);
1137 gint percentages
[2];
1138 percentages
[1] = ctrl
->editbox
.percentwidth
;
1139 percentages
[0] = 100 - ctrl
->editbox
.percentwidth
;
1140 columns_set_cols(COLUMNS(container
), 2, percentages
);
1141 columns_add(COLUMNS(container
), label
, 0, 1);
1142 columns_force_left_align(COLUMNS(container
), label
);
1143 columns_add(COLUMNS(container
), w
, 1, 1);
1145 gtk_widget_show(label
);
1150 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "focus_out_event",
1151 GTK_SIGNAL_FUNC(editbox_lostfocus
), dp
);
1153 case CTRL_FILESELECT
:
1154 case CTRL_FONTSELECT
:
1159 (ctrl
->generic
.type
== CTRL_FILESELECT ?
1160 "Browse..." : "Change...");
1162 gint percentages
[] = { 75, 25 };
1164 columns_set_cols(COLUMNS(w
), 2, percentages
);
1166 if (ctrl
->generic
.label
) {
1167 ww
= gtk_label_new(ctrl
->generic
.label
);
1168 columns_add(COLUMNS(w
), ww
, 0, 2);
1169 columns_force_left_align(COLUMNS(w
), ww
);
1170 gtk_widget_show(ww
);
1171 shortcut_add(scs
, ww
,
1172 (ctrl
->generic
.type
== CTRL_FILESELECT ?
1173 ctrl
->fileselect
.shortcut
:
1174 ctrl
->fontselect
.shortcut
),
1175 SHORTCUT_UCTRL
, uc
);
1178 uc
->entry
= ww
= gtk_entry_new();
1179 gtk_widget_size_request(ww
, &req
);
1180 gtk_widget_set_usize(ww
, 10, req
.height
);
1181 columns_add(COLUMNS(w
), ww
, 0, 1);
1182 gtk_widget_show(ww
);
1184 uc
->button
= ww
= gtk_button_new_with_label(browsebtn
);
1185 columns_add(COLUMNS(w
), ww
, 1, 1);
1186 gtk_widget_show(ww
);
1188 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "key_press_event",
1189 GTK_SIGNAL_FUNC(editbox_key
), dp
);
1190 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "changed",
1191 GTK_SIGNAL_FUNC(editbox_changed
), dp
);
1192 gtk_signal_connect(GTK_OBJECT(ww
), "clicked",
1193 GTK_SIGNAL_FUNC(filefont_clicked
), dp
);
1197 if (ctrl
->listbox
.height
== 0) {
1198 uc
->optmenu
= w
= gtk_option_menu_new();
1199 uc
->menu
= gtk_menu_new();
1200 gtk_option_menu_set_menu(GTK_OPTION_MENU(w
), uc
->menu
);
1201 gtk_object_set_data(GTK_OBJECT(uc
->menu
), "user-data",
1202 (gpointer
)uc
->optmenu
);
1204 uc
->list
= gtk_list_new();
1205 gtk_list_set_selection_mode(GTK_LIST(uc
->list
),
1206 (ctrl
->listbox
.multisel ?
1207 GTK_SELECTION_MULTIPLE
:
1208 GTK_SELECTION_SINGLE
));
1209 w
= gtk_scrolled_window_new(NULL
, NULL
);
1210 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(w
),
1212 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
1214 GTK_POLICY_AUTOMATIC
);
1215 gtk_widget_show(uc
->list
);
1216 gtk_signal_connect(GTK_OBJECT(uc
->list
), "selection-changed",
1217 GTK_SIGNAL_FUNC(list_selchange
), dp
);
1220 * Adjust the height of the scrolled window to the
1221 * minimum given by the height parameter.
1223 * This piece of guesswork is a horrid hack based
1224 * on looking inside the GTK 1.2 sources
1225 * (specifically gtkviewport.c, which appears to be
1226 * the widget which provides the border around the
1227 * scrolling area). Anyone lets me know how I can
1228 * do this in a way which isn't at risk from GTK
1229 * upgrades, I'd be grateful.
1232 int edge
= GTK_WIDGET(uc
->list
)->style
->klass
->ythickness
;
1233 gtk_widget_set_usize(w
, 10,
1234 2*edge
+ (ctrl
->listbox
.height
*
1238 if (ctrl
->listbox
.draglist
) {
1240 * GTK doesn't appear to make it easy to
1241 * implement a proper draggable list; so
1242 * instead I'm just going to have to put an Up
1243 * and a Down button to the right of the actual
1244 * list box. Ah well.
1246 GtkWidget
*cols
, *button
;
1247 static const gint percentages
[2] = { 80, 20 };
1249 cols
= columns_new(4);
1250 columns_set_cols(COLUMNS(cols
), 2, percentages
);
1251 columns_add(COLUMNS(cols
), w
, 0, 1);
1253 button
= gtk_button_new_with_label("Up");
1254 columns_add(COLUMNS(cols
), button
, 1, 1);
1255 gtk_widget_show(button
);
1256 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1257 GTK_SIGNAL_FUNC(draglist_up
), dp
);
1258 button
= gtk_button_new_with_label("Down");
1259 columns_add(COLUMNS(cols
), button
, 1, 1);
1260 gtk_widget_show(button
);
1261 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1262 GTK_SIGNAL_FUNC(draglist_down
), dp
);
1268 if (ctrl
->generic
.label
) {
1269 GtkWidget
*label
, *container
;
1271 label
= gtk_label_new(ctrl
->generic
.label
);
1273 container
= columns_new(4);
1274 if (ctrl
->listbox
.percentwidth
== 100) {
1275 columns_add(COLUMNS(container
), label
, 0, 1);
1276 columns_force_left_align(COLUMNS(container
), label
);
1277 columns_add(COLUMNS(container
), w
, 0, 1);
1279 gint percentages
[2];
1280 percentages
[1] = ctrl
->listbox
.percentwidth
;
1281 percentages
[0] = 100 - ctrl
->listbox
.percentwidth
;
1282 columns_set_cols(COLUMNS(container
), 2, percentages
);
1283 columns_add(COLUMNS(container
), label
, 0, 1);
1284 columns_force_left_align(COLUMNS(container
), label
);
1285 columns_add(COLUMNS(container
), w
, 1, 1);
1287 gtk_widget_show(label
);
1289 shortcut_add(scs
, label
, ctrl
->listbox
.shortcut
,
1290 SHORTCUT_UCTRL
, uc
);
1295 uc
->text
= w
= gtk_label_new(ctrl
->generic
.label
);
1296 gtk_label_set_line_wrap(GTK_LABEL(w
), TRUE
);
1297 /* FIXME: deal with wrapping! */
1303 columns_add(cols
, w
,
1304 COLUMN_START(ctrl
->generic
.column
),
1305 COLUMN_SPAN(ctrl
->generic
.column
));
1307 columns_force_left_align(cols
, w
);
1311 dlg_add_uctrl(dp
, uc
);
1318 struct dlgparam
*dp
;
1320 GtkWidget
*panel
, *treeitem
;
1321 struct Shortcuts shortcuts
;
1324 static void treeitem_sel(GtkItem
*item
, gpointer data
)
1326 struct selparam
*sp
= (struct selparam
*)data
;
1328 panels_switch_to(sp
->panels
, sp
->panel
);
1330 sp
->dp
->shortcuts
= &sp
->shortcuts
;
1333 void destroy(GtkWidget
*widget
, gpointer data
)
1338 int win_key_press(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1340 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1342 if (event
->keyval
== GDK_Escape
) {
1347 if ((event
->state
& GDK_MOD1_MASK
) &&
1348 (unsigned char)event
->string
[0] > 0 &&
1349 (unsigned char)event
->string
[0] <= 127) {
1350 int schr
= (unsigned char)event
->string
[0];
1351 struct Shortcut
*sc
= &dp
->shortcuts
->sc
[schr
];
1353 switch (sc
->action
) {
1354 case SHORTCUT_FOCUS
:
1355 gtk_widget_grab_focus(sc
->widget
);
1357 case SHORTCUT_UCTRL
:
1359 * We must do something sensible with a uctrl.
1360 * Precisely what this is depends on the type of
1363 switch (sc
->uc
->ctrl
->generic
.type
) {
1366 /* Check boxes and buttons get the focus _and_ get toggled. */
1367 gtk_widget_grab_focus(sc
->uc
->toplevel
);
1368 gtk_signal_emit_by_name(GTK_OBJECT(sc
->uc
->toplevel
),
1371 case CTRL_FILESELECT
:
1372 case CTRL_FONTSELECT
:
1373 /* File/font selectors have their buttons pressed (ooer),
1374 * and focus transferred to the edit box. */
1375 gtk_signal_emit_by_name(GTK_OBJECT(sc
->uc
->button
),
1377 gtk_widget_grab_focus(sc
->uc
->entry
);
1381 * Radio buttons are fun, because they have
1382 * multiple shortcuts. We must find whether the
1383 * activated shortcut is the shortcut for the whole
1384 * group, or for a particular button. In the former
1385 * case, we find the currently selected button and
1386 * focus it; in the latter, we focus-and-click the
1387 * button whose shortcut was pressed.
1389 if (schr
== sc
->uc
->ctrl
->radio
.shortcut
) {
1391 for (i
= 0; i
< sc
->uc
->ctrl
->radio
.nbuttons
; i
++)
1392 if (gtk_toggle_button_get_active
1393 (GTK_TOGGLE_BUTTON(sc
->uc
->buttons
[i
]))) {
1394 gtk_widget_grab_focus(sc
->uc
->buttons
[i
]);
1396 } else if (sc
->uc
->ctrl
->radio
.shortcuts
) {
1398 for (i
= 0; i
< sc
->uc
->ctrl
->radio
.nbuttons
; i
++)
1399 if (schr
== sc
->uc
->ctrl
->radio
.shortcuts
[i
]) {
1400 gtk_widget_grab_focus(sc
->uc
->buttons
[i
]);
1401 gtk_signal_emit_by_name
1402 (GTK_OBJECT(sc
->uc
->buttons
[i
]), "clicked");
1408 * List boxes are fun too. If the list is really an
1409 * option menu, we simply focus and click it.
1410 * Otherwise we must do something clever (FIXME).
1412 if (sc
->uc
->optmenu
) {
1416 gtk_widget_grab_focus(sc
->uc
->optmenu
);
1417 /* Option menus don't work using the "clicked" signal.
1418 * We need to manufacture a button press event :-/ */
1419 bev
.type
= GDK_BUTTON_PRESS
;
1421 gtk_signal_emit_by_name(GTK_OBJECT(sc
->uc
->optmenu
),
1422 "button_press_event",
1435 void shortcut_add(struct Shortcuts
*scs
, GtkWidget
*labelw
,
1436 int chr
, int action
, void *ptr
)
1438 GtkLabel
*label
= GTK_LABEL(labelw
);
1439 gchar
*currstr
, *pattern
;
1442 if (chr
== NO_SHORTCUT
)
1445 chr
= tolower((unsigned char)chr
);
1447 assert(scs
->sc
[chr
].action
== SHORTCUT_EMPTY
);
1449 scs
->sc
[chr
].action
= action
;
1451 if (action
== SHORTCUT_FOCUS
) {
1452 scs
->sc
[chr
].uc
= NULL
;
1453 scs
->sc
[chr
].widget
= (GtkWidget
*)ptr
;
1455 scs
->sc
[chr
].widget
= NULL
;
1456 scs
->sc
[chr
].uc
= (struct uctrl
*)ptr
;
1459 gtk_label_get(label
, &currstr
);
1460 for (i
= 0; currstr
[i
]; i
++)
1461 if (tolower((unsigned char)currstr
[i
]) == chr
) {
1464 pattern
= dupprintf("%*s_", i
, "");
1466 gtk_widget_size_request(GTK_WIDGET(label
), &req
);
1467 gtk_label_set_pattern(label
, pattern
);
1468 gtk_widget_set_usize(GTK_WIDGET(label
), -1, req
.height
);
1475 void do_config_box(void)
1477 GtkWidget
*window
, *hbox
, *vbox
, *cols
, *label
,
1478 *tree
, *treescroll
, *panels
, *panelvbox
;
1479 int index
, level
, listitemheight
;
1480 struct controlbox
*ctrlbox
;
1482 GtkTreeItem
*treeitemlevels
[8];
1483 GtkTree
*treelevels
[8];
1487 struct Shortcuts scs
;
1489 struct selparam
*selparams
= NULL
;
1490 int nselparams
= 0, selparamsize
= 0;
1492 do_defaults(NULL
, &cfg
);
1497 GtkWidget
*listitem
= gtk_list_item_new_with_label("foo");
1499 gtk_widget_size_request(listitem
, &req
);
1500 listitemheight
= req
.height
;
1501 gtk_widget_unref(listitem
);
1506 for (index
= 0; index
< lenof(scs
.sc
); index
++) {
1507 scs
.sc
[index
].action
= SHORTCUT_EMPTY
;
1510 ctrlbox
= ctrl_new_box();
1511 setup_config_box(ctrlbox
, &sl
, FALSE
, 0);
1512 unix_setup_config_box(ctrlbox
, FALSE
);
1514 window
= gtk_dialog_new();
1515 hbox
= gtk_hbox_new(FALSE
, 4);
1516 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->vbox
), hbox
, TRUE
, TRUE
, 0);
1517 gtk_container_set_border_width(GTK_CONTAINER(hbox
), 10);
1518 gtk_widget_show(hbox
);
1519 vbox
= gtk_vbox_new(FALSE
, 4);
1520 gtk_box_pack_start(GTK_BOX(hbox
), vbox
, FALSE
, FALSE
, 0);
1521 gtk_widget_show(vbox
);
1522 cols
= columns_new(4);
1523 gtk_box_pack_start(GTK_BOX(vbox
), cols
, FALSE
, FALSE
, 0);
1524 gtk_widget_show(cols
);
1525 label
= gtk_label_new("Category:");
1526 columns_add(COLUMNS(cols
), label
, 0, 1);
1527 columns_force_left_align(COLUMNS(cols
), label
);
1528 gtk_widget_show(label
);
1529 treescroll
= gtk_scrolled_window_new(NULL
, NULL
);
1530 tree
= gtk_tree_new();
1531 /* FIXME: focusing treescroll doesn't help */
1532 shortcut_add(&scs
, label
, 'g', SHORTCUT_FOCUS
, tree
);
1533 gtk_tree_set_view_mode(GTK_TREE(tree
), GTK_TREE_VIEW_ITEM
);
1534 gtk_tree_set_selection_mode(GTK_TREE(tree
), GTK_SELECTION_BROWSE
);
1535 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(treescroll
),
1537 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll
),
1539 GTK_POLICY_AUTOMATIC
);
1540 gtk_widget_show(tree
);
1541 gtk_widget_show(treescroll
);
1542 gtk_box_pack_start(GTK_BOX(vbox
), treescroll
, TRUE
, TRUE
, 0);
1543 panels
= panels_new();
1544 gtk_box_pack_start(GTK_BOX(hbox
), panels
, TRUE
, TRUE
, 0);
1545 gtk_widget_show(panels
);
1550 for (index
= 0; index
< ctrlbox
->nctrlsets
; index
++) {
1551 struct controlset
*s
= ctrlbox
->ctrlsets
[index
];
1554 if (!*s
->pathname
) {
1555 w
= layout_ctrls(&dp
, &scs
, s
, listitemheight
, GTK_WINDOW(window
));
1556 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->action_area
),
1559 int j
= path ?
ctrl_path_compare(s
->pathname
, path
) : 0;
1560 if (j
!= INT_MAX
) { /* add to treeview, start new panel */
1562 GtkWidget
*treeitem
;
1566 * We expect never to find an implicit path
1567 * component. For example, we expect never to see
1568 * A/B/C followed by A/D/E, because that would
1569 * _implicitly_ create A/D. All our path prefixes
1570 * are expected to contain actual controls and be
1571 * selectable in the treeview; so we would expect
1572 * to see A/D _explicitly_ before encountering
1575 assert(j
== ctrl_path_elements(s
->pathname
) - 1);
1577 c
= strrchr(s
->pathname
, '/');
1583 treeitem
= gtk_tree_item_new_with_label(c
);
1584 assert(j
-1 < level
);
1586 if (!treelevels
[j
-1]) {
1587 treelevels
[j
-1] = GTK_TREE(gtk_tree_new());
1588 gtk_tree_item_set_subtree
1589 (treeitemlevels
[j
-1],
1590 GTK_WIDGET(treelevels
[j
-1]));
1591 gtk_tree_item_expand(treeitemlevels
[j
-1]);
1593 gtk_tree_append(treelevels
[j
-1], treeitem
);
1595 gtk_tree_append(GTK_TREE(tree
), treeitem
);
1597 treeitemlevels
[j
] = GTK_TREE_ITEM(treeitem
);
1598 treelevels
[j
] = NULL
;
1601 gtk_widget_show(treeitem
);
1605 first
= (panelvbox
== NULL
);
1607 panelvbox
= gtk_vbox_new(FALSE
, 4);
1608 gtk_container_add(GTK_CONTAINER(panels
), panelvbox
);
1610 panels_switch_to(PANELS(panels
), panelvbox
);
1611 gtk_tree_select_child(GTK_TREE(tree
), treeitem
);
1614 if (nselparams
>= selparamsize
) {
1616 selparams
= srealloc(selparams
,
1617 selparamsize
* sizeof(*selparams
));
1619 selparams
[nselparams
].dp
= &dp
;
1620 selparams
[nselparams
].panels
= PANELS(panels
);
1621 selparams
[nselparams
].panel
= panelvbox
;
1622 selparams
[nselparams
].shortcuts
= scs
; /* structure copy */
1623 selparams
[nselparams
].treeitem
= treeitem
;
1628 w
= layout_ctrls(&dp
,
1629 &selparams
[nselparams
-1].shortcuts
,
1630 s
, listitemheight
, NULL
);
1631 gtk_box_pack_start(GTK_BOX(panelvbox
), w
, FALSE
, FALSE
, 0);
1636 for (index
= 0; index
< nselparams
; index
++) {
1637 gtk_signal_connect(GTK_OBJECT(selparams
[index
].treeitem
), "select",
1638 GTK_SIGNAL_FUNC(treeitem_sel
),
1643 dlg_refresh(NULL
, &dp
);
1645 dp
.shortcuts
= &selparams
[0].shortcuts
;
1647 gtk_widget_show(window
);
1649 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
1650 GTK_SIGNAL_FUNC(destroy
), NULL
);
1651 gtk_signal_connect(GTK_OBJECT(window
), "key_press_event",
1652 GTK_SIGNAL_FUNC(win_key_press
), &dp
);
1660 /* ======================================================================
1661 * Below here is a stub main program which allows the dialog box
1662 * code to be compiled and tested with a minimal amount of the rest
1668 /* Compile command for testing:
1670 gcc -g -o gtkdlg gtk{dlg,cols,panel}.c ../{config,dialog,settings}.c \
1671 ../{misc,tree234,be_none}.c ux{store,misc,print,cfg}.c \
1672 -I. -I.. -I../charset -DTESTMODE `gtk-config --cflags --libs`
1675 void modalfatalbox(char *p
, ...)
1678 fprintf(stderr
, "FATAL ERROR: ");
1680 vfprintf(stderr
, p
, ap
);
1682 fputc('\n', stderr
);
1686 char *cp_name(int codepage
)
1688 return (codepage
== 123 ?
"testing123" :
1689 codepage
== 234 ?
"testing234" :
1690 codepage
== 345 ?
"testing345" :
1694 char *cp_enumerate(int index
)
1696 return (index
== 0 ?
"testing123" :
1697 index
== 1 ?
"testing234" :
1701 int decode_codepage(char *cp_name
)
1703 return (!strcmp(cp_name
, "testing123") ?
123 :
1704 !strcmp(cp_name
, "testing234") ?
234 :
1705 !strcmp(cp_name
, "testing345") ?
345 :
1709 struct printer_enum_tag
{ int dummy
; } printer_test
;
1711 printer_enum
*printer_start_enum(int *nprinters_ptr
) {
1713 return &printer_test
;
1715 char *printer_get_name(printer_enum
*pe
, int i
) {
1716 return (i
==0 ?
"lpr" : i
==1 ?
"lpr -Pfoobar" : NULL
);
1718 void printer_finish_enum(printer_enum
*pe
) { }
1720 char *platform_default_s(const char *name
)
1725 int platform_default_i(const char *name
, int def
)
1730 FontSpec
platform_default_fontspec(const char *name
)
1733 if (!strcmp(name
, "Font"))
1734 strcpy(ret
.name
, "fixed");
1740 Filename
platform_default_filename(const char *name
)
1743 if (!strcmp(name
, "LogFileName"))
1744 strcpy(ret
.path
, "putty.log");
1750 char *x_get_default(const char *key
)
1755 int main(int argc
, char **argv
)
1757 gtk_init(&argc
, &argv
);