2 * dialog.c - a reasonably platform-independent mechanism for
3 * describing dialog boxes.
11 #define DEFINE_INTORPTR_FNS
16 int ctrl_path_elements(char *path
)
20 if (*path
== '/') i
++;
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(char *p1
, char *p2
)
32 if ((*p1
== '/' || *p1
== '\0') &&
33 (*p2
== '/' || *p2
== '\0'))
34 i
++; /* a whole element matches, ooh */
36 return i
; /* mismatch */
39 return INT_MAX
; /* exact match */
42 struct controlbox
*ctrl_new_box(void)
44 struct controlbox
*ret
= snew(struct controlbox
);
46 ret
->nctrlsets
= ret
->ctrlsetsize
= 0;
48 ret
->nfrees
= ret
->freesize
= 0;
50 ret
->freefuncs
= NULL
;
55 void ctrl_free_box(struct controlbox
*b
)
59 for (i
= 0; i
< b
->nctrlsets
; i
++) {
60 ctrl_free_set(b
->ctrlsets
[i
]);
62 for (i
= 0; i
< b
->nfrees
; i
++)
63 b
->freefuncs
[i
](b
->frees
[i
]);
70 void ctrl_free_set(struct controlset
*s
)
77 for (i
= 0; i
< s
->ncontrols
; i
++) {
78 ctrl_free(s
->ctrls
[i
]);
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
89 static int ctrl_find_set(struct controlbox
*b
, char *path
, int start
)
94 for (i
= 0; i
< b
->nctrlsets
; i
++) {
95 thisone
= ctrl_path_compare(path
, b
->ctrlsets
[i
]->pathname
);
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.
103 if ((start
&& thisone
== INT_MAX
) || thisone
< last
)
107 return b
->nctrlsets
; /* insert at end */
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.
115 int ctrl_find_path(struct controlbox
*b
, char *path
, int index
)
118 index
= ctrl_find_set(b
, path
, 1);
122 if (index
< b
->nctrlsets
&& !strcmp(path
, b
->ctrlsets
[index
]->pathname
))
128 /* Set up a panel title. */
129 struct controlset
*ctrl_settitle(struct controlbox
*b
,
130 char *path
, char *title
)
133 struct controlset
*s
= snew(struct controlset
);
134 int index
= ctrl_find_set(b
, path
, 1);
135 s
->pathname
= dupstr(path
);
137 s
->boxtitle
= dupstr(title
);
138 s
->ncontrols
= s
->ctrlsize
= 0;
139 s
->ncolumns
= 0; /* this is a title! */
141 if (b
->nctrlsets
>= b
->ctrlsetsize
) {
142 b
->ctrlsetsize
= b
->nctrlsets
+ 32;
143 b
->ctrlsets
= sresize(b
->ctrlsets
, b
->ctrlsetsize
,struct controlset
*);
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
;
153 /* Retrieve a pointer to a controlset, creating it if absent. */
154 struct controlset
*ctrl_getset(struct controlbox
*b
,
155 char *path
, char *name
, char *boxtitle
)
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
];
166 s
= snew(struct controlset
);
167 s
->pathname
= dupstr(path
);
168 s
->boxname
= dupstr(name
);
169 s
->boxtitle
= boxtitle ?
dupstr(boxtitle
) : NULL
;
171 s
->ncontrols
= s
->ctrlsize
= 0;
173 if (b
->nctrlsets
>= b
->ctrlsetsize
) {
174 b
->ctrlsetsize
= b
->nctrlsets
+ 32;
175 b
->ctrlsets
= sresize(b
->ctrlsets
, b
->ctrlsetsize
,struct controlset
*);
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
;
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
)
191 * This is an internal allocation routine, so it's allowed to
192 * use smalloc directly.
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
);
200 b
->frees
[b
->nfrees
] = p
;
201 b
->freefuncs
[b
->nfrees
] = freefunc
;
206 static void ctrl_default_free(void *p
)
211 void *ctrl_alloc(struct controlbox
*b
, size_t size
)
213 return ctrl_alloc_with_free(b
, size
, ctrl_default_free
);
216 static union control
*ctrl_new(struct controlset
*s
, int type
,
217 intorptr helpctx
, handler_fn handler
,
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
*);
225 s
->ctrls
[s
->ncontrols
++] = c
;
227 * Fill in the standard fields.
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
;
239 /* `ncolumns' is followed by that many percentages, as integers. */
240 union control
*ctrl_columns(struct controlset
*s
, int ncolumns
, ...)
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
;
247 c
->columns
.percentages
= NULL
;
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);
260 union control
*ctrl_editbox(struct controlset
*s
, char *label
, char shortcut
,
262 intorptr helpctx
, handler_fn handler
,
263 intorptr context
, intorptr context2
)
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
;
275 union control
*ctrl_combobox(struct controlset
*s
, char *label
, char shortcut
,
277 intorptr helpctx
, handler_fn handler
,
278 intorptr context
, intorptr context2
)
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
;
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'
296 union control
*ctrl_radiobuttons(struct controlset
*s
, char *label
,
297 char shortcut
, int ncolumns
, intorptr helpctx
,
298 handler_fn handler
, intorptr context
, ...)
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
;
307 * Initial pass along variable argument list to count the
310 va_start(ap
, context
);
312 while (va_arg(ap
, char *) != NULL
) {
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
);
319 c
->radio
.nbuttons
= i
;
320 if (c
->radio
.shortcut
== NO_SHORTCUT
)
321 c
->radio
.shortcuts
= snewn(c
->radio
.nbuttons
, char);
323 c
->radio
.shortcuts
= NULL
;
324 c
->radio
.buttons
= snewn(c
->radio
.nbuttons
, char *);
325 c
->radio
.buttondata
= snewn(c
->radio
.nbuttons
, intorptr
);
327 * Second pass along variable argument list to actually fill in
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
);
342 union control
*ctrl_pushbutton(struct controlset
*s
,char *label
,char shortcut
,
343 intorptr helpctx
, handler_fn handler
,
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;
354 union control
*ctrl_listbox(struct controlset
*s
,char *label
,char shortcut
,
355 intorptr helpctx
, handler_fn handler
,
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
;
370 union control
*ctrl_droplist(struct controlset
*s
, char *label
, char shortcut
,
371 int percentage
, intorptr helpctx
,
372 handler_fn handler
, intorptr context
)
374 union control
*c
= ctrl_new(s
, CTRL_LISTBOX
, helpctx
, handler
, context
);
375 c
->listbox
.label
= label ?
dupstr(label
) : NULL
;
376 c
->listbox
.shortcut
= shortcut
;
377 c
->listbox
.height
= 0; /* means it's a drop-down list */
378 c
->listbox
.draglist
= 0;
379 c
->listbox
.multisel
= 0;
380 c
->listbox
.percentwidth
= percentage
;
381 c
->listbox
.ncols
= 0;
382 c
->listbox
.percentages
= NULL
;
386 union control
*ctrl_draglist(struct controlset
*s
,char *label
,char shortcut
,
387 intorptr helpctx
, handler_fn handler
,
390 union control
*c
= ctrl_new(s
, CTRL_LISTBOX
, helpctx
, handler
, context
);
391 c
->listbox
.label
= label ?
dupstr(label
) : NULL
;
392 c
->listbox
.shortcut
= shortcut
;
393 c
->listbox
.height
= 5; /* *shrug* a plausible default */
394 c
->listbox
.draglist
= 1;
395 c
->listbox
.multisel
= 0;
396 c
->listbox
.percentwidth
= 100;
397 c
->listbox
.ncols
= 0;
398 c
->listbox
.percentages
= NULL
;
402 union control
*ctrl_filesel(struct controlset
*s
,char *label
,char shortcut
,
403 char const *filter
, int write
, char *title
,
404 intorptr helpctx
, handler_fn handler
,
407 union control
*c
= ctrl_new(s
, CTRL_FILESELECT
, helpctx
, handler
, context
);
408 c
->fileselect
.label
= label ?
dupstr(label
) : NULL
;
409 c
->fileselect
.shortcut
= shortcut
;
410 c
->fileselect
.filter
= filter
;
411 c
->fileselect
.for_writing
= write
;
412 c
->fileselect
.title
= dupstr(title
);
416 union control
*ctrl_fontsel(struct controlset
*s
,char *label
,char shortcut
,
417 intorptr helpctx
, handler_fn handler
,
420 union control
*c
= ctrl_new(s
, CTRL_FONTSELECT
, helpctx
, handler
, context
);
421 c
->fontselect
.label
= label ?
dupstr(label
) : NULL
;
422 c
->fontselect
.shortcut
= shortcut
;
426 union control
*ctrl_tabdelay(struct controlset
*s
, union control
*ctrl
)
428 union control
*c
= ctrl_new(s
, CTRL_TABDELAY
, P(NULL
), NULL
, P(NULL
));
429 c
->tabdelay
.ctrl
= ctrl
;
433 union control
*ctrl_text(struct controlset
*s
, char *text
, intorptr helpctx
)
435 union control
*c
= ctrl_new(s
, CTRL_TEXT
, helpctx
, NULL
, P(NULL
));
436 c
->text
.label
= dupstr(text
);
440 union control
*ctrl_checkbox(struct controlset
*s
, char *label
, char shortcut
,
441 intorptr helpctx
, handler_fn handler
,
444 union control
*c
= ctrl_new(s
, CTRL_CHECKBOX
, helpctx
, handler
, context
);
445 c
->checkbox
.label
= label ?
dupstr(label
) : NULL
;
446 c
->checkbox
.shortcut
= shortcut
;
450 void ctrl_free(union control
*ctrl
)
454 sfree(ctrl
->generic
.label
);
455 switch (ctrl
->generic
.type
) {
457 for (i
= 0; i
< ctrl
->radio
.nbuttons
; i
++)
458 sfree(ctrl
->radio
.buttons
[i
]);
459 sfree(ctrl
->radio
.buttons
);
460 sfree(ctrl
->radio
.shortcuts
);
461 sfree(ctrl
->radio
.buttondata
);
464 sfree(ctrl
->columns
.percentages
);
467 sfree(ctrl
->listbox
.percentages
);
469 case CTRL_FILESELECT
:
470 sfree(ctrl
->fileselect
.title
);