xref: /freebsd/contrib/bsddialog/lib/slider.c (revision f2d966f6c13d93cccceb8e594a69d1a50471d314)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2025 Braulio Rivas
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 #define MINHSLIDER 13
37 #define MINWSLIDER 36
38 
39 #define NULLWIN -1
40 #define START_WIN 0
41 #define END_WIN 1
42 #define STEP_WIN 2
43 #define SLIDER_WIN 3
44 #define NWIN 4
45 
46 enum operation {
47 	MOVERIGHT,
48 	MOVEFARRIGHT,
49 	MOVEFASTRIGHT,
50 	MOVELEFT,
51 	MOVEFARLEFT,
52 	MOVEFASTLEFT,
53 	INCREASELEFT,
54 	DECREASELEFT,
55 	INCREASERIGHT,
56 	DECREASERIGHT,
57 	INCREASESTEP,
58 	DECREASESTEP,
59 };
60 
61 struct sliderctl {
62 	enum operation op;
63 	unsigned long (*spaces)[2];
64 	int nspaces; /* api unsigned, but segfault handlesliderctl():MOVELEFT */
65 	unsigned long length;
66 	unsigned long *start;
67 	unsigned long *end;
68 	unsigned long step;
69 };
70 
crashes(long x,long y,long a,long b)71 static int crashes(long x, long y, long a, long b)
72 {
73 	return ((x <= a && a <= y) || (x <= b && b <= y));
74 }
75 
fits(long x,long y,long a,long b)76 static int fits(long x, long y, long a, long b)
77 {
78 	return ((x <= a) && (b <= y));
79 }
80 
handlesliderctl(struct sliderctl * sliderctl)81 static void handlesliderctl(struct sliderctl *sliderctl)
82 {
83 	int i, step, tmpstep;
84 	unsigned long x, y, size, old_start, old_end;
85 	signed long new_start, new_end;
86 
87 	step = sliderctl->step;
88 	old_start = *(sliderctl->start);
89 	new_start = old_start;
90 	old_end = *(sliderctl->end);
91 	new_end = old_end;
92 	size = old_end - old_start + 1;
93 
94 	switch (sliderctl->op) {
95 	case MOVERIGHT:
96 		new_start = old_start + step;
97 		new_end = old_end + step;
98 
99 		for (i = 0; i < sliderctl->nspaces; i++) {
100 			x = (sliderctl->spaces)[i][0];
101 			y = (sliderctl->spaces)[i][1];
102 
103 			if (crashes(x, y, new_start, new_end)) {
104 				new_start = y + 1;
105 				new_end = new_start + size - 1;
106 				break;
107 			}
108 		}
109 		break;
110 	case MOVELEFT:
111 		new_start = old_start - step;
112 		new_end = old_end - step;
113 
114 		for (i = sliderctl->nspaces - 1; i >= 0; i--) {
115 			x = (sliderctl->spaces)[i][0];
116 			y = (sliderctl->spaces)[i][1];
117 
118 			if (crashes(x, y, new_start, new_end)) {
119 				new_end = x - 1;
120 				new_start = new_end - size + 1;
121 				break;
122 			}
123 		}
124 		break;
125 	case INCREASELEFT:
126 		new_start = old_start + step;
127 		break;
128 	case DECREASELEFT:
129 		new_start = old_start - step;
130 		for (i = 0; i < sliderctl->nspaces; i++) {
131 			x = (sliderctl->spaces)[i][0];
132 			y = (sliderctl->spaces)[i][1];
133 
134 			if (crashes(x, y, new_start, new_end)) {
135 				new_start = old_start;
136 				break;
137 			}
138 		}
139 		break;
140 	case INCREASERIGHT:
141 		new_end = old_end + step;
142 		for (i = 0; i < sliderctl->nspaces; i++) {
143 			x = (sliderctl->spaces)[i][0];
144 			y = (sliderctl->spaces)[i][1];
145 
146 			if (crashes(x, y, new_start, new_end)) {
147 				new_end = old_end;
148 				break;
149 			}
150 		}
151 		break;
152 	case DECREASERIGHT:
153 		new_end = old_end - step;
154 		break;
155 	case MOVEFARLEFT:
156 		new_start = 0;
157 		new_end = size - 1;
158 		for (i = 0; i < sliderctl->nspaces; i++) {
159 			x = (sliderctl->spaces)[i][0];
160 			y = (sliderctl->spaces)[i][1];
161 
162 			if (crashes(x, y, new_start, new_end)) {
163 				new_start = y + 1;
164 				new_end = new_start + size - 1;
165 				break;
166 			}
167 		}
168 		break;
169 	case MOVEFARRIGHT:
170 		new_end = (sliderctl->length) - 1;
171 		new_start = new_end - size + 1;
172 		for (i = sliderctl->nspaces - 1; i >= 0; i--) {
173 			x = (sliderctl->spaces)[i][0];
174 			y = (sliderctl->spaces)[i][1];
175 
176 			if (crashes(x, y, new_start, new_end)) {
177 				new_end = x - 1;
178 				new_start = new_end - size + 1;
179 				break;
180 			}
181 		}
182 		break;
183 	case MOVEFASTLEFT:
184 		if (size < 10) {
185 			tmpstep = 1;
186 		} else {
187 			tmpstep = ((sliderctl->length) * 10) / 100;
188 		}
189 		new_start = old_start - tmpstep;
190 		new_end = old_end - tmpstep;
191 
192 		for (i = sliderctl->nspaces - 1; i >= 0; i--) {
193 			x = (sliderctl->spaces)[i][0];
194 			y = (sliderctl->spaces)[i][1];
195 
196 			if (crashes(x, y, new_start, new_end)) {
197 				new_end = x - 1;
198 				new_start = new_end - size + 1;
199 				break;
200 			}
201 		}
202 		break;
203 	case MOVEFASTRIGHT:
204 		if (size < 10) {
205 			tmpstep = 1;
206 		} else {
207 			tmpstep = ((sliderctl->length) * 10) / 100;
208 		}
209 		new_start = old_start + tmpstep;
210 		new_end = old_end + tmpstep;
211 
212 		for (i = 0; i < sliderctl->nspaces; i++) {
213 			x = (sliderctl->spaces)[i][0];
214 			y = (sliderctl->spaces)[i][1];
215 
216 			if (crashes(x, y, new_start, new_end)) {
217 				new_start = y + 1;
218 				new_end = new_start + size - 1;
219 				break;
220 			}
221 		}
222 		break;
223 	case INCREASESTEP:
224 		++step;
225 		break;
226 	case DECREASESTEP:
227 		if (step > 1) {
228 			--step;
229 		}
230 		break;
231 	}
232 
233 	if (fits(0, (sliderctl->length) - 1, new_start, new_end) != 1) {
234 		new_start = old_start;
235 		new_end = old_end;
236 	}
237 
238 	if (new_start > new_end) {
239 		new_start = old_start;
240 		new_end = old_end;
241 	}
242 
243 	sliderctl->step = step;
244 
245 	*(sliderctl->start) = new_start;
246 	*(sliderctl->end) = new_end;
247 }
248 
249 static void
drawsquare(struct bsddialog_conf * conf,WINDOW * win,enum elevation elev,bool focus,const char * fmt,unsigned long value)250 drawsquare(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev,
251     bool focus, const char *fmt, unsigned long value)
252 {
253 	int h, l, w;
254 
255 	getmaxyx(win, h, w);
256 	draw_borders(conf, win, elev);
257 	if (focus) {
258 		l = 2 + w % 2;
259 		wattron(win, t.dialog.arrowcolor);
260 		mvwhline(win, 0, w / 2 - l / 2, UARROW(conf), l);
261 		mvwhline(win, h - 1, w / 2 - l / 2, DARROW(conf), l);
262 		wattroff(win, t.dialog.arrowcolor);
263 	}
264 
265 	if (focus)
266 		wattron(win, t.menu.f_namecolor);
267 
268 	mvwprintw(win, 1, 1, fmt, value);
269 
270 	if (focus)
271 		wattroff(win, t.menu.f_namecolor);
272 
273 	wnoutrefresh(win);
274 }
275 
276 static void
print_slider(struct bsddialog_conf * conf,WINDOW * win,unsigned long spaces[][2],int nspaces,unsigned long length,unsigned long * start,unsigned long * end,bool active)277 print_slider(struct bsddialog_conf *conf, WINDOW *win,
278     unsigned long spaces[][2], int nspaces, unsigned long length,
279     unsigned long *start, unsigned long *end, bool active)
280 {
281 	int i, y, x, l, height, width;
282 	unsigned long s, e;
283 	chtype ch;
284 
285 	getmaxyx(win, height, width);
286 	wclear(win);
287 	draw_borders(conf, win, RAISED);
288 
289 	if (active) {
290 		wattron(win, t.dialog.arrowcolor);
291 		mvwvline(win, 1, 0, LARROW(conf), 1);
292 		mvwvline(win, 1, width - 1, RARROW(conf), 1);
293 		wattroff(win, t.dialog.arrowcolor);
294 	}
295 
296 	y = height / 2;
297 	width -= 1;
298 
299 	ch = ' ' | bsddialog_color(BSDDIALOG_RED, BSDDIALOG_RED, 0);
300 	for (i = 0; i < nspaces; i++) {
301 		s = spaces[i][0];
302 		e = spaces[i][1];
303 
304 		x = (s * width) / length;
305 		l = ((e - s) * width) / length;
306 
307 		if ((e - s) == 0) {
308 			l = 0;
309 		} else if (l == 0) {
310 			l = 1;
311 		}
312 
313 		mvwhline(win, y, x + 1, ch, l);
314 	}
315 
316 	ch = ' ' | t.bar.f_color;
317 	s = ((*start) * width) / length;
318 	l = (((*end) - (*start)) * width) / length;
319 	if ((*end - *start) == 0) {
320 		l = 0;
321 	} else if (l == 0) {
322 		l = 1;
323 	}
324 	mvwhline(win, y, s + 1, ch, l);
325 
326 	wnoutrefresh(win);
327 }
328 
329 static int
slider_draw(struct dialog * d,bool redraw,WINDOW * start_win,WINDOW * end_win,WINDOW * size_win,WINDOW * step_win,WINDOW * slider_win,const char * unit)330 slider_draw(struct dialog *d, bool redraw, WINDOW *start_win, WINDOW *end_win,
331     WINDOW *size_win, WINDOW *step_win, WINDOW *slider_win, const char *unit)
332 {
333 	char *buf;
334 	int yslider, xslider;
335 
336 	if (redraw) {
337 		hide_dialog(d);
338 		refresh(); /* Important for decreasing screen */
339 	}
340 	if (dialog_size_position(d, MINHSLIDER, MINWSLIDER, NULL) != 0)
341 		return (BSDDIALOG_ERROR);
342 	if (draw_dialog(d) != 0) /* doupdate in main loop */
343 		return (BSDDIALOG_ERROR);
344 	if (redraw)
345 		refresh(); /* Important to fix grey lines expanding screen */
346 	TEXTPAD(d, MINHSLIDER + HBUTTONS);
347 
348 	yslider = d->y + d->h - 15;
349 	xslider = d->x + d->w / 2 - 17;
350 	asprintf(&buf, "Start (%s)", unit);
351 	mvwaddstr(d->widget, d->h - 16, d->w / 2 - 17, buf);
352 	free(buf);
353 	update_box(d->conf, start_win, yslider, xslider, 3, 17, RAISED);
354 	asprintf(&buf, "End (%s)", unit);
355 	mvwaddstr(d->widget, d->h - 16, d->w / 2, buf);
356 	free(buf);
357 	update_box(d->conf, end_win, yslider, xslider + 17, 3, 17, RAISED);
358 	asprintf(&buf, "Size (%s)", unit);
359 	mvwaddstr(d->widget, d->h - 12, d->w / 2 - 17, buf);
360 	free(buf);
361 	update_box(d->conf, size_win, yslider + 4, xslider, 3, 17, RAISED);
362 	asprintf(&buf, "Step (%s)", unit);
363 	mvwaddstr(d->widget, d->h - 12, d->w / 2, buf);
364 	free(buf);
365 	update_box(d->conf, step_win, yslider + 4, xslider + 17, 3, 17, RAISED);
366 
367 	update_box(d->conf, slider_win, yslider + 7, xslider, 3, 34, RAISED);
368 	wnoutrefresh(d->widget);
369 
370 	return (0);
371 }
372 
373 /* API */
374 int
bsddialog_slider(struct bsddialog_conf * conf,const char * text,int rows,int cols,const char * unit,unsigned long length,unsigned long * start,unsigned long * end,bool resize,unsigned int nblocks,unsigned long blocks[][2])375 bsddialog_slider(struct bsddialog_conf *conf, const char *text, int rows,
376     int cols, const char *unit, unsigned long length, unsigned long *start,
377     unsigned long *end, bool resize, unsigned int nblocks,
378     unsigned long blocks[][2])
379 {
380 	struct sliderctl ctl;
381 	bool loop, focusbuttons;
382 	int retval, sel;
383 	wint_t input;
384 	unsigned long size;
385 	WINDOW *start_win, *end_win, *size_win, *step_win, *slider_win;
386 	struct dialog dialog;
387 
388 	CHECK_PTR(start);
389 	CHECK_PTR(end);
390 
391 	ctl.spaces = blocks;
392 	ctl.nspaces = nblocks;
393 	ctl.length = length;
394 	ctl.start = start;
395 	ctl.end = end;
396 	ctl.step = 1;
397 
398 	if (prepare_dialog(conf, text, rows, cols, &dialog) != 0)
399 		return (BSDDIALOG_ERROR);
400 	set_buttons(&dialog, true, OK_LABEL, CANCEL_LABEL);
401 
402 	if ((start_win = newwin(1, 1, 1, 1)) == NULL)
403 		RETURN_ERROR("Cannot build WINDOW for start");
404 	wbkgd(start_win, t.dialog.color);
405 
406 	if ((end_win = newwin(1, 1, 1, 1)) == NULL)
407 		RETURN_ERROR("Cannot build WINDOW for end");
408 	wbkgd(end_win, t.dialog.color);
409 
410 	if ((step_win = newwin(1, 1, 1, 1)) == NULL)
411 		RETURN_ERROR("Cannot build WINDOW for step");
412 	wbkgd(step_win, t.dialog.color);
413 
414 	if ((size_win = newwin(1, 1, 1, 1)) == NULL)
415 		RETURN_ERROR("Cannot build WINDOW for size");
416 	wbkgd(size_win, t.dialog.color);
417 
418 	if ((slider_win = newwin(1, 1, 1, 1)) == NULL)
419 		RETURN_ERROR("Cannot build WINDOW for slider");
420 	wbkgd(slider_win, t.dialog.color);
421 
422 	if (slider_draw(&dialog, false, start_win, end_win, size_win, step_win,
423 	    slider_win, unit) != 0)
424 		return (BSDDIALOG_ERROR);
425 
426 	sel = NULLWIN;
427 	loop = focusbuttons = true;
428 	while (loop) {
429 		size = *(ctl.end) - *(ctl.start) + 1;
430 		drawsquare(conf, start_win, RAISED, sel == START_WIN, "%15lu", *start);
431 		drawsquare(conf, end_win, RAISED, sel == END_WIN, "%15lu", *end);
432 		drawsquare(conf, size_win, RAISED, 0, "%15lu", size);
433 		drawsquare(conf, step_win, RAISED, sel == STEP_WIN, "%15d", ctl.step);
434 		print_slider(conf, slider_win, blocks, nblocks, length, start,
435 		    end, sel == SLIDER_WIN);
436 		doupdate();
437 
438 		if (get_wch(&input) == ERR)
439 			continue;
440 		switch (input) {
441 		case KEY_ENTER:
442 		case 10: /* Enter */
443 			if (focusbuttons || conf->button.always_active) {
444 				retval = BUTTONVALUE(dialog.bs);
445 				loop = false;
446 			}
447 			break;
448 		case 27: /* Esc */
449 			if (conf->key.enable_esc) {
450 				retval = BSDDIALOG_ESC;
451 				loop = false;
452 			}
453 			break;
454 		case '\t': /* TAB */
455 			if (focusbuttons) {
456 				dialog.bs.curr++;
457 				if (dialog.bs.curr >= (int)dialog.bs.nbuttons) {
458 					focusbuttons = false;
459 					sel = START_WIN;
460 					dialog.bs.curr =
461 					    conf->button.always_active ? 0 : -1;
462 				}
463 			} else {
464 				sel++;
465 				if ((sel + 1) > NWIN) {
466 					focusbuttons = true;
467 					sel = NULLWIN;
468 					dialog.bs.curr = 0;
469 				}
470 			}
471 			DRAW_BUTTONS(dialog);
472 			break;
473 		case KEY_CTRL('n'):
474 		case KEY_RIGHT:
475 			if (focusbuttons) {
476 				dialog.bs.curr++;
477 				if (dialog.bs.curr >= (int)dialog.bs.nbuttons) {
478 					focusbuttons = false;
479 					sel = START_WIN;
480 					dialog.bs.curr =
481 					    conf->button.always_active ? 0 : -1;
482 				}
483 			} else if (sel == SLIDER_WIN) {
484 				ctl.op = MOVERIGHT;
485 				handlesliderctl(&ctl);
486 			} else {
487 				sel++;
488 			}
489 			DRAW_BUTTONS(dialog);
490 			break;
491 		case KEY_CTRL('p'):
492 		case KEY_LEFT:
493 			if (focusbuttons) {
494 				dialog.bs.curr--;
495 				if (dialog.bs.curr < 0) {
496 					focusbuttons = false;
497 					sel = SLIDER_WIN;
498 					dialog.bs.curr =
499 					    conf->button.always_active ? 0 : -1;
500 				}
501 			} else if (sel == SLIDER_WIN) {
502 				ctl.op = MOVELEFT;
503 				handlesliderctl(&ctl);
504 			} else if (sel == END_WIN) {
505 				sel = START_WIN;
506 			} else {
507 				focusbuttons = true;
508 				sel = NULLWIN;
509 				dialog.bs.curr = 0;
510 			}
511 			DRAW_BUTTONS(dialog);
512 			break;
513 		case KEY_UP:
514 			if (focusbuttons) {
515 				sel = SLIDER_WIN;
516 				focusbuttons = false;
517 				dialog.bs.curr =
518 				     conf->button.always_active ? 0 : -1;
519 				DRAW_BUTTONS(dialog);
520 			} else if (sel == START_WIN) {
521 				if (resize) {
522 					ctl.op = INCREASELEFT;
523 				} else {
524 					ctl.op = MOVERIGHT;
525 				}
526 				handlesliderctl(&ctl);
527 			} else if (sel == END_WIN) {
528 				if (resize) {
529 					ctl.op = INCREASERIGHT;
530 				} else {
531 					ctl.op = MOVERIGHT;
532 				}
533 				handlesliderctl(&ctl);
534 			} else if (sel == STEP_WIN) {
535 				ctl.op = INCREASESTEP;
536 				handlesliderctl(&ctl);
537 			}
538 			break;
539 		case KEY_DOWN:
540 			if (focusbuttons) {
541 				break;
542 			} else if (sel == START_WIN) {
543 				if (resize) {
544 					ctl.op = DECREASELEFT;
545 				} else {
546 					ctl.op = MOVELEFT;
547 				}
548 				handlesliderctl(&ctl);
549 			} else if (sel == END_WIN) {
550 				if (resize) {
551 					ctl.op = DECREASERIGHT;
552 				} else {
553 					ctl.op = MOVELEFT;
554 				}
555 				handlesliderctl(&ctl);
556 			} else if (sel == STEP_WIN) {
557 				ctl.op = DECREASESTEP;
558 				handlesliderctl(&ctl);
559 			}
560 			break;
561 		case '-':
562 			if (focusbuttons) {
563 				break;
564 			} else if (sel == START_WIN) {
565 				if (resize) {
566 					ctl.op = DECREASELEFT;
567 				} else {
568 					ctl.op = MOVELEFT;
569 				}
570 				handlesliderctl(&ctl);
571 			} else if (sel == END_WIN) {
572 				if (resize) {
573 					ctl.op = DECREASERIGHT;
574 				} else {
575 					ctl.op = MOVELEFT;
576 				}
577 				handlesliderctl(&ctl);
578 			} else if (sel == STEP_WIN) {
579 				ctl.op = DECREASESTEP;
580 				handlesliderctl(&ctl);
581 			}
582 			break;
583 		case '+':
584 			if (focusbuttons) {
585 				break;
586 			} else if (sel == START_WIN) {
587 				if (resize) {
588 					ctl.op = INCREASELEFT;
589 				} else {
590 					ctl.op = MOVERIGHT;
591 				}
592 				handlesliderctl(&ctl);
593 			} else if (sel == END_WIN) {
594 				if (resize) {
595 					ctl.op = INCREASERIGHT;
596 				} else {
597 					ctl.op = MOVERIGHT;
598 				}
599 				handlesliderctl(&ctl);
600 			} else if (sel == STEP_WIN) {
601 				ctl.op = INCREASESTEP;
602 				handlesliderctl(&ctl);
603 			}
604 			break;
605 		case KEY_HOME:
606 			if (focusbuttons) {
607 				break;
608 			} else if (sel == SLIDER_WIN) {
609 				ctl.op = MOVEFARLEFT;
610 				handlesliderctl(&ctl);
611 			}
612 			break;
613 		case KEY_END:
614 			if (focusbuttons) {
615 				break;
616 			} else if (sel == SLIDER_WIN) {
617 				ctl.op = MOVEFARRIGHT;
618 				handlesliderctl(&ctl);
619 			}
620 			break;
621 		case KEY_PPAGE:
622 			if (focusbuttons) {
623 				break;
624 			} else if (sel == SLIDER_WIN) {
625 				ctl.op = MOVEFASTLEFT;
626 				handlesliderctl(&ctl);
627 			}
628 			break;
629 		case KEY_NPAGE:
630 			if (focusbuttons) {
631 				break;
632 			} else if (sel == SLIDER_WIN) {
633 				ctl.op = MOVEFASTRIGHT;
634 				handlesliderctl(&ctl);
635 			}
636 			break;
637 		case KEY_F(1):
638 			if (conf->key.f1_file == NULL &&
639 			    conf->key.f1_message == NULL)
640 				break;
641 			if (f1help_dialog(conf) != 0)
642 				return (BSDDIALOG_ERROR);
643 			if (slider_draw(&dialog, true, start_win, end_win, size_win,
644 			    step_win, slider_win, unit) != 0)
645 				return (BSDDIALOG_ERROR);
646 			break;
647 		case KEY_CTRL('l'):
648 		case KEY_RESIZE:
649 			if (slider_draw(&dialog, true, start_win, end_win, size_win,
650 			    step_win, slider_win, unit) != 0)
651 				return (BSDDIALOG_ERROR);
652 			break;
653 		default:
654 			if (shortcut_buttons(input, &dialog.bs)) {
655 				DRAW_BUTTONS(dialog);
656 				doupdate();
657 				retval = BUTTONVALUE(dialog.bs);
658 				loop = false;
659 			}
660 		}
661 	}
662 
663 	delwin(start_win);
664 	delwin(end_win);
665 	delwin(step_win);
666 	delwin(slider_win);
667 	end_dialog(&dialog);
668 
669 	return (retval);
670 }
671