Timing is correct now! Forgot to update samples_per_tick when parsing the 0xf effect.
[pineappletracker.git] / modes.c
blob0ac956ca0805726eb10de6353b28aa09ee2c7ca5
1 /* vi:set syntax= ts=8 sts=8 sw=8 noexpandtab: */
2 /* WARNING: this file is ROSS STYLE */
4 #include "pineapple.h"
5 #include "gui.h"
6 #include "filetypes.h"
7 #include "hvl_replay.h"
9 #include <curses.h>
10 #include <ctype.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
15 int f;
16 int tcliplen, icliplen = 0;
17 int lastinsert = 0;
19 int hexdigit(char c){
20 if(c >= '0' && c <= '9') return c - '0';
21 if(c >= 'a' && c <= 'f') return c - 'a' + 10;
22 return -1;
25 int nextfreetrack(){
26 int skiptherest = 0;
28 for(int i = 1; i <= 0xff; i++){
29 for(int j = 0; j < tune->tracklen; j++){
30 if(tune->trk[i].line[j].note) skiptherest = 1;
31 for(int k = 0; k < 2; k++){
32 if(tune->trk[i].line[j].cmd[k]) skiptherest = 1;
33 if(tune->trk[i].line[j].param[k]) skiptherest = 1;
36 // skip the rest of this track?
37 if(skiptherest){
38 skiptherest = 0;
39 break;
42 // this track is free, so return the index
43 if(j == (tune->tracklen)-1) return i;
47 setdisplay("nextfreetrack() failed somehow..");
48 return -1;
51 int nextfreeinstr(){
52 for(int i = 1; i <= 0xff; i++){
53 if(instrument[i].line[0].cmd == '0')
54 return i;
57 setdisplay("nextfreeinstr() failed somehow..");
58 return -1;
61 void _insertc(int c){
62 int x;
64 x = hexdigit(c);
65 if(x >= 0){
66 if(currtab == 2
67 && instrx > 0
68 && instrument[currinstr].line[instry].cmd != '+'
69 && instrument[currinstr].line[instry].cmd != '='){
70 switch(instrx){
71 case 1: SETHI(instrument[currinstr].line[instry].param, x); break;
72 case 2: SETLO(instrument[currinstr].line[instry].param, x); break;
75 if(currtab == 1 && trackx > 1){
76 switch(trackx){
77 case 2: SETHI(tune->trk[currtrack].line[tracky].instr, x); break;
78 case 3: SETLO(tune->trk[currtrack].line[tracky].instr, x); break;
79 case 5: if(tune->trk[currtrack].line[tracky].cmd[0])
80 SETHI(tune->trk[currtrack].line[tracky].param[0], x); break;
81 case 6: if(tune->trk[currtrack].line[tracky].cmd[0])
82 SETLO(tune->trk[currtrack].line[tracky].param[0], x); break;
83 case 8: if(tune->trk[currtrack].line[tracky].cmd[1])
84 SETHI(tune->trk[currtrack].line[tracky].param[1], x); break;
85 case 9: if(tune->trk[currtrack].line[tracky].cmd[1])
86 SETLO(tune->trk[currtrack].line[tracky].param[1], x); break;
89 if(currtab == 0){
90 switch(songx & 3){
91 case 0: SETHI(tune->sng[songy].track[songx / 4], x); break;
92 case 1: SETLO(tune->sng[songy].track[songx / 4], x); break;
93 case 2: SETHI(tune->sng[songy].transp[songx / 4], x); break;
94 case 3: SETLO(tune->sng[songy].transp[songx / 4], x); break;
98 x = freqkey(c);
99 if(x >= 0){
100 if(currtab == 2
101 && instrx
102 && (instrument[currinstr].line[instry].cmd == '+' || instrument[currinstr].line[instry].cmd == '=')){
103 instrument[currinstr].line[instry].param = x;
105 if(currtab == 1 && !trackx){
106 tune->trk[currtrack].line[tracky].note = x;
107 //htTune->ht_Tracks[currtrack][tracky].stp_Note = x;
108 if(x){
109 tune->trk[currtrack].line[tracky].instr = currinstr;
110 //htTune->ht_Tracks[currtrack][tracky].stp_Instrument = currinstr;
111 }else{
112 tune->trk[currtrack].line[tracky].instr = 0;
114 if(x) {
115 //shouldn't have to use this if statement...grr...
116 if(tune->type == LFT) tune->iedplonk(x, currinstr, tune);
117 //if(tune->type == AHX) tune->iedplonk(x, currinstr, htTune);
118 if(tune->type == AHX){
119 tune->plonked = 1;
120 tune->currnote = x;
123 //if(x){
124 //hvl_playNote(htTune, (int8 *) hivelyLeft, (int8 *) hivelyRight, 2, &htTune->ht_Voices[0]);
125 //htTune->curNote = x;
129 if(currtab == 2 && instrx == 0){
130 if(strchr(validcmds, c))
131 instrument[currinstr].line[instry].cmd = c;
133 if(currtab == 1 && (trackx == 4 || trackx == 7)){
134 if(strchr(validcmds, c)){
135 if(c == '.' || c == '0') c = 0;
136 tune->trk[currtrack].line[tracky].cmd[(trackx - 3) / 3] = c;
139 // for repeat
140 lastinsert = c;
143 // _isnumber() determines if a string is a number
144 // returns 1 if the given arg is a number, 0 otherwise.
145 // for use with isdigit(), isxdigit(), etc...
146 int _isnumber(const char* str, int (*func) (int)){
147 int i = 0;
149 for(;;){
150 if( func(str[i]) ){
151 i++;
152 }else if( str[i]=='\0' ){
153 return 1;
154 }else{
155 return 0;
160 // Converts a hexadecimal string to integer
161 // return values:
162 // 0: conversion successful
163 // 1: string is empty
164 // 2: string has more than 8 bytes
165 // 4: Conversion is in process but abnormally terminated by
166 // illegal hexadecimal character
167 // from http://devpinoy.org/blogs/cvega/archive/2006/06/19/xtoi-hex-to-integer-c-function.aspx
168 int xtoi(const char* xs, unsigned int* result){
169 size_t szlen = strlen(xs);
170 int i, xv, fact;
172 if(szlen > 0){
173 // Converting more than 32bit hexadecimal value?
174 if (szlen>8) return 2; // exit
176 // Begin conversion here
177 *result = 0;
178 fact = 1;
180 // Run until no more character to convert
181 for(i=szlen-1; i>=0 ;i--){
182 if(isxdigit(*(xs+i))){
183 if (*(xs+i)>=97){
184 xv = ( *(xs+i) - 97) + 10;
185 }else if( *(xs+i) >= 65){
186 xv = (*(xs+i) - 65) + 10;
187 }else{
188 xv = *(xs+i) - 48;
190 *result += (xv * fact);
191 fact *= 16;
192 }else{
193 // Conversion was abnormally terminated
194 // by non hexadecimal digit, hence
195 // returning only the converted with
196 // an error value 4 (illegal hex character)
197 return 4;
201 // Nothing to convert
202 return 1;
205 void _parsecmd(char cmd[]){
206 //if(cmd[1] == 'w'){
207 //switch(strcmp(cmd,
208 if(strcmp(cmd, ":w") == 0){
209 lft_savefile(filename);
210 saved = 1;
211 }else if(strcmp(cmd, ":q") == 0){
212 if(!saved){
213 setdisplay("no write since last change! use :q! to override");
214 }else{
215 erase();
216 refresh();
217 endwin();
218 exit(0);
220 }else if(strcmp(cmd, ":q!") == 0){
221 erase();
222 refresh();
223 endwin();
224 exit(0);
225 }else if(strcmp(cmd, ":write") == 0){
226 lft_savefile(filename);
227 saved = 1;
228 }else if(strcmp(cmd, ":wq") == 0 || strcmp(cmd, ":x") == 0){
229 lft_savefile(filename);
230 saved = 1;
231 erase();
232 refresh();
233 endwin();
234 exit(0);
235 }else if(strcmp(cmd, ":quit") == 0){
236 erase();
237 refresh();
238 endwin();
239 exit(0);
240 }else if(cmd[1]=='e' && cmd[2]==' '){
241 // if the file doesn't exist, clear the song
242 if(lft_loadfile(cmd+3)){
243 initsonglines();
244 inittracks();
245 initinstrs();
247 //yucky if statement below.....probably better way to do it
248 // maybe this is better??
249 }else if(!strncmp(cmd+1,"save ",5)){
250 lft_saveinstrument(cmd+6);
251 setdisplay("d-_-b saved ins! d-_-b");
252 }else if(!strncmp(cmd+1,"load ",5)){
253 lft_loadinstrument(cmd+6);
254 setdisplay("d-_-b loaded ins! d-_-b");
255 }else if( _isnumber((char *)cmd+1,isxdigit) ){
256 unsigned int goton = 1;
257 xtoi(cmd+1,&goton);
259 switch(currtab){
260 case 0:
261 songy = (goton>tune->songlen)? tune->songlen-1 : goton;
262 break;
263 case 1:
264 currtrack = (goton>0xff)? 0xff : goton;
265 break;
266 case 2:
267 currinstr = (goton>0xff)? 0xff : goton;
268 break;
270 }else if(cmd[1] == 'c' && cmd[2] == ' '){
271 strncpy(comment, cmd+3, sizeof(comment));
272 }else
273 setdisplay("not a tracker command!");
274 return;
277 /* normal mode */
278 void normalmode(int c){
279 int i;
281 // don't save the action for repeat if it's a movement or a repeat, or
282 // something else that doesnt make sense to repeat
283 if(c != 'h' &&
284 c != 'j' &&
285 c != 'k' &&
286 c != 'l' &&
287 c != CTRL('D') &&
288 c != CTRL('U') &&
289 c != CTRL('H') &&
290 c != CTRL('L') &&
291 c != 'H' &&
292 c != 'M' &&
293 c != 'L' &&
294 c != 'g' &&
295 c != 'G' &&
296 c != '.'){
297 lastaction = c;
298 lastrepeatnum = cmdrepeatnum;
301 for(i=0; i<cmdrepeatnum; i++){
302 switch(c){
303 /* add line */
304 case 'a':
305 act_addline();
306 break;
307 case '.':
308 // if the last command was a replace, just insert the last thing
309 // inserted instead of calling insertmode()
310 if(lastaction == 'r')
311 _insertc(lastinsert);
312 else
313 normalmode(lastaction);
314 cmdrepeatnum = lastrepeatnum;
315 break;
316 case KEY_ESCAPE:
317 disptick = 0;
318 jammermode();
319 break;
320 case CTRL('Y'):
321 switch(currtab){
322 case 0:
323 if(songoffs>0){
324 if(songy==getmaxy(stdscr)-3+songoffs)
325 songy--;
326 songoffs--;
328 break;
329 case 1:
330 if(trackoffs>0){
331 if(tracky==getmaxy(stdscr)-3+trackoffs)
332 tracky--;
333 trackoffs--;
335 break;
336 case 2:
337 if(instroffs>0){
338 if(instry==getmaxy(stdscr)-3+instroffs)
339 instry--;
340 instroffs--;
342 break;
344 break;
345 case CTRL('E'):
346 switch(currtab){
347 case 0:
348 if(songy<=tune->songlen-2){
349 if(songy==songoffs)
350 songy++;
351 songoffs++;
353 break;
354 case 1:
355 if(tracky<=(tune->tracklen)-2){
356 if(tracky==trackoffs)
357 tracky++;
358 trackoffs++;
360 break;
361 case 2:
362 if(instry<=instrument[currinstr].length-2){
363 if(instry==instroffs)
364 instry++;
365 instroffs++;
367 break;
369 break;
370 case 'H':
371 switch(currtab){
372 case 0:
373 songy = songoffs;
374 break;
375 case 1:
376 tracky = trackoffs;
377 break;
378 case 2:
379 instry = instroffs;
380 break;
382 break;
384 // the second cases (to the right of the colon) for M and L
385 // took some serious guesswork, so I'm not sure if they're
386 // correct but they seem to work.
387 case 'M':
388 switch(currtab){
389 case 0:
390 songy = (tune->songlen <= getmaxy(stdscr)-2)?
391 tune->songlen/2
392 : ((getmaxy(stdscr)-6)/2) + songoffs;
393 break;
394 case 1:
395 tracky = (tune->tracklen <= getmaxy(stdscr)-2)?
396 tune->tracklen/2
397 : ((getmaxy(stdscr)-6)/2) + trackoffs;
398 break;
399 case 2:
400 instry = (instrument[currinstr].length <= getmaxy(stdscr)-2)?
401 instrument[currinstr].length/2
402 : ((getmaxy(stdscr)-6)/2) + instroffs;
403 break;
405 break;
406 case 'L':
407 switch(currtab){
408 case 0:
409 songy = (tune->songlen <= getmaxy(stdscr)-2)?
410 tune->songlen-1
411 : getmaxy(stdscr)-3+songoffs;
412 break;
413 case 1:
414 tracky = (tune->tracklen <= getmaxy(stdscr)-2)?
415 tune->tracklen-1
416 : getmaxy(stdscr)-3+trackoffs;
417 break;
418 case 2:
419 instry = (instrument[currinstr].length <= getmaxy(stdscr)-2)?
420 instrument[currinstr].length-1
421 : getmaxy(stdscr)-3+instroffs;
422 break;
424 break;
425 case 'g':
426 if(nextchar() == 'g'){
427 act_mvtop();
429 break;
430 case 'G':
431 act_mvbottom();
432 break;
434 // yank
435 case 'y':
436 c = nextchar();
437 switch(c){
438 case 'y':
439 //tclip = malloc(1);
440 if(currtab == 0){
441 tcliplen = 1;
442 memcpy(&tclip, &tune->sng[songy], sizeof(struct songline));
443 }else if(currtab == 1){
444 tcliplen = 1;
445 memcpy(&tclip, &tune->trk[currtrack].line[tracky], sizeof(struct trackline));
446 }else if(currtab == 2){
447 icliplen = 1;
448 memcpy(&iclip, &instrument[currinstr].line[instry], sizeof(struct instrline));
450 break;
451 case 'j':
452 //tclip = malloc(2);
453 if(currtab == 0){
454 tcliplen = 2;
455 memcpy(&tclip[0], &tune->sng[songy], sizeof(struct songline));
456 act_mvdown();
457 memcpy(&tclip[1], &tune->sng[songy], sizeof(struct songline));
458 }else if(currtab == 1){
459 tcliplen = 2;
460 memcpy(&tclip[0], &tune->trk[currtrack].line[tracky], sizeof(struct trackline));
461 act_mvdown();
462 memcpy(&tclip[1], &tune->trk[currtrack].line[tracky], sizeof(struct trackline));
463 }else if(currtab == 2){
464 icliplen = 2;
465 memcpy(&iclip[0], &instrument[currinstr].line[instry], sizeof(struct instrline));
466 act_mvdown();
467 memcpy(&iclip[1], &instrument[currinstr].line[instry], sizeof(struct instrline));
469 break;
470 case 'k':
471 //tclip = malloc(2);
472 if(currtab == 0){
473 tcliplen = 2;
474 memcpy(&tclip[1], &tune->sng[songy], sizeof(struct songline));
475 act_mvup();
476 memcpy(&tclip[0], &tune->sng[songy], sizeof(struct songline));
477 }else if(currtab == 1){
478 tcliplen = 2;
479 memcpy(&tclip[1], &tune->trk[currtrack].line[tracky], sizeof(struct trackline));
480 act_mvup();
481 memcpy(&tclip[0], &tune->trk[currtrack].line[tracky], sizeof(struct trackline));
482 }else if(currtab == 2){
483 icliplen = 2;
484 memcpy(&iclip[1], &instrument[currinstr].line[instry], sizeof(struct instrline));
485 act_mvup();
486 memcpy(&iclip[0], &instrument[currinstr].line[instry], sizeof(struct instrline));
488 break;
490 break;
492 //paste
493 case 'p':
494 if(currtab == 0){
495 if(tune->songlen < 256){
496 for(int i = 0; i < tcliplen; i++){
497 // insert new line
498 memmove(&tune->sng[songy + 2], &tune->sng[songy + 1], sizeof(struct songline) * (tune->songlen - songy - 1));
499 songy++;
500 tune->songlen++;
501 memset(&tune->sng[songy], 0, sizeof(struct songline));
503 // paste to new line
504 memcpy(&tune->sng[songy], &tclip[i], sizeof(struct songline));
507 }else if(currtab == 1){
508 for(int i = 0; i < tcliplen; i++){
509 memcpy(&tune->trk[currtrack].line[tracky], &tclip[i], sizeof(struct trackline));
510 if(tracky < (tune->tracklen)-step) tracky += step;
511 else tracky = (tune->tracklen)-1;
513 }else if(currtab == 2){
514 if(instrument[currinstr].length < 256){
515 // insert new line
516 for(int i = 0; i < icliplen; i++){
517 struct instrument *in = &instrument[currinstr];
519 instry++;
520 memmove(&in->line[instry + 1], &in->line[instry + 0], sizeof(struct instrline) * (in->length - instry));
521 in->length++;
522 in->line[instry].cmd = '0';
523 in->line[instry].param = 0;
525 // paste to new line
526 memcpy(&instrument[currinstr].line[instry], &iclip[i], sizeof(struct instrline));
529 //if(instry < instrument[currinstr].length-1) instry++;
531 break;
533 // copy everything in the current phrase or instrument into the next free one
534 case '^':
535 if(currtab == 1){
536 f = nextfreetrack();
537 memcpy(&tune->trk[f], &tune->trk[currtrack], sizeof(struct track));
538 currtrack = f;
539 }else if(currtab == 2){
540 f = nextfreeinstr();
541 memcpy(&instrument[f], &instrument[currinstr], sizeof(struct instrument));
542 currinstr = f;
544 break;
546 // TODO: Y and P can be removed after we make visual mode
547 // copy whole phrase or instrument
548 case 'Y':
549 if(currtab == 1){
550 memcpy(&tclip, &tune->trk[currtrack], sizeof(struct track));
551 }else if(currtab == 2){
552 memcpy(&iclip, &instrument[currinstr], sizeof(struct instrument));
554 break;
555 // paste whole phrase or instrument
556 case 'P':
557 if(currtab == 1){
558 memcpy(&tune->trk[currtrack], &tclip, sizeof(struct track));
559 }else if(currtab == 2){
560 memcpy(&instrument[currinstr], &iclip, sizeof(struct instrument));
562 break;
564 /* delete line */
565 // TODO: clean this SHIT up
566 // TODO: add an ACT_ function for delete
567 case 'd':
568 c = nextchar();
569 switch(c){
570 case 'd':
571 act_delline();
572 break;
573 case 'k':
574 if(currtab == 2){
575 struct instrument *in = &instrument[currinstr];
576 instry--;
577 int i;
578 for(i=0; i<2; i++){
579 if(in->length > 1){
580 memmove(&in->line[instry + 0], &in->line[instry + 1], sizeof(struct instrline) * (in->length - instry - 1));
581 in->length--;
582 if(instry >= in->length) instry = in->length - 1;
585 }else if(currtab == 0){
586 songy--;
587 int i;
588 for(i=0; i<2; i++){
589 if(tune->songlen > 1){
590 memmove(&tune->sng[songy + 0], &tune->sng[songy + 1], sizeof(struct songline) * (tune->songlen - songy - 1));
591 tune->songlen--;
592 if(songy >= tune->songlen) songy = tune->songlen - 1;
596 break;
597 case 'j':
598 if(currtab == 2){
599 struct instrument *in = &instrument[currinstr];
601 int i;
602 for(i=0; i<2; i++){
603 if(in->length > 1){
604 memmove(&in->line[instry + 0], &in->line[instry + 1], sizeof(struct instrline) * (in->length - instry - 1));
605 in->length--;
606 if(instry >= in->length) instry = in->length - 1;
609 }else if(currtab == 0){
610 int i;
611 for(i=0; i<2; i++){
612 if(tune->songlen > 1){
613 memmove(&tune->sng[songy + 0], &tune->sng[songy + 1], sizeof(struct songline) * (tune->songlen - songy - 1));
614 tune->songlen--;
615 if(songy >= tune->songlen) songy = tune->songlen - 1;
619 break;
621 break;
622 /* undo */
623 case 'u':
624 act_undo();
625 /* Clear */
626 case 'x':
627 act_clronething();
628 break;
629 case 'X':
630 act_clritall();
631 break;
632 case ENTER:
633 if(currtab != 2){
634 if(currtab == 1){
635 silence();
636 startplaytrack(currtrack);
637 }else if(currtab == 0){
638 silence();
639 startplaysong(songy);
642 break;
643 case 'Z':
644 c = nextchar();
645 switch(c){
646 case 'Z':
647 lft_savefile(filename);
648 erase();
649 refresh();
650 endwin();
651 exit(0);
652 break;
653 case 'Q':
654 erase();
655 refresh();
656 endwin();
657 exit(0);
658 break;
660 break;
661 /* Enter command mode */
662 case ':':
663 cmdlinemode();
664 break;
665 case ' ':
666 silence();
667 tune->plonked = 0;
668 break;
669 // TODO: make an act_ function for '`'
670 case '`':
671 if(currtab == 0){
672 int t = tune->sng[songy].track[songx / 4];
673 if(t) currtrack = t;
674 currtab = 1;
675 if(playtrack){
676 startplaytrack(currtrack);
678 }else if((currtab == 1) && ((trackx == 2) || (trackx == 3))){
679 int i = tune->trk[currtrack].line[tracky].instr;
680 if(i) currinstr = i;
681 currtab = 2;
682 } else if(currtab == 1){
683 currtab = 0;
684 }else if(currtab == 2){
685 currtab = 1;
687 break;
688 /* Enter insert mode */
689 case 'i':
690 insertmode();
691 break;
692 /* Enter visual mode */
693 case 'v':
694 visualmode();
695 break;
696 /* Enter visual line mode */
697 case 'V':
698 visuallinemode();
699 break;
700 /* enter jammer mode */
701 case CTRL('A'):
702 jammermode();
703 break;
704 /* Add new line and enter insert mode */
705 case 'o':
706 act_addline();
707 insertmode();
708 break;
709 case 'h':
710 case KEY_LEFT:
711 act_mvleft();
712 break;
713 case 'j':
714 case KEY_DOWN:
715 act_mvdown();
716 break;
717 case 'k':
718 case KEY_UP:
719 act_mvup();
720 break;
721 case 'l':
722 case KEY_RIGHT:
723 act_mvright();
724 break;
725 case '<':
726 if(octave) octave--;
727 break;
728 case '>':
729 if(octave < 8) octave++;
730 break;
731 case '{':
732 if(currtrack > 1) currtrack--;
733 break;
734 case '}':
735 if(currtrack < 255) currtrack++;
736 break;
737 case 'J':
738 if(currtab == 0){
739 if( (songx%4) < 2){
740 act_trackdec();
741 }else{
742 act_transpdec();
744 }else if(currtab == 1){
745 switch(trackx){
746 case 0:
747 act_notedec();
748 break;
749 case 1:
750 act_octavedec();
751 break;
752 case 2:
753 act_instrdec();
754 break;
755 case 3:
756 act_instrdec();
757 break;
758 case 4:
759 act_fxdec();
760 break;
761 case 5:
762 case 6:
763 act_paramdec();
764 break;
765 case 7:
766 act_fxdec();
767 break;
768 case 8:
769 case 9:
770 act_paramdec();
771 break;
772 default:
773 setdisplay("in J");
774 break;
776 }else if(currtab == 2){
777 switch(instrx){
778 case 0:
779 act_fxdec();
780 break;
781 case 1:
782 if(instrument[currinstr].line[instry].cmd == '+' || instrument[currinstr].line[instry].cmd == '='){
783 act_notedec();
784 }else{
785 act_paramdec();
787 break;
788 case 2:
789 if(instrument[currinstr].line[instry].cmd == '+' || instrument[currinstr].line[instry].cmd == '='){
790 act_notedec();
791 }else{
792 act_paramdec();
794 break;
797 break;
798 case 'K':
799 if(currtab == 0){
800 if( (songx%4) < 2){
801 act_trackinc();
802 }else{
803 act_transpinc();
805 }else if(currtab == 1){
806 switch(trackx){
807 case 0:
808 act_noteinc();
809 break;
810 case 1:
811 act_octaveinc();
812 break;
813 case 2:
814 act_instrinc();
815 break;
816 case 3:
817 act_instrinc();
818 break;
819 case 4:
820 act_fxinc();
821 break;
822 case 5:
823 case 6:
824 act_paraminc();
825 break;
826 case 7:
827 act_fxinc();
828 break;
829 case 8:
830 case 9:
831 act_paraminc();
832 break;
833 default:
834 setdisplay("in K");
835 break;
837 }else if(currtab == 2){
838 switch(instrx){
839 case 0:
840 act_fxinc();
841 break;
842 case 1:
843 if(instrument[currinstr].line[instry].cmd == '+' || instrument[currinstr].line[instry].cmd == '='){
844 act_noteinc();
845 }else{
846 act_paraminc();
848 break;
849 case 2:
850 if(instrument[currinstr].line[instry].cmd == '+' || instrument[currinstr].line[instry].cmd == '='){
851 act_noteinc();
852 }else{
853 act_paraminc();
855 break;
858 break;
859 case CTRL('J'):
860 if(currtab == 2){
861 act_viewinstrdec();
862 }else if(currtab == 1){
863 act_viewtrackdec();
865 break;
866 case CTRL('K'):
867 if(currtab == 2){
868 act_viewinstrinc();
869 }else if(currtab == 1){
870 act_viewtrackinc();
872 break;
873 case '[':
874 act_viewinstrdec();
875 break;
876 case ']':
877 act_viewinstrinc();
878 break;
879 case '(':
880 callbacktime++;
881 break;
882 case ')':
883 callbacktime--;
884 break;
885 case '-':
886 if(step > 0)
887 step--;
888 break;
889 case '=':
890 if(step < 0x0f)
891 step++;
892 break;
893 case CTRL('H'):
894 currtab--;
895 if(currtab < 0)
896 currtab = 2;
897 break;
898 case CTRL('L'):
899 currtab++;
900 currtab %= 3;
901 break;
902 case KEY_TAB:
903 currtab++;
904 currtab %= 3;
905 break;
906 case CTRL('U'):
907 act_bigmvup();
908 break;
909 case CTRL('D'):
910 act_bigmvdown();
911 break;
912 /*case CTRL('P'):
913 vimode = false;
914 break;*/
916 // replace
917 case 'r':
918 _insertc(nextchar());
919 break;
921 default:
922 break;
923 } // end switch
924 } // end for
925 cmdrepeatnum = 1;
926 cmdrepeat = 0;
929 /* vi cmdline mode */
930 void cmdlinemode(void){
931 u16 c;
932 int len;
933 keypad(stdscr, TRUE);
935 currmode = PM_CMDLINE;
936 strncat(cmdstr, ":", 100);
937 for(;;){
938 drawgui();
940 c = nextchar();
941 switch(c){
942 case KEY_ESCAPE:
943 //cmdstr = "";
944 currmode = PM_NORMAL;
945 goto end;
946 case ENTER:
947 _parsecmd(cmdstr);
948 goto end;
949 #ifndef WINDOWS
950 case BACKSPACE:
951 setdisplay("\\o/");
952 cmdstr[strlen(cmdstr)-1] = '\0';
953 break;
954 #endif
955 case '\t':
956 break;
957 default:
958 len = strlen(cmdstr);
959 cmdstr[len++] = c;
960 cmdstr[len++] = '\0';
961 break;
964 end:
965 strcpy(cmdstr, "");
966 keypad(stdscr, FALSE);
967 return;
970 /* vi insert mode */
971 void insertmode(void){
972 int c;
973 currmode = PM_INSERT;
974 drawgui();
975 for(;;){
976 if((c = getch()) != ERR) switch(c){
977 case KEY_ESCAPE:
978 currmode = PM_NORMAL;
979 return;
980 case 'h':
981 case KEY_LEFT:
982 act_mvleft();
983 break;
984 case 'j':
985 case KEY_DOWN:
986 act_mvdown();
987 break;
988 case 'k':
989 case KEY_UP:
990 act_mvup();
991 break;
992 case 'l':
993 case KEY_RIGHT:
994 act_mvright();
995 break;
996 /* change octave */
997 case '<':
998 if(octave) octave--;
999 break;
1000 case '>':
1001 if(octave < 8) octave++;
1002 break;
1003 /* change instrument */
1004 case CTRL('J'):
1005 if(currtab == 2){
1006 act_viewinstrdec();
1007 }else if(currtab == 1){
1008 act_viewtrackdec();
1010 break;
1011 case CTRL('K'):
1012 if(currtab == 2){
1013 act_viewinstrinc();
1014 }else if(currtab == 1){
1015 act_viewtrackinc();
1017 break;
1018 case '[':
1019 act_viewinstrdec();
1020 break;
1021 case ']':
1022 act_viewinstrinc();
1023 break;
1024 case CTRL('H'):
1025 currtab--;
1026 if(currtab < 0)
1027 currtab = 2;
1028 break;
1029 case CTRL('L'):
1030 currtab++;
1031 currtab %= 3;
1032 break;
1033 case 'Z':
1034 c = nextchar();
1035 switch(c){
1036 case 'Z':
1037 lft_savefile(filename);
1038 erase();
1039 refresh();
1040 endwin();
1041 exit(0);
1042 break;
1043 case 'Q':
1044 erase();
1045 refresh();
1046 endwin();
1047 exit(0);
1048 break;
1050 break;
1051 case ' ':
1052 silence();
1053 currmode = PM_NORMAL;
1054 return;
1055 case ENTER:
1056 if(currtab != 2){
1057 if(currtab == 1){
1058 silence();
1059 startplaytrack(currtrack);
1060 }else if(currtab == 0){
1061 silence();
1062 startplaysong(songy);
1065 break;
1066 case '`':
1067 if(currtab == 0){
1068 int t = tune->sng[songy].track[songx / 4];
1069 if(t) currtrack = t;
1070 currtab = 1;
1071 }else if(currtab == 1){
1072 currtab = 0;
1074 break;
1075 default:
1076 _insertc(c);
1077 if(currtab == 1){
1078 tracky+=step;
1079 tracky %= (tune->tracklen);
1080 }else if(currtab == 2){
1081 //if(instry < instrument[currinstr].length-1) instry++;
1082 if(instrx < 2) instrx++;
1083 else instrx--;
1084 instry %= instrument[currinstr].length;
1086 saved = 0;
1088 drawgui();
1089 usleep(10000);
1093 /* jammer mode */
1094 void jammermode(void){
1095 int c, x;
1096 currmode = PM_JAMMER;
1097 while(currmode == PM_JAMMER){
1098 if((c = getch()) != ERR) switch(c){
1099 case KEY_ESCAPE:
1100 currmode = PM_NORMAL;
1101 break;
1102 case '[':
1103 act_viewinstrdec();
1104 break;
1105 case ']':
1106 act_viewinstrinc();
1107 break;
1108 case '<':
1109 if(octave) octave--;
1110 break;
1111 case '>':
1112 if(octave < 8) octave++;
1113 break;
1114 default:
1115 x = freqkey(c);
1116 if(x) {
1117 //shouldn't have to use this if statement...grr...
1118 if(tune->type == LFT) tune->iedplonk(x, currinstr, tune);
1119 else if(tune->type == AHX) tune->iedplonk(x, currinstr, htTune);
1121 break;
1123 drawgui();
1124 usleep(10000);
1128 /* visual mode */
1129 void visualmode(void){
1130 int c;
1132 currmode = PM_VISUAL;
1133 //attrset(A_REVERSE);
1134 if(currtab == 0){
1135 }else if(currtab == 1){
1136 highlight_firstx = trackx;
1137 highlight_lastx = trackx;
1138 highlight_firsty = tracky;
1139 highlight_lasty = tracky;
1140 }else if(currtab == 2){
1141 }else{
1142 highlight_firstx = -1;
1143 highlight_lastx = -1;
1144 highlight_firsty = -1;
1145 highlight_lasty = -1;
1148 while(currmode == PM_VISUAL){
1149 if((c = getch()) != ERR) switch(c){
1150 case 'v':
1151 case KEY_ESCAPE:
1152 currmode = PM_NORMAL;
1153 break;
1154 case 'V':
1155 visuallinemode();
1156 break;
1157 case 'h':
1158 act_mvleft();
1159 if(currtab==0){
1160 }else if(currtab==1){
1161 highlight_lastx = trackx;
1162 }else if(currtab==2){
1164 break;
1165 case 'j':
1166 act_mvdown();
1167 if(currtab==0){
1168 }else if(currtab==1){
1169 highlight_lasty = tracky;
1170 }else if(currtab==2){
1172 break;
1173 case 'k':
1174 act_mvup();
1175 if(currtab==0){
1176 }else if(currtab==1){
1177 highlight_lasty = tracky;
1178 }else if(currtab==2){
1180 break;
1181 case 'l':
1182 act_mvright();
1183 if(currtab==0){
1184 }else if(currtab==1){
1185 highlight_lastx = trackx;
1186 }else if(currtab==2){
1188 break;
1190 drawgui();
1192 attrset(A_BOLD);
1193 return;
1196 /* visual line mode */
1197 void visuallinemode(void){
1198 int c;
1199 int min, max;
1200 char buf[1024];
1202 currmode = PM_VISUALLINE;
1204 /* Store the current line as the first and last node of a linked list */
1205 if(currtab==0){
1206 highlight_firstline = songy;
1207 highlight_lastline = songy;
1208 }else if(currtab==1){
1209 highlight_firstline = tracky;
1210 highlight_lastline = tracky;
1211 }else if(currtab==2){
1212 highlight_firstline = instry;
1213 highlight_lastline = instry;
1214 }else{
1215 highlight_firstline = -1;
1216 highlight_lastline = -1;
1219 // initialize difference
1220 highlight_lineamount = 1;
1222 // make it visible to gui.c
1223 //highlightlines = firstnode;
1225 while(currmode == PM_VISUALLINE){
1226 if((c = getch()) != ERR) switch(c){
1227 case 'V':
1228 case KEY_ESCAPE:
1229 currmode = PM_NORMAL;
1230 break;
1231 case 'v':
1232 visualmode();
1233 case 'h':
1234 act_mvleft();
1235 break;
1236 case 'j':
1237 act_mvdown();
1238 // update lastnode
1239 if(currtab==0){
1240 highlight_lastline = songy;
1241 }else if(currtab==1){
1242 highlight_lastline = tracky;
1243 }else if(currtab==2){
1244 highlight_lastline = instry;
1246 // update the highlighted length
1247 highlight_lineamount = (highlight_firstline>highlight_lastline)?
1248 highlight_firstline - highlight_lastline +1
1249 : highlight_lastline - highlight_firstline +1;
1250 break;
1251 case 'k':
1252 act_mvup();
1253 // update lastnode
1254 if(currtab==0){
1255 highlight_lastline = songy;
1256 }else if(currtab==1){
1257 highlight_lastline = tracky;
1258 }else if(currtab==2){
1259 highlight_lastline = instry;
1261 // update the highlighted length
1262 highlight_lineamount = (highlight_firstline>highlight_lastline)?
1263 highlight_firstline - highlight_lastline +1
1264 : highlight_lastline - highlight_firstline +1;
1265 break;
1266 case 'l':
1267 act_mvright();
1268 break;
1269 case 'g':
1270 if(nextchar() == 'g'){
1271 act_mvtop();
1273 break;
1274 case 'G':
1275 act_mvbottom();
1276 break;
1277 // d: copy every line that is highlighted to the paste buffer and clear them, too
1278 case 'd':
1279 min = (highlight_firstline < highlight_lastline)?
1280 highlight_firstline
1281 : highlight_lastline;
1282 max = (highlight_firstline < highlight_lastline)?
1283 highlight_lastline
1284 : highlight_firstline;
1285 if(currtab == 0){
1286 for(int i=min; i<=max; i++)
1287 act_clrinsongtab(i);
1288 }else if(currtab == 1){
1289 for(int i=min; i<=max; i++)
1290 act_clrintracktab(currtrack, i);
1291 }else if(currtab == 2){
1292 for(int i=min; i<=max; i++)
1293 act_clrininstrtab(currinstr, i);
1295 //snprintf(buf, sizeof(buf), "%d fewer lines", highlight_lineamount);
1296 //infinitemsg = buf;
1297 currmode = PM_NORMAL;
1298 break;
1299 // y: copy every line that is highlighted to the paste buffer
1300 case 'y':
1301 if(currtab == 0){
1302 //tcliplen = 1;
1303 //memcpy(&tclip, &tune->sng[songy], sizeof(struct songline)*highlight_lineamount);
1304 tcliplen = highlight_lineamount;
1305 //moved up, then yanked
1306 if(highlight_firstline > highlight_lastline){
1307 for(int i = 0; i < highlight_lineamount; i++)
1308 memcpy(&tclip[i], &tune->sng[songy+i], sizeof(struct songline));
1309 //moved down, then yanked
1310 }else if(highlight_lastline > highlight_firstline){
1311 for(int i = highlight_lineamount-1, j = 0; i >= 0; i--, j++){
1312 memcpy(&tclip[i], &tune->sng[songy-j], sizeof(struct songline));
1315 }else if(currtab == 1){
1316 tcliplen = highlight_lineamount;
1317 //moved up, then yanked
1318 if(highlight_firstline > highlight_lastline){
1319 for(int i = 0; i < highlight_lineamount; i++)
1320 memcpy(&tclip[i], &tune->trk[currtrack].line[tracky+i], sizeof(struct trackline));
1321 //moved down, then yanked
1322 }else if(highlight_lastline > highlight_firstline){
1323 for(int i = highlight_lineamount-1, j = 0; i >= 0; i--, j++){
1324 memcpy(&tclip[i], &tune->trk[currtrack].line[tracky-j], sizeof(struct trackline));
1327 }else if(currtab == 2){
1328 //icliplen = 1;
1329 //memcpy(&iclip, &instrument[currinstr].line[instry], sizeof(struct instrline)*highlight_lineamount);
1330 icliplen = highlight_lineamount;
1331 //moved up, then yanked
1332 if(highlight_firstline > highlight_lastline){
1333 for(int i = 0; i < highlight_lineamount; i++)
1334 memcpy(&iclip[i], &instrument[currinstr].line[instry+i], sizeof(struct instrline));
1335 //moved down, then yanked
1336 }else if(highlight_lastline > highlight_firstline){
1337 for(int i = highlight_lineamount-1, j = 0; i >= 0; i--, j++){
1338 memcpy(&iclip[i], &instrument[currinstr].line[instry-j], sizeof(struct instrline));
1343 snprintf(buf, sizeof(buf), "%d lines yanked", highlight_lineamount);
1344 infinitemsg = buf;
1345 currmode = PM_NORMAL;
1346 break;
1348 drawgui();
1350 // update the highlighted length
1351 /*highlight_lineamount = (highlight_firstline>highlight_lastline)?
1352 highlight_firstline - highlight_lastline +1
1353 : highlight_lastline - highlight_firstline +1;
1356 highlight_firstline = -1;
1357 highlight_lastline = -1;
1359 return;