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 
26 #include "it.h"
27 #include "song.h"
28 #include "page.h"
29 
30 #include "sdlmain.h"
31 
32 /* --------------------------------------------------------------------- */
print_double(double x)33 
34 static struct widget widgets_orderpan[65], widgets_ordervol[65];
35 
36 static int top_order = 0;
37 static int current_order = 0;
38 static int orderlist_cursor_pos = 0;
39 
40 static unsigned char saved_orderlist[256];
41 static int _did_save_orderlist = 0;
42 
43 /* --------------------------------------------------------------------- */
44 
45 static void orderlist_reposition(void)
46 {
47 	if (current_order < top_order) {
48 		top_order = current_order;
49 	} else if (current_order > top_order + 31) {
50 		top_order = current_order - 31;
51 	}
52 }
53 
54 /* --------------------------------------------------------------------- */
55 
56 void update_current_order(void)
57 {
58 	char buf[4];
59 
60 	draw_text(numtostr(3, current_order, buf), 12, 5, 5, 0);
61 	draw_text(numtostr(3, csf_last_order(current_song), buf), 16, 5, 5, 0);
62 }
main(void)63 
64 
65 void set_current_order(int order)
66 {
67 	current_order = CLAMP(order, 0, 255);
68 	orderlist_reposition();
69 
70 	status.flags |= NEED_UPDATE;
71 }
72 
73 int get_current_order(void)
74 {
75 	return current_order;
76 }
77 
78 /* --------------------------------------------------------------------- */
79 /* called from the pattern editor on ctrl-plus/minus */
80 
81 void prev_order_pattern(void)
82 {
83 	int new_order = current_order;
84 	int last_pattern = current_song->orderlist[new_order];
85 
86 	do {
87 		if (--new_order < 0) {
88 			new_order = 0;
89 			break;
90 		}
91 	} while (!(status.flags & CLASSIC_MODE)
92 			&& last_pattern == current_song->orderlist[new_order]
93 			&& current_song->orderlist[new_order] == ORDER_SKIP);
94 
95 	if (current_song->orderlist[new_order] < 200) {
96 		current_order = new_order;
97 		orderlist_reposition();
98 		set_current_pattern(current_song->orderlist[new_order]);
99 	}
100 }
101 
102 void next_order_pattern(void)
103 {
104 	int new_order = current_order;
105 	int last_pattern = current_song->orderlist[new_order];
106 
107 	do {
108 		if (++new_order > 255) {
109 			new_order = 255;
110 			break;
111 		}
112 	} while (!(status.flags & CLASSIC_MODE)
113 			&& last_pattern == current_song->orderlist[new_order]
114 			&&  current_song->orderlist[new_order] == ORDER_SKIP);
115 
116 	if (current_song->orderlist[new_order] < 200) {
117 		current_order = new_order;
118 		orderlist_reposition();
119 		set_current_pattern(current_song->orderlist[new_order]);
120 	}
121 }
122 
123 static void orderlist_cheater(void)
124 {
125 	song_note_t *data;
126 	int cp, i, best, first;
127 	int rows;
128 
129 	if (current_song->orderlist[current_order] != ORDER_SKIP
130 	    && current_song->orderlist[current_order] != ORDER_LAST) {
131 		return;
132 	}
133 	cp = get_current_pattern();
134 	best = first = -1;
135 	for (i = 0; i < 199; i++) {
136 		if (csf_pattern_is_empty(current_song, i)) {
137 			if (first == -1) first = i;
138 			if (best == -1) best = i;
139 		} else {
140 			best = -1;
141 		}
142 	}
143 	if (best == -1) best = first;
144 	if (best == -1) return;
145 
146 	status_text_flash("Pattern %d copied to pattern %d, order %d", cp, best, current_order);
147 
148 	data = song_pattern_allocate_copy(cp, &rows);
149 	song_pattern_resize(best, rows);
150 	song_pattern_install(best, data, rows);
151 	current_song->orderlist[current_order] = best;
152 	current_order++;
153 	status.flags |= SONG_NEEDS_SAVE;
154 	status.flags |= NEED_UPDATE;
155 }
156 
157 /* --------------------------------------------------------------------- */
158 
159 static void get_pattern_string(unsigned char pattern, char *buf)
160 {
161 	switch (pattern) {
162 	case ORDER_SKIP:
163 		buf[0] = buf[1] = buf[2] = '+';
164 		buf[3] = 0;
165 		break;
166 	case ORDER_LAST:
167 		buf[0] = buf[1] = buf[2] = '-';
168 		buf[3] = 0;
169 		break;
170 	default:
171 		numtostr(3, pattern, buf);
172 		break;
173 	}
174 }
175 
176 static void orderlist_draw(void)
177 {
178 	char buf[4];
179 	int pos, n;
180 	int playing_order = (song_get_mode() == MODE_PLAYING ? song_get_current_order() : -1);
181 
182 	/* draw the list */
183 	for (pos = 0, n = top_order; pos < 32; pos++, n++) {
184 		draw_text(numtostr(3, n, buf), 2, 15 + pos, (n == playing_order ? 3 : 0), 2);
185 		get_pattern_string(current_song->orderlist[n], buf);
186 		draw_text(buf, 6, 15 + pos, 2, 0);
187 	}
188 
189 	/* draw the cursor */
190 	if (ACTIVE_PAGE.selected_widget == 0) {
191 		get_pattern_string(current_song->orderlist[current_order], buf);
192 		pos = current_order - top_order;
193 		draw_char(buf[orderlist_cursor_pos], orderlist_cursor_pos + 6, 15 + pos, 0, 3);
194 	}
195 
196 	status.flags |= NEED_UPDATE;
197 }
198 
199 /* --------------------------------------------------------------------- */
200 
201 static void orderlist_insert_pos(void)
202 {
203 	memmove(current_song->orderlist + current_order + 1,
204 		current_song->orderlist + current_order,
205 		255 - current_order);
206 	current_song->orderlist[current_order] = ORDER_LAST;
207 
208 	status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
209 }
210 
211 static void orderlist_save(void)
212 {
213 	memcpy(saved_orderlist, current_song->orderlist, 255);
214 	_did_save_orderlist = 1;
215 }
216 static void orderlist_restore(void)
217 {
218 	unsigned char oldlist[256];
219 	if (!_did_save_orderlist) return;
220 	memcpy(oldlist, current_song->orderlist, 255);
221 	memcpy(current_song->orderlist, saved_orderlist, 255);
222 	memcpy(saved_orderlist, oldlist, 255);
223 }
224 
225 static void orderlist_delete_pos(void)
226 {
227 	memmove(current_song->orderlist + current_order,
228 		current_song->orderlist + current_order + 1,
229 		255 - current_order);
230 	current_song->orderlist[255] = ORDER_LAST;
231 
232 	status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
233 }
234 
235 static void orderlist_insert_next(void)
236 {
237 	int next_pattern;
238 
239 	if (current_order == 0 || current_song->orderlist[current_order - 1] > 199)
240 		return;
241 	next_pattern = current_song->orderlist[current_order - 1] + 1;
242 	if (next_pattern > 199)
243 		next_pattern = 199;
244 	current_song->orderlist[current_order] = next_pattern;
245 	if (current_order < 255)
246 		current_order++;
247 	orderlist_reposition();
248 
249 	status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
250 }
251 
252 static void orderlist_add_unused_patterns(void)
253 {
254 	/* n0 = the first free order
255 	 * n = orderlist position
256 	 * p = pattern iterator
257 	 * np = number of patterns */
258 	int n0, n, p, np = csf_get_num_patterns(current_song);
259 	uint8_t used[200] = {0};                /* could be a bitset... */
260 
261 	for (n = 0; n < 255; n++)
262 		if (current_song->orderlist[n] < 200)
263 			used[current_song->orderlist[n]] = 1;
264 
265 	/* after the loop, n == 255 */
266 	while (n >= 0 && current_song->orderlist[n] == 0xff)
267 		n--;
268 	if (n == -1)
269 		n = 0;
270 	else
271 		n += 2;
272 
273 	n0 = n;
274 	for (p = 0; p < np; p++) {
275 		if (used[p] || csf_pattern_is_empty(current_song, p))
276 			continue;
277 		if (n > 255) {
278 			/* status_text_flash("No more room in orderlist"); */
279 			break;
280 		}
281 		current_song->orderlist[n++] = p;
282 	}
283 	if (n == n0) {
284 		status_text_flash("No unused patterns");
285 	} else {
286 		set_current_order(n - 1);
287 		set_current_order(n0);
288 		if (n - n0 == 1) {
289 			status_text_flash("1 unused pattern found");
290 		} else {
291 			status_text_flash("%d unused patterns found", n - n0);
292 		}
293 	}
294 
295 	status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
296 }
297 
298 static void orderlist_reorder(void)
299 {
300 	/* err, I hope this is going to be done correctly...
301 	*/
302 	song_note_t *np[256] = {};
303 	int nplen[256];
304 	unsigned char mapol[256];
305 	int i, j;
306 
307 	song_lock_audio();
308 
309 	orderlist_add_unused_patterns();
310 
311 	memset(mapol, ORDER_LAST, sizeof(mapol));
312 	for (i = j = 0; i < 255; i++) {
313 		if (current_song->orderlist[i] == ORDER_LAST || current_song->orderlist[i] == ORDER_SKIP) {
314 			continue;
315 		}
316 		if (mapol[ current_song->orderlist[i] ] == ORDER_LAST) {
317 			np[j] = song_pattern_allocate_copy(current_song->orderlist[i], &nplen[j]);
318 			mapol[ current_song->orderlist[i] ] = j;
319 			j++;
320 		}
321 		/* replace orderlist entry */
322 		current_song->orderlist[i] = mapol[ current_song->orderlist[i] ];
323 	}
324 	for (i = 0; i < 200; i++) {
325 		if (!np[i]) {
326 			song_pattern_install(i, NULL, 64);
327 		} else {
328 			song_pattern_install(i, np[i], nplen[i]);
329 		}
330 	}
331 
332 	status.flags |= NEED_UPDATE | SONG_NEEDS_SAVE;
333 
334 	song_stop_unlocked(0);
335 
336 	song_unlock_audio();
337 }
338 
339 static int orderlist_handle_char(struct key_event *k)
340 {
341 	int c;
342 	int cur_pattern;
343 	//unsigned char *list;
344 	song_note_t *tmp;
345 	int n[3] = { 0 };
346 
347 	switch (k->sym) {
348 	case SDLK_PLUS:
349 		if (k->state == KEY_RELEASE)
350 			return 1;
351 		status.flags |= SONG_NEEDS_SAVE;
352 		current_song->orderlist[current_order] = ORDER_SKIP;
353 		orderlist_cursor_pos = 2;
354 		break;
355 	case SDLK_PERIOD:
356 	case SDLK_MINUS:
357 		if (k->state == KEY_RELEASE)
358 			return 1;
359 		status.flags |= SONG_NEEDS_SAVE;
360 		current_song->orderlist[current_order] = ORDER_LAST;
361 		orderlist_cursor_pos = 2;
362 		break;
363 	default:
364 		c = numeric_key_event(k, 0);
365 		if (c == -1) return 0;
366 		if (k->state == KEY_RELEASE)
367 			return 1;
368 
369 		status.flags |= SONG_NEEDS_SAVE;
370 		cur_pattern = current_song->orderlist[current_order];
371 		if (cur_pattern < 200) {
372 			n[0] = cur_pattern / 100;
373 			n[1] = cur_pattern / 10 % 10;
374 			n[2] = cur_pattern % 10;
375 		}
376 
377 		n[orderlist_cursor_pos] = c;
378 		cur_pattern = n[0] * 100 + n[1] * 10 + n[2];
379 		cur_pattern = CLAMP(cur_pattern, 0, 199);
380 		song_get_pattern(cur_pattern, &tmp); /* make sure it exists */
381 		current_song->orderlist[current_order] = cur_pattern;
382 		break;
383 	};
384 
385 	if (orderlist_cursor_pos == 2) {
386 		if (current_order < 255)
387 			current_order++;
388 		orderlist_cursor_pos = 0;
389 		orderlist_reposition();
390 	} else {
391 		orderlist_cursor_pos++;
392 	}
393 
394 	status.flags |= NEED_UPDATE;
395 
396 	return 1;
397 }
398 
399 static int orderlist_handle_key_on_list(struct key_event * k)
400 {
401 	int prev_order = current_order;
402 	int new_order = prev_order;
403 	int new_cursor_pos = orderlist_cursor_pos;
404 	int n, p;
405 
406 	if (k->mouse != MOUSE_NONE) {
407 		if (k->x >= 6 && k->x <= 8 && k->y >= 15 && k->y <= 46) {
408 			/* FIXME adjust top_order, not the cursor */
409 			if (k->mouse == MOUSE_SCROLL_UP) {
410 				new_order -= MOUSE_SCROLL_LINES;
411 			} else if (k->mouse == MOUSE_SCROLL_DOWN) {
412 				new_order += MOUSE_SCROLL_LINES;
413 			} else {
414 				if (k->state == KEY_PRESS)
415 					return 0;
416 
417 				new_order = (k->y - 15) + top_order;
418 				set_current_order(new_order);
419 				new_order = current_order;
420 
421 				if (current_song->orderlist[current_order] != ORDER_LAST
422 				&& current_song->orderlist[current_order] != ORDER_SKIP) {
423 					new_cursor_pos = (k->x - 6);
424 				}
425 			}
426 		}
427 	}
428 
429 	switch (k->sym) {
430 	case SDLK_BACKSPACE:
431 		if (status.flags & CLASSIC_MODE) return 0;
432 		if (!(k->mod & KMOD_ALT)) return 0;
433 		if (k->state == KEY_PRESS)
434 			return 1;
435 		if (!_did_save_orderlist) return 1;
436 		status_text_flash("Restored orderlist");
437 		orderlist_restore();
438 		return 1;
439 
440 	case SDLK_RETURN:
441 	case SDLK_KP_ENTER:
442 		if (status.flags & CLASSIC_MODE) return 0;
443 		if (k->mod & KMOD_ALT) {
444 			if (k->state == KEY_PRESS)
445 				return 1;
446 			status_text_flash("Saved orderlist");
447 			orderlist_save();
448 			return 1;
449 		}
450 		// else fall through
451 
452 	case SDLK_g:
453 		if (!NO_MODIFIER(k->mod))
454 			return 0;
455 		if (k->state == KEY_PRESS)
456 			return 1;
457 		n = current_song->orderlist[new_order];
458 		while (n >= 200 && new_order > 0)
459 			n = current_song->orderlist[--new_order];
460 		if (n < 200) {
461 			set_current_pattern(n);
462 			set_page(PAGE_PATTERN_EDITOR);
463 		}
464 		return 1;
465 
466 	case SDLK_TAB:
467 		if (k->mod & KMOD_SHIFT) {
468 			if (k->state == KEY_RELEASE)
469 				return 1;
470 			change_focus_to(33);
471 		} else {
472 			if (!NO_MODIFIER(k->mod)) return 0;
473 			if (k->state == KEY_RELEASE)
474 				return 1;
475 			change_focus_to(1);
476 		}
477 		return 1;
478 	case SDLK_LEFT:
479 		if (!NO_MODIFIER(k->mod))
480 			return 0;
481 		if (k->state == KEY_RELEASE)
482 			return 1;
483 		new_cursor_pos--;
484 		break;
485 	case SDLK_RIGHT:
486 		if (!NO_MODIFIER(k->mod))
487 			return 0;
488 		if (k->state == KEY_RELEASE)
489 			return 1;
490 		new_cursor_pos++;
491 		break;
492 	case SDLK_HOME:
493 		if (!NO_MODIFIER(k->mod))
494 			return 0;
495 		if (k->state == KEY_RELEASE)
496 			return 1;
497 		new_order = 0;
498 		break;
499 	case SDLK_END:
500 		if (!NO_MODIFIER(k->mod))
501 			return 0;
502 		if (k->state == KEY_RELEASE)
503 			return 1;
504 		new_order = csf_last_order(current_song);
505 		if (current_song->orderlist[new_order] != ORDER_LAST)
506 			new_order++;
507 		break;
508 	case SDLK_UP:
509 		if (k->mod & KMOD_CTRL) {
510 			if (status.flags & CLASSIC_MODE) return 0;
511 			if (k->state == KEY_RELEASE)
512 				return 1;
513 			sample_set(sample_get_current()-1);
514 			status.flags |= NEED_UPDATE;
515 			return 1;
516 		}
517 		if (!NO_MODIFIER(k->mod))
518 			return 0;
519 		if (k->state == KEY_RELEASE)
520 			return 1;
521 		new_order--;
522 		break;
523 	case SDLK_DOWN:
524 		if (k->mod & KMOD_CTRL) {
525 			if (status.flags & CLASSIC_MODE) return 0;
526 			if (k->state == KEY_RELEASE)
527 				return 1;
528 			sample_set(sample_get_current()+1);
529 			status.flags |= NEED_UPDATE;
530 			return 1;
531 		}
532 		if (!NO_MODIFIER(k->mod))
533 			return 0;
534 		if (k->state == KEY_RELEASE)
535 			return 1;
536 		new_order++;
537 		break;
538 	case SDLK_PAGEUP:
539 		if (!NO_MODIFIER(k->mod))
540 			return 0;
541 		if (k->state == KEY_RELEASE)
542 			return 1;
543 		new_order -= 16;
544 		break;
545 	case SDLK_PAGEDOWN:
546 		if (!NO_MODIFIER(k->mod))
547 			return 0;
548 		if (k->state == KEY_RELEASE)
549 			return 1;
550 		new_order += 16;
551 		break;
552 	case SDLK_INSERT:
553 		if (!NO_MODIFIER(k->mod))
554 			return 0;
555 		if (k->state == KEY_RELEASE)
556 			return 1;
557 		orderlist_insert_pos();
558 		return 1;
559 	case SDLK_DELETE:
560 		if (!NO_MODIFIER(k->mod))
561 			return 0;
562 		if (k->state == KEY_RELEASE)
563 			return 1;
564 		orderlist_delete_pos();
565 		return 1;
566 	case SDLK_F7:
567 		if (!(k->mod & KMOD_CTRL)) return 0;
568 		/* fall through */
569 	case SDLK_SPACE:
570 		if (k->state == KEY_RELEASE)
571 			return 1;
572 		song_set_next_order(current_order);
573 		status_text_flash("Playing order %d next", current_order);
574 		return 1;
575 	case SDLK_F6:
576 		if (k->mod & KMOD_SHIFT) {
577 			if (k->state == KEY_RELEASE)
578 				return 1;
579 			song_start_at_order(current_order, 0);
580 			return 1;
581 		}
582 		return 0;
583 
584 	case SDLK_n:
585 		if (k->mod & KMOD_SHIFT) {
586 			if (k->state == KEY_PRESS)
587 				return 1;
588 			orderlist_cheater();
589 			return 1;
590 		}
591 		if (!NO_MODIFIER(k->mod))
592 			return 0;
593 		if (k->state == KEY_RELEASE)
594 			return 1;
595 		orderlist_insert_next();
596 		return 1;
597 	case SDLK_c:
598 		if (!NO_MODIFIER(k->mod))
599 			return 0;
600 		if (status.flags & CLASSIC_MODE) return 0;
601 		if (k->state == KEY_PRESS)
602 			return 1;
603 		p = get_current_pattern();
604 		for (n = current_order+1; n < 256; n++) {
605 			if (current_song->orderlist[n] == p) {
606 				new_order = n;
607 				break;
608 			}
609 		}
610 		if (n == 256) {
611 			for (n = 0; n < current_order; n++) {
612 				if (current_song->orderlist[n] == p) {
613 					new_order = n;
614 					break;
615 				}
616 			}
617 			if (n == current_order) {
618 				status_text_flash("Pattern %d not on Order List", p);
619 				return 1;
620 			}
621 		}
622 		break;
623 
624 	case SDLK_r:
625 		if (k->mod & KMOD_ALT) {
626 			if (k->state == KEY_PRESS)
627 				return 1;
628 			orderlist_reorder();
629 			return 1;
630 		}
631 		return 0;
632 	case SDLK_u:
633 		if (k->mod & KMOD_ALT) {
634 			if (k->state == KEY_RELEASE)
635 				return 1;
636 			orderlist_add_unused_patterns();
637 			return 1;
638 		}
639 		return 0;
640 
641 	case SDLK_b:
642 		if (k->mod & KMOD_SHIFT)
643 			return 0;
644 		/* fall through */
645 	case SDLK_o:
646 		if (!(k->mod & KMOD_CTRL))
647 			return 0;
648 		if (k->state == KEY_RELEASE)
649 			return 1;
650 		song_pattern_to_sample(current_song->orderlist[current_order],
651 				!!(k->mod & KMOD_SHIFT), !!(k->sym == SDLK_b));
652 		return 1;
653 
654 	case SDLK_LESS:
655 	case SDLK_SEMICOLON:
656 	case SDLK_COLON:
657 		if (!NO_MODIFIER(k->mod)) return 0;
658 		if (k->state == KEY_RELEASE)
659 			return 1;
660 		sample_set(sample_get_current()-1);
661 		status.flags |= NEED_UPDATE;
662 		return 1;
663 	case SDLK_GREATER:
664 	case SDLK_QUOTE:
665 	case SDLK_QUOTEDBL:
666 		if (!NO_MODIFIER(k->mod)) return 0;
667 		if (k->state == KEY_RELEASE)
668 			return 1;
669 		sample_set(sample_get_current()+1);
670 		status.flags |= NEED_UPDATE;
671 		return 1;
672 	default:
673 		if (k->mouse == MOUSE_NONE) {
674 			if ((k->mod & (KMOD_CTRL | KMOD_ALT))==0) {
675 				return orderlist_handle_char(k);
676 			}
677 			return 0;
678 		}
679 	}
680 
681 	if (new_cursor_pos < 0)
682 		new_cursor_pos = 2;
683 	else if (new_cursor_pos > 2)
684 		new_cursor_pos = 0;
685 
686 	if (new_order != prev_order) {
687 		set_current_order(new_order);
688 	} else if (new_cursor_pos != orderlist_cursor_pos) {
689 		orderlist_cursor_pos = new_cursor_pos;
690 	} else {
691 		return 0;
692 	}
693 
694 	status.flags |= NEED_UPDATE;
695 	return 1;
696 }
697 
698 /* --------------------------------------------------------------------- */
699 
700 static void order_pan_vol_draw_const(void)
701 {
702 	draw_box(5, 14, 9, 47, BOX_THICK | BOX_INNER | BOX_INSET);
703 
704 	draw_box(30, 14, 40, 47, BOX_THICK | BOX_INNER | BOX_FLAT_LIGHT);
705 	draw_box(64, 14, 74, 47, BOX_THICK | BOX_INNER | BOX_FLAT_LIGHT);
706 
707 	draw_char(146, 30, 14, 3, 2);
708 	draw_char(145, 40, 14, 3, 2);
709 
710 	draw_char(146, 64, 14, 3, 2);
711 	draw_char(145, 74, 14, 3, 2);
712 }
713 
714 static void orderpan_draw_const(void)
715 {
716 	order_pan_vol_draw_const();
717 	draw_text("L   M   R", 31, 14, 0, 3);
718 	draw_text("L   M   R", 65, 14, 0, 3);
719 }
720 
721 static void ordervol_draw_const(void)
722 {
723 	int n;
724 	char buf[16];
725 	int fg;
726 
727 	strcpy(buf, "Channel 42");
728 
729 	order_pan_vol_draw_const();
730 
731 	draw_text(" Volumes ", 31, 14, 0, 3);
732 	draw_text(" Volumes ", 65, 14, 0, 3);
733 
734 	for (n = 1; n <= 32; n++) {
735 		fg = 0;
736 		if (!(status.flags & CLASSIC_MODE)) {
737 			if (ACTIVE_PAGE.selected_widget == n) {
738 				fg = 3;
739 			}
740 		}
741 
742 		numtostr(2, n, buf + 8);
743 		draw_text(buf, 20, 14 + n, fg, 2);
744 
745 		fg = 0;
746 		if (!(status.flags & CLASSIC_MODE)) {
747 			if (ACTIVE_PAGE.selected_widget == n+32) {
748 				fg = 3;
749 			}
750 		}
751 
752 		numtostr(2, n + 32, buf + 8);
753 		draw_text(buf, 54, 14 + n, fg, 2);
754 	}
755 }
756 
757 /* --------------------------------------------------------------------- */
758 
759 static void order_pan_vol_playback_update(void)
760 {
761 	static int last_order = -1;
762 	int order = ((song_get_mode() == MODE_STOPPED) ? -1 : song_get_current_order());
763 
764 	if (order != last_order) {
765 		last_order = order;
766 		status.flags |= NEED_UPDATE;
767 	}
768 }
769 
770 /* --------------------------------------------------------------------- */
771 
772 static void orderpan_update_values_in_song(void)
773 {
774 	song_channel_t *chn;
775 	int n;
776 
777 	status.flags |= SONG_NEEDS_SAVE;
778 	for (n = 0; n < 64; n++) {
779 		chn = song_get_channel(n);
780 
781 		/* yet another modplug hack here! */
782 		chn->panning = widgets_orderpan[n + 1].d.panbar.value * 4;
783 
784 		if (widgets_orderpan[n + 1].d.panbar.surround)
785 			chn->flags |= CHN_SURROUND;
786 		else
787 			chn->flags &= ~CHN_SURROUND;
788 
789 		song_set_channel_mute(n, widgets_orderpan[n + 1].d.panbar.muted);
790 	}
791 }
792 
793 static void ordervol_update_values_in_song(void)
794 {
795 	int n;
796 
797 	status.flags |= SONG_NEEDS_SAVE;
798 	for (n = 0; n < 64; n++)
799 		song_get_channel(n)->volume = widgets_ordervol[n + 1].d.thumbbar.value;
800 }
801 
802 /* called when a channel is muted/unmuted by means other than the panning
803  * page (alt-f10 in the pattern editor, space on the info page...) */
804 void orderpan_recheck_muted_channels(void)
805 {
806 	int n;
807 	for (n = 0; n < 64; n++)
808 		widgets_orderpan[n + 1].d.panbar.muted = !!(song_get_channel(n)->flags & CHN_MUTE);
809 
810 	if (status.current_page == PAGE_ORDERLIST_PANNING)
811 		status.flags |= NEED_UPDATE;
812 }
813 
814 static void order_pan_vol_song_changed_cb(void)
815 {
816 	int n;
817 	song_channel_t *chn;
818 
819 	for (n = 0; n < 64; n++) {
820 		chn = song_get_channel(n);
821 		widgets_orderpan[n + 1].d.panbar.value = chn->panning / 4;
822 		widgets_orderpan[n + 1].d.panbar.surround = !!(chn->flags & CHN_SURROUND);
823 		widgets_orderpan[n + 1].d.panbar.muted = !!(chn->flags & CHN_MUTE);
824 		widgets_ordervol[n + 1].d.thumbbar.value = chn->volume;
825 	}
826 }
827 
828 /* --------------------------------------------------------------------- */
829 
830 static void order_pan_vol_handle_key(struct key_event * k)
831 {
832 	int n = ACTIVE_PAGE.selected_widget;
833 
834 	if (k->state == KEY_RELEASE)
835 		return;
836 
837 	if (!NO_MODIFIER(k->mod))
838 		return;
839 
840 	switch (k->sym) {
841 	case SDLK_PAGEDOWN:
842 		n += 8;
843 		break;
844 	case SDLK_PAGEUP:
845 		n -= 8;
846 		break;
847 	default:
848 		return;
849 	}
850 
851 	n = CLAMP(n, 1, 64);
852 	if (ACTIVE_PAGE.selected_widget != n)
853 		change_focus_to(n);
854 }
855 
856 static int order_pre_key(struct key_event *k)
857 {
858 	// hack to sync the active widget between pan/vol pages
859 	if (!(status.flags & CLASSIC_MODE)) {
860 		pages[PAGE_ORDERLIST_PANNING].selected_widget
861 			= pages[PAGE_ORDERLIST_VOLUMES].selected_widget
862 			= ACTIVE_PAGE.selected_widget;
863 	}
864 
865 	if (k->sym == SDLK_F7) {
866 		if (!NO_MODIFIER(k->mod)) return 0;
867 		if (k->state == KEY_RELEASE)
868 			return 1;
869 		play_song_from_mark_orderpan();
870 		return 1;
871 	}
872 	return 0;
873 }
874 
875 static void order_pan_set_page(void)
876 {
877 	orderpan_recheck_muted_channels();
878 }
879 
880 /* --------------------------------------------------------------------- */
881 
882 void orderpan_load_page(struct page *page)
883 {
884 	int n;
885 
886 	page->title = "Order List and Panning (F11)";
887 	page->draw_const = orderpan_draw_const;
888 	/* this does the work for both pages */
889 	page->song_changed_cb = order_pan_vol_song_changed_cb;
890 	page->playback_update = order_pan_vol_playback_update;
891 	page->pre_handle_key = order_pre_key;
892 	page->handle_key = order_pan_vol_handle_key;
893 	page->set_page = order_pan_set_page;
894 	page->total_widgets = 65;
895 	page->widgets = widgets_orderpan;
896 	page->help_index = HELP_ORDERLIST_PANNING;
897 
898 	/* 0 = order list */
899 	create_other(widgets_orderpan + 0, 1, orderlist_handle_key_on_list, orderlist_draw);
900 	widgets_orderpan[0].accept_text = 0;
901 	widgets_orderpan[0].x = 6;
902 	widgets_orderpan[0].y = 15;
903 	widgets_orderpan[0].width = 3;
904 	widgets_orderpan[0].height = 32;
905 
906 	/* 1-64 = panbars */
907 	create_panbar(widgets_orderpan + 1, 20, 15, 1, 2, 33, orderpan_update_values_in_song, 1);
908 	for (n = 2; n <= 32; n++) {
909 		create_panbar(widgets_orderpan + n, 20, 14 + n, n - 1, n + 1, n + 32,
910 			      orderpan_update_values_in_song, n);
911 		create_panbar(widgets_orderpan + n + 31, 54, 13 + n, n + 30, n + 32, 0,
912 			      orderpan_update_values_in_song, n + 31);
913 	}
914 	create_panbar(widgets_orderpan + 64, 54, 46, 63, 64, 0, orderpan_update_values_in_song, 64);
915 }
916 
917 void ordervol_load_page(struct page *page)
918 {
919 	int n;
920 
921 	page->title = "Order List and Channel Volume (F11)";
922 	page->draw_const = ordervol_draw_const;
923 	page->playback_update = order_pan_vol_playback_update;
924 	page->pre_handle_key = order_pre_key;
925 	page->handle_key = order_pan_vol_handle_key;
926 	page->total_widgets = 65;
927 	page->widgets = widgets_ordervol;
928 	page->help_index = HELP_ORDERLIST_VOLUME;
929 
930 	/* 0 = order list */
931 	create_other(widgets_ordervol + 0, 1, orderlist_handle_key_on_list, orderlist_draw);
932 	widgets_ordervol[0].accept_text = 0;
933 	widgets_ordervol[0].x = 6;
934 	widgets_ordervol[0].y = 15;
935 	widgets_ordervol[0].width = 3;
936 	widgets_ordervol[0].height = 32;
937 
938 	/* 1-64 = thumbbars */
939 	create_thumbbar(widgets_ordervol + 1, 31, 15, 9, 1, 2, 33, ordervol_update_values_in_song, 0, 64);
940 	for (n = 2; n <= 32; n++) {
941 		create_thumbbar(widgets_ordervol + n, 31, 14 + n, 9, n - 1, n + 1, n + 32,
942 				ordervol_update_values_in_song, 0, 64);
943 		create_thumbbar(widgets_ordervol + n + 31, 65, 13 + n, 9, n + 30, n + 32, 0,
944 				ordervol_update_values_in_song, 0, 64);
945 	}
946 	create_thumbbar(widgets_ordervol + 64, 65, 46, 9, 63, 64, 0, ordervol_update_values_in_song, 0, 64);
947 }
948 
949 /* --------------------------------------------------------------------- */
950 /* this function is a lost little puppy */
951 
952 #define MAX_CHANNELS 64 // blah
953 void song_set_pan_scheme(int scheme)
954 {
955 	int n, nc;
956 	//int half;
957 	int active = 0;
958 	song_channel_t *chn = song_get_channel(0);
959 
960 	//mphack alert, all pan values multiplied by 4
961 	switch (scheme) {
962 	case PANS_STEREO:
963 		for (n = 0; n < MAX_CHANNELS; n++) {
964 			if (!(chn[n].flags & CHN_MUTE))
965 				chn[n].panning = (n & 1) ? 256 : 0;
966 		}
967 		break;
968 	case PANS_AMIGA:
969 		for (n = 0; n < MAX_CHANNELS; n++) {
970 			if (!(chn[n].flags & CHN_MUTE))
971 				chn[n].panning = ((n + 1) & 2) ? 256 : 0;
972 		}
973 		break;
974 	case PANS_LEFT:
975 		for (n = 0; n < MAX_CHANNELS; n++) {
976 			if (!(chn[n].flags & CHN_MUTE))
977 				chn[n].panning = 0;
978 		}
979 		break;
980 	case PANS_RIGHT:
981 		for (n = 0; n < MAX_CHANNELS; n++) {
982 			if (!(chn[n].flags & CHN_MUTE))
983 				chn[n].panning = 256;
984 		}
985 		break;
986 	case PANS_MONO:
987 		for (n = 0; n < MAX_CHANNELS; n++) {
988 			if (!(chn[n].flags & CHN_MUTE))
989 				chn[n].panning = 128;
990 		}
991 		break;
992 	case PANS_SLASH:
993 		for (n = 0; n < MAX_CHANNELS; n++) {
994 			if (!(chn[n].flags & CHN_MUTE))
995 				active++;
996 		}
997 		for (n = 0, nc = 0; nc < active; n++) {
998 			if (!(chn[n].flags & CHN_MUTE)) {
999 				chn[n].panning = 256 - (256 * nc / (active - 1));
1000 				nc++;
1001 			}
1002 		}
1003 		break;
1004 	case PANS_BACKSLASH:
1005 		for (n = 0; n < MAX_CHANNELS; n++) {
1006 			if (!(chn[n].flags & CHN_MUTE))
1007 				active++;
1008 		}
1009 		for (n = 0, nc = 0; nc < active; n++) {
1010 			if (!(chn[n].flags & CHN_MUTE)) {
1011 				chn[n].panning = (256 * nc / (active - 1));
1012 				nc++;
1013 			}
1014 		}
1015 		break;
1016 #if 0
1017 	case PANS_CROSS:
1018 		for (n = 0; n < MAX_CHANNELS; n++) {
1019 			if (!(chn[n].flags & CHN_MUTE))
1020 				active++;
1021 		}
1022 		half = active / 2;
1023 		for (n = 0, nc = 0; nc < half; n++) {
1024 			if (!(chn[n].flags & CHN_MUTE)) {
1025 				if (nc & 1) {
1026 					// right bias - go from 64 to 32
1027 					chn[n].panning = (64 - (32 * nc / half)) * 4;
1028 				} else {
1029 					// left bias - go from 0 to 32
1030 					chn[n].panning = (32 * nc / half) * 4;
1031 				}
1032 				nc++;
1033 			}
1034 		}
1035 		for (; nc < active; n++) {
1036 			if (!(chn[n].flags & CHN_MUTE)) {
1037 				if (nc & 1) {
1038 					chn[n].panning = (64 - (32 * (active - nc) / half)) * 4;
1039 				} else {
1040 					chn[n].panning = (32 * (active - nc) / half) * 4;
1041 				}
1042 				nc++;
1043 			}
1044 		}
1045 		break;
1046 #endif
1047 	default:
1048 		printf("oh i am confused\n");
1049 	}
1050 	// get the values on the page to correspond to the song...
1051 	order_pan_vol_song_changed_cb();
1052 }
1053 
1054