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*)(&note_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*)&note_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