]> git.ipfire.org Git - thirdparty/newt.git/blame - dialogboxes.c
define env NEWT_NOFLOWCTRL to disable flow control
[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
eb0a0545 257int listBox(const char * text, int height, int width, 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 listHeight;
264 int numItems = 0;
265 int allocedItems = 5;
266 int i, top;
9eb2e489 267 int rc = DLG_ERROR;
feef2cb5 268 char buf[MAXBUF];
649a0152 269 int maxTagWidth = 0;
270 int maxTextWidth = 0;
feef2cb5 271 int defItem = -1;
394c9a80 272 int scrollFlag;
feef2cb5 273 int lineWidth, textWidth, tagWidth;
649a0152 274 struct {
6f481af2 275 const char * text;
276 const char * tag;
649a0152 277 } * itemInfo = malloc(allocedItems * sizeof(*itemInfo));
9eb2e489 278 void * tmp;
649a0152 279
9eb2e489
ML
280 if (itemInfo == NULL)
281 goto error;
282 if (!(arg = poptGetArg(optCon)))
283 goto error;
649a0152 284 listHeight = strtoul(arg, &end, 10);
9eb2e489
ML
285 if (*end)
286 goto error;
649a0152 287
288 while ((arg = poptGetArg(optCon))) {
6f481af2 289 if (allocedItems == numItems) {
290 allocedItems += 5;
9eb2e489
ML
291 tmp = realloc(itemInfo, sizeof(*itemInfo) * allocedItems);
292 if (tmp == NULL)
293 goto error;
294 itemInfo = tmp;
6f481af2 295 }
296
297 itemInfo[numItems].tag = arg;
feef2cb5 298 if (default_item && (strcmp(default_item, arg) == 0)) {
299 defItem = numItems;
300 }
9eb2e489
ML
301 if (!(arg = poptGetArg(optCon)))
302 goto error;
6f481af2 303
304 if (!(flags & FLAG_NOITEM)) {
305 itemInfo[numItems].text = arg;
306 } else
307 itemInfo[numItems].text = "";
308
309 if (wstrlen(itemInfo[numItems].text,-1) > (unsigned int)maxTextWidth)
310 maxTextWidth = wstrlen(itemInfo[numItems].text,-1);
311 if (wstrlen(itemInfo[numItems].tag,-1) > (unsigned int)maxTagWidth)
312 maxTagWidth = wstrlen(itemInfo[numItems].tag,-1);
313
314 numItems++;
649a0152 315 }
b3ddfdd7 316 if (numItems == 0)
9eb2e489 317 goto error;
649a0152 318
57cb49e7 319 if (flags & FLAG_NOTAGS) {
6f481af2 320 maxTagWidth = 0;
57cb49e7 321 }
322
649a0152 323 form = newtForm(NULL, NULL, 0);
324
325 tb = textbox(height - 4 - buttonHeight - listHeight, width - 2,
6f481af2 326 text, flags, &top);
649a0152 327
328 if (listHeight >= numItems) {
6f481af2 329 scrollFlag = 0;
330 i = 0;
649a0152 331 } else {
6f481af2 332 scrollFlag = NEWT_FLAG_SCROLL;
333 i = 2;
649a0152 334 }
335
10bbfd28 336 lineWidth = min(maxTagWidth + maxTextWidth + i + 1, SLtt_Screen_Cols - 6);
feef2cb5 337 listBox = newtListbox( (width - lineWidth) / 2 , top + 1, listHeight,
338 NEWT_FLAG_RETURNEXIT | scrollFlag);
649a0152 339
feef2cb5 340 textWidth = maxTextWidth;
341 tagWidth = maxTagWidth;
342 if (maxTextWidth == 0) {
343 tagWidth = lineWidth;
344 } else {
10bbfd28
ML
345 tagWidth++;
346 textWidth++;
347 while (textWidth + tagWidth + i > lineWidth) {
348 if (textWidth >= tagWidth && textWidth > 0)
349 textWidth--;
350 else if (tagWidth > 0)
351 tagWidth--;
352 else
353 break;
354 }
649a0152 355 }
356
feef2cb5 357 if (!(flags & FLAG_NOTAGS)) {
358 for (i = 0; i < numItems; i++) {
359 int w = tagWidth;
360 int len, j;
361 len = mystrncpyw(buf, itemInfo[i].tag, MAXBUF, &w);
362 for (j = 0; j < tagWidth - w; j++) {
3341bdc5
ML
363 if (len + 1 >= MAXBUF)
364 break;
feef2cb5 365 buf[len++] = ' ';
366 }
367 buf[len] = '\0';
368 w = textWidth;
369 mystrncpyw(buf + len, itemInfo[i].text, MAXBUF-len, &w);
8bec7d99 370 newtListboxAddEntry(listBox, buf, (void *)(long) i);
feef2cb5 371 }
372 } else {
373 for (i = 0; i < numItems; i++) {
374 snprintf(buf, MAXBUF, "%s", itemInfo[i].text);
8bec7d99 375 newtListboxAddEntry(listBox, buf, (void *)(long) i);
feef2cb5 376 }
377 }
378
379 if (defItem != -1)
380 newtListboxSetCurrent (listBox, defItem);
381
649a0152 382 newtFormAddComponents(form, tb, listBox, NULL);
383
384 addButtons(height, width, form, &okay, &cancel, flags);
385
386 answer = newtRunForm(form);
f40f18ae 387 *result = NULL;
649a0152 388 if (answer == cancel)
6f481af2 389 rc = DLG_CANCEL;
5a34899c 390 else if (answer == NULL)
feef2cb5 391 rc = DLG_ESCAPE;
f40f18ae
ML
392 else {
393 i = (long) newtListboxGetCurrent(listBox);
394 *result = strdup(itemInfo[i].tag);
9eb2e489
ML
395 if (*result == NULL)
396 goto error;
397 rc = DLG_OKAY;
f40f18ae 398 }
649a0152 399
9eb2e489
ML
400error:
401 if (form)
402 newtFormDestroy(form);
3341bdc5
ML
403 free(itemInfo);
404
649a0152 405 return rc;
406}
407
eb0a0545 408int checkList(const char * text, int height, int width, poptContext optCon,
f40f18ae 409 int useRadio, int flags, char *** selections) {
9eb2e489 410 newtComponent form = NULL, okay, tb, subform, answer;
649a0152 411 newtComponent sb = NULL, cancel = NULL;
eb0a0545 412 const char * arg;
413 char * end;
649a0152 414 int listHeight;
415 int numBoxes = 0;
416 int allocedBoxes = 5;
417 int i;
418 int numSelected;
9eb2e489 419 int rc = DLG_ERROR;
feef2cb5 420 char buf[MAXBUF], format[MAXFORMAT];
649a0152 421 int maxWidth = 0;
422 int top;
423 struct {
6f481af2 424 const char * text;
425 const char * tag;
426 newtComponent comp;
649a0152 427 } * cbInfo = malloc(allocedBoxes * sizeof(*cbInfo));
3341bdc5 428 char * cbStates = malloc(allocedBoxes * sizeof(*cbStates));
9eb2e489 429 void * tmp;
649a0152 430
9eb2e489
ML
431 if (cbInfo == NULL || cbStates == NULL)
432 goto error;
433 if (!(arg = poptGetArg(optCon)))
434 goto error;
649a0152 435 listHeight = strtoul(arg, &end, 10);
9eb2e489
ML
436 if (*end)
437 goto error;
649a0152 438
439 while ((arg = poptGetArg(optCon))) {
6f481af2 440 if (allocedBoxes == numBoxes) {
441 allocedBoxes += 5;
9eb2e489
ML
442
443 tmp = realloc(cbInfo, sizeof(*cbInfo) * allocedBoxes);
444 if (tmp == NULL)
445 goto error;
446 cbInfo = tmp;
447
448 tmp = realloc(cbStates, sizeof(*cbStates) * allocedBoxes);
449 if (tmp == NULL)
450 goto error;
451 cbStates = tmp;
6f481af2 452 }
453
454 cbInfo[numBoxes].tag = arg;
9eb2e489
ML
455 if (!(arg = poptGetArg(optCon)))
456 goto error;
6f481af2 457
458 if (!(flags & FLAG_NOITEM)) {
459 cbInfo[numBoxes].text = arg;
9eb2e489
ML
460 if (!(arg = poptGetArg(optCon)))
461 goto error;
6f481af2 462 } else
463 cbInfo[numBoxes].text = "";
464
465 if (!strcmp(arg, "1") || !strcasecmp(arg, "on") ||
466 !strcasecmp(arg, "yes"))
467 cbStates[numBoxes] = '*';
468 else
469 cbStates[numBoxes] = ' ';
470
471 if (wstrlen(cbInfo[numBoxes].tag,-1) > (unsigned int)maxWidth)
472 maxWidth = wstrlen(cbInfo[numBoxes].tag,-1);
473
474 numBoxes++;
649a0152 475 }
476
477 form = newtForm(NULL, NULL, 0);
478
479 tb = textbox(height - 3 - buttonHeight - listHeight, width - 2,
6f481af2 480 text, flags, &top);
649a0152 481
482 if (listHeight < numBoxes) {
6f481af2 483 sb = newtVerticalScrollbar(width - 4,
484 top + 1,
485 listHeight, NEWT_COLORSET_CHECKBOX,
486 NEWT_COLORSET_ACTCHECKBOX);
487 newtFormAddComponent(form, sb);
649a0152 488 }
489 subform = newtForm(sb, NULL, 0);
490 newtFormSetBackground(subform, NEWT_COLORSET_CHECKBOX);
491
52b75483
ML
492 if (flags & FLAG_NOTAGS)
493 snprintf(format, MAXFORMAT, "%%.0s%%s");
494 else
495 snprintf(format, MAXFORMAT, "%%-%ds %%s", maxWidth);
496
649a0152 497 for (i = 0; i < numBoxes; i++) {
6f481af2 498 snprintf(buf, MAXBUF, format, cbInfo[i].tag, cbInfo[i].text);
649a0152 499
6f481af2 500 if (useRadio)
501 cbInfo[i].comp = newtRadiobutton(4, top + 1 + i, buf,
502 cbStates[i] != ' ',
503 i ? cbInfo[i - 1].comp : NULL);
504 else
505 cbInfo[i].comp = newtCheckbox(4, top + 1 + i, buf,
506 cbStates[i], NULL, cbStates + i);
649a0152 507
feef2cb5 508 newtCheckboxSetFlags(cbInfo[i].comp, NEWT_FLAG_RETURNEXIT, NEWT_FLAGS_SET);
6f481af2 509 newtFormAddComponent(subform, cbInfo[i].comp);
649a0152 510 }
511
512 newtFormSetHeight(subform, listHeight);
513 newtFormSetWidth(subform, width - 10);
514
515 newtFormAddComponents(form, tb, subform, NULL);
516
517 addButtons(height, width, form, &okay, &cancel, flags);
518
519 answer = newtRunForm(form);
f40f18ae 520 *selections = NULL;
649a0152 521 if (answer == cancel)
6f481af2 522 rc = DLG_CANCEL;
5a34899c 523 else if (answer == NULL)
feef2cb5 524 rc = DLG_ESCAPE;
f40f18ae
ML
525 else {
526 if (useRadio) {
527 answer = newtRadioGetCurrent(cbInfo[0].comp);
528 *selections = malloc(sizeof(char *) * 2);
529 if (*selections == NULL)
9eb2e489 530 goto error;
f40f18ae
ML
531 (*selections)[0] = (*selections)[1] = NULL;
532 for (i = 0; i < numBoxes; i++)
533 if (cbInfo[i].comp == answer) {
534 (*selections)[0] = strdup(cbInfo[i].tag);
535 break;
536 }
537 } else {
538 numSelected = 0;
539 for (i = 0; i < numBoxes; i++) {
540 if (cbStates[i] != ' ') numSelected++;
6f481af2 541 }
649a0152 542
f40f18ae
ML
543 *selections = malloc(sizeof(char *) * (numSelected + 1));
544 if (*selections == NULL)
9eb2e489 545 goto error;
649a0152 546
f40f18ae
ML
547 numSelected = 0;
548 for (i = 0; i < numBoxes; i++) {
549 if (cbStates[i] != ' ')
550 (*selections)[numSelected++] = strdup(cbInfo[i].tag);
551 }
649a0152 552
f40f18ae
ML
553 (*selections)[numSelected] = NULL;
554 }
9eb2e489
ML
555
556 rc = DLG_OKAY;
649a0152 557 }
558
9eb2e489 559error:
73f1f27f
ML
560 free(cbInfo);
561 free(cbStates);
9eb2e489
ML
562 if (form)
563 newtFormDestroy(form);
3341bdc5 564
649a0152 565 return rc;
566}
567
eb0a0545 568int messageBox(const char * text, int height, int width, int type, int flags) {
649a0152 569 newtComponent form, yes, tb, answer;
570 newtComponent no = NULL;
feef2cb5 571 int rc = DLG_OKAY;
649a0152 572 int tFlag = (flags & FLAG_SCROLL_TEXT) ? NEWT_FLAG_SCROLL : 0;
573
574 form = newtForm(NULL, NULL, 0);
575
576 tb = newtTextbox(1, 1, width - 2, height - 3 - buttonHeight,
6f481af2 577 NEWT_FLAG_WRAP | tFlag);
649a0152 578 newtTextboxSetText(tb, text);
579
580 newtFormAddComponent(form, tb);
581
139f06bc 582 switch ( type ) {
583 case MSGBOX_INFO:
6f481af2 584 break;
139f06bc 585 case MSGBOX_MSG:
feef2cb5 586 // FIXME Do something about the hard-coded constants
587 yes = makeButton((width - 8) / 2, height - 1 - buttonHeight,
60d3c50b 588 getButtonText(BUTTON_OK));
6f481af2 589 newtFormAddComponent(form, yes);
590 break;
139f06bc 591 default:
feef2cb5 592 yes = makeButton((width - 16) / 3, height - 1 - buttonHeight,
60d3c50b 593 getButtonText(BUTTON_YES));
6f481af2 594 no = makeButton(((width - 16) / 3) * 2 + 9, height - 1 - buttonHeight,
60d3c50b 595 getButtonText(BUTTON_NO));
6f481af2 596 newtFormAddComponents(form, yes, no, NULL);
649a0152 597
6f481af2 598 if (flags & FLAG_DEFAULT_NO)
599 newtFormSetCurrent(form, no);
649a0152 600 }
601
139f06bc 602 if ( type != MSGBOX_INFO ) {
feef2cb5 603 if (newtRunForm(form) == NULL)
604 rc = DLG_ESCAPE;
139f06bc 605
6f481af2 606 answer = newtFormGetCurrent(form);
139f06bc 607
6f481af2 608 if (answer == no)
5a34899c 609 rc = DLG_CANCEL;
139f06bc 610 }
611 else {
6f481af2 612 newtDrawForm(form);
613 newtRefresh();
139f06bc 614 }
649a0152 615
3341bdc5 616 newtFormDestroy(form);
649a0152 617
feef2cb5 618 return rc;
649a0152 619}
620
621void useFullButtons(int state) {
622 if (state) {
6f481af2 623 buttonHeight = 3;
624 makeButton = newtButton;
649a0152 625 } else {
6f481af2 626 buttonHeight = 1;
627 makeButton = newtCompactButton;
649a0152 628 }
629}
60d3c50b 630
631void setButtonText(const char * text, int button) {
632 if (button < 0 || button >= BUTTONS)
633 return;
634 buttonText[button] = text;
635}