Fixed right to left deleting in event editor.
[epichord.git] / src / arranger.cpp
blob53651ca5fc9c9e7c0944a1666e9d9fb0e6ee40c6
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_key()==fltk::DeleteKey){
119 apply_delete();
120 delete_flag = 0;
121 redraw();
122 return 1;
124 if(zoom_out_key(event_key(),event_state())){
125 //if(event_key()==fltk::LeftKey){
126 if(zoom_n > 1){
127 zoom_n--;
128 zoom = 30*(1<<zoom_n)/16;
129 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
130 ui->song_timeline->update(get_play_position());
131 ui->song_timeline->redraw();
132 relayout();
134 redraw();
135 return 1;
137 if(zoom_in_key(event_key(),event_state())){
138 //if(event_key()==fltk::RightKey){
139 if(zoom_n < 8){
140 zoom_n++;
141 zoom = 30*(1<<zoom_n)/16;
142 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
143 ui->song_timeline->update(get_play_position());
144 ui->song_timeline->redraw();
145 relayout();
147 redraw();
148 return 1;
150 return 0;
151 case fltk::PUSH:
152 take_focus();
153 if(event_button()==1){//left mouse
154 seqpat* s = over_seqpat();
155 if(s==NULL){
156 if(color_flag || unclone_flag){//do nothing
158 else if(event_state()&fltk::SHIFT){//begin box
159 box_flag = 1;
160 box_x1=X;
161 box_x2=X;
162 box_y1=Y;
163 box_y2=Y;
164 box_t1=xpix2tick(X+scrollx);
165 box_t2=box_t1;
166 box_k1=(Y+scrolly)/30;
167 box_k2=box_k1;
169 else{//begin insert
170 insert_flag = 1;
171 insert_torig = xpix2tick(X+scrollx)/q_tick*q_tick;
172 insert_toffset = q_tick;
173 insert_track = (Y+scrolly) / 30;
176 else{
177 main_sel = s;
178 if(color_flag){
179 color_sel = s->p;
180 color_orig_x = X;
181 color_orig_y = Y;
182 color_orig_h = color_sel->h;
183 color_orig_v = color_sel->v;
184 color_h = color_orig_h;
185 color_v = color_orig_v;
186 return 1;
188 if(unclone_flag){
189 apply_unclone();
191 if(!s->selected && !(event_state()&SHIFT)){
192 unselect_all();
194 s->selected = 1;
195 if(fltk::event_clicks() > 0){//'double click'
196 ui->piano_roll->load(s);
197 ui->event_edit->load(s);
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)){//begin resize
208 lresize_flag = 1;
209 lresize_torig = s->tick;
210 lresize_toffset = 0;
212 else if(over_rhandle(s)){//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+scrollx;
225 move_y = Y+scrolly;
226 move_offset = xpix2tick(X+scrollx)/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(X+scrollx));
242 paste_track = (Y+scrolly) / 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;
271 resize_arrow_color = fltk::color(128,0,0);
274 redraw();
275 return 1;
276 case fltk::DRAG:
277 if(box_flag){
278 box_x2 = X;
279 box_y2 = Y;
280 box_t2 = xpix2tick(X+scrollx);
281 box_k2 = (Y+scrolly)/30;
283 if(color_flag && color_sel){
284 color_sel->h = color_orig_h + (color_orig_x - X)/1.0;
285 color_sel->v = color_orig_v + (color_orig_y - Y)/100.0;
286 color_sel->regen_colors();
287 color_h = color_sel->h;
288 color_v = color_sel->v;
289 set_default_hsv_value(color_v);
291 if(insert_flag){
292 insert_toffset = xpix2tick(X+scrollx)/q_tick*q_tick+q_tick-insert_torig;
293 if(insert_toffset <=0){
294 insert_toffset -= q_tick;
296 insert_track = (Y+scrolly) / 30;
298 else if(rresize_flag){
299 rresize_toffset = xpix2tick(X+scrollx)/128*128 - rresize_torig;
301 else if(lresize_flag){
302 lresize_toffset = xpix2tick(X+scrollx)/128*128 - lresize_torig;
304 else if(move_flag){
305 move_toffset = quantize(xpix2tick(X+scrollx)) - move_torig - move_offset;
306 move_koffset = (Y+scrolly) / 30 - move_korig;
308 else if(paste_flag){
309 paste_t = quantize(xpix2tick(X+scrollx));
310 paste_track = (Y+scrolly) / 30;
312 redraw();
313 return 1;
314 case fltk::RELEASE:
315 if(event_button()==1){
316 if(box_flag){
317 apply_box();
318 box_flag = 0;
320 if(insert_flag){
321 apply_insert();
322 insert_flag = 0;
324 else if(move_flag){
325 apply_move();
326 move_flag = 0;
328 else if(rresize_flag){
329 apply_rresize();
330 rresize_flag = 0;
331 resize_arrow = 0;
333 else if(lresize_flag){
334 apply_lresize();
335 lresize_flag = 0;
336 resize_arrow = 0;
339 insert_flag=0;
340 color_sel = NULL;
342 else if(event_button()==2){
343 if(paste_flag){
344 apply_paste();
346 paste_flag=0;
348 else if(event_button()==3){
349 seqpat* over_s = over_seqpat();
350 if(delete_flag && over_s){
351 if(over_s->selected){
352 apply_delete();
355 delete_flag=0;
356 resize_arrow = 0;
361 redraw();
362 return 1;
363 case fltk::MOVE:
364 if(color_flag){break;}
365 seqpat* s = over_seqpat();
366 if(s){
367 if(over_rhandle(s)){
368 if(resize_s != s || resize_arrow != 1){
369 if(s->selected){resize_arrow_color = fltk::color(128,128,0);}
370 else{resize_arrow_color = fltk::color(s->p->r2,s->p->g2,s->p->b2);}
371 resize_s = s;
372 resize_arrow = 1;
373 resize_x = tick2xpix(s->tick + s->dur)-scrollx-resize_handle_width-1;
374 resize_y = s->track*30-scrolly;
375 redraw();
378 else if(over_lhandle(s)){
379 if(resize_s != s || resize_arrow != 1){
380 if(s->selected){resize_arrow_color = fltk::color(128,128,0);}
381 else{resize_arrow_color = fltk::color(s->p->r2,s->p->g2,s->p->b2);}
382 resize_s = s;
383 resize_arrow = -1;
384 resize_x = tick2xpix(s->tick)+1 - scrollx;
385 resize_y = s->track*30 - scrolly;
386 redraw();
389 else{
390 if(resize_arrow != 0){
391 resize_arrow=0;
392 redraw();
396 else{
397 if(resize_arrow != 0){
398 resize_arrow=0;
399 redraw();
402 return 1;
405 return 0;
408 void Arranger::draw(){
410 fltk::push_clip(0,0,w(),h());
412 fltk::setfont(fltk::HELVETICA,8);
414 fltk::setcolor(fltk::GRAY05);
415 fltk::fillrect(0,0,w(),h());
417 fltk::setcolor(fltk::GRAY20);
418 int M = config.beats_per_measure;
419 int I=0;
420 for(int i=1; I<w(); i++){
421 I = i*zoom*M/4 - scrollx;
422 if(I>=0){
423 fltk::fillrect(I,0,1,h());
427 fltk::setcolor(fltk::GRAY50);
428 int P = config.measures_per_phrase;
429 if(P){
430 I=0;
431 for(int i=1; I<w(); i++){
432 I = i*zoom*4*P*M/4/4 - scrollx;
433 if(I>=0){
434 fltk::fillrect(I,0,1,h());
439 if(insert_flag){
440 fltk::setcolor(fltk::BLUE);
441 int T1 = insert_torig;
442 int T2 = T1 + insert_toffset;
443 int tmp;
444 if(T1>T2){SWAP(T1,T2);}
445 int X = tick2xpix(T1)+1 - scrollx;
446 int Y = insert_track*30 - scrolly;
447 int W = tick2xpix(T2)-tick2xpix(T1) - 1;
448 fltk::fillrect(X,Y,W,28);
451 if(move_flag){
452 if(check_move_safety()){
453 fltk::setcolor(fltk::MAGENTA);
455 else{
456 fltk::setcolor(fltk::RED);
459 for(int i=0; i<tracks.size(); i++){
460 seqpat* s = tracks[i]->head->next;
461 while(s){
462 if(s->selected){
463 int X = tick2xpix(s->tick + move_toffset) - scrollx;
464 int Y = (s->track + move_koffset)*30 - scrolly;
465 int W = tick2xpix(s->dur);
466 fltk::fillrect(X+1,Y+1,W-1,1);
467 fltk::fillrect(X+1,Y+1,1,29-1);
468 fltk::fillrect(X+1,Y+29-1,W-1,1);
469 fltk::fillrect(X+W-1,Y+1,1,29-1);
471 s = s->next;
476 if(paste_flag){
477 fltk::setcolor(fltk::GREEN);
478 int X = tick2xpix(paste_t)+1 - scrollx;
479 int Y = paste_track*30 - scrolly;
480 int W = tick2xpix(main_sel->dur);
481 fltk::fillrect(X,Y,W-1,1);
482 fltk::fillrect(X,Y+28,W-1,1);
483 fltk::fillrect(X,Y,1,28);
484 fltk::fillrect(X+W-2,Y,1,28);
487 int tmp;
488 if(box_flag){
489 fltk::setcolor(fltk::GREEN);
490 int X1,X2,Y1,Y2;
491 X1 = box_x1;
492 X2 = box_x2;
493 Y1 = box_y1;
494 Y2 = box_y2;
495 if(X1>X2){SWAP(X1,X2);}
496 if(Y1>Y2){SWAP(Y1,Y2);}
497 fltk::fillrect(X1,Y1,X2-X1,1);
498 fltk::fillrect(X1,Y1,1,Y2-Y1);
499 fltk::fillrect(X2,Y1,1,Y2-Y1);
500 fltk::fillrect(X1,Y2,X2-X1,1);
503 //draw all seqpat
504 seqpat* s;
505 fltk::Color c;
507 fltk::Color c1,c2,c3,cx;
508 c1 = fltk::BLACK;
510 for(int i=0; i<tracks.size(); i++){
512 s = tracks[i]->head->next;
513 while(s){
515 pattern* p = s->p;
517 get_outline_color(s,&c1,&c2,&c3,&cx);
519 fltk::setcolor(c1);
521 int R1 = lresize_flag&&s->selected ? lresize_toffset : 0;
522 int R2 = rresize_flag&&s->selected ? rresize_toffset : 0;
524 int T1 = s->tick+R1;
525 int T2 = s->tick+s->dur+R2;
527 if(T1 > T2){SWAP(T1,T2)};
529 int X = tick2xpix(T1)+1 - scrollx;
530 int Y = s->track * 30 - scrolly;
531 int W = tick2xpix(T2)-tick2xpix(T1)-1;
533 if(rresize_flag && s->selected && T1==T2){
534 W = tick2xpix(128)-1;
536 if(lresize_flag && s->selected && T1==T2){
537 W = tick2xpix(128)-1;
540 fillrect(X+1,Y+1,W-2,27);
541 //float a = 1.5f;
544 fltk::setcolor(c2);
545 fillrect(X+W-1,Y,1,29);
546 fillrect(X,Y+28,W-1,1);
548 fltk::setcolor(c3);
549 fillrect(X,Y,1,28);
550 fillrect(X,Y,W,1);
552 fltk::push_clip(X,Y,W,30);
554 fltk::setcolor(cx);
557 mevent* e = s->p->events;
558 while(e){
559 if(e->tick >= s->dur){
560 break;
562 if(e->type == MIDI_NOTE_ON){
563 int X2 = tick2xpix(e->tick) + tick2xpix(s->tick)+2 - scrollx;
564 Y = s->track*30 + 27 - e->value1*27/127 - scrolly;
565 int W2 = tick2xpix(e->dur);
566 if(W2==0){W2=1;}
567 if(!(X2+W2<0 || X2>X+W )){
568 fillrect(X2,Y,W2,1);
571 e=e->next;
575 int total = s->layer_total();
576 if(total > 1){
577 fltk::setcolor(fltk::BLACK);
578 int X = tick2xpix(s->tick) - scrollx;
579 int Y = s->track * 30 - scrolly;
580 int count = s->layer_index()+1;
581 char buf[16];
582 snprintf(buf,16,"%d / %d",count,total);
583 fltk::drawtext(buf,X+2,Y+27);
587 fltk::pop_clip();
589 s=s->next;
593 if(!rresize_flag && !lresize_flag){
594 if(resize_arrow > 0){
595 setcolor(resize_arrow_color);
597 int W = resize_handle_width;
598 int H = 28;
599 int X = resize_x;
600 int Y = resize_y;
602 addvertex(X,Y);
603 addvertex(X,Y+H);
604 addvertex(X+W,Y+H/2);
605 fillpath();
607 else if(resize_arrow < 0){
608 setcolor(resize_arrow_color);
610 int W = resize_handle_width;
611 int H = 28;
612 int X = resize_x;
613 int Y = resize_y;
615 addvertex(X+W,Y);
616 addvertex(X+W,Y+H);
617 addvertex(X,Y+H/2);
618 fillpath();
622 fltk::pop_clip();
626 void Arranger::scrollTo(int X, int Y){
628 if(is_backend_playing() && config.follow){
629 int pos = tick2xpix(get_play_position());
630 if(pos < X || pos > X + w() - scrollbuffer - 30){
631 ui->song_hscroll->value(scrollx);
632 return;
636 scrollx = X;
637 ui->song_hscroll->value(scrollx);
638 scrolly = Y;
639 redraw();
640 ui->song_timeline->scroll = X;
641 ui->song_timeline->redraw();
642 ui->track_info->scroll = Y;
643 ui->track_info->redraw();
648 seqpat* Arranger::over_seqpat(){
649 int track = (event_y()+scrolly) / 30;
650 if(track >= tracks.size()){
651 return NULL;
653 int tick = xpix2tick(event_x()+scrollx);
654 seqpat* s = tfind<seqpat>(tracks[track]->head,tick);
655 if(s){
656 if(tick < s->tick+s->dur){
657 return s;
660 return NULL;
664 //true if over right handle of s
665 int Arranger::over_rhandle(seqpat* s){
666 int X = event_x();
667 int Y = event_y();
668 int X1 = tick2xpix(s->tick) - scrollx;
669 int X2 = X1 + tick2xpix(s->dur);
670 int Y1 = s->track * 30 + 1 - scrolly;
671 int Y2 = Y1 + 29;
673 if(tick2xpix(s->dur) < 10){
674 return 0;
677 return (Y > Y1 && Y < Y2 && X < X2 && X > X2 - 5);
680 //true if over left handle of s
681 int Arranger::over_lhandle(seqpat* s){
682 int X = event_x();
683 int Y = event_y();
684 int X1 = tick2xpix(s->tick) - scrollx;
685 int X2 = X1 + tick2xpix(s->dur);
686 int Y1 = s->track * 30 + 1 - scrolly;
687 int Y2 = Y1 + 29;
689 if(tick2xpix(s->dur) < 10){
690 return 0;
693 if(Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1){
694 //printf("success\n");
696 return (Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1);
699 // 4=beats per measure, 128=ticks per beat, 30=width of measure in pixels
700 int Arranger::tick2xpix(int tick){
701 return tick *zoom /(128*4);
704 int Arranger::xpix2tick(int xpix){
705 return xpix * (128*4) /zoom;
708 int Arranger::quantize(int tick){
709 return tick/q_tick * q_tick;
713 void Arranger::update(int pos){
714 if(!is_backend_playing()){
715 return;
717 int X1 = tick2xpix(pos);
718 int X2 = X1 - scrollx;
719 if(X2 < 0){
720 int target = X1-50<0?0:X1-50;
721 scrollTo(target,scrolly);
722 // ui->song_hscroll->value(target);
724 if(X2 > w()-scrollbuffer){
725 int target = X1-50;
726 scrollTo(target,scrolly);
727 //ui->song_hscroll->value(target);
731 static int kludge=2;
732 void Arranger::layout(){
733 if(kludge!=0){
734 kludge--;
735 return;
737 fakeh = tracks.size()*30;
738 if(fakeh<h()){
739 fakeh = h();
741 ui->song_vscroll->maximum(0);
742 ui->song_vscroll->minimum(fakeh-h());
743 int M = h() - 30;
744 int newsize = M-(fakeh-h());
745 if(newsize<20){
746 newsize=20;
748 //ui->song_vscroll->slider_size(379);
752 void Arranger::unselect_all(){
753 seqpat* s;
754 for(int i=0; i<tracks.size(); i++){
755 s = tracks[i]->head->next;
756 while(s){
757 if(s->selected==1){
758 s->selected = 0;
760 s = s->next;
765 void Arranger::get_outline_color(seqpat* s, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3, fltk::Color* cx){
767 pattern* p = s->p;
768 *c1 = fltk::color(p->r1, p->g1, p->b1);
769 *cx = fltk::color(p->rx, p->gx, p->bx);
771 int T1,T2;
772 int tmp;
773 seqpat* over_s = over_seqpat();
774 if(delete_flag && s->selected){
775 *c1 = fltk::color(255,0,0);
776 *c2 = fltk::color(255,0,0);
777 *c3 = fltk::color(255,0,0);
778 return;
781 if(box_flag){
782 T1=box_t1;
783 T2=box_t2;
784 int K1 = box_k1;
785 int K2 = box_k2;
786 int K = s->track;
787 if(T1>T2){SWAP(T1,T2);}
788 if(K1<K2){SWAP(K1,K2);}
789 if(s->tick+s->dur > T1 && s->tick < T2 && K >= K2 && K <= K1){
790 *c1 = fltk::color(0,255,0);
791 *c2 = fltk::color(71,120,59);
792 *c3 = fltk::color(108,229,75);
793 return;
797 if(s->selected){
798 *c1 = fltk::color(255,255,0);
799 *c2 = fltk::color(140,137,46);
800 *c3 = fltk::color(232,255,37);
801 *cx = fltk::color(128,128,0);
802 return;
806 *c2 = fltk::color(p->r2,p->g2,p->b2);
807 *c3 = fltk::color(p->r3,p->g3,p->b3);
812 void Arranger::apply_insert(){
814 if(!check_insert_safety()){
815 return;
818 int tmp;
819 int T1 = insert_torig;
820 int T2 = T1 + insert_toffset;
821 if(T1>T2){SWAP(T1,T2);}
823 Command* c=new CreateSeqpatBlank(insert_track,T1,T2-T1);
824 set_undo(c);
825 undo_push(1);
827 if(T2>maxt){relayout();}
830 void Arranger::apply_box(){
831 seqpat* s;
832 int tmp;
833 int T1=box_t1;
834 int T2=box_t2;
835 int K1 = box_k1;
836 int K2 = box_k2;
837 if(T1>T2){SWAP(T1,T2);}
838 if(K1>K2){SWAP(K1,K2);}
839 if(K1 < 0){K1 = 0;}
840 if(K2 > tracks.size()-1){K2 = tracks.size()-1;}
841 for(int i=K1; i<=K2; i++){
842 s = tracks[i]->head->next;
843 while(s){
844 if(s->tick+s->dur > T1 && s->tick < T2){
845 s->selected = 1;
847 s = s->next;
853 void Arranger::apply_delete(){
854 Command* c;
855 seqpat* s;
856 seqpat* next;
857 int N=0;
858 for(int i=0; i<tracks.size(); i++){
859 s = tracks[i]->head->next;
860 while(s){
861 next = s->next;
862 if(s->selected){
863 tracks[s->track]->modified = 1;
864 c=new DeleteSeqpat(s);
865 set_undo(c);
866 N++;
868 s = next;
871 undo_push(N);
873 unmodify_and_unstick_tracks();
877 void Arranger::apply_move(){
878 if(move_toffset==0 && move_koffset==0){
879 return;
882 if(!check_move_safety()){
883 return;
886 Command* c;
887 seqpat* s;
888 seqpat* next;
889 int N=0;
890 for(int i=0; i<tracks.size(); i++){
891 s = tracks[i]->head->next;
892 while(s){
893 next = s->next;
894 if(s->selected && s->modified == 0){
895 int K = s->track + move_koffset;
896 int T = s->tick + move_toffset;
897 tracks[s->track]->modified = 1;
898 tracks[K]->modified = 1;
899 s->modified = 1;
900 c=new MoveSeqpat(s,K,T);
901 set_undo(c);
902 N++;
904 if(T+s->dur > maxt){relayout();}
906 s = next;
909 undo_push(N);
911 unmodify_blocks();
912 unmodify_and_unstick_tracks();
915 void Arranger::apply_paste(){
916 //safety check
919 Command* c;
921 c = new CreateSeqpat(paste_track,paste_t,main_sel,config.alwayscopy);
922 set_undo(c);
923 undo_push(1);
928 void Arranger::apply_rresize(){
929 if(rresize_toffset==0){
930 return;
933 if(!check_resize_safety()){
934 return;
937 Command* c;
938 seqpat* s;
939 seqpat* next;
940 int tmp;
941 int N=0;
942 for(int i=0; i<tracks.size(); i++){
943 s = tracks[i]->head->next;
944 while(s){
945 next = s->next;
946 if(s->selected && s->modified == 0){
947 tracks[i]->modified = 1;
948 s->modified = 1;
949 int T1 = s->tick;
950 int T2 = s->tick + s->dur + rresize_toffset;
951 if(T1 > T2){
952 SWAP(T1,T2);
953 seqpat* stmp = s->prev;
954 //c=new ReverseSeqpat(s);
955 //set_undo(c);
956 s = stmp->next;
957 c=new ResizeSeqpat(s,T2-T1);
958 set_undo(c);
959 s = stmp->next;
960 c=new MoveSeqpat(s,s->track,T1);
961 set_undo(c);
962 N+=2;
964 s = stmp->next;
965 if(s->tick+s->dur > maxt){relayout();}
967 else{
968 if(T1==T2){
969 T2 = T1+128; //magic
971 c=new ResizeSeqpat(s,T2-T1);
972 set_undo(c);
973 N++;
975 if(T2 > maxt){relayout();}
979 s = next;
982 undo_push(N);
984 unmodify_blocks();
985 unmodify_and_unstick_tracks();
988 void Arranger::apply_lresize(){
989 if(lresize_toffset==0){
990 return;
993 if(!check_resize_safety()){
994 return;
997 Command* c;
998 seqpat* s;
999 seqpat* next;
1000 int tmp;
1001 int N=0;
1002 for(int i=0; i<tracks.size(); i++){
1003 s = tracks[i]->head->next;
1004 while(s){
1005 next = s->next;
1006 if(s->selected && s->modified == 0){
1007 tracks[i]->modified = 1;
1008 s->modified = 1;
1009 int T1 = s->tick + lresize_toffset;
1010 int T2 = s->tick + s->dur;
1011 if(T1 > T2){
1012 SWAP(T1,T2);
1013 seqpat* stmp = s->prev;
1014 //c=new ReverseSeqpat(s);
1015 //set_undo(c);
1016 s = stmp->next;
1017 c=new ResizeSeqpat(s,T2-T1);
1018 set_undo(c);
1019 s = stmp->next;
1020 c=new MoveSeqpat(s,s->track,T1);
1021 set_undo(c);
1022 N+=2;
1024 s = stmp->next;
1025 if(s->tick+s->dur > maxt){relayout();}
1027 else{
1028 if(T1==T2){
1029 T2 = T1+128; //magic
1031 seqpat* stmp = s->prev;
1032 c=new MoveSeqpat(s,s->track,T1);
1033 set_undo(c);
1034 s = stmp->next;
1035 c=new ResizeSeqpat(s,T2-T1);
1036 set_undo(c);
1037 N+=2;
1039 s = stmp->next;
1040 if(s->tick+s->dur>maxt){relayout();}
1044 s = next;
1047 undo_push(N);
1049 unmodify_blocks();
1050 unmodify_and_unstick_tracks();
1055 int collision_test(int t11, int t12, int t21, int t22){
1056 return !((t11 < t21 && t12 <= t21) ||
1057 (t11 >= t22 && t12 > t22)) ? 1 : 0;
1060 int Arranger::check_move_safety(){
1061 seqpat* s;
1062 seqpat* ptr;
1064 for(int i=0; i<tracks.size(); i++){
1065 s = tracks[i]->head->next;
1066 while(s){
1067 if(s->selected){
1068 if(i+move_koffset < 0 || i+move_koffset > tracks.size()-1 ||
1069 s->tick + move_toffset < 0){
1070 return 0;
1072 ptr = tracks[i+move_koffset]->head->next;
1073 while(ptr){
1074 if(ptr == s){
1075 ptr=ptr->next; continue;
1077 if(collision_test(s->tick+move_toffset,s->tick+s->dur+move_toffset,ptr->tick,ptr->tick+ptr->dur) ){
1078 if(!ptr->selected){
1079 return 0;
1082 ptr = ptr->next;
1085 s = s->next;
1089 return 1;
1092 int Arranger::check_insert_safety(){
1093 seqpat* s;
1095 int T1 = insert_torig;
1096 int T2 = T1 + insert_toffset;
1097 int tmp;
1099 if(T1>T2){SWAP(T1,T2);}
1101 if(T1 < 0){
1102 return 0;
1104 if(insert_track > tracks.size()-1){
1105 return 0;
1107 if(tracks[insert_track]==NULL){
1108 return 0;
1111 s = tracks[insert_track]->head->next;
1113 while(s){
1114 if(collision_test(T1,T2,s->tick,s->tick+s->dur)){
1115 return 0;
1117 s = s->next;
1120 return 1;
1123 int Arranger::check_resize_safety(){
1124 seqpat* s;
1125 seqpat* ptr;
1127 int T1,T2;
1128 int S1,S2;
1129 int tmp;
1131 for(int i=0; i<tracks.size(); i++){
1132 s = tracks[i]->head->next;
1133 while(s){
1134 if(!s->selected){
1135 s = s->next; continue;
1138 if(rresize_flag){
1139 T1 = s->tick;
1140 T2 = s->tick + s->dur + rresize_toffset;
1142 else if(lresize_flag){
1143 T1 = s->tick + lresize_toffset;
1144 T2 = s->tick + s->dur;
1146 if(T1>T2){SWAP(T1,T2);}
1148 if(T1 < 0){
1149 return 0;
1151 ptr = tracks[s->track]->head->next;
1152 while(ptr){
1153 if(ptr == s){
1154 ptr=ptr->next; continue;
1157 S1 = ptr->tick;
1158 S2 = ptr->tick + ptr->dur;
1159 if(ptr->selected){
1160 if(rresize_flag){
1161 S2 += rresize_toffset;
1163 else if(lresize_flag){
1164 S1 += lresize_toffset;
1168 if(collision_test(T1,T2,S1,S2)){
1169 return 0;
1171 ptr = ptr->next;
1174 s = s->next;
1178 return 1;
1182 int Arranger::check_paste_safety(){
1183 return 1;
1187 void Arranger::apply_unclone(){
1188 Command* c;
1190 pattern* p2 = new pattern(main_sel->p);
1191 //does p2 need to have its ref_c set
1192 float a = randf(0.2,0.8);
1193 while(fabs(p2->v - a)<0.1){
1194 a = randf(0.2,0.8);
1196 p2->v = a;
1197 p2->regen_colors();
1199 //this creates a copy of the block, but uses a copy of the pattern
1200 seqpat* s2 = new seqpat(main_sel,p2);
1203 c = new DeleteSeqpat(main_sel);
1204 set_undo(c);
1205 main_sel = NULL;
1207 c = new CreateSeqpat(s2->track,s2->tick,s2,0);
1208 set_undo(c);
1210 undo_push(2);