Added join tool.
[epichord.git] / src / arranger.cpp
blob48fd232ff39ce2f8bd73103e6c0436af0b0a0a4c
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 split_s = NULL;
88 join_s = NULL;
91 int Arranger::handle(int event){
92 Command* c;
94 int prevt;
96 int X = event_x();
97 int Y = event_y();
99 seqpat* s;
101 switch(event){
102 case fltk::FOCUS:
103 return 1;
104 case fltk::ENTER:
105 return 1;
106 case fltk::KEYUP:
108 return 0;
109 case fltk::MOUSEWHEEL:
110 s = over_seqpat();
111 if(s){
112 s->autocomplete();
113 if(event_dy()>0){
114 s->prev_layer();
116 else if(event_dy()<0){
117 s->next_layer();
119 s->restate();
120 redraw();
122 return 1;
123 case fltk::SHORTCUT:
124 if(event_key()==fltk::DeleteKey){
125 apply_delete();
126 delete_flag = 0;
127 redraw();
128 return 1;
130 if(zoom_out_key(event_key(),event_state())){
131 //if(event_key()==fltk::LeftKey){
132 if(zoom_n > 1){
133 prevt = xpix2tick(scrollx);
134 zoom_n--;
135 zoom = 30*(1<<zoom_n)/16;
136 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
137 scrollTo(tick2xpix(prevt),scrolly);
138 //ui->song_timeline->update(get_play_position());
139 //ui->song_timeline->redraw();
140 //relayout();
142 redraw();
143 return 1;
145 if(zoom_in_key(event_key(),event_state())){
146 //if(event_key()==fltk::RightKey){
147 if(zoom_n < 8){
148 prevt = xpix2tick(scrollx);
149 zoom_n++;
150 zoom = 30*(1<<zoom_n)/16;
151 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
152 scrollTo(tick2xpix(prevt),scrolly);
153 //ui->song_timeline->update(get_play_position());
154 //ui->song_timeline->redraw();
156 //relayout();
158 redraw();
159 return 1;
161 return 0;
162 case fltk::PUSH:
163 take_focus();
164 if(event_button()==1){//left mouse
165 seqpat* s = over_seqpat();
166 if(s==NULL){
167 if(color_flag||unclone_flag||split_flag||join_flag){//do nothing
169 else if(event_state()&fltk::SHIFT){//begin box
170 box_flag = 1;
171 box_x1=X;
172 box_x2=X;
173 box_y1=Y;
174 box_y2=Y;
175 box_t1=xpix2tick(X+scrollx);
176 box_t2=box_t1;
177 box_k1=(Y+scrolly)/30;
178 box_k2=box_k1;
180 else{//begin insert
181 insert_flag = 1;
182 insert_torig = xpix2tick(X+scrollx)/q_tick*q_tick;
183 insert_toffset = q_tick;
184 insert_track = (Y+scrolly) / 30;
187 else{
188 main_sel = s;
189 if(color_flag){
190 color_sel = s->p;
191 color_orig_x = X;
192 color_orig_y = Y;
193 color_orig_h = color_sel->h;
194 color_orig_v = color_sel->v;
195 color_h = color_orig_h;
196 color_v = color_orig_v;
197 return 1;
199 if(unclone_flag){
200 apply_unclone();
201 redraw();
202 return 1;
204 if(split_flag){
205 apply_split();
206 redraw();
207 return 1;
209 if(join_flag){
210 join_s = s;
211 apply_join();
212 join_s = NULL;
213 redraw();
214 return 1;
216 if(!s->selected && !(event_state()&SHIFT)){
217 unselect_all();
219 s->selected = 1;
220 if(fltk::event_clicks() > 0){//'double click'
221 ui->piano_roll->load(s);
222 ui->event_edit->load(s);
223 ui->pattern_timeline->update(get_play_position());
224 ui->keyboard->cur_port = tracks[s->track]->port;
225 ui->keyboard->cur_chan = tracks[s->track]->chan;
226 ui->track_info->set_rec(s->track);
227 set_rec_track(s->track);
228 show_pattern_edit();
229 return 1;
232 if(over_lhandle(s)){//begin resize
233 lresize_flag = 1;
234 lresize_torig = s->tick;
235 lresize_toffset = 0;
237 else if(over_rhandle(s)){//begin resizemove
238 rresize_flag = 1;
239 rresize_torig = s->tick+s->dur;
240 rresize_toffset = 0;
243 else{//begin move
244 move_flag = 1;
245 move_torig = s->tick;
246 move_toffset = 0;
247 move_korig = s->track;
248 move_koffset = 0;
249 move_x = X+scrollx;
250 move_y = Y+scrolly;
251 move_offset = xpix2tick(X+scrollx)/q_tick*q_tick - s->tick;
255 else if(event_button()==2){//middle mouse
256 seqpat* s = over_seqpat();
257 if(color_flag && s){
258 s->p->h = color_h;
259 s->p->v = color_v;
260 s->p->regen_colors();
261 redraw();
262 return 1;
264 if(main_sel){
265 paste_flag = 1;
266 paste_t = quantize(xpix2tick(X+scrollx));
267 paste_track = (Y+scrolly) / 30;
270 else if(event_button()==3){//right mouse
271 seqpat* s = over_seqpat();
272 if(color_flag && s){
273 seqpat* ptr = tracks[s->track]->head->next;
274 while(ptr){
275 ptr->p->h = color_h;
276 ptr->p->v = color_v;
277 ptr->p->regen_colors();
278 ptr = ptr->next;
280 redraw();
281 return 1;
283 if(s==NULL){
284 unselect_all();
285 delete_sel = NULL;
286 main_sel = NULL;
287 color_sel = NULL;
289 else{//begin delete
290 delete_flag = 1;
291 delete_sel = s;//this line needs to be removed
292 if(!(s->selected)){
293 unselect_all();
295 s->selected = 1;
296 resize_arrow_color = fltk::color(128,0,0);
299 redraw();
300 return 1;
301 case fltk::DRAG:
302 if(split_flag||unclone_flag||join_flag){return 0;}
303 if(box_flag){
304 box_x2 = X;
305 box_y2 = Y;
306 box_t2 = xpix2tick(X+scrollx);
307 box_k2 = (Y+scrolly)/30;
309 if(color_flag && color_sel){
310 color_sel->h = color_orig_h + (color_orig_x - X)/1.0;
311 color_sel->v = color_orig_v + (color_orig_y - Y)/100.0;
312 color_sel->regen_colors();
313 color_h = color_sel->h;
314 color_v = color_sel->v;
315 set_default_hsv_value(color_v);
317 if(insert_flag){
318 insert_toffset = xpix2tick(X+scrollx)/q_tick*q_tick+q_tick-insert_torig;
319 if(insert_toffset <=0){
320 insert_toffset -= q_tick;
322 insert_track = (Y+scrolly) / 30;
324 else if(rresize_flag){
325 rresize_toffset = xpix2tick(X+scrollx)/128*128 - rresize_torig;
327 else if(lresize_flag){
328 lresize_toffset = xpix2tick(X+scrollx)/128*128 - lresize_torig;
330 else if(move_flag){
331 move_toffset = quantize(xpix2tick(X+scrollx)) - move_torig - move_offset;
332 move_koffset = (Y+scrolly) / 30 - move_korig;
334 else if(paste_flag){
335 paste_t = quantize(xpix2tick(X+scrollx));
336 paste_track = (Y+scrolly) / 30;
338 redraw();
339 return 1;
340 case fltk::RELEASE:
341 if(split_flag||unclone_flag||join_flag){return 0;}
342 if(event_button()==1){
343 if(box_flag){
344 apply_box();
345 box_flag = 0;
347 if(insert_flag){
348 apply_insert();
349 insert_flag = 0;
351 else if(move_flag){
352 apply_move();
353 move_flag = 0;
355 else if(rresize_flag){
356 apply_rresize();
357 rresize_flag = 0;
358 resize_arrow = 0;
360 else if(lresize_flag){
361 apply_lresize();
362 lresize_flag = 0;
363 resize_arrow = 0;
366 insert_flag=0;
367 color_sel = NULL;
369 else if(event_button()==2){
370 if(paste_flag){
371 apply_paste();
373 paste_flag=0;
375 else if(event_button()==3){
376 seqpat* over_s = over_seqpat();
377 if(delete_flag && over_s){
378 if(over_s->selected){
379 apply_delete();
382 delete_flag=0;
383 resize_arrow = 0;
388 redraw();
389 return 1;
390 case fltk::MOVE:
391 if(color_flag||unclone_flag){break;}
392 seqpat* s = over_seqpat();
393 if(s){
394 if(split_flag){
395 split_s = s;
396 int temp_t = xpix2tick(X+scrollx)/TICKS_PER_BEAT*TICKS_PER_BEAT;
397 if(temp_t != split_t){
398 split_t = temp_t;
399 redraw();
402 else if(join_flag){
403 if(s!=join_s){
404 join_s = s;
405 if(check_join_safety()){
406 redraw();
408 else{
409 join_s = NULL;
410 redraw();
414 else if(over_rhandle(s)){
415 if(resize_s != s || resize_arrow != 1){
416 if(s->selected){resize_arrow_color = fltk::color(128,128,0);}
417 else{resize_arrow_color = fltk::color(s->p->r2,s->p->g2,s->p->b2);}
418 resize_s = s;
419 resize_arrow = 1;
420 resize_x = tick2xpix(s->tick + s->dur)-scrollx-resize_handle_width-1;
421 resize_y = s->track*30-scrolly;
422 redraw();
425 else if(over_lhandle(s)){
426 if(resize_s != s || resize_arrow != 1){
427 if(s->selected){resize_arrow_color = fltk::color(128,128,0);}
428 else{resize_arrow_color = fltk::color(s->p->r2,s->p->g2,s->p->b2);}
429 resize_s = s;
430 resize_arrow = -1;
431 resize_x = tick2xpix(s->tick)+1 - scrollx;
432 resize_y = s->track*30 - scrolly;
433 redraw();
436 else{
437 if(resize_arrow != 0){
438 resize_arrow=0;
439 redraw();
443 else{
444 int redraw_question = 0;
445 if(join_s != NULL){
446 join_s = NULL;
447 redraw_question = 1;
449 if(split_s != NULL){
450 split_s = NULL;
451 redraw_question = 1;
453 if(resize_arrow != 0){
454 resize_arrow=0;
455 redraw_question = 1;
457 if(redraw_question){redraw();}
459 return 1;
462 return 0;
465 void Arranger::draw(){
467 fltk::push_clip(0,0,w(),h());
469 fltk::setfont(fltk::HELVETICA,8);
471 fltk::setcolor(fltk::GRAY05);
472 fltk::fillrect(0,0,w(),h());
474 fltk::setcolor(fltk::GRAY20);
475 int M = config.beats_per_measure;
476 int I=0;
477 for(int i=1; I<w(); i++){
478 I = i*zoom*M/4 - scrollx;
479 if(I>=0){
480 fltk::fillrect(I,0,1,h());
484 fltk::setcolor(fltk::GRAY50);
485 int P = config.measures_per_phrase;
486 if(P){
487 I=0;
488 for(int i=1; I<w(); i++){
489 I = i*zoom*4*P*M/4/4 - scrollx;
490 if(I>=0){
491 fltk::fillrect(I,0,1,h());
496 if(insert_flag){
497 fltk::setcolor(fltk::BLUE);
498 int T1 = insert_torig;
499 int T2 = T1 + insert_toffset;
500 int tmp;
501 if(T1>T2){SWAP(T1,T2);}
502 int X = tick2xpix(T1)+1 - scrollx;
503 int Y = insert_track*30 - scrolly;
504 int W = tick2xpix(T2)-tick2xpix(T1) - 1;
505 fltk::fillrect(X,Y,W,28);
508 if(move_flag){
509 if(check_move_safety()){
510 fltk::setcolor(fltk::MAGENTA);
512 else{
513 fltk::setcolor(fltk::RED);
516 for(int i=0; i<tracks.size(); i++){
517 seqpat* s = tracks[i]->head->next;
518 while(s){
519 if(s->selected){
520 int X = tick2xpix(s->tick + move_toffset) - scrollx;
521 int Y = (s->track + move_koffset)*30 - scrolly;
522 int W = tick2xpix(s->dur);
523 fltk::fillrect(X+1,Y+1,W-1,1);
524 fltk::fillrect(X+1,Y+1,1,29-1);
525 fltk::fillrect(X+1,Y+29-1,W-1,1);
526 fltk::fillrect(X+W-1,Y+1,1,29-1);
528 s = s->next;
533 if(paste_flag){
534 fltk::setcolor(fltk::GREEN);
535 int X = tick2xpix(paste_t)+1 - scrollx;
536 int Y = paste_track*30 - scrolly;
537 int W = tick2xpix(main_sel->dur);
538 fltk::fillrect(X,Y,W-1,1);
539 fltk::fillrect(X,Y+28,W-1,1);
540 fltk::fillrect(X,Y,1,28);
541 fltk::fillrect(X+W-2,Y,1,28);
544 int tmp;
545 if(box_flag){
546 fltk::setcolor(fltk::GREEN);
547 int X1,X2,Y1,Y2;
548 X1 = box_x1;
549 X2 = box_x2;
550 Y1 = box_y1;
551 Y2 = box_y2;
552 if(X1>X2){SWAP(X1,X2);}
553 if(Y1>Y2){SWAP(Y1,Y2);}
554 fltk::fillrect(X1,Y1,X2-X1,1);
555 fltk::fillrect(X1,Y1,1,Y2-Y1);
556 fltk::fillrect(X2,Y1,1,Y2-Y1);
557 fltk::fillrect(X1,Y2,X2-X1,1);
560 //draw all seqpat
561 seqpat* s;
562 fltk::Color c;
564 fltk::Color c1,c2,c3,cx;
565 c1 = fltk::BLACK;
567 for(int i=0; i<tracks.size(); i++){
569 s = tracks[i]->head->next;
570 while(s){
572 pattern* p = s->p;
574 get_outline_color(s,&c1,&c2,&c3,&cx);
576 fltk::setcolor(c1);
578 int R1 = lresize_flag&&s->selected ? lresize_toffset : 0;
579 int R2 = rresize_flag&&s->selected ? rresize_toffset : 0;
581 int T1 = s->tick+R1;
582 int T2 = s->tick+s->dur+R2;
584 if(T1 > T2){SWAP(T1,T2)};
586 int X = tick2xpix(T1)+1 - scrollx;
587 int Y = s->track * 30 - scrolly;
588 int W = tick2xpix(T2)-tick2xpix(T1)-1;
590 if(rresize_flag && s->selected && T1==T2){
591 W = tick2xpix(128)-1;
593 if(lresize_flag && s->selected && T1==T2){
594 W = tick2xpix(128)-1;
597 fillrect(X+1,Y+1,W-2,27);
598 //float a = 1.5f;
601 fltk::setcolor(c2);
602 fillrect(X+W-1,Y,1,29);
603 fillrect(X,Y+28,W-1,1);
605 fltk::setcolor(c3);
606 fillrect(X,Y,1,28);
607 fillrect(X,Y,W,1);
609 if(s==join_s){
610 fltk::setcolor(fltk::YELLOW);
611 fillrect(X-2,Y,3,28);
612 fltk::setcolor(fltk::color(128,128,0));
613 fillrect(X-2,Y+28,3,1);
616 fltk::push_clip(X,Y,W,30);
618 fltk::setcolor(cx);
620 mevent* e = s->p->events;
621 while(e){
622 if(e->tick >= s->dur){
623 break;
625 if(e->type == MIDI_NOTE_ON){
626 int X2 = tick2xpix(e->tick) + tick2xpix(s->tick)+2 - scrollx;
627 Y = s->track*30 + 27 - e->value1*27/127 - scrolly;
628 int W2 = tick2xpix(e->dur);
629 if(W2==0){W2=1;}
630 if(!(X2+W2<0 || X2>X+W )){
631 fillrect(X2,Y,W2,1);
634 e=e->next;
638 int total = s->layer_total();
639 if(total > 1){
640 fltk::setcolor(fltk::BLACK);
641 int X = tick2xpix(s->tick) - scrollx;
642 int Y = s->track * 30 - scrolly;
643 int count = s->layer_index()+1;
644 char buf[16];
645 snprintf(buf,16,"%d / %d",count,total);
646 fltk::drawtext(buf,X+2,Y+27);
650 fltk::pop_clip();
652 s=s->next;
656 if(!rresize_flag && !lresize_flag){
657 if(resize_arrow > 0){
658 setcolor(resize_arrow_color);
660 int W = resize_handle_width;
661 int H = 28;
662 int X = resize_x;
663 int Y = resize_y;
665 addvertex(X,Y);
666 addvertex(X,Y+H);
667 addvertex(X+W,Y+H/2);
668 fillpath();
670 else if(resize_arrow < 0){
671 setcolor(resize_arrow_color);
673 int W = resize_handle_width;
674 int H = 28;
675 int X = resize_x;
676 int Y = resize_y;
678 addvertex(X+W,Y);
679 addvertex(X+W,Y+H);
680 addvertex(X,Y+H/2);
681 fillpath();
685 if(split_s){
686 fltk::setcolor(fltk::RED);
687 int X = tick2xpix(split_t) - scrollx;
688 int Y = split_s->track*30;
689 fltk::fillrect(X,Y+1,1,28);
692 fltk::pop_clip();
696 void Arranger::scrollTo(int X, int Y){
698 if(is_backend_playing() && config.follow){
699 int pos = tick2xpix(get_play_position());
700 if(pos < X || pos > X + w() - scrollbuffer - 30){
701 ui->song_hscroll->value(scrollx);
702 return;
706 scrollx = X;
707 ui->song_hscroll->value(scrollx);
708 scrolly = Y;
709 redraw();
710 ui->song_timeline->scroll = X;
711 ui->song_timeline->redraw();
712 ui->track_info->scroll = Y;
713 ui->track_info->redraw();
718 seqpat* Arranger::over_seqpat(){
719 int track = (event_y()+scrolly) / 30;
720 if(track >= tracks.size()){
721 return NULL;
723 int tick = xpix2tick(event_x()+scrollx);
724 seqpat* s = tfind<seqpat>(tracks[track]->head,tick);
725 if(s){
726 if(tick < s->tick+s->dur){
727 return s;
730 return NULL;
734 //true if over right handle of s
735 int Arranger::over_rhandle(seqpat* s){
736 int X = event_x();
737 int Y = event_y();
738 int X1 = tick2xpix(s->tick) - scrollx;
739 int X2 = X1 + tick2xpix(s->dur);
740 int Y1 = s->track * 30 + 1 - scrolly;
741 int Y2 = Y1 + 29;
743 if(tick2xpix(s->dur) < 10){
744 return 0;
747 return (Y > Y1 && Y < Y2 && X < X2 && X > X2 - 5);
750 //true if over left handle of s
751 int Arranger::over_lhandle(seqpat* s){
752 int X = event_x();
753 int Y = event_y();
754 int X1 = tick2xpix(s->tick) - scrollx;
755 int X2 = X1 + tick2xpix(s->dur);
756 int Y1 = s->track * 30 + 1 - scrolly;
757 int Y2 = Y1 + 29;
759 if(tick2xpix(s->dur) < 10){
760 return 0;
763 if(Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1){
764 //printf("success\n");
766 return (Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1);
769 // 4=beats per measure, 128=ticks per beat, 30=width of measure in pixels
770 int Arranger::tick2xpix(int tick){
771 return tick *zoom /(128*4);
774 int Arranger::xpix2tick(int xpix){
775 return xpix * (128*4) /zoom;
778 int Arranger::quantize(int tick){
779 return tick/q_tick * q_tick;
783 void Arranger::update(int pos){
784 if(!is_backend_playing()){
785 return;
787 int X1 = tick2xpix(pos);
788 int X2 = X1 - scrollx;
789 if(X2 < 0){
790 int target = X1-50<0?0:X1-50;
791 scrollTo(target,scrolly);
792 // ui->song_hscroll->value(target);
794 if(X2 > w()-scrollbuffer){
795 int target = X1-50;
796 scrollTo(target,scrolly);
797 //ui->song_hscroll->value(target);
801 static int kludge=2;
802 void Arranger::layout(){
803 if(kludge!=0){
804 kludge--;
805 return;
807 fakeh = tracks.size()*30;
808 if(fakeh<h()){
809 fakeh = h();
811 ui->song_vscroll->maximum(0);
812 ui->song_vscroll->minimum(fakeh-h());
813 int M = h() - 30;
814 int newsize = M-(fakeh-h());
815 if(newsize<20){
816 newsize=20;
818 //ui->song_vscroll->slider_size(379);
822 void Arranger::unselect_all(){
823 seqpat* s;
824 for(int i=0; i<tracks.size(); i++){
825 s = tracks[i]->head->next;
826 while(s){
827 if(s->selected==1){
828 s->selected = 0;
830 s = s->next;
835 void Arranger::get_outline_color(seqpat* s, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3, fltk::Color* cx){
837 pattern* p = s->p;
838 *c1 = fltk::color(p->r1, p->g1, p->b1);
839 *cx = fltk::color(p->rx, p->gx, p->bx);
841 int T1,T2;
842 int tmp;
843 seqpat* over_s = over_seqpat();
844 if(delete_flag && s->selected){
845 *c1 = fltk::color(255,0,0);
846 *c2 = fltk::color(255,0,0);
847 *c3 = fltk::color(255,0,0);
848 return;
851 if(box_flag){
852 T1=box_t1;
853 T2=box_t2;
854 int K1 = box_k1;
855 int K2 = box_k2;
856 int K = s->track;
857 if(T1>T2){SWAP(T1,T2);}
858 if(K1<K2){SWAP(K1,K2);}
859 if(s->tick+s->dur > T1 && s->tick < T2 && K >= K2 && K <= K1){
860 *c1 = fltk::color(0,255,0);
861 *c2 = fltk::color(71,120,59);
862 *c3 = fltk::color(108,229,75);
863 return;
867 if(s->selected){
868 *c1 = fltk::color(255,255,0);
869 *c2 = fltk::color(140,137,46);
870 *c3 = fltk::color(232,255,37);
871 *cx = fltk::color(128,128,0);
872 return;
876 *c2 = fltk::color(p->r2,p->g2,p->b2);
877 *c3 = fltk::color(p->r3,p->g3,p->b3);
882 void Arranger::apply_insert(){
884 if(!check_insert_safety()){
885 return;
888 int tmp;
889 int T1 = insert_torig;
890 int T2 = T1 + insert_toffset;
891 if(T1>T2){SWAP(T1,T2);}
893 Command* c=new CreateSeqpatBlank(insert_track,T1,T2-T1);
894 set_undo(c);
895 undo_push(1);
897 if(T2>maxt){relayout();}
900 void Arranger::apply_box(){
901 seqpat* s;
902 int tmp;
903 int T1=box_t1;
904 int T2=box_t2;
905 int K1 = box_k1;
906 int K2 = box_k2;
907 if(T1>T2){SWAP(T1,T2);}
908 if(K1>K2){SWAP(K1,K2);}
909 if(K1 < 0){K1 = 0;}
910 if(K2 > tracks.size()-1){K2 = tracks.size()-1;}
911 for(int i=K1; i<=K2; i++){
912 s = tracks[i]->head->next;
913 while(s){
914 if(s->tick+s->dur > T1 && s->tick < T2){
915 s->selected = 1;
917 s = s->next;
923 void Arranger::apply_delete(){
924 Command* c;
925 seqpat* s;
926 seqpat* next;
927 int N=0;
928 for(int i=0; i<tracks.size(); i++){
929 s = tracks[i]->head->next;
930 while(s){
931 next = s->next;
932 if(s->selected){
933 tracks[s->track]->modified = 1;
934 c=new DeleteSeqpat(s);
935 set_undo(c);
936 N++;
938 s = next;
941 undo_push(N);
943 unmodify_and_unstick_tracks();
947 void Arranger::apply_move(){
948 if(move_toffset==0 && move_koffset==0){
949 return;
952 if(!check_move_safety()){
953 return;
956 Command* c;
957 seqpat* s;
958 seqpat* next;
959 int N=0;
960 for(int i=0; i<tracks.size(); i++){
961 s = tracks[i]->head->next;
962 while(s){
963 next = s->next;
964 if(s->selected && s->modified == 0){
965 int K = s->track + move_koffset;
966 int T = s->tick + move_toffset;
967 tracks[s->track]->modified = 1;
968 tracks[K]->modified = 1;
969 s->modified = 1;
970 c=new MoveSeqpat(s,K,T);
971 set_undo(c);
972 N++;
974 if(T+s->dur > maxt){relayout();}
976 s = next;
979 undo_push(N);
981 unmodify_blocks();
982 unmodify_and_unstick_tracks();
985 void Arranger::apply_paste(){
986 //safety check
989 Command* c;
991 c = new CreateSeqpat(paste_track,paste_t,main_sel,config.alwayscopy);
992 set_undo(c);
993 undo_push(1);
998 void Arranger::apply_rresize(){
999 if(rresize_toffset==0){
1000 return;
1003 if(!check_resize_safety()){
1004 return;
1007 Command* c;
1008 seqpat* s;
1009 seqpat* next;
1010 int tmp;
1011 int N=0;
1012 for(int i=0; i<tracks.size(); i++){
1013 s = tracks[i]->head->next;
1014 while(s){
1015 next = s->next;
1016 if(s->selected && s->modified == 0){
1017 tracks[i]->modified = 1;
1018 s->modified = 1;
1019 int T1 = s->tick;
1020 int T2 = s->tick + s->dur + rresize_toffset;
1021 if(T1 > T2){
1022 SWAP(T1,T2);
1023 seqpat* stmp = s->prev;
1024 //c=new ReverseSeqpat(s);
1025 //set_undo(c);
1026 s = stmp->next;
1027 c=new ResizeSeqpat(s,T2-T1);
1028 set_undo(c);
1029 s = stmp->next;
1030 c=new MoveSeqpat(s,s->track,T1);
1031 set_undo(c);
1032 N+=2;
1034 s = stmp->next;
1035 if(s->tick+s->dur > maxt){relayout();}
1037 else{
1038 if(T1==T2){
1039 T2 = T1+128; //magic
1041 c=new ResizeSeqpat(s,T2-T1);
1042 set_undo(c);
1043 N++;
1045 if(T2 > maxt){relayout();}
1049 s = next;
1052 undo_push(N);
1054 unmodify_blocks();
1055 unmodify_and_unstick_tracks();
1058 void Arranger::apply_lresize(){
1059 if(lresize_toffset==0){
1060 return;
1063 if(!check_resize_safety()){
1064 return;
1067 Command* c;
1068 seqpat* s;
1069 seqpat* next;
1070 int tmp;
1071 int N=0;
1072 for(int i=0; i<tracks.size(); i++){
1073 s = tracks[i]->head->next;
1074 while(s){
1075 next = s->next;
1076 if(s->selected && s->modified == 0){
1077 tracks[i]->modified = 1;
1078 s->modified = 1;
1079 int T1 = s->tick + lresize_toffset;
1080 int T2 = s->tick + s->dur;
1081 if(T1 > T2){
1082 SWAP(T1,T2);
1083 seqpat* stmp = s->prev;
1084 //c=new ReverseSeqpat(s);
1085 //set_undo(c);
1086 s = stmp->next;
1087 c=new ResizeSeqpat(s,T2-T1);
1088 set_undo(c);
1089 s = stmp->next;
1090 c=new MoveSeqpat(s,s->track,T1);
1091 set_undo(c);
1092 N+=2;
1094 s = stmp->next;
1095 if(s->tick+s->dur > maxt){relayout();}
1097 else{
1098 if(T1==T2){
1099 T2 = T1+128; //magic
1101 seqpat* stmp = s->prev;
1102 c=new MoveSeqpat(s,s->track,T1);
1103 set_undo(c);
1104 s = stmp->next;
1105 c=new ResizeSeqpat(s,T2-T1);
1106 set_undo(c);
1107 N+=2;
1109 s = stmp->next;
1110 if(s->tick+s->dur>maxt){relayout();}
1114 s = next;
1117 undo_push(N);
1119 unmodify_blocks();
1120 unmodify_and_unstick_tracks();
1125 int collision_test(int t11, int t12, int t21, int t22){
1126 return !((t11 < t21 && t12 <= t21) ||
1127 (t11 >= t22 && t12 > t22)) ? 1 : 0;
1130 int Arranger::check_move_safety(){
1131 seqpat* s;
1132 seqpat* ptr;
1134 for(int i=0; i<tracks.size(); i++){
1135 s = tracks[i]->head->next;
1136 while(s){
1137 if(s->selected){
1138 if(i+move_koffset < 0 || i+move_koffset > tracks.size()-1 ||
1139 s->tick + move_toffset < 0){
1140 return 0;
1142 ptr = tracks[i+move_koffset]->head->next;
1143 while(ptr){
1144 if(ptr == s){
1145 ptr=ptr->next; continue;
1147 if(collision_test(s->tick+move_toffset,s->tick+s->dur+move_toffset,ptr->tick,ptr->tick+ptr->dur) ){
1148 if(!ptr->selected){
1149 return 0;
1152 ptr = ptr->next;
1155 s = s->next;
1159 return 1;
1162 int Arranger::check_insert_safety(){
1163 seqpat* s;
1165 int T1 = insert_torig;
1166 int T2 = T1 + insert_toffset;
1167 int tmp;
1169 if(T1>T2){SWAP(T1,T2);}
1171 if(T1 < 0){
1172 return 0;
1174 if(insert_track > tracks.size()-1){
1175 return 0;
1177 if(tracks[insert_track]==NULL){
1178 return 0;
1181 s = tracks[insert_track]->head->next;
1183 while(s){
1184 if(collision_test(T1,T2,s->tick,s->tick+s->dur)){
1185 return 0;
1187 s = s->next;
1190 return 1;
1193 int Arranger::check_resize_safety(){
1194 seqpat* s;
1195 seqpat* ptr;
1197 int T1,T2;
1198 int S1,S2;
1199 int tmp;
1201 for(int i=0; i<tracks.size(); i++){
1202 s = tracks[i]->head->next;
1203 while(s){
1204 if(!s->selected){
1205 s = s->next; continue;
1208 if(rresize_flag){
1209 T1 = s->tick;
1210 T2 = s->tick + s->dur + rresize_toffset;
1212 else if(lresize_flag){
1213 T1 = s->tick + lresize_toffset;
1214 T2 = s->tick + s->dur;
1216 if(T1>T2){SWAP(T1,T2);}
1218 if(T1 < 0){
1219 return 0;
1221 ptr = tracks[s->track]->head->next;
1222 while(ptr){
1223 if(ptr == s){
1224 ptr=ptr->next; continue;
1227 S1 = ptr->tick;
1228 S2 = ptr->tick + ptr->dur;
1229 if(ptr->selected){
1230 if(rresize_flag){
1231 S2 += rresize_toffset;
1233 else if(lresize_flag){
1234 S1 += lresize_toffset;
1238 if(collision_test(T1,T2,S1,S2)){
1239 return 0;
1241 ptr = ptr->next;
1244 s = s->next;
1248 return 1;
1252 int Arranger::check_paste_safety(){
1253 return 1;
1256 int Arranger::check_join_safety(){
1257 seqpat* s = join_s;
1258 if(s->prev->tick+s->prev->dur==s->tick && s->prev->prev){
1259 return 1;
1261 return 0;
1265 void Arranger::apply_unclone(){
1266 Command* c;
1268 pattern* p2 = new pattern(main_sel->p);
1269 //does p2 need to have its ref_c set
1270 float a = randf(0.2,0.8);
1271 while(fabs(p2->v - a)<0.1){
1272 a = randf(0.2,0.8);
1274 p2->v = a;
1275 p2->regen_colors();
1277 //this creates a copy of the block, but uses a copy of the pattern
1278 seqpat* s2 = new seqpat(main_sel,p2);
1281 c = new DeleteSeqpat(main_sel);
1282 set_undo(c);
1283 main_sel = NULL;
1285 c = new CreateSeqpat(s2->track,s2->tick,s2,0);
1286 set_undo(c);
1288 undo_push(2);
1292 void Arranger::apply_split(){
1293 seqpat* s = split_s;
1294 if(split_t == s->tick || split_t == s->tick+s->dur){
1295 return;
1297 Command* c = new SplitSeqpat(split_s,split_t);
1298 set_undo(c);
1299 undo_push(1);
1303 void Arranger::apply_join(){
1304 seqpat* s = join_s;
1305 if(!check_join_safety()){
1306 return;
1308 Command* c = new JoinSeqpat(join_s->prev,join_s);
1309 set_undo(c);
1310 undo_push(1);