]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bsddialog/lib/datebox.c
zfs: merge openzfs/zfs@e0bd8118d
[FreeBSD/FreeBSD.git] / contrib / bsddialog / lib / datebox.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022-2023 Alfonso Sabato Siciliano
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <curses.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "bsddialog.h"
33 #include "bsddialog_theme.h"
34 #include "lib_util.h"
35
36 /* Calendar */
37 #define MIN_YEAR_CAL   0
38 #define MAX_YEAR_CAL   999999999
39 #define MINHCAL        13
40 #define MINWCAL        36 /* 34 calendar, 1 + 1 margins */
41 /* Datebox */
42 #define MIN_YEAR_DATE  0
43 #define MAX_YEAR_DATE  9999
44 #define MINWDATE       23 /* 3 windows and their borders */
45
46 #define ISLEAP(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
47
48 static int minyear;
49 static int maxyear;
50
51 static const char *m[12] = {
52         "January",
53         "February",
54         "March",
55         "April",
56         "May",
57         "June",
58         "July",
59         "August",
60         "September",
61         "October",
62         "November",
63         "December"
64 };
65
66 enum operation {
67         UP_DAY,
68         DOWN_DAY,
69         LEFT_DAY,
70         RIGHT_DAY,
71         UP_MONTH,
72         DOWN_MONTH,
73         UP_YEAR,
74         DOWN_YEAR
75 };
76
77 /* private datebox item */
78 struct dateitem {
79         enum operation up;
80         enum operation down;
81         WINDOW *win;
82         int width;
83         const char *fmt;
84         int *value;
85 };
86
87 static int month_days(int yy, int mm)
88 {
89         int days;
90
91         if (mm == 2)
92                 days = ISLEAP(yy) ? 29 : 28;
93         else if (mm == 4 || mm == 6 || mm == 9 || mm == 11)
94                 days = 30;
95         else
96                 days = 31;
97
98         return (days);
99 }
100
101 static int week_day(int yy, int mm, int dd)
102 {
103         int wd;
104
105         dd += mm < 3 ? yy-- : yy - 2;
106         wd = 23*mm/9 + dd + 4 + yy/4 - yy/100 + yy/400;
107         wd %= 7;
108
109         return (wd);
110 }
111
112 static void
113 init_date(unsigned int *year, unsigned int *month, unsigned int *day, int *yy,
114     int *mm, int *dd)
115 {
116         *yy = MIN(*year, (unsigned int)maxyear);
117         if (*yy < minyear)
118                 *yy = minyear;
119         *mm = MIN(*month, 12);
120         if (*mm == 0)
121                 *mm = 1;
122         *dd = (*day == 0) ? 1 : *day;
123         if(*dd > month_days(*yy, *mm))
124                 *dd = month_days(*yy, *mm);
125 }
126
127 static void datectl(enum operation op, int *yy, int *mm, int *dd)
128 {
129         int ndays;
130
131         ndays = month_days(*yy, *mm);
132
133         switch (op) {
134         case UP_DAY:
135                 if (*dd > 7)
136                         *dd -= 7;
137                 else {
138                         if (*mm == 1) {
139                                 *yy -= 1;
140                                 *mm = 12;
141                         } else
142                                 *mm -= 1;
143                         ndays = month_days(*yy, *mm);
144                         *dd = ndays - abs(7 - *dd);
145                 }
146                 break;
147         case DOWN_DAY:
148                 if (*dd + 7 < ndays)
149                         *dd += 7;
150                 else {
151                         if (*mm == 12) {
152                                 *yy += 1;
153                                 *mm = 1;
154                         } else
155                                 *mm += 1;
156                         *dd = *dd + 7 - ndays;
157                 }
158                 break;
159         case LEFT_DAY:
160                 if (*dd > 1)
161                         *dd -= 1;
162                 else {
163                         if (*mm == 1) {
164                                 *yy -= 1;
165                                 *mm = 12;
166                         } else
167                                 *mm -= 1;
168                         *dd = month_days(*yy, *mm);
169                 }
170                 break;
171         case RIGHT_DAY:
172                 if (*dd < ndays)
173                         *dd += 1;
174                 else {
175                         if (*mm == 12) {
176                                 *yy += 1;
177                                 *mm = 1;
178                         } else
179                                 *mm += 1;
180                         *dd = 1;
181                 }
182                 break;
183         case UP_MONTH:
184                 if (*mm == 1) {
185                         *mm = 12;
186                         *yy -= 1;
187                 } else
188                         *mm -= 1;
189                 ndays = month_days(*yy, *mm);
190                 if (*dd > ndays)
191                         *dd = ndays;
192                 break;
193         case DOWN_MONTH:
194                 if (*mm == 12) {
195                         *mm = 1;
196                         *yy += 1;
197                 } else
198                         *mm += 1;
199                 ndays = month_days(*yy, *mm);
200                 if (*dd > ndays)
201                         *dd = ndays;
202                 break;
203         case UP_YEAR:
204                 *yy -= 1;
205                 ndays = month_days(*yy, *mm);
206                 if (*dd > ndays)
207                         *dd = ndays;
208                 break;
209         case DOWN_YEAR:
210                 *yy += 1;
211                 ndays = month_days(*yy, *mm);
212                 if (*dd > ndays)
213                         *dd = ndays;
214                 break;
215         }
216
217         if (*yy < minyear) {
218                 *yy = minyear;
219                 *mm = 1;
220                 *dd = 1;
221         }
222         if (*yy > maxyear) {
223                 *yy = maxyear;
224                 *mm = 12;
225                 *dd = 31;
226         }
227 }
228
229 static void
230 drawsquare(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev,
231     const char *fmt, int value, bool focus)
232 {
233         int h, l, w;
234
235         getmaxyx(win, h, w);
236         draw_borders(conf, win, elev);
237         if (focus) {
238                 l = 2 + w%2;
239                 wattron(win, t.dialog.arrowcolor);
240                 mvwhline(win, 0, w/2 - l/2,
241                     conf->ascii_lines ? '^' : ACS_UARROW, l);
242                 mvwhline(win, h-1, w/2 - l/2,
243                     conf->ascii_lines ? 'v' : ACS_DARROW, l);
244                 wattroff(win, t.dialog.arrowcolor);
245         }
246
247         if (focus)
248                 wattron(win, t.menu.f_namecolor);
249         if (strchr(fmt, 's') != NULL)
250                 mvwprintw(win, 1, 1, fmt, m[value - 1]);
251         else
252                 mvwprintw(win, 1, 1, fmt, value);
253         if (focus)
254                 wattroff(win, t.menu.f_namecolor);
255
256         wnoutrefresh(win);
257 }
258
259 static void
260 print_calendar(struct bsddialog_conf *conf, WINDOW *win, int yy, int mm, int dd,
261     bool active)
262 {
263         int ndays, i, y, x, wd, h, w;
264
265         getmaxyx(win, h, w);
266         wclear(win);
267         draw_borders(conf, win, RAISED);
268         if (active) {
269                 wattron(win, t.dialog.arrowcolor);
270                 mvwhline(win, 0, 15, conf->ascii_lines ? '^' : ACS_UARROW, 4);
271                 mvwhline(win, h-1, 15, conf->ascii_lines ? 'v' : ACS_DARROW, 4);
272                 mvwvline(win, 3, 0, conf->ascii_lines ? '<' : ACS_LARROW, 3);
273                 mvwvline(win, 3, w-1, conf->ascii_lines ? '>' : ACS_RARROW, 3);
274                 wattroff(win, t.dialog.arrowcolor);
275         }
276
277         mvwaddstr(win, 1, 5, "Sun Mon Tue Wed Thu Fri Sat");
278         ndays = month_days(yy, mm);
279         y = 2;
280         wd = week_day(yy, mm, 1);
281         for (i = 1; i <= ndays; i++) {
282                 x = 5 + (4 * wd); /* x has to be 6 with week number */
283                 wmove(win, y, x);
284                 mvwprintw(win, y, x, "%2d", i);
285                 if (i == dd) {
286                         wattron(win, t.menu.f_namecolor);
287                         mvwprintw(win, y, x, "%2d", i);
288                         wattroff(win, t.menu.f_namecolor);
289                 }
290                 wd++;
291                 if (wd > 6) {
292                         wd = 0;
293                         y++;
294                 }
295         }
296
297         wnoutrefresh(win);
298 }
299
300 static int
301 calendar_redraw(struct dialog *d, WINDOW *yy_win, WINDOW *mm_win,
302     WINDOW *dd_win)
303 {
304         int ycal, xcal;
305
306         if (d->built) {
307                 hide_dialog(d);
308                 refresh(); /* Important for decreasing screen */
309         }
310         if (dialog_size_position(d, MINHCAL, MINWCAL, NULL) != 0)
311                 return (BSDDIALOG_ERROR);
312         if (draw_dialog(d) != 0)
313                 return (BSDDIALOG_ERROR);
314         if (d->built)
315                 refresh(); /* Important to fix grey lines expanding screen */
316         TEXTPAD(d, MINHCAL + HBUTTONS);
317
318         ycal = d->y + d->h - 15;
319         xcal = d->x + d->w/2 - 17;
320         mvwaddstr(d->widget, d->h - 16, d->w/2 - 17, "Month");
321         update_box(d->conf, mm_win, ycal, xcal, 3, 17, RAISED);
322         mvwaddstr(d->widget, d->h - 16, d->w/2, "Year");
323         update_box(d->conf, yy_win, ycal, xcal + 17, 3, 17, RAISED);
324         update_box(d->conf, dd_win, ycal + 3, xcal, 9, 34, RAISED);
325         wnoutrefresh(d->widget);
326
327         return (0);
328 }
329
330 int
331 bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows,
332     int cols, unsigned int *year, unsigned int *month, unsigned int *day)
333 {
334         bool loop, focusbuttons;
335         int retval, sel, yy, mm, dd;
336         wint_t input;
337         WINDOW *yy_win, *mm_win, *dd_win;
338         struct dialog d;
339
340         CHECK_PTR(year);
341         CHECK_PTR(month);
342         CHECK_PTR(day);
343         minyear = MIN_YEAR_CAL;
344         maxyear = MAX_YEAR_CAL;
345         init_date(year, month, day, &yy, &mm, &dd);
346
347         if (prepare_dialog(conf, text, rows, cols, &d) != 0)
348                 return (BSDDIALOG_ERROR);
349         set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
350         if ((yy_win = newwin(1, 1, 1, 1)) == NULL)
351                 RETURN_ERROR("Cannot build WINDOW for yy");
352         wbkgd(yy_win, t.dialog.color);
353         if ((mm_win = newwin(1, 1, 1, 1)) == NULL)
354                 RETURN_ERROR("Cannot build WINDOW for mm");
355         wbkgd(mm_win, t.dialog.color);
356         if ((dd_win = newwin(1, 1, 1, 1)) == NULL)
357                 RETURN_ERROR("Cannot build WINDOW for dd");
358         wbkgd(dd_win, t.dialog.color);
359         if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0)
360                 return (BSDDIALOG_ERROR);
361
362         sel = -1;
363         loop = focusbuttons = true;
364         while (loop) {
365                 drawsquare(conf, mm_win, RAISED, "%15s", mm, sel == 0);
366                 drawsquare(conf, yy_win, RAISED, "%15d", yy, sel == 1);
367                 print_calendar(conf, dd_win, yy, mm, dd, sel == 2);
368                 doupdate();
369
370                 if (get_wch(&input) == ERR)
371                         continue;
372                 switch(input) {
373                 case KEY_ENTER:
374                 case 10: /* Enter */
375                         if (focusbuttons || conf->button.always_active) {
376                                 retval = BUTTONVALUE(d.bs);
377                                 loop = false;
378                         }
379                         break;
380                 case 27: /* Esc */
381                         if (conf->key.enable_esc) {
382                                 retval = BSDDIALOG_ESC;
383                                 loop = false;
384                         }
385                         break;
386                 case '\t': /* TAB */
387                         if (focusbuttons) {
388                                 d.bs.curr++;
389                                 if (d.bs.curr >= (int)d.bs.nbuttons) {
390                                         focusbuttons = false;
391                                         sel = 0;
392                                         d.bs.curr = conf->button.always_active ?
393                                             0 : -1;
394                                 }
395                         } else {
396                                 sel++;
397                                 if (sel > 2) {
398                                         focusbuttons = true;
399                                         sel = -1;
400                                         d.bs.curr = 0;
401                                 }
402                         }
403                         DRAW_BUTTONS(d);
404                         break;
405                 case KEY_RIGHT:
406                         if (focusbuttons) {
407                                 d.bs.curr++;
408                                 if (d.bs.curr >= (int)d.bs.nbuttons) {
409                                         focusbuttons = false;
410                                         sel = 0;
411                                         d.bs.curr = conf->button.always_active ?
412                                             0 : -1;
413                                 }
414                         } else if (sel == 2) {
415                                 datectl(RIGHT_DAY, &yy, &mm, &dd);
416                         } else { /* Month or Year*/
417                                 sel++;
418                         }
419                         DRAW_BUTTONS(d);
420                         break;
421                 case KEY_LEFT:
422                         if (focusbuttons) {
423                                 d.bs.curr--;
424                                 if (d.bs.curr < 0) {
425                                         focusbuttons = false;
426                                         sel = 2;
427                                         d.bs.curr = conf->button.always_active ?
428                                             0 : -1;
429                                 }
430                         } else if (sel == 2) {
431                                 datectl(LEFT_DAY, &yy, &mm, &dd);
432                         } else if (sel == 1) {
433                                 sel = 0;
434                         } else { /* sel = 0, Month */
435                                 focusbuttons = true;
436                                 sel = -1;
437                                 d.bs.curr = 0;
438                         }
439                         DRAW_BUTTONS(d);
440                         break;
441                 case KEY_UP:
442                         if (focusbuttons) {
443                                 sel = 2;
444                                 focusbuttons = false;
445                                 d.bs.curr = conf->button.always_active ? 0 : -1;
446                                 DRAW_BUTTONS(d);
447                         } else if (sel == 0) {
448                                 datectl(UP_MONTH, &yy, &mm, &dd);
449                         } else if (sel == 1) {
450                                 datectl(UP_YEAR, &yy, &mm, &dd);
451                         } else { /* sel = 2 */
452                                 datectl(UP_DAY, &yy, &mm, &dd);
453                         }
454                         break;
455                 case KEY_DOWN:
456                         if (focusbuttons) {
457                                 break;
458                         } else if (sel == 0) {
459                                 datectl(DOWN_MONTH, &yy, &mm, &dd);
460                         } else if (sel == 1) {
461                                 datectl(DOWN_YEAR, &yy, &mm, &dd);
462                         } else { /* sel = 2 */
463                                 datectl(DOWN_DAY, &yy, &mm, &dd);
464                         }
465                         break;
466                 case KEY_HOME:
467                         datectl(UP_MONTH, &yy, &mm, &dd);
468                         break;
469                 case KEY_END:
470                         datectl(DOWN_MONTH, &yy, &mm, &dd);
471                         break;
472                 case KEY_PPAGE:
473                         datectl(UP_YEAR, &yy, &mm, &dd);
474                         break;
475                 case KEY_NPAGE:
476                         datectl(DOWN_YEAR, &yy, &mm, &dd);
477                         break;
478                 case KEY_F(1):
479                         if (conf->key.f1_file == NULL &&
480                             conf->key.f1_message == NULL)
481                                 break;
482                         if (f1help_dialog(conf) != 0)
483                                 return (BSDDIALOG_ERROR);
484                         if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0)
485                                 return (BSDDIALOG_ERROR);
486                         break;
487                 case KEY_RESIZE:
488                         if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0)
489                                 return (BSDDIALOG_ERROR);
490                         break;
491                 default:
492                         if (shortcut_buttons(input, &d.bs)) {
493                                 DRAW_BUTTONS(d);
494                                 doupdate();
495                                 retval = BUTTONVALUE(d.bs);
496                                 loop = false;
497                         }
498                 }
499         }
500
501         *year  = yy;
502         *month = mm;
503         *day   = dd;
504
505         delwin(yy_win);
506         delwin(mm_win);
507         delwin(dd_win);
508         end_dialog(&d);
509
510         return (retval);
511 }
512
513 static int datebox_redraw(struct dialog *d, struct dateitem *di)
514 {
515         int y, x;
516
517         if (d->built) {
518                 hide_dialog(d);
519                 refresh(); /* Important for decreasing screen */
520         }
521         if (dialog_size_position(d, 3 /*windows*/, MINWDATE, NULL) != 0)
522                 return (BSDDIALOG_ERROR);
523         if (draw_dialog(d) != 0)
524                 return (BSDDIALOG_ERROR);
525         if (d->built)
526                 refresh(); /* Important to fix grey lines expanding screen */
527         TEXTPAD(d, 3 /*windows*/ + HBUTTONS);
528
529         y = d->y + d->h - 6;
530         x = (d->x + d->w / 2) - 11;
531         update_box(d->conf, di[0].win, y, x, 3, di[0].width, LOWERED);
532         mvwaddch(d->widget, d->h - 5, x - d->x + di[0].width, '/');
533         x += di[0].width + 1;
534         update_box(d->conf, di[1].win, y, x , 3, di[1].width, LOWERED);
535         mvwaddch(d->widget, d->h - 5, x - d->x + di[1].width, '/');
536         x += di[1].width + 1;
537         update_box(d->conf, di[2].win, y, x, 3, di[2].width, LOWERED);
538         wnoutrefresh(d->widget);
539
540         return (0);
541 }
542
543 static int
544 build_dateitem(const char *format, int *yy, int *mm, int *dd,
545     struct dateitem *dt)
546 {
547         int i;
548         wchar_t *wformat;
549         struct dateitem init[3] = {
550                 {UP_YEAR,  DOWN_YEAR,  NULL, 6,  "%4d",  yy},
551                 {UP_MONTH, DOWN_MONTH, NULL, 11, "%9s",  mm},
552                 {LEFT_DAY, RIGHT_DAY,  NULL, 4,  "%02d", dd},
553         };
554
555         for (i = 0; i < 3; i++) {
556                 if ((init[i].win = newwin(1, 1, 1, 1)) == NULL)
557                         RETURN_FMTERROR("Cannot build WINDOW dateitem[%d]", i);
558                 wbkgd(init[i].win, t.dialog.color);
559         }
560
561         if ((wformat = alloc_mbstows(CHECK_STR(format))) == NULL)
562                 RETURN_ERROR("Cannot allocate conf.date.format in wchar_t*");
563         if (format == NULL || wcscmp(wformat, L"d/m/y") == 0) {
564                 dt[0] = init[2];
565                 dt[1] = init[1];
566                 dt[2] = init[0];
567         } else if (wcscmp(wformat, L"m/d/y") == 0) {
568                 dt[0] = init[1];
569                 dt[1] = init[2];
570                 dt[2] = init[0];
571         } else if (wcscmp(wformat, L"y/m/d") == 0) {
572                 dt[0] = init[0];
573                 dt[1] = init[1];
574                 dt[2] = init[2];
575         } else
576                 RETURN_FMTERROR("Invalid conf.date.format=\"%s\"", format);
577         free(wformat);
578
579         return (0);
580 }
581
582 int
583 bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows,
584     int cols, unsigned int *year, unsigned int *month, unsigned int *day)
585 {
586         bool loop, focusbuttons;
587         int retval, i, sel, yy, mm, dd;
588         wint_t input;
589         struct dateitem  di[3];
590         struct dialog d;
591
592         CHECK_PTR(year);
593         CHECK_PTR(month);
594         CHECK_PTR(day);
595         minyear = MIN_YEAR_DATE;
596         maxyear = MAX_YEAR_DATE;
597         init_date(year, month, day, &yy, &mm, &dd);
598
599         if (prepare_dialog(conf, text, rows, cols, &d) != 0)
600                 return (BSDDIALOG_ERROR);
601         set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
602         if (build_dateitem(conf->date.format, &yy, &mm, &dd, di) != 0)
603                 return (BSDDIALOG_ERROR);
604         if (datebox_redraw(&d, di) != 0)
605                 return (BSDDIALOG_ERROR);
606
607         sel = -1;
608         loop = focusbuttons = true;
609         while (loop) {
610                 for (i = 0; i < 3; i++)
611                         drawsquare(conf, di[i].win, LOWERED, di[i].fmt,
612                             *di[i].value, sel == i);
613                 doupdate();
614
615                 if (get_wch(&input) == ERR)
616                         continue;
617                 switch(input) {
618                 case KEY_ENTER:
619                 case 10: /* Enter */
620                         if (focusbuttons || conf->button.always_active) {
621                                 retval = BUTTONVALUE(d.bs);
622                                 loop = false;
623                         }
624                         break;
625                 case 27: /* Esc */
626                         if (conf->key.enable_esc) {
627                                 retval = BSDDIALOG_ESC;
628                                 loop = false;
629                         }
630                         break;
631                 case KEY_RIGHT:
632                 case '\t': /* TAB */
633                         if (focusbuttons) {
634                                 d.bs.curr++;
635                                 focusbuttons = d.bs.curr < (int)d.bs.nbuttons ?
636                                     true : false;
637                                 if (focusbuttons == false) {
638                                         sel = 0;
639                                         d.bs.curr = conf->button.always_active ?
640                                             0 : -1;
641                                 }
642                         } else {
643                                 sel++;
644                                 focusbuttons = sel > 2 ? true : false;
645                                 if (focusbuttons) {
646                                         d.bs.curr = 0;
647                                 }
648                         }
649                         DRAW_BUTTONS(d);
650                         break;
651                 case KEY_LEFT:
652                         if (focusbuttons) {
653                                 d.bs.curr--;
654                                 focusbuttons = d.bs.curr < 0 ? false : true;
655                                 if (focusbuttons == false) {
656                                         sel = 2;
657                                         d.bs.curr = conf->button.always_active ?
658                                             0 : -1;
659                                 }
660                         } else {
661                                 sel--;
662                                 focusbuttons = sel < 0 ? true : false;
663                                 if (focusbuttons)
664                                         d.bs.curr = (int)d.bs.nbuttons - 1;
665                         }
666                         DRAW_BUTTONS(d);
667                         break;
668                 case KEY_UP:
669                         if (focusbuttons) {
670                                 sel = 0;
671                                 focusbuttons = false;
672                                 d.bs.curr = conf->button.always_active ? 0 : -1;
673                                 DRAW_BUTTONS(d);
674                         } else {
675                                 datectl(di[sel].up, &yy, &mm, &dd);
676                         }
677                         break;
678                 case KEY_DOWN:
679                         if (focusbuttons)
680                                 break;
681                         datectl(di[sel].down, &yy, &mm, &dd);
682                         break;
683                 case KEY_F(1):
684                         if (conf->key.f1_file == NULL &&
685                             conf->key.f1_message == NULL)
686                                 break;
687                         if (f1help_dialog(conf) != 0)
688                                 return (BSDDIALOG_ERROR);
689                         if (datebox_redraw(&d, di) != 0)
690                                 return (BSDDIALOG_ERROR);
691                         break;
692                 case KEY_RESIZE:
693                         if (datebox_redraw(&d, di) != 0)
694                                 return (BSDDIALOG_ERROR);
695                         break;
696                 default:
697                         if (shortcut_buttons(input, &d.bs)) {
698                                 DRAW_BUTTONS(d);
699                                 doupdate();
700                                 retval = BUTTONVALUE(d.bs);
701                                 loop = false;
702                         }
703                 }
704         }
705
706         *year  = yy;
707         *month = mm;
708         *day   = dd;
709
710         for (i = 0; i < 3 ; i++)
711                 delwin(di[i].win);
712         end_dialog(&d);
713
714         return (retval);
715 }