Fixed bug that crashed on saving after importing.
[epichord.git] / src / pianoroll.cpp
blob8832f2769098f66440bc1e3f3a92be977c9e4202
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>
26 #include <fltk/events.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include "ui.h"
32 #include "util.h"
34 #include "backend.h"
36 #include "uihelper.h"
38 extern UI* ui;
39 extern std::vector<track*> tracks;
41 extern struct conf config;
43 using namespace fltk;
45 #define SWAP(X,Y) tmp=X; X=Y; Y=tmp;
47 PianoRoll::PianoRoll(int x, int y, int w, int h, const char* label = 0) : fltk::Widget(x, y, w, h, label) {
48 wkeyh = 12;
49 bkeyh = 7;
50 cur_seqpat = NULL;
52 zoom = 15;
53 zoom_n = 3;
55 q_tick = 32;
57 xp_last = 0;
58 yp_last = 0;
60 box_flag = 0;
62 move_toffset = 0;
65 lresize_flag = 0;
66 rresize_flag = 0;
68 resize_arrow = 0;
69 resize_e = NULL;
70 resize_handle_width = 4;
73 int PianoRoll::handle(int event){
74 Command* c;
75 pattern* p;
76 mevent* e;
78 int X = event_x();
79 int Y = event_y();
81 switch(event){
82 case fltk::ENTER:
83 return 1;
84 case fltk::FOCUS:
85 return 1;
86 case fltk::SHORTCUT:
87 if(event_key()==fltk::DeleteKey){
88 apply_delete();
89 delete_flag = 0;
90 redraw();
91 resize_arrow = 0;
92 ui->event_edit->redraw();
93 return 1;
95 if(event_state(CTRL) && event_key()=='c'){
96 //printf("roll copy\n");
97 return 1;
99 if(zoom_out_key(event_key(),event_state())){
100 if(zoom_n > 1){
101 zoom_n--;
102 set_zoom(30*(1<<zoom_n)/16);
103 ui->pattern_timeline->zoom = zoom;
104 ui->pattern_timeline->update(get_play_position());
105 ui->pattern_timeline->redraw();
106 ui->event_edit->zoom = zoom;
107 ui->event_edit->redraw();
109 redraw();
110 return 1;
112 if(zoom_in_key(event_key(),event_state())){
113 if(zoom_n < 8){
114 zoom_n++;
115 set_zoom(30*(1<<zoom_n)/16);
116 ui->pattern_timeline->zoom = zoom;
117 ui->pattern_timeline->update(get_play_position());
118 ui->pattern_timeline->redraw();
119 ui->event_edit->zoom = zoom;
120 ui->event_edit->redraw();
122 redraw();
123 return 1;
125 return 0;
126 case fltk::PUSH:
127 take_focus();
128 e = over_note();
129 if(event_button()==1){//left mouse
130 if(e==NULL){//new note init
131 if(event_state()&fltk::SHIFT){//begin box
132 box_flag = 1;
133 box_x1=X;
134 box_x2=X;
135 box_y1=Y;
136 box_y2=Y;
137 box_t1=xpix2tick(X);
138 box_t2=box_t1;
139 box_n1=ypix2note(Y,1);
140 box_n2=box_n1;
142 else{//begin insert
143 insert_flag = 1;
144 insert_torig = quantize(xpix2tick(event_x()));
145 insert_toffset = q_tick;
146 //new_orig_t = new_left_t;
147 insert_note = ypix2note(event_y(),1);
149 last_note = insert_note;
150 if(config.playinsert){
151 ui->keyboard->play_note(last_note,0);
156 else{
158 if(!(e->selected) && !(event_state()&fltk::SHIFT)){
159 unselect_all();
161 e->selected = 1;
162 resize_arrow_color = fltk::color(128,128,0);
164 if(over_rhandle(e,X,Y)){//resize
165 rresize_flag = 1;
166 rresize_torig = e->tick+e->dur;
167 rresize_toffset = 0;
169 else if(over_lhandle(e,X,Y)){//resize move
170 lresize_flag = 1;
171 lresize_torig = e->tick;
172 lresize_toffset = 0;
174 else{//begin move
175 move_flag = 1;
177 move_torig = e->tick;
178 move_qoffset = e->tick - quantize(e->tick);
180 move_toffset = -move_qoffset;
182 //move_offset = quantize(xpix2tick(X)) - move_torig - move_qoffset;
183 //move_toffset = 0;
184 move_offset = X - tick2xpix(e->tick);
185 move_norig = ypix2note(event_y(),1);
186 move_noffset = 0;
188 last_note = move_norig;
189 if(config.playmove){
190 ui->keyboard->play_note(last_note,0);
195 else if(event_button()==2){//middle mouse
196 //button initiates paste
198 else if(event_button()==3){//right mouse
199 if(e==NULL){
200 unselect_all();
202 ui->event_edit->redraw();
204 else{//set up for deletion
205 if(!(e->selected) && !(event_state()&fltk::SHIFT)){
206 unselect_all();
208 e->selected = 1;
209 delete_flag = 1;
210 resize_arrow_color = fltk::color(120,60,58);
214 redraw();
215 return 1;
216 case fltk::DRAG:
218 if(box_flag){
219 box_x2 = X;
220 box_y2 = Y;
221 box_t2 = xpix2tick(X);
222 box_n2 = ypix2note(Y,1);
224 else if(insert_flag){
225 insert_toffset = quantize(xpix2tick(X)+q_tick) - insert_torig;
226 if(insert_toffset<=0){
227 insert_toffset -= q_tick;
229 insert_note = ypix2note(Y,1);
230 if(insert_note != last_note){
231 if(config.playinsert){//play on insert
232 ui->keyboard->release_note(last_note,0);
233 ui->keyboard->play_note(insert_note,0);
235 last_note = insert_note;
238 else if(move_flag){
239 move_toffset = quantize(xpix2tick(X - move_offset)) - move_torig;
240 move_noffset = ypix2note(Y,1) - move_norig;
241 int N = move_norig+move_noffset;
242 if(N != last_note){
243 if(config.playmove){//play on move
244 ui->keyboard->release_note(last_note,0);
245 ui->keyboard->play_note(N,0);
247 last_note = N;
250 else if(rresize_flag){
251 rresize_toffset = quantize(xpix2tick(X)) + q_tick - rresize_torig;
253 else if(lresize_flag){
254 lresize_toffset = quantize(xpix2tick(X)) - lresize_torig;
256 redraw();
257 return 1;
258 case fltk::RELEASE:
259 e = over_note();
260 if(event_button()==1){
261 if(box_flag){
262 apply_box();
263 ui->event_edit->redraw();
264 box_flag=0;
266 else if(rresize_flag){
267 apply_rresize();
268 rresize_flag = 0;
269 resize_arrow = 0;
270 ui->event_edit->redraw();
272 else if(lresize_flag){
273 apply_lresize();
274 lresize_flag = 0;
275 resize_arrow = 0;
276 ui->event_edit->redraw();
278 else if(insert_flag){
279 apply_insert();
281 insert_flag = 0;
283 ui->keyboard->release_note(insert_note,0);
284 ui->keyboard->redraw();
285 ui->event_edit->has[0]=1;
286 ui->event_edit->has[1]=1;
287 ui->event_edit->redraw();
288 ui->event_menu->redraw();
290 else if(move_flag){
291 apply_move();
292 move_flag = 0;
294 midi_track_off(cur_seqpat->track);
295 ui->keyboard->release_note(last_note,0);
296 ui->keyboard->release_note(move_norig+move_noffset,0);
297 ui->keyboard->redraw();
298 ui->event_edit->redraw();
300 insert_flag=0;
301 move_flag=0;
303 if(event_button()==3){
304 mevent* over_n = over_note();
305 if(delete_flag && over_n){
306 if(over_n->selected){
307 apply_delete();
308 midi_track_off(cur_seqpat->track);
309 ui->event_edit->redraw();
312 delete_flag=0;
313 resize_arrow = 0;
315 redraw();
317 return 1;
319 case fltk::MOVE:
320 e = over_note();
321 if(e){
322 if(over_rhandle(e,X,Y)){
323 if(resize_e != e || resize_arrow != 1){
324 if(e->selected){resize_arrow_color = fltk::color(128,128,0);}
325 else{resize_arrow_color = fltk::color(95,58,119);}
326 resize_e = e;
327 resize_arrow = 1;
328 resize_x = tick2xpix(e->tick + e->dur) - resize_handle_width;
329 resize_y = note2ypix(e->value1);
330 redraw();
333 else if(over_lhandle(e,X,Y)){
334 if(resize_e != e || resize_arrow != 1){
335 if(e->selected){resize_arrow_color = fltk::color(128,128,0);}
336 else{resize_arrow_color = fltk::color(95,58,119);}
337 resize_e = e;
338 resize_arrow = -1;
339 resize_x = tick2xpix(e->tick)+1;
340 resize_y = note2ypix(e->value1);
341 redraw();
344 else{
345 if(resize_e != e || resize_arrow != 0){
346 resize_e = e;
347 resize_arrow = 0;
348 redraw();
352 else{
353 if(resize_arrow != 0){
354 resize_arrow = 0;
355 redraw();
359 return 1;
361 return 0;
364 void PianoRoll::draw(){
366 fltk::setcolor(fltk::GRAY05);
367 fltk::fillrect(0,0,w(),h());
369 fltk::setcolor(fltk::GRAY20);
370 for(int i=12; i<h(); i+=12){
371 fltk::drawline(0,i,w(),i);
373 for(int i=zoom; i<w(); i+=zoom){
374 fltk::drawline(i,0,i,h());
377 fltk::setcolor(fltk::GRAY30);
378 for(int i=12*5; i<h(); i+=12*7){
379 fltk::drawline(0,i,w(),i);
382 fltk::setcolor(fltk::GRAY50);
383 for(int i=zoom*4; i<w(); i+=zoom*4){
384 fltk::drawline(i,0,i,h());
387 fltk::setcolor(fltk::WHITE);
388 int M = config.beats_per_measure;
389 for(int i=zoom*4*M; i<w(); i+=zoom*4*M){
390 fltk::fillrect(i,0,1,h());
393 fltk::setcolor(fltk::color(128,0,0));
394 int rightend = tick2xpix(cur_seqpat->dur);
395 fltk::fillrect(rightend,0,1,h());
397 fltk::setcolor(fltk::color(128,128,0));
398 fltk::drawline(0,12*40,w(),12*40);
400 int tmp;
401 if(insert_flag){
402 fltk::setcolor(fltk::BLUE);
403 int T1 = insert_torig;
404 int T2 = T1 + insert_toffset;
405 if(T1>T2){SWAP(T1,T2);}
406 int X = tick2xpix(T1)+1;
407 int Y = note2ypix(insert_note);
408 int W = tick2xpix(T2) - X;
409 fltk::fillrect(X,Y,W,11);
412 if(move_flag){
413 fltk::setcolor(fltk::MAGENTA);
414 mevent* ptr = cur_seqpat->p->events->next;
415 while(ptr){
416 if(ptr->type == MIDI_NOTE_ON && ptr->selected){
417 int X = tick2xpix(ptr->tick+move_toffset)+1;
418 int Y = note2ypix(ptr->value1+move_noffset);
419 int W = tick2xpix(ptr->dur);
420 fltk::fillrect(X,Y,W-1,1);
421 fltk::fillrect(X,Y+11,W-1,1);
422 fltk::fillrect(X,Y,1,11);
423 fltk::fillrect(X+W-2,Y,1,11);
425 ptr=ptr->next;
431 //draw all notes
432 mevent* e = cur_seqpat->p->events->next;
434 fltk::Color c1,c2,c3;
436 while(e){
437 if(e->type == MIDI_NOTE_ON){
438 //fltk::fillrect(tick2xpix(e->tick),note2ypix(e->value),e->dur,11);
440 int R1 = rresize_flag&&e->selected ? rresize_toffset : 0;
441 int R2 = lresize_flag&&e->selected ? lresize_toffset : 0;
443 int T1 = e->tick + R2;
444 int T2 = e->tick+e->dur + R1;
446 if(T1 >= T2-q_tick && e->selected){
447 if(rresize_flag){
448 T1 = e->tick;
449 T2 = T1 + q_tick;
451 else if(lresize_flag){
452 T2 = e->tick + e->dur;
453 T1 = T2 - q_tick;
457 int X = tick2xpix(T1) + 1;
458 int Y = note2ypix(e->value1);
459 int W = tick2xpix(T2) - X;
460 get_event_color(e,&c1,&c2,&c3);
462 fltk::setcolor(c1);
463 fltk::fillrect(X+1,Y+1,W-1,10);
465 fltk::setcolor(c2);
466 fltk::fillrect(X,Y+11,W,1);
467 fltk::fillrect(X+W-1,Y+1,1,11);
469 fltk::setcolor(c3);
470 fltk::fillrect(X,Y,W,1);
471 fltk::fillrect(X,Y,1,11);
473 e=e->next;
477 if(!rresize_flag && !lresize_flag){
478 if(resize_arrow > 0){
479 setcolor(resize_arrow_color);
481 int W = resize_handle_width;
482 int H = 12;
483 int X = resize_x;
484 int Y = resize_y;
486 addvertex(X,Y);
487 addvertex(X,Y+H);
488 addvertex(X+W,Y+H/2);
489 fillpath();
491 else if(resize_arrow < 0){
492 setcolor(resize_arrow_color);
494 int W = resize_handle_width;
495 int H = 12;
496 int X = resize_x;
497 int Y = resize_y;
499 addvertex(X+W,Y);
500 addvertex(X+W,Y+H);
501 addvertex(X,Y+H/2);
502 fillpath();
507 if(box_flag){
508 fltk::setcolor(fltk::GREEN);
509 int X1,X2,Y1,Y2;
510 if(box_x1>box_x2){
511 X1=box_x2;
512 X2=box_x1;
514 else{
515 X1=box_x1;
516 X2=box_x2;
518 if(box_y1>box_y2){
519 Y1=box_y2;
520 Y2=box_y1;
522 else{
523 Y1=box_y1;
524 Y2=box_y2;
526 fltk::fillrect(X1,Y1,X2-X1,1);
527 fltk::fillrect(X1,Y1,1,Y2-Y1);
528 fltk::fillrect(X2,Y1,1,Y2-Y1);
529 fltk::fillrect(X1,Y2,X2-X1,1);
535 static int kludge = 4; //very powerful magic
536 void PianoRoll::layout(){
538 /* the kludge is used so the fltk::ScrollGroup can update
539 widgets not contained within it. Better solution, the
540 scrollgroup could do its callback if it scrolls.
541 Subclassing fltk::ScrollGroup to add this behavior failed. */
542 if(kludge != 0){
543 kludge--;
544 return;
547 ui->pattern_timeline->zoom = zoom;
548 ui->event_edit->zoom = zoom;
551 if(cur_seqpat){
552 int W = tick2xpix(cur_seqpat->dur);
553 resize(W+300,h());
558 int wp = ui->pattern_scroll->w();
559 if(wp > w()){
560 w(wp+120);
563 int hp = ui->pattern_scroll->h();
564 if(hp > h()){
565 h(hp+120);
569 int xp = ui->pattern_scroll->xposition();
570 int yp = ui->pattern_scroll->yposition();
573 if(xp > w() - wp){
574 xp = w() - wp;
575 ui->pattern_scroll->scrollTo(xp,yp);
578 ui->pattern_timeline->scroll = xp;
579 ui->event_edit->scroll = xp;
580 ui->keyboard->scroll = yp;
582 if(cur_seqpat){
583 cur_seqpat->scrolly = yp;
584 cur_seqpat->scrollx = xp;
587 if(xp_last != xp){
588 ui->pattern_timeline->redraw();
589 ui->event_edit->redraw();
591 if(yp_last != yp){
592 ui->keyboard->redraw();
595 yp_last = yp;
596 xp_last = xp;
601 void PianoRoll::load(seqpat* s){
603 ui->pattern_scroll->scrollTo(0,300);
605 cur_seqpat = s;
606 cur_track = tracks[s->track];
607 int W = tick2xpix(s->dur);
608 resize(W+300,h());
610 ui->pattern_timeline->ticks_offset = s->tick;
615 int PianoRoll::note2ypix(int note){
616 int udy = 6*(note + (note+7)/12 + note/12) + 12;
617 return h() - udy + 1;
620 int PianoRoll::tick2xpix(int tick){
621 return tick*zoom*4 / 128;
624 int PianoRoll::xpix2tick(int xpix){
625 return xpix*128 / (zoom*4);
628 int PianoRoll::quantize(int tick){
629 return tick/q_tick * q_tick;
633 void PianoRoll::set_zoom(int z){
634 zoom = z;
635 relayout();
636 //int W = tick2xpix(cur_seqpat->dur);
637 //resize(W+300,h());
641 mevent* PianoRoll::over_note(){
642 mevent* e = cur_seqpat->p->events->next;
644 int cy, lx, rx;
645 while(e){
646 if(e->type == MIDI_NOTE_ON){
647 cy = note2ypix(e->value1);
648 lx = tick2xpix(e->tick);
649 rx = tick2xpix(e->tick+e->dur);
650 if(event_x() > lx && event_x() < rx &&
651 event_y() < cy+12 && event_y() > cy){
652 return e;
655 e = e->next;
658 return NULL;
663 void PianoRoll::update(int pos){
664 if(!is_backend_playing() || !cur_seqpat){
665 return;
667 int wp = ui->pattern_scroll->w();
668 int xp = ui->pattern_scroll->xposition();
669 int yp = ui->pattern_scroll->yposition();
670 int X1 = tick2xpix(pos-cur_seqpat->tick);
671 int X2 = X1 - xp;
672 if(X1 > w()-40){
673 return;
675 if(X2 < 0){
676 ui->pattern_scroll->scrollTo(X1-50<0?0:X1-50,yp);
678 if(X2 > wp-30){
679 ui->pattern_scroll->scrollTo(X1-50,yp);
684 void PianoRoll::unselect_all(){
685 mevent* e = cur_seqpat->p->events;
686 while(e){
687 if(e->type == MIDI_NOTE_ON && e->selected==1){
688 e->selected = 0;
690 e = e->next;
696 void PianoRoll::get_event_color(mevent* e, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3){
698 int T1,T2;
699 int tmp;
700 if(delete_flag){
701 if(e->selected){
702 *c1 = fltk::color(229,79,75);
703 *c2 = fltk::color(120,60,58);
704 *c3 = fltk::color(225,131,109);
705 return;
709 if(box_flag){
710 T1=box_t1;
711 T2=box_t2;
712 int N1 = box_n1;
713 int N2 = box_n2;
714 int N = e->value1;
715 if(T1>T2){SWAP(T1,T2);}
716 if(N1<N2){SWAP(N1,N2);}
717 if(e->tick+e->dur > T1 && e->tick < T2 && N >= N2 && N <= N1){
718 *c1 = fltk::color(108,229,75);
719 *c2 = fltk::color(71,120,59);
720 *c3 = fltk::color(108,229,75);
721 return;
725 if(e->selected){
726 *c1 = fltk::color(255,248,47);
727 *c2 = fltk::color(140,137,46);
728 *c3 = fltk::color(232,255,37);
729 return;
732 *c1 = fltk::color(169,75,229);
733 *c2 = fltk::color(95,58,119);
734 *c3 = fltk::color(198,109,225);
738 void PianoRoll::apply_box(){
739 mevent* e = cur_seqpat->p->events->next;
740 int tmp;
741 int T1=box_t1;
742 int T2=box_t2;
743 int N1 = box_n1;
744 int N2 = box_n2;
746 if(T1>T2){SWAP(T1,T2);}
747 if(N1<N2){SWAP(N1,N2);}
748 while(e){
749 int N = e->value1;
750 if(e->type == MIDI_NOTE_ON &&
751 e->tick+e->dur > T1 && e->tick < T2 &&
752 N >= N2 && N <= N1){
753 e->selected = 1;
755 e = e->next;
759 void PianoRoll::apply_insert(){
760 if(insert_note > 127 || insert_note < 0){
761 return;
764 int tmp;
765 int T1 = insert_torig;
766 int T2 = T1 + insert_toffset;
767 if(T1>T2){SWAP(T1,T2);}
769 if(T1 < 0){
770 return;
773 pattern* p = cur_seqpat->p;
774 Command* c=new CreateNote(p,insert_note,config.defaultvelocity,T1,T2-T1);
775 set_undo(c);
776 undo_push(1);
778 cur_track->restate();
781 void PianoRoll::apply_delete(){
782 Command* c;
783 mevent* e;
784 mevent* next;
785 pattern* p = cur_seqpat->p;
786 int N=0;
788 e = cur_seqpat->p->events->next;
789 while(e){
790 next = e->next;
791 if(e->selected && e->type == MIDI_NOTE_ON){
792 c=new DeleteNote(p,e);
793 set_undo(c);
794 N++;
796 e = next;
798 undo_push(N);
800 cur_track->restate();
803 void PianoRoll::apply_move(){
804 if(move_toffset==0 && move_noffset==0){
805 return;
808 pattern* p = cur_seqpat->p;
809 mevent* e = p->events->next;
810 while(e){
811 int K = e->value1+move_noffset;
812 int T = e->tick+move_toffset;
813 if(e->type == MIDI_NOTE_ON && e->selected && (T<0 || K < 0 || K > 127)){
814 return;
816 e = e->next;
820 Command* c;
821 e = p->events->next;
823 mevent* next;
824 int M=0;
825 for(int i=0; i<tracks.size(); i++){
826 e = p->events->next;
827 while(e){
828 next = e->next;
829 if(e->selected && e->modified == 0){
830 int K = e->value1 + move_noffset;
831 int T = e->tick + move_toffset;
832 e->modified = 1;
833 c=new MoveNote(p,e,T,K);
834 set_undo(c);
835 M++;
837 e = next;
840 undo_push(M);
842 e = p->events->next;
843 while(e){
844 if(e->modified){e->modified=0;}
845 e = e->next;
848 cur_track->restate();
851 void PianoRoll::apply_paste(){
857 void PianoRoll::apply_rresize(){
858 if(rresize_toffset==0){
859 return;
862 Command* c;
863 mevent* e;
864 mevent* next;
865 pattern* p = cur_seqpat->p;
866 int tmp;
867 int N=0;
869 e = p->events->next;
870 while(e){
871 next = e->next;
872 if(e->type == MIDI_NOTE_ON && e->selected && e->modified == 0){
873 e->modified = 1;
874 int W = e->dur;
875 int R = rresize_toffset;
876 if(W+R < q_tick){
877 R = q_tick-W;
879 c=new ResizeNote(p,e,W+R);
880 set_undo(c);
881 N++;
883 e = next;
886 e = p->events->next;
887 while(e){
888 if(e->modified){e->modified=0;}
889 e = e->next;
892 cur_track->restate();
893 undo_push(N);
896 void PianoRoll::apply_lresize(){
897 if(lresize_toffset==0){
898 return;
901 Command* c;
902 mevent* e;
903 mevent* next;
904 pattern* p = cur_seqpat->p;
905 int tmp;
906 int N=0;
908 e = p->events->next;
909 while(e){
910 if(e->type == MIDI_NOTE_ON && e->selected){
911 if(e->tick + lresize_toffset < 0){
912 return;
915 e = e->next;
918 e = p->events->next;
919 while(e){
920 next = e->next;
921 if(e->type == MIDI_NOTE_ON && e->selected && e->modified == 0){
922 e->modified = 1;
923 int T = e->tick;
924 int W = e->dur;
925 int R = lresize_toffset;
927 if(R > W-q_tick){
928 R = W-q_tick;
931 mevent* etmp = e->prev;
932 c=new ResizeNote(p,e,W-R);
933 set_undo(c);
935 e = etmp->next;
936 c=new MoveNote(p,e,T+R,e->value1);
937 set_undo(c);
939 N+=2;
941 e = next;
944 e = p->events->next;
945 while(e){
946 if(e->modified){e->modified=0;}
947 e = e->next;
950 cur_track->restate();
951 undo_push(N);
962 int PianoRoll::over_rhandle(mevent* e, int X, int Y){
963 int X1 = tick2xpix(e->tick);
964 int X2 = X1 + tick2xpix(e->dur);
965 int Y1 = note2ypix(e->value1);
966 int Y2 = Y1 + 12;
968 if(X2-X1 < resize_handle_width*3){
969 return 0;
972 return (Y > Y1 && Y < Y2 && X < X2 && X > X2 - resize_handle_width);
975 int PianoRoll::over_lhandle(mevent* e, int X, int Y){
976 int X1 = tick2xpix(e->tick);
977 int X2 = X1 + tick2xpix(e->dur);
978 int Y1 = note2ypix(e->value1);
979 int Y2 = Y1 + 12;
981 if(X2-X1 < resize_handle_width*3){
982 return 0;
985 return (Y > Y1 && Y < Y2 && X < X1+ resize_handle_width +1 && X > X1 + 1);