]> git.ipfire.org Git - thirdparty/newt.git/blame - dialogboxes.c
restore automatic menu/list-height in whiptail
[thirdparty/newt.git] / dialogboxes.c
CommitLineData
649a0152 1/* simple dialog boxes, used by both whiptail and tcl dialog bindings */
2
feef2cb5 3#include "config.h"
649a0152 4#include <fcntl.h>
5#include <stdio.h>
6#include <string.h>
7#include <stdlib.h>
8#include <unistd.h>
feef2cb5 9#include <wchar.h>
10#include <slang.h>
649a0152 11
feef2cb5 12#include "nls.h"
649a0152 13#include "dialogboxes.h"
14#include "newt.h"
349586bb 15#include "newt_pr.h"
649a0152 16#include "popt.h"
17
b3ddfdd7 18#define MAXBUF 200
19#define MAXFORMAT 20
60d3c50b 20#define BUTTONS 4
b3ddfdd7 21
649a0152 22/* globals -- ick */
59f42e91 23static int buttonHeight = 1;
60d3c50b 24static const char * buttonText[BUTTONS];
feef2cb5 25
26int max (int a, int b)
27{
28 return (a > b) ? a : b;
29}
30
31int min (int a, int b)
32{
33 return ( a < b) ? a : b ;
34}
35
59f42e91 36static newtComponent (*makeButton)(int left, int right, const char * text) =
6f481af2 37 newtCompactButton;
649a0152 38
60d3c50b 39static const char * getButtonText(int button) {
40 const char * text;
41 if (button < 0 || button >= BUTTONS)
42 return NULL;
43
44 text = buttonText[button];
45 if (text)
46 return text;
47
48 switch (button) {
eadebb02 49 case 0: return dgettext(PACKAGE, "Ok");
50 case 1: return dgettext(PACKAGE, "Cancel");
51 case 2: return dgettext(PACKAGE, "Yes");
52 case 3: return dgettext(PACKAGE, "No");
60d3c50b 53 default:
54 return NULL;
55 }
60d3c50b 56}
57
649a0152 58static void addButtons(int height, int width, newtComponent form,
6f481af2 59 newtComponent * okay, newtComponent * cancel,
60 int flags) {
feef2cb5 61 // FIXME: DO SOMETHING ABOUT THE HARD-CODED CONSTANTS
649a0152 62 if (flags & FLAG_NOCANCEL) {
feef2cb5 63 *okay = makeButton((width - 8) / 2, height - buttonHeight - 1,
60d3c50b 64 getButtonText(BUTTON_OK));
feef2cb5 65 *cancel = NULL;
6f481af2 66 newtFormAddComponent(form, *okay);
649a0152 67 } else {
feef2cb5 68 *okay = makeButton((width - 18) / 3, height - buttonHeight - 1,
60d3c50b 69 getButtonText(BUTTON_OK));
6f481af2 70 *cancel = makeButton(((width - 18) / 3) * 2 + 9,
feef2cb5 71 height - buttonHeight - 1,
60d3c50b 72 getButtonText(BUTTON_CANCEL));
6f481af2 73 newtFormAddComponents(form, *okay, *cancel, NULL);
649a0152 74 }
75}
76
ae446151
ML
77static void cleanNewlines(char *text)
78{
79 char *p, *q;
80
81 for (p = q = text; *p; p++, q++)
82 if (*p == '\\' && p[1] == 'n') {
83 p++;
84 *q = '\n';
85 } else
86 *q = *p;
87 *q = '\0';
88}
89
eb0a0545 90static newtComponent textbox(int maxHeight, int width, const char * text,
6f481af2 91 int flags, int * height) {
649a0152 92 newtComponent tb;
93 int sFlag = (flags & FLAG_SCROLL_TEXT) ? NEWT_FLAG_SCROLL : 0;
94 int i;
ae446151
ML
95 char *buf;
96
97 buf = alloca(strlen(text) + 1);
98 strcpy(buf, text);
99 cleanNewlines(buf);
649a0152 100
101 tb = newtTextbox(1, 0, width, maxHeight, NEWT_FLAG_WRAP | sFlag);
102 newtTextboxSetText(tb, buf);
103
104 i = newtTextboxGetNumLines(tb);
105 if (i < maxHeight) {
6f481af2 106 newtTextboxSetHeight(tb, i);
107 maxHeight = i;
649a0152 108 }
109
110 *height = maxHeight;
111
112 return tb;
113}
114
eb0a0545 115int gauge(const char * text, int height, int width, poptContext optCon, int fd,
6f481af2 116 int flags) {
649a0152 117 newtComponent form, scale, tb;
118 int top;
eb0a0545 119 const char * arg;
120 char * end;
649a0152 121 int val;
122 FILE * f = fdopen(fd, "r");
123 char buf[3000];
124 char buf3[50];
125 int i;
126
127 setlinebuf(f);
128
129 if (!(arg = poptGetArg(optCon))) return DLG_ERROR;
130 val = strtoul(arg, &end, 10);
131 if (*end) return DLG_ERROR;
132
133 tb = textbox(height - 3, width - 2, text, flags, &top);
134
135 form = newtForm(NULL, NULL, 0);
136
137 scale = newtScale(2, height - 2, width - 4, 100);
138 newtScaleSet(scale, val);
139
140 newtFormAddComponents(form, tb, scale, NULL);
141
142 newtDrawForm(form);
143 newtRefresh();
144
cf6356e4
ML
145 do {
146 if (!fgets(buf, sizeof(buf) - 1, f))
147 continue;
6f481af2 148 buf[strlen(buf) - 1] = '\0';
149
150 if (!strcmp(buf, "XXX")) {
cf6356e4
ML
151 while (!fgets(buf3, sizeof(buf3) - 1, f) && !feof(f))
152 ;
153 if (feof(f))
c00a8d1b 154 break;
6f481af2 155 buf3[strlen(buf3) - 1] = '\0';
6f481af2 156
157 i = 0;
cf6356e4
ML
158 do {
159 if (!fgets(buf + i, sizeof(buf) - 1 - i, f))
160 continue;
ae446151 161 if (!strcmp(buf + i, "XXX\n")) {
6f481af2 162 *(buf + i) = '\0';
163 break;
164 }
165 i = strlen(buf);
cf6356e4 166 } while (!feof(f));
6f481af2 167
ae446151
ML
168 if (i > 0)
169 buf[strlen(buf) - 1] = '\0';
170 else
171 buf[0] = '\0';
172
173 cleanNewlines(buf);
6f481af2 174 newtTextboxSetText(tb, buf);
44e7bee7
ML
175
176 arg = buf3;
177 } else {
178 arg = buf;
6f481af2 179 }
180
44e7bee7 181 val = strtoul(arg, &end, 10);
ae446151 182 if (!*end) {
6f481af2 183 newtScaleSet(scale, val);
184 newtDrawForm(form);
185 newtRefresh();
186 }
cf6356e4 187 } while (!feof(f));
649a0152 188
3341bdc5
ML
189 newtFormDestroy(form);
190
649a0152 191 return DLG_OKAY;
192}
193
eb0a0545 194int inputBox(const char * text, int height, int width, poptContext optCon,
f40f18ae 195 int flags, char ** result) {
649a0152 196 newtComponent form, entry, okay, cancel, answer, tb;
d78245ab 197 const char * val;
feef2cb5 198 int pFlag = (flags & FLAG_PASSWORD) ? NEWT_FLAG_PASSWORD : 0;
649a0152 199 int rc = DLG_OKAY;
200 int top;
201
202 val = poptGetArg(optCon);
203 tb = textbox(height - 3 - buttonHeight, width - 2,
6f481af2 204 text, flags, &top);
649a0152 205
206 form = newtForm(NULL, NULL, 0);
207 entry = newtEntry(1, top + 1, val, width - 2, &val,
feef2cb5 208 NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT | pFlag);
649a0152 209
210 newtFormAddComponents(form, tb, entry, NULL);
211
212 addButtons(height, width, form, &okay, &cancel, flags);
213
214 answer = newtRunForm(form);
f40f18ae 215 *result = NULL;
649a0152 216 if (answer == cancel)
6f481af2 217 rc = DLG_CANCEL;
feef2cb5 218 else if (answer == NULL)
219 rc = DLG_ESCAPE;
f40f18ae
ML
220 else
221 *result = strdup(val);
649a0152 222
3341bdc5
ML
223 newtFormDestroy(form);
224
649a0152 225 return rc;
226}
227
feef2cb5 228static int mystrncpyw(char *dest, const char *src, int n, int *maxwidth)
229{
230 int i = 0;
231 int w = 0, cw;
232 wchar_t c;
233 mbstate_t ps;
234 const char *p = src;
235 char *d = dest;
236
237 memset(&ps, 0, sizeof(ps));
238
239 for (;;) {
240 int ret = mbrtowc(&c, p, MB_CUR_MAX, &ps);
241 if (ret <= 0) break;
242 if (ret + i >= n) break;
243 cw = wcwidth(c);
244 if (cw < 0) break;
245 if (cw + w > *maxwidth) break;
246 w += cw;
247 memcpy(d, p, ret);
248 d += ret;
249 p += ret;
250 i += ret;
251 }
252 dest[i] = '\0';
253 *maxwidth = w;
254 return i;
255}
256
9c4eb622 257int listBox(const char * text, int height, int width, int listHeight, poptContext optCon,
f40f18ae 258 int flags, const char *default_item, char ** result) {
9eb2e489 259 newtComponent form = NULL, okay, tb, answer, listBox;
649a0152 260 newtComponent cancel = NULL;
eb0a0545 261 const char * arg;
262 char * end;
649a0152 263 int numItems = 0;
264 int allocedItems = 5;
265 int i, top;
9eb2e489 266 int rc = DLG_ERROR;
feef2cb5 267 char buf[MAXBUF];
649a0152 268 int maxTagWidth = 0;
269 int maxTextWidth = 0;
feef2cb5 270 int defItem = -1;
394c9a80 271 int scrollFlag;
feef2cb5 272 int lineWidth, textWidth, tagWidth;
649a0152 273 struct {
6f481af2 274 const char * text;
275 const char * tag;
649a0152 276 } * itemInfo = malloc(allocedItems * sizeof(*itemInfo));
9eb2e489 277 void * tmp;
649a0152 278
9eb2e489
ML
279 if (itemInfo == NULL)
280 goto error;
649a0152 281
282 while ((arg = poptGetArg(optCon))) {
6f481af2 283 if (allocedItems == numItems) {
284 allocedItems += 5;
9eb2e489
ML
285 tmp = realloc(itemInfo, sizeof(*itemInfo) * allocedItems);
286 if (tmp == NULL)
287 goto error;
288 itemInfo = tmp;
6f481af2 289 }
290
291 itemInfo[numItems].tag = arg;
feef2cb5 292 if (default_item && (strcmp(default_item, arg) == 0)) {
293 defItem = numItems;
294 }
9eb2e489
ML
295 if (!(arg = poptGetArg(optCon)))
296 goto error;
6f481af2 297
298 if (!(flags & FLAG_NOITEM)) {
299 itemInfo[numItems].text = arg;
300 } else
301 itemInfo[numItems].text = "";
302
303 if (wstrlen(itemInfo[numItems].text,-1) > (unsigned int)maxTextWidth)
304 maxTextWidth = wstrlen(itemInfo[numItems].text,-1);
305 if (wstrlen(itemInfo[numItems].tag,-1) > (unsigned int)maxTagWidth)
306 maxTagWidth = wstrlen(itemInfo[numItems].tag,-1);
307
308 numItems++;
649a0152 309 }
b3ddfdd7 310 if (numItems == 0)
9eb2e489 311 goto error;
649a0152 312
57cb49e7 313 if (flags & FLAG_NOTAGS) {
6f481af2 314 maxTagWidth = 0;
57cb49e7 315 }
316
649a0152 317 form = newtForm(NULL, NULL, 0);
318
319 tb = textbox(height - 4 - buttonHeight - listHeight, width - 2,
6f481af2 320 text, flags, &top);
649a0152 321
322 if (listHeight >= numItems) {
6f481af2 323 scrollFlag = 0;
324 i = 0;
649a0152 325 } else {
6f481af2 326 scrollFlag = NEWT_FLAG_SCROLL;
327 i = 2;
649a0152 328 }
329
10bbfd28 330 lineWidth = min(maxTagWidth + maxTextWidth + i + 1, SLtt_Screen_Cols - 6);
feef2cb5 331 listBox = newtListbox( (width - lineWidth) / 2 , top + 1, listHeight,
332 NEWT_FLAG_RETURNEXIT | scrollFlag);
649a0152 333
feef2cb5 334 textWidth = maxTextWidth;
335 tagWidth = maxTagWidth;
336 if (maxTextWidth == 0) {
337 tagWidth = lineWidth;
338 } else {
10bbfd28
ML
339 tagWidth++;
340 textWidth++;
341 while (textWidth + tagWidth + i > lineWidth) {
342 if (textWidth >= tagWidth && textWidth > 0)
343 textWidth--;
344 else if (tagWidth > 0)
345 tagWidth--;
346 else
347 break;
348 }
649a0152 349 }
350
feef2cb5 351 if (!(flags & FLAG_NOTAGS)) {
352 for (i = 0; i < numItems; i++) {
353 int w = tagWidth;
354 int len, j;
355 len = mystrncpyw(buf, itemInfo[i].tag, MAXBUF, &w);
356 for (j = 0; j < tagWidth - w; j++) {
3341bdc5
ML
357 if (len + 1 >= MAXBUF)
358 break;
feef2cb5 359 buf[len++] = ' ';
360 }
361 buf[len] = '\0';
362 w = textWidth;
363 mystrncpyw(buf + len, itemInfo[i].text, MAXBUF-len, &w);
8bec7d99 364 newtListboxAddEntry(listBox, buf, (void *)(long) i);
feef2cb5 365 }
366 } else {
367 for (i = 0; i < numItems; i++) {
368 snprintf(buf, MAXBUF, "%s", itemInfo[i].text);
8bec7d99 369 newtListboxAddEntry(listBox, buf, (void *)(long) i);
feef2cb5 370 }
371 }
372
373 if (defItem != -1)
374 newtListboxSetCurrent (listBox, defItem);
375
649a0152 376 newtFormAddComponents(form, tb, listBox, NULL);
377
378 addButtons(height, width, form, &okay, &cancel, flags);
379
380 answer = newtRunForm(form);
f40f18ae 381 *result = NULL;
649a0152 382 if (answer == cancel)
6f481af2 383 rc = DLG_CANCEL;
5a34899c 384 else if (answer == NULL)
feef2cb5 385 rc = DLG_ESCAPE;
f40f18ae
ML
386 else {
387 i = (long) newtListboxGetCurrent(listBox);
388 *result = strdup(itemInfo[i].tag);
9eb2e489
ML
389 if (*result == NULL)
390 goto error;
391 rc = DLG_OKAY;
f40f18ae 392 }
649a0152 393
9eb2e489
ML
394error:
395 if (form)
396 newtFormDestroy(form);
3341bdc5
ML
397 free(itemInfo);
398
649a0152 399 return rc;
400}
401
9c4eb622
ML
402int checkList(const char * text, int height, int width, int listHeight,
403 poptContext optCon, int useRadio, int flags, char *** selections) {
9eb2e489 404 newtComponent form = NULL, okay, tb, subform, answer;
649a0152 405 newtComponent sb = NULL, cancel = NULL;
eb0a0545 406 const char * arg;
407 char * end;
649a0152 408 int numBoxes = 0;
409 int allocedBoxes = 5;
410 int i;
411 int numSelected;
9eb2e489 412 int rc = DLG_ERROR;
feef2cb5 413 char buf[MAXBUF], format[MAXFORMAT];
649a0152 414 int maxWidth = 0;
415 int top;
416 struct {
6f481af2 417 const char * text;
418 const char * tag;
419 newtComponent comp;
649a0152 420 } * cbInfo = malloc(allocedBoxes * sizeof(*cbInfo));
3341bdc5 421 char * cbStates = malloc(allocedBoxes * sizeof(*cbStates));
9eb2e489 422 void * tmp;
649a0152 423
9eb2e489
ML
424 if (cbInfo == NULL || cbStates == NULL)
425 goto error;
649a0152 426
427 while ((arg = poptGetArg(optCon))) {
6f481af2 428 if (allocedBoxes == numBoxes) {
429 allocedBoxes += 5;
9eb2e489
ML
430
431 tmp = realloc(cbInfo, sizeof(*cbInfo) * allocedBoxes);
432 if (tmp == NULL)
433 goto error;
434 cbInfo = tmp;
435
436 tmp = realloc(cbStates, sizeof(*cbStates) * allocedBoxes);
437 if (tmp == NULL)
438 goto error;
439 cbStates = tmp;
6f481af2 440 }
441
442 cbInfo[numBoxes].tag = arg;
9eb2e489
ML
443 if (!(arg = poptGetArg(optCon)))
444 goto error;
6f481af2 445
446 if (!(flags & FLAG_NOITEM)) {
447 cbInfo[numBoxes].text = arg;
9eb2e489
ML
448 if (!(arg = poptGetArg(optCon)))
449 goto error;
6f481af2 450 } else
451 cbInfo[numBoxes].text = "";
452
453 if (!strcmp(arg, "1") || !strcasecmp(arg, "on") ||
454 !strcasecmp(arg, "yes"))
455 cbStates[numBoxes] = '*';
456 else
457 cbStates[numBoxes] = ' ';
458
459 if (wstrlen(cbInfo[numBoxes].tag,-1) > (unsigned int)maxWidth)
460 maxWidth = wstrlen(cbInfo[numBoxes].tag,-1);
461
462 numBoxes++;
649a0152 463 }
464
465 form = newtForm(NULL, NULL, 0);
466
467 tb = textbox(height - 3 - buttonHeight - listHeight, width - 2,
6f481af2 468 text, flags, &top);
649a0152 469
470 if (listHeight < numBoxes) {
6f481af2 471 sb = newtVerticalScrollbar(width - 4,
472 top + 1,
473 listHeight, NEWT_COLORSET_CHECKBOX,
474 NEWT_COLORSET_ACTCHECKBOX);
475 newtFormAddComponent(form, sb);
649a0152 476 }
477 subform = newtForm(sb, NULL, 0);
478 newtFormSetBackground(subform, NEWT_COLORSET_CHECKBOX);
479
52b75483
ML
480 if (flags & FLAG_NOTAGS)
481 snprintf(format, MAXFORMAT, "%%.0s%%s");
482 else
483 snprintf(format, MAXFORMAT, "%%-%ds %%s", maxWidth);
484
649a0152 485 for (i = 0; i < numBoxes; i++) {
6f481af2 486 snprintf(buf, MAXBUF, format, cbInfo[i].tag, cbInfo[i].text);
649a0152 487
6f481af2 488 if (useRadio)
489 cbInfo[i].comp = newtRadiobutton(4, top + 1 + i, buf,
490 cbStates[i] != ' ',
491 i ? cbInfo[i - 1].comp : NULL);
492 else
493 cbInfo[i].comp = newtCheckbox(4, top + 1 + i, buf,
494 cbStates[i], NULL, cbStates + i);
649a0152 495
feef2cb5 496 newtCheckboxSetFlags(cbInfo[i].comp, NEWT_FLAG_RETURNEXIT, NEWT_FLAGS_SET);
6f481af2 497 newtFormAddComponent(subform, cbInfo[i].comp);
649a0152 498 }
499
500 newtFormSetHeight(subform, listHeight);
501 newtFormSetWidth(subform, width - 10);
502
503 newtFormAddComponents(form, tb, subform, NULL);
504
505 addButtons(height, width, form, &okay, &cancel, flags);
506
507 answer = newtRunForm(form);
f40f18ae 508 *selections = NULL;
649a0152 509 if (answer == cancel)
6f481af2 510 rc = DLG_CANCEL;
5a34899c 511 else if (answer == NULL)
feef2cb5 512 rc = DLG_ESCAPE;
f40f18ae
ML
513 else {
514 if (useRadio) {
515 answer = newtRadioGetCurrent(cbInfo[0].comp);
516 *selections = malloc(sizeof(char *) * 2);
517 if (*selections == NULL)
9eb2e489 518 goto error;
f40f18ae
ML
519 (*selections)[0] = (*selections)[1] = NULL;
520 for (i = 0; i < numBoxes; i++)
521 if (cbInfo[i].comp == answer) {
522 (*selections)[0] = strdup(cbInfo[i].tag);
523 break;
524 }
525 } else {
526 numSelected = 0;
527 for (i = 0; i < numBoxes; i++) {
528 if (cbStates[i] != ' ') numSelected++;
6f481af2 529 }
649a0152 530
f40f18ae
ML
531 *selections = malloc(sizeof(char *) * (numSelected + 1));
532 if (*selections == NULL)
9eb2e489 533 goto error;
649a0152 534
f40f18ae
ML
535 numSelected = 0;
536 for (i = 0; i < numBoxes; i++) {
537 if (cbStates[i] != ' ')
538 (*selections)[numSelected++] = strdup(cbInfo[i].tag);
539 }
649a0152 540
f40f18ae
ML
541 (*selections)[numSelected] = NULL;
542 }
9eb2e489
ML
543
544 rc = DLG_OKAY;
649a0152 545 }
546
9eb2e489 547error:
73f1f27f
ML
548 free(cbInfo);
549 free(cbStates);
9eb2e489
ML
550 if (form)
551 newtFormDestroy(form);
3341bdc5 552
649a0152 553 return rc;
554}
555
eb0a0545 556int messageBox(const char * text, int height, int width, int type, int flags) {
649a0152 557 newtComponent form, yes, tb, answer;
558 newtComponent no = NULL;
feef2cb5 559 int rc = DLG_OKAY;
649a0152 560 int tFlag = (flags & FLAG_SCROLL_TEXT) ? NEWT_FLAG_SCROLL : 0;
561
562 form = newtForm(NULL, NULL, 0);
563
564 tb = newtTextbox(1, 1, width - 2, height - 3 - buttonHeight,
6f481af2 565 NEWT_FLAG_WRAP | tFlag);
649a0152 566 newtTextboxSetText(tb, text);
567
568 newtFormAddComponent(form, tb);
569
139f06bc 570 switch ( type ) {
571 case MSGBOX_INFO:
6f481af2 572 break;
139f06bc 573 case MSGBOX_MSG:
feef2cb5 574 // FIXME Do something about the hard-coded constants
575 yes = makeButton((width - 8) / 2, height - 1 - buttonHeight,
60d3c50b 576 getButtonText(BUTTON_OK));
6f481af2 577 newtFormAddComponent(form, yes);
578 break;
139f06bc 579 default:
feef2cb5 580 yes = makeButton((width - 16) / 3, height - 1 - buttonHeight,
60d3c50b 581 getButtonText(BUTTON_YES));
6f481af2 582 no = makeButton(((width - 16) / 3) * 2 + 9, height - 1 - buttonHeight,
60d3c50b 583 getButtonText(BUTTON_NO));
6f481af2 584 newtFormAddComponents(form, yes, no, NULL);
649a0152 585
6f481af2 586 if (flags & FLAG_DEFAULT_NO)
587 newtFormSetCurrent(form, no);
649a0152 588 }
589
139f06bc 590 if ( type != MSGBOX_INFO ) {
feef2cb5 591 if (newtRunForm(form) == NULL)
592 rc = DLG_ESCAPE;
139f06bc 593
6f481af2 594 answer = newtFormGetCurrent(form);
139f06bc 595
6f481af2 596 if (answer == no)
5a34899c 597 rc = DLG_CANCEL;
139f06bc 598 }
599 else {
6f481af2 600 newtDrawForm(form);
601 newtRefresh();
139f06bc 602 }
649a0152 603
3341bdc5 604 newtFormDestroy(form);
649a0152 605
feef2cb5 606 return rc;
649a0152 607}
608
609void useFullButtons(int state) {
610 if (state) {
6f481af2 611 buttonHeight = 3;
612 makeButton = newtButton;
649a0152 613 } else {
6f481af2 614 buttonHeight = 1;
615 makeButton = newtCompactButton;
649a0152 616 }
617}
60d3c50b 618
619void setButtonText(const char * text, int button) {
620 if (button < 0 || button >= BUTTONS)
621 return;
622 buttonText[button] = text;
623}