Fixed right to left line tool in event editor. Again.
[epichord.git] / src / eventedit.cpp
blob22d7f6deeccf4f6e8707f50be704e5394b978497
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;
71 line_t1 = 0;
72 line_t2 = 0;
74 cur_seqpat = NULL;
77 int EventEdit::handle(int event){
78 int X,Y;
79 switch(event){
80 case FOCUS:
81 return 1;
82 case MOUSEWHEEL:
83 if(event_dy() < 0){
84 event_type_next();
86 else if(event_dy() > 0){
87 event_type_prev();
89 redraw();
90 return 1;
91 break;
92 case PUSH:
93 X=event_x();
94 Y=event_y();
95 if(event_button()==1){
96 if(event_state()&fltk::CTRL){//insert
97 insert_flag = 1;
98 insert_x = X;
99 insert_y = Y;
100 insert_t = quantize(xpix2tick(X));
101 insert_M = ypix2mag(Y);
103 else if(event_state()&fltk::SHIFT){//box select
104 box_flag=1;
105 box_x1=X;
106 box_x2=X;
107 box_y1=Y;
108 box_y2=Y;
109 box_t1=xpix2tick(X);
110 box_t2=box_t1;
111 box_m1=ypix2mag(Y);
112 box_m2=box_m1;
114 else{//line
115 line_flag=1;
116 line_x1=X;
117 line_x2=X;
118 line_y1=Y;
119 line_y2=Y;
120 line_t1=xpix2tick(X);
121 line_M1=ypix2mag(Y);
122 line_t2=line_t1;
123 line_M2=line_M1;
126 else if(event_button()==2){//paste
127 paste_flag = 1;
128 paste_x = X;
129 paste_t = xpix2tick(X);
131 else if(event_button()==3){//delete
132 delete_flag = 1;
133 delete_x1 = X;
134 delete_x2 = X;
135 delete_t1 = xpix2tick(X);
136 delete_t2 = delete_t1;
138 redraw();
139 return 1;
140 break;
141 case DRAG:
142 X=event_x();
143 Y=event_y();
144 if(line_flag){
145 line_x2 = X;
146 line_y2 = Y;
147 line_t2 = xpix2tick(X);
148 line_M2 = ypix2mag(Y);
150 if(box_flag){
151 box_x2 = X;
152 box_y2 = Y;
153 box_t2 = xpix2tick(X);
154 box_m2 = ypix2mag(Y);
156 if(insert_flag){
157 insert_x = X;
158 insert_y = Y;
159 insert_t = quantize(xpix2tick(X));
160 insert_M = ypix2mag(Y);
162 if(delete_flag){
163 delete_x2 = X;
164 delete_t2 = xpix2tick(X);
166 if(paste_flag){
167 paste_x = X;
168 paste_t = xpix2tick(X);
170 redraw();
171 break;
172 case RELEASE:
173 if(event_button()==1){//insert, box, line
174 if(line_flag){
175 apply_line();
176 line_flag=0;
178 if(box_flag){
179 apply_box();
180 ui->piano_roll->redraw();
181 box_flag=0;
183 if(insert_flag){
184 apply_insert();
185 insert_flag=0;
188 else if(event_button()==2){//complete paste
189 apply_paste();
190 paste_flag=0;
192 else if(event_button()==3){//delete
193 apply_delete();
194 delete_flag = 0;
195 clear_selection();
197 redraw();
198 break;
200 return 0;
203 void EventEdit::draw(){
204 fltk::setfont(fltk::HELVETICA,12);
205 fltk::setcolor(fltk::GRAY05);
206 fltk::fillrect(0,0,w(),h());
208 fltk::push_clip(0,0,w(),h());
210 if(delete_flag){
211 fltk::setcolor(fltk::color(64,0,0));
212 int X1,X2;
213 if(delete_x1>delete_x2){
214 X1=delete_x2;
215 X2=delete_x1;
217 else{
218 X1=delete_x1;
219 X2=delete_x2;
221 fltk::fillrect(X1,0,X2-X1,h());
224 fltk::setcolor(fltk::GRAY20);
225 fltk::drawtext(event_type_name(), 2, h()-5);
227 fltk::setcolor(fltk::GRAY20);
228 fltk::fillrect(0,h()-3,w(),1);
229 for(int i=zoom - scroll; i<w(); i+=zoom){
230 if(i>=0){
231 fltk::drawline(i,0,i,h()-1);
235 fltk::setcolor(fltk::GRAY50);
236 for(int i=zoom*4-scroll; i<w(); i+=zoom*4){
237 if(i>=0){
238 fltk::drawline(i,0,i,h()-1);
242 fltk::setcolor(fltk::WHITE);
243 int M = config.beats_per_measure;
244 int I = 0;
245 for(int i=1; I<w(); i++){
246 I = i*zoom*4*M - scroll;
247 if(I>=0){
248 fltk::fillrect(I,0,1,h());
252 fltk::setcolor(fltk::color(128,0,0));
253 int rightend = tick2xpix(cur_seqpat->dur)-scroll;
254 if(rightend >=0 && rightend < w()){
255 fltk::fillrect(rightend,0,1,h());
258 mevent* e = cur_seqpat->p->events->next;
260 while(e){
261 if(e->type==event_type){
262 if(e->type==MIDI_CONTROLLER_CHANGE){
263 if(e->value1 == controller_type){
264 M = val2mag(e->value2);
266 else{
267 e=e->next;
268 continue;
271 else{
272 switch(e->type){
273 case -1:
274 e = e->next;
275 continue;
276 case MIDI_PROGRAM_CHANGE:
277 case MIDI_CHANNEL_PRESSURE:
278 M = val2mag(e->value1);
279 break;
280 default:
281 M = val2mag(e->value2);
282 break;
285 int T1 = line_t1;
286 int T2 = line_t2;
287 int M1 = line_M1;
288 int M2 = line_M2;
289 if(T1>T2){
290 int tmp = T2;
291 T2 = T1;
292 T1 = tmp;
293 tmp = M2;
294 M2 = M1;
295 M1 = tmp;
297 if(line_flag && e->tick > T1 && e->tick < T2){
298 if(!select_flag || e->selected){
299 float m = (float)(M2-M1)/(T2-T1);
300 float b = M1 - T1*m;
301 M = (int)(m*e->tick + b);
302 if(M<0){M=0;}
303 if(M>MAG_MAX){M=MAG_MAX;}
306 int X = tick2xpix(e->tick) - scroll;
307 int Y = mag2ypix(M);
308 int H = h()-Y;
309 if(!(X<0 || X>w())){
310 fltk::Color c1,c2,c3;
311 c1 = fltk::BLACK;
312 c2 = fltk::BLACK;
313 c3 = fltk::BLACK;
314 get_event_color(e,&c1,&c2,&c3);
316 fltk::setcolor(c1);
317 fltk::fillrect(X,Y+1,1,H);
318 fltk::fillrect(X+1,Y,1,1);
319 fltk::setcolor(c2);
320 fltk::fillrect(X+1,Y+1,1,H);
321 fltk::setcolor(c3);
322 fltk::fillrect(X,Y,1,1);
323 if(label_flag){
324 fltk::setcolor(c1);
325 char buf[16];
326 if(e->type == MIDI_PITCH_WHEEL){
327 snprintf(buf,16,"%d",M);
329 else{
330 snprintf(buf,16,"%d",mag2val(M));
332 fltk::drawtext(buf,X+2,Y+12<h()-3?Y+12:h()-3);
336 e = e->next;
339 if(line_flag){
340 fltk::setcolor(fltk::BLUE);
341 fltk::drawline(line_x1,line_y1,line_x2,line_y2);
344 if(box_flag){
345 fltk::setcolor(fltk::GREEN);
346 int X1,X2,Y1,Y2;
347 if(box_x1>box_x2){
348 X1=box_x2;
349 X2=box_x1;
351 else{
352 X1=box_x1;
353 X2=box_x2;
355 if(box_y1>box_y2){
356 Y1=box_y2;
357 Y2=box_y1;
359 else{
360 Y1=box_y1;
361 Y2=box_y2;
363 fltk::fillrect(X1,Y1,X2-X1,1);
364 fltk::fillrect(X1,Y1,1,Y2-Y1);
365 fltk::fillrect(X2,Y1,1,Y2-Y1);
366 fltk::fillrect(X1,Y2,X2-X1,1);
369 if(insert_flag){
370 fltk::setcolor(fltk::CYAN);
371 int X = tick2xpix(insert_t)-scroll;
372 int Y = insert_y;
373 if(Y<0){Y=0;}
374 if(Y>h()-3){Y=h()-3;}
375 int M = insert_M;
376 if(M<0){M=0;}
377 if(M>MAG_MAX){M=MAG_MAX;}
378 fltk::fillrect(X,Y,2,h()-Y);
379 if(label_flag){
380 char buf[16];
381 if(event_type == MIDI_PITCH_WHEEL){
382 snprintf(buf,16,"%d",M);
384 else{
385 snprintf(buf,16,"%d",mag2val(M));
387 fltk::drawtext(buf,X-fltk::getwidth(buf),Y+12<h()-3?Y+12:h()-3);
392 fltk::pop_clip();
397 void EventEdit::load(seqpat* s){
398 cur_seqpat = s;
399 cur_track = tracks[s->track];
400 recount_has();
403 int EventEdit::tick2xpix(int tick){
404 return tick*zoom*4 / 128;
407 const char* EventEdit::event_type_name(){
408 switch(event_type){
409 case MIDI_NOTE_OFF:
410 return "note off velocity";
411 case MIDI_NOTE_ON:
412 return "note on velocity";
413 case MIDI_AFTERTOUCH:
414 return "polyphonic key pressure (aftertouch)";
415 case MIDI_CONTROLLER_CHANGE:
416 return controller_names[controller_type];
417 case MIDI_PROGRAM_CHANGE:
418 return "program change";
419 case MIDI_CHANNEL_PRESSURE:
420 return "channel pressure";
421 case MIDI_PITCH_WHEEL:
422 return "pitch wheel";
423 default:
424 return "booya";
428 void EventEdit::event_type_next(){
429 switch(event_type){
430 case 0x80:
431 event_type = 0xA0;
432 break;
433 case 0x90:
434 event_type = 0x80;
435 break;
436 case 0xA0:
437 event_type = 0xC0;
438 break;
439 case 0xB0:
440 if(controller_type == 127){
441 event_type = 0x90;
443 else{
444 controller_type++;
446 break;
447 case 0xE0:
448 event_type = 0xB0;
449 controller_type = 0;
450 break;
451 default:
452 event_type += 0x10;
456 void EventEdit::event_type_prev(){
457 switch(event_type){
458 case 0x80:
459 event_type = 0x90;
460 break;
461 case 0x90:
462 event_type = 0xB0;
463 controller_type = 127;
464 break;
465 case 0xC0:
466 event_type = 0xA0;
467 break;
468 case 0xB0:
469 if(controller_type == 0){
470 event_type = 0xE0;
472 else{
473 controller_type--;
475 break;
476 case 0xA0:
477 event_type = 0x80;
478 break;
479 default:
480 event_type -= 0x10;
484 void EventEdit::set_event_type(int type, int controller){
485 event_type = type;
486 controller_type = controller;
490 int EventEdit::ypix2mag(int ypix){
491 int H = h()-3;
492 int R = ypix*MAG_MAX/H;
493 return MAG_MAX-R;
496 int EventEdit::mag2ypix(int mag){
497 int H = mag*(h()-3)/MAG_MAX;
498 return h()-H-3;
501 int EventEdit::mag2val(int mag){
502 return mag*127/MAG_MAX;
505 int EventEdit::val2mag(int val){
506 return val*MAG_MAX/127;
509 void EventEdit::apply_line(){
510 mevent* e = cur_seqpat->p->events;
511 Command* c;
512 int N = 0;
513 int T1 = line_t1;
514 int T2 = line_t2;
515 int M1 = line_M1;
516 int M2 = line_M2;
517 int tmp;
518 if(T1>T2){SWAP(T1,T2);}
519 //if(M1>M2){SWAP(M1,M2);}
521 while(e->tick < T1){
522 e = e->next;
523 if(!e){
524 return;
527 while(e){
528 if(e->tick > T2){
529 break;
531 if(match_event_type(e) && (e->selected || !select_flag)){
532 float m = (float)(M2-M1)/(line_t2-line_t1);
533 float b = M1 - m*line_t1;
534 int M = (int)(m*e->tick + b);
535 int V1, V2;
536 if(M<0){M=0;}
537 if(M>MAG_MAX){M=MAG_MAX;}
538 switch(e->type){
539 case MIDI_NOTE_OFF:
540 case MIDI_NOTE_ON:
541 case MIDI_AFTERTOUCH:
542 case MIDI_CONTROLLER_CHANGE:
543 V1 = e->value1;
544 V2 = mag2val(M);
545 break;
546 case MIDI_PROGRAM_CHANGE:
547 case MIDI_CHANNEL_PRESSURE:
548 V1 = mag2val(M);
549 break;
550 case MIDI_PITCH_WHEEL:
551 V1 = M&0x7f;
552 V2 = (M&0x3f80) >> 7;
553 break;
555 c = new ChangeEvent(e,V1,V2);
556 set_undo(c);
557 N++;
559 e = e->next;
561 undo_push(N);
564 void EventEdit::apply_box(){
565 select_flag=1;
566 mevent* e = cur_seqpat->p->events->next;
567 int T1=box_t1;
568 int T2=box_t2;
569 int M1 = box_m1;
570 int M2 = box_m2;
571 int tmp;
572 while(e){
573 if(e->tick > T2){break;}
574 int M = get_event_mag(e);
575 if(T1>T2){SWAP(T1,T2);}
576 if(M1<M2){SWAP(M1,M2);}
577 if(e->tick > T1 && e->tick < T2 && M > M2){
578 e->selected = 1;
580 e=e->next;
584 void EventEdit::apply_insert(){
585 int V1,V2;
586 Command* c;
587 get_event_value(&V1,&V2);
588 c = new CreateEvent(cur_seqpat->p,event_type,insert_t,V1,V2);
589 set_undo(c);
590 undo_push(1);
592 switch(event_type){
593 case MIDI_NOTE_ON: has[0]=1; break;
594 case MIDI_NOTE_OFF: has[1]=1; break;
595 case MIDI_AFTERTOUCH: has[2]=1; break;
596 case MIDI_PROGRAM_CHANGE: has[3]=1; break;
597 case MIDI_CHANNEL_PRESSURE: has[4]=1; break;
598 case MIDI_PITCH_WHEEL: has[5]=1; break;
599 default: has[controller_type+6]=1; break;
602 if(event_type==MIDI_NOTE_ON){
603 ui->piano_roll->redraw();
607 void EventEdit::delete_events(int (EventEdit::*pred)(mevent* e)){
608 mevent* e = cur_seqpat->p->events->next;
609 mevent* next;
610 Command* c;
611 int N=0;
612 while(e){
613 if(((this)->*(pred))(e)){
614 next = e->next;
615 c = new DeleteEvent(e);
616 set_undo(c);
617 N++;
618 e = next;
620 else{
621 e = e->next;
624 undo_push(N);
628 int EventEdit::delete_type_in_range_pred(mevent* e){
629 int L = delete_t1;
630 int R = delete_t2;
631 int tmp;
632 if(L>R){SWAP(L,R);}
633 if(e->tick > L && e->tick < R && match_event_type(e))
634 return 1;
635 else
636 return 0;
639 void EventEdit::apply_delete(){
640 delete_events(&EventEdit::delete_type_in_range_pred);
641 ui->piano_roll->redraw();
646 void EventEdit::apply_paste(){
647 printf("apply paste\n");
650 int EventEdit::match_event_type(mevent* e){
651 if(e->type == event_type){
652 if(e->type == MIDI_CONTROLLER_CHANGE){
653 if(e->value1 == controller_type){
654 return 1;
657 else{
658 return 1;
661 return 0;
664 void EventEdit::get_event_color(mevent* e, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3){
666 int T1,T2;
667 int tmp;
668 if(delete_flag){
669 T1=delete_t1;
670 T2=delete_t2;
671 if(T1>T2){SWAP(T1,T2);}
672 if(e->tick > T1 && e->tick < T2){
673 *c1 = fltk::color(229,79,75);
674 *c2 = fltk::color(120,60,58);
675 *c3 = fltk::color(225,131,109);
676 return;
680 if(box_flag){
681 T1=box_t1;
682 T2=box_t2;
683 int M1 = box_m1;
684 int M2 = box_m2;
685 int M = get_event_mag(e);
686 if(T1>T2){SWAP(T1,T2);}
687 if(M1<M2){SWAP(M1,M2);}
688 if(e->tick > T1 && e->tick < T2 && M > M2){
689 *c1 = fltk::color(108,229,75);
690 *c2 = fltk::color(71,120,59);
691 *c3 = fltk::color(108,229,75);
692 return;
696 if(line_flag){
697 T1=line_t1;
698 T2=line_t2;
699 if(T1>T2){SWAP(T1,T2);}
700 if(e->tick > T1 && e->tick < T2){
701 if(!select_flag || e->selected){
702 *c1 = fltk::color(75,119,229);
703 *c2 = fltk::color(58,76,120);
704 *c3 = fltk::color(109,123,225);
705 return;
710 if(e->selected){
711 *c1 = fltk::color(255,248,47);
712 *c2 = fltk::color(140,137,46);
713 *c3 = fltk::color(232,255,37);
714 return;
717 *c1 = fltk::color(169,75,229);
718 *c2 = fltk::color(95,58,119);
719 *c3 = fltk::color(198,109,225);
722 void EventEdit::get_event_value(int* v1, int* v2){
723 int M = insert_M;
724 if(M<0){M=0;}
725 if(M>MAG_MAX){M=MAG_MAX;}
726 switch(event_type){
727 case MIDI_NOTE_OFF:
728 case MIDI_NOTE_ON:
729 case MIDI_AFTERTOUCH:
730 *v1 = 60;
731 *v2 = mag2val(M);
732 break;
733 case MIDI_CONTROLLER_CHANGE:
734 *v1 = controller_type;
735 *v2 = mag2val(M);
736 break;
737 case MIDI_PROGRAM_CHANGE:
738 case MIDI_CHANNEL_PRESSURE:
739 *v1 = mag2val(M);
740 break;
741 case MIDI_PITCH_WHEEL:
742 *v1 = M&0x7f;
743 *v2 = (M&0x3f80) >> 7;
744 break;
748 int EventEdit::xpix2tick(int xpix){
749 ui->piano_roll->xpix2tick(xpix+scroll);
752 int EventEdit::get_event_mag(mevent* e){
753 switch(e->type){
754 case MIDI_NOTE_OFF:
755 case MIDI_NOTE_ON:
756 case MIDI_AFTERTOUCH:
757 case MIDI_CONTROLLER_CHANGE:
758 return val2mag(e->value2);
759 case MIDI_PROGRAM_CHANGE:
760 case MIDI_CHANNEL_PRESSURE:
761 return val2mag(e->value1);
762 case MIDI_PITCH_WHEEL:
763 return e->value1 | (e->value2<<7);
765 return 0;
769 int EventEdit::delete_type_all_pred(mevent* e){
770 if(match_event_type(e))
771 return 1;
772 else
773 return 0;
776 int EventEdit::delete_all_non_note_pred(mevent* e){
777 if(e->type != MIDI_NOTE_ON && e->type != MIDI_NOTE_OFF)
778 return 1;
779 else
780 return 0;
783 int EventEdit::delete_all_pred(mevent* e){
784 return 1;
787 void EventEdit::clear_events(){
788 delete_events(&EventEdit::delete_type_all_pred);
789 switch(event_type){
790 case MIDI_NOTE_ON: has[0]=0; break;
791 case MIDI_NOTE_OFF: has[1]=0; break;
792 case MIDI_AFTERTOUCH: has[2]=0; break;
793 case MIDI_PROGRAM_CHANGE: has[3]=0; break;
794 case MIDI_CHANNEL_PRESSURE: has[4]=0; break;
795 case MIDI_PITCH_WHEEL: has[5]=0; break;
796 default: has[controller_type+6]=0; break;
798 redraw();
799 ui->piano_roll->redraw();
800 ui->event_menu->redraw();
803 void EventEdit::clear_non_note_events(){
804 delete_events(&EventEdit::delete_all_non_note_pred);
805 redraw();
806 ui->piano_roll->redraw();
807 for(int i=2;i<134;i++){
808 has[i]=0;
810 ui->event_menu->redraw();
813 void EventEdit::clear_all_events(){
814 delete_events(&EventEdit::delete_all_pred);
815 redraw();
816 ui->piano_roll->redraw();
817 for(int i=0;i<134;i++){
818 has[i]=0;
820 ui->event_menu->redraw();
823 void EventEdit::clear_selected_events(){
827 void EventEdit::clear_selection(){
828 select_flag = 0;
829 mevent* e = cur_seqpat->p->events->next;
830 while(e){
831 e->selected=0;
832 e = e->next;
834 redraw();
835 ui->piano_roll->redraw();
838 int EventEdit::quantize(int tick){
839 return ui->piano_roll->quantize(tick);
842 void EventEdit::recount_has(){
843 for(int i=0; i<134; i++){has[i]=0;}
844 mevent* e = cur_seqpat->p->events->next;
845 while(e){
846 switch(e->type){
847 case MIDI_NOTE_ON: has[0]=1; break;
848 case MIDI_NOTE_OFF: has[1]=1; break;
849 case MIDI_AFTERTOUCH: has[2]=1; break;
850 case MIDI_PROGRAM_CHANGE: has[3]=1; break;
851 case MIDI_CHANNEL_PRESSURE: has[4]=1; break;
852 case MIDI_PITCH_WHEEL: has[5]=1; break;
853 default: has[e->value1+6]=1; break;
855 e = e->next;