1
2 /*
3 * The Real SoundTracker - track editor
4 *
5 * Copyright (C) 1998-2019 Michael Krause
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <math.h>
26
27 #include <gdk/gdkkeysyms.h>
28 #include <glib.h>
29 #include <glib/gprintf.h>
30 #include <glib/gi18n.h>
31
32 #include "audio.h"
33 #include "gui-settings.h"
34 #include "gui-subs.h"
35 #include "gui.h"
36 #include "history.h"
37 #include "keys.h"
38 #include "main.h"
39 #include "menubar.h"
40 #include "preferences.h"
41 #include "st-subs.h"
42 #include "track-editor.h"
43 #include "tracker-settings.h"
44 #include "xm-player.h"
45
46 #define NG_0x40 4
47
48 /* How many parameters each FX has. 0 means that there is no effect with this code,
49 3 -- that the effect has only one 4-bit parameter, NG_0x40 -- that the value
50 should not be greater than 0x40 */
51
52 static const guint8 n_params[] =
53 {2, 1, 1, 1, 2, 1, 1, 2, 1, 1, /* 0 - 9 */
54 2, 1, 1 | NG_0x40, 1, 3, 1, 1 | NG_0x40, 2, 0, 0, /* a - j */
55 1, 1, 0, 0, 0, 2, 1, 2, 0, 2, /* k - t */
56 0, 0, 0, 3, 0, 1}; /* u - z */
57
58 extern GtkBuilder *gui_builder;
59
60 Tracker* tracker;
61 GtkWidget* trackersettings;
62 GtkWidget* vscrollbar;
63
64 static GtkWidget* hscrollbar;
65 static GtkAdjustment* adj;
66
67 static XMPattern* pattern_buffer = NULL;
68 static XMNote* track_buffer = NULL;
69 static int track_buffer_length;
70 static guint hscroll_tag, vscroll_tag;
71
72 /* Block stuff */
73 static XMPattern block_buffer;
74
75 /* this array contains -1 if the note is not running on the specified channel
76 or the note number if it is being played. This is necessary to handle the
77 key on/off situation. */
78 static int note_running[32] = {-1};
79
80 static int update_freq = 30;
81 static int gtktimer = -1;
82
83 static guint track_editor_editmode_status_idle_handler = 0;
84 static gchar track_editor_editmode_status_ed_buf[512];
85 #define SEBUF_SIZE sizeof(track_editor_editmode_status_ed_buf)
86
87 /* jazz edit stuff */
88 static GtkWidget* jazzbox;
89 static GtkWidget* jazztable;
90 static GtkToggleButton* jazztoggles[32];
91 static int jazz_numshown = 0;
92 static gboolean jazz_enabled = FALSE;
93
94 /* Live recording stuff */
95 static gint8 cur_tick = 0;
96 static gdouble curtime = 0.0, prevtime = 0.0, nexttime = 0.0;
97 static gint cur_pos = 0, cur_pat = 0, cur_tempo = 0, prev_tempo = 0, patno = 0, bpm = 0;
98
99 static void vscrollbar_changed(GtkAdjustment* adj);
100 static void hscrollbar_changed(GtkAdjustment* adj);
101 static void update_mainmenu_blockmark(Tracker* t, int state);
102 static gboolean track_editor_handle_column_input(Tracker* t, int gdkkey);
103
104 /* Note recording stuff */
105 static struct {
106 guint32 key;
107 int chn;
108 gboolean act;
109 } reckey[32];
110
111 static gint
track_editor_editmode_status_idle_function(void)112 track_editor_editmode_status_idle_function(void)
113 {
114 gui_statusbar_update_message(track_editor_editmode_status_ed_buf, FALSE);
115
116 track_editor_editmode_status_idle_handler = 0;
117 return FALSE;
118 }
119
120 #define PRINT_STATUS(pos, ...) \
121 g_snprintf(&track_editor_editmode_status_ed_buf[pos], SEBUF_SIZE - pos, __VA_ARGS__)
122
show_editmode_status(void)123 void show_editmode_status(void)
124 {
125 Tracker* t = tracker;
126 XMNote* note = &t->curpattern->channels[t->cursor_ch][t->patpos];
127 const gchar *line = NULL;
128 gint cmd_p1, cmd_p2, pos;
129
130 static const gchar* fx_commands[] = {
131 N_("Arpeggio"), /* 0 */
132 N_("Porta up"), /* 1 */
133 N_("Porta down"), /* 2 */
134 N_("Tone porta"), /* 3 */
135 N_("Vibrato"), /* 4 */
136 N_("Tone porta + Volume slide"), /* 5 */
137 N_("Vibrato + Volume slide"), /* 6 */
138 N_("Tremolo"), /* 7 */
139 N_("Set panning"), /* 8 */
140 N_("Sample offset"), /* 9 */
141 N_("Set volume"), /* A */
142 N_("Position jump"), /* B */
143 N_("Set volume"), /* C */
144 N_("Pattern break"), /* D */
145 NULL, /* E */
146 N_("Set tempo/bpm"), /* F */
147 N_("Set global volume"), /* G */
148 N_("Global volume slide"), /* H */
149 NULL, /* I */
150 NULL, /* J */
151 N_("Key off"), /* K */
152 N_("Set envelop position"), /* L */
153 NULL, /* M */
154 NULL, /* N */
155 NULL, /* O */
156 N_("Panning slide"), /* P */
157 N_("LP filter resonance"), /* Q */
158 N_("Multi retrig note"), /* R */
159 NULL, /* S */
160 N_("Tremor"), /* T */
161 NULL, /* U */
162 NULL, /* V */
163 NULL, /* W */
164 NULL, /* X */
165 NULL, /* Y */
166 N_("LP filter cutoff"), /* Z */
167 };
168
169 static const gchar* e_fx_commands[] = {
170 NULL, /* 0 */
171 N_("Fine porta up"), /* 1 */
172 N_("Fine porta down"), /* 2 */
173 N_("Set gliss control"), /* 3 */
174 N_("Set vibrato control"), /* 4 */
175 N_("Set finetune"), /* 5 */
176 N_("Pattern loop"), /* 6 */
177 N_("Set tremolo control"), /* 7 */
178 NULL, /* 8 */
179 N_("Retrig note"), /* 9 */
180 N_("Fine volume slide up"), /* A */
181 N_("Fine volume slide down"), /* B */
182 N_("Note cut"), /* C */
183 N_("Note delay"), /* D */
184 N_("Pattern delay"), /* E */
185 NULL, /* F */
186 };
187
188 static const gchar* vol_fx_commands[] = {
189 N_("Volume slide down"),
190 N_("Volume slide up"),
191 N_("Fine volume slide down"),
192 N_("Fine volume slide up"),
193 N_("Set vibrato speed"),
194 N_("Vibrato"),
195 N_("Set panning"),
196 N_("Panning slide left"),
197 N_("Panning slide right"),
198 N_("Tone porta"),
199 };
200
201 static const gchar* e47_fx_forms[] = {
202 N_("sine"), /* 0 */
203 N_("ramp down"), /* 1 */
204 N_("square"), /* 2 */
205 };
206
207 pos = PRINT_STATUS(0, _("[Chnn: %02d] [Pos: %03d] [Instr: %03d] [Vol: "), t->cursor_ch + 1,
208 t->patpos,
209 note->instrument);
210
211 if (note->volume >= 0x10 && note->volume <= 0x50)
212 pos += PRINT_STATUS(pos, _("Set volume"));
213 else if (note->volume >= 0x60)
214 pos += PRINT_STATUS(pos, "%s", _(vol_fx_commands[((note->volume & 0xf0) - 0x60) >> 4]));
215 else
216 pos += PRINT_STATUS(pos, _("None"));
217
218 if (note->volume & 0xf0)
219 pos += PRINT_STATUS(pos, " => %02d ] ",
220 (note->volume >= 0x10 && note->volume <= 0x50) ? note->volume - 0x10 : note->volume & 0xf);
221 else
222 pos += PRINT_STATUS(pos, " ] ");
223
224 pos += PRINT_STATUS(pos, _("[Cmd: "));
225
226 switch (note->fxtype) {
227 case xmpCmdArpeggio:
228 if (note->fxparam)
229 line = fx_commands[note->fxtype];
230 break;
231 case xmpCmdExtended:
232 switch ((note->fxparam & 0xf0) >> 4) {
233 case 0:
234 case 8:
235 case 15:
236 break;
237 default:
238 line = e_fx_commands[(note->fxparam & 0xf0) >> 4];
239 break;
240 }
241 break;
242 case xmpCmdXPorta:
243 switch ((note->fxparam & 0xf0) >> 4) {
244 case 1:
245 line = N_("Extra fine porta up");
246 break;
247 case 2:
248 line = N_("Extra fine porta down");
249 break;
250 default:
251 break;
252 }
253 break;
254 default:
255 line = fx_commands[note->fxtype];
256 break;
257 }
258 pos += PRINT_STATUS(pos, "%s", line ? _(line) : _("None ]"));
259
260 cmd_p1 = (note->fxparam & 0xf0) >> 4;
261 cmd_p2 = note->fxparam & 0xf;
262
263 if (line) {
264 switch (n_params[note->fxtype] & 3) {
265 case 1:
266 case 3:
267 switch (note->fxtype) {
268 case xmpCmdSpeed:
269 if (note->fxparam < 32)
270 PRINT_STATUS(pos, _(" => tempo: %02d ]"), note->fxparam);
271 else
272 PRINT_STATUS(pos, _(" => BPM: %03d ]"), note->fxparam);
273 break;
274 case xmpCmdOffset:
275 PRINT_STATUS(pos, _(" => offset: %d ]"), note->fxparam << 8);
276 break;
277 case xmpCmdExtended:
278 if ((cmd_p1 == 4 || cmd_p1 == 7) && (cmd_p2 < 7) && cmd_p2 != 3)
279 /* Vibrato / tremolo control */
280 PRINT_STATUS(pos, " => %02d (%s)%s ]", cmd_p2, _(e47_fx_forms[cmd_p2 & 3]),
281 cmd_p2 & 4 ? _(", continuous mode") : "");
282 else if (cmd_p1 == 6) {
283 /* Pattern loop */
284 if (cmd_p2 == 0)
285 PRINT_STATUS(pos, _(" begin ]"));
286 else
287 PRINT_STATUS(pos, _(" %02d times ]"), cmd_p2);
288 } else
289 PRINT_STATUS(pos, " => %02d ]", cmd_p2);
290 break;
291 default:
292 PRINT_STATUS(pos, " => %03d ]", note->fxparam);
293 }
294
295 break;
296 case 2:
297 if (note->fxtype != xmpCmdArpeggio || note->fxparam) /* TODO detailed explanation of arpeggio */
298 PRINT_STATUS(pos, " => %02d %02d ]", cmd_p1, cmd_p2);
299 break;
300 default:
301 PRINT_STATUS(pos, "]");
302 break;
303 }
304 }
305
306 if (!track_editor_editmode_status_idle_handler) {
307 track_editor_editmode_status_idle_handler = g_idle_add(
308 (GSourceFunc)track_editor_editmode_status_idle_function,
309 NULL);
310 g_assert(track_editor_editmode_status_idle_handler != 0);
311 }
312 }
313
track_editor_set_patpos(gint row)314 void track_editor_set_patpos(gint row)
315 {
316 gtk_adjustment_set_value(adj, row);
317 }
318
track_editor_set_num_channels(int n)319 void track_editor_set_num_channels(int n)
320 {
321 int i;
322
323 tracker_set_num_channels(tracker, n);
324
325 // Remove superfluous togglebuttons from table
326 for (i = n; i < jazz_numshown; i++) {
327 g_object_ref(G_OBJECT(jazztoggles[i]));
328 gtk_container_remove(GTK_CONTAINER(jazztable), GTK_WIDGET(jazztoggles[i]));
329 }
330
331 // Resize table
332 gtk_table_resize(GTK_TABLE(jazztable), 1, n);
333
334 // Add new togglebuttons to table
335 for (i = jazz_numshown; i < n; i++) {
336 gtk_table_attach_defaults(GTK_TABLE(jazztable), GTK_WIDGET(jazztoggles[i]), i, i + 1, 0, 1);
337 }
338
339 jazz_numshown = n;
340 }
341
342 static void
set_vscrollbar(Tracker * t,int patpos,GtkAdjustment * adj)343 set_vscrollbar(Tracker* t,
344 int patpos,
345 GtkAdjustment* adj)
346 {
347 g_signal_handler_block(vscrollbar, vscroll_tag);
348 gtk_adjustment_set_value(adj, patpos);
349 g_signal_handler_unblock(vscrollbar, vscroll_tag);
350 }
351
352 static void
update_vscrollbar(Tracker * t,int patpos,int patlen,int disprows,GtkAdjustment * adj)353 update_vscrollbar(Tracker* t,
354 int patpos,
355 int patlen,
356 int disprows,
357 GtkAdjustment* adj)
358 {
359 g_signal_handler_block(vscrollbar, vscroll_tag);
360 gtk_adjustment_configure(adj, patpos, 0, patlen + disprows - 1, 1, disprows, disprows);
361 g_signal_handler_unblock(vscrollbar, vscroll_tag);
362 }
363
364 static void
set_hscrollbar(Tracker * t,int leftchan,GtkAdjustment * adj)365 set_hscrollbar(Tracker* t,
366 int leftchan,
367 GtkAdjustment* adj)
368 {
369 g_signal_handler_block(hscrollbar, hscroll_tag);
370 gtk_adjustment_set_value(adj, leftchan);
371 g_signal_handler_unblock(hscrollbar, hscroll_tag);
372 }
373
374 static void
update_hscrollbar(Tracker * t,int leftchan,int numchans,int dispchans,GtkAdjustment * adj)375 update_hscrollbar(Tracker* t,
376 int leftchan,
377 int numchans,
378 int dispchans,
379 GtkAdjustment* adj)
380 {
381 g_signal_handler_block(hscrollbar, hscroll_tag);
382 gtk_adjustment_configure(adj, leftchan, 0, numchans, 1, dispchans, dispchans);//or numchans - 1???
383 g_signal_handler_unblock(hscrollbar, hscroll_tag);
384 }
385
386 struct NoteArg {
387 gint channel, row;
388 XMNote note;
389 };
390
391 static void
note_undo(const gint ins,const gint smp,const gboolean redo,gpointer arg,gpointer data)392 note_undo(const gint ins, const gint smp, const gboolean redo,
393 gpointer arg, gpointer data)
394 {
395 XMNote tmp_value;
396 Tracker* t;
397 struct NoteArg* na = arg;
398
399 g_assert(IS_TRACKER(data));
400 t = TRACKER(data);
401 tmp_value = t->curpattern->channels[na->channel][na->row];
402 t->curpattern->channels[na->channel][na->row] = na->note;
403 na->note = tmp_value;
404
405 tracker_set_cursor_channel(t, na->channel);
406 track_editor_set_patpos(na->row);
407 }
408
409 void
track_editor_log_note(Tracker * t,const gchar * title,const gint pattern,const gint channel,const gint row,const gint flags)410 track_editor_log_note(Tracker* t,
411 const gchar* title,
412 const gint pattern, const gint channel, const gint row,
413 const gint flags)
414 {
415 struct NoteArg* arg = g_new(struct NoteArg, 1);
416
417 arg->channel = channel;
418 arg->row = row;
419 arg->note = (pattern == -1 ? t->curpattern : &xm->patterns[pattern])->channels[channel][row];
420 history_log_action(HISTORY_ACTION_POINTER, _(title), flags | HISTORY_FLAG_LOG_POS |
421 (pattern == -1 ? HISTORY_FLAG_LOG_PAT : HISTORY_SET_PAT(pattern)), note_undo,
422 t, sizeof(struct NoteArg), arg);
423 }
424
425 typedef struct
426 {
427 gint width, length, channel, pos;
428 /* Manual 2D indexing is supposed if needed */
429 XMNote notes[1];
430 } BlockArg;
431
track_editor_block_undo(const gint ins,const gint smp,const gboolean redo,gpointer arg,gpointer data)432 static void track_editor_block_undo(const gint ins, const gint smp, const gboolean redo,
433 gpointer arg, gpointer data)
434 {
435 Tracker* t;
436 BlockArg* ba = arg;
437 const gsize chansize = ba->length * sizeof(XMNote);
438 const gsize size = ba->width * chansize;
439 XMNote* tmp_notes = g_malloc(size);
440 gint i, chaddr;
441
442 g_assert(IS_TRACKER(data));
443 t = TRACKER(data);
444
445 /* Current pattern is already set by undo routines */
446 for (i = 0, chaddr = 0; i < ba->width; i++, chaddr += ba->length) {
447 memcpy(&tmp_notes[chaddr],
448 &t->curpattern->channels[ba->channel + i][ba->pos], chansize);
449 memcpy(&t->curpattern->channels[ba->channel + i][ba->pos],
450 &ba->notes[chaddr], chansize);
451 }
452 memcpy(ba->notes, tmp_notes, size);
453
454 g_free(tmp_notes);
455 tracker_redraw(t);
456 }
457
track_editor_log_block(Tracker * t,const gchar * title,const gint channel,const gint pos,const gint width,const gint length)458 void track_editor_log_block(Tracker* t, const gchar* title,
459 const gint channel, const gint pos, const gint width, const gint length)
460 {
461 const gsize chansize = length * sizeof(XMNote);
462 const gsize asize = sizeof(BlockArg) + sizeof(XMNote) * (length * width - 1);
463 gint i, chaddr;
464 /* We already have 1 XMNote allocated */
465 BlockArg* arg = g_malloc(asize);
466
467 arg->width = width;
468 arg->length = length;
469 arg->channel = channel;
470 arg->pos = pos;
471 for (i = 0, chaddr = 0; i < width; i++, chaddr += length)
472 memcpy(&arg->notes[chaddr], &t->curpattern->channels[channel + i][pos], chansize);
473
474 history_log_action(HISTORY_ACTION_POINTER, _(title),
475 HISTORY_FLAG_LOG_POS | HISTORY_FLAG_LOG_PAT | HISTORY_FLAG_LOG_PAGE,
476 track_editor_block_undo, t, asize, arg);
477 }
478
479 static void
track_editor_parameter_scroll(Tracker * t,gint cur_ch,gint cur_item,gint patpos,gint direction)480 track_editor_parameter_scroll(Tracker* t,
481 gint cur_ch,
482 gint cur_item,
483 gint patpos,
484 gint direction)
485 {
486
487 if (GUI_EDITING) {
488 guint8 vol, n_p, fxtype;
489 gint fxparam, fxparam1;
490 gboolean redraw = FALSE;
491 XMNote* note_entry = &t->curpattern->channels[cur_ch][patpos];
492
493 vol = note_entry->volume;
494 fxparam = note_entry->fxparam;
495
496 switch (cur_item) {
497 case 3: /* Volume column */
498 case 4:
499 if (!vol) { /* No volume specified => take it from sample basic value */
500 gint ins = note_entry->instrument;
501
502 if (ins)
503 vol = xm->instruments[ins - 1].samples[xm->instruments[ins - 1].samplemap[note_entry->note]].volume + 0x10;
504 }
505 if (vol >= 0x10 && vol <= 0x50) {
506 vol = CLAMP(vol + direction, 0x10, 0x50);
507 redraw = TRUE;
508 } else if (vol >= 0x60) {
509 gint8 nibble = vol & 0xf;
510
511 nibble = CLAMP(nibble + direction, 0, 0xf);
512 vol = (vol & 0xf0) | nibble;
513 redraw = TRUE;
514 }
515 break;
516 case 5 ... 7:
517 fxtype = note_entry->fxtype;
518 if ((fxtype || fxparam != 0) && fxtype < 36) { /* Valid XMs, non-empty effect */
519 n_p = n_params[fxtype];
520
521 switch (n_p & 3) {
522 case 1:
523 fxparam = CLAMP(fxparam + direction, 0, (n_p & NG_0x40) ? 0x40 : 0xff);
524 redraw = TRUE;
525 break;
526 case 3:
527 fxparam1 = fxparam & 0xf0;
528 fxparam = CLAMP((fxparam & 0xf) + direction, 0, 0xf) | fxparam1;
529 redraw = TRUE;
530 break;
531 case 2:
532 fxparam1 = fxparam & 0xf0;
533 fxparam = fxparam & 0xf;
534 if (cur_item == 7)
535 fxparam = CLAMP(fxparam + direction, 0, 0xf);
536 else
537 fxparam1 = CLAMP(fxparam1 + direction * 16, 0, 0xf0);
538 fxparam |= fxparam1;
539 redraw = TRUE;
540 break;
541 default:
542 break;
543 }
544 }
545 break;
546 default:
547 break;
548 }
549 if (redraw) {
550 static gpointer prev_pattern = NULL;
551 static gint prev_ch = -1, prev_patpos = -1;
552
553 if (t->curpattern != prev_pattern || prev_ch != cur_ch || prev_patpos != patpos) {
554 prev_pattern = t->curpattern;
555 prev_ch = cur_ch;
556 prev_patpos = patpos;
557 track_editor_log_note(t, N_("Volume / FX parameter scrolling"),
558 -1, cur_ch, patpos, HISTORY_FLAG_LOG_PAGE);
559 }
560 note_entry->volume = vol;
561 note_entry->fxparam = fxparam;
562 tracker_redraw_row(t, patpos);
563 }
564 }
565 }
566
tracker_page_create(GtkNotebook * nb)567 void tracker_page_create(GtkNotebook* nb)
568 {
569 GtkWidget *vbox, *hbox, *table, *thing;
570 int i;
571
572 vbox = gtk_vbox_new(FALSE, 2);
573 gtk_widget_show(vbox);
574
575 jazzbox = hbox = gtk_hbox_new(FALSE, 4);
576 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
577
578 thing = gtk_label_new(_("Jazz Edit:"));
579 gtk_box_pack_start(GTK_BOX(hbox), thing, FALSE, TRUE, 0);
580 gtk_widget_show(thing);
581
582 jazztable = table = gtk_table_new(1, 1, FALSE);
583 gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0);
584 gtk_widget_show(table);
585
586 for (i = 0; i < 32; i++) {
587 char buf[10];
588 g_sprintf(buf, "%02d", i + 1);
589 thing = gtk_toggle_button_new_with_label(buf);
590 jazztoggles[i] = GTK_TOGGLE_BUTTON(thing);
591 gtk_widget_show(thing);
592 }
593
594 table = gtk_table_new(2, 2, FALSE);
595 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
596 gtk_widget_show(table);
597
598 thing = tracker_new();
599 gtk_table_attach_defaults(GTK_TABLE(table), thing, 0, 1, 0, 1);
600 gtk_widget_show(thing);
601 g_signal_connect(thing, "mainmenu_blockmark_set", G_CALLBACK(update_mainmenu_blockmark), NULL);
602 g_signal_connect(thing, "alt_scroll", G_CALLBACK(track_editor_parameter_scroll), NULL);
603 tracker = TRACKER(thing);
604
605 tracker_set_update_freq(gui_settings.tracker_update_freq);
606
607 trackersettings = trackersettings_new();
608 trackersettings_set_tracker_widget(TRACKERSETTINGS(trackersettings), tracker);
609
610 hscrollbar = gtk_hscrollbar_new(NULL);
611 adj = gtk_range_get_adjustment(GTK_RANGE(hscrollbar));
612 gtk_table_attach(GTK_TABLE(table), hscrollbar, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
613 gtk_widget_show(hscrollbar);
614 g_signal_connect(thing, "xpanning", G_CALLBACK(set_hscrollbar), adj);
615 g_signal_connect(thing, "xconf", G_CALLBACK(update_hscrollbar), adj);
616 hscroll_tag = g_signal_connect_swapped(hscrollbar, "value-changed",
617 G_CALLBACK(hscrollbar_changed), adj);
618
619 vscrollbar = gtk_vscrollbar_new(NULL);
620 adj = gtk_range_get_adjustment(GTK_RANGE(vscrollbar));
621 gtk_table_attach(GTK_TABLE(table), vscrollbar, 1, 2, 0, 1, 0, GTK_FILL, 0, 0);
622 gtk_widget_show(vscrollbar);
623 g_signal_connect(thing, "patpos", G_CALLBACK(set_vscrollbar), adj);
624 g_signal_connect(thing, "yconf", G_CALLBACK(update_vscrollbar), adj);
625 vscroll_tag = g_signal_connect_swapped(vscrollbar, "value-changed",
626 G_CALLBACK(vscrollbar_changed), adj);
627
628 gtk_notebook_append_page(nb, vbox, gtk_label_new(_("Tracker")));
629 gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
630
631 memset(&block_buffer, 0, sizeof(block_buffer));
632 trackersettings_apply_font(TRACKERSETTINGS(trackersettings));
633
634 thing = gui_get_widget(gui_builder, "track_editor_popup_menu", XML_FILE);
635 gui_popup_menu_attach(thing, &tracker->widget, NULL);
636 }
637
638 static void
update_mainmenu_blockmark(Tracker * t,int state)639 update_mainmenu_blockmark(Tracker* t,
640 int state)
641 {
642 menubar_block_mode_set((gboolean)state, TRUE);
643 }
644
645 static void
vscrollbar_changed(GtkAdjustment * adj)646 vscrollbar_changed(GtkAdjustment* adj)
647 {
648 tracker_set_patpos(TRACKER(tracker), rint(gtk_adjustment_get_value(adj)));
649 }
650
651 static void
hscrollbar_changed(GtkAdjustment * adj)652 hscrollbar_changed(GtkAdjustment* adj)
653 {
654 tracker_set_xpanning(TRACKER(tracker), rint(gtk_adjustment_get_value(adj)));
655 }
656
track_editor_toggle_jazz_edit(GtkCheckMenuItem * b)657 void track_editor_toggle_jazz_edit(GtkCheckMenuItem* b)
658 {
659 jazz_enabled = gtk_check_menu_item_get_active(b);
660 (jazz_enabled ? gtk_widget_show : gtk_widget_hide)(jazzbox);
661 }
662
663 static int
track_editor_find_next_jazz_channel(int current_channel)664 track_editor_find_next_jazz_channel(int current_channel)
665 {
666 /* Jazz Edit Mode. The user has selected some channels that we can
667 use. Go to the next one where no sample is currently
668 playing. */
669
670 int j, n;
671
672 for (j = 0, n = (current_channel + 1) % xm->num_channels;
673 j < xm->num_channels - 1;
674 j++, n = (n + 1) % xm->num_channels) {
675 if (jazztoggles[n]->active && note_running[n] < 0) {
676 return n;
677 }
678 }
679
680 return current_channel;
681 }
682
683 /* Returns the channel number on which the specified note is running,
684 or -1 if no such note is found. If was == TRUE, look for the channel
685 when the note was running */
686 static gint
note_is_running(const guint note,const gboolean was)687 note_is_running(const guint note, const gboolean was)
688 {
689 guint i;
690 for (i = 0; i < ARRAY_SIZE(note_running); i++)
691 if (was) {
692 if (note_running[i] == -(note + 1))
693 return i;
694 } else {
695 if (note_running[i] == note + 1)
696 return i;
697 }
698
699 return -1;
700 }
701
track_editor_do_the_note_key(int notekeymeaning,gboolean pressed,guint32 xkeysym,int modifiers,gboolean always_poly)702 gboolean track_editor_do_the_note_key(int notekeymeaning,
703 gboolean pressed,
704 guint32 xkeysym,
705 int modifiers,
706 gboolean always_poly)
707 {
708 static int playchan = 0, trychan = 0;
709
710 int n;
711 int note;
712
713 note = notekeymeaning + 12 * gui_get_current_octave_value() + 1;
714
715 if (!(note >= 0 && note < NUM_NOTES))
716 return FALSE;
717
718 if (pressed) {
719 if (note_is_running(note, FALSE) == -1) {
720 if (gui_settings.try_polyphony &&
721 (always_poly || (!GTK_TOGGLE_BUTTON(editing_toggle)->active)) &&
722 gui_playing_mode != PLAYING_SONG &&
723 gui_playing_mode != PLAYING_PATTERN) {
724 gint cur_chan = -1, playchan;
725
726 if (!gui_settings.repeat_same_note)
727 cur_chan = note_is_running(note, TRUE);
728 gui_play_note(playchan = cur_chan == -1 ? trychan : cur_chan, note, TRUE);
729 note_running[playchan] = note + 1;
730 /* All 32 channels are using for trying independent on
731 actual number of channels used */
732 if (cur_chan == -1)
733 trychan = (trychan + 1) & 31;
734 } else {
735 if (jazz_enabled) {
736 if (GTK_TOGGLE_BUTTON(editing_toggle)->active) {
737 gui_play_note(tracker->cursor_ch, note, FALSE);
738 note_running[tracker->cursor_ch] = note + 1;
739 n = track_editor_find_next_jazz_channel(tracker->cursor_ch);
740 tracker_step_cursor_channel(tracker, n - tracker->cursor_ch);
741 } else {
742 playchan = track_editor_find_next_jazz_channel(playchan);
743 gui_play_note(playchan, note, FALSE);
744 note_running[playchan] = note + 1;
745 }
746 } else {
747 gui_play_note(tracker->cursor_ch, note, FALSE);
748 note_running[tracker->cursor_ch] = note + 1;
749 }
750 }
751 return TRUE; /* New note */
752 }
753 return FALSE; /* Key repeate from keyboard */
754 } else {
755 gint pc;
756
757 if ((pc = note_is_running(note, FALSE)) != -1) {
758 if (keys_is_key_pressed(xkeysym, modifiers)) {
759 /* this is just an auto-repeat fake keyoff. pooh.
760 in reality this key is still being held down */
761 return FALSE;
762 }
763 gui_play_note_keyoff(pc);
764 }
765 note_running[pc] = -(note + 1);
766 return TRUE;
767 }
768 }
769
770 #define EDITING_PATTERN (gui_playing_mode == PLAYING_PATTERN ? \
771 tracker->curpattern : &xm->patterns[xm->pattern_order_table[action_pat]])
772
773 static void
track_editor_put_note(guint ch,guint nn,guint instr)774 track_editor_put_note(guint ch, guint nn, guint instr)
775 {
776 gdouble press_time = current_driver->get_play_time(current_driver_object) - gui_settings.delay_comp;
777 gint8 action_tick = cur_tick;
778 gint action_pos = cur_pos;
779 gint action_pat = cur_pat;
780 XMNote *note, newnote;
781
782 if (gui_settings.precise_timing) {
783 /* This solution is not perfect since it can calculate only +- 1 tick shift.
784 At high BPM this may be not true. But thorough calculations are much more difficult.
785 Sorry... */
786 if (press_time > (nexttime + curtime) * 0.5) {
787 if (++action_tick >= cur_tempo) {
788 /* Roll over the pattern line boundary */
789 action_tick = 0;
790 action_pos++;
791 }
792 } else if (prevtime > 0.0 && press_time < (curtime + prevtime) * 0.5) {
793 if (--action_tick < 0) {
794 /* Roll over the pattern line boundary */
795 if (prev_tempo == 0)
796 prev_tempo = cur_tempo;
797 action_tick = prev_tempo - 1;
798 action_pos--;
799 }
800 }
801 } else {
802 action_tick = 0;
803 /* The key is pressed later than the half of the line -- moving it to the next line */
804 if (press_time >
805 curtime + (nexttime - curtime) * ((gdouble)cur_tempo * 0.5 - (gdouble)cur_tick))
806 action_pos++;
807 }
808
809 /* Roll over the pattern boundary */
810 if (action_pos < 0) {
811 if (--action_pat < 0)
812 /* Roll over the song boundary */
813 action_pat = xm->song_length - 1;
814 action_pos = EDITING_PATTERN->length - 1;
815 }
816 if (action_pos >= EDITING_PATTERN->length) {
817 if (++action_pat >= xm->song_length)
818 /* Roll over the song boundary */
819 action_pat = 0;
820 action_pos = 0;
821 }
822
823 note = &EDITING_PATTERN->channels[ch][action_pos];
824 newnote = *note;
825 if (action_tick == 0) {
826 newnote.note = nn;
827 newnote.instrument = instr;
828 } else {
829 if (nn == XM_PATTERN_NOTE_OFF) {
830 /* Key release */
831 if (note->fxtype == 0 && note->fxparam == 0) {
832 /* Effect field is free; we can use 'k' effect */
833 newnote.fxtype = xmpCmdKeyOff;
834 newnote.fxparam = action_tick;
835 } else {
836 /* Effect field is occupied; we can nothing to do else than just put key off
837 at the next line. Regard the possible rollovers. */
838 if (++action_pos >= xm->patterns[xm->pattern_order_table[action_pat]].length) {
839 if (++action_pat >= xm->song_length)
840 /* Roll over the song boundary */
841 action_pat = 0;
842 action_pos = 0;
843 }
844 note = &xm->patterns[xm->pattern_order_table[action_pat]].channels[ch][action_pos];
845 newnote = *note;
846 newnote.note = nn;
847 newnote.instrument = instr;
848 }
849 } else {
850 /* We use 'ed' effect to record a note at the proper tick */
851 newnote.note = nn;
852 newnote.instrument = instr;
853 newnote.fxtype = xmpCmdExtended;
854 newnote.fxparam = XMP_CMD_EXTENDED(xmpCmdDelayNote, action_tick);
855 }
856 }
857
858 track_editor_log_note(tracker, N_("Note input"), xm->pattern_order_table[action_pat],
859 ch, action_pos, HISTORY_FLAG_LOG_PAGE);
860 *note = newnote;
861 if (action_pat == cur_pat)
862 /* Redrawing is needed only if note is recorded in the current pattern */
863 tracker_redraw_row(tracker, action_pos);
864 }
865
866 gboolean
track_editor_handle_keys(const int shift,const int ctrl,const int alt,const guint32 keyval,const gboolean pressed)867 track_editor_handle_keys(const int shift,
868 const int ctrl,
869 const int alt,
870 const guint32 keyval,
871 const gboolean pressed)
872 {
873 int c, i, m, tip;
874 Tracker* t = tracker;
875 gboolean handled = FALSE;
876 gboolean in_sel = tracker_is_in_selection_mode(t);
877 const gboolean sel_ft2 = gui_settings.selection_mode == SELECTION_FT2 ||
878 gui_settings.selection_mode == SELECTION_MIXED;
879
880 m = i = keys_get_key_meaning(keyval, ENCODE_MODIFIERS(shift, ctrl, alt), -1);
881 tip = KEYS_MEANING_TYPE(i);
882
883 if (i != -1) {
884 if (tip == KEYS_MEANING_NOTE && !GTK_TOGGLE_BUTTON(editing_toggle)->active)
885 track_editor_do_the_note_key(m, pressed, keyval, ENCODE_MODIFIERS(shift, ctrl, alt), FALSE);
886
887 if (t->cursor_item == 0) {
888 if (tip == KEYS_MEANING_NOTE) {
889 i += 12 * gui_get_current_octave_value() + 1;
890
891 if (GTK_TOGGLE_BUTTON(editing_toggle)->active) {
892 if (i < NUM_NOTES) {
893 if (t->cursor_item == 0) {
894 if (!jazztoggles[tracker->cursor_ch]->active && jazz_enabled) {
895 int n = track_editor_find_next_jazz_channel(tracker->cursor_ch);
896 tracker_step_cursor_channel(tracker, n - tracker->cursor_ch);
897 }
898
899 if (!GUI_ENABLED && !ASYNCEDIT) { // Recording mode
900 if (pressed) { // Insert note
901
902 for (c = 0; c < 32; c++) { // Cleanup
903 if (!reckey[c].act)
904 continue;
905 if (reckey[c].key == keyval)
906 break; // Key is allready down
907 else if (reckey[c].chn == t->cursor_ch)
908 reckey[c].act = 0; // There can be only one sound per channel
909 }
910
911 if (c == 32) {
912 // Find free reckey
913 for (c = 0; c < 32; c++)
914 if (!reckey[c].act)
915 break;
916
917 // Fill in the reckey
918 reckey[c].key = keyval;
919 reckey[c].chn = t->cursor_ch;
920 reckey[c].act = TRUE;
921 track_editor_put_note(t->cursor_ch, i, gui_get_current_instrument());
922 }
923 } else if (!keys_is_key_pressed(keyval,
924 ENCODE_MODIFIERS(shift, ctrl, alt))) { // Release key
925
926 // Find right reckey
927 for (c = 0; c < 32; c++)
928 if (reckey[c].act && reckey[c].key == keyval)
929 break;
930 if (c < 32) { /* Check if the key was not released by other means */
931 reckey[c].act = FALSE;
932
933 if (gui_settings.insert_noteoff)
934 track_editor_put_note(reckey[c].chn, XM_PATTERN_NOTE_OFF, 0);
935 }
936 }
937 } else if (pressed) {
938 gint jump = gui_get_current_jump_value();
939 XMNote* note = &t->curpattern->channels[t->cursor_ch][t->patpos];
940
941 track_editor_log_note(t, N_("Note input"),
942 -1, t->cursor_ch, t->patpos, HISTORY_FLAG_LOG_PAGE);
943 note->note = i;
944 note->instrument = gui_get_current_instrument();
945 if (jump)
946 tracker_step_cursor_row(t, jump);
947 else
948 tracker_redraw_current_row(t);
949 }
950 }
951 }
952 show_editmode_status();
953 track_editor_do_the_note_key(m, pressed, keyval, ENCODE_MODIFIERS(shift, ctrl, alt), FALSE);
954 }
955
956 return TRUE;
957 } else if (tip == KEYS_MEANING_KEYOFF) {
958 if (GTK_TOGGLE_BUTTON(editing_toggle)->active) {
959 if (pressed) {
960 gint jump = gui_get_current_jump_value();
961 XMNote* note = &t->curpattern->channels[t->cursor_ch][t->patpos];
962
963 track_editor_log_note(t, N_("Note input"),
964 -1, t->cursor_ch, t->patpos, HISTORY_FLAG_LOG_PAGE);
965 note->note = XM_PATTERN_NOTE_OFF;
966 note->instrument = 0;
967 if (jump)
968 tracker_step_cursor_row(t, gui_get_current_jump_value());
969 else
970 tracker_redraw_current_row(t);
971 }
972 show_editmode_status();
973 }
974
975 return TRUE;
976 }
977 }
978 }
979
980 if (!pressed) {
981 if (keyval == GDK_Shift_L && in_sel &&
982 gui_settings.selection_mode == SELECTION_FT2) {
983 menubar_block_mode_set(FALSE, FALSE);
984 return TRUE;
985 }
986 return FALSE;
987 }
988
989 switch (tip) {
990 case KEYS_MEANING_FUNC:
991 switch (KEYS_MEANING_VALUE(i)) {
992 case KEY_JMP_PLUS:
993 m = gui_get_current_jump_value() + 1;
994 if (m <= 16)
995 gui_set_jump_value(m);
996 handled = TRUE;
997 break;
998 case KEY_JMP_MINUS:
999 m = gui_get_current_jump_value() - 1;
1000 if (m >= 0)
1001 gui_set_jump_value(m);
1002 handled = TRUE;
1003 break;
1004 case KEY_PLAY_ROW:
1005 gui_play_current_pattern_row();
1006 handled = TRUE;
1007 break;
1008 case KEY_PLAY_BLK:
1009 gui_play_block();
1010 handled = TRUE;
1011 break;
1012 default:
1013 break;
1014 }
1015 if (handled)
1016 return TRUE;
1017 break;
1018 case KEYS_MEANING_CH:
1019 tracker_set_cursor_channel(t, KEYS_MEANING_VALUE(i));
1020 if (GTK_TOGGLE_BUTTON(editing_toggle)->active)
1021 show_editmode_status();
1022 return TRUE;
1023 }
1024
1025 switch (keyval) {
1026 case GDK_Up:
1027 if ((GUI_ENABLED || ASYNCEDIT) && !ctrl && !alt) {
1028 if (shift && !in_sel && sel_ft2) {
1029 /* Start selection */
1030 in_sel = TRUE;
1031 menubar_block_mode_set(TRUE, FALSE);
1032 }
1033 track_editor_set_patpos(t->patpos > 0 ? t->patpos - 1 : t->curpattern->length - 1);
1034 handled = TRUE;
1035 }
1036 break;
1037 case GDK_Down:
1038 if ((GUI_ENABLED || ASYNCEDIT) && !ctrl && !alt) {
1039 if (shift && !in_sel && sel_ft2) {
1040 /* Start selection */
1041 in_sel = TRUE;
1042 menubar_block_mode_set(TRUE, FALSE);
1043 }
1044 track_editor_set_patpos(t->patpos < t->curpattern->length - 1 ? t->patpos + 1 : 0);
1045 handled = TRUE;
1046 }
1047 break;
1048 case GDK_Page_Up:
1049 if ((GUI_ENABLED || ASYNCEDIT) && !ctrl) {
1050 if (shift && !in_sel && sel_ft2) {
1051 /* Start selection */
1052 in_sel = TRUE;
1053 menubar_block_mode_set(TRUE, FALSE);
1054 }
1055 if (t->patpos >= 16)
1056 track_editor_set_patpos(t->patpos - 16);
1057 else
1058 track_editor_set_patpos(0);
1059 handled = TRUE;
1060 }
1061 break;
1062 case GDK_Page_Down:
1063 if ((GUI_ENABLED || ASYNCEDIT) && !ctrl) {
1064 if (shift && !in_sel && sel_ft2) {
1065 /* Start selection */
1066 in_sel = TRUE;
1067 menubar_block_mode_set(TRUE, FALSE);
1068 }
1069 if (t->patpos < t->curpattern->length - 16)
1070 track_editor_set_patpos(t->patpos + 16);
1071 else
1072 track_editor_set_patpos(t->curpattern->length - 1);
1073 handled = TRUE;
1074 }
1075 break;
1076 case GDK_Home:
1077 if (ctrl)
1078 break;
1079 case GDK_F9:
1080 if (GUI_ENABLED || ASYNCEDIT) {
1081 if (shift && !in_sel && sel_ft2) {
1082 /* Start selection */
1083 in_sel = TRUE;
1084 menubar_block_mode_set(TRUE, FALSE);
1085 }
1086 track_editor_set_patpos(0);
1087 handled = TRUE;
1088 }
1089 break;
1090 case GDK_F10:
1091 if (GUI_ENABLED || ASYNCEDIT) {
1092 if (shift && !in_sel && sel_ft2) {
1093 /* Start selection */
1094 in_sel = TRUE;
1095 menubar_block_mode_set(TRUE, FALSE);
1096 }
1097 track_editor_set_patpos(t->curpattern->length / 4);
1098 handled = TRUE;
1099 }
1100 break;
1101 case GDK_F11:
1102 if (GUI_ENABLED || ASYNCEDIT) {
1103 if (shift && !in_sel && sel_ft2) {
1104 /* Start selection */
1105 in_sel = TRUE;
1106 menubar_block_mode_set(TRUE, FALSE);
1107 }
1108 track_editor_set_patpos(t->curpattern->length / 2);
1109 handled = TRUE;
1110 }
1111 break;
1112 case GDK_F12:
1113 if (GUI_ENABLED || ASYNCEDIT) {
1114 if (shift && !in_sel && sel_ft2) {
1115 /* Start selection */
1116 in_sel = TRUE;
1117 menubar_block_mode_set(TRUE, FALSE);
1118 }
1119 track_editor_set_patpos(3 * t->curpattern->length / 4);
1120 handled = TRUE;
1121 }
1122 break;
1123 case GDK_End:
1124 if ((GUI_ENABLED || ASYNCEDIT) && !ctrl) {
1125 if (shift && !in_sel && sel_ft2) {
1126 /* Start selection */
1127 in_sel = TRUE;
1128 menubar_block_mode_set(TRUE, FALSE);
1129 }
1130 track_editor_set_patpos(t->curpattern->length - 1);
1131 handled = TRUE;
1132 }
1133 break;
1134 case GDK_Left:
1135 if (!ctrl && !alt) {
1136 if (shift && !in_sel && sel_ft2) {
1137 /* Start selection */
1138 in_sel = TRUE;
1139 menubar_block_mode_set(TRUE, FALSE);
1140 }
1141 /* cursor left */
1142 in_sel ? tracker_step_cursor_channel(t, -1)
1143 : tracker_step_cursor_item(t, -1);
1144 handled = TRUE;
1145 }
1146 break;
1147 case GDK_Right:
1148 if (!ctrl && !alt) {
1149 if (shift && !in_sel && sel_ft2) {
1150 /* Start selection */
1151 in_sel = TRUE;
1152 menubar_block_mode_set(TRUE, FALSE);
1153 }
1154 /* cursor right */
1155 in_sel ? tracker_step_cursor_channel(t, 1)
1156 : tracker_step_cursor_item(t, 1);
1157 handled = TRUE;
1158 }
1159 break;
1160 case GDK_Tab:
1161 case GDK_ISO_Left_Tab:
1162 tracker_step_cursor_channel(t, shift ? -1 : 1);
1163 handled = TRUE;
1164 break;
1165 case GDK_Delete:
1166 if (GTK_TOGGLE_BUTTON(editing_toggle)->active) {
1167 gint jump = gui_get_current_jump_value();
1168 XMNote* note = &t->curpattern->channels[t->cursor_ch][t->patpos];
1169
1170 track_editor_log_note(t, N_("Note cleaning"),
1171 -1, t->cursor_ch, t->patpos, HISTORY_FLAG_LOG_PAGE);
1172 if (shift) {
1173 note->note = 0;
1174 note->instrument = 0;
1175 note->volume = 0;
1176 note->fxtype = 0;
1177 note->fxparam = 0;
1178 } else if (ctrl) {
1179 note->volume = 0;
1180 note->fxtype = 0;
1181 note->fxparam = 0;
1182 } else if (alt) {
1183 note->fxtype = 0;
1184 note->fxparam = 0;
1185 } else {
1186 switch (t->cursor_item) {
1187 case 0:
1188 case 1:
1189 case 2:
1190 note->note = 0;
1191 note->instrument = 0;
1192 break;
1193 case 3:
1194 case 4:
1195 note->volume = 0;
1196 break;
1197 case 5:
1198 case 6:
1199 case 7:
1200 note->fxtype = 0;
1201 note->fxparam = 0;
1202 break;
1203 default:
1204 g_assert_not_reached();
1205 break;
1206 }
1207 }
1208
1209 if (jump)
1210 tracker_step_cursor_row(t, jump);
1211 else
1212 tracker_redraw_current_row(t);
1213 handled = TRUE;
1214 }
1215 break;
1216 case GDK_Insert:
1217 if (GTK_TOGGLE_BUTTON(editing_toggle)->active && !shift && !alt && !ctrl) {
1218 XMNote* note = &t->curpattern->channels[t->cursor_ch][t->patpos];
1219
1220 track_editor_log_block(t, N_("Note inserting"),
1221 t->cursor_ch, t->patpos, 1, t->curpattern->length - t->patpos);
1222 for (i = t->curpattern->length - 1; i > t->patpos; --i)
1223 t->curpattern->channels[t->cursor_ch][i] = t->curpattern->channels[t->cursor_ch][i - 1];
1224
1225 note->note = 0;
1226 note->instrument = 0;
1227 note->volume = 0;
1228 note->fxtype = 0;
1229 note->fxparam = 0;
1230
1231 tracker_redraw(t);
1232 handled = TRUE;
1233 }
1234 break;
1235 case GDK_BackSpace:
1236 if (GTK_TOGGLE_BUTTON(editing_toggle)->active && !shift &&!ctrl && !alt) {
1237 XMNote* note;
1238
1239 if (t->patpos) {
1240 track_editor_log_block(t, N_("Note removing"),
1241 t->cursor_ch, t->patpos - 1, 1, t->curpattern->length - t->patpos + 1);
1242 for (i = t->patpos - 1; i < t->curpattern->length - 1; i++)
1243 t->curpattern->channels[t->cursor_ch][i] = t->curpattern->channels[t->cursor_ch][i + 1];
1244
1245 note = &t->curpattern->channels[t->cursor_ch][t->curpattern->length - 1];
1246 note->note = 0;
1247 note->instrument = 0;
1248 note->volume = 0;
1249 note->fxtype = 0;
1250 note->fxparam = 0;
1251
1252 tracker_redraw(t);
1253 track_editor_set_patpos(t->patpos - 1);
1254 handled = TRUE;
1255 }
1256 }
1257 break;
1258
1259 case GDK_Escape:
1260 if (shift) {
1261 tracker_set_cursor_item(t, 0);
1262 handled = TRUE;
1263 }
1264 break;
1265 default:
1266 if (!ctrl && !alt) {
1267 if (GTK_TOGGLE_BUTTON(editing_toggle)->active) {
1268 handled = track_editor_handle_column_input(t, keyval);
1269 }
1270 }
1271 break;
1272 }
1273
1274 if (GTK_TOGGLE_BUTTON(editing_toggle)->active && handled)
1275 show_editmode_status();
1276
1277 return handled;
1278 }
1279
track_editor_copy_pattern(GtkWidget * w,Tracker * t)1280 void track_editor_copy_pattern(GtkWidget* w, Tracker* t)
1281 {
1282 XMPattern* p = t->curpattern;
1283
1284 if (pattern_buffer) {
1285 st_free_pattern_channels(pattern_buffer);
1286 free(pattern_buffer);
1287 }
1288 pattern_buffer = st_dup_pattern(p);
1289 tracker_redraw(t);
1290 }
1291
track_editor_cut_pattern(GtkWidget * w,Tracker * t)1292 void track_editor_cut_pattern(GtkWidget* w, Tracker* t)
1293 {
1294 if (GUI_EDITING) {
1295 XMPattern* p = t->curpattern;
1296
1297 gui_log_pattern(p, N_("Pattern cut"), -1, -1, -1);
1298 if (pattern_buffer) {
1299 st_free_pattern_channels(pattern_buffer);
1300 free(pattern_buffer);
1301 }
1302 pattern_buffer = st_dup_pattern(p);
1303 st_clear_pattern(p);
1304 tracker_redraw(t);
1305 }
1306 }
1307
track_editor_paste_pattern(GtkWidget * w,Tracker * t)1308 void track_editor_paste_pattern(GtkWidget* w, Tracker* t)
1309 {
1310 if (GUI_EDITING) {
1311 XMPattern* p = t->curpattern;
1312 int i;
1313
1314 if (!pattern_buffer)
1315 return;
1316 gui_log_pattern(p, N_("Pattern paste"), -1, -1,
1317 MAX(p->length, pattern_buffer->length));
1318 for (i = 0; i < 32; i++) {
1319 free(p->channels[i]);
1320 p->channels[i] = st_dup_track(pattern_buffer->channels[i], pattern_buffer->length);
1321 }
1322 p->alloc_length = pattern_buffer->length;
1323 if (p->length != pattern_buffer->length) {
1324 p->length = pattern_buffer->length;
1325 gui_update_pattern_data();
1326 tracker_reset(t);
1327 } else {
1328 tracker_redraw(t);
1329 }
1330 }
1331 }
1332
track_editor_copy_track(GtkWidget * w,Tracker * t)1333 void track_editor_copy_track(GtkWidget* w, Tracker* t)
1334 {
1335 int l = t->curpattern->length;
1336 XMNote* n = t->curpattern->channels[t->cursor_ch];
1337
1338 if (track_buffer) {
1339 free(track_buffer);
1340 }
1341 track_buffer_length = l;
1342 track_buffer = st_dup_track(n, l);
1343 tracker_redraw(t);
1344 }
1345
track_editor_cut_track(GtkWidget * w,Tracker * t)1346 void track_editor_cut_track(GtkWidget* w, Tracker* t)
1347 {
1348 if (GUI_EDITING) {
1349 int l = t->curpattern->length;
1350 XMNote* n = t->curpattern->channels[t->cursor_ch];
1351
1352 track_editor_log_block(t, N_("Track cut"), t->cursor_ch, 0, 1, l);
1353 if (track_buffer) {
1354 free(track_buffer);
1355 }
1356 track_buffer_length = l;
1357 track_buffer = st_dup_track(n, l);
1358 st_clear_track(n, l);
1359 tracker_redraw(t);
1360 }
1361 }
1362
track_editor_paste_track(GtkWidget * w,Tracker * t)1363 void track_editor_paste_track(GtkWidget* w, Tracker* t)
1364 {
1365 if (GUI_EDITING) {
1366 int l = t->curpattern->length;
1367 XMNote* n = t->curpattern->channels[t->cursor_ch];
1368 int i;
1369
1370 if (!track_buffer)
1371 return;
1372 track_editor_log_block(t, N_("Track paste"), t->cursor_ch, 0, 1, l);
1373 i = track_buffer_length;
1374 if (l < i)
1375 i = l;
1376 while (i--)
1377 n[i] = track_buffer[i];
1378 tracker_redraw(t);
1379 }
1380 }
1381
track_editor_delete_track(GtkWidget * w,Tracker * t)1382 void track_editor_delete_track(GtkWidget* w, Tracker* t)
1383 {
1384 if (GUI_EDITING) {
1385 gui_log_pattern(t->curpattern, N_("Track removing"), -1, -1, -1);
1386 st_pattern_delete_track(t->curpattern, t->cursor_ch);
1387 tracker_redraw(t);
1388 }
1389 }
1390
track_editor_insert_track(GtkWidget * w,Tracker * t)1391 void track_editor_insert_track(GtkWidget* w, Tracker* t)
1392 {
1393 if (GUI_EDITING) {
1394 gboolean is_ok = TRUE;
1395
1396 if (!st_is_empty_track(t->curpattern->channels[t->num_channels - 1], t->curpattern->length)) {
1397 if (t->num_channels < 32)
1398 is_ok = gui_ok_cancel_modal(mainwindow,
1399 _("The last track of the pattern is not empty. It will be shifted beyond the pattern scope. "
1400 "You can increase the number of channels to see it. Continue?"));
1401 else
1402 is_ok = gui_ok_cancel_modal(mainwindow,
1403 _("Warning! The last track of the pattern is not empty. It will be brought to digital nought! "
1404 "Do you really want to do this?"));
1405 }
1406 if (is_ok) {
1407 gui_log_pattern(t->curpattern, N_("Track insertion"), -1, -1, -1);
1408 st_pattern_insert_track(t->curpattern, t->cursor_ch);
1409 tracker_redraw(t);
1410 }
1411 }
1412 }
1413
track_editor_kill_notes_track(GtkWidget * w,Tracker * t)1414 void track_editor_kill_notes_track(GtkWidget* w, Tracker* t)
1415 {
1416 if (GUI_EDITING) {
1417 int i;
1418 XMNote* note;
1419
1420 track_editor_log_block(t, N_("Killing notes"),
1421 t->cursor_ch, t->patpos, 1, t->curpattern->length - t->patpos);
1422
1423 for (i = t->patpos; i < t->curpattern->length; i++) {
1424 note = &t->curpattern->channels[t->cursor_ch][i];
1425 note->note = 0;
1426 note->instrument = 0;
1427 note->volume = 0;
1428 note->fxtype = 0;
1429 note->fxparam = 0;
1430 }
1431
1432 tracker_redraw(t);
1433 }
1434 }
1435
track_editor_cmd_mvalue(Tracker * t,gboolean mode)1436 void track_editor_cmd_mvalue(Tracker* t, gboolean mode)
1437 {
1438 XMNote* note;
1439 int nparam, tpos;
1440
1441 tpos = t->cursor_item;
1442
1443 if (tpos >= 3) {
1444 gint from = t->patpos - gui_get_current_jump_value();
1445 gint jump = gui_get_current_jump_value();
1446
1447 if (from >= 0) {
1448 XMNote newnote = t->curpattern->channels[t->cursor_ch][t->patpos];
1449 note = &t->curpattern->channels[t->cursor_ch][from];
1450
1451 if (tpos < 5) {
1452 if (!(nparam = note->volume))
1453 return;
1454 if (nparam < 0x10 || nparam > 0x50) {
1455 nparam &= 0xf;
1456 mode ? nparam++ : nparam--;
1457
1458 newnote.volume &= 0xf0;
1459 newnote.volume |= nparam & 0xf;
1460 } else {
1461 if (mode && nparam < 0x50)
1462 nparam++;
1463 if (!mode && nparam > 0x10)
1464 nparam--;
1465
1466 newnote.volume = nparam;
1467 }
1468 } else {
1469 if (!(nparam = note->fxparam) && ! note->fxtype)
1470 return;
1471 mode ? nparam++ : nparam--;
1472 newnote.fxparam = nparam & 0xff;
1473 }
1474 track_editor_log_note(t, N_("Vol / FX value increasing / decreasing"), -1,
1475 t->cursor_ch, t->patpos, HISTORY_FLAG_LOG_PAGE);
1476 t->curpattern->channels[t->cursor_ch][t->patpos] = newnote;
1477
1478 if (jump)
1479 tracker_step_cursor_row(t, jump);
1480 else
1481 tracker_redraw_current_row(t);
1482 }
1483 }
1484 }
1485
track_editor_mark_selection(Tracker * t)1486 void track_editor_mark_selection(Tracker* t)
1487 {
1488 tracker_mark_selection(t, TRUE);
1489 }
1490
track_editor_clear_mark_selection(GtkWidget * w,Tracker * t)1491 void track_editor_clear_mark_selection(GtkWidget* w, Tracker* t)
1492 {
1493 tracker_clear_mark_selection(t);
1494 menubar_block_mode_set(FALSE, TRUE);
1495 }
1496
1497 static void
track_editor_copy_cut_selection_common(Tracker * t,gboolean cut)1498 track_editor_copy_cut_selection_common(Tracker* t,
1499 gboolean cut)
1500 {
1501 int i;
1502 int height, width, chStart, rowStart;
1503
1504 if (!tracker_is_valid_selection(t))
1505 return;
1506
1507 if (tracker_is_in_selection_mode(t))
1508 tracker_mark_selection(t, FALSE);
1509
1510 tracker_get_selection_rect(t, &chStart, &rowStart, &width, &height);
1511
1512 block_buffer.alloc_length = block_buffer.length = height;
1513
1514 for (i = 0; i < 32; i++) {
1515 free(block_buffer.channels[i]);
1516 block_buffer.channels[i] = NULL;
1517 }
1518
1519 if (cut)
1520 track_editor_log_block(t, N_("Block cutting"), chStart, rowStart, width, height);
1521 for (i = 0; i < width; i++) {
1522 block_buffer.channels[i] = st_dup_track_wrap(t->curpattern->channels[(chStart + i) % xm->num_channels],
1523 t->curpattern->length,
1524 rowStart,
1525 height);
1526 if (cut) {
1527 st_clear_track_wrap(t->curpattern->channels[(chStart + i) % xm->num_channels],
1528 t->curpattern->length,
1529 rowStart,
1530 height);
1531 }
1532 }
1533 }
1534
track_editor_copy_selection(GtkWidget * w,Tracker * t)1535 void track_editor_copy_selection(GtkWidget* w, Tracker* t)
1536 {
1537 track_editor_copy_cut_selection_common(t, FALSE);
1538 menubar_block_mode_set(FALSE, TRUE);
1539 }
1540
track_editor_cut_selection(GtkWidget * w,Tracker * t)1541 void track_editor_cut_selection(GtkWidget* w, Tracker* t)
1542 {
1543 if (GUI_EDITING) {
1544 track_editor_copy_cut_selection_common(t, TRUE);
1545 menubar_block_mode_set(FALSE, TRUE);
1546 tracker_redraw(t);
1547 }
1548 }
1549
track_editor_paste_selection(GtkWidget * w,Tracker * t,gboolean advance_cursor)1550 void track_editor_paste_selection(GtkWidget* w, Tracker* t, gboolean advance_cursor)
1551 {
1552 if (GUI_EDITING) {
1553 int i;
1554
1555 if (block_buffer.length > t->curpattern->length)
1556 return;
1557
1558 gui_log_pattern(t->curpattern, N_("Selection paste"), -1, -1, -1);
1559 for (i = 0; i < 32; i++) {
1560 st_paste_track_into_track_wrap(block_buffer.channels[i],
1561 t->curpattern->channels[(t->cursor_ch + i) % xm->num_channels],
1562 t->curpattern->length,
1563 t->patpos,
1564 block_buffer.length);
1565 }
1566
1567 if (advance_cursor)
1568 track_editor_set_patpos((t->patpos + block_buffer.length) % t->curpattern->length);
1569 tracker_redraw(t);
1570 }
1571 }
1572
track_editor_paste_selection_no_advance(GtkWidget * w,Tracker * t)1573 void track_editor_paste_selection_no_advance(GtkWidget* w, Tracker* t)
1574 {
1575 track_editor_paste_selection(w, t, FALSE);
1576 }
1577
1578 static void
track_editor_do_interpolate_fx(GtkWidget * w,Tracker * t,const gboolean match)1579 track_editor_do_interpolate_fx(GtkWidget* w, Tracker* t, const gboolean match)
1580 {
1581 if (GUI_EDITING) {
1582 gint height, width, chStart, rowStart;
1583 gint xmnote_offset;
1584 guint8 xmnote_mask, xmnote_mask1 = 0, vol_fx = 0;
1585 XMNote *note_start, *note_end;
1586 gint i;
1587 gint dy, dy1 = 0;
1588 gint start_value, start_value1, start_char;
1589
1590 if (!tracker_is_valid_selection(t))
1591 return;
1592
1593 tracker_get_selection_rect(t, &chStart, &rowStart, &width, &height);
1594 if (width != 1 || t->cursor_ch != chStart)
1595 return;
1596
1597 if (t->cursor_item >= 3)
1598 track_editor_log_block(t, N_("FX interpolating"),
1599 t->cursor_ch, rowStart, 1, height);
1600 else
1601 return;
1602
1603 note_start = &t->curpattern->channels[t->cursor_ch][rowStart];
1604 note_end = &t->curpattern->channels[t->cursor_ch][rowStart + height - 1];
1605
1606 if (t->cursor_item <= 4) {
1607 // Interpolate volume column
1608 xmnote_offset = (void*)(¬e_start->volume) - (void*)note_start;
1609
1610 if (note_start->volume >= 0x10 && note_start->volume <= 0x50) {
1611 if (note_end->volume < 0x10 || note_end->volume > 0x50)
1612 return;
1613 xmnote_mask = 0xff;
1614 } else {
1615 vol_fx = note_start->volume & 0xf0;
1616 if ((note_end->volume & 0xf0) != vol_fx)
1617 return;
1618 xmnote_mask = 0x0f;
1619 }
1620 } else {
1621 // Interpolate effects column
1622 xmnote_offset = (void*)¬e_start->fxparam - (void*)note_start;
1623
1624 if (note_start->fxtype != note_end->fxtype)
1625 return;
1626
1627 switch (n_params[note_start->fxtype] & 3) {
1628 case 0:
1629 return;
1630 case 1:
1631 xmnote_mask = 0xff;
1632 break;
1633 case 2:
1634 xmnote_mask1 = 0xf0;
1635 xmnote_mask = 0x0f;
1636 break;
1637 case 3:
1638 if ((note_start->fxparam & 0xf0) != (note_end->fxparam & 0xf0))
1639 return;
1640 xmnote_mask = 0x0f;
1641 break;
1642 }
1643
1644 if (!match)
1645 for (i = 1; i < height - 1; i++) {
1646 // Skip lines that allready have effect on them
1647 if ((note_start + i)->fxtype)
1648 continue;
1649
1650 // Copy the effect type into all rows in between
1651 (note_start + i)->fxtype = note_start->fxtype;
1652 /* Single 4-bit parameter, most significant nibble should also be added */
1653 if (xmnote_mask == 0x0f && !xmnote_mask1)
1654 (note_start + i)->fxparam = note_start->fxparam & 0xf0;
1655 }
1656
1657 }
1658
1659 /* Bit-fiddling coming up... */
1660
1661 dy = *((guint8*)(note_end) + xmnote_offset) & xmnote_mask;
1662 start_char = *((guint8*)(note_start) + xmnote_offset);
1663 start_value = start_char & xmnote_mask;
1664 dy -= start_value;
1665 if (xmnote_mask1) {
1666 dy1 = *((guint8*)(note_end) + xmnote_offset) & xmnote_mask1;
1667 start_value1 = start_char & xmnote_mask1;
1668 dy1 -= start_value1;
1669 }
1670
1671 for (i = 1; i < height - 1; i++) {
1672 gint new_value;
1673
1674 // On effect interpolation, skip lines that allready contain different effects
1675 if (t->cursor_item >= 5 && (note_start + i)->fxtype != note_start->fxtype)
1676 continue;
1677
1678 /* Effect with single 4-bit parameter. The most significant parameter nibble acts
1679 like subeffect and must be taken into account */
1680 if (t->cursor_item >= 5 && xmnote_mask == 0x0f && !xmnote_mask1 &&
1681 ((note_start + i)->fxparam & 0xf0) != (note_start->fxparam & 0xf0))
1682 continue;
1683
1684 /* In match mode modify only if FX in the volume column matches */
1685 if (match && t->cursor_item <= 4) {
1686 const guint8 volume = (note_start + i)->volume;
1687
1688 if (vol_fx) {
1689 if (volume && 0xf0 != vol_fx)
1690 continue;
1691 } else { /* Volume, don't touch effects and empty fields */
1692 if (volume < 0x10 || volume > 0x50)
1693 continue;
1694 }
1695 }
1696
1697 new_value = start_value + (gint)((gfloat)i * dy / (height - 1) + (dy >= 0 ? 1.0 : -1.0) * 0.5);
1698 new_value &= xmnote_mask;
1699 new_value |= (start_char & ~xmnote_mask);
1700 if (xmnote_mask1) {
1701 gint new_value1;
1702
1703 new_value1 = start_value1 + (gint)((gfloat)i * dy1 / (height - 1) + (dy1 >= 0 ? 1.0 : -1.0) * 0.5);
1704 new_value1 &= xmnote_mask1;g_print("New: %i\n", new_value1);
1705 new_value = (new_value & ~xmnote_mask1) | new_value1;
1706 }
1707
1708 *((guint8*)(note_start + i) + xmnote_offset) = new_value;
1709 }
1710
1711 tracker_redraw(t);
1712 }
1713 }
1714
track_editor_interpolate_fx(GtkWidget * w,Tracker * t)1715 void track_editor_interpolate_fx(GtkWidget* w, Tracker* t)
1716 {
1717 track_editor_do_interpolate_fx(w, t, FALSE);
1718 }
1719
track_editor_interpolate_efx(GtkWidget * w,Tracker * t)1720 void track_editor_interpolate_efx(GtkWidget* w, Tracker* t)
1721 {
1722 track_editor_do_interpolate_fx(w, t, TRUE);
1723 }
1724
1725 static gint
char2hex(gint n,const gchar maxl,const gchar maxu)1726 char2hex(gint n, const gchar maxl, const gchar maxu)
1727 {
1728 if (n >= '0' && n <= '9')
1729 n -= '0';
1730 else {
1731 if (n >= 'a' && n <= maxl)
1732 n = n - 'a' + 10;
1733 else if (n >= 'A' && n <= maxu)
1734 n = n - 'A' + 10;
1735 else
1736 return -1;
1737 }
1738
1739 return n;
1740 }
1741
1742 static gboolean
track_editor_handle_semidec_column_input(Tracker * t,int expr,guint8 * modpt,int n)1743 track_editor_handle_semidec_column_input(Tracker* t,
1744 int expr,
1745 guint8* modpt,
1746 int n)
1747 {
1748 switch (expr) {
1749 case 0:
1750 if (n < '0' || n > '9')
1751 return FALSE;
1752 *modpt = (*modpt / 10) * 10 + n - '0';
1753 break;
1754 case 1:
1755 /* 128 instruments maximum, so only 'a', 'b' and 'c' are acceptable */
1756 n = char2hex(n, 'c', 'C');
1757 if (n == -1)
1758 return FALSE;
1759 *modpt = (*modpt % 10) + 10 * n;
1760 break;
1761 }
1762 if (*modpt > 128)
1763 *modpt = 128;
1764
1765 return TRUE;
1766 }
1767
1768 static void
track_editor_offset_cursor(Tracker * t,gint expr)1769 track_editor_offset_cursor(Tracker* t, gint expr)
1770 {
1771 gint step_row = 0;
1772
1773 if (!gui_settings.advance_cursor_in_fx_columns) {
1774 step_row = gui_get_current_jump_value();
1775 } else {
1776 if (expr) { /* not at the end */
1777 tracker_step_cursor_item(t, 1);
1778 } else {
1779 step_row = gui_get_current_jump_value();
1780 /* -2 in case of fx col */
1781 tracker_step_cursor_item(t, t->cursor_item == 7 ? -2 : -1);
1782 }
1783 }
1784 if (step_row)
1785 tracker_step_cursor_row(t, step_row);
1786 else
1787 tracker_redraw_current_row(t);
1788 }
1789
1790 static gboolean
track_editor_handle_hex_column_input(Tracker * t,gint expr,guint8 * modpt,gint n)1791 track_editor_handle_hex_column_input(Tracker* t,
1792 gint expr,
1793 guint8* modpt,
1794 gint n)
1795 {
1796 gint s, rest;
1797
1798 if (gui_settings.tracker_ft2_volume) {
1799 if (gui_settings.tracker_vol_dec) {
1800 switch (t->cursor_item) {
1801 case 3:
1802 if (n < '0' || n > '6')
1803 /* This is not volume */
1804 break;
1805 /* Then falling through */
1806 case 4:
1807 s = *modpt;
1808 if (s > 0x40 && t->cursor_item == 4)
1809 /* The first byte refers to an FX rather than volume */
1810 break;
1811 if (n < '0' || n > '9')
1812 return FALSE;
1813
1814 n -= '0';
1815 /* Exp can be only 0 or 1 here */
1816 if (expr && s > 0x40) {
1817 /* FX is converted to volume, we keep the parameter as is,
1818 possibly limiting it to the value of 9 */
1819 rest = s & 0xf;
1820 rest = rest > 9 ? 9 : rest;
1821 } else
1822 rest = s % 10;
1823 s = expr ? rest + n * 10 : s - rest + n;
1824 if (s > 64)
1825 s = 64;
1826 *modpt = s;
1827
1828 return TRUE;
1829 default:
1830 break;
1831 }
1832 }
1833 /* Convert '-' symbol to '6' FX */
1834 if (n == '-' && t->cursor_item == 3)
1835 n = '6';
1836 }
1837
1838 n = char2hex(n, 'f', 'F');
1839 if (n == -1)
1840 return FALSE;
1841
1842 s = *modpt;
1843 if (gui_settings.tracker_ft2_volume &&
1844 gui_settings.tracker_vol_dec &&
1845 t->cursor_item == 3 && s <= 0x40) {
1846 /* Trasfer volume -> FX, preserving the lower digit */
1847 rest = s % 10;
1848 s = n << 4 | rest;
1849 } else {
1850 expr *= 4;
1851 s &= 0xf0 >> expr;
1852 s |= n << expr;
1853 }
1854 *modpt = s;
1855
1856 return TRUE;
1857 }
1858
1859 static gboolean
track_editor_handle_column_input(Tracker * t,int gdkkey)1860 track_editor_handle_column_input(Tracker* t,
1861 int gdkkey)
1862 {
1863 static XMPattern* fxcp = NULL;
1864 static gint fxch = -1, fxpatpos = -1;
1865 gboolean handled = FALSE;
1866 gint n;
1867 guint8 tmp_value;
1868 guchar volume;
1869 XMNote* note = &t->curpattern->channels[t->cursor_ch][t->patpos];
1870 const gboolean ft2_volume = gui_settings.tracker_ft2_volume;
1871
1872 if (t->cursor_item == 5) {
1873 gint jump = 0;
1874
1875 /* Effect column (not the parameter) */
1876 switch (gdkkey) {
1877 case '0' ... '9':
1878 n = gdkkey - '0';
1879 break;
1880 case 'a' ... 'z':
1881 case 'A' ... 'Z':
1882 gdkkey = tolower(gdkkey);
1883 n = gdkkey - 'a' + 10;
1884 break;
1885 default:
1886 return FALSE;
1887 }
1888 if (fxcp != t->curpattern || fxch != t->cursor_ch || fxpatpos != t->patpos) {
1889 fxcp = t->curpattern;
1890 fxch = t->cursor_ch;
1891 fxpatpos = t->patpos;
1892
1893 track_editor_log_note(t, N_("Effect changing"),
1894 -1, t->cursor_ch, t->patpos, HISTORY_FLAG_LOG_PAGE);
1895 }
1896 note->fxtype = n;
1897 if (!gui_settings.advance_cursor_in_fx_columns)
1898 jump = gui_get_current_jump_value();
1899 else
1900 tracker_step_cursor_item(t, 1);
1901
1902 if (jump)
1903 tracker_step_cursor_row(t, jump);
1904 else
1905 tracker_redraw_current_row(t);
1906
1907 return TRUE;
1908 }
1909
1910 switch (t->cursor_item) {
1911 case 1:
1912 case 2: /* instrument column */
1913 tmp_value = note->instrument;
1914 if (track_editor_handle_semidec_column_input(t, 2 - t->cursor_item, &tmp_value, gdkkey)) {
1915 static XMPattern* cp = NULL;
1916 static gint ch = -1, patpos = -1;
1917
1918 if (cp != t->curpattern || ch != t->cursor_ch || patpos != t->patpos) {
1919 cp = t->curpattern;
1920 ch = t->cursor_ch;
1921 patpos = t->patpos;
1922
1923 track_editor_log_note(t, N_("Instrument changing"),
1924 -1, t->cursor_ch, t->patpos, HISTORY_FLAG_LOG_PAGE);
1925 }
1926 note->instrument = tmp_value;
1927 track_editor_offset_cursor(t, 2 - t->cursor_item);
1928 handled = TRUE;
1929 }
1930 break;
1931 case 3:
1932 if (ft2_volume) {
1933 /* Convert some keybingings to emulate FT2 */
1934 switch (gdkkey) {
1935 /* '-' -> '6' is a special case, the conversion is performed
1936 in track_editor_handle_hex_column_input() */
1937 case '+':
1938 gdkkey = '7';
1939 break;
1940 case 'd':
1941 case 'D':
1942 gdkkey = '8';
1943 break;
1944 case 'u':
1945 case 'U':
1946 gdkkey = '9';
1947 break;
1948 case 's':
1949 case 'S':
1950 gdkkey = 'a';
1951 break;
1952 case 'v':
1953 case 'V':
1954 gdkkey = 'b';
1955 break;
1956 case 'p':
1957 case 'P':
1958 gdkkey = 'c';
1959 break;
1960 case 'l':
1961 case 'L':
1962 case '<':
1963 gdkkey = 'd';
1964 break;
1965 case 'r':
1966 case 'R':
1967 case '>':
1968 gdkkey = 'e';
1969 break;
1970 case 'm':
1971 case 'M':
1972 gdkkey = 'f';
1973 break;
1974 default:
1975 break;
1976 }
1977 } /* Then falling through */
1978 case 4: /* volume column */
1979 volume = note->volume;
1980 /* The volume value is shifted by 0x10, so if we want to see the real value (FT2 mode),
1981 but store it correctly, we should subtract 0x10 before the modification, and add
1982 this value afterwards */
1983 if (ft2_volume && volume < 0x60 && volume >= 0x10)
1984 volume -= 0x10;
1985 if (track_editor_handle_hex_column_input(t, 4 - t->cursor_item, &volume, gdkkey)) {
1986 if (ft2_volume && volume < 0x50)
1987 volume += 0x10;
1988 /* Avoiding unused volume ranges */
1989 if (volume > 0x50 && volume < 0x60)
1990 volume = 0x50;
1991 if (volume >= 0x10) {
1992 static XMPattern* cp = NULL;
1993 static gint ch = -1, patpos = -1;
1994
1995 if (cp != t->curpattern || ch != t->cursor_ch || patpos != t->patpos) {
1996 cp = t->curpattern;
1997 ch = t->cursor_ch;
1998 patpos = t->patpos;
1999
2000 track_editor_log_note(t, N_("Volume / Vol. FX changing"),
2001 -1, t->cursor_ch, t->patpos, HISTORY_FLAG_LOG_PAGE);
2002 }
2003 note->volume = volume;
2004 track_editor_offset_cursor(t, 4 - t->cursor_item);
2005 }
2006 handled = TRUE;
2007 }
2008 break;
2009 case 6:
2010 case 7: /* effect parameter */
2011 tmp_value = note->fxparam;
2012 if (track_editor_handle_hex_column_input(t, 7 - t->cursor_item, &tmp_value, gdkkey)) {
2013 if (fxcp != t->curpattern || fxch != t->cursor_ch || fxpatpos != t->patpos) {
2014 fxcp = t->curpattern;
2015 fxch = t->cursor_ch;
2016 fxpatpos = t->patpos;
2017
2018 track_editor_log_note(t, N_("Effect changing"),
2019 -1, t->cursor_ch, t->patpos, HISTORY_FLAG_LOG_PAGE);
2020 }
2021 note->fxparam = tmp_value;
2022 track_editor_offset_cursor(t, 7 - t->cursor_item);
2023 handled = TRUE;
2024 }
2025 break;
2026 default:
2027 break;
2028 }
2029
2030 return handled;
2031 }
2032
2033 static void
tracker_timeout_foreach(gpointer data,gpointer user_data)2034 tracker_timeout_foreach(gpointer data,
2035 gpointer user_data)
2036 {
2037 /* Walking through all events from player because one of them
2038 can be the stop command */
2039 if (data) {
2040 audio_player_pos* p = data;
2041
2042 if (p->command == AUDIO_COMMAND_STOP_PLAYING)
2043 gui_play_stop();
2044 else {
2045 cur_tick = p->curtick;
2046 curtime = p->time;
2047 cur_pos = p->patpos;
2048 cur_pat = p->songpos;
2049 cur_tempo = p->tempo;
2050 prev_tempo = p->prev_tempo;
2051 prevtime = p->prev_tick_time;
2052 nexttime = p->prev_tick_time;
2053 patno = p->patno;
2054 bpm = p->bpm;
2055 *((gboolean *)user_data) = TRUE;
2056 }
2057 }
2058 }
2059
2060 static gint
tracker_timeout(gpointer data)2061 tracker_timeout(gpointer data)
2062 {
2063 g_debug("tracker_timeout()");
2064
2065 double display_songtime;
2066 gboolean new_event = TRUE;
2067
2068 if (current_driver_object == NULL) {
2069 /* Can happen when audio thread stops on its own. Note that
2070 * tracker_stop_updating() is called in
2071 * gui.c::read_mixer_pipe(). */
2072 return TRUE;
2073 }
2074
2075 display_songtime = current_driver->get_play_time(current_driver_object);
2076 g_debug("tracker_timeout() songtime=%lf", display_songtime);
2077
2078 if (display_songtime < 0.0) {
2079 gui_clipping_indicator_update(FALSE);
2080 } else {
2081 audio_clipping_indicator* c =
2082 time_buffer_get(audio_clipping_indicator_tb, display_songtime);
2083 if (c) {
2084 gui_clipping_indicator_update(c->clipping);
2085 g_free(c);
2086 }
2087 }
2088
2089 time_buffer_foreach(audio_playerpos_tb, display_songtime, tracker_timeout_foreach, &new_event);
2090 if (new_event)
2091 gui_update_player_pos(curtime, cur_pat, patno, cur_pos, cur_tempo, bpm);
2092
2093 return TRUE;
2094 }
2095
tracker_start_updating(void)2096 void tracker_start_updating(void)
2097 {
2098 g_debug("tracker_start_updating()");
2099
2100 if (gtktimer != -1)
2101 return;
2102
2103 gtktimer = g_timeout_add(1000 / update_freq, tracker_timeout, NULL);
2104 }
2105
tracker_stop_updating(void)2106 void tracker_stop_updating(void)
2107 {
2108 g_debug("tracker_stop_updating()");
2109
2110 if (gtktimer == -1)
2111 return;
2112
2113 g_source_remove(gtktimer);
2114 gtktimer = -1;
2115 }
2116
tracker_set_update_freq(int freq)2117 void tracker_set_update_freq(int freq)
2118 {
2119 update_freq = freq;
2120 if (gtktimer != -1) {
2121 tracker_stop_updating();
2122 tracker_start_updating();
2123 }
2124 }
2125
track_editor_load_config(void)2126 void track_editor_load_config(void)
2127 {
2128 gboolean* buf;
2129 int i;
2130
2131 buf = prefs_get_bool_array("settings", "jazz-toggle", NULL);
2132 if (buf) {
2133 for (i = 0; i < 32; i++) {
2134 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(jazztoggles[i]), buf[i]);
2135 }
2136
2137 g_free(buf);
2138 }
2139 }
2140
track_editor_save_config(void)2141 void track_editor_save_config(void)
2142 {
2143 gboolean buf[32];
2144 guint i;
2145
2146 if (gui_settings.save_settings_on_exit) {
2147 trackersettings_write_settings();
2148 }
2149
2150 for (i = 0; i < 32; i++)
2151 buf[i] = GTK_TOGGLE_BUTTON(jazztoggles[i])->active;
2152 prefs_put_bool_array("settings", "jazz-toggle", buf, 32);
2153 }
2154
track_editor_toggle_permanentness(Tracker * t,gboolean all)2155 void track_editor_toggle_permanentness(Tracker* t, gboolean all)
2156 {
2157 int i = t->cursor_ch;
2158
2159 if (!all)
2160 gui_settings.permanent_channels ^= 1 << i;
2161 else
2162 gui_settings.permanent_channels = gui_settings.permanent_channels ? 0 : 0xFFFFFFFF;
2163
2164 tracker_redraw(t);
2165 }
2166