2 * gtkdlg.c - GTK implementation of the PuTTY configuration box.
12 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
19 void *dlg_get_privdata(union control
*ctrl
, void *dlg
)
21 return NULL
; /* FIXME */
24 void dlg_set_privdata(union control
*ctrl
, void *dlg
, void *ptr
)
29 void *dlg_alloc_privdata(union control
*ctrl
, void *dlg
, size_t size
)
31 return NULL
; /* FIXME */
34 union control
*dlg_last_focused(void *dlg
)
36 return NULL
; /* FIXME */
39 void dlg_radiobutton_set(union control
*ctrl
, void *dlg
, int whichbutton
)
44 int dlg_radiobutton_get(union control
*ctrl
, void *dlg
)
49 void dlg_checkbox_set(union control
*ctrl
, void *dlg
, int checked
)
54 int dlg_checkbox_get(union control
*ctrl
, void *dlg
)
59 void dlg_editbox_set(union control
*ctrl
, void *dlg
, char const *text
)
64 void dlg_editbox_get(union control
*ctrl
, void *dlg
, char *buffer
, int length
)
69 /* The `listbox' functions can also apply to combo boxes. */
70 void dlg_listbox_clear(union control
*ctrl
, void *dlg
)
75 void dlg_listbox_del(union control
*ctrl
, void *dlg
, int index
)
80 void dlg_listbox_add(union control
*ctrl
, void *dlg
, char const *text
)
86 * Each listbox entry may have a numeric id associated with it.
87 * Note that some front ends only permit a string to be stored at
88 * each position, which means that _if_ you put two identical
89 * strings in any listbox then you MUST not assign them different
90 * IDs and expect to get meaningful results back.
92 void dlg_listbox_addwithindex(union control
*ctrl
, void *dlg
,
93 char const *text
, int id
)
98 int dlg_listbox_getid(union control
*ctrl
, void *dlg
, int index
)
100 return -1; /* FIXME */
103 /* dlg_listbox_index returns <0 if no single element is selected. */
104 int dlg_listbox_index(union control
*ctrl
, void *dlg
)
106 return -1; /* FIXME */
109 int dlg_listbox_issel(union control
*ctrl
, void *dlg
, int index
)
111 return 0; /* FIXME */
114 void dlg_listbox_select(union control
*ctrl
, void *dlg
, int index
)
119 void dlg_text_set(union control
*ctrl
, void *dlg
, char const *text
)
124 void dlg_filesel_set(union control
*ctrl
, void *dlg
, Filename fn
)
129 void dlg_filesel_get(union control
*ctrl
, void *dlg
, Filename
*fn
)
134 void dlg_fontsel_set(union control
*ctrl
, void *dlg
, FontSpec fs
)
139 void dlg_fontsel_get(union control
*ctrl
, void *dlg
, FontSpec
*fs
)
145 * Bracketing a large set of updates in these two functions will
146 * cause the front end (if possible) to delay updating the screen
147 * until it's all complete, thus avoiding flicker.
149 void dlg_update_start(union control
*ctrl
, void *dlg
)
154 void dlg_update_done(union control
*ctrl
, void *dlg
)
159 void dlg_set_focus(union control
*ctrl
, void *dlg
)
165 * During event processing, you might well want to give an error
166 * indication to the user. dlg_beep() is a quick and easy generic
167 * error; dlg_error() puts up a message-box or equivalent.
169 void dlg_beep(void *dlg
)
174 void dlg_error_msg(void *dlg
, char *msg
)
180 * This function signals to the front end that the dialog's
181 * processing is completed, and passes an integer value (typically
184 void dlg_end(void *dlg
, int value
)
189 void dlg_refresh(union control
*ctrl
, void *dlg
)
194 void dlg_coloursel_start(union control
*ctrl
, void *dlg
, int r
, int g
, int b
)
199 int dlg_coloursel_results(union control
*ctrl
, void *dlg
,
200 int *r
, int *g
, int *b
)
202 return 0; /* FIXME */
206 * This function does the main layout work: it reads a controlset,
207 * it creates the relevant GTK controls, and returns a GtkWidget
208 * containing the result. (This widget might be a title of some
209 * sort, it might be a Columns containing many controls, or it
210 * might be a GtkFrame containing a Columns; whatever it is, it's
211 * definitely a GtkWidget and should probably be added to a
214 GtkWidget
*layout_ctrls(struct controlset
*s
)
220 if (!s
->boxname
&& s
->boxtitle
) {
221 /* This controlset is a panel title. */
222 return gtk_label_new(s
->boxtitle
);
226 * Otherwise, we expect to be laying out actual controls, so
227 * we'll start by creating a Columns for the purpose.
229 cols
= COLUMNS(columns_new(4));
230 ret
= GTK_WIDGET(cols
);
231 gtk_widget_show(ret
);
234 * Create a containing frame if we have a box name.
237 ret
= gtk_frame_new(s
->boxtitle
); /* NULL is valid here */
238 gtk_container_set_border_width(GTK_CONTAINER(cols
), 4);
239 gtk_container_add(GTK_CONTAINER(ret
), GTK_WIDGET(cols
));
240 gtk_widget_show(ret
);
244 * Now iterate through the controls themselves, create them,
245 * and add them to the Columns.
247 for (i
= 0; i
< s
->ncontrols
; i
++) {
248 union control
*ctrl
= s
->ctrls
[i
];
251 switch (ctrl
->generic
.type
) {
254 static const int simplecols
[1] = { 100 };
255 columns_set_cols(cols
, ctrl
->columns
.ncols
,
256 (ctrl
->columns
.percentages ?
257 ctrl
->columns
.percentages
: simplecols
));
259 continue; /* no actual control created */
261 /* FIXME: we can do columns_taborder_last easily enough, but
262 * we need to be able to remember which GtkWidget(s) correspond
263 * to ctrl->tabdelay.ctrl. */
264 continue; /* no actual control created */
266 w
= gtk_button_new_with_label(ctrl
->generic
.label
);
269 w
= gtk_check_button_new_with_label(ctrl
->generic
.label
);
273 * Radio buttons get to go inside their own Columns, no
277 gint i
, *percentages
;
281 if (ctrl
->generic
.label
) {
282 GtkWidget
*label
= gtk_label_new(ctrl
->generic
.label
);
283 columns_add(COLUMNS(w
), label
, 0, 1);
284 columns_force_left_align(COLUMNS(w
), label
);
285 gtk_widget_show(label
);
287 percentages
= g_new(gint
, ctrl
->radio
.ncolumns
);
288 for (i
= 0; i
< ctrl
->radio
.ncolumns
; i
++) {
290 ((100 * (i
+1) / ctrl
->radio
.ncolumns
) -
291 100 * i
/ ctrl
->radio
.ncolumns
);
293 columns_set_cols(COLUMNS(w
), ctrl
->radio
.ncolumns
,
297 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++) {
301 b
= (gtk_radio_button_new_with_label
302 (group
, ctrl
->radio
.buttons
[i
]));
303 group
= gtk_radio_button_group(GTK_RADIO_BUTTON(b
));
304 colstart
= i
% ctrl
->radio
.ncolumns
;
305 columns_add(COLUMNS(w
), b
, colstart
,
306 (i
== ctrl
->radio
.nbuttons
-1 ?
307 ctrl
->radio
.ncolumns
- colstart
: 1));
313 if (ctrl
->editbox
.has_list
) {
317 if (ctrl
->editbox
.password
)
318 gtk_entry_set_visibility(GTK_ENTRY(w
), FALSE
);
321 * Edit boxes, for some strange reason, have a minimum
322 * width of 150 in GTK 1.2. We don't want this - we'd
323 * rather the edit boxes acquired their natural width
324 * from the column layout of the rest of the box.
328 gtk_widget_size_request(w
, &req
);
329 gtk_widget_set_usize(w
, 10, req
.height
);
331 if (ctrl
->generic
.label
) {
332 GtkWidget
*label
, *container
;
334 label
= gtk_label_new(ctrl
->generic
.label
);
336 container
= columns_new(4);
337 if (ctrl
->editbox
.percentwidth
== 100) {
338 columns_add(COLUMNS(container
), label
, 0, 1);
339 columns_force_left_align(COLUMNS(container
), label
);
340 columns_add(COLUMNS(container
), w
, 0, 1);
343 percentages
[1] = ctrl
->editbox
.percentwidth
;
344 percentages
[0] = 100 - ctrl
->editbox
.percentwidth
;
345 columns_set_cols(COLUMNS(container
), 2, percentages
);
346 columns_add(COLUMNS(container
), label
, 0, 1);
347 columns_force_left_align(COLUMNS(container
), label
);
348 columns_add(COLUMNS(container
), w
, 1, 1);
350 gtk_widget_show(label
);
357 w
= gtk_label_new(ctrl
->generic
.label
);
358 gtk_label_set_line_wrap(GTK_LABEL(w
), TRUE
);
359 gtk_label_set_justify(GTK_LABEL(w
), GTK_JUSTIFY_FILL
);
364 COLUMN_START(ctrl
->generic
.column
),
365 COLUMN_SPAN(ctrl
->generic
.column
));
375 GtkWidget
*panel
, *treeitem
;
378 static void treeitem_sel(GtkItem
*item
, gpointer data
)
380 struct selparam
*sp
= (struct selparam
*)data
;
382 panels_switch_to(sp
->panels
, sp
->panel
);
385 void destroy(GtkWidget
*widget
, gpointer data
)
390 void do_config_box(void)
392 GtkWidget
*window
, *hbox
, *vbox
, *cols
, *label
,
393 *tree
, *treescroll
, *panels
, *panelvbox
;
395 struct controlbox
*ctrlbox
;
397 GtkTreeItem
*treeitemlevels
[8];
398 GtkTree
*treelevels
[8];
401 struct selparam
*selparams
= NULL
;
402 int nselparams
= 0, selparamsize
= 0;
404 do_defaults(NULL
, &cfg
);
406 ctrlbox
= ctrl_new_box();
407 setup_config_box(ctrlbox
, NULL
, FALSE
, 0);
409 window
= gtk_dialog_new();
410 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(window
)->vbox
), 4);
411 hbox
= gtk_hbox_new(FALSE
, 4);
412 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->vbox
), hbox
, TRUE
, TRUE
, 0);
413 gtk_widget_show(hbox
);
414 vbox
= gtk_vbox_new(FALSE
, 4);
415 gtk_box_pack_start(GTK_BOX(hbox
), vbox
, FALSE
, FALSE
, 0);
416 gtk_widget_show(vbox
);
417 cols
= columns_new(4);
418 gtk_box_pack_start(GTK_BOX(vbox
), cols
, FALSE
, FALSE
, 0);
419 gtk_widget_show(cols
);
420 label
= gtk_label_new("Category:");
421 columns_add(COLUMNS(cols
), label
, 0, 1);
422 columns_force_left_align(COLUMNS(cols
), label
);
423 gtk_widget_show(label
);
424 treescroll
= gtk_scrolled_window_new(NULL
, NULL
);
425 tree
= gtk_tree_new();
426 gtk_tree_set_view_mode(GTK_TREE(tree
), GTK_TREE_VIEW_ITEM
);
427 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(treescroll
),
429 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll
),
431 GTK_POLICY_AUTOMATIC
);
432 gtk_widget_show(tree
);
433 gtk_widget_show(treescroll
);
434 gtk_box_pack_start(GTK_BOX(vbox
), treescroll
, TRUE
, TRUE
, 0);
435 panels
= panels_new();
436 gtk_box_pack_start(GTK_BOX(hbox
), panels
, TRUE
, TRUE
, 0);
437 gtk_widget_show(panels
);
442 for (index
= 0; index
< ctrlbox
->nctrlsets
; index
++) {
443 struct controlset
*s
= ctrlbox
->ctrlsets
[index
];
444 GtkWidget
*w
= layout_ctrls(s
);
447 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->action_area
),
450 int j
= path ?
ctrl_path_compare(s
->pathname
, path
) : 0;
451 if (j
!= INT_MAX
) { /* add to treeview, start new panel */
457 * We expect never to find an implicit path
458 * component. For example, we expect never to see
459 * A/B/C followed by A/D/E, because that would
460 * _implicitly_ create A/D. All our path prefixes
461 * are expected to contain actual controls and be
462 * selectable in the treeview; so we would expect
463 * to see A/D _explicitly_ before encountering
466 assert(j
== ctrl_path_elements(s
->pathname
) - 1);
468 c
= strrchr(s
->pathname
, '/');
474 treeitem
= gtk_tree_item_new_with_label(c
);
477 if (!treelevels
[j
-1]) {
478 treelevels
[j
-1] = GTK_TREE(gtk_tree_new());
479 gtk_tree_item_set_subtree
480 (treeitemlevels
[j
-1],
481 GTK_WIDGET(treelevels
[j
-1]));
482 gtk_tree_item_expand(treeitemlevels
[j
-1]);
484 gtk_tree_append(treelevels
[j
-1], treeitem
);
486 gtk_tree_append(GTK_TREE(tree
), treeitem
);
488 treeitemlevels
[j
] = GTK_TREE_ITEM(treeitem
);
489 treelevels
[j
] = NULL
;
492 gtk_widget_show(treeitem
);
496 first
= (panelvbox
== NULL
);
498 panelvbox
= gtk_vbox_new(FALSE
, 4);
499 gtk_container_add(GTK_CONTAINER(panels
), panelvbox
);
501 panels_switch_to(PANELS(panels
), panelvbox
);
502 gtk_tree_select_child(GTK_TREE(tree
), treeitem
);
505 if (nselparams
>= selparamsize
) {
507 selparams
= srealloc(selparams
,
508 selparamsize
* sizeof(*selparams
));
510 selparams
[nselparams
].panels
= PANELS(panels
);
511 selparams
[nselparams
].panel
= panelvbox
;
512 selparams
[nselparams
].treeitem
= treeitem
;
517 gtk_box_pack_start(GTK_BOX(panelvbox
), w
, FALSE
, FALSE
, 0);
521 for (index
= 0; index
< nselparams
; index
++) {
522 gtk_signal_connect(GTK_OBJECT(selparams
[index
].treeitem
), "select",
523 GTK_SIGNAL_FUNC(treeitem_sel
),
527 gtk_widget_show(window
);
529 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
530 GTK_SIGNAL_FUNC(destroy
), NULL
);
537 /* ======================================================================
538 * Below here is a stub main program which allows the dialog box
539 * code to be compiled and tested with a minimal amount of the rest
545 /* Compile command for testing:
547 gcc -o gtkdlg gtk{dlg,cols,panel}.c ../{config,dialog,settings}.c \
548 ../{misc,tree234,be_none}.c ux{store,misc,print}.c \
549 -I. -I.. -I../charset -DTESTMODE `gtk-config --cflags --libs`
552 void modalfatalbox(char *p
, ...)
555 fprintf(stderr
, "FATAL ERROR: ");
557 vfprintf(stderr
, p
, ap
);
563 char *cp_name(int codepage
)
565 return (codepage
== 123 ?
"testing123" :
566 codepage
== 234 ?
"testing234" :
567 codepage
== 345 ?
"testing345" :
571 char *cp_enumerate(int index
)
573 return (index
== 0 ?
"testing123" :
574 index
== 1 ?
"testing234" :
578 int decode_codepage(char *cp_name
)
580 return (!strcmp(cp_name
, "testing123") ?
123 :
581 !strcmp(cp_name
, "testing234") ?
234 :
582 !strcmp(cp_name
, "testing345") ?
345 :
586 struct printer_enum_tag
{ int dummy
; } printer_test
;
588 printer_enum
*printer_start_enum(int *nprinters_ptr
) {
590 return &printer_test
;
592 char *printer_get_name(printer_enum
*pe
, int i
) {
593 return (i
==0 ?
"lpr" : i
==1 ?
"lpr -Pfoobar" : NULL
);
595 void printer_finish_enum(printer_enum
*pe
) { }
597 char *platform_default_s(const char *name
)
602 int platform_default_i(const char *name
, int def
)
607 FontSpec
platform_default_fontspec(const char *name
)
614 Filename
platform_default_filename(const char *name
)
621 char *x_get_default(const char *key
)
626 int main(int argc
, char **argv
)
628 gtk_init(&argc
, &argv
);