1 /*
2  * Schism Tracker - a cross-platform Impulse Tracker clone
3  * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4  * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5  * copyright (c) 2009 Storlek & Mrs. Brisby
6  * copyright (c) 2010-2012 Storlek
7  * URL: http://schismtracker.org/
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #include "headers.h"
25 #include "it.h"
26 #include "page.h"
27 #include "song.h"
28 
29 /* --------------------------------------------------------------------- */
30 
31 /* n => the delta-value */
numentry_move_cursor(struct widget * widget,int n)32 static void numentry_move_cursor(struct widget *widget, int n)
33 {
34 	if (widget->d.numentry.reverse) return;
35 	n += *(widget->d.numentry.cursor_pos);
36 	n = CLAMP(n, 0, widget->width - 1);
37 	if (*(widget->d.numentry.cursor_pos) == n)
38 		return;
39 	*(widget->d.numentry.cursor_pos) = n;
40 	status.flags |= NEED_UPDATE;
41 }
42 
textentry_move_cursor(struct widget * widget,int n)43 static void textentry_move_cursor(struct widget *widget, int n)
44 {
45 	n += widget->d.textentry.cursor_pos;
46 	n = CLAMP(n, 0, widget->d.textentry.max_length);
47 	if (widget->d.textentry.cursor_pos == n)
48 		return;
49 	widget->d.textentry.cursor_pos = n;
50 	status.flags |= NEED_UPDATE;
51 }
52 
bitset_move_cursor(struct widget * widget,int n)53 static void bitset_move_cursor(struct widget *widget, int n)
54 {
55 	n += *widget->d.bitset.cursor_pos;
56 	n = CLAMP(n, 0, widget->d.bitset.nbits-1);
57 	if (*widget->d.bitset.cursor_pos == n)
58 		return;
59 	*widget->d.bitset.cursor_pos = n;
60 	status.flags |= NEED_UPDATE;
61 }
62 
63 /* --------------------------------------------------------------------- */
64 /* thumbbar value prompt */
65 
thumbbar_prompt_finish(int n)66 static void thumbbar_prompt_finish(int n)
67 {
68 	if (n >= ACTIVE_WIDGET.d.thumbbar.min && n <= ACTIVE_WIDGET.d.thumbbar.max) {
69 		ACTIVE_WIDGET.d.thumbbar.value = n;
70 		if (ACTIVE_WIDGET.changed) ACTIVE_WIDGET.changed();
71 	}
72 
73 	status.flags |= NEED_UPDATE;
74 }
75 
thumbbar_prompt_value(struct widget * widget,struct key_event * k)76 static int thumbbar_prompt_value(struct widget *widget, struct key_event *k)
77 {
78 	int c;
79 
80 	if (!NO_MODIFIER(k->mod)) {
81 		/* annoying */
82 		return 0;
83 	}
84 	if (k->sym == SDLK_MINUS) {
85 		if (widget->d.thumbbar.min >= 0)
86 			return 0;
87 		c = '-';
88 	} else {
89 		c = numeric_key_event(k, 0);
90 		if (c < 0)
91 			return 0;
92 		c += '0';
93 	}
94 
95 	numprompt_create("Enter Value", thumbbar_prompt_finish, c);
96 
97 	return 1;
98 }
99 
100 /* --------------------------------------------------------------------- */
101 /* This function is completely disgustipated. */
102 
103 
_backtab(void)104 static void _backtab(void)
105 {
106 	struct widget *w;
107 	int i;
108 
109 	/* hunt for a widget that leads back to this one */
110 	if (!total_widgets || !selected_widget) return;
111 
112 	for (i = 0; i < *total_widgets; i++) {
113 		w = &widgets[i];
114 		if (w->next.tab == *selected_widget) {
115 			/* found backtab */
116 			change_focus_to(i);
117 			return;
118 		}
119 
120 	}
121 	if (status.flags & CLASSIC_MODE) {
122 		for (i = 0; i < *total_widgets; i++) {
123 			w = &widgets[i];
124 			if (w->next.right == *selected_widget) {
125 				/* simulate backtab */
126 				change_focus_to(i);
127 				return;
128 			}
129 		}
130 		for (i = 0; i < *total_widgets; i++) {
131 			w = &widgets[i];
132 			if (w->next.down == *selected_widget) {
133 				/* simulate backtab */
134 				change_focus_to(i);
135 				return;
136 			}
137 		}
138 	} else {
139 		for (i = 0; i < *total_widgets; i++) {
140 			w = &widgets[i];
141 			if (w->next.down == *selected_widget) {
142 				/* simulate backtab */
143 				change_focus_to(i);
144 				return;
145 			}
146 		}
147 		for (i = 0; i < *total_widgets; i++) {
148 			w = &widgets[i];
149 			if (w->next.right == *selected_widget) {
150 				/* simulate backtab */
151 				change_focus_to(i);
152 				return;
153 			}
154 		}
155 	}
156 	change_focus_to(0); /* err... */
157 }
158 
159 
160 /* return: 1 = handled key, 0 = didn't */
widget_handle_key(struct key_event * k)161 int widget_handle_key(struct key_event * k)
162 {
163 	struct widget *widget = &ACTIVE_WIDGET;
164 	if (!widget)
165 		return 0;
166 
167 	int n, onw, wx, fmin, fmax, pad;
168 	void (*changed)(void);
169 	enum widget_type current_type = widget->type;
170 
171 	if (!(status.flags & DISKWRITER_ACTIVE)
172 	    && (current_type == WIDGET_OTHER)
173 	    && widget->d.other.handle_key(k))
174 		return 1;
175 
176 	if (!(status.flags & DISKWRITER_ACTIVE) && k->mouse
177 	    && (status.flags & CLASSIC_MODE)) {
178 		switch(current_type) {
179 		case WIDGET_NUMENTRY:
180 			if (k->mouse_button == MOUSE_BUTTON_LEFT) {
181 				k->sym = SDLK_MINUS;
182 				k->mouse = MOUSE_NONE;
183 			} else if (k->mouse_button == MOUSE_BUTTON_RIGHT) {
184 				k->sym = SDLK_PLUS;
185 				k->mouse = MOUSE_NONE;
186 			}
187 			break;
188 		default:
189 			break;
190 		};
191 	}
192 
193 	if (k->mouse == MOUSE_CLICK) {
194 		if (status.flags & DISKWRITER_ACTIVE) return 0;
195 		switch (current_type) {
196 		case WIDGET_TOGGLE:
197 			if (!NO_MODIFIER(k->mod))
198 				return 0;
199 			if (k->state == KEY_RELEASE)
200 				return 1;
201 			widget->d.toggle.state = !widget->d.toggle.state;
202 			if (widget->changed) widget->changed();
203 			status.flags |= NEED_UPDATE;
204 			return 1;
205 		case WIDGET_MENUTOGGLE:
206 			if (!NO_MODIFIER(k->mod))
207 				return 0;
208 			if (k->state == KEY_RELEASE)
209 				return 1;
210 			widget->d.menutoggle.state = (widget->d.menutoggle.state + 1)
211 				% widget->d.menutoggle.num_choices;
212 			if (widget->changed) widget->changed();
213 			status.flags |= NEED_UPDATE;
214 			return 1;
215 		default:
216 			break;
217 		}
218 	} else if (k->mouse == MOUSE_DBLCLICK) {
219 		if (status.flags & DISKWRITER_ACTIVE) return 0;
220 		if (current_type == WIDGET_PANBAR) {
221 			if (!NO_MODIFIER(k->mod))
222 				return 0;
223 			widget->d.panbar.muted = !widget->d.panbar.muted;
224 			changed = widget->changed;
225 			if (changed) changed();
226 			return 1;
227 		}
228 	}
229 
230 	if (k->mouse == MOUSE_CLICK
231 	    || (k->mouse == MOUSE_NONE && k->sym == SDLK_RETURN)) {
232 #if 0
233 		if (k->mouse && k->mouse_button == MOUSE_BUTTON_MIDDLE) {
234 			if (status.flags & DISKWRITER_ACTIVE) return 0;
235 			if (k->state == KEY_PRESS)
236 				return 1;
237 			status.flags |= CLIPPY_PASTE_SELECTION;
238 			return 1;
239 		}
240 #endif
241 		if (k->mouse && (current_type == WIDGET_THUMBBAR
242 		|| current_type == WIDGET_PANBAR)) {
243 			if (status.flags & DISKWRITER_ACTIVE) return 0;
244 
245 			/* swallow it */
246 			if (!k->on_target) return 0;
247 
248 			fmin = widget->d.thumbbar.min;
249 			fmax = widget->d.thumbbar.max;
250 			if (current_type == WIDGET_PANBAR) {
251 				n = k->fx - ((widget->x + 11) * k->rx);
252 				wx = (widget->width - 16) * k->rx;
253 			} else {
254 				n = k->fx - (widget->x * k->rx);
255 				wx = (widget->width-1) * k->rx;
256 			}
257 			if (n < 0) n = 0;
258 			else if (n >= wx) n = wx;
259 			n = fmin + ((n * (fmax - fmin)) / wx);
260 
261 			if (n < fmin)
262 				n = fmin;
263 			else if (n > fmax)
264 				n = fmax;
265 			if (current_type == WIDGET_PANBAR) {
266 				widget->d.panbar.muted = 0;
267 				widget->d.panbar.surround = 0;
268 				if (k->x - widget->x < 11) return 1;
269 				if (k->x - widget->x > 19) return 1;
270 			}
271 			numentry_change_value(widget, n);
272 			return 1;
273 		}
274 		if (k->mouse) {
275 			switch (widget->type) {
276 			case WIDGET_BUTTON:
277 				pad = widget->d.button.padding+1;
278 				break;
279 			case WIDGET_TOGGLEBUTTON:
280 				pad = widget->d.togglebutton.padding+1;
281 				break;
282 			default:
283 				pad = 0;
284 			};
285 			onw = ((signed) k->x < widget->x
286 			       || (signed) k->x >= widget->x + widget->width + pad
287 			       || (signed) k->y != widget->y) ? 0 : 1;
288 			n = (k->state == KEY_RELEASE && onw) ? 1 : 0;
289 			if (widget->depressed != n) status.flags |= NEED_UPDATE;
290 			widget->depressed = n;
291 			if (current_type != WIDGET_TEXTENTRY && current_type != WIDGET_NUMENTRY) {
292 				if (k->state == KEY_PRESS || !onw)
293 					return 1;
294 			} else if (!onw) {
295 				return 1;
296 			}
297 		} else {
298 			n = (k->state == KEY_PRESS) ? 1 : 0;
299 			if (widget->depressed != n)
300 				status.flags |= NEED_UPDATE;
301 			else if (k->state == KEY_RELEASE)
302 				return 1; // swallor
303 			widget->depressed = n;
304 			if (k->state == KEY_PRESS)
305 				return 1;
306 		}
307 
308 		if (k->mouse) {
309 			switch(current_type) {
310 			case WIDGET_MENUTOGGLE:
311 			case WIDGET_BUTTON:
312 			case WIDGET_TOGGLEBUTTON:
313 				if (k->on_target && widget->activate) widget->activate();
314 			default:
315 				break;
316 			};
317 		} else if (current_type != WIDGET_OTHER) {
318 			if (widget->activate) widget->activate();
319 		}
320 
321 		switch (current_type) {
322 		case WIDGET_OTHER:
323 			break;
324 		case WIDGET_TEXTENTRY:
325 			if (status.flags & DISKWRITER_ACTIVE) return 0;
326 			/* LOL WOW THIS SUCKS */
327 			if (k->mouse == MOUSE_CLICK && k->on_target) {
328 				/* position cursor */
329 				n = k->x - widget->x;
330 				n = CLAMP(n, 0, widget->width - 1);
331 				wx = k->sx - widget->x;
332 				wx = CLAMP(wx, 0, widget->width - 1);
333 				widget->d.textentry.cursor_pos = n+widget->d.textentry.firstchar;
334 				wx  = wx+widget->d.textentry.firstchar;
335 				if (widget->d.textentry.cursor_pos >= (signed) strlen(widget->d.textentry.text))
336 					widget->d.textentry.cursor_pos = strlen(widget->d.textentry.text);
337 				if (wx >= (signed) strlen(widget->d.textentry.text))
338 					wx = strlen(widget->d.textentry.text);
339 				status.flags |= NEED_UPDATE;
340 			}
341 
342 			/* for a text entry, the only thing enter does is run the activate callback.
343 			thus, if no activate callback is defined, the key wasn't handled */
344 			return (widget->activate != NULL);
345 
346 		case WIDGET_NUMENTRY:
347 			if (status.flags & DISKWRITER_ACTIVE) return 0;
348 			if (k->mouse == MOUSE_CLICK && k->on_target) {
349 				/* position cursor */
350 				n = k->x - widget->x;
351 				n = CLAMP(n, 0, widget->width - 1);
352 				wx = k->sx - widget->x;
353 				wx = CLAMP(wx, 0, widget->width - 1);
354 				if (n >= widget->width)
355 					n = widget->width-1;
356 				*widget->d.numentry.cursor_pos = n;
357 				status.flags |= NEED_UPDATE;
358 			}
359 
360 			break;
361 
362 		case WIDGET_TOGGLEBUTTON:
363 			if (status.flags & DISKWRITER_ACTIVE) return 0;
364 			if (widget->d.togglebutton.group) {
365 				/* this also runs the changed callback and redraws the button(s) */
366 				togglebutton_set(widgets, *selected_widget, 1);
367 				return 1;
368 			}
369 			/* else... */
370 			widget->d.togglebutton.state = !widget->d.togglebutton.state;
371 			/* and fall through */
372 		case WIDGET_BUTTON:
373 			/* maybe buttons should ignore the changed callback, and use activate instead...
374 			(but still call the changed callback for togglebuttons if they *actually* changed) */
375 			if (widget->changed) widget->changed();
376 			status.flags |= NEED_UPDATE;
377 			return 1;
378 		default:
379 			break;
380 		}
381 		return 0;
382 	}
383 
384 	/* a WIDGET_OTHER that *didn't* handle the key itself needs to get run through the switch
385 	statement to account for stuff like the tab key */
386 	if (k->state == KEY_RELEASE)
387 		return 0;
388 
389 	if (k->mouse == MOUSE_SCROLL_UP && current_type == WIDGET_NUMENTRY) {
390 		k->sym = SDLK_MINUS;
391 	} else if (k->mouse == MOUSE_SCROLL_DOWN && current_type == WIDGET_NUMENTRY) {
392 		k->sym = SDLK_PLUS;
393 	}
394 
395 	switch (k->sym) {
396 	case SDLK_ESCAPE:
397 		/* this is to keep the text entries from taking the key hostage and inserting '<-'
398 		characters instead of showing the menu */
399 		return 0;
400 	case SDLK_UP:
401 		if (status.flags & DISKWRITER_ACTIVE) return 0;
402 		if (!NO_MODIFIER(k->mod))
403 			return 0;
404 		change_focus_to(widget->next.up);
405 		return 1;
406 	case SDLK_DOWN:
407 		if (status.flags & DISKWRITER_ACTIVE) return 0;
408 		if (!NO_MODIFIER(k->mod))
409 			return 0;
410 		change_focus_to(widget->next.down);
411 		return 1;
412 	case SDLK_TAB:
413 		if (status.flags & DISKWRITER_ACTIVE) return 0;
414 		if (k->mod & KMOD_SHIFT) {
415 			_backtab();
416 			return 1;
417 		}
418 		if (!NO_MODIFIER(k->mod))
419 			return 0;
420 		change_focus_to(widget->next.tab);
421 		return 1;
422 	case SDLK_LEFT:
423 		if (status.flags & DISKWRITER_ACTIVE) return 0;
424 		switch (current_type) {
425 		case WIDGET_BITSET:
426 		    if (NO_MODIFIER(k->mod))
427 			bitset_move_cursor(widget, -1);
428 		    break;
429 		case WIDGET_NUMENTRY:
430 			if (!NO_MODIFIER(k->mod)) {
431 				return 0;
432 			}
433 			numentry_move_cursor(widget, -1);
434 			return 1;
435 		case WIDGET_TEXTENTRY:
436 			if (!NO_MODIFIER(k->mod)) {
437 				return 0;
438 			}
439 			textentry_move_cursor(widget, -1);
440 			return 1;
441 		case WIDGET_PANBAR:
442 			widget->d.panbar.muted = 0;
443 			widget->d.panbar.surround = 0;
444 			/* fall through */
445 		case WIDGET_THUMBBAR:
446 			/* I'm handling the key modifiers differently than Impulse Tracker, but only
447 			because I think this is much more useful. :) */
448 			n = 1;
449 			if (k->mod & (KMOD_ALT | KMOD_META))
450 				n *= 8;
451 			if (k->mod & KMOD_SHIFT)
452 				n *= 4;
453 			if (k->mod & KMOD_CTRL)
454 				n *= 2;
455 			n = widget->d.numentry.value - n;
456 			numentry_change_value(widget, n);
457 			return 1;
458 		default:
459 			if (!NO_MODIFIER(k->mod))
460 				return 0;
461 			change_focus_to(widget->next.left);
462 			return 1;
463 		}
464 		break;
465 	case SDLK_RIGHT:
466 		if (status.flags & DISKWRITER_ACTIVE) return 0;
467 		/* pretty much the same as left, but with a few small
468 		 * changes here and there... */
469 		switch (current_type) {
470 		case WIDGET_BITSET:
471 		    if (NO_MODIFIER(k->mod))
472 			bitset_move_cursor(widget, 1);
473 		    break;
474 		case WIDGET_NUMENTRY:
475 			if (!NO_MODIFIER(k->mod)) {
476 				return 0;
477 			}
478 			numentry_move_cursor(widget, 1);
479 			return 1;
480 		case WIDGET_TEXTENTRY:
481 			if (!NO_MODIFIER(k->mod)) {
482 				return 0;
483 			}
484 			textentry_move_cursor(widget, 1);
485 			return 1;
486 		case WIDGET_PANBAR:
487 			widget->d.panbar.muted = 0;
488 			widget->d.panbar.surround = 0;
489 			/* fall through */
490 		case WIDGET_THUMBBAR:
491 			n = 1;
492 			if (k->mod & (KMOD_ALT | KMOD_META))
493 				n *= 8;
494 			if (k->mod & KMOD_SHIFT)
495 				n *= 4;
496 			if (k->mod & KMOD_CTRL)
497 				n *= 2;
498 			n = widget->d.numentry.value + n;
499 			numentry_change_value(widget, n);
500 			return 1;
501 		default:
502 			if (!NO_MODIFIER(k->mod))
503 				return 0;
504 			change_focus_to(widget->next.right);
505 			return 1;
506 		}
507 		break;
508 	case SDLK_HOME:
509 		if (status.flags & DISKWRITER_ACTIVE) return 0;
510 		/* Impulse Tracker only does home/end for the thumbbars.
511 		 * This stuff is all extra. */
512 		switch (current_type) {
513 		case WIDGET_NUMENTRY:
514 			if (!NO_MODIFIER(k->mod))
515 				return 0;
516 			*(widget->d.numentry.cursor_pos) = 0;
517 			status.flags |= NEED_UPDATE;
518 			return 1;
519 		case WIDGET_TEXTENTRY:
520 			if (!NO_MODIFIER(k->mod))
521 				return 0;
522 			widget->d.textentry.cursor_pos = 0;
523 			status.flags |= NEED_UPDATE;
524 			return 1;
525 		case WIDGET_PANBAR:
526 			widget->d.panbar.muted = 0;
527 			widget->d.panbar.surround = 0;
528 			/* fall through */
529 		case WIDGET_THUMBBAR:
530 			n = widget->d.thumbbar.min;
531 			numentry_change_value(widget, n);
532 			return 1;
533 		default:
534 			break;
535 		}
536 		break;
537 	case SDLK_END:
538 		if (status.flags & DISKWRITER_ACTIVE) return 0;
539 		switch (current_type) {
540 		case WIDGET_NUMENTRY:
541 			if (!NO_MODIFIER(k->mod))
542 				return 0;
543 			*(widget->d.numentry.cursor_pos) = widget->width - 1;
544 			status.flags |= NEED_UPDATE;
545 			return 1;
546 		case WIDGET_TEXTENTRY:
547 			if (!NO_MODIFIER(k->mod))
548 				return 0;
549 			widget->d.textentry.cursor_pos = strlen(widget->d.textentry.text);
550 			status.flags |= NEED_UPDATE;
551 			return 1;
552 		case WIDGET_PANBAR:
553 			widget->d.panbar.muted = 0;
554 			widget->d.panbar.surround = 0;
555 			/* fall through */
556 		case WIDGET_THUMBBAR:
557 			n = widget->d.thumbbar.max;
558 			numentry_change_value(widget, n);
559 			return 1;
560 		default:
561 			break;
562 		}
563 		break;
564 	case SDLK_SPACE:
565 		if (status.flags & DISKWRITER_ACTIVE) return 0;
566 		switch (current_type) {
567 		case WIDGET_BITSET:
568 		    if (!NO_MODIFIER(k->mod))
569 			return 0;
570 		    widget->d.bitset.value ^= (1 << *widget->d.bitset.cursor_pos);
571 			if (widget->changed) widget->changed();
572 		    status.flags |= NEED_UPDATE;
573 		    return 1;
574 		case WIDGET_TOGGLE:
575 			if (!NO_MODIFIER(k->mod))
576 				return 0;
577 			widget->d.toggle.state = !widget->d.toggle.state;
578 			if (widget->changed) widget->changed();
579 			status.flags |= NEED_UPDATE;
580 			return 1;
581 		case WIDGET_MENUTOGGLE:
582 			if (!NO_MODIFIER(k->mod))
583 				return 0;
584 			widget->d.menutoggle.state = (widget->d.menutoggle.state + 1)
585 				% widget->d.menutoggle.num_choices;
586 			if (widget->changed) widget->changed();
587 			status.flags |= NEED_UPDATE;
588 			return 1;
589 		case WIDGET_PANBAR:
590 			if (!NO_MODIFIER(k->mod))
591 				return 0;
592 			widget->d.panbar.muted = !widget->d.panbar.muted;
593 			changed = widget->changed;
594 			change_focus_to(widget->next.down);
595 			if (changed) changed();
596 			return 1;
597 		default:
598 			break;
599 		}
600 		break;
601 	case SDLK_BACKSPACE:
602 		if (status.flags & DISKWRITER_ACTIVE) return 0;
603 		if (current_type == WIDGET_NUMENTRY) {
604 			if (widget->d.numentry.reverse) {
605 				/* woot! */
606 				widget->d.numentry.value /= 10;
607 				if (widget->changed) widget->changed();
608 				status.flags |= NEED_UPDATE;
609 				return 1;
610 			}
611 		}
612 
613 		/* this ought to be in a separate function. */
614 		if (current_type != WIDGET_TEXTENTRY)
615 			break;
616 		if (!widget->d.textentry.text[0]) {
617 			/* nothing to do */
618 			return 1;
619 		}
620 		if (k->mod & KMOD_CTRL) {
621 			/* clear the whole field */
622 			widget->d.textentry.text[0] = 0;
623 			widget->d.textentry.cursor_pos = 0;
624 		} else {
625 			if (widget->d.textentry.cursor_pos == 0) {
626 				/* act like ST3 */
627 				text_delete_next_char(widget->d.textentry.text,
628 						      &(widget->d.textentry.cursor_pos),
629 						      widget->d.textentry.max_length);
630 			} else {
631 				text_delete_char(widget->d.textentry.text,
632 						 &(widget->d.textentry.cursor_pos),
633 						 widget->d.textentry.max_length);
634 			}
635 		}
636 		if (widget->changed) widget->changed();
637 		status.flags |= NEED_UPDATE;
638 		return 1;
639 	case SDLK_DELETE:
640 		if (status.flags & DISKWRITER_ACTIVE) return 0;
641 		if (current_type != WIDGET_TEXTENTRY)
642 			break;
643 		if (!widget->d.textentry.text[0]) {
644 			/* nothing to do */
645 			return 1;
646 		}
647 		text_delete_next_char(widget->d.textentry.text,
648 				      &(widget->d.textentry.cursor_pos), widget->d.textentry.max_length);
649 		if (widget->changed) widget->changed();
650 		status.flags |= NEED_UPDATE;
651 		return 1;
652 	case SDLK_PLUS:
653 		if (status.flags & DISKWRITER_ACTIVE) return 0;
654 		if (current_type == WIDGET_NUMENTRY && NO_MODIFIER(k->mod)) {
655 			numentry_change_value(widget, widget->d.numentry.value + 1);
656 			return 1;
657 		}
658 		break;
659 	case SDLK_MINUS:
660 		if (status.flags & DISKWRITER_ACTIVE) return 0;
661 		if (current_type == WIDGET_NUMENTRY && NO_MODIFIER(k->mod)) {
662 			numentry_change_value(widget, widget->d.numentry.value - 1);
663 			return 1;
664 		}
665 		break;
666 	case SDLK_l:
667 		if (status.flags & DISKWRITER_ACTIVE) return 0;
668 		if (current_type == WIDGET_PANBAR) {
669 			if (k->mod & KMOD_ALT) {
670 				song_set_pan_scheme(PANS_LEFT);
671 				return 1;
672 			} else if (NO_MODIFIER(k->mod)) {
673 				widget->d.panbar.muted = 0;
674 				widget->d.panbar.surround = 0;
675 				numentry_change_value(widget, 0);
676 				return 1;
677 			}
678 		}
679 		break;
680 	case SDLK_m:
681 		if (status.flags & DISKWRITER_ACTIVE) return 0;
682 		if (current_type == WIDGET_PANBAR) {
683 			if (k->mod & KMOD_ALT) {
684 				song_set_pan_scheme(PANS_MONO);
685 				return 1;
686 			} else if (NO_MODIFIER(k->mod)) {
687 				widget->d.panbar.muted = 0;
688 				widget->d.panbar.surround = 0;
689 				numentry_change_value(widget, 32);
690 				return 1;
691 			}
692 		}
693 		break;
694 	case SDLK_r:
695 		if (status.flags & DISKWRITER_ACTIVE) return 0;
696 		if (current_type == WIDGET_PANBAR) {
697 			if (k->mod & KMOD_ALT) {
698 				song_set_pan_scheme(PANS_RIGHT);
699 				return 1;
700 			} else if (NO_MODIFIER(k->mod)) {
701 				widget->d.panbar.muted = 0;
702 				widget->d.panbar.surround = 0;
703 				numentry_change_value(widget, 64);
704 				return 1;
705 			}
706 		}
707 		break;
708 	case SDLK_s:
709 		if (status.flags & DISKWRITER_ACTIVE) return 0;
710 		if (current_type == WIDGET_PANBAR) {
711 			if (k->mod & KMOD_ALT) {
712 				song_set_pan_scheme(PANS_STEREO);
713 				return 1;
714 			} else if(NO_MODIFIER(k->mod)) {
715 				widget->d.panbar.muted = 0;
716 				widget->d.panbar.surround = 1;
717 				if (widget->changed) widget->changed();
718 				status.flags |= NEED_UPDATE;
719 				return 1;
720 			}
721 		}
722 		break;
723 	case SDLK_a:
724 		if (status.flags & DISKWRITER_ACTIVE) return 0;
725 		if (current_type == WIDGET_PANBAR && (k->mod & KMOD_ALT)) {
726 			song_set_pan_scheme(PANS_AMIGA);
727 			return 1;
728 		}
729 		break;
730 #if 0
731 	case SDLK_x:
732 		if (status.flags & DISKWRITER_ACTIVE) return 0;
733 		if (current_type == WIDGET_PANBAR && (k->mod & KMOD_ALT)) {
734 			song_set_pan_scheme(PANS_CROSS);
735 			return 1;
736 		}
737 		break;
738 #endif
739 	case SDLK_SLASH:
740 	case SDLK_KP_DIVIDE:
741 		if (status.flags & DISKWRITER_ACTIVE) return 0;
742 		if (current_type == WIDGET_PANBAR && (k->mod & KMOD_ALT)) {
743 			song_set_pan_scheme(PANS_SLASH);
744 			return 1;
745 		}
746 		break;
747 	case SDLK_BACKSLASH:
748 		if (status.flags & DISKWRITER_ACTIVE) return 0;
749 		if (current_type == WIDGET_PANBAR && (k->mod & KMOD_ALT)) {
750 			song_set_pan_scheme(PANS_BACKSLASH);
751 			return 1;
752 		}
753 		break;
754 	default:
755 		/* this avoids a warning about all the values of an enum not being handled.
756 		(sheesh, it's already hundreds of lines long as it is!) */
757 		break;
758 	}
759 	if (status.flags & DISKWRITER_ACTIVE) return 0;
760 
761 	/* if we're here, that mess didn't completely handle the key (gosh...) so now here's another mess. */
762 	switch (current_type) {
763 	case WIDGET_MENUTOGGLE:
764 		if (menutoggle_handle_key(widget, k))
765 			return 1;
766 		break;
767 	case WIDGET_BITSET:
768 		if (bitset_handle_key(widget, k))
769 			return 1;
770 		break;
771 	case WIDGET_NUMENTRY:
772 		if (numentry_handle_digit(widget, k))
773 			return 1;
774 		break;
775 	case WIDGET_THUMBBAR:
776 	case WIDGET_PANBAR:
777 		if (thumbbar_prompt_value(widget, k))
778 			return 1;
779 		break;
780 	case WIDGET_TEXTENTRY:
781 		if ((k->mod & (KMOD_CTRL | KMOD_ALT | KMOD_META)) == 0
782 				&& textentry_add_char(widget, k->unicode))
783 			return 1;
784 		break;
785 	default:
786 		break;
787 	}
788 
789 	/* if we got down here the key wasn't handled */
790 	return 0;
791 }
792 
793