1 /* 2 * $Id: timebox.c,v 1.41 2010/01/18 10:33:42 tom Exp $ 3 * 4 * timebox.c -- implements the timebox dialog 5 * 6 * Copyright 2001-2009,2010 Thomas E. Dickey 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License, version 2.1 10 * as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program; if not, write to 19 * Free Software Foundation, Inc. 20 * 51 Franklin St., Fifth Floor 21 * Boston, MA 02110, USA. 22 */ 23 24 #include <dialog.h> 25 #include <dlg_keys.h> 26 27 #include <time.h> 28 29 #define ONE_HIGH 1 30 #define ONE_WIDE 2 31 #define BTN_HIGH 2 32 33 #define MIN_HIGH (ONE_HIGH + BTN_HIGH + (4 * MARGIN)) 34 #define MIN_WIDE ((3 * (ONE_WIDE + 2 * MARGIN)) + 2 + (2 * MARGIN)) 35 36 typedef enum { 37 sHR = -3 38 ,sMN = -2 39 ,sSC = -1 40 } STATES; 41 42 struct _box; 43 44 typedef int (*BOX_DRAW) (struct _box *, struct tm *); 45 46 typedef struct _box { 47 WINDOW *parent; 48 WINDOW *window; 49 int x; 50 int y; 51 int width; 52 int height; 53 int period; 54 int value; 55 } BOX; 56 57 static int 58 next_or_previous(int key) 59 { 60 int result = 0; 61 62 switch (key) { 63 case DLGK_ITEM_PREV: 64 result = -1; 65 break; 66 case DLGK_ITEM_NEXT: 67 result = 1; 68 break; 69 default: 70 beep(); 71 break; 72 } 73 return result; 74 } 75 /* 76 * Draw the hour-of-month selection box 77 */ 78 static int 79 draw_cell(BOX * data) 80 { 81 werase(data->window); 82 dlg_draw_box(data->parent, 83 data->y - MARGIN, data->x - MARGIN, 84 data->height + (2 * MARGIN), data->width + (2 * MARGIN), 85 menubox_border_attr, menubox_attr); 86 87 wattrset(data->window, item_attr); 88 wprintw(data->window, "%02d", data->value); 89 return 0; 90 } 91 92 static int 93 init_object(BOX * data, 94 WINDOW *parent, 95 int x, int y, 96 int width, int height, 97 int period, int value, 98 int code) 99 { 100 data->parent = parent; 101 data->x = x; 102 data->y = y; 103 data->width = width; 104 data->height = height; 105 data->period = period; 106 data->value = value % period; 107 108 data->window = derwin(data->parent, 109 data->height, data->width, 110 data->y, data->x); 111 if (data->window == 0) 112 return -1; 113 (void) keypad(data->window, TRUE); 114 115 dlg_mouse_setbase(getbegx(parent), getbegy(parent)); 116 dlg_mouse_mkregion(y, x, height, width, code); 117 118 return 0; 119 } 120 121 static int 122 CleanupResult(int code, WINDOW *dialog, char *prompt, DIALOG_VARS * save_vars) 123 { 124 dlg_del_window(dialog); 125 dlg_mouse_free_regions(); 126 free(prompt); 127 dlg_restore_vars(save_vars); 128 129 return code; 130 } 131 132 #define DrawObject(data) draw_cell(data) 133 134 /* 135 * Display a dialog box for entering a date 136 */ 137 int 138 dialog_timebox(const char *title, 139 const char *subtitle, 140 int height, 141 int width, 142 int hour, 143 int minute, 144 int second) 145 { 146 /* *INDENT-OFF* */ 147 static DLG_KEYS_BINDING binding[] = { 148 DLG_KEYS_DATA( DLGK_DELETE_RIGHT,KEY_DC ), 149 ENTERKEY_BINDINGS, 150 DLG_KEYS_DATA( DLGK_ENTER, ' ' ), 151 DLG_KEYS_DATA( DLGK_FIELD_FIRST,KEY_HOME ), 152 DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_END ), 153 DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_LL ), 154 DLG_KEYS_DATA( DLGK_FIELD_NEXT, CHR_NEXT ), 155 DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ), 156 DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), 157 DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_BACKSPACE ), 158 DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_PREVIOUS ), 159 DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), 160 DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ), 161 DLG_KEYS_DATA( DLGK_ITEM_NEXT, '+'), 162 DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN), 163 DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_NEXT), 164 DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_NPAGE), 165 DLG_KEYS_DATA( DLGK_ITEM_PREV, '-' ), 166 DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_PPAGE ), 167 DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_PREVIOUS ), 168 DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ), 169 END_KEYS_BINDING 170 }; 171 /* *INDENT-ON* */ 172 173 #ifdef KEY_RESIZE 174 int old_height = height; 175 int old_width = width; 176 #endif 177 BOX hr_box, mn_box, sc_box; 178 int key = 0, key2, fkey; 179 int button; 180 int result = DLG_EXIT_UNKNOWN; 181 WINDOW *dialog; 182 time_t now_time = time((time_t *) 0); 183 struct tm current; 184 int state = dlg_defaultno_button(); 185 const char **buttons = dlg_ok_labels(); 186 char *prompt = dlg_strclone(subtitle); 187 char buffer[MAX_LEN]; 188 DIALOG_VARS save_vars; 189 190 now_time = time((time_t *) 0); 191 current = *localtime(&now_time); 192 193 dlg_save_vars(&save_vars); 194 dialog_vars.separate_output = TRUE; 195 196 dlg_does_output(); 197 198 #ifdef KEY_RESIZE 199 retry: 200 #endif 201 202 dlg_auto_size(title, prompt, &height, &width, 0, 0); 203 height += MIN_HIGH; 204 if (width < MIN_WIDE) 205 width = MIN_WIDE; 206 dlg_button_layout(buttons, &width); 207 dlg_print_size(height, width); 208 dlg_ctl_size(height, width); 209 210 dialog = dlg_new_window(height, width, 211 dlg_box_y_ordinate(height), 212 dlg_box_x_ordinate(width)); 213 dlg_register_window(dialog, "timebox", binding); 214 dlg_register_buttons(dialog, "timebox", buttons); 215 216 dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); 217 dlg_draw_bottom_box(dialog); 218 dlg_draw_title(dialog, title); 219 220 wattrset(dialog, dialog_attr); 221 dlg_print_autowrap(dialog, prompt, height, width); 222 223 /* compute positions of hour, month and year boxes */ 224 memset(&hr_box, 0, sizeof(hr_box)); 225 memset(&mn_box, 0, sizeof(mn_box)); 226 memset(&sc_box, 0, sizeof(sc_box)); 227 228 if (init_object(&hr_box, 229 dialog, 230 (width - MIN_WIDE + 1) / 2 + MARGIN, 231 (height - MIN_HIGH + MARGIN), 232 ONE_WIDE, 233 ONE_HIGH, 234 24, 235 hour >= 0 ? hour : current.tm_hour, 236 'H') < 0 237 || DrawObject(&hr_box) < 0) { 238 return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars); 239 } 240 241 mvwprintw(dialog, hr_box.y, hr_box.x + ONE_WIDE + MARGIN, ":"); 242 if (init_object(&mn_box, 243 dialog, 244 hr_box.x + (ONE_WIDE + 2 * MARGIN + 1), 245 hr_box.y, 246 hr_box.width, 247 hr_box.height, 248 60, 249 minute >= 0 ? minute : current.tm_min, 250 'M') < 0 251 || DrawObject(&mn_box) < 0) { 252 return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars); 253 } 254 255 mvwprintw(dialog, mn_box.y, mn_box.x + ONE_WIDE + MARGIN, ":"); 256 if (init_object(&sc_box, 257 dialog, 258 mn_box.x + (ONE_WIDE + 2 * MARGIN + 1), 259 mn_box.y, 260 mn_box.width, 261 mn_box.height, 262 60, 263 second >= 0 ? second : current.tm_sec, 264 'S') < 0 265 || DrawObject(&sc_box) < 0) { 266 return CleanupResult(DLG_EXIT_ERROR, dialog, prompt, &save_vars); 267 } 268 269 while (result == DLG_EXIT_UNKNOWN) { 270 BOX *obj = (state == sHR ? &hr_box 271 : (state == sMN ? &mn_box : 272 (state == sSC ? &sc_box : 0))); 273 274 button = (state < 0) ? 0 : state; 275 dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); 276 if (obj != 0) 277 dlg_set_focus(dialog, obj->window); 278 279 key = dlg_mouse_wgetch(dialog, &fkey); 280 if (dlg_result_key(key, fkey, &result)) 281 break; 282 283 if ((key2 = dlg_char_to_button(key, buttons)) >= 0) { 284 result = key2; 285 } else { 286 /* handle function-keys */ 287 if (fkey) { 288 switch (key) { 289 case DLGK_MOUSE(0): 290 result = DLG_EXIT_OK; 291 break; 292 case DLGK_MOUSE(1): 293 result = DLG_EXIT_CANCEL; 294 break; 295 case DLGK_MOUSE('H'): 296 state = sHR; 297 break; 298 case DLGK_MOUSE('M'): 299 state = sMN; 300 break; 301 case DLGK_MOUSE('S'): 302 state = sSC; 303 break; 304 case DLGK_ENTER: 305 result = button; 306 break; 307 case DLGK_FIELD_PREV: 308 state = dlg_prev_ok_buttonindex(state, sHR); 309 break; 310 case DLGK_FIELD_NEXT: 311 state = dlg_next_ok_buttonindex(state, sHR); 312 break; 313 case DLGK_FIELD_FIRST: 314 if (obj != 0) { 315 obj->value = 0; 316 (void) DrawObject(obj); 317 } 318 break; 319 case DLGK_FIELD_LAST: 320 if (obj != 0) { 321 switch (state) { 322 case sHR: 323 obj->value = 23; 324 break; 325 case sMN: 326 case sSC: 327 obj->value = 59; 328 break; 329 } 330 (void) DrawObject(obj); 331 } 332 break; 333 case DLGK_DELETE_RIGHT: 334 if (obj != 0) { 335 obj->value /= 10; 336 (void) DrawObject(obj); 337 } 338 break; 339 #ifdef KEY_RESIZE 340 case KEY_RESIZE: 341 /* reset data */ 342 height = old_height; 343 width = old_width; 344 hour = hr_box.value; 345 minute = mn_box.value; 346 second = sc_box.value; 347 /* repaint */ 348 dlg_clear(); 349 dlg_del_window(dialog); 350 refresh(); 351 dlg_mouse_free_regions(); 352 goto retry; 353 #endif 354 default: 355 if (obj != 0) { 356 int step = next_or_previous(key); 357 if (step != 0) { 358 obj->value += step; 359 while (obj->value < 0) 360 obj->value += obj->period; 361 obj->value %= obj->period; 362 (void) DrawObject(obj); 363 } 364 } 365 break; 366 } 367 } else if (isdigit(key)) { 368 if (obj != 0) { 369 int digit = (key - '0'); 370 int value = (obj->value * 10) + digit; 371 if (value < obj->period) { 372 obj->value = value; 373 (void) DrawObject(obj); 374 } else { 375 beep(); 376 } 377 } 378 } else { 379 beep(); 380 } 381 } 382 } 383 384 #define DefaultFormat(dst, src) \ 385 sprintf(dst, "%02d:%02d:%02d", \ 386 hr_box.value, mn_box.value, sc_box.value) 387 388 #if defined(HAVE_STRFTIME) 389 if (dialog_vars.time_format != 0) { 390 size_t used; 391 time_t now = time((time_t *) 0); 392 struct tm *parts = localtime(&now); 393 394 parts->tm_sec = sc_box.value; 395 parts->tm_min = mn_box.value; 396 parts->tm_hour = hr_box.value; 397 used = strftime(buffer, 398 sizeof(buffer) - 1, 399 dialog_vars.time_format, 400 parts); 401 if (used == 0 || *buffer == '\0') 402 DefaultFormat(buffer, hr_box); 403 } else 404 #endif 405 DefaultFormat(buffer, hr_box); 406 407 dlg_add_result(buffer); 408 dlg_add_separator(); 409 410 return CleanupResult(result, dialog, prompt, &save_vars); 411 } 412