d9b15094 |
1 | /* |
2 | * gtkdlg.c - GTK implementation of the PuTTY configuration box. |
3 | */ |
4 | |
1d0d4a3b |
5 | /* |
6 | * TODO: |
7 | * |
8 | * - event handling, in general! |
9 | * |
10 | * - keyboard stuff |
11 | * + accelerators |
12 | * + tab order |
13 | * + default button |
14 | * |
15 | * - cosmetics: |
16 | * + can't we _somehow_ have less leading between radio buttons? |
17 | * + wrapping text widgets, the horror, the horror |
18 | * + labels and their associated edit boxes don't line up |
19 | * properly |
20 | */ |
21 | |
d9b15094 |
22 | #include <assert.h> |
23 | #include <gtk/gtk.h> |
24 | |
25 | #include "gtkcols.h" |
26 | #include "gtkpanel.h" |
27 | |
28 | #ifdef TESTMODE |
29 | #define PUTTY_DO_GLOBALS /* actually _define_ globals */ |
30 | #endif |
31 | |
32 | #include "putty.h" |
33 | #include "dialog.h" |
34 | |
d9b15094 |
35 | void *dlg_get_privdata(union control *ctrl, void *dlg) |
36 | { |
37 | return NULL; /* FIXME */ |
38 | } |
39 | |
40 | void dlg_set_privdata(union control *ctrl, void *dlg, void *ptr) |
41 | { |
42 | /* FIXME */ |
43 | } |
44 | |
45 | void *dlg_alloc_privdata(union control *ctrl, void *dlg, size_t size) |
46 | { |
47 | return NULL; /* FIXME */ |
48 | } |
49 | |
50 | union control *dlg_last_focused(void *dlg) |
51 | { |
52 | return NULL; /* FIXME */ |
53 | } |
54 | |
55 | void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton) |
56 | { |
57 | /* FIXME */ |
58 | } |
59 | |
60 | int dlg_radiobutton_get(union control *ctrl, void *dlg) |
61 | { |
62 | return 0; /* FIXME */ |
63 | } |
64 | |
65 | void dlg_checkbox_set(union control *ctrl, void *dlg, int checked) |
66 | { |
67 | /* FIXME */ |
68 | } |
69 | |
70 | int dlg_checkbox_get(union control *ctrl, void *dlg) |
71 | { |
72 | return 0; /* FIXME */ |
73 | } |
74 | |
75 | void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) |
76 | { |
77 | /* FIXME */ |
78 | } |
79 | |
80 | void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length) |
81 | { |
82 | /* FIXME */ |
83 | } |
84 | |
85 | /* The `listbox' functions can also apply to combo boxes. */ |
86 | void dlg_listbox_clear(union control *ctrl, void *dlg) |
87 | { |
88 | /* FIXME */ |
89 | } |
90 | |
91 | void dlg_listbox_del(union control *ctrl, void *dlg, int index) |
92 | { |
93 | /* FIXME */ |
94 | } |
95 | |
96 | void dlg_listbox_add(union control *ctrl, void *dlg, char const *text) |
97 | { |
98 | /* FIXME */ |
99 | } |
100 | |
101 | /* |
102 | * Each listbox entry may have a numeric id associated with it. |
103 | * Note that some front ends only permit a string to be stored at |
104 | * each position, which means that _if_ you put two identical |
105 | * strings in any listbox then you MUST not assign them different |
106 | * IDs and expect to get meaningful results back. |
107 | */ |
108 | void dlg_listbox_addwithindex(union control *ctrl, void *dlg, |
109 | char const *text, int id) |
110 | { |
111 | /* FIXME */ |
112 | } |
113 | |
114 | int dlg_listbox_getid(union control *ctrl, void *dlg, int index) |
115 | { |
116 | return -1; /* FIXME */ |
117 | } |
118 | |
119 | /* dlg_listbox_index returns <0 if no single element is selected. */ |
120 | int dlg_listbox_index(union control *ctrl, void *dlg) |
121 | { |
122 | return -1; /* FIXME */ |
123 | } |
124 | |
125 | int dlg_listbox_issel(union control *ctrl, void *dlg, int index) |
126 | { |
127 | return 0; /* FIXME */ |
128 | } |
129 | |
130 | void dlg_listbox_select(union control *ctrl, void *dlg, int index) |
131 | { |
132 | /* FIXME */ |
133 | } |
134 | |
135 | void dlg_text_set(union control *ctrl, void *dlg, char const *text) |
136 | { |
137 | /* FIXME */ |
138 | } |
139 | |
140 | void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn) |
141 | { |
142 | /* FIXME */ |
143 | } |
144 | |
145 | void dlg_filesel_get(union control *ctrl, void *dlg, Filename *fn) |
146 | { |
147 | /* FIXME */ |
148 | } |
149 | |
150 | void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec fs) |
151 | { |
152 | /* FIXME */ |
153 | } |
154 | |
155 | void dlg_fontsel_get(union control *ctrl, void *dlg, FontSpec *fs) |
156 | { |
157 | /* FIXME */ |
158 | } |
159 | |
160 | /* |
161 | * Bracketing a large set of updates in these two functions will |
162 | * cause the front end (if possible) to delay updating the screen |
163 | * until it's all complete, thus avoiding flicker. |
164 | */ |
165 | void dlg_update_start(union control *ctrl, void *dlg) |
166 | { |
167 | /* FIXME */ |
168 | } |
169 | |
170 | void dlg_update_done(union control *ctrl, void *dlg) |
171 | { |
172 | /* FIXME */ |
173 | } |
174 | |
175 | void dlg_set_focus(union control *ctrl, void *dlg) |
176 | { |
177 | /* FIXME */ |
178 | } |
179 | |
180 | /* |
181 | * During event processing, you might well want to give an error |
182 | * indication to the user. dlg_beep() is a quick and easy generic |
183 | * error; dlg_error() puts up a message-box or equivalent. |
184 | */ |
185 | void dlg_beep(void *dlg) |
186 | { |
187 | /* FIXME */ |
188 | } |
189 | |
190 | void dlg_error_msg(void *dlg, char *msg) |
191 | { |
192 | /* FIXME */ |
193 | } |
194 | |
195 | /* |
196 | * This function signals to the front end that the dialog's |
197 | * processing is completed, and passes an integer value (typically |
198 | * a success status). |
199 | */ |
200 | void dlg_end(void *dlg, int value) |
201 | { |
202 | /* FIXME */ |
203 | } |
204 | |
205 | void dlg_refresh(union control *ctrl, void *dlg) |
206 | { |
207 | /* FIXME */ |
208 | } |
209 | |
210 | void dlg_coloursel_start(union control *ctrl, void *dlg, int r, int g, int b) |
211 | { |
212 | /* FIXME */ |
213 | } |
214 | |
215 | int dlg_coloursel_results(union control *ctrl, void *dlg, |
216 | int *r, int *g, int *b) |
217 | { |
218 | return 0; /* FIXME */ |
219 | } |
220 | |
221 | /* |
222 | * This function does the main layout work: it reads a controlset, |
223 | * it creates the relevant GTK controls, and returns a GtkWidget |
224 | * containing the result. (This widget might be a title of some |
225 | * sort, it might be a Columns containing many controls, or it |
226 | * might be a GtkFrame containing a Columns; whatever it is, it's |
227 | * definitely a GtkWidget and should probably be added to a |
228 | * GtkVbox.) |
229 | */ |
1d0d4a3b |
230 | GtkWidget *layout_ctrls(struct controlset *s, int listitemheight) |
d9b15094 |
231 | { |
232 | Columns *cols; |
233 | GtkWidget *ret; |
234 | int i; |
235 | |
236 | if (!s->boxname && s->boxtitle) { |
237 | /* This controlset is a panel title. */ |
238 | return gtk_label_new(s->boxtitle); |
239 | } |
240 | |
241 | /* |
242 | * Otherwise, we expect to be laying out actual controls, so |
243 | * we'll start by creating a Columns for the purpose. |
244 | */ |
245 | cols = COLUMNS(columns_new(4)); |
246 | ret = GTK_WIDGET(cols); |
247 | gtk_widget_show(ret); |
248 | |
249 | /* |
250 | * Create a containing frame if we have a box name. |
251 | */ |
252 | if (*s->boxname) { |
253 | ret = gtk_frame_new(s->boxtitle); /* NULL is valid here */ |
254 | gtk_container_set_border_width(GTK_CONTAINER(cols), 4); |
255 | gtk_container_add(GTK_CONTAINER(ret), GTK_WIDGET(cols)); |
256 | gtk_widget_show(ret); |
257 | } |
258 | |
259 | /* |
260 | * Now iterate through the controls themselves, create them, |
261 | * and add them to the Columns. |
262 | */ |
263 | for (i = 0; i < s->ncontrols; i++) { |
264 | union control *ctrl = s->ctrls[i]; |
265 | GtkWidget *w = NULL; |
266 | |
267 | switch (ctrl->generic.type) { |
268 | case CTRL_COLUMNS: |
269 | { |
270 | static const int simplecols[1] = { 100 }; |
271 | columns_set_cols(cols, ctrl->columns.ncols, |
272 | (ctrl->columns.percentages ? |
273 | ctrl->columns.percentages : simplecols)); |
274 | } |
275 | continue; /* no actual control created */ |
276 | case CTRL_TABDELAY: |
277 | /* FIXME: we can do columns_taborder_last easily enough, but |
278 | * we need to be able to remember which GtkWidget(s) correspond |
279 | * to ctrl->tabdelay.ctrl. */ |
280 | continue; /* no actual control created */ |
281 | case CTRL_BUTTON: |
282 | w = gtk_button_new_with_label(ctrl->generic.label); |
283 | break; |
284 | case CTRL_CHECKBOX: |
285 | w = gtk_check_button_new_with_label(ctrl->generic.label); |
286 | break; |
287 | case CTRL_RADIO: |
288 | /* |
289 | * Radio buttons get to go inside their own Columns, no |
290 | * matter what. |
291 | */ |
292 | { |
293 | gint i, *percentages; |
294 | GSList *group; |
295 | |
296 | w = columns_new(1); |
297 | if (ctrl->generic.label) { |
298 | GtkWidget *label = gtk_label_new(ctrl->generic.label); |
299 | columns_add(COLUMNS(w), label, 0, 1); |
300 | columns_force_left_align(COLUMNS(w), label); |
301 | gtk_widget_show(label); |
302 | } |
303 | percentages = g_new(gint, ctrl->radio.ncolumns); |
304 | for (i = 0; i < ctrl->radio.ncolumns; i++) { |
305 | percentages[i] = |
306 | ((100 * (i+1) / ctrl->radio.ncolumns) - |
307 | 100 * i / ctrl->radio.ncolumns); |
308 | } |
309 | columns_set_cols(COLUMNS(w), ctrl->radio.ncolumns, |
310 | percentages); |
311 | g_free(percentages); |
312 | group = NULL; |
313 | for (i = 0; i < ctrl->radio.nbuttons; i++) { |
314 | GtkWidget *b; |
315 | gint colstart; |
316 | |
317 | b = (gtk_radio_button_new_with_label |
318 | (group, ctrl->radio.buttons[i])); |
319 | group = gtk_radio_button_group(GTK_RADIO_BUTTON(b)); |
320 | colstart = i % ctrl->radio.ncolumns; |
321 | columns_add(COLUMNS(w), b, colstart, |
322 | (i == ctrl->radio.nbuttons-1 ? |
323 | ctrl->radio.ncolumns - colstart : 1)); |
324 | gtk_widget_show(b); |
325 | } |
326 | } |
327 | break; |
328 | case CTRL_EDITBOX: |
329 | if (ctrl->editbox.has_list) { |
330 | w = gtk_combo_new(); |
331 | } else { |
332 | w = gtk_entry_new(); |
333 | if (ctrl->editbox.password) |
334 | gtk_entry_set_visibility(GTK_ENTRY(w), FALSE); |
335 | } |
336 | /* |
337 | * Edit boxes, for some strange reason, have a minimum |
338 | * width of 150 in GTK 1.2. We don't want this - we'd |
339 | * rather the edit boxes acquired their natural width |
340 | * from the column layout of the rest of the box. |
341 | */ |
342 | { |
343 | GtkRequisition req; |
344 | gtk_widget_size_request(w, &req); |
345 | gtk_widget_set_usize(w, 10, req.height); |
346 | } |
347 | if (ctrl->generic.label) { |
348 | GtkWidget *label, *container; |
349 | |
350 | label = gtk_label_new(ctrl->generic.label); |
351 | |
352 | container = columns_new(4); |
353 | if (ctrl->editbox.percentwidth == 100) { |
354 | columns_add(COLUMNS(container), label, 0, 1); |
355 | columns_force_left_align(COLUMNS(container), label); |
356 | columns_add(COLUMNS(container), w, 0, 1); |
357 | } else { |
358 | gint percentages[2]; |
359 | percentages[1] = ctrl->editbox.percentwidth; |
360 | percentages[0] = 100 - ctrl->editbox.percentwidth; |
361 | columns_set_cols(COLUMNS(container), 2, percentages); |
362 | columns_add(COLUMNS(container), label, 0, 1); |
363 | columns_force_left_align(COLUMNS(container), label); |
364 | columns_add(COLUMNS(container), w, 1, 1); |
365 | } |
366 | gtk_widget_show(label); |
367 | gtk_widget_show(w); |
368 | |
369 | w = container; |
370 | } |
371 | break; |
1d0d4a3b |
372 | case CTRL_FILESELECT: |
373 | case CTRL_FONTSELECT: |
374 | { |
375 | GtkWidget *ww; |
376 | GtkRequisition req; |
377 | char *browsebtn = |
378 | (ctrl->generic.type == CTRL_FILESELECT ? |
379 | "Browse..." : "Change..."); |
380 | |
381 | gint percentages[] = { 75, 25 }; |
382 | w = columns_new(4); |
383 | columns_set_cols(COLUMNS(w), 2, percentages); |
384 | |
385 | if (ctrl->generic.label) { |
386 | ww = gtk_label_new(ctrl->generic.label); |
387 | columns_add(COLUMNS(w), ww, 0, 2); |
388 | columns_force_left_align(COLUMNS(w), ww); |
389 | gtk_widget_show(ww); |
390 | } |
391 | |
392 | ww = gtk_entry_new(); |
393 | gtk_widget_size_request(ww, &req); |
394 | gtk_widget_set_usize(ww, 10, req.height); |
395 | columns_add(COLUMNS(w), ww, 0, 1); |
396 | gtk_widget_show(ww); |
397 | |
398 | ww = gtk_button_new_with_label(browsebtn); |
399 | columns_add(COLUMNS(w), ww, 1, 1); |
400 | gtk_widget_show(ww); |
401 | } |
402 | break; |
403 | case CTRL_LISTBOX: |
404 | if (ctrl->listbox.height == 0) { |
405 | w = gtk_option_menu_new(); |
406 | } else { |
407 | GtkWidget *list; |
408 | list = gtk_list_new(); |
409 | gtk_list_set_selection_mode(GTK_LIST(list), |
410 | (ctrl->listbox.multisel ? |
411 | GTK_SELECTION_MULTIPLE : |
412 | GTK_SELECTION_SINGLE)); |
413 | w = gtk_scrolled_window_new(NULL, NULL); |
414 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(w), |
415 | list); |
416 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), |
417 | GTK_POLICY_NEVER, |
418 | GTK_POLICY_AUTOMATIC); |
419 | gtk_widget_show(list); |
420 | |
421 | /* |
422 | * Adjust the height of the scrolled window to the |
423 | * minimum given by the height parameter. |
424 | * |
425 | * This piece of guesswork is a horrid hack based |
426 | * on looking inside the GTK 1.2 sources |
427 | * (specifically gtkviewport.c, which appears to be |
428 | * the widget which provides the border around the |
429 | * scrolling area). Anyone lets me know how I can |
430 | * do this in a way which isn't at risk from GTK |
431 | * upgrades, I'd be grateful. |
432 | */ |
433 | { |
434 | int edge = GTK_WIDGET(list)->style->klass->ythickness; |
435 | gtk_widget_set_usize(w, 10, |
436 | 2*edge + (ctrl->listbox.height * |
437 | listitemheight)); |
438 | } |
439 | #if 1 |
440 | /* here is an example of a percentage-based tabbed list item */ |
441 | { int i; for (i=0; i<10; i++) { |
442 | GtkWidget *listitem = gtk_list_item_new(); |
443 | GtkWidget *cols = columns_new(4); |
444 | GtkWidget *label1 = gtk_label_new("left"); |
445 | GtkWidget *label2 = gtk_label_new("right"); |
446 | GList *itemlist; |
447 | static const gint percents[] = { 50, 50 }; |
448 | columns_set_cols(COLUMNS(cols), 2, percents); |
449 | columns_add(COLUMNS(cols), label1, 0, 1); |
450 | columns_force_left_align(COLUMNS(cols), label1); |
451 | columns_add(COLUMNS(cols), label2, 1, 1); |
452 | columns_force_left_align(COLUMNS(cols), label2); |
453 | gtk_widget_show(label1); |
454 | gtk_widget_show(label2); |
455 | gtk_widget_show(cols); |
456 | gtk_container_add(GTK_CONTAINER(listitem), cols); |
457 | itemlist = g_list_append(NULL, listitem); |
458 | gtk_list_append_items(GTK_LIST(list), itemlist); |
459 | gtk_widget_show(listitem); |
460 | } } |
461 | #endif |
462 | |
463 | if (ctrl->listbox.draglist) { |
464 | /* |
465 | * GTK doesn't appear to make it easy to |
466 | * implement a proper draggable list; so |
467 | * instead I'm just going to have to put an Up |
468 | * and a Down button to the right of the actual |
469 | * list box. Ah well. |
470 | */ |
471 | GtkWidget *cols, *button; |
472 | static const gint percentages[2] = { 80, 20 }; |
473 | |
474 | cols = columns_new(4); |
475 | columns_set_cols(COLUMNS(cols), 2, percentages); |
476 | columns_add(COLUMNS(cols), w, 0, 1); |
477 | gtk_widget_show(w); |
478 | button = gtk_button_new_with_label("Up"); |
479 | columns_add(COLUMNS(cols), button, 1, 1); |
480 | gtk_widget_show(button); |
481 | button = gtk_button_new_with_label("Down"); |
482 | columns_add(COLUMNS(cols), button, 1, 1); |
483 | gtk_widget_show(button); |
484 | |
485 | w = cols; |
486 | } |
487 | |
488 | } |
489 | if (ctrl->generic.label) { |
490 | GtkWidget *label, *container; |
491 | |
492 | label = gtk_label_new(ctrl->generic.label); |
493 | |
494 | container = columns_new(4); |
495 | if (ctrl->listbox.percentwidth == 100) { |
496 | columns_add(COLUMNS(container), label, 0, 1); |
497 | columns_force_left_align(COLUMNS(container), label); |
498 | columns_add(COLUMNS(container), w, 0, 1); |
499 | } else { |
500 | gint percentages[2]; |
501 | percentages[1] = ctrl->listbox.percentwidth; |
502 | percentages[0] = 100 - ctrl->listbox.percentwidth; |
503 | columns_set_cols(COLUMNS(container), 2, percentages); |
504 | columns_add(COLUMNS(container), label, 0, 1); |
505 | columns_force_left_align(COLUMNS(container), label); |
506 | columns_add(COLUMNS(container), w, 1, 1); |
507 | } |
508 | gtk_widget_show(label); |
509 | gtk_widget_show(w); |
510 | |
511 | w = container; |
512 | } |
513 | break; |
d9b15094 |
514 | case CTRL_TEXT: |
515 | w = gtk_label_new(ctrl->generic.label); |
516 | gtk_label_set_line_wrap(GTK_LABEL(w), TRUE); |
1d0d4a3b |
517 | /* FIXME: deal with wrapping! */ |
d9b15094 |
518 | break; |
519 | } |
520 | if (w) { |
521 | columns_add(cols, w, |
522 | COLUMN_START(ctrl->generic.column), |
523 | COLUMN_SPAN(ctrl->generic.column)); |
524 | gtk_widget_show(w); |
525 | } |
526 | } |
527 | |
528 | return ret; |
529 | } |
530 | |
531 | struct selparam { |
532 | Panels *panels; |
533 | GtkWidget *panel, *treeitem; |
534 | }; |
535 | |
536 | static void treeitem_sel(GtkItem *item, gpointer data) |
537 | { |
538 | struct selparam *sp = (struct selparam *)data; |
539 | |
540 | panels_switch_to(sp->panels, sp->panel); |
541 | } |
542 | |
543 | void destroy(GtkWidget *widget, gpointer data) |
544 | { |
545 | gtk_main_quit(); |
546 | } |
547 | |
548 | void do_config_box(void) |
549 | { |
550 | GtkWidget *window, *hbox, *vbox, *cols, *label, |
551 | *tree, *treescroll, *panels, *panelvbox; |
1d0d4a3b |
552 | int index, level, listitemheight; |
d9b15094 |
553 | struct controlbox *ctrlbox; |
554 | char *path; |
555 | GtkTreeItem *treeitemlevels[8]; |
556 | GtkTree *treelevels[8]; |
557 | Config cfg; |
558 | |
559 | struct selparam *selparams = NULL; |
560 | int nselparams = 0, selparamsize = 0; |
561 | |
562 | do_defaults(NULL, &cfg); |
563 | |
1d0d4a3b |
564 | { |
565 | GtkWidget *listitem = gtk_list_item_new_with_label("foo"); |
566 | GtkRequisition req; |
567 | gtk_widget_size_request(listitem, &req); |
568 | listitemheight = req.height; |
569 | gtk_widget_unref(listitem); |
570 | } |
571 | |
d9b15094 |
572 | ctrlbox = ctrl_new_box(); |
573 | setup_config_box(ctrlbox, NULL, FALSE, 0); |
1d0d4a3b |
574 | unix_setup_config_box(ctrlbox, FALSE); |
d9b15094 |
575 | |
576 | window = gtk_dialog_new(); |
d9b15094 |
577 | hbox = gtk_hbox_new(FALSE, 4); |
578 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), hbox, TRUE, TRUE, 0); |
1d0d4a3b |
579 | gtk_container_set_border_width(GTK_CONTAINER(hbox), 10); |
d9b15094 |
580 | gtk_widget_show(hbox); |
581 | vbox = gtk_vbox_new(FALSE, 4); |
582 | gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); |
583 | gtk_widget_show(vbox); |
584 | cols = columns_new(4); |
585 | gtk_box_pack_start(GTK_BOX(vbox), cols, FALSE, FALSE, 0); |
586 | gtk_widget_show(cols); |
587 | label = gtk_label_new("Category:"); |
588 | columns_add(COLUMNS(cols), label, 0, 1); |
589 | columns_force_left_align(COLUMNS(cols), label); |
590 | gtk_widget_show(label); |
591 | treescroll = gtk_scrolled_window_new(NULL, NULL); |
592 | tree = gtk_tree_new(); |
593 | gtk_tree_set_view_mode(GTK_TREE(tree), GTK_TREE_VIEW_ITEM); |
1d0d4a3b |
594 | gtk_tree_set_selection_mode(GTK_TREE(tree), GTK_SELECTION_BROWSE); |
d9b15094 |
595 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(treescroll), |
596 | tree); |
597 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll), |
598 | GTK_POLICY_NEVER, |
599 | GTK_POLICY_AUTOMATIC); |
600 | gtk_widget_show(tree); |
601 | gtk_widget_show(treescroll); |
602 | gtk_box_pack_start(GTK_BOX(vbox), treescroll, TRUE, TRUE, 0); |
603 | panels = panels_new(); |
604 | gtk_box_pack_start(GTK_BOX(hbox), panels, TRUE, TRUE, 0); |
605 | gtk_widget_show(panels); |
606 | |
607 | panelvbox = NULL; |
608 | path = NULL; |
609 | level = 0; |
610 | for (index = 0; index < ctrlbox->nctrlsets; index++) { |
611 | struct controlset *s = ctrlbox->ctrlsets[index]; |
1d0d4a3b |
612 | GtkWidget *w = layout_ctrls(s, listitemheight); |
d9b15094 |
613 | |
614 | if (!*s->pathname) { |
615 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->action_area), |
616 | w, TRUE, TRUE, 0); |
617 | } else { |
618 | int j = path ? ctrl_path_compare(s->pathname, path) : 0; |
619 | if (j != INT_MAX) { /* add to treeview, start new panel */ |
620 | char *c; |
621 | GtkWidget *treeitem; |
622 | int first; |
623 | |
624 | /* |
625 | * We expect never to find an implicit path |
626 | * component. For example, we expect never to see |
627 | * A/B/C followed by A/D/E, because that would |
628 | * _implicitly_ create A/D. All our path prefixes |
629 | * are expected to contain actual controls and be |
630 | * selectable in the treeview; so we would expect |
631 | * to see A/D _explicitly_ before encountering |
632 | * A/D/E. |
633 | */ |
634 | assert(j == ctrl_path_elements(s->pathname) - 1); |
635 | |
636 | c = strrchr(s->pathname, '/'); |
637 | if (!c) |
638 | c = s->pathname; |
639 | else |
640 | c++; |
641 | |
642 | treeitem = gtk_tree_item_new_with_label(c); |
643 | assert(j-1 < level); |
644 | if (j > 0) { |
645 | if (!treelevels[j-1]) { |
646 | treelevels[j-1] = GTK_TREE(gtk_tree_new()); |
647 | gtk_tree_item_set_subtree |
648 | (treeitemlevels[j-1], |
649 | GTK_WIDGET(treelevels[j-1])); |
650 | gtk_tree_item_expand(treeitemlevels[j-1]); |
651 | } |
652 | gtk_tree_append(treelevels[j-1], treeitem); |
653 | } else { |
654 | gtk_tree_append(GTK_TREE(tree), treeitem); |
655 | } |
656 | treeitemlevels[j] = GTK_TREE_ITEM(treeitem); |
657 | treelevels[j] = NULL; |
658 | level = j+1; |
659 | |
660 | gtk_widget_show(treeitem); |
661 | |
662 | path = s->pathname; |
663 | |
664 | first = (panelvbox == NULL); |
665 | |
666 | panelvbox = gtk_vbox_new(FALSE, 4); |
667 | gtk_container_add(GTK_CONTAINER(panels), panelvbox); |
668 | if (first) { |
669 | panels_switch_to(PANELS(panels), panelvbox); |
670 | gtk_tree_select_child(GTK_TREE(tree), treeitem); |
671 | } |
672 | |
673 | if (nselparams >= selparamsize) { |
674 | selparamsize += 16; |
675 | selparams = srealloc(selparams, |
676 | selparamsize * sizeof(*selparams)); |
677 | } |
678 | selparams[nselparams].panels = PANELS(panels); |
679 | selparams[nselparams].panel = panelvbox; |
680 | selparams[nselparams].treeitem = treeitem; |
681 | nselparams++; |
682 | |
683 | } |
684 | |
685 | gtk_box_pack_start(GTK_BOX(panelvbox), w, FALSE, FALSE, 0); |
1d0d4a3b |
686 | gtk_widget_show(w); |
d9b15094 |
687 | } |
688 | } |
689 | |
690 | for (index = 0; index < nselparams; index++) { |
691 | gtk_signal_connect(GTK_OBJECT(selparams[index].treeitem), "select", |
692 | GTK_SIGNAL_FUNC(treeitem_sel), |
693 | &selparams[index]); |
694 | } |
695 | |
696 | gtk_widget_show(window); |
697 | |
698 | gtk_signal_connect(GTK_OBJECT(window), "destroy", |
699 | GTK_SIGNAL_FUNC(destroy), NULL); |
700 | |
701 | gtk_main(); |
702 | |
703 | sfree(selparams); |
704 | } |
705 | |
706 | /* ====================================================================== |
707 | * Below here is a stub main program which allows the dialog box |
708 | * code to be compiled and tested with a minimal amount of the rest |
709 | * of PuTTY. |
710 | */ |
711 | |
712 | #ifdef TESTMODE |
713 | |
714 | /* Compile command for testing: |
715 | |
1d0d4a3b |
716 | gcc -g -o gtkdlg gtk{dlg,cols,panel}.c ../{config,dialog,settings}.c \ |
717 | ../{misc,tree234,be_none}.c ux{store,misc,print,cfg}.c \ |
d9b15094 |
718 | -I. -I.. -I../charset -DTESTMODE `gtk-config --cflags --libs` |
719 | */ |
720 | |
721 | void modalfatalbox(char *p, ...) |
722 | { |
723 | va_list ap; |
724 | fprintf(stderr, "FATAL ERROR: "); |
725 | va_start(ap, p); |
726 | vfprintf(stderr, p, ap); |
727 | va_end(ap); |
728 | fputc('\n', stderr); |
729 | exit(1); |
730 | } |
731 | |
732 | char *cp_name(int codepage) |
733 | { |
734 | return (codepage == 123 ? "testing123" : |
735 | codepage == 234 ? "testing234" : |
736 | codepage == 345 ? "testing345" : |
737 | "unknown"); |
738 | } |
739 | |
740 | char *cp_enumerate(int index) |
741 | { |
742 | return (index == 0 ? "testing123" : |
743 | index == 1 ? "testing234" : |
744 | NULL); |
745 | } |
746 | |
747 | int decode_codepage(char *cp_name) |
748 | { |
749 | return (!strcmp(cp_name, "testing123") ? 123 : |
750 | !strcmp(cp_name, "testing234") ? 234 : |
751 | !strcmp(cp_name, "testing345") ? 345 : |
752 | -2); |
753 | } |
754 | |
755 | struct printer_enum_tag { int dummy; } printer_test; |
756 | |
757 | printer_enum *printer_start_enum(int *nprinters_ptr) { |
758 | *nprinters_ptr = 2; |
759 | return &printer_test; |
760 | } |
761 | char *printer_get_name(printer_enum *pe, int i) { |
762 | return (i==0 ? "lpr" : i==1 ? "lpr -Pfoobar" : NULL); |
763 | } |
764 | void printer_finish_enum(printer_enum *pe) { } |
765 | |
766 | char *platform_default_s(const char *name) |
767 | { |
768 | return NULL; |
769 | } |
770 | |
771 | int platform_default_i(const char *name, int def) |
772 | { |
773 | return def; |
774 | } |
775 | |
776 | FontSpec platform_default_fontspec(const char *name) |
777 | { |
778 | FontSpec ret; |
779 | *ret.name = '\0'; |
780 | return ret; |
781 | } |
782 | |
783 | Filename platform_default_filename(const char *name) |
784 | { |
785 | Filename ret; |
786 | *ret.path = '\0'; |
787 | return ret; |
788 | } |
789 | |
790 | char *x_get_default(const char *key) |
791 | { |
792 | return NULL; |
793 | } |
794 | |
795 | int main(int argc, char **argv) |
796 | { |
797 | gtk_init(&argc, &argv); |
798 | do_config_box(); |
799 | return 0; |
800 | } |
801 | |
802 | #endif |