2 * gtkdlg.c - GTK implementation of the PuTTY configuration box.
8 * - colour selection woe: what to do about GTK's colour selector
9 * not allowing us full resolution in our own colour selections?
10 * Perhaps making the colour resolution per-platform, at least
11 * at the config level, is actually the least unpleasant
15 * + can't we _somehow_ have less leading between radio buttons?
16 * + wrapping text widgets, the horror, the horror
17 * + labels and their associated edit boxes don't line up
19 * + don't suppose we can fix the vertical offset labels get
20 * from their underlines?
21 * + apparently Left/Right on the treeview should be expanding
22 * and collapsing branches.
23 * + why the hell are the Up/Down focus movement keys sorting
24 * things by _width_? (See the Logging and Features panels
26 * + the error message box is in totally the wrong place and
27 * also looks ugly. Try to fix; look at how (frex) AisleRiot
32 * TODO when porting to GTK 2.0:
34 * - GtkTree is apparently deprecated and we should switch to
35 * GtkTreeView instead.
36 * - GtkLabel has a built-in mnemonic scheme, so we should at
37 * least consider switching to that from the current adhockery.
43 #include <gdk/gdkkeysyms.h>
46 #include <X11/Xutil.h>
52 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
66 struct Shortcut sc
[128];
73 int privdata_needs_free
;
74 GtkWidget
**buttons
; int nbuttons
; /* for radio buttons */
75 GtkWidget
*entry
; /* for editbox, combobox, filesel, fontsel */
76 GtkWidget
*button
; /* for filesel, fontsel */
77 GtkWidget
*list
; /* for combobox, listbox */
78 GtkWidget
*menu
; /* for optionmenu (==droplist) */
79 GtkWidget
*optmenu
; /* also for optionmenu */
80 GtkWidget
*text
; /* for text */
81 GtkAdjustment
*adj
; /* for the scrollbar in a list box */
85 tree234
*byctrl
, *bywidget
;
87 struct { unsigned char r
, g
, b
, ok
; } coloursel_result
; /* 0-255 */
88 /* `flags' are set to indicate when a GTK signal handler is being called
89 * due to automatic processing and should not flag a user event. */
91 struct Shortcuts
*shortcuts
;
92 GtkWidget
*cancelbutton
, *currtreeitem
, **treeitems
;
93 union control
*currfocus
, *lastfocus
;
97 #define FLAG_UPDATING_COMBO_LIST 1
99 enum { /* values for Shortcut.action */
100 SHORTCUT_EMPTY
, /* no shortcut on this key */
101 SHORTCUT_FOCUS
, /* focus the supplied widget */
102 SHORTCUT_UCTRL
, /* do something sane with uctrl */
103 SHORTCUT_UCTRL_UP
, /* uctrl is a draglist, move Up */
104 SHORTCUT_UCTRL_DOWN
, /* uctrl is a draglist, move Down */
108 * Forward references.
110 static gboolean
widget_focus(GtkWidget
*widget
, GdkEventFocus
*event
,
112 static void shortcut_add(struct Shortcuts
*scs
, GtkWidget
*labelw
,
113 int chr
, int action
, void *ptr
);
114 static int listitem_single_key(GtkWidget
*item
, GdkEventKey
*event
,
116 static int listitem_multi_key(GtkWidget
*item
, GdkEventKey
*event
,
118 static int listitem_button(GtkWidget
*item
, GdkEventButton
*event
,
120 static void menuitem_activate(GtkMenuItem
*item
, gpointer data
);
121 static void coloursel_ok(GtkButton
*button
, gpointer data
);
122 static void coloursel_cancel(GtkButton
*button
, gpointer data
);
123 static void window_destroy(GtkWidget
*widget
, gpointer data
);
125 static int uctrl_cmp_byctrl(void *av
, void *bv
)
127 struct uctrl
*a
= (struct uctrl
*)av
;
128 struct uctrl
*b
= (struct uctrl
*)bv
;
129 if (a
->ctrl
< b
->ctrl
)
131 else if (a
->ctrl
> b
->ctrl
)
136 static int uctrl_cmp_byctrl_find(void *av
, void *bv
)
138 union control
*a
= (union control
*)av
;
139 struct uctrl
*b
= (struct uctrl
*)bv
;
142 else if (a
> b
->ctrl
)
147 static int uctrl_cmp_bywidget(void *av
, void *bv
)
149 struct uctrl
*a
= (struct uctrl
*)av
;
150 struct uctrl
*b
= (struct uctrl
*)bv
;
151 if (a
->toplevel
< b
->toplevel
)
153 else if (a
->toplevel
> b
->toplevel
)
158 static int uctrl_cmp_bywidget_find(void *av
, void *bv
)
160 GtkWidget
*a
= (GtkWidget
*)av
;
161 struct uctrl
*b
= (struct uctrl
*)bv
;
164 else if (a
> b
->toplevel
)
169 static void dlg_init(struct dlgparam
*dp
)
171 dp
->byctrl
= newtree234(uctrl_cmp_byctrl
);
172 dp
->bywidget
= newtree234(uctrl_cmp_bywidget
);
173 dp
->coloursel_result
.ok
= FALSE
;
176 static void dlg_cleanup(struct dlgparam
*dp
)
180 freetree234(dp
->byctrl
); /* doesn't free the uctrls inside */
181 while ( (uc
= index234(dp
->bywidget
, 0)) != NULL
) {
182 del234(dp
->bywidget
, uc
);
183 if (uc
->privdata_needs_free
)
188 freetree234(dp
->bywidget
);
189 sfree(dp
->treeitems
);
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(union control
*ctrl
, void *dlg
)
241 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
242 if (dp
->currfocus
!= ctrl
)
243 return dp
->currfocus
;
245 return dp
->lastfocus
;
248 void dlg_radiobutton_set(union control
*ctrl
, void *dlg
, int which
)
250 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
251 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
252 assert(uc
->ctrl
->generic
.type
== CTRL_RADIO
);
253 assert(uc
->buttons
!= NULL
);
254 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc
->buttons
[which
]), TRUE
);
257 int dlg_radiobutton_get(union control
*ctrl
, void *dlg
)
259 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
260 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
263 assert(uc
->ctrl
->generic
.type
== CTRL_RADIO
);
264 assert(uc
->buttons
!= NULL
);
265 for (i
= 0; i
< uc
->nbuttons
; i
++)
266 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc
->buttons
[i
])))
268 return 0; /* got to return something */
271 void dlg_checkbox_set(union control
*ctrl
, void *dlg
, int checked
)
273 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
274 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
275 assert(uc
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
276 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc
->toplevel
), checked
);
279 int dlg_checkbox_get(union control
*ctrl
, void *dlg
)
281 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
282 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
283 assert(uc
->ctrl
->generic
.type
== CTRL_CHECKBOX
);
284 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc
->toplevel
));
287 void dlg_editbox_set(union control
*ctrl
, void *dlg
, char const *text
)
289 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
290 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
291 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
);
292 assert(uc
->entry
!= NULL
);
293 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), text
);
296 void dlg_editbox_get(union control
*ctrl
, void *dlg
, char *buffer
, int length
)
298 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
299 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
300 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
);
301 assert(uc
->entry
!= NULL
);
302 strncpy(buffer
, gtk_entry_get_text(GTK_ENTRY(uc
->entry
)),
304 buffer
[length
-1] = '\0';
307 static void container_remove_and_destroy(GtkWidget
*w
, gpointer data
)
309 GtkContainer
*cont
= GTK_CONTAINER(data
);
310 /* gtk_container_remove will unref the widget for us; we need not. */
311 gtk_container_remove(cont
, w
);
314 /* The `listbox' functions can also apply to combo boxes. */
315 void dlg_listbox_clear(union control
*ctrl
, void *dlg
)
317 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
318 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
321 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
322 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
323 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
325 cont
= (uc
->menu ?
GTK_CONTAINER(uc
->menu
) : GTK_CONTAINER(uc
->list
));
327 gtk_container_foreach(cont
, container_remove_and_destroy
, cont
);
330 void dlg_listbox_del(union control
*ctrl
, void *dlg
, int index
)
332 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
333 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
335 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
336 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
337 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
341 (GTK_CONTAINER(uc
->menu
),
342 g_list_nth_data(GTK_MENU_SHELL(uc
->menu
)->children
, index
));
344 gtk_list_clear_items(GTK_LIST(uc
->list
), index
, index
+1);
348 void dlg_listbox_add(union control
*ctrl
, void *dlg
, char const *text
)
350 dlg_listbox_addwithindex(ctrl
, dlg
, text
, 0);
354 * Each listbox entry may have a numeric id associated with it.
355 * Note that some front ends only permit a string to be stored at
356 * each position, which means that _if_ you put two identical
357 * strings in any listbox then you MUST not assign them different
358 * IDs and expect to get meaningful results back.
360 void dlg_listbox_addwithindex(union control
*ctrl
, void *dlg
,
361 char const *text
, int id
)
363 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
364 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
366 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
367 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
368 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
370 dp
->flags
|= FLAG_UPDATING_COMBO_LIST
;
374 * List item in a drop-down (but non-combo) list. Tabs are
375 * ignored; we just provide a standard menu item with the
378 GtkWidget
*menuitem
= gtk_menu_item_new_with_label(text
);
380 gtk_container_add(GTK_CONTAINER(uc
->menu
), menuitem
);
381 gtk_widget_show(menuitem
);
383 gtk_object_set_data(GTK_OBJECT(menuitem
), "user-data", (gpointer
)id
);
384 gtk_signal_connect(GTK_OBJECT(menuitem
), "activate",
385 GTK_SIGNAL_FUNC(menuitem_activate
), dp
);
386 } else if (!uc
->entry
) {
388 * List item in a non-combo-box list box. We make all of
389 * these Columns containing GtkLabels. This allows us to do
390 * the nasty force_left hack irrespective of whether there
391 * are tabs in the thing.
393 GtkWidget
*listitem
= gtk_list_item_new();
394 GtkWidget
*cols
= columns_new(10);
398 /* Count the tabs in the text, and hence determine # of columns. */
400 for (i
= 0; text
[i
]; i
++)
405 (uc
->ctrl
->listbox
.ncols ? uc
->ctrl
->listbox
.ncols
: 1));
406 percents
= smalloc(ncols
* sizeof(gint
));
407 percents
[ncols
-1] = 100;
408 for (i
= 0; i
< ncols
-1; i
++) {
409 percents
[i
] = uc
->ctrl
->listbox
.percentages
[i
];
410 percents
[ncols
-1] -= percents
[i
];
412 columns_set_cols(COLUMNS(cols
), ncols
, percents
);
415 for (i
= 0; i
< ncols
; i
++) {
416 int len
= strcspn(text
, "\t");
417 char *dup
= dupprintf("%.*s", len
, text
);
422 label
= gtk_label_new(dup
);
425 columns_add(COLUMNS(cols
), label
, i
, 1);
426 columns_force_left_align(COLUMNS(cols
), label
);
427 gtk_widget_show(label
);
429 gtk_container_add(GTK_CONTAINER(listitem
), cols
);
430 gtk_widget_show(cols
);
431 gtk_container_add(GTK_CONTAINER(uc
->list
), listitem
);
432 gtk_widget_show(listitem
);
434 if (ctrl
->listbox
.multisel
) {
435 gtk_signal_connect(GTK_OBJECT(listitem
), "key_press_event",
436 GTK_SIGNAL_FUNC(listitem_multi_key
), uc
->adj
);
438 gtk_signal_connect(GTK_OBJECT(listitem
), "key_press_event",
439 GTK_SIGNAL_FUNC(listitem_single_key
), uc
->adj
);
441 gtk_signal_connect(GTK_OBJECT(listitem
), "focus_in_event",
442 GTK_SIGNAL_FUNC(widget_focus
), dp
);
443 gtk_signal_connect(GTK_OBJECT(listitem
), "button_press_event",
444 GTK_SIGNAL_FUNC(listitem_button
), dp
);
445 gtk_object_set_data(GTK_OBJECT(listitem
), "user-data", (gpointer
)id
);
448 * List item in a combo-box list, which means the sensible
449 * thing to do is make it a perfectly normal label. Hence
450 * tabs are disregarded.
452 GtkWidget
*listitem
= gtk_list_item_new_with_label(text
);
454 gtk_container_add(GTK_CONTAINER(uc
->list
), listitem
);
455 gtk_widget_show(listitem
);
457 gtk_object_set_data(GTK_OBJECT(listitem
), "user-data", (gpointer
)id
);
460 dp
->flags
&= ~FLAG_UPDATING_COMBO_LIST
;
463 int dlg_listbox_getid(union control
*ctrl
, void *dlg
, int index
)
465 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
466 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
470 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
471 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
472 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
474 children
= gtk_container_children(GTK_CONTAINER(uc
->menu ? uc
->menu
:
476 item
= GTK_OBJECT(g_list_nth_data(children
, index
));
477 g_list_free(children
);
479 return (int)gtk_object_get_data(GTK_OBJECT(item
), "user-data");
482 /* dlg_listbox_index returns <0 if no single element is selected. */
483 int dlg_listbox_index(union control
*ctrl
, void *dlg
)
485 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
486 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
488 GtkWidget
*item
, *activeitem
;
492 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
493 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
494 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
497 activeitem
= gtk_menu_get_active(GTK_MENU(uc
->menu
));
499 children
= gtk_container_children(GTK_CONTAINER(uc
->menu ? uc
->menu
:
501 for (i
= 0; children
!=NULL
&& (item
= GTK_WIDGET(children
->data
))!=NULL
;
502 i
++, children
= children
->next
) {
503 if (uc
->menu ? activeitem
== item
:
504 GTK_WIDGET_STATE(item
) == GTK_STATE_SELECTED
) {
511 g_list_free(children
);
512 return selected
< 0 ?
-1 : selected
;
515 int dlg_listbox_issel(union control
*ctrl
, void *dlg
, int index
)
517 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
518 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
520 GtkWidget
*item
, *activeitem
;
522 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
523 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
524 assert(uc
->menu
!= NULL
|| uc
->list
!= NULL
);
526 children
= gtk_container_children(GTK_CONTAINER(uc
->menu ? uc
->menu
:
528 item
= GTK_WIDGET(g_list_nth_data(children
, index
));
529 g_list_free(children
);
532 activeitem
= gtk_menu_get_active(GTK_MENU(uc
->menu
));
533 return item
== activeitem
;
535 return GTK_WIDGET_STATE(item
) == GTK_STATE_SELECTED
;
539 void dlg_listbox_select(union control
*ctrl
, void *dlg
, int index
)
541 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
542 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
544 assert(uc
->ctrl
->generic
.type
== CTRL_EDITBOX
||
545 uc
->ctrl
->generic
.type
== CTRL_LISTBOX
);
546 assert(uc
->optmenu
!= NULL
|| uc
->list
!= NULL
);
549 gtk_option_menu_set_history(GTK_OPTION_MENU(uc
->optmenu
), index
);
551 gtk_list_select_item(GTK_LIST(uc
->list
), index
);
555 void dlg_text_set(union control
*ctrl
, void *dlg
, char const *text
)
557 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
558 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
560 assert(uc
->ctrl
->generic
.type
== CTRL_TEXT
);
561 assert(uc
->text
!= NULL
);
563 gtk_label_set_text(GTK_LABEL(uc
->text
), text
);
566 void dlg_filesel_set(union control
*ctrl
, void *dlg
, Filename fn
)
568 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
569 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
570 assert(uc
->ctrl
->generic
.type
== CTRL_FILESELECT
);
571 assert(uc
->entry
!= NULL
);
572 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), fn
.path
);
575 void dlg_filesel_get(union control
*ctrl
, void *dlg
, Filename
*fn
)
577 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
578 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
579 assert(uc
->ctrl
->generic
.type
== CTRL_FILESELECT
);
580 assert(uc
->entry
!= NULL
);
581 strncpy(fn
->path
, gtk_entry_get_text(GTK_ENTRY(uc
->entry
)),
583 fn
->path
[lenof(fn
->path
)-1] = '\0';
586 void dlg_fontsel_set(union control
*ctrl
, void *dlg
, FontSpec fs
)
588 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
589 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
590 assert(uc
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
591 assert(uc
->entry
!= NULL
);
592 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), fs
.name
);
595 void dlg_fontsel_get(union control
*ctrl
, void *dlg
, FontSpec
*fs
)
597 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
598 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
599 assert(uc
->ctrl
->generic
.type
== CTRL_FONTSELECT
);
600 assert(uc
->entry
!= NULL
);
601 strncpy(fs
->name
, gtk_entry_get_text(GTK_ENTRY(uc
->entry
)),
603 fs
->name
[lenof(fs
->name
)-1] = '\0';
607 * Bracketing a large set of updates in these two functions will
608 * cause the front end (if possible) to delay updating the screen
609 * until it's all complete, thus avoiding flicker.
611 void dlg_update_start(union control
*ctrl
, void *dlg
)
614 * Apparently we can't do this at all in GTK. GtkCList supports
615 * freeze and thaw, but not GtkList. Bah.
619 void dlg_update_done(union control
*ctrl
, void *dlg
)
622 * Apparently we can't do this at all in GTK. GtkCList supports
623 * freeze and thaw, but not GtkList. Bah.
627 void dlg_set_focus(union control
*ctrl
, void *dlg
)
629 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
630 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
632 switch (ctrl
->generic
.type
) {
635 /* Check boxes and buttons get the focus _and_ get toggled. */
636 gtk_widget_grab_focus(uc
->toplevel
);
638 case CTRL_FILESELECT
:
639 case CTRL_FONTSELECT
:
641 /* Anything containing an edit box gets that focused. */
642 gtk_widget_grab_focus(uc
->entry
);
646 * Radio buttons: we find the currently selected button and
651 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++)
652 if (gtk_toggle_button_get_active
653 (GTK_TOGGLE_BUTTON(uc
->buttons
[i
]))) {
654 gtk_widget_grab_focus(uc
->buttons
[i
]);
660 * If the list is really an option menu, we focus it.
661 * Otherwise we tell it to focus one of its children, which
662 * appears to do the Right Thing.
665 gtk_widget_grab_focus(uc
->optmenu
);
667 assert(uc
->list
!= NULL
);
668 gtk_container_focus(GTK_CONTAINER(uc
->list
), GTK_DIR_TAB_FORWARD
);
675 * During event processing, you might well want to give an error
676 * indication to the user. dlg_beep() is a quick and easy generic
677 * error; dlg_error() puts up a message-box or equivalent.
679 void dlg_beep(void *dlg
)
684 static void errmsg_button_clicked(GtkButton
*button
, gpointer data
)
686 gtk_widget_destroy(GTK_WIDGET(data
));
689 void dlg_error_msg(void *dlg
, char *msg
)
691 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
692 GtkWidget
*window
, *text
, *ok
;
694 window
= gtk_dialog_new();
695 text
= gtk_label_new(msg
);
696 gtk_misc_set_alignment(GTK_MISC(text
), 0.0, 0.0);
697 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->vbox
),
698 text
, FALSE
, FALSE
, 0);
699 gtk_widget_show(text
);
700 gtk_label_set_line_wrap(GTK_LABEL(text
), TRUE
);
701 ok
= gtk_button_new_with_label("OK");
702 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(window
)->action_area
),
703 ok
, FALSE
, FALSE
, 0);
705 GTK_WIDGET_SET_FLAGS(ok
, GTK_CAN_DEFAULT
);
706 gtk_window_set_default(GTK_WINDOW(window
), ok
);
707 gtk_signal_connect(GTK_OBJECT(ok
), "clicked",
708 GTK_SIGNAL_FUNC(errmsg_button_clicked
), window
);
709 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
710 GTK_SIGNAL_FUNC(window_destroy
), NULL
);
711 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
712 gtk_widget_show(window
);
717 * This function signals to the front end that the dialog's
718 * processing is completed, and passes an integer value (typically
721 void dlg_end(void *dlg
, int value
)
723 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
728 void dlg_refresh(union control
*ctrl
, void *dlg
)
730 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
734 if (ctrl
->generic
.handler
!= NULL
)
735 ctrl
->generic
.handler(ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
739 for (i
= 0; (uc
= index234(dp
->byctrl
, i
)) != NULL
; i
++) {
740 assert(uc
->ctrl
!= NULL
);
741 if (uc
->ctrl
->generic
.handler
!= NULL
)
742 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
,
743 dp
->data
, EVENT_REFRESH
);
748 void dlg_coloursel_start(union control
*ctrl
, void *dlg
, int r
, int g
, int b
)
750 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
751 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
);
754 GtkWidget
*coloursel
=
755 gtk_color_selection_dialog_new("Select a colour");
756 GtkColorSelectionDialog
*ccs
= GTK_COLOR_SELECTION_DIALOG(coloursel
);
758 dp
->coloursel_result
.ok
= FALSE
;
760 gtk_window_set_modal(GTK_WINDOW(coloursel
), TRUE
);
761 gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(ccs
->colorsel
), FALSE
);
762 cvals
[0] = r
/ 255.0;
763 cvals
[1] = g
/ 255.0;
764 cvals
[2] = b
/ 255.0;
765 cvals
[3] = 1.0; /* fully opaque! */
766 gtk_color_selection_set_color(GTK_COLOR_SELECTION(ccs
->colorsel
), cvals
);
768 gtk_object_set_data(GTK_OBJECT(ccs
->ok_button
), "user-data",
769 (gpointer
)coloursel
);
770 gtk_object_set_data(GTK_OBJECT(ccs
->cancel_button
), "user-data",
771 (gpointer
)coloursel
);
772 gtk_object_set_data(GTK_OBJECT(coloursel
), "user-data", (gpointer
)uc
);
773 gtk_signal_connect(GTK_OBJECT(ccs
->ok_button
), "clicked",
774 GTK_SIGNAL_FUNC(coloursel_ok
), (gpointer
)dp
);
775 gtk_signal_connect(GTK_OBJECT(ccs
->cancel_button
), "clicked",
776 GTK_SIGNAL_FUNC(coloursel_cancel
), (gpointer
)dp
);
777 gtk_signal_connect_object(GTK_OBJECT(ccs
->ok_button
), "clicked",
778 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
779 (gpointer
)coloursel
);
780 gtk_signal_connect_object(GTK_OBJECT(ccs
->cancel_button
), "clicked",
781 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
782 (gpointer
)coloursel
);
783 gtk_widget_show(coloursel
);
786 int dlg_coloursel_results(union control
*ctrl
, void *dlg
,
787 int *r
, int *g
, int *b
)
789 struct dlgparam
*dp
= (struct dlgparam
*)dlg
;
790 if (dp
->coloursel_result
.ok
) {
791 *r
= dp
->coloursel_result
.r
;
792 *g
= dp
->coloursel_result
.g
;
793 *b
= dp
->coloursel_result
.b
;
799 /* ----------------------------------------------------------------------
800 * Signal handlers while the dialog box is active.
803 static gboolean
widget_focus(GtkWidget
*widget
, GdkEventFocus
*event
,
806 struct dlgparam
*dp
= (struct dlgparam
*)data
;
807 struct uctrl
*uc
= dlg_find_bywidget(dp
, widget
);
808 union control
*focus
;
815 if (focus
!= dp
->currfocus
) {
816 dp
->lastfocus
= dp
->currfocus
;
817 dp
->currfocus
= focus
;
823 static void button_clicked(GtkButton
*button
, gpointer data
)
825 struct dlgparam
*dp
= (struct dlgparam
*)data
;
826 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
827 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_ACTION
);
830 static void button_toggled(GtkToggleButton
*tb
, gpointer data
)
832 struct dlgparam
*dp
= (struct dlgparam
*)data
;
833 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(tb
));
834 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
837 static int editbox_key(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
840 * GtkEntry has a nasty habit of eating the Return key, which
841 * is unhelpful since it doesn't actually _do_ anything with it
842 * (it calls gtk_widget_activate, but our edit boxes never need
843 * activating). So I catch Return before GtkEntry sees it, and
844 * pass it straight on to the parent widget. Effect: hitting
845 * Return in an edit box will now activate the default button
846 * in the dialog just like it will everywhere else.
848 if (event
->keyval
== GDK_Return
&& widget
->parent
!= NULL
) {
850 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget
), "key_press_event");
851 gtk_signal_emit_by_name(GTK_OBJECT(widget
->parent
), "key_press_event",
858 static void editbox_changed(GtkEditable
*ed
, gpointer data
)
860 struct dlgparam
*dp
= (struct dlgparam
*)data
;
861 if (!(dp
->flags
& FLAG_UPDATING_COMBO_LIST
)) {
862 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(ed
));
863 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
867 static void editbox_lostfocus(GtkWidget
*ed
, GdkEventFocus
*event
,
870 struct dlgparam
*dp
= (struct dlgparam
*)data
;
871 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(ed
));
872 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_REFRESH
);
875 static int listitem_key(GtkWidget
*item
, GdkEventKey
*event
, gpointer data
,
878 GtkAdjustment
*adj
= GTK_ADJUSTMENT(data
);
880 if (event
->keyval
== GDK_Up
|| event
->keyval
== GDK_KP_Up
||
881 event
->keyval
== GDK_Down
|| event
->keyval
== GDK_KP_Down
||
882 event
->keyval
== GDK_Page_Up
|| event
->keyval
== GDK_KP_Page_Up
||
883 event
->keyval
== GDK_Page_Down
|| event
->keyval
== GDK_KP_Page_Down
) {
885 * Up, Down, PgUp or PgDn have been pressed on a ListItem
886 * in a list box. So, if the list box is single-selection:
888 * - if the list item in question isn't already selected,
889 * we simply select it.
890 * - otherwise, we find the next one (or next
891 * however-far-away) in whichever direction we're going,
893 * + in this case, we must also fiddle with the
894 * scrollbar to ensure the newly selected item is
897 * If it's multiple-selection, we do all of the above
898 * except actually selecting anything, so we move the focus
899 * and fiddle the scrollbar to follow it.
901 GtkWidget
*list
= item
->parent
;
903 gtk_signal_emit_stop_by_name(GTK_OBJECT(item
), "key_press_event");
906 GTK_WIDGET_STATE(item
) != GTK_STATE_SELECTED
) {
907 gtk_list_select_child(GTK_LIST(list
), item
);
910 (event
->keyval
==GDK_Up
|| event
->keyval
==GDK_KP_Up
||
911 event
->keyval
==GDK_Page_Up
|| event
->keyval
==GDK_KP_Page_Up
)
914 (event
->keyval
==GDK_Page_Down
||
915 event
->keyval
==GDK_KP_Page_Down
||
916 event
->keyval
==GDK_Page_Up
|| event
->keyval
==GDK_KP_Page_Up
)
920 GList
*children
, *chead
;
922 chead
= children
= gtk_container_children(GTK_CONTAINER(list
));
924 n
= g_list_length(children
);
928 * Figure out how many list items to a screenful,
929 * and adjust the step appropriately.
931 step
= 0.5 + adj
->page_size
* n
/ (adj
->upper
- adj
->lower
);
932 step
--; /* go by one less than that */
936 while (children
!= NULL
) {
937 if (item
== children
->data
)
939 children
= children
->next
;
944 if (direction
< 0 && i
> 0)
945 children
= children
->prev
, i
--;
946 else if (direction
> 0 && i
< n
-1)
947 children
= children
->next
, i
++;
951 if (children
&& children
->data
) {
953 gtk_list_select_child(GTK_LIST(list
),
954 GTK_WIDGET(children
->data
));
955 gtk_widget_grab_focus(GTK_WIDGET(children
->data
));
956 gtk_adjustment_clamp_page
958 adj
->lower
+ (adj
->upper
-adj
->lower
) * i
/ n
,
959 adj
->lower
+ (adj
->upper
-adj
->lower
) * (i
+1) / n
);
970 static int listitem_single_key(GtkWidget
*item
, GdkEventKey
*event
,
973 listitem_key(item
, event
, data
, FALSE
);
976 static int listitem_multi_key(GtkWidget
*item
, GdkEventKey
*event
,
979 listitem_key(item
, event
, data
, TRUE
);
982 static int listitem_button(GtkWidget
*item
, GdkEventButton
*event
,
985 struct dlgparam
*dp
= (struct dlgparam
*)data
;
986 if (event
->type
== GDK_2BUTTON_PRESS
||
987 event
->type
== GDK_3BUTTON_PRESS
) {
988 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(item
));
989 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_ACTION
);
995 static void list_selchange(GtkList
*list
, gpointer data
)
997 struct dlgparam
*dp
= (struct dlgparam
*)data
;
998 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(list
));
999 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_SELCHANGE
);
1002 static void menuitem_activate(GtkMenuItem
*item
, gpointer data
)
1004 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1005 GtkWidget
*menushell
= GTK_WIDGET(item
)->parent
;
1006 gpointer optmenu
= gtk_object_get_data(GTK_OBJECT(menushell
), "user-data");
1007 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(optmenu
));
1008 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_SELCHANGE
);
1011 static void draglist_move(struct dlgparam
*dp
, struct uctrl
*uc
, int direction
)
1013 int index
= dlg_listbox_index(uc
->ctrl
, dp
);
1014 GList
*children
= gtk_container_children(GTK_CONTAINER(uc
->list
));
1018 (index
== 0 && direction
< 0) ||
1019 (index
== g_list_length(children
)-1 && direction
> 0)) {
1024 child
= g_list_nth_data(children
, index
);
1025 gtk_widget_ref(child
);
1026 gtk_list_clear_items(GTK_LIST(uc
->list
), index
, index
+1);
1027 g_list_free(children
);
1030 children
= g_list_append(children
, child
);
1031 gtk_list_insert_items(GTK_LIST(uc
->list
), children
, index
+ direction
);
1032 gtk_list_select_item(GTK_LIST(uc
->list
), index
+ direction
);
1033 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_VALCHANGE
);
1036 static void draglist_up(GtkButton
*button
, gpointer data
)
1038 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1039 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
1040 draglist_move(dp
, uc
, -1);
1043 static void draglist_down(GtkButton
*button
, gpointer data
)
1045 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1046 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
1047 draglist_move(dp
, uc
, +1);
1050 static void filesel_ok(GtkButton
*button
, gpointer data
)
1052 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1053 gpointer filesel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
1054 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(filesel
), "user-data");
1055 char *name
= gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel
));
1056 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), name
);
1059 static void fontsel_ok(GtkButton
*button
, gpointer data
)
1061 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1062 gpointer fontsel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
1063 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(fontsel
), "user-data");
1064 char *name
= gtk_font_selection_dialog_get_font_name
1065 (GTK_FONT_SELECTION_DIALOG(fontsel
));
1066 gtk_entry_set_text(GTK_ENTRY(uc
->entry
), name
);
1069 static void coloursel_ok(GtkButton
*button
, gpointer data
)
1071 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1072 gpointer coloursel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
1073 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(coloursel
), "user-data");
1075 gtk_color_selection_get_color
1076 (GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(coloursel
)->colorsel
),
1078 dp
->coloursel_result
.r
= (int) (255 * cvals
[0]);
1079 dp
->coloursel_result
.g
= (int) (255 * cvals
[1]);
1080 dp
->coloursel_result
.b
= (int) (255 * cvals
[2]);
1081 dp
->coloursel_result
.ok
= TRUE
;
1082 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_CALLBACK
);
1085 static void coloursel_cancel(GtkButton
*button
, gpointer data
)
1087 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1088 gpointer coloursel
= gtk_object_get_data(GTK_OBJECT(button
), "user-data");
1089 struct uctrl
*uc
= gtk_object_get_data(GTK_OBJECT(coloursel
), "user-data");
1090 dp
->coloursel_result
.ok
= FALSE
;
1091 uc
->ctrl
->generic
.handler(uc
->ctrl
, dp
, dp
->data
, EVENT_CALLBACK
);
1094 static void filefont_clicked(GtkButton
*button
, gpointer data
)
1096 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1097 struct uctrl
*uc
= dlg_find_bywidget(dp
, GTK_WIDGET(button
));
1099 if (uc
->ctrl
->generic
.type
== CTRL_FILESELECT
) {
1100 GtkWidget
*filesel
=
1101 gtk_file_selection_new(uc
->ctrl
->fileselect
.title
);
1102 gtk_window_set_modal(GTK_WINDOW(filesel
), TRUE
);
1104 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->ok_button
), "user-data",
1106 gtk_object_set_data(GTK_OBJECT(filesel
), "user-data", (gpointer
)uc
);
1108 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->ok_button
), "clicked",
1109 GTK_SIGNAL_FUNC(filesel_ok
), (gpointer
)dp
);
1110 gtk_signal_connect_object
1111 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->ok_button
), "clicked",
1112 GTK_SIGNAL_FUNC(gtk_widget_destroy
), (gpointer
)filesel
);
1113 gtk_signal_connect_object
1114 (GTK_OBJECT(GTK_FILE_SELECTION(filesel
)->cancel_button
), "clicked",
1115 GTK_SIGNAL_FUNC(gtk_widget_destroy
), (gpointer
)filesel
);
1116 gtk_widget_show(filesel
);
1119 if (uc
->ctrl
->generic
.type
== CTRL_FONTSELECT
) {
1120 gchar
*spacings
[] = { "c", "m", NULL
};
1121 gchar
*fontname
= gtk_entry_get_text(GTK_ENTRY(uc
->entry
));
1122 GtkWidget
*fontsel
=
1123 gtk_font_selection_dialog_new("Select a font");
1124 gtk_window_set_modal(GTK_WINDOW(fontsel
), TRUE
);
1125 gtk_font_selection_dialog_set_filter
1126 (GTK_FONT_SELECTION_DIALOG(fontsel
),
1127 GTK_FONT_FILTER_BASE
, GTK_FONT_ALL
,
1128 NULL
, NULL
, NULL
, NULL
, spacings
, NULL
);
1129 if (!gtk_font_selection_dialog_set_font_name
1130 (GTK_FONT_SELECTION_DIALOG(fontsel
), fontname
)) {
1132 * If the font name wasn't found as it was, try opening
1133 * it and extracting its FONT property. This should
1134 * have the effect of mapping short aliases into true
1137 GdkFont
*font
= gdk_font_load(fontname
);
1139 XFontStruct
*xfs
= GDK_FONT_XFONT(font
);
1140 Display
*disp
= GDK_FONT_XDISPLAY(font
);
1141 Atom fontprop
= XInternAtom(disp
, "FONT", False
);
1143 if (XGetFontProperty(xfs
, fontprop
, &ret
)) {
1144 char *name
= XGetAtomName(disp
, (Atom
)ret
);
1146 gtk_font_selection_dialog_set_font_name
1147 (GTK_FONT_SELECTION_DIALOG(fontsel
), name
);
1149 gdk_font_unref(font
);
1153 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->ok_button
),
1154 "user-data", (gpointer
)fontsel
);
1155 gtk_object_set_data(GTK_OBJECT(fontsel
), "user-data", (gpointer
)uc
);
1157 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->ok_button
),
1158 "clicked", GTK_SIGNAL_FUNC(fontsel_ok
), (gpointer
)dp
);
1159 gtk_signal_connect_object
1160 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->ok_button
),
1161 "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy
),
1163 gtk_signal_connect_object
1164 (GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel
)->cancel_button
),
1165 "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy
),
1167 gtk_widget_show(fontsel
);
1171 /* ----------------------------------------------------------------------
1172 * This function does the main layout work: it reads a controlset,
1173 * it creates the relevant GTK controls, and returns a GtkWidget
1174 * containing the result. (This widget might be a title of some
1175 * sort, it might be a Columns containing many controls, or it
1176 * might be a GtkFrame containing a Columns; whatever it is, it's
1177 * definitely a GtkWidget and should probably be added to a
1180 * `listitemheight' is used to calculate a usize for list boxes: it
1181 * should be the height from the size request of a GtkListItem.
1183 * `win' is required for setting the default button. If it is
1184 * non-NULL, all buttons created will be default-capable (so they
1185 * have extra space round them for the default highlight).
1187 GtkWidget
*layout_ctrls(struct dlgparam
*dp
, struct Shortcuts
*scs
,
1188 struct controlset
*s
, int listitemheight
,
1195 if (!s
->boxname
&& s
->boxtitle
) {
1196 /* This controlset is a panel title. */
1197 return gtk_label_new(s
->boxtitle
);
1201 * Otherwise, we expect to be laying out actual controls, so
1202 * we'll start by creating a Columns for the purpose.
1204 cols
= COLUMNS(columns_new(4));
1205 ret
= GTK_WIDGET(cols
);
1206 gtk_widget_show(ret
);
1209 * Create a containing frame if we have a box name.
1212 ret
= gtk_frame_new(s
->boxtitle
); /* NULL is valid here */
1213 gtk_container_set_border_width(GTK_CONTAINER(cols
), 4);
1214 gtk_container_add(GTK_CONTAINER(ret
), GTK_WIDGET(cols
));
1215 gtk_widget_show(ret
);
1219 * Now iterate through the controls themselves, create them,
1220 * and add them to the Columns.
1222 for (i
= 0; i
< s
->ncontrols
; i
++) {
1223 union control
*ctrl
= s
->ctrls
[i
];
1226 GtkWidget
*w
= NULL
;
1228 switch (ctrl
->generic
.type
) {
1231 static const int simplecols
[1] = { 100 };
1232 columns_set_cols(cols
, ctrl
->columns
.ncols
,
1233 (ctrl
->columns
.percentages ?
1234 ctrl
->columns
.percentages
: simplecols
));
1236 continue; /* no actual control created */
1239 struct uctrl
*uc
= dlg_find_byctrl(dp
, ctrl
->tabdelay
.ctrl
);
1241 columns_taborder_last(cols
, uc
->toplevel
);
1243 continue; /* no actual control created */
1246 uc
= smalloc(sizeof(struct uctrl
));
1248 uc
->privdata
= NULL
;
1249 uc
->privdata_needs_free
= FALSE
;
1251 uc
->entry
= uc
->list
= uc
->menu
= NULL
;
1252 uc
->button
= uc
->optmenu
= uc
->text
= NULL
;
1254 switch (ctrl
->generic
.type
) {
1256 w
= gtk_button_new_with_label(ctrl
->generic
.label
);
1258 GTK_WIDGET_SET_FLAGS(w
, GTK_CAN_DEFAULT
);
1259 if (ctrl
->button
.isdefault
)
1260 gtk_window_set_default(win
, w
);
1261 if (ctrl
->button
.iscancel
)
1262 dp
->cancelbutton
= w
;
1264 gtk_signal_connect(GTK_OBJECT(w
), "clicked",
1265 GTK_SIGNAL_FUNC(button_clicked
), dp
);
1266 gtk_signal_connect(GTK_OBJECT(w
), "focus_in_event",
1267 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1268 shortcut_add(scs
, GTK_BIN(w
)->child
, ctrl
->button
.shortcut
,
1269 SHORTCUT_UCTRL
, uc
);
1272 w
= gtk_check_button_new_with_label(ctrl
->generic
.label
);
1273 gtk_signal_connect(GTK_OBJECT(w
), "toggled",
1274 GTK_SIGNAL_FUNC(button_toggled
), dp
);
1275 gtk_signal_connect(GTK_OBJECT(w
), "focus_in_event",
1276 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1277 shortcut_add(scs
, GTK_BIN(w
)->child
, ctrl
->checkbox
.shortcut
,
1278 SHORTCUT_UCTRL
, uc
);
1283 * Radio buttons get to go inside their own Columns, no
1287 gint i
, *percentages
;
1291 if (ctrl
->generic
.label
) {
1292 GtkWidget
*label
= gtk_label_new(ctrl
->generic
.label
);
1293 columns_add(COLUMNS(w
), label
, 0, 1);
1294 columns_force_left_align(COLUMNS(w
), label
);
1295 gtk_widget_show(label
);
1296 shortcut_add(scs
, label
, ctrl
->radio
.shortcut
,
1297 SHORTCUT_UCTRL
, uc
);
1299 percentages
= g_new(gint
, ctrl
->radio
.ncolumns
);
1300 for (i
= 0; i
< ctrl
->radio
.ncolumns
; i
++) {
1302 ((100 * (i
+1) / ctrl
->radio
.ncolumns
) -
1303 100 * i
/ ctrl
->radio
.ncolumns
);
1305 columns_set_cols(COLUMNS(w
), ctrl
->radio
.ncolumns
,
1307 g_free(percentages
);
1310 uc
->nbuttons
= ctrl
->radio
.nbuttons
;
1311 uc
->buttons
= smalloc(uc
->nbuttons
* sizeof(GtkWidget
*));
1313 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
1317 b
= (gtk_radio_button_new_with_label
1318 (group
, ctrl
->radio
.buttons
[i
]));
1320 group
= gtk_radio_button_group(GTK_RADIO_BUTTON(b
));
1321 colstart
= i
% ctrl
->radio
.ncolumns
;
1322 columns_add(COLUMNS(w
), b
, colstart
,
1323 (i
== ctrl
->radio
.nbuttons
-1 ?
1324 ctrl
->radio
.ncolumns
- colstart
: 1));
1325 columns_force_left_align(COLUMNS(w
), b
);
1327 gtk_signal_connect(GTK_OBJECT(b
), "toggled",
1328 GTK_SIGNAL_FUNC(button_toggled
), dp
);
1329 gtk_signal_connect(GTK_OBJECT(b
), "focus_in_event",
1330 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1331 if (ctrl
->radio
.shortcuts
) {
1332 shortcut_add(scs
, GTK_BIN(b
)->child
,
1333 ctrl
->radio
.shortcuts
[i
],
1334 SHORTCUT_UCTRL
, uc
);
1340 if (ctrl
->editbox
.has_list
) {
1341 w
= gtk_combo_new();
1342 gtk_combo_set_value_in_list(GTK_COMBO(w
), FALSE
, TRUE
);
1343 uc
->entry
= GTK_COMBO(w
)->entry
;
1344 uc
->list
= GTK_COMBO(w
)->list
;
1346 w
= gtk_entry_new();
1347 if (ctrl
->editbox
.password
)
1348 gtk_entry_set_visibility(GTK_ENTRY(w
), FALSE
);
1351 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "changed",
1352 GTK_SIGNAL_FUNC(editbox_changed
), dp
);
1353 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "key_press_event",
1354 GTK_SIGNAL_FUNC(editbox_key
), dp
);
1355 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "focus_in_event",
1356 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1358 * Edit boxes, for some strange reason, have a minimum
1359 * width of 150 in GTK 1.2. We don't want this - we'd
1360 * rather the edit boxes acquired their natural width
1361 * from the column layout of the rest of the box.
1365 gtk_widget_size_request(w
, &req
);
1366 gtk_widget_set_usize(w
, 10, req
.height
);
1368 if (ctrl
->generic
.label
) {
1369 GtkWidget
*label
, *container
;
1371 label
= gtk_label_new(ctrl
->generic
.label
);
1372 shortcut_add(scs
, label
, ctrl
->editbox
.shortcut
,
1373 SHORTCUT_FOCUS
, uc
->entry
);
1375 container
= columns_new(4);
1376 if (ctrl
->editbox
.percentwidth
== 100) {
1377 columns_add(COLUMNS(container
), label
, 0, 1);
1378 columns_force_left_align(COLUMNS(container
), label
);
1379 columns_add(COLUMNS(container
), w
, 0, 1);
1381 gint percentages
[2];
1382 percentages
[1] = ctrl
->editbox
.percentwidth
;
1383 percentages
[0] = 100 - ctrl
->editbox
.percentwidth
;
1384 columns_set_cols(COLUMNS(container
), 2, percentages
);
1385 columns_add(COLUMNS(container
), label
, 0, 1);
1386 columns_force_left_align(COLUMNS(container
), label
);
1387 columns_add(COLUMNS(container
), w
, 1, 1);
1389 gtk_widget_show(label
);
1394 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "focus_out_event",
1395 GTK_SIGNAL_FUNC(editbox_lostfocus
), dp
);
1397 case CTRL_FILESELECT
:
1398 case CTRL_FONTSELECT
:
1403 (ctrl
->generic
.type
== CTRL_FILESELECT ?
1404 "Browse..." : "Change...");
1406 gint percentages
[] = { 75, 25 };
1408 columns_set_cols(COLUMNS(w
), 2, percentages
);
1410 if (ctrl
->generic
.label
) {
1411 ww
= gtk_label_new(ctrl
->generic
.label
);
1412 columns_add(COLUMNS(w
), ww
, 0, 2);
1413 columns_force_left_align(COLUMNS(w
), ww
);
1414 gtk_widget_show(ww
);
1415 shortcut_add(scs
, ww
,
1416 (ctrl
->generic
.type
== CTRL_FILESELECT ?
1417 ctrl
->fileselect
.shortcut
:
1418 ctrl
->fontselect
.shortcut
),
1419 SHORTCUT_UCTRL
, uc
);
1422 uc
->entry
= ww
= gtk_entry_new();
1423 gtk_widget_size_request(ww
, &req
);
1424 gtk_widget_set_usize(ww
, 10, req
.height
);
1425 columns_add(COLUMNS(w
), ww
, 0, 1);
1426 gtk_widget_show(ww
);
1428 uc
->button
= ww
= gtk_button_new_with_label(browsebtn
);
1429 columns_add(COLUMNS(w
), ww
, 1, 1);
1430 gtk_widget_show(ww
);
1432 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "key_press_event",
1433 GTK_SIGNAL_FUNC(editbox_key
), dp
);
1434 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "changed",
1435 GTK_SIGNAL_FUNC(editbox_changed
), dp
);
1436 gtk_signal_connect(GTK_OBJECT(uc
->entry
), "focus_in_event",
1437 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1438 gtk_signal_connect(GTK_OBJECT(uc
->button
), "focus_in_event",
1439 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1440 gtk_signal_connect(GTK_OBJECT(ww
), "clicked",
1441 GTK_SIGNAL_FUNC(filefont_clicked
), dp
);
1445 if (ctrl
->listbox
.height
== 0) {
1446 uc
->optmenu
= w
= gtk_option_menu_new();
1447 uc
->menu
= gtk_menu_new();
1448 gtk_option_menu_set_menu(GTK_OPTION_MENU(w
), uc
->menu
);
1449 gtk_object_set_data(GTK_OBJECT(uc
->menu
), "user-data",
1450 (gpointer
)uc
->optmenu
);
1451 gtk_signal_connect(GTK_OBJECT(uc
->optmenu
), "focus_in_event",
1452 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1454 uc
->list
= gtk_list_new();
1455 if (ctrl
->listbox
.multisel
) {
1456 gtk_list_set_selection_mode(GTK_LIST(uc
->list
),
1457 GTK_SELECTION_MULTIPLE
);
1459 gtk_list_set_selection_mode(GTK_LIST(uc
->list
),
1460 GTK_SELECTION_SINGLE
);
1462 w
= gtk_scrolled_window_new(NULL
, NULL
);
1463 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(w
),
1465 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
1467 GTK_POLICY_AUTOMATIC
);
1468 uc
->adj
= gtk_scrolled_window_get_vadjustment
1469 (GTK_SCROLLED_WINDOW(w
));
1471 gtk_widget_show(uc
->list
);
1472 gtk_signal_connect(GTK_OBJECT(uc
->list
), "selection-changed",
1473 GTK_SIGNAL_FUNC(list_selchange
), dp
);
1474 gtk_signal_connect(GTK_OBJECT(uc
->list
), "focus_in_event",
1475 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1478 * Adjust the height of the scrolled window to the
1479 * minimum given by the height parameter.
1481 * This piece of guesswork is a horrid hack based
1482 * on looking inside the GTK 1.2 sources
1483 * (specifically gtkviewport.c, which appears to be
1484 * the widget which provides the border around the
1485 * scrolling area). Anyone lets me know how I can
1486 * do this in a way which isn't at risk from GTK
1487 * upgrades, I'd be grateful.
1490 int edge
= GTK_WIDGET(uc
->list
)->style
->klass
->ythickness
;
1491 gtk_widget_set_usize(w
, 10,
1492 2*edge
+ (ctrl
->listbox
.height
*
1496 if (ctrl
->listbox
.draglist
) {
1498 * GTK doesn't appear to make it easy to
1499 * implement a proper draggable list; so
1500 * instead I'm just going to have to put an Up
1501 * and a Down button to the right of the actual
1502 * list box. Ah well.
1504 GtkWidget
*cols
, *button
;
1505 static const gint percentages
[2] = { 80, 20 };
1507 cols
= columns_new(4);
1508 columns_set_cols(COLUMNS(cols
), 2, percentages
);
1509 columns_add(COLUMNS(cols
), w
, 0, 1);
1511 button
= gtk_button_new_with_label("Up");
1512 columns_add(COLUMNS(cols
), button
, 1, 1);
1513 gtk_widget_show(button
);
1514 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1515 GTK_SIGNAL_FUNC(draglist_up
), dp
);
1516 gtk_signal_connect(GTK_OBJECT(button
), "focus_in_event",
1517 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1518 button
= gtk_button_new_with_label("Down");
1519 columns_add(COLUMNS(cols
), button
, 1, 1);
1520 gtk_widget_show(button
);
1521 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1522 GTK_SIGNAL_FUNC(draglist_down
), dp
);
1523 gtk_signal_connect(GTK_OBJECT(button
), "focus_in_event",
1524 GTK_SIGNAL_FUNC(widget_focus
), dp
);
1530 if (ctrl
->generic
.label
) {
1531 GtkWidget
*label
, *container
;
1533 label
= gtk_label_new(ctrl
->generic
.label
);
1535 container
= columns_new(4);
1536 if (ctrl
->listbox
.percentwidth
== 100) {
1537 columns_add(COLUMNS(container
), label
, 0, 1);
1538 columns_force_left_align(COLUMNS(container
), label
);
1539 columns_add(COLUMNS(container
), w
, 0, 1);
1541 gint percentages
[2];
1542 percentages
[1] = ctrl
->listbox
.percentwidth
;
1543 percentages
[0] = 100 - ctrl
->listbox
.percentwidth
;
1544 columns_set_cols(COLUMNS(container
), 2, percentages
);
1545 columns_add(COLUMNS(container
), label
, 0, 1);
1546 columns_force_left_align(COLUMNS(container
), label
);
1547 columns_add(COLUMNS(container
), w
, 1, 1);
1549 gtk_widget_show(label
);
1551 shortcut_add(scs
, label
, ctrl
->listbox
.shortcut
,
1552 SHORTCUT_UCTRL
, uc
);
1557 uc
->text
= w
= gtk_label_new(ctrl
->generic
.label
);
1558 gtk_misc_set_alignment(GTK_MISC(w
), 0.0, 0.0);
1559 gtk_label_set_line_wrap(GTK_LABEL(w
), TRUE
);
1560 /* FIXME: deal with wrapping! */
1566 columns_add(cols
, w
,
1567 COLUMN_START(ctrl
->generic
.column
),
1568 COLUMN_SPAN(ctrl
->generic
.column
));
1570 columns_force_left_align(cols
, w
);
1574 dlg_add_uctrl(dp
, uc
);
1581 struct dlgparam
*dp
;
1583 GtkWidget
*panel
, *treeitem
;
1584 struct Shortcuts shortcuts
;
1587 static void treeitem_sel(GtkItem
*item
, gpointer data
)
1589 struct selparam
*sp
= (struct selparam
*)data
;
1591 panels_switch_to(sp
->panels
, sp
->panel
);
1593 sp
->dp
->shortcuts
= &sp
->shortcuts
;
1594 sp
->dp
->currtreeitem
= sp
->treeitem
;
1597 static void window_destroy(GtkWidget
*widget
, gpointer data
)
1602 int win_key_press(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1604 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1606 if (event
->keyval
== GDK_Escape
&& dp
->cancelbutton
) {
1607 gtk_signal_emit_by_name(GTK_OBJECT(dp
->cancelbutton
), "clicked");
1611 if ((event
->state
& GDK_MOD1_MASK
) &&
1612 (unsigned char)event
->string
[0] > 0 &&
1613 (unsigned char)event
->string
[0] <= 127) {
1614 int schr
= (unsigned char)event
->string
[0];
1615 struct Shortcut
*sc
= &dp
->shortcuts
->sc
[schr
];
1617 switch (sc
->action
) {
1618 case SHORTCUT_FOCUS
:
1619 gtk_widget_grab_focus(sc
->widget
);
1621 case SHORTCUT_UCTRL
:
1623 * We must do something sensible with a uctrl.
1624 * Precisely what this is depends on the type of
1627 switch (sc
->uc
->ctrl
->generic
.type
) {
1630 /* Check boxes and buttons get the focus _and_ get toggled. */
1631 gtk_widget_grab_focus(sc
->uc
->toplevel
);
1632 gtk_signal_emit_by_name(GTK_OBJECT(sc
->uc
->toplevel
),
1635 case CTRL_FILESELECT
:
1636 case CTRL_FONTSELECT
:
1637 /* File/font selectors have their buttons pressed (ooer),
1638 * and focus transferred to the edit box. */
1639 gtk_signal_emit_by_name(GTK_OBJECT(sc
->uc
->button
),
1641 gtk_widget_grab_focus(sc
->uc
->entry
);
1645 * Radio buttons are fun, because they have
1646 * multiple shortcuts. We must find whether the
1647 * activated shortcut is the shortcut for the whole
1648 * group, or for a particular button. In the former
1649 * case, we find the currently selected button and
1650 * focus it; in the latter, we focus-and-click the
1651 * button whose shortcut was pressed.
1653 if (schr
== sc
->uc
->ctrl
->radio
.shortcut
) {
1655 for (i
= 0; i
< sc
->uc
->ctrl
->radio
.nbuttons
; i
++)
1656 if (gtk_toggle_button_get_active
1657 (GTK_TOGGLE_BUTTON(sc
->uc
->buttons
[i
]))) {
1658 gtk_widget_grab_focus(sc
->uc
->buttons
[i
]);
1660 } else if (sc
->uc
->ctrl
->radio
.shortcuts
) {
1662 for (i
= 0; i
< sc
->uc
->ctrl
->radio
.nbuttons
; i
++)
1663 if (schr
== sc
->uc
->ctrl
->radio
.shortcuts
[i
]) {
1664 gtk_widget_grab_focus(sc
->uc
->buttons
[i
]);
1665 gtk_signal_emit_by_name
1666 (GTK_OBJECT(sc
->uc
->buttons
[i
]), "clicked");
1672 * If the list is really an option menu, we focus
1673 * and click it. Otherwise we tell it to focus one
1674 * of its children, which appears to do the Right
1677 if (sc
->uc
->optmenu
) {
1681 gtk_widget_grab_focus(sc
->uc
->optmenu
);
1682 /* Option menus don't work using the "clicked" signal.
1683 * We need to manufacture a button press event :-/ */
1684 bev
.type
= GDK_BUTTON_PRESS
;
1686 gtk_signal_emit_by_name(GTK_OBJECT(sc
->uc
->optmenu
),
1687 "button_press_event",
1690 assert(sc
->uc
->list
!= NULL
);
1692 gtk_container_focus(GTK_CONTAINER(sc
->uc
->list
),
1693 GTK_DIR_TAB_FORWARD
);
1704 int tree_key_press(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1706 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1708 if (event
->keyval
== GDK_Up
|| event
->keyval
== GDK_KP_Up
||
1709 event
->keyval
== GDK_Down
|| event
->keyval
== GDK_KP_Down
) {
1711 for (i
= 0; i
< dp
->ntreeitems
; i
++)
1712 if (widget
== dp
->treeitems
[i
]) {
1713 if (event
->keyval
== GDK_Up
|| event
->keyval
== GDK_KP_Up
) {
1717 if (i
< dp
->ntreeitems
-1)
1722 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget
),
1726 gtk_signal_emit_by_name(GTK_OBJECT(dp
->treeitems
[j
]), "toggle");
1727 gtk_widget_grab_focus(dp
->treeitems
[j
]);
1735 gint
tree_focus(GtkContainer
*container
, GtkDirectionType direction
,
1738 struct dlgparam
*dp
= (struct dlgparam
*)data
;
1741 gtk_signal_emit_stop_by_name(GTK_OBJECT(container
), "focus");
1742 dir
= (direction
== GTK_DIR_UP
|| direction
== GTK_DIR_LEFT
||
1743 direction
== GTK_DIR_TAB_BACKWARD
) ?
-1 : +1;
1746 * See if any of the treeitems has the focus.
1749 for (i
= 0; i
< dp
->ntreeitems
; i
++)
1750 if (GTK_WIDGET_HAS_FOCUS(dp
->treeitems
[i
])) {
1756 * If there's a focused treeitem, we return FALSE to cause the
1757 * focus to move on to some totally other control. If not, we
1758 * focus the selected one.
1763 gtk_widget_grab_focus(dp
->currtreeitem
);
1768 void shortcut_add(struct Shortcuts
*scs
, GtkWidget
*labelw
,
1769 int chr
, int action
, void *ptr
)
1771 GtkLabel
*label
= GTK_LABEL(labelw
);
1772 gchar
*currstr
, *pattern
;
1775 if (chr
== NO_SHORTCUT
)
1778 chr
= tolower((unsigned char)chr
);
1780 assert(scs
->sc
[chr
].action
== SHORTCUT_EMPTY
);
1782 scs
->sc
[chr
].action
= action
;
1784 if (action
== SHORTCUT_FOCUS
) {
1785 scs
->sc
[chr
].uc
= NULL
;
1786 scs
->sc
[chr
].widget
= (GtkWidget
*)ptr
;
1788 scs
->sc
[chr
].widget
= NULL
;
1789 scs
->sc
[chr
].uc
= (struct uctrl
*)ptr
;
1792 gtk_label_get(label
, &currstr
);
1793 for (i
= 0; currstr
[i
]; i
++)
1794 if (tolower((unsigned char)currstr
[i
]) == chr
) {
1797 pattern
= dupprintf("%*s_", i
, "");
1799 gtk_widget_size_request(GTK_WIDGET(label
), &req
);
1800 gtk_label_set_pattern(label
, pattern
);
1801 gtk_widget_set_usize(GTK_WIDGET(label
), -1, req
.height
);
1808 int do_config_box(void)
1810 GtkWidget
*window
, *hbox
, *vbox
, *cols
, *label
,
1811 *tree
, *treescroll
, *panels
, *panelvbox
;
1812 int index
, level
, listitemheight
;
1813 struct controlbox
*ctrlbox
;
1815 GtkTreeItem
*treeitemlevels
[8];
1816 GtkTree
*treelevels
[8];
1820 struct Shortcuts scs
;
1822 struct selparam
*selparams
= NULL
;
1823 int nselparams
= 0, selparamsize
= 0;
1825 do_defaults(NULL
, &cfg
);
1830 GtkWidget
*listitem
= gtk_list_item_new_with_label("foo");
1832 gtk_widget_size_request(listitem
, &req
);
1833 listitemheight
= req
.height
;
1834 gtk_widget_unref(listitem
);
1839 for (index
= 0; index
< lenof(scs
.sc
); index
++) {
1840 scs
.sc
[index
].action
= SHORTCUT_EMPTY
;
1843 ctrlbox
= ctrl_new_box();
1844 setup_config_box(ctrlbox
, &sl
, FALSE
, 0);
1845 unix_setup_config_box(ctrlbox
, FALSE
);
1847 window
= gtk_dialog_new();
1848 hbox
= gtk_hbox_new(FALSE
, 4);
1849 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->vbox
), hbox
, TRUE
, TRUE
, 0);
1850 gtk_container_set_border_width(GTK_CONTAINER(hbox
), 10);
1851 gtk_widget_show(hbox
);
1852 vbox
= gtk_vbox_new(FALSE
, 4);
1853 gtk_box_pack_start(GTK_BOX(hbox
), vbox
, FALSE
, FALSE
, 0);
1854 gtk_widget_show(vbox
);
1855 cols
= columns_new(4);
1856 gtk_box_pack_start(GTK_BOX(vbox
), cols
, FALSE
, FALSE
, 0);
1857 gtk_widget_show(cols
);
1858 label
= gtk_label_new("Category:");
1859 columns_add(COLUMNS(cols
), label
, 0, 1);
1860 columns_force_left_align(COLUMNS(cols
), label
);
1861 gtk_widget_show(label
);
1862 treescroll
= gtk_scrolled_window_new(NULL
, NULL
);
1863 tree
= gtk_tree_new();
1864 gtk_signal_connect(GTK_OBJECT(tree
), "focus_in_event",
1865 GTK_SIGNAL_FUNC(widget_focus
), &dp
);
1866 shortcut_add(&scs
, label
, 'g', SHORTCUT_FOCUS
, tree
);
1867 gtk_tree_set_view_mode(GTK_TREE(tree
), GTK_TREE_VIEW_ITEM
);
1868 gtk_tree_set_selection_mode(GTK_TREE(tree
), GTK_SELECTION_BROWSE
);
1869 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(treescroll
),
1871 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll
),
1873 GTK_POLICY_AUTOMATIC
);
1874 gtk_signal_connect(GTK_OBJECT(tree
), "focus",
1875 GTK_SIGNAL_FUNC(tree_focus
), &dp
);
1876 gtk_widget_show(tree
);
1877 gtk_widget_show(treescroll
);
1878 gtk_box_pack_start(GTK_BOX(vbox
), treescroll
, TRUE
, TRUE
, 0);
1879 panels
= panels_new();
1880 gtk_box_pack_start(GTK_BOX(hbox
), panels
, TRUE
, TRUE
, 0);
1881 gtk_widget_show(panels
);
1886 for (index
= 0; index
< ctrlbox
->nctrlsets
; index
++) {
1887 struct controlset
*s
= ctrlbox
->ctrlsets
[index
];
1890 if (!*s
->pathname
) {
1891 w
= layout_ctrls(&dp
, &scs
, s
, listitemheight
, GTK_WINDOW(window
));
1892 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->action_area
),
1895 int j
= path ?
ctrl_path_compare(s
->pathname
, path
) : 0;
1896 if (j
!= INT_MAX
) { /* add to treeview, start new panel */
1898 GtkWidget
*treeitem
;
1902 * We expect never to find an implicit path
1903 * component. For example, we expect never to see
1904 * A/B/C followed by A/D/E, because that would
1905 * _implicitly_ create A/D. All our path prefixes
1906 * are expected to contain actual controls and be
1907 * selectable in the treeview; so we would expect
1908 * to see A/D _explicitly_ before encountering
1911 assert(j
== ctrl_path_elements(s
->pathname
) - 1);
1913 c
= strrchr(s
->pathname
, '/');
1919 treeitem
= gtk_tree_item_new_with_label(c
);
1920 assert(j
-1 < level
);
1922 if (!treelevels
[j
-1]) {
1923 treelevels
[j
-1] = GTK_TREE(gtk_tree_new());
1924 gtk_tree_item_set_subtree
1925 (treeitemlevels
[j
-1],
1926 GTK_WIDGET(treelevels
[j
-1]));
1927 gtk_tree_item_expand(treeitemlevels
[j
-1]);
1929 gtk_tree_append(treelevels
[j
-1], treeitem
);
1931 gtk_tree_append(GTK_TREE(tree
), treeitem
);
1933 treeitemlevels
[j
] = GTK_TREE_ITEM(treeitem
);
1934 treelevels
[j
] = NULL
;
1937 gtk_signal_connect(GTK_OBJECT(treeitem
), "key_press_event",
1938 GTK_SIGNAL_FUNC(tree_key_press
), &dp
);
1939 gtk_signal_connect(GTK_OBJECT(treeitem
), "focus_in_event",
1940 GTK_SIGNAL_FUNC(widget_focus
), &dp
);
1942 gtk_widget_show(treeitem
);
1946 first
= (panelvbox
== NULL
);
1948 panelvbox
= gtk_vbox_new(FALSE
, 4);
1949 gtk_container_add(GTK_CONTAINER(panels
), panelvbox
);
1951 panels_switch_to(PANELS(panels
), panelvbox
);
1952 gtk_tree_select_child(GTK_TREE(tree
), treeitem
);
1955 if (nselparams
>= selparamsize
) {
1957 selparams
= srealloc(selparams
,
1958 selparamsize
* sizeof(*selparams
));
1960 selparams
[nselparams
].dp
= &dp
;
1961 selparams
[nselparams
].panels
= PANELS(panels
);
1962 selparams
[nselparams
].panel
= panelvbox
;
1963 selparams
[nselparams
].shortcuts
= scs
; /* structure copy */
1964 selparams
[nselparams
].treeitem
= treeitem
;
1969 w
= layout_ctrls(&dp
,
1970 &selparams
[nselparams
-1].shortcuts
,
1971 s
, listitemheight
, NULL
);
1972 gtk_box_pack_start(GTK_BOX(panelvbox
), w
, FALSE
, FALSE
, 0);
1977 dp
.ntreeitems
= nselparams
;
1978 dp
.treeitems
= smalloc(dp
.ntreeitems
* sizeof(GtkWidget
*));
1980 for (index
= 0; index
< nselparams
; index
++) {
1981 gtk_signal_connect(GTK_OBJECT(selparams
[index
].treeitem
), "select",
1982 GTK_SIGNAL_FUNC(treeitem_sel
),
1984 dp
.treeitems
[index
] = selparams
[index
].treeitem
;
1988 dlg_refresh(NULL
, &dp
);
1990 dp
.shortcuts
= &selparams
[0].shortcuts
;
1991 dp
.currtreeitem
= dp
.treeitems
[0];
1992 dp
.lastfocus
= NULL
;
1995 gtk_widget_show(window
);
1998 * Set focus into the first available control.
2000 for (index
= 0; index
< ctrlbox
->nctrlsets
; index
++) {
2001 struct controlset
*s
= ctrlbox
->ctrlsets
[index
];
2006 for (j
= 0; j
< s
->ncontrols
; j
++)
2007 if (s
->ctrls
[j
]->generic
.type
!= CTRL_TABDELAY
&&
2008 s
->ctrls
[j
]->generic
.type
!= CTRL_COLUMNS
&&
2009 s
->ctrls
[j
]->generic
.type
!= CTRL_TEXT
) {
2010 dlg_set_focus(s
->ctrls
[j
], &dp
);
2011 dp
.lastfocus
= s
->ctrls
[j
];
2020 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
2021 GTK_SIGNAL_FUNC(window_destroy
), NULL
);
2022 gtk_signal_connect(GTK_OBJECT(window
), "key_press_event",
2023 GTK_SIGNAL_FUNC(win_key_press
), &dp
);
2033 /* ======================================================================
2034 * Below here is a stub main program which allows the dialog box
2035 * code to be compiled and tested with a minimal amount of the rest
2041 /* Compile command for testing:
2043 gcc -g -o gtkdlg gtk{dlg,cols,panel}.c ../{config,dialog,settings}.c \
2044 ../{misc,tree234,be_none}.c ux{store,misc,print,cfg}.c \
2045 -I. -I.. -I../charset -DTESTMODE `gtk-config --cflags --libs`
2048 void modalfatalbox(char *p
, ...)
2051 fprintf(stderr
, "FATAL ERROR: ");
2053 vfprintf(stderr
, p
, ap
);
2055 fputc('\n', stderr
);
2059 char *cp_name(int codepage
)
2061 return (codepage
== 123 ?
"testing123" :
2062 codepage
== 234 ?
"testing234" :
2063 codepage
== 345 ?
"testing345" :
2067 char *cp_enumerate(int index
)
2069 return (index
== 0 ?
"testing123" :
2070 index
== 1 ?
"testing234" :
2074 int decode_codepage(char *cp_name
)
2076 return (!strcmp(cp_name
, "testing123") ?
123 :
2077 !strcmp(cp_name
, "testing234") ?
234 :
2078 !strcmp(cp_name
, "testing345") ?
345 :
2082 struct printer_enum_tag
{ int dummy
; } printer_test
;
2084 printer_enum
*printer_start_enum(int *nprinters_ptr
) {
2086 return &printer_test
;
2088 char *printer_get_name(printer_enum
*pe
, int i
) {
2089 return (i
==0 ?
"lpr" : i
==1 ?
"lpr -Pfoobar" : NULL
);
2091 void printer_finish_enum(printer_enum
*pe
) { }
2093 char *platform_default_s(const char *name
)
2098 int platform_default_i(const char *name
, int def
)
2103 FontSpec
platform_default_fontspec(const char *name
)
2106 if (!strcmp(name
, "Font"))
2107 strcpy(ret
.name
, "fixed");
2113 Filename
platform_default_filename(const char *name
)
2116 if (!strcmp(name
, "LogFileName"))
2117 strcpy(ret
.path
, "putty.log");
2123 char *x_get_default(const char *key
)
2128 int main(int argc
, char **argv
)
2130 gtk_init(&argc
, &argv
);
2131 printf("returned %d\n", do_config_box());