Unhardcoded ticks per beat. Fixed triplet bugs.
[epichord.git] / src / arranger.cpp
blob4a149ca5fd1c85a877020a568aeac8832d7fac4c
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 = TICKS_PER_BEAT*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 = TICKS_PER_BEAT*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(!s){
265 paste_flag = 1;
266 paste_tcenter1 = quantize(xpix2tick(X+scrollx));
267 paste_kcenter1 = (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)/TICKS_PER_BEAT*TICKS_PER_BEAT -
326 rresize_torig;
328 else if(lresize_flag){
329 lresize_toffset = xpix2tick(X+scrollx)/TICKS_PER_BEAT*TICKS_PER_BEAT -
330 lresize_torig;
332 else if(move_flag){
333 move_toffset = quantize(xpix2tick(X+scrollx)) - move_torig - move_offset;
334 move_koffset = (Y+scrolly) / 30 - move_korig;
336 else if(paste_flag){
337 paste_tcenter1 = quantize(xpix2tick(X+scrollx));
338 paste_kcenter1 = (Y+scrolly) / 30;
340 redraw();
341 return 1;
342 case fltk::RELEASE:
343 if(split_flag||unclone_flag||join_flag){return 0;}
344 if(event_button()==1){
345 if(box_flag){
346 apply_box();
347 box_flag = 0;
349 if(insert_flag){
350 apply_insert();
351 insert_flag = 0;
353 else if(move_flag){
354 apply_move();
355 move_flag = 0;
357 else if(rresize_flag){
358 apply_rresize();
359 rresize_flag = 0;
360 resize_arrow = 0;
362 else if(lresize_flag){
363 apply_lresize();
364 lresize_flag = 0;
365 resize_arrow = 0;
368 insert_flag=0;
369 color_sel = NULL;
371 else if(event_button()==2){
372 if(paste_flag){
373 apply_paste();
375 paste_flag=0;
377 else if(event_button()==3){
378 seqpat* over_s = over_seqpat();
379 if(delete_flag && over_s){
380 if(over_s->selected){
381 apply_delete();
384 delete_flag=0;
385 resize_arrow = 0;
390 redraw();
391 return 1;
392 case fltk::MOVE:
393 if(color_flag||unclone_flag){break;}
394 seqpat* s = over_seqpat();
395 if(s){
396 if(split_flag){
397 int temp_t = xpix2tick(X+scrollx)/TICKS_PER_BEAT*TICKS_PER_BEAT;
398 if(split_s != s || temp_t != split_t){
399 split_s = s;
400 split_t = temp_t;
401 redraw();
404 else if(join_flag){
405 if(s!=join_s){
406 join_s = s;
407 if(check_join_safety()){
408 redraw();
410 else{
411 join_s = NULL;
412 redraw();
416 else if(over_rhandle(s)){
417 if(resize_s != s || resize_arrow != 1){
418 if(s->selected){resize_arrow_color = fltk::color(128,128,0);}
419 else{resize_arrow_color = fltk::color(s->p->r2,s->p->g2,s->p->b2);}
420 resize_s = s;
421 resize_arrow = 1;
422 resize_x = tick2xpix(s->tick + s->dur)-scrollx-resize_handle_width-1;
423 resize_y = s->track*30-scrolly;
424 redraw();
427 else if(over_lhandle(s)){
428 if(resize_s != s || resize_arrow != 1){
429 if(s->selected){resize_arrow_color = fltk::color(128,128,0);}
430 else{resize_arrow_color = fltk::color(s->p->r2,s->p->g2,s->p->b2);}
431 resize_s = s;
432 resize_arrow = -1;
433 resize_x = tick2xpix(s->tick)+1 - scrollx;
434 resize_y = s->track*30 - scrolly;
435 redraw();
438 else{
439 if(resize_arrow != 0){
440 resize_arrow=0;
441 redraw();
445 else{
446 int redraw_question = 0;
447 if(join_s != NULL){
448 join_s = NULL;
449 redraw_question = 1;
451 if(split_s != NULL){
452 split_s = NULL;
453 redraw_question = 1;
455 if(resize_arrow != 0){
456 resize_arrow=0;
457 redraw_question = 1;
459 if(redraw_question){redraw();}
461 return 1;
464 return 0;
467 void Arranger::draw(){
469 fltk::push_clip(0,0,w(),h());
471 fltk::setfont(fltk::HELVETICA,8);
473 fltk::setcolor(fltk::GRAY05);
474 fltk::fillrect(0,0,w(),h());
476 fltk::setcolor(fltk::GRAY20);
477 int M = config.beats_per_measure;
478 int I=0;
479 for(int i=1; I<w(); i++){
480 I = i*zoom*M/4 - scrollx;
481 if(I>=0){
482 fltk::fillrect(I,0,1,h());
486 fltk::setcolor(fltk::GRAY50);
487 int P = config.measures_per_phrase;
488 if(P){
489 I=0;
490 for(int i=1; I<w(); i++){
491 I = i*zoom*4*P*M/4/4 - scrollx;
492 if(I>=0){
493 fltk::fillrect(I,0,1,h());
498 if(insert_flag){
499 fltk::setcolor(fltk::BLUE);
500 int T1 = insert_torig;
501 int T2 = T1 + insert_toffset;
502 int tmp;
503 if(T1>T2){SWAP(T1,T2);}
504 int X = tick2xpix(T1)+1 - scrollx;
505 int Y = insert_track*30 - scrolly;
506 int W = tick2xpix(T2)-tick2xpix(T1) - 1;
507 fltk::fillrect(X,Y,W,28);
510 if(move_flag){
511 if(check_move_safety()){
512 fltk::setcolor(fltk::MAGENTA);
514 else{
515 fltk::setcolor(fltk::RED);
518 for(int i=0; i<tracks.size(); i++){
519 seqpat* s = tracks[i]->head->next;
520 while(s){
521 if(s->selected){
522 int X = tick2xpix(s->tick + move_toffset) - scrollx;
523 int Y = (s->track + move_koffset)*30 - scrolly;
524 int W = tick2xpix(s->dur);
525 fltk::fillrect(X+1,Y+1,W-1,1);
526 fltk::fillrect(X+1,Y+1,1,29-1);
527 fltk::fillrect(X+1,Y+29-1,W-1,1);
528 fltk::fillrect(X+W-1,Y+1,1,29-1);
530 s = s->next;
536 if(paste_flag){
537 //recalc_paste_center();
538 if(check_paste_safety()){
539 fltk::setcolor(fltk::GREEN);
541 else{
542 fltk::setcolor(fltk::RED);
545 for(int i=0; i<tracks.size(); i++){
546 seqpat* s = tracks[i]->head->next;
547 while(s){
548 if(s->selected){
549 //int X = tick2xpix(s->tick-paste_tcenter0+paste_tcenter1) - scrollx;
550 //int Y = (s->track-paste_tcenter0+paste_kcenter1)*30 - scrolly;
551 int X = tick2xpix(paste_tcenter1) - scrollx;
552 int Y = paste_kcenter1*30 - scrolly;
553 int W = tick2xpix(s->dur);
554 fltk::fillrect(X+1,Y+1,W-1,1);
555 fltk::fillrect(X+1,Y+1,1,29-1);
556 fltk::fillrect(X+1,Y+29-1,W-1,1);
557 fltk::fillrect(X+W-1,Y+1,1,29-1);
559 s = s->next;
564 int tmp;
565 if(box_flag){
566 fltk::setcolor(fltk::GREEN);
567 int X1,X2,Y1,Y2;
568 X1 = box_x1;
569 X2 = box_x2;
570 Y1 = box_y1;
571 Y2 = box_y2;
572 if(X1>X2){SWAP(X1,X2);}
573 if(Y1>Y2){SWAP(Y1,Y2);}
574 fltk::fillrect(X1,Y1,X2-X1,1);
575 fltk::fillrect(X1,Y1,1,Y2-Y1);
576 fltk::fillrect(X2,Y1,1,Y2-Y1);
577 fltk::fillrect(X1,Y2,X2-X1,1);
580 //draw all seqpat
581 seqpat* s;
582 fltk::Color c;
584 fltk::Color c1,c2,c3,cx;
585 c1 = fltk::BLACK;
587 for(int i=0; i<tracks.size(); i++){
589 s = tracks[i]->head->next;
590 while(s){
592 pattern* p = s->p;
594 get_outline_color(s,&c1,&c2,&c3,&cx);
596 fltk::setcolor(c1);
598 int R1 = lresize_flag&&s->selected ? lresize_toffset : 0;
599 int R2 = rresize_flag&&s->selected ? rresize_toffset : 0;
601 int T1 = s->tick+R1;
602 int T2 = s->tick+s->dur+R2;
604 if(T1 > T2){SWAP(T1,T2)};
606 int X = tick2xpix(T1)+1 - scrollx;
607 int Y = s->track * 30 - scrolly;
608 int W = tick2xpix(T2)-tick2xpix(T1)-1;
610 if(rresize_flag && s->selected && T1==T2){
611 W = tick2xpix(TICKS_PER_BEAT)-1;
613 if(lresize_flag && s->selected && T1==T2){
614 W = tick2xpix(TICKS_PER_BEAT)-1;
617 fillrect(X+1,Y+1,W-2,27);
618 //float a = 1.5f;
621 fltk::setcolor(c2);
622 fillrect(X+W-1,Y,1,29);
623 fillrect(X,Y+28,W-1,1);
625 fltk::setcolor(c3);
626 fillrect(X,Y,1,28);
627 fillrect(X,Y,W,1);
629 if(s==join_s){
630 fltk::setcolor(fltk::YELLOW);
631 fillrect(X-2,Y,3,28);
632 fltk::setcolor(fltk::color(128,128,0));
633 fillrect(X-2,Y+28,3,1);
636 fltk::push_clip(X,Y,W,30);
638 fltk::setcolor(cx);
640 mevent* e = s->p->events;
641 while(e){
642 if(e->tick >= s->dur){
643 break;
645 if(e->type == MIDI_NOTE_ON){
646 int X2 = tick2xpix(e->tick) + tick2xpix(s->tick)+2 - scrollx;
647 Y = s->track*30 + 27 - e->value1*27/127 - scrolly;
648 int W2 = tick2xpix(e->dur);
649 if(W2==0){W2=1;}
650 if(!(X2+W2<0 || X2>X+W )){
651 fillrect(X2,Y,W2,1);
654 e=e->next;
658 int total = s->layer_total();
659 if(total > 1){
660 fltk::setcolor(fltk::BLACK);
661 int X = tick2xpix(s->tick) - scrollx;
662 int Y = s->track * 30 - scrolly;
663 int count = s->layer_index()+1;
664 char buf[16];
665 snprintf(buf,16,"%d / %d",count,total);
666 fltk::drawtext(buf,X+2,Y+27);
670 fltk::pop_clip();
672 s=s->next;
676 if(!rresize_flag && !lresize_flag){
677 if(resize_arrow > 0){
678 setcolor(resize_arrow_color);
680 int W = resize_handle_width;
681 int H = 28;
682 int X = resize_x;
683 int Y = resize_y;
685 addvertex(X,Y);
686 addvertex(X,Y+H);
687 addvertex(X+W,Y+H/2);
688 fillpath();
690 else if(resize_arrow < 0){
691 setcolor(resize_arrow_color);
693 int W = resize_handle_width;
694 int H = 28;
695 int X = resize_x;
696 int Y = resize_y;
698 addvertex(X+W,Y);
699 addvertex(X+W,Y+H);
700 addvertex(X,Y+H/2);
701 fillpath();
705 if(split_s){
706 fltk::setcolor(fltk::RED);
707 int X = tick2xpix(split_t) - scrollx;
708 int Y = split_s->track*30;
709 fltk::fillrect(X,Y+1,1,28);
712 fltk::pop_clip();
716 void Arranger::scrollTo(int X, int Y){
718 if(is_backend_playing() && config.follow){
719 int pos = tick2xpix(get_play_position());
720 if(pos < X || pos > X + w() - scrollbuffer - 30){
721 ui->song_hscroll->value(scrollx);
722 return;
726 scrollx = X;
727 ui->song_hscroll->value(scrollx);
728 scrolly = Y;
729 redraw();
730 ui->song_timeline->scroll = X;
731 ui->song_timeline->redraw();
732 ui->track_info->scroll = Y;
733 ui->track_info->redraw();
738 seqpat* Arranger::over_seqpat(){
739 int track = (event_y()+scrolly) / 30;
740 if(track >= tracks.size()){
741 return NULL;
743 int tick = xpix2tick(event_x()+scrollx);
744 seqpat* s = tfind<seqpat>(tracks[track]->head,tick);
745 if(s){
746 if(tick < s->tick+s->dur){
747 return s;
750 return NULL;
754 //true if over right handle of s
755 int Arranger::over_rhandle(seqpat* s){
756 int X = event_x();
757 int Y = event_y();
758 int X1 = tick2xpix(s->tick) - scrollx;
759 int X2 = X1 + tick2xpix(s->dur);
760 int Y1 = s->track * 30 + 1 - scrolly;
761 int Y2 = Y1 + 29;
763 if(tick2xpix(s->dur) < 10){
764 return 0;
767 return (Y > Y1 && Y < Y2 && X < X2 && X > X2 - 5);
770 //true if over left handle of s
771 int Arranger::over_lhandle(seqpat* s){
772 int X = event_x();
773 int Y = event_y();
774 int X1 = tick2xpix(s->tick) - scrollx;
775 int X2 = X1 + tick2xpix(s->dur);
776 int Y1 = s->track * 30 + 1 - scrolly;
777 int Y2 = Y1 + 29;
779 if(tick2xpix(s->dur) < 10){
780 return 0;
783 if(Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1){
784 //printf("success\n");
786 return (Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1);
789 // 4=beats per measure, TICKS_PER_BEAT=ticks per beat, 30=width of measure in pixels
790 int Arranger::tick2xpix(int tick){
791 return tick *zoom /(TICKS_PER_BEAT*4);
794 int Arranger::xpix2tick(int xpix){
795 return xpix * (TICKS_PER_BEAT*4) /zoom;
798 int Arranger::quantize(int tick){
799 return tick/q_tick * q_tick;
803 void Arranger::update(int pos){
804 if(!is_backend_playing()){
805 return;
807 int X1 = tick2xpix(pos);
808 int X2 = X1 - scrollx;
809 if(X2 < 0){
810 int target = X1-50<0?0:X1-50;
811 scrollTo(target,scrolly);
812 // ui->song_hscroll->value(target);
814 if(X2 > w()-scrollbuffer){
815 int target = X1-50;
816 scrollTo(target,scrolly);
817 //ui->song_hscroll->value(target);
821 static int kludge=2;
822 void Arranger::layout(){
823 if(kludge!=0){
824 kludge--;
825 return;
827 fakeh = tracks.size()*30;
828 if(fakeh<h()){
829 fakeh = h();
831 ui->song_vscroll->maximum(0);
832 ui->song_vscroll->minimum(fakeh-h());
833 int M = h() - 30;
834 int newsize = M-(fakeh-h());
835 if(newsize<20){
836 newsize=20;
838 //ui->song_vscroll->slider_size(379);
842 void Arranger::unselect_all(){
843 seqpat* s;
844 for(int i=0; i<tracks.size(); i++){
845 s = tracks[i]->head->next;
846 while(s){
847 if(s->selected==1){
848 s->selected = 0;
850 s = s->next;
855 void Arranger::get_outline_color(seqpat* s, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3, fltk::Color* cx){
857 pattern* p = s->p;
858 *c1 = fltk::color(p->r1, p->g1, p->b1);
859 *cx = fltk::color(p->rx, p->gx, p->bx);
861 int T1,T2;
862 int tmp;
863 seqpat* over_s = over_seqpat();
864 if(delete_flag && s->selected){
865 *c1 = fltk::color(255,0,0);
866 *c2 = fltk::color(255,0,0);
867 *c3 = fltk::color(255,0,0);
868 return;
871 if(box_flag){
872 T1=box_t1;
873 T2=box_t2;
874 int K1 = box_k1;
875 int K2 = box_k2;
876 int K = s->track;
877 if(T1>T2){SWAP(T1,T2);}
878 if(K1<K2){SWAP(K1,K2);}
879 if(s->tick+s->dur > T1 && s->tick < T2 && K >= K2 && K <= K1){
880 *c1 = fltk::color(0,255,0);
881 *c2 = fltk::color(71,120,59);
882 *c3 = fltk::color(108,229,75);
883 return;
887 if(s->selected){
888 *c1 = fltk::color(255,255,0);
889 *c2 = fltk::color(140,137,46);
890 *c3 = fltk::color(232,255,37);
891 *cx = fltk::color(128,128,0);
892 return;
896 *c2 = fltk::color(p->r2,p->g2,p->b2);
897 *c3 = fltk::color(p->r3,p->g3,p->b3);
902 void Arranger::apply_insert(){
904 if(!check_insert_safety()){
905 return;
908 int tmp;
909 int T1 = insert_torig;
910 int T2 = T1 + insert_toffset;
911 if(T1>T2){SWAP(T1,T2);}
913 Command* c=new CreateSeqpatBlank(insert_track,T1,T2-T1);
914 set_undo(c);
915 undo_push(1);
917 if(T2>maxt){relayout();}
920 void Arranger::apply_box(){
921 seqpat* s;
922 int tmp;
923 int T1=box_t1;
924 int T2=box_t2;
925 int K1 = box_k1;
926 int K2 = box_k2;
927 if(T1>T2){SWAP(T1,T2);}
928 if(K1>K2){SWAP(K1,K2);}
929 if(K1 < 0){K1 = 0;}
930 if(K2 > tracks.size()-1){K2 = tracks.size()-1;}
931 for(int i=K1; i<=K2; i++){
932 s = tracks[i]->head->next;
933 while(s){
934 if(s->tick+s->dur > T1 && s->tick < T2){
935 s->selected = 1;
937 s = s->next;
943 void Arranger::apply_delete(){
944 Command* c;
945 seqpat* s;
946 seqpat* next;
947 int N=0;
948 for(int i=0; i<tracks.size(); i++){
949 s = tracks[i]->head->next;
950 while(s){
951 next = s->next;
952 if(s->selected){
953 tracks[s->track]->modified = 1;
954 c=new DeleteSeqpat(s);
955 set_undo(c);
956 N++;
958 s = next;
961 undo_push(N);
963 unmodify_and_unstick_tracks();
967 void Arranger::apply_move(){
968 if(move_toffset==0 && move_koffset==0){
969 return;
972 if(!check_move_safety()){
973 return;
976 Command* c;
977 seqpat* s;
978 seqpat* next;
979 int N=0;
980 for(int i=0; i<tracks.size(); i++){
981 s = tracks[i]->head->next;
982 while(s){
983 next = s->next;
984 if(s->selected && s->modified == 0){
985 int K = s->track + move_koffset;
986 int T = s->tick + move_toffset;
987 tracks[s->track]->modified = 1;
988 tracks[K]->modified = 1;
989 s->modified = 1;
990 c=new MoveSeqpat(s,K,T);
991 set_undo(c);
992 N++;
994 if(T+s->dur > maxt){relayout();}
996 s = next;
999 undo_push(N);
1001 unmodify_blocks();
1002 unmodify_and_unstick_tracks();
1005 void Arranger::apply_paste(){
1007 //recalc_paste_center();
1009 //safety check
1010 //if(!check_paste_safety()){
1011 // return;
1014 Command* c;
1015 c = new CreateSeqpat(paste_kcenter1,paste_tcenter1,main_sel,config.alwayscopy);
1016 set_undo(c);
1017 undo_push(1);
1019 tracks[paste_kcenter1]->restate();
1022 void Arranger::recalc_paste_center(){
1024 seqpat* s;
1026 int accum_t = 0;
1027 int accum_k = 0;
1028 int T = 0;
1029 int K = 0;
1031 for(int i=0; i<tracks.size(); i++){
1032 s = tracks[i]->head->next;
1033 while(s){
1034 if(s->selected){
1035 accum_t = (s->tick+s->dur) / 2;
1036 T++;
1038 accum_k = s->track;
1039 K++;
1041 s = s->next;
1046 printf("T %d K %d %d %d \n",T,K,accum_t/T,accum_k/K);
1048 paste_tcenter0 = accum_t / T;
1049 paste_kcenter0 = accum_k / K;
1053 void Arranger::apply_rresize(){
1054 if(rresize_toffset==0){
1055 return;
1058 if(!check_resize_safety()){
1059 return;
1062 Command* c;
1063 seqpat* s;
1064 seqpat* next;
1065 int tmp;
1066 int N=0;
1067 for(int i=0; i<tracks.size(); i++){
1068 s = tracks[i]->head->next;
1069 while(s){
1070 next = s->next;
1071 if(s->selected && s->modified == 0){
1072 tracks[i]->modified = 1;
1073 s->modified = 1;
1074 int T1 = s->tick;
1075 int T2 = s->tick + s->dur + rresize_toffset;
1076 if(T1 > T2){
1077 SWAP(T1,T2);
1078 seqpat* stmp = s->prev;
1079 //c=new ReverseSeqpat(s);
1080 //set_undo(c);
1081 s = stmp->next;
1082 c=new ResizeSeqpat(s,T2-T1);
1083 set_undo(c);
1084 s = stmp->next;
1085 c=new MoveSeqpat(s,s->track,T1);
1086 set_undo(c);
1087 N+=2;
1089 s = stmp->next;
1090 if(s->tick+s->dur > maxt){relayout();}
1092 else{
1093 if(T1==T2){
1094 T2 = T1+TICKS_PER_BEAT;
1096 c=new ResizeSeqpat(s,T2-T1);
1097 set_undo(c);
1098 N++;
1100 if(T2 > maxt){relayout();}
1104 s = next;
1107 undo_push(N);
1109 unmodify_blocks();
1110 unmodify_and_unstick_tracks();
1113 void Arranger::apply_lresize(){
1114 if(lresize_toffset==0){
1115 return;
1118 if(!check_resize_safety()){
1119 return;
1122 Command* c;
1123 seqpat* s;
1124 seqpat* next;
1125 int tmp;
1126 int N=0;
1127 for(int i=0; i<tracks.size(); i++){
1128 s = tracks[i]->head->next;
1129 while(s){
1130 next = s->next;
1131 if(s->selected && s->modified == 0){
1132 tracks[i]->modified = 1;
1133 s->modified = 1;
1134 int T1 = s->tick + lresize_toffset;
1135 int T2 = s->tick + s->dur;
1136 if(T1 > T2){
1137 SWAP(T1,T2);
1138 seqpat* stmp = s->prev;
1139 //c=new ReverseSeqpat(s);
1140 //set_undo(c);
1141 s = stmp->next;
1142 c=new ResizeSeqpat(s,T2-T1);
1143 set_undo(c);
1144 s = stmp->next;
1145 c=new MoveSeqpat(s,s->track,T1);
1146 set_undo(c);
1147 N+=2;
1149 s = stmp->next;
1150 if(s->tick+s->dur > maxt){relayout();}
1152 else{
1153 if(T1==T2){
1154 T2 = T1+TICKS_PER_BEAT;
1156 seqpat* stmp = s->prev;
1157 c=new MoveSeqpat(s,s->track,T1);
1158 set_undo(c);
1159 s = stmp->next;
1160 c=new ResizeSeqpat(s,T2-T1);
1161 set_undo(c);
1162 N+=2;
1164 s = stmp->next;
1165 if(s->tick+s->dur>maxt){relayout();}
1169 s = next;
1172 undo_push(N);
1174 unmodify_blocks();
1175 unmodify_and_unstick_tracks();
1180 int collision_test(int t11, int t12, int t21, int t22){
1181 return !((t11 < t21 && t12 <= t21) ||
1182 (t11 >= t22 && t12 > t22)) ? 1 : 0;
1185 int Arranger::check_move_safety(){
1186 seqpat* s;
1187 seqpat* ptr;
1189 for(int i=0; i<tracks.size(); i++){
1190 s = tracks[i]->head->next;
1191 while(s){
1192 if(s->selected){
1193 if(i+move_koffset < 0 || i+move_koffset > tracks.size()-1 ||
1194 s->tick + move_toffset < 0){
1195 return 0;
1197 ptr = tracks[i+move_koffset]->head->next;
1198 while(ptr){
1199 if(ptr == s){
1200 ptr=ptr->next; continue;
1202 if(collision_test(s->tick+move_toffset,s->tick+s->dur+move_toffset,ptr->tick,ptr->tick+ptr->dur) ){
1203 if(!ptr->selected){
1204 return 0;
1207 ptr = ptr->next;
1210 s = s->next;
1214 return 1;
1217 int Arranger::check_insert_safety(){
1218 seqpat* s;
1220 int T1 = insert_torig;
1221 int T2 = T1 + insert_toffset;
1222 int tmp;
1224 if(T1>T2){SWAP(T1,T2);}
1226 if(T1 < 0){
1227 return 0;
1229 if(insert_track > tracks.size()-1){
1230 return 0;
1232 if(tracks[insert_track]==NULL){
1233 return 0;
1236 s = tracks[insert_track]->head->next;
1238 while(s){
1239 if(collision_test(T1,T2,s->tick,s->tick+s->dur)){
1240 return 0;
1242 s = s->next;
1245 return 1;
1248 int Arranger::check_resize_safety(){
1249 seqpat* s;
1250 seqpat* ptr;
1252 int T1,T2;
1253 int S1,S2;
1254 int tmp;
1256 for(int i=0; i<tracks.size(); i++){
1257 s = tracks[i]->head->next;
1258 while(s){
1259 if(!s->selected){
1260 s = s->next; continue;
1263 if(rresize_flag){
1264 T1 = s->tick;
1265 T2 = s->tick + s->dur + rresize_toffset;
1267 else if(lresize_flag){
1268 T1 = s->tick + lresize_toffset;
1269 T2 = s->tick + s->dur;
1271 if(T1>T2){SWAP(T1,T2);}
1273 if(T1 < 0){
1274 return 0;
1276 ptr = tracks[s->track]->head->next;
1277 while(ptr){
1278 if(ptr == s){
1279 ptr=ptr->next; continue;
1282 S1 = ptr->tick;
1283 S2 = ptr->tick + ptr->dur;
1284 if(ptr->selected){
1285 if(rresize_flag){
1286 S2 += rresize_toffset;
1288 else if(lresize_flag){
1289 S1 += lresize_toffset;
1293 if(collision_test(T1,T2,S1,S2)){
1294 return 0;
1296 ptr = ptr->next;
1299 s = s->next;
1303 return 1;
1307 int Arranger::check_paste_safety(){
1309 return 1;
1312 int Arranger::check_join_safety(){
1313 seqpat* s = join_s;
1314 if(s->prev->tick+s->prev->dur==s->tick && s->prev->prev){
1315 return 1;
1317 return 0;
1321 void Arranger::apply_unclone(){
1322 Command* c;
1324 pattern* p2 = new pattern(main_sel->p);
1325 //does p2 need to have its ref_c set
1326 float a = randf(0.2,0.8);
1327 while(fabs(p2->v - a)<0.1){
1328 a = randf(0.2,0.8);
1330 p2->v = a;
1331 p2->regen_colors();
1333 //this creates a copy of the block, but uses a copy of the pattern
1334 seqpat* s2 = new seqpat(main_sel,p2);
1337 c = new DeleteSeqpat(main_sel);
1338 set_undo(c);
1339 main_sel = NULL;
1341 c = new CreateSeqpat(s2->track,s2->tick,s2,0);
1342 set_undo(c);
1344 undo_push(2);
1348 void Arranger::apply_split(){
1349 seqpat* s = split_s;
1350 if(split_t == s->tick || split_t == s->tick+s->dur){
1351 return;
1353 Command* c = new SplitSeqpat(split_s,split_t);
1354 set_undo(c);
1355 undo_push(1);
1359 void Arranger::apply_join(){
1360 seqpat* s = join_s;
1361 if(!check_join_safety()){
1362 return;
1364 Command* c = new JoinSeqpat(join_s->prev,join_s);
1365 set_undo(c);
1366 undo_push(1);