Restored pattern editor functionality.
[epichord.git] / src / eventedit.cpp
blob3402dfe11fd9d895b413522fac458e1bd5cc1f1b
1 /*
2 Epichord - a midi sequencer
3 Copyright (C) 2008 Evan Rinehart
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to
18 The Free Software Foundation, Inc.
19 51 Franklin Street, Fifth Floor
20 Boston, MA 02110-1301, USA
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <vector>
27 #include <fltk/Group.h>
28 #include <fltk/Widget.h>
29 #include <fltk/events.h>
31 #include "ui.h"
33 #include "eventedit.h"
35 #include "uihelper.h"
38 #define MAG_MAX 16383
40 #define SWAP(X,Y) tmp=X; X=Y; Y=tmp;
42 extern struct conf config;
44 extern UI* ui;
45 extern std::vector<track*> tracks;
47 extern char controller_names[128][64];
49 using namespace fltk;
51 EventEdit::EventEdit(int x, int y, int w, int h, const char* label = 0) : fltk::Widget(x, y, w, h, label) {
53 zoom = 15;
55 event_type = MIDI_NOTE_ON;
56 controller_type = 0;
59 label_flag = 0;
60 select_flag = 0;
61 insert_flag = 0;
62 line_flag = 0;
63 paste_flag = 0;
64 delete_flag = 0;
65 box_flag = 0;
67 for(int i=0; i<134; i++){
68 has[i] = 0;
72 int EventEdit::handle(int event){
73 int X,Y;
74 switch(event){
75 case FOCUS:
76 return 1;
77 case MOUSEWHEEL:
78 if(event_dy() < 0){
79 event_type_next();
81 else if(event_dy() > 0){
82 event_type_prev();
84 redraw();
85 return 1;
86 break;
87 case PUSH:
88 X=event_x();
89 Y=event_y();
90 if(event_button()==1){
91 if(event_state()&fltk::CTRL){//insert
92 insert_flag = 1;
93 insert_x = X;
94 insert_y = Y;
95 insert_t = quantize(xpix2tick(X));
96 insert_M = ypix2mag(Y);
98 else if(event_state()&fltk::SHIFT){//box select
99 box_flag=1;
100 box_x1=X;
101 box_x2=X;
102 box_y1=Y;
103 box_y2=Y;
104 box_t1=xpix2tick(X);
105 box_t2=box_t1;
106 box_m1=ypix2mag(Y);
107 box_m2=box_m1;
109 else{//line
110 line_flag=1;
111 line_x1=X;
112 line_x2=X;
113 line_y1=Y;
114 line_y2=Y;
115 line_t1=xpix2tick(X);
116 line_M1=ypix2mag(Y);
117 line_t2=line_t1;
118 line_M2=line_M1;
121 else if(event_button()==2){//paste
122 paste_flag = 1;
123 paste_x = X;
124 paste_t = xpix2tick(X);
126 else if(event_button()==3){//delete
127 delete_flag = 1;
128 delete_x1 = X;
129 delete_x2 = X;
130 delete_t1 = xpix2tick(X);
131 delete_t2 = delete_t1;
133 redraw();
134 return 1;
135 break;
136 case DRAG:
137 X=event_x();
138 Y=event_y();
139 if(line_flag){
140 line_x2 = X;
141 line_y2 = Y;
142 line_t2 = xpix2tick(X);
143 line_M2 = ypix2mag(Y);
145 if(box_flag){
146 box_x2 = X;
147 box_y2 = Y;
148 box_t2 = xpix2tick(X);
149 box_m2 = ypix2mag(Y);
151 if(insert_flag){
152 insert_x = X;
153 insert_y = Y;
154 insert_t = quantize(xpix2tick(X));
155 insert_M = ypix2mag(Y);
157 if(delete_flag){
158 delete_x2 = X;
159 delete_t2 = xpix2tick(X);
161 if(paste_flag){
162 paste_x = X;
163 paste_t = xpix2tick(X);
165 redraw();
166 break;
167 case RELEASE:
168 if(event_button()==1){//insert, box, line
169 if(line_flag){
170 apply_line();
171 line_flag=0;
173 if(box_flag){
174 apply_box();
175 ui->piano_roll->redraw();
176 box_flag=0;
178 if(insert_flag){
179 apply_insert();
180 insert_flag=0;
183 else if(event_button()==2){//complete paste
184 apply_paste();
185 paste_flag=0;
187 else if(event_button()==3){//delete
188 apply_delete();
189 delete_flag = 0;
190 clear_selection();
192 redraw();
193 break;
195 return 0;
198 void EventEdit::draw(){
199 fltk::setfont(fltk::HELVETICA,12);
200 fltk::setcolor(fltk::GRAY05);
201 fltk::fillrect(0,0,w(),h());
203 fltk::push_clip(0,0,w(),h());
205 if(delete_flag){
206 fltk::setcolor(fltk::color(64,0,0));
207 int X1,X2;
208 if(delete_x1>delete_x2){
209 X1=delete_x2;
210 X2=delete_x1;
212 else{
213 X1=delete_x1;
214 X2=delete_x2;
216 fltk::fillrect(X1,0,X2-X1,h());
219 fltk::setcolor(fltk::GRAY20);
220 fltk::drawtext(event_type_name(), 2, h()-5);
222 fltk::setcolor(fltk::GRAY20);
223 fltk::fillrect(0,h()-3,w(),1);
224 for(int i=zoom - scroll; i<w(); i+=zoom){
225 if(i>=0){
226 fltk::drawline(i,0,i,h()-1);
230 fltk::setcolor(fltk::GRAY50);
231 for(int i=zoom*4-scroll; i<w(); i+=zoom*4){
232 if(i>=0){
233 fltk::drawline(i,0,i,h()-1);
237 fltk::setcolor(fltk::WHITE);
238 int M = config.beats_per_measure;
239 int I = 0;
240 for(int i=1; I<w(); i++){
241 I = i*zoom*4*M - scroll;
242 if(I>=0){
243 fltk::fillrect(I,0,1,h());
247 fltk::setcolor(fltk::color(128,0,0));
248 int rightend = tick2xpix(cur_seqpat->dur)-scroll;
249 if(rightend >=0 && rightend < w()){
250 fltk::fillrect(rightend,0,1,h());
253 mevent* e = cur_seqpat->p->events->next;
255 while(e){
256 if(e->type==event_type){
257 if(e->type==MIDI_CONTROLLER_CHANGE){
258 if(e->value1 == controller_type){
259 M = val2mag(e->value2);
261 else{
262 e=e->next;
263 continue;
266 else{
267 switch(e->type){
268 case -1:
269 e = e->next;
270 continue;
271 case MIDI_PROGRAM_CHANGE:
272 case MIDI_CHANNEL_PRESSURE:
273 M = val2mag(e->value1);
274 break;
275 default:
276 M = val2mag(e->value2);
277 break;
280 int T1 = line_t1;
281 int T2 = line_t2;
282 int M1 = line_M1;
283 int M2 = line_M2;
284 if(T1>T2){
285 int tmp = T2;
286 T2 = T1;
287 T1 = tmp;
288 tmp = M2;
289 M2 = M1;
290 M1 = tmp;
292 if(line_flag && e->tick > T1 && e->tick < T2){
293 if(!select_flag || e->selected){
294 float m = (float)(M2-M1)/(T2-T1);
295 float b = M1 - T1*m;
296 M = (int)(m*e->tick + b);
297 if(M<0){M=0;}
298 if(M>MAG_MAX){M=MAG_MAX;}
301 int X = tick2xpix(e->tick) - scroll;
302 int Y = mag2ypix(M);
303 int H = h()-Y;
305 fltk::Color c1,c2,c3;
306 c1 = fltk::BLACK;
307 c2 = fltk::BLACK;
308 c3 = fltk::BLACK;
309 get_event_color(e,&c1,&c2,&c3);
311 fltk::setcolor(c1);
312 fltk::fillrect(X,Y+1,1,H);
313 fltk::fillrect(X+1,Y,1,1);
314 fltk::setcolor(c2);
315 fltk::fillrect(X+1,Y+1,1,H);
316 fltk::setcolor(c3);
317 fltk::fillrect(X,Y,1,1);
318 if(label_flag){
319 fltk::setcolor(c1);
320 char buf[16];
321 if(e->type == MIDI_PITCH_WHEEL){
322 snprintf(buf,16,"%d",M);
324 else{
325 snprintf(buf,16,"%d",mag2val(M));
327 fltk::drawtext(buf,X-fltk::getwidth(buf),Y+12<h()-3?Y+12:h()-3);
330 e = e->next;
333 if(line_flag){
334 fltk::setcolor(fltk::BLUE);
335 fltk::drawline(line_x1,line_y1,line_x2,line_y2);
338 if(box_flag){
339 fltk::setcolor(fltk::GREEN);
340 int X1,X2,Y1,Y2;
341 if(box_x1>box_x2){
342 X1=box_x2;
343 X2=box_x1;
345 else{
346 X1=box_x1;
347 X2=box_x2;
349 if(box_y1>box_y2){
350 Y1=box_y2;
351 Y2=box_y1;
353 else{
354 Y1=box_y1;
355 Y2=box_y2;
357 fltk::fillrect(X1,Y1,X2-X1,1);
358 fltk::fillrect(X1,Y1,1,Y2-Y1);
359 fltk::fillrect(X2,Y1,1,Y2-Y1);
360 fltk::fillrect(X1,Y2,X2-X1,1);
363 if(insert_flag){
364 fltk::setcolor(fltk::CYAN);
365 int X = tick2xpix(insert_t)-scroll;
366 int Y = insert_y;
367 if(Y<0){Y=0;}
368 if(Y>h()-3){Y=h()-3;}
369 int M = insert_M;
370 if(M<0){M=0;}
371 if(M>MAG_MAX){M=MAG_MAX;}
372 fltk::fillrect(X,Y,2,h()-Y);
373 if(label_flag){
374 char buf[16];
375 if(event_type == MIDI_PITCH_WHEEL){
376 snprintf(buf,16,"%d",M);
378 else{
379 snprintf(buf,16,"%d",mag2val(M));
381 fltk::drawtext(buf,X-fltk::getwidth(buf),Y+12<h()-3?Y+12:h()-3);
386 fltk::pop_clip();
391 void EventEdit::load(seqpat* s){
392 cur_seqpat = s;
393 cur_track = tracks[s->track];
394 recount_has();
397 int EventEdit::tick2xpix(int tick){
398 return tick*zoom*4 / 128;
401 const char* EventEdit::event_type_name(){
402 switch(event_type){
403 case MIDI_NOTE_OFF:
404 return "note off velocity";
405 case MIDI_NOTE_ON:
406 return "note on velocity";
407 case MIDI_AFTERTOUCH:
408 return "polyphonic key pressure (aftertouch)";
409 case MIDI_CONTROLLER_CHANGE:
410 return controller_names[controller_type];
411 case MIDI_PROGRAM_CHANGE:
412 return "program change";
413 case MIDI_CHANNEL_PRESSURE:
414 return "channel pressure";
415 case MIDI_PITCH_WHEEL:
416 return "pitch wheel";
417 default:
418 return "booya";
422 void EventEdit::event_type_next(){
423 switch(event_type){
424 case 0x80:
425 event_type = 0xA0;
426 break;
427 case 0x90:
428 event_type = 0x80;
429 break;
430 case 0xA0:
431 event_type = 0xC0;
432 break;
433 case 0xB0:
434 if(controller_type == 127){
435 event_type = 0x90;
437 else{
438 controller_type++;
440 break;
441 case 0xE0:
442 event_type = 0xB0;
443 controller_type = 0;
444 break;
445 default:
446 event_type += 0x10;
450 void EventEdit::event_type_prev(){
451 switch(event_type){
452 case 0x80:
453 event_type = 0x90;
454 break;
455 case 0x90:
456 event_type = 0xB0;
457 controller_type = 127;
458 break;
459 case 0xC0:
460 event_type = 0xA0;
461 break;
462 case 0xB0:
463 if(controller_type == 0){
464 event_type = 0xE0;
466 else{
467 controller_type--;
469 break;
470 case 0xA0:
471 event_type = 0x80;
472 break;
473 default:
474 event_type -= 0x10;
478 void EventEdit::set_event_type(int type, int controller){
479 event_type = type;
480 controller_type = controller;
484 int EventEdit::ypix2mag(int ypix){
485 int H = h()-3;
486 int R = ypix*MAG_MAX/H;
487 return MAG_MAX-R;
490 int EventEdit::mag2ypix(int mag){
491 int H = mag*(h()-3)/MAG_MAX;
492 return h()-H-3;
495 int EventEdit::mag2val(int mag){
496 return mag*127/MAG_MAX;
499 int EventEdit::val2mag(int val){
500 return val*MAG_MAX/127;
503 void EventEdit::apply_line(){
504 mevent* e = cur_seqpat->p->events;
505 Command* c;
506 int N = 0;
507 int T1, T2;
508 int M1 = line_M1;
509 int M2 = line_M2;
510 if(line_t1>line_t2){
511 T1=line_t2;
512 T2=line_t1;
514 else{
515 T1=line_t1;
516 T2=line_t2;
518 while(e->tick < T1){
519 e = e->next;
520 if(!e){
521 return;
524 while(e){
525 if(e->tick > T2){
526 break;
528 if(match_event_type(e) && (e->selected || !select_flag)){
529 float m = (float)(M2-M1)/(T2-T1);
530 float b = M1 - m*T1;
531 int M = (int)(m*e->tick + b);
532 int V1, V2;
533 if(M<0){M=0;}
534 if(M>MAG_MAX){M=MAG_MAX;}
535 switch(e->type){
536 case MIDI_NOTE_OFF:
537 case MIDI_NOTE_ON:
538 case MIDI_AFTERTOUCH:
539 case MIDI_CONTROLLER_CHANGE:
540 V1 = e->value1;
541 V2 = mag2val(M);
542 break;
543 case MIDI_PROGRAM_CHANGE:
544 case MIDI_CHANNEL_PRESSURE:
545 V1 = mag2val(M);
546 break;
547 case MIDI_PITCH_WHEEL:
548 V1 = M&0x7f;
549 V2 = (M&0x3f80) >> 7;
550 break;
552 c = new ChangeEvent(e,V1,V2);
553 set_undo(c);
554 N++;
556 e = e->next;
558 undo_push(N);
561 void EventEdit::apply_box(){
562 select_flag=1;
563 mevent* e = cur_seqpat->p->events->next;
564 int T1=box_t1;
565 int T2=box_t2;
566 int M1 = box_m1;
567 int M2 = box_m2;
568 int tmp;
569 while(e){
570 if(e->tick > T2){break;}
571 int M = get_event_mag(e);
572 if(T1>T2){SWAP(T1,T2);}
573 if(M1<M2){SWAP(M1,M2);}
574 if(e->tick > T1 && e->tick < T2 && M > M2){
575 e->selected = 1;
577 e=e->next;
581 void EventEdit::apply_insert(){
582 int V1,V2;
583 Command* c;
584 get_event_value(&V1,&V2);
585 c = new CreateEvent(cur_seqpat->p,event_type,insert_t,V1,V2);
586 set_undo(c);
587 undo_push(1);
589 switch(event_type){
590 case MIDI_NOTE_ON: has[0]=1; break;
591 case MIDI_NOTE_OFF: has[1]=1; break;
592 case MIDI_AFTERTOUCH: has[2]=1; break;
593 case MIDI_PROGRAM_CHANGE: has[3]=1; break;
594 case MIDI_CHANNEL_PRESSURE: has[4]=1; break;
595 case MIDI_PITCH_WHEEL: has[5]=1; break;
596 default: has[controller_type+6]=1; break;
599 if(event_type==MIDI_NOTE_ON){
600 ui->piano_roll->redraw();
604 void EventEdit::delete_events(int (EventEdit::*pred)(mevent* e)){
605 mevent* e = cur_seqpat->p->events->next;
606 mevent* next;
607 Command* c;
608 int N=0;
609 while(e){
610 if(((this)->*(pred))(e)){
611 next = e->next;
612 c = new DeleteEvent(e);
613 set_undo(c);
614 N++;
615 e = next;
617 else{
618 e = e->next;
621 undo_push(N);
625 int EventEdit::delete_type_in_range_pred(mevent* e){
626 if(e->tick > delete_t1 && e->tick < delete_t2 && match_event_type(e))
627 return 1;
628 else
629 return 0;
632 void EventEdit::apply_delete(){
633 delete_events(&EventEdit::delete_type_in_range_pred);
634 ui->piano_roll->redraw();
639 void EventEdit::apply_paste(){
640 printf("apply paste\n");
643 int EventEdit::match_event_type(mevent* e){
644 if(e->type == event_type){
645 if(e->type == MIDI_CONTROLLER_CHANGE){
646 if(e->value1 == controller_type){
647 return 1;
650 else{
651 return 1;
654 return 0;
657 void EventEdit::get_event_color(mevent* e, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3){
659 int T1,T2;
660 int tmp;
661 if(delete_flag){
662 T1=delete_t1;
663 T2=delete_t2;
664 if(T1>T2){SWAP(T1,T2);}
665 if(e->tick > T1 && e->tick < T2){
666 *c1 = fltk::color(229,79,75);
667 *c2 = fltk::color(120,60,58);
668 *c3 = fltk::color(225,131,109);
669 return;
673 if(box_flag){
674 T1=box_t1;
675 T2=box_t2;
676 int M1 = box_m1;
677 int M2 = box_m2;
678 int M = get_event_mag(e);
679 if(T1>T2){SWAP(T1,T2);}
680 if(M1<M2){SWAP(M1,M2);}
681 if(e->tick > T1 && e->tick < T2 && M > M2){
682 *c1 = fltk::color(108,229,75);
683 *c2 = fltk::color(71,120,59);
684 *c3 = fltk::color(108,229,75);
685 return;
689 if(line_flag){
690 T1=line_t1;
691 T2=line_t2;
692 if(T1>T2){SWAP(T1,T2);}
693 if(e->tick > T1 && e->tick < T2){
694 if(!select_flag || e->selected){
695 *c1 = fltk::color(75,119,229);
696 *c2 = fltk::color(58,76,120);
697 *c3 = fltk::color(109,123,225);
698 return;
703 if(e->selected){
704 *c1 = fltk::color(255,248,47);
705 *c2 = fltk::color(140,137,46);
706 *c3 = fltk::color(232,255,37);
707 return;
710 *c1 = fltk::color(169,75,229);
711 *c2 = fltk::color(95,58,119);
712 *c3 = fltk::color(198,109,225);
715 void EventEdit::get_event_value(int* v1, int* v2){
716 int M = insert_M;
717 if(M<0){M=0;}
718 if(M>MAG_MAX){M=MAG_MAX;}
719 switch(event_type){
720 case MIDI_NOTE_OFF:
721 case MIDI_NOTE_ON:
722 case MIDI_AFTERTOUCH:
723 *v1 = 60;
724 *v2 = mag2val(M);
725 break;
726 case MIDI_CONTROLLER_CHANGE:
727 *v1 = controller_type;
728 *v2 = mag2val(M);
729 break;
730 case MIDI_PROGRAM_CHANGE:
731 case MIDI_CHANNEL_PRESSURE:
732 *v1 = mag2val(M);
733 break;
734 case MIDI_PITCH_WHEEL:
735 *v1 = M&0x7f;
736 *v2 = (M&0x3f80) >> 7;
737 break;
741 int EventEdit::xpix2tick(int xpix){
742 ui->piano_roll->xpix2tick(xpix+scroll);
745 int EventEdit::get_event_mag(mevent* e){
746 switch(e->type){
747 case MIDI_NOTE_OFF:
748 case MIDI_NOTE_ON:
749 case MIDI_AFTERTOUCH:
750 case MIDI_CONTROLLER_CHANGE:
751 return val2mag(e->value2);
752 case MIDI_PROGRAM_CHANGE:
753 case MIDI_CHANNEL_PRESSURE:
754 return val2mag(e->value1);
755 case MIDI_PITCH_WHEEL:
756 return e->value1 | (e->value2<<7);
758 return 0;
762 int EventEdit::delete_type_all_pred(mevent* e){
763 if(match_event_type(e))
764 return 1;
765 else
766 return 0;
769 int EventEdit::delete_all_non_note_pred(mevent* e){
770 if(e->type != MIDI_NOTE_ON && e->type != MIDI_NOTE_OFF)
771 return 1;
772 else
773 return 0;
776 int EventEdit::delete_all_pred(mevent* e){
777 return 1;
780 void EventEdit::clear_events(){
781 delete_events(&EventEdit::delete_type_all_pred);
782 switch(event_type){
783 case MIDI_NOTE_ON: has[0]=0; break;
784 case MIDI_NOTE_OFF: has[1]=0; break;
785 case MIDI_AFTERTOUCH: has[2]=0; break;
786 case MIDI_PROGRAM_CHANGE: has[3]=0; break;
787 case MIDI_CHANNEL_PRESSURE: has[4]=0; break;
788 case MIDI_PITCH_WHEEL: has[5]=0; break;
789 default: has[controller_type+6]=0; break;
791 redraw();
792 ui->piano_roll->redraw();
793 ui->event_menu->redraw();
796 void EventEdit::clear_non_note_events(){
797 delete_events(&EventEdit::delete_all_non_note_pred);
798 redraw();
799 ui->piano_roll->redraw();
800 for(int i=2;i<134;i++){
801 has[i]=0;
803 ui->event_menu->redraw();
806 void EventEdit::clear_all_events(){
807 delete_events(&EventEdit::delete_all_pred);
808 redraw();
809 ui->piano_roll->redraw();
810 for(int i=0;i<134;i++){
811 has[i]=0;
813 ui->event_menu->redraw();
816 void EventEdit::clear_selected_events(){
820 void EventEdit::clear_selection(){
821 select_flag = 0;
822 mevent* e = cur_seqpat->p->events->next;
823 while(e){
824 e->selected=0;
825 e = e->next;
827 redraw();
828 ui->piano_roll->redraw();
831 int EventEdit::quantize(int tick){
832 return ui->piano_roll->quantize(tick);
835 void EventEdit::recount_has(){
836 for(int i=0; i<134; i++){has[i]=0;}
837 mevent* e = cur_seqpat->p->events->next;
838 while(e){
839 switch(e->type){
840 case MIDI_NOTE_ON: has[0]=1; break;
841 case MIDI_NOTE_OFF: has[1]=1; break;
842 case MIDI_AFTERTOUCH: has[2]=1; break;
843 case MIDI_PROGRAM_CHANGE: has[3]=1; break;
844 case MIDI_CHANNEL_PRESSURE: has[4]=1; break;
845 case MIDI_PITCH_WHEEL: has[5]=1; break;
846 default: has[e->value1+6]=1; break;
848 e = e->next;