2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2022 Alfonso Sabato Siciliano
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/param.h>
34 #include "bsddialog.h"
35 #include "bsddialog_theme.h"
39 #define MINWCAL 36 /* 34 calendar, 1 + 1 margins */
41 #define MAXYEAR 999999999
43 static int month_days(int yy, int mm)
48 days = ISLEAP(yy) ? 29 : 28;
49 else if (mm == 4 || mm == 6 || mm == 9 || mm == 11)
68 static void datectl(enum operation op, int *yy, int *mm, int *dd)
72 ndays = month_days(*yy, *mm);
84 ndays = month_days(*yy, *mm);
85 *dd = ndays - abs(7 - *dd);
97 *dd = *dd + 7 - ndays;
109 *dd = month_days(*yy, *mm);
130 ndays = month_days(*yy, *mm);
140 ndays = month_days(*yy, *mm);
146 ndays = month_days(*yy, *mm);
152 ndays = month_days(*yy, *mm);
170 static int week_day(int yy, int mm, int dd)
174 dd += mm < 3 ? yy-- : yy - 2;
175 wd = 23*mm/9 + dd + 4 + yy/4 - yy/100 + yy/400;
182 print_calendar(struct bsddialog_conf *conf, WINDOW *win, int yy, int mm, int dd,
185 int ndays, i, y, x, wd, h, w;
189 draw_borders(conf, win, h, w, RAISED);
191 wattron(win, t.dialog.arrowcolor);
192 mvwhline(win, 0, 15, conf->ascii_lines ? '^' : ACS_UARROW, 4);
193 mvwhline(win, h-1, 15, conf->ascii_lines ? 'v' : ACS_DARROW, 4);
194 mvwvline(win, 3, 0, conf->ascii_lines ? '<' : ACS_LARROW, 3);
195 mvwvline(win, 3, w-1, conf->ascii_lines ? '>' : ACS_RARROW, 3);
196 wattroff(win, t.dialog.arrowcolor);
199 mvwaddstr(win, 1, 5, "Sun Mon Tue Wed Thu Fri Sat");
200 ndays = month_days(yy, mm);
202 wd = week_day(yy, mm, 1);
203 for (i = 1; i <= ndays; i++) {
204 x = 5 + (4 * wd); /* x has to be 6 with week number */
206 mvwprintw(win, y, x, "%2d", i);
208 wattron(win, t.menu.f_namecolor);
209 mvwprintw(win, y, x, "%2d", i);
210 wattroff(win, t.menu.f_namecolor);
223 drawsquare(struct bsddialog_conf *conf, WINDOW *win, const char *fmt,
224 const void *value, bool focus)
229 draw_borders(conf, win, h, w, RAISED);
231 wattron(win, t.dialog.arrowcolor);
232 mvwhline(win, 0, 7, conf->ascii_lines ? '^' : ACS_UARROW, 3);
233 mvwhline(win, 2, 7, conf->ascii_lines ? 'v' : ACS_DARROW, 3);
234 wattroff(win, t.dialog.arrowcolor);
238 wattron(win, t.menu.f_namecolor);
239 if (strchr(fmt, 's') != NULL)
240 mvwprintw(win, 1, 1, fmt, (const char*)value);
242 mvwprintw(win, 1, 1, fmt, *((const int*)value));
244 wattroff(win, t.menu.f_namecolor);
250 calendar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
251 int *w, const char *text, struct buttons bs)
255 if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
256 if (text_size(conf, rows, cols, text, &bs, MINHCAL, MINWCAL,
257 &htext, &wtext) != 0)
258 return (BSDDIALOG_ERROR);
261 if (cols == BSDDIALOG_AUTOSIZE)
262 *w = widget_min_width(conf, wtext, MINWCAL, &bs);
264 if (rows == BSDDIALOG_AUTOSIZE)
265 *h = widget_min_height(conf, htext, MINHCAL, true);
270 static int calendar_checksize(int rows, int cols, struct buttons bs)
274 mincols = MAX(MINWCAL, buttons_min_width(bs));
278 RETURN_ERROR("Few cols for this calendar (at least 38)");
280 if (rows < MINHCAL + 2 + 2) /* 2 buttons + 2 borders */
281 RETURN_ERROR("Few rows for calendar (at least 17)");
287 bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows,
288 int cols, unsigned int *yy, unsigned int *mm, unsigned int *dd)
290 bool loop, focusbuttons;
291 int retval, y, x, h, w, sel, ycal, xcal, year, month, day;
293 WINDOW *widget, *textpad, *shadow, *yearwin, *monthwin, *daywin;
295 const char *m[12] = {
296 "January", "February", "March", "April", "May", "June", "July",
297 "August", "September", "October", "November", "December"
300 if (yy == NULL || mm == NULL || dd == NULL)
301 RETURN_ERROR("yy / mm / dd cannot be NULL");
303 year = *yy > MAXYEAR ? MAXYEAR : *yy;
306 month = *mm > 12 ? 12 : *mm;
309 day = *dd == 0 ? 1 : *dd;
310 if(day > month_days(year, month))
311 day = month_days(year, month);
313 get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
315 if (set_widget_size(conf, rows, cols, &h, &w) != 0)
316 return (BSDDIALOG_ERROR);
317 if (calendar_autosize(conf, rows, cols, &h, &w, text, bs) != 0)
318 return (BSDDIALOG_ERROR);
319 if (calendar_checksize(h, w, bs) != 0)
320 return (BSDDIALOG_ERROR);
321 if (set_widget_position(conf, &y, &x, h, w) != 0)
322 return (BSDDIALOG_ERROR);
324 if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
326 return (BSDDIALOG_ERROR);
328 pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-17, x+w-2);
333 mvwaddstr(widget, h - 16, w/2 - 17, "Month");
334 monthwin = new_boxed_window(conf, ycal, xcal, 3, 17, RAISED);
335 mvwaddstr(widget, h - 16, w/2, "Year");
336 yearwin = new_boxed_window(conf, ycal, xcal + 17, 3, 17, RAISED);
337 daywin = new_boxed_window(conf, ycal + 3, xcal, 9, 34, RAISED);
342 loop = focusbuttons = true;
344 drawsquare(conf, monthwin, "%15s", m[month - 1], sel == 0);
345 drawsquare(conf, yearwin, "%15d", &year, sel == 1);
346 print_calendar(conf, daywin, year, month, day, sel == 2);
348 if (get_wch(&input) == ERR)
353 if (focusbuttons || conf->button.always_active) {
354 retval = bs.value[bs.curr];
359 if (conf->key.enable_esc) {
360 retval = BSDDIALOG_ESC;
367 if (bs.curr >= (int)bs.nbuttons) {
368 focusbuttons = false;
370 bs.curr = conf->button.always_active ?
381 draw_buttons(widget, bs, true);
387 if (bs.curr >= (int)bs.nbuttons) {
388 focusbuttons = false;
390 bs.curr = conf->button.always_active ?
393 } else if (sel == 2) {
394 datectl(RIGHT_DAY, &year, &month, &day);
395 } else { /* Month or Year*/
398 draw_buttons(widget, bs, true);
405 focusbuttons = false;
407 bs.curr = conf->button.always_active ?
410 } else if (sel == 2) {
411 datectl(LEFT_DAY, &year, &month, &day);
412 } else if (sel == 1) {
414 } else { /* sel = 0, Month */
419 draw_buttons(widget, bs, true);
425 focusbuttons = false;
426 bs.curr = conf->button.always_active ? 0 : -1;
427 draw_buttons(widget, bs, true);
429 } else if (sel == 0) {
430 datectl(UP_MONTH, &year, &month, &day);
431 } else if (sel == 1) {
432 datectl(UP_YEAR, &year, &month, &day);
433 } else { /* sel = 2 */
434 datectl(UP_DAY, &year, &month, &day);
440 } else if (sel == 0) {
441 datectl(DOWN_MONTH, &year, &month, &day);
442 } else if (sel == 1) {
443 datectl(DOWN_YEAR, &year, &month, &day);
444 } else { /* sel = 2 */
445 datectl(DOWN_DAY, &year, &month, &day);
449 datectl(UP_MONTH, &year, &month, &day);
452 datectl(DOWN_MONTH, &year, &month, &day);
455 datectl(UP_YEAR, &year, &month, &day);
458 datectl(DOWN_YEAR, &year, &month, &day);
461 if (conf->key.f1_file == NULL &&
462 conf->key.f1_message == NULL)
464 if (f1help(conf) != 0)
465 return (BSDDIALOG_ERROR);
466 /* No break, screen size can change */
468 /* Important for decreasing screen */
469 hide_widget(y, x, h, w, conf->shadow);
472 if (set_widget_size(conf, rows, cols, &h, &w) != 0)
473 return (BSDDIALOG_ERROR);
474 if (calendar_autosize(conf, rows, cols, &h, &w, text,
476 return (BSDDIALOG_ERROR);
477 if (calendar_checksize(h, w, bs) != 0)
478 return (BSDDIALOG_ERROR);
479 if (set_widget_position(conf, &y, &x, h, w) != 0)
480 return (BSDDIALOG_ERROR);
482 if (update_dialog(conf, shadow, widget, y, x, h, w,
483 textpad, text, &bs, true) != 0)
484 return (BSDDIALOG_ERROR);
485 pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-17, x+w-2);
490 mvwaddstr(widget, h - 16, w/2 - 17, "Month");
491 mvwin(monthwin, ycal, xcal);
492 mvwaddstr(widget, h - 16, w/2, "Year");
493 mvwin(yearwin, ycal, xcal + 17);
494 mvwin(daywin, ycal + 3, xcal);
497 /* Important to avoid grey lines expanding screen */
501 if (shortcut_buttons(input, &bs)) {
502 retval = bs.value[bs.curr];
508 if (retval == BSDDIALOG_OK) {
517 end_dialog(conf, shadow, widget, textpad);