Unhardcoded ticks per beat. Fixed triplet bugs.
[epichord.git] / src / pianoroll.cpp
blob4ce0be3ad619a53ca3e47a4f37222d16e4663825
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 <vector>
24 #include <fltk/Group.h>
25 #include <fltk/Widget.h>
27 #include <fltk/Scrollbar.h>
29 #include <fltk/events.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include "ui.h"
35 #include "util.h"
37 #include "backend.h"
39 #include "uihelper.h"
41 extern UI* ui;
42 extern std::vector<track*> tracks;
44 extern struct conf config;
46 using namespace fltk;
48 #define SWAP(X,Y) tmp=X; X=Y; Y=tmp;
50 PianoRoll::PianoRoll(int x, int y, int w, int h, const char* label = 0) : fltk::Widget(x, y, w, h, label) {
51 wkeyh = 12;
52 bkeyh = 7;
53 cur_seqpat = NULL;
55 zoom = 15;
56 zoom_n = 3;
58 q_tick = 32;
60 box_flag = 0;
62 trip_flag = 0;
64 move_toffset = 0;
66 insert_flag = 0;
67 move_flag = 0;
68 delete_flag=0;
69 lresize_flag = 0;
70 rresize_flag = 0;
72 resize_arrow = 0;
73 resize_e = NULL;
74 resize_handle_width = 4;
76 fakeh = wkeyh*75;
77 fakehmin = wkeyh*75;
78 if(fakeh < h){fakeh = h;}
80 scrollx=0;
81 scrolly=0;
84 int PianoRoll::handle(int event){
85 Command* c;
86 pattern* p;
87 mevent* e;
89 int X = event_x();
90 int Y = event_y();
91 int yprime;
92 int prevt;
94 switch(event){
95 case fltk::ENTER:
96 return 1;
97 case fltk::FOCUS:
98 return 1;
99 case fltk::MOUSEWHEEL:
100 yprime = scrolly+32*event_dy();
101 if(yprime<0){yprime=0;}
102 if(yprime>75*12 - h()){yprime = 75*12 - h();}
103 scrollTo(scrollx,yprime);
104 return 1;
105 case fltk::SHORTCUT:
106 if(event_key()==fltk::DeleteKey){
107 apply_delete();
108 delete_flag = 0;
109 redraw();
110 resize_arrow = 0;
111 ui->event_edit->redraw();
112 return 1;
114 if(event_state(CTRL) && event_key()=='c'){
115 //printf("roll copy\n");
116 return 1;
118 if(zoom_out_key(event_key(),event_state())){
119 if(zoom_n > 1){
120 prevt = xpix2tick(scrollx);
121 zoom_n--;
122 set_zoom(30*(1<<zoom_n)/16);
123 ui->pattern_timeline->zoom = zoom;
124 //ui->pattern_timeline->update(get_play_position());
125 //ui->pattern_timeline->redraw();
126 ui->event_edit->zoom = zoom;
127 scrollTo(tick2xpix(prevt),scrolly);
128 //ui->event_edit->redraw();
130 redraw();
131 return 1;
133 if(zoom_in_key(event_key(),event_state())){
134 if(zoom_n < 8){
135 prevt = xpix2tick(scrollx);
136 zoom_n++;
137 set_zoom(30*(1<<zoom_n)/16);
138 ui->pattern_timeline->zoom = zoom;
139 //ui->pattern_timeline->update(get_play_position());
140 //ui->pattern_timeline->redraw();
141 ui->event_edit->zoom = zoom;
142 scrollTo(tick2xpix(prevt),scrolly);
143 //ui->event_edit->redraw();
145 redraw();
146 return 1;
148 return 0;
149 case fltk::PUSH:
150 take_focus();
151 e = over_note();
152 if(event_button()==1){//left mouse
153 if(e==NULL){//new note init
154 if(event_state()&fltk::SHIFT){//begin box
155 box_flag = 1;
156 box_x1=X;
157 box_x2=X;
158 box_y1=Y;
159 box_y2=Y;
160 box_t1=xpix2tick(X+scrollx);
161 box_t2=box_t1;
162 box_n1=ypix2note(Y+scrolly,1);
163 box_n2=box_n1;
165 else{//begin insert
166 insert_flag = 1;
167 insert_torig = quantize(xpix2tick(X+scrollx));
168 if(trip_flag){
169 insert_toffset = (q_tick*4) / 3;
171 else{
172 insert_toffset = q_tick;
174 //new_orig_t = new_left_t;
175 insert_note = ypix2note(Y+scrolly,1);
177 last_note = insert_note;
178 if(config.playinsert){
179 ui->keyboard->play_note(last_note,0);
184 else{
186 if(!(e->selected) && !(event_state()&fltk::SHIFT)){
187 unselect_all();
189 e->selected = 1;
190 ui->event_edit->select_flag = 1;
191 resize_arrow_color = fltk::color(128,128,0);
193 if(over_rhandle(e)){//resize
194 rresize_flag = 1;
195 rresize_torig = e->tick+e->dur;
196 rresize_toffset = 0;
198 else if(over_lhandle(e)){//resize move
199 lresize_flag = 1;
200 lresize_torig = e->tick;
201 lresize_toffset = 0;
203 else{//begin move
204 move_flag = 1;
206 move_torig = e->tick;
207 move_qoffset = e->tick - quantize(e->tick);
209 move_toffset = -move_qoffset;
211 //move_offset = quantize(xpix2tick(X)) - move_torig - move_qoffset;
212 //move_toffset = 0;
213 move_offset = X - tick2xpix(e->tick);
214 //move_norig = ypix2note(Y+scrolly,1);
215 move_norig = e->value1;
216 move_noffset = 0;
218 last_note = e->value1;
219 if(config.playmove){
220 ui->keyboard->play_note(last_note,0);
221 ui->keyboard->highlight_note(last_note);
226 else if(event_button()==2){//middle mouse
227 //button initiates paste
229 else if(event_button()==3){//right mouse
230 if(e==NULL){
231 unselect_all();
233 ui->event_edit->redraw();
235 else{//set up for deletion
236 if(!(e->selected) && !(event_state()&fltk::SHIFT)){
237 unselect_all();
239 e->selected = 1;
240 delete_flag = 1;
241 resize_arrow_color = fltk::color(120,60,58);
244 redraw();
245 return 1;
246 case fltk::DRAG:
247 ui->keyboard->highlight_clear();
248 if(box_flag){
249 box_x2 = X;
250 box_y2 = Y;
251 box_t2 = xpix2tick(X+scrollx);
252 box_n2 = ypix2note(Y+scrolly,1);
254 else if(insert_flag){
255 if(trip_flag){
256 int q_tri = (q_tick*4) / 3;
257 insert_toffset = quantize(xpix2tick(X+scrollx)+q_tri) - insert_torig;
258 if(insert_toffset<=0){
259 insert_toffset -= q_tri;
262 else{
263 insert_toffset = quantize(xpix2tick(X+scrollx)+q_tick) - insert_torig;
264 if(insert_toffset<=0){
265 insert_toffset -= q_tick;
268 insert_note = ypix2note(Y+scrolly,1);
269 if(insert_note != last_note){
270 if(config.playinsert){//play on insert
271 ui->keyboard->release_note(last_note,0);
272 ui->keyboard->play_note(insert_note,0);
274 last_note = insert_note;
277 else if(move_flag){
278 move_toffset = quantize(xpix2tick(X - move_offset)) - move_torig;
279 move_noffset = ypix2note(Y+scrolly,1) - move_norig;
280 int N = move_norig+move_noffset;
281 if(N != last_note){
282 ui->keyboard->highlight_note(N);
283 if(config.playmove){//play on move
284 ui->keyboard->release_note(last_note,0);
285 ui->keyboard->play_note(N,0);
287 last_note = N;
290 else if(rresize_flag){
291 rresize_toffset = quantize(xpix2tick(X+scrollx))+q_tick-rresize_torig;
293 else if(lresize_flag){
294 lresize_toffset = quantize(xpix2tick(X+scrollx)) - lresize_torig;
296 redraw();
297 return 1;
298 case fltk::RELEASE:
299 e = over_note();
300 if(event_button()==1){
301 if(box_flag){
302 apply_box();
303 ui->event_edit->redraw();
304 box_flag=0;
306 else if(rresize_flag){
307 apply_rresize();
308 rresize_flag = 0;
309 resize_arrow = 0;
310 ui->event_edit->redraw();
312 else if(lresize_flag){
313 apply_lresize();
314 lresize_flag = 0;
315 resize_arrow = 0;
316 ui->event_edit->redraw();
318 else if(insert_flag){
319 apply_insert();
321 insert_flag = 0;
323 ui->keyboard->release_note(insert_note,0);
324 ui->keyboard->redraw();
325 ui->event_edit->has[0]=1;
326 ui->event_edit->has[1]=1;
327 ui->event_edit->redraw();
328 ui->event_menu->redraw();
330 else if(move_flag){
331 apply_move();
332 move_flag = 0;
334 midi_track_off(cur_seqpat->track);
335 ui->keyboard->release_note(last_note,0);
336 ui->keyboard->release_note(move_norig+move_noffset,0);
337 ui->keyboard->redraw();
338 ui->event_edit->redraw();
340 insert_flag=0;
341 move_flag=0;
343 if(event_button()==3){
344 mevent* over_n = over_note();
345 if(delete_flag && over_n){
346 if(over_n->selected){
347 apply_delete();
348 midi_track_off(cur_seqpat->track);
349 ui->event_edit->redraw();
352 delete_flag=0;
353 resize_arrow = 0;
355 redraw();
357 return 1;
359 case fltk::MOVE:
360 ui->keyboard->highlight_note(ypix2note(Y+scrolly,1));
361 e = over_note();
362 if(e){
363 if(over_rhandle(e)){
364 if(resize_e != e || resize_arrow != 1){
365 if(e->selected){resize_arrow_color = fltk::color(128,128,0);}
366 else{resize_arrow_color = fltk::color(95,58,119);}
367 resize_e = e;
368 resize_arrow = 1;
369 resize_x = tick2xpix(e->tick + e->dur)-scrollx-resize_handle_width;
370 resize_y = note2ypix(e->value1)-scrolly;
371 redraw();
374 else if(over_lhandle(e)){
375 if(resize_e != e || resize_arrow != 1){
376 if(e->selected){resize_arrow_color = fltk::color(128,128,0);}
377 else{resize_arrow_color = fltk::color(95,58,119);}
378 resize_e = e;
379 resize_arrow = -1;
380 resize_x = tick2xpix(e->tick)+1 - scrollx;
381 resize_y = note2ypix(e->value1) - scrolly;
382 redraw();
385 else{
386 if(resize_e != e || resize_arrow != 0){
387 resize_e = e;
388 resize_arrow = 0;
389 redraw();
393 else{
394 if(resize_arrow != 0){
395 resize_arrow = 0;
396 redraw();
400 return 1;
403 case fltk::LEAVE:
404 ui->keyboard->highlight_clear();
405 return 1;
407 return 0;
410 void PianoRoll::draw(){
412 fltk::push_clip(0,0,w(),h());
414 fltk::setcolor(fltk::GRAY05);
415 fltk::fillrect(0,0,w(),h());
417 fltk::setcolor(fltk::GRAY20);
418 for(int i=12-scrolly; i<h(); i+=12){
419 if(i>=0){
420 fltk::fillrect(0,i,w(),1);
423 for(int i=zoom-scrollx; i<w(); i+=zoom){
424 if(i>=0){
425 fltk::fillrect(i,0,1,h());
429 fltk::setcolor(fltk::GRAY30);
430 for(int i=12*5-scrolly; i<h(); i+=12*7){
431 if(i>=0){
432 fltk::fillrect(0,i,w(),1);
436 fltk::setcolor(fltk::GRAY50);
437 for(int i=zoom*4-scrollx; i<w(); i+=zoom*4){
438 if(i>=0){
439 fltk::fillrect(i,0,1,h());
443 fltk::setcolor(fltk::WHITE);
444 int M = config.beats_per_measure;
445 for(int i=zoom*4*M-scrollx; i<w(); i+=zoom*4*M){
446 if(i>=0){
447 fltk::fillrect(i,0,1,h());
451 fltk::setcolor(fltk::color(128,0,0));
452 int rightend = tick2xpix(cur_seqpat->dur)-scrollx;
453 if(rightend >=0 && rightend < w()){
454 fltk::fillrect(rightend,0,1,h());
457 fltk::setcolor(fltk::color(128,128,0));
458 fltk::fillrect(0,12*40-scrolly,w(),1);
460 int tmp;
461 if(insert_flag){
462 fltk::setcolor(fltk::BLUE);
463 int T1 = insert_torig;
464 int T2 = T1 + insert_toffset;
465 if(T1>T2){SWAP(T1,T2);}
466 int X = tick2xpix(T1)+1 - scrollx;
467 int Y = note2ypix(insert_note) - scrolly;
468 int W = tick2xpix(T2)-scrollx - X;
469 fltk::fillrect(X,Y,W,11);
472 if(move_flag){
473 fltk::setcolor(fltk::MAGENTA);
474 mevent* ptr = cur_seqpat->p->events->next;
475 while(ptr){
476 if(ptr->type == MIDI_NOTE_ON && ptr->selected){
477 int X = tick2xpix(ptr->tick+move_toffset)+1-scrollx;
478 int Y = note2ypix(ptr->value1+move_noffset)-scrolly;
479 int W = tick2xpix(ptr->dur);
480 fltk::fillrect(X,Y,W-1,1);
481 fltk::fillrect(X,Y+11,W-1,1);
482 fltk::fillrect(X,Y,1,11);
483 fltk::fillrect(X+W-2,Y,1,11);
485 ptr=ptr->next;
491 //draw all notes
492 mevent* e = cur_seqpat->p->events->next;
494 fltk::Color c1,c2,c3;
496 while(e){
497 if(e->type == MIDI_NOTE_ON){
498 //fltk::fillrect(tick2xpix(e->tick),note2ypix(e->value),e->dur,11);
500 int R1 = rresize_flag&&e->selected ? rresize_toffset : 0;
501 int R2 = lresize_flag&&e->selected ? lresize_toffset : 0;
503 int T1 = e->tick + R2;
504 int T2 = e->tick+e->dur + R1;
506 if(T1 >= T2-q_tick && e->selected){
507 if(rresize_flag){
508 T1 = e->tick;
509 T2 = T1 + q_tick;
511 else if(lresize_flag){
512 T2 = e->tick + e->dur;
513 T1 = T2 - q_tick;
517 int X = tick2xpix(T1) + 1 - scrollx;
518 int Y = note2ypix(e->value1) - scrolly;
520 int W = tick2xpix(T2)-scrollx - X;
521 get_event_color(e,&c1,&c2,&c3);
523 if(!(X+W<0 || X > w())){
525 fltk::setcolor(c1);
526 fltk::fillrect(X+1,Y+1,W-1,10);
528 fltk::setcolor(c2);
529 fltk::fillrect(X,Y+11,W,1);
530 fltk::fillrect(X+W-1,Y+1,1,11);
532 fltk::setcolor(c3);
533 fltk::fillrect(X,Y,W,1);
534 fltk::fillrect(X,Y,1,11);
537 e=e->next;
541 if(!rresize_flag && !lresize_flag){
542 if(resize_arrow > 0){
543 setcolor(resize_arrow_color);
545 int W = resize_handle_width;
546 int H = 12;
547 int X = resize_x;
548 int Y = resize_y;
550 addvertex(X,Y);
551 addvertex(X,Y+H);
552 addvertex(X+W,Y+H/2);
553 fillpath();
555 else if(resize_arrow < 0){
556 setcolor(resize_arrow_color);
558 int W = resize_handle_width;
559 int H = 12;
560 int X = resize_x;
561 int Y = resize_y;
563 addvertex(X+W,Y);
564 addvertex(X+W,Y+H);
565 addvertex(X,Y+H/2);
566 fillpath();
571 if(box_flag){
572 fltk::setcolor(fltk::GREEN);
573 int X1,X2,Y1,Y2;
574 if(box_x1>box_x2){
575 X1=box_x2;
576 X2=box_x1;
578 else{
579 X1=box_x1;
580 X2=box_x2;
582 if(box_y1>box_y2){
583 Y1=box_y2;
584 Y2=box_y1;
586 else{
587 Y1=box_y1;
588 Y2=box_y2;
590 fltk::fillrect(X1,Y1,X2-X1,1);
591 fltk::fillrect(X1,Y1,1,Y2-Y1);
592 fltk::fillrect(X2,Y1,1,Y2-Y1);
593 fltk::fillrect(X1,Y2,X2-X1,1);
596 fltk::pop_clip();
602 void PianoRoll::scrollTo(int X, int Y){
604 if(!cur_seqpat){
605 return;
608 if(is_backend_playing() && config.follow){
609 int pos = tick2xpix(get_play_position() - cur_seqpat->tick);
610 if(pos < cur_seqpat->tick){
612 else if(pos < X || pos > X + w() - 30 - 30){
613 ui->pattern_hscroll->value(scrollx);
614 return;
618 scrollx = X;
619 scrolly = Y;
620 cur_seqpat->scrollx = X;
621 cur_seqpat->scrolly = Y;
623 redraw();
624 ui->pattern_hscroll->value(X);
625 ui->pattern_hscroll->redraw();
626 ui->pattern_vscroll->value(Y);
627 ui->pattern_timeline->scroll = X;
628 ui->pattern_timeline->redraw();
629 ui->event_edit->scroll = X;
630 ui->event_edit->redraw();
631 ui->keyboard->scroll = Y;
632 ui->keyboard->redraw();
635 static int kludge=2;
636 void PianoRoll::layout(){
637 if(kludge!=0){
638 kludge--;
639 return;
641 fakeh = 900;
642 if(fakeh<h()){
643 fakeh = h();
646 fltk::Scrollbar* sb = ui->pattern_vscroll;
648 sb->maximum(0);
649 sb->minimum(fakeh-h());
651 if(sb->value() > sb->minimum()){
652 scrollTo(scrollx,900-h());
654 int M = h() - 30;
655 int newsize = M-(fakeh-h());
656 if(newsize<20){
657 newsize=20;
659 ui->song_vscroll->slider_size(60);
662 void PianoRoll::load(seqpat* s){
663 cur_seqpat = s;
664 scrollTo(s->scrollx,s->scrolly);
665 cur_track = tracks[s->track];
666 ui->pattern_timeline->ticks_offset = s->tick;
671 int PianoRoll::note2ypix(int note){
672 int udy = 6*(note + (note+7)/12 + note/12) + 12;
673 return 900 - udy + 1;
676 int PianoRoll::tick2xpix(int tick){
677 return tick*zoom*4 / TICKS_PER_BEAT;
680 int PianoRoll::xpix2tick(int xpix){
681 return xpix*TICKS_PER_BEAT / (zoom*4);
684 int PianoRoll::quantize(int tick){
685 if(trip_flag){
686 int T = (q_tick*4) / 3;
687 int P = tick/(q_tick*4) * (q_tick*4);
688 int Q = (tick%(q_tick*4));
689 return P + Q / T * T;
691 else{
692 return tick/(q_tick) * q_tick;
697 void PianoRoll::set_zoom(int z){
698 zoom = z;
699 relayout();
703 mevent* PianoRoll::over_note(){
704 mevent* e = cur_seqpat->p->events->next;
706 int X = event_x()+scrollx;
707 int Y = event_y()+scrolly;
709 int cy, lx, rx;
710 while(e){
711 if(e->type == MIDI_NOTE_ON){
712 cy = note2ypix(e->value1);
713 lx = tick2xpix(e->tick);
714 rx = tick2xpix(e->tick+e->dur);
715 if(X > lx && X < rx &&
716 Y < cy+12 && Y > cy){
717 return e;
720 e = e->next;
723 return NULL;
728 void PianoRoll::update(int pos){
729 if(!is_backend_playing() || !cur_seqpat){
730 return;
732 if(pos < cur_seqpat->tick){
733 return;
735 int X1 = tick2xpix(pos-cur_seqpat->tick);
736 int X2 = X1 - scrollx;
737 if(X2 < 0){
738 scrollTo(X1-50<0?0:X1-50,scrolly);
740 if(X2 > w()-30){
741 scrollTo(X1-50,scrolly);
746 void PianoRoll::unselect_all(){
747 mevent* e = cur_seqpat->p->events;
748 while(e){
749 if(e->type == MIDI_NOTE_ON && e->selected==1){
750 e->selected = 0;
752 e = e->next;
758 void PianoRoll::get_event_color(mevent* e, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3){
760 int T1,T2;
761 int tmp;
762 if(delete_flag){
763 if(e->selected){
764 *c1 = fltk::color(229,79,75);
765 *c2 = fltk::color(120,60,58);
766 *c3 = fltk::color(225,131,109);
767 return;
771 if(box_flag){
772 T1=box_t1;
773 T2=box_t2;
774 int N1 = box_n1;
775 int N2 = box_n2;
776 int N = e->value1;
777 if(T1>T2){SWAP(T1,T2);}
778 if(N1<N2){SWAP(N1,N2);}
779 if(e->tick+e->dur > T1 && e->tick < T2 && N >= N2 && N <= N1){
780 *c1 = fltk::color(108,229,75);
781 *c2 = fltk::color(71,120,59);
782 *c3 = fltk::color(108,229,75);
783 return;
787 if(e->selected){
788 *c1 = fltk::color(255,248,47);
789 *c2 = fltk::color(140,137,46);
790 *c3 = fltk::color(232,255,37);
791 return;
794 *c1 = fltk::color(169,75,229);
795 *c2 = fltk::color(95,58,119);
796 *c3 = fltk::color(198,109,225);
800 void PianoRoll::apply_box(){
801 mevent* e = cur_seqpat->p->events->next;
802 int tmp;
803 int T1=box_t1;
804 int T2=box_t2;
805 int N1 = box_n1;
806 int N2 = box_n2;
808 if(T1>T2){SWAP(T1,T2);}
809 if(N1<N2){SWAP(N1,N2);}
810 while(e){
811 int N = e->value1;
812 if(e->type == MIDI_NOTE_ON &&
813 e->tick+e->dur > T1 && e->tick < T2 &&
814 N >= N2 && N <= N1){
815 e->selected = 1;
817 e = e->next;
821 void PianoRoll::apply_insert(){
822 if(insert_note > 127 || insert_note < 0){
823 return;
826 int tmp;
827 int T1 = insert_torig;
828 int T2 = T1 + insert_toffset;
829 if(T1>T2){SWAP(T1,T2);}
831 if(T1 < 0){
832 return;
835 pattern* p = cur_seqpat->p;
836 Command* c=new CreateNote(p,insert_note,config.defaultvelocity,T1,T2-T1);
837 set_undo(c);
838 undo_push(1);
840 cur_track->restate();
843 void PianoRoll::apply_delete(){
844 Command* c;
845 mevent* e;
846 mevent* next;
847 pattern* p = cur_seqpat->p;
848 int N=0;
850 e = cur_seqpat->p->events->next;
851 while(e){
852 next = e->next;
853 if(e->selected && e->type == MIDI_NOTE_ON){
854 c=new DeleteNote(p,e);
855 set_undo(c);
856 N++;
858 e = next;
860 undo_push(N);
862 cur_track->restate();
865 void PianoRoll::apply_move(){
866 if(move_toffset==0 && move_noffset==0){
867 return;
870 pattern* p = cur_seqpat->p;
871 mevent* e = p->events->next;
872 while(e){
873 int K = e->value1+move_noffset;
874 int T = e->tick+move_toffset;
875 if(e->type == MIDI_NOTE_ON && e->selected && (T<0 || K < 0 || K > 127)){
876 return;
878 e = e->next;
882 Command* c;
883 e = p->events->next;
885 mevent* next;
886 int M=0;
887 for(int i=0; i<tracks.size(); i++){
888 e = p->events->next;
889 while(e){
890 next = e->next;
891 if(e->selected && e->modified == 0){
892 int K = e->value1 + move_noffset;
893 int T = e->tick + move_toffset;
894 e->modified = 1;
895 c=new MoveNote(p,e,T,K);
896 set_undo(c);
897 M++;
899 e = next;
902 undo_push(M);
904 e = p->events->next;
905 while(e){
906 if(e->modified){e->modified=0;}
907 e = e->next;
910 cur_track->restate();
913 void PianoRoll::apply_paste(){
919 void PianoRoll::apply_rresize(){
920 if(rresize_toffset==0){
921 return;
924 Command* c;
925 mevent* e;
926 mevent* next;
927 pattern* p = cur_seqpat->p;
928 int tmp;
929 int N=0;
931 e = p->events->next;
932 while(e){
933 next = e->next;
934 if(e->type == MIDI_NOTE_ON && e->selected && e->modified == 0){
935 e->modified = 1;
936 int W = e->dur;
937 int R = rresize_toffset;
938 if(W+R < q_tick){
939 R = q_tick-W;
941 c=new ResizeNote(p,e,W+R);
942 set_undo(c);
943 N++;
945 e = next;
948 e = p->events->next;
949 while(e){
950 if(e->modified){e->modified=0;}
951 e = e->next;
954 cur_track->restate();
955 undo_push(N);
958 void PianoRoll::apply_lresize(){
959 if(lresize_toffset==0){
960 return;
963 Command* c;
964 mevent* e;
965 mevent* next;
966 pattern* p = cur_seqpat->p;
967 int tmp;
968 int N=0;
970 e = p->events->next;
971 while(e){
972 if(e->type == MIDI_NOTE_ON && e->selected){
973 if(e->tick + lresize_toffset < 0){
974 return;
977 e = e->next;
980 e = p->events->next;
981 while(e){
982 next = e->next;
983 if(e->type == MIDI_NOTE_ON && e->selected && e->modified == 0){
984 e->modified = 1;
985 int T = e->tick;
986 int W = e->dur;
987 int R = lresize_toffset;
989 if(R > W-q_tick){
990 R = W-q_tick;
993 mevent* etmp = e->prev;
994 c=new ResizeNote(p,e,W-R);
995 set_undo(c);
997 e = etmp->next;
998 c=new MoveNote(p,e,T+R,e->value1);
999 set_undo(c);
1001 N+=2;
1003 e = next;
1006 e = p->events->next;
1007 while(e){
1008 if(e->modified){e->modified=0;}
1009 e = e->next;
1012 cur_track->restate();
1013 undo_push(N);
1024 int PianoRoll::over_rhandle(mevent* e){
1025 int X = event_x()+scrollx;
1026 int Y = event_y()+scrolly;
1027 int X1 = tick2xpix(e->tick);
1028 int X2 = X1 + tick2xpix(e->dur);
1029 int Y1 = note2ypix(e->value1);
1030 int Y2 = Y1 + 12;
1032 if(X2-X1 < resize_handle_width*3){
1033 return 0;
1036 return (Y > Y1 && Y < Y2 && X < X2 && X > X2 - resize_handle_width);
1039 int PianoRoll::over_lhandle(mevent* e){
1040 int X = event_x()+scrollx;
1041 int Y = event_y()+scrolly;
1042 int X1 = tick2xpix(e->tick);
1043 int X2 = X1 + tick2xpix(e->dur);
1044 int Y1 = note2ypix(e->value1);
1045 int Y2 = Y1 + 12;
1047 if(X2-X1 < resize_handle_width*3){
1048 return 0;
1051 return (Y > Y1 && Y < Y2 && X < X1+ resize_handle_width +1 && X > X1 + 1);