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