Rewrote overwrite and layer operations to be undoable.
[epichord.git] / src / arranger.cpp
blob3fa3d01b8b7338e0163c55eb38a5384ce447c82d
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 <math.h>
25 #include <vector>
26 #include <fltk/Group.h>
27 #include <fltk/Widget.h>
28 #include <fltk/events.h>
30 #include "ui.h"
32 #include "uihelper.h"
34 #include "util.h"
36 #include "backend.h"
38 extern UI* ui;
40 extern std::vector<track*> tracks;
42 extern struct conf config;
44 using namespace fltk;
46 #define SWAP(X,Y) tmp=X; X=Y; Y=tmp;
48 Arranger::Arranger(int x, int y, int w, int h, const char* label = 0) : fltk::Widget(x, y, w, h, label) {
49 new_default_w = 128*4;
50 delete_flag = 0;
51 move_flag = 0;
52 paste_flag = 0;
53 main_sel = NULL;
55 zoom = 30;
56 zoom_n = 4;
58 q_tick = 128*4;
60 insert_flag = 0;
61 box_flag = 0;
62 rresize_flag = 0;
63 lresize_flag = 0;
65 color_flag = 0;
67 maxt = 0;
70 fakeh = 16*30;
71 if(fakeh < h){fakeh = h;}
73 scrollbuffer = 30;
75 scrollx=0;
76 scrolly=0;
78 resize_s = NULL;
79 resize_handle_width = 4;
80 resize_arrow = 0;
82 unclone_flag=0;
83 join_flag=0;
84 split_flag=0;
87 int Arranger::handle(int event){
88 Command* c;
90 int X = event_x();
91 int Y = event_y();
93 seqpat* s;
95 switch(event){
96 case fltk::FOCUS:
97 return 1;
98 case fltk::ENTER:
99 return 1;
100 case fltk::KEYUP:
102 return 0;
103 case fltk::MOUSEWHEEL:
104 s = over_seqpat();
105 if(s){
106 s->autocomplete();
107 if(event_dy()>0){
108 s->prev_layer();
110 else if(event_dy()<0){
111 s->next_layer();
113 s->restate();
114 redraw();
116 return 1;
117 case fltk::SHORTCUT:
118 if(event_state() && event_key()=='c'){
120 return 1;
122 if(event_state() && event_key()=='v'){
124 return 1;
126 if(event_state() && event_key()=='z'){
128 return 1;
130 if(event_key()==fltk::DeleteKey){
131 apply_delete();
132 delete_flag = 0;
133 redraw();
134 return 1;
136 if(zoom_out_key(event_key(),event_state())){
137 //if(event_key()==fltk::LeftKey){
138 if(zoom_n > 1){
139 zoom_n--;
140 zoom = 30*(1<<zoom_n)/16;
141 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
142 ui->song_timeline->update(get_play_position());
143 ui->song_timeline->redraw();
144 relayout();
146 redraw();
147 return 1;
149 if(zoom_in_key(event_key(),event_state())){
150 //if(event_key()==fltk::RightKey){
151 if(zoom_n < 8){
152 zoom_n++;
153 zoom = 30*(1<<zoom_n)/16;
154 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
155 ui->song_timeline->update(get_play_position());
156 ui->song_timeline->redraw();
157 relayout();
159 redraw();
160 return 1;
162 return 0;
163 case fltk::PUSH:
164 take_focus();
165 if(event_button()==1){//left mouse
166 seqpat* s = over_seqpat();
167 if(s==NULL){
168 if(color_flag || unclone_flag){//do nothing
170 else if(event_state()&fltk::SHIFT){//begin box
171 box_flag = 1;
172 box_x1=X;
173 box_x2=X;
174 box_y1=Y;
175 box_y2=Y;
176 box_t1=xpix2tick(X+scrollx);
177 box_t2=box_t1;
178 box_k1=(Y+scrolly)/30;
179 box_k2=box_k1;
181 else{//begin insert
182 insert_flag = 1;
183 insert_torig = xpix2tick(X+scrollx)/q_tick*q_tick;
184 insert_toffset = q_tick;
185 insert_track = (Y+scrolly) / 30;
188 else{
189 main_sel = s;
190 if(color_flag){
191 color_sel = s->p;
192 color_orig_x = X;
193 color_orig_y = Y;
194 color_orig_h = color_sel->h;
195 color_orig_v = color_sel->v;
196 color_h = color_orig_h;
197 color_v = color_orig_v;
198 return 1;
200 if(unclone_flag){
201 apply_unclone();
203 if(!s->selected && !(event_state()&SHIFT)){
204 unselect_all();
206 s->selected = 1;
207 if(fltk::event_clicks() > 0){//'double click'
208 ui->piano_roll->load(s);
209 ui->event_edit->load(s);
210 ui->pattern_timeline->update(get_play_position());
211 ui->keyboard->cur_port = tracks[s->track]->port;
212 ui->keyboard->cur_chan = tracks[s->track]->chan;
213 ui->track_info->set_rec(s->track);
214 set_rec_track(s->track);
215 show_pattern_edit();
216 return 1;
219 if(over_lhandle(s)){//begin resize
220 lresize_flag = 1;
221 lresize_torig = s->tick;
222 lresize_toffset = 0;
224 else if(over_rhandle(s)){//begin resizemove
225 rresize_flag = 1;
226 rresize_torig = s->tick+s->dur;
227 rresize_toffset = 0;
230 else{//begin move
231 move_flag = 1;
232 move_torig = s->tick;
233 move_toffset = 0;
234 move_korig = s->track;
235 move_koffset = 0;
236 move_x = X+scrollx;
237 move_y = Y+scrolly;
238 move_offset = xpix2tick(X+scrollx)/q_tick*q_tick - s->tick;
242 else if(event_button()==2){//middle mouse
243 seqpat* s = over_seqpat();
244 if(color_flag && s){
245 s->p->h = color_h;
246 s->p->v = color_v;
247 s->p->regen_colors();
248 redraw();
249 return 1;
251 if(main_sel){
252 paste_flag = 1;
253 paste_t = quantize(xpix2tick(X+scrollx));
254 paste_track = (Y+scrolly) / 30;
257 else if(event_button()==3){//right mouse
258 seqpat* s = over_seqpat();
259 if(color_flag && s){
260 seqpat* ptr = tracks[s->track]->head->next;
261 while(ptr){
262 ptr->p->h = color_h;
263 ptr->p->v = color_v;
264 ptr->p->regen_colors();
265 ptr = ptr->next;
267 redraw();
268 return 1;
270 if(s==NULL){
271 unselect_all();
272 delete_sel = NULL;
273 main_sel = NULL;
274 color_sel = NULL;
276 else{//begin delete
277 delete_flag = 1;
278 delete_sel = s;//this line needs to be removed
279 if(!(s->selected)){
280 unselect_all();
282 s->selected = 1;
283 resize_arrow_color = fltk::color(128,0,0);
286 redraw();
287 return 1;
288 case fltk::DRAG:
289 if(box_flag){
290 box_x2 = X;
291 box_y2 = Y;
292 box_t2 = xpix2tick(X+scrollx);
293 box_k2 = (Y+scrolly)/30;
295 if(color_flag && color_sel){
296 color_sel->h = color_orig_h + (color_orig_x - X)/1.0;
297 color_sel->v = color_orig_v + (color_orig_y - Y)/100.0;
298 color_sel->regen_colors();
299 color_h = color_sel->h;
300 color_v = color_sel->v;
301 set_default_hsv_value(color_v);
303 if(insert_flag){
304 insert_toffset = xpix2tick(X+scrollx)/q_tick*q_tick+q_tick-insert_torig;
305 if(insert_toffset <=0){
306 insert_toffset -= q_tick;
308 insert_track = (Y+scrolly) / 30;
310 else if(rresize_flag){
311 rresize_toffset = xpix2tick(X+scrollx)/128*128 - rresize_torig;
313 else if(lresize_flag){
314 lresize_toffset = xpix2tick(X+scrollx)/128*128 - lresize_torig;
316 else if(move_flag){
317 move_toffset = quantize(xpix2tick(X+scrollx)) - move_torig - move_offset;
318 move_koffset = (Y+scrolly) / 30 - move_korig;
320 else if(paste_flag){
321 paste_t = quantize(xpix2tick(X+scrollx));
322 paste_track = (Y+scrolly) / 30;
324 redraw();
325 return 1;
326 case fltk::RELEASE:
327 if(event_button()==1){
328 if(box_flag){
329 apply_box();
330 box_flag = 0;
332 if(insert_flag){
333 apply_insert();
334 insert_flag = 0;
336 else if(move_flag){
337 apply_move();
338 move_flag = 0;
340 else if(rresize_flag){
341 apply_rresize();
342 rresize_flag = 0;
343 resize_arrow = 0;
345 else if(lresize_flag){
346 apply_lresize();
347 lresize_flag = 0;
348 resize_arrow = 0;
351 insert_flag=0;
352 color_sel = NULL;
354 else if(event_button()==2){
355 if(paste_flag){
356 apply_paste();
358 paste_flag=0;
360 else if(event_button()==3){
361 seqpat* over_s = over_seqpat();
362 if(delete_flag && over_s){
363 if(over_s->selected){
364 apply_delete();
367 delete_flag=0;
368 resize_arrow = 0;
373 redraw();
374 return 1;
375 case fltk::MOVE:
376 if(color_flag){break;}
377 seqpat* s = over_seqpat();
378 if(s){
379 if(over_rhandle(s)){
380 if(resize_s != s || resize_arrow != 1){
381 if(s->selected){resize_arrow_color = fltk::color(128,128,0);}
382 else{resize_arrow_color = fltk::color(s->p->r2,s->p->g2,s->p->b2);}
383 resize_s = s;
384 resize_arrow = 1;
385 resize_x = tick2xpix(s->tick + s->dur)-scrollx-resize_handle_width-1;
386 resize_y = s->track*30-scrolly;
387 redraw();
390 else if(over_lhandle(s)){
391 if(resize_s != s || resize_arrow != 1){
392 if(s->selected){resize_arrow_color = fltk::color(128,128,0);}
393 else{resize_arrow_color = fltk::color(s->p->r2,s->p->g2,s->p->b2);}
394 resize_s = s;
395 resize_arrow = -1;
396 resize_x = tick2xpix(s->tick)+1 - scrollx;
397 resize_y = s->track*30 - scrolly;
398 redraw();
401 else{
402 if(resize_arrow != 0){
403 resize_arrow=0;
404 redraw();
408 else{
409 if(resize_arrow != 0){
410 resize_arrow=0;
411 redraw();
414 return 1;
417 return 0;
420 void Arranger::draw(){
422 fltk::push_clip(0,0,w(),h());
424 fltk::setfont(fltk::HELVETICA,8);
426 fltk::setcolor(fltk::GRAY05);
427 fltk::fillrect(0,0,w(),h());
429 fltk::setcolor(fltk::GRAY20);
430 int M = config.beats_per_measure;
431 int I=0;
432 for(int i=1; I<w(); i++){
433 I = i*zoom*M/4 - scrollx;
434 if(I>=0){
435 fltk::fillrect(I,0,1,h());
439 fltk::setcolor(fltk::GRAY50);
440 int P = config.measures_per_phrase;
441 if(P){
442 I=0;
443 for(int i=1; I<w(); i++){
444 I = i*zoom*4*P*M/4/4 - scrollx;
445 if(I>=0){
446 fltk::fillrect(I,0,1,h());
451 if(insert_flag){
452 fltk::setcolor(fltk::BLUE);
453 int T1 = insert_torig;
454 int T2 = T1 + insert_toffset;
455 int tmp;
456 if(T1>T2){SWAP(T1,T2);}
457 int X = tick2xpix(T1)+1 - scrollx;
458 int Y = insert_track*30 - scrolly;
459 int W = tick2xpix(T2)-tick2xpix(T1) - 1;
460 fltk::fillrect(X,Y,W,28);
463 if(move_flag){
464 if(check_move_safety()){
465 fltk::setcolor(fltk::MAGENTA);
467 else{
468 fltk::setcolor(fltk::RED);
471 for(int i=0; i<tracks.size(); i++){
472 seqpat* s = tracks[i]->head->next;
473 while(s){
474 if(s->selected){
475 int X = tick2xpix(s->tick + move_toffset) - scrollx;
476 int Y = (s->track + move_koffset)*30 - scrolly;
477 int W = tick2xpix(s->dur);
478 fltk::fillrect(X+1,Y+1,W-1,1);
479 fltk::fillrect(X+1,Y+1,1,29-1);
480 fltk::fillrect(X+1,Y+29-1,W-1,1);
481 fltk::fillrect(X+W-1,Y+1,1,29-1);
483 s = s->next;
488 if(paste_flag){
489 fltk::setcolor(fltk::GREEN);
490 int X = tick2xpix(paste_t)+1 - scrollx;
491 int Y = paste_track*30 - scrolly;
492 int W = tick2xpix(main_sel->dur);
493 fltk::fillrect(X,Y,W-1,1);
494 fltk::fillrect(X,Y+28,W-1,1);
495 fltk::fillrect(X,Y,1,28);
496 fltk::fillrect(X+W-2,Y,1,28);
499 int tmp;
500 if(box_flag){
501 fltk::setcolor(fltk::GREEN);
502 int X1,X2,Y1,Y2;
503 X1 = box_x1;
504 X2 = box_x2;
505 Y1 = box_y1;
506 Y2 = box_y2;
507 if(X1>X2){SWAP(X1,X2);}
508 if(Y1>Y2){SWAP(Y1,Y2);}
509 fltk::fillrect(X1,Y1,X2-X1,1);
510 fltk::fillrect(X1,Y1,1,Y2-Y1);
511 fltk::fillrect(X2,Y1,1,Y2-Y1);
512 fltk::fillrect(X1,Y2,X2-X1,1);
515 //draw all seqpat
516 seqpat* s;
517 fltk::Color c;
519 fltk::Color c1,c2,c3,cx;
520 c1 = fltk::BLACK;
522 for(int i=0; i<tracks.size(); i++){
524 s = tracks[i]->head->next;
525 while(s){
527 pattern* p = s->p;
529 get_outline_color(s,&c1,&c2,&c3,&cx);
531 fltk::setcolor(c1);
533 int R1 = lresize_flag&&s->selected ? lresize_toffset : 0;
534 int R2 = rresize_flag&&s->selected ? rresize_toffset : 0;
536 int T1 = s->tick+R1;
537 int T2 = s->tick+s->dur+R2;
539 if(T1 > T2){SWAP(T1,T2)};
541 int X = tick2xpix(T1)+1 - scrollx;
542 int Y = s->track * 30 - scrolly;
543 int W = tick2xpix(T2)-tick2xpix(T1)-1;
545 if(rresize_flag && s->selected && T1==T2){
546 W = tick2xpix(128)-1;
548 if(lresize_flag && s->selected && T1==T2){
549 W = tick2xpix(128)-1;
552 fillrect(X+1,Y+1,W-2,27);
553 //float a = 1.5f;
556 fltk::setcolor(c2);
557 fillrect(X+W-1,Y,1,29);
558 fillrect(X,Y+28,W-1,1);
560 fltk::setcolor(c3);
561 fillrect(X,Y,1,28);
562 fillrect(X,Y,W,1);
564 fltk::push_clip(X,Y,W,30);
566 fltk::setcolor(cx);
569 mevent* e = s->p->events;
570 while(e){
571 if(e->tick >= s->dur){
572 break;
574 if(e->type == MIDI_NOTE_ON){
575 int X2 = tick2xpix(e->tick) + tick2xpix(s->tick)+2 - scrollx;
576 Y = s->track*30 + 27 - e->value1*27/127 - scrolly;
577 int W2 = tick2xpix(e->dur);
578 if(W2==0){W2=1;}
579 if(!(X2+W2<0 || X2>X+W )){
580 fillrect(X2,Y,W2,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) - scrollx;
591 int Y = s->track * 30 - scrolly;
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 if(!rresize_flag && !lresize_flag){
606 if(resize_arrow > 0){
607 setcolor(resize_arrow_color);
609 int W = resize_handle_width;
610 int H = 28;
611 int X = resize_x;
612 int Y = resize_y;
614 addvertex(X,Y);
615 addvertex(X,Y+H);
616 addvertex(X+W,Y+H/2);
617 fillpath();
619 else if(resize_arrow < 0){
620 setcolor(resize_arrow_color);
622 int W = resize_handle_width;
623 int H = 28;
624 int X = resize_x;
625 int Y = resize_y;
627 addvertex(X+W,Y);
628 addvertex(X+W,Y+H);
629 addvertex(X,Y+H/2);
630 fillpath();
634 fltk::pop_clip();
638 void Arranger::scrollTo(int X, int Y){
640 if(is_backend_playing() && config.follow){
641 int pos = tick2xpix(get_play_position());
642 if(pos < X || pos > X + w() - scrollbuffer - 30){
643 ui->song_hscroll->value(scrollx);
644 return;
648 scrollx = X;
649 ui->song_hscroll->value(scrollx);
650 scrolly = Y;
651 redraw();
652 ui->song_timeline->scroll = X;
653 ui->song_timeline->redraw();
654 ui->track_info->scroll = Y;
655 ui->track_info->redraw();
660 seqpat* Arranger::over_seqpat(){
661 int track = (event_y()+scrolly) / 30;
662 if(track >= tracks.size()){
663 return NULL;
665 int tick = xpix2tick(event_x()+scrollx);
666 seqpat* s = tfind<seqpat>(tracks[track]->head,tick);
667 if(s){
668 if(tick < s->tick+s->dur){
669 return s;
672 return NULL;
676 //true if over right handle of s
677 int Arranger::over_rhandle(seqpat* s){
678 int X = event_x();
679 int Y = event_y();
680 int X1 = tick2xpix(s->tick) - scrollx;
681 int X2 = X1 + tick2xpix(s->dur);
682 int Y1 = s->track * 30 + 1 - scrolly;
683 int Y2 = Y1 + 29;
685 if(tick2xpix(s->dur) < 10){
686 return 0;
689 return (Y > Y1 && Y < Y2 && X < X2 && X > X2 - 5);
692 //true if over left handle of s
693 int Arranger::over_lhandle(seqpat* s){
694 int X = event_x();
695 int Y = event_y();
696 int X1 = tick2xpix(s->tick) - scrollx;
697 int X2 = X1 + tick2xpix(s->dur);
698 int Y1 = s->track * 30 + 1 - scrolly;
699 int Y2 = Y1 + 29;
701 if(tick2xpix(s->dur) < 10){
702 return 0;
705 if(Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1){
706 //printf("success\n");
708 return (Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1);
711 // 4=beats per measure, 128=ticks per beat, 30=width of measure in pixels
712 int Arranger::tick2xpix(int tick){
713 return tick *zoom /(128*4);
716 int Arranger::xpix2tick(int xpix){
717 return xpix * (128*4) /zoom;
720 int Arranger::quantize(int tick){
721 return tick/q_tick * q_tick;
725 void Arranger::update(int pos){
726 if(!is_backend_playing()){
727 return;
729 int X1 = tick2xpix(pos);
730 int X2 = X1 - scrollx;
731 if(X2 < 0){
732 int target = X1-50<0?0:X1-50;
733 scrollTo(target,scrolly);
734 // ui->song_hscroll->value(target);
736 if(X2 > w()-scrollbuffer){
737 int target = X1-50;
738 scrollTo(target,scrolly);
739 //ui->song_hscroll->value(target);
743 static int kludge=2;
744 void Arranger::layout(){
745 if(kludge!=0){
746 kludge--;
747 return;
749 fakeh = tracks.size()*30;
750 if(fakeh<h()){
751 fakeh = h();
753 ui->song_vscroll->maximum(0);
754 ui->song_vscroll->minimum(fakeh-h());
755 int M = h() - 30;
756 int newsize = M-(fakeh-h());
757 if(newsize<20){
758 newsize=20;
760 //ui->song_vscroll->slider_size(379);
764 void Arranger::unselect_all(){
765 seqpat* s;
766 for(int i=0; i<tracks.size(); i++){
767 s = tracks[i]->head->next;
768 while(s){
769 if(s->selected==1){
770 s->selected = 0;
772 s = s->next;
777 void Arranger::get_outline_color(seqpat* s, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3, fltk::Color* cx){
779 pattern* p = s->p;
780 *c1 = fltk::color(p->r1, p->g1, p->b1);
781 *cx = fltk::color(p->rx, p->gx, p->bx);
783 int T1,T2;
784 int tmp;
785 seqpat* over_s = over_seqpat();
786 if(delete_flag && s->selected){
787 *c1 = fltk::color(255,0,0);
788 *c2 = fltk::color(255,0,0);
789 *c3 = fltk::color(255,0,0);
790 return;
793 if(box_flag){
794 T1=box_t1;
795 T2=box_t2;
796 int K1 = box_k1;
797 int K2 = box_k2;
798 int K = s->track;
799 if(T1>T2){SWAP(T1,T2);}
800 if(K1<K2){SWAP(K1,K2);}
801 if(s->tick+s->dur > T1 && s->tick < T2 && K >= K2 && K <= K1){
802 *c1 = fltk::color(0,255,0);
803 *c2 = fltk::color(71,120,59);
804 *c3 = fltk::color(108,229,75);
805 return;
809 if(s->selected){
810 *c1 = fltk::color(255,255,0);
811 *c2 = fltk::color(140,137,46);
812 *c3 = fltk::color(232,255,37);
813 *cx = fltk::color(128,128,0);
814 return;
818 *c2 = fltk::color(p->r2,p->g2,p->b2);
819 *c3 = fltk::color(p->r3,p->g3,p->b3);
824 void Arranger::apply_insert(){
826 if(!check_insert_safety()){
827 return;
830 int tmp;
831 int T1 = insert_torig;
832 int T2 = T1 + insert_toffset;
833 if(T1>T2){SWAP(T1,T2);}
835 Command* c=new CreateSeqpatBlank(insert_track,T1,T2-T1);
836 set_undo(c);
837 undo_push(1);
839 if(T2>maxt){relayout();}
842 void Arranger::apply_box(){
843 seqpat* s;
844 int tmp;
845 int T1=box_t1;
846 int T2=box_t2;
847 int K1 = box_k1;
848 int K2 = box_k2;
849 if(T1>T2){SWAP(T1,T2);}
850 if(K1>K2){SWAP(K1,K2);}
851 if(K1 < 0){K1 = 0;}
852 if(K2 > tracks.size()-1){K2 = tracks.size()-1;}
853 for(int i=K1; i<=K2; i++){
854 s = tracks[i]->head->next;
855 while(s){
856 if(s->tick+s->dur > T1 && s->tick < T2){
857 s->selected = 1;
859 s = s->next;
865 void Arranger::apply_delete(){
866 Command* c;
867 seqpat* s;
868 seqpat* next;
869 int N=0;
870 for(int i=0; i<tracks.size(); i++){
871 s = tracks[i]->head->next;
872 while(s){
873 next = s->next;
874 if(s->selected){
875 tracks[s->track]->modified = 1;
876 c=new DeleteSeqpat(s);
877 set_undo(c);
878 N++;
880 s = next;
883 undo_push(N);
885 unmodify_and_unstick_tracks();
889 void Arranger::apply_move(){
890 if(move_toffset==0 && move_koffset==0){
891 return;
894 if(!check_move_safety()){
895 return;
898 Command* c;
899 seqpat* s;
900 seqpat* next;
901 int N=0;
902 for(int i=0; i<tracks.size(); i++){
903 s = tracks[i]->head->next;
904 while(s){
905 next = s->next;
906 if(s->selected && s->modified == 0){
907 int K = s->track + move_koffset;
908 int T = s->tick + move_toffset;
909 tracks[s->track]->modified = 1;
910 tracks[K]->modified = 1;
911 s->modified = 1;
912 c=new MoveSeqpat(s,K,T);
913 set_undo(c);
914 N++;
916 if(T+s->dur > maxt){relayout();}
918 s = next;
921 undo_push(N);
923 unmodify_blocks();
924 unmodify_and_unstick_tracks();
927 void Arranger::apply_paste(){
928 //safety check
931 Command* c;
933 c = new CreateSeqpat(paste_track,paste_t,main_sel,config.alwayscopy);
934 set_undo(c);
935 undo_push(1);
940 void Arranger::apply_rresize(){
941 if(rresize_toffset==0){
942 return;
945 if(!check_resize_safety()){
946 return;
949 Command* c;
950 seqpat* s;
951 seqpat* next;
952 int tmp;
953 int N=0;
954 for(int i=0; i<tracks.size(); i++){
955 s = tracks[i]->head->next;
956 while(s){
957 next = s->next;
958 if(s->selected && s->modified == 0){
959 tracks[i]->modified = 1;
960 s->modified = 1;
961 int T1 = s->tick;
962 int T2 = s->tick + s->dur + rresize_toffset;
963 if(T1 > T2){
964 SWAP(T1,T2);
965 seqpat* stmp = s->prev;
966 //c=new ReverseSeqpat(s);
967 //set_undo(c);
968 s = stmp->next;
969 c=new ResizeSeqpat(s,T2-T1);
970 set_undo(c);
971 s = stmp->next;
972 c=new MoveSeqpat(s,s->track,T1);
973 set_undo(c);
974 N+=2;
976 s = stmp->next;
977 if(s->tick+s->dur > maxt){relayout();}
979 else{
980 if(T1==T2){
981 T2 = T1+128; //magic
983 c=new ResizeSeqpat(s,T2-T1);
984 set_undo(c);
985 N++;
987 if(T2 > maxt){relayout();}
991 s = next;
994 undo_push(N);
996 unmodify_blocks();
997 unmodify_and_unstick_tracks();
1000 void Arranger::apply_lresize(){
1001 if(lresize_toffset==0){
1002 return;
1005 if(!check_resize_safety()){
1006 return;
1009 Command* c;
1010 seqpat* s;
1011 seqpat* next;
1012 int tmp;
1013 int N=0;
1014 for(int i=0; i<tracks.size(); i++){
1015 s = tracks[i]->head->next;
1016 while(s){
1017 next = s->next;
1018 if(s->selected && s->modified == 0){
1019 tracks[i]->modified = 1;
1020 s->modified = 1;
1021 int T1 = s->tick + lresize_toffset;
1022 int T2 = s->tick + s->dur;
1023 if(T1 > T2){
1024 SWAP(T1,T2);
1025 seqpat* stmp = s->prev;
1026 //c=new ReverseSeqpat(s);
1027 //set_undo(c);
1028 s = stmp->next;
1029 c=new ResizeSeqpat(s,T2-T1);
1030 set_undo(c);
1031 s = stmp->next;
1032 c=new MoveSeqpat(s,s->track,T1);
1033 set_undo(c);
1034 N+=2;
1036 s = stmp->next;
1037 if(s->tick+s->dur > maxt){relayout();}
1039 else{
1040 if(T1==T2){
1041 T2 = T1+128; //magic
1043 seqpat* stmp = s->prev;
1044 c=new MoveSeqpat(s,s->track,T1);
1045 set_undo(c);
1046 s = stmp->next;
1047 c=new ResizeSeqpat(s,T2-T1);
1048 set_undo(c);
1049 N+=2;
1051 s = stmp->next;
1052 if(s->tick+s->dur>maxt){relayout();}
1056 s = next;
1059 undo_push(N);
1061 unmodify_blocks();
1062 unmodify_and_unstick_tracks();
1067 int collision_test(int t11, int t12, int t21, int t22){
1068 return !((t11 < t21 && t12 <= t21) ||
1069 (t11 >= t22 && t12 > t22)) ? 1 : 0;
1072 int Arranger::check_move_safety(){
1073 seqpat* s;
1074 seqpat* ptr;
1076 for(int i=0; i<tracks.size(); i++){
1077 s = tracks[i]->head->next;
1078 while(s){
1079 if(s->selected){
1080 if(i+move_koffset < 0 || i+move_koffset > tracks.size()-1 ||
1081 s->tick + move_toffset < 0){
1082 return 0;
1084 ptr = tracks[i+move_koffset]->head->next;
1085 while(ptr){
1086 if(ptr == s){
1087 ptr=ptr->next; continue;
1089 if(collision_test(s->tick+move_toffset,s->tick+s->dur+move_toffset,ptr->tick,ptr->tick+ptr->dur) ){
1090 if(!ptr->selected){
1091 return 0;
1094 ptr = ptr->next;
1097 s = s->next;
1101 return 1;
1104 int Arranger::check_insert_safety(){
1105 seqpat* s;
1107 int T1 = insert_torig;
1108 int T2 = T1 + insert_toffset;
1109 int tmp;
1111 if(T1>T2){SWAP(T1,T2);}
1113 if(T1 < 0){
1114 return 0;
1116 if(insert_track > tracks.size()-1){
1117 return 0;
1119 if(tracks[insert_track]==NULL){
1120 return 0;
1123 s = tracks[insert_track]->head->next;
1125 while(s){
1126 if(collision_test(T1,T2,s->tick,s->tick+s->dur)){
1127 return 0;
1129 s = s->next;
1132 return 1;
1135 int Arranger::check_resize_safety(){
1136 seqpat* s;
1137 seqpat* ptr;
1139 int T1,T2;
1140 int S1,S2;
1141 int tmp;
1143 for(int i=0; i<tracks.size(); i++){
1144 s = tracks[i]->head->next;
1145 while(s){
1146 if(!s->selected){
1147 s = s->next; continue;
1150 if(rresize_flag){
1151 T1 = s->tick;
1152 T2 = s->tick + s->dur + rresize_toffset;
1154 else if(lresize_flag){
1155 T1 = s->tick + lresize_toffset;
1156 T2 = s->tick + s->dur;
1158 if(T1>T2){SWAP(T1,T2);}
1160 if(T1 < 0){
1161 return 0;
1163 ptr = tracks[s->track]->head->next;
1164 while(ptr){
1165 if(ptr == s){
1166 ptr=ptr->next; continue;
1169 S1 = ptr->tick;
1170 S2 = ptr->tick + ptr->dur;
1171 if(ptr->selected){
1172 if(rresize_flag){
1173 S2 += rresize_toffset;
1175 else if(lresize_flag){
1176 S1 += lresize_toffset;
1180 if(collision_test(T1,T2,S1,S2)){
1181 return 0;
1183 ptr = ptr->next;
1186 s = s->next;
1190 return 1;
1194 int Arranger::check_paste_safety(){
1195 return 1;
1199 void Arranger::apply_unclone(){
1200 Command* c;
1202 pattern* p2 = new pattern(main_sel->p);
1203 //does p2 need to have its ref_c set
1204 float a = randf(0.2,0.8);
1205 while(fabs(p2->v - a)<0.1){
1206 a = randf(0.2,0.8);
1208 p2->v = a;
1209 p2->regen_colors();
1211 //this creates a copy of the block, but uses a copy of the pattern
1212 seqpat* s2 = new seqpat(main_sel,p2);
1215 c = new DeleteSeqpat(main_sel);
1216 set_undo(c);
1217 main_sel = NULL;
1219 c = new CreateSeqpat(s2->track,s2->tick,s2,0);
1220 set_undo(c);
1222 undo_push(2);