]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - dialog.c
first pass
[PuTTY.git] / dialog.c
1 /*
2  * dialog.c - a reasonably platform-independent mechanism for
3  * describing dialog boxes.
4  */
5
6 #include <assert.h>
7 #include <limits.h>
8 #include <stdarg.h>
9 #include <stdlib.h>
10
11 #define DEFINE_INTORPTR_FNS
12
13 #include "putty.h"
14 #include "dialog.h"
15
16 int ctrl_path_elements(const char *path)
17 {
18     int i = 1;
19     while (*path) {
20         if (*path == '/') i++;
21         path++;
22     }
23     return i;
24 }
25
26 /* Return the number of matching path elements at the starts of p1 and p2,
27  * or INT_MAX if the paths are identical. */
28 int ctrl_path_compare(const char *p1, const char *p2)
29 {
30     int i = 0;
31     while (*p1 || *p2) {
32         if ((*p1 == '/' || *p1 == '\0') &&
33             (*p2 == '/' || *p2 == '\0'))
34             i++;                       /* a whole element matches, ooh */
35         if (*p1 != *p2)
36             return i;                  /* mismatch */
37         p1++, p2++;
38     }
39     return INT_MAX;                    /* exact match */
40 }
41
42 struct controlbox *ctrl_new_box(void)
43 {
44     struct controlbox *ret = snew(struct controlbox);
45
46     ret->nctrlsets = ret->ctrlsetsize = 0;
47     ret->ctrlsets = NULL;
48     ret->nfrees = ret->freesize = 0;
49     ret->frees = NULL;
50     ret->freefuncs = NULL;
51
52     return ret;
53 }
54
55 void ctrl_free_box(struct controlbox *b)
56 {
57     int i;
58
59     for (i = 0; i < b->nctrlsets; i++) {
60         ctrl_free_set(b->ctrlsets[i]);
61     }
62     for (i = 0; i < b->nfrees; i++)
63         b->freefuncs[i](b->frees[i]);
64     sfree(b->ctrlsets);
65     sfree(b->frees);
66     sfree(b->freefuncs);
67     sfree(b);
68 }
69
70 void ctrl_free_set(struct controlset *s)
71 {
72     int i;
73
74     sfree(s->pathname);
75     sfree(s->boxname);
76     sfree(s->boxtitle);
77     for (i = 0; i < s->ncontrols; i++) {
78         ctrl_free(s->ctrls[i]);
79     }
80     sfree(s->ctrls);
81     sfree(s);
82 }
83
84 /*
85  * Find the index of first controlset in a controlbox for a given
86  * path. If that path doesn't exist, return the index where it
87  * should be inserted.
88  */
89 static int ctrl_find_set(struct controlbox *b, const char *path, int start)
90 {
91     int i, last, thisone;
92
93     last = 0;
94     for (i = 0; i < b->nctrlsets; i++) {
95         thisone = ctrl_path_compare(path, b->ctrlsets[i]->pathname);
96         /*
97          * If `start' is true and there exists a controlset with
98          * exactly the path we've been given, we should return the
99          * index of the first such controlset we find. Otherwise,
100          * we should return the index of the first entry in which
101          * _fewer_ path elements match than they did last time.
102          */
103         if ((start && thisone == INT_MAX) || thisone < last)
104             return i;
105         last = thisone;
106     }
107     return b->nctrlsets;               /* insert at end */
108 }
109
110 /*
111  * Find the index of next controlset in a controlbox for a given
112  * path, or -1 if no such controlset exists. If -1 is passed as
113  * input, finds the first.
114  */
115 int ctrl_find_path(struct controlbox *b, const char *path, int index)
116 {
117     if (index < 0)
118         index = ctrl_find_set(b, path, 1);
119     else
120         index++;
121
122     if (index < b->nctrlsets && !strcmp(path, b->ctrlsets[index]->pathname))
123         return index;
124     else
125         return -1;
126 }
127
128 /* Set up a panel title. */
129 struct controlset *ctrl_settitle(struct controlbox *b,
130                                  const char *path, const char *title)
131 {
132     
133     struct controlset *s = snew(struct controlset);
134     int index = ctrl_find_set(b, path, 1);
135     s->pathname = dupstr(path);
136     s->boxname = NULL;
137     s->boxtitle = dupstr(title);
138     s->ncontrols = s->ctrlsize = 0;
139     s->ncolumns = 0;                   /* this is a title! */
140     s->ctrls = NULL;
141     if (b->nctrlsets >= b->ctrlsetsize) {
142         b->ctrlsetsize = b->nctrlsets + 32;
143         b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);
144     }
145     if (index < b->nctrlsets)
146         memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],
147                 (b->nctrlsets-index) * sizeof(*b->ctrlsets));
148     b->ctrlsets[index] = s;
149     b->nctrlsets++;
150     return s;
151 }
152
153 /* Retrieve a pointer to a controlset, creating it if absent. */
154 struct controlset *ctrl_getset(struct controlbox *b, const char *path,
155                                const char *name, const char *boxtitle)
156 {
157     struct controlset *s;
158     int index = ctrl_find_set(b, path, 1);
159     while (index < b->nctrlsets &&
160            !strcmp(b->ctrlsets[index]->pathname, path)) {
161         if (b->ctrlsets[index]->boxname &&
162             !strcmp(b->ctrlsets[index]->boxname, name))
163             return b->ctrlsets[index];
164         index++;
165     }
166     s = snew(struct controlset);
167     s->pathname = dupstr(path);
168     s->boxname = dupstr(name);
169     s->boxtitle = boxtitle ? dupstr(boxtitle) : NULL;
170     s->ncolumns = 1;
171     s->ncontrols = s->ctrlsize = 0;
172     s->ctrls = NULL;
173     if (b->nctrlsets >= b->ctrlsetsize) {
174         b->ctrlsetsize = b->nctrlsets + 32;
175         b->ctrlsets = sresize(b->ctrlsets, b->ctrlsetsize,struct controlset *);
176     }
177     if (index < b->nctrlsets)
178         memmove(&b->ctrlsets[index+1], &b->ctrlsets[index],
179                 (b->nctrlsets-index) * sizeof(*b->ctrlsets));
180     b->ctrlsets[index] = s;
181     b->nctrlsets++;
182     return s;
183 }
184
185 /* Allocate some private data in a controlbox. */
186 void *ctrl_alloc_with_free(struct controlbox *b, size_t size,
187                            ctrl_freefn_t freefunc)
188 {
189     void *p;
190     /*
191      * This is an internal allocation routine, so it's allowed to
192      * use smalloc directly.
193      */
194     p = smalloc(size);
195     if (b->nfrees >= b->freesize) {
196         b->freesize = b->nfrees + 32;
197         b->frees = sresize(b->frees, b->freesize, void *);
198         b->freefuncs = sresize(b->freefuncs, b->freesize, ctrl_freefn_t);
199     }
200     b->frees[b->nfrees] = p;
201     b->freefuncs[b->nfrees] = freefunc;
202     b->nfrees++;
203     return p;
204 }
205
206 static void ctrl_default_free(void *p)
207 {
208     sfree(p);
209 }
210
211 void *ctrl_alloc(struct controlbox *b, size_t size)
212 {
213     return ctrl_alloc_with_free(b, size, ctrl_default_free);
214 }
215
216 static union control *ctrl_new(struct controlset *s, int type,
217                                intorptr helpctx, handler_fn handler,
218                                intorptr context)
219 {
220     union control *c = snew(union control);
221     if (s->ncontrols >= s->ctrlsize) {
222         s->ctrlsize = s->ncontrols + 32;
223         s->ctrls = sresize(s->ctrls, s->ctrlsize, union control *);
224     }
225     s->ctrls[s->ncontrols++] = c;
226     /*
227      * Fill in the standard fields.
228      */
229     c->generic.type = type;
230     c->generic.tabdelay = 0;
231     c->generic.column = COLUMN_FIELD(0, s->ncolumns);
232     c->generic.helpctx = helpctx;
233     c->generic.handler = handler;
234     c->generic.context = context;
235     c->generic.label = NULL;
236     return c;
237 }
238
239 /* `ncolumns' is followed by that many percentages, as integers. */
240 union control *ctrl_columns(struct controlset *s, int ncolumns, ...)
241 {
242     union control *c = ctrl_new(s, CTRL_COLUMNS, P(NULL), NULL, P(NULL));
243     assert(s->ncolumns == 1 || ncolumns == 1);
244     c->columns.ncols = ncolumns;
245     s->ncolumns = ncolumns;
246     if (ncolumns == 1) {
247         c->columns.percentages = NULL;
248     } else {
249         va_list ap;
250         int i;
251         c->columns.percentages = snewn(ncolumns, int);
252         va_start(ap, ncolumns);
253         for (i = 0; i < ncolumns; i++)
254             c->columns.percentages[i] = va_arg(ap, int);
255         va_end(ap);
256     }
257     return c;
258 }
259
260 union control *ctrl_editbox(struct controlset *s, const char *label,
261                             char shortcut, int percentage,
262                             intorptr helpctx, handler_fn handler,
263                             intorptr context, intorptr context2)
264 {
265     union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);
266     c->editbox.label = label ? dupstr(label) : NULL;
267     c->editbox.shortcut = shortcut;
268     c->editbox.percentwidth = percentage;
269     c->editbox.password = 0;
270     c->editbox.has_list = 0;
271     c->editbox.context2 = context2;
272     return c;
273 }
274
275 union control *ctrl_combobox(struct controlset *s, const char *label,
276                              char shortcut, int percentage,
277                              intorptr helpctx, handler_fn handler,
278                              intorptr context, intorptr context2)
279 {
280     union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context);
281     c->editbox.label = label ? dupstr(label) : NULL;
282     c->editbox.shortcut = shortcut;
283     c->editbox.percentwidth = percentage;
284     c->editbox.password = 0;
285     c->editbox.has_list = 1;
286     c->editbox.context2 = context2;
287     return c;
288 }
289
290 /*
291  * `ncolumns' is followed by (alternately) radio button titles and
292  * intorptrs, until a NULL in place of a title string is seen. Each
293  * title is expected to be followed by a shortcut _iff_ `shortcut'
294  * is NO_SHORTCUT.
295  */
296 union control *ctrl_radiobuttons(struct controlset *s, const char *label,
297                                  char shortcut, int ncolumns, intorptr helpctx,
298                                  handler_fn handler, intorptr context, ...)
299 {
300     va_list ap;
301     int i;
302     union control *c = ctrl_new(s, CTRL_RADIO, helpctx, handler, context);
303     c->radio.label = label ? dupstr(label) : NULL;
304     c->radio.shortcut = shortcut;
305     c->radio.ncolumns = ncolumns;
306     /*
307      * Initial pass along variable argument list to count the
308      * buttons.
309      */
310     va_start(ap, context);
311     i = 0;
312     while (va_arg(ap, char *) != NULL) {
313         i++;
314         if (c->radio.shortcut == NO_SHORTCUT)
315             (void)va_arg(ap, int);     /* char promotes to int in arg lists */
316         (void)va_arg(ap, intorptr);
317     }
318     va_end(ap);
319     c->radio.nbuttons = i;
320     if (c->radio.shortcut == NO_SHORTCUT)
321         c->radio.shortcuts = snewn(c->radio.nbuttons, char);
322     else
323         c->radio.shortcuts = NULL;
324     c->radio.buttons = snewn(c->radio.nbuttons, char *);
325     c->radio.buttondata = snewn(c->radio.nbuttons, intorptr);
326     /*
327      * Second pass along variable argument list to actually fill in
328      * the structure.
329      */
330     va_start(ap, context);
331     for (i = 0; i < c->radio.nbuttons; i++) {
332         c->radio.buttons[i] = dupstr(va_arg(ap, char *));
333         if (c->radio.shortcut == NO_SHORTCUT)
334             c->radio.shortcuts[i] = va_arg(ap, int);
335                                        /* char promotes to int in arg lists */
336         c->radio.buttondata[i] = va_arg(ap, intorptr);
337     }
338     va_end(ap);
339     return c;
340 }
341
342 union control *ctrl_pushbutton(struct controlset *s, const char *label,
343                                char shortcut, intorptr helpctx,
344                                handler_fn handler, intorptr context)
345 {
346     union control *c = ctrl_new(s, CTRL_BUTTON, helpctx, handler, context);
347     c->button.label = label ? dupstr(label) : NULL;
348     c->button.shortcut = shortcut;
349     c->button.isdefault = 0;
350     c->button.iscancel = 0;
351     return c;
352 }
353
354 union control *ctrl_listbox(struct controlset *s, const char *label,
355                             char shortcut, intorptr helpctx,
356                             handler_fn handler, intorptr context)
357 {
358     union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
359     c->listbox.label = label ? dupstr(label) : NULL;
360     c->listbox.shortcut = shortcut;
361     c->listbox.height = 5;             /* *shrug* a plausible default */
362     c->listbox.draglist = 0;
363     c->listbox.multisel = 0;
364     c->listbox.percentwidth = 100;
365     c->listbox.ncols = 0;
366     c->listbox.percentages = NULL;
367     c->listbox.hscroll = TRUE;
368     return c;
369 }
370
371 union control *ctrl_droplist(struct controlset *s, const char *label,
372                              char shortcut, int percentage, intorptr helpctx,
373                              handler_fn handler, intorptr context)
374 {
375     union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
376     c->listbox.label = label ? dupstr(label) : NULL;
377     c->listbox.shortcut = shortcut;
378     c->listbox.height = 0;             /* means it's a drop-down list */
379     c->listbox.draglist = 0;
380     c->listbox.multisel = 0;
381     c->listbox.percentwidth = percentage;
382     c->listbox.ncols = 0;
383     c->listbox.percentages = NULL;
384     c->listbox.hscroll = FALSE;
385     return c;
386 }
387
388 union control *ctrl_draglist(struct controlset *s, const char *label,
389                              char shortcut, intorptr helpctx,
390                              handler_fn handler, intorptr context)
391 {
392     union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context);
393     c->listbox.label = label ? dupstr(label) : NULL;
394     c->listbox.shortcut = shortcut;
395     c->listbox.height = 5;             /* *shrug* a plausible default */
396     c->listbox.draglist = 1;
397     c->listbox.multisel = 0;
398     c->listbox.percentwidth = 100;
399     c->listbox.ncols = 0;
400     c->listbox.percentages = NULL;
401     c->listbox.hscroll = FALSE;
402     return c;
403 }
404
405 union control *ctrl_filesel(struct controlset *s, const char *label,
406                             char shortcut, const char *filter, int write,
407                             const char *title, intorptr helpctx,
408                             handler_fn handler, intorptr context)
409 {
410     union control *c = ctrl_new(s, CTRL_FILESELECT, helpctx, handler, context);
411     c->fileselect.label = label ? dupstr(label) : NULL;
412     c->fileselect.shortcut = shortcut;
413     c->fileselect.filter = filter;
414     c->fileselect.for_writing = write;
415     c->fileselect.title = dupstr(title);
416     return c;
417 }
418
419 union control *ctrl_fontsel(struct controlset *s, const char *label,
420                             char shortcut, intorptr helpctx,
421                             handler_fn handler, intorptr context)
422 {
423     union control *c = ctrl_new(s, CTRL_FONTSELECT, helpctx, handler, context);
424     c->fontselect.label = label ? dupstr(label) : NULL;
425     c->fontselect.shortcut = shortcut;
426     return c;
427 }
428
429 union control *ctrl_tabdelay(struct controlset *s, union control *ctrl)
430 {
431     union control *c = ctrl_new(s, CTRL_TABDELAY, P(NULL), NULL, P(NULL));
432     c->tabdelay.ctrl = ctrl;
433     return c;
434 }
435
436 union control *ctrl_text(struct controlset *s, const char *text,
437                          intorptr helpctx)
438 {
439     union control *c = ctrl_new(s, CTRL_TEXT, helpctx, NULL, P(NULL));
440     c->text.label = dupstr(text);
441     return c;
442 }
443
444 union control *ctrl_checkbox(struct controlset *s, const char *label,
445                              char shortcut, intorptr helpctx,
446                              handler_fn handler, intorptr context)
447 {
448     union control *c = ctrl_new(s, CTRL_CHECKBOX, helpctx, handler, context);
449     c->checkbox.label = label ? dupstr(label) : NULL;
450     c->checkbox.shortcut = shortcut;
451     return c;
452 }
453
454 void ctrl_free(union control *ctrl)
455 {
456     int i;
457
458     sfree(ctrl->generic.label);
459     switch (ctrl->generic.type) {
460       case CTRL_RADIO:
461         for (i = 0; i < ctrl->radio.nbuttons; i++)
462             sfree(ctrl->radio.buttons[i]);
463         sfree(ctrl->radio.buttons);
464         sfree(ctrl->radio.shortcuts);
465         sfree(ctrl->radio.buttondata);
466         break;
467       case CTRL_COLUMNS:
468         sfree(ctrl->columns.percentages);
469         break;
470       case CTRL_LISTBOX:
471         sfree(ctrl->listbox.percentages);
472         break;
473       case CTRL_FILESELECT:
474         sfree(ctrl->fileselect.title);
475         break;
476     }
477     sfree(ctrl);
478 }