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 #include "pattern-view.h"
30 #include "config-parser.h"
31 
32 #include "sdlmain.h"
33 
34 #include <assert.h>
35 
36 /* --------------------------------------------------------------------- */
37 
38 static struct widget widgets_info[1];
39 
40 /* nonzero => use velocity bars */
41 static int velocity_mode = 0;
42 
43 /* nonzero => instrument names */
44 static int instrument_names = 0;
45 
46 /* --------------------------------------------------------------------- */
47 /* window setup */
48 
49 struct info_window_type {
50 	const char *id;
51 
52 	void (*draw) (int base, int height, int active, int first_channel);
53 	void (*click) (int x, int y, int num_vis_channel, int first_channel);
54 
55 	/* if this is set, the first row contains actual text (not just the top part of a box) */
56 	int first_row;
57 
58 	/* how many channels are shown -- just use 0 for windows that don't show specific channel info.
59 	for windows that put the channels vertically (i.e. sample names) this should be the amount to ADD
60 	to the height to get the number of channels, so it should be NEGATIVE. (example: the sample name
61 	view uses the first position for the top of the box and the last position for the bottom, so it
62 	uses -2.) confusing, almost to the point of being painful, but it works. (ok, i admit, it's not
63 	the most brilliant idea i ever had ;) */
64 	int channels;
65 };
66 
67 struct info_window {
68 	int type;
69 	int height;
70 	int first_channel;
71 };
72 
73 static int selected_window = 0;
74 static int num_windows = 3;
75 static int selected_channel = 1;
76 
77 /* five, because that's Impulse Tracker's maximum */
78 #define MAX_WINDOWS 5
79 static struct info_window windows[MAX_WINDOWS] = {
80 	{0, 19, 1},     /* samples (18 channels displayed) */
81 	{8, 3, 1},      /* active channels */
82 	{5, 15, 1},     /* 24chn track view */
83 };
84 
85 /* --------------------------------------------------------------------- */
86 /* the various stuff that can be drawn... */
87 
info_draw_technical(int base,int height,int active,int first_channel)88 static void info_draw_technical(int base, int height, int active, int first_channel)
89 {
90 	int smp, pos, fg, c = first_channel;
91 	char buf[16];
92 	const char *ptr;
93 
94 	/*
95 	FVl - 0-128, final calculated volume, taking everything into account:
96 		(sample volume, sample global volume, instrument volume, inst. global volume,
97 		volume envelope, volume swing, fadeout, channel volume, song global volume, effects (I/Q/R)
98 	Vl - 0-64, sample volume / volume column (also affected by I/Q/R)
99 	CV - 0-64, channel volume (M/N)
100 	SV - 0-64, sample global volume + inst global volume
101 	Fde - 0-512, HALF the fade
102 		(initially 1024, and subtracted by instrument fade value each tick when fading out)
103 	Pn - 0-64 (or "Su"), final channel panning
104 		+ pan swing + pitch/pan + current pan envelope value! + Yxx
105 		(note: suggests that Xxx panning is reduced to 64 values when it's applied?)
106 	PE - 0-64, pan envelope
107 		note: this value is not changed if pan env is turned off (e.g. with S79) -- so it's copied
108 	all of the above are still set to valid values in sample mode
109 	*/
110 
111 	draw_fill_chars(5, base + 1, 29, base + height - 2, 0);
112 	draw_box(4, base, 30, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
113 	draw_text("Frequency", 6, base, 2, 1);
114 	draw_text("Position", 17, base, 2, 1);
115 	draw_text("Smp", 27, base, 2, 1);
116 
117 	draw_fill_chars(32, base + 1, 56, base + height - 2, 0);
118 	draw_box(31, base, 57, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
119 	draw_text("FVl", 32, base, 2, 1);
120 	draw_text("Vl",  36, base, 2, 1);
121 	draw_text("CV",  39, base, 2, 1);
122 	draw_text("SV",  42, base, 2, 1);
123 	draw_text("VE",  45, base, 2, 1);
124 	draw_text("Fde", 48, base, 2, 1);
125 	draw_text("Pn",  52, base, 2, 1);
126 	draw_text("PE",  55, base, 2, 1);
127 
128 	if (song_is_instrument_mode()) {
129 		draw_fill_chars(59, base + 1, 65, base + height - 2, 0);
130 		draw_box(58, base, 66, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
131 		draw_text("NNA", 59, base, 2, 1);
132 		draw_text("Tot", 63, base, 2, 1);
133 	}
134 
135 	for (pos = base + 1; pos < base + height - 1; pos++, c++) {
136 		song_channel_t *channel = current_song->channels + c - 1;
137 		song_voice_t *voice = current_song->voices + c - 1;
138 
139 		if (c == selected_channel) {
140 			fg = (channel->flags & CHN_MUTE) ? 6 : 3;
141 		} else {
142 			if (channel->flags & CHN_MUTE)
143 				fg = 2;
144 			else
145 				fg = active ? 1 : 0;
146 		}
147 		draw_text(num99tostr(c, buf), 2, pos, fg, 2); /* channel number */
148 
149 		draw_char(168, 15, pos, 2, 0);
150 		draw_char(168, 26, pos, 2, 0);
151 		draw_char(168, 35, pos, 2, 0);
152 		draw_char(168, 38, pos, 2, 0);
153 		draw_char(168, 41, pos, 2, 0);
154 		draw_char(168, 44, pos, 2, 0);
155 		draw_char(168, 47, pos, 2, 0);
156 		draw_char(168, 51, pos, 2, 0);
157 		draw_char(168, 54, pos, 2, 0);
158 
159 		if (song_is_instrument_mode()) {
160 			draw_text("---\xa8", 59, pos, 2, 0); /* will be overwritten if something's playing */
161 
162 			/* count how many voices claim this channel */
163 			int nv, tot;
164 			for (nv = tot = 0; nv < MAX_VOICES; nv++) {
165 				song_voice_t *v = current_song->voices + nv;
166 				if (v->master_channel == (unsigned int) c && v->current_sample_data && v->length)
167 					tot++;
168 			}
169 			if (voice->current_sample_data && voice->length)
170 				tot++;
171 			draw_text(numtostr(3, tot, buf), 63, pos, 2, 0);
172 		}
173 
174 		if (voice->current_sample_data && voice->length && voice->ptr_sample) {
175 			// again with the hacks...
176 			smp = voice->ptr_sample - current_song->samples;
177 			if (smp <= 0 || smp >= MAX_SAMPLES)
178 				continue;
179 		} else {
180 			continue;
181 		}
182 
183 		// Frequency
184 		sprintf(buf, "%10d", voice->sample_freq);
185 		draw_text(buf, 5, pos, 2, 0);
186 		// Position
187 		sprintf(buf, "%10d", voice->position);
188 		draw_text(buf, 16, pos, 2, 0);
189 
190 		draw_text(numtostr(3, smp, buf), 27, pos, 2, 0); // Smp
191 		draw_text(numtostr(3, voice->final_volume / 128, buf), 32, pos, 2, 0); // FVl
192 		draw_text(numtostr(2, voice->volume >> 2, buf), 36, pos, 2, 0); // Vl
193 		draw_text(numtostr(2, voice->global_volume, buf), 39, pos, 2, 0); // CV
194 		draw_text(numtostr(2, voice->ptr_sample->global_volume, buf), 42, pos, 2, 0); // SV
195         // FIXME: VE means volume envelope. Also, voice->instrument_volume is actually sample global volume
196 		draw_text(numtostr(2, voice->instrument_volume, buf), 45, pos, 2, 0); // VE
197 		draw_text(numtostr(3, voice->fadeout_volume / 128, buf), 48, pos, 2, 0); // Fde
198 
199 		// Pn
200 		if (voice->flags & CHN_SURROUND)
201 			draw_text("Su", 52, pos, 2, 0);
202 		else
203 			draw_text(numtostr(2, voice->panning >> 2, buf), 52, pos, 2, 0);
204 
205 		draw_text(numtostr(2, voice->final_panning >> 2, buf), 55, pos, 2, 0); // PE
206 
207 		if (song_is_instrument_mode()) {
208 			switch (voice->nna) {
209 				case NNA_NOTECUT: ptr = "Cut"; break;
210 				case NNA_CONTINUE: ptr = "Con"; break;
211 				case NNA_NOTEOFF: ptr = "Off"; break;
212 				case NNA_NOTEFADE: ptr = "Fde"; break;
213 				default: ptr = "???"; break;
214 			};
215 			draw_text(ptr, 59, pos, 2, 0);
216 		}
217 	}
218 }
219 
info_draw_samples(int base,int height,int active,int first_channel)220 static void info_draw_samples(int base, int height, int active, int first_channel)
221 {
222 	int vu, smp, ins, n, pos, fg, fg2, c = first_channel;
223 	char buf[8];
224 	char *ptr;
225 
226 	draw_fill_chars(5, base + 1, 28, base + height - 2, 0);
227 	draw_fill_chars(31, base + 1, 61, base + height - 2, 0);
228 
229 	draw_box(4, base, 29, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
230 	draw_box(30, base, 62, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
231 	if (song_is_stereo()) {
232 		draw_fill_chars(64, base + 1, 72, base + height - 2, 0);
233 		draw_box(63, base, 73, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
234 	} else {
235 		draw_fill_chars(63, base, 73, base + height, 2);
236 	}
237 
238 	if (song_get_mode() == MODE_STOPPED) {
239 		for (pos = base + 1; pos < base + height - 1; pos++, c++) {
240 			song_channel_t *channel = song_get_channel(c - 1);
241 
242 			if (c == selected_channel) {
243 				fg = (channel->flags & CHN_MUTE) ? 6 : 3;
244 			} else {
245 				if (channel->flags & CHN_MUTE)
246 					continue;
247 				fg = active ? 1 : 0;
248 			}
249 			draw_text(numtostr(2, c, buf), 2, pos, fg, 2);
250 		}
251 		return;
252 	}
253 
254 	for (pos = base + 1; pos < base + height - 1; pos++, c++) {
255 		song_voice_t *voice = current_song->voices + c - 1;
256 
257 		/* always draw the channel number */
258 		if (c == selected_channel)
259 			fg = (voice->flags & CHN_MUTE) ? 6 : 3;
260 		else if (voice->flags & CHN_MUTE)
261 			fg = 2; /* same as bg */
262 		else
263 			fg = active ? 1 : 0;
264 		draw_text(numtostr(2, c, buf), 2, pos, fg, 2);
265 
266 		if (!(voice->current_sample_data && voice->length))
267 			continue;
268 
269 		/* first box: vu meter */
270 		if (velocity_mode)
271 			vu = voice->final_volume >> 8;
272 		else
273 			vu = voice->vu_meter >> 2;
274 		if (voice->flags & CHN_MUTE) {
275 			fg = 1; fg2 = 2;
276 		} else {
277 			fg = 5; fg2 = 4;
278 		}
279 		draw_vu_meter(5, pos, 24, vu, fg, fg2);
280 
281 		/* second box: sample number/name */
282 		ins = song_get_instrument_number(voice->ptr_instrument);
283 		/* figuring out the sample number is an ugly hack... considering all the crap that's
284 		copied to the channel, i'm surprised that the sample and instrument numbers aren't
285 		in there somewhere... */
286 		if (voice->ptr_sample)
287 			smp = voice->ptr_sample - current_song->samples;
288 		else
289 			smp = ins = 0;
290 		if(smp < 0 || smp >= MAX_SAMPLES)
291 			smp = ins = 0; /* This sample is not in the sample array */
292 
293 		if (smp) {
294 			draw_text(num99tostr(smp, buf), 31, pos, 6, 0);
295 			if (ins) {
296 				draw_char('/', 33, pos, 6, 0);
297 				draw_text(num99tostr(ins, buf), 34, pos, 6, 0);
298 				n = 36;
299 			} else {
300 				n = 33;
301 			}
302 			if (voice->volume == 0)
303 				fg = 4;
304 			else if (voice->flags & (CHN_KEYOFF | CHN_NOTEFADE))
305 				fg = 7;
306 			else
307 				fg = 6;
308 			draw_char(':', n++, pos, fg, 0);
309 			if (instrument_names && voice->ptr_instrument) {
310 				ptr = voice->ptr_instrument->name;
311 			} else {
312 				ptr = current_song->samples[smp].name;
313 			}
314 			draw_text_len(ptr, 25, n, pos, 6, 0);
315 		} else if (ins && voice->ptr_instrument && voice->ptr_instrument->midi_channel_mask) {
316 			// XXX why? what?
317 			if (voice->ptr_instrument->midi_channel_mask >= 0x10000) {
318 				draw_text(numtostr(2, ((c-1) % 16)+1, buf), 31, pos, 6, 0);
319 			} else {
320 				int ch = 0;
321 				while(!(voice->ptr_instrument->midi_channel_mask & (1 << ch))) ++ch;
322 				draw_text(numtostr(2, ch, buf), 31, pos, 6, 0);
323 			}
324 			draw_char('/', 33, pos, 6, 0);
325 			draw_text(num99tostr(ins, buf), 34, pos, 6, 0);
326 			n = 36;
327 			if (voice->volume == 0)
328 				fg = 4;
329 			else if (voice->flags & (CHN_KEYOFF | CHN_NOTEFADE))
330 				fg = 7;
331 			else
332 				fg = 6;
333 			draw_char(':', n++, pos, fg, 0);
334 			ptr = voice->ptr_instrument->name;
335 			draw_text_len( ptr, 25, n, pos, 6, 0);
336 		} else {
337 			continue;
338 		}
339 
340 		/* last box: panning. this one's much easier than the
341 		 * other two, thankfully :) */
342 		if (song_is_stereo()) {
343 			if (!voice->ptr_sample) {
344 				/* nothing... */
345 			} else if (voice->flags & CHN_SURROUND) {
346 				draw_text("Surround", 64, pos, 2, 0);
347 			} else if (voice->final_panning >> 2 == 0) {
348 				draw_text("Left", 64, pos, 2, 0);
349 			} else if ((voice->final_panning + 3) >> 2 == 64) {
350 				draw_text("Right", 68, pos, 2, 0);
351 			} else {
352 				draw_thumb_bar(64, pos, 9, 0, 256, voice->final_panning, 0);
353 			}
354 		}
355 	}
356 }
357 
_draw_fill_notes(int col,int first_row,int height,int num_channels,int channel_width,int separator,draw_note_func draw_note,int bg)358 static void _draw_fill_notes(int col, int first_row, int height, int num_channels,
359 			     int channel_width, int separator, draw_note_func draw_note, int bg)
360 {
361 	int row_pos, chan_pos;
362 
363 	for (row_pos = first_row; row_pos < first_row + height; row_pos++) {
364 		for (chan_pos = 0; chan_pos < num_channels - 1; chan_pos++) {
365 			draw_note(col + channel_width * chan_pos, row_pos, blank_note, -1, 6, bg);
366 			if (separator)
367 				draw_char(168, (col - 1 + channel_width * (chan_pos + 1)), row_pos, 2, bg);
368 		}
369 		draw_note(col + channel_width * chan_pos, row_pos, blank_note, -1, 6, bg);
370 	}
371 }
372 
_draw_track_view(int base,int height,int first_channel,int num_channels,int channel_width,int separator,draw_note_func draw_note)373 static void _draw_track_view(int base, int height, int first_channel, int num_channels,
374 			     int channel_width, int separator, draw_note_func draw_note)
375 {
376 	/* way too many variables */
377 	int current_row = song_get_current_row();
378 	int current_order = song_get_current_order();
379 	const song_note_t *note;
380 	// These can't be const because of song_get_pattern, but song_get_pattern is stupid and smells funny.
381 	song_note_t *cur_pattern, *prev_pattern, *next_pattern;
382 	const song_note_t *pattern; /* points to either {cur,prev,next}_pattern */
383 	int cur_pattern_rows = 0, prev_pattern_rows = 0, next_pattern_rows = 0;
384 	int total_rows; /* same as {cur,prev_next}_pattern_rows */
385 	int chan_pos, row, row_pos, rows_before;
386 	char buf[4];
387 
388 	if (separator)
389 		channel_width++;
390 
391 #if 0
392 	/* can't do this here -- each view does channel numbers differently, don't draw on top of them */
393 	draw_box(4, base, 5 + num_channels * channel_width - !!separator, base + height - 1,
394 		 BOX_THICK | BOX_INNER | BOX_INSET);
395 #endif
396 
397 	switch (song_get_mode()) {
398 	case MODE_PATTERN_LOOP:
399 		prev_pattern_rows = next_pattern_rows = cur_pattern_rows
400 			= song_get_pattern(song_get_playing_pattern(), &cur_pattern);
401 		prev_pattern = next_pattern = cur_pattern;
402 		break;
403 	case MODE_PLAYING:
404 		if (current_song->orderlist[current_order] >= 200) {
405 			/* this does, in fact, happen. just pretend that
406 			 * it's stopped :P */
407 	default:
408 			/* stopped */
409 			draw_fill_chars(5, base + 1, 4 + num_channels * channel_width - !!separator,
410 					base + height - 2, 0);
411 			return;
412 		}
413 		cur_pattern_rows = song_get_pattern(current_song->orderlist[current_order], &cur_pattern);
414 		if (current_order > 0 && current_song->orderlist[current_order - 1] < 200)
415 			prev_pattern_rows = song_get_pattern(current_song->orderlist[current_order - 1],
416 								&prev_pattern);
417 		else
418 			prev_pattern = NULL;
419 		if (current_order < 255 && current_song->orderlist[current_order + 1] < 200)
420 			next_pattern_rows = song_get_pattern(current_song->orderlist[current_order + 1],
421 								&next_pattern);
422 		else
423 			next_pattern = NULL;
424 		break;
425 	}
426 
427 	/* -2 for the top and bottom border, -1 because if there are an even number
428 	 * of rows visible, the current row is drawn above center. */
429 	rows_before = (height - 3) / 2;
430 
431 	/* "fake" channels (hack for 64-channel view) */
432 	if (num_channels > 64) {
433 		_draw_fill_notes(5 + 64, base + 1, height - 2,
434 				 num_channels - 64, channel_width, separator, draw_note, 0);
435 		_draw_fill_notes(5 + 64, base + 1 + rows_before, 1,
436 				 num_channels - 64, channel_width, separator, draw_note, 14);
437 		num_channels = 64;
438 	}
439 
440 	/* draw the area above the current row */
441 	pattern = cur_pattern;
442 	total_rows = cur_pattern_rows;
443 	row = current_row - 1;
444 	row_pos = base + rows_before;
445 	while (row_pos > base) {
446 		if (row < 0) {
447 			if (prev_pattern == NULL) {
448 				_draw_fill_notes(5, base + 1, row_pos - base,
449 						 num_channels, channel_width, separator, draw_note, 0);
450 				break;
451 			}
452 			pattern = prev_pattern;
453 			total_rows = prev_pattern_rows;
454 			row = total_rows - 1;
455 		}
456 		draw_text(numtostr(3, row, buf), 1, row_pos, 0, 2);
457 		note = pattern + 64 * row + first_channel - 1;
458 		for (chan_pos = 0; chan_pos < num_channels - 1; chan_pos++) {
459 			draw_note(5 + channel_width * chan_pos, row_pos, note, -1, 6, 0);
460 			if (separator)
461 				draw_char(168, (4 + channel_width * (chan_pos + 1)), row_pos, 2, 0);
462 			note++;
463 		}
464 		draw_note(5 + channel_width * chan_pos, row_pos, note, -1, 6, 0);
465 		row--;
466 		row_pos--;
467 	}
468 
469 	/* draw the current row */
470 	pattern = cur_pattern;
471 	total_rows = cur_pattern_rows;
472 	row_pos = base + rows_before + 1;
473 	draw_text(numtostr(3, current_row, buf), 1, row_pos, 0, 2);
474 	note = pattern + 64 * current_row + first_channel - 1;
475 	for (chan_pos = 0; chan_pos < num_channels - 1; chan_pos++) {
476 		draw_note(5 + channel_width * chan_pos, row_pos, note, -1, 6, 14);
477 		if (separator)
478 			draw_char(168, (4 + channel_width * (chan_pos + 1)), row_pos, 2, 14);
479 		note++;
480 	}
481 	draw_note(5 + channel_width * chan_pos, row_pos, note, -1, 6, 14);
482 
483 	/* draw the area under the current row */
484 	row = current_row + 1;
485 	row_pos++;
486 	while (row_pos < base + height - 1) {
487 		if (row >= total_rows) {
488 			if (next_pattern == NULL) {
489 				_draw_fill_notes(5, row_pos, base + height - row_pos - 1,
490 						 num_channels, channel_width, separator, draw_note, 0);
491 				break;
492 			}
493 			pattern = next_pattern;
494 			total_rows = next_pattern_rows;
495 			row = 0;
496 		}
497 		draw_text(numtostr(3, row, buf), 1, row_pos, 0, 2);
498 		note = pattern + 64 * row + first_channel - 1;
499 		for (chan_pos = 0; chan_pos < num_channels - 1; chan_pos++) {
500 			draw_note(5 + channel_width * chan_pos, row_pos, note, -1, 6, 0);
501 			if (separator)
502 				draw_char(168, (4 + channel_width * (chan_pos + 1)), row_pos, 2, 0);
503 			note++;
504 		}
505 		draw_note(5 + channel_width * chan_pos, row_pos, note, -1, 6, 0);
506 		row++;
507 		row_pos++;
508 	}
509 }
510 
info_draw_track_5(int base,int height,int active,int first_channel)511 static void info_draw_track_5(int base, int height, int active, int first_channel)
512 {
513 	int chan, chan_pos, fg;
514 
515 	draw_box(4, base, 74, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
516 	for (chan = first_channel, chan_pos = 0; chan_pos < 5; chan++, chan_pos++) {
517 		if (current_song->channels[chan - 1].flags & CHN_MUTE)
518 			fg = (chan == selected_channel ? 6 : 1);
519 		else
520 			fg = (chan == selected_channel ? 3 : (active ? 2 : 0));
521 		draw_channel_header_13(chan, 5 + 14 * chan_pos, base, fg);
522 	}
523 	_draw_track_view(base, height, first_channel, 5, 13, 1, draw_note_13);
524 }
525 
info_draw_track_8(int base,int height,int active,int first_channel)526 static void info_draw_track_8(int base, int height, int active, int first_channel)
527 {
528 	int chan, chan_pos, fg;
529 	char buf[4];
530 
531 	draw_box(4, base, 76, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
532 	for (chan = first_channel, chan_pos = 0; chan_pos < 8; chan++, chan_pos++) {
533 		if (current_song->channels[chan - 1].flags & CHN_MUTE)
534 			fg = (chan == selected_channel ? 6 : 1);
535 		else
536 			fg = (chan == selected_channel ? 3 : (active ? 2 : 0));
537 		draw_char(0, 6 + 9 * chan_pos, base, 1, 1);
538 		draw_char(0, 6 + 9 * chan_pos + 1, base, 1, 1);
539 		draw_text(numtostr(2, chan, buf), 6 + 9 * chan_pos + 2, base, fg, 1);
540 		draw_char(0, 6 + 9 * chan_pos + 4, base, 1, 1);
541 		draw_char(0, 6 + 9 * chan_pos + 5, base, 1, 1);
542 	}
543 	_draw_track_view(base, height, first_channel, 8, 8, 1, draw_note_8);
544 }
545 
info_draw_track_10(int base,int height,int active,int first_channel)546 static void info_draw_track_10(int base, int height, int active, int first_channel)
547 {
548 	int chan, chan_pos, fg;
549 	char buf[4];
550 
551 	draw_box(4, base, 75, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
552 	for (chan = first_channel, chan_pos = 0; chan_pos < 10; chan++, chan_pos++) {
553 		if (current_song->channels[chan - 1].flags & CHN_MUTE)
554 			fg = (chan == selected_channel ? 6 : 1);
555 		else
556 			fg = (chan == selected_channel ? 3 : (active ? 2 : 0));
557 		draw_char(0, 5 + 7 * chan_pos, base, 1, 1);
558 		draw_char(0, 5 + 7 * chan_pos + 1, base, 1, 1);
559 		draw_text(numtostr(2, chan, buf), 5 + 7 * chan_pos + 2, base, fg, 1);
560 		draw_char(0, 5 + 7 * chan_pos + 4, base, 1, 1);
561 		draw_char(0, 5 + 7 * chan_pos + 5, base, 1, 1);
562 	}
563 	_draw_track_view(base, height, first_channel, 10, 7, 0, draw_note_7);
564 }
565 
info_draw_track_12(int base,int height,int active,int first_channel)566 static void info_draw_track_12(int base, int height, int active, int first_channel)
567 {
568 	int chan, chan_pos, fg;
569 	char buf[4];
570 
571 	draw_box(4, base, 77, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
572 	for (chan = first_channel, chan_pos = 0; chan_pos < 12; chan++, chan_pos++) {
573 		if (current_song->channels[chan - 1].flags & CHN_MUTE)
574 			fg = (chan == selected_channel ? 6 : 1);
575 		else
576 			fg = (chan == selected_channel ? 3 : (active ? 2 : 0));
577 		/* draw_char(0, 5 + 6 * chan_pos, base, 1, 1); */
578 		draw_char(0, 5 + 6 * chan_pos + 1, base, 1, 1);
579 		draw_text(numtostr(2, chan, buf), 5 + 6 * chan_pos + 2, base, fg, 1);
580 		draw_char(0, 5 + 6 * chan_pos + 4, base, 1, 1);
581 		/* draw_char(0, 5 + 6 * chan_pos + 5, base, 1, 1); */
582 	}
583 	_draw_track_view(base, height, first_channel, 12, 6, 0, draw_note_6);
584 }
585 
info_draw_track_18(int base,int height,int active,int first_channel)586 static void info_draw_track_18(int base, int height, int active, int first_channel)
587 {
588 	int chan, chan_pos, fg;
589 	char buf[4];
590 
591 	draw_box(4, base, 76, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
592 	for (chan = first_channel, chan_pos = 0; chan_pos < 18; chan++, chan_pos++) {
593 		if (current_song->channels[chan - 1].flags & CHN_MUTE)
594 			fg = (chan == selected_channel ? 6 : 1);
595 		else
596 			fg = (chan == selected_channel ? 3 : (active ? 2 : 0));
597 		draw_text(numtostr(2, chan, buf), 5 + 4 * chan_pos + 1, base, fg, 1);
598 	}
599 	_draw_track_view(base, height, first_channel, 18, 3, 1, draw_note_3);
600 }
601 
info_draw_track_24(int base,int height,int active,int first_channel)602 static void info_draw_track_24(int base, int height, int active, int first_channel)
603 {
604 	int chan, chan_pos, fg;
605 	char buf[4];
606 
607 	draw_box(4, base, 77, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
608 	for (chan = first_channel, chan_pos = 0; chan_pos < 24; chan++, chan_pos++) {
609 		if (current_song->channels[chan - 1].flags & CHN_MUTE)
610 			fg = (chan == selected_channel ? 6 : 1);
611 		else
612 			fg = (chan == selected_channel ? 3 : (active ? 2 : 0));
613 		draw_text(numtostr(2, chan, buf), 5 + 3 * chan_pos + 1, base, fg, 1);
614 	}
615 	_draw_track_view(base, height, first_channel, 24, 3, 0, draw_note_3);
616 }
617 
info_draw_track_36(int base,int height,int active,int first_channel)618 static void info_draw_track_36(int base, int height, int active, int first_channel)
619 {
620 	int chan, chan_pos, fg;
621 	char buf[4];
622 
623 	draw_box(4, base, 77, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
624 	for (chan = first_channel, chan_pos = 0; chan_pos < 36; chan++, chan_pos++) {
625 		if (current_song->channels[chan - 1].flags & CHN_MUTE)
626 			fg = (chan == selected_channel ? 6 : 1);
627 		else
628 			fg = (chan == selected_channel ? 3 : (active ? 2 : 0));
629 		draw_text(numtostr(2, chan, buf), 5 + 2 * chan_pos, base, fg, 1);
630 	}
631 	_draw_track_view(base, height, first_channel, 36, 2, 0, draw_note_2);
632 }
633 
info_draw_track_64(int base,int height,int active,int first_channel)634 static void info_draw_track_64(int base, int height, int active, int first_channel)
635 {
636 	int chan, chan_pos, fg;
637 	/* IT draws nine more blank "channels" on the right */
638 	int nchan = (status.flags & CLASSIC_MODE) ? 73 : 64;
639 
640 	assert(first_channel == 1);
641 
642 	draw_box(4, base, nchan + 5, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
643 	for (chan = first_channel, chan_pos = 0; chan_pos < 64; chan++, chan_pos++) {
644 		if (current_song->channels[chan - 1].flags & CHN_MUTE)
645 			fg = (chan == selected_channel ? 14 : 9);
646 		else
647 			fg = (chan == selected_channel ? 3 : (active ? 10 : 8));
648 		draw_half_width_chars(chan / 10 + '0', chan % 10 + '0', 5 + chan_pos, base, fg, 1, fg, 1);
649 	}
650 	for (; chan_pos < nchan; chan_pos++)
651 		draw_char(0, 5 + chan_pos, base, 1, 1);
652 
653 	_draw_track_view(base, height, first_channel, nchan, 1, 0, draw_note_1);
654 }
655 
info_draw_channels(int base,UNUSED int height,int active,UNUSED int first_channel)656 static void info_draw_channels(int base, UNUSED int height, int active, UNUSED int first_channel)
657 {
658 	char buf[32];
659 	int fg = (active ? 3 : 0);
660 
661 	snprintf(buf, 32, "Active Channels: %d (%d)", song_get_playing_channels(), song_get_max_channels());
662 	draw_text(buf, 2, base, fg, 2);
663 
664 	snprintf(buf, 32, "Global Volume: %d", song_get_current_global_volume());
665 	draw_text(buf, 4, base + 1, fg, 2);
666 }
667 
668 
669 /* Yay it works, only took me forever and a day to get it right. */
info_draw_note_dots(int base,int height,int active,int first_channel)670 static void info_draw_note_dots(int base, int height, int active, int first_channel)
671 {
672 	int fg, v;
673 	int c, pos;
674 	int n;
675 	song_voice_t *voice;
676 	char buf[4];
677 	uint8_t d, dn;
678 	uint8_t dot_field[73][36] = { {0} }; // f#2 -> f#8 = 73 columns
679 
680 	draw_fill_chars(5, base + 1, 77, base + height - 2, 0);
681 	draw_box(4, base, 78, base + height - 1, BOX_THICK | BOX_INNER | BOX_INSET);
682 
683 	n = current_song->num_voices;
684 	while (n--) {
685 		voice = current_song->voices + current_song->voice_mix[n];
686 
687 		/* 31 = f#2, 103 = f#8. (i hope ;) */
688 		if (!(voice->ptr_sample && voice->note >= 31 && voice->note <= 103))
689 			continue;
690 		pos = voice->master_channel ?: (1 + current_song->voice_mix[n]);
691 		if (pos < first_channel)
692 			continue;
693 		pos -= first_channel;
694 		if (pos > height - 1)
695 			continue;
696 
697 		fg = (voice->flags & CHN_MUTE) ? 1 : ((voice->ptr_sample - current_song->samples) % 4 + 2);
698 
699 		if (velocity_mode || (status.flags & CLASSIC_MODE))
700 			v = (voice->final_volume + 2047) >> 11;
701 		else
702 			v = (voice->vu_meter + 31) >> 5;
703 		d = dot_field[voice->note - 31][pos];
704 		dn = (v << 4) | fg;
705 		if (dn > d)
706 			dot_field[voice->note - 31][pos] = dn;
707 	}
708 
709 	for (c = first_channel, pos = 0; pos < height - 2; pos++, c++) {
710 		for (n = 0; n < 73; n++) {
711 			d = dot_field[n][pos] ?: 0x06;
712 
713 			fg = d & 0xf;
714 			v = d >> 4;
715 			draw_char(v + 193, n + 5, pos + base + 1, fg, 0);
716 		}
717 
718 		if (c == selected_channel) {
719 			fg = (current_song->channels[c - 1].flags & CHN_MUTE) ? 6 : 3;
720 		} else {
721 			if (current_song->channels[c - 1].flags & CHN_MUTE)
722 				continue;
723 			fg = active ? 1 : 0;
724 		}
725 		draw_text(numtostr(2, c, buf), 2, pos + base + 1, fg, 2);
726 	}
727 }
728 
729 /* --------------------------------------------------------------------- */
730 /* click receivers */
731 
click_chn_x(int x,int w,int skip,int fc)732 static void click_chn_x(int x, int w, int skip, int fc)
733 {
734 	while (x > 0 && fc <= 64) {
735 		if (x < w) {
736 			selected_channel = CLAMP(fc, 1, 64);
737 			return;
738 		}
739 		fc++;
740 		x -= w;
741 		x -= skip;
742 	}
743 }
744 
click_chn_is_x(int x,UNUSED int y,int nc,int fc)745 static void click_chn_is_x(int x, UNUSED int y, int nc, int fc)
746 {
747 	if (x < 5) return;
748 	x -= 4;
749 	switch (nc) {
750 	case 5:
751 		click_chn_x(x, 13, 1, fc);
752 		break;
753 	case 10:
754 		click_chn_x(x, 7, 0, fc);
755 		break;
756 	case 12:
757 		click_chn_x(x, 6, 0, fc);
758 		break;
759 	case 18:
760 		click_chn_x(x, 3, 1, fc);
761 		break;
762 	case 24:
763 		click_chn_x(x, 3, 0, fc);
764 		break;
765 	case 36:
766 		click_chn_x(x, 2, 0, fc);
767 		break;
768 	case 64:
769 		click_chn_x(x, 1, 0, fc);
770 		break;
771 	};
772 }
773 
click_chn_is_y_nohead(UNUSED int x,int y,UNUSED int nc,int fc)774 static void click_chn_is_y_nohead(UNUSED int x, int y, UNUSED int nc, int fc)
775 {
776 	selected_channel = CLAMP(y+fc, 1, 64);
777 }
778 
click_chn_is_y(UNUSED int x,int y,UNUSED int nc,int fc)779 static void click_chn_is_y(UNUSED int x, int y, UNUSED int nc, int fc)
780 {
781 	if (!y) return;
782 	selected_channel = CLAMP((y+fc)-1, 1, 64);
783 }
784 
click_chn_nil(UNUSED int x,UNUSED int y,UNUSED int nc,UNUSED int fc)785 static void click_chn_nil(UNUSED int x, UNUSED int y, UNUSED int nc, UNUSED int fc)
786 {
787 	/* do nothing */
788 }
789 
790 /* --------------------------------------------------------------------- */
791 /* declarations of the window types */
792 
793 #define TRACK_VIEW(n) {"track" # n, info_draw_track_##n, click_chn_is_x, 1, n}
794 static const struct info_window_type window_types[] = {
795 	{"samples", info_draw_samples, click_chn_is_y_nohead, 0, -2},
796 	TRACK_VIEW(5),
797 	TRACK_VIEW(8),
798 	TRACK_VIEW(10),
799 	TRACK_VIEW(12),
800 	TRACK_VIEW(18),
801 	TRACK_VIEW(24),
802 	TRACK_VIEW(36),
803 	TRACK_VIEW(64),
804 	{"global", info_draw_channels, click_chn_nil, 1, 0},
805 	{"dots", info_draw_note_dots, click_chn_is_y_nohead, 0, -2},
806 	{"tech", info_draw_technical, click_chn_is_y, 1, -2},
807 };
808 #undef TRACK_VIEW
809 
810 #define NUM_WINDOW_TYPES ARRAY_SIZE(window_types)
811 
812 /* --------------------------------------------------------------------- */
813 
_fix_channels(int n)814 static void _fix_channels(int n)
815 {
816 	struct info_window *w = windows + n;
817 	int channels = window_types[w->type].channels;
818 
819 	if (channels == 0)
820 		return;
821 
822 	if (channels < 0) {
823 		channels += w->height;
824 		if (n == 0 && !(window_types[w->type].first_row)) {
825 			/* crappy hack (to squeeze in an extra row on the top window) */
826 			channels++;
827 		}
828 	}
829 	if (selected_channel < w->first_channel)
830 		w->first_channel = selected_channel;
831 	else if (selected_channel >= (w->first_channel + channels))
832 		w->first_channel = selected_channel - channels + 1;
833 	w->first_channel = CLAMP(w->first_channel, 1, 65 - channels);
834 }
835 
info_handle_click(int x,int y)836 static int info_handle_click(int x, int y)
837 {
838 	int n;
839 	if (y < 13) return 0; /* NA */
840 	y -= 13;
841 	for (n = 0; n < num_windows; n++) {
842 		if (y < windows[n].height) {
843 			window_types[windows[n].type].click(
844 				x, y,
845 
846 				window_types[windows[n].type].channels,
847 				windows[n].first_channel);
848 			return 1;
849 		}
850 		y -= windows[n].height;
851 	}
852 	return 0;
853 }
854 
recalculate_windows(void)855 static void recalculate_windows(void)
856 {
857 	int n, pos;
858 
859 	pos = 13;
860 	for (n = 0; n < num_windows - 1; n++) {
861 		_fix_channels(n);
862 		pos += windows[n].height;
863 		if (pos > 50) {
864 			/* Too big? Throw out the rest of the windows. */
865 			num_windows = n;
866 		}
867 	}
868 	assert(num_windows > 0);
869 	windows[n].height = 50 - pos;
870 	_fix_channels(n);
871 }
872 
873 /* --------------------------------------------------------------------------------------------------------- */
874 /* settings */
875 
cfg_save_info(cfg_file_t * cfg)876 void cfg_save_info(cfg_file_t *cfg)
877 {
878 	// for 5 windows, roughly 12 chars per window, this is way more than enough
879 	char buf[256] = "";
880 	char *s = buf;
881 	int rem = sizeof(buf) - 1;
882 	int len;
883 	int i;
884 
885 	for (i = 0; i < num_windows; i++) {
886 		len = snprintf(s, rem, " %s %d", window_types[windows[i].type].id, windows[i].height);
887 		if (!len) {
888 			// this should not ever happen
889 			break;
890 		}
891 		rem -= len;
892 		s += len;
893 	}
894 	buf[255] = '\0';
895 
896 	// (don't write the first space to the config)
897 	cfg_set_string(cfg, "Info Page", "layout", buf + 1);
898 }
899 
cfg_load_info_old(cfg_file_t * cfg)900 static void cfg_load_info_old(cfg_file_t *cfg)
901 {
902 	char key[] = "windowX";
903 	int i;
904 
905 	num_windows = cfg_get_number(cfg, "Info Page", "num_windows", -1);
906 	if (num_windows <= 0 || num_windows > MAX_WINDOWS)
907 		num_windows = -1;
908 
909 	for (i = 0; i < num_windows; i++) {
910 		int tmp;
911 
912 		key[6] = i + '0';
913 		tmp = cfg_get_number(cfg, "Info Page", key, -1);
914 		if (tmp == -1) {
915 			num_windows = -1;
916 			break;
917 		}
918 		windows[i].type = tmp >> 8;
919 		if (windows[i].type >= 2) {
920 			// compensate for added 8-channel view
921 			windows[i].type++;
922 		}
923 		windows[i].height = tmp & 0xff;
924 		if (windows[i].type < 0 || windows[i].type >= NUM_WINDOW_TYPES || windows[i].height < 3) {
925 			/* Broken window? */
926 			num_windows = -1;
927 			break;
928 		}
929 	}
930 	/* last window's size < 3 lines? */
931 
932 	if (num_windows == -1) {
933 		/* Fall back to defaults */
934 		num_windows = 3;
935 		windows[0].type = 0;    /* samples */
936 		windows[0].height = 19;
937 		windows[1].type = 9;    /* active channels */
938 		windows[1].height = 3;
939 		windows[2].type = 6;    /* 24chn track view */
940 		windows[2].height = 15;
941 	}
942 
943 	for (i = 0; i < num_windows; i++) {
944 		windows[i].first_channel = 1;
945 	}
946 
947 	recalculate_windows();
948 	if (status.current_page == PAGE_INFO)
949 		status.flags |= NEED_UPDATE;
950 }
951 
cfg_load_info(cfg_file_t * cfg)952 void cfg_load_info(cfg_file_t *cfg)
953 {
954 	int n;
955 	char buf[256];
956 	char *left, *right;
957 	size_t len;
958 
959 	if (!cfg_get_string(cfg, "Info Page", "layout", buf, 255, NULL)) {
960 		cfg_load_info_old(cfg);
961 		return;
962 	}
963 
964 	left = buf;
965 	num_windows = 0;
966 	do {
967 		left += strspn(left, " \t");
968 		len = strcspn(left, " \t");
969 		if (!len) {
970 			break;
971 		}
972 		left[len] = '\0'; // chop it into pieces
973 		windows[num_windows].first_channel = 1;
974 		windows[num_windows].type = -1;
975 		for (n = 0; n < NUM_WINDOW_TYPES; n++) {
976 			if (strcasecmp(window_types[n].id, left) == 0) {
977 				windows[num_windows].type = n;
978 				break;
979 			}
980 		}
981 		// (a pythonic for...else would be lovely right about here)
982 		if (windows[num_windows].type == -1) {
983 			break;
984 		}
985 		right = left + len + 1;
986 		left[len] = '\0';
987 		n = strtol(right, &left, 10);
988 		if (!left || left == right || n < 3) {
989 			// failed to parse any digits, or number is too small
990 			break;
991 		}
992 		windows[num_windows++].height = n;
993 	} while (num_windows < MAX_WINDOWS - 1);
994 
995 	recalculate_windows();
996 	if (status.current_page == PAGE_INFO)
997 		status.flags |= NEED_UPDATE;
998 }
999 
1000 /* --------------------------------------------------------------------- */
1001 
info_page_redraw(void)1002 static void info_page_redraw(void)
1003 {
1004 	int n, height, pos = (window_types[windows[0].type].first_row ? 13 : 12);
1005 
1006 	for (n = 0; n < num_windows - 1; n++) {
1007 		height = windows[n].height;
1008 		if (pos == 12)
1009 			height++;
1010 		window_types[windows[n].type].draw(pos, height, (n == selected_window),
1011 						   windows[n].first_channel);
1012 		pos += height;
1013 	}
1014 	/* the last window takes up all the rest of the screen */
1015 	window_types[windows[n].type].draw(pos, 50 - pos, (n == selected_window), windows[n].first_channel);
1016 }
1017 
1018 /* --------------------------------------------------------------------- */
1019 
info_page_handle_key(struct key_event * k)1020 static int info_page_handle_key(struct key_event * k)
1021 {
1022 	int n, p, order;
1023 
1024 	if (k->mouse == MOUSE_CLICK || k->mouse == MOUSE_DBLCLICK) {
1025 		p = selected_channel;
1026 		n = info_handle_click(k->x, k->y);
1027 		if (k->mouse == MOUSE_DBLCLICK) {
1028 			if (p == selected_channel) {
1029 				set_current_channel(selected_channel);
1030 				order = song_get_current_order();
1031 
1032 				if (song_get_mode() == MODE_PLAYING) {
1033 					n = current_song->orderlist[order];
1034 				} else {
1035 					n = song_get_playing_pattern();
1036 				}
1037 				if (n < 200) {
1038 					set_current_order(order);
1039 					set_current_pattern(n);
1040 					set_current_row(song_get_current_row());
1041 					set_page(PAGE_PATTERN_EDITOR);
1042 				}
1043 			}
1044 		}
1045 		return n;
1046 	}
1047 
1048 	/* hack to render this useful :) */
1049 	if (k->orig_sym == SDLK_KP9) {
1050 		k->sym = SDLK_F9;
1051 	} else if (k->orig_sym == SDLK_KP0) {
1052 		k->sym = SDLK_F10;
1053 	}
1054 
1055 	switch (k->sym) {
1056 	case SDLK_g:
1057 		if (k->state == KEY_PRESS)
1058 			return 1;
1059 
1060 		set_current_channel(selected_channel);
1061 		order = song_get_current_order();
1062 
1063 		if (song_get_mode() == MODE_PLAYING) {
1064 			n = current_song->orderlist[order];
1065 		} else {
1066 			n = song_get_playing_pattern();
1067 		}
1068 		if (n < 200) {
1069 			set_current_order(order);
1070 			set_current_pattern(n);
1071 			set_current_row(song_get_current_row());
1072 			set_page(PAGE_PATTERN_EDITOR);
1073 		}
1074 	       return 1;
1075 	case SDLK_v:
1076 		if (k->state == KEY_RELEASE)
1077 			return 1;
1078 
1079 		velocity_mode = !velocity_mode;
1080 		status_text_flash("Using %s bars", (velocity_mode ? "velocity" : "volume"));
1081 		status.flags |= NEED_UPDATE;
1082 		return 1;
1083 	case SDLK_i:
1084 		if (k->state == KEY_RELEASE)
1085 			return 1;
1086 
1087 		instrument_names = !instrument_names;
1088 		status_text_flash("Using %s names", (instrument_names ? "instrument" : "sample"));
1089 		status.flags |= NEED_UPDATE;
1090 		return 1;
1091 	case SDLK_r:
1092 		if (k->mod & KMOD_ALT) {
1093 			if (k->state == KEY_RELEASE)
1094 				return 1;
1095 
1096 			song_flip_stereo();
1097 			status_text_flash("Left/right outputs reversed");
1098 			return 1;
1099 		}
1100 		return 0;
1101 	case SDLK_PLUS:
1102 		if (k->state == KEY_RELEASE)
1103 			return 1;
1104 		if (song_get_mode() == MODE_PLAYING) {
1105 			song_set_current_order(song_get_current_order() + 1);
1106 		}
1107 		return 1;
1108 	case SDLK_MINUS:
1109 		if (k->state == KEY_RELEASE)
1110 			return 1;
1111 		if (song_get_mode() == MODE_PLAYING) {
1112 			song_set_current_order(song_get_current_order() - 1);
1113 		}
1114 		return 1;
1115 	case SDLK_q:
1116 		if (k->state == KEY_RELEASE)
1117 			return 1;
1118 		song_toggle_channel_mute(selected_channel - 1);
1119 		orderpan_recheck_muted_channels();
1120 		status.flags |= NEED_UPDATE;
1121 		return 1;
1122 	case SDLK_s:
1123 		if (k->state == KEY_RELEASE)
1124 			return 1;
1125 
1126 		if (k->mod & KMOD_ALT) {
1127 			song_toggle_stereo();
1128 			status_text_flash("Stereo %s", song_is_stereo()
1129 					  ? "Enabled" : "Disabled");
1130 		} else {
1131 			song_handle_channel_solo(selected_channel - 1);
1132 			orderpan_recheck_muted_channels();
1133 		}
1134 		status.flags |= NEED_UPDATE;
1135 		return 1;
1136 	case SDLK_SPACE:
1137 		if (!NO_MODIFIER(k->mod))
1138 			return 0;
1139 
1140 		if (k->state == KEY_RELEASE)
1141 			return 1;
1142 		song_toggle_channel_mute(selected_channel - 1);
1143 		if (selected_channel < 64)
1144 			selected_channel++;
1145 		orderpan_recheck_muted_channels();
1146 		break;
1147 	case SDLK_UP:
1148 		if (k->state == KEY_RELEASE)
1149 			return 1;
1150 		if (k->mod & KMOD_ALT) {
1151 			/* make the current window one line shorter, and give the line to the next window
1152 			below it. if the window is already as small as it can get (3 lines) or if it's
1153 			the last window, don't do anything. */
1154 			if (selected_window == num_windows - 1 || windows[selected_window].height == 3) {
1155 				return 1;
1156 			}
1157 			windows[selected_window].height--;
1158 			windows[selected_window + 1].height++;
1159 			break;
1160 		}
1161 		if (selected_channel > 1)
1162 			selected_channel--;
1163 		break;
1164 	case SDLK_LEFT:
1165 		if (!NO_MODIFIER(k->mod) && !(k->mod & KMOD_ALT))
1166 			return 0;
1167 		if (k->state == KEY_RELEASE)
1168 			return 1;
1169 		if (selected_channel > 1)
1170 			selected_channel--;
1171 		break;
1172 	case SDLK_DOWN:
1173 		if (k->state == KEY_RELEASE)
1174 			return 1;
1175 		if (k->mod & KMOD_ALT) {
1176 			/* expand the current window, taking a line from
1177 			 * the next window down. BUT: don't do anything if
1178 			 * (a) this is the last window, or (b) the next
1179 			 * window is already as small as it can be (three
1180 			 * lines). */
1181 			if (selected_window == num_windows - 1
1182 			    || windows[selected_window + 1].height == 3) {
1183 				return 1;
1184 			}
1185 			windows[selected_window].height++;
1186 			windows[selected_window + 1].height--;
1187 			break;
1188 		}
1189 		if (selected_channel < 64)
1190 			selected_channel++;
1191 		break;
1192 	case SDLK_RIGHT:
1193 		if (!NO_MODIFIER(k->mod) && !(k->mod & KMOD_ALT))
1194 			return 0;
1195 		if (k->state == KEY_RELEASE)
1196 			return 1;
1197 		if (selected_channel < 64)
1198 			selected_channel++;
1199 		break;
1200 	case SDLK_HOME:
1201 		if (!NO_MODIFIER(k->mod))
1202 			return 0;
1203 		if (k->state == KEY_RELEASE)
1204 			return 1;
1205 		selected_channel = 1;
1206 		break;
1207 	case SDLK_END:
1208 		if (!NO_MODIFIER(k->mod))
1209 			return 0;
1210 		if (k->state == KEY_RELEASE)
1211 			return 1;
1212 		selected_channel = song_find_last_channel();
1213 		break;
1214 	case SDLK_INSERT:
1215 		if (!NO_MODIFIER(k->mod))
1216 			return 0;
1217 		if (k->state == KEY_RELEASE)
1218 			return 1;
1219 		/* add a new window, unless there's already five (the maximum)
1220 		or if the current window isn't big enough to split in half. */
1221 		if (num_windows == MAX_WINDOWS || (windows[selected_window].height < 6)) {
1222 			return 1;
1223 		}
1224 
1225 		num_windows++;
1226 
1227 		/* shift the windows under the current one down */
1228 		memmove(windows + selected_window + 1, windows + selected_window,
1229 			((num_windows - selected_window - 1) * sizeof(*windows)));
1230 
1231 		/* split the height between the two windows */
1232 		n = windows[selected_window].height;
1233 		windows[selected_window].height = n / 2;
1234 		windows[selected_window + 1].height = n / 2;
1235 		if ((n & 1) && num_windows != 2) {
1236 			/* odd number? compensate. (the selected window gets the extra line) */
1237 			windows[selected_window + 1].height++;
1238 		}
1239 		break;
1240 	case SDLK_DELETE:
1241 		if (!NO_MODIFIER(k->mod))
1242 			return 0;
1243 		if (k->state == KEY_RELEASE)
1244 			return 1;
1245 		/* delete the current window and give the extra space to the next window down.
1246 		if this is the only window, well then don't delete it ;) */
1247 		if (num_windows == 1)
1248 			return 1;
1249 
1250 		n = windows[selected_window].height + windows[selected_window + 1].height;
1251 
1252 		/* shift the windows under the current one up */
1253 		memmove(windows + selected_window, windows + selected_window + 1,
1254 			((num_windows - selected_window - 1) * sizeof(*windows)));
1255 
1256 		/* fix the current window's height */
1257 		windows[selected_window].height = n;
1258 
1259 		num_windows--;
1260 		if (selected_window == num_windows)
1261 			selected_window--;
1262 		break;
1263 	case SDLK_PAGEUP:
1264 		if (!NO_MODIFIER(k->mod))
1265 			return 0;
1266 		if (k->state == KEY_RELEASE)
1267 			return 1;
1268 		n = windows[selected_window].type;
1269 		if (n == 0)
1270 			n = NUM_WINDOW_TYPES;
1271 		n--;
1272 		windows[selected_window].type = n;
1273 		break;
1274 	case SDLK_PAGEDOWN:
1275 		if (!NO_MODIFIER(k->mod))
1276 			return 0;
1277 		if (k->state == KEY_RELEASE)
1278 			return 1;
1279 		windows[selected_window].type = (windows[selected_window].type + 1) % NUM_WINDOW_TYPES;
1280 		break;
1281 	case SDLK_TAB:
1282 		if (k->state == KEY_RELEASE)
1283 			return 1;
1284 		if (k->mod & KMOD_SHIFT) {
1285 			if (selected_window == 0)
1286 				selected_window = num_windows;
1287 			selected_window--;
1288 		} else {
1289 			selected_window = (selected_window + 1) % num_windows;
1290 		}
1291 		status.flags |= NEED_UPDATE;
1292 		return 1;
1293 	case SDLK_F9:
1294 		if (k->state == KEY_RELEASE)
1295 			return 1;
1296 		if (k->mod & KMOD_ALT) {
1297 			song_toggle_channel_mute(selected_channel - 1);
1298 			orderpan_recheck_muted_channels();
1299 			return 1;
1300 		}
1301 		return 0;
1302 	case SDLK_F10:
1303 		if (k->mod & KMOD_ALT) {
1304 			if (k->state == KEY_RELEASE)
1305 				return 1;
1306 			song_handle_channel_solo(selected_channel - 1);
1307 			orderpan_recheck_muted_channels();
1308 			return 1;
1309 		}
1310 		return 0;
1311 	default:
1312 		return 0;
1313 	}
1314 
1315 	recalculate_windows();
1316 	status.flags |= NEED_UPDATE;
1317 	return 1;
1318 }
1319 
1320 /* --------------------------------------------------------------------- */
1321 
info_page_playback_update(void)1322 static void info_page_playback_update(void)
1323 {
1324 	if (song_get_mode() != MODE_STOPPED)
1325 		status.flags |= NEED_UPDATE;
1326 }
1327 
info_load_page(struct page * page)1328 void info_load_page(struct page *page)
1329 {
1330 	page->title = "Info Page (F5)";
1331 	page->playback_update = info_page_playback_update;
1332 	page->total_widgets = 1;
1333 	page->widgets = widgets_info;
1334 	page->help_index = HELP_INFO_PAGE;
1335 
1336 	create_other(widgets_info + 0, 0, info_page_handle_key, info_page_redraw);
1337 }
1338