dialog.c 14 KB

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