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