Restored pattern editor functionality.
[epichord.git] / src / arranger.cpp
blobaf13875e80663002433c22679b10aa214dc4ea4b
1 /*
2 Epichord - a midi sequencer
3 Copyright (C) 2008 Evan Rinehart
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to
18 The Free Software Foundation, Inc.
19 51 Franklin Street, Fifth Floor
20 Boston, MA 02110-1301, USA
23 #include <stdio.h>
24 #include <vector>
25 #include <fltk/Group.h>
26 #include <fltk/Widget.h>
27 #include <fltk/events.h>
29 #include "ui.h"
31 #include "uihelper.h"
33 #include "util.h"
35 #include "backend.h"
37 extern UI* ui;
39 extern std::vector<track*> tracks;
41 extern struct conf config;
43 using namespace fltk;
45 #define SWAP(X,Y) tmp=X; X=Y; Y=tmp;
47 Arranger::Arranger(int x, int y, int w, int h, const char* label = 0) : fltk::Widget(x, y, w, h, label) {
48 new_default_w = 128*4;
49 delete_flag = 0;
50 move_flag = 0;
51 paste_flag = 0;
52 main_sel = NULL;
54 zoom = 30;
55 zoom_n = 4;
57 q_tick = 128*4;
59 insert_flag = 0;
60 box_flag = 0;
61 rresize_flag = 0;
62 lresize_flag = 0;
64 last_handle == NULL;
66 color_flag = 0;
68 maxt = 0;
71 fakeh = 16*30;
72 if(fakeh < h){fakeh = h;}
74 scrollbuffer = 30;
78 int Arranger::handle(int event){
79 Command* c;
81 int X = event_x();
82 int Y = event_y();
84 seqpat* s;
86 switch(event){
87 case fltk::FOCUS:
88 return 1;
89 case fltk::ENTER:
90 return 1;
91 case fltk::KEYUP:
93 return 0;
94 case fltk::MOUSEWHEEL:
95 s = over_seqpat();
96 if(s){
97 s->autocomplete();
98 if(event_dy()>0){
99 s->prev_layer();
101 else if(event_dy()<0){
102 s->next_layer();
104 s->restate();
105 redraw();
107 return 1;
108 case fltk::SHORTCUT:
109 if(event_state() && event_key()=='c'){
111 return 1;
113 if(event_state() && event_key()=='v'){
115 return 1;
117 if(event_state() && event_key()=='z'){
119 return 1;
121 if(event_key()==fltk::DeleteKey){
122 apply_delete();
123 delete_flag = 0;
124 redraw();
125 return 1;
127 if(zoom_out_key(event_key(),event_state())){
128 //if(event_key()==fltk::LeftKey){
129 if(zoom_n > 1){
130 zoom_n--;
131 zoom = 30*(1<<zoom_n)/16;
132 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
133 ui->song_timeline->update(get_play_position());
134 ui->song_timeline->redraw();
135 relayout();
137 redraw();
138 return 1;
140 if(zoom_in_key(event_key(),event_state())){
141 //if(event_key()==fltk::RightKey){
142 if(zoom_n < 8){
143 zoom_n++;
144 zoom = 30*(1<<zoom_n)/16;
145 ui->song_timeline->zoom = 30*(1<<zoom_n)/16;
146 ui->song_timeline->update(get_play_position());
147 ui->song_timeline->redraw();
148 relayout();
150 redraw();
151 return 1;
153 return 0;
154 case fltk::PUSH:
155 take_focus();
156 if(event_button()==1){//left mouse
157 seqpat* s = over_seqpat();
158 if(s==NULL){
159 if(color_flag){//do nothing
161 else if(event_state()&fltk::SHIFT){//begin box
162 box_flag = 1;
163 box_x1=X;
164 box_x2=X;
165 box_y1=Y;
166 box_y2=Y;
167 box_t1=xpix2tick(X+scrollx);
168 box_t2=box_t1;
169 box_k1=(Y+scrolly)/30;
170 box_k2=box_k1;
172 else{//begin insert
173 insert_flag = 1;
174 insert_torig = xpix2tick(X+scrollx)/q_tick*q_tick;
175 insert_toffset = q_tick;
176 insert_track = (Y+scrolly) / 30;
179 else{
180 main_sel = s;
181 if(color_flag){
182 color_sel = s->p;
183 color_orig_x = X;
184 color_orig_y = Y;
185 color_orig_h = color_sel->h;
186 color_orig_v = color_sel->v;
187 color_h = color_orig_h;
188 color_v = color_orig_v;
189 return 1;
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->piano_roll->scrollTo(s->scrollx,s->scrolly);
199 ui->pattern_timeline->update(get_play_position());
200 ui->keyboard->cur_port = tracks[s->track]->port;
201 ui->keyboard->cur_chan = tracks[s->track]->chan;
202 ui->track_info->set_rec(s->track);
203 set_rec_track(s->track);
204 show_pattern_edit();
205 return 1;
208 if(over_lhandle(s)){//begin resize
209 lresize_flag = 1;
210 lresize_torig = s->tick;
211 lresize_toffset = 0;
213 else if(over_rhandle(s)){//begin resizemove
214 rresize_flag = 1;
215 rresize_torig = s->tick+s->dur;
216 rresize_toffset = 0;
219 else{//begin move
220 move_flag = 1;
221 move_torig = s->tick;
222 move_toffset = 0;
223 move_korig = s->track;
224 move_koffset = 0;
225 move_x = X+scrollx;
226 move_y = Y+scrolly;
227 move_offset = xpix2tick(X+scrollx)/q_tick*q_tick - s->tick;
231 else if(event_button()==2){//middle mouse
232 seqpat* s = over_seqpat();
233 if(color_flag && s){
234 s->p->h = color_h;
235 s->p->v = color_v;
236 s->p->regen_colors();
237 redraw();
238 return 1;
240 if(main_sel){
241 paste_flag = 1;
242 paste_t = quantize(xpix2tick(X+scrollx));
243 paste_track = (Y+scrolly) / 30;
246 else if(event_button()==3){//right mouse
247 seqpat* s = over_seqpat();
248 if(color_flag && s){
249 seqpat* ptr = tracks[s->track]->head->next;
250 while(ptr){
251 ptr->p->h = color_h;
252 ptr->p->v = color_v;
253 ptr->p->regen_colors();
254 ptr = ptr->next;
256 redraw();
257 return 1;
259 if(s==NULL){
260 unselect_all();
261 delete_sel = NULL;
262 main_sel = NULL;
263 color_sel = NULL;
265 else{//begin delete
266 delete_flag = 1;
267 delete_sel = s;//this line needs to be removed
268 if(!(s->selected)){
269 unselect_all();
271 s->selected = 1;
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 if(last_handle){
332 last_handle->lhandle = 0;
333 last_handle->rhandle = 0;
336 else if(lresize_flag){
337 apply_lresize();
338 lresize_flag = 0;
339 if(last_handle){
340 last_handle->lhandle = 0;
341 last_handle->rhandle = 0;
345 insert_flag=0;
346 color_sel = NULL;
348 else if(event_button()==2){
349 if(paste_flag){
350 apply_paste();
352 paste_flag=0;
354 else if(event_button()==3){
355 seqpat* over_s = over_seqpat();
356 if(delete_flag && over_s){
357 if(over_s->selected){
358 apply_delete();
361 delete_flag=0;
362 //last_handle==NULL;
367 redraw();
368 return 1;
369 case fltk::MOVE:
370 if(color_flag){break;}
371 seqpat* s = over_seqpat();
372 if(s){
373 if(over_rhandle(s)){s->rhandle = 1;}
374 else{s->rhandle = 0;}
375 if(over_lhandle(s)){s->lhandle = 1;}
376 else{s->lhandle = 0;}
377 if(s != last_handle){
378 if(last_handle){
379 last_handle->rhandle = 0;
380 last_handle->lhandle = 0;
382 last_handle = s;
384 redraw();
386 else if(last_handle){
387 last_handle->rhandle = 0;
388 last_handle->lhandle = 0;
389 last_handle = NULL;
390 redraw();
392 return 1;
395 return 0;
398 void Arranger::draw(){
400 fltk::push_clip(0,0,w(),h());
402 fltk::setfont(fltk::HELVETICA,8);
404 fltk::setcolor(fltk::GRAY05);
405 fltk::fillrect(0,0,w(),h());
407 fltk::setcolor(fltk::GRAY20);
408 int M = config.beats_per_measure;
409 int I=0;
410 for(int i=1; I<w(); i++){
411 I = i*zoom*M/4 - scrollx;
412 if(I>=0){
413 fltk::fillrect(I,0,1,h());
416 fltk::setcolor(fltk::GRAY50);
417 int P = config.measures_per_phrase;
418 if(P){
419 I=0;
420 for(int i=1; I<w(); i++){
421 I = i*zoom*4*P*M/4/4 - scrollx;
422 if(I>=0){
423 fltk::fillrect(I,0,1,h());
429 if(insert_flag){
430 fltk::setcolor(fltk::BLUE);
431 int T1 = insert_torig;
432 int T2 = T1 + insert_toffset;
433 int tmp;
434 if(T1>T2){SWAP(T1,T2);}
435 int X = tick2xpix(T1)+1 - scrollx;
436 int Y = insert_track*30 - scrolly;
437 int W = tick2xpix(T2)-tick2xpix(T1) - 1;
438 fltk::fillrect(X,Y,W,28);
441 if(move_flag){
442 if(check_move_safety()){
443 fltk::setcolor(fltk::MAGENTA);
445 else{
446 fltk::setcolor(fltk::RED);
449 for(int i=0; i<tracks.size(); i++){
450 seqpat* s = tracks[i]->head->next;
451 while(s){
452 if(s->selected){
453 int X = tick2xpix(s->tick + move_toffset) - scrollx;
454 int Y = (s->track + move_koffset)*30 - scrolly;
455 int W = tick2xpix(s->dur);
456 fltk::fillrect(X+1,Y+1,W-1,1);
457 fltk::fillrect(X+1,Y+1,1,29-1);
458 fltk::fillrect(X+1,Y+29-1,W-1,1);
459 fltk::fillrect(X+W-1,Y+1,1,29-1);
461 s = s->next;
466 if(paste_flag){
467 fltk::setcolor(fltk::GREEN);
468 int X = tick2xpix(paste_t)+1 - scrollx;
469 int Y = paste_track*30 - scrolly;
470 int W = tick2xpix(main_sel->dur);
471 fltk::fillrect(X,Y,W-1,1);
472 fltk::fillrect(X,Y+28,W-1,1);
473 fltk::fillrect(X,Y,1,28);
474 fltk::fillrect(X+W-2,Y,1,28);
477 int tmp;
478 if(box_flag){
479 fltk::setcolor(fltk::GREEN);
480 int X1,X2,Y1,Y2;
481 X1 = box_x1;
482 X2 = box_x2;
483 Y1 = box_y1;
484 Y2 = box_y2;
485 if(X1>X2){SWAP(X1,X2);}
486 if(Y1>Y2){SWAP(Y1,Y2);}
487 fltk::fillrect(X1,Y1,X2-X1,1);
488 fltk::fillrect(X1,Y1,1,Y2-Y1);
489 fltk::fillrect(X2,Y1,1,Y2-Y1);
490 fltk::fillrect(X1,Y2,X2-X1,1);
493 //draw all seqpat
494 seqpat* s;
495 fltk::Color c;
497 fltk::Color c1,c2,c3,cx;
498 c1 = fltk::BLACK;
500 for(int i=0; i<tracks.size(); i++){
502 s = tracks[i]->head->next;
503 while(s){
505 pattern* p = s->p;
507 get_outline_color(s,&c1,&c2,&c3,&cx);
509 fltk::setcolor(c1);
511 int R1 = lresize_flag&&s->selected ? lresize_toffset : 0;
512 int R2 = rresize_flag&&s->selected ? rresize_toffset : 0;
514 int T1 = s->tick+R1;
515 int T2 = s->tick+s->dur+R2;
517 if(T1 > T2){SWAP(T1,T2)};
519 int X = tick2xpix(T1)+1 - scrollx;
520 int Y = s->track * 30 - scrolly;
521 int W = tick2xpix(T2)-tick2xpix(T1)-1;
523 if(rresize_flag && s->selected && T1==T2){
524 W = tick2xpix(128)-1;
526 if(lresize_flag && s->selected && T1==T2){
527 W = tick2xpix(128)-1;
530 fillrect(X+1,Y+1,W-2,27);
531 float a = 1.5f;
534 fltk::setcolor(c2);
535 fillrect(X+W-1,Y,1,29);
536 fillrect(X,Y+28,W-1,1);
538 fltk::setcolor(c3);
539 fillrect(X,Y,1,28);
540 fillrect(X,Y,W,1);
542 fltk::push_clip(tick2xpix(T1)-scrollx,s->track*30-scrolly,tick2xpix(T2-T1),30);
544 if(s->rhandle && !rresize_flag){
545 setcolor(cx);
546 if(delete_flag){
547 setcolor(fltk::color(128,0,0));
550 W = 5;
551 X = tick2xpix(s->tick+s->dur) - W - 1 - scrollx;
552 Y = s->track*30 - scrolly;
553 addvertex(X+W,Y+28/2);
554 addvertex(X,Y);
555 addvertex(X,Y+28);
556 fillpath();
559 if(s->lhandle && !lresize_flag){
560 setcolor(cx);
561 if(delete_flag){
562 setcolor(fltk::color(128,0,0));
564 W = 5;
565 X = tick2xpix(s->tick)+1-scrollx;
566 Y = s->track*30 - scrolly;
567 addvertex(X,Y+28/2);
568 addvertex(X+W,Y);
569 addvertex(X+W,Y+28);
570 fillpath();
573 fltk::setcolor(cx);
576 mevent* e = s->p->events;
577 while(e){
578 if(e->tick >= s->dur){
579 break;
581 if(e->type == MIDI_NOTE_ON){
582 X = tick2xpix(e->tick) + tick2xpix(s->tick)+2 - scrollx;
583 Y = s->track*30 + 27 - e->value1*27/127;
584 W = tick2xpix(e->dur);
585 if(W==0){W=1;}
586 fillrect(X,Y,W,1);
588 e=e->next;
592 int total = s->layer_total();
593 if(total > 1){
594 fltk::setcolor(fltk::BLACK);
595 int X = tick2xpix(s->tick) - scrollx;
596 int Y = s->track * 30 - scrolly;
597 int count = s->layer_index()+1;
598 char buf[16];
599 snprintf(buf,16,"%d / %d",count,total);
600 fltk::drawtext(buf,X+2,Y+27);
604 fltk::pop_clip();
606 s=s->next;
610 fltk::pop_clip();
614 void Arranger::scrollTo(int X, int Y){
616 if(is_backend_playing() && config.follow){
617 int pos = tick2xpix(get_play_position());
618 if(pos < X || pos > X + w() - scrollbuffer - 30){
619 ui->song_hscroll->value(scrollx);
620 return;
623 //and the other one
626 scrollx = X;
627 ui->song_hscroll->value(scrollx);
628 //and the other one
629 scrolly = Y;
630 redraw();
631 ui->song_timeline->scroll = X;
632 ui->song_timeline->redraw();
633 ui->track_info->scroll = Y;
634 ui->track_info->redraw();
639 seqpat* Arranger::over_seqpat(){
640 int track = (event_y()+scrolly) / 30;
641 if(track >= tracks.size()){
642 return NULL;
644 int tick = xpix2tick(event_x()+scrollx);
645 seqpat* s = tfind<seqpat>(tracks[track]->head,tick);
646 if(s){
647 if(tick < s->tick+s->dur){
648 return s;
651 return NULL;
655 //true if over right handle of s
656 int Arranger::over_rhandle(seqpat* s){
657 int X = event_x();
658 int Y = event_y();
659 int X1 = tick2xpix(s->tick) - scrollx;
660 int X2 = X1 + tick2xpix(s->dur);
661 int Y1 = s->track * 30 + 1 - scrolly;
662 int Y2 = Y1 + 29;
664 if(tick2xpix(s->dur) < 10){
665 return 0;
668 return (Y > Y1 && Y < Y2 && X < X2 && X > X2 - 5);
671 //true if over left handle of s
672 int Arranger::over_lhandle(seqpat* s){
673 int X = event_x();
674 int Y = event_y();
675 int X1 = tick2xpix(s->tick) - scrollx;
676 int X2 = X1 + tick2xpix(s->dur);
677 int Y1 = s->track * 30 + 1 - scrolly;
678 int Y2 = Y1 + 29;
680 if(tick2xpix(s->dur) < 10){
681 return 0;
684 if(Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1){
685 //printf("success\n");
687 return (Y > Y1 && Y < Y2 && X < X1 + 5 + 1 && X > X1+1);
690 // 4=beats per measure, 128=ticks per beat, 30=width of measure in pixels
691 int Arranger::tick2xpix(int tick){
692 return tick *zoom /(128*4);
695 int Arranger::xpix2tick(int xpix){
696 return xpix * (128*4) /zoom;
699 int Arranger::quantize(int tick){
700 return tick/q_tick * q_tick;
704 void Arranger::update(int pos){
705 if(!is_backend_playing()){
706 return;
708 int X1 = tick2xpix(pos);
709 int X2 = X1 - scrollx;
710 if(X2 < 0){
711 int target = X1-50<0?0:X1-50;
712 scrollTo(target,scrolly);
713 // ui->song_hscroll->value(target);
715 if(X2 > w()-scrollbuffer){
716 int target = X1-50;
717 scrollTo(target,scrolly);
718 //ui->song_hscroll->value(target);
722 int kludge=2;
723 void Arranger::layout(){
724 if(kludge!=0){
725 kludge--;
726 return;
728 fakeh = tracks.size()*30;
729 if(fakeh<h()){
730 fakeh = h();
732 ui->song_vscroll->maximum(0);
733 ui->song_vscroll->minimum(fakeh-h());
734 int M = ui->song_vscroll->h() - 30;
735 int newsize = M-(fakeh-h());
736 if(newsize<20){
737 newsize=20;
739 ui->song_vscroll->slider_size(newsize);
743 void Arranger::unselect_all(){
744 seqpat* s;
745 for(int i=0; i<tracks.size(); i++){
746 s = tracks[i]->head->next;
747 while(s){
748 if(s->selected==1){
749 s->selected = 0;
751 s = s->next;
756 void Arranger::get_outline_color(seqpat* s, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3, fltk::Color* cx){
758 pattern* p = s->p;
759 *c1 = fltk::color(p->r1, p->g1, p->b1);
760 *cx = fltk::color(p->rx, p->gx, p->bx);
762 int T1,T2;
763 int tmp;
764 seqpat* over_s = over_seqpat();
765 if(delete_flag && s->selected){
766 *c1 = fltk::color(255,0,0);
767 *c2 = fltk::color(255,0,0);
768 *c3 = fltk::color(255,0,0);
769 return;
772 if(box_flag){
773 T1=box_t1;
774 T2=box_t2;
775 int K1 = box_k1;
776 int K2 = box_k2;
777 int K = s->track;
778 if(T1>T2){SWAP(T1,T2);}
779 if(K1<K2){SWAP(K1,K2);}
780 if(s->tick+s->dur > T1 && s->tick < T2 && K >= K2 && K <= K1){
781 *c1 = fltk::color(0,255,0);
782 *c2 = fltk::color(71,120,59);
783 *c3 = fltk::color(108,229,75);
784 return;
788 if(s->selected){
789 *c1 = fltk::color(255,255,0);
790 *c2 = fltk::color(140,137,46);
791 *c3 = fltk::color(232,255,37);
792 *cx = fltk::color(128,128,0);
793 return;
797 *c2 = fltk::color(p->r2,p->g2,p->b2);
798 *c3 = fltk::color(p->r3,p->g3,p->b3);
803 void Arranger::apply_insert(){
805 if(!check_insert_safety()){
806 return;
809 int tmp;
810 int T1 = insert_torig;
811 int T2 = T1 + insert_toffset;
812 if(T1>T2){SWAP(T1,T2);}
814 Command* c=new CreateSeqpatBlank(insert_track,T1,T2-T1);
815 set_undo(c);
816 undo_push(1);
818 if(T2>maxt){relayout();}
821 void Arranger::apply_box(){
822 seqpat* s;
823 int tmp;
824 int T1=box_t1;
825 int T2=box_t2;
826 int K1 = box_k1;
827 int K2 = box_k2;
828 if(T1>T2){SWAP(T1,T2);}
829 if(K1>K2){SWAP(K1,K2);}
830 if(K1 < 0){K1 = 0;}
831 if(K2 > tracks.size()-1){K2 = tracks.size()-1;}
832 for(int i=K1; i<=K2; i++){
833 s = tracks[i]->head->next;
834 while(s){
835 if(s->tick+s->dur > T1 && s->tick < T2){
836 s->selected = 1;
838 s = s->next;
844 void Arranger::apply_delete(){
845 Command* c;
846 seqpat* s;
847 seqpat* next;
848 int N=0;
849 for(int i=0; i<tracks.size(); i++){
850 s = tracks[i]->head->next;
851 while(s){
852 next = s->next;
853 if(s->selected){
854 tracks[s->track]->modified = 1;
855 c=new DeleteSeqpat(s);
856 set_undo(c);
857 N++;
859 s = next;
862 undo_push(N);
864 unmodify_and_unstick_tracks();
868 void Arranger::apply_move(){
869 if(move_toffset==0 && move_koffset==0){
870 return;
873 if(!check_move_safety()){
874 return;
877 Command* c;
878 seqpat* s;
879 seqpat* next;
880 int N=0;
881 for(int i=0; i<tracks.size(); i++){
882 s = tracks[i]->head->next;
883 while(s){
884 next = s->next;
885 if(s->selected && s->modified == 0){
886 int K = s->track + move_koffset;
887 int T = s->tick + move_toffset;
888 tracks[s->track]->modified = 1;
889 tracks[K]->modified = 1;
890 s->modified = 1;
891 c=new MoveSeqpat(s,K,T);
892 set_undo(c);
893 N++;
895 if(T+s->dur > maxt){relayout();}
897 s = next;
900 undo_push(N);
902 unmodify_blocks();
903 unmodify_and_unstick_tracks();
906 void Arranger::apply_paste(){
907 //safety check
910 Command* c;
912 c = new CreateSeqpat(paste_track,paste_t,main_sel,0);
913 set_undo(c);
914 undo_push(1);
919 void Arranger::apply_rresize(){
920 if(rresize_toffset==0){
921 return;
924 if(!check_resize_safety()){
925 return;
928 Command* c;
929 seqpat* s;
930 seqpat* next;
931 int tmp;
932 int N=0;
933 for(int i=0; i<tracks.size(); i++){
934 s = tracks[i]->head->next;
935 while(s){
936 next = s->next;
937 if(s->selected && s->modified == 0){
938 tracks[i]->modified = 1;
939 s->modified = 1;
940 int T1 = s->tick;
941 int T2 = s->tick + s->dur + rresize_toffset;
942 if(T1 > T2){
943 SWAP(T1,T2);
944 seqpat* stmp = s->prev;
945 //c=new ReverseSeqpat(s);
946 //set_undo(c);
947 s = stmp->next;
948 c=new ResizeSeqpat(s,T2-T1);
949 set_undo(c);
950 s = stmp->next;
951 c=new MoveSeqpat(s,s->track,T1);
952 set_undo(c);
953 N+=2;
955 s = stmp->next;
956 if(s->tick+s->dur > maxt){relayout();}
958 else{
959 if(T1==T2){
960 T2 = T1+128; //magic
962 c=new ResizeSeqpat(s,T2-T1);
963 set_undo(c);
964 N++;
966 if(T2 > maxt){relayout();}
970 s = next;
973 undo_push(N);
975 unmodify_blocks();
976 unmodify_and_unstick_tracks();
979 void Arranger::apply_lresize(){
980 if(lresize_toffset==0){
981 return;
984 if(!check_resize_safety()){
985 return;
988 Command* c;
989 seqpat* s;
990 seqpat* next;
991 int tmp;
992 int N=0;
993 for(int i=0; i<tracks.size(); i++){
994 s = tracks[i]->head->next;
995 while(s){
996 next = s->next;
997 if(s->selected && s->modified == 0){
998 tracks[i]->modified = 1;
999 s->modified = 1;
1000 int T1 = s->tick + lresize_toffset;
1001 int T2 = s->tick + s->dur;
1002 if(T1 > T2){
1003 SWAP(T1,T2);
1004 seqpat* stmp = s->prev;
1005 //c=new ReverseSeqpat(s);
1006 //set_undo(c);
1007 s = stmp->next;
1008 c=new ResizeSeqpat(s,T2-T1);
1009 set_undo(c);
1010 s = stmp->next;
1011 c=new MoveSeqpat(s,s->track,T1);
1012 set_undo(c);
1013 N+=2;
1015 s = stmp->next;
1016 if(s->tick+s->dur > maxt){relayout();}
1018 else{
1019 if(T1==T2){
1020 T2 = T1+128; //magic
1022 seqpat* stmp = s->prev;
1023 c=new MoveSeqpat(s,s->track,T1);
1024 set_undo(c);
1025 s = stmp->next;
1026 c=new ResizeSeqpat(s,T2-T1);
1027 set_undo(c);
1028 N+=2;
1030 s = stmp->next;
1031 if(s->tick+s->dur>maxt){relayout();}
1035 s = next;
1038 undo_push(N);
1040 unmodify_blocks();
1041 unmodify_and_unstick_tracks();
1046 int collision_test(int t11, int t12, int t21, int t22){
1047 return !((t11 < t21 && t12 <= t21) ||
1048 (t11 >= t22 && t12 > t22)) ? 1 : 0;
1051 int Arranger::check_move_safety(){
1052 seqpat* s;
1053 seqpat* ptr;
1055 for(int i=0; i<tracks.size(); i++){
1056 s = tracks[i]->head->next;
1057 while(s){
1058 if(s->selected){
1059 if(i+move_koffset < 0 || i+move_koffset > tracks.size()-1 ||
1060 s->tick + move_toffset < 0){
1061 return 0;
1063 ptr = tracks[i+move_koffset]->head->next;
1064 while(ptr){
1065 if(ptr == s){
1066 ptr=ptr->next; continue;
1068 if(collision_test(s->tick+move_toffset,s->tick+s->dur+move_toffset,ptr->tick,ptr->tick+ptr->dur) ){
1069 if(!ptr->selected){
1070 return 0;
1073 ptr = ptr->next;
1076 s = s->next;
1080 return 1;
1083 int Arranger::check_insert_safety(){
1084 seqpat* s;
1086 int T1 = insert_torig;
1087 int T2 = T1 + insert_toffset;
1088 int tmp;
1090 if(T1>T2){SWAP(T1,T2);}
1092 if(T1 < 0){
1093 return 0;
1095 if(insert_track > tracks.size()-1){
1096 return 0;
1098 if(tracks[insert_track]==NULL){
1099 return 0;
1102 s = tracks[insert_track]->head->next;
1104 while(s){
1105 if(collision_test(T1,T2,s->tick,s->tick+s->dur)){
1106 return 0;
1108 s = s->next;
1111 return 1;
1114 int Arranger::check_resize_safety(){
1115 seqpat* s;
1116 seqpat* ptr;
1118 int T1,T2;
1119 int S1,S2;
1120 int tmp;
1122 for(int i=0; i<tracks.size(); i++){
1123 s = tracks[i]->head->next;
1124 while(s){
1125 if(!s->selected){
1126 s = s->next; continue;
1129 if(rresize_flag){
1130 T1 = s->tick;
1131 T2 = s->tick + s->dur + rresize_toffset;
1133 else if(lresize_flag){
1134 T1 = s->tick + lresize_toffset;
1135 T2 = s->tick + s->dur;
1137 if(T1>T2){SWAP(T1,T2);}
1139 if(T1 < 0){
1140 return 0;
1142 ptr = tracks[s->track]->head->next;
1143 while(ptr){
1144 if(ptr == s){
1145 ptr=ptr->next; continue;
1148 S1 = ptr->tick;
1149 S2 = ptr->tick + ptr->dur;
1150 if(ptr->selected){
1151 if(rresize_flag){
1152 S2 += rresize_toffset;
1154 else if(lresize_flag){
1155 S1 += lresize_toffset;
1159 if(collision_test(T1,T2,S1,S2)){
1160 return 0;
1162 ptr = ptr->next;
1165 s = s->next;
1169 return 1;
1173 int Arranger::check_paste_safety(){
1174 return 1;