Fixed timeline drawing problem at extreme range.
[epichord.git] / src / arranger.cpp
blob45de7da3c04b8a29dc3017b7649203e7b3d9a1eb
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 <vector>
25 #include <fltk/Group.h>
26 #include <fltk/Widget.h>
27 #include <fltk/events.h>
29 #include "ui.h"
31 #include "uihelper.h"
33 #include "util.h"
35 #include "backend.h"
37 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 Arranger::Arranger(int x, int y, int w, int h, const char* label = 0) : fltk::Widget(x, y, w, h, label) {
48 new_default_w = 128*4;
49 delete_flag = 0;
50 move_flag = 0;
51 paste_flag = 0;
52 main_sel = NULL;
54 zoom = 30;
55 zoom_n = 4;
57 q_tick = 128*4;
59 insert_flag = 0;
60 box_flag = 0;
61 rresize_flag = 0;
62 lresize_flag = 0;
64 last_handle == NULL;
66 color_flag = 0;
68 maxt = 0;
71 fakew = 1000;
72 fakeh = 16*30;
73 if(fakeh < h){fakeh = h;}
77 int Arranger::handle(int event){
78 Command* c;
80 int X = event_x();
81 int Y = event_y();
83 seqpat* s;
85 switch(event){
86 case fltk::FOCUS:
87 return 1;
88 case fltk::ENTER:
89 return 1;
90 case fltk::KEYUP:
92 return 0;
93 case fltk::MOUSEWHEEL:
94 s = over_seqpat();
95 if(s){
96 s->autocomplete();
97 if(event_dy()>0){
98 s->prev_layer();
100 else if(event_dy()<0){
101 s->next_layer();
103 s->restate();
104 redraw();
106 return 1;
107 case fltk::SHORTCUT:
108 if(event_state() && event_key()=='c'){
110 return 1;
112 if(event_state() && event_key()=='v'){
114 return 1;
116 if(event_state() && event_key()=='z'){
118 return 1;
120 if(event_key()==fltk::DeleteKey){
121 apply_delete();
122 delete_flag = 0;
123 redraw();
124 return 1;
126 if(zoom_out_key(event_key(),event_state())){
127 //if(event_key()==fltk::LeftKey){
128 if(zoom_n > 1){
129 zoom_n--;
130 zoom = 30*(1<<zoom_n)/16;
131 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
132 ui->song_timeline->update(get_play_position());
133 ui->song_timeline->redraw();
134 relayout();
136 redraw();
137 return 1;
139 if(zoom_in_key(event_key(),event_state())){
140 //if(event_key()==fltk::RightKey){
141 if(zoom_n < 8){
142 zoom_n++;
143 zoom = 30*(1<<zoom_n)/16;
144 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
145 ui->song_timeline->update(get_play_position());
146 ui->song_timeline->redraw();
147 relayout();
149 redraw();
150 return 1;
152 return 0;
153 case fltk::PUSH:
154 take_focus();
155 if(event_button()==1){//left mouse
156 seqpat* s = over_seqpat();
157 if(s==NULL){
158 if(color_flag){//do nothing
160 else if(event_state()&fltk::SHIFT){//begin box
161 box_flag = 1;
162 box_x1=X;
163 box_x2=X;
164 box_y1=Y;
165 box_y2=Y;
166 box_t1=xpix2tick(X);
167 box_t2=box_t1;
168 box_k1=Y/30;
169 box_k2=box_k1;
171 else{//begin insert
172 insert_flag = 1;
173 insert_torig = xpix2tick(X)/q_tick*q_tick;
174 insert_toffset = q_tick;
175 insert_track = event_y() / 30;
178 else{
179 main_sel = s;
180 if(color_flag){
181 color_sel = s->p;
182 color_orig_x = event_x();
183 color_orig_y = event_y();
184 color_orig_h = color_sel->h;
185 color_orig_v = color_sel->v;
186 color_h = color_orig_h;
187 color_v = color_orig_v;
188 return 1;
190 if(!s->selected && !(event_state()&SHIFT)){
191 unselect_all();
193 s->selected = 1;
194 if(fltk::event_clicks() > 0){//'double click'
195 ui->piano_roll->load(s);
196 ui->event_edit->load(s);
197 ui->piano_roll->scrollTo(s->scrollx,s->scrolly);
198 ui->pattern_timeline->update(get_play_position());
199 ui->keyboard->cur_port = tracks[s->track]->port;
200 ui->keyboard->cur_chan = tracks[s->track]->chan;
201 ui->track_info->set_rec(s->track);
202 set_rec_track(s->track);
203 show_pattern_edit();
204 return 1;
207 if(over_lhandle(s,X,Y)){//begin resize
208 lresize_flag = 1;
209 lresize_torig = s->tick;
210 lresize_toffset = 0;
212 else if(over_rhandle(s,X,Y)){//begin resizemove
213 rresize_flag = 1;
214 rresize_torig = s->tick+s->dur;
215 rresize_toffset = 0;
218 else{//begin move
219 move_flag = 1;
220 move_torig = s->tick;
221 move_toffset = 0;
222 move_korig = s->track;
223 move_koffset = 0;
224 move_x = X;
225 move_y = Y;
226 move_offset = xpix2tick(X)/q_tick*q_tick - s->tick;
230 else if(event_button()==2){//middle mouse
231 seqpat* s = over_seqpat();
232 if(color_flag && s){
233 s->p->h = color_h;
234 s->p->v = color_v;
235 s->p->regen_colors();
236 redraw();
237 return 1;
239 if(main_sel){
240 paste_flag = 1;
241 paste_t = quantize(xpix2tick(event_x()));
242 paste_track = event_y() / 30;
245 else if(event_button()==3){//right mouse
246 seqpat* s = over_seqpat();
247 if(color_flag && s){
248 seqpat* ptr = tracks[s->track]->head->next;
249 while(ptr){
250 ptr->p->h = color_h;
251 ptr->p->v = color_v;
252 ptr->p->regen_colors();
253 ptr = ptr->next;
255 redraw();
256 return 1;
258 if(s==NULL){
259 unselect_all();
260 delete_sel = NULL;
261 main_sel = NULL;
262 color_sel = NULL;
264 else{//begin delete
265 delete_flag = 1;
266 delete_sel = s;//this line needs to be removed
267 if(!(s->selected)){
268 unselect_all();
270 s->selected = 1;
273 redraw();
274 return 1;
275 case fltk::DRAG:
276 if(box_flag){
277 box_x2 = X;
278 box_y2 = Y;
279 box_t2 = xpix2tick(X);
280 box_k2 = Y/30;
282 if(color_flag && color_sel){
283 color_sel->h = color_orig_h + (color_orig_x - event_x())/1.0;
284 color_sel->v = color_orig_v + (color_orig_y - event_y())/100.0;
285 color_sel->regen_colors();
286 color_h = color_sel->h;
287 color_v = color_sel->v;
288 set_default_hsv_value(color_v);
290 if(insert_flag){
291 insert_toffset = xpix2tick(X)/q_tick*q_tick + q_tick - insert_torig;
292 if(insert_toffset <=0){
293 insert_toffset -= q_tick;
295 insert_track = Y / 30;
297 else if(rresize_flag){
298 rresize_toffset = xpix2tick(X)/128*128 - rresize_torig;
300 else if(lresize_flag){
301 lresize_toffset = xpix2tick(X)/128*128 - lresize_torig;
303 else if(move_flag){
304 move_toffset = quantize(xpix2tick(X)) - move_torig - move_offset;
305 move_koffset = event_y() / 30 - move_korig;
307 else if(paste_flag){
308 paste_t = quantize(xpix2tick(event_x()));
309 paste_track = event_y() / 30;
311 redraw();
312 return 1;
313 case fltk::RELEASE:
314 if(event_button()==1){
315 if(box_flag){
316 apply_box();
317 box_flag = 0;
319 if(insert_flag){
320 apply_insert();
321 insert_flag = 0;
323 else if(move_flag){
324 apply_move();
325 move_flag = 0;
327 else if(rresize_flag){
328 apply_rresize();
329 rresize_flag = 0;
330 if(last_handle){
331 last_handle->lhandle = 0;
332 last_handle->rhandle = 0;
335 else if(lresize_flag){
336 apply_lresize();
337 lresize_flag = 0;
338 if(last_handle){
339 last_handle->lhandle = 0;
340 last_handle->rhandle = 0;
344 insert_flag=0;
345 color_sel = NULL;
347 else if(event_button()==2){
348 if(paste_flag){
349 apply_paste();
351 paste_flag=0;
353 else if(event_button()==3){
354 seqpat* over_s = over_seqpat();
355 if(delete_flag && over_s){
356 if(over_s->selected){
357 apply_delete();
360 delete_flag=0;
361 //last_handle==NULL;
366 redraw();
367 return 1;
368 case fltk::MOVE:
369 if(color_flag){break;}
370 seqpat* s = over_seqpat();
371 if(s){
372 if(over_rhandle(s,X,Y)){s->rhandle = 1;}
373 else{s->rhandle = 0;}
374 if(over_lhandle(s,X,Y)){s->lhandle = 1;}
375 else{s->lhandle = 0;}
376 if(s != last_handle){
377 if(last_handle){
378 last_handle->rhandle = 0;
379 last_handle->lhandle = 0;
381 last_handle = s;
383 redraw();
385 else if(last_handle){
386 last_handle->rhandle = 0;
387 last_handle->lhandle = 0;
388 last_handle = NULL;
389 redraw();
391 return 1;
394 return 0;
397 void Arranger::draw(){
399 fltk::push_clip(0,0,w(),h());
401 fltk::setfont(fltk::HELVETICA,8);
403 fltk::setcolor(fltk::GRAY05);
404 fltk::fillrect(0,0,w(),h());
406 fltk::setcolor(fltk::GRAY20);
407 int M = config.beats_per_measure;
408 int I=0;
409 for(int i=1; I<w(); i++){
410 I = i*zoom*M/4;
411 fltk::fillrect(I,0,1,h());
413 fltk::setcolor(fltk::GRAY50);
414 int P = config.measures_per_phrase;
415 if(P){
416 I=0;
417 for(int i=1; I<w(); i++){
418 I = i*zoom*4*P*M/4/4;
419 fltk::fillrect(I,0,1,h());
424 if(insert_flag){
425 fltk::setcolor(fltk::BLUE);
426 int T1 = insert_torig;
427 int T2 = T1 + insert_toffset;
428 int tmp;
429 if(T1>T2){SWAP(T1,T2);}
430 int X = tick2xpix(T1)+1;
431 int Y = insert_track*30;
432 int W = tick2xpix(T2)-tick2xpix(T1) - 1;
433 fltk::fillrect(X,Y,W,28);
436 if(move_flag){
437 if(check_move_safety()){
438 fltk::setcolor(fltk::MAGENTA);
440 else{
441 fltk::setcolor(fltk::RED);
444 for(int i=0; i<tracks.size(); i++){
445 seqpat* s = tracks[i]->head->next;
446 while(s){
447 if(s->selected){
448 int X = tick2xpix(s->tick + move_toffset);
449 int Y = (s->track + move_koffset)*30;
450 int W = tick2xpix(s->dur);
451 fltk::fillrect(X+1,Y+1,W-1,1);
452 fltk::fillrect(X+1,Y+1,1,29-1);
453 fltk::fillrect(X+1,Y+29-1,W-1,1);
454 fltk::fillrect(X+W-1,Y+1,1,29-1);
456 s = s->next;
461 if(paste_flag){
462 fltk::setcolor(fltk::GREEN);
463 int X = tick2xpix(paste_t)+1;
464 int Y = paste_track*zoom;
465 int W = tick2xpix(main_sel->dur);
466 fltk::fillrect(X,Y,W-1,1);
467 fltk::fillrect(X,Y+28,W-1,1);
468 fltk::fillrect(X,Y,1,28);
469 fltk::fillrect(X+W-2,Y,1,28);
472 int tmp;
473 if(box_flag){
474 fltk::setcolor(fltk::GREEN);
475 int X1,X2,Y1,Y2;
476 X1 = box_x1;
477 X2 = box_x2;
478 Y1 = box_y1;
479 Y2 = box_y2;
480 if(X1>X2){SWAP(X1,X2);}
481 if(Y1>Y2){SWAP(Y1,Y2);}
482 fltk::fillrect(X1,Y1,X2-X1,1);
483 fltk::fillrect(X1,Y1,1,Y2-Y1);
484 fltk::fillrect(X2,Y1,1,Y2-Y1);
485 fltk::fillrect(X1,Y2,X2-X1,1);
488 //draw all seqpat
489 seqpat* s;
490 fltk::Color c;
492 fltk::Color c1,c2,c3,cx;
493 c1 = fltk::BLACK;
495 for(int i=0; i<tracks.size(); i++){
497 s = tracks[i]->head->next;
498 while(s){
500 pattern* p = s->p;
502 get_outline_color(s,&c1,&c2,&c3,&cx);
504 fltk::setcolor(c1);
506 int R1 = lresize_flag&&s->selected ? lresize_toffset : 0;
507 int R2 = rresize_flag&&s->selected ? rresize_toffset : 0;
509 int T1 = s->tick+R1;
510 int T2 = s->tick+s->dur+R2;
512 if(T1 > T2){SWAP(T1,T2)};
514 int X = tick2xpix(T1)+1;
515 int Y = s->track * 30;
516 int W = tick2xpix(T2)-tick2xpix(T1)-1;
518 if(rresize_flag && s->selected && T1==T2){
519 W = tick2xpix(128)-1;
521 if(lresize_flag && s->selected && T1==T2){
522 W = tick2xpix(128)-1;
525 fillrect(X+1,Y+1,W-2,27);
526 float a = 1.5f;
529 fltk::setcolor(c2);
530 fillrect(X+W-1,Y,1,29);
531 fillrect(X,Y+28,W-1,1);
533 fltk::setcolor(c3);
534 fillrect(X,Y,1,28);
535 fillrect(X,Y,W,1);
537 fltk::push_clip(tick2xpix(T1),s->track*30,tick2xpix(T2-T1),30);
539 if(s->rhandle && !rresize_flag){
540 setcolor(cx);
541 if(delete_flag){
542 setcolor(fltk::color(128,0,0));
545 W = 5;
546 X = tick2xpix(s->tick+s->dur) - W - 1;
547 Y = s->track*30;
548 addvertex(X+W,Y+28/2);
549 addvertex(X,Y);
550 addvertex(X,Y+28);
551 fillpath();
554 if(s->lhandle && !lresize_flag){
555 setcolor(cx);
556 if(delete_flag){
557 setcolor(fltk::color(128,0,0));
559 W = 5;
560 X = tick2xpix(s->tick)+1;
561 Y = s->track*30;
562 addvertex(X,Y+28/2);
563 addvertex(X+W,Y);
564 addvertex(X+W,Y+28);
565 fillpath();
568 fltk::setcolor(cx);
571 mevent* e = s->p->events;
572 while(e){
573 if(e->tick >= s->dur){
574 break;
576 if(e->type == MIDI_NOTE_ON){
577 X = tick2xpix(e->tick) + tick2xpix(s->tick)+2;
578 Y = s->track*30 + 27 - e->value1*27/127;
579 W = tick2xpix(e->dur);
580 if(W==0){W=1;}
581 fillrect(X,Y,W,1);
583 e=e->next;
587 int total = s->layer_total();
588 if(total > 1){
589 fltk::setcolor(fltk::BLACK);
590 int X = tick2xpix(s->tick);
591 int Y = s->track * 30;
592 int count = s->layer_index()+1;
593 char buf[16];
594 snprintf(buf,16,"%d / %d",count,total);
595 fltk::drawtext(buf,X+2,Y+27);
599 fltk::pop_clip();
601 s=s->next;
605 fltk::pop_clip();
609 void Arranger::scrollTo(int X, int Y){
610 scrollx = X;
611 scrolly = Y;
612 redraw();
613 ui->song_timeline->scroll = X;
614 ui->song_timeline->redraw();
615 ui->track_info->scroll = Y;
616 ui->track_info->redraw();
621 seqpat* Arranger::over_seqpat(){
622 int track = event_y() / 30;
623 if(track >= tracks.size()){
624 return NULL;
626 int tick = xpix2tick(event_x());
627 seqpat* s = tfind<seqpat>(tracks[track]->head,tick);
628 if(s){
629 if(tick < s->tick+s->dur){
630 return s;
633 return NULL;
637 //true if over right handle of s
638 int Arranger::over_rhandle(seqpat* s, int X, int Y){
639 int X1 = tick2xpix(s->tick);
640 int X2 = X1 + tick2xpix(s->dur);
641 int Y1 = s->track * 30 + 1;
642 int Y2 = Y1 + 29;
644 if(tick2xpix(s->dur) < 10){
645 return 0;
648 return (Y > Y1 && Y < Y2 && X < X2 && X > X2 - 5);
651 //true if over left handle of s
652 int Arranger::over_lhandle(seqpat* s, int X, int Y){
653 int X1 = tick2xpix(s->tick);
654 int X2 = X1 + tick2xpix(s->dur);
655 int Y1 = s->track * 30 + 1;
656 int Y2 = Y1 + 29;
658 if(tick2xpix(s->dur) < 10){
659 return 0;
662 return (Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1);
665 // 4=beats per measure, 128=ticks per beat, 30=width of measure in pixels
666 int Arranger::tick2xpix(int tick){
667 return tick *zoom /(128*4);
670 int Arranger::xpix2tick(int xpix){
671 return xpix * (128*4) /zoom;
674 int Arranger::quantize(int tick){
675 return tick/q_tick * q_tick;
679 void Arranger::update(int pos){
680 if(!is_backend_playing()){
681 return;
683 //int wp = ui->song_scroll->w();
684 int X1 = tick2xpix(pos);
685 int X2 = X1 - scrollx;
686 if(X1 > w()-40){
687 return;
689 if(X2 < 0){
690 scrollTo(X1-50<0?0:X1-50,scrolly);
692 if(X2 > w()-30){
693 scrollTo(X1-50,scrolly);
697 int kludge=2;
698 void Arranger::layout(){
699 if(kludge!=0){
700 kludge--;
701 return;
703 fakeh = tracks.size()*30;
704 if(fakeh<h()){
705 fakeh = h();
707 ui->song_vscroll->maximum(0);
708 ui->song_vscroll->minimum(fakeh-h());
709 int M = ui->song_vscroll->h() - 30;
710 ui->song_vscroll->slider_size(M - (fakeh-h()));
714 void Arranger::unselect_all(){
715 seqpat* s;
716 for(int i=0; i<tracks.size(); i++){
717 s = tracks[i]->head->next;
718 while(s){
719 if(s->selected==1){
720 s->selected = 0;
722 s = s->next;
727 void Arranger::get_outline_color(seqpat* s, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3, fltk::Color* cx){
729 pattern* p = s->p;
730 *c1 = fltk::color(p->r1, p->g1, p->b1);
731 *cx = fltk::color(p->rx, p->gx, p->bx);
733 int T1,T2;
734 int tmp;
735 seqpat* over_s = over_seqpat();
736 if(delete_flag && s->selected){
737 *c1 = fltk::color(255,0,0);
738 *c2 = fltk::color(255,0,0);
739 *c3 = fltk::color(255,0,0);
740 return;
743 if(box_flag){
744 T1=box_t1;
745 T2=box_t2;
746 int K1 = box_k1;
747 int K2 = box_k2;
748 int K = s->track;
749 if(T1>T2){SWAP(T1,T2);}
750 if(K1<K2){SWAP(K1,K2);}
751 if(s->tick+s->dur > T1 && s->tick < T2 && K >= K2 && K <= K1){
752 *c1 = fltk::color(0,255,0);
753 *c2 = fltk::color(71,120,59);
754 *c3 = fltk::color(108,229,75);
755 return;
759 if(s->selected){
760 *c1 = fltk::color(255,255,0);
761 *c2 = fltk::color(140,137,46);
762 *c3 = fltk::color(232,255,37);
763 *cx = fltk::color(128,128,0);
764 return;
768 *c2 = fltk::color(p->r2,p->g2,p->b2);
769 *c3 = fltk::color(p->r3,p->g3,p->b3);
774 void Arranger::apply_insert(){
776 if(!check_insert_safety()){
777 return;
780 int tmp;
781 int T1 = insert_torig;
782 int T2 = T1 + insert_toffset;
783 if(T1>T2){SWAP(T1,T2);}
785 Command* c=new CreateSeqpatBlank(insert_track,T1,T2-T1);
786 set_undo(c);
787 undo_push(1);
789 if(T2>maxt){relayout();}
792 void Arranger::apply_box(){
793 seqpat* s;
794 int tmp;
795 int T1=box_t1;
796 int T2=box_t2;
797 int K1 = box_k1;
798 int K2 = box_k2;
799 if(T1>T2){SWAP(T1,T2);}
800 if(K1>K2){SWAP(K1,K2);}
801 if(K1 < 0){K1 = 0;}
802 if(K2 > tracks.size()-1){K2 = tracks.size()-1;}
803 for(int i=K1; i<=K2; i++){
804 s = tracks[i]->head->next;
805 while(s){
806 if(s->tick+s->dur > T1 && s->tick < T2){
807 s->selected = 1;
809 s = s->next;
815 void Arranger::apply_delete(){
816 Command* c;
817 seqpat* s;
818 seqpat* next;
819 int N=0;
820 for(int i=0; i<tracks.size(); i++){
821 s = tracks[i]->head->next;
822 while(s){
823 next = s->next;
824 if(s->selected){
825 tracks[s->track]->modified = 1;
826 c=new DeleteSeqpat(s);
827 set_undo(c);
828 N++;
830 s = next;
833 undo_push(N);
835 unmodify_and_unstick_tracks();
839 void Arranger::apply_move(){
840 if(move_toffset==0 && move_koffset==0){
841 return;
844 if(!check_move_safety()){
845 return;
848 Command* c;
849 seqpat* s;
850 seqpat* next;
851 int N=0;
852 for(int i=0; i<tracks.size(); i++){
853 s = tracks[i]->head->next;
854 while(s){
855 next = s->next;
856 if(s->selected && s->modified == 0){
857 int K = s->track + move_koffset;
858 int T = s->tick + move_toffset;
859 tracks[s->track]->modified = 1;
860 tracks[K]->modified = 1;
861 s->modified = 1;
862 c=new MoveSeqpat(s,K,T);
863 set_undo(c);
864 N++;
866 if(T+s->dur > maxt){relayout();}
868 s = next;
871 undo_push(N);
873 unmodify_blocks();
874 unmodify_and_unstick_tracks();
877 void Arranger::apply_paste(){
878 //safety check
881 Command* c;
883 c = new CreateSeqpat(paste_track,paste_t,main_sel,0);
884 set_undo(c);
885 undo_push(1);
890 void Arranger::apply_rresize(){
891 if(rresize_toffset==0){
892 return;
895 if(!check_resize_safety()){
896 return;
899 Command* c;
900 seqpat* s;
901 seqpat* next;
902 int tmp;
903 int N=0;
904 for(int i=0; i<tracks.size(); i++){
905 s = tracks[i]->head->next;
906 while(s){
907 next = s->next;
908 if(s->selected && s->modified == 0){
909 tracks[i]->modified = 1;
910 s->modified = 1;
911 int T1 = s->tick;
912 int T2 = s->tick + s->dur + rresize_toffset;
913 if(T1 > T2){
914 SWAP(T1,T2);
915 seqpat* stmp = s->prev;
916 //c=new ReverseSeqpat(s);
917 //set_undo(c);
918 s = stmp->next;
919 c=new ResizeSeqpat(s,T2-T1);
920 set_undo(c);
921 s = stmp->next;
922 c=new MoveSeqpat(s,s->track,T1);
923 set_undo(c);
924 N+=2;
926 s = stmp->next;
927 if(s->tick+s->dur > maxt){relayout();}
929 else{
930 if(T1==T2){
931 T2 = T1+128; //magic
933 c=new ResizeSeqpat(s,T2-T1);
934 set_undo(c);
935 N++;
937 if(T2 > maxt){relayout();}
941 s = next;
944 undo_push(N);
946 unmodify_blocks();
947 unmodify_and_unstick_tracks();
950 void Arranger::apply_lresize(){
951 if(lresize_toffset==0){
952 return;
955 if(!check_resize_safety()){
956 return;
959 Command* c;
960 seqpat* s;
961 seqpat* next;
962 int tmp;
963 int N=0;
964 for(int i=0; i<tracks.size(); i++){
965 s = tracks[i]->head->next;
966 while(s){
967 next = s->next;
968 if(s->selected && s->modified == 0){
969 tracks[i]->modified = 1;
970 s->modified = 1;
971 int T1 = s->tick + lresize_toffset;
972 int T2 = s->tick + s->dur;
973 if(T1 > T2){
974 SWAP(T1,T2);
975 seqpat* stmp = s->prev;
976 //c=new ReverseSeqpat(s);
977 //set_undo(c);
978 s = stmp->next;
979 c=new ResizeSeqpat(s,T2-T1);
980 set_undo(c);
981 s = stmp->next;
982 c=new MoveSeqpat(s,s->track,T1);
983 set_undo(c);
984 N+=2;
986 s = stmp->next;
987 if(s->tick+s->dur > maxt){relayout();}
989 else{
990 if(T1==T2){
991 T2 = T1+128; //magic
993 seqpat* stmp = s->prev;
994 c=new MoveSeqpat(s,s->track,T1);
995 set_undo(c);
996 s = stmp->next;
997 c=new ResizeSeqpat(s,T2-T1);
998 set_undo(c);
999 N+=2;
1001 s = stmp->next;
1002 if(s->tick+s->dur>maxt){relayout();}
1006 s = next;
1009 undo_push(N);
1011 unmodify_blocks();
1012 unmodify_and_unstick_tracks();
1017 int collision_test(int t11, int t12, int t21, int t22){
1018 return !((t11 < t21 && t12 <= t21) ||
1019 (t11 >= t22 && t12 > t22)) ? 1 : 0;
1022 int Arranger::check_move_safety(){
1023 seqpat* s;
1024 seqpat* ptr;
1026 for(int i=0; i<tracks.size(); i++){
1027 s = tracks[i]->head->next;
1028 while(s){
1029 if(s->selected){
1030 if(i+move_koffset < 0 || i+move_koffset > tracks.size()-1 ||
1031 s->tick + move_toffset < 0){
1032 return 0;
1034 ptr = tracks[i+move_koffset]->head->next;
1035 while(ptr){
1036 if(ptr == s){
1037 ptr=ptr->next; continue;
1039 if(collision_test(s->tick+move_toffset,s->tick+s->dur+move_toffset,ptr->tick,ptr->tick+ptr->dur) ){
1040 if(!ptr->selected){
1041 return 0;
1044 ptr = ptr->next;
1047 s = s->next;
1051 return 1;
1054 int Arranger::check_insert_safety(){
1055 seqpat* s;
1057 int T1 = insert_torig;
1058 int T2 = T1 + insert_toffset;
1059 int tmp;
1061 if(T1>T2){SWAP(T1,T2);}
1063 if(T1 < 0){
1064 return 0;
1066 if(insert_track > tracks.size()-1){
1067 return 0;
1069 if(tracks[insert_track]==NULL){
1070 return 0;
1073 s = tracks[insert_track]->head->next;
1075 while(s){
1076 if(collision_test(T1,T2,s->tick,s->tick+s->dur)){
1077 return 0;
1079 s = s->next;
1082 return 1;
1085 int Arranger::check_resize_safety(){
1086 seqpat* s;
1087 seqpat* ptr;
1089 int T1,T2;
1090 int S1,S2;
1091 int tmp;
1093 for(int i=0; i<tracks.size(); i++){
1094 s = tracks[i]->head->next;
1095 while(s){
1096 if(!s->selected){
1097 s = s->next; continue;
1100 if(rresize_flag){
1101 T1 = s->tick;
1102 T2 = s->tick + s->dur + rresize_toffset;
1104 else if(lresize_flag){
1105 T1 = s->tick + lresize_toffset;
1106 T2 = s->tick + s->dur;
1108 if(T1>T2){SWAP(T1,T2);}
1110 if(T1 < 0){
1111 return 0;
1113 ptr = tracks[s->track]->head->next;
1114 while(ptr){
1115 if(ptr == s){
1116 ptr=ptr->next; continue;
1119 S1 = ptr->tick;
1120 S2 = ptr->tick + ptr->dur;
1121 if(ptr->selected){
1122 if(rresize_flag){
1123 S2 += rresize_toffset;
1125 else if(lresize_flag){
1126 S1 += lresize_toffset;
1130 if(collision_test(T1,T2,S1,S2)){
1131 return 0;
1133 ptr = ptr->next;
1136 s = s->next;
1140 return 1;
1144 int Arranger::check_paste_safety(){
1145 return 1;